diff options
Diffstat (limited to 'drivers')
364 files changed, 37373 insertions, 5954 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 1c26f33f99c2..5504162c55da 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -182,4 +182,8 @@ source "drivers/bif/Kconfig" source "drivers/sensors/Kconfig" +source "drivers/phy/Kconfig" + +source "drivers/soc/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index d01aa057b563..a0e9647fba37 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -8,6 +8,8 @@ obj-y += irqchip/ obj-y += bus/ +obj-$(CONFIG_GENERIC_PHY) += phy/ + # GPIO must come after pinctrl as gpios may need to mux pins etc obj-y += pinctrl/ obj-y += gpio/ @@ -17,6 +19,7 @@ obj-$(CONFIG_PARISC) += parisc/ obj-$(CONFIG_RAPIDIO) += rapidio/ obj-y += video/ obj-y += idle/ +obj-y += soc/ # IPMI must come before ACPI in order to provide IPMI opregion support obj-$(CONFIG_IPMI_HANDLER) += char/ipmi/ @@ -55,7 +58,7 @@ obj-$(CONFIG_FB_I810) += video/i810/ obj-$(CONFIG_FB_INTEL) += video/intelfb/ obj-$(CONFIG_PARPORT) += parport/ -obj-y += base/ block/ misc/ mfd/ nfc/ +obj-y += base/ block/ misc/ mfd/ nfc/ soc/ obj-$(CONFIG_NUBUS) += nubus/ obj-y += macintosh/ obj-$(CONFIG_IDE) += ide/ diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index a5a3ebcbdd2c..da5290224954 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -97,6 +97,17 @@ config SATA_AHCI_PLATFORM If unsure, say N. +config SATA_AHCI_MSM + tristate "MSM AHCI SATA support" + depends on OF && ARCH_MSM + select SATA_AHCI_PLATFORM + help + This option enables support for AHCI SATA controller + integrated into MSM chipsets. For more information + please refer to http://www.qualcomm.com/chipsets. + + If unsure, say N. + config SATA_FSL tristate "Freescale 3.0Gbps SATA support" depends on FSL_SOC diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index c04d0fd038a3..34f1b73ed3e3 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o obj-$(CONFIG_SATA_SIL24) += sata_sil24.o obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o +obj-$(CONFIG_SATA_AHCI_MSM) += ahci_msm.o # SFF w/ custom DMA obj-$(CONFIG_PDC_ADMA) += pdc_adma.o diff --git a/drivers/ata/ahci_msm.c b/drivers/ata/ahci_msm.c new file mode 100644 index 000000000000..db990353227a --- /dev/null +++ b/drivers/ata/ahci_msm.c @@ -0,0 +1,528 @@ +/* + * Copyright (c) 2013, The Linux Foundation. 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 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/ahci_platform.h> +#include <linux/phy/phy.h> +#include <linux/of.h> +#include <linux/of_device.h> + +struct msm_ahci_clk_info { + struct list_head list; + struct clk *clk; + const char *name; + u32 max_freq; + bool enabled; +}; + +struct msm_ahci_host { + struct platform_device *ahci_pdev; + struct list_head clk_list_head; + void __iomem *ahci_base; + struct phy *phy; + bool phy_powered_on; +}; + +static int msm_ahci_enable_clk(struct device *dev, + struct msm_ahci_clk_info *clki) +{ + int ret = 0; + + if (clki->enabled) + goto out; + + ret = clk_prepare_enable(clki->clk); + if (ret) { + dev_err(dev, "%s: %s prepare enable failed, %d\n", + __func__, clki->name, ret); + } else { + clki->enabled = true; + dev_dbg(dev, "%s: clk: %s enabled\n", + __func__, clki->name); + } +out: + return ret; +} + +static void msm_ahci_disable_clk(struct device *dev, + struct msm_ahci_clk_info *clki) +{ + if (!clki->enabled) + return; + + clk_disable_unprepare(clki->clk); + clki->enabled = false; + dev_dbg(dev, "%s: clk: %s disabled\n", + __func__, clki->name); +} + +static int msm_ahci_setup_asic_rbc_clks(struct msm_ahci_host *host, bool on) +{ + int ret = 0; + struct device *dev = host->ahci_pdev->dev.parent; + struct msm_ahci_clk_info *clki; + struct list_head *head = &host->clk_list_head; + + if (!head || list_empty(head)) + goto out; + + /* + * asic0_clk and rbc0_clk should be enabled/disabled + * only when PHY is powered on. + */ + if (!host->phy_powered_on) + goto out; + +gate_ungate: + list_for_each_entry(clki, head, list) { + if (IS_ERR_OR_NULL(clki->clk)) + continue; + + if (!strcmp(clki->name, "asic0_clk") || + !strcmp(clki->name, "rbc0_clk")) { + if (on) + ret = msm_ahci_enable_clk(dev, clki); + else + msm_ahci_disable_clk(dev, clki); + } + } +out: + if (ret) { + on = false; + goto gate_ungate; + } + + return ret; +} + +static int msm_ahci_setup_clocks(struct msm_ahci_host *host, bool on) +{ + int ret = 0; + struct device *dev = host->ahci_pdev->dev.parent; + struct msm_ahci_clk_info *clki; + struct list_head *head = &host->clk_list_head; + + if (!head || list_empty(head)) + goto out; + +gate_ungate: + list_for_each_entry(clki, head, list) { + if (IS_ERR_OR_NULL(clki->clk)) + continue; + + /* + * asic0_clk and rbc0_clk are handled separately + * see msm_ahci_setup_asic_rbc_clks(). + */ + if (!strcmp(clki->name, "asic0_clk") || + !strcmp(clki->name, "rbc0_clk")) + continue; + if (on) + ret = msm_ahci_enable_clk(dev, clki); + else + msm_ahci_disable_clk(dev, clki); + } +out: + if (ret) { + on = false; + goto gate_ungate; + } + + return ret; +} + +static int msm_ahci_get_clocks(struct msm_ahci_host *host) +{ + int ret = 0; + struct msm_ahci_clk_info *clki; + struct device *dev = host->ahci_pdev->dev.parent; + struct list_head *head = &host->clk_list_head; + + if (!head || list_empty(head)) + goto out; + + list_for_each_entry(clki, head, list) { + if (!clki->name) + continue; + + clki->clk = devm_clk_get(dev, clki->name); + if (IS_ERR(clki->clk)) { + ret = PTR_ERR(clki->clk); + dev_err(dev, "%s: %s clk get failed, %d\n", + __func__, clki->name, ret); + goto out; + } + + if (clki->max_freq) { + ret = clk_set_rate(clki->clk, clki->max_freq); + if (ret) { + dev_err(dev, "%s: %s clk set rate(%dHz) failed, %d\n", + __func__, clki->name, + clki->max_freq, ret); + goto out; + } + } + dev_dbg(dev, "%s: clk: %s, rate: %lu\n", __func__, + clki->name, clk_get_rate(clki->clk)); + } +out: + return ret; +} + +static int msm_ahci_parse_clock_info(struct msm_ahci_host *host) +{ + int ret = 0; + int cnt; + int i; + struct device *dev = host->ahci_pdev->dev.parent; + struct device_node *np = dev->of_node; + char *name; + u32 *clkfreq = NULL; + struct msm_ahci_clk_info *clki; + + if (!np) + goto out; + + INIT_LIST_HEAD(&host->clk_list_head); + + cnt = of_property_count_strings(np, "clock-names"); + if (!cnt || (cnt == -EINVAL)) { + dev_info(dev, "%s: Unable to find clocks, assuming enabled\n", + __func__); + } else if (cnt < 0) { + dev_err(dev, "%s: count clock strings failed, err %d\n", + __func__, cnt); + ret = cnt; + } + + if (cnt <= 0) + goto out; + + clkfreq = kzalloc(cnt * sizeof(*clkfreq), GFP_KERNEL); + if (!clkfreq) { + ret = -ENOMEM; + dev_err(dev, "%s: memory alloc failed\n", __func__); + goto out; + } + + ret = of_property_read_u32_array(np, + "max-clock-frequency-hz", clkfreq, cnt); + if (ret && (ret != -EINVAL)) { + dev_err(dev, "%s: invalid max-clock-frequency-hz property, %d\n", + __func__, ret); + goto out; + } + + for (i = 0; i < cnt; i++) { + ret = of_property_read_string_index(np, + "clock-names", i, (const char **)&name); + if (ret) + goto out; + + clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL); + if (!clki) { + ret = -ENOMEM; + goto out; + } + + clki->max_freq = clkfreq[i]; + clki->name = kstrdup(name, GFP_KERNEL); + list_add_tail(&clki->list, &host->clk_list_head); + } +out: + kfree(clkfreq); + return ret; +} + +static int msm_ahci_init_clocks(struct msm_ahci_host *host) +{ + int ret = 0; + + ret = msm_ahci_parse_clock_info(host); + if (ret) + goto out; + + ret = msm_ahci_get_clocks(host); + if (ret) + goto out; + + ret = msm_ahci_setup_clocks(host, true); + if (ret) + goto out; +out: + return ret; +} + +static void msm_ahci_exit_clocks(struct msm_ahci_host *host) +{ + msm_ahci_setup_clocks(host, false); +} + +static int msm_ahci_init_phy(struct msm_ahci_host *host) +{ + int ret = 0; + struct device *dev = host->ahci_pdev->dev.parent; + + host->phy = devm_phy_get(dev, "sata-6g"); + if (IS_ERR(host->phy)) { + ret = PTR_ERR(host->phy); + dev_err(dev, "PHY get failed %d\n", ret); + goto out; + } + + ret = phy_init(host->phy); + if (ret) { + dev_err(dev, "PHY initialization failed %d\n", ret); + goto out; + } + + ret = phy_power_on(host->phy); + if (ret) { + dev_err(dev, "PHY power on failed %d\n", ret); + goto out; + } + + host->phy_powered_on = true; + + /* asic0 and rbc0 clks needs to be ungated only after phy power on */ + ret = msm_ahci_setup_asic_rbc_clks(host, true); + if (ret) { + dev_err(dev, "failed to enable asic0/rbc0 clks %d", ret); + goto out; + } + +out: + return ret; +} + +static void msm_ahci_exit_phy(struct msm_ahci_host *host) +{ + msm_ahci_setup_asic_rbc_clks(host, false); + phy_power_off(host->phy); + host->phy_powered_on = false; +} + +static int msm_ahci_init(struct device *ahci_dev, void __iomem *addr) +{ + int ret; + struct device *dev = ahci_dev->parent; + struct msm_ahci_host *host = dev_get_drvdata(dev); + + /* Save ahci mmio to access vendor specific registers */ + host->ahci_base = addr; + + ret = msm_ahci_init_clocks(host); + if (ret) { + dev_err(dev, "AHCI clk init failed with err=%d\n", ret); + goto out; + } + + ret = msm_ahci_init_phy(host); + if (ret) { + dev_err(dev, "SATA PHY init failed with err=%d\n", ret); + goto out_exit_clks; + } + + return 0; + +out_exit_clks: + msm_ahci_exit_clocks(host); +out: + return ret; +} + +static void msm_ahci_exit(struct device *ahci_dev) +{ + struct device *dev = ahci_dev->parent; + struct msm_ahci_host *host = dev_get_drvdata(dev); + + + msm_ahci_exit_phy(host); + msm_ahci_exit_clocks(host); +} + +static int msm_ahci_suspend(struct device *ahci_dev) +{ + int ret; + struct device *dev = ahci_dev->parent; + struct msm_ahci_host *host = dev_get_drvdata(dev); + + msm_ahci_setup_asic_rbc_clks(host, false); + ret = phy_power_off(host->phy); + if (ret) { + dev_err(dev, "%s: PHY power off failed %d\n", __func__, ret); + goto out; + } else { + host->phy_powered_on = false; + } + + msm_ahci_setup_clocks(host, false); +out: + return ret; +} + +static int msm_ahci_resume(struct device *ahci_dev) +{ + int ret; + struct device *dev = ahci_dev->parent; + struct msm_ahci_host *host = dev_get_drvdata(dev); + + ret = msm_ahci_setup_clocks(host, true); + if (ret) + goto out; + + ret = phy_power_on(host->phy); + if (ret) { + dev_err(dev, "%s: PHY power on failed %d\n", __func__, ret); + goto out; + } else { + host->phy_powered_on = true; + } + + /* asic0 and rbc0 clks needs to be ungated only after phy power on */ + ret = msm_ahci_setup_asic_rbc_clks(host, true); +out: + return ret; +} + +static struct ahci_platform_data msm_ahci_pdata = { + .init = msm_ahci_init, + .exit = msm_ahci_exit, + .suspend = msm_ahci_suspend, + .resume = msm_ahci_resume, +}; + +static const struct of_device_id msm_ahci_of_match[] = { + { .compatible = "qcom,msm-ahci", .data = &msm_ahci_pdata }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, msm_ahci_of_match); + +static int msm_ahci_probe(struct platform_device *pdev) +{ + int err; + struct msm_ahci_host *host; + struct device *dev = &pdev->dev; + const struct of_device_id *of_id; + const struct ahci_platform_data *pdata = NULL; + struct platform_device *ahci_pdev; + struct device *ahci_dev; + int ret = 0; + + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) { + dev_err(dev, "failed to allocate memory for msm host\n"); + ret = -ENOMEM; + goto err; + } + + ahci_pdev = platform_device_alloc("ahci", -1); + if (!ahci_pdev) { + dev_err(dev, "failed to allocate ahci platform device\n"); + ret = -ENODEV; + goto err; + } + + ahci_dev = &ahci_pdev->dev; + ahci_dev->parent = dev; + + if (!dev->dma_mask) + dev->dma_mask = &dev->coherent_dma_mask; + + if (!dma_set_mask(dev, DMA_BIT_MASK(64))) { + dma_set_coherent_mask(dev, DMA_BIT_MASK(64)); + } else if (!dma_set_mask(dev, DMA_BIT_MASK(32))) { + dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); + } else { + err = -EIO; + dev_err(dev, "unable to set dma mask\n"); + goto err_put_device; + } + + ahci_dev->dma_mask = dev->dma_mask; + ahci_dev->dma_parms = dev->dma_parms; + dma_set_coherent_mask(ahci_dev, dev->coherent_dma_mask); + + host->ahci_pdev = ahci_pdev; + + platform_set_drvdata(pdev, host); + + of_id = of_match_device(msm_ahci_of_match, dev); + if (of_id) { + pdata = of_id->data; + } else { + ret = -EINVAL; + dev_err(dev, "pdata is required to initialze ahci %d\n", ret); + goto err; + } + + ahci_dev->of_node = dev->of_node; + + ret = platform_device_add_resources(ahci_pdev, + pdev->resource, pdev->num_resources); + if (ret) { + dev_err(dev, "failed to add resources %d\n", ret); + goto err_put_device; + } + + ret = platform_device_add_data(ahci_pdev, pdata, sizeof(*pdata)); + if (ret) { + dev_err(dev, "failed to add pdata %d\n", ret); + goto err_put_device; + } + + ret = platform_device_add(ahci_pdev); + if (ret) { + dev_err(dev, "failed to register ahci device %d\n", ret); + goto err_put_device; + } + + return 0; + +err_put_device: + platform_device_put(ahci_pdev); +err: + return ret; +} + +static int msm_ahci_remove(struct platform_device *pdev) +{ + struct msm_ahci_host *host = platform_get_drvdata(pdev); + + platform_device_unregister(host->ahci_pdev); + + return 0; +} + +static struct platform_driver msm_ahci_driver = { + .probe = msm_ahci_probe, + .remove = msm_ahci_remove, + .driver = { + .name = "ahci-msm", + .owner = THIS_MODULE, + .of_match_table = msm_ahci_of_match, + }, +}; +module_platform_driver(msm_ahci_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("AHCI platform MSM Glue Layer"); diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 4221d1132aff..f451f43c4f21 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -356,7 +356,6 @@ static const struct dev_pm_ops ahci_pm_ops = { static const struct of_device_id ahci_of_match[] = { { .compatible = "snps,spear-ahci", }, - { .compatible = "qcom,msm-ahci", }, {}, }; MODULE_DEVICE_TABLE(of, ahci_of_match); diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index f0077cb8e249..9ee8975e2a7f 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -21,7 +21,7 @@ #include <linux/list.h> #include <linux/rculist.h> #include <linux/rcupdate.h> -#include <linux/opp.h> +#include <linux/pm_opp.h> #include <linux/of.h> #include <linux/export.h> @@ -136,7 +136,7 @@ static struct device_opp *find_device_opp(struct device *dev) } /** - * opp_get_voltage() - Gets the voltage corresponding to an available opp + * dev_pm_opp_get_voltage() - Gets the voltage corresponding to an available opp * @opp: opp for which voltage has to be returned for * * Return voltage in micro volt corresponding to the opp, else @@ -150,7 +150,7 @@ static struct device_opp *find_device_opp(struct device *dev) * prior to unlocking with rcu_read_unlock() to maintain the integrity of the * pointer. */ -unsigned long opp_get_voltage(struct opp *opp) +unsigned long dev_pm_opp_get_voltage(struct opp *opp) { struct opp *tmp_opp; unsigned long v = 0; @@ -163,10 +163,10 @@ unsigned long opp_get_voltage(struct opp *opp) return v; } -EXPORT_SYMBOL_GPL(opp_get_voltage); +EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage); /** - * opp_get_freq() - Gets the frequency corresponding to an available opp + * dev_pm_opp_get_freq() - Gets the frequency corresponding to an available opp * @opp: opp for which frequency has to be returned for * * Return frequency in hertz corresponding to the opp, else @@ -180,7 +180,7 @@ EXPORT_SYMBOL_GPL(opp_get_voltage); * prior to unlocking with rcu_read_unlock() to maintain the integrity of the * pointer. */ -unsigned long opp_get_freq(struct opp *opp) +unsigned long dev_pm_opp_get_freq(struct opp *opp) { struct opp *tmp_opp; unsigned long f = 0; @@ -193,10 +193,10 @@ unsigned long opp_get_freq(struct opp *opp) return f; } -EXPORT_SYMBOL_GPL(opp_get_freq); +EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); /** - * opp_get_opp_count() - Get number of opps available in the opp list + * dev_pm_opp_get_opp_count() - Get number of opps available in the opp list * @dev: device for which we do this operation * * This function returns the number of available opps if there are any, @@ -206,7 +206,7 @@ EXPORT_SYMBOL_GPL(opp_get_freq); * internally references two RCU protected structures: device_opp and opp which * are safe as long as we are under a common RCU locked section. */ -int opp_get_opp_count(struct device *dev) +int dev_pm_opp_get_opp_count(struct device *dev) { struct device_opp *dev_opp; struct opp *temp_opp; @@ -226,10 +226,10 @@ int opp_get_opp_count(struct device *dev) return count; } -EXPORT_SYMBOL_GPL(opp_get_opp_count); +EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count); /** - * opp_find_freq_exact() - search for an exact frequency + * dev_pm_opp_find_freq_exact() - search for an exact frequency * @dev: device for which we do this operation * @freq: frequency to search for * @available: true/false - match for available opp @@ -254,7 +254,7 @@ EXPORT_SYMBOL_GPL(opp_get_opp_count); * under the locked area. The pointer returned must be used prior to unlocking * with rcu_read_unlock() to maintain the integrity of the pointer. */ -struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq, +struct opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, bool available) { struct device_opp *dev_opp; @@ -277,10 +277,10 @@ struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq, return opp; } -EXPORT_SYMBOL_GPL(opp_find_freq_exact); +EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); /** - * opp_find_freq_ceil() - Search for an rounded ceil freq + * dev_pm_opp_find_freq_ceil() - Search for an rounded ceil freq * @dev: device for which we do this operation * @freq: Start frequency * @@ -300,7 +300,7 @@ EXPORT_SYMBOL_GPL(opp_find_freq_exact); * under the locked area. The pointer returned must be used prior to unlocking * with rcu_read_unlock() to maintain the integrity of the pointer. */ -struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq) +struct opp *dev_pm_opp_find_freq_ceil(struct device *dev, unsigned long *freq) { struct device_opp *dev_opp; struct opp *temp_opp, *opp = ERR_PTR(-ERANGE); @@ -324,10 +324,10 @@ struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq) return opp; } -EXPORT_SYMBOL_GPL(opp_find_freq_ceil); +EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil); /** - * opp_find_freq_floor() - Search for a rounded floor freq + * dev_pm_opp_find_freq_floor() - Search for a rounded floor freq * @dev: device for which we do this operation * @freq: Start frequency * @@ -347,7 +347,7 @@ EXPORT_SYMBOL_GPL(opp_find_freq_ceil); * under the locked area. The pointer returned must be used prior to unlocking * with rcu_read_unlock() to maintain the integrity of the pointer. */ -struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq) +struct opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq) { struct device_opp *dev_opp; struct opp *temp_opp, *opp = ERR_PTR(-ERANGE); @@ -375,17 +375,17 @@ struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq) return opp; } -EXPORT_SYMBOL_GPL(opp_find_freq_floor); +EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); /** - * opp_add() - Add an OPP table from a table definitions + * dev_pm_opp_add() - Add an OPP table from a table definitions * @dev: device for which we do this operation * @freq: Frequency in Hz for this OPP * @u_volt: Voltage in uVolts for this OPP * * This function adds an opp definition to the opp list and returns status. * The opp is made available by default and it can be controlled using - * opp_enable/disable functions. + * dev_pm_opp_enable/disable functions. * * Locking: The internal device_opp and opp structures are RCU protected. * Hence this function internally uses RCU updater strategy with mutex locks @@ -393,7 +393,7 @@ EXPORT_SYMBOL_GPL(opp_find_freq_floor); * that this function is *NOT* called under RCU protection or in contexts where * mutex cannot be locked. */ -int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) +int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) { struct device_opp *dev_opp = NULL; struct opp *opp, *new_opp; @@ -460,6 +460,7 @@ int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) srcu_notifier_call_chain(&dev_opp->head, OPP_EVENT_ADD, new_opp); return 0; } +EXPORT_SYMBOL_GPL(dev_pm_opp_add); /** * opp_set_availability() - helper to set the availability of an opp @@ -551,13 +552,13 @@ unlock: } /** - * opp_enable() - Enable a specific OPP + * dev_pm_opp_enable() - Enable a specific OPP * @dev: device for which we do this operation * @freq: OPP frequency to enable * * Enables a provided opp. If the operation is valid, this returns 0, else the * corresponding error value. It is meant to be used for users an OPP available - * after being temporarily made unavailable with opp_disable. + * after being temporarily made unavailable with dev_pm_opp_disable. * * Locking: The internal device_opp and opp structures are RCU protected. * Hence this function indirectly uses RCU and mutex locks to keep the @@ -565,21 +566,21 @@ unlock: * this function is *NOT* called under RCU protection or in contexts where * mutex locking or synchronize_rcu() blocking calls cannot be used. */ -int opp_enable(struct device *dev, unsigned long freq) +int dev_pm_opp_enable(struct device *dev, unsigned long freq) { return opp_set_availability(dev, freq, true); } -EXPORT_SYMBOL_GPL(opp_enable); +EXPORT_SYMBOL_GPL(dev_pm_opp_enable); /** - * opp_disable() - Disable a specific OPP + * dev_pm_opp_disable() - Disable a specific OPP * @dev: device for which we do this operation * @freq: OPP frequency to disable * * Disables a provided opp. If the operation is valid, this returns * 0, else the corresponding error value. It is meant to be a temporary * control by users to make this OPP not available until the circumstances are - * right to make it available again (with a call to opp_enable). + * right to make it available again (with a call to dev_pm_opp_enable). * * Locking: The internal device_opp and opp structures are RCU protected. * Hence this function indirectly uses RCU and mutex locks to keep the @@ -587,15 +588,15 @@ EXPORT_SYMBOL_GPL(opp_enable); * this function is *NOT* called under RCU protection or in contexts where * mutex locking or synchronize_rcu() blocking calls cannot be used. */ -int opp_disable(struct device *dev, unsigned long freq) +int dev_pm_opp_disable(struct device *dev, unsigned long freq) { return opp_set_availability(dev, freq, false); } -EXPORT_SYMBOL_GPL(opp_disable); +EXPORT_SYMBOL_GPL(dev_pm_opp_disable); #ifdef CONFIG_CPU_FREQ /** - * opp_init_cpufreq_table() - create a cpufreq table for a device + * dev_pm_opp_init_cpufreq_table() - create a cpufreq table for a device * @dev: device for which we do this operation * @table: Cpufreq table returned back to caller * @@ -618,7 +619,7 @@ EXPORT_SYMBOL_GPL(opp_disable); * Callers should ensure that this function is *NOT* called under RCU protection * or in contexts where mutex locking cannot be used. */ -int opp_init_cpufreq_table(struct device *dev, +int dev_pm_opp_init_cpufreq_table(struct device *dev, struct cpufreq_frequency_table **table) { struct device_opp *dev_opp; @@ -638,7 +639,7 @@ int opp_init_cpufreq_table(struct device *dev, } freq_table = kzalloc(sizeof(struct cpufreq_frequency_table) * - (opp_get_opp_count(dev) + 1), GFP_KERNEL); + (dev_pm_opp_get_opp_count(dev) + 1), GFP_KERNEL); if (!freq_table) { mutex_unlock(&dev_opp_list_lock); dev_warn(dev, "%s: Unable to allocate frequency table\n", @@ -648,30 +649,30 @@ int opp_init_cpufreq_table(struct device *dev, list_for_each_entry(opp, &dev_opp->opp_list, node) { if (opp->available) { - freq_table[i].index = i; + freq_table[i].driver_data = i; freq_table[i].frequency = opp->rate / 1000; i++; } } mutex_unlock(&dev_opp_list_lock); - freq_table[i].index = i; + freq_table[i].driver_data = i; freq_table[i].frequency = CPUFREQ_TABLE_END; *table = &freq_table[0]; return 0; } -EXPORT_SYMBOL_GPL(opp_init_cpufreq_table); +EXPORT_SYMBOL_GPL(dev_pm_opp_init_cpufreq_table); /** - * opp_free_cpufreq_table() - free the cpufreq table + * dev_pm_opp_free_cpufreq_table() - free the cpufreq table * @dev: device for which we do this operation * @table: table to free * - * Free up the table allocated by opp_init_cpufreq_table + * Free up the table allocated by dev_pm_opp_init_cpufreq_table */ -void opp_free_cpufreq_table(struct device *dev, +void dev_pm_opp_free_cpufreq_table(struct device *dev, struct cpufreq_frequency_table **table) { if (!table) @@ -680,14 +681,14 @@ void opp_free_cpufreq_table(struct device *dev, kfree(*table); *table = NULL; } -EXPORT_SYMBOL_GPL(opp_free_cpufreq_table); +EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table); #endif /* CONFIG_CPU_FREQ */ /** - * opp_get_notifier() - find notifier_head of the device with opp + * dev_pm_opp_get_notifier() - find notifier_head of the device with opp * @dev: device pointer used to lookup device OPPs. */ -struct srcu_notifier_head *opp_get_notifier(struct device *dev) +struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev) { struct device_opp *dev_opp = find_device_opp(dev); @@ -731,7 +732,7 @@ int of_init_opp_table(struct device *dev) unsigned long freq = be32_to_cpup(val++) * 1000; unsigned long volt = be32_to_cpup(val++); - if (opp_add(dev, freq, volt)) { + if (dev_pm_opp_add(dev, freq, volt)) { dev_warn(dev, "%s: Failed to add OPP %ld\n", __func__, freq); continue; diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 2b870dfacb8a..5f165941f5f7 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -632,7 +632,7 @@ config MSM_ROTATOR_USE_IMEM config MSM_ADSPRPC tristate "Qualcomm ADSP RPC driver" - depends on MSM_AUDIO_QDSP6 || MSM_AUDIO_QDSP6V2 + depends on MSM_AUDIO_QDSP6 help Provides a communication mechanism that allows for clients to make remote method invocations across processor boundary to @@ -657,7 +657,7 @@ config CSDIO_DEVICE_ID config MSM_RDBG tristate "Qualcomm Remote debug driver" - depends on MSM_AUDIO_QDSP6 || MSM_AUDIO_QDSP6V2 + depends on MSM_AUDIO_QDSP6 help Implements a shared memory based transport mechanism that allows for a debugger running on a host PC to communicate with a remote diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c index 77c3d5d8bcf7..eb76ea843412 100644 --- a/drivers/char/diag/diag_dci.c +++ b/drivers/char/diag/diag_dci.c @@ -433,6 +433,34 @@ static inline int __diag_dci_query_event_mask(struct diag_dci_client_tbl *entry, return ((*event_mask_ptr & byte_mask) == byte_mask) ? 1 : 0; } +static int diag_dci_filter_commands(struct diag_pkt_header_t *header) +{ + if (!header) + return -ENOMEM; + + switch (header->cmd_code) { + case 0x7d: /* Msg Mask Configuration */ + case 0x73: /* Log Mask Configuration */ + case 0x81: /* Event Mask Configuration */ + case 0x82: /* Event Mask Change */ + case 0x60: /* Event Mask Toggle */ + return 1; + } + + if (header->cmd_code == 0x4b && header->subsys_id == 0x12) { + switch (header->subsys_cmd_code) { + case 0x60: /* Extended Event Mask Config */ + case 0x61: /* Extended Msg Mask Config */ + case 0x62: /* Extended Log Mask Config */ + case 0x20C: /* Set current Preset ID */ + case 0x20D: /* Get current Preset ID */ + return 1; + } + } + + return 0; +} + static struct dci_pkt_req_entry_t *diag_register_dci_transaction(int uid) { struct dci_pkt_req_entry_t *entry = NULL; @@ -986,8 +1014,8 @@ static int diag_send_dci_pkt(struct diag_master_table entry, int diag_process_dci_transaction(unsigned char *buf, int len) { unsigned char *temp = buf; - uint16_t subsys_cmd_code, log_code, item_num; - int subsys_id, cmd_code, ret = -1, found = 0; + uint16_t log_code, item_num; + int ret = -1, found = 0, req_uid; struct diag_master_table entry; int count, set_mask, num_codes, bit_index, event_id, offset = 0, i; unsigned int byte_index, read_len = 0; @@ -995,6 +1023,7 @@ int diag_process_dci_transaction(unsigned char *buf, int len) uint8_t *event_mask_ptr; struct diag_dci_client_tbl *dci_entry = NULL; struct dci_pkt_req_entry_t *req_entry = NULL; + struct diag_pkt_header_t *header = NULL; if (!temp) { pr_err("diag: Invalid buffer in %s\n", __func__); @@ -1008,59 +1037,55 @@ int diag_process_dci_transaction(unsigned char *buf, int len) __func__); return -EIO; } - /* enter this UID into kernel table and return index */ - req_entry = diag_register_dci_transaction(*(int *)temp); - if (!req_entry) { - pr_alert("diag: registering new DCI transaction failed\n"); - return DIAG_DCI_NO_REG; - } + req_uid = *(int *)temp; temp += sizeof(int); - /* - * Check for registered peripheral and fwd pkt to - * appropriate proc - */ - cmd_code = (int)(*(char *)temp); - temp++; - subsys_id = (int)(*(char *)temp); - temp++; - subsys_cmd_code = *(uint16_t *)temp; - temp += sizeof(uint16_t); - read_len += sizeof(int) + 2 + sizeof(uint16_t); + header = (struct diag_pkt_header_t *)temp; + temp += sizeof(struct diag_pkt_header_t); + read_len = sizeof(int) + sizeof(struct diag_pkt_header_t); if (read_len >= USER_SPACE_DATA) { pr_err("diag: dci: Invalid length in %s\n", __func__); return -EIO; } - pr_debug("diag: %d %d %d", cmd_code, subsys_id, - subsys_cmd_code); + /* check if the command is allowed on DCI */ + if (diag_dci_filter_commands(header)) { + pr_debug("diag: command not supported %d %d %d", + header->cmd_code, + header->subsys_id, + header->subsys_cmd_code); + return DIAG_DCI_SEND_DATA_FAIL; + } + /* enter this UID into kernel table */ + req_entry = diag_register_dci_transaction(req_uid); + if (!req_entry) { + pr_alert("diag: registering new DCI transaction failed\n"); + return DIAG_DCI_NO_REG; + } for (i = 0; i < diag_max_reg; i++) { entry = driver->table[i]; - if (entry.process_id != NO_PROCESS) { - if (entry.cmd_code == cmd_code && - entry.subsys_id == subsys_id && - entry.cmd_code_lo <= subsys_cmd_code && - entry.cmd_code_hi >= subsys_cmd_code) { + if (entry.process_id == NO_PROCESS) + continue; + if (entry.cmd_code == header->cmd_code && + entry.subsys_id == header->subsys_id && + entry.cmd_code_lo <= header->subsys_cmd_code && + entry.cmd_code_hi >= header->subsys_cmd_code) { + ret = diag_send_dci_pkt(entry, buf, len, + req_entry->tag); + } else if (entry.cmd_code == 255 && + header->cmd_code == 75) { + if (entry.subsys_id == header->subsys_id && + entry.cmd_code_lo <= + header->subsys_cmd_code && + entry.cmd_code_hi >= + header->subsys_cmd_code) { ret = diag_send_dci_pkt(entry, buf, len, req_entry->tag); - } else if (entry.cmd_code == 255 - && cmd_code == 75) { - if (entry.subsys_id == subsys_id && - entry.cmd_code_lo <= - subsys_cmd_code && - entry.cmd_code_hi >= - subsys_cmd_code) { - ret = diag_send_dci_pkt(entry, - buf, len, - req_entry->tag); - } - } else if (entry.cmd_code == 255 && - entry.subsys_id == 255) { - if (entry.cmd_code_lo <= cmd_code && - entry.cmd_code_hi >= - cmd_code) { - ret = diag_send_dci_pkt(entry, - buf, len, + } + } else if (entry.cmd_code == 255 && + entry.subsys_id == 255) { + if (entry.cmd_code_lo <= header->cmd_code && + entry.cmd_code_hi >= header->cmd_code) { + ret = diag_send_dci_pkt(entry, buf, len, req_entry->tag); - } } } } diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c index 37bf60ec2c7a..4bbe94837c1b 100644 --- a/drivers/char/diag/diag_debugfs.c +++ b/drivers/char/diag/diag_debugfs.c @@ -183,7 +183,7 @@ static ssize_t diag_dbgfs_read_status(struct file *file, char __user *ubuf, driver->real_time_mode); #ifdef CONFIG_DIAG_OVER_USB - ret += scnprintf(buf+ret, DEBUG_BUF_SIZE-ret, + ret += scnprintf(buf+ret, DEBUG_BUF_SIZE, "usb_connected: %d\n", driver->usb_connected); #endif @@ -197,8 +197,7 @@ static ssize_t diag_dbgfs_read_dcistats(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) { char *buf = NULL; - unsigned int bytes_remaining, bytes_written = 0; - unsigned int bytes_in_buf = 0, i = 0; + int bytes_remaining, bytes_written = 0, bytes_in_buf = 0, i = 0; struct diag_dci_data_info *temp_data = dci_data_smd; int buf_size = (DEBUG_BUF_SIZE < count) ? DEBUG_BUF_SIZE : count; @@ -336,7 +335,7 @@ static ssize_t diag_dbgfs_read_workpending(struct file *file, diag_notify_update_smd_work))); #ifdef CONFIG_DIAG_OVER_USB - ret += scnprintf(buf+ret, DEBUG_BUF_SIZE-ret, + ret += scnprintf(buf+ret, DEBUG_BUF_SIZE, "diag_proc_hdlc_work: %d\n" "diag_read_work: %d\n", work_pending(&(driver->diag_proc_hdlc_work)), @@ -354,9 +353,9 @@ static ssize_t diag_dbgfs_read_table(struct file *file, char __user *ubuf, char *buf; int ret = 0; int i; - unsigned int bytes_remaining; - unsigned int bytes_in_buffer = 0; - unsigned int bytes_written; + int bytes_remaining; + int bytes_in_buffer = 0; + int bytes_written; int buf_size = (DEBUG_BUF_SIZE < count) ? DEBUG_BUF_SIZE : count; if (diag_dbgfs_table_index >= diag_max_reg) { @@ -527,9 +526,9 @@ static ssize_t diag_dbgfs_read_bridge(struct file *file, char __user *ubuf, char *buf; int ret; int i; - unsigned int bytes_remaining; - unsigned int bytes_in_buffer = 0; - unsigned int bytes_written; + int bytes_remaining; + int bytes_in_buffer = 0; + int bytes_written; int buf_size = (DEBUG_BUF_SIZE < count) ? DEBUG_BUF_SIZE : count; int bytes_hsic_inited = 45; int bytes_hsic_not_inited = 410; diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h index 502173e3bf5d..2a4221d4d366 100644 --- a/drivers/char/diag/diagchar.h +++ b/drivers/char/diag/diagchar.h @@ -172,6 +172,12 @@ enum remote_procs { QSC = 5, }; +struct diag_pkt_header_t { + uint8_t cmd_code; + uint8_t subsys_id; + uint16_t subsys_cmd_code; +} __packed; + struct diag_master_table { uint16_t cmd_code; uint16_t subsys_id; diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index 485af7a67a98..2bbfb7a4fc4b 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -51,7 +51,6 @@ MODULE_DESCRIPTION("Diag Char Driver"); MODULE_LICENSE("GPL v2"); MODULE_VERSION("1.0"); -#define MIN_SIZ_ALLOW 4 #define INIT 1 #define EXIT -1 struct diagchar_dev *driver; @@ -1451,10 +1450,6 @@ static ssize_t diagchar_write(struct file *file, const char __user *buf, index = 0; /* Get the packet type F3/log/event/Pkt response */ err = copy_from_user((&pkt_type), buf, 4); - if (err) { - pr_alert("diag: copy failed for pkt_type\n"); - return -EAGAIN; - } /* First 4 bytes indicate the type of payload - ignore these */ if (count < 4) { pr_err("diag: Client sending short data\n"); @@ -1498,8 +1493,8 @@ static ssize_t diagchar_write(struct file *file, const char __user *buf, return err; } if (pkt_type == CALLBACK_DATA_TYPE) { - if (payload_size > itemsize || payload_size <= MIN_SIZ_ALLOW) { - pr_err("diag: Dropping packet, invalid packet size. Current payload size %d\n", + if (payload_size > driver->itemsize) { + pr_err("diag: Dropping packet, packet payload size crosses 4KB limit. Current payload size %d\n", payload_size); driver->dropped_count++; return -EBADMSG; @@ -1629,16 +1624,10 @@ static ssize_t diagchar_write(struct file *file, const char __user *buf, return -EIO; } /* Check for proc_type */ - remote_proc = diag_get_remote(*(int *)user_space_data); + remote_proc = + diag_get_remote(*(int *)driver->user_space_data_buf); + if (remote_proc) { - if (payload_size <= MIN_SIZ_ALLOW) { - pr_err("diag: Integer underflow in %s, payload size: %d", - __func__, payload_size); - diagmem_free(driver, user_space_data, - POOL_TYPE_USER); - user_space_data = NULL; - return -EBADMSG; - } token_offset = 4; payload_size -= 4; buf += 4; diff --git a/drivers/char/diag/diagchar_hdlc.c b/drivers/char/diag/diagchar_hdlc.c index 39f1f4487467..d5ba452877d3 100644 --- a/drivers/char/diag/diagchar_hdlc.c +++ b/drivers/char/diag/diagchar_hdlc.c @@ -177,8 +177,8 @@ int diag_hdlc_decode(struct diag_hdlc_decode_type *hdlc) int msg_start; if (hdlc && hdlc->src_ptr && hdlc->dest_ptr && - (hdlc->src_size > hdlc->src_idx) && - (hdlc->dest_size > hdlc->dest_idx)) { + (hdlc->src_size - hdlc->src_idx > 0) && + (hdlc->dest_size - hdlc->dest_idx > 0)) { msg_start = (hdlc->src_idx == 0) ? 1 : 0; diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c index 424d74d6ae3c..aac2281a2073 100644 --- a/drivers/char/diag/diagfwd.c +++ b/drivers/char/diag/diagfwd.c @@ -1942,7 +1942,6 @@ int diagfwd_disconnect(void) printk(KERN_DEBUG "diag: USB disconnected\n"); driver->usb_connected = 0; driver->debug_flag = 1; - usb_diag_free_req(driver->legacy_ch); if (driver->logging_mode == USB_MODE) { for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) { smd_info = &driver->smd_data[i]; @@ -2197,12 +2196,10 @@ static int diag_smd_probe(struct platform_device *pdev) index = MODEM_DATA; channel_name = "DIAG"; } -#if defined(CONFIG_MSM_N_WAY_SMD) else if (pdev->id == SMD_APPS_QDSP) { index = LPASS_DATA; channel_name = "DIAG"; } -#endif else if (pdev->id == SMD_APPS_WCNSS) { index = WCNSS_DATA; channel_name = "APPS_RIVA_DATA"; @@ -2711,8 +2708,6 @@ void diagfwd_exit(void) diag_smd_destructor(&driver->smd_data[i]); #ifdef CONFIG_DIAG_OVER_USB - if (driver->usb_connected) - usb_diag_free_req(driver->legacy_ch); usb_diag_close(driver->legacy_ch); #endif platform_driver_unregister(&msm_smd_ch1_driver); diff --git a/drivers/char/diag/diagfwd_bridge.c b/drivers/char/diag/diagfwd_bridge.c index 143959b39a4b..142d15a6f217 100644 --- a/drivers/char/diag/diagfwd_bridge.c +++ b/drivers/char/diag/diagfwd_bridge.c @@ -133,7 +133,6 @@ int diagfwd_disconnect_bridge(int process_cable) /* If the usb cable is being disconnected */ if (process_cable) { diag_bridge[i].usb_connected = 0; - usb_diag_free_req(diag_bridge[i].ch); } if (i == SMUX) { @@ -383,8 +382,6 @@ void diagfwd_bridge_exit(void) for (i = 0; i < MAX_BRIDGES; i++) { if (diag_bridge[i].enabled) { #ifdef CONFIG_DIAG_OVER_USB - if (diag_bridge[i].usb_connected) - usb_diag_free_req(diag_bridge[i].ch); usb_diag_close(diag_bridge[i].ch); #endif kfree(diag_bridge[i].usb_buf_out); diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c index e383dafcc57d..64399318b834 100644 --- a/drivers/char/diag/diagfwd_cntl.c +++ b/drivers/char/diag/diagfwd_cntl.c @@ -349,7 +349,7 @@ void diag_send_diag_mode_update_by_smd(struct diag_smd_info *smd_info, int wr_size = -ENOMEM, retry_count = 0, timer; struct diag_smd_info *data = NULL; - if (!smd_info && smd_info->type != SMD_CNTL_TYPE) { + if (!smd_info || smd_info->type != SMD_CNTL_TYPE) { pr_err("diag: In %s, invalid channel info, smd_info: %p type: %d\n", __func__, smd_info, ((smd_info) ? smd_info->type : -1)); @@ -483,12 +483,10 @@ static int diag_smd_cntl_probe(struct platform_device *pdev) index = MODEM_DATA; channel_name = "DIAG_CNTL"; } -#if defined(CONFIG_MSM_N_WAY_SMD) else if (pdev->id == SMD_APPS_QDSP) { index = LPASS_DATA; channel_name = "DIAG_CNTL"; } -#endif else if (pdev->id == SMD_APPS_WCNSS) { index = WCNSS_DATA; channel_name = "APPS_RIVA_CTRL"; diff --git a/drivers/char/diag/diagfwd_sdio.c b/drivers/char/diag/diagfwd_sdio.c index 7d4e0d54f8a0..ef56a1b22b7f 100644 --- a/drivers/char/diag/diagfwd_sdio.c +++ b/drivers/char/diag/diagfwd_sdio.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011, 2013, The Linux Foundation. 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 and @@ -131,7 +131,6 @@ int diagfwd_connect_sdio(void) int diagfwd_disconnect_sdio(void) { - usb_diag_free_req(driver->mdm_ch); if (driver->sdio_ch && (driver->logging_mode == USB_MODE)) { driver->in_busy_sdio = 1; diag_sdio_close(); @@ -280,10 +279,6 @@ err: void diagfwd_sdio_exit(void) { -#ifdef CONFIG_DIAG_OVER_USB - if (driver->usb_connected) - usb_diag_free_req(driver->mdm_ch); -#endif platform_driver_unregister(&msm_sdio_ch_driver); #ifdef CONFIG_DIAG_OVER_USB usb_diag_close(driver->mdm_ch); diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index f79ea4d0b30c..07f8776ed507 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o +obj-$(CONFIG_ARCH_MSM) += qcom/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_PLAT_SAMSUNG) += samsung/ diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index 9b570816ca4d..757c2ccb5acd 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -143,7 +143,7 @@ struct clk *clk_get_sys(const char *dev_id, const char *con_id) cl = NULL; mutex_unlock(&clocks_mutex); - return cl ? cl->clk : ERR_PTR(-ENOENT); + return cl ? cl->clk : ERR_PTR(-EPROBE_DEFER); } EXPORT_SYMBOL(clk_get_sys); diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile new file mode 100644 index 000000000000..88de4708ace1 --- /dev/null +++ b/drivers/clk/qcom/Makefile @@ -0,0 +1,5 @@ +obj-y += clock.o +obj-y += clock-dummy.o +obj-y += clock-generic.o + +obj-$(CONFIG_DEBUG_FS) += clock-debug.o diff --git a/drivers/clk/qcom/clock-debug.c b/drivers/clk/qcom/clock-debug.c new file mode 100644 index 000000000000..9cdd2f0c7b2f --- /dev/null +++ b/drivers/clk/qcom/clock-debug.c @@ -0,0 +1,643 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2013, The Linux Foundation. All rights reserved. + * + * 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/ctype.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/clk.h> +#include <linux/list.h> +#include <linux/clkdev.h> +#include <linux/uaccess.h> +#include <linux/mutex.h> +#include <linux/io.h> +#include <linux/clk/msm-clk-provider.h> + + +#include "clock.h" + +static LIST_HEAD(clk_list); +static DEFINE_MUTEX(clk_list_lock); + +static struct dentry *debugfs_base; +static u32 debug_suspend; + +struct clk_table { + struct list_head node; + struct clk_lookup *clocks; + size_t num_clocks; +}; + +static int clock_debug_rate_set(void *data, u64 val) +{ + struct clk *clock = data; + int ret; + + /* Only increases to max rate will succeed, but that's actually good + * for debugging purposes so we don't check for error. */ + if (clock->flags & CLKFLAG_MAX) + clk_set_max_rate(clock, val); + ret = clk_set_rate(clock, val); + if (ret) + pr_err("clk_set_rate(%s, %lu) failed (%d)\n", clock->dbg_name, + (unsigned long)val, ret); + + return ret; +} + +static int clock_debug_rate_get(void *data, u64 *val) +{ + struct clk *clock = data; + *val = clk_get_rate(clock); + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(clock_rate_fops, clock_debug_rate_get, + clock_debug_rate_set, "%llu\n"); + +static struct clk *measure; + +static int clock_debug_measure_get(void *data, u64 *val) +{ + struct clk *clock = data, *par; + int ret, is_hw_gated; + unsigned long meas_rate, sw_rate; + + /* Check to see if the clock is in hardware gating mode */ + if (clock->ops->in_hwcg_mode) + is_hw_gated = clock->ops->in_hwcg_mode(clock); + else + is_hw_gated = 0; + + ret = clk_set_parent(measure, clock); + if (!ret) { + /* + * Disable hw gating to get accurate rate measurements. Only do + * this if the clock is explictly enabled by software. This + * allows us to detect errors where clocks are on even though + * software is not requesting them to be on due to broken + * hardware gating signals. + */ + if (is_hw_gated && clock->count) + clock->ops->disable_hwcg(clock); + par = measure; + while (par && par != clock) { + if (par->ops->enable) + par->ops->enable(par); + par = par->parent; + } + *val = clk_get_rate(measure); + /* Reenable hwgating if it was disabled */ + if (is_hw_gated && clock->count) + clock->ops->enable_hwcg(clock); + } + + /* + * If there's a divider on the path from the clock output to the + * measurement circuitry, account for it by dividing the original clock + * rate with the rate set on the parent of the measure clock. + */ + meas_rate = clk_get_rate(clock); + sw_rate = clk_get_rate(measure->parent); + if (sw_rate && meas_rate >= (sw_rate * 2)) + *val *= DIV_ROUND_CLOSEST(meas_rate, sw_rate); + + return ret; +} + +DEFINE_SIMPLE_ATTRIBUTE(clock_measure_fops, clock_debug_measure_get, + NULL, "%lld\n"); + +static int clock_debug_enable_set(void *data, u64 val) +{ + struct clk *clock = data; + int rc = 0; + + if (val) + rc = clk_prepare_enable(clock); + else + clk_disable_unprepare(clock); + + return rc; +} + +static int clock_debug_enable_get(void *data, u64 *val) +{ + struct clk *clock = data; + int enabled; + + if (clock->ops->is_enabled) + enabled = clock->ops->is_enabled(clock); + else + enabled = !!(clock->count); + + *val = enabled; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(clock_enable_fops, clock_debug_enable_get, + clock_debug_enable_set, "%lld\n"); + +static int clock_debug_local_get(void *data, u64 *val) +{ + struct clk *clock = data; + + if (!clock->ops->is_local) + *val = true; + else + *val = clock->ops->is_local(clock); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(clock_local_fops, clock_debug_local_get, + NULL, "%llu\n"); + +static int clock_debug_hwcg_get(void *data, u64 *val) +{ + struct clk *clock = data; + if (clock->ops->in_hwcg_mode) + *val = !!clock->ops->in_hwcg_mode(clock); + else + *val = 0; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(clock_hwcg_fops, clock_debug_hwcg_get, + NULL, "%llu\n"); + +static void clock_print_fmax_by_level(struct seq_file *m, int level) +{ + struct clk *clock = m->private; + struct clk_vdd_class *vdd_class = clock->vdd_class; + int off, i, vdd_level, nregs = vdd_class->num_regulators; + + vdd_level = find_vdd_level(clock, clock->rate); + + seq_printf(m, "%2s%10lu", vdd_level == level ? "[" : "", + clock->fmax[level]); + for (i = 0; i < nregs; i++) { + off = nregs*level + i; + if (vdd_class->vdd_uv) + seq_printf(m, "%10u", vdd_class->vdd_uv[off]); + if (vdd_class->vdd_ua) + seq_printf(m, "%10u", vdd_class->vdd_ua[off]); + } + + if (vdd_level == level) + seq_puts(m, "]"); + seq_puts(m, "\n"); +} + +static int fmax_rates_show(struct seq_file *m, void *unused) +{ + struct clk *clock = m->private; + struct clk_vdd_class *vdd_class = clock->vdd_class; + int level = 0, i, nregs = vdd_class->num_regulators; + char reg_name[10]; + + int vdd_level = find_vdd_level(clock, clock->rate); + if (vdd_level < 0) { + seq_printf(m, "could not find_vdd_level for %s, %ld\n", + clock->dbg_name, clock->rate); + return 0; + } + + seq_printf(m, "%12s", ""); + for (i = 0; i < nregs; i++) { + snprintf(reg_name, ARRAY_SIZE(reg_name), "reg %d", i); + seq_printf(m, "%10s", reg_name); + if (vdd_class->vdd_ua) + seq_printf(m, "%10s", ""); + } + + seq_printf(m, "\n%12s", "freq"); + for (i = 0; i < nregs; i++) { + seq_printf(m, "%10s", "uV"); + if (vdd_class->vdd_ua) + seq_printf(m, "%10s", "uA"); + } + seq_printf(m, "\n"); + + for (level = 0; level < clock->num_fmax; level++) + clock_print_fmax_by_level(m, level); + + return 0; +} + +static int fmax_rates_open(struct inode *inode, struct file *file) +{ + return single_open(file, fmax_rates_show, inode->i_private); +} + +static const struct file_operations fmax_rates_fops = { + .open = fmax_rates_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +#define clock_debug_output(m, c, fmt, ...) \ +do { \ + if (m) \ + seq_printf(m, fmt, ##__VA_ARGS__); \ + else if (c) \ + pr_cont(fmt, ##__VA_ARGS__); \ + else \ + pr_info(fmt, ##__VA_ARGS__); \ +} while (0) + +static int clock_debug_print_clock(struct clk *c, struct seq_file *m) +{ + char *start = ""; + + if (!c || !c->prepare_count) + return 0; + + clock_debug_output(m, 0, "\t"); + do { + if (c->vdd_class) + clock_debug_output(m, 1, "%s%s:%u:%u [%ld, %lu]", start, + c->dbg_name, c->prepare_count, c->count, + c->rate, c->vdd_class->cur_level); + else + clock_debug_output(m, 1, "%s%s:%u:%u [%ld]", start, + c->dbg_name, c->prepare_count, c->count, + c->rate); + start = " -> "; + } while ((c = clk_get_parent(c))); + + clock_debug_output(m, 1, "\n"); + + return 1; +} + +/** + * clock_debug_print_enabled_clocks() - Print names of enabled clocks + * + */ +static void clock_debug_print_enabled_clocks(struct seq_file *m) +{ + struct clk_table *table; + int i, cnt = 0; + + if (!mutex_trylock(&clk_list_lock)) { + pr_err("clock-debug: Clocks are being registered. Cannot print clock state now.\n"); + return; + } + clock_debug_output(m, 0, "Enabled clocks:\n"); + list_for_each_entry(table, &clk_list, node) { + for (i = 0; i < table->num_clocks; i++) + cnt += clock_debug_print_clock(table->clocks[i].clk, m); + } + mutex_unlock(&clk_list_lock); + + if (cnt) + clock_debug_output(m, 0, "Enabled clock count: %d\n", cnt); + else + clock_debug_output(m, 0, "No clocks enabled.\n"); +} + +static int enabled_clocks_show(struct seq_file *m, void *unused) +{ + clock_debug_print_enabled_clocks(m); + return 0; +} + +static int enabled_clocks_open(struct inode *inode, struct file *file) +{ + return single_open(file, enabled_clocks_show, inode->i_private); +} + +static const struct file_operations enabled_clocks_fops = { + .open = enabled_clocks_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int list_rates_show(struct seq_file *m, void *unused) +{ + struct clk *clock = m->private; + int level, i = 0; + unsigned long rate, fmax = 0; + + /* Find max frequency supported within voltage constraints. */ + if (!clock->vdd_class) { + fmax = ULONG_MAX; + } else { + for (level = 0; level < clock->num_fmax; level++) + if (clock->fmax[level]) + fmax = clock->fmax[level]; + } + + /* + * List supported frequencies <= fmax. Higher frequencies may appear in + * the frequency table, but are not valid and should not be listed. + */ + while (!IS_ERR_VALUE(rate = clock->ops->list_rate(clock, i++))) { + if (rate <= fmax) + seq_printf(m, "%lu\n", rate); + } + + return 0; +} + +static int list_rates_open(struct inode *inode, struct file *file) +{ + return single_open(file, list_rates_show, inode->i_private); +} + +static const struct file_operations list_rates_fops = { + .open = list_rates_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static ssize_t clock_parent_read(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct clk *clock = filp->private_data; + struct clk *p = clock->parent; + char name[256] = {0}; + + snprintf(name, sizeof(name), "%s\n", p ? p->dbg_name : "None\n"); + + return simple_read_from_buffer(ubuf, cnt, ppos, name, strlen(name)); +} + + +static ssize_t clock_parent_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + struct clk *clock = filp->private_data; + char buf[256]; + char *cmp; + struct clk_table *table; + int i, ret; + struct clk *parent = NULL; + + cnt = min(cnt, sizeof(buf) - 1); + if (copy_from_user(&buf, ubuf, cnt)) + return -EFAULT; + buf[cnt] = '\0'; + cmp = strstrip(buf); + + mutex_lock(&clk_list_lock); + list_for_each_entry(table, &clk_list, node) { + for (i = 0; i < table->num_clocks; i++) + if (!strcmp(cmp, table->clocks[i].clk->dbg_name)) { + parent = table->clocks[i].clk; + break; + } + if (parent) + break; + } + + if (!parent) { + ret = -EINVAL; + goto err; + } + + mutex_unlock(&clk_list_lock); + ret = clk_set_parent(clock, table->clocks[i].clk); + if (ret) + return ret; + + return cnt; +err: + mutex_unlock(&clk_list_lock); + return ret; +} + + +static const struct file_operations clock_parent_fops = { + .open = simple_open, + .read = clock_parent_read, + .write = clock_parent_write, +}; + +void clk_debug_print_hw(struct clk *clk, struct seq_file *f) +{ + void __iomem *base; + struct clk_register_data *regs; + u32 i, j, size; + + if (IS_ERR_OR_NULL(clk)) + return; + + clk_debug_print_hw(clk->parent, f); + + clock_debug_output(f, false, "%s\n", clk->dbg_name); + + if (!clk->ops->list_registers) + return; + + j = 0; + base = clk->ops->list_registers(clk, j, ®s, &size); + while (!IS_ERR(base)) { + for (i = 0; i < size; i++) { + u32 val = readl_relaxed(base + regs[i].offset); + clock_debug_output(f, false, "%20s: 0x%.8x\n", + regs[i].name, val); + } + j++; + base = clk->ops->list_registers(clk, j, ®s, &size); + } +} + +static int print_hw_show(struct seq_file *m, void *unused) +{ + struct clk *c = m->private; + clk_debug_print_hw(c, m); + + return 0; +} + +static int print_hw_open(struct inode *inode, struct file *file) +{ + return single_open(file, print_hw_show, inode->i_private); +} + +static const struct file_operations clock_print_hw_fops = { + .open = print_hw_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + + +static void clock_measure_add(struct clk *clock) +{ + if (IS_ERR_OR_NULL(measure)) + return; + + if (clk_set_parent(measure, clock)) + return; + + debugfs_create_file("measure", S_IRUGO, clock->clk_dir, clock, + &clock_measure_fops); +} + +static int clock_debug_add(struct clk *clock) +{ + char temp[50], *ptr; + struct dentry *clk_dir; + + if (!debugfs_base) + return -ENOMEM; + + strlcpy(temp, clock->dbg_name, ARRAY_SIZE(temp)); + for (ptr = temp; *ptr; ptr++) + *ptr = tolower(*ptr); + + clk_dir = debugfs_create_dir(temp, debugfs_base); + if (!clk_dir) + return -ENOMEM; + + clock->clk_dir = clk_dir; + + if (!debugfs_create_file("rate", S_IRUGO | S_IWUSR, clk_dir, + clock, &clock_rate_fops)) + goto error; + + if (!debugfs_create_file("enable", S_IRUGO | S_IWUSR, clk_dir, + clock, &clock_enable_fops)) + goto error; + + if (!debugfs_create_file("is_local", S_IRUGO, clk_dir, clock, + &clock_local_fops)) + goto error; + + if (!debugfs_create_file("has_hw_gating", S_IRUGO, clk_dir, clock, + &clock_hwcg_fops)) + goto error; + + if (clock->ops->list_rate) + if (!debugfs_create_file("list_rates", + S_IRUGO, clk_dir, clock, &list_rates_fops)) + goto error; + + if (clock->vdd_class && !debugfs_create_file("fmax_rates", + S_IRUGO, clk_dir, clock, &fmax_rates_fops)) + goto error; + + if (!debugfs_create_file("parent", S_IRUGO, clk_dir, clock, + &clock_parent_fops)) + goto error; + + if (!debugfs_create_file("print", S_IRUGO, clk_dir, clock, + &clock_print_hw_fops)) + goto error; + + clock_measure_add(clock); + + return 0; +error: + debugfs_remove_recursive(clk_dir); + return -ENOMEM; +} +static DEFINE_MUTEX(clk_debug_lock); +static int clk_debug_init_once; + +/** + * clock_debug_init() - Initialize clock debugfs + * Lock clk_debug_lock before invoking this function. + */ +static int clock_debug_init(void) +{ + if (clk_debug_init_once) + return 0; + + clk_debug_init_once = 1; + + debugfs_base = debugfs_create_dir("clk", NULL); + if (!debugfs_base) + return -ENOMEM; + + if (!debugfs_create_u32("debug_suspend", S_IRUGO | S_IWUSR, + debugfs_base, &debug_suspend)) { + debugfs_remove_recursive(debugfs_base); + return -ENOMEM; + } + + if (!debugfs_create_file("enabled_clocks", S_IRUGO, debugfs_base, NULL, + &enabled_clocks_fops)) + return -ENOMEM; + + return 0; +} + +/** + * clock_debug_register() - Add additional clocks to clock debugfs hierarchy + * @table: Table of clocks to create debugfs nodes for + * @size: Size of @table + * + */ +int clock_debug_register(struct clk_lookup *table, size_t size) +{ + struct clk_table *clk_table, *clk_table_tmp; + int i, ret; + + mutex_lock(&clk_debug_lock); + + ret = clock_debug_init(); + if (ret) + goto out; + + clk_table = kmalloc(sizeof(*clk_table), GFP_KERNEL); + if (!clk_table) { + ret = -ENOMEM; + goto out; + } + + clk_table->clocks = table; + clk_table->num_clocks = size; + + if (IS_ERR_OR_NULL(measure)) { + measure = clk_get_sys("debug", "measure"); + if (!IS_ERR(measure)) { + mutex_lock(&clk_list_lock); + list_for_each_entry(clk_table_tmp, &clk_list, node) { + for (i = 0; i < clk_table_tmp->num_clocks; i++) + clock_measure_add(clk_table_tmp->clocks[i].clk); + } + mutex_unlock(&clk_list_lock); + } + } + + mutex_lock(&clk_list_lock); + list_add_tail(&clk_table->node, &clk_list); + mutex_unlock(&clk_list_lock); + + for (i = 0; i < size; i++) + clock_debug_add(table[i].clk); +out: + mutex_unlock(&clk_debug_lock); + return ret; +} + +/* + * Print the names of enabled clocks and their parents if debug_suspend is set + */ +void clock_debug_print_enabled(void) +{ + if (likely(!debug_suspend)) + return; + + clock_debug_print_enabled_clocks(NULL); +} diff --git a/drivers/clk/qcom/clock-dummy.c b/drivers/clk/qcom/clock-dummy.c new file mode 100644 index 000000000000..e86aaae2f0de --- /dev/null +++ b/drivers/clk/qcom/clock-dummy.c @@ -0,0 +1,100 @@ +/* Copyright (c) 2011,2013, The Linux Foundation. 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 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. + */ + +#include <linux/clk/msm-clk-provider.h> +#include <linux/platform_device.h> +#include <linux/of.h> + +static int dummy_clk_reset(struct clk *clk, enum clk_reset_action action) +{ + return 0; +} + +static int dummy_clk_set_rate(struct clk *clk, unsigned long rate) +{ + clk->rate = rate; + return 0; +} + +static int dummy_clk_set_max_rate(struct clk *clk, unsigned long rate) +{ + return 0; +} + +static int dummy_clk_set_flags(struct clk *clk, unsigned flags) +{ + return 0; +} + +static unsigned long dummy_clk_get_rate(struct clk *clk) +{ + return clk->rate; +} + +static long dummy_clk_round_rate(struct clk *clk, unsigned long rate) +{ + return rate; +} + +struct clk_ops clk_ops_dummy = { + .reset = dummy_clk_reset, + .set_rate = dummy_clk_set_rate, + .set_max_rate = dummy_clk_set_max_rate, + .set_flags = dummy_clk_set_flags, + .get_rate = dummy_clk_get_rate, + .round_rate = dummy_clk_round_rate, +}; + +struct clk dummy_clk = { + .dbg_name = "dummy_clk", + .ops = &clk_ops_dummy, + CLK_INIT(dummy_clk), +}; + +static struct clk *of_dummy_get(struct of_phandle_args *clkspec, + void *data) +{ + return &dummy_clk; +} + +static struct of_device_id msm_clock_dummy_match_table[] = { + { .compatible = "qcom,dummycc" }, + {} +}; + +static int msm_clock_dummy_probe(struct platform_device *pdev) +{ + int ret; + + ret = of_clk_add_provider(pdev->dev.of_node, of_dummy_get, NULL); + if (ret) + return -ENOMEM; + + dev_info(&pdev->dev, "Registered DUMMY provider.\n"); + return ret; +} + +static struct platform_driver msm_clock_dummy_driver = { + .probe = msm_clock_dummy_probe, + .driver = { + .name = "clock-dummy", + .of_match_table = msm_clock_dummy_match_table, + .owner = THIS_MODULE, + }, +}; + +int __init msm_dummy_clk_init(void) +{ + return platform_driver_register(&msm_clock_dummy_driver); +} +arch_initcall(msm_dummy_clk_init); + diff --git a/drivers/clk/qcom/clock-generic.c b/drivers/clk/qcom/clock-generic.c new file mode 100644 index 000000000000..9efc9f8c1de6 --- /dev/null +++ b/drivers/clk/qcom/clock-generic.c @@ -0,0 +1,729 @@ +/* + * Copyright (c) 2013, The Linux Foundation. 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 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. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/clk/msm-clk-provider.h> +#include <linux/clk/msm-clock-generic.h> + +/* ==================== Mux clock ==================== */ + +int parent_to_src_sel(struct clk_src *parents, int num_parents, struct clk *p) +{ + int i; + + for (i = 0; i < num_parents; i++) { + if (parents[i].src == p) + return parents[i].sel; + } + + return -EINVAL; +} + +static int mux_set_parent(struct clk *c, struct clk *p) +{ + struct mux_clk *mux = to_mux_clk(c); + int sel = parent_to_src_sel(mux->parents, mux->num_parents, p); + struct clk *old_parent; + int rc = 0, i; + unsigned long flags; + + if (sel < 0 && mux->rec_set_par) { + for (i = 0; i < mux->num_parents; i++) { + rc = clk_set_parent(mux->parents[i].src, p); + if (!rc) { + sel = mux->parents[i].sel; + /* + * This is necessary to ensure prepare/enable + * counts get propagated correctly. + */ + p = mux->parents[i].src; + break; + } + } + } + + if (sel < 0) + return sel; + + rc = __clk_pre_reparent(c, p, &flags); + if (rc) + goto out; + + rc = mux->ops->set_mux_sel(mux, sel); + if (rc) + goto set_fail; + + old_parent = c->parent; + c->parent = p; + c->rate = clk_get_rate(p); + __clk_post_reparent(c, old_parent, &flags); + + return 0; + +set_fail: + __clk_post_reparent(c, p, &flags); +out: + return rc; +} + +static long mux_round_rate(struct clk *c, unsigned long rate) +{ + struct mux_clk *mux = to_mux_clk(c); + int i; + unsigned long prate, rrate = 0; + + for (i = 0; i < mux->num_parents; i++) { + prate = clk_round_rate(mux->parents[i].src, rate); + if (is_better_rate(rate, rrate, prate)) + rrate = prate; + } + if (!rrate) + return -EINVAL; + + return rrate; +} + +static int mux_set_rate(struct clk *c, unsigned long rate) +{ + struct mux_clk *mux = to_mux_clk(c); + struct clk *new_parent = NULL; + int rc = 0, i; + unsigned long new_par_curr_rate; + unsigned long flags; + + for (i = 0; i < mux->num_parents; i++) { + if (clk_round_rate(mux->parents[i].src, rate) == rate) { + new_parent = mux->parents[i].src; + break; + } + } + if (new_parent == NULL) + return -EINVAL; + + /* + * Switch to safe parent since the old and new parent might be the + * same and the parent might temporarily turn off while switching + * rates. + */ + if (mux->safe_sel >= 0) { + /* + * Some mux implementations might switch to/from a low power + * parent as part of their disable/enable ops. Grab the + * enable lock to avoid racing with these implementations. + */ + spin_lock_irqsave(&c->lock, flags); + rc = mux->ops->set_mux_sel(mux, mux->safe_sel); + spin_unlock_irqrestore(&c->lock, flags); + } + if (rc) + return rc; + + new_par_curr_rate = clk_get_rate(new_parent); + rc = clk_set_rate(new_parent, rate); + if (rc) + goto set_rate_fail; + + rc = mux_set_parent(c, new_parent); + if (rc) + goto set_par_fail; + + return 0; + +set_par_fail: + clk_set_rate(new_parent, new_par_curr_rate); +set_rate_fail: + WARN(mux->ops->set_mux_sel(mux, + parent_to_src_sel(mux->parents, mux->num_parents, c->parent)), + "Set rate failed for %s. Also in bad state!\n", c->dbg_name); + return rc; +} + +static int mux_enable(struct clk *c) +{ + struct mux_clk *mux = to_mux_clk(c); + if (mux->ops->enable) + return mux->ops->enable(mux); + return 0; +} + +static void mux_disable(struct clk *c) +{ + struct mux_clk *mux = to_mux_clk(c); + if (mux->ops->disable) + return mux->ops->disable(mux); +} + +static struct clk *mux_get_parent(struct clk *c) +{ + struct mux_clk *mux = to_mux_clk(c); + int sel = mux->ops->get_mux_sel(mux); + int i; + + for (i = 0; i < mux->num_parents; i++) { + if (mux->parents[i].sel == sel) + return mux->parents[i].src; + } + + /* Unfamiliar parent. */ + return NULL; +} + +static enum handoff mux_handoff(struct clk *c) +{ + struct mux_clk *mux = to_mux_clk(c); + + c->rate = clk_get_rate(c->parent); + mux->safe_sel = parent_to_src_sel(mux->parents, mux->num_parents, + mux->safe_parent); + + if (mux->en_mask && mux->ops && mux->ops->is_enabled) + return mux->ops->is_enabled(mux) + ? HANDOFF_ENABLED_CLK + : HANDOFF_DISABLED_CLK; + + /* + * If this function returns 'enabled' even when the clock downstream + * of this clock is disabled, then handoff code will unnecessarily + * enable the current parent of this clock. If this function always + * returns 'disabled' and a clock downstream is on, the clock handoff + * code will bump up the ref count for this clock and its current + * parent as necessary. So, clocks without an actual HW gate can + * always return disabled. + */ + return HANDOFF_DISABLED_CLK; +} + +struct clk_ops clk_ops_gen_mux = { + .enable = mux_enable, + .disable = mux_disable, + .set_parent = mux_set_parent, + .round_rate = mux_round_rate, + .set_rate = mux_set_rate, + .handoff = mux_handoff, + .get_parent = mux_get_parent, +}; + +/* ==================== Divider clock ==================== */ + +static long __div_round_rate(struct div_data *data, unsigned long rate, + struct clk *parent, unsigned int *best_div, unsigned long *best_prate) +{ + unsigned int div, min_div, max_div, _best_div = 1; + unsigned long prate, _best_prate = 0, rrate = 0; + + rate = max(rate, 1UL); + + min_div = max(data->min_div, 1U); + max_div = min(data->max_div, (unsigned int) (ULONG_MAX / rate)); + + for (div = min_div; div <= max_div; div++) { + prate = clk_round_rate(parent, rate * div); + if (IS_ERR_VALUE(prate)) + break; + + if (is_better_rate(rate, rrate, prate / div)) { + rrate = prate / div; + _best_div = div; + _best_prate = prate; + } + + /* + * Trying higher dividers is only going to ask the parent for + * a higher rate. If it can't even output a rate higher than + * the one we request for this divider, the parent is not + * going to be able to output an even higher rate required + * for a higher divider. So, stop trying higher dividers. + */ + if (prate / div < rate) + break; + + if (rrate <= rate + data->rate_margin) + break; + } + + if (!rrate) + return -EINVAL; + if (best_div) + *best_div = _best_div; + if (best_prate) + *best_prate = _best_prate; + + return rrate; +} + +static long div_round_rate(struct clk *c, unsigned long rate) +{ + struct div_clk *d = to_div_clk(c); + + return __div_round_rate(&d->data, rate, c->parent, NULL, NULL); +} + +static int div_set_rate(struct clk *c, unsigned long rate) +{ + struct div_clk *d = to_div_clk(c); + int div, rc = 0; + long rrate, old_prate, new_prate; + struct div_data *data = &d->data; + + rrate = __div_round_rate(data, rate, c->parent, &div, &new_prate); + if (rrate != rate) + return -EINVAL; + + /* + * For fixed divider clock we don't want to return an error if the + * requested rate matches the achievable rate. So, don't check for + * !d->ops and return an error. __div_round_rate() ensures div == + * d->div if !d->ops. + */ + if (div > data->div) + rc = d->ops->set_div(d, div); + if (rc) + return rc; + + old_prate = clk_get_rate(c->parent); + rc = clk_set_rate(c->parent, new_prate); + if (rc) + goto set_rate_fail; + + if (div < data->div) + rc = d->ops->set_div(d, div); + if (rc) + goto div_dec_fail; + + data->div = div; + + return 0; + +div_dec_fail: + WARN(clk_set_rate(c->parent, old_prate), + "Set rate failed for %s. Also in bad state!\n", c->dbg_name); +set_rate_fail: + if (div > data->div) + WARN(d->ops->set_div(d, data->div), + "Set rate failed for %s. Also in bad state!\n", + c->dbg_name); + return rc; +} + +static int div_enable(struct clk *c) +{ + struct div_clk *d = to_div_clk(c); + if (d->ops && d->ops->enable) + return d->ops->enable(d); + return 0; +} + +static void div_disable(struct clk *c) +{ + struct div_clk *d = to_div_clk(c); + if (d->ops && d->ops->disable) + return d->ops->disable(d); +} + +static enum handoff div_handoff(struct clk *c) +{ + struct div_clk *d = to_div_clk(c); + unsigned int div = d->data.div; + + if (d->ops && d->ops->get_div) + div = max(d->ops->get_div(d), 1); + div = max(div, 1U); + c->rate = clk_get_rate(c->parent) / div; + + if (!d->ops || !d->ops->set_div) + d->data.min_div = d->data.max_div = div; + d->data.div = div; + + if (d->en_mask && d->ops && d->ops->is_enabled) + return d->ops->is_enabled(d) + ? HANDOFF_ENABLED_CLK + : HANDOFF_DISABLED_CLK; + + /* + * If this function returns 'enabled' even when the clock downstream + * of this clock is disabled, then handoff code will unnecessarily + * enable the current parent of this clock. If this function always + * returns 'disabled' and a clock downstream is on, the clock handoff + * code will bump up the ref count for this clock and its current + * parent as necessary. So, clocks without an actual HW gate can + * always return disabled. + */ + return HANDOFF_DISABLED_CLK; +} + +struct clk_ops clk_ops_div = { + .enable = div_enable, + .disable = div_disable, + .round_rate = div_round_rate, + .set_rate = div_set_rate, + .handoff = div_handoff, +}; + +static long __slave_div_round_rate(struct clk *c, unsigned long rate, + int *best_div) +{ + struct div_clk *d = to_div_clk(c); + unsigned int div, min_div, max_div; + long p_rate; + + rate = max(rate, 1UL); + + min_div = d->data.min_div; + max_div = d->data.max_div; + + p_rate = clk_get_rate(c->parent); + div = p_rate / rate; + div = max(div, min_div); + div = min(div, max_div); + if (best_div) + *best_div = div; + + return p_rate / div; +} + +static long slave_div_round_rate(struct clk *c, unsigned long rate) +{ + return __slave_div_round_rate(c, rate, NULL); +} + +static int slave_div_set_rate(struct clk *c, unsigned long rate) +{ + struct div_clk *d = to_div_clk(c); + int div, rc = 0; + long rrate; + + rrate = __slave_div_round_rate(c, rate, &div); + if (rrate != rate) + return -EINVAL; + + if (div == d->data.div) + return 0; + + /* + * For fixed divider clock we don't want to return an error if the + * requested rate matches the achievable rate. So, don't check for + * !d->ops and return an error. __slave_div_round_rate() ensures + * div == d->data.div if !d->ops. + */ + rc = d->ops->set_div(d, div); + if (rc) + return rc; + + d->data.div = div; + + return 0; +} + +static unsigned long slave_div_get_rate(struct clk *c) +{ + struct div_clk *d = to_div_clk(c); + if (!d->data.div) + return 0; + return clk_get_rate(c->parent) / d->data.div; +} + +struct clk_ops clk_ops_slave_div = { + .enable = div_enable, + .disable = div_disable, + .round_rate = slave_div_round_rate, + .set_rate = slave_div_set_rate, + .get_rate = slave_div_get_rate, + .handoff = div_handoff, +}; + + +/** + * External clock + * Some clock controllers have input clock signal that come from outside the + * clock controller. That input clock signal might then be used as a source for + * several clocks inside the clock controller. This external clock + * implementation models this input clock signal by just passing on the requests + * to the clock's parent, the original external clock source. The driver for the + * clock controller should clk_get() the original external clock in the probe + * function and set is as a parent to this external clock.. + */ + +static long ext_round_rate(struct clk *c, unsigned long rate) +{ + return clk_round_rate(c->parent, rate); +} + +static int ext_set_rate(struct clk *c, unsigned long rate) +{ + return clk_set_rate(c->parent, rate); +} + +static unsigned long ext_get_rate(struct clk *c) +{ + return clk_get_rate(c->parent); +} + +static int ext_set_parent(struct clk *c, struct clk *p) +{ + return clk_set_parent(c->parent, p); +} + +static enum handoff ext_handoff(struct clk *c) +{ + c->rate = clk_get_rate(c->parent); + /* Similar reasoning applied in div_handoff, see comment there. */ + return HANDOFF_DISABLED_CLK; +} + +struct clk_ops clk_ops_ext = { + .handoff = ext_handoff, + .round_rate = ext_round_rate, + .set_rate = ext_set_rate, + .get_rate = ext_get_rate, + .set_parent = ext_set_parent, +}; + + +/* ==================== Mux_div clock ==================== */ + +static int mux_div_clk_enable(struct clk *c) +{ + struct mux_div_clk *md = to_mux_div_clk(c); + + if (md->ops->enable) + return md->ops->enable(md); + return 0; +} + +static void mux_div_clk_disable(struct clk *c) +{ + struct mux_div_clk *md = to_mux_div_clk(c); + + if (md->ops->disable) + return md->ops->disable(md); +} + +static long __mux_div_round_rate(struct clk *c, unsigned long rate, + struct clk **best_parent, int *best_div, unsigned long *best_prate) +{ + struct mux_div_clk *md = to_mux_div_clk(c); + unsigned int i; + unsigned long rrate, best = 0, _best_div = 0, _best_prate = 0; + struct clk *_best_parent = 0; + + for (i = 0; i < md->num_parents; i++) { + int div; + unsigned long prate; + + rrate = __div_round_rate(&md->data, rate, md->parents[i].src, + &div, &prate); + + if (is_better_rate(rate, best, rrate)) { + best = rrate; + _best_div = div; + _best_prate = prate; + _best_parent = md->parents[i].src; + } + + if (rate <= rrate && rrate <= rate + md->data.rate_margin) + break; + } + + if (best_div) + *best_div = _best_div; + if (best_prate) + *best_prate = _best_prate; + if (best_parent) + *best_parent = _best_parent; + + if (best) + return best; + return -EINVAL; +} + +static long mux_div_clk_round_rate(struct clk *c, unsigned long rate) +{ + return __mux_div_round_rate(c, rate, NULL, NULL, NULL); +} + +/* requires enable lock to be held */ +static int __set_src_div(struct mux_div_clk *md, struct clk *parent, u32 div) +{ + u32 rc = 0, src_sel; + + src_sel = parent_to_src_sel(md->parents, md->num_parents, parent); + /* + * If the clock is disabled, don't change to the new settings until + * the clock is reenabled + */ + if (md->c.count) + rc = md->ops->set_src_div(md, src_sel, div); + if (!rc) { + md->data.div = div; + md->src_sel = src_sel; + } + + return rc; +} + +static int set_src_div(struct mux_div_clk *md, struct clk *parent, u32 div) +{ + unsigned long flags; + u32 rc; + + spin_lock_irqsave(&md->c.lock, flags); + rc = __set_src_div(md, parent, div); + spin_unlock_irqrestore(&md->c.lock, flags); + + return rc; +} + +/* Must be called after handoff to ensure parent clock rates are initialized */ +static int safe_parent_init_once(struct clk *c) +{ + unsigned long rrate; + u32 best_div; + struct clk *best_parent; + struct mux_div_clk *md = to_mux_div_clk(c); + + if (IS_ERR(md->safe_parent)) + return -EINVAL; + if (!md->safe_freq || md->safe_parent) + return 0; + + rrate = __mux_div_round_rate(c, md->safe_freq, &best_parent, + &best_div, NULL); + + if (rrate == md->safe_freq) { + md->safe_div = best_div; + md->safe_parent = best_parent; + } else { + md->safe_parent = ERR_PTR(-EINVAL); + return -EINVAL; + } + return 0; +} + +static int mux_div_clk_set_rate(struct clk *c, unsigned long rate) +{ + struct mux_div_clk *md = to_mux_div_clk(c); + unsigned long flags, rrate; + unsigned long new_prate, old_prate; + struct clk *old_parent, *new_parent; + u32 new_div, old_div; + int rc; + + rc = safe_parent_init_once(c); + if (rc) + return rc; + + rrate = __mux_div_round_rate(c, rate, &new_parent, &new_div, + &new_prate); + if (rrate != rate) + return -EINVAL; + + old_parent = c->parent; + old_div = md->data.div; + old_prate = clk_get_rate(c->parent); + + /* Refer to the description of safe_freq in clock-generic.h */ + if (md->safe_freq) + rc = set_src_div(md, md->safe_parent, md->safe_div); + + else if (new_parent == old_parent && new_div >= old_div) { + /* + * If both the parent_rate and divider changes, there may be an + * intermediate frequency generated. Ensure this intermediate + * frequency is less than both the new rate and previous rate. + */ + rc = set_src_div(md, old_parent, new_div); + } + if (rc) + return rc; + + rc = clk_set_rate(new_parent, new_prate); + if (rc) { + pr_err("failed to set %s to %ld\n", + new_parent->dbg_name, new_prate); + goto err_set_rate; + } + + rc = __clk_pre_reparent(c, new_parent, &flags); + if (rc) + goto err_pre_reparent; + + /* Set divider and mux src atomically */ + rc = __set_src_div(md, new_parent, new_div); + if (rc) + goto err_set_src_div; + + c->parent = new_parent; + + __clk_post_reparent(c, old_parent, &flags); + return 0; + +err_set_src_div: + /* Not switching to new_parent, so disable it */ + __clk_post_reparent(c, new_parent, &flags); +err_pre_reparent: + rc = clk_set_rate(old_parent, old_prate); + WARN(rc, "%s: error changing parent (%s) rate to %ld\n", + c->dbg_name, old_parent->dbg_name, old_prate); +err_set_rate: + rc = set_src_div(md, old_parent, old_div); + WARN(rc, "%s: error changing back to original div (%d) and parent (%s)\n", + c->dbg_name, old_div, old_parent->dbg_name); + + return rc; +} + +static struct clk *mux_div_clk_get_parent(struct clk *c) +{ + struct mux_div_clk *md = to_mux_div_clk(c); + u32 i, div, src_sel; + + md->ops->get_src_div(md, &src_sel, &div); + + md->data.div = div; + md->src_sel = src_sel; + + for (i = 0; i < md->num_parents; i++) { + if (md->parents[i].sel == src_sel) + return md->parents[i].src; + } + + return NULL; +} + +static enum handoff mux_div_clk_handoff(struct clk *c) +{ + struct mux_div_clk *md = to_mux_div_clk(c); + unsigned long parent_rate; + + parent_rate = clk_get_rate(c->parent); + c->rate = parent_rate / md->data.div; + + if (!md->ops->is_enabled) + return HANDOFF_DISABLED_CLK; + if (md->ops->is_enabled(md)) + return HANDOFF_ENABLED_CLK; + return HANDOFF_DISABLED_CLK; +} + +struct clk_ops clk_ops_mux_div_clk = { + .enable = mux_div_clk_enable, + .disable = mux_div_clk_disable, + .set_rate = mux_div_clk_set_rate, + .round_rate = mux_div_clk_round_rate, + .get_parent = mux_div_clk_get_parent, + .handoff = mux_div_clk_handoff, +}; diff --git a/drivers/clk/qcom/clock.c b/drivers/clk/qcom/clock.c new file mode 100644 index 000000000000..e69f0cbdd659 --- /dev/null +++ b/drivers/clk/qcom/clock.c @@ -0,0 +1,905 @@ +/* arch/arm/mach-msm/clock.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2013, The Linux Foundation. All rights reserved. + * + * 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 <linux/kernel.h> +#include <linux/err.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/list.h> +#include <linux/regulator/consumer.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/clk/msm-clk-provider.h> +#include <trace/events/power.h> +#include "clock.h" + +struct handoff_clk { + struct list_head list; + struct clk *clk; +}; +static LIST_HEAD(handoff_list); + +struct handoff_vdd { + struct list_head list; + struct clk_vdd_class *vdd_class; +}; +static LIST_HEAD(handoff_vdd_list); + +static DEFINE_MUTEX(msm_clock_init_lock); + +/* Find the voltage level required for a given rate. */ +int find_vdd_level(struct clk *clk, unsigned long rate) +{ + int level; + + for (level = 0; level < clk->num_fmax; level++) + if (rate <= clk->fmax[level]) + break; + + if (level == clk->num_fmax) { + pr_err("Rate %lu for %s is greater than highest Fmax\n", rate, + clk->dbg_name); + return -EINVAL; + } + + return level; +} + +/* Update voltage level given the current votes. */ +static int update_vdd(struct clk_vdd_class *vdd_class) +{ + int level, rc = 0, i, ignore; + struct regulator **r = vdd_class->regulator; + int *uv = vdd_class->vdd_uv; + int *ua = vdd_class->vdd_ua; + int n_reg = vdd_class->num_regulators; + int cur_lvl = vdd_class->cur_level; + int max_lvl = vdd_class->num_levels - 1; + int cur_base = cur_lvl * n_reg; + int new_base; + + /* aggregate votes */ + for (level = max_lvl; level > 0; level--) + if (vdd_class->level_votes[level]) + break; + + if (level == cur_lvl) + return 0; + + max_lvl = max_lvl * n_reg; + new_base = level * n_reg; + for (i = 0; i < vdd_class->num_regulators; i++) { + rc = regulator_set_voltage(r[i], uv[new_base + i], + uv[max_lvl + i]); + if (rc) + goto set_voltage_fail; + + if (ua) { + rc = regulator_set_optimum_mode(r[i], ua[new_base + i]); + rc = rc > 0 ? 0 : rc; + if (rc) + goto set_mode_fail; + } + if (cur_lvl == 0 || cur_lvl == vdd_class->num_levels) + rc = regulator_enable(r[i]); + else if (level == 0) + rc = regulator_disable(r[i]); + if (rc) + goto enable_disable_fail; + } + if (vdd_class->set_vdd && !vdd_class->num_regulators) + rc = vdd_class->set_vdd(vdd_class, level); + + if (!rc) + vdd_class->cur_level = level; + + return rc; + +enable_disable_fail: + /* + * set_optimum_mode could use voltage to derive mode. Restore + * previous voltage setting for r[i] first. + */ + if (ua) { + regulator_set_voltage(r[i], uv[cur_base + i], uv[max_lvl + i]); + regulator_set_optimum_mode(r[i], ua[cur_base + i]); + } + +set_mode_fail: + regulator_set_voltage(r[i], uv[cur_base + i], uv[max_lvl + i]); + +set_voltage_fail: + for (i--; i >= 0; i--) { + regulator_set_voltage(r[i], uv[cur_base + i], uv[max_lvl + i]); + if (ua) + regulator_set_optimum_mode(r[i], ua[cur_base + i]); + if (cur_lvl == 0 || cur_lvl == vdd_class->num_levels) + regulator_disable(r[i]); + else if (level == 0) + ignore = regulator_enable(r[i]); + } + return rc; +} + +/* Vote for a voltage level. */ +int vote_vdd_level(struct clk_vdd_class *vdd_class, int level) +{ + int rc; + + if (level >= vdd_class->num_levels) + return -EINVAL; + + mutex_lock(&vdd_class->lock); + vdd_class->level_votes[level]++; + rc = update_vdd(vdd_class); + if (rc) + vdd_class->level_votes[level]--; + mutex_unlock(&vdd_class->lock); + + return rc; +} + +/* Remove vote for a voltage level. */ +int unvote_vdd_level(struct clk_vdd_class *vdd_class, int level) +{ + int rc = 0; + + if (level >= vdd_class->num_levels) + return -EINVAL; + + mutex_lock(&vdd_class->lock); + if (WARN(!vdd_class->level_votes[level], + "Reference counts are incorrect for %s level %d\n", + vdd_class->class_name, level)) + goto out; + vdd_class->level_votes[level]--; + rc = update_vdd(vdd_class); + if (rc) + vdd_class->level_votes[level]++; +out: + mutex_unlock(&vdd_class->lock); + return rc; +} + +/* Vote for a voltage level corresponding to a clock's rate. */ +static int vote_rate_vdd(struct clk *clk, unsigned long rate) +{ + int level; + + if (!clk->vdd_class) + return 0; + + level = find_vdd_level(clk, rate); + if (level < 0) + return level; + + return vote_vdd_level(clk->vdd_class, level); +} + +/* Remove vote for a voltage level corresponding to a clock's rate. */ +static void unvote_rate_vdd(struct clk *clk, unsigned long rate) +{ + int level; + + if (!clk->vdd_class) + return; + + level = find_vdd_level(clk, rate); + if (level < 0) + return; + + unvote_vdd_level(clk->vdd_class, level); +} + +/* Check if the rate is within the voltage limits of the clock. */ +static bool is_rate_valid(struct clk *clk, unsigned long rate) +{ + int level; + + if (!clk->vdd_class) + return true; + + level = find_vdd_level(clk, rate); + return level >= 0; +} + +/** + * __clk_pre_reparent() - Set up the new parent before switching to it and + * prevent the enable state of the child clock from changing. + * @c: The child clock that's going to switch parents + * @new: The new parent that the child clock is going to switch to + * @flags: Pointer to scratch space to save spinlock flags + * + * Cannot be called from atomic context. + * + * Use this API to set up the @new parent clock to be able to support the + * current prepare and enable state of the child clock @c. Once the parent is + * set up, the child clock can safely switch to it. + * + * The caller shall grab the prepare_lock of clock @c before calling this API + * and only release it after calling __clk_post_reparent() for clock @c (or + * if this API fails). This is necessary to prevent the prepare state of the + * child clock @c from changing while the reparenting is in progress. Since + * this API takes care of grabbing the enable lock of @c, only atomic + * operation are allowed between calls to __clk_pre_reparent and + * __clk_post_reparent() + * + * The scratch space pointed to by @flags should not be altered before + * calling __clk_post_reparent() for clock @c. + * + * See also: __clk_post_reparent() + */ +int __clk_pre_reparent(struct clk *c, struct clk *new, unsigned long *flags) +{ + int rc; + + if (c->prepare_count) { + rc = clk_prepare(new); + if (rc) + return rc; + } + + spin_lock_irqsave(&c->lock, *flags); + if (c->count) { + rc = clk_enable(new); + if (rc) { + spin_unlock_irqrestore(&c->lock, *flags); + clk_unprepare(new); + return rc; + } + } + return 0; +} + +/** + * __clk_post_reparent() - Release requirements on old parent after switching + * away from it and allow changes to the child clock's enable state. + * @c: The child clock that switched parents + * @old: The old parent that the child clock switched away from or the new + * parent of a failed reparent attempt. + * @flags: Pointer to scratch space where spinlock flags were saved + * + * Cannot be called from atomic context. + * + * This API works in tandem with __clk_pre_reparent. Use this API to + * - Remove prepare and enable requirements from the @old parent after + * switching away from it + * - Or, undo the effects of __clk_pre_reparent() after a failed attempt to + * change parents + * + * The caller shall release the prepare_lock of @c that was grabbed before + * calling __clk_pre_reparent() only after this API is called (or if + * __clk_pre_reparent() fails). This is necessary to prevent the prepare + * state of the child clock @c from changing while the reparenting is in + * progress. Since this API releases the enable lock of @c, the limit to + * atomic operations set by __clk_pre_reparent() is no longer present. + * + * The scratch space pointed to by @flags shall not be altered since the call + * to __clk_pre_reparent() for clock @c. + * + * See also: __clk_pre_reparent() + */ +void __clk_post_reparent(struct clk *c, struct clk *old, unsigned long *flags) +{ + if (c->count) + clk_disable(old); + spin_unlock_irqrestore(&c->lock, *flags); + + if (c->prepare_count) + clk_unprepare(old); +} + +int clk_prepare(struct clk *clk) +{ + int ret = 0; + struct clk *parent; + + if (!clk) + return 0; + if (IS_ERR(clk)) + return -EINVAL; + + mutex_lock(&clk->prepare_lock); + if (clk->prepare_count == 0) { + parent = clk->parent; + + ret = clk_prepare(parent); + if (ret) + goto out; + ret = clk_prepare(clk->depends); + if (ret) + goto err_prepare_depends; + + ret = vote_rate_vdd(clk, clk->rate); + if (ret) + goto err_vote_vdd; + if (clk->ops->prepare) + ret = clk->ops->prepare(clk); + if (ret) + goto err_prepare_clock; + } + clk->prepare_count++; +out: + mutex_unlock(&clk->prepare_lock); + return ret; +err_prepare_clock: + unvote_rate_vdd(clk, clk->rate); +err_vote_vdd: + clk_unprepare(clk->depends); +err_prepare_depends: + clk_unprepare(parent); + goto out; +} +EXPORT_SYMBOL(clk_prepare); + +/* + * Standard clock functions defined in include/linux/clk.h + */ +int clk_enable(struct clk *clk) +{ + int ret = 0; + unsigned long flags; + struct clk *parent; + const char *name = clk ? clk->dbg_name : NULL; + + if (!clk) + return 0; + if (IS_ERR(clk)) + return -EINVAL; + + spin_lock_irqsave(&clk->lock, flags); + WARN(!clk->prepare_count, + "%s: Don't call enable on unprepared clocks\n", name); + if (clk->count == 0) { + parent = clk->parent; + + ret = clk_enable(parent); + if (ret) + goto err_enable_parent; + ret = clk_enable(clk->depends); + if (ret) + goto err_enable_depends; + + trace_clock_enable(name, 1, smp_processor_id()); + if (clk->ops->enable) + ret = clk->ops->enable(clk); + if (ret) + goto err_enable_clock; + } + clk->count++; + spin_unlock_irqrestore(&clk->lock, flags); + + return 0; + +err_enable_clock: + clk_disable(clk->depends); +err_enable_depends: + clk_disable(parent); +err_enable_parent: + spin_unlock_irqrestore(&clk->lock, flags); + return ret; +} +EXPORT_SYMBOL(clk_enable); + +void clk_disable(struct clk *clk) +{ + const char *name = clk ? clk->dbg_name : NULL; + unsigned long flags; + + if (IS_ERR_OR_NULL(clk)) + return; + + spin_lock_irqsave(&clk->lock, flags); + WARN(!clk->prepare_count, + "%s: Never called prepare or calling disable after unprepare\n", + name); + if (WARN(clk->count == 0, "%s is unbalanced", name)) + goto out; + if (clk->count == 1) { + struct clk *parent = clk->parent; + + trace_clock_disable(name, 0, smp_processor_id()); + if (clk->ops->disable) + clk->ops->disable(clk); + clk_disable(clk->depends); + clk_disable(parent); + } + clk->count--; +out: + spin_unlock_irqrestore(&clk->lock, flags); +} +EXPORT_SYMBOL(clk_disable); + +void clk_unprepare(struct clk *clk) +{ + const char *name = clk ? clk->dbg_name : NULL; + + if (IS_ERR_OR_NULL(clk)) + return; + + mutex_lock(&clk->prepare_lock); + if (WARN(!clk->prepare_count, "%s is unbalanced (prepare)", name)) + goto out; + if (clk->prepare_count == 1) { + struct clk *parent = clk->parent; + + WARN(clk->count, + "%s: Don't call unprepare when the clock is enabled\n", + name); + + if (clk->ops->unprepare) + clk->ops->unprepare(clk); + unvote_rate_vdd(clk, clk->rate); + clk_unprepare(clk->depends); + clk_unprepare(parent); + } + clk->prepare_count--; +out: + mutex_unlock(&clk->prepare_lock); +} +EXPORT_SYMBOL(clk_unprepare); + +int clk_reset(struct clk *clk, enum clk_reset_action action) +{ + if (IS_ERR_OR_NULL(clk)) + return -EINVAL; + + if (!clk->ops->reset) + return -ENOSYS; + + return clk->ops->reset(clk, action); +} +EXPORT_SYMBOL(clk_reset); + +unsigned long clk_get_rate(struct clk *clk) +{ + if (IS_ERR_OR_NULL(clk)) + return 0; + + if (!clk->ops->get_rate) + return clk->rate; + + return clk->ops->get_rate(clk); +} +EXPORT_SYMBOL(clk_get_rate); + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long start_rate; + int rc = 0; + const char *name = clk ? clk->dbg_name : NULL; + + if (IS_ERR_OR_NULL(clk)) + return -EINVAL; + + if (!clk->ops->set_rate) + return -ENOSYS; + + if (!is_rate_valid(clk, rate)) + return -EINVAL; + + mutex_lock(&clk->prepare_lock); + + /* Return early if the rate isn't going to change */ + if (clk->rate == rate && !(clk->flags & CLKFLAG_NO_RATE_CACHE)) + goto out; + + trace_clock_set_rate(name, rate, raw_smp_processor_id()); + + start_rate = clk->rate; + + if (clk->ops->pre_set_rate) + rc = clk->ops->pre_set_rate(clk, rate); + if (rc) + goto out; + + /* Enforce vdd requirements for target frequency. */ + if (clk->prepare_count) { + rc = vote_rate_vdd(clk, rate); + if (rc) + goto err_vote_vdd; + } + + rc = clk->ops->set_rate(clk, rate); + if (rc) + goto err_set_rate; + clk->rate = rate; + + /* Release vdd requirements for starting frequency. */ + if (clk->prepare_count) + unvote_rate_vdd(clk, start_rate); + + if (clk->ops->post_set_rate) + clk->ops->post_set_rate(clk, start_rate); + +out: + mutex_unlock(&clk->prepare_lock); + return rc; + +err_set_rate: + if (clk->prepare_count) + unvote_rate_vdd(clk, rate); +err_vote_vdd: + /* clk->rate is still the old rate. So, pass the new rate instead. */ + if (clk->ops->post_set_rate) + clk->ops->post_set_rate(clk, rate); + goto out; +} +EXPORT_SYMBOL(clk_set_rate); + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + long rrate; + unsigned long fmax = 0, i; + + if (IS_ERR_OR_NULL(clk)) + return -EINVAL; + + if (!clk->ops->round_rate) + return -ENOSYS; + + for (i = 0; i < clk->num_fmax; i++) + fmax = max(fmax, clk->fmax[i]); + + if (!fmax) + fmax = ULONG_MAX; + + rate = min(rate, fmax); + rrate = clk->ops->round_rate(clk, rate); + if (rrate > fmax) + return -EINVAL; + return rrate; +} +EXPORT_SYMBOL(clk_round_rate); + +int clk_set_max_rate(struct clk *clk, unsigned long rate) +{ + if (IS_ERR_OR_NULL(clk)) + return -EINVAL; + + if (!clk->ops->set_max_rate) + return -ENOSYS; + + return clk->ops->set_max_rate(clk, rate); +} +EXPORT_SYMBOL(clk_set_max_rate); + +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + int rc = 0; + + if (!clk->ops->set_parent && clk->parent == parent) + return 0; + + if (!clk->ops->set_parent) + return -ENOSYS; + + mutex_lock(&clk->prepare_lock); + if (clk->parent == parent && !(clk->flags & CLKFLAG_NO_RATE_CACHE)) + goto out; + rc = clk->ops->set_parent(clk, parent); +out: + mutex_unlock(&clk->prepare_lock); + + return rc; +} +EXPORT_SYMBOL(clk_set_parent); + +struct clk *clk_get_parent(struct clk *clk) +{ + if (IS_ERR_OR_NULL(clk)) + return NULL; + + return clk->parent; +} +EXPORT_SYMBOL(clk_get_parent); + +int clk_set_flags(struct clk *clk, unsigned long flags) +{ + if (IS_ERR_OR_NULL(clk)) + return -EINVAL; + if (!clk->ops->set_flags) + return -ENOSYS; + + return clk->ops->set_flags(clk, flags); +} +EXPORT_SYMBOL(clk_set_flags); + +static LIST_HEAD(initdata_list); + +static void init_sibling_lists(struct clk_lookup *clock_tbl, size_t num_clocks) +{ + struct clk *clk, *parent; + unsigned n; + + for (n = 0; n < num_clocks; n++) { + clk = clock_tbl[n].clk; + parent = clk->parent; + if (parent && list_empty(&clk->siblings)) + list_add(&clk->siblings, &parent->children); + } +} + +static void vdd_class_init(struct clk_vdd_class *vdd) +{ + struct handoff_vdd *v; + + if (!vdd) + return; + + list_for_each_entry(v, &handoff_vdd_list, list) { + if (v->vdd_class == vdd) + return; + } + + pr_debug("voting for vdd_class %s\n", vdd->class_name); + if (vote_vdd_level(vdd, vdd->num_levels - 1)) + pr_err("failed to vote for %s\n", vdd->class_name); + + v = kmalloc(sizeof(*v), GFP_KERNEL); + if (!v) { + pr_err("Unable to kmalloc. %s will be stuck at max.\n", + vdd->class_name); + return; + } + + v->vdd_class = vdd; + list_add_tail(&v->list, &handoff_vdd_list); +} + +static int __handoff_clk(struct clk *clk) +{ + enum handoff state = HANDOFF_DISABLED_CLK; + struct handoff_clk *h = NULL; + int rc; + + if (clk == NULL || clk->flags & CLKFLAG_INIT_DONE || + clk->flags & CLKFLAG_SKIP_HANDOFF) + return 0; + + if (clk->flags & CLKFLAG_INIT_ERR) + return -ENXIO; + + /* Handoff any 'depends' clock first. */ + rc = __handoff_clk(clk->depends); + if (rc) + goto err; + + /* + * Handoff functions for the parent must be called before the + * children can be handed off. Without handing off the parents and + * knowing their rate and state (on/off), it's impossible to figure + * out the rate and state of the children. + */ + if (clk->ops->get_parent) + clk->parent = clk->ops->get_parent(clk); + + if (IS_ERR(clk->parent)) { + rc = PTR_ERR(clk->parent); + goto err; + } + + rc = __handoff_clk(clk->parent); + if (rc) + goto err; + + if (clk->ops->handoff) + state = clk->ops->handoff(clk); + + if (state == HANDOFF_ENABLED_CLK) { + + h = kmalloc(sizeof(*h), GFP_KERNEL); + if (!h) { + rc = -ENOMEM; + goto err; + } + + rc = clk_prepare_enable(clk->parent); + if (rc) + goto err; + + rc = clk_prepare_enable(clk->depends); + if (rc) + goto err_depends; + + rc = vote_rate_vdd(clk, clk->rate); + WARN(rc, "%s unable to vote for voltage!\n", clk->dbg_name); + + clk->count = 1; + clk->prepare_count = 1; + h->clk = clk; + list_add_tail(&h->list, &handoff_list); + + pr_debug("Handed off %s rate=%lu\n", clk->dbg_name, clk->rate); + } + + clk->flags |= CLKFLAG_INIT_DONE; + + return 0; + +err_depends: + clk_disable_unprepare(clk->parent); +err: + kfree(h); + clk->flags |= CLKFLAG_INIT_ERR; + pr_err("%s handoff failed (%d)\n", clk->dbg_name, rc); + return rc; +} + +/** + * msm_clock_register() - Register additional clock tables + * @table: Table of clocks + * @size: Size of @table + * + * Upon return, clock APIs may be used to control clocks registered using this + * function. + */ +int msm_clock_register(struct clk_lookup *table, size_t size) +{ + int n = 0; + + mutex_lock(&msm_clock_init_lock); + + init_sibling_lists(table, size); + + /* + * Enable regulators and temporarily set them up at maximum voltage. + * Once all the clocks have made their respective vote, remove this + * temporary vote. The removing of the temporary vote is done at + * late_init, by which time we assume all the clocks would have been + * handed off. + */ + for (n = 0; n < size; n++) + vdd_class_init(table[n].clk->vdd_class); + + /* + * Detect and preserve initial clock state until clock_late_init() or + * a driver explicitly changes it, whichever is first. + */ + for (n = 0; n < size; n++) + __handoff_clk(table[n].clk); + + clkdev_add_table(table, size); + + clock_debug_register(table, size); + + mutex_unlock(&msm_clock_init_lock); + + return 0; +} +EXPORT_SYMBOL(msm_clock_register); + +struct of_msm_provider_data { + struct clk_lookup *table; + size_t size; +}; + +static struct clk *of_clk_src_get(struct of_phandle_args *clkspec, + void *data) +{ + struct of_msm_provider_data *ofdata = data; + int n; + + for (n = 0; n < ofdata->size; n++) { + if (clkspec->args[0] == ofdata->table[n].of_idx) + return ofdata->table[n].clk; + } + return ERR_PTR(-ENOENT); +} + +/** + * of_msm_clock_register() - Register clock tables with clkdev and with the + * clock DT framework + * @table: Table of clocks + * @size: Size of @table + * @np: Device pointer corresponding to the clock-provider device + * + * Upon return, clock APIs may be used to control clocks registered using this + * function. + */ +int of_msm_clock_register(struct device_node *np, struct clk_lookup *table, + size_t size) +{ + int ret = 0; + struct of_msm_provider_data *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->table = table; + data->size = size; + + ret = of_clk_add_provider(np, of_clk_src_get, data); + if (ret) { + kfree(data); + return -ENOMEM; + } + + return msm_clock_register(table, size); +} +EXPORT_SYMBOL(of_msm_clock_register); + +/** + * msm_clock_init() - Register and initialize a clock driver + * @data: Driver-specific clock initialization data + * + * Upon return from this call, clock APIs may be used to control + * clocks registered with this API. + */ +int __init msm_clock_init(struct clock_init_data *data) +{ + if (!data) + return -EINVAL; + + if (data->pre_init) + data->pre_init(); + + mutex_lock(&msm_clock_init_lock); + if (data->late_init) + list_add(&data->list, &initdata_list); + mutex_unlock(&msm_clock_init_lock); + + msm_clock_register(data->table, data->size); + + if (data->post_init) + data->post_init(); + + return 0; +} + +static int __init clock_late_init(void) +{ + struct handoff_clk *h, *h_temp; + struct handoff_vdd *v, *v_temp; + struct clock_init_data *initdata, *initdata_temp; + int ret = 0; + + pr_info("%s: Removing enables held for handed-off clocks\n", __func__); + + mutex_lock(&msm_clock_init_lock); + + list_for_each_entry_safe(initdata, initdata_temp, + &initdata_list, list) { + ret = initdata->late_init(); + if (ret) + pr_err("%s: %pS failed late_init.\n", __func__, + initdata); + } + + list_for_each_entry_safe(h, h_temp, &handoff_list, list) { + clk_disable_unprepare(h->clk); + list_del(&h->list); + kfree(h); + } + + list_for_each_entry_safe(v, v_temp, &handoff_vdd_list, list) { + unvote_vdd_level(v->vdd_class, v->vdd_class->num_levels - 1); + list_del(&v->list); + kfree(v); + } + + mutex_unlock(&msm_clock_init_lock); + + return ret; +} +/* clock_late_init should run only after all deferred probing + * (excluding DLKM probes) has completed. + */ +late_initcall_sync(clock_late_init); diff --git a/drivers/clk/msm/clock.h b/drivers/clk/qcom/clock.h index b0152d8c1fe1..b0152d8c1fe1 100644 --- a/drivers/clk/msm/clock.h +++ b/drivers/clk/qcom/clock.h diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index e09ecdd49a03..56684aed5701 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -415,6 +415,7 @@ u64 arch_counter_get_cntpct(void) else return arch_counter_get_cntpct_mem(); } +EXPORT_SYMBOL(arch_counter_get_cntpct); u64 arch_counter_get_cntvct(void) { diff --git a/drivers/coresight/coresight-tmc.c b/drivers/coresight/coresight-tmc.c index e85e4220ebaf..265e16ad8a89 100644 --- a/drivers/coresight/coresight-tmc.c +++ b/drivers/coresight/coresight-tmc.c @@ -1295,16 +1295,26 @@ static ssize_t tmc_etr_store_byte_cntr_value(struct device *dev, struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); unsigned long val; - if (!drvdata->byte_cntr_present || drvdata->byte_cntr_enable) + mutex_lock(&drvdata->byte_cntr_lock); + if (!drvdata->byte_cntr_present || drvdata->byte_cntr_enable) { + mutex_unlock(&drvdata->byte_cntr_lock); return -EPERM; - if (sscanf(buf, "%lx", &val) != 1) + } + if (sscanf(buf, "%lx", &val) != 1) { + mutex_unlock(&drvdata->byte_cntr_lock); return -EINVAL; - if ((drvdata->size / 8) < val) + } + if ((drvdata->size / 8) < val) { + mutex_unlock(&drvdata->byte_cntr_lock); return -EINVAL; - if (val && drvdata->size % (val * 8) != 0) + } + if (val && drvdata->size % (val * 8) != 0) { + mutex_unlock(&drvdata->byte_cntr_lock); return -EINVAL; + } drvdata->byte_cntr_value = val; + mutex_unlock(&drvdata->byte_cntr_lock); return size; } static DEVICE_ATTR(byte_cntr_value, S_IRUGO | S_IWUSR, diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 9dd53eb968d7..afe80b927fbd 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -17,15 +17,11 @@ config CPU_FREQ if CPU_FREQ -config CPU_FREQ_TABLE - tristate - config CPU_FREQ_GOV_COMMON bool config CPU_FREQ_STAT tristate "CPU frequency translation statistics" - select CPU_FREQ_TABLE default y help This driver exports CPU frequency statistics information through sysfs @@ -153,7 +149,6 @@ config CPU_FREQ_GOV_USERSPACE config CPU_FREQ_GOV_ONDEMAND tristate "'ondemand' cpufreq policy governor" - select CPU_FREQ_TABLE select CPU_FREQ_GOV_COMMON help 'ondemand' - This driver adds a dynamic cpufreq policy governor. @@ -225,7 +220,6 @@ config CPU_BOOST config GENERIC_CPUFREQ_CPU0 tristate "Generic CPU0 cpufreq driver" depends on HAVE_CLK && REGULATOR && PM_OPP && OF - select CPU_FREQ_TABLE help This adds a generic cpufreq driver for CPU0 frequency management. It supports both uniprocessor (UP) and symmetric multiprocessor (SMP) @@ -261,7 +255,6 @@ depends on IA64 config IA64_ACPI_CPUFREQ tristate "ACPI Processor P-States driver" - select CPU_FREQ_TABLE depends on ACPI_PROCESSOR help This driver adds a CPUFreq driver which utilizes the ACPI @@ -278,7 +271,6 @@ depends on MIPS config LOONGSON2_CPUFREQ tristate "Loongson2 CPUFreq Driver" - select CPU_FREQ_TABLE help This option adds a CPUFreq driver for loongson processors which support software configurable cpu frequency. @@ -300,7 +292,6 @@ menu "SPARC CPU frequency scaling drivers" depends on SPARC64 config SPARC_US3_CPUFREQ tristate "UltraSPARC-III CPU Frequency driver" - select CPU_FREQ_TABLE help This adds the CPUFreq driver for UltraSPARC-III processors. @@ -310,7 +301,6 @@ config SPARC_US3_CPUFREQ config SPARC_US2E_CPUFREQ tristate "UltraSPARC-IIe CPU Frequency driver" - select CPU_FREQ_TABLE help This adds the CPUFreq driver for UltraSPARC-IIe processors. @@ -323,7 +313,6 @@ menu "SH CPU Frequency scaling" depends on SUPERH config SH_CPU_FREQ tristate "SuperH CPU Frequency driver" - select CPU_FREQ_TABLE help This adds the cpufreq driver for SuperH. Any CPU that supports clock rate rounding through the clock framework can use this diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 6e57543fe0b9..a6dd7be49991 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -55,7 +55,6 @@ config ARM_EXYNOS5440_CPUFREQ config ARM_HIGHBANK_CPUFREQ tristate "Calxeda Highbank-based" depends on ARCH_HIGHBANK - select CPU_FREQ_TABLE select GENERIC_CPUFREQ_CPU0 select PM_OPP select REGULATOR @@ -94,7 +93,6 @@ config ARM_OMAP2PLUS_CPUFREQ bool "TI OMAP2+" depends on ARCH_OMAP2PLUS default ARCH_OMAP2PLUS - select CPU_FREQ_TABLE config ARM_S3C2416_CPUFREQ bool "S3C2416 CPU Frequency scaling support" @@ -130,7 +128,6 @@ config ARM_S3C64XX_CPUFREQ config ARM_S5PV210_CPUFREQ bool "Samsung S5PV210 and S5PC110" depends on CPU_S5PV210 - select CPU_FREQ_TABLE default y help This adds the CPUFreq driver for Samsung S5PV210 and diff --git a/drivers/cpufreq/Kconfig.powerpc b/drivers/cpufreq/Kconfig.powerpc index 9c926ca0d718..43221bf5fe12 100644 --- a/drivers/cpufreq/Kconfig.powerpc +++ b/drivers/cpufreq/Kconfig.powerpc @@ -19,7 +19,6 @@ config CPU_FREQ_CBE_PMI config CPU_FREQ_MAPLE bool "Support for Maple 970FX Evaluation Board" depends on PPC_MAPLE - select CPU_FREQ_TABLE help This adds support for frequency switching on Maple 970FX Evaluation Board and compatible boards (IBM JS2x blades). diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86 index 6bd63d63d356..6897ad85b046 100644 --- a/drivers/cpufreq/Kconfig.x86 +++ b/drivers/cpufreq/Kconfig.x86 @@ -31,7 +31,6 @@ config X86_PCC_CPUFREQ config X86_ACPI_CPUFREQ tristate "ACPI Processor P-States driver" - select CPU_FREQ_TABLE depends on ACPI_PROCESSOR help This driver adds a CPUFreq driver which utilizes the ACPI @@ -60,7 +59,6 @@ config X86_ACPI_CPUFREQ_CPB config ELAN_CPUFREQ tristate "AMD Elan SC400 and SC410" - select CPU_FREQ_TABLE depends on MELAN ---help--- This adds the CPUFreq driver for AMD Elan SC400 and SC410 @@ -76,7 +74,6 @@ config ELAN_CPUFREQ config SC520_CPUFREQ tristate "AMD Elan SC520" - select CPU_FREQ_TABLE depends on MELAN ---help--- This adds the CPUFreq driver for AMD Elan SC520 processor. @@ -88,7 +85,6 @@ config SC520_CPUFREQ config X86_POWERNOW_K6 tristate "AMD Mobile K6-2/K6-3 PowerNow!" - select CPU_FREQ_TABLE depends on X86_32 help This adds the CPUFreq driver for mobile AMD K6-2+ and mobile @@ -100,7 +96,6 @@ config X86_POWERNOW_K6 config X86_POWERNOW_K7 tristate "AMD Mobile Athlon/Duron PowerNow!" - select CPU_FREQ_TABLE depends on X86_32 help This adds the CPUFreq driver for mobile AMD K7 mobile processors. @@ -118,7 +113,6 @@ config X86_POWERNOW_K7_ACPI config X86_POWERNOW_K8 tristate "AMD Opteron/Athlon64 PowerNow!" - select CPU_FREQ_TABLE depends on ACPI && ACPI_PROCESSOR && X86_ACPI_CPUFREQ help This adds the CPUFreq driver for K8/early Opteron/Athlon64 processors. @@ -159,7 +153,6 @@ config X86_GX_SUSPMOD config X86_SPEEDSTEP_CENTRINO tristate "Intel Enhanced SpeedStep (deprecated)" - select CPU_FREQ_TABLE select X86_SPEEDSTEP_CENTRINO_TABLE if X86_32 depends on X86_32 || (X86_64 && ACPI_PROCESSOR) help @@ -189,7 +182,6 @@ config X86_SPEEDSTEP_CENTRINO_TABLE config X86_SPEEDSTEP_ICH tristate "Intel Speedstep on ICH-M chipsets (ioport interface)" - select CPU_FREQ_TABLE depends on X86_32 help This adds the CPUFreq driver for certain mobile Intel Pentium III @@ -203,7 +195,6 @@ config X86_SPEEDSTEP_ICH config X86_SPEEDSTEP_SMI tristate "Intel SpeedStep on 440BX/ZX/MX chipsets (SMI interface)" - select CPU_FREQ_TABLE depends on X86_32 help This adds the CPUFreq driver for certain mobile Intel Pentium III @@ -216,7 +207,6 @@ config X86_SPEEDSTEP_SMI config X86_P4_CLOCKMOD tristate "Intel Pentium 4 clock modulation" - select CPU_FREQ_TABLE help This adds the CPUFreq driver for Intel Pentium 4 / XEON processors. When enabled it will lower CPU temperature by skipping @@ -258,7 +248,6 @@ config X86_LONGRUN config X86_LONGHAUL tristate "VIA Cyrix III Longhaul" - select CPU_FREQ_TABLE depends on X86_32 && ACPI_PROCESSOR help This adds the CPUFreq driver for VIA Samuel/CyrixIII, @@ -271,7 +260,6 @@ config X86_LONGHAUL config X86_E_POWERSAVER tristate "VIA C7 Enhanced PowerSaver (DANGEROUS)" - select CPU_FREQ_TABLE depends on X86_32 && ACPI_PROCESSOR help This adds the CPUFreq driver for VIA C7 processors. However, this driver diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index e2603ddd1e7b..2ed1e50615c9 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -1,5 +1,5 @@ # CPUfreq core -obj-$(CONFIG_CPU_FREQ) += cpufreq.o +obj-$(CONFIG_CPU_FREQ) += cpufreq.o freq_table.o # CPUfreq stats obj-$(CONFIG_CPU_FREQ_STAT) += cpufreq_stats.o @@ -13,9 +13,6 @@ obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o obj-$(CONFIG_CPU_BOOST) += cpu-boost.o -# CPUfreq cross-arch helpers -obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o - obj-$(CONFIG_GENERIC_CPUFREQ_CPU0) += cpufreq-cpu0.o ################################################################################## diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index edc089e9d0c4..d26f04e6601e 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -70,6 +70,7 @@ struct acpi_cpufreq_data { struct cpufreq_frequency_table *freq_table; unsigned int resume; unsigned int cpu_feature; + cpumask_var_t freqdomain_cpus; }; static DEFINE_PER_CPU(struct acpi_cpufreq_data *, acfreq_data); @@ -176,6 +177,15 @@ static struct global_attr global_boost = __ATTR(boost, 0644, show_global_boost, store_global_boost); +static ssize_t show_freqdomain_cpus(struct cpufreq_policy *policy, char *buf) +{ + struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu); + + return cpufreq_show_cpus(data->freqdomain_cpus, buf); +} + +cpufreq_freq_attr_ro(freqdomain_cpus); + #ifdef CONFIG_X86_ACPI_CPUFREQ_CPB static ssize_t store_cpb(struct cpufreq_policy *policy, const char *buf, size_t count) @@ -232,7 +242,7 @@ static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data) perf = data->acpi_data; for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { - if (msr == perf->states[data->freq_table[i].index].status) + if (msr == perf->states[data->freq_table[i].driver_data].status) return data->freq_table[i].frequency; } return data->freq_table[0].frequency; @@ -442,7 +452,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, goto out; } - next_perf_state = data->freq_table[next_state].index; + next_perf_state = data->freq_table[next_state].driver_data; if (perf->state == next_perf_state) { if (unlikely(data->resume)) { pr_debug("Called after resume, resetting to P%d\n", @@ -698,10 +708,15 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) return blacklisted; #endif - data = kzalloc(sizeof(struct acpi_cpufreq_data), GFP_KERNEL); + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; + if (!zalloc_cpumask_var(&data->freqdomain_cpus, GFP_KERNEL)) { + result = -ENOMEM; + goto err_free; + } + data->acpi_data = per_cpu_ptr(acpi_perf_data, cpu); per_cpu(acfreq_data, cpu) = data; @@ -710,7 +725,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) result = acpi_processor_register_performance(data->acpi_data, cpu); if (result) - goto err_free; + goto err_free_mask; perf = data->acpi_data; policy->shared_type = perf->shared_type; @@ -723,6 +738,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) { cpumask_copy(policy->cpus, perf->shared_cpu_map); } + cpumask_copy(data->freqdomain_cpus, perf->shared_cpu_map); #ifdef CONFIG_SMP dmi_check_system(sw_any_bug_dmi_table); @@ -734,6 +750,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) if (check_amd_hwpstate_cpu(cpu) && !acpi_pstate_strict) { cpumask_clear(policy->cpus); cpumask_set_cpu(cpu, policy->cpus); + cpumask_copy(data->freqdomain_cpus, cpu_sibling_mask(cpu)); policy->shared_type = CPUFREQ_SHARED_TYPE_HW; pr_info_once(PFX "overriding BIOS provided _PSD data\n"); } @@ -781,7 +798,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) goto err_unreg; } - data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) * + data->freq_table = kmalloc(sizeof(*data->freq_table) * (perf->state_count+1), GFP_KERNEL); if (!data->freq_table) { result = -ENOMEM; @@ -811,7 +828,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) data->freq_table[valid_states-1].frequency / 1000) continue; - data->freq_table[valid_states].index = i; + data->freq_table[valid_states].driver_data = i; data->freq_table[valid_states].frequency = perf->states[i].core_frequency * 1000; valid_states++; @@ -868,6 +885,8 @@ err_freqfree: kfree(data->freq_table); err_unreg: acpi_processor_unregister_performance(perf, cpu); +err_free_mask: + free_cpumask_var(data->freqdomain_cpus); err_free: kfree(data); per_cpu(acfreq_data, cpu) = NULL; @@ -886,6 +905,7 @@ static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy) per_cpu(acfreq_data, policy->cpu) = NULL; acpi_processor_unregister_performance(data->acpi_data, policy->cpu); + free_cpumask_var(data->freqdomain_cpus); kfree(data->freq_table); kfree(data); } @@ -906,6 +926,7 @@ static int acpi_cpufreq_resume(struct cpufreq_policy *policy) static struct freq_attr *acpi_cpufreq_attr[] = { &cpufreq_freq_attr_scaling_available_freqs, + &freqdomain_cpus, NULL, /* this is a placeholder for cpb, do not remove */ NULL, }; @@ -918,7 +939,6 @@ static struct cpufreq_driver acpi_cpufreq_driver = { .exit = acpi_cpufreq_cpu_exit, .resume = acpi_cpufreq_resume, .name = "acpi-cpufreq", - .owner = THIS_MODULE, .attr = acpi_cpufreq_attr, }; @@ -947,7 +967,7 @@ static void __init acpi_cpufreq_boost_init(void) /* We create the boost file in any case, though for systems without * hardware support it will be read-only and hardwired to return 0. */ - if (sysfs_create_file(cpufreq_global_kobject, &(global_boost.attr))) + if (cpufreq_sysfs_create_file(&(global_boost.attr))) pr_warn(PFX "could not register global boost sysfs file\n"); else pr_debug("registered global boost sysfs file\n"); @@ -955,7 +975,7 @@ static void __init acpi_cpufreq_boost_init(void) static void __exit acpi_cpufreq_boost_exit(void) { - sysfs_remove_file(cpufreq_global_kobject, &(global_boost.attr)); + cpufreq_sysfs_remove_file(&(global_boost.attr)); if (msrs) { unregister_cpu_notifier(&boost_nb); diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index 5d7f53fcd6f5..5519933813ea 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -24,112 +24,323 @@ #include <linux/cpufreq.h> #include <linux/cpumask.h> #include <linux/export.h> +#include <linux/mutex.h> #include <linux/of_platform.h> -#include <linux/opp.h> +#include <linux/pm_opp.h> #include <linux/slab.h> #include <linux/topology.h> #include <linux/types.h> +#include <asm/bL_switcher.h> #include "arm_big_little.h" /* Currently we support only two clusters */ +#define A15_CLUSTER 0 +#define A7_CLUSTER 1 #define MAX_CLUSTERS 2 +#ifdef CONFIG_BL_SWITCHER +static bool bL_switching_enabled; +#define is_bL_switching_enabled() bL_switching_enabled +#define set_switching_enabled(x) (bL_switching_enabled = (x)) +#else +#define is_bL_switching_enabled() false +#define set_switching_enabled(x) do { } while (0) +#endif + +#define ACTUAL_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq << 1 : freq) +#define VIRT_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq >> 1 : freq) + static struct cpufreq_arm_bL_ops *arm_bL_ops; static struct clk *clk[MAX_CLUSTERS]; -static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS]; -static atomic_t cluster_usage[MAX_CLUSTERS] = {ATOMIC_INIT(0), ATOMIC_INIT(0)}; +static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS + 1]; +static atomic_t cluster_usage[MAX_CLUSTERS + 1]; + +static unsigned int clk_big_min; /* (Big) clock frequencies */ +static unsigned int clk_little_max; /* Maximum clock frequency (Little) */ + +static DEFINE_PER_CPU(unsigned int, physical_cluster); +static DEFINE_PER_CPU(unsigned int, cpu_last_req_freq); -static unsigned int bL_cpufreq_get(unsigned int cpu) +static struct mutex cluster_lock[MAX_CLUSTERS]; + +static inline int raw_cpu_to_cluster(int cpu) { - u32 cur_cluster = cpu_to_cluster(cpu); + return topology_physical_package_id(cpu); +} - return clk_get_rate(clk[cur_cluster]) / 1000; +static inline int cpu_to_cluster(int cpu) +{ + return is_bL_switching_enabled() ? + MAX_CLUSTERS : raw_cpu_to_cluster(cpu); } -/* Validate policy frequency range */ -static int bL_cpufreq_verify_policy(struct cpufreq_policy *policy) +static unsigned int find_cluster_maxfreq(int cluster) { - u32 cur_cluster = cpu_to_cluster(policy->cpu); + int j; + u32 max_freq = 0, cpu_freq; + + for_each_online_cpu(j) { + cpu_freq = per_cpu(cpu_last_req_freq, j); + + if ((cluster == per_cpu(physical_cluster, j)) && + (max_freq < cpu_freq)) + max_freq = cpu_freq; + } + + pr_debug("%s: cluster: %d, max freq: %d\n", __func__, cluster, + max_freq); + + return max_freq; +} + +static unsigned int clk_get_cpu_rate(unsigned int cpu) +{ + u32 cur_cluster = per_cpu(physical_cluster, cpu); + u32 rate = clk_get_rate(clk[cur_cluster]) / 1000; + + /* For switcher we use virtual A7 clock rates */ + if (is_bL_switching_enabled()) + rate = VIRT_FREQ(cur_cluster, rate); + + pr_debug("%s: cpu: %d, cluster: %d, freq: %u\n", __func__, cpu, + cur_cluster, rate); + + return rate; +} + +static unsigned int bL_cpufreq_get_rate(unsigned int cpu) +{ + if (is_bL_switching_enabled()) { + pr_debug("%s: freq: %d\n", __func__, per_cpu(cpu_last_req_freq, + cpu)); + + return per_cpu(cpu_last_req_freq, cpu); + } else { + return clk_get_cpu_rate(cpu); + } +} + +static unsigned int +bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate) +{ + u32 new_rate, prev_rate; + int ret; + bool bLs = is_bL_switching_enabled(); + + mutex_lock(&cluster_lock[new_cluster]); - return cpufreq_frequency_table_verify(policy, freq_table[cur_cluster]); + if (bLs) { + prev_rate = per_cpu(cpu_last_req_freq, cpu); + per_cpu(cpu_last_req_freq, cpu) = rate; + per_cpu(physical_cluster, cpu) = new_cluster; + + new_rate = find_cluster_maxfreq(new_cluster); + new_rate = ACTUAL_FREQ(new_cluster, new_rate); + } else { + new_rate = rate; + } + + pr_debug("%s: cpu: %d, old cluster: %d, new cluster: %d, freq: %d\n", + __func__, cpu, old_cluster, new_cluster, new_rate); + + ret = clk_set_rate(clk[new_cluster], new_rate * 1000); + if (WARN_ON(ret)) { + pr_err("clk_set_rate failed: %d, new cluster: %d\n", ret, + new_cluster); + if (bLs) { + per_cpu(cpu_last_req_freq, cpu) = prev_rate; + per_cpu(physical_cluster, cpu) = old_cluster; + } + + mutex_unlock(&cluster_lock[new_cluster]); + + return ret; + } + + mutex_unlock(&cluster_lock[new_cluster]); + + /* Recalc freq for old cluster when switching clusters */ + if (old_cluster != new_cluster) { + pr_debug("%s: cpu: %d, old cluster: %d, new cluster: %d\n", + __func__, cpu, old_cluster, new_cluster); + + /* Switch cluster */ + bL_switch_request(cpu, new_cluster); + + mutex_lock(&cluster_lock[old_cluster]); + + /* Set freq of old cluster if there are cpus left on it */ + new_rate = find_cluster_maxfreq(old_cluster); + new_rate = ACTUAL_FREQ(old_cluster, new_rate); + + if (new_rate) { + pr_debug("%s: Updating rate of old cluster: %d, to freq: %d\n", + __func__, old_cluster, new_rate); + + if (clk_set_rate(clk[old_cluster], new_rate * 1000)) + pr_err("%s: clk_set_rate failed: %d, old cluster: %d\n", + __func__, ret, old_cluster); + } + mutex_unlock(&cluster_lock[old_cluster]); + } + + return 0; } /* Set clock frequency */ static int bL_cpufreq_set_target(struct cpufreq_policy *policy, - unsigned int target_freq, unsigned int relation) + unsigned int index) { - struct cpufreq_freqs freqs; - u32 cpu = policy->cpu, freq_tab_idx, cur_cluster; - int ret = 0; + u32 cpu = policy->cpu, cur_cluster, new_cluster, actual_cluster; + unsigned int freqs_new; + + cur_cluster = cpu_to_cluster(cpu); + new_cluster = actual_cluster = per_cpu(physical_cluster, cpu); + + freqs_new = freq_table[cur_cluster][index].frequency; + + if (is_bL_switching_enabled()) { + if ((actual_cluster == A15_CLUSTER) && + (freqs_new < clk_big_min)) { + new_cluster = A7_CLUSTER; + } else if ((actual_cluster == A7_CLUSTER) && + (freqs_new > clk_little_max)) { + new_cluster = A15_CLUSTER; + } + } - cur_cluster = cpu_to_cluster(policy->cpu); + return bL_cpufreq_set_rate(cpu, actual_cluster, new_cluster, freqs_new); +} - freqs.old = bL_cpufreq_get(policy->cpu); +static inline u32 get_table_count(struct cpufreq_frequency_table *table) +{ + int count; - /* Determine valid target frequency using freq_table */ - cpufreq_frequency_table_target(policy, freq_table[cur_cluster], - target_freq, relation, &freq_tab_idx); - freqs.new = freq_table[cur_cluster][freq_tab_idx].frequency; + for (count = 0; table[count].frequency != CPUFREQ_TABLE_END; count++) + ; - pr_debug("%s: cpu: %d, cluster: %d, oldfreq: %d, target freq: %d, new freq: %d\n", - __func__, cpu, cur_cluster, freqs.old, target_freq, - freqs.new); + return count; +} - if (freqs.old == freqs.new) - return 0; +/* get the minimum frequency in the cpufreq_frequency_table */ +static inline u32 get_table_min(struct cpufreq_frequency_table *table) +{ + int i; + uint32_t min_freq = ~0; + for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) + if (table[i].frequency < min_freq) + min_freq = table[i].frequency; + return min_freq; +} - cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); +/* get the maximum frequency in the cpufreq_frequency_table */ +static inline u32 get_table_max(struct cpufreq_frequency_table *table) +{ + int i; + uint32_t max_freq = 0; + for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) + if (table[i].frequency > max_freq) + max_freq = table[i].frequency; + return max_freq; +} - ret = clk_set_rate(clk[cur_cluster], freqs.new * 1000); - if (ret) { - pr_err("clk_set_rate failed: %d\n", ret); - return ret; +static int merge_cluster_tables(void) +{ + int i, j, k = 0, count = 1; + struct cpufreq_frequency_table *table; + + for (i = 0; i < MAX_CLUSTERS; i++) + count += get_table_count(freq_table[i]); + + table = kzalloc(sizeof(*table) * count, GFP_KERNEL); + if (!table) + return -ENOMEM; + + freq_table[MAX_CLUSTERS] = table; + + /* Add in reverse order to get freqs in increasing order */ + for (i = MAX_CLUSTERS - 1; i >= 0; i--) { + for (j = 0; freq_table[i][j].frequency != CPUFREQ_TABLE_END; + j++) { + table[k].frequency = VIRT_FREQ(i, + freq_table[i][j].frequency); + pr_debug("%s: index: %d, freq: %d\n", __func__, k, + table[k].frequency); + k++; + } } - policy->cur = freqs.new; + table[k].driver_data = k; + table[k].frequency = CPUFREQ_TABLE_END; - cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); + pr_debug("%s: End, table: %p, count: %d\n", __func__, table, k); - return ret; + return 0; +} + +static void _put_cluster_clk_and_freq_table(struct device *cpu_dev) +{ + u32 cluster = raw_cpu_to_cluster(cpu_dev->id); + + if (!freq_table[cluster]) + return; + + clk_put(clk[cluster]); + dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]); + dev_dbg(cpu_dev, "%s: cluster: %d\n", __func__, cluster); } static void put_cluster_clk_and_freq_table(struct device *cpu_dev) { u32 cluster = cpu_to_cluster(cpu_dev->id); + int i; + + if (atomic_dec_return(&cluster_usage[cluster])) + return; + + if (cluster < MAX_CLUSTERS) + return _put_cluster_clk_and_freq_table(cpu_dev); - if (!atomic_dec_return(&cluster_usage[cluster])) { - clk_put(clk[cluster]); - opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]); - dev_dbg(cpu_dev, "%s: cluster: %d\n", __func__, cluster); + for_each_present_cpu(i) { + struct device *cdev = get_cpu_device(i); + if (!cdev) { + pr_err("%s: failed to get cpu%d device\n", __func__, i); + return; + } + + _put_cluster_clk_and_freq_table(cdev); } + + /* free virtual table */ + kfree(freq_table[cluster]); } -static int get_cluster_clk_and_freq_table(struct device *cpu_dev) +static int _get_cluster_clk_and_freq_table(struct device *cpu_dev) { - u32 cluster = cpu_to_cluster(cpu_dev->id); + u32 cluster = raw_cpu_to_cluster(cpu_dev->id); char name[14] = "cpu-cluster."; int ret; - if (atomic_inc_return(&cluster_usage[cluster]) != 1) + if (freq_table[cluster]) return 0; ret = arm_bL_ops->init_opp_table(cpu_dev); if (ret) { dev_err(cpu_dev, "%s: init_opp_table failed, cpu: %d, err: %d\n", __func__, cpu_dev->id, ret); - goto atomic_dec; + goto out; } - ret = opp_init_cpufreq_table(cpu_dev, &freq_table[cluster]); + ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table[cluster]); if (ret) { dev_err(cpu_dev, "%s: failed to init cpufreq table, cpu: %d, err: %d\n", __func__, cpu_dev->id, ret); - goto atomic_dec; + goto out; } name[12] = cluster + '0'; - clk[cluster] = clk_get_sys(name, NULL); + clk[cluster] = clk_get(cpu_dev, name); if (!IS_ERR(clk[cluster])) { dev_dbg(cpu_dev, "%s: clk: %p & freq table: %p, cluster: %d\n", __func__, clk[cluster], freq_table[cluster], @@ -140,15 +351,74 @@ static int get_cluster_clk_and_freq_table(struct device *cpu_dev) dev_err(cpu_dev, "%s: Failed to get clk for cpu: %d, cluster: %d\n", __func__, cpu_dev->id, cluster); ret = PTR_ERR(clk[cluster]); - opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]); + dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]); -atomic_dec: - atomic_dec(&cluster_usage[cluster]); +out: dev_err(cpu_dev, "%s: Failed to get data for cluster: %d\n", __func__, cluster); return ret; } +static int get_cluster_clk_and_freq_table(struct device *cpu_dev) +{ + u32 cluster = cpu_to_cluster(cpu_dev->id); + int i, ret; + + if (atomic_inc_return(&cluster_usage[cluster]) != 1) + return 0; + + if (cluster < MAX_CLUSTERS) { + ret = _get_cluster_clk_and_freq_table(cpu_dev); + if (ret) + atomic_dec(&cluster_usage[cluster]); + return ret; + } + + /* + * Get data for all clusters and fill virtual cluster with a merge of + * both + */ + for_each_present_cpu(i) { + struct device *cdev = get_cpu_device(i); + if (!cdev) { + pr_err("%s: failed to get cpu%d device\n", __func__, i); + return -ENODEV; + } + + ret = _get_cluster_clk_and_freq_table(cdev); + if (ret) + goto put_clusters; + } + + ret = merge_cluster_tables(); + if (ret) + goto put_clusters; + + /* Assuming 2 cluster, set clk_big_min and clk_little_max */ + clk_big_min = get_table_min(freq_table[0]); + clk_little_max = VIRT_FREQ(1, get_table_max(freq_table[1])); + + pr_debug("%s: cluster: %d, clk_big_min: %d, clk_little_max: %d\n", + __func__, cluster, clk_big_min, clk_little_max); + + return 0; + +put_clusters: + for_each_present_cpu(i) { + struct device *cdev = get_cpu_device(i); + if (!cdev) { + pr_err("%s: failed to get cpu%d device\n", __func__, i); + return -ENODEV; + } + + _put_cluster_clk_and_freq_table(cdev); + } + + atomic_dec(&cluster_usage[cluster]); + + return ret; +} + /* Per-CPU initialization */ static int bL_cpufreq_init(struct cpufreq_policy *policy) { @@ -167,7 +437,7 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy) if (ret) return ret; - ret = cpufreq_frequency_table_cpuinfo(policy, freq_table[cur_cluster]); + ret = cpufreq_table_validate_and_show(policy, freq_table[cur_cluster]); if (ret) { dev_err(cpu_dev, "CPU %d, cluster: %d invalid freq table\n", policy->cpu, cur_cluster); @@ -175,7 +445,14 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy) return ret; } - cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu); + if (cur_cluster < MAX_CLUSTERS) { + cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); + + per_cpu(physical_cluster, policy->cpu) = cur_cluster; + } else { + /* Assumption: during init, we are always running on A15 */ + per_cpu(physical_cluster, policy->cpu) = A15_CLUSTER; + } if (arm_bL_ops->get_transition_latency) policy->cpuinfo.transition_latency = @@ -183,9 +460,8 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy) else policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; - policy->cur = bL_cpufreq_get(policy->cpu); - - cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); + if (is_bL_switching_enabled()) + per_cpu(cpu_last_req_freq, policy->cpu) = clk_get_cpu_rate(policy->cpu); dev_info(cpu_dev, "%s: CPU %d initialized\n", __func__, policy->cpu); return 0; @@ -202,33 +478,60 @@ static int bL_cpufreq_exit(struct cpufreq_policy *policy) return -ENODEV; } + cpufreq_frequency_table_put_attr(policy->cpu); put_cluster_clk_and_freq_table(cpu_dev); dev_dbg(cpu_dev, "%s: Exited, cpu: %d\n", __func__, policy->cpu); return 0; } -/* Export freq_table to sysfs */ -static struct freq_attr *bL_cpufreq_attr[] = { - &cpufreq_freq_attr_scaling_available_freqs, - NULL, -}; - static struct cpufreq_driver bL_cpufreq_driver = { .name = "arm-big-little", - .flags = CPUFREQ_STICKY, - .verify = bL_cpufreq_verify_policy, - .target = bL_cpufreq_set_target, - .get = bL_cpufreq_get, + .flags = CPUFREQ_STICKY | + CPUFREQ_HAVE_GOVERNOR_PER_POLICY, + .verify = cpufreq_generic_frequency_table_verify, + .target_index = bL_cpufreq_set_target, + .get = bL_cpufreq_get_rate, .init = bL_cpufreq_init, .exit = bL_cpufreq_exit, - .have_governor_per_policy = true, - .attr = bL_cpufreq_attr, + .attr = cpufreq_generic_attr, +}; + +static int bL_cpufreq_switcher_notifier(struct notifier_block *nfb, + unsigned long action, void *_arg) +{ + pr_debug("%s: action: %ld\n", __func__, action); + + switch (action) { + case BL_NOTIFY_PRE_ENABLE: + case BL_NOTIFY_PRE_DISABLE: + cpufreq_unregister_driver(&bL_cpufreq_driver); + break; + + case BL_NOTIFY_POST_ENABLE: + set_switching_enabled(true); + cpufreq_register_driver(&bL_cpufreq_driver); + break; + + case BL_NOTIFY_POST_DISABLE: + set_switching_enabled(false); + cpufreq_register_driver(&bL_cpufreq_driver); + break; + + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static struct notifier_block bL_switcher_notifier = { + .notifier_call = bL_cpufreq_switcher_notifier, }; int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops) { - int ret; + int ret, i; if (arm_bL_ops) { pr_debug("%s: Already registered: %s, exiting\n", __func__, @@ -243,16 +546,29 @@ int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops) arm_bL_ops = ops; + ret = bL_switcher_get_enabled(); + set_switching_enabled(ret); + + for (i = 0; i < MAX_CLUSTERS; i++) + mutex_init(&cluster_lock[i]); + ret = cpufreq_register_driver(&bL_cpufreq_driver); if (ret) { pr_info("%s: Failed registering platform driver: %s, err: %d\n", __func__, ops->name, ret); arm_bL_ops = NULL; } else { - pr_info("%s: Registered platform driver: %s\n", __func__, - ops->name); + ret = bL_switcher_register_notifier(&bL_switcher_notifier); + if (ret) { + cpufreq_unregister_driver(&bL_cpufreq_driver); + arm_bL_ops = NULL; + } else { + pr_info("%s: Registered platform driver: %s\n", + __func__, ops->name); + } } + bL_switcher_put_enabled(); return ret; } EXPORT_SYMBOL_GPL(bL_cpufreq_register); @@ -265,7 +581,10 @@ void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops) return; } + bL_switcher_get_enabled(); + bL_switcher_unregister_notifier(&bL_switcher_notifier); cpufreq_unregister_driver(&bL_cpufreq_driver); + bL_switcher_put_enabled(); pr_info("%s: Un-registered platform driver: %s\n", __func__, arm_bL_ops->name); arm_bL_ops = NULL; diff --git a/drivers/cpufreq/arm_big_little.h b/drivers/cpufreq/arm_big_little.h index 79b2ce17884d..70f18fc12d4a 100644 --- a/drivers/cpufreq/arm_big_little.h +++ b/drivers/cpufreq/arm_big_little.h @@ -34,11 +34,6 @@ struct cpufreq_arm_bL_ops { int (*init_opp_table)(struct device *cpu_dev); }; -static inline int cpu_to_cluster(int cpu) -{ - return topology_physical_package_id(cpu); -} - int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops); void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops); diff --git a/drivers/cpufreq/arm_big_little_dt.c b/drivers/cpufreq/arm_big_little_dt.c index fd9e3ea6a480..8d9d59108906 100644 --- a/drivers/cpufreq/arm_big_little_dt.c +++ b/drivers/cpufreq/arm_big_little_dt.c @@ -19,13 +19,12 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/cpu.h> #include <linux/cpufreq.h> #include <linux/device.h> #include <linux/export.h> #include <linux/module.h> -#include <linux/of.h> -#include <linux/opp.h> +#include <linux/of_device.h> +#include <linux/pm_opp.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/types.h> @@ -34,27 +33,13 @@ /* get cpu node with valid operating-points */ static struct device_node *get_cpu_node_with_valid_op(int cpu) { - struct device_node *np = NULL, *parent; - int count = 0; + struct device_node *np = of_cpu_device_node_get(cpu); - parent = of_find_node_by_path("/cpus"); - if (!parent) { - pr_err("failed to find OF /cpus\n"); - return NULL; + if (!of_get_property(np, "operating-points", NULL)) { + of_node_put(np); + np = NULL; } - for_each_child_of_node(parent, np) { - if (count++ != cpu) - continue; - if (!of_get_property(np, "operating-points", NULL)) { - of_node_put(np); - np = NULL; - } - - break; - } - - of_node_put(parent); return np; } @@ -63,11 +48,12 @@ static int dt_init_opp_table(struct device *cpu_dev) struct device_node *np; int ret; - np = get_cpu_node_with_valid_op(cpu_dev->id); - if (!np) - return -ENODATA; + np = of_node_get(cpu_dev->of_node); + if (!np) { + pr_err("failed to find cpu%d node\n", cpu_dev->id); + return -ENOENT; + } - cpu_dev->of_node = np; ret = of_init_opp_table(cpu_dev); of_node_put(np); @@ -79,9 +65,11 @@ static int dt_get_transition_latency(struct device *cpu_dev) struct device_node *np; u32 transition_latency = CPUFREQ_ETERNAL; - np = get_cpu_node_with_valid_op(cpu_dev->id); - if (!np) + np = of_node_get(cpu_dev->of_node); + if (!np) { + pr_info("Failed to find cpu node. Use CPUFREQ_ETERNAL transition latency\n"); return CPUFREQ_ETERNAL; + } of_property_read_u32(np, "clock-latency", &transition_latency); of_node_put(np); diff --git a/drivers/cpufreq/at32ap-cpufreq.c b/drivers/cpufreq/at32ap-cpufreq.c index 654488723cb5..e0c38d938997 100644 --- a/drivers/cpufreq/at32ap-cpufreq.c +++ b/drivers/cpufreq/at32ap-cpufreq.c @@ -108,7 +108,6 @@ static int __init at32_cpufreq_driver_init(struct cpufreq_policy *policy) static struct cpufreq_driver at32_driver = { .name = "at32ap", - .owner = THIS_MODULE, .init = at32_cpufreq_driver_init, .verify = at32_verify_speed, .target = at32_set_target, diff --git a/drivers/cpufreq/blackfin-cpufreq.c b/drivers/cpufreq/blackfin-cpufreq.c index 995511e80bef..ef05978a7237 100644 --- a/drivers/cpufreq/blackfin-cpufreq.c +++ b/drivers/cpufreq/blackfin-cpufreq.c @@ -20,23 +20,23 @@ /* this is the table of CCLK frequencies, in Hz */ -/* .index is the entry in the auxiliary dpm_state_table[] */ +/* .driver_data is the entry in the auxiliary dpm_state_table[] */ static struct cpufreq_frequency_table bfin_freq_table[] = { { .frequency = CPUFREQ_TABLE_END, - .index = 0, + .driver_data = 0, }, { .frequency = CPUFREQ_TABLE_END, - .index = 1, + .driver_data = 1, }, { .frequency = CPUFREQ_TABLE_END, - .index = 2, + .driver_data = 2, }, { .frequency = CPUFREQ_TABLE_END, - .index = 0, + .driver_data = 0, }, }; @@ -225,7 +225,6 @@ static struct cpufreq_driver bfin_driver = { .get = bfin_getfreq_khz, .init = __bfin_cpu_init, .name = "bfin cpufreq", - .owner = THIS_MODULE, .attr = bfin_freq_attr, }; diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index ad1fde277661..16d3979c485e 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -16,7 +16,7 @@ #include <linux/err.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/opp.h> +#include <linux/pm_opp.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> @@ -71,7 +71,7 @@ static int cpu0_set_target(struct cpufreq_policy *policy, if (cpu_reg) { rcu_read_lock(); - opp = opp_find_freq_ceil(cpu_dev, &freq_Hz); + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); if (IS_ERR(opp)) { rcu_read_unlock(); pr_err("failed to find OPP for %ld\n", freq_Hz); @@ -79,7 +79,7 @@ static int cpu0_set_target(struct cpufreq_policy *policy, ret = PTR_ERR(opp); goto post_notify; } - volt = opp_get_voltage(opp); + volt = dev_pm_opp_get_voltage(opp); rcu_read_unlock(); tol = volt * voltage_tolerance / 100; volt_old = regulator_get_voltage(cpu_reg); @@ -226,7 +226,7 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) goto out_put_node; } - ret = opp_init_cpufreq_table(cpu_dev, &freq_table); + ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); if (ret) { pr_err("failed to init cpufreq table: %d\n", ret); goto out_put_node; @@ -250,12 +250,12 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) ; rcu_read_lock(); - opp = opp_find_freq_exact(cpu_dev, + opp = dev_pm_opp_find_freq_exact(cpu_dev, freq_table[0].frequency * 1000, true); - min_uV = opp_get_voltage(opp); - opp = opp_find_freq_exact(cpu_dev, + min_uV = dev_pm_opp_get_voltage(opp); + opp = dev_pm_opp_find_freq_exact(cpu_dev, freq_table[i-1].frequency * 1000, true); - max_uV = opp_get_voltage(opp); + max_uV = dev_pm_opp_get_voltage(opp); rcu_read_unlock(); ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV); if (ret > 0) @@ -273,7 +273,7 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev) return 0; out_free_table: - opp_free_cpufreq_table(cpu_dev, &freq_table); + dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); out_put_node: of_node_put(np); out_put_parent: @@ -284,7 +284,7 @@ out_put_parent: static int cpu0_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&cpu0_cpufreq_driver); - opp_free_cpufreq_table(cpu_dev, &freq_table); + dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); return 0; } diff --git a/drivers/cpufreq/cpufreq-nforce2.c b/drivers/cpufreq/cpufreq-nforce2.c index af1542d41440..56c964c16a66 100644 --- a/drivers/cpufreq/cpufreq-nforce2.c +++ b/drivers/cpufreq/cpufreq-nforce2.c @@ -303,9 +303,7 @@ static int nforce2_verify(struct cpufreq_policy *policy) if (policy->min < (fsb_pol_max * fid * 100)) policy->max = (fsb_pol_max + 1) * fid * 100; - cpufreq_verify_within_limits(policy, - policy->cpuinfo.min_freq, - policy->cpuinfo.max_freq); + cpufreq_verify_within_cpu_limits(policy); return 0; } @@ -379,7 +377,6 @@ static struct cpufreq_driver nforce2_driver = { .get = nforce2_get, .init = nforce2_cpu_init, .exit = nforce2_cpu_exit, - .owner = THIS_MODULE, }; #ifdef MODULE diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 9397798e5586..2f5134212abe 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -3,6 +3,7 @@ * * Copyright (C) 2001 Russell King * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> + * (C) 2013 Viresh Kumar <viresh.kumar@linaro.org> * * Oct 2005 - Ashok Raj <ashok.raj@intel.com> * Added handling for CPU hotplug @@ -12,26 +13,21 @@ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/notifier.h> +#include <linux/cpu.h> #include <linux/cpufreq.h> #include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/spinlock.h> #include <linux/device.h> -#include <linux/slab.h> -#include <linux/cpu.h> -#include <linux/completion.h> +#include <linux/init.h> +#include <linux/kernel_stat.h> +#include <linux/module.h> #include <linux/mutex.h> +#include <linux/slab.h> #include <linux/syscore_ops.h> - +#include <linux/tick.h> #include <trace/events/power.h> /** @@ -41,6 +37,11 @@ */ static struct cpufreq_driver *cpufreq_driver; static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data); +static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data_fallback); +static DEFINE_RWLOCK(cpufreq_driver_lock); +static DEFINE_MUTEX(cpufreq_governor_lock); +static LIST_HEAD(cpufreq_policy_list); + #ifdef CONFIG_HOTPLUG_CPU /* * This one keeps track of the previously set governor and user-set @@ -52,52 +53,17 @@ struct cpufreq_cpu_save_data { }; static DEFINE_PER_CPU(struct cpufreq_cpu_save_data, cpufreq_policy_save); #endif -static DEFINE_RWLOCK(cpufreq_driver_lock); -static DEFINE_MUTEX(cpufreq_governor_lock); -/* - * cpu_policy_rwsem is a per CPU reader-writer semaphore designed to cure - * all cpufreq/hotplug/workqueue/etc related lock issues. - * - * The rules for this semaphore: - * - Any routine that wants to read from the policy structure will - * do a down_read on this semaphore. - * - Any routine that will write to the policy structure and/or may take away - * the policy altogether (eg. CPU hotplug), will hold this lock in write - * mode before doing so. - * - * Additional rules: - * - Governor routines that can be called in cpufreq hotplug path should not - * take this sem as top level hotplug notifier handler takes this. - * - Lock should not be held across - * __cpufreq_governor(data, CPUFREQ_GOV_STOP); - */ -static DEFINE_PER_CPU(int, cpufreq_policy_cpu); -static DEFINE_PER_CPU(struct rw_semaphore, cpu_policy_rwsem); - -#define lock_policy_rwsem(mode, cpu) \ -static int lock_policy_rwsem_##mode(int cpu) \ -{ \ - int policy_cpu = per_cpu(cpufreq_policy_cpu, cpu); \ - BUG_ON(policy_cpu == -1); \ - down_##mode(&per_cpu(cpu_policy_rwsem, policy_cpu)); \ - \ - return 0; \ -} - -lock_policy_rwsem(read, cpu); -lock_policy_rwsem(write, cpu); - -#define unlock_policy_rwsem(mode, cpu) \ -static void unlock_policy_rwsem_##mode(int cpu) \ -{ \ - int policy_cpu = per_cpu(cpufreq_policy_cpu, cpu); \ - BUG_ON(policy_cpu == -1); \ - up_##mode(&per_cpu(cpu_policy_rwsem, policy_cpu)); \ +static inline bool has_target(void) +{ + return cpufreq_driver->target_index || cpufreq_driver->target; } -unlock_policy_rwsem(read, cpu); -unlock_policy_rwsem(write, cpu); +/* + * rwsem to guarantee that cpufreq driver module doesn't unload during critical + * sections + */ +static DECLARE_RWSEM(cpufreq_rwsem); /* internal prototypes */ static int __cpufreq_governor(struct cpufreq_policy *policy, @@ -138,82 +104,125 @@ static DEFINE_MUTEX(cpufreq_governor_mutex); bool have_governor_per_policy(void) { - return cpufreq_driver->have_governor_per_policy; + return !!(cpufreq_driver->flags & CPUFREQ_HAVE_GOVERNOR_PER_POLICY); } +EXPORT_SYMBOL_GPL(have_governor_per_policy); -static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, bool sysfs) +struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy) { - struct cpufreq_policy *data; - unsigned long flags; + if (have_governor_per_policy()) + return &policy->kobj; + else + return cpufreq_global_kobject; +} +EXPORT_SYMBOL_GPL(get_governor_parent_kobj); - if (cpu >= nr_cpu_ids) - goto err_out; +static inline u64 get_cpu_idle_time_jiffy(unsigned int cpu, u64 *wall) +{ + u64 idle_time; + u64 cur_wall_time; + u64 busy_time; - /* get the cpufreq driver */ - read_lock_irqsave(&cpufreq_driver_lock, flags); + cur_wall_time = jiffies64_to_cputime64(get_jiffies_64()); - if (!cpufreq_driver) - goto err_out_unlock; + busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE]; - if (!try_module_get(cpufreq_driver->owner)) - goto err_out_unlock; + idle_time = cur_wall_time - busy_time; + if (wall) + *wall = cputime_to_usecs(cur_wall_time); + return cputime_to_usecs(idle_time); +} - /* get the CPU */ - data = per_cpu(cpufreq_cpu_data, cpu); +u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy) +{ + u64 idle_time = get_cpu_idle_time_us(cpu, io_busy ? wall : NULL); - if (!data) - goto err_out_put_module; + if (idle_time == -1ULL) + return get_cpu_idle_time_jiffy(cpu, wall); + else if (!io_busy) + idle_time += get_cpu_iowait_time_us(cpu, wall); - if (!sysfs && !kobject_get(&data->kobj)) - goto err_out_put_module; + return idle_time; +} +EXPORT_SYMBOL_GPL(get_cpu_idle_time); - read_unlock_irqrestore(&cpufreq_driver_lock, flags); - return data; +/* + * This is a generic cpufreq init() routine which can be used by cpufreq + * drivers of SMP systems. It will do following: + * - validate & show freq table passed + * - set policies transition latency + * - policy->cpus with all possible CPUs + */ +int cpufreq_generic_init(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table, + unsigned int transition_latency) +{ + int ret; -err_out_put_module: - module_put(cpufreq_driver->owner); -err_out_unlock: - read_unlock_irqrestore(&cpufreq_driver_lock, flags); -err_out: - return NULL; + ret = cpufreq_table_validate_and_show(policy, table); + if (ret) { + pr_err("%s: invalid frequency table: %d\n", __func__, ret); + return ret; + } + + policy->cpuinfo.transition_latency = transition_latency; + + /* + * The driver only supports the SMP configuartion where all processors + * share the clock and voltage and clock. + */ + cpumask_setall(policy->cpus); + + return 0; } +EXPORT_SYMBOL_GPL(cpufreq_generic_init); struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) { - if (cpufreq_disabled()) + struct cpufreq_policy *policy = NULL; + unsigned long flags; + + if (cpufreq_disabled() || (cpu >= nr_cpu_ids)) return NULL; - return __cpufreq_cpu_get(cpu, false); -} -EXPORT_SYMBOL_GPL(cpufreq_cpu_get); + if (!down_read_trylock(&cpufreq_rwsem)) + return NULL; -static struct cpufreq_policy *cpufreq_cpu_get_sysfs(unsigned int cpu) -{ - return __cpufreq_cpu_get(cpu, true); -} + /* get the cpufreq driver */ + read_lock_irqsave(&cpufreq_driver_lock, flags); -static void __cpufreq_cpu_put(struct cpufreq_policy *data, bool sysfs) -{ - if (!sysfs) - kobject_put(&data->kobj); - module_put(cpufreq_driver->owner); + if (cpufreq_driver) { + /* get the CPU */ + policy = per_cpu(cpufreq_cpu_data, cpu); + if (policy) + kobject_get(&policy->kobj); + } + + read_unlock_irqrestore(&cpufreq_driver_lock, flags); + + if (!policy) + up_read(&cpufreq_rwsem); + + return policy; } +EXPORT_SYMBOL_GPL(cpufreq_cpu_get); -void cpufreq_cpu_put(struct cpufreq_policy *data) +void cpufreq_cpu_put(struct cpufreq_policy *policy) { if (cpufreq_disabled()) return; - __cpufreq_cpu_put(data, false); + kobject_put(&policy->kobj); + up_read(&cpufreq_rwsem); } EXPORT_SYMBOL_GPL(cpufreq_cpu_put); -static void cpufreq_cpu_put_sysfs(struct cpufreq_policy *data) -{ - __cpufreq_cpu_put(data, true); -} - /********************************************************************* * EXTERNALLY AFFECTING FREQUENCY CHANGES * *********************************************************************/ @@ -228,7 +237,7 @@ static void cpufreq_cpu_put_sysfs(struct cpufreq_policy *data) */ #ifndef CONFIG_SMP static unsigned long l_p_j_ref; -static unsigned int l_p_j_ref_freq; +static unsigned int l_p_j_ref_freq; static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) { @@ -241,7 +250,7 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) pr_debug("saving %lu as reference value for loops_per_jiffy; " "freq is %u kHz\n", l_p_j_ref, l_p_j_ref_freq); } - if ((val == CPUFREQ_POSTCHANGE && ci->old != ci->new) || + if ((val == CPUFREQ_POSTCHANGE && ci->old != ci->new) || (val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) { loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new); @@ -256,8 +265,7 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) } #endif - -void __cpufreq_notify_transition(struct cpufreq_policy *policy, +static void __cpufreq_notify_transition(struct cpufreq_policy *policy, struct cpufreq_freqs *freqs, unsigned int state) { BUG_ON(irqs_disabled()); @@ -304,6 +312,7 @@ void __cpufreq_notify_transition(struct cpufreq_policy *policy, break; } } + /** * cpufreq_notify_transition - call notifier chain and adjust_jiffies * on frequency transition. @@ -321,7 +330,6 @@ void cpufreq_notify_transition(struct cpufreq_policy *policy, EXPORT_SYMBOL_GPL(cpufreq_notify_transition); - /********************************************************************* * SYSFS INTERFACE * *********************************************************************/ @@ -357,7 +365,7 @@ static int cpufreq_parse_governor(char *str_governor, unsigned int *policy, *policy = CPUFREQ_POLICY_POWERSAVE; err = 0; } - } else if (cpufreq_driver->target) { + } else if (has_target()) { struct cpufreq_governor *t; mutex_lock(&cpufreq_governor_mutex); @@ -386,7 +394,6 @@ out: return err; } - /** * cpufreq_per_cpu_attr_read() / show_##file_name() - * print out cpufreq information @@ -409,8 +416,8 @@ show_one(scaling_min_freq, min); show_one(scaling_max_freq, max); show_one(scaling_cur_freq, cur); -static int __cpufreq_set_policy(struct cpufreq_policy *data, - struct cpufreq_policy *policy); +static int cpufreq_set_policy(struct cpufreq_policy *policy, + struct cpufreq_policy *new_policy); /** * cpufreq_per_cpu_attr_write() / store_##file_name() - sysfs write access @@ -419,7 +426,7 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, static ssize_t store_##file_name \ (struct cpufreq_policy *policy, const char *buf, size_t count) \ { \ - unsigned int ret; \ + int ret; \ struct cpufreq_policy new_policy; \ \ ret = cpufreq_get_policy(&new_policy, policy->cpu); \ @@ -435,7 +442,7 @@ static ssize_t store_##file_name \ pr_err("cpufreq: Frequency verification failed\n"); \ \ policy->user_policy.object = new_policy.object; \ - ret = __cpufreq_set_policy(policy, &new_policy); \ + ret = cpufreq_set_policy(policy, &new_policy); \ \ return ret ? ret : count; \ } @@ -455,7 +462,6 @@ static ssize_t show_cpuinfo_cur_freq(struct cpufreq_policy *policy, return sprintf(buf, "%u\n", cur_freq); } - /** * show_scaling_governor - show the current policy for the specified CPU */ @@ -471,14 +477,13 @@ static ssize_t show_scaling_governor(struct cpufreq_policy *policy, char *buf) return -EINVAL; } - /** * store_scaling_governor - store policy for the specified CPU */ static ssize_t store_scaling_governor(struct cpufreq_policy *policy, const char *buf, size_t count) { - unsigned int ret; + int ret; char str_governor[16]; struct cpufreq_policy new_policy; @@ -494,9 +499,7 @@ static ssize_t store_scaling_governor(struct cpufreq_policy *policy, &new_policy.governor)) return -EINVAL; - /* Do not use cpufreq_set_policy here or the user_policy.max - will be wrongly overridden */ - ret = __cpufreq_set_policy(policy, &new_policy); + ret = cpufreq_set_policy(policy, &new_policy); policy->user_policy.policy = policy->policy; policy->user_policy.governor = policy->governor; @@ -526,7 +529,7 @@ static ssize_t show_scaling_available_governors(struct cpufreq_policy *policy, ssize_t i = 0; struct cpufreq_governor *t; - if (!cpufreq_driver->target) { + if (!has_target()) { i += sprintf(buf, "performance powersave"); goto out; } @@ -542,7 +545,7 @@ out: return i; } -static ssize_t show_cpus(const struct cpumask *mask, char *buf) +ssize_t cpufreq_show_cpus(const struct cpumask *mask, char *buf) { ssize_t i = 0; unsigned int cpu; @@ -557,6 +560,7 @@ static ssize_t show_cpus(const struct cpumask *mask, char *buf) i += sprintf(&buf[i], "\n"); return i; } +EXPORT_SYMBOL_GPL(cpufreq_show_cpus); /** * show_related_cpus - show the CPUs affected by each transition even if @@ -564,7 +568,7 @@ static ssize_t show_cpus(const struct cpumask *mask, char *buf) */ static ssize_t show_related_cpus(struct cpufreq_policy *policy, char *buf) { - return show_cpus(policy->related_cpus, buf); + return cpufreq_show_cpus(policy->related_cpus, buf); } /** @@ -572,7 +576,7 @@ static ssize_t show_related_cpus(struct cpufreq_policy *policy, char *buf) */ static ssize_t show_affected_cpus(struct cpufreq_policy *policy, char *buf) { - return show_cpus(policy->cpus, buf); + return cpufreq_show_cpus(policy->cpus, buf); } static ssize_t store_scaling_setspeed(struct cpufreq_policy *policy, @@ -646,9 +650,6 @@ static struct attribute *default_attrs[] = { NULL }; -struct kobject *cpufreq_global_kobject; -EXPORT_SYMBOL(cpufreq_global_kobject); - #define to_policy(k) container_of(k, struct cpufreq_policy, kobj) #define to_attr(a) container_of(a, struct freq_attr, attr) @@ -656,23 +657,21 @@ static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf) { struct cpufreq_policy *policy = to_policy(kobj); struct freq_attr *fattr = to_attr(attr); - ssize_t ret = -EINVAL; - policy = cpufreq_cpu_get_sysfs(policy->cpu); - if (!policy) - goto no_policy; + ssize_t ret; + + if (!down_read_trylock(&cpufreq_rwsem)) + return -EINVAL; - if (lock_policy_rwsem_read(policy->cpu) < 0) - goto fail; + down_read(&policy->rwsem); if (fattr->show) ret = fattr->show(policy, buf); else ret = -EIO; - unlock_policy_rwsem_read(policy->cpu); -fail: - cpufreq_cpu_put_sysfs(policy); -no_policy: + up_read(&policy->rwsem); + up_read(&cpufreq_rwsem); + return ret; } @@ -682,22 +681,28 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr, struct cpufreq_policy *policy = to_policy(kobj); struct freq_attr *fattr = to_attr(attr); ssize_t ret = -EINVAL; - policy = cpufreq_cpu_get_sysfs(policy->cpu); - if (!policy) - goto no_policy; - if (lock_policy_rwsem_write(policy->cpu) < 0) - goto fail; + get_online_cpus(); + + if (!cpu_online(policy->cpu)) + goto unlock; + + if (!down_read_trylock(&cpufreq_rwsem)) + goto unlock; + + down_write(&policy->rwsem); if (fattr->store) ret = fattr->store(policy, buf, count); else ret = -EIO; - unlock_policy_rwsem_write(policy->cpu); -fail: - cpufreq_cpu_put_sysfs(policy); -no_policy: + up_write(&policy->rwsem); + + up_read(&cpufreq_rwsem); +unlock: + put_online_cpus(); + return ret; } @@ -719,42 +724,76 @@ static struct kobj_type ktype_cpufreq = { .release = cpufreq_sysfs_release, }; +struct kobject *cpufreq_global_kobject; +EXPORT_SYMBOL(cpufreq_global_kobject); + +static int cpufreq_global_kobject_usage; + +int cpufreq_get_global_kobject(void) +{ + if (!cpufreq_global_kobject_usage++) + return kobject_add(cpufreq_global_kobject, + &cpu_subsys.dev_root->kobj, "%s", "cpufreq"); + + return 0; +} +EXPORT_SYMBOL(cpufreq_get_global_kobject); + +void cpufreq_put_global_kobject(void) +{ + if (!--cpufreq_global_kobject_usage) + kobject_del(cpufreq_global_kobject); +} +EXPORT_SYMBOL(cpufreq_put_global_kobject); + +int cpufreq_sysfs_create_file(const struct attribute *attr) +{ + int ret = cpufreq_get_global_kobject(); + + if (!ret) { + ret = sysfs_create_file(cpufreq_global_kobject, attr); + if (ret) + cpufreq_put_global_kobject(); + } + + return ret; +} +EXPORT_SYMBOL(cpufreq_sysfs_create_file); + +void cpufreq_sysfs_remove_file(const struct attribute *attr) +{ + sysfs_remove_file(cpufreq_global_kobject, attr); + cpufreq_put_global_kobject(); +} +EXPORT_SYMBOL(cpufreq_sysfs_remove_file); + /* symlink affected CPUs */ -static int cpufreq_add_dev_symlink(unsigned int cpu, - struct cpufreq_policy *policy) +static int cpufreq_add_dev_symlink(struct cpufreq_policy *policy) { unsigned int j; int ret = 0; for_each_cpu(j, policy->cpus) { - struct cpufreq_policy *managed_policy; struct device *cpu_dev; - if (j == cpu) + if (j == policy->cpu) continue; - pr_debug("CPU %u already managed, adding link\n", j); - managed_policy = cpufreq_cpu_get(cpu); + pr_debug("Adding link for CPU: %u\n", j); cpu_dev = get_cpu_device(j); ret = sysfs_create_link(&cpu_dev->kobj, &policy->kobj, "cpufreq"); - if (ret) { - cpufreq_cpu_put(managed_policy); - return ret; - } + if (ret) + break; } return ret; } -static int cpufreq_add_dev_interface(unsigned int cpu, - struct cpufreq_policy *policy, +static int cpufreq_add_dev_interface(struct cpufreq_policy *policy, struct device *dev) { - struct cpufreq_policy new_policy; struct freq_attr **drv_attr; - unsigned long flags; int ret = 0; - unsigned int j; /* prepare interface data */ ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq, @@ -775,7 +814,7 @@ static int cpufreq_add_dev_interface(unsigned int cpu, if (ret) goto err_out_kobj_put; } - if (cpufreq_driver->target) { + if (has_target()) { ret = sysfs_create_file(&policy->kobj, &scaling_cur_freq.attr); if (ret) goto err_out_kobj_put; @@ -786,23 +825,29 @@ static int cpufreq_add_dev_interface(unsigned int cpu, goto err_out_kobj_put; } - write_lock_irqsave(&cpufreq_driver_lock, flags); - for_each_cpu(j, policy->cpus) { - per_cpu(cpufreq_cpu_data, j) = policy; - per_cpu(cpufreq_policy_cpu, j) = policy->cpu; - } - write_unlock_irqrestore(&cpufreq_driver_lock, flags); - - ret = cpufreq_add_dev_symlink(cpu, policy); + ret = cpufreq_add_dev_symlink(policy); if (ret) goto err_out_kobj_put; - memcpy(&new_policy, policy, sizeof(struct cpufreq_policy)); - /* assure that the starting sequence is run in __cpufreq_set_policy */ + return ret; + +err_out_kobj_put: + kobject_put(&policy->kobj); + wait_for_completion(&policy->kobj_unregister); + return ret; +} + +static void cpufreq_init_policy(struct cpufreq_policy *policy) +{ + struct cpufreq_policy new_policy; + int ret = 0; + + memcpy(&new_policy, policy, sizeof(*policy)); + /* assure that the starting sequence is run in cpufreq_set_policy */ policy->governor = NULL; /* set default policy */ - ret = __cpufreq_set_policy(policy, &new_policy); + ret = cpufreq_set_policy(policy, &new_policy); policy->user_policy.policy = policy->policy; policy->user_policy.governor = policy->governor; @@ -811,72 +856,125 @@ static int cpufreq_add_dev_interface(unsigned int cpu, if (cpufreq_driver->exit) cpufreq_driver->exit(policy); } - return ret; - -err_out_kobj_put: - kobject_put(&policy->kobj); - wait_for_completion(&policy->kobj_unregister); - return ret; } #ifdef CONFIG_HOTPLUG_CPU -static int cpufreq_add_policy_cpu(unsigned int cpu, unsigned int sibling, - struct device *dev) +static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy, + unsigned int cpu, struct device *dev, + bool frozen) { - struct cpufreq_policy *policy; - int ret = 0, has_target = !!cpufreq_driver->target; + int ret = 0; unsigned long flags; - policy = cpufreq_cpu_get(sibling); - WARN_ON(!policy); - - if (has_target) - __cpufreq_governor(policy, CPUFREQ_GOV_STOP); + if (has_target()) { + ret = __cpufreq_governor(policy, CPUFREQ_GOV_STOP); + if (ret) { + pr_err("%s: Failed to stop governor\n", __func__); + return ret; + } + } - lock_policy_rwsem_write(sibling); + down_write(&policy->rwsem); write_lock_irqsave(&cpufreq_driver_lock, flags); cpumask_set_cpu(cpu, policy->cpus); - per_cpu(cpufreq_policy_cpu, cpu) = policy->cpu; per_cpu(cpufreq_cpu_data, cpu) = policy; write_unlock_irqrestore(&cpufreq_driver_lock, flags); - unlock_policy_rwsem_write(sibling); + up_write(&policy->rwsem); - if (has_target) { - __cpufreq_governor(policy, CPUFREQ_GOV_START); - __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS); + if (has_target()) { + if ((ret = __cpufreq_governor(policy, CPUFREQ_GOV_START)) || + (ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS))) { + pr_err("%s: Failed to start governor\n", __func__); + return ret; + } } - ret = sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq"); - if (ret) { - cpufreq_cpu_put(policy); - return ret; - } + /* Don't touch sysfs links during light-weight init */ + if (!frozen) + ret = sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq"); - return 0; + return ret; } #endif -/** - * cpufreq_add_dev - add a CPU device - * - * Adds the cpufreq interface for a CPU device. - * - * The Oracle says: try running cpufreq registration/unregistration concurrently - * with with cpu hotplugging and all hell will break loose. Tried to clean this - * mess up, but more thorough testing is needed. - Mathieu - */ -static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) +static struct cpufreq_policy *cpufreq_policy_restore(unsigned int cpu) +{ + struct cpufreq_policy *policy; + unsigned long flags; + + read_lock_irqsave(&cpufreq_driver_lock, flags); + + policy = per_cpu(cpufreq_cpu_data_fallback, cpu); + + read_unlock_irqrestore(&cpufreq_driver_lock, flags); + + return policy; +} + +static struct cpufreq_policy *cpufreq_policy_alloc(void) +{ + struct cpufreq_policy *policy; + + policy = kzalloc(sizeof(*policy), GFP_KERNEL); + if (!policy) + return NULL; + + if (!alloc_cpumask_var(&policy->cpus, GFP_KERNEL)) + goto err_free_policy; + + if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL)) + goto err_free_cpumask; + + INIT_LIST_HEAD(&policy->policy_list); + init_rwsem(&policy->rwsem); + + return policy; + +err_free_cpumask: + free_cpumask_var(policy->cpus); +err_free_policy: + kfree(policy); + + return NULL; +} + +static void cpufreq_policy_free(struct cpufreq_policy *policy) +{ + free_cpumask_var(policy->related_cpus); + free_cpumask_var(policy->cpus); + kfree(policy); +} + +static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu) +{ + if (WARN_ON(cpu == policy->cpu)) + return; + + down_write(&policy->rwsem); + + policy->last_cpu = policy->cpu; + policy->cpu = cpu; + + up_write(&policy->rwsem); + + cpufreq_frequency_table_update_policy_cpu(policy); + blocking_notifier_call_chain(&cpufreq_policy_notifier_list, + CPUFREQ_UPDATE_POLICY_CPU, policy); +} + +static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, + bool frozen) { unsigned int j, cpu = dev->id; int ret = -ENOMEM; struct cpufreq_policy *policy; unsigned long flags; #ifdef CONFIG_HOTPLUG_CPU + struct cpufreq_policy *tpolicy; struct cpufreq_governor *gov; - int sibling; #endif if (cpu_is_offline(cpu)) @@ -892,43 +990,49 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) cpufreq_cpu_put(policy); return 0; } +#endif + + if (!down_read_trylock(&cpufreq_rwsem)) + return 0; #ifdef CONFIG_HOTPLUG_CPU /* Check if this cpu was hot-unplugged earlier and has siblings */ read_lock_irqsave(&cpufreq_driver_lock, flags); - for_each_online_cpu(sibling) { - struct cpufreq_policy *cp = per_cpu(cpufreq_cpu_data, sibling); - if (cp && cpumask_test_cpu(cpu, cp->related_cpus)) { + list_for_each_entry(tpolicy, &cpufreq_policy_list, policy_list) { + if (cpumask_test_cpu(cpu, tpolicy->related_cpus)) { read_unlock_irqrestore(&cpufreq_driver_lock, flags); - return cpufreq_add_policy_cpu(cpu, sibling, dev); + ret = cpufreq_add_policy_cpu(tpolicy, cpu, dev, frozen); + up_read(&cpufreq_rwsem); + return ret; } } read_unlock_irqrestore(&cpufreq_driver_lock, flags); #endif -#endif - if (!try_module_get(cpufreq_driver->owner)) { - ret = -EINVAL; - goto module_out; - } + if (frozen) + /* Restore the saved policy when doing light-weight init */ + policy = cpufreq_policy_restore(cpu); + else + policy = cpufreq_policy_alloc(); - policy = kzalloc(sizeof(struct cpufreq_policy), GFP_KERNEL); if (!policy) goto nomem_out; - if (!alloc_cpumask_var(&policy->cpus, GFP_KERNEL)) - goto err_free_policy; - if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL)) - goto err_free_cpumask; + /* + * In the resume path, since we restore a saved policy, the assignment + * to policy->cpu is like an update of the existing policy, rather than + * the creation of a brand new one. So we need to perform this update + * by invoking update_policy_cpu(). + */ + if (frozen && cpu != policy->cpu) + update_policy_cpu(policy, cpu); + else + policy->cpu = cpu; - policy->cpu = cpu; policy->governor = CPUFREQ_DEFAULT_GOVERNOR; cpumask_copy(policy->cpus, cpumask_of(cpu)); - /* Initially set CPU itself as the policy_cpu */ - per_cpu(cpufreq_policy_cpu, cpu) = cpu; - init_completion(&policy->kobj_unregister); INIT_WORK(&policy->update, handle_update); @@ -941,6 +1045,14 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) goto err_set_policy_cpu; } + if (cpufreq_driver->get) { + policy->cur = cpufreq_driver->get(policy->cpu); + if (!policy->cur) { + pr_err("%s: ->get() failed\n", __func__); + goto err_get_freq; + } + } + /* related cpus should atleast have policy->cpus */ cpumask_or(policy->related_cpus, policy->related_cpus, policy->cpus); @@ -975,12 +1087,26 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) policy->min, policy->max); #endif - ret = cpufreq_add_dev_interface(cpu, policy, dev); - if (ret) - goto err_out_unregister; + write_lock_irqsave(&cpufreq_driver_lock, flags); + for_each_cpu(j, policy->cpus) + per_cpu(cpufreq_cpu_data, j) = policy; + write_unlock_irqrestore(&cpufreq_driver_lock, flags); + + if (!frozen) { + ret = cpufreq_add_dev_interface(policy, dev); + if (ret) + goto err_out_unregister; + } + + write_lock_irqsave(&cpufreq_driver_lock, flags); + list_add(&policy->policy_list, &cpufreq_policy_list); + write_unlock_irqrestore(&cpufreq_driver_lock, flags); + + cpufreq_init_policy(policy); kobject_uevent(&policy->kobj, KOBJ_ADD); - module_put(cpufreq_driver->owner); + up_read(&cpufreq_rwsem); + pr_debug("initialization complete\n"); return 0; @@ -991,171 +1117,234 @@ err_out_unregister: per_cpu(cpufreq_cpu_data, j) = NULL; write_unlock_irqrestore(&cpufreq_driver_lock, flags); - kobject_put(&policy->kobj); - wait_for_completion(&policy->kobj_unregister); - +err_get_freq: + if (cpufreq_driver->exit) + cpufreq_driver->exit(policy); err_set_policy_cpu: - per_cpu(cpufreq_policy_cpu, cpu) = -1; - free_cpumask_var(policy->related_cpus); -err_free_cpumask: - free_cpumask_var(policy->cpus); -err_free_policy: - kfree(policy); + cpufreq_policy_free(policy); nomem_out: - module_put(cpufreq_driver->owner); -module_out: + up_read(&cpufreq_rwsem); + return ret; } -static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu) +/** + * cpufreq_add_dev - add a CPU device + * + * Adds the cpufreq interface for a CPU device. + * + * The Oracle says: try running cpufreq registration/unregistration concurrently + * with with cpu hotplugging and all hell will break loose. Tried to clean this + * mess up, but more thorough testing is needed. - Mathieu + */ +static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) { - int j; + return __cpufreq_add_dev(dev, sif, false); +} - policy->last_cpu = policy->cpu; - policy->cpu = cpu; +static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *policy, + unsigned int old_cpu, bool frozen) +{ + struct device *cpu_dev; + int ret; - for_each_cpu(j, policy->cpus) - per_cpu(cpufreq_policy_cpu, j) = cpu; + /* first sibling now owns the new sysfs dir */ + cpu_dev = get_cpu_device(cpumask_any_but(policy->cpus, old_cpu)); -#ifdef CONFIG_CPU_FREQ_TABLE - cpufreq_frequency_table_update_policy_cpu(policy); -#endif - blocking_notifier_call_chain(&cpufreq_policy_notifier_list, - CPUFREQ_UPDATE_POLICY_CPU, policy); + /* Don't touch sysfs files during light-weight tear-down */ + if (frozen) + return cpu_dev->id; + + sysfs_remove_link(&cpu_dev->kobj, "cpufreq"); + ret = kobject_move(&policy->kobj, &cpu_dev->kobj); + if (ret) { + pr_err("%s: Failed to move kobj: %d", __func__, ret); + + down_write(&policy->rwsem); + cpumask_set_cpu(old_cpu, policy->cpus); + up_write(&policy->rwsem); + + ret = sysfs_create_link(&cpu_dev->kobj, &policy->kobj, + "cpufreq"); + + return -EINVAL; + } + + return cpu_dev->id; } -/** - * __cpufreq_remove_dev - remove a CPU device - * - * Removes the cpufreq interface for a CPU device. - * Caller should already have policy_rwsem in write mode for this CPU. - * This routine frees the rwsem before returning. - */ -static int __cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) +static int __cpufreq_remove_dev_prepare(struct device *dev, + struct subsys_interface *sif, + bool frozen) { - unsigned int cpu = dev->id, ret, cpus; + unsigned int cpu = dev->id, cpus; + int new_cpu, ret; unsigned long flags; - struct cpufreq_policy *data; - struct kobject *kobj; - struct completion *cmp; - struct device *cpu_dev; + struct cpufreq_policy *policy; pr_debug("%s: unregistering CPU %u\n", __func__, cpu); write_lock_irqsave(&cpufreq_driver_lock, flags); - data = per_cpu(cpufreq_cpu_data, cpu); - per_cpu(cpufreq_cpu_data, cpu) = NULL; + policy = per_cpu(cpufreq_cpu_data, cpu); + + /* Save the policy somewhere when doing a light-weight tear-down */ + if (frozen) + per_cpu(cpufreq_cpu_data_fallback, cpu) = policy; write_unlock_irqrestore(&cpufreq_driver_lock, flags); - if (!data) { + if (!policy) { pr_debug("%s: No cpu_data found\n", __func__); return -EINVAL; } - if (cpufreq_driver->target) - __cpufreq_governor(data, CPUFREQ_GOV_STOP); + if (has_target()) { + ret = __cpufreq_governor(policy, CPUFREQ_GOV_STOP); + if (ret) { + pr_err("%s: Failed to stop governor\n", __func__); + return ret; + } + } #ifdef CONFIG_HOTPLUG_CPU if (!cpufreq_driver->setpolicy) strlcpy(per_cpu(cpufreq_policy_save, cpu).gov, - data->governor->name, CPUFREQ_NAME_LEN); - per_cpu(cpufreq_policy_save, cpu).min = data->user_policy.min; - per_cpu(cpufreq_policy_save, cpu).max = data->user_policy.max; + policy->governor->name, CPUFREQ_NAME_LEN); + per_cpu(cpufreq_policy_save, cpu).min = policy->user_policy.min; + per_cpu(cpufreq_policy_save, cpu).max = policy->user_policy.max; pr_debug("Saving CPU%d user policy min %d and max %d\n", - cpu, data->user_policy.min, data->user_policy.max); + cpu, policy->user_policy.min, policy->user_policy.max); #endif - WARN_ON(lock_policy_rwsem_write(cpu)); - cpus = cpumask_weight(data->cpus); - - if (cpus > 1) - cpumask_clear_cpu(cpu, data->cpus); - unlock_policy_rwsem_write(cpu); + down_read(&policy->rwsem); + cpus = cpumask_weight(policy->cpus); + up_read(&policy->rwsem); - if (cpu != data->cpu) { - sysfs_remove_link(&dev->kobj, "cpufreq"); + if (cpu != policy->cpu) { + if (!frozen) + sysfs_remove_link(&dev->kobj, "cpufreq"); } else if (cpus > 1) { - /* first sibling now owns the new sysfs dir */ - cpu_dev = get_cpu_device(cpumask_first(data->cpus)); - sysfs_remove_link(&cpu_dev->kobj, "cpufreq"); - ret = kobject_move(&data->kobj, &cpu_dev->kobj); - if (ret) { - pr_err("%s: Failed to move kobj: %d", __func__, ret); + new_cpu = cpufreq_nominate_new_policy_cpu(policy, cpu, frozen); + if (new_cpu >= 0) { + update_policy_cpu(policy, new_cpu); - WARN_ON(lock_policy_rwsem_write(cpu)); - cpumask_set_cpu(cpu, data->cpus); + if (!frozen) { + pr_debug("%s: policy Kobject moved to cpu: %d from: %d\n", + __func__, new_cpu, cpu); + } + } + } - write_lock_irqsave(&cpufreq_driver_lock, flags); - per_cpu(cpufreq_cpu_data, cpu) = data; - write_unlock_irqrestore(&cpufreq_driver_lock, flags); + return 0; +} - unlock_policy_rwsem_write(cpu); +static int __cpufreq_remove_dev_finish(struct device *dev, + struct subsys_interface *sif, + bool frozen) +{ + unsigned int cpu = dev->id, cpus; + int ret; + unsigned long flags; + struct cpufreq_policy *policy; + struct kobject *kobj; + struct completion *cmp; - ret = sysfs_create_link(&cpu_dev->kobj, &data->kobj, - "cpufreq"); - return -EINVAL; - } + read_lock_irqsave(&cpufreq_driver_lock, flags); + policy = per_cpu(cpufreq_cpu_data, cpu); + read_unlock_irqrestore(&cpufreq_driver_lock, flags); - WARN_ON(lock_policy_rwsem_write(cpu)); - update_policy_cpu(data, cpu_dev->id); - unlock_policy_rwsem_write(cpu); - pr_debug("%s: policy Kobject moved to cpu: %d from: %d\n", - __func__, cpu_dev->id, cpu); + if (!policy) { + pr_debug("%s: No cpu_data found\n", __func__); + return -EINVAL; } + down_write(&policy->rwsem); + cpus = cpumask_weight(policy->cpus); + + if (cpus > 1) + cpumask_clear_cpu(cpu, policy->cpus); + up_write(&policy->rwsem); + /* If cpu is last user of policy, free policy */ if (cpus == 1) { - if (cpufreq_driver->target) - __cpufreq_governor(data, CPUFREQ_GOV_POLICY_EXIT); - - lock_policy_rwsem_read(cpu); - kobj = &data->kobj; - cmp = &data->kobj_unregister; - unlock_policy_rwsem_read(cpu); - kobject_put(kobj); - - /* we need to make sure that the underlying kobj is actually - * not referenced anymore by anybody before we proceed with - * unloading. - */ - pr_debug("waiting for dropping of refcount\n"); - wait_for_completion(cmp); - pr_debug("wait complete\n"); + if (has_target()) { + ret = __cpufreq_governor(policy, + CPUFREQ_GOV_POLICY_EXIT); + if (ret) { + pr_err("%s: Failed to exit governor\n", + __func__); + return ret; + } + } + if (!frozen) { + down_read(&policy->rwsem); + kobj = &policy->kobj; + cmp = &policy->kobj_unregister; + up_read(&policy->rwsem); + kobject_put(kobj); + + /* + * We need to make sure that the underlying kobj is + * actually not referenced anymore by anybody before we + * proceed with unloading. + */ + pr_debug("waiting for dropping of refcount\n"); + wait_for_completion(cmp); + pr_debug("wait complete\n"); + } + + /* + * Perform the ->exit() even during light-weight tear-down, + * since this is a core component, and is essential for the + * subsequent light-weight ->init() to succeed. + */ if (cpufreq_driver->exit) - cpufreq_driver->exit(data); + cpufreq_driver->exit(policy); - free_cpumask_var(data->related_cpus); - free_cpumask_var(data->cpus); - kfree(data); + /* Remove policy from list of active policies */ + write_lock_irqsave(&cpufreq_driver_lock, flags); + list_del(&policy->policy_list); + write_unlock_irqrestore(&cpufreq_driver_lock, flags); + + if (!frozen) + cpufreq_policy_free(policy); } else { - pr_debug("%s: removing link, cpu: %d\n", __func__, cpu); - cpufreq_cpu_put(data); - if (cpufreq_driver->target) { - __cpufreq_governor(data, CPUFREQ_GOV_START); - __cpufreq_governor(data, CPUFREQ_GOV_LIMITS); + if (has_target()) { + if ((ret = __cpufreq_governor(policy, CPUFREQ_GOV_START)) || + (ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS))) { + pr_err("%s: Failed to start governor\n", + __func__); + return ret; + } } } - per_cpu(cpufreq_policy_cpu, cpu) = -1; + per_cpu(cpufreq_cpu_data, cpu) = NULL; return 0; } - +/** + * cpufreq_remove_dev - remove a CPU device + * + * Removes the cpufreq interface for a CPU device. + */ static int cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) { unsigned int cpu = dev->id; - int retval; + int ret; if (cpu_is_offline(cpu)) return 0; - retval = __cpufreq_remove_dev(dev, sif); - return retval; -} + ret = __cpufreq_remove_dev_prepare(dev, sif, false); + if (!ret) + ret = __cpufreq_remove_dev_finish(dev, sif, false); + + return ret; +} static void handle_update(struct work_struct *work) { @@ -1167,7 +1356,8 @@ static void handle_update(struct work_struct *work) } /** - * cpufreq_out_of_sync - If actual and saved CPU frequency differs, we're in deep trouble. + * cpufreq_out_of_sync - If actual and saved CPU frequency differs, we're + * in deep trouble. * @cpu: cpu number * @old_freq: CPU frequency the kernel thinks the CPU runs at * @new_freq: CPU frequency the CPU actually runs at @@ -1182,7 +1372,6 @@ static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq, struct cpufreq_freqs freqs; unsigned long flags; - pr_debug("Warning: CPU frequency out of sync: cpufreq and timing " "core thinks of %u, is %u kHz.\n", old_freq, new_freq); @@ -1197,7 +1386,6 @@ static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq, cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); } - /** * cpufreq_quick_get - get the CPU frequency (in kHz) from policy->cur * @cpu: CPU number @@ -1243,7 +1431,6 @@ unsigned int cpufreq_quick_get_max(unsigned int cpu) } EXPORT_SYMBOL(cpufreq_quick_get_max); - static unsigned int __cpufreq_get(unsigned int cpu) { struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); @@ -1275,22 +1462,24 @@ static unsigned int __cpufreq_get(unsigned int cpu) */ unsigned int cpufreq_get(unsigned int cpu) { + struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); unsigned int ret_freq = 0; - struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); - if (!policy) - goto out; + if (cpufreq_disabled() || !cpufreq_driver) + return -ENOENT; - if (unlikely(lock_policy_rwsem_read(cpu))) - goto out_policy; + BUG_ON(!policy); + + if (!down_read_trylock(&cpufreq_rwsem)) + return 0; + + down_read(&policy->rwsem); ret_freq = __cpufreq_get(cpu); - unlock_policy_rwsem_read(cpu); + up_read(&policy->rwsem); + up_read(&cpufreq_rwsem); -out_policy: - cpufreq_cpu_put(policy); -out: return ret_freq; } EXPORT_SYMBOL(cpufreq_get); @@ -1302,7 +1491,6 @@ static struct subsys_interface cpufreq_interface = { .remove_dev = cpufreq_remove_dev, }; - /** * cpufreq_bp_suspend - Prepare the boot CPU for system suspend. * @@ -1314,23 +1502,23 @@ static int cpufreq_bp_suspend(void) int ret = 0; int cpu = smp_processor_id(); - struct cpufreq_policy *cpu_policy; + struct cpufreq_policy *policy; pr_debug("suspending cpu %u\n", cpu); /* If there's no policy for the boot CPU, we have nothing to do. */ - cpu_policy = cpufreq_cpu_get(cpu); - if (!cpu_policy) + policy = cpufreq_cpu_get(cpu); + if (!policy) return 0; if (cpufreq_driver->suspend) { - ret = cpufreq_driver->suspend(cpu_policy); + ret = cpufreq_driver->suspend(policy); if (ret) printk(KERN_ERR "cpufreq: suspend failed in ->suspend " - "step on CPU %u\n", cpu_policy->cpu); + "step on CPU %u\n", policy->cpu); } - cpufreq_cpu_put(cpu_policy); + cpufreq_cpu_put(policy); return ret; } @@ -1352,28 +1540,28 @@ static void cpufreq_bp_resume(void) int ret = 0; int cpu = smp_processor_id(); - struct cpufreq_policy *cpu_policy; + struct cpufreq_policy *policy; pr_debug("resuming cpu %u\n", cpu); /* If there's no policy for the boot CPU, we have nothing to do. */ - cpu_policy = cpufreq_cpu_get(cpu); - if (!cpu_policy) + policy = cpufreq_cpu_get(cpu); + if (!policy) return; if (cpufreq_driver->resume) { - ret = cpufreq_driver->resume(cpu_policy); + ret = cpufreq_driver->resume(policy); if (ret) { printk(KERN_ERR "cpufreq: resume failed in ->resume " - "step on CPU %u\n", cpu_policy->cpu); + "step on CPU %u\n", policy->cpu); goto fail; } } - schedule_work(&cpu_policy->update); + schedule_work(&policy->update); fail: - cpufreq_cpu_put(cpu_policy); + cpufreq_cpu_put(policy); } static struct syscore_ops cpufreq_syscore_ops = { @@ -1439,11 +1627,10 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list) } EXPORT_SYMBOL(cpufreq_register_notifier); - /** * cpufreq_unregister_notifier - unregister a driver with cpufreq * @nb: notifier block to be unregistered - * @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER + * @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER * * Remove a driver from the CPU frequency notifier list. * @@ -1479,7 +1666,6 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier); * GOVERNORS * *********************************************************************/ - int __cpufreq_driver_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) @@ -1499,12 +1685,75 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, pr_debug("target for CPU %u: %u kHz, relation %u, requested %u kHz\n", policy->cpu, target_freq, relation, old_target_freq); + /* + * This might look like a redundant call as we are checking it again + * after finding index. But it is left intentionally for cases where + * exactly same freq is called again and so we can save on few function + * calls. + */ if (target_freq == policy->cur) return 0; if (cpufreq_driver->target) retval = cpufreq_driver->target(policy, target_freq, relation); + else if (cpufreq_driver->target_index) { + struct cpufreq_frequency_table *freq_table; + struct cpufreq_freqs freqs; + bool notify; + int index; + + freq_table = cpufreq_frequency_get_table(policy->cpu); + if (unlikely(!freq_table)) { + pr_err("%s: Unable to find freq_table\n", __func__); + goto out; + } + + retval = cpufreq_frequency_table_target(policy, freq_table, + target_freq, relation, &index); + if (unlikely(retval)) { + pr_err("%s: Unable to find matching freq\n", __func__); + goto out; + } + + if (freq_table[index].frequency == policy->cur) { + retval = 0; + goto out; + } + + notify = !(cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION); + + if (notify) { + freqs.old = policy->cur; + freqs.new = freq_table[index].frequency; + freqs.flags = 0; + + pr_debug("%s: cpu: %d, oldfreq: %u, new freq: %u\n", + __func__, policy->cpu, freqs.old, + freqs.new); + cpufreq_notify_transition(policy, &freqs, + CPUFREQ_PRECHANGE); + } + + retval = cpufreq_driver->target_index(policy, index); + if (retval) + pr_err("%s: Failed to change cpu frequency: %d\n", + __func__, retval); + + if (notify) { + /* + * Notify with old freq in case we failed to change + * frequency + */ + if (retval) + freqs.new = freqs.old; + + cpufreq_notify_transition(policy, &freqs, + CPUFREQ_POSTCHANGE); + } + } + +out: return retval; } EXPORT_SYMBOL_GPL(__cpufreq_driver_target); @@ -1515,45 +1764,16 @@ int cpufreq_driver_target(struct cpufreq_policy *policy, { int ret = -EINVAL; - policy = cpufreq_cpu_get(policy->cpu); - if (!policy) - goto no_policy; - - if (unlikely(lock_policy_rwsem_write(policy->cpu))) - goto fail; + down_write(&policy->rwsem); ret = __cpufreq_driver_target(policy, target_freq, relation); - unlock_policy_rwsem_write(policy->cpu); + up_write(&policy->rwsem); -fail: - cpufreq_cpu_put(policy); -no_policy: return ret; } EXPORT_SYMBOL_GPL(cpufreq_driver_target); -int __cpufreq_driver_getavg(struct cpufreq_policy *policy, unsigned int cpu) -{ - int ret = 0; - - if (cpufreq_disabled()) - return ret; - - if (!cpufreq_driver->getavg) - return 0; - - policy = cpufreq_cpu_get(policy->cpu); - if (!policy) - return -EINVAL; - - ret = cpufreq_driver->getavg(policy, cpu); - - cpufreq_cpu_put(policy); - return ret; -} -EXPORT_SYMBOL_GPL(__cpufreq_driver_getavg); - /* * when "event" is CPUFREQ_GOV_LIMITS */ @@ -1588,15 +1808,17 @@ static int __cpufreq_governor(struct cpufreq_policy *policy, } } - if (!try_module_get(policy->governor->owner)) - return -EINVAL; + if (event == CPUFREQ_GOV_POLICY_INIT) + if (!try_module_get(policy->governor->owner)) + return -EINVAL; pr_debug("__cpufreq_governor for CPU %u, event %u\n", policy->cpu, event); mutex_lock(&cpufreq_governor_lock); - if ((!policy->governor_enabled && (event == CPUFREQ_GOV_STOP)) || - (policy->governor_enabled && (event == CPUFREQ_GOV_START))) { + if ((policy->governor_enabled && event == CPUFREQ_GOV_START) + || (!policy->governor_enabled + && (event == CPUFREQ_GOV_LIMITS || event == CPUFREQ_GOV_STOP))) { mutex_unlock(&cpufreq_governor_lock); return -EBUSY; } @@ -1625,17 +1847,13 @@ static int __cpufreq_governor(struct cpufreq_policy *policy, mutex_unlock(&cpufreq_governor_lock); } - /* we keep one module reference alive for - each CPU governed by this CPU */ - if ((event != CPUFREQ_GOV_START) || ret) - module_put(policy->governor->owner); - if ((event == CPUFREQ_GOV_STOP) && !ret) + if (((event == CPUFREQ_GOV_POLICY_INIT) && ret) || + ((event == CPUFREQ_GOV_POLICY_EXIT) && !ret)) module_put(policy->governor->owner); return ret; } - int cpufreq_register_governor(struct cpufreq_governor *governor) { int err; @@ -1660,7 +1878,6 @@ int cpufreq_register_governor(struct cpufreq_governor *governor) } EXPORT_SYMBOL_GPL(cpufreq_register_governor); - void cpufreq_unregister_governor(struct cpufreq_governor *governor) { #ifdef CONFIG_HOTPLUG_CPU @@ -1692,7 +1909,6 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor) EXPORT_SYMBOL_GPL(cpufreq_unregister_governor); - /********************************************************************* * POLICY INTERFACE * *********************************************************************/ @@ -1714,105 +1930,105 @@ int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu) if (!cpu_policy) return -EINVAL; - memcpy(policy, cpu_policy, sizeof(struct cpufreq_policy)); + memcpy(policy, cpu_policy, sizeof(*policy)); cpufreq_cpu_put(cpu_policy); return 0; } EXPORT_SYMBOL(cpufreq_get_policy); - /* - * data : current policy. - * policy : policy to be set. + * policy : current policy. + * new_policy: policy to be set. */ -static int __cpufreq_set_policy(struct cpufreq_policy *data, - struct cpufreq_policy *policy) +static int cpufreq_set_policy(struct cpufreq_policy *policy, + struct cpufreq_policy *new_policy) { int ret = 0, failed = 1; - pr_debug("setting new policy for CPU %u: %u - %u kHz\n", policy->cpu, - policy->min, policy->max); + pr_debug("setting new policy for CPU %u: %u - %u kHz\n", new_policy->cpu, + new_policy->min, new_policy->max); - memcpy(&policy->cpuinfo, &data->cpuinfo, - sizeof(struct cpufreq_cpuinfo)); + memcpy(&new_policy->cpuinfo, &policy->cpuinfo, sizeof(policy->cpuinfo)); - if (policy->min > data->max || policy->max < data->min) { + if (new_policy->min > policy->max || new_policy->max < policy->min) { ret = -EINVAL; goto error_out; } /* verify the cpu speed can be set within this limit */ - ret = cpufreq_driver->verify(policy); + ret = cpufreq_driver->verify(new_policy); if (ret) goto error_out; /* adjust if necessary - all reasons */ blocking_notifier_call_chain(&cpufreq_policy_notifier_list, - CPUFREQ_ADJUST, policy); + CPUFREQ_ADJUST, new_policy); /* adjust if necessary - hardware incompatibility*/ blocking_notifier_call_chain(&cpufreq_policy_notifier_list, - CPUFREQ_INCOMPATIBLE, policy); + CPUFREQ_INCOMPATIBLE, new_policy); - /* verify the cpu speed can be set within this limit, - which might be different to the first one */ - ret = cpufreq_driver->verify(policy); + /* + * verify the cpu speed can be set within this limit, which might be + * different to the first one + */ + ret = cpufreq_driver->verify(new_policy); if (ret) goto error_out; /* notification of the new policy */ blocking_notifier_call_chain(&cpufreq_policy_notifier_list, - CPUFREQ_NOTIFY, policy); + CPUFREQ_NOTIFY, new_policy); - data->min = policy->min; - data->max = policy->max; + policy->min = new_policy->min; + policy->max = new_policy->max; pr_debug("new min and max freqs are %u - %u kHz\n", - data->min, data->max); + policy->min, policy->max); if (cpufreq_driver->setpolicy) { - data->policy = policy->policy; + policy->policy = new_policy->policy; pr_debug("setting range\n"); - ret = cpufreq_driver->setpolicy(policy); + ret = cpufreq_driver->setpolicy(new_policy); } else { - if (policy->governor != data->governor) { + if (new_policy->governor != policy->governor) { /* save old, working values */ - struct cpufreq_governor *old_gov = data->governor; + struct cpufreq_governor *old_gov = policy->governor; pr_debug("governor switch\n"); /* end old governor */ - if (data->governor) { - __cpufreq_governor(data, CPUFREQ_GOV_STOP); - unlock_policy_rwsem_write(policy->cpu); - __cpufreq_governor(data, + if (policy->governor) { + __cpufreq_governor(policy, CPUFREQ_GOV_STOP); + up_write(&policy->rwsem); + __cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT); - lock_policy_rwsem_write(policy->cpu); + down_write(&policy->rwsem); } /* start new governor */ - data->governor = policy->governor; - if (!__cpufreq_governor(data, CPUFREQ_GOV_POLICY_INIT)) { - if (!__cpufreq_governor(data, CPUFREQ_GOV_START)) { + policy->governor = new_policy->governor; + if (!__cpufreq_governor(policy, CPUFREQ_GOV_POLICY_INIT)) { + if (!__cpufreq_governor(policy, CPUFREQ_GOV_START)) { failed = 0; } else { - unlock_policy_rwsem_write(policy->cpu); - __cpufreq_governor(data, + up_write(&policy->rwsem); + __cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT); - lock_policy_rwsem_write(policy->cpu); + down_write(&policy->rwsem); } } if (failed) { /* new governor failed, so re-start old one */ pr_debug("starting governor %s failed\n", - data->governor->name); + policy->governor->name); if (old_gov) { - data->governor = old_gov; - __cpufreq_governor(data, + policy->governor = old_gov; + __cpufreq_governor(policy, CPUFREQ_GOV_POLICY_INIT); - __cpufreq_governor(data, + __cpufreq_governor(policy, CPUFREQ_GOV_START); } ret = -EINVAL; @@ -1821,7 +2037,7 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, /* might be a policy change, too, so fall through */ } pr_debug("governor: change or update limits\n"); - __cpufreq_governor(data, CPUFREQ_GOV_LIMITS); + ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS); } error_out: @@ -1837,72 +2053,79 @@ error_out: */ int cpufreq_update_policy(unsigned int cpu) { - struct cpufreq_policy *data = cpufreq_cpu_get(cpu); - struct cpufreq_policy policy; + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); + struct cpufreq_policy new_policy; int ret; - if (!data) { + if (!policy) { ret = -ENODEV; goto no_policy; } - if (unlikely(lock_policy_rwsem_write(cpu))) { - ret = -EINVAL; - goto fail; - } + down_write(&policy->rwsem); pr_debug("updating policy for CPU %u\n", cpu); - memcpy(&policy, data, sizeof(struct cpufreq_policy)); - policy.min = data->user_policy.min; - policy.max = data->user_policy.max; - policy.policy = data->user_policy.policy; - policy.governor = data->user_policy.governor; - - /* BIOS might change freq behind our back - -> ask driver for current freq and notify governors about a change */ + memcpy(&new_policy, policy, sizeof(*policy)); + new_policy.min = policy->user_policy.min; + new_policy.max = policy->user_policy.max; + new_policy.policy = policy->user_policy.policy; + new_policy.governor = policy->user_policy.governor; + + /* + * BIOS might change freq behind our back + * -> ask driver for current freq and notify governors about a change + */ if (cpufreq_driver->get) { - policy.cur = cpufreq_driver->get(cpu); - if (!data->cur) { + new_policy.cur = cpufreq_driver->get(cpu); + if (!policy->cur) { pr_debug("Driver did not initialize current freq"); - data->cur = policy.cur; + policy->cur = new_policy.cur; } else { - if (data->cur != policy.cur && cpufreq_driver->target) - cpufreq_out_of_sync(cpu, data->cur, - policy.cur); + if (policy->cur != new_policy.cur && has_target()) + cpufreq_out_of_sync(cpu, policy->cur, + new_policy.cur); } } - ret = __cpufreq_set_policy(data, &policy); + ret = cpufreq_set_policy(policy, &new_policy); - unlock_policy_rwsem_write(cpu); + up_write(&policy->rwsem); -fail: - cpufreq_cpu_put(data); + cpufreq_cpu_put(policy); no_policy: return ret; } EXPORT_SYMBOL(cpufreq_update_policy); -static int __cpuinit cpufreq_cpu_callback(struct notifier_block *nfb, +static int cpufreq_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { unsigned int cpu = (unsigned long)hcpu; struct device *dev; + bool frozen = false; dev = get_cpu_device(cpu); if (dev) { - switch (action) { + + if (action & CPU_TASKS_FROZEN) + frozen = true; + + switch (action & ~CPU_TASKS_FROZEN) { case CPU_ONLINE: - case CPU_ONLINE_FROZEN: - cpufreq_add_dev(dev, NULL); + __cpufreq_add_dev(dev, NULL, frozen); + cpufreq_update_policy(cpu); break; + case CPU_DOWN_PREPARE: - case CPU_DOWN_PREPARE_FROZEN: - __cpufreq_remove_dev(dev, NULL); + __cpufreq_remove_dev_prepare(dev, NULL, frozen); break; + + case CPU_POST_DEAD: + __cpufreq_remove_dev_finish(dev, NULL, frozen); + break; + case CPU_DOWN_FAILED: - case CPU_DOWN_FAILED_FROZEN: - cpufreq_add_dev(dev, NULL); + __cpufreq_add_dev(dev, NULL, frozen); break; } } @@ -1910,7 +2133,7 @@ static int __cpuinit cpufreq_cpu_callback(struct notifier_block *nfb, } static struct notifier_block __refdata cpufreq_cpu_notifier = { - .notifier_call = cpufreq_cpu_callback, + .notifier_call = cpufreq_cpu_callback, }; /********************************************************************* @@ -1922,7 +2145,7 @@ static struct notifier_block __refdata cpufreq_cpu_notifier = { * @driver_data: A struct cpufreq_driver containing the values# * submitted by the CPU Frequency driver. * - * Registers a CPU Frequency driver to this core code. This code + * Registers a CPU Frequency driver to this core code. This code * returns zero on success, -EBUSY when another driver got here first * (and isn't unregistered in the meantime). * @@ -1936,7 +2159,8 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) return -ENODEV; if (!driver_data || !driver_data->verify || !driver_data->init || - ((!driver_data->setpolicy) && (!driver_data->target))) + !(driver_data->setpolicy || driver_data->target_index || + driver_data->target)) return -EINVAL; pr_debug("trying to register driver %s\n", driver_data->name); @@ -1947,7 +2171,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) write_lock_irqsave(&cpufreq_driver_lock, flags); if (cpufreq_driver) { write_unlock_irqrestore(&cpufreq_driver_lock, flags); - return -EBUSY; + return -EEXIST; } cpufreq_driver = driver_data; write_unlock_irqrestore(&cpufreq_driver_lock, flags); @@ -1989,11 +2213,10 @@ err_null_driver: } EXPORT_SYMBOL_GPL(cpufreq_register_driver); - /** * cpufreq_unregister_driver - unregister the current CPUFreq driver * - * Unregister the current CPUFreq driver. Only call this if you have + * Unregister the current CPUFreq driver. Only call this if you have * the right to do so, i.e. if you have succeeded in initialising before! * Returns zero if successful, and -EINVAL if the cpufreq_driver is * currently not initialised. @@ -2010,9 +2233,13 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver) subsys_interface_unregister(&cpufreq_interface); unregister_hotcpu_notifier(&cpufreq_cpu_notifier); + down_write(&cpufreq_rwsem); write_lock_irqsave(&cpufreq_driver_lock, flags); + cpufreq_driver = NULL; + write_unlock_irqrestore(&cpufreq_driver_lock, flags); + up_write(&cpufreq_rwsem); return 0; } @@ -2020,17 +2247,10 @@ EXPORT_SYMBOL_GPL(cpufreq_unregister_driver); static int __init cpufreq_core_init(void) { - int cpu; - if (cpufreq_disabled()) return -ENODEV; - for_each_possible_cpu(cpu) { - per_cpu(cpufreq_policy_cpu, cpu) = -1; - init_rwsem(&per_cpu(cpu_policy_rwsem, cpu)); - } - - cpufreq_global_kobject = kobject_create_and_add("cpufreq", &cpu_subsys.dev_root->kobj); + cpufreq_global_kobject = kobject_create(); BUG_ON(!cpufreq_global_kobject); register_syscore_ops(&cpufreq_syscore_ops); diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 0ceb2eff5a7e..25a70d06c5bf 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -11,19 +11,7 @@ * published by the Free Software Foundation. */ -#include <linux/cpufreq.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/kernel_stat.h> -#include <linux/kobject.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/notifier.h> -#include <linux/percpu-defs.h> #include <linux/slab.h> -#include <linux/sysfs.h> -#include <linux/types.h> - #include "cpufreq_governor.h" /* Conservative governor macros */ @@ -79,6 +67,7 @@ static void cs_check_cpu(int cpu, unsigned int load) return; dbs_info->requested_freq += get_freq_target(cs_tuners, policy); + if (dbs_info->requested_freq > policy->max) dbs_info->requested_freq = policy->max; @@ -94,14 +83,17 @@ static void cs_check_cpu(int cpu, unsigned int load) /* Check for frequency decrease */ if (load < cs_tuners->down_threshold) { + unsigned int freq_target; /* * if we cannot reduce the frequency anymore, break out early */ if (policy->cur == policy->min) return; - dbs_info->requested_freq -= get_freq_target(cs_tuners, policy); - if (dbs_info->requested_freq < policy->min) + freq_target = get_freq_target(cs_tuners, policy); + if (dbs_info->requested_freq > freq_target) + dbs_info->requested_freq -= freq_target; + else dbs_info->requested_freq = policy->min; __cpufreq_driver_target(policy, dbs_info->requested_freq, @@ -221,8 +213,8 @@ static ssize_t store_down_threshold(struct dbs_data *dbs_data, const char *buf, return count; } -static ssize_t store_ignore_nice(struct dbs_data *dbs_data, const char *buf, - size_t count) +static ssize_t store_ignore_nice_load(struct dbs_data *dbs_data, + const char *buf, size_t count) { struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; unsigned int input, j; @@ -235,10 +227,10 @@ static ssize_t store_ignore_nice(struct dbs_data *dbs_data, const char *buf, if (input > 1) input = 1; - if (input == cs_tuners->ignore_nice) /* nothing to do */ + if (input == cs_tuners->ignore_nice_load) /* nothing to do */ return count; - cs_tuners->ignore_nice = input; + cs_tuners->ignore_nice_load = input; /* we need to re-evaluate prev_cpu_idle */ for_each_online_cpu(j) { @@ -246,7 +238,7 @@ static ssize_t store_ignore_nice(struct dbs_data *dbs_data, const char *buf, dbs_info = &per_cpu(cs_cpu_dbs_info, j); dbs_info->cdbs.prev_cpu_idle = get_cpu_idle_time(j, &dbs_info->cdbs.prev_cpu_wall, 0); - if (cs_tuners->ignore_nice) + if (cs_tuners->ignore_nice_load) dbs_info->cdbs.prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; } @@ -279,7 +271,7 @@ show_store_one(cs, sampling_rate); show_store_one(cs, sampling_down_factor); show_store_one(cs, up_threshold); show_store_one(cs, down_threshold); -show_store_one(cs, ignore_nice); +show_store_one(cs, ignore_nice_load); show_store_one(cs, freq_step); declare_show_sampling_rate_min(cs); @@ -287,7 +279,7 @@ gov_sys_pol_attr_rw(sampling_rate); gov_sys_pol_attr_rw(sampling_down_factor); gov_sys_pol_attr_rw(up_threshold); gov_sys_pol_attr_rw(down_threshold); -gov_sys_pol_attr_rw(ignore_nice); +gov_sys_pol_attr_rw(ignore_nice_load); gov_sys_pol_attr_rw(freq_step); gov_sys_pol_attr_ro(sampling_rate_min); @@ -297,7 +289,7 @@ static struct attribute *dbs_attributes_gov_sys[] = { &sampling_down_factor_gov_sys.attr, &up_threshold_gov_sys.attr, &down_threshold_gov_sys.attr, - &ignore_nice_gov_sys.attr, + &ignore_nice_load_gov_sys.attr, &freq_step_gov_sys.attr, NULL }; @@ -313,7 +305,7 @@ static struct attribute *dbs_attributes_gov_pol[] = { &sampling_down_factor_gov_pol.attr, &up_threshold_gov_pol.attr, &down_threshold_gov_pol.attr, - &ignore_nice_gov_pol.attr, + &ignore_nice_load_gov_pol.attr, &freq_step_gov_pol.attr, NULL }; @@ -329,7 +321,7 @@ static int cs_init(struct dbs_data *dbs_data) { struct cs_dbs_tuners *tuners; - tuners = kzalloc(sizeof(struct cs_dbs_tuners), GFP_KERNEL); + tuners = kzalloc(sizeof(*tuners), GFP_KERNEL); if (!tuners) { pr_err("%s: kzalloc failed\n", __func__); return -ENOMEM; @@ -338,7 +330,7 @@ static int cs_init(struct dbs_data *dbs_data) tuners->up_threshold = DEF_FREQUENCY_UP_THRESHOLD; tuners->down_threshold = DEF_FREQUENCY_DOWN_THRESHOLD; tuners->sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR; - tuners->ignore_nice = 0; + tuners->ignore_nice_load = 0; tuners->freq_step = DEF_FREQUENCY_STEP; dbs_data->tuners = tuners; diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index ca21101a4fed..e6be63561fa6 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -16,27 +16,12 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <asm/cputime.h> -#include <linux/cpufreq.h> -#include <linux/cpumask.h> #include <linux/export.h> #include <linux/kernel_stat.h> -#include <linux/mutex.h> #include <linux/slab.h> -#include <linux/tick.h> -#include <linux/types.h> -#include <linux/workqueue.h> #include "cpufreq_governor.h" -static struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy) -{ - if (have_governor_per_policy()) - return &policy->kobj; - else - return cpufreq_global_kobject; -} - static struct attribute_group *get_sysfs_attr(struct dbs_data *dbs_data) { if (have_governor_per_policy()) @@ -45,41 +30,6 @@ static struct attribute_group *get_sysfs_attr(struct dbs_data *dbs_data) return dbs_data->cdata->attr_group_gov_sys; } -static inline u64 get_cpu_idle_time_jiffy(unsigned int cpu, u64 *wall) -{ - u64 idle_time; - u64 cur_wall_time; - u64 busy_time; - - cur_wall_time = jiffies64_to_cputime64(get_jiffies_64()); - - busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE]; - - idle_time = cur_wall_time - busy_time; - if (wall) - *wall = cputime_to_usecs(cur_wall_time); - - return cputime_to_usecs(idle_time); -} - -u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy) -{ - u64 idle_time = get_cpu_idle_time_us(cpu, io_busy ? wall : NULL); - - if (idle_time == -1ULL) - return get_cpu_idle_time_jiffy(cpu, wall); - else if (!io_busy) - idle_time += get_cpu_iowait_time_us(cpu, wall); - - return idle_time; -} -EXPORT_SYMBOL_GPL(get_cpu_idle_time); - void dbs_check_cpu(struct dbs_data *dbs_data, int cpu) { struct cpu_dbs_common_info *cdbs = dbs_data->cdata->get_cpu_cdbs(cpu); @@ -91,13 +41,13 @@ void dbs_check_cpu(struct dbs_data *dbs_data, int cpu) unsigned int j; if (dbs_data->cdata->governor == GOV_ONDEMAND) - ignore_nice = od_tuners->ignore_nice; + ignore_nice = od_tuners->ignore_nice_load; else - ignore_nice = cs_tuners->ignore_nice; + ignore_nice = cs_tuners->ignore_nice_load; policy = cdbs->cur_policy; - /* Get Absolute Load (in terms of freq for ondemand gov) */ + /* Get Absolute Load */ for_each_cpu(j, policy->cpus) { struct cpu_dbs_common_info *j_cdbs; u64 cur_wall_time, cur_idle_time; @@ -148,14 +98,6 @@ void dbs_check_cpu(struct dbs_data *dbs_data, int cpu) load = 100 * (wall_time - idle_time) / wall_time; - if (dbs_data->cdata->governor == GOV_ONDEMAND) { - int freq_avg = __cpufreq_driver_getavg(policy, j); - if (freq_avg <= 0) - freq_avg = policy->cur; - - load *= freq_avg; - } - if (load > max_load) max_load = load; } @@ -285,6 +227,9 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, return rc; } + if (!have_governor_per_policy()) + WARN_ON(cpufreq_get_global_kobject()); + rc = sysfs_create_group(get_governor_parent_kobj(policy), get_sysfs_attr(dbs_data)); if (rc) { @@ -295,7 +240,7 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, policy->governor_data = dbs_data; - /* policy latency is in nS. Convert it to uS first */ + /* policy latency is in ns. Convert it to us first */ latency = policy->cpuinfo.transition_latency / 1000; if (latency == 0) latency = 1; @@ -323,6 +268,9 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, sysfs_remove_group(get_governor_parent_kobj(policy), get_sysfs_attr(dbs_data)); + if (!have_governor_per_policy()) + cpufreq_put_global_kobject(); + if ((dbs_data->cdata->governor == GOV_CONSERVATIVE) && (policy->governor->initialized == 1)) { struct cs_ops *cs_ops = dbs_data->cdata->gov_ops; @@ -346,12 +294,12 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, cs_tuners = dbs_data->tuners; cs_dbs_info = dbs_data->cdata->get_cpu_dbs_info_s(cpu); sampling_rate = cs_tuners->sampling_rate; - ignore_nice = cs_tuners->ignore_nice; + ignore_nice = cs_tuners->ignore_nice_load; } else { od_tuners = dbs_data->tuners; od_dbs_info = dbs_data->cdata->get_cpu_dbs_info_s(cpu); sampling_rate = od_tuners->sampling_rate; - ignore_nice = od_tuners->ignore_nice; + ignore_nice = od_tuners->ignore_nice_load; od_ops = dbs_data->cdata->gov_ops; io_busy = od_tuners->io_is_busy; } @@ -380,10 +328,6 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, dbs_data->cdata->gov_dbs_timer); } - /* - * conservative does not implement micro like ondemand - * governor, thus we are bound to jiffes/HZ - */ if (dbs_data->cdata->governor == GOV_CONSERVATIVE) { cs_dbs_info->down_skip = 0; cs_dbs_info->enable = 1; diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h index e16a96130cb3..b5f2b8618949 100644 --- a/drivers/cpufreq/cpufreq_governor.h +++ b/drivers/cpufreq/cpufreq_governor.h @@ -18,19 +18,18 @@ #define _CPUFREQ_GOVERNOR_H #include <linux/cpufreq.h> -#include <linux/kobject.h> +#include <linux/kernel_stat.h> +#include <linux/module.h> #include <linux/mutex.h> -#include <linux/workqueue.h> -#include <linux/sysfs.h> /* * The polling frequency depends on the capability of the processor. Default * polling frequency is 1000 times the transition latency of the processor. The - * governor will work on any processor with transition latency <= 10mS, using + * governor will work on any processor with transition latency <= 10ms, using * appropriate sampling rate. * - * For CPUs with transition latency > 10mS (mostly drivers with CPUFREQ_ETERNAL) - * this governor will not work. All times here are in uS. + * For CPUs with transition latency > 10ms (mostly drivers with CPUFREQ_ETERNAL) + * this governor will not work. All times here are in us (micro seconds). */ #define MIN_SAMPLING_RATE_RATIO (2) #define LATENCY_MULTIPLIER (1000) @@ -81,7 +80,7 @@ static ssize_t show_##file_name##_gov_sys \ return sprintf(buf, "%u\n", tuners->file_name); \ } \ \ -static ssize_t show_##file_name##_gov_pol \ +static ssize_t show_##file_name##_gov_pol \ (struct cpufreq_policy *policy, char *buf) \ { \ struct dbs_data *dbs_data = policy->governor_data; \ @@ -91,7 +90,7 @@ static ssize_t show_##file_name##_gov_pol \ #define store_one(_gov, file_name) \ static ssize_t store_##file_name##_gov_sys \ -(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) \ +(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) \ { \ struct dbs_data *dbs_data = _gov##_dbs_cdata.gdbs_data; \ return store_##file_name(dbs_data, buf, count); \ @@ -163,19 +162,18 @@ struct cs_cpu_dbs_info_s { unsigned int enable:1; }; -/* Per policy Governers sysfs tunables */ +/* Per policy Governors sysfs tunables */ struct od_dbs_tuners { - unsigned int ignore_nice; + unsigned int ignore_nice_load; unsigned int sampling_rate; unsigned int sampling_down_factor; unsigned int up_threshold; - unsigned int adj_up_threshold; unsigned int powersave_bias; unsigned int io_is_busy; }; struct cs_dbs_tuners { - unsigned int ignore_nice; + unsigned int ignore_nice_load; unsigned int sampling_rate; unsigned int sampling_down_factor; unsigned int up_threshold; @@ -183,7 +181,7 @@ struct cs_dbs_tuners { unsigned int freq_step; }; -/* Common Governer data across policies */ +/* Common Governor data across policies */ struct dbs_data; struct common_dbs_data { /* Common across governors */ @@ -193,7 +191,10 @@ struct common_dbs_data { struct attribute_group *attr_group_gov_sys; /* one governor - system */ struct attribute_group *attr_group_gov_pol; /* one governor - policy */ - /* Common data for platforms that don't set have_governor_per_policy */ + /* + * Common data for platforms that don't set + * CPUFREQ_HAVE_GOVERNOR_PER_POLICY + */ struct dbs_data *gdbs_data; struct cpu_dbs_common_info *(*get_cpu_cdbs)(int cpu); @@ -207,7 +208,7 @@ struct common_dbs_data { void *gov_ops; }; -/* Governer Per policy data */ +/* Governor Per policy data */ struct dbs_data { struct common_dbs_data *cdata; unsigned int min_sampling_rate; @@ -223,7 +224,7 @@ struct od_ops { void (*powersave_bias_init_cpu)(int cpu); unsigned int (*powersave_bias_target)(struct cpufreq_policy *policy, unsigned int freq_next, unsigned int relation); - void (*freq_increase)(struct cpufreq_policy *p, unsigned int freq); + void (*freq_increase)(struct cpufreq_policy *policy, unsigned int freq); }; struct cs_ops { @@ -256,7 +257,6 @@ static ssize_t show_sampling_rate_min_gov_pol \ return sprintf(buf, "%u\n", dbs_data->min_sampling_rate); \ } -u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy); void dbs_check_cpu(struct dbs_data *dbs_data, int cpu); bool need_load_eval(struct cpu_dbs_common_info *cdbs, unsigned int sampling_rate); diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 8feae37183c7..e79e0cca6457 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -144,42 +144,6 @@ struct cpufreq_governor cpufreq_gov_interactive = { .owner = THIS_MODULE, }; -static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu, - cputime64_t *wall) -{ - u64 idle_time; - u64 cur_wall_time; - u64 busy_time; - - cur_wall_time = jiffies64_to_cputime64(get_jiffies_64()); - - busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE]; - - idle_time = cur_wall_time - busy_time; - if (wall) - *wall = jiffies_to_usecs(cur_wall_time); - - return jiffies_to_usecs(idle_time); -} - -static inline cputime64_t get_cpu_idle_time(unsigned int cpu, - cputime64_t *wall) -{ - u64 idle_time = get_cpu_idle_time_us(cpu, wall); - - if (idle_time == -1ULL) - idle_time = get_cpu_idle_time_jiffy(cpu, wall); - else if (!io_is_busy) - idle_time += get_cpu_iowait_time_us(cpu, wall); - - return idle_time; -} - static void cpufreq_interactive_timer_resched( struct cpufreq_interactive_cpuinfo *pcpu) { @@ -189,7 +153,7 @@ static void cpufreq_interactive_timer_resched( spin_lock_irqsave(&pcpu->load_lock, flags); pcpu->time_in_idle = get_cpu_idle_time(smp_processor_id(), - &pcpu->time_in_idle_timestamp); + &pcpu->time_in_idle_timestamp, io_is_busy); pcpu->cputime_speedadj = 0; pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; expires = jiffies + usecs_to_jiffies(timer_rate); @@ -223,7 +187,8 @@ static void cpufreq_interactive_timer_start(int cpu) spin_lock_irqsave(&pcpu->load_lock, flags); pcpu->time_in_idle = - get_cpu_idle_time(cpu, &pcpu->time_in_idle_timestamp); + get_cpu_idle_time(cpu, &pcpu->time_in_idle_timestamp, + io_is_busy); pcpu->cputime_speedadj = 0; pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; spin_unlock_irqrestore(&pcpu->load_lock, flags); @@ -364,7 +329,7 @@ static u64 update_load(int cpu) unsigned int delta_time; u64 active_time; - now_idle = get_cpu_idle_time(cpu, &now); + now_idle = get_cpu_idle_time(cpu, &now, io_is_busy); delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle); delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp); @@ -1234,7 +1199,10 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, return 0; } - rc = sysfs_create_group(cpufreq_global_kobject, + if (!have_governor_per_policy()) + WARN_ON(cpufreq_get_global_kobject()); + + rc = sysfs_create_group(get_governor_parent_kobj(policy), &interactive_attr_group); if (rc) { mutex_unlock(&gov_lock); @@ -1266,8 +1234,10 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, cpufreq_unregister_notifier( &cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); idle_notifier_unregister(&cpufreq_interactive_idle_nb); - sysfs_remove_group(cpufreq_global_kobject, + sysfs_remove_group(get_governor_parent_kobj(policy), &interactive_attr_group); + if (!have_governor_per_policy()) + cpufreq_put_global_kobject(); mutex_unlock(&gov_lock); break; diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 93eb5cbcc1f6..18d409189092 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -12,28 +12,16 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/cpufreq.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/kernel_stat.h> -#include <linux/kobject.h> -#include <linux/module.h> -#include <linux/mutex.h> +#include <linux/cpu.h> #include <linux/percpu-defs.h> #include <linux/slab.h> -#include <linux/sysfs.h> #include <linux/tick.h> -#include <linux/types.h> -#include <linux/cpu.h> - #include "cpufreq_governor.h" /* On-demand governor macros */ -#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10) #define DEF_FREQUENCY_UP_THRESHOLD (80) #define DEF_SAMPLING_DOWN_FACTOR (1) #define MAX_SAMPLING_DOWN_FACTOR (100000) -#define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3) #define MICRO_FREQUENCY_UP_THRESHOLD (95) #define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000) #define MIN_FREQUENCY_UP_THRESHOLD (11) @@ -144,31 +132,27 @@ static void ondemand_powersave_bias_init(void) } } -static void dbs_freq_increase(struct cpufreq_policy *p, unsigned int freq) +static void dbs_freq_increase(struct cpufreq_policy *policy, unsigned int freq) { - struct dbs_data *dbs_data = p->governor_data; + struct dbs_data *dbs_data = policy->governor_data; struct od_dbs_tuners *od_tuners = dbs_data->tuners; if (od_tuners->powersave_bias) - freq = od_ops.powersave_bias_target(p, freq, + freq = od_ops.powersave_bias_target(policy, freq, CPUFREQ_RELATION_H); - else if (p->cur == p->max) + else if (policy->cur == policy->max) return; - __cpufreq_driver_target(p, freq, od_tuners->powersave_bias ? + __cpufreq_driver_target(policy, freq, od_tuners->powersave_bias ? CPUFREQ_RELATION_L : CPUFREQ_RELATION_H); } /* * Every sampling_rate, we check, if current idle time is less than 20% - * (default), then we try to increase frequency. Every sampling_rate, we look - * for the lowest frequency which can sustain the load while keeping idle time - * over 30%. If such a frequency exist, we try to decrease to this frequency. - * - * Any frequency increase takes it to the maximum frequency. Frequency reduction - * happens at minimum steps of 5% (default) of current frequency + * (default), then we try to increase frequency. Else, we adjust the frequency + * proportional to load. */ -static void od_check_cpu(int cpu, unsigned int load_freq) +static void od_check_cpu(int cpu, unsigned int load) { struct od_cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, cpu); struct cpufreq_policy *policy = dbs_info->cdbs.cur_policy; @@ -178,36 +162,20 @@ static void od_check_cpu(int cpu, unsigned int load_freq) dbs_info->freq_lo = 0; /* Check for frequency increase */ - if (load_freq > od_tuners->up_threshold * policy->cur) { + if (load > od_tuners->up_threshold) { /* If switching to max speed, apply sampling_down_factor */ if (policy->cur < policy->max) dbs_info->rate_mult = od_tuners->sampling_down_factor; dbs_freq_increase(policy, policy->max); - return; - } - - /* Check for frequency decrease */ - /* if we cannot reduce the frequency anymore, break out early */ - if (policy->cur == policy->min) - return; - - /* - * The optimal frequency is the frequency that is the lowest that can - * support the current CPU usage without triggering the up policy. To be - * safe, we focus 10 points under the threshold. - */ - if (load_freq < od_tuners->adj_up_threshold - * policy->cur) { + } else { + /* Calculate the next frequency proportional to load */ unsigned int freq_next; - freq_next = load_freq / od_tuners->adj_up_threshold; + freq_next = load * policy->cpuinfo.max_freq / 100; /* No longer fully busy, reset rate_mult */ dbs_info->rate_mult = 1; - if (freq_next < policy->min) - freq_next = policy->min; - if (!od_tuners->powersave_bias) { __cpufreq_driver_target(policy, freq_next, CPUFREQ_RELATION_L); @@ -374,9 +342,6 @@ static ssize_t store_up_threshold(struct dbs_data *dbs_data, const char *buf, input < MIN_FREQUENCY_UP_THRESHOLD) { return -EINVAL; } - /* Calculate the new adj_up_threshold */ - od_tuners->adj_up_threshold += input; - od_tuners->adj_up_threshold -= od_tuners->up_threshold; od_tuners->up_threshold = input; return count; @@ -403,8 +368,8 @@ static ssize_t store_sampling_down_factor(struct dbs_data *dbs_data, return count; } -static ssize_t store_ignore_nice(struct dbs_data *dbs_data, const char *buf, - size_t count) +static ssize_t store_ignore_nice_load(struct dbs_data *dbs_data, + const char *buf, size_t count) { struct od_dbs_tuners *od_tuners = dbs_data->tuners; unsigned int input; @@ -419,10 +384,10 @@ static ssize_t store_ignore_nice(struct dbs_data *dbs_data, const char *buf, if (input > 1) input = 1; - if (input == od_tuners->ignore_nice) { /* nothing to do */ + if (input == od_tuners->ignore_nice_load) { /* nothing to do */ return count; } - od_tuners->ignore_nice = input; + od_tuners->ignore_nice_load = input; /* we need to re-evaluate prev_cpu_idle */ for_each_online_cpu(j) { @@ -430,7 +395,7 @@ static ssize_t store_ignore_nice(struct dbs_data *dbs_data, const char *buf, dbs_info = &per_cpu(od_cpu_dbs_info, j); dbs_info->cdbs.prev_cpu_idle = get_cpu_idle_time(j, &dbs_info->cdbs.prev_cpu_wall, od_tuners->io_is_busy); - if (od_tuners->ignore_nice) + if (od_tuners->ignore_nice_load) dbs_info->cdbs.prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; @@ -461,7 +426,7 @@ show_store_one(od, sampling_rate); show_store_one(od, io_is_busy); show_store_one(od, up_threshold); show_store_one(od, sampling_down_factor); -show_store_one(od, ignore_nice); +show_store_one(od, ignore_nice_load); show_store_one(od, powersave_bias); declare_show_sampling_rate_min(od); @@ -469,7 +434,7 @@ gov_sys_pol_attr_rw(sampling_rate); gov_sys_pol_attr_rw(io_is_busy); gov_sys_pol_attr_rw(up_threshold); gov_sys_pol_attr_rw(sampling_down_factor); -gov_sys_pol_attr_rw(ignore_nice); +gov_sys_pol_attr_rw(ignore_nice_load); gov_sys_pol_attr_rw(powersave_bias); gov_sys_pol_attr_ro(sampling_rate_min); @@ -478,7 +443,7 @@ static struct attribute *dbs_attributes_gov_sys[] = { &sampling_rate_gov_sys.attr, &up_threshold_gov_sys.attr, &sampling_down_factor_gov_sys.attr, - &ignore_nice_gov_sys.attr, + &ignore_nice_load_gov_sys.attr, &powersave_bias_gov_sys.attr, &io_is_busy_gov_sys.attr, NULL @@ -494,7 +459,7 @@ static struct attribute *dbs_attributes_gov_pol[] = { &sampling_rate_gov_pol.attr, &up_threshold_gov_pol.attr, &sampling_down_factor_gov_pol.attr, - &ignore_nice_gov_pol.attr, + &ignore_nice_load_gov_pol.attr, &powersave_bias_gov_pol.attr, &io_is_busy_gov_pol.attr, NULL @@ -513,7 +478,7 @@ static int od_init(struct dbs_data *dbs_data) u64 idle_time; int cpu; - tuners = kzalloc(sizeof(struct od_dbs_tuners), GFP_KERNEL); + tuners = kzalloc(sizeof(*tuners), GFP_KERNEL); if (!tuners) { pr_err("%s: kzalloc failed\n", __func__); return -ENOMEM; @@ -525,8 +490,6 @@ static int od_init(struct dbs_data *dbs_data) if (idle_time != -1ULL) { /* Idle micro accounting is supported. Use finer thresholds */ tuners->up_threshold = MICRO_FREQUENCY_UP_THRESHOLD; - tuners->adj_up_threshold = MICRO_FREQUENCY_UP_THRESHOLD - - MICRO_FREQUENCY_DOWN_DIFFERENTIAL; /* * In nohz/micro accounting case we set the minimum frequency * not depending on HZ, but fixed (very low). The deferred @@ -535,8 +498,6 @@ static int od_init(struct dbs_data *dbs_data) dbs_data->min_sampling_rate = MICRO_FREQUENCY_MIN_SAMPLE_RATE; } else { tuners->up_threshold = DEF_FREQUENCY_UP_THRESHOLD; - tuners->adj_up_threshold = DEF_FREQUENCY_UP_THRESHOLD - - DEF_FREQUENCY_DOWN_DIFFERENTIAL; /* For correct statistics, we need 10 ticks for each measure */ dbs_data->min_sampling_rate = MIN_SAMPLING_RATE_RATIO * @@ -544,7 +505,7 @@ static int od_init(struct dbs_data *dbs_data) } tuners->sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR; - tuners->ignore_nice = 0; + tuners->ignore_nice_load = 0; tuners->powersave_bias = default_powersave_bias; tuners->io_is_busy = should_io_be_busy(); diff --git a/drivers/cpufreq/cpufreq_performance.c b/drivers/cpufreq/cpufreq_performance.c index ceee06849b91..cf117deb39b1 100644 --- a/drivers/cpufreq/cpufreq_performance.c +++ b/drivers/cpufreq/cpufreq_performance.c @@ -12,11 +12,9 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/kernel.h> -#include <linux/module.h> #include <linux/cpufreq.h> #include <linux/init.h> - +#include <linux/module.h> static int cpufreq_governor_performance(struct cpufreq_policy *policy, unsigned int event) @@ -44,19 +42,16 @@ struct cpufreq_governor cpufreq_gov_performance = { .owner = THIS_MODULE, }; - static int __init cpufreq_gov_performance_init(void) { return cpufreq_register_governor(&cpufreq_gov_performance); } - static void __exit cpufreq_gov_performance_exit(void) { cpufreq_unregister_governor(&cpufreq_gov_performance); } - MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>"); MODULE_DESCRIPTION("CPUfreq policy governor 'performance'"); MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/cpufreq_powersave.c b/drivers/cpufreq/cpufreq_powersave.c index 2d948a171155..e3b874c235ea 100644 --- a/drivers/cpufreq/cpufreq_powersave.c +++ b/drivers/cpufreq/cpufreq_powersave.c @@ -1,7 +1,7 @@ /* - * linux/drivers/cpufreq/cpufreq_powersave.c + * linux/drivers/cpufreq/cpufreq_powersave.c * - * Copyright (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> + * Copyright (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> * * * This program is free software; you can redistribute it and/or modify @@ -12,10 +12,9 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/kernel.h> -#include <linux/module.h> #include <linux/cpufreq.h> #include <linux/init.h> +#include <linux/module.h> static int cpufreq_governor_powersave(struct cpufreq_policy *policy, unsigned int event) @@ -48,13 +47,11 @@ static int __init cpufreq_gov_powersave_init(void) return cpufreq_register_governor(&cpufreq_gov_powersave); } - static void __exit cpufreq_gov_powersave_exit(void) { cpufreq_unregister_governor(&cpufreq_gov_powersave); } - MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>"); MODULE_DESCRIPTION("CPUfreq policy governor 'powersave'"); MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 6c287ae7bbba..de23310061f9 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -9,17 +9,10 @@ * published by the Free Software Foundation. */ -#include <linux/kernel.h> -#include <linux/slab.h> #include <linux/cpu.h> -#include <linux/sysfs.h> #include <linux/cpufreq.h> #include <linux/module.h> -#include <linux/jiffies.h> -#include <linux/percpu.h> -#include <linux/kobject.h> -#include <linux/spinlock.h> -#include <linux/notifier.h> +#include <linux/slab.h> #include <asm/cputime.h> static spinlock_t cpufreq_stats_lock; @@ -27,7 +20,7 @@ static spinlock_t cpufreq_stats_lock; struct cpufreq_stats { unsigned int cpu; unsigned int total_trans; - unsigned long long last_time; + unsigned long long last_time; unsigned int max_state; unsigned int state_num; unsigned int last_index; @@ -81,7 +74,7 @@ static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf) for (i = 0; i < stat->state_num; i++) { len += sprintf(buf + len, "%u %llu\n", stat->freq_table[i], (unsigned long long) - cputime64_to_clock_t(stat->time_in_state[i])); + jiffies_64_to_clock_t(stat->time_in_state[i])); } return len; } @@ -116,7 +109,7 @@ static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf) len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ", stat->freq_table[i]); - for (j = 0; j < stat->state_num; j++) { + for (j = 0; j < stat->state_num; j++) { if (len >= PAGE_SIZE) break; len += snprintf(buf + len, PAGE_SIZE - len, "%9u ", @@ -200,22 +193,22 @@ static int cpufreq_stats_create_table(struct cpufreq_policy *policy, { unsigned int i, j, count = 0, ret = 0; struct cpufreq_stats *stat; - struct cpufreq_policy *data; + struct cpufreq_policy *current_policy; unsigned int alloc_size; unsigned int cpu = policy->cpu; if (per_cpu(cpufreq_stats_table, cpu)) return -EBUSY; - stat = kzalloc(sizeof(struct cpufreq_stats), GFP_KERNEL); + stat = kzalloc(sizeof(*stat), GFP_KERNEL); if ((stat) == NULL) return -ENOMEM; - data = cpufreq_cpu_get(cpu); - if (data == NULL) { + current_policy = cpufreq_cpu_get(cpu); + if (current_policy == NULL) { ret = -EINVAL; goto error_get_fail; } - ret = sysfs_create_group(&data->kobj, &stats_attr_group); + ret = sysfs_create_group(¤t_policy->kobj, &stats_attr_group); if (ret) goto error_out; @@ -258,10 +251,10 @@ static int cpufreq_stats_create_table(struct cpufreq_policy *policy, stat->last_time = get_jiffies_64(); stat->last_index = freq_table_get_index(stat, policy->cur); spin_unlock(&cpufreq_stats_lock); - cpufreq_cpu_put(data); + cpufreq_cpu_put(current_policy); return 0; error_out: - cpufreq_cpu_put(data); + cpufreq_cpu_put(current_policy); error_get_fail: kfree(stat); per_cpu(cpufreq_stats_table, cpu) = NULL; @@ -362,23 +355,17 @@ out: return ret; } -static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, +static int cpufreq_stat_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { unsigned int cpu = (unsigned long)hcpu; switch (action) { - case CPU_ONLINE: - case CPU_ONLINE_FROZEN: - cpufreq_update_policy(cpu); - break; case CPU_DOWN_PREPARE: - case CPU_DOWN_PREPARE_FROZEN: cpufreq_stats_free_sysfs(cpu); break; case CPU_DEAD: - case CPU_DEAD_FROZEN: cpufreq_stats_free_table(cpu); break; case CPU_DOWN_FAILED: @@ -415,8 +402,6 @@ static int __init cpufreq_stats_init(void) return ret; register_hotcpu_notifier(&cpufreq_stat_cpu_notifier); - for_each_online_cpu(cpu) - cpufreq_update_policy(cpu); ret = cpufreq_register_notifier(¬ifier_trans_block, CPUFREQ_TRANSITION_NOTIFIER); diff --git a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c index bbeb9c0720a6..4dbf1db16aca 100644 --- a/drivers/cpufreq/cpufreq_userspace.c +++ b/drivers/cpufreq/cpufreq_userspace.c @@ -13,55 +13,13 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/smp.h> -#include <linux/init.h> -#include <linux/spinlock.h> -#include <linux/interrupt.h> #include <linux/cpufreq.h> -#include <linux/cpu.h> -#include <linux/types.h> -#include <linux/fs.h> -#include <linux/sysfs.h> +#include <linux/init.h> +#include <linux/module.h> #include <linux/mutex.h> -/** - * A few values needed by the userspace governor - */ -static DEFINE_PER_CPU(unsigned int, cpu_max_freq); -static DEFINE_PER_CPU(unsigned int, cpu_min_freq); -static DEFINE_PER_CPU(unsigned int, cpu_cur_freq); /* current CPU freq */ -static DEFINE_PER_CPU(unsigned int, cpu_set_freq); /* CPU freq desired by - userspace */ static DEFINE_PER_CPU(unsigned int, cpu_is_managed); - static DEFINE_MUTEX(userspace_mutex); -static int cpus_using_userspace_governor; - -/* keep track of frequency transitions */ -static int -userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val, - void *data) -{ - struct cpufreq_freqs *freq = data; - - if (!per_cpu(cpu_is_managed, freq->cpu)) - return 0; - - if (val == CPUFREQ_POSTCHANGE) { - pr_debug("saving cpu_cur_freq of cpu %u to be %u kHz\n", - freq->cpu, freq->new); - per_cpu(cpu_cur_freq, freq->cpu) = freq->new; - } - - return 0; -} - -static struct notifier_block userspace_cpufreq_notifier_block = { - .notifier_call = userspace_cpufreq_notifier -}; - /** * cpufreq_set - set the CPU frequency @@ -80,34 +38,15 @@ static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq) if (!per_cpu(cpu_is_managed, policy->cpu)) goto err; - per_cpu(cpu_set_freq, policy->cpu) = freq; - - if (freq < per_cpu(cpu_min_freq, policy->cpu)) - freq = per_cpu(cpu_min_freq, policy->cpu); - if (freq > per_cpu(cpu_max_freq, policy->cpu)) - freq = per_cpu(cpu_max_freq, policy->cpu); - - /* - * We're safe from concurrent calls to ->target() here - * as we hold the userspace_mutex lock. If we were calling - * cpufreq_driver_target, a deadlock situation might occur: - * A: cpufreq_set (lock userspace_mutex) -> - * cpufreq_driver_target(lock policy->lock) - * B: cpufreq_set_policy(lock policy->lock) -> - * __cpufreq_governor -> - * cpufreq_governor_userspace (lock userspace_mutex) - */ ret = __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L); - err: mutex_unlock(&userspace_mutex); return ret; } - static ssize_t show_speed(struct cpufreq_policy *policy, char *buf) { - return sprintf(buf, "%u\n", per_cpu(cpu_cur_freq, policy->cpu)); + return sprintf(buf, "%u\n", policy->cur); } static int cpufreq_governor_userspace(struct cpufreq_policy *policy, @@ -119,73 +58,37 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy, switch (event) { case CPUFREQ_GOV_START: BUG_ON(!policy->cur); - mutex_lock(&userspace_mutex); - - if (cpus_using_userspace_governor == 0) { - cpufreq_register_notifier( - &userspace_cpufreq_notifier_block, - CPUFREQ_TRANSITION_NOTIFIER); - } - cpus_using_userspace_governor++; + pr_debug("started managing cpu %u\n", cpu); + mutex_lock(&userspace_mutex); per_cpu(cpu_is_managed, cpu) = 1; - per_cpu(cpu_min_freq, cpu) = policy->min; - per_cpu(cpu_max_freq, cpu) = policy->max; - per_cpu(cpu_cur_freq, cpu) = policy->cur; - per_cpu(cpu_set_freq, cpu) = policy->cur; - pr_debug("managing cpu %u started " - "(%u - %u kHz, currently %u kHz)\n", - cpu, - per_cpu(cpu_min_freq, cpu), - per_cpu(cpu_max_freq, cpu), - per_cpu(cpu_cur_freq, cpu)); - mutex_unlock(&userspace_mutex); break; case CPUFREQ_GOV_STOP: - mutex_lock(&userspace_mutex); - cpus_using_userspace_governor--; - if (cpus_using_userspace_governor == 0) { - cpufreq_unregister_notifier( - &userspace_cpufreq_notifier_block, - CPUFREQ_TRANSITION_NOTIFIER); - } + pr_debug("managing cpu %u stopped\n", cpu); + mutex_lock(&userspace_mutex); per_cpu(cpu_is_managed, cpu) = 0; - per_cpu(cpu_min_freq, cpu) = 0; - per_cpu(cpu_max_freq, cpu) = 0; - per_cpu(cpu_set_freq, cpu) = 0; - pr_debug("managing cpu %u stopped\n", cpu); mutex_unlock(&userspace_mutex); break; case CPUFREQ_GOV_LIMITS: mutex_lock(&userspace_mutex); - pr_debug("limit event for cpu %u: %u - %u kHz, " - "currently %u kHz, last set to %u kHz\n", + pr_debug("limit event for cpu %u: %u - %u kHz, currently %u kHz\n", cpu, policy->min, policy->max, - per_cpu(cpu_cur_freq, cpu), - per_cpu(cpu_set_freq, cpu)); - if (policy->max < per_cpu(cpu_set_freq, cpu)) { + policy->cur); + + if (policy->max < policy->cur) __cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H); - } else if (policy->min > per_cpu(cpu_set_freq, cpu)) { + else if (policy->min > policy->cur) __cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L); - } else { - __cpufreq_driver_target(policy, - per_cpu(cpu_set_freq, cpu), - CPUFREQ_RELATION_L); - } - per_cpu(cpu_min_freq, cpu) = policy->min; - per_cpu(cpu_max_freq, cpu) = policy->max; - per_cpu(cpu_cur_freq, cpu) = policy->cur; mutex_unlock(&userspace_mutex); break; } return rc; } - #ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE static #endif @@ -202,13 +105,11 @@ static int __init cpufreq_gov_userspace_init(void) return cpufreq_register_governor(&cpufreq_gov_userspace); } - static void __exit cpufreq_gov_userspace_exit(void) { cpufreq_unregister_governor(&cpufreq_gov_userspace); } - MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>, " "Russell King <rmk@arm.linux.org.uk>"); MODULE_DESCRIPTION("CPUfreq policy governor 'userspace'"); diff --git a/drivers/cpufreq/cris-artpec3-cpufreq.c b/drivers/cpufreq/cris-artpec3-cpufreq.c index ee142c490575..cb8276dd19ca 100644 --- a/drivers/cpufreq/cris-artpec3-cpufreq.c +++ b/drivers/cpufreq/cris-artpec3-cpufreq.c @@ -111,7 +111,6 @@ static struct cpufreq_driver cris_freq_driver = { .init = cris_freq_cpu_init, .exit = cris_freq_cpu_exit, .name = "cris_freq", - .owner = THIS_MODULE, .attr = cris_freq_attr, }; diff --git a/drivers/cpufreq/cris-etraxfs-cpufreq.c b/drivers/cpufreq/cris-etraxfs-cpufreq.c index 12952235d5db..72328f77dc53 100644 --- a/drivers/cpufreq/cris-etraxfs-cpufreq.c +++ b/drivers/cpufreq/cris-etraxfs-cpufreq.c @@ -108,7 +108,6 @@ static struct cpufreq_driver cris_freq_driver = { .init = cris_freq_cpu_init, .exit = cris_freq_cpu_exit, .name = "cris_freq", - .owner = THIS_MODULE, .attr = cris_freq_attr, }; diff --git a/drivers/cpufreq/davinci-cpufreq.c b/drivers/cpufreq/davinci-cpufreq.c index c33c76c360fa..9b4c58bda31d 100644 --- a/drivers/cpufreq/davinci-cpufreq.c +++ b/drivers/cpufreq/davinci-cpufreq.c @@ -50,9 +50,7 @@ static int davinci_verify_speed(struct cpufreq_policy *policy) if (policy->cpu) return -EINVAL; - cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, - policy->cpuinfo.max_freq); - + cpufreq_verify_within_cpu_limits(policy); policy->min = clk_round_rate(armclk, policy->min * 1000) / 1000; policy->max = clk_round_rate(armclk, policy->max * 1000) / 1000; cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, @@ -68,28 +66,18 @@ static unsigned int davinci_getspeed(unsigned int cpu) return clk_get_rate(cpufreq.armclk) / 1000; } -static int davinci_target(struct cpufreq_policy *policy, - unsigned int target_freq, unsigned int relation) +static int davinci_target(struct cpufreq_policy *policy, unsigned int idx) { int ret = 0; - unsigned int idx; struct cpufreq_freqs freqs; struct davinci_cpufreq_config *pdata = cpufreq.dev->platform_data; struct clk *armclk = cpufreq.armclk; freqs.old = davinci_getspeed(0); - freqs.new = clk_round_rate(armclk, target_freq * 1000) / 1000; - - if (freqs.old == freqs.new) - return ret; + freqs.new = pdata->freq_table[idx].frequency; dev_dbg(cpufreq.dev, "transition: %u --> %u\n", freqs.old, freqs.new); - ret = cpufreq_frequency_table_target(policy, pdata->freq_table, - freqs.new, relation, &idx); - if (ret) - return -EINVAL; - cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); /* if moving to higher frequency, up the voltage beforehand */ @@ -170,7 +158,7 @@ static struct freq_attr *davinci_cpufreq_attr[] = { static struct cpufreq_driver davinci_driver = { .flags = CPUFREQ_STICKY, .verify = davinci_verify_speed, - .target = davinci_target, + .target_index = davinci_target, .get = davinci_getspeed, .init = davinci_cpu_init, .exit = davinci_cpu_exit, diff --git a/drivers/cpufreq/dbx500-cpufreq.c b/drivers/cpufreq/dbx500-cpufreq.c index 6ec6539ae041..8c005ac8b701 100644 --- a/drivers/cpufreq/dbx500-cpufreq.c +++ b/drivers/cpufreq/dbx500-cpufreq.c @@ -82,7 +82,7 @@ static unsigned int dbx500_cpufreq_getspeed(unsigned int cpu) return freq_table[i].frequency; } -static int __cpuinit dbx500_cpufreq_init(struct cpufreq_policy *policy) +static int dbx500_cpufreq_init(struct cpufreq_policy *policy) { int res; diff --git a/drivers/cpufreq/e_powersaver.c b/drivers/cpufreq/e_powersaver.c index 37380fb92621..637b48107b76 100644 --- a/drivers/cpufreq/e_powersaver.c +++ b/drivers/cpufreq/e_powersaver.c @@ -54,7 +54,7 @@ static struct acpi_processor_performance *eps_acpi_cpu_perf; /* Minimum necessary to get acpi_processor_get_bios_limit() working */ static int eps_acpi_init(void) { - eps_acpi_cpu_perf = kzalloc(sizeof(struct acpi_processor_performance), + eps_acpi_cpu_perf = kzalloc(sizeof(*eps_acpi_cpu_perf), GFP_KERNEL); if (!eps_acpi_cpu_perf) return -ENOMEM; @@ -188,7 +188,7 @@ static int eps_target(struct cpufreq_policy *policy, } /* Make frequency transition */ - dest_state = centaur->freq_table[newstate].index & 0xffff; + dest_state = centaur->freq_table[newstate].driver_data & 0xffff; ret = eps_set_state(centaur, policy, dest_state); if (ret) printk(KERN_ERR "eps: Timeout!\n"); @@ -363,7 +363,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy) states = 2; /* Allocate private data and frequency table for current cpu */ - centaur = kzalloc(sizeof(struct eps_cpu_data) + centaur = kzalloc(sizeof(*centaur) + (states + 1) * sizeof(struct cpufreq_frequency_table), GFP_KERNEL); if (!centaur) @@ -380,9 +380,9 @@ static int eps_cpu_init(struct cpufreq_policy *policy) f_table = ¢aur->freq_table[0]; if (brand != EPS_BRAND_C7M) { f_table[0].frequency = fsb * min_multiplier; - f_table[0].index = (min_multiplier << 8) | min_voltage; + f_table[0].driver_data = (min_multiplier << 8) | min_voltage; f_table[1].frequency = fsb * max_multiplier; - f_table[1].index = (max_multiplier << 8) | max_voltage; + f_table[1].driver_data = (max_multiplier << 8) | max_voltage; f_table[2].frequency = CPUFREQ_TABLE_END; } else { k = 0; @@ -391,7 +391,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy) for (i = min_multiplier; i <= max_multiplier; i++) { voltage = (k * step) / 256 + min_voltage; f_table[k].frequency = fsb * i; - f_table[k].index = (i << 8) | voltage; + f_table[k].driver_data = (i << 8) | voltage; k++; } f_table[k].frequency = CPUFREQ_TABLE_END; @@ -433,7 +433,6 @@ static struct cpufreq_driver eps_driver = { .exit = eps_cpu_exit, .get = eps_get, .name = "e_powersaver", - .owner = THIS_MODULE, .attr = eps_attr, }; diff --git a/drivers/cpufreq/elanfreq.c b/drivers/cpufreq/elanfreq.c index 658d860344b0..823a400d98fd 100644 --- a/drivers/cpufreq/elanfreq.c +++ b/drivers/cpufreq/elanfreq.c @@ -274,7 +274,6 @@ static struct cpufreq_driver elanfreq_driver = { .init = elanfreq_cpu_init, .exit = elanfreq_cpu_exit, .name = "elanfreq", - .owner = THIS_MODULE, .attr = elanfreq_attr, }; diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index 475b4f607f0d..4aeb7ffbdd0e 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -283,7 +283,7 @@ static int __init exynos_cpufreq_init(void) { int ret = -EINVAL; - exynos_info = kzalloc(sizeof(struct exynos_dvfs_info), GFP_KERNEL); + exynos_info = kzalloc(sizeof(*exynos_info), GFP_KERNEL); if (!exynos_info) return -ENOMEM; diff --git a/drivers/cpufreq/exynos5440-cpufreq.c b/drivers/cpufreq/exynos5440-cpufreq.c index 0c74018eda47..a73ca88155c7 100644 --- a/drivers/cpufreq/exynos5440-cpufreq.c +++ b/drivers/cpufreq/exynos5440-cpufreq.c @@ -20,7 +20,7 @@ #include <linux/module.h> #include <linux/of_address.h> #include <linux/of_irq.h> -#include <linux/opp.h> +#include <linux/pm_opp.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -123,7 +123,7 @@ static int init_div_table(void) rcu_read_lock(); for (i = 0; freq_tbl[i].frequency != CPUFREQ_TABLE_END; i++) { - opp = opp_find_freq_exact(dvfs_info->dev, + opp = dev_pm_opp_find_freq_exact(dvfs_info->dev, freq_tbl[i].frequency * 1000, true); if (IS_ERR(opp)) { rcu_read_unlock(); @@ -142,7 +142,7 @@ static int init_div_table(void) << P0_7_CSCLKDEV_SHIFT; /* Calculate EMA */ - volt_id = opp_get_voltage(opp); + volt_id = dev_pm_opp_get_voltage(opp); volt_id = (MAX_VOLTAGE - volt_id) / VOLTAGE_STEP; if (volt_id < PMIC_HIGH_VOLT) { ema_div = (CPUEMA_HIGH << P0_7_CPUEMA_SHIFT) | @@ -339,7 +339,7 @@ static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) } static struct cpufreq_driver exynos_driver = { - .flags = CPUFREQ_STICKY, + .flags = CPUFREQ_STICKY | CPUFREQ_ASYNC_NOTIFICATION, .verify = exynos_verify_speed, .target = exynos_target, .get = exynos_getspeed, @@ -396,13 +396,14 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) goto err_put_node; } - ret = opp_init_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table); + ret = dev_pm_opp_init_cpufreq_table(dvfs_info->dev, + &dvfs_info->freq_table); if (ret) { dev_err(dvfs_info->dev, "failed to init cpufreq table: %d\n", ret); goto err_put_node; } - dvfs_info->freq_count = opp_get_opp_count(dvfs_info->dev); + dvfs_info->freq_count = dev_pm_opp_get_opp_count(dvfs_info->dev); exynos_sort_descend_freq_table(); if (of_property_read_u32(np, "clock-latency", &dvfs_info->latency)) @@ -451,7 +452,7 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) return 0; err_free_table: - opp_free_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table); + dev_pm_opp_free_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table); err_put_node: of_node_put(np); dev_err(dvfs_info->dev, "%s: failed initialization\n", __func__); @@ -461,7 +462,7 @@ err_put_node: static int exynos_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&exynos_driver); - opp_free_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table); + dev_pm_opp_free_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table); return 0; } diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index d7a79662e24c..3458d27f63b4 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -11,10 +11,8 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> #include <linux/cpufreq.h> +#include <linux/module.h> /********************************************************************* * FREQUENCY TABLE HELPERS * @@ -34,8 +32,8 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy, continue; } - pr_debug("table entry %u: %u kHz, %u index\n", - i, freq, table[i].index); + pr_debug("table entry %u: %u kHz, %u driver_data\n", + i, freq, table[i].driver_data); if (freq < min_freq) min_freq = freq; if (freq > max_freq) @@ -56,31 +54,30 @@ EXPORT_SYMBOL_GPL(cpufreq_frequency_table_cpuinfo); int cpufreq_frequency_table_verify(struct cpufreq_policy *policy, struct cpufreq_frequency_table *table) { - unsigned int next_larger = ~0; - unsigned int i; - unsigned int count = 0; + unsigned int next_larger = ~0, freq, i = 0; + bool found = false; pr_debug("request for verification of policy (%u - %u kHz) for cpu %u\n", policy->min, policy->max, policy->cpu); - cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, - policy->cpuinfo.max_freq); + cpufreq_verify_within_cpu_limits(policy); - for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) { - unsigned int freq = table[i].frequency; + for (; freq = table[i].frequency, freq != CPUFREQ_TABLE_END; i++) { if (freq == CPUFREQ_ENTRY_INVALID) continue; - if ((freq >= policy->min) && (freq <= policy->max)) - count++; - else if ((next_larger > freq) && (freq > policy->max)) + if ((freq >= policy->min) && (freq <= policy->max)) { + found = true; + break; + } + + if ((next_larger > freq) && (freq > policy->max)) next_larger = freq; } - if (!count) + if (!found) { policy->max = next_larger; - - cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, - policy->cpuinfo.max_freq); + cpufreq_verify_within_cpu_limits(policy); + } pr_debug("verification lead to (%u - %u kHz) for cpu %u\n", policy->min, policy->max, policy->cpu); @@ -89,6 +86,20 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy, } EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify); +/* + * Generic routine to verify policy & frequency table, requires driver to call + * cpufreq_frequency_table_get_attr() prior to it. + */ +int cpufreq_generic_frequency_table_verify(struct cpufreq_policy *policy) +{ + struct cpufreq_frequency_table *table = + cpufreq_frequency_get_table(policy->cpu); + if (!table) + return -ENODEV; + + return cpufreq_frequency_table_verify(policy, table); +} +EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify); int cpufreq_frequency_table_target(struct cpufreq_policy *policy, struct cpufreq_frequency_table *table, @@ -97,11 +108,11 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, unsigned int *index) { struct cpufreq_frequency_table optimal = { - .index = ~0, + .driver_data = ~0, .frequency = 0, }; struct cpufreq_frequency_table suboptimal = { - .index = ~0, + .driver_data = ~0, .frequency = 0, }; unsigned int i; @@ -129,12 +140,12 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, if (freq <= target_freq) { if (freq >= optimal.frequency) { optimal.frequency = freq; - optimal.index = i; + optimal.driver_data = i; } } else { if (freq <= suboptimal.frequency) { suboptimal.frequency = freq; - suboptimal.index = i; + suboptimal.driver_data = i; } } break; @@ -142,26 +153,26 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, if (freq >= target_freq) { if (freq <= optimal.frequency) { optimal.frequency = freq; - optimal.index = i; + optimal.driver_data = i; } } else { if (freq >= suboptimal.frequency) { suboptimal.frequency = freq; - suboptimal.index = i; + suboptimal.driver_data = i; } } break; } } - if (optimal.index > i) { - if (suboptimal.index > i) + if (optimal.driver_data > i) { + if (suboptimal.driver_data > i) return -EINVAL; - *index = suboptimal.index; + *index = suboptimal.driver_data; } else - *index = optimal.index; + *index = optimal.driver_data; pr_debug("target is %u (%u kHz, %u)\n", *index, table[*index].frequency, - table[*index].index); + table[*index].driver_data); return 0; } @@ -202,6 +213,12 @@ struct freq_attr cpufreq_freq_attr_scaling_available_freqs = { }; EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs); +struct freq_attr *cpufreq_generic_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; +EXPORT_SYMBOL_GPL(cpufreq_generic_attr); + /* * if you use these, you must assure that the frequency table is valid * all the time between get_attr and put_attr! @@ -221,6 +238,18 @@ void cpufreq_frequency_table_put_attr(unsigned int cpu) } EXPORT_SYMBOL_GPL(cpufreq_frequency_table_put_attr); +int cpufreq_table_validate_and_show(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table) +{ + int ret = cpufreq_frequency_table_cpuinfo(policy, table); + + if (!ret) + cpufreq_frequency_table_get_attr(table, policy->cpu); + + return ret; +} +EXPORT_SYMBOL_GPL(cpufreq_table_validate_and_show); + void cpufreq_frequency_table_update_policy_cpu(struct cpufreq_policy *policy) { pr_debug("Updating show_table for new_cpu %u from last_cpu %u\n", diff --git a/drivers/cpufreq/gx-suspmod.c b/drivers/cpufreq/gx-suspmod.c index 3dfc99b9ca86..ef5fee7dc0b3 100644 --- a/drivers/cpufreq/gx-suspmod.c +++ b/drivers/cpufreq/gx-suspmod.c @@ -446,7 +446,6 @@ static struct cpufreq_driver gx_suspmod_driver = { .target = cpufreq_gx_target, .init = cpufreq_gx_cpu_init, .name = "gx-suspmod", - .owner = THIS_MODULE, }; static int __init cpufreq_gx_init(void) @@ -466,7 +465,7 @@ static int __init cpufreq_gx_init(void) pr_debug("geode suspend modulation available.\n"); - params = kzalloc(sizeof(struct gxfreq_params), GFP_KERNEL); + params = kzalloc(sizeof(*params), GFP_KERNEL); if (params == NULL) return -ENOMEM; diff --git a/drivers/cpufreq/ia64-acpi-cpufreq.c b/drivers/cpufreq/ia64-acpi-cpufreq.c index c0075dbaa633..e126f120b3cb 100644 --- a/drivers/cpufreq/ia64-acpi-cpufreq.c +++ b/drivers/cpufreq/ia64-acpi-cpufreq.c @@ -141,7 +141,6 @@ processor_set_freq ( { int ret = 0; u32 value = 0; - struct cpufreq_freqs cpufreq_freqs; cpumask_t saved_mask; int retval; @@ -168,13 +167,6 @@ processor_set_freq ( pr_debug("Transitioning from P%d to P%d\n", data->acpi_data.state, state); - /* cpufreq frequency struct */ - cpufreq_freqs.old = data->freq_table[data->acpi_data.state].frequency; - cpufreq_freqs.new = data->freq_table[state].frequency; - - /* notify cpufreq */ - cpufreq_notify_transition(policy, &cpufreq_freqs, CPUFREQ_PRECHANGE); - /* * First we write the target state's 'control' value to the * control_register. @@ -186,22 +178,11 @@ processor_set_freq ( ret = processor_set_pstate(value); if (ret) { - unsigned int tmp = cpufreq_freqs.new; - cpufreq_notify_transition(policy, &cpufreq_freqs, - CPUFREQ_POSTCHANGE); - cpufreq_freqs.new = cpufreq_freqs.old; - cpufreq_freqs.old = tmp; - cpufreq_notify_transition(policy, &cpufreq_freqs, - CPUFREQ_PRECHANGE); - cpufreq_notify_transition(policy, &cpufreq_freqs, - CPUFREQ_POSTCHANGE); printk(KERN_WARNING "Transition failed with error %d\n", ret); retval = -ENODEV; goto migrate_end; } - cpufreq_notify_transition(policy, &cpufreq_freqs, CPUFREQ_POSTCHANGE); - data->acpi_data.state = state; retval = 0; @@ -274,7 +255,7 @@ acpi_cpufreq_cpu_init ( pr_debug("acpi_cpufreq_cpu_init\n"); - data = kzalloc(sizeof(struct cpufreq_acpi_io), GFP_KERNEL); + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return (-ENOMEM); @@ -304,7 +285,7 @@ acpi_cpufreq_cpu_init ( } /* alloc freq_table */ - data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) * + data->freq_table = kmalloc(sizeof(*data->freq_table) * (data->acpi_data.state_count + 1), GFP_KERNEL); if (!data->freq_table) { @@ -326,7 +307,7 @@ acpi_cpufreq_cpu_init ( /* table init */ for (i = 0; i <= data->acpi_data.state_count; i++) { - data->freq_table[i].index = i; + data->freq_table[i].driver_data = i; if (i < data->acpi_data.state_count) { data->freq_table[i].frequency = data->acpi_data.states[i].core_frequency * 1000; @@ -409,7 +390,6 @@ static struct cpufreq_driver acpi_cpufreq_driver = { .init = acpi_cpufreq_cpu_init, .exit = acpi_cpufreq_cpu_exit, .name = "acpi-cpufreq", - .owner = THIS_MODULE, .attr = acpi_cpufreq_attr, }; diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index b78bc35973ba..5da79271bd27 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -12,7 +12,7 @@ #include <linux/err.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/opp.h> +#include <linux/pm_opp.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> @@ -71,14 +71,14 @@ static int imx6q_set_target(struct cpufreq_policy *policy, cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); rcu_read_lock(); - opp = opp_find_freq_ceil(cpu_dev, &freq_hz); + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz); if (IS_ERR(opp)) { rcu_read_unlock(); dev_err(cpu_dev, "failed to find OPP for %ld\n", freq_hz); return PTR_ERR(opp); } - volt = opp_get_voltage(opp); + volt = dev_pm_opp_get_voltage(opp); rcu_read_unlock(); volt_old = regulator_get_voltage(arm_reg); @@ -246,14 +246,14 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) } /* We expect an OPP table supplied by platform */ - num = opp_get_opp_count(cpu_dev); + num = dev_pm_opp_get_opp_count(cpu_dev); if (num < 0) { ret = num; dev_err(cpu_dev, "no OPP table is found: %d\n", ret); goto put_node; } - ret = opp_init_cpufreq_table(cpu_dev, &freq_table); + ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); if (ret) { dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); goto put_node; @@ -268,12 +268,12 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) * same order. */ rcu_read_lock(); - opp = opp_find_freq_exact(cpu_dev, + opp = dev_pm_opp_find_freq_exact(cpu_dev, freq_table[0].frequency * 1000, true); - min_volt = opp_get_voltage(opp); - opp = opp_find_freq_exact(cpu_dev, + min_volt = dev_pm_opp_get_voltage(opp); + opp = dev_pm_opp_find_freq_exact(cpu_dev, freq_table[--num].frequency * 1000, true); - max_volt = opp_get_voltage(opp); + max_volt = dev_pm_opp_get_voltage(opp); rcu_read_unlock(); ret = regulator_set_voltage_time(arm_reg, min_volt, max_volt); if (ret > 0) @@ -301,7 +301,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) return 0; free_freq_table: - opp_free_cpufreq_table(cpu_dev, &freq_table); + dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); put_node: of_node_put(np); return ret; @@ -310,7 +310,7 @@ put_node: static int imx6q_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&imx6q_cpufreq_driver); - opp_free_cpufreq_table(cpu_dev, &freq_table); + dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); return 0; } diff --git a/drivers/cpufreq/integrator-cpufreq.c b/drivers/cpufreq/integrator-cpufreq.c index f7c99df0880b..8152a9bb7e2c 100644 --- a/drivers/cpufreq/integrator-cpufreq.c +++ b/drivers/cpufreq/integrator-cpufreq.c @@ -59,9 +59,7 @@ static int integrator_verify_policy(struct cpufreq_policy *policy) { struct icst_vco vco; - cpufreq_verify_within_limits(policy, - policy->cpuinfo.min_freq, - policy->cpuinfo.max_freq); + cpufreq_verify_within_cpu_limits(policy); vco = icst_hz_to_vco(&cclk_params, policy->max * 1000); policy->max = icst_hz(&cclk_params, vco) / 1000; @@ -69,10 +67,7 @@ static int integrator_verify_policy(struct cpufreq_policy *policy) vco = icst_hz_to_vco(&cclk_params, policy->min * 1000); policy->min = icst_hz(&cclk_params, vco) / 1000; - cpufreq_verify_within_limits(policy, - policy->cpuinfo.min_freq, - policy->cpuinfo.max_freq); - + cpufreq_verify_within_cpu_limits(policy); return 0; } diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 07f2840ad805..000dd7514ec1 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -606,9 +606,7 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) static int intel_pstate_verify_policy(struct cpufreq_policy *policy) { - cpufreq_verify_within_limits(policy, - policy->cpuinfo.min_freq, - policy->cpuinfo.max_freq); + cpufreq_verify_within_cpu_limits(policy); if ((policy->policy != CPUFREQ_POLICY_POWERSAVE) && (policy->policy != CPUFREQ_POLICY_PERFORMANCE)) @@ -617,7 +615,7 @@ static int intel_pstate_verify_policy(struct cpufreq_policy *policy) return 0; } -static int __cpuinit intel_pstate_cpu_exit(struct cpufreq_policy *policy) +static int intel_pstate_cpu_exit(struct cpufreq_policy *policy) { int cpu = policy->cpu; @@ -627,7 +625,7 @@ static int __cpuinit intel_pstate_cpu_exit(struct cpufreq_policy *policy) return 0; } -static int __cpuinit intel_pstate_cpu_init(struct cpufreq_policy *policy) +static int intel_pstate_cpu_init(struct cpufreq_policy *policy) { int rc, min_pstate, max_pstate; struct cpudata *cpu; @@ -665,7 +663,6 @@ static struct cpufreq_driver intel_pstate_driver = { .init = intel_pstate_cpu_init, .exit = intel_pstate_cpu_exit, .name = "intel_pstate", - .owner = THIS_MODULE, }; static int __initdata no_load; diff --git a/drivers/cpufreq/kirkwood-cpufreq.c b/drivers/cpufreq/kirkwood-cpufreq.c index b2644af985ec..45e4d7fc261d 100644 --- a/drivers/cpufreq/kirkwood-cpufreq.c +++ b/drivers/cpufreq/kirkwood-cpufreq.c @@ -59,7 +59,7 @@ static void kirkwood_cpufreq_set_cpu_state(struct cpufreq_policy *policy, unsigned int index) { struct cpufreq_freqs freqs; - unsigned int state = kirkwood_freq_table[index].index; + unsigned int state = kirkwood_freq_table[index].driver_data; unsigned long reg; freqs.old = kirkwood_cpufreq_get_cpu_frequency(0); @@ -158,7 +158,6 @@ static struct cpufreq_driver kirkwood_cpufreq_driver = { .init = kirkwood_cpufreq_cpu_init, .exit = kirkwood_cpufreq_cpu_exit, .name = "kirkwood-cpufreq", - .owner = THIS_MODULE, .attr = kirkwood_cpufreq_attr, }; diff --git a/drivers/cpufreq/longhaul.c b/drivers/cpufreq/longhaul.c index b448638e34de..4ada1cccb052 100644 --- a/drivers/cpufreq/longhaul.c +++ b/drivers/cpufreq/longhaul.c @@ -254,7 +254,7 @@ static void longhaul_setstate(struct cpufreq_policy *policy, u32 bm_timeout = 1000; unsigned int dir = 0; - mults_index = longhaul_table[table_index].index; + mults_index = longhaul_table[table_index].driver_data; /* Safety precautions */ mult = mults[mults_index & 0x1f]; if (mult == -1) @@ -422,7 +422,7 @@ static int guess_fsb(int mult) } -static int __cpuinit longhaul_get_ranges(void) +static int longhaul_get_ranges(void) { unsigned int i, j, k = 0; unsigned int ratio; @@ -487,7 +487,7 @@ static int __cpuinit longhaul_get_ranges(void) if (ratio > maxmult || ratio < minmult) continue; longhaul_table[k].frequency = calc_speed(ratio); - longhaul_table[k].index = j; + longhaul_table[k].driver_data = j; k++; } if (k <= 1) { @@ -508,8 +508,8 @@ static int __cpuinit longhaul_get_ranges(void) if (min_i != j) { swap(longhaul_table[j].frequency, longhaul_table[min_i].frequency); - swap(longhaul_table[j].index, - longhaul_table[min_i].index); + swap(longhaul_table[j].driver_data, + longhaul_table[min_i].driver_data); } } @@ -517,7 +517,7 @@ static int __cpuinit longhaul_get_ranges(void) /* Find index we are running on */ for (j = 0; j < k; j++) { - if (mults[longhaul_table[j].index & 0x1f] == mult) { + if (mults[longhaul_table[j].driver_data & 0x1f] == mult) { longhaul_index = j; break; } @@ -526,7 +526,7 @@ static int __cpuinit longhaul_get_ranges(void) } -static void __cpuinit longhaul_setup_voltagescaling(void) +static void longhaul_setup_voltagescaling(void) { union msr_longhaul longhaul; struct mV_pos minvid, maxvid, vid; @@ -613,7 +613,7 @@ static void __cpuinit longhaul_setup_voltagescaling(void) pos = (speed - min_vid_speed) / kHz_step + minvid.pos; else pos = minvid.pos; - longhaul_table[j].index |= mV_vrm_table[pos] << 8; + longhaul_table[j].driver_data |= mV_vrm_table[pos] << 8; vid = vrm_mV_table[mV_vrm_table[pos]]; printk(KERN_INFO PFX "f: %d kHz, index: %d, vid: %d mV\n", speed, j, vid.mV); @@ -656,12 +656,12 @@ static int longhaul_target(struct cpufreq_policy *policy, * this in hardware, C3 is old and we need to do this * in software. */ i = longhaul_index; - current_vid = (longhaul_table[longhaul_index].index >> 8); + current_vid = (longhaul_table[longhaul_index].driver_data >> 8); current_vid &= 0x1f; if (table_index > longhaul_index) dir = 1; while (i != table_index) { - vid = (longhaul_table[i].index >> 8) & 0x1f; + vid = (longhaul_table[i].driver_data >> 8) & 0x1f; if (vid != current_vid) { longhaul_setstate(policy, i); current_vid = vid; @@ -780,7 +780,7 @@ static int longhaul_setup_southbridge(void) return 0; } -static int __cpuinit longhaul_cpu_init(struct cpufreq_policy *policy) +static int longhaul_cpu_init(struct cpufreq_policy *policy) { struct cpuinfo_x86 *c = &cpu_data(0); char *cpuname = NULL; @@ -948,7 +948,6 @@ static struct cpufreq_driver longhaul_driver = { .init = longhaul_cpu_init, .exit = longhaul_cpu_exit, .name = "longhaul", - .owner = THIS_MODULE, .attr = longhaul_attr, }; diff --git a/drivers/cpufreq/longhaul.h b/drivers/cpufreq/longhaul.h index e2dc436099d1..1928b923a57b 100644 --- a/drivers/cpufreq/longhaul.h +++ b/drivers/cpufreq/longhaul.h @@ -56,7 +56,7 @@ union msr_longhaul { /* * VIA C3 Samuel 1 & Samuel 2 (stepping 0) */ -static const int __cpuinitconst samuel1_mults[16] = { +static const int samuel1_mults[16] = { -1, /* 0000 -> RESERVED */ 30, /* 0001 -> 3.0x */ 40, /* 0010 -> 4.0x */ @@ -75,7 +75,7 @@ static const int __cpuinitconst samuel1_mults[16] = { -1, /* 1111 -> RESERVED */ }; -static const int __cpuinitconst samuel1_eblcr[16] = { +static const int samuel1_eblcr[16] = { 50, /* 0000 -> RESERVED */ 30, /* 0001 -> 3.0x */ 40, /* 0010 -> 4.0x */ @@ -97,7 +97,7 @@ static const int __cpuinitconst samuel1_eblcr[16] = { /* * VIA C3 Samuel2 Stepping 1->15 */ -static const int __cpuinitconst samuel2_eblcr[16] = { +static const int samuel2_eblcr[16] = { 50, /* 0000 -> 5.0x */ 30, /* 0001 -> 3.0x */ 40, /* 0010 -> 4.0x */ @@ -119,7 +119,7 @@ static const int __cpuinitconst samuel2_eblcr[16] = { /* * VIA C3 Ezra */ -static const int __cpuinitconst ezra_mults[16] = { +static const int ezra_mults[16] = { 100, /* 0000 -> 10.0x */ 30, /* 0001 -> 3.0x */ 40, /* 0010 -> 4.0x */ @@ -138,7 +138,7 @@ static const int __cpuinitconst ezra_mults[16] = { 120, /* 1111 -> 12.0x */ }; -static const int __cpuinitconst ezra_eblcr[16] = { +static const int ezra_eblcr[16] = { 50, /* 0000 -> 5.0x */ 30, /* 0001 -> 3.0x */ 40, /* 0010 -> 4.0x */ @@ -160,7 +160,7 @@ static const int __cpuinitconst ezra_eblcr[16] = { /* * VIA C3 (Ezra-T) [C5M]. */ -static const int __cpuinitconst ezrat_mults[32] = { +static const int ezrat_mults[32] = { 100, /* 0000 -> 10.0x */ 30, /* 0001 -> 3.0x */ 40, /* 0010 -> 4.0x */ @@ -196,7 +196,7 @@ static const int __cpuinitconst ezrat_mults[32] = { -1, /* 1111 -> RESERVED (12.0x) */ }; -static const int __cpuinitconst ezrat_eblcr[32] = { +static const int ezrat_eblcr[32] = { 50, /* 0000 -> 5.0x */ 30, /* 0001 -> 3.0x */ 40, /* 0010 -> 4.0x */ @@ -235,7 +235,7 @@ static const int __cpuinitconst ezrat_eblcr[32] = { /* * VIA C3 Nehemiah */ -static const int __cpuinitconst nehemiah_mults[32] = { +static const int nehemiah_mults[32] = { 100, /* 0000 -> 10.0x */ -1, /* 0001 -> 16.0x */ 40, /* 0010 -> 4.0x */ @@ -270,7 +270,7 @@ static const int __cpuinitconst nehemiah_mults[32] = { -1, /* 1111 -> 12.0x */ }; -static const int __cpuinitconst nehemiah_eblcr[32] = { +static const int nehemiah_eblcr[32] = { 50, /* 0000 -> 5.0x */ 160, /* 0001 -> 16.0x */ 40, /* 0010 -> 4.0x */ @@ -315,7 +315,7 @@ struct mV_pos { unsigned short pos; }; -static const struct mV_pos __cpuinitconst vrm85_mV[32] = { +static const struct mV_pos vrm85_mV[32] = { {1250, 8}, {1200, 6}, {1150, 4}, {1100, 2}, {1050, 0}, {1800, 30}, {1750, 28}, {1700, 26}, {1650, 24}, {1600, 22}, {1550, 20}, {1500, 18}, @@ -326,14 +326,14 @@ static const struct mV_pos __cpuinitconst vrm85_mV[32] = { {1475, 17}, {1425, 15}, {1375, 13}, {1325, 11} }; -static const unsigned char __cpuinitconst mV_vrm85[32] = { +static const unsigned char mV_vrm85[32] = { 0x04, 0x14, 0x03, 0x13, 0x02, 0x12, 0x01, 0x11, 0x00, 0x10, 0x0f, 0x1f, 0x0e, 0x1e, 0x0d, 0x1d, 0x0c, 0x1c, 0x0b, 0x1b, 0x0a, 0x1a, 0x09, 0x19, 0x08, 0x18, 0x07, 0x17, 0x06, 0x16, 0x05, 0x15 }; -static const struct mV_pos __cpuinitconst mobilevrm_mV[32] = { +static const struct mV_pos mobilevrm_mV[32] = { {1750, 31}, {1700, 30}, {1650, 29}, {1600, 28}, {1550, 27}, {1500, 26}, {1450, 25}, {1400, 24}, {1350, 23}, {1300, 22}, {1250, 21}, {1200, 20}, @@ -344,7 +344,7 @@ static const struct mV_pos __cpuinitconst mobilevrm_mV[32] = { {675, 3}, {650, 2}, {625, 1}, {600, 0} }; -static const unsigned char __cpuinitconst mV_mobilevrm[32] = { +static const unsigned char mV_mobilevrm[32] = { 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, diff --git a/drivers/cpufreq/longrun.c b/drivers/cpufreq/longrun.c index 8bc9f5fbbaeb..074971b12635 100644 --- a/drivers/cpufreq/longrun.c +++ b/drivers/cpufreq/longrun.c @@ -33,7 +33,7 @@ static unsigned int longrun_low_freq, longrun_high_freq; * Reads the current LongRun policy by access to MSR_TMTA_LONGRUN_FLAGS * and MSR_TMTA_LONGRUN_CTRL */ -static void __cpuinit longrun_get_policy(struct cpufreq_policy *policy) +static void longrun_get_policy(struct cpufreq_policy *policy) { u32 msr_lo, msr_hi; @@ -129,9 +129,7 @@ static int longrun_verify_policy(struct cpufreq_policy *policy) return -EINVAL; policy->cpu = 0; - cpufreq_verify_within_limits(policy, - policy->cpuinfo.min_freq, - policy->cpuinfo.max_freq); + cpufreq_verify_within_cpu_limits(policy); if ((policy->policy != CPUFREQ_POLICY_POWERSAVE) && (policy->policy != CPUFREQ_POLICY_PERFORMANCE)) @@ -163,7 +161,7 @@ static unsigned int longrun_get(unsigned int cpu) * TMTA rules: * performance_pctg = (target_freq - low_freq)/(high_freq - low_freq) */ -static int __cpuinit longrun_determine_freqs(unsigned int *low_freq, +static int longrun_determine_freqs(unsigned int *low_freq, unsigned int *high_freq) { u32 msr_lo, msr_hi; @@ -256,7 +254,7 @@ static int __cpuinit longrun_determine_freqs(unsigned int *low_freq, } -static int __cpuinit longrun_cpu_init(struct cpufreq_policy *policy) +static int longrun_cpu_init(struct cpufreq_policy *policy) { int result = 0; @@ -286,7 +284,6 @@ static struct cpufreq_driver longrun_driver = { .get = longrun_get, .init = longrun_cpu_init, .name = "longrun", - .owner = THIS_MODULE, }; static const struct x86_cpu_id longrun_ids[] = { diff --git a/drivers/cpufreq/loongson2_cpufreq.c b/drivers/cpufreq/loongson2_cpufreq.c index d53912768946..e65de91b534b 100644 --- a/drivers/cpufreq/loongson2_cpufreq.c +++ b/drivers/cpufreq/loongson2_cpufreq.c @@ -72,7 +72,7 @@ static int loongson2_cpufreq_target(struct cpufreq_policy *policy, freq = ((cpu_clock_freq / 1000) * - loongson2_clockmod_table[newstate].index) / 8; + loongson2_clockmod_table[newstate].driver_data) / 8; if (freq < policy->min || freq > policy->max) return -EINVAL; @@ -157,7 +157,6 @@ static struct freq_attr *loongson2_table_attr[] = { }; static struct cpufreq_driver loongson2_cpufreq_driver = { - .owner = THIS_MODULE, .name = "loongson2", .init = loongson2_cpufreq_cpu_init, .verify = loongson2_cpufreq_verify, diff --git a/drivers/cpufreq/maple-cpufreq.c b/drivers/cpufreq/maple-cpufreq.c index cdd62915efaf..41c601f4631e 100644 --- a/drivers/cpufreq/maple-cpufreq.c +++ b/drivers/cpufreq/maple-cpufreq.c @@ -190,7 +190,6 @@ static int maple_cpufreq_cpu_init(struct cpufreq_policy *policy) static struct cpufreq_driver maple_cpufreq_driver = { .name = "maple", - .owner = THIS_MODULE, .flags = CPUFREQ_CONST_LOOPS, .init = maple_cpufreq_cpu_init, .verify = maple_cpufreq_verify, diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index 0279d18a57f9..1814318e1aaf 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -22,7 +22,7 @@ #include <linux/err.h> #include <linux/clk.h> #include <linux/io.h> -#include <linux/opp.h> +#include <linux/pm_opp.h> #include <linux/cpu.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -108,14 +108,14 @@ static int omap_target(struct cpufreq_policy *policy, if (mpu_reg) { rcu_read_lock(); - opp = opp_find_freq_ceil(mpu_dev, &freq); + opp = dev_pm_opp_find_freq_ceil(mpu_dev, &freq); if (IS_ERR(opp)) { rcu_read_unlock(); dev_err(mpu_dev, "%s: unable to find MPU OPP for %d\n", __func__, freqs.new); return -EINVAL; } - volt = opp_get_voltage(opp); + volt = dev_pm_opp_get_voltage(opp); rcu_read_unlock(); tol = volt * OPP_TOLERANCE / 100; volt_old = regulator_get_voltage(mpu_reg); @@ -162,10 +162,10 @@ done: static inline void freq_table_free(void) { if (atomic_dec_and_test(&freq_table_users)) - opp_free_cpufreq_table(mpu_dev, &freq_table); + dev_pm_opp_free_cpufreq_table(mpu_dev, &freq_table); } -static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) +static int omap_cpu_init(struct cpufreq_policy *policy) { int result = 0; @@ -181,7 +181,7 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) policy->cur = omap_getspeed(policy->cpu); if (!freq_table) - result = opp_init_cpufreq_table(mpu_dev, &freq_table); + result = dev_pm_opp_init_cpufreq_table(mpu_dev, &freq_table); if (result) { dev_err(mpu_dev, "%s: cpu%d: failed creating freq table[%d]\n", diff --git a/drivers/cpufreq/p4-clockmod.c b/drivers/cpufreq/p4-clockmod.c index 421ef37d0bb3..2f0a2a65c37f 100644 --- a/drivers/cpufreq/p4-clockmod.c +++ b/drivers/cpufreq/p4-clockmod.c @@ -118,7 +118,7 @@ static int cpufreq_p4_target(struct cpufreq_policy *policy, return -EINVAL; freqs.old = cpufreq_p4_get(policy->cpu); - freqs.new = stock_freq * p4clockmod_table[newstate].index / 8; + freqs.new = stock_freq * p4clockmod_table[newstate].driver_data / 8; if (freqs.new == freqs.old) return 0; @@ -131,7 +131,7 @@ static int cpufreq_p4_target(struct cpufreq_policy *policy, * Developer's Manual, Volume 3 */ for_each_cpu(i, policy->cpus) - cpufreq_p4_setdc(i, p4clockmod_table[newstate].index); + cpufreq_p4_setdc(i, p4clockmod_table[newstate].driver_data); /* notifiers */ cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); @@ -279,7 +279,6 @@ static struct cpufreq_driver p4clockmod_driver = { .exit = cpufreq_p4_cpu_exit, .get = cpufreq_p4_get, .name = "p4-clockmod", - .owner = THIS_MODULE, .attr = p4clockmod_attr, }; diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c index 0de00081a81e..dce5533efd16 100644 --- a/drivers/cpufreq/pcc-cpufreq.c +++ b/drivers/cpufreq/pcc-cpufreq.c @@ -111,8 +111,7 @@ static struct pcc_cpu __percpu *pcc_cpu_info; static int pcc_cpufreq_verify(struct cpufreq_policy *policy) { - cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, - policy->cpuinfo.max_freq); + cpufreq_verify_within_cpu_limits(policy); return 0; } @@ -585,7 +584,6 @@ static struct cpufreq_driver pcc_cpufreq_driver = { .init = pcc_cpufreq_cpu_init, .exit = pcc_cpufreq_cpu_exit, .name = "pcc-cpufreq", - .owner = THIS_MODULE, }; static int __init pcc_cpufreq_init(void) diff --git a/drivers/cpufreq/powernow-k6.c b/drivers/cpufreq/powernow-k6.c index ea0222a45b7b..85f1c8c25ddc 100644 --- a/drivers/cpufreq/powernow-k6.c +++ b/drivers/cpufreq/powernow-k6.c @@ -58,7 +58,7 @@ static int powernow_k6_get_cpu_multiplier(void) msrval = POWERNOW_IOPORT + 0x0; wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */ - return clock_ratio[(invalue >> 5)&7].index; + return clock_ratio[(invalue >> 5)&7].driver_data; } @@ -75,13 +75,13 @@ static void powernow_k6_set_state(struct cpufreq_policy *policy, unsigned long msrval; struct cpufreq_freqs freqs; - if (clock_ratio[best_i].index > max_multiplier) { + if (clock_ratio[best_i].driver_data > max_multiplier) { printk(KERN_ERR PFX "invalid target frequency\n"); return; } freqs.old = busfreq * powernow_k6_get_cpu_multiplier(); - freqs.new = busfreq * clock_ratio[best_i].index; + freqs.new = busfreq * clock_ratio[best_i].driver_data; cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); @@ -156,7 +156,7 @@ static int powernow_k6_cpu_init(struct cpufreq_policy *policy) /* table init */ for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) { - f = clock_ratio[i].index; + f = clock_ratio[i].driver_data; if (f > max_multiplier) clock_ratio[i].frequency = CPUFREQ_ENTRY_INVALID; else @@ -207,7 +207,6 @@ static struct cpufreq_driver powernow_k6_driver = { .exit = powernow_k6_cpu_exit, .get = powernow_k6_get, .name = "powernow-k6", - .owner = THIS_MODULE, .attr = powernow_k6_attr, }; diff --git a/drivers/cpufreq/powernow-k7.c b/drivers/cpufreq/powernow-k7.c index 53888dacbe58..14ce480be8ab 100644 --- a/drivers/cpufreq/powernow-k7.c +++ b/drivers/cpufreq/powernow-k7.c @@ -177,7 +177,7 @@ static int get_ranges(unsigned char *pst) unsigned int speed; u8 fid, vid; - powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) * + powernow_table = kzalloc((sizeof(*powernow_table) * (number_scales + 1)), GFP_KERNEL); if (!powernow_table) return -ENOMEM; @@ -186,7 +186,7 @@ static int get_ranges(unsigned char *pst) fid = *pst++; powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10; - powernow_table[j].index = fid; /* lower 8 bits */ + powernow_table[j].driver_data = fid; /* lower 8 bits */ speed = powernow_table[j].frequency; @@ -203,7 +203,7 @@ static int get_ranges(unsigned char *pst) maximum_speed = speed; vid = *pst++; - powernow_table[j].index |= (vid << 8); /* upper 8 bits */ + powernow_table[j].driver_data |= (vid << 8); /* upper 8 bits */ pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) " "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10, @@ -212,7 +212,7 @@ static int get_ranges(unsigned char *pst) mobile_vid_table[vid]%1000); } powernow_table[number_scales].frequency = CPUFREQ_TABLE_END; - powernow_table[number_scales].index = 0; + powernow_table[number_scales].driver_data = 0; return 0; } @@ -260,8 +260,8 @@ static void change_speed(struct cpufreq_policy *policy, unsigned int index) * vid are the upper 8 bits. */ - fid = powernow_table[index].index & 0xFF; - vid = (powernow_table[index].index & 0xFF00) >> 8; + fid = powernow_table[index].driver_data & 0xFF; + vid = (powernow_table[index].driver_data & 0xFF00) >> 8; rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val); cfid = fidvidstatus.bits.CFID; @@ -309,8 +309,7 @@ static int powernow_acpi_init(void) goto err0; } - acpi_processor_perf = kzalloc(sizeof(struct acpi_processor_performance), - GFP_KERNEL); + acpi_processor_perf = kzalloc(sizeof(*acpi_processor_perf), GFP_KERNEL); if (!acpi_processor_perf) { retval = -ENOMEM; goto err0; @@ -346,7 +345,7 @@ static int powernow_acpi_init(void) goto err2; } - powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) * + powernow_table = kzalloc((sizeof(*powernow_table) * (number_scales + 1)), GFP_KERNEL); if (!powernow_table) { retval = -ENOMEM; @@ -373,8 +372,8 @@ static int powernow_acpi_init(void) fid = pc.bits.fid; powernow_table[i].frequency = fsb * fid_codes[fid] / 10; - powernow_table[i].index = fid; /* lower 8 bits */ - powernow_table[i].index |= (vid << 8); /* upper 8 bits */ + powernow_table[i].driver_data = fid; /* lower 8 bits */ + powernow_table[i].driver_data |= (vid << 8); /* upper 8 bits */ speed = powernow_table[i].frequency; speed_mhz = speed / 1000; @@ -417,7 +416,7 @@ static int powernow_acpi_init(void) } powernow_table[i].frequency = CPUFREQ_TABLE_END; - powernow_table[i].index = 0; + powernow_table[i].driver_data = 0; /* notify BIOS that we exist */ acpi_processor_notify_smm(THIS_MODULE); @@ -497,7 +496,7 @@ static int powernow_decode_bios(int maxfid, int startvid) "relevant to this CPU).\n", psb->numpst); - p += sizeof(struct psb_s); + p += sizeof(*psb); pst = (struct pst_s *) p; @@ -510,12 +509,12 @@ static int powernow_decode_bios(int maxfid, int startvid) (maxfid == pst->maxfid) && (startvid == pst->startvid)) { print_pst_entry(pst, j); - p = (char *)pst + sizeof(struct pst_s); + p = (char *)pst + sizeof(*pst); ret = get_ranges(p); return ret; } else { unsigned int k; - p = (char *)pst + sizeof(struct pst_s); + p = (char *)pst + sizeof(*pst); for (k = 0; k < number_scales; k++) p += 2; } @@ -563,7 +562,7 @@ static int powernow_verify(struct cpufreq_policy *policy) * We will then get the same kind of behaviour already tested under * the "well-known" other OS. */ -static int __cpuinit fixup_sgtc(void) +static int fixup_sgtc(void) { unsigned int sgtc; unsigned int m; @@ -597,7 +596,7 @@ static unsigned int powernow_get(unsigned int cpu) } -static int __cpuinit acer_cpufreq_pst(const struct dmi_system_id *d) +static int acer_cpufreq_pst(const struct dmi_system_id *d) { printk(KERN_WARNING PFX "%s laptop with broken PST tables in BIOS detected.\n", @@ -615,7 +614,7 @@ static int __cpuinit acer_cpufreq_pst(const struct dmi_system_id *d) * A BIOS update is all that can save them. * Mention this, and disable cpufreq. */ -static struct dmi_system_id __cpuinitdata powernow_dmi_table[] = { +static struct dmi_system_id powernow_dmi_table[] = { { .callback = acer_cpufreq_pst, .ident = "Acer Aspire", @@ -627,7 +626,7 @@ static struct dmi_system_id __cpuinitdata powernow_dmi_table[] = { { } }; -static int __cpuinit powernow_cpu_init(struct cpufreq_policy *policy) +static int powernow_cpu_init(struct cpufreq_policy *policy) { union msr_fidvidstatus fidvidstatus; int result; @@ -717,7 +716,6 @@ static struct cpufreq_driver powernow_driver = { .init = powernow_cpu_init, .exit = powernow_cpu_exit, .name = "powernow-k7", - .owner = THIS_MODULE, .attr = powernow_table_attr, }; diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index b828efe4b2f8..9669cbc980f5 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -584,9 +584,9 @@ static void print_basics(struct powernow_k8_data *data) CPUFREQ_ENTRY_INVALID) { printk(KERN_INFO PFX "fid 0x%x (%d MHz), vid 0x%x\n", - data->powernow_table[j].index & 0xff, + data->powernow_table[j].driver_data & 0xff, data->powernow_table[j].frequency/1000, - data->powernow_table[j].index >> 8); + data->powernow_table[j].driver_data >> 8); } } if (data->batps) @@ -623,7 +623,7 @@ static int fill_powernow_table(struct powernow_k8_data *data, if (check_pst_table(data, pst, maxvid)) return -EINVAL; - powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) + powernow_table = kmalloc((sizeof(*powernow_table) * (data->numps + 1)), GFP_KERNEL); if (!powernow_table) { printk(KERN_ERR PFX "powernow_table memory alloc failure\n"); @@ -632,13 +632,13 @@ static int fill_powernow_table(struct powernow_k8_data *data, for (j = 0; j < data->numps; j++) { int freq; - powernow_table[j].index = pst[j].fid; /* lower 8 bits */ - powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */ + powernow_table[j].driver_data = pst[j].fid; /* lower 8 bits */ + powernow_table[j].driver_data |= (pst[j].vid << 8); /* upper 8 bits */ freq = find_khz_freq_from_fid(pst[j].fid); powernow_table[j].frequency = freq; } powernow_table[data->numps].frequency = CPUFREQ_TABLE_END; - powernow_table[data->numps].index = 0; + powernow_table[data->numps].driver_data = 0; if (query_current_values_with_pending_wait(data)) { kfree(powernow_table); @@ -793,7 +793,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) } /* fill in data->powernow_table */ - powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) + powernow_table = kmalloc((sizeof(*powernow_table) * (data->acpi_data.state_count + 1)), GFP_KERNEL); if (!powernow_table) { pr_debug("powernow_table memory alloc failure\n"); @@ -810,7 +810,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) powernow_table[data->acpi_data.state_count].frequency = CPUFREQ_TABLE_END; - powernow_table[data->acpi_data.state_count].index = 0; + powernow_table[data->acpi_data.state_count].driver_data = 0; data->powernow_table = powernow_table; if (cpumask_first(cpu_core_mask(data->cpu)) == data->cpu) @@ -865,7 +865,7 @@ static int fill_powernow_table_fidvid(struct powernow_k8_data *data, pr_debug(" %d : fid 0x%x, vid 0x%x\n", i, fid, vid); index = fid | (vid<<8); - powernow_table[i].index = index; + powernow_table[i].driver_data = index; freq = find_khz_freq_from_fid(fid); powernow_table[i].frequency = freq; @@ -941,8 +941,8 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data, * the cpufreq frequency table in find_psb_table, vid * are the upper 8 bits. */ - fid = data->powernow_table[index].index & 0xFF; - vid = (data->powernow_table[index].index & 0xFF00) >> 8; + fid = data->powernow_table[index].driver_data & 0xFF; + vid = (data->powernow_table[index].driver_data & 0xFF00) >> 8; pr_debug("table matched fid 0x%x, giving vid 0x%x\n", fid, vid); @@ -1069,7 +1069,7 @@ struct init_on_cpu { int rc; }; -static void __cpuinit powernowk8_cpu_init_on_cpu(void *_init_on_cpu) +static void powernowk8_cpu_init_on_cpu(void *_init_on_cpu) { struct init_on_cpu *init_on_cpu = _init_on_cpu; @@ -1096,7 +1096,7 @@ static const char missing_pss_msg[] = FW_BUG PFX "If that doesn't help, try upgrading your BIOS.\n"; /* per CPU init entry point to the driver */ -static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol) +static int powernowk8_cpu_init(struct cpufreq_policy *pol) { struct powernow_k8_data *data; struct init_on_cpu init_on_cpu; @@ -1106,7 +1106,7 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol) if (rc) return -ENODEV; - data = kzalloc(sizeof(struct powernow_k8_data), GFP_KERNEL); + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { printk(KERN_ERR PFX "unable to alloc powernow_k8_data"); return -ENOMEM; @@ -1233,6 +1233,7 @@ static struct freq_attr *powernow_k8_attr[] = { }; static struct cpufreq_driver cpufreq_amd64_driver = { + .flags = CPUFREQ_ASYNC_NOTIFICATION, .verify = powernowk8_verify, .target = powernowk8_target, .bios_limit = acpi_processor_get_bios_limit, @@ -1240,7 +1241,6 @@ static struct cpufreq_driver cpufreq_amd64_driver = { .exit = powernowk8_cpu_exit, .get = powernowk8_get, .name = "powernow-k8", - .owner = THIS_MODULE, .attr = powernow_k8_attr, }; @@ -1263,7 +1263,7 @@ static void __request_acpi_cpufreq(void) } /* driver entry point for init */ -static int __cpuinit powernowk8_init(void) +static int powernowk8_init(void) { unsigned int i, supported_cpus = 0; int ret; diff --git a/drivers/cpufreq/ppc_cbe_cpufreq.c b/drivers/cpufreq/ppc_cbe_cpufreq.c index e577a1dbbfcd..2e448f0bbdc5 100644 --- a/drivers/cpufreq/ppc_cbe_cpufreq.c +++ b/drivers/cpufreq/ppc_cbe_cpufreq.c @@ -106,7 +106,7 @@ static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy) /* initialize frequency table */ for (i=0; cbe_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) { - cbe_freqs[i].frequency = max_freq / cbe_freqs[i].index; + cbe_freqs[i].frequency = max_freq / cbe_freqs[i].driver_data; pr_debug("%d: %d\n", i, cbe_freqs[i].frequency); } @@ -165,7 +165,7 @@ static int cbe_cpufreq_target(struct cpufreq_policy *policy, "1/%d of max frequency\n", policy->cpu, cbe_freqs[cbe_pmode_new].frequency, - cbe_freqs[cbe_pmode_new].index); + cbe_freqs[cbe_pmode_new].driver_data); rc = set_pmode(policy->cpu, cbe_pmode_new); @@ -181,7 +181,6 @@ static struct cpufreq_driver cbe_cpufreq_driver = { .init = cbe_cpufreq_cpu_init, .exit = cbe_cpufreq_cpu_exit, .name = "cbe-cpufreq", - .owner = THIS_MODULE, .flags = CPUFREQ_CONST_LOOPS, }; diff --git a/drivers/cpufreq/pxa2xx-cpufreq.c b/drivers/cpufreq/pxa2xx-cpufreq.c index 9e5bc8e388a0..fb3981ac829f 100644 --- a/drivers/cpufreq/pxa2xx-cpufreq.c +++ b/drivers/cpufreq/pxa2xx-cpufreq.c @@ -420,7 +420,7 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy) /* Generate pxa25x the run cpufreq_frequency_table struct */ for (i = 0; i < NUM_PXA25x_RUN_FREQS; i++) { pxa255_run_freq_table[i].frequency = pxa255_run_freqs[i].khz; - pxa255_run_freq_table[i].index = i; + pxa255_run_freq_table[i].driver_data = i; } pxa255_run_freq_table[i].frequency = CPUFREQ_TABLE_END; @@ -428,7 +428,7 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy) for (i = 0; i < NUM_PXA25x_TURBO_FREQS; i++) { pxa255_turbo_freq_table[i].frequency = pxa255_turbo_freqs[i].khz; - pxa255_turbo_freq_table[i].index = i; + pxa255_turbo_freq_table[i].driver_data = i; } pxa255_turbo_freq_table[i].frequency = CPUFREQ_TABLE_END; @@ -440,9 +440,9 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy) if (freq > pxa27x_maxfreq) break; pxa27x_freq_table[i].frequency = freq; - pxa27x_freq_table[i].index = i; + pxa27x_freq_table[i].driver_data = i; } - pxa27x_freq_table[i].index = i; + pxa27x_freq_table[i].driver_data = i; pxa27x_freq_table[i].frequency = CPUFREQ_TABLE_END; /* diff --git a/drivers/cpufreq/pxa3xx-cpufreq.c b/drivers/cpufreq/pxa3xx-cpufreq.c index 15d60f857ad5..9c92ef032a9e 100644 --- a/drivers/cpufreq/pxa3xx-cpufreq.c +++ b/drivers/cpufreq/pxa3xx-cpufreq.c @@ -98,10 +98,10 @@ static int setup_freqs_table(struct cpufreq_policy *policy, return -ENOMEM; for (i = 0; i < num; i++) { - table[i].index = i; + table[i].driver_data = i; table[i].frequency = freqs[i].cpufreq_mhz * 1000; } - table[num].index = i; + table[num].driver_data = i; table[num].frequency = CPUFREQ_TABLE_END; pxa3xx_freqs = freqs; diff --git a/drivers/cpufreq/s3c2416-cpufreq.c b/drivers/cpufreq/s3c2416-cpufreq.c index 4f1881eee3f1..600c9ad07cd4 100644 --- a/drivers/cpufreq/s3c2416-cpufreq.c +++ b/drivers/cpufreq/s3c2416-cpufreq.c @@ -231,7 +231,7 @@ static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int relation) { struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; - struct cpufreq_freqs freqs; + unsigned int new_freq; int idx, ret, to_dvs = 0; unsigned int i; @@ -244,7 +244,7 @@ static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy, if (ret != 0) goto out; - idx = s3c_freq->freq_table[i].index; + idx = s3c_freq->freq_table[i].driver_data; if (idx == SOURCE_HCLK) to_dvs = 1; @@ -256,25 +256,14 @@ static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy, goto out; } - freqs.flags = 0; - freqs.old = s3c_freq->is_dvs ? FREQ_DVS - : clk_get_rate(s3c_freq->armclk) / 1000; - /* When leavin dvs mode, always switch the armdiv to the hclk rate * The S3C2416 has stability issues when switching directly to * higher frequencies. */ - freqs.new = (s3c_freq->is_dvs && !to_dvs) + new_freq = (s3c_freq->is_dvs && !to_dvs) ? clk_get_rate(s3c_freq->hclk) / 1000 : s3c_freq->freq_table[i].frequency; - pr_debug("cpufreq: Transition %d-%dkHz\n", freqs.old, freqs.new); - - if (!to_dvs && freqs.old == freqs.new) - goto out; - - cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); - if (to_dvs) { pr_debug("cpufreq: enter dvs\n"); ret = s3c2416_cpufreq_enter_dvs(s3c_freq, idx); @@ -282,12 +271,10 @@ static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy, pr_debug("cpufreq: leave dvs\n"); ret = s3c2416_cpufreq_leave_dvs(s3c_freq, idx); } else { - pr_debug("cpufreq: change armdiv to %dkHz\n", freqs.new); - ret = s3c2416_cpufreq_set_armdiv(s3c_freq, freqs.new); + pr_debug("cpufreq: change armdiv to %dkHz\n", new_freq); + ret = s3c2416_cpufreq_set_armdiv(s3c_freq, new_freq); } - cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); - out: mutex_unlock(&cpufreq_lock); @@ -524,7 +511,6 @@ static struct freq_attr *s3c2416_cpufreq_attr[] = { }; static struct cpufreq_driver s3c2416_cpufreq_driver = { - .owner = THIS_MODULE, .flags = 0, .verify = s3c2416_cpufreq_verify_speed, .target = s3c2416_cpufreq_set_target, diff --git a/drivers/cpufreq/s3c64xx-cpufreq.c b/drivers/cpufreq/s3c64xx-cpufreq.c index 27cacb524796..c53c6a6d5e97 100644 --- a/drivers/cpufreq/s3c64xx-cpufreq.c +++ b/drivers/cpufreq/s3c64xx-cpufreq.c @@ -87,7 +87,7 @@ static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, freqs.old = clk_get_rate(armclk) / 1000; freqs.new = s3c64xx_freq_table[i].frequency; freqs.flags = 0; - dvfs = &s3c64xx_dvfs_table[s3c64xx_freq_table[i].index]; + dvfs = &s3c64xx_dvfs_table[s3c64xx_freq_table[i].driver_data]; if (freqs.old == freqs.new) return 0; @@ -259,7 +259,6 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy) } static struct cpufreq_driver s3c64xx_cpufreq_driver = { - .owner = THIS_MODULE, .flags = 0, .verify = s3c64xx_cpufreq_verify_speed, .target = s3c64xx_cpufreq_set_target, diff --git a/drivers/cpufreq/sc520_freq.c b/drivers/cpufreq/sc520_freq.c index f740b134d27b..d6f6c6f4efa7 100644 --- a/drivers/cpufreq/sc520_freq.c +++ b/drivers/cpufreq/sc520_freq.c @@ -71,7 +71,7 @@ static void sc520_freq_set_cpu_state(struct cpufreq_policy *policy, local_irq_disable(); clockspeed_reg = *cpuctl & ~0x03; - *cpuctl = clockspeed_reg | sc520_freq_table[state].index; + *cpuctl = clockspeed_reg | sc520_freq_table[state].driver_data; local_irq_enable(); @@ -147,7 +147,6 @@ static struct cpufreq_driver sc520_freq_driver = { .init = sc520_freq_cpu_init, .exit = sc520_freq_cpu_exit, .name = "sc520_freq", - .owner = THIS_MODULE, .attr = sc520_freq_attr, }; diff --git a/drivers/cpufreq/sh-cpufreq.c b/drivers/cpufreq/sh-cpufreq.c index 73adb64651e8..f671aa1e0bcb 100644 --- a/drivers/cpufreq/sh-cpufreq.c +++ b/drivers/cpufreq/sh-cpufreq.c @@ -87,15 +87,12 @@ static int sh_cpufreq_verify(struct cpufreq_policy *policy) if (freq_table) return cpufreq_frequency_table_verify(policy, freq_table); - cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, - policy->cpuinfo.max_freq); + cpufreq_verify_within_cpu_limits(policy); policy->min = (clk_round_rate(cpuclk, 1) + 500) / 1000; policy->max = (clk_round_rate(cpuclk, ~0UL) + 500) / 1000; - cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, - policy->cpuinfo.max_freq); - + cpufreq_verify_within_cpu_limits(policy); return 0; } @@ -160,7 +157,6 @@ static struct freq_attr *sh_freq_attr[] = { }; static struct cpufreq_driver sh_cpufreq_driver = { - .owner = THIS_MODULE, .name = "sh", .get = sh_cpufreq_get, .target = sh_cpufreq_target, diff --git a/drivers/cpufreq/sparc-us2e-cpufreq.c b/drivers/cpufreq/sparc-us2e-cpufreq.c index 306ae462bba6..8ea3a1b9098e 100644 --- a/drivers/cpufreq/sparc-us2e-cpufreq.c +++ b/drivers/cpufreq/sparc-us2e-cpufreq.c @@ -252,7 +252,6 @@ static void us2e_set_cpu_divider_index(struct cpufreq_policy *policy, unsigned long new_bits, new_freq; unsigned long clock_tick, divisor, old_divisor, estar; cpumask_t cpus_allowed; - struct cpufreq_freqs freqs; cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current)); set_cpus_allowed_ptr(current, cpumask_of(cpu)); @@ -266,16 +265,10 @@ static void us2e_set_cpu_divider_index(struct cpufreq_policy *policy, old_divisor = estar_to_divisor(estar); - freqs.old = clock_tick / old_divisor; - freqs.new = new_freq; - cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); - if (old_divisor != divisor) us2e_transition(estar, new_bits, clock_tick * 1000, old_divisor, divisor); - cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); - set_cpus_allowed_ptr(current, &cpus_allowed); } @@ -308,17 +301,17 @@ static int __init us2e_freq_cpu_init(struct cpufreq_policy *policy) struct cpufreq_frequency_table *table = &us2e_freq_table[cpu].table[0]; - table[0].index = 0; + table[0].driver_data = 0; table[0].frequency = clock_tick / 1; - table[1].index = 1; + table[1].driver_data = 1; table[1].frequency = clock_tick / 2; - table[2].index = 2; + table[2].driver_data = 2; table[2].frequency = clock_tick / 4; - table[2].index = 3; + table[2].driver_data = 3; table[2].frequency = clock_tick / 6; - table[2].index = 4; + table[2].driver_data = 4; table[2].frequency = clock_tick / 8; - table[2].index = 5; + table[2].driver_data = 5; table[3].frequency = CPUFREQ_TABLE_END; policy->cpuinfo.transition_latency = 0; @@ -351,12 +344,11 @@ static int __init us2e_freq_init(void) struct cpufreq_driver *driver; ret = -ENOMEM; - driver = kzalloc(sizeof(struct cpufreq_driver), GFP_KERNEL); + driver = kzalloc(sizeof(*driver), GFP_KERNEL); if (!driver) goto err_out; - us2e_freq_table = kzalloc( - (NR_CPUS * sizeof(struct us2e_freq_percpu_info)), + us2e_freq_table = kzalloc((NR_CPUS * sizeof(*us2e_freq_table)), GFP_KERNEL); if (!us2e_freq_table) goto err_out; @@ -366,7 +358,6 @@ static int __init us2e_freq_init(void) driver->target = us2e_freq_target; driver->get = us2e_freq_get; driver->exit = us2e_freq_cpu_exit; - driver->owner = THIS_MODULE, strcpy(driver->name, "UltraSPARC-IIe"); cpufreq_us2e_driver = driver; diff --git a/drivers/cpufreq/sparc-us3-cpufreq.c b/drivers/cpufreq/sparc-us3-cpufreq.c index c71ee142347a..241650ac95f2 100644 --- a/drivers/cpufreq/sparc-us3-cpufreq.c +++ b/drivers/cpufreq/sparc-us3-cpufreq.c @@ -99,7 +99,6 @@ static void us3_set_cpu_divider_index(struct cpufreq_policy *policy, unsigned int cpu = policy->cpu; unsigned long new_bits, new_freq, reg; cpumask_t cpus_allowed; - struct cpufreq_freqs freqs; cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current)); set_cpus_allowed_ptr(current, cpumask_of(cpu)); @@ -125,16 +124,10 @@ static void us3_set_cpu_divider_index(struct cpufreq_policy *policy, reg = read_safari_cfg(); - freqs.old = get_current_freq(cpu, reg); - freqs.new = new_freq; - cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); - reg &= ~SAFARI_CFG_DIV_MASK; reg |= new_bits; write_safari_cfg(reg); - cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); - set_cpus_allowed_ptr(current, &cpus_allowed); } @@ -169,13 +162,13 @@ static int __init us3_freq_cpu_init(struct cpufreq_policy *policy) struct cpufreq_frequency_table *table = &us3_freq_table[cpu].table[0]; - table[0].index = 0; + table[0].driver_data = 0; table[0].frequency = clock_tick / 1; - table[1].index = 1; + table[1].driver_data = 1; table[1].frequency = clock_tick / 2; - table[2].index = 2; + table[2].driver_data = 2; table[2].frequency = clock_tick / 32; - table[3].index = 0; + table[3].driver_data = 0; table[3].frequency = CPUFREQ_TABLE_END; policy->cpuinfo.transition_latency = 0; @@ -212,12 +205,11 @@ static int __init us3_freq_init(void) struct cpufreq_driver *driver; ret = -ENOMEM; - driver = kzalloc(sizeof(struct cpufreq_driver), GFP_KERNEL); + driver = kzalloc(sizeof(*driver), GFP_KERNEL); if (!driver) goto err_out; - us3_freq_table = kzalloc( - (NR_CPUS * sizeof(struct us3_freq_percpu_info)), + us3_freq_table = kzalloc((NR_CPUS * sizeof(*us3_freq_table)), GFP_KERNEL); if (!us3_freq_table) goto err_out; @@ -227,7 +219,6 @@ static int __init us3_freq_init(void) driver->target = us3_freq_target; driver->get = us3_freq_get; driver->exit = us3_freq_cpu_exit; - driver->owner = THIS_MODULE, strcpy(driver->name, "UltraSPARC-III"); cpufreq_us3_driver = driver; diff --git a/drivers/cpufreq/spear-cpufreq.c b/drivers/cpufreq/spear-cpufreq.c index 156829f4576d..c3efa7f2a908 100644 --- a/drivers/cpufreq/spear-cpufreq.c +++ b/drivers/cpufreq/spear-cpufreq.c @@ -250,11 +250,11 @@ static int spear_cpufreq_driver_init(void) } for (i = 0; i < cnt; i++) { - freq_tbl[i].index = i; + freq_tbl[i].driver_data = i; freq_tbl[i].frequency = be32_to_cpup(val++); } - freq_tbl[i].index = i; + freq_tbl[i].driver_data = i; freq_tbl[i].frequency = CPUFREQ_TABLE_END; spear_cpufreq.freq_tbl = freq_tbl; diff --git a/drivers/cpufreq/speedstep-centrino.c b/drivers/cpufreq/speedstep-centrino.c index 618e6f417b1c..f897d5105842 100644 --- a/drivers/cpufreq/speedstep-centrino.c +++ b/drivers/cpufreq/speedstep-centrino.c @@ -79,11 +79,11 @@ static struct cpufreq_driver centrino_driver; /* Computes the correct form for IA32_PERF_CTL MSR for a particular frequency/voltage operating point; frequency in MHz, volts in mV. - This is stored as "index" in the structure. */ + This is stored as "driver_data" in the structure. */ #define OP(mhz, mv) \ { \ .frequency = (mhz) * 1000, \ - .index = (((mhz)/100) << 8) | ((mv - 700) / 16) \ + .driver_data = (((mhz)/100) << 8) | ((mv - 700) / 16) \ } /* @@ -307,7 +307,7 @@ static unsigned extract_clock(unsigned msr, unsigned int cpu, int failsafe) per_cpu(centrino_model, cpu)->op_points[i].frequency != CPUFREQ_TABLE_END; i++) { - if (msr == per_cpu(centrino_model, cpu)->op_points[i].index) + if (msr == per_cpu(centrino_model, cpu)->op_points[i].driver_data) return per_cpu(centrino_model, cpu)-> op_points[i].frequency; } @@ -501,7 +501,7 @@ static int centrino_target (struct cpufreq_policy *policy, break; } - msr = per_cpu(centrino_model, cpu)->op_points[newstate].index; + msr = per_cpu(centrino_model, cpu)->op_points[newstate].driver_data; if (first_cpu) { rdmsr_on_cpu(good_cpu, MSR_IA32_PERF_CTL, &oldmsr, &h); @@ -575,7 +575,6 @@ static struct cpufreq_driver centrino_driver = { .target = centrino_target, .get = get_cur_freq, .attr = centrino_attr, - .owner = THIS_MODULE, }; /* diff --git a/drivers/cpufreq/speedstep-ich.c b/drivers/cpufreq/speedstep-ich.c index e2e5aa971452..5355abb69afc 100644 --- a/drivers/cpufreq/speedstep-ich.c +++ b/drivers/cpufreq/speedstep-ich.c @@ -378,7 +378,6 @@ static struct cpufreq_driver speedstep_driver = { .init = speedstep_cpu_init, .exit = speedstep_cpu_exit, .get = speedstep_get, - .owner = THIS_MODULE, .attr = speedstep_attr, }; diff --git a/drivers/cpufreq/speedstep-smi.c b/drivers/cpufreq/speedstep-smi.c index f5a6b70ee6c0..abfba4f731eb 100644 --- a/drivers/cpufreq/speedstep-smi.c +++ b/drivers/cpufreq/speedstep-smi.c @@ -375,7 +375,6 @@ static struct cpufreq_driver speedstep_driver = { .exit = speedstep_cpu_exit, .get = speedstep_get, .resume = speedstep_resume, - .owner = THIS_MODULE, .attr = speedstep_attr, }; diff --git a/drivers/cpufreq/unicore2-cpufreq.c b/drivers/cpufreq/unicore2-cpufreq.c index 12fc904d7dab..6e56ae0997e2 100644 --- a/drivers/cpufreq/unicore2-cpufreq.c +++ b/drivers/cpufreq/unicore2-cpufreq.c @@ -29,9 +29,7 @@ int ucv2_verify_speed(struct cpufreq_policy *policy) if (policy->cpu) return -EINVAL; - cpufreq_verify_within_limits(policy, - policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); - + cpufreq_verify_within_cpu_limits(policy); return 0; } diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c index c2d3704f2960..e22a2874e542 100644 --- a/drivers/crypto/msm/qce50.c +++ b/drivers/crypto/msm/qce50.c @@ -2618,8 +2618,9 @@ static void qce_sps_release_bam(struct qce_device *pce_dev) pr_debug("delete bam 0x%x\n", pbam->bam_mem); list_del(&pbam->qlist); kfree(pbam); - pce_dev->pbam = NULL; + ret: + pce_dev->pbam = NULL; mutex_unlock(&bam_register_lock); } diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c index b6e453ed7387..cb07ca58c1e1 100644 --- a/drivers/crypto/msm/qcrypto.c +++ b/drivers/crypto/msm/qcrypto.c @@ -380,10 +380,11 @@ static void qcrypto_ce_set_bus(struct crypto_engine *pengine, int ret = 0; if (high_bw_req && pengine->high_bw_req == false) { + pm_stay_awake(&pengine->pdev->dev); ret = qce_enable_clk(pengine->qce); if (ret) { pr_err("%s Unable enable clk\n", __func__); - return; + goto clk_err; } ret = msm_bus_scale_client_update_request( pengine->bus_scale_handle, 1); @@ -391,7 +392,7 @@ static void qcrypto_ce_set_bus(struct crypto_engine *pengine, pr_err("%s Unable to set to high bandwidth\n", __func__); qce_disable_clk(pengine->qce); - return; + goto clk_err; } pengine->high_bw_req = true; } else if (high_bw_req == false && pengine->high_bw_req == true) { @@ -400,7 +401,7 @@ static void qcrypto_ce_set_bus(struct crypto_engine *pengine, if (ret) { pr_err("%s Unable to set to low bandwidth\n", __func__); - return; + goto clk_err; } ret = qce_disable_clk(pengine->qce); if (ret) { @@ -410,10 +411,16 @@ static void qcrypto_ce_set_bus(struct crypto_engine *pengine, if (ret) pr_err("%s Unable to set to high bandwidth\n", __func__); - return; + goto clk_err; } pengine->high_bw_req = false; + pm_relax(&pengine->pdev->dev); } + return; +clk_err: + pm_relax(&pengine->pdev->dev); + return; + } static void qcrypto_bw_scale_down_timer_callback(unsigned long data) @@ -804,6 +811,7 @@ static void _qcrypto_remove_engine(struct crypto_engine *pengine) tasklet_kill(&pengine->done_tasklet); cancel_work_sync(&pengine->low_bw_req_ws); del_timer_sync(&pengine->bw_scale_down_timer); + device_init_wakeup(&pengine->pdev->dev, false); if (pengine->bus_scale_handle != 0) msm_bus_scale_unregister_client(pengine->bus_scale_handle); @@ -3792,6 +3800,9 @@ static int _qcrypto_probe(struct platform_device *pdev) INIT_WORK(&pengine->low_bw_req_ws, qcrypto_low_bw_req_work); pengine->bw_scale_down_timer.function = qcrypto_bw_scale_down_timer_callback; + + device_init_wakeup(&pengine->pdev->dev, true); + tasklet_init(&pengine->done_tasklet, req_done, (unsigned long)pengine); crypto_init_queue(&pengine->req_queue, 50); diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 4b705c74be35..15fa143312fe 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -71,6 +71,14 @@ config DEVFREQ_GOV_MSM_ADRENO_TZ Sets the frequency using a "on-demand" algorithm. This governor is unlikely to be useful for other devices. +config DEVFREQ_GOV_MSM_CPUFREQ + bool "MSM CPUfreq" + depends on CPU_FREQ_MSM + help + MSM CPUfreq based governor for CPU bandwidth voting. Sets the CPU + to DDR BW vote based on the current CPU frequency. This governor + is unlikely to be useful for non-MSM devices. + comment "DEVFREQ Drivers" config ARM_EXYNOS4_BUS_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 29b48ff42db4..3a960a4fbb38 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o obj-$(CONFIG_DEVFREQ_GOV_MSM_ADRENO_TZ) += governor_msm_adreno_tz.o +obj-$(CONFIG_DEVFREQ_GOV_MSM_CPUFREQ) += governor_msm_cpufreq.o # DEVFREQ Drivers obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos4_bus.o diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index d5b30e3d5a5c..802f39f9a698 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -18,7 +18,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/stat.h> -#include <linux/opp.h> +#include <linux/pm_opp.h> #include <linux/devfreq.h> #include <linux/workqueue.h> #include <linux/platform_device.h> @@ -936,7 +936,7 @@ static ssize_t show_available_freqs(struct device *d, rcu_read_lock(); do { - opp = opp_find_freq_ceil(dev, &freq); + opp = dev_pm_opp_find_freq_ceil(dev, &freq); if (IS_ERR(opp)) break; @@ -1062,18 +1062,18 @@ struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq, if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) { /* The freq is an upper bound. opp should be lower */ - opp = opp_find_freq_floor(dev, freq); + opp = dev_pm_opp_find_freq_floor(dev, freq); /* If not available, use the closest opp */ if (opp == ERR_PTR(-ERANGE)) - opp = opp_find_freq_ceil(dev, freq); + opp = dev_pm_opp_find_freq_ceil(dev, freq); } else { /* The freq is an lower bound. opp should be higher */ - opp = opp_find_freq_ceil(dev, freq); + opp = dev_pm_opp_find_freq_ceil(dev, freq); /* If not available, use the closest opp */ if (opp == ERR_PTR(-ERANGE)) - opp = opp_find_freq_floor(dev, freq); + opp = dev_pm_opp_find_freq_floor(dev, freq); } return opp; @@ -1092,7 +1092,7 @@ int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq) int ret = 0; rcu_read_lock(); - nh = opp_get_notifier(dev); + nh = dev_pm_opp_get_notifier(dev); if (IS_ERR(nh)) ret = PTR_ERR(nh); rcu_read_unlock(); @@ -1118,7 +1118,7 @@ int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq) int ret = 0; rcu_read_lock(); - nh = opp_get_notifier(dev); + nh = dev_pm_opp_get_notifier(dev); if (IS_ERR(nh)) ret = PTR_ERR(nh); rcu_read_unlock(); diff --git a/drivers/devfreq/governor_msm_cpufreq.c b/drivers/devfreq/governor_msm_cpufreq.c new file mode 100644 index 000000000000..9b13e26a3c3e --- /dev/null +++ b/drivers/devfreq/governor_msm_cpufreq.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2013, The Linux Foundation. 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 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. + */ + +#include <linux/devfreq.h> +#include <mach/cpufreq.h> +#include "governor.h" + +DEFINE_MUTEX(df_lock); +static struct devfreq *df; + +static int devfreq_msm_cpufreq_get_freq(struct devfreq *df, + unsigned long *freq, + u32 *flag) +{ + *freq = msm_cpufreq_get_bw(); + return 0; +} + +int devfreq_msm_cpufreq_update_bw(void) +{ + int ret = 0; + + mutex_lock(&df_lock); + if (df) { + mutex_lock(&df->lock); + ret = update_devfreq(df); + mutex_unlock(&df->lock); + } + mutex_unlock(&df_lock); + return ret; +} + +static int devfreq_msm_cpufreq_ev_handler(struct devfreq *devfreq, + unsigned int event, void *data) +{ + int ret; + + switch (event) { + case DEVFREQ_GOV_START: + mutex_lock(&df_lock); + df = devfreq; + mutex_unlock(&df_lock); + + ret = devfreq_msm_cpufreq_update_bw(); + if (ret) { + pr_err("Unable to update BW! Gov start failed!\n"); + return ret; + } + + devfreq_monitor_stop(df); + pr_debug("Enabled MSM CPUfreq governor\n"); + break; + + case DEVFREQ_GOV_STOP: + mutex_lock(&df_lock); + df = NULL; + mutex_unlock(&df_lock); + + pr_debug("Disabled MSM CPUfreq governor\n"); + break; + } + + return 0; +} + +static struct devfreq_governor devfreq_msm_cpufreq = { + .name = "msm_cpufreq", + .get_target_freq = devfreq_msm_cpufreq_get_freq, + .event_handler = devfreq_msm_cpufreq_ev_handler, +}; + +int register_devfreq_msm_cpufreq(void) +{ + return devfreq_add_governor(&devfreq_msm_cpufreq); +} diff --git a/drivers/esoc/Kconfig b/drivers/esoc/Kconfig index 2d30aee4a102..f66d5576b22b 100644 --- a/drivers/esoc/Kconfig +++ b/drivers/esoc/Kconfig @@ -24,4 +24,20 @@ config ESOC_DEBUG help Say yes here to enable debugging support in the ESOC framework and individual esoc drivers. + +config ESOC_MDM_4x + bool "Add support for external mdm9x25/mdm9x35" + help + In some qualcomm boards, an external modem such as mdm9x25 or mdm9x35 + is connected to a primary msm. The primary soc can control/monitor + the modem via gpios. The data communication with such modems can + occur over PCIE or HSIC. + +config ESOC_MDM_DRV + tristate "Command engine for 4x series external modems" + help + Provides a command engine to control the behavior of an external modem + such as mdm9x25/mdm9x35/QSC. Allows the primary soc to put the + external modem in a specific mode. Also listens for events on the + external modem. endif diff --git a/drivers/esoc/Makefile b/drivers/esoc/Makefile index 8720bda39527..394930d7195a 100644 --- a/drivers/esoc/Makefile +++ b/drivers/esoc/Makefile @@ -3,4 +3,5 @@ ccflags-$(CONFIG_ESOC_DEBUG) := -DDEBUG obj-$(CONFIG_ESOC) += esoc_bus.o obj-$(CONFIG_ESOC_DEV) += esoc_dev.o - +obj-$(CONFIG_ESOC_MDM_4x) += esoc-mdm-4x.o +obj-$(CONFIG_ESOC_MDM_DRV) += esoc-mdm-drv.o diff --git a/drivers/esoc/esoc-mdm-4x.c b/drivers/esoc/esoc-mdm-4x.c new file mode 100644 index 000000000000..1fba10c2d3c6 --- /dev/null +++ b/drivers/esoc/esoc-mdm-4x.c @@ -0,0 +1,857 @@ +/* Copyright (c) 2013, The Linux Foundation. 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 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. + */ + +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> +#include <mach/gpiomux.h> +#include <mach/sysmon.h> +#include "esoc.h" + +#define MDM_PBLRDY_CNT 20 +#define INVALID_GPIO (-1) +#define MDM_GPIO(mdm, i) (mdm->gpios[i]) +#define MDM9x25_LABEL "MDM9x25" +#define MDM9x25_HSIC "HSIC" +#define MDM2AP_STATUS_TIMEOUT_MS 120000L +#define MDM_MODEM_TIMEOUT 6000 +#define MDM_MODEM_DELTA 100 +#define DEF_RAMDUMP_TIMEOUT 120000 +#define DEF_RAMDUMP_DELAY 2000 +#define RD_BUF_SIZE 100 +#define SFR_MAX_RETRIES 10 +#define SFR_RETRY_INTERVAL 1000 + +enum mdm_gpio { + AP2MDM_WAKEUP = 0, + AP2MDM_STATUS, + AP2MDM_SOFT_RESET, + AP2MDM_VDD_MIN, + AP2MDM_CHNLRDY, + AP2MDM_ERRFATAL, + AP2MDM_VDDMIN, + AP2MDM_PMIC_PWR_EN, + MDM2AP_WAKEUP, + MDM2AP_ERRFATAL, + MDM2AP_PBLRDY, + MDM2AP_STATUS, + MDM2AP_VDDMIN, + NUM_GPIOS, +}; + +enum gpio_update_config { + GPIO_UPDATE_BOOTING_CONFIG = 1, + GPIO_UPDATE_RUNNING_CONFIG, +}; + +enum irq_mask { + IRQ_ERRFATAL = 0x1, + IRQ_STATUS = 0x2, + IRQ_PBLRDY = 0x4, +}; + +struct mdm_ctrl { + unsigned gpios[NUM_GPIOS]; + spinlock_t status_lock; + struct workqueue_struct *mdm_queue; + struct delayed_work mdm2ap_status_check_work; + struct work_struct mdm_status_work; + struct work_struct restart_reason_work; + struct completion debug_done; + struct device *dev; + struct gpiomux_setting *mdm2ap_status_gpio_run_cfg; + struct gpiomux_setting mdm2ap_status_old_config; + int mdm2ap_status_valid_old_config; + int soft_reset_inverted; + int errfatal_irq; + int status_irq; + int pblrdy_irq; + int debug; + bool debug_fail; + unsigned int dump_timeout_ms; + unsigned int ramdump_delay_ms; + int sysmon_subsys_id; + struct esoc_clink *esoc; + bool get_restart_reason; + unsigned long irq_mask; + bool ready; + u32 status; +}; + +struct mdm_ops { + struct esoc_clink_ops *clink_ops; + int (*config_hw)(struct mdm_ctrl *mdm, + const struct esoc_clink_ops const *ops, + struct platform_device *pdev); +}; + +static struct gpio_map { + const char *name; + int index; +} gpio_map[] = { + {"qcom,mdm2ap-errfatal-gpio", MDM2AP_ERRFATAL}, + {"qcom,ap2mdm-errfatal-gpio", AP2MDM_ERRFATAL}, + {"qcom,mdm2ap-status-gpio", MDM2AP_STATUS}, + {"qcom,ap2mdm-status-gpio", AP2MDM_STATUS}, + {"qcom,mdm2ap-pblrdy-gpio", MDM2AP_PBLRDY}, + {"qcom,ap2mdm-wakeup-gpio", AP2MDM_WAKEUP}, + {"qcom,ap2mdm-chnlrdy-gpio", AP2MDM_CHNLRDY}, + {"qcom,mdm2ap-wakeup-gpio", MDM2AP_WAKEUP}, + {"qcom,ap2mdm-vddmin-gpio", AP2MDM_VDDMIN}, + {"qcom,mdm2ap-vddmin-gpio", MDM2AP_VDDMIN}, + {"qcom,ap2mdm-pmic-pwr-en-gpio", AP2MDM_PMIC_PWR_EN}, +}; + +/* Required gpios */ +static const int required_gpios[] = { + MDM2AP_ERRFATAL, + AP2MDM_ERRFATAL, + MDM2AP_STATUS, + AP2MDM_STATUS, + AP2MDM_SOFT_RESET +}; + +static void mdm_debug_gpio_show(struct mdm_ctrl *mdm) +{ + struct device *dev = mdm->dev; + + dev_dbg(dev, "%s: MDM2AP_ERRFATAL gpio = %d\n", + __func__, MDM_GPIO(mdm, MDM2AP_ERRFATAL)); + dev_dbg(dev, "%s: AP2MDM_ERRFATAL gpio = %d\n", + __func__, MDM_GPIO(mdm, AP2MDM_ERRFATAL)); + dev_dbg(dev, "%s: MDM2AP_STATUS gpio = %d\n", + __func__, MDM_GPIO(mdm, MDM2AP_STATUS)); + dev_dbg(dev, "%s: AP2MDM_STATUS gpio = %d\n", + __func__, MDM_GPIO(mdm, AP2MDM_STATUS)); + dev_dbg(dev, "%s: AP2MDM_SOFT_RESET gpio = %d\n", + __func__, MDM_GPIO(mdm, AP2MDM_SOFT_RESET)); + dev_dbg(dev, "%s: MDM2AP_WAKEUP gpio = %d\n", + __func__, MDM_GPIO(mdm, MDM2AP_WAKEUP)); + dev_dbg(dev, "%s: AP2MDM_WAKEUP gpio = %d\n", + __func__, MDM_GPIO(mdm, AP2MDM_WAKEUP)); + dev_dbg(dev, "%s: AP2MDM_PMIC_PWR_EN gpio = %d\n", + __func__, MDM_GPIO(mdm, AP2MDM_PMIC_PWR_EN)); + dev_dbg(dev, "%s: MDM2AP_PBLRDY gpio = %d\n", + __func__, MDM_GPIO(mdm, MDM2AP_PBLRDY)); + dev_dbg(dev, "%s: AP2MDM_VDDMIN gpio = %d\n", + __func__, MDM_GPIO(mdm, AP2MDM_VDDMIN)); + dev_dbg(dev, "%s: MDM2AP_VDDMIN gpio = %d\n", + __func__, MDM_GPIO(mdm, MDM2AP_VDDMIN)); +} + +static void mdm_enable_irqs(struct mdm_ctrl *mdm) +{ + if (!mdm) + return; + if (mdm->irq_mask & IRQ_ERRFATAL) { + enable_irq(mdm->errfatal_irq); + mdm->irq_mask &= ~IRQ_ERRFATAL; + } + if (mdm->irq_mask & IRQ_STATUS) { + enable_irq(mdm->status_irq); + mdm->irq_mask &= ~IRQ_STATUS; + } + if (mdm->irq_mask & IRQ_PBLRDY) { + enable_irq(mdm->pblrdy_irq); + mdm->irq_mask &= ~IRQ_PBLRDY; + } +} + +static void mdm_disable_irqs(struct mdm_ctrl *mdm) +{ + if (!mdm) + return; + if (!(mdm->irq_mask & IRQ_ERRFATAL)) { + disable_irq_nosync(mdm->errfatal_irq); + mdm->irq_mask |= IRQ_ERRFATAL; + } + if (!(mdm->irq_mask & IRQ_STATUS)) { + disable_irq_nosync(mdm->status_irq); + mdm->irq_mask |= IRQ_STATUS; + } + if (!(mdm->irq_mask & IRQ_PBLRDY)) { + disable_irq_nosync(mdm->pblrdy_irq); + mdm->irq_mask |= IRQ_PBLRDY; + } +} + +static void mdm_deconfigure_ipc(struct mdm_ctrl *mdm) +{ + int i; + + for (i = 0; i < NUM_GPIOS; ++i) { + if (gpio_is_valid(MDM_GPIO(mdm, i))) + gpio_free(MDM_GPIO(mdm, i)); + } + if (mdm->mdm_queue) { + destroy_workqueue(mdm->mdm_queue); + mdm->mdm_queue = NULL; + } +} + +static void mdm_update_gpio_configs(struct mdm_ctrl *mdm, + enum gpio_update_config gpio_config) +{ + struct device *dev = mdm->dev; + /* Some gpio configuration may need updating after modem bootup.*/ + switch (gpio_config) { + case GPIO_UPDATE_RUNNING_CONFIG: + if (mdm->mdm2ap_status_gpio_run_cfg) { + if (msm_gpiomux_write(MDM_GPIO(mdm, MDM2AP_STATUS), + GPIOMUX_ACTIVE, + mdm->mdm2ap_status_gpio_run_cfg, + &mdm->mdm2ap_status_old_config)) + dev_err(dev, "switch to run failed\n"); + else + mdm->mdm2ap_status_valid_old_config = 1; + } + break; + case GPIO_UPDATE_BOOTING_CONFIG: + if (mdm->mdm2ap_status_valid_old_config) { + msm_gpiomux_write(MDM_GPIO(mdm, MDM2AP_STATUS), + GPIOMUX_ACTIVE, + &mdm->mdm2ap_status_old_config, + NULL); + mdm->mdm2ap_status_valid_old_config = 0; + } + break; + default: + dev_err(dev, "%s: called with no config\n", __func__); + break; + } +} + +/* This function can be called from atomic context. */ +static void mdm_toggle_soft_reset(struct mdm_ctrl *mdm) +{ + int soft_reset_direction_assert = 0, + soft_reset_direction_de_assert = 1; + + if (mdm->soft_reset_inverted) { + soft_reset_direction_assert = 1; + soft_reset_direction_de_assert = 0; + } + gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), + soft_reset_direction_assert); + /* + * Allow PS hold assert to be detected + */ + usleep_range(8000, 9000); + gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), + soft_reset_direction_de_assert); +} + +static void mdm_do_first_power_on(struct mdm_ctrl *mdm) +{ + int i; + int pblrdy; + struct device *dev = mdm->dev; + + dev_dbg(dev, "Powering on modem for the first time\n"); + mdm_toggle_soft_reset(mdm); + /* Add a delay to allow PON sequence to complete*/ + msleep(50); + gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 1); + for (i = 0; i < MDM_PBLRDY_CNT; i++) { + pblrdy = gpio_get_value(MDM_GPIO(mdm, MDM2AP_PBLRDY)); + if (pblrdy) + break; + usleep_range(5000, 6000); + } + dev_dbg(dev, "pblrdy i:%d\n", i); + msleep(200); +} + +static void mdm_power_down(struct mdm_ctrl *mdm) +{ + struct device *dev = mdm->dev; + int soft_reset_direction = mdm->soft_reset_inverted ? 1 : 0; + /* Assert the soft reset line whether mdm2ap_status went low or not */ + gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), + soft_reset_direction); + dev_dbg(dev, "Doing a hard reset\n"); + gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), + soft_reset_direction); + /* + * Currently, there is a debounce timer on the charm PMIC. It is + * necessary to hold the PMIC RESET low for ~3.5 seconds + * for the reset to fully take place. Sleep here to ensure the + * reset has occured before the function exits. + */ + msleep(4000); +} + +static int mdm_cmd_exe(enum esoc_cmd cmd, struct esoc_clink *esoc) +{ + int ret; + unsigned long end_time; + bool status_down = false; + struct mdm_ctrl *mdm = get_esoc_clink_data(esoc); + struct device *dev = mdm->dev; + + switch (cmd) { + case ESOC_PWR_ON: + gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0); + mdm_enable_irqs(mdm); + mdm_do_first_power_on(mdm); + break; + case ESOC_PWR_OFF: + mdm_disable_irqs(mdm); + mdm->debug = 0; + mdm->ready = false; + ret = sysmon_send_shutdown(mdm->sysmon_subsys_id); + if (ret) + dev_err(mdm->dev, "Graceful shutdown fail, ret = %d\n", + ret); + else { + dev_dbg(mdm->dev, "Waiting for status gpio go low\n"); + status_down = false; + end_time = jiffies + msecs_to_jiffies(10000); + while (time_before(jiffies, end_time)) { + if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)) + == 0) { + dev_dbg(dev, "Status went low\n"); + status_down = true; + break; + } + msleep(100); + } + if (status_down) { + dev_err(dev, "forcing shutdown\n"); + gpio_set_value(MDM_GPIO(mdm, AP2MDM_STATUS), 0); + mdm_update_gpio_configs(mdm, + GPIO_UPDATE_BOOTING_CONFIG); + dev_dbg(dev, "shutdown successful\n"); + return 0; + } else + dev_err(mdm->dev, "graceful poff ipc fail\n"); + } + mdm_power_down(mdm); + mdm_update_gpio_configs(mdm, GPIO_UPDATE_BOOTING_CONFIG); + gpio_set_value(MDM_GPIO(mdm, AP2MDM_STATUS), 0); + break; + case ESOC_RESET: + mdm_toggle_soft_reset(mdm); + break; + case ESOC_PREPARE_DEBUG: + /* + * disable all irqs except request irq (pblrdy) + * force a reset of the mdm by signaling + * an APQ crash, wait till mdm is ready for ramdumps. + * if mdm has not reset till then, force a reset + */ + mdm->debug = 1; + mdm->ready = false; + cancel_delayed_work(&mdm->mdm2ap_status_check_work); + gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1); + dev_dbg(mdm->dev, "set ap2mdm errfatal to force reset\n"); + msleep(mdm->ramdump_delay_ms); + if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)) != 0) + mdm_toggle_soft_reset(mdm); + break; + case ESOC_EXE_DEBUG: + /* + * wait for ramdumps to be collected + * then power down the mdm and switch gpios to booting + * config + */ + if (!wait_for_completion_timeout(&mdm->debug_done, + msecs_to_jiffies(mdm->dump_timeout_ms))) { + dev_err(mdm->dev, "ramdump collection timedout\n"); + return -ETIMEDOUT; + } + if (mdm->debug_fail) { + dev_err(mdm->dev, "unable to collect ramdumps\n"); + return -EIO; + } + dev_dbg(mdm->dev, "ramdump collection done\n"); + init_completion(&mdm->debug_done); + break; + case ESOC_EXIT_DEBUG: + /* + * Deassert APQ to mdm err fatal + * Power on the mdm + */ + mdm->debug = 0; + gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0); + dev_dbg(mdm->dev, "exiting debug state after power on\n"); + mdm->get_restart_reason = true; + break; + default: + return -EINVAL; + }; + return 0; +} + +static void mdm2ap_status_check(struct work_struct *work) +{ + struct mdm_ctrl *mdm = + container_of(work, struct mdm_ctrl, + mdm2ap_status_check_work.work); + struct device *dev = mdm->dev; + struct esoc_clink *esoc = mdm->esoc; + if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)) == 0) { + dev_dbg(dev, "MDM2AP_STATUS did not go high\n"); + esoc_clink_evt_notify(ESOC_UNEXPECTED_RESET, esoc); + } +} + +static void mdm_status_fn(struct work_struct *work) +{ + struct mdm_ctrl *mdm = + container_of(work, struct mdm_ctrl, mdm_status_work); + struct device *dev = mdm->dev; + int value = gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)); + + dev_dbg(dev, "%s: status:%d\n", __func__, value); + /* Update gpio configuration to "running" config. */ + mdm_update_gpio_configs(mdm, GPIO_UPDATE_RUNNING_CONFIG); +} + +static void mdm_get_restart_reason(struct work_struct *work) +{ + int ret, ntries = 0; + char sfr_buf[RD_BUF_SIZE]; + struct mdm_ctrl *mdm = + container_of(work, struct mdm_ctrl, restart_reason_work); + struct device *dev = mdm->dev; + + do { + ret = sysmon_get_reason(mdm->sysmon_subsys_id, sfr_buf, + sizeof(sfr_buf)); + if (!ret) { + dev_err(dev, "mdm restart reason is %s\n", sfr_buf); + break; + } + msleep(SFR_RETRY_INTERVAL); + } while (++ntries < SFR_MAX_RETRIES); + if (ntries == SFR_MAX_RETRIES) + dev_dbg(dev, "%s: Error retrieving restart reason: %d\n", + __func__, ret); + mdm->get_restart_reason = false; +} + +static void mdm_notify(enum esoc_notify notify, struct esoc_clink *esoc) +{ + bool status_down; + unsigned long end_time; + struct mdm_ctrl *mdm = get_esoc_clink_data(esoc); + struct device *dev = mdm->dev; + + switch (notify) { + case ESOC_IMG_XFER_DONE: + if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)) == 0) + schedule_delayed_work(&mdm->mdm2ap_status_check_work, + msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS)); + break; + case ESOC_IMG_XFER_RETRY: + mdm_toggle_soft_reset(mdm); + break; + case ESOC_IMG_XFER_FAIL: + esoc_clink_evt_notify(ESOC_BOOT_FAIL, esoc); + break; + case ESOC_UPGRADE_AVAILABLE: + break; + case ESOC_DEBUG_DONE: + mdm->debug_fail = false; + mdm_update_gpio_configs(mdm, GPIO_UPDATE_BOOTING_CONFIG); + complete(&mdm->debug_done); + break; + case ESOC_DEBUG_FAIL: + mdm->debug_fail = true; + complete(&mdm->debug_done); + break; + case ESOC_PRIMARY_CRASH: + mdm_disable_irqs(mdm); + status_down = false; + dev_dbg(dev, "signal apq err fatal for graceful restart\n"); + gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1); + end_time = jiffies + msecs_to_jiffies(MDM_MODEM_TIMEOUT); + while (time_before(jiffies, end_time)) { + msleep(MDM_MODEM_DELTA); + if (gpio_get_value(MDM_GPIO(mdm, + MDM2AP_STATUS)) == 0) { + status_down = true; + break; + } + } + if (!status_down) { + dev_err(mdm->dev, "%s MDM2AP status didnot go low\n", + __func__); + mdm_toggle_soft_reset(mdm); + } + break; + }; + return; +} + +static irqreturn_t mdm_errfatal(int irq, void *dev_id) +{ + struct mdm_ctrl *mdm = (struct mdm_ctrl *)dev_id; + struct esoc_clink *esoc; + struct device *dev; + + if (!mdm) + goto no_mdm_irq; + dev = mdm->dev; + if (!mdm->ready) + goto mdm_pwroff_irq; + esoc = mdm->esoc; + dev_err(dev, "%s: mdm sent errfatal interrupt\n", + __func__); + /* disable irq ?*/ + esoc_clink_evt_notify(ESOC_ERR_FATAL, esoc); + return IRQ_HANDLED; +mdm_pwroff_irq: + dev_info(dev, "errfatal irq when in pwroff\n"); +no_mdm_irq: + return IRQ_HANDLED; +} + +static irqreturn_t mdm_status_change(int irq, void *dev_id) +{ + int value; + struct esoc_clink *esoc; + struct mdm_ctrl *mdm = (struct mdm_ctrl *)dev_id; + struct device *dev = mdm->dev; + + if (!mdm) + return IRQ_HANDLED; + esoc = mdm->esoc; + value = gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)); + if (value == 0 && mdm->ready) { + dev_err(dev, "unexpected reset external modem\n"); + esoc_clink_evt_notify(ESOC_UNEXPECTED_RESET, esoc); + } else if (value == 1) { + cancel_delayed_work(&mdm->mdm2ap_status_check_work); + dev_dbg(dev, "status = 1: mdm is now ready\n"); + esoc_clink_evt_notify(ESOC_RUN_STATE, esoc); + mdm->ready = true; + queue_work(mdm->mdm_queue, &mdm->mdm_status_work); + if (mdm->get_restart_reason) + queue_work(mdm->mdm_queue, &mdm->restart_reason_work); + } + return IRQ_HANDLED; +} + +static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id) +{ + struct mdm_ctrl *mdm; + struct device *dev; + struct esoc_clink *esoc; + + mdm = (struct mdm_ctrl *)dev_id; + if (!mdm) + return IRQ_HANDLED; + esoc = mdm->esoc; + dev = mdm->dev; + dev_dbg(dev, "pbl ready %d:\n", + gpio_get_value(MDM_GPIO(mdm, MDM2AP_PBLRDY))); + if (mdm->debug) { + esoc_clink_queue_request(ESOC_REQ_DEBUG, esoc); + return IRQ_HANDLED; + } + esoc_clink_queue_request(ESOC_REQ_IMG, esoc); + return IRQ_HANDLED; +} + +static int mdm_get_status(u32 *status, struct esoc_clink *esoc) +{ + struct mdm_ctrl *mdm = get_esoc_clink_data(esoc); + + if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)) == 0) + *status = 0; + else + *status = 1; + return 0; +} + +/* Fail if any of the required gpios is absent. */ +static int mdm_dt_parse_gpios(struct mdm_ctrl *mdm) +{ + int i, val, rc = 0; + struct device_node *node = mdm->dev->of_node; + enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW; + + for (i = 0; i < NUM_GPIOS; i++) + mdm->gpios[i] = INVALID_GPIO; + + for (i = 0; i < ARRAY_SIZE(gpio_map); i++) { + val = of_get_named_gpio(node, gpio_map[i].name, 0); + if (val >= 0) + MDM_GPIO(mdm, gpio_map[i].index) = val; + } + /* These two are special because they can be inverted. */ + val = of_get_named_gpio_flags(node, "qcom,ap2mdm-soft-reset-gpio", + 0, &flags); + if (val >= 0) { + MDM_GPIO(mdm, AP2MDM_SOFT_RESET) = val; + if (flags & OF_GPIO_ACTIVE_LOW) + mdm->soft_reset_inverted = 1; + } + /* Verify that the required gpios have valid values */ + for (i = 0; i < ARRAY_SIZE(required_gpios); i++) { + if (MDM_GPIO(mdm, required_gpios[i]) == INVALID_GPIO) { + rc = -ENXIO; + break; + } + } + mdm_debug_gpio_show(mdm); + return rc; +} + +static int mdm_configure_ipc(struct mdm_ctrl *mdm, struct platform_device *pdev) +{ + int ret = -1; + int irq; + struct device *dev = mdm->dev; + struct device_node *node = pdev->dev.of_node; + + ret = of_property_read_u32(node, "qcom,ramdump-timeout-ms", + &mdm->dump_timeout_ms); + if (ret) + mdm->dump_timeout_ms = DEF_RAMDUMP_TIMEOUT; + ret = of_property_read_u32(node, "qcom,ramdump-delay-ms", + &mdm->ramdump_delay_ms); + if (ret) + mdm->ramdump_delay_ms = DEF_RAMDUMP_DELAY; + /* Multilple gpio_request calls are allowed */ + if (gpio_request(MDM_GPIO(mdm, AP2MDM_STATUS), "AP2MDM_STATUS")) + dev_err(dev, "Failed to configure AP2MDM_STATUS gpio\n"); + /* Multilple gpio_request calls are allowed */ + if (gpio_request(MDM_GPIO(mdm, AP2MDM_ERRFATAL), "AP2MDM_ERRFATAL")) + dev_err(dev, "%s Failed to configure AP2MDM_ERRFATAL gpio\n", + __func__); + if (gpio_request(MDM_GPIO(mdm, MDM2AP_STATUS), "MDM2AP_STATUS")) { + dev_err(dev, "%s Failed to configure MDM2AP_STATUS gpio\n", + __func__); + goto fatal_err; + } + if (gpio_request(MDM_GPIO(mdm, MDM2AP_ERRFATAL), "MDM2AP_ERRFATAL")) { + dev_err(dev, "%s Failed to configure MDM2AP_ERRFATAL gpio\n", + __func__); + goto fatal_err; + } + if (gpio_is_valid(MDM_GPIO(mdm, MDM2AP_PBLRDY))) { + if (gpio_request(MDM_GPIO(mdm, MDM2AP_PBLRDY), + "MDM2AP_PBLRDY")) { + dev_err(dev, "Cannot configure MDM2AP_PBLRDY gpio\n"); + goto fatal_err; + } + } + if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_SOFT_RESET))) { + if (gpio_request(MDM_GPIO(mdm, AP2MDM_SOFT_RESET), + "AP2MDM_SOFT_RESET")) { + dev_err(dev, "Cannot config AP2MDM_SOFT_RESET gpio\n"); + goto fatal_err; + } + } + if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_WAKEUP))) { + if (gpio_request(MDM_GPIO(mdm, AP2MDM_WAKEUP), + "AP2MDM_WAKEUP")) { + dev_err(dev, "Cannot configure AP2MDM_WAKEUP gpio\n"); + goto fatal_err; + } + } + if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_CHNLRDY))) { + if (gpio_request(MDM_GPIO(mdm, AP2MDM_CHNLRDY), + "AP2MDM_CHNLRDY")) { + dev_err(dev, "Cannot configure AP2MDM_CHNLRDY gpio\n"); + goto fatal_err; + } + } + ret = of_property_read_u32(node, "qcom,sysmon-subsys-id", + &mdm->sysmon_subsys_id); + if (ret < 0) + dev_dbg(dev, "sysmon_subsys_id not set.\n"); + + gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 0); + gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0); + + if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_CHNLRDY))) + gpio_direction_output(MDM_GPIO(mdm, AP2MDM_CHNLRDY), 0); + + gpio_direction_input(MDM_GPIO(mdm, MDM2AP_STATUS)); + gpio_direction_input(MDM_GPIO(mdm, MDM2AP_ERRFATAL)); + + /* ERR_FATAL irq. */ + irq = platform_get_irq_byname(pdev, "err_fatal_irq"); + if (irq < 0) { + dev_err(dev, "bad MDM2AP_ERRFATAL IRQ resource\n"); + goto errfatal_err; + } + ret = request_irq(irq, mdm_errfatal, + IRQF_TRIGGER_RISING , "mdm errfatal", mdm); + + if (ret < 0) { + dev_err(dev, "%s: MDM2AP_ERRFATAL IRQ#%d request failed,\n", + __func__, irq); + goto errfatal_err; + } + mdm->errfatal_irq = irq; + +errfatal_err: + /* status irq */ + irq = platform_get_irq_byname(pdev, "status_irq"); + if (irq < 0) { + dev_err(dev, "%s: bad MDM2AP_STATUS IRQ resource, err = %d\n", + __func__, irq); + goto status_err; + } + ret = request_threaded_irq(irq, NULL, mdm_status_change, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "mdm status", mdm); + if (ret < 0) { + dev_err(dev, "%s: MDM2AP_STATUS IRQ#%d request failed, err=%d", + __func__, irq, ret); + goto status_err; + } + mdm->status_irq = irq; +status_err: + if (gpio_is_valid(MDM_GPIO(mdm, MDM2AP_PBLRDY))) { + irq = platform_get_irq_byname(pdev, "plbrdy_irq"); + if (irq < 0) { + dev_err(dev, "%s: MDM2AP_PBLRDY IRQ request failed\n", + __func__); + goto pblrdy_err; + } + + ret = request_threaded_irq(irq, NULL, mdm_pblrdy_change, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "mdm pbl ready", mdm); + if (ret < 0) { + dev_err(dev, "MDM2AP_PBL IRQ#%d request failed %d\n", + irq, ret); + goto pblrdy_err; + } + mdm->pblrdy_irq = irq; + } + mdm_disable_irqs(mdm); +pblrdy_err: + return 0; +fatal_err: + mdm_deconfigure_ipc(mdm); + return ret; + +} + +static int mdm9x25_setup_hw(struct mdm_ctrl *mdm, + struct esoc_clink_ops const *ops, + struct platform_device *pdev) +{ + int ret; + struct esoc_clink *esoc; + + mdm->dev = &pdev->dev; + esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL); + if (IS_ERR(esoc)) { + dev_err(mdm->dev, "cannot allocate esoc device\n"); + return PTR_ERR(esoc); + } + mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0); + if (!mdm->mdm_queue) { + dev_err(mdm->dev, "could not create mdm_queue\n"); + return -ENOMEM; + } + mdm->irq_mask = 0; + mdm->ready = false; + ret = mdm_dt_parse_gpios(mdm); + if (ret) + return ret; + dev_err(mdm->dev, "parsing gpio done\n"); + ret = mdm_configure_ipc(mdm, pdev); + if (ret) + return ret; + dev_err(mdm->dev, "ipc configure done\n"); + esoc->name = MDM9x25_LABEL; + esoc->link_name = MDM9x25_HSIC; + esoc->clink_ops = ops; + esoc->parent = mdm->dev; + esoc->owner = THIS_MODULE; + set_esoc_clink_data(esoc, mdm); + ret = esoc_clink_register(esoc); + if (ret) { + dev_err(mdm->dev, "esoc registration failed\n"); + return ret; + } + dev_dbg(mdm->dev, "esoc registration done\n"); + init_completion(&mdm->debug_done); + INIT_WORK(&mdm->mdm_status_work, mdm_status_fn); + INIT_WORK(&mdm->restart_reason_work, mdm_get_restart_reason); + INIT_DELAYED_WORK(&mdm->mdm2ap_status_check_work, mdm2ap_status_check); + mdm->get_restart_reason = false; + mdm->debug_fail = false; + mdm->esoc = esoc; + return 0; +} + +static struct esoc_clink_ops mdm_cops = { + .cmd_exe = mdm_cmd_exe, + .get_status = mdm_get_status, + .notify = mdm_notify, +}; + +static struct mdm_ops mdm9x25_ops = { + .clink_ops = &mdm_cops, + .config_hw = mdm9x25_setup_hw, +}; + +static const struct of_device_id mdm_dt_match[] = { + { .compatible = "qcom,ext-mdm9x25", + .data = &mdm9x25_ops, }, + {}, +}; +MODULE_DEVICE_TABLE(of, mdm_dt_match); + +static int mdm_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + const struct mdm_ops *mdm_ops; + struct device_node *node = pdev->dev.of_node; + struct mdm_ctrl *mdm; + + match = of_match_node(mdm_dt_match, node); + if (IS_ERR(match)) + return PTR_ERR(match); + mdm_ops = match->data; + mdm = devm_kzalloc(&pdev->dev, sizeof(*mdm), GFP_KERNEL); + if (IS_ERR(mdm)) + return PTR_ERR(mdm); + return mdm_ops->config_hw(mdm, mdm_ops->clink_ops, pdev); +} + +static struct platform_driver mdm_driver = { + .probe = mdm_probe, + .driver = { + .name = "ext-mdm", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(mdm_dt_match), + }, +}; + +static int __init mdm_register(void) +{ + return platform_driver_register(&mdm_driver); +} +module_init(mdm_register); + +static void __exit mdm_unregister(void) +{ + platform_driver_unregister(&mdm_driver); +} +module_exit(mdm_unregister); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/esoc/esoc-mdm-drv.c b/drivers/esoc/esoc-mdm-drv.c new file mode 100644 index 000000000000..47f6a136272e --- /dev/null +++ b/drivers/esoc/esoc-mdm-drv.c @@ -0,0 +1,250 @@ +/* Copyright (c) 2013, The Linux Foundation. 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 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. + */ + +#include <linux/delay.h> +#include <linux/workqueue.h> +#include "esoc.h" + +enum { + PWR_OFF = 0x1, + PWR_ON, + BOOT, + RUN, + CRASH, + IN_DEBUG, + SHUTDOWN, + RESET, + PEER_CRASH, +}; + +#define MDM_BOOT_TIMEOUT 60000L + +struct mdm_drv { + unsigned mode; + struct esoc_eng cmd_eng; + struct completion boot_done; + struct esoc_clink *esoc_clink; + bool boot_fail; + struct workqueue_struct *mdm_queue; + struct work_struct ssr_work; +}; + +#define to_mdm_drv(d) container_of(d, struct mdm_drv, cmd_eng) + +static void mdm_handle_clink_evt(enum esoc_evt evt, + struct esoc_eng *eng) +{ + struct mdm_drv *mdm_drv = to_mdm_drv(eng); + switch (evt) { + case ESOC_BOOT_FAIL: + mdm_drv->boot_fail = true; + complete(&mdm_drv->boot_done); + break; + case ESOC_RUN_STATE: + mdm_drv->boot_fail = false; + mdm_drv->mode = RUN, + complete(&mdm_drv->boot_done); + break; + case ESOC_UNEXPECTED_RESET: + case ESOC_ERR_FATAL: + if (mdm_drv->mode == CRASH) + return; + mdm_drv->mode = CRASH; + queue_work(mdm_drv->mdm_queue, &mdm_drv->ssr_work); + break; + default: + break; + } +} + +static void mdm_ssr_fn(struct work_struct *work) +{ + int ret; + struct mdm_drv *mdm_drv = container_of(work, struct mdm_drv, ssr_work); + struct esoc_clink *esoc_clink = mdm_drv->esoc_clink; + const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops; + + /* + * If esoc bus cannot allow restart, then forcibly shut down the + * esoc + */ + ret = esoc_clink_request_ssr(mdm_drv->esoc_clink); + if (ret) { + dev_err(&esoc_clink->dev, "ssr request refused\n"); + clink_ops->cmd_exe(ESOC_PWR_OFF, esoc_clink); + } + return; +} + +static void mdm_crash_shutdown(const struct subsys_desc *mdm_subsys) +{ + struct esoc_clink *esoc_clink = + container_of(mdm_subsys, + struct esoc_clink, + subsys); + const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops; + clink_ops->notify(ESOC_PRIMARY_CRASH, esoc_clink); +} + +static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys, + bool force_stop) +{ + int ret; + struct esoc_clink *esoc_clink = + container_of(crashed_subsys, struct esoc_clink, subsys); + struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink); + const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops; + + if (mdm_drv->mode == CRASH || mdm_drv->mode == PEER_CRASH) { + ret = clink_ops->cmd_exe(ESOC_PREPARE_DEBUG, + esoc_clink); + if (ret) { + dev_err(&esoc_clink->dev, "failed to enter debug\n"); + return ret; + } + mdm_drv->mode = IN_DEBUG; + } else { + ret = clink_ops->cmd_exe(ESOC_PWR_OFF, esoc_clink); + if (ret) { + dev_err(&esoc_clink->dev, "failed to exe power off\n"); + return ret; + } + mdm_drv->mode = PWR_OFF; + } + return 0; +} + +static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys) +{ + int ret; + struct esoc_clink *esoc_clink = + container_of(crashed_subsys, struct esoc_clink, + subsys); + struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink); + const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops; + + if (mdm_drv->mode == PWR_OFF) { + ret = clink_ops->cmd_exe(ESOC_PWR_ON, esoc_clink); + if (ret) { + dev_err(&esoc_clink->dev, "pwr on fail\n"); + return ret; + } + } else if (mdm_drv->mode == IN_DEBUG) { + ret = clink_ops->cmd_exe(ESOC_EXIT_DEBUG, esoc_clink); + if (ret) { + dev_err(&esoc_clink->dev, "cannot exit debug mode\n"); + return ret; + } + ret = clink_ops->cmd_exe(ESOC_PWR_OFF, esoc_clink); + if (ret) { + dev_err(&esoc_clink->dev, "pwr off fail\n"); + return ret; + } + mdm_drv->mode = PWR_OFF; + ret = clink_ops->cmd_exe(ESOC_PWR_ON, esoc_clink); + if (ret) { + dev_err(&esoc_clink->dev, "pwr on fail\n"); + return ret; + } + } + if (!wait_for_completion_timeout(&mdm_drv->boot_done, + msecs_to_jiffies(MDM_BOOT_TIMEOUT))) { + dev_err(&esoc_clink->dev, "Unable to boot\n"); + return -ETIMEDOUT; + } + if (mdm_drv->boot_fail) { + dev_err(&esoc_clink->dev, "booting failed\n"); + return -EIO; + } + return 0; +} + +static int mdm_subsys_ramdumps(int want_dumps, + const struct subsys_desc *crashed_subsys) +{ + int ret; + struct esoc_clink *esoc_clink = + container_of(crashed_subsys, struct esoc_clink, + subsys); + const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops; + + if (want_dumps) { + ret = clink_ops->cmd_exe(ESOC_EXE_DEBUG, esoc_clink); + if (ret) { + dev_err(&esoc_clink->dev, "debugging failed\n"); + return ret; + } + } + return 0; +} + +static int mdm_register_ssr(struct esoc_clink *esoc_clink) +{ + esoc_clink->subsys.shutdown = mdm_subsys_shutdown; + esoc_clink->subsys.ramdump = mdm_subsys_ramdumps; + esoc_clink->subsys.powerup = mdm_subsys_powerup; + esoc_clink->subsys.crash_shutdown = mdm_crash_shutdown; + return esoc_clink_register_ssr(esoc_clink); +} + +int esoc_ssr_probe(struct esoc_clink *esoc_clink) +{ + int ret; + struct mdm_drv *mdm_drv; + struct esoc_eng *esoc_eng; + + mdm_drv = devm_kzalloc(&esoc_clink->dev, sizeof(*mdm_drv), GFP_KERNEL); + if (IS_ERR(mdm_drv)) + return PTR_ERR(mdm_drv); + esoc_eng = &mdm_drv->cmd_eng; + esoc_eng->handle_clink_evt = mdm_handle_clink_evt; + ret = esoc_clink_register_cmd_eng(esoc_clink, esoc_eng); + if (ret) { + dev_err(&esoc_clink->dev, "failed to register cmd engine\n"); + return ret; + } + ret = mdm_register_ssr(esoc_clink); + if (ret) + goto ssr_err; + mdm_drv->mdm_queue = alloc_workqueue("mdm_drv_queue", 0, 0); + if (!mdm_drv->mdm_queue) { + dev_err(&esoc_clink->dev, "could not create mdm_queue\n"); + goto queue_err; + } + esoc_set_drv_data(esoc_clink, mdm_drv); + init_completion(&mdm_drv->boot_done); + INIT_WORK(&mdm_drv->ssr_work, mdm_ssr_fn); + mdm_drv->esoc_clink = esoc_clink; + mdm_drv->mode = PWR_OFF; + mdm_drv->boot_fail = false; + return 0; +queue_err: + esoc_clink_unregister_ssr(esoc_clink); +ssr_err: + esoc_clink_unregister_cmd_eng(esoc_clink, esoc_eng); + return ret; +} + +static struct esoc_drv esoc_ssr_drv = { + .owner = THIS_MODULE, + .probe = esoc_ssr_probe, + .driver = { + .name = "MDM9x25", + }, +}; + +int __init esoc_ssr_init(void) +{ + return esoc_drv_register(&esoc_ssr_drv); +} +module_init(esoc_ssr_init); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 7fab6eef36eb..8b3ae1959422 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -155,22 +155,6 @@ config GPIO_MPC8XXX Say Y here if you're going to use hardware that connects to the MPC512x/831x/834x/837x/8572/8610 GPIOs. -config GPIO_MSM_V1 - tristate "Qualcomm MSM GPIO v1" - depends on GPIOLIB && ARCH_MSM && (ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50) - help - Say yes here to support the GPIO interface on ARM v6 based - Qualcomm MSM chips. Most of the pins on the MSM can be - selected for GPIO, and are controlled by this driver. - -config GPIO_MSM_V2 - tristate "Qualcomm MSM GPIO v2" - depends on GPIOLIB && ARCH_MSM - help - Say yes here to support the GPIO interface on ARM v7 based - Qualcomm MSM chips. Most of the pins on the MSM can be - selected for GPIO, and are controlled by this driver. - config GPIO_MSM_V3 tristate "Qualcomm MSM GPIO v3" depends on GPIOLIB && ARCH_MSM diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index dae4d3cd3fcb..ac1f7e725311 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -45,8 +45,6 @@ obj-$(CONFIG_GPIO_MM_LANTIQ) += gpio-mm-lantiq.o obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o -obj-$(CONFIG_GPIO_MSM_V1) += gpio-msm-v1.o -obj-$(CONFIG_GPIO_MSM_V2) += gpio-msm-common.o gpio-msm-v2.o obj-$(CONFIG_GPIO_MSM_V3) += gpio-msm-common.o gpio-msm-v3.o obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o diff --git a/drivers/gpio/gpio-msm-common.c b/drivers/gpio/gpio-msm-common.c index 3c56260c497d..3a20533f097c 100644 --- a/drivers/gpio/gpio-msm-common.c +++ b/drivers/gpio/gpio-msm-common.c @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/irqchip/chained_irq.h> +#include <linux/irqchip/msm-mpm-irq.h> #include <linux/irq.h> #include <linux/io.h> #include <linux/module.h> @@ -29,7 +30,6 @@ #include <mach/msm_iomap.h> #include <mach/gpiomux.h> -#include <mach/mpm.h> #include "gpio-msm-common.h" #ifdef CONFIG_GPIO_MSM_V3 diff --git a/drivers/gpio/gpio-msm-v1.c b/drivers/gpio/gpio-msm-v1.c deleted file mode 100644 index 064d56f60933..000000000000 --- a/drivers/gpio/gpio-msm-v1.c +++ /dev/null @@ -1,840 +0,0 @@ -/* linux/arch/arm/mach-msm/gpio.c - * - * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved. - * - * 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 <linux/bitops.h> -#include <linux/gpio.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/irq.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <asm/mach/irq.h> -#include <mach/gpiomux.h> -#include <mach/msm_iomap.h> -#include <mach/msm_smem.h> -#include <mach/proc_comm.h> - - -/* see 80-VA736-2 Rev C pp 695-751 -** -** These are actually the *shadow* gpio registers, since the -** real ones (which allow full access) are only available to the -** ARM9 side of the world. -** -** Since the _BASE need to be page-aligned when we're mapping them -** to virtual addresses, adjust for the additional offset in these -** macros. -*/ - -#if defined(CONFIG_ARCH_MSM7X30) -#define MSM_GPIO1_REG(off) (MSM_GPIO1_BASE + (off)) -#define MSM_GPIO2_REG(off) (MSM_GPIO2_BASE + 0x400 + (off)) -#else -#define MSM_GPIO1_REG(off) (MSM_GPIO1_BASE + 0x800 + (off)) -#define MSM_GPIO2_REG(off) (MSM_GPIO2_BASE + 0xC00 + (off)) -#endif - -#if defined(CONFIG_ARCH_MSM7X00A) || defined(CONFIG_ARCH_MSM7X25) ||\ - defined(CONFIG_ARCH_MSM7X27) - -/* output value */ -#define MSM_GPIO_OUT_0 MSM_GPIO1_REG(0x00) /* gpio 15-0 */ -#define MSM_GPIO_OUT_1 MSM_GPIO2_REG(0x00) /* gpio 42-16 */ -#define MSM_GPIO_OUT_2 MSM_GPIO1_REG(0x04) /* gpio 67-43 */ -#define MSM_GPIO_OUT_3 MSM_GPIO1_REG(0x08) /* gpio 94-68 */ -#define MSM_GPIO_OUT_4 MSM_GPIO1_REG(0x0C) /* gpio 106-95 */ -#define MSM_GPIO_OUT_5 MSM_GPIO1_REG(0x50) /* gpio 107-121 */ - -/* same pin map as above, output enable */ -#define MSM_GPIO_OE_0 MSM_GPIO1_REG(0x10) -#define MSM_GPIO_OE_1 MSM_GPIO2_REG(0x08) -#define MSM_GPIO_OE_2 MSM_GPIO1_REG(0x14) -#define MSM_GPIO_OE_3 MSM_GPIO1_REG(0x18) -#define MSM_GPIO_OE_4 MSM_GPIO1_REG(0x1C) -#define MSM_GPIO_OE_5 MSM_GPIO1_REG(0x54) - -/* same pin map as above, input read */ -#define MSM_GPIO_IN_0 MSM_GPIO1_REG(0x34) -#define MSM_GPIO_IN_1 MSM_GPIO2_REG(0x20) -#define MSM_GPIO_IN_2 MSM_GPIO1_REG(0x38) -#define MSM_GPIO_IN_3 MSM_GPIO1_REG(0x3C) -#define MSM_GPIO_IN_4 MSM_GPIO1_REG(0x40) -#define MSM_GPIO_IN_5 MSM_GPIO1_REG(0x44) - -/* same pin map as above, 1=edge 0=level interrup */ -#define MSM_GPIO_INT_EDGE_0 MSM_GPIO1_REG(0x60) -#define MSM_GPIO_INT_EDGE_1 MSM_GPIO2_REG(0x50) -#define MSM_GPIO_INT_EDGE_2 MSM_GPIO1_REG(0x64) -#define MSM_GPIO_INT_EDGE_3 MSM_GPIO1_REG(0x68) -#define MSM_GPIO_INT_EDGE_4 MSM_GPIO1_REG(0x6C) -#define MSM_GPIO_INT_EDGE_5 MSM_GPIO1_REG(0xC0) - -/* same pin map as above, 1=positive 0=negative */ -#define MSM_GPIO_INT_POS_0 MSM_GPIO1_REG(0x70) -#define MSM_GPIO_INT_POS_1 MSM_GPIO2_REG(0x58) -#define MSM_GPIO_INT_POS_2 MSM_GPIO1_REG(0x74) -#define MSM_GPIO_INT_POS_3 MSM_GPIO1_REG(0x78) -#define MSM_GPIO_INT_POS_4 MSM_GPIO1_REG(0x7C) -#define MSM_GPIO_INT_POS_5 MSM_GPIO1_REG(0xBC) - -/* same pin map as above, interrupt enable */ -#define MSM_GPIO_INT_EN_0 MSM_GPIO1_REG(0x80) -#define MSM_GPIO_INT_EN_1 MSM_GPIO2_REG(0x60) -#define MSM_GPIO_INT_EN_2 MSM_GPIO1_REG(0x84) -#define MSM_GPIO_INT_EN_3 MSM_GPIO1_REG(0x88) -#define MSM_GPIO_INT_EN_4 MSM_GPIO1_REG(0x8C) -#define MSM_GPIO_INT_EN_5 MSM_GPIO1_REG(0xB8) - -/* same pin map as above, write 1 to clear interrupt */ -#define MSM_GPIO_INT_CLEAR_0 MSM_GPIO1_REG(0x90) -#define MSM_GPIO_INT_CLEAR_1 MSM_GPIO2_REG(0x68) -#define MSM_GPIO_INT_CLEAR_2 MSM_GPIO1_REG(0x94) -#define MSM_GPIO_INT_CLEAR_3 MSM_GPIO1_REG(0x98) -#define MSM_GPIO_INT_CLEAR_4 MSM_GPIO1_REG(0x9C) -#define MSM_GPIO_INT_CLEAR_5 MSM_GPIO1_REG(0xB4) - -/* same pin map as above, 1=interrupt pending */ -#define MSM_GPIO_INT_STATUS_0 MSM_GPIO1_REG(0xA0) -#define MSM_GPIO_INT_STATUS_1 MSM_GPIO2_REG(0x70) -#define MSM_GPIO_INT_STATUS_2 MSM_GPIO1_REG(0xA4) -#define MSM_GPIO_INT_STATUS_3 MSM_GPIO1_REG(0xA8) -#define MSM_GPIO_INT_STATUS_4 MSM_GPIO1_REG(0xAC) -#define MSM_GPIO_INT_STATUS_5 MSM_GPIO1_REG(0xB0) - -#endif - -#if defined(CONFIG_ARCH_MSM7X30) - -/* output value */ -#define MSM_GPIO_OUT_0 MSM_GPIO1_REG(0x00) /* gpio 15-0 */ -#define MSM_GPIO_OUT_1 MSM_GPIO2_REG(0x00) /* gpio 43-16 */ -#define MSM_GPIO_OUT_2 MSM_GPIO1_REG(0x04) /* gpio 67-44 */ -#define MSM_GPIO_OUT_3 MSM_GPIO1_REG(0x08) /* gpio 94-68 */ -#define MSM_GPIO_OUT_4 MSM_GPIO1_REG(0x0C) /* gpio 106-95 */ -#define MSM_GPIO_OUT_5 MSM_GPIO1_REG(0x50) /* gpio 133-107 */ -#define MSM_GPIO_OUT_6 MSM_GPIO1_REG(0xC4) /* gpio 150-134 */ -#define MSM_GPIO_OUT_7 MSM_GPIO1_REG(0x214) /* gpio 181-151 */ - -/* same pin map as above, output enable */ -#define MSM_GPIO_OE_0 MSM_GPIO1_REG(0x10) -#define MSM_GPIO_OE_1 MSM_GPIO2_REG(0x08) -#define MSM_GPIO_OE_2 MSM_GPIO1_REG(0x14) -#define MSM_GPIO_OE_3 MSM_GPIO1_REG(0x18) -#define MSM_GPIO_OE_4 MSM_GPIO1_REG(0x1C) -#define MSM_GPIO_OE_5 MSM_GPIO1_REG(0x54) -#define MSM_GPIO_OE_6 MSM_GPIO1_REG(0xC8) -#define MSM_GPIO_OE_7 MSM_GPIO1_REG(0x218) - -/* same pin map as above, input read */ -#define MSM_GPIO_IN_0 MSM_GPIO1_REG(0x34) -#define MSM_GPIO_IN_1 MSM_GPIO2_REG(0x20) -#define MSM_GPIO_IN_2 MSM_GPIO1_REG(0x38) -#define MSM_GPIO_IN_3 MSM_GPIO1_REG(0x3C) -#define MSM_GPIO_IN_4 MSM_GPIO1_REG(0x40) -#define MSM_GPIO_IN_5 MSM_GPIO1_REG(0x44) -#define MSM_GPIO_IN_6 MSM_GPIO1_REG(0xCC) -#define MSM_GPIO_IN_7 MSM_GPIO1_REG(0x21C) - -/* same pin map as above, 1=edge 0=level interrup */ -#define MSM_GPIO_INT_EDGE_0 MSM_GPIO1_REG(0x60) -#define MSM_GPIO_INT_EDGE_1 MSM_GPIO2_REG(0x50) -#define MSM_GPIO_INT_EDGE_2 MSM_GPIO1_REG(0x64) -#define MSM_GPIO_INT_EDGE_3 MSM_GPIO1_REG(0x68) -#define MSM_GPIO_INT_EDGE_4 MSM_GPIO1_REG(0x6C) -#define MSM_GPIO_INT_EDGE_5 MSM_GPIO1_REG(0xC0) -#define MSM_GPIO_INT_EDGE_6 MSM_GPIO1_REG(0xD0) -#define MSM_GPIO_INT_EDGE_7 MSM_GPIO1_REG(0x240) - -/* same pin map as above, 1=positive 0=negative */ -#define MSM_GPIO_INT_POS_0 MSM_GPIO1_REG(0x70) -#define MSM_GPIO_INT_POS_1 MSM_GPIO2_REG(0x58) -#define MSM_GPIO_INT_POS_2 MSM_GPIO1_REG(0x74) -#define MSM_GPIO_INT_POS_3 MSM_GPIO1_REG(0x78) -#define MSM_GPIO_INT_POS_4 MSM_GPIO1_REG(0x7C) -#define MSM_GPIO_INT_POS_5 MSM_GPIO1_REG(0xBC) -#define MSM_GPIO_INT_POS_6 MSM_GPIO1_REG(0xD4) -#define MSM_GPIO_INT_POS_7 MSM_GPIO1_REG(0x228) - -/* same pin map as above, interrupt enable */ -#define MSM_GPIO_INT_EN_0 MSM_GPIO1_REG(0x80) -#define MSM_GPIO_INT_EN_1 MSM_GPIO2_REG(0x60) -#define MSM_GPIO_INT_EN_2 MSM_GPIO1_REG(0x84) -#define MSM_GPIO_INT_EN_3 MSM_GPIO1_REG(0x88) -#define MSM_GPIO_INT_EN_4 MSM_GPIO1_REG(0x8C) -#define MSM_GPIO_INT_EN_5 MSM_GPIO1_REG(0xB8) -#define MSM_GPIO_INT_EN_6 MSM_GPIO1_REG(0xD8) -#define MSM_GPIO_INT_EN_7 MSM_GPIO1_REG(0x22C) - -/* same pin map as above, write 1 to clear interrupt */ -#define MSM_GPIO_INT_CLEAR_0 MSM_GPIO1_REG(0x90) -#define MSM_GPIO_INT_CLEAR_1 MSM_GPIO2_REG(0x68) -#define MSM_GPIO_INT_CLEAR_2 MSM_GPIO1_REG(0x94) -#define MSM_GPIO_INT_CLEAR_3 MSM_GPIO1_REG(0x98) -#define MSM_GPIO_INT_CLEAR_4 MSM_GPIO1_REG(0x9C) -#define MSM_GPIO_INT_CLEAR_5 MSM_GPIO1_REG(0xB4) -#define MSM_GPIO_INT_CLEAR_6 MSM_GPIO1_REG(0xDC) -#define MSM_GPIO_INT_CLEAR_7 MSM_GPIO1_REG(0x230) - -/* same pin map as above, 1=interrupt pending */ -#define MSM_GPIO_INT_STATUS_0 MSM_GPIO1_REG(0xA0) -#define MSM_GPIO_INT_STATUS_1 MSM_GPIO2_REG(0x70) -#define MSM_GPIO_INT_STATUS_2 MSM_GPIO1_REG(0xA4) -#define MSM_GPIO_INT_STATUS_3 MSM_GPIO1_REG(0xA8) -#define MSM_GPIO_INT_STATUS_4 MSM_GPIO1_REG(0xAC) -#define MSM_GPIO_INT_STATUS_5 MSM_GPIO1_REG(0xB0) -#define MSM_GPIO_INT_STATUS_6 MSM_GPIO1_REG(0xE0) -#define MSM_GPIO_INT_STATUS_7 MSM_GPIO1_REG(0x234) - -#endif - -enum { - GPIO_DEBUG_SLEEP = 1U << 0, -}; -static int msm_gpio_debug_mask; -module_param_named(debug_mask, msm_gpio_debug_mask, int, - S_IRUGO | S_IWUSR | S_IWGRP); - -#define FIRST_GPIO_IRQ MSM_GPIO_TO_INT(0) - -#define MSM_GPIO_BANK(bank, first, last) \ - { \ - .regs = { \ - .out = MSM_GPIO_OUT_##bank, \ - .in = MSM_GPIO_IN_##bank, \ - .int_status = MSM_GPIO_INT_STATUS_##bank, \ - .int_clear = MSM_GPIO_INT_CLEAR_##bank, \ - .int_en = MSM_GPIO_INT_EN_##bank, \ - .int_edge = MSM_GPIO_INT_EDGE_##bank, \ - .int_pos = MSM_GPIO_INT_POS_##bank, \ - .oe = MSM_GPIO_OE_##bank, \ - }, \ - .chip = { \ - .base = (first), \ - .ngpio = (last) - (first) + 1, \ - .get = msm_gpio_get, \ - .set = msm_gpio_set, \ - .direction_input = msm_gpio_direction_input, \ - .direction_output = msm_gpio_direction_output, \ - .to_irq = msm_gpio_to_irq, \ - .request = msm_gpio_request, \ - .free = msm_gpio_free, \ - } \ - } - -#define MSM_GPIO_BROKEN_INT_CLEAR 1 - -struct msm_gpio_regs { - void __iomem *out; - void __iomem *in; - void __iomem *int_status; - void __iomem *int_clear; - void __iomem *int_en; - void __iomem *int_edge; - void __iomem *int_pos; - void __iomem *oe; -}; - -struct msm_gpio_chip { - spinlock_t lock; - struct gpio_chip chip; - struct msm_gpio_regs regs; -#if MSM_GPIO_BROKEN_INT_CLEAR - unsigned int_status_copy; -#endif - unsigned int both_edge_detect; - unsigned int int_enable[2]; /* 0: awake, 1: sleep */ -}; - -static int msm_gpio_write(struct msm_gpio_chip *msm_chip, - unsigned offset, unsigned on) -{ - unsigned mask = BIT(offset); - unsigned val; - - val = __raw_readl(msm_chip->regs.out); - if (on) - __raw_writel(val | mask, msm_chip->regs.out); - else - __raw_writel(val & ~mask, msm_chip->regs.out); - return 0; -} - -static void msm_gpio_update_both_edge_detect(struct msm_gpio_chip *msm_chip) -{ - int loop_limit = 100; - unsigned pol, val, val2, intstat; - do { - val = __raw_readl(msm_chip->regs.in); - pol = __raw_readl(msm_chip->regs.int_pos); - pol = (pol & ~msm_chip->both_edge_detect) | - (~val & msm_chip->both_edge_detect); - __raw_writel(pol, msm_chip->regs.int_pos); - intstat = __raw_readl(msm_chip->regs.int_status); - val2 = __raw_readl(msm_chip->regs.in); - if (((val ^ val2) & msm_chip->both_edge_detect & ~intstat) == 0) - return; - } while (loop_limit-- > 0); - printk(KERN_ERR "msm_gpio_update_both_edge_detect, " - "failed to reach stable state %x != %x\n", val, val2); -} - -static int msm_gpio_clear_detect_status(struct msm_gpio_chip *msm_chip, - unsigned offset) -{ - unsigned bit = BIT(offset); - -#if MSM_GPIO_BROKEN_INT_CLEAR - /* Save interrupts that already triggered before we loose them. */ - /* Any interrupt that triggers between the read of int_status */ - /* and the write to int_clear will still be lost though. */ - msm_chip->int_status_copy |= __raw_readl(msm_chip->regs.int_status); - msm_chip->int_status_copy &= ~bit; -#endif - __raw_writel(bit, msm_chip->regs.int_clear); - msm_gpio_update_both_edge_detect(msm_chip); - return 0; -} - -static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - struct msm_gpio_chip *msm_chip; - unsigned long irq_flags; - - msm_chip = container_of(chip, struct msm_gpio_chip, chip); - spin_lock_irqsave(&msm_chip->lock, irq_flags); - __raw_writel(__raw_readl(msm_chip->regs.oe) & ~BIT(offset), - msm_chip->regs.oe); - mb(); - spin_unlock_irqrestore(&msm_chip->lock, irq_flags); - return 0; -} - -static int -msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) -{ - struct msm_gpio_chip *msm_chip; - unsigned long irq_flags; - - msm_chip = container_of(chip, struct msm_gpio_chip, chip); - spin_lock_irqsave(&msm_chip->lock, irq_flags); - msm_gpio_write(msm_chip, offset, value); - __raw_writel(__raw_readl(msm_chip->regs.oe) | BIT(offset), - msm_chip->regs.oe); - mb(); - spin_unlock_irqrestore(&msm_chip->lock, irq_flags); - return 0; -} - -static int msm_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct msm_gpio_chip *msm_chip; - int rc; - - msm_chip = container_of(chip, struct msm_gpio_chip, chip); - rc = (__raw_readl(msm_chip->regs.in) & (1U << offset)) ? 1 : 0; - mb(); - return rc; -} - -static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct msm_gpio_chip *msm_chip; - unsigned long irq_flags; - - msm_chip = container_of(chip, struct msm_gpio_chip, chip); - spin_lock_irqsave(&msm_chip->lock, irq_flags); - msm_gpio_write(msm_chip, offset, value); - mb(); - spin_unlock_irqrestore(&msm_chip->lock, irq_flags); -} - -static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - return MSM_GPIO_TO_INT(chip->base + offset); -} - -#ifdef CONFIG_MSM_GPIOMUX -static int msm_gpio_request(struct gpio_chip *chip, unsigned offset) -{ - return msm_gpiomux_get(chip->base + offset); -} - -static void msm_gpio_free(struct gpio_chip *chip, unsigned offset) -{ - msm_gpiomux_put(chip->base + offset); -} -#else -#define msm_gpio_request NULL -#define msm_gpio_free NULL -#endif - -struct msm_gpio_chip msm_gpio_chips[] = { -#if defined(CONFIG_ARCH_MSM7X00A) - MSM_GPIO_BANK(0, 0, 15), - MSM_GPIO_BANK(1, 16, 42), - MSM_GPIO_BANK(2, 43, 67), - MSM_GPIO_BANK(3, 68, 94), - MSM_GPIO_BANK(4, 95, 106), - MSM_GPIO_BANK(5, 107, 121), -#elif defined(CONFIG_ARCH_MSM7X25) || defined(CONFIG_ARCH_MSM7X27) - MSM_GPIO_BANK(0, 0, 15), - MSM_GPIO_BANK(1, 16, 42), - MSM_GPIO_BANK(2, 43, 67), - MSM_GPIO_BANK(3, 68, 94), - MSM_GPIO_BANK(4, 95, 106), - MSM_GPIO_BANK(5, 107, 132), -#elif defined(CONFIG_ARCH_MSM7X30) - MSM_GPIO_BANK(0, 0, 15), - MSM_GPIO_BANK(1, 16, 43), - MSM_GPIO_BANK(2, 44, 67), - MSM_GPIO_BANK(3, 68, 94), - MSM_GPIO_BANK(4, 95, 106), - MSM_GPIO_BANK(5, 107, 133), - MSM_GPIO_BANK(6, 134, 150), - MSM_GPIO_BANK(7, 151, 181), -#elif defined(CONFIG_ARCH_QSD8X50) - MSM_GPIO_BANK(0, 0, 15), - MSM_GPIO_BANK(1, 16, 42), - MSM_GPIO_BANK(2, 43, 67), - MSM_GPIO_BANK(3, 68, 94), - MSM_GPIO_BANK(4, 95, 103), - MSM_GPIO_BANK(5, 104, 121), - MSM_GPIO_BANK(6, 122, 152), - MSM_GPIO_BANK(7, 153, 164), -#endif -}; - -static void msm_gpio_irq_ack(struct irq_data *d) -{ - unsigned long irq_flags; - struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq); - spin_lock_irqsave(&msm_chip->lock, irq_flags); - msm_gpio_clear_detect_status(msm_chip, - d->irq - gpio_to_irq(msm_chip->chip.base)); - spin_unlock_irqrestore(&msm_chip->lock, irq_flags); -} - -static void msm_gpio_irq_mask(struct irq_data *d) -{ - unsigned long irq_flags; - struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq); - unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base); - - spin_lock_irqsave(&msm_chip->lock, irq_flags); - /* level triggered interrupts are also latched */ - if (!(__raw_readl(msm_chip->regs.int_edge) & BIT(offset))) - msm_gpio_clear_detect_status(msm_chip, offset); - msm_chip->int_enable[0] &= ~BIT(offset); - __raw_writel(msm_chip->int_enable[0], msm_chip->regs.int_en); - mb(); - spin_unlock_irqrestore(&msm_chip->lock, irq_flags); -} - -static void msm_gpio_irq_unmask(struct irq_data *d) -{ - unsigned long irq_flags; - struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq); - unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base); - - spin_lock_irqsave(&msm_chip->lock, irq_flags); - /* level triggered interrupts are also latched */ - if (!(__raw_readl(msm_chip->regs.int_edge) & BIT(offset))) - msm_gpio_clear_detect_status(msm_chip, offset); - msm_chip->int_enable[0] |= BIT(offset); - __raw_writel(msm_chip->int_enable[0], msm_chip->regs.int_en); - mb(); - spin_unlock_irqrestore(&msm_chip->lock, irq_flags); -} - -static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on) -{ - unsigned long irq_flags; - struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq); - unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base); - - spin_lock_irqsave(&msm_chip->lock, irq_flags); - - if (on) - msm_chip->int_enable[1] |= BIT(offset); - else - msm_chip->int_enable[1] &= ~BIT(offset); - - spin_unlock_irqrestore(&msm_chip->lock, irq_flags); - return 0; -} - -static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type) -{ - unsigned long irq_flags; - struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq); - unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base); - unsigned val, mask = BIT(offset); - - spin_lock_irqsave(&msm_chip->lock, irq_flags); - val = __raw_readl(msm_chip->regs.int_edge); - if (flow_type & IRQ_TYPE_EDGE_BOTH) { - __raw_writel(val | mask, msm_chip->regs.int_edge); - __irq_set_handler_locked(d->irq, handle_edge_irq); - } else { - __raw_writel(val & ~mask, msm_chip->regs.int_edge); - __irq_set_handler_locked(d->irq, handle_level_irq); - } - if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { - msm_chip->both_edge_detect |= mask; - msm_gpio_update_both_edge_detect(msm_chip); - } else { - msm_chip->both_edge_detect &= ~mask; - val = __raw_readl(msm_chip->regs.int_pos); - if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH)) - __raw_writel(val | mask, msm_chip->regs.int_pos); - else - __raw_writel(val & ~mask, msm_chip->regs.int_pos); - } - mb(); - spin_unlock_irqrestore(&msm_chip->lock, irq_flags); - return 0; -} - -static void msm_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) -{ - int i, j, mask; - unsigned val; - struct irq_chip *chip = irq_desc_get_chip(desc); - - chained_irq_enter(chip, desc); - - for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) { - struct msm_gpio_chip *msm_chip = &msm_gpio_chips[i]; - val = __raw_readl(msm_chip->regs.int_status); - val &= msm_chip->int_enable[0]; - while (val) { - mask = val & -val; - j = fls(mask) - 1; - /* printk("%s %08x %08x bit %d gpio %d irq %d\n", - __func__, v, m, j, msm_chip->chip.start + j, - FIRST_GPIO_IRQ + msm_chip->chip.start + j); */ - val &= ~mask; - generic_handle_irq(FIRST_GPIO_IRQ + - msm_chip->chip.base + j); - } - } - - chained_irq_exit(chip, desc); -} - -static struct irq_chip msm_gpio_irq_chip = { - .name = "msmgpio", - .irq_ack = msm_gpio_irq_ack, - .irq_mask = msm_gpio_irq_mask, - .irq_unmask = msm_gpio_irq_unmask, - .irq_set_wake = msm_gpio_irq_set_wake, - .irq_set_type = msm_gpio_irq_set_type, -}; - -#define NUM_GPIO_SMEM_BANKS 6 -#define GPIO_SMEM_NUM_GROUPS 2 -#define GPIO_SMEM_MAX_PC_INTERRUPTS 8 -struct tramp_gpio_smem { - uint16_t num_fired[GPIO_SMEM_NUM_GROUPS]; - uint16_t fired[GPIO_SMEM_NUM_GROUPS][GPIO_SMEM_MAX_PC_INTERRUPTS]; - uint32_t enabled[NUM_GPIO_SMEM_BANKS]; - uint32_t detection[NUM_GPIO_SMEM_BANKS]; - uint32_t polarity[NUM_GPIO_SMEM_BANKS]; -}; - -static void msm_gpio_sleep_int(unsigned long arg) -{ - int i, j; - struct tramp_gpio_smem *smem_gpio; - - BUILD_BUG_ON(NR_GPIO_IRQS > NUM_GPIO_SMEM_BANKS * 32); - - smem_gpio = smem_find(SMEM_GPIO_INT, sizeof(*smem_gpio), 0, - SMEM_ANY_HOST_FLAG); - if (smem_gpio == NULL) - return; - - local_irq_disable(); - for (i = 0; i < GPIO_SMEM_NUM_GROUPS; i++) { - int count = smem_gpio->num_fired[i]; - for (j = 0; j < count; j++) { - /* TODO: Check mask */ - generic_handle_irq( - MSM_GPIO_TO_INT(smem_gpio->fired[i][j])); - } - } - local_irq_enable(); -} - -static DECLARE_TASKLET(msm_gpio_sleep_int_tasklet, msm_gpio_sleep_int, 0); - -void msm_gpio_enter_sleep(int from_idle) -{ - int i; - struct tramp_gpio_smem *smem_gpio; - - smem_gpio = smem_find(SMEM_GPIO_INT, sizeof(*smem_gpio), 0, - SMEM_ANY_HOST_FLAG); - - if (smem_gpio) { - for (i = 0; i < ARRAY_SIZE(smem_gpio->enabled); i++) { - smem_gpio->enabled[i] = 0; - smem_gpio->detection[i] = 0; - smem_gpio->polarity[i] = 0; - } - } - - for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) { - __raw_writel(msm_gpio_chips[i].int_enable[!from_idle], - msm_gpio_chips[i].regs.int_en); - if (smem_gpio) { - uint32_t tmp; - int start, index, shiftl, shiftr; - start = msm_gpio_chips[i].chip.base; - index = start / 32; - shiftl = start % 32; - shiftr = 32 - shiftl; - tmp = msm_gpio_chips[i].int_enable[!from_idle]; - smem_gpio->enabled[index] |= tmp << shiftl; - smem_gpio->enabled[index+1] |= tmp >> shiftr; - smem_gpio->detection[index] |= - __raw_readl(msm_gpio_chips[i].regs.int_edge) << - shiftl; - smem_gpio->detection[index+1] |= - __raw_readl(msm_gpio_chips[i].regs.int_edge) >> - shiftr; - smem_gpio->polarity[index] |= - __raw_readl(msm_gpio_chips[i].regs.int_pos) << - shiftl; - smem_gpio->polarity[index+1] |= - __raw_readl(msm_gpio_chips[i].regs.int_pos) >> - shiftr; - } - } - mb(); - - if (smem_gpio) { - if (msm_gpio_debug_mask & GPIO_DEBUG_SLEEP) - for (i = 0; i < ARRAY_SIZE(smem_gpio->enabled); i++) { - printk("msm_gpio_enter_sleep gpio %d-%d: enable" - " %08x, edge %08x, polarity %08x\n", - i * 32, i * 32 + 31, - smem_gpio->enabled[i], - smem_gpio->detection[i], - smem_gpio->polarity[i]); - } - for (i = 0; i < GPIO_SMEM_NUM_GROUPS; i++) - smem_gpio->num_fired[i] = 0; - } -} - -void msm_gpio_exit_sleep(void) -{ - int i; - struct tramp_gpio_smem *smem_gpio; - - smem_gpio = smem_find(SMEM_GPIO_INT, sizeof(*smem_gpio), 0, - SMEM_ANY_HOST_FLAG); - - for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) { - __raw_writel(msm_gpio_chips[i].int_enable[0], - msm_gpio_chips[i].regs.int_en); - } - mb(); - - if (smem_gpio && (smem_gpio->num_fired[0] || smem_gpio->num_fired[1])) { - if (msm_gpio_debug_mask & GPIO_DEBUG_SLEEP) - printk(KERN_INFO "gpio: fired %x %x\n", - smem_gpio->num_fired[0], smem_gpio->num_fired[1]); - tasklet_schedule(&msm_gpio_sleep_int_tasklet); - } -} - - -int gpio_tlmm_config(unsigned config, unsigned disable) -{ - return msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, &disable); -} -EXPORT_SYMBOL(gpio_tlmm_config); - -int msm_gpios_request_enable(const struct msm_gpio *table, int size) -{ - int rc = msm_gpios_request(table, size); - if (rc) - return rc; - rc = msm_gpios_enable(table, size); - if (rc) - msm_gpios_free(table, size); - return rc; -} -EXPORT_SYMBOL(msm_gpios_request_enable); - -void msm_gpios_disable_free(const struct msm_gpio *table, int size) -{ - msm_gpios_disable(table, size); - msm_gpios_free(table, size); -} -EXPORT_SYMBOL(msm_gpios_disable_free); - -int msm_gpios_request(const struct msm_gpio *table, int size) -{ - int rc; - int i; - const struct msm_gpio *g; - for (i = 0; i < size; i++) { - g = table + i; - rc = gpio_request(GPIO_PIN(g->gpio_cfg), g->label); - if (rc) { - pr_err("gpio_request(%d) <%s> failed: %d\n", - GPIO_PIN(g->gpio_cfg), g->label ?: "?", rc); - goto err; - } - } - return 0; -err: - msm_gpios_free(table, i); - return rc; -} -EXPORT_SYMBOL(msm_gpios_request); - -void msm_gpios_free(const struct msm_gpio *table, int size) -{ - int i; - const struct msm_gpio *g; - for (i = size-1; i >= 0; i--) { - g = table + i; - gpio_free(GPIO_PIN(g->gpio_cfg)); - } -} -EXPORT_SYMBOL(msm_gpios_free); - -int msm_gpios_enable(const struct msm_gpio *table, int size) -{ - int rc; - int i; - const struct msm_gpio *g; - for (i = 0; i < size; i++) { - g = table + i; - rc = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_ENABLE); - if (rc) { - pr_err("gpio_tlmm_config(0x%08x, GPIO_CFG_ENABLE)" - " <%s> failed: %d\n", - g->gpio_cfg, g->label ?: "?", rc); - pr_err("pin %d func %d dir %d pull %d drvstr %d\n", - GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg), - GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg), - GPIO_DRVSTR(g->gpio_cfg)); - goto err; - } - } - return 0; -err: - msm_gpios_disable(table, i); - return rc; -} -EXPORT_SYMBOL(msm_gpios_enable); - -int msm_gpios_disable(const struct msm_gpio *table, int size) -{ - int rc = 0; - int i; - const struct msm_gpio *g; - for (i = size-1; i >= 0; i--) { - int tmp; - g = table + i; - tmp = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_DISABLE); - if (tmp) { - pr_err("gpio_tlmm_config(0x%08x, GPIO_CFG_DISABLE)" - " <%s> failed: %d\n", - g->gpio_cfg, g->label ?: "?", rc); - pr_err("pin %d func %d dir %d pull %d drvstr %d\n", - GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg), - GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg), - GPIO_DRVSTR(g->gpio_cfg)); - if (!rc) - rc = tmp; - } - } - - return rc; -} -EXPORT_SYMBOL(msm_gpios_disable); - -/* Locate the GPIO_OUT register for the given GPIO and return its address - * and the bit position of the gpio's bit within the register. - * - * This function is used by gpiomux-v1 in order to support output transitions. - */ -void msm_gpio_find_out(const unsigned gpio, void __iomem **out, - unsigned *offset) -{ - struct msm_gpio_chip *msm_chip = msm_gpio_chips; - - while (gpio >= msm_chip->chip.base + msm_chip->chip.ngpio) - ++msm_chip; - - *out = msm_chip->regs.out; - *offset = gpio - msm_chip->chip.base; -} - -static int msm_gpio_probe(struct platform_device *dev) -{ - int i, j = 0; - int grp_irq; - - for (i = FIRST_GPIO_IRQ; i < FIRST_GPIO_IRQ + NR_GPIO_IRQS; i++) { - if (i - FIRST_GPIO_IRQ >= - msm_gpio_chips[j].chip.base + - msm_gpio_chips[j].chip.ngpio) - j++; - irq_set_chip_data(i, &msm_gpio_chips[j]); - irq_set_chip_and_handler(i, &msm_gpio_irq_chip, - handle_edge_irq); - set_irq_flags(i, IRQF_VALID); - } - - for (i = 0; i < dev->num_resources; i++) { - grp_irq = platform_get_irq(dev, i); - if (grp_irq < 0) - return -ENXIO; - - irq_set_chained_handler(grp_irq, msm_gpio_irq_handler); - irq_set_irq_wake(grp_irq, (i + 1)); - } - - for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) { - spin_lock_init(&msm_gpio_chips[i].lock); - __raw_writel(0, msm_gpio_chips[i].regs.int_en); - gpiochip_add(&msm_gpio_chips[i].chip); - } - - mb(); - return 0; -} - -static struct platform_driver msm_gpio_driver = { - .probe = msm_gpio_probe, - .driver = { - .name = "msmgpio", - .owner = THIS_MODULE, - }, -}; - -static int __init msm_gpio_init(void) -{ - return platform_driver_register(&msm_gpio_driver); -} -postcore_initcall(msm_gpio_init); diff --git a/drivers/gpio/gpio-msm-v2.c b/drivers/gpio/gpio-msm-v2.c deleted file mode 100644 index 8c42bd61872b..000000000000 --- a/drivers/gpio/gpio-msm-v2.c +++ /dev/null @@ -1,225 +0,0 @@ -/* Copyright (c) 2010-2012, The Linux Foundation. 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 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. - * - */ -#include <linux/bitmap.h> -#include <linux/bitops.h> -#include <linux/delay.h> -#include <linux/gpio.h> -#include <linux/init.h> -#include <linux/io.h> -#include <linux/irq.h> - -#include <mach/msm_iomap.h> -#include <mach/gpiomux.h> -#include "gpio-msm-common.h" - -/* Bits of interest in the GPIO_IN_OUT register. - */ -enum { - GPIO_IN_BIT = 0, - GPIO_OUT_BIT = 1 -}; - -/* Bits of interest in the GPIO_INTR_STATUS register. - */ -enum { - INTR_STATUS_BIT = 0, -}; - -/* Bits of interest in the GPIO_CFG register. - */ -enum { - GPIO_OE_BIT = 9, -}; - -/* Bits of interest in the GPIO_INTR_CFG register. - */ -enum { - INTR_ENABLE_BIT = 0, - INTR_POL_CTL_BIT = 1, - INTR_DECT_CTL_BIT = 2, - INTR_RAW_STATUS_EN_BIT = 3, -}; - -/* Codes of interest in GPIO_INTR_CFG_SU. - */ -enum { - TARGET_PROC_SCORPION = 4, - TARGET_PROC_NONE = 7, -}; - -/* - * There is no 'DC_POLARITY_LO' because the GIC is incapable - * of asserting on falling edge or level-low conditions. Even though - * the registers allow for low-polarity inputs, the case can never arise. - */ -enum { - DC_POLARITY_HI = BIT(11), - DC_IRQ_ENABLE = BIT(3), -}; - -/* - * When a GPIO triggers, two separate decisions are made, controlled - * by two separate flags. - * - * - First, INTR_RAW_STATUS_EN controls whether or not the GPIO_INTR_STATUS - * register for that GPIO will be updated to reflect the triggering of that - * gpio. If this bit is 0, this register will not be updated. - * - Second, INTR_ENABLE controls whether an interrupt is triggered. - * - * If INTR_ENABLE is set and INTR_RAW_STATUS_EN is NOT set, an interrupt - * can be triggered but the status register will not reflect it. - */ -#define INTR_RAW_STATUS_EN BIT(INTR_RAW_STATUS_EN_BIT) -#define INTR_ENABLE BIT(INTR_ENABLE_BIT) -#define INTR_DECT_CTL_EDGE BIT(INTR_DECT_CTL_BIT) -#define INTR_POL_CTL_HI BIT(INTR_POL_CTL_BIT) - -#define GPIO_INTR_CFG_SU(gpio) (MSM_TLMM_BASE + 0x0400 + (0x04 * (gpio))) -#define DIR_CONN_INTR_CFG_SU(irq) (MSM_TLMM_BASE + 0x0700 + (0x04 * (irq))) -#define GPIO_CONFIG(gpio) (MSM_TLMM_BASE + 0x1000 + (0x10 * (gpio))) -#define GPIO_IN_OUT(gpio) (MSM_TLMM_BASE + 0x1004 + (0x10 * (gpio))) -#define GPIO_INTR_CFG(gpio) (MSM_TLMM_BASE + 0x1008 + (0x10 * (gpio))) -#define GPIO_INTR_STATUS(gpio) (MSM_TLMM_BASE + 0x100c + (0x10 * (gpio))) - -static inline void set_gpio_bits(unsigned n, void __iomem *reg) -{ - __raw_writel(__raw_readl(reg) | n, reg); -} - -static inline void clr_gpio_bits(unsigned n, void __iomem *reg) -{ - __raw_writel(__raw_readl(reg) & ~n, reg); -} - -unsigned __msm_gpio_get_inout(unsigned gpio) -{ - return __raw_readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN_BIT); -} - -void __msm_gpio_set_inout(unsigned gpio, unsigned val) -{ - __raw_writel(val ? BIT(GPIO_OUT_BIT) : 0, GPIO_IN_OUT(gpio)); -} - -void __msm_gpio_set_config_direction(unsigned gpio, int input, int val) -{ - if (input) - clr_gpio_bits(BIT(GPIO_OE_BIT), GPIO_CONFIG(gpio)); - else { - __msm_gpio_set_inout(gpio, val); - set_gpio_bits(BIT(GPIO_OE_BIT), GPIO_CONFIG(gpio)); - } -} - -void __msm_gpio_set_polarity(unsigned gpio, unsigned val) -{ - if (val) - clr_gpio_bits(INTR_POL_CTL_HI, GPIO_INTR_CFG(gpio)); - else - set_gpio_bits(INTR_POL_CTL_HI, GPIO_INTR_CFG(gpio)); -} - -unsigned __msm_gpio_get_intr_status(unsigned gpio) -{ - return __raw_readl(GPIO_INTR_STATUS(gpio)) & - BIT(INTR_STATUS_BIT); -} - -void __msm_gpio_set_intr_status(unsigned gpio) -{ - __raw_writel(BIT(INTR_STATUS_BIT), GPIO_INTR_STATUS(gpio)); -} - -unsigned __msm_gpio_get_intr_config(unsigned gpio) -{ - return __raw_readl(GPIO_INTR_CFG(gpio)); -} - -void __msm_gpio_set_intr_cfg_enable(unsigned gpio, unsigned val) -{ - if (val) { - set_gpio_bits(INTR_ENABLE, GPIO_INTR_CFG(gpio)); - - } else { - clr_gpio_bits(INTR_ENABLE, GPIO_INTR_CFG(gpio)); - } -} - -unsigned __msm_gpio_get_intr_cfg_enable(unsigned gpio) -{ - return __msm_gpio_get_intr_config(gpio) & INTR_ENABLE; -} - -void __msm_gpio_set_intr_cfg_type(unsigned gpio, unsigned type) -{ - unsigned cfg; - - /* RAW_STATUS_EN is left on for all gpio irqs. Due to the - * internal circuitry of TLMM, toggling the RAW_STATUS - * could cause the INTR_STATUS to be set for EDGE interrupts. - */ - cfg = __msm_gpio_get_intr_config(gpio); - cfg |= INTR_RAW_STATUS_EN; - __raw_writel(cfg, GPIO_INTR_CFG(gpio)); - __raw_writel(TARGET_PROC_SCORPION, GPIO_INTR_CFG_SU(gpio)); - - cfg = __msm_gpio_get_intr_config(gpio); - if (type & IRQ_TYPE_EDGE_BOTH) - cfg |= INTR_DECT_CTL_EDGE; - else - cfg &= ~INTR_DECT_CTL_EDGE; - - if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH)) - cfg |= INTR_POL_CTL_HI; - else - cfg &= ~INTR_POL_CTL_HI; - - __raw_writel(cfg, GPIO_INTR_CFG(gpio)); - /* Sometimes it might take a little while to update - * the interrupt status after the RAW_STATUS is enabled - * We clear the interrupt status before enabling the - * interrupt in the unmask call-back. - */ - udelay(5); -} - -void __gpio_tlmm_config(unsigned config) -{ - uint32_t flags; - unsigned gpio = GPIO_PIN(config); - - flags = ((GPIO_DIR(config) << 9) & (0x1 << 9)) | - ((GPIO_DRVSTR(config) << 6) & (0x7 << 6)) | - ((GPIO_FUNC(config) << 2) & (0xf << 2)) | - ((GPIO_PULL(config) & 0x3)); - __raw_writel(flags, GPIO_CONFIG(gpio)); -} - -void __msm_gpio_install_direct_irq(unsigned gpio, unsigned irq, - unsigned int input_polarity) -{ - uint32_t bits; - - __raw_writel(__raw_readl(GPIO_CONFIG(gpio)) | BIT(GPIO_OE_BIT), - GPIO_CONFIG(gpio)); - __raw_writel(__raw_readl(GPIO_INTR_CFG(gpio)) & - ~(INTR_RAW_STATUS_EN | INTR_ENABLE), - GPIO_INTR_CFG(gpio)); - __raw_writel(DC_IRQ_ENABLE | TARGET_PROC_NONE, - GPIO_INTR_CFG_SU(gpio)); - - bits = TARGET_PROC_SCORPION | (gpio << 3); - if (input_polarity) - bits |= DC_POLARITY_HI; - __raw_writel(bits, DIR_CONN_INTR_CFG_SU(irq)); -} diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c index 27af202c5d86..9379fb0dfff6 100644 --- a/drivers/gpio/gpio-sx150x.c +++ b/drivers/gpio/gpio-sx150x.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010, 2013, The Linux Foundation. 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 and @@ -18,7 +18,10 @@ #include <linux/mutex.h> #include <linux/slab.h> #include <linux/workqueue.h> +#include <linux/of_gpio.h> #include <linux/i2c/sx150x.h> +#include <linux/of.h> +#include <linux/delay.h> #define NO_UPDATE_PENDING -1 @@ -53,8 +56,11 @@ struct sx150x_chip { struct mutex lock; }; +#define SX1508Q_ID 0 +#define SX1509Q_ID 1 + static const struct sx150x_device_data sx150x_devices[] = { - [0] = { /* sx1508q */ + [SX1508Q_ID] = { /* sx1508q */ .reg_pullup = 0x03, .reg_pulldn = 0x04, .reg_drain = 0x05, @@ -69,7 +75,7 @@ static const struct sx150x_device_data sx150x_devices[] = { .reg_reset = 0x7d, .ngpios = 8 }, - [1] = { /* sx1509q */ + [SX1509Q_ID] = { /* sx1509q */ .reg_pullup = 0x07, .reg_pulldn = 0x09, .reg_drain = 0x0b, @@ -86,9 +92,24 @@ static const struct sx150x_device_data sx150x_devices[] = { }, }; +#ifdef CONFIG_OF +static struct of_device_id sx150x_match_table[] = { + { + .compatible = "sx1508q", .data = NULL, + }, + { + .compatible = "sx1509q", .data = NULL, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, sx150x_match_table); +#else +#define sx150x_match_table NULL +#endif + static const struct i2c_device_id sx150x_id[] = { - {"sx1508q", 0}, - {"sx1509q", 1}, + {"sx1508q", SX1508Q_ID}, + {"sx1509q", SX1509Q_ID}, {} }; MODULE_DEVICE_TABLE(i2c, sx150x_id); @@ -424,6 +445,13 @@ static void sx150x_init_chip(struct sx150x_chip *chip, { mutex_init(&chip->lock); + if (client->dev.of_node) { + if (of_device_is_compatible(client->dev.of_node, "sx1508q")) + driver_data = SX1508Q_ID; + else + driver_data = SX1509Q_ID; + } + chip->client = client; chip->dev_cfg = &sx150x_devices[driver_data]; chip->gpio_chip.label = client->name; @@ -438,6 +466,10 @@ static void sx150x_init_chip(struct sx150x_chip *chip, if (pdata->oscio_is_gpo) ++chip->gpio_chip.ngpio; + if (client && client->dev.of_node) + chip->gpio_chip.of_node = + of_node_get(client->dev.of_node); + chip->irq_chip.name = client->name; chip->irq_chip.irq_mask = sx150x_irq_mask; chip->irq_chip.irq_unmask = sx150x_irq_unmask; @@ -466,6 +498,29 @@ static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg) static int sx150x_reset(struct sx150x_chip *chip) { int err; + struct device_node *np = chip->gpio_chip.of_node; + enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW; + int rst_gpio; + + if (np) { + rst_gpio = of_get_named_gpio_flags(np, "sx150x,reset_gpio", + 0, &flags); + if (rst_gpio < 0) + pr_debug("%s: No Reset GPIO found %d\n", + __func__, rst_gpio); + else { + err = gpio_request(rst_gpio, "sx150x_rst"); + if (err) { + pr_err("Failed to get reset GPIO %d\n", err); + return err; + } + + /* Max. time for NRESET low is 2.5ms */ + gpio_direction_output(rst_gpio, 0x0); + usleep(2500); + gpio_direction_output(rst_gpio, 0x1); + } + } err = i2c_smbus_write_byte_data(chip->client, chip->dev_cfg->reg_reset, @@ -572,16 +627,85 @@ static void sx150x_remove_irq_chip(struct sx150x_chip *chip) } } +#ifdef CONFIG_OF +static int sx150x_parse_dt(struct device *dev, + struct sx150x_platform_data *pdata) +{ + int rc; + unsigned int temp; + struct device_node *np = dev->of_node; + + if (!np) + return -ENODEV; + + pdata->oscio_is_gpo = of_property_read_bool(np, "sx150x,oscio_is_gpo"); + pdata->reset_during_probe = + of_property_read_bool(np, "sx150x,reset_onprobe"); + + rc = of_property_read_u32(np, "sx150x,pullup_ena", &temp); + if (rc) { + pr_err("%s: Failed to find pullup_ena %d\n", __func__, rc); + return rc; + } + pdata->io_pullup_ena = temp; + + rc = of_property_read_u32(np, "sx150x,pulldn_ena", &temp); + if (rc) { + pr_err("%s: Failed to find pulldn_ena %d\n", __func__, rc); + return rc; + } + pdata->io_pulldn_ena = temp; + + rc = of_property_read_u32(np, "sx150x,float_ena", &temp); + if (rc) { + pr_err("%s: Failed to find float_ena %d\n", __func__, rc); + return rc; + } + pdata->io_open_drain_ena = temp; + + rc = of_property_read_u32(np, "sx150x,polarity", &temp); + if (rc) { + pr_err("%s: Failed to find polarity %d\n", __func__, rc); + return rc; + } + pdata->io_polarity = temp; + + /* TODO: Add support for Interrupts */ + pdata->irq_summary = -1; + pdata->gpio_base = -1; + + return 0; +} +#else +static int sx150x_parse_dt(struct device *dev, + struct sx150x_platform_data *pdata) +{ + return -ENODEV; +} +#endif + static int sx150x_probe(struct i2c_client *client, const struct i2c_device_id *id) { static const u32 i2c_funcs = I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_WRITE_WORD_DATA; + I2C_FUNC_SMBUS_WRITE_WORD_DATA; struct sx150x_platform_data *pdata; struct sx150x_chip *chip; int rc; - pdata = client->dev.platform_data; + if (client->dev.of_node) { + pdata = devm_kzalloc(&client->dev, + sizeof(struct sx150x_platform_data), GFP_KERNEL); + if (!pdata) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + rc = sx150x_parse_dt(&client->dev, pdata); + if (rc) + return rc; + } else + pdata = client->dev.platform_data; if (!pdata) return -EINVAL; @@ -640,7 +764,8 @@ static int sx150x_remove(struct i2c_client *client) static struct i2c_driver sx150x_driver = { .driver = { .name = "sx150x", - .owner = THIS_MODULE + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(sx150x_match_table) }, .probe = sx150x_probe, .remove = sx150x_remove, diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c index 2b6f83d4d196..5fa2514628a6 100644 --- a/drivers/gpu/ion/msm/msm_ion.c +++ b/drivers/gpu/ion/msm/msm_ion.c @@ -32,8 +32,8 @@ #include "../ion_priv.h" #include "ion_cp_common.h" -#define ION_COMPAT_STR "qti,msm-ion" -#define ION_COMPAT_MEM_RESERVE_STR "qti,msm-ion-reserve" +#define ION_COMPAT_STR "qcom,msm-ion" +#define ION_COMPAT_MEM_RESERVE_STR "qcom,msm-ion-reserve" static struct ion_device *idev; static int num_heaps; @@ -445,7 +445,7 @@ static int msm_init_extra_data(struct device_node *node, unsigned int val; ret = of_property_read_u32(node, - "qti,default-prefetch-size", &val); + "qcom,default-prefetch-size", &val); if (!ret) { heap->extra_data = kzalloc(sizeof(struct ion_cma_pdata), @@ -491,7 +491,7 @@ static int msm_ion_get_heap_type_from_dt_node(struct device_node *node, { const char *name; int i, ret = -EINVAL; - ret = of_property_read_string(node, "qti,ion-heap-type", &name); + ret = of_property_read_string(node, "qcom,ion-heap-type", &name); if (ret) goto out; for (i = 0; i < ARRAY_SIZE(heap_types_info); ++i) { @@ -545,7 +545,7 @@ static void msm_ion_get_heap_align(struct device_node *node, { unsigned int val; - int ret = of_property_read_u32(node, "qti,heap-align", &val); + int ret = of_property_read_u32(node, "qcom,heap-align", &val); if (!ret) { switch ((int) heap->type) { case ION_HEAP_TYPE_CP: @@ -578,11 +578,11 @@ static int msm_ion_get_heap_size(struct device_node *node, u32 out_values[2]; struct device_node *pnode; - ret = of_property_read_u32(node, "qti,memory-reservation-size", &val); + ret = of_property_read_u32(node, "qcom,memory-reservation-size", &val); if (!ret) heap->size = val; - ret = of_property_read_u32_array(node, "qti,memory-fixed", + ret = of_property_read_u32_array(node, "qcom,memory-fixed", out_values, 2); if (!ret) { heap->size = out_values[1]; @@ -617,7 +617,7 @@ static void msm_ion_get_heap_base(struct device_node *node, int ret = 0; struct device_node *pnode; - ret = of_property_read_u32_array(node, "qti,memory-fixed", + ret = of_property_read_u32_array(node, "qcom,memory-fixed", out_values, 2); if (!ret) heap->base = out_values[0]; @@ -635,7 +635,7 @@ static void msm_ion_get_heap_adjacent(struct device_node *node, struct ion_platform_heap *heap) { unsigned int val; - int ret = of_property_read_u32(node, "qti,heap-adjacent", &val); + int ret = of_property_read_u32(node, "qcom,heap-adjacent", &val); if (!ret) { switch (heap->type) { case ION_HEAP_TYPE_CARVEOUT: @@ -805,7 +805,7 @@ static long msm_ion_custom_ioctl(struct ion_client *client, sizeof(struct ion_flush_data))) return -EFAULT; - if (data.handle >= 0) { + if (data.handle > 0) { handle = ion_handle_get_by_id(client, (int)data.handle); if (IS_ERR(handle)) { pr_info("%s: Could not find handle: %d\n", diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index b5f50d03a958..b2d8c1c178d2 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -30,6 +30,7 @@ #include "kgsl_cffdump.h" #include "kgsl_sharedmem.h" #include "kgsl_iommu.h" +#include "kgsl_trace.h" #include "adreno.h" #include "adreno_pm4types.h" @@ -1407,7 +1408,7 @@ static int adreno_of_get_iommu(struct device_node *parent, data->physstart = reg_val[0]; data->physend = data->physstart + reg_val[1] - 1; data->iommu_halt_enable = of_property_read_bool(node, - "qti,iommu-enable-halt"); + "qcom,iommu-enable-halt"); data->iommu_ctx_count = 0; @@ -1861,6 +1862,8 @@ static int _adreno_start(struct adreno_device *adreno_dev) device->reset_counter++; + set_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv); + return 0; error_rb_stop: @@ -1898,7 +1901,25 @@ static void adreno_start_work(struct work_struct *work) set_user_nice(current, _wake_nice); mutex_lock(&device->mutex); - _status = _adreno_start(adreno_dev); + /* + * If adreno start is already called, no need to call it again + * it can lead to unpredictable behavior if we try to start + * the device that is already started. + * Below is the sequence of events that can go bad without the check + * 1) thread 1 calls adreno_start to be scheduled on high priority wq + * 2) thread 2 calls adreno_start with normal priority + * 3) thread 1 after checking the device to be in slumber state gives + * up mutex to be scheduled on high priority wq + * 4) thread 2 after checking the device to be in slumber state gets + * the mutex and finishes adreno_start before thread 1 is scheduled + * on high priority wq. + * 5) thread 1 gets scheduled on high priority wq and executes + * adreno_start again. This leads to unpredictable behavior. + */ + if (!test_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv)) + _status = _adreno_start(adreno_dev); + else + _status = 0; mutex_unlock(&device->mutex); } @@ -1962,6 +1983,8 @@ static int adreno_stop(struct kgsl_device *device) kgsl_cffdump_close(device); + clear_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv); + return 0; } @@ -2807,7 +2830,7 @@ static void adreno_regwrite(struct kgsl_device *device, if (!in_interrupt()) kgsl_pre_hwaccess(device); - kgsl_trace_regwrite(device, offsetwords, value); + trace_kgsl_regwrite(device, offsetwords, value); kgsl_cffdump_regwrite(device, offsetwords << 2, value); reg = (unsigned int *)(device->reg_virt + (offsetwords << 2)); @@ -2847,7 +2870,7 @@ static int adreno_waittimestamp(struct kgsl_device *device, return -EINVAL; ret = adreno_drawctxt_wait(ADRENO_DEVICE(device), context, - timestamp, msecs_to_jiffies(msecs)); + timestamp, msecs); /* If the context got invalidated then return a specific error */ drawctxt = ADRENO_CONTEXT(context); diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index 204cc807d70c..8b48ba0d634c 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -56,8 +56,6 @@ #define KGSL_END_OF_PROFILE_IDENTIFIER 0x2DEFADE2 #define KGSL_PWRON_FIXUP_IDENTIFIER 0x2AFAFAFA -void adreno_debugfs_init(struct kgsl_device *device); - #define ADRENO_ISTORE_START 0x5000 /* Istore offset */ #define ADRENO_NUM_CTX_SWITCH_ALLOWED_BEFORE_DRAW 50 @@ -206,6 +204,7 @@ enum adreno_device_flags { ADRENO_DEVICE_INITIALIZED = 2, ADRENO_DEVICE_CORESIGHT = 3, ADRENO_DEVICE_HANG_INTR = 4, + ADRENO_DEVICE_STARTED = 5, }; #define PERFCOUNTER_FLAG_NONE 0x0 diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c index 17c3c8a0a8a8..a8ab126959ed 100644 --- a/drivers/gpu/msm/adreno_a3xx.c +++ b/drivers/gpu/msm/adreno_a3xx.c @@ -1947,6 +1947,7 @@ void a3xx_perfcounter_close(struct adreno_device *adreno_dev) int a3xx_perfcounter_init(struct adreno_device *adreno_dev) { int ret; + struct kgsl_device *device = &adreno_dev->dev; /* SP[3] counter is broken on a330 so disable it if a330 device */ if (adreno_is_a330(adreno_dev)) a3xx_perfcounters_sp[3].countable = KGSL_PERFCOUNTER_BROKEN; @@ -1959,16 +1960,19 @@ int a3xx_perfcounter_init(struct adreno_device *adreno_dev) ret = adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_PWR, 1, NULL, NULL, PERFCOUNTER_FLAG_KERNEL); - /* VBIF waiting for RAM */ - ret |= adreno_perfcounter_get(adreno_dev, + if (device->pwrctrl.bus_control) { + /* VBIF waiting for RAM */ + ret |= adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_VBIF_PWR, 0, NULL, NULL, PERFCOUNTER_FLAG_KERNEL); - /* VBIF DDR cycles */ - ret |= adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_VBIF, + /* VBIF DDR cycles */ + ret |= adreno_perfcounter_get(adreno_dev, + KGSL_PERFCOUNTER_GROUP_VBIF, VBIF_AXI_TOTAL_BEATS, &adreno_dev->ram_cycles_lo, NULL, PERFCOUNTER_FLAG_KERNEL); + } /* Default performance counter profiling to false */ adreno_dev->profile.enabled = false; diff --git a/drivers/gpu/msm/adreno_a4xx.c b/drivers/gpu/msm/adreno_a4xx.c index 82c1146e5036..33a1df25cf25 100644 --- a/drivers/gpu/msm/adreno_a4xx.c +++ b/drivers/gpu/msm/adreno_a4xx.c @@ -392,6 +392,10 @@ static void a4xx_start(struct adreno_device *adreno_dev) /* Turn on the GPU busy counter and let it run free */ memset(&adreno_dev->busy_data, 0, sizeof(adreno_dev->busy_data)); + /* Disable L2 bypass to avoid UCHE out of bounds errors */ + kgsl_regwrite(device, UCHE_TRAP_BASE_LO, 0xffff0000); + kgsl_regwrite(device, UCHE_TRAP_BASE_HI, 0xffff0000); + /* On A420 cores turn on SKIP_IB2_DISABLE in addition to the default */ kgsl_regwrite(device, A4XX_CP_DEBUG, A4XX_CP_DEBUG_DEFAULT | (adreno_is_a420(adreno_dev) ? (1 << 29) : 0)); diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index 1fd4a5a02aab..301aaa5fafb8 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -20,6 +20,7 @@ #include "kgsl.h" #include "kgsl_sharedmem.h" #include "kgsl_cffdump.h" +#include "kgsl_trace.h" #include "adreno.h" #include "adreno_pm4types.h" @@ -1223,7 +1224,7 @@ int adreno_ringbuffer_submitcmd(struct adreno_device *adreno_dev, done: device->pwrctrl.irq_last = 0; - kgsl_trace_issueibcmds(device, context->id, cmdbatch, + trace_kgsl_issueibcmds(device, context->id, cmdbatch, cmdbatch->timestamp, cmdbatch->flags, ret, drawctxt->type); diff --git a/drivers/gpu/msm/adreno_trace.h b/drivers/gpu/msm/adreno_trace.h index d0b98b0776a4..6301ea479efd 100644 --- a/drivers/gpu/msm/adreno_trace.h +++ b/drivers/gpu/msm/adreno_trace.h @@ -18,6 +18,7 @@ #define TRACE_SYSTEM kgsl #undef TRACE_INCLUDE_PATH #define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE #define TRACE_INCLUDE_FILE adreno_trace #include <linux/tracepoint.h> diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index fe9a8e7ac9e9..81b290b33ddd 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -65,44 +65,6 @@ static void kgsl_mem_entry_detach_process(struct kgsl_mem_entry *entry); static void kgsl_put_process_private(struct kgsl_device *device, struct kgsl_process_private *private); -/** - * kgsl_trace_issueibcmds() - Call trace_issueibcmds by proxy - * device: KGSL device - * id: ID of the context submitting the command - * cmdbatch: Pointer to kgsl_cmdbatch describing these commands - * timestamp: Timestamp assigned to the command batch - * flags: Flags sent by the user - * result: Result of the submission attempt - * type: Type of context issuing the command - * - * Wrap the issueibcmds ftrace hook into a function that can be called from the - * GPU specific modules. - */ -void kgsl_trace_issueibcmds(struct kgsl_device *device, int id, - struct kgsl_cmdbatch *cmdbatch, - unsigned int timestamp, unsigned int flags, - int result, unsigned int type) -{ - trace_kgsl_issueibcmds(device, id, cmdbatch, - timestamp, flags, result, type); -} -EXPORT_SYMBOL(kgsl_trace_issueibcmds); - -/** - * kgsl_trace_regwrite - call regwrite ftrace function by proxy - * device: KGSL device - * offset: dword offset of the register being written - * value: Value of the register being written - * - * Wrap the regwrite ftrace hook into a function that can be called from the - * GPU specific modules. - */ -void kgsl_trace_regwrite(struct kgsl_device *device, unsigned int offset, - unsigned int value) -{ - trace_kgsl_regwrite(device, offset, value); -} -EXPORT_SYMBOL(kgsl_trace_regwrite); static int kgsl_memfree_hist_init(void) { diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 60fade0e6c47..ebadca4bf1d1 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -291,14 +291,6 @@ extern const struct dev_pm_ops kgsl_pm_ops; int kgsl_suspend_driver(struct platform_device *pdev, pm_message_t state); int kgsl_resume_driver(struct platform_device *pdev); -void kgsl_trace_regwrite(struct kgsl_device *device, unsigned int offset, - unsigned int value); - -void kgsl_trace_issueibcmds(struct kgsl_device *device, int id, - struct kgsl_cmdbatch *cmdbatch, - unsigned int timestamp, unsigned int flags, - int result, unsigned int type); - #ifdef CONFIG_MSM_KGSL_DRM extern int kgsl_drm_init(struct platform_device *dev); extern void kgsl_drm_exit(void); diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c index 600d9b25b185..0cd5e36c98cf 100644 --- a/drivers/gpu/msm/kgsl_events.c +++ b/drivers/gpu/msm/kgsl_events.c @@ -210,7 +210,7 @@ int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts, kgsl_event_func func, void *priv, void *owner) { struct kgsl_event *event; - unsigned int queued, cur_ts; + unsigned int queued = 0, cur_ts; struct kgsl_context *context = NULL; BUG_ON(!mutex_is_locked(&device->mutex)); @@ -223,12 +223,20 @@ int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts, if (context == NULL) return -EINVAL; } + /* + * If the caller is creating their own timestamps, let them schedule + * events in the future. Otherwise only allow timestamps that have been + * queued. + */ + if (context == NULL || + ((context->flags & KGSL_CONTEXT_USER_GENERATED_TS) == 0)) { + kgsl_readtimestamp(device, context, + KGSL_TIMESTAMP_QUEUED, &queued); - kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_QUEUED, &queued); - - if (timestamp_cmp(ts, queued) > 0) { - kgsl_context_put(context); - return -EINVAL; + if (timestamp_cmp(ts, queued) > 0) { + kgsl_context_put(context); + return -EINVAL; + } } kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED, &cur_ts); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 0262896e1d73..a03809a6b1ec 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -130,6 +130,9 @@ void kgsl_pwrctrl_buslevel_update(struct kgsl_device *device, int buslevel = 0; if (!pwr->pcl) return; + /* the bus should be ON to update the active frequency */ + if (on && !(test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags))) + return; /* * If the bus should remain on calculate our request and submit it, * otherwise request bus level 0, off. @@ -173,25 +176,12 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, pwr->bus_mod = 0; pwrlevel = &pwr->pwrlevels[pwr->active_pwrlevel]; - if (test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)) { - kgsl_pwrctrl_buslevel_update(device, true); - if (pwr->ebi1_clk) - clk_set_rate(pwr->ebi1_clk, pwrlevel->bus_freq); - } + kgsl_pwrctrl_buslevel_update(device, true); if (test_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->power_flags) || (device->state == KGSL_STATE_NAP)) { /* - * On some platforms, instability is caused on - * changing clock freq when the core is busy. - * Idle the gpu core before changing the clock freq. - */ - - if (pwr->idle_needed == true) - device->ftbl->idle(device); - - /* * Don't shift by more than one level at a time to * avoid glitches. */ @@ -949,22 +939,12 @@ static void kgsl_pwrctrl_axi(struct kgsl_device *device, int state) if (test_and_clear_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)) { trace_kgsl_bus(device, state); - if (pwr->ebi1_clk) { - clk_set_rate(pwr->ebi1_clk, 0); - clk_disable_unprepare(pwr->ebi1_clk); - } kgsl_pwrctrl_buslevel_update(device, false); } } else if (state == KGSL_PWRFLAGS_ON) { if (!test_and_set_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)) { trace_kgsl_bus(device, state); - if (pwr->ebi1_clk) { - clk_prepare_enable(pwr->ebi1_clk); - clk_set_rate(pwr->ebi1_clk, - pwr->pwrlevels[pwr->active_pwrlevel]. - bus_freq); - } kgsl_pwrctrl_buslevel_update(device, true); } } @@ -1103,16 +1083,8 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) pwr->power_flags = 0; - pwr->idle_needed = pdata->idle_needed; pwr->interval_timeout = pdata->idle_timeout; pwr->strtstp_sleepwake = pdata->strtstp_sleepwake; - pwr->ebi1_clk = clk_get(&pdev->dev, "bus_clk"); - if (IS_ERR(pwr->ebi1_clk)) - pwr->ebi1_clk = NULL; - else - clk_set_rate(pwr->ebi1_clk, - pwr->pwrlevels[pwr->active_pwrlevel]. - bus_freq); /* Set the CPU latency to 501usec to allow low latency PC modes */ pwr->pm_qos_latency = 501; @@ -1183,8 +1155,6 @@ void kgsl_pwrctrl_close(struct kgsl_device *device) pm_runtime_disable(device->parentdev); - clk_put(pwr->ebi1_clk); - if (pwr->pcl) msm_bus_scale_unregister_client(pwr->pcl); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index a5416a11c208..9362840a7279 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -43,7 +43,6 @@ struct kgsl_clk_stats { /** * struct kgsl_pwrctrl - Power control settings for a KGSL device * @interrupt_num - The interrupt number for the device - * @ebi1_clk - Pointer to the EBI clock structure * @grp_clks - Array of clocks structures that we control * @power_flags - Control flags for power * @pwrlevels - List of supported power levels @@ -59,7 +58,6 @@ struct kgsl_clk_stats { * @gpu_reg - pointer to the regulator structure for gpu_reg * @gpu_cx - pointer to the regulator structure for gpu_cx * @pcl - bus scale identifier - * @idle_needed - true if the device needs a idle before clock change * @irq_name - resource name for the IRQ * @clk_stats - structure of clock statistics * @pm_qos_req_dma - the power management quality of service structure @@ -72,7 +70,6 @@ struct kgsl_clk_stats { struct kgsl_pwrctrl { int interrupt_num; - struct clk *ebi1_clk; struct clk *grp_clks[KGSL_MAX_CLKS]; unsigned long power_flags; unsigned long ctrl_flags; @@ -90,7 +87,6 @@ struct kgsl_pwrctrl { struct regulator *gpu_reg; struct regulator *gpu_cx; uint32_t pcl; - unsigned int idle_needed; const char *irq_name; struct kgsl_clk_stats clk_stats; struct pm_qos_request pm_qos_req_dma; diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h index 9b71ae69f4b4..8e6715183af2 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.h +++ b/drivers/gpu/msm/kgsl_sharedmem.h @@ -131,7 +131,7 @@ static inline unsigned int kgsl_get_sg_pa(struct scatterlist *sg) static inline void *kgsl_sg_alloc(unsigned int sglen) { - if (sglen >= ULONG_MAX / sizeof(struct scatterlist)) + if ((sglen == 0) || (sglen >= ULONG_MAX / sizeof(struct scatterlist))) return NULL; if ((sglen * sizeof(struct scatterlist)) < PAGE_SIZE) diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c index b35cebdc6846..d59342eece87 100644 --- a/drivers/gpu/msm/kgsl_snapshot.c +++ b/drivers/gpu/msm/kgsl_snapshot.c @@ -187,7 +187,6 @@ static int snapshot_os(struct kgsl_device *device, header->power_level = pwr->active_pwrlevel; header->power_interval_timeout = pwr->interval_timeout; header->grpclk = kgsl_get_clkrate(pwr->grp_clks[0]); - header->busclk = kgsl_get_clkrate(pwr->ebi1_clk); /* Save the last active context */ kgsl_sharedmem_readl(&device->memstore, &header->current_context, diff --git a/drivers/gpu/msm/kgsl_trace.c b/drivers/gpu/msm/kgsl_trace.c index e432729fb353..253034e2a4fa 100644 --- a/drivers/gpu/msm/kgsl_trace.c +++ b/drivers/gpu/msm/kgsl_trace.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011, 2013, The Linux Foundation. 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 and @@ -11,9 +11,14 @@ * */ +#include <linux/module.h> + #include "kgsl.h" #include "kgsl_device.h" /* Instantiate tracepoints */ #define CREATE_TRACE_POINTS #include "kgsl_trace.h" + +EXPORT_TRACEPOINT_SYMBOL(kgsl_regwrite); +EXPORT_TRACEPOINT_SYMBOL(kgsl_issueibcmds); diff --git a/drivers/hwmon/epm_adc.c b/drivers/hwmon/epm_adc.c index e271beb718b1..f62093ad3773 100644 --- a/drivers/hwmon/epm_adc.c +++ b/drivers/hwmon/epm_adc.c @@ -18,6 +18,7 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/gpio.h> +#include <linux/of_gpio.h> #include <linux/hwmon.h> #include <linux/delay.h> #include <linux/epm_adc.h> @@ -133,6 +134,7 @@ struct epm_adc_drv { uint32_t bus_id; struct miscdevice misc; uint32_t channel_mask; + uint32_t epm_global_en_gpio; struct epm_chan_properties epm_psoc_ch_prop[0]; }; @@ -705,22 +707,24 @@ conv_err: return rc; } -static int epm_adc_psoc_gpio_init(bool enable) +static int epm_adc_psoc_gpio_init(struct epm_adc_drv *epm_adc, + bool enable) { int rc = 0; if (enable) { - rc = gpio_request(EPM_PSOC_GLOBAL_ENABLE, "EPM_PSOC_GLOBAL_EN"); + rc = gpio_request(epm_adc->epm_global_en_gpio, + "EPM_PSOC_GLOBAL_EN"); if (!rc) { - gpio_direction_output(EPM_PSOC_GLOBAL_ENABLE, 1); + gpio_direction_output(epm_adc->epm_global_en_gpio, 1); } else { pr_err("%s: Configure EPM_GLOBAL_EN Failed\n", __func__); return rc; } } else { - gpio_direction_output(EPM_PSOC_GLOBAL_ENABLE, 0); - gpio_free(EPM_PSOC_GLOBAL_ENABLE); + gpio_direction_output(epm_adc->epm_global_en_gpio, 0); + gpio_free(epm_adc->epm_global_en_gpio); } return 0; @@ -899,6 +903,12 @@ static int epm_psoc_init(struct epm_adc_drv *epm_adc, init_resp->num_dev = rx_buf[6]; init_resp->num_channel = rx_buf[7]; + pr_debug("EPM PSOC response for hello command: resp_cmd:0x%x\n", + rx_buf[0]); + pr_debug("EPM PSOC version:0x%x\n", rx_buf[1]); + pr_debug("EPM PSOC firmware version:0x%x\n", + rx_buf[6] | rx_buf[5] | rx_buf[4] | rx_buf[3]); + return rc; } @@ -1694,7 +1704,7 @@ static long epm_adc_ioctl(struct file *file, unsigned int cmd, } if (!rc) { - rc = epm_adc_psoc_gpio_init(true); + rc = epm_adc_psoc_gpio_init(epm_adc, true); if (rc) { pr_err("GPIO init failed\n"); return -EINVAL; @@ -1709,7 +1719,7 @@ static long epm_adc_ioctl(struct file *file, unsigned int cmd, case EPM_PSOC_ADC_DEINIT: { uint32_t result; - result = epm_adc_psoc_gpio_init(false); + result = epm_adc_psoc_gpio_init(epm_adc, false); if (copy_to_user((void __user *)arg, &result, sizeof(uint32_t))) @@ -2071,7 +2081,7 @@ static ssize_t epm_adc_psoc_show_in(struct device *dev, struct epm_psoc_get_data psoc_get_meas; int rc = 0; - rc = epm_adc_psoc_gpio_init(true); + rc = epm_adc_psoc_gpio_init(epm_adc, true); if (rc) { pr_err("GPIO init failed\n"); return 0; @@ -2111,7 +2121,7 @@ static ssize_t epm_adc_psoc_show_in(struct device *dev, psoc_get_meas.reading_value, attr->index); - rc = epm_adc_psoc_gpio_init(false); + rc = epm_adc_psoc_gpio_init(epm_adc, false); if (rc) { pr_err("GPIO de-init failed\n"); return 0; @@ -2177,7 +2187,7 @@ static int get_device_tree_data(struct spi_device *spi) const struct device_node *node = spi->dev.of_node; struct epm_adc_drv *epm_adc; u32 *epm_ch_gain, *epm_ch_rsense; - u32 rc = 0, epm_num_channels, i, channel_mask; + u32 rc = 0, epm_num_channels, i, channel_mask, epm_gpio_num; if (!node) return -EINVAL; @@ -2224,6 +2234,13 @@ static int get_device_tree_data(struct spi_device *spi) return -ENODEV; } + epm_gpio_num = of_get_named_gpio(spi->dev.of_node, + "qcom,epm-enable-gpio", 0); + if (epm_gpio_num < 0) { + dev_err(&spi->dev, "missing global en gpio num\n"); + return -ENODEV; + } + epm_adc = devm_kzalloc(&spi->dev, sizeof(struct epm_adc_drv) + (epm_num_channels * @@ -2242,6 +2259,7 @@ static int get_device_tree_data(struct spi_device *spi) } epm_adc->channel_mask = channel_mask; + epm_adc->epm_global_en_gpio = epm_gpio_num; epm_adc_drv = epm_adc; return 0; diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c index ffb8dc7486a2..9c58718d56c2 100644 --- a/drivers/hwmon/qpnp-adc-common.c +++ b/drivers/hwmon/qpnp-adc-common.c @@ -238,6 +238,60 @@ static const struct qpnp_vadc_map_pt adcmap_qrd_skuaa_btm_threshold[] = { {800, 549}, }; +static const struct qpnp_vadc_map_pt adcmap_qrd_skug_btm_threshold[] = { + {-200, 1338}, + {-180, 1307}, + {-160, 1276}, + {-140, 1244}, + {-120, 1213}, + {-100, 1182}, + {-80, 1151}, + {-60, 1121}, + {-40, 1092}, + {-20, 1063}, + {0, 1035}, + {20, 1008}, + {40, 982}, + {60, 957}, + {80, 933}, + {100, 910}, + {120, 889}, + {140, 868}, + {160, 848}, + {180, 830}, + {200, 812}, + {220, 795}, + {240, 780}, + {260, 765}, + {280, 751}, + {300, 738}, + {320, 726}, + {340, 714}, + {360, 704}, + {380, 694}, + {400, 684}, + {420, 675}, + {440, 667}, + {460, 659}, + {480, 652}, + {500, 645}, + {520, 639}, + {540, 633}, + {560, 627}, + {580, 622}, + {600, 617}, + {620, 613}, + {640, 608}, + {660, 604}, + {680, 600}, + {700, 597}, + {720, 593}, + {740, 590}, + {760, 587}, + {780, 585}, + {800, 582}, +}; + /* Voltage to temperature */ static const struct qpnp_vadc_map_pt adcmap_100k_104ef_104fb[] = { {1758, -40}, @@ -528,7 +582,8 @@ int32_t qpnp_adc_scale_pmic_therm(struct qpnp_vadc_chip *vadc, if (!chan_properties || !chan_properties->offset_gain_numerator || !chan_properties->offset_gain_denominator || !adc_properties - || !adc_chan_result) + || !adc_chan_result + || !chan_properties->adc_graph[CALIB_ABSOLUTE].dy) return -EINVAL; pmic_voltage = (adc_code - @@ -698,6 +753,25 @@ int32_t qpnp_adc_scale_qrd_skuaa_batt_therm(struct qpnp_vadc_chip *chip, } EXPORT_SYMBOL(qpnp_adc_scale_qrd_skuaa_batt_therm); +int32_t qpnp_adc_scale_qrd_skug_batt_therm(struct qpnp_vadc_chip *chip, + int32_t adc_code, + const struct qpnp_adc_properties *adc_properties, + const struct qpnp_vadc_chan_properties *chan_properties, + struct qpnp_vadc_result *adc_chan_result) +{ + int64_t bat_voltage = 0; + + bat_voltage = qpnp_adc_scale_ratiometric_calib(adc_code, + adc_properties, chan_properties); + + return qpnp_adc_map_temp_voltage( + adcmap_qrd_skug_btm_threshold, + ARRAY_SIZE(adcmap_qrd_skug_btm_threshold), + bat_voltage, + &adc_chan_result->physical); +} +EXPORT_SYMBOL(qpnp_adc_scale_qrd_skug_batt_therm); + int32_t qpnp_adc_scale_smb_batt_therm(struct qpnp_vadc_chip *chip, int32_t adc_code, const struct qpnp_adc_properties *adc_properties, diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c index dc8b991d5660..40734a2c3f88 100644 --- a/drivers/hwmon/qpnp-adc-current.c +++ b/drivers/hwmon/qpnp-adc-current.c @@ -1060,6 +1060,11 @@ int32_t qpnp_iadc_read(struct qpnp_iadc_chip *iadc, if (qpnp_iadc_is_valid(iadc) < 0) return -EPROBE_DEFER; + if ((iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw) == 0) { + pr_err("raw offset errors! run iadc calibration again\n"); + return -EINVAL; + } + rc = qpnp_check_pmic_temp(iadc); if (rc) { pr_err("Error checking pmic therm temp\n"); diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c index c3cd522b0795..511dbc6d8b79 100644 --- a/drivers/hwmon/qpnp-adc-voltage.c +++ b/drivers/hwmon/qpnp-adc-voltage.c @@ -130,6 +130,7 @@ static struct qpnp_vadc_scale_fn vadc_scale_fn[] = { [SCALE_QRD_BATT_THERM] = {qpnp_adc_scale_qrd_batt_therm}, [SCALE_QRD_SKUAA_BATT_THERM] = {qpnp_adc_scale_qrd_skuaa_batt_therm}, [SCALE_SMB_BATT_THERM] = {qpnp_adc_scale_smb_batt_therm}, + [SCALE_QRD_SKUG_BATT_THERM] = {qpnp_adc_scale_qrd_skug_batt_therm}, }; static int32_t qpnp_vadc_read_reg(struct qpnp_vadc_chip *vadc, int16_t reg, @@ -854,7 +855,14 @@ static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc) } pr_debug("absolute reference raw: 625mV:0x%x 1.25V:0x%x\n", - calib_read_1, calib_read_2); + calib_read_2, calib_read_1); + + if (calib_read_1 == calib_read_2) { + pr_err("absolute reference raw: 625mV:0x%x 1.25V:0x%x\n", + calib_read_2, calib_read_1); + rc = -EINVAL; + goto calib_fail; + } vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dy = (calib_read_1 - calib_read_2); @@ -934,6 +942,14 @@ static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc) pr_debug("ratiometric reference raw: VDD:0x%x GND:0x%x\n", calib_read_1, calib_read_2); + + if (calib_read_1 == calib_read_2) { + pr_err("ratiometric reference raw: VDD:0x%x GND:0x%x\n", + calib_read_1, calib_read_2); + rc = -EINVAL; + goto calib_fail; + } + vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].dy = (calib_read_1 - calib_read_2); vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].dx = diff --git a/drivers/hwspinlock/msm_remote_spinlock.c b/drivers/hwspinlock/msm_remote_spinlock.c index e541f1f30884..c0ac7b0eae9f 100644 --- a/drivers/hwspinlock/msm_remote_spinlock.c +++ b/drivers/hwspinlock/msm_remote_spinlock.c @@ -19,7 +19,7 @@ #include <linux/of_address.h> #include <linux/msm_remote_spinlock.h> -#include <mach/msm_smem.h> +#include <soc/qcom/smem.h> #define SPINLOCK_PID_APPS 1 diff --git a/drivers/input/misc/bmp18x-core.c b/drivers/input/misc/bmp18x-core.c index 6c0f2f2e0784..5821a5987ac9 100644 --- a/drivers/input/misc/bmp18x-core.c +++ b/drivers/input/misc/bmp18x-core.c @@ -88,6 +88,7 @@ struct bmp18x_data { struct device *dev; struct mutex lock; struct bmp18x_calibration_data calibration; + struct sensors_classdev cdev; u8 oversampling_setting; u8 sw_oversampling_setting; u32 raw_temperature; @@ -113,9 +114,13 @@ static struct sensors_classdev sensors_cdev = { .max_range = "1100.0", .resolution = "0.01", .sensor_power = "0.67", - .min_delay = 20000, + .min_delay = 20000, /* microsecond */ .fifo_reserved_event_count = 0, .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, /* millisecond */ + .sensors_enable = NULL, + .sensors_poll_delay = NULL, }; #ifdef CONFIG_HAS_EARLYSUSPEND @@ -403,6 +408,19 @@ static ssize_t show_sw_oversampling(struct device *dev, static DEVICE_ATTR(sw_oversampling, S_IWUSR | S_IRUGO, show_sw_oversampling, set_sw_oversampling); +static ssize_t bmp18x_poll_delay_set(struct sensors_classdev *sensors_cdev, + unsigned int delay_msec) +{ + struct bmp18x_data *data = container_of(sensors_cdev, + struct bmp18x_data, cdev); + mutex_lock(&data->lock); + data->delay = delay_msec; + mutex_unlock(&data->lock); + + return 0; +} + + static ssize_t show_delay(struct device *dev, struct device_attribute *attr, char *buf) { @@ -416,17 +434,43 @@ static ssize_t set_delay(struct device *dev, { struct bmp18x_data *data = dev_get_drvdata(dev); unsigned long delay; - int success = kstrtoul(buf, 10, &delay); - if (success == 0) { - mutex_lock(&data->lock); - data->delay = delay; - mutex_unlock(&data->lock); - } - return success; + int err = kstrtoul(buf, 10, &delay); + if (err < 0) + return err; + + err = bmp18x_poll_delay_set(&data->cdev, delay); + if (err < 0) + return err; + + return count; } -static DEVICE_ATTR(delay, S_IWUSR | S_IRUGO, + +static DEVICE_ATTR(poll_delay, S_IWUSR | S_IRUGO, show_delay, set_delay); +static ssize_t bmp18x_enable_set(struct sensors_classdev *sensors_cdev, + unsigned int enabled) +{ + struct bmp18x_data *data = container_of(sensors_cdev, + struct bmp18x_data, cdev); + struct device *dev = data->dev; + + mutex_lock(&data->lock); + data->enable = enabled ? 1 : 0; + + if (data->enable) { + bmp18x_enable(dev); + schedule_delayed_work(&data->work, + msecs_to_jiffies(data->delay)); + } else { + cancel_delayed_work_sync(&data->work); + bmp18x_disable(dev); + } + mutex_unlock(&data->lock); + + return 0; +} + static ssize_t show_enable(struct device *dev, struct device_attribute *attr, char *buf) { @@ -440,24 +484,17 @@ static ssize_t set_enable(struct device *dev, { struct bmp18x_data *data = dev_get_drvdata(dev); unsigned long enable; - int success = kstrtoul(buf, 10, &enable); - if (success == 0) { - mutex_lock(&data->lock); - data->enable = enable ? 1 : 0; - - if (data->enable) { - bmp18x_enable(dev); - schedule_delayed_work(&data->work, - msecs_to_jiffies(data->delay)); - } else { - cancel_delayed_work_sync(&data->work); - bmp18x_disable(dev); - } - mutex_unlock(&data->lock); + int err = kstrtoul(buf, 10, &enable); + if (err < 0) + return err; + + err = bmp18x_enable_set(&data->cdev, enable); + if (err < 0) + return err; - } return count; } + static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, show_enable, set_enable); @@ -499,7 +536,7 @@ static struct attribute *bmp18x_attributes[] = { &dev_attr_pressure0_input.attr, &dev_attr_oversampling.attr, &dev_attr_sw_oversampling.attr, - &dev_attr_delay.attr, + &dev_attr_poll_delay.attr, &dev_attr_enable.attr, NULL }; @@ -628,7 +665,10 @@ int bmp18x_probe(struct device *dev, struct bmp18x_data_bus *data_bus) if (err) goto error_sysfs; - err = sensors_classdev_register(&data->input->dev, &sensors_cdev); + data->cdev = sensors_cdev; + data->cdev.sensors_enable = bmp18x_enable_set; + data->cdev.sensors_poll_delay = bmp18x_poll_delay_set; + err = sensors_classdev_register(&data->input->dev, &data->cdev); if (err) { pr_err("class device create failed: %d\n", err); goto error_class_sysfs; diff --git a/drivers/input/misc/cm36283.c b/drivers/input/misc/cm36283.c index c8a276b9d19c..76162ac6f5a5 100644 --- a/drivers/input/misc/cm36283.c +++ b/drivers/input/misc/cm36283.c @@ -639,12 +639,6 @@ static const struct file_operations psensor_fops = { .unlocked_ioctl = psensor_ioctl }; -struct miscdevice psensor_misc = { - .minor = MISC_DYNAMIC_MINOR, - .name = "proximity", - .fops = &psensor_fops -}; - void lightsensor_set_kvalue(struct cm36283_info *lpi) { if (!lpi) { @@ -792,13 +786,6 @@ static const struct file_operations lightsensor_fops = { .unlocked_ioctl = lightsensor_ioctl }; -static struct miscdevice lightsensor_misc = { - .minor = MISC_DYNAMIC_MINOR, - .name = "lightsensor", - .fops = &lightsensor_fops -}; - - static ssize_t ps_adc_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -843,7 +830,6 @@ static ssize_t ps_enable_store(struct device *dev, return count; } -static DEVICE_ATTR(ps_adc, 0664, ps_adc_show, ps_enable_store); static ssize_t ps_parameters_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -887,10 +873,6 @@ static ssize_t ps_parameters_store(struct device *dev, return count; } -static DEVICE_ATTR(ps_parameters, 0664, - ps_parameters_show, ps_parameters_store); - - static ssize_t ps_conf_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -916,7 +898,6 @@ static ssize_t ps_conf_store(struct device *dev, return count; } -static DEVICE_ATTR(ps_conf, 0664, ps_conf_show, ps_conf_store); static ssize_t ps_thd_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -945,7 +926,6 @@ static ssize_t ps_thd_store(struct device *dev, return count; } -static DEVICE_ATTR(ps_thd, 0664, ps_thd_show, ps_thd_store); static ssize_t ps_hw_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -968,7 +948,6 @@ static ssize_t ps_hw_store(struct device *dev, return count; } -static DEVICE_ATTR(ps_hw, 0664, ps_hw_show, ps_hw_store); static ssize_t ls_adc_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -982,8 +961,6 @@ static ssize_t ls_adc_show(struct device *dev, return ret; } -static DEVICE_ATTR(ls_adc, 0664, ls_adc_show, NULL); - static ssize_t ls_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1032,8 +1009,6 @@ static ssize_t ls_enable_store(struct device *dev, return count; } -static DEVICE_ATTR(ls_auto, 0664, - ls_enable_show, ls_enable_store); static ssize_t ls_kadc_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1076,7 +1051,6 @@ static ssize_t ls_kadc_store(struct device *dev, return count; } -static DEVICE_ATTR(ls_kadc, 0664, ls_kadc_show, ls_kadc_store); static ssize_t ls_gadc_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1116,7 +1090,6 @@ static ssize_t ls_gadc_store(struct device *dev, return count; } -static DEVICE_ATTR(ls_gadc, 0664, ls_gadc_show, ls_gadc_store); static ssize_t ls_adc_table_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1167,9 +1140,6 @@ static ssize_t ls_adc_table_store(struct device *dev, return count; } -static DEVICE_ATTR(ls_adc_table, 0664, - ls_adc_table_show, ls_adc_table_store); - static ssize_t ls_conf_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1191,7 +1161,6 @@ static ssize_t ls_conf_store(struct device *dev, _cm36283_I2C_Write_Word(lpi->slave_addr, ALS_CONF, lpi->ls_cmd); return count; } -static DEVICE_ATTR(ls_conf, 0664, ls_conf_show, ls_conf_store); static ssize_t ls_poll_delay_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1218,9 +1187,6 @@ static ssize_t ls_poll_delay_store(struct device *dev, return count; } -static DEVICE_ATTR(ls_poll_delay, 0664, ls_poll_delay_show, - ls_poll_delay_store); - static ssize_t ps_poll_delay_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1246,9 +1212,6 @@ static ssize_t ps_poll_delay_store(struct device *dev, return count; } -static DEVICE_ATTR(ps_poll_delay, 0664, ps_poll_delay_show, - ps_poll_delay_store); - static ssize_t ls_fLevel_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1270,7 +1233,6 @@ static ssize_t ls_fLevel_store(struct device *dev, fLevel=-1; return count; } -static DEVICE_ATTR(ls_flevel, 0664, ls_fLevel_show, ls_fLevel_store); static int lightsensor_setup(struct cm36283_info *lpi) { @@ -1285,6 +1247,7 @@ static int lightsensor_setup(struct cm36283_info *lpi) return -ENOMEM; } lpi->ls_input_dev->name = "cm36283-ls"; + lpi->ls_input_dev->id.bustype = BUS_I2C; set_bit(EV_ABS, lpi->ls_input_dev->evbit); range = get_als_range(); @@ -1297,17 +1260,8 @@ static int lightsensor_setup(struct cm36283_info *lpi) goto err_free_ls_input_device; } - ret = misc_register(&lightsensor_misc); - if (ret < 0) { - pr_err("[LS][CM36283 error]%s: can not register ls misc device\n", - __func__); - goto err_unregister_ls_input_device; - } - return ret; -err_unregister_ls_input_device: - input_unregister_device(lpi->ls_input_dev); err_free_ls_input_device: input_free_device(lpi->ls_input_dev); return ret; @@ -1325,6 +1279,7 @@ static int psensor_setup(struct cm36283_info *lpi) return -ENOMEM; } lpi->ps_input_dev->name = "cm36283-ps"; + lpi->ps_input_dev->id.bustype = BUS_I2C; set_bit(EV_ABS, lpi->ps_input_dev->evbit); input_set_abs_params(lpi->ps_input_dev, ABS_DISTANCE, 0, 1, 0, 0); @@ -1336,18 +1291,8 @@ static int psensor_setup(struct cm36283_info *lpi) goto err_free_ps_input_device; } - ret = misc_register(&psensor_misc); - if (ret < 0) { - pr_err( - "[PS][CM36283 error]%s: could not register ps misc device\n", - __func__); - goto err_unregister_ps_input_device; - } - return ret; -err_unregister_ps_input_device: - input_unregister_device(lpi->ps_input_dev); err_free_ps_input_device: input_free_device(lpi->ps_input_dev); return ret; @@ -1495,6 +1440,59 @@ static int cm36283_parse_dt(struct device *dev, return 0; } +static int create_sysfs_interfaces(struct device *dev, + struct device_attribute *attributes, int len) +{ + int i; + int err; + for (i = 0; i < len; i++) { + err = device_create_file(dev, attributes + i); + if (err) + goto error; + } + return 0; + +error: + for (; i >= 0; i--) + device_remove_file(dev, attributes + i); + dev_err(dev, "%s:Unable to create interface\n", __func__); + return err; +} + +static int remove_sysfs_interfaces(struct device *dev, + struct device_attribute *attributes, int len) +{ + int i; + for (i = 0; i < len; i++) + device_remove_file(dev, attributes + i); + return 0; +} + +static struct device_attribute light_attr[] = { + __ATTR(ls_adc, 0664, ls_adc_show, NULL), + __ATTR(ls_kadc, 0664, ls_kadc_show, ls_kadc_store), + __ATTR(ls_gadc, 0664, ls_gadc_show, ls_gadc_store), + __ATTR(ls_conf, 0664, ls_conf_show, ls_conf_store), + __ATTR(ls_adc_table, 0664, + ls_adc_table_show, ls_adc_table_store), + __ATTR(poll_delay, 0664, ls_poll_delay_show, + ls_poll_delay_store), + __ATTR(enable, 0664, + ls_enable_show, ls_enable_store), +}; + +static struct device_attribute proximity_attr[] = { + __ATTR(enable, 0664, ps_adc_show, ps_enable_store), + __ATTR(ps_parameters, 0664, + ps_parameters_show, ps_parameters_store), + __ATTR(ps_conf, 0664, ps_conf_show, ps_conf_store), + __ATTR(ps_hw, 0664, ps_hw_show, ps_hw_store), + __ATTR(ps_thd, 0664, ps_thd_show, ps_thd_store), + __ATTR(poll_delay, 0664, ps_poll_delay_show, + ps_poll_delay_store), + __ATTR(ls_flevel, 0664, ls_fLevel_show, ls_fLevel_store), +}; + static int cm36283_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1632,95 +1630,24 @@ static int cm36283_probe(struct i2c_client *client, goto err_psensor_setup; } - lpi->cm36283_class = class_create(THIS_MODULE, "optical_sensors"); - if (IS_ERR(lpi->cm36283_class)) { - ret = PTR_ERR(lpi->cm36283_class); - lpi->cm36283_class = NULL; - goto err_create_class; - } - - lpi->ls_dev = device_create(lpi->cm36283_class, - NULL, 0, "%s", "lightsensor"); - if (unlikely(IS_ERR(lpi->ls_dev))) { - ret = PTR_ERR(lpi->ls_dev); - lpi->ls_dev = NULL; - goto err_create_ls_device; + ret = create_sysfs_interfaces(&lpi->ls_input_dev->dev, light_attr, + ARRAY_SIZE(light_attr)); + if (ret < 0) { + dev_err(&client->dev, "failed to create sysfs\n"); + goto err_input_cleanup; } - /* register the attributes */ - ret = device_create_file(lpi->ls_dev, &dev_attr_ls_adc); - if (ret) - goto err_create_ls_device_file; - - /* register the attributes */ - ret = device_create_file(lpi->ls_dev, &dev_attr_ls_auto); - if (ret) - goto err_create_ls_device_file; - - /* register the attributes */ - ret = device_create_file(lpi->ls_dev, &dev_attr_ls_kadc); - if (ret) - goto err_create_ls_device_file; - - ret = device_create_file(lpi->ls_dev, &dev_attr_ls_gadc); - if (ret) - goto err_create_ls_device_file; - - ret = device_create_file(lpi->ls_dev, &dev_attr_ls_adc_table); - if (ret) - goto err_create_ls_device_file; - - ret = device_create_file(lpi->ls_dev, &dev_attr_ls_conf); - if (ret) - goto err_create_ls_device_file; - - ret = device_create_file(lpi->ls_dev, &dev_attr_ls_flevel); - if (ret) - goto err_create_ls_device_file; - - ret = device_create_file(lpi->ls_dev, &dev_attr_ls_poll_delay); - if (ret) - goto err_create_ls_device_file; - - lpi->ps_dev = device_create(lpi->cm36283_class, - NULL, 0, "%s", "proximity"); - if (unlikely(IS_ERR(lpi->ps_dev))) { - ret = PTR_ERR(lpi->ps_dev); - lpi->ps_dev = NULL; - goto err_create_ps_device; + ret = create_sysfs_interfaces(&lpi->ps_input_dev->dev, proximity_attr, + ARRAY_SIZE(proximity_attr)); + if (ret < 0) { + dev_err(&client->dev, "failed to create sysfs\n"); + goto err_light_sysfs_cleanup; } - /* register the attributes */ - ret = device_create_file(lpi->ps_dev, &dev_attr_ps_adc); - if (ret) - goto err_create_ps_device_file; - - ret = device_create_file(lpi->ps_dev, - &dev_attr_ps_parameters); - if (ret) - goto err_create_ps_device_file; - - /* register the attributes */ - ret = device_create_file(lpi->ps_dev, &dev_attr_ps_conf); - if (ret) - goto err_create_ps_device_file; - - /* register the attributes */ - ret = device_create_file(lpi->ps_dev, &dev_attr_ps_thd); - if (ret) - goto err_create_ps_device_file; - - ret = device_create_file(lpi->ps_dev, &dev_attr_ps_hw); - if (ret) - goto err_create_ps_device_file; - - ret = device_create_file(lpi->ps_dev, &dev_attr_ps_poll_delay); - if (ret) - goto err_create_ps_device_file; ret = sensors_classdev_register(&client->dev, &sensors_light_cdev); if (ret) - goto err_create_ps_device_file; + goto err_proximity_sysfs_cleanup; ret = sensors_classdev_register(&client->dev, &sensors_proximity_cdev); if (ret) @@ -1734,19 +1661,16 @@ static int cm36283_probe(struct i2c_client *client, return ret; err_create_class_sysfs: sensors_classdev_unregister(&sensors_light_cdev); -err_create_ps_device_file: - device_unregister(lpi->ps_dev); -err_create_ps_device: -err_create_ls_device_file: - device_unregister(lpi->ls_dev); -err_create_ls_device: - class_destroy(lpi->cm36283_class); -err_create_class: - misc_deregister(&psensor_misc); +err_proximity_sysfs_cleanup: + remove_sysfs_interfaces(&lpi->ps_input_dev->dev, proximity_attr, + ARRAY_SIZE(proximity_attr)); +err_light_sysfs_cleanup: + remove_sysfs_interfaces(&lpi->ls_input_dev->dev, light_attr, + ARRAY_SIZE(light_attr)); +err_input_cleanup: input_unregister_device(lpi->ps_input_dev); input_free_device(lpi->ps_input_dev); err_psensor_setup: - misc_deregister(&lightsensor_misc); input_unregister_device(lpi->ls_input_dev); input_free_device(lpi->ls_input_dev); err_lightsensor_setup: diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c index 8120e6306dfa..225805401a01 100644 --- a/drivers/input/misc/kxtj9.c +++ b/drivers/input/misc/kxtj9.c @@ -87,9 +87,13 @@ static struct sensors_classdev sensors_cdev = { .max_range = "19.6", .resolution = "0.01", .sensor_power = "0.2", - .min_delay = 2000, + .min_delay = 2000, /* microsecond */ .fifo_reserved_event_count = 0, .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, /* millisecond */ + .sensors_enable = NULL, + .sensors_poll_delay = NULL, }; static const struct { @@ -121,6 +125,7 @@ struct kxtj9_data { bool power_enabled; struct regulator *vdd; struct regulator *vio; + struct sensors_classdev cdev; }; static int kxtj9_i2c_read(struct kxtj9_data *tj9, u8 addr, u8 *data, int len) @@ -489,6 +494,36 @@ static int kxtj9_setup_input_device(struct kxtj9_data *tj9) return 0; } +static int kxtj9_enable_set(struct sensors_classdev *sensors_cdev, + unsigned int enabled) +{ + struct kxtj9_data *tj9 = container_of(sensors_cdev, + struct kxtj9_data, cdev); + struct input_dev *input_dev = tj9->input_dev; + + mutex_lock(&input_dev->mutex); + + if (enabled == 0) { + disable_irq(tj9->client->irq); + kxtj9_disable(tj9); + tj9->enable = false; + } else if (enabled == 1) { + if (!kxtj9_enable(tj9)) { + enable_irq(tj9->client->irq); + tj9->enable = true; + } + } else { + dev_err(&tj9->client->dev, + "Invalid value of input, input=%d\n", enabled); + mutex_unlock(&input_dev->mutex); + return -EINVAL; + } + + mutex_unlock(&input_dev->mutex); + + return 0; +} + static ssize_t kxtj9_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -504,31 +539,16 @@ static ssize_t kxtj9_enable_store(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct kxtj9_data *tj9 = i2c_get_clientdata(client); - struct input_dev *input_dev = tj9->input_dev; unsigned long data; int error; error = kstrtoul(buf, 10, &data); - if (error) + if (error < 0) return error; - mutex_lock(&input_dev->mutex); - - if (data == 0) { - disable_irq(client->irq); - kxtj9_disable(tj9); - tj9->enable = false; - } else if (data == 1) { - if (!kxtj9_enable(tj9)) { - enable_irq(client->irq); - tj9->enable = true; - } - } else { - dev_err(&tj9->client->dev, - "Invalid value of input, input=%ld\n", data); - } - - mutex_unlock(&input_dev->mutex); + error = kxtj9_enable_set(&tj9->cdev, data); + if (error < 0) + return error; return count; } @@ -545,6 +565,29 @@ static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR|S_IWGRP, * will be responsible for retrieving data from the input node at the desired * interval. */ +static int kxtj9_poll_delay_set(struct sensors_classdev *sensors_cdev, + unsigned int delay_msec) +{ + struct kxtj9_data *tj9 = container_of(sensors_cdev, + struct kxtj9_data, cdev); + struct input_dev *input_dev = tj9->input_dev; + + /* Lock the device to prevent races with open/close (and itself) */ + mutex_lock(&input_dev->mutex); + + if (tj9->enable) + disable_irq(tj9->client->irq); + + tj9->last_poll_interval = max(delay_msec, tj9->pdata.min_interval); + + if (tj9->enable) { + kxtj9_update_odr(tj9, tj9->last_poll_interval); + enable_irq(tj9->client->irq); + } + mutex_unlock(&input_dev->mutex); + + return 0; +} /* Returns currently selected poll interval (in ms) */ static ssize_t kxtj9_get_poll_delay(struct device *dev, @@ -563,7 +606,6 @@ static ssize_t kxtj9_set_poll_delay(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct kxtj9_data *tj9 = i2c_get_clientdata(client); - struct input_dev *input_dev = tj9->input_dev; unsigned int interval; int error; @@ -571,24 +613,9 @@ static ssize_t kxtj9_set_poll_delay(struct device *dev, if (error < 0) return error; - /* Lock the device to prevent races with open/close (and itself) */ - mutex_lock(&input_dev->mutex); - - if (tj9->enable) - disable_irq(client->irq); - - /* - * Set current interval to the greater of the minimum interval or - * the requested interval - */ - tj9->last_poll_interval = max(interval, tj9->pdata.min_interval); - - if (tj9->enable) { - kxtj9_update_odr(tj9, tj9->last_poll_interval); - enable_irq(client->irq); - } - mutex_unlock(&input_dev->mutex); - + error = kxtj9_poll_delay_set(&tj9->cdev, interval); + if (error < 0) + return error; return count; } @@ -605,7 +632,6 @@ static struct attribute_group kxtj9_attribute_group = { .attrs = kxtj9_attributes }; - #ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE static void kxtj9_poll(struct input_polled_dev *dev) { @@ -861,6 +887,18 @@ static int kxtj9_probe(struct i2c_client *client, tj9->ctrl_reg1 = tj9->pdata.res_ctl | tj9->pdata.g_range; tj9->last_poll_interval = tj9->pdata.init_interval; + tj9->cdev = sensors_cdev; + /* The min_delay is used by userspace and the unit is microsecond. */ + tj9->cdev.min_delay = tj9->pdata.min_interval * 1000; + tj9->cdev.delay_msec = tj9->pdata.init_interval; + tj9->cdev.sensors_enable = kxtj9_enable_set; + tj9->cdev.sensors_poll_delay = kxtj9_poll_delay_set; + err = sensors_classdev_register(&client->dev, &tj9->cdev); + if (err) { + dev_err(&client->dev, "class device create failed: %d\n", err); + goto err_power_off; + } + if (client->irq) { /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */ tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL; @@ -892,11 +930,6 @@ static int kxtj9_probe(struct i2c_client *client, goto err_power_off; } - err = sensors_classdev_register(&client->dev, &sensors_cdev); - if (err) { - dev_err(&client->dev, "class device create failed: %d\n", err); - goto err_free_irq; - } dev_dbg(&client->dev, "%s: kxtj9_probe OK.\n", __func__); kxtj9_device_power_off(tj9); diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c index de9ddc86d0f6..2a14a28d98a2 100644 --- a/drivers/input/misc/mpu3050.c +++ b/drivers/input/misc/mpu3050.c @@ -122,6 +122,7 @@ struct mpu3050_sensor { struct input_dev *idev; struct mpu3050_gyro_platform_data *platform_data; struct delayed_work input_work; + struct sensors_classdev cdev; u32 use_poll; u32 poll_interval; u32 dlpf_index; @@ -141,6 +142,10 @@ static struct sensors_classdev sensors_cdev = { .min_delay = 2000, .fifo_reserved_event_count = 0, .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = MPU3050_DEFAULT_POLL_INTERVAL, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, }; struct sensor_regulator { @@ -258,6 +263,40 @@ error_vdd: return rc; } +static int mpu3050_poll_delay_set(struct sensors_classdev *sensors_cdev, + unsigned int delay_msec) +{ + struct mpu3050_sensor *sensor = container_of(sensors_cdev, + struct mpu3050_sensor, cdev); + unsigned int dlpf_index; + u8 divider, reg; + int ret; + + dlpf_index = interval_to_dlpf_cfg(delay_msec); + divider = delay_msec * dlpf_table[dlpf_index].sample_rate - 1; + + if (sensor->dlpf_index != dlpf_index) { + /* Set low pass filter and full scale */ + reg = dlpf_table[dlpf_index].cfg; + reg |= MPU3050_DEFAULT_FS_RANGE << 3; + reg |= MPU3050_EXT_SYNC_NONE << 5; + ret = i2c_smbus_write_byte_data(sensor->client, + MPU3050_DLPF_FS_SYNC, reg); + if (!ret) + sensor->dlpf_index = dlpf_index; + } + + if (sensor->poll_interval != delay_msec) { + /* Output frequency divider. The poll interval */ + ret = i2c_smbus_write_byte_data(sensor->client, + MPU3050_SMPLRT_DIV, divider); + if (!ret) + sensor->poll_interval = delay_msec; + } + + return 0; +} + /** * mpu3050_attr_get_polling_rate - get the sampling rate */ @@ -280,8 +319,6 @@ static ssize_t mpu3050_attr_set_polling_rate(struct device *dev, { struct mpu3050_sensor *sensor = dev_get_drvdata(dev); unsigned long interval_ms; - unsigned int dlpf_index; - u8 divider, reg; int ret; if (kstrtoul(buf, 10, &interval_ms)) @@ -290,29 +327,39 @@ static ssize_t mpu3050_attr_set_polling_rate(struct device *dev, (interval_ms > MPU3050_MAX_POLL_INTERVAL)) return -EINVAL; - dlpf_index = interval_to_dlpf_cfg(interval_ms); - divider = interval_ms * dlpf_table[dlpf_index].sample_rate - 1; + ret = mpu3050_poll_delay_set(&sensor->cdev, interval_ms); - if (sensor->dlpf_index != dlpf_index) { - /* Set low pass filter and full scale */ - reg = dlpf_table[dlpf_index].cfg; - reg |= MPU3050_DEFAULT_FS_RANGE << 3; - reg |= MPU3050_EXT_SYNC_NONE << 5; - ret = i2c_smbus_write_byte_data(sensor->client, - MPU3050_DLPF_FS_SYNC, reg); - if (ret == 0) - sensor->dlpf_index = dlpf_index; - } + return ret < 0 ? ret : size; +} +static int mpu3050_enable_set(struct sensors_classdev *sensors_cdev, + unsigned int enabled) +{ + struct mpu3050_sensor *sensor = container_of(sensors_cdev, + struct mpu3050_sensor, cdev); - if (sensor->poll_interval != interval_ms) { - /* Output frequency divider. The poll interval */ - ret = i2c_smbus_write_byte_data(sensor->client, - MPU3050_SMPLRT_DIV, divider); - if (ret == 0) - sensor->poll_interval = interval_ms; + + if (enabled && (!sensor->enable)) { + sensor->enable = enabled; + pm_runtime_get_sync(sensor->dev); + if (sensor->use_poll) + schedule_delayed_work(&sensor->input_work, + msecs_to_jiffies(sensor->poll_interval)); + else + enable_irq(sensor->client->irq); + } else if (!enabled && sensor->enable) { + if (sensor->use_poll) + cancel_delayed_work_sync(&sensor->input_work); + else + disable_irq(sensor->client->irq); + pm_runtime_put_sync(sensor->dev); + sensor->enable = enabled; + } else { + dev_warn(&sensor->client->dev, + "ignore enable state change from %d to %d\n", + sensor->enable, enabled); } - return size; + return 0; } /** @@ -325,32 +372,14 @@ static ssize_t mpu3050_attr_set_enable(struct device *dev, { struct mpu3050_sensor *sensor = dev_get_drvdata(dev); unsigned long val; + int err; if (kstrtoul(buf, 10, &val)) return -EINVAL; - sensor->enable = (u32)val == 0 ? 0 : 1; - if (sensor->enable) { - pm_runtime_get_sync(sensor->dev); - gpio_set_value(sensor->enable_gpio, 1); - if (sensor->use_poll) - schedule_delayed_work(&sensor->input_work, - msecs_to_jiffies(sensor->poll_interval)); - else { - i2c_smbus_write_byte_data(sensor->client, - MPU3050_INT_CFG, - MPU3050_ACTIVE_LOW | - MPU3050_OPEN_DRAIN | - MPU3050_RAW_RDY_EN); - enable_irq(sensor->client->irq); - } - } else { - if (sensor->use_poll) - cancel_delayed_work_sync(&sensor->input_work); - else - disable_irq(sensor->client->irq); - gpio_set_value(sensor->enable_gpio, 0); - pm_runtime_put(sensor->dev); - } + err = mpu3050_enable_set(&sensor->cdev, val); + if (err < 0) + return err; + return count; } @@ -546,12 +575,6 @@ static int mpu3050_hw_init(struct mpu3050_sensor *sensor) int ret; u8 reg; - /* Reset */ - ret = i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM, - MPU3050_PWR_MGM_RESET); - if (ret < 0) - return ret; - ret = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM); if (ret < 0) return ret; @@ -576,6 +599,16 @@ static int mpu3050_hw_init(struct mpu3050_sensor *sensor) if (ret < 0) return ret; + /* Enable interrupts */ + if (!sensor->use_poll) { + reg = MPU3050_ACTIVE_LOW; + reg |= MPU3050_OPEN_DRAIN; + reg |= MPU3050_RAW_RDY_EN; + ret = i2c_smbus_write_byte_data(client, MPU3050_INT_CFG, reg); + if (ret < 0) + return ret; + } + return 0; } #ifdef CONFIG_OF @@ -675,6 +708,19 @@ static int mpu3050_probe(struct i2c_client *client, sensor->enable_gpio = -EINVAL; } + sensor->cdev = sensors_cdev; + sensor->cdev.min_delay = MPU3050_MIN_POLL_INTERVAL; + sensor->cdev.delay_msec = sensor->poll_interval; + sensor->cdev.sensors_enable = mpu3050_enable_set; + sensor->cdev.sensors_poll_delay = mpu3050_poll_delay_set; + ret = sensors_classdev_register(&client->dev, &sensor->cdev); + + if (ret) { + dev_err(&client->dev, "class device create failed: %d\n", ret); + error = -EINVAL; + goto err_free_mem; + } + if (gpio_is_valid(sensor->enable_gpio)) { ret = gpio_request(sensor->enable_gpio, "GYRO_EN_PM"); gpio_direction_output(sensor->enable_gpio, 1); @@ -686,7 +732,7 @@ static int mpu3050_probe(struct i2c_client *client, if (ret < 0) { dev_err(&client->dev, "failed to detect device\n"); error = -ENXIO; - goto err_free_mem; + goto err_class_sysfs; } for (i = 0; i < ARRAY_SIZE(mpu3050_chip_ids); i++) @@ -696,7 +742,7 @@ static int mpu3050_probe(struct i2c_client *client, if (i == ARRAY_SIZE(mpu3050_chip_ids)) { dev_err(&client->dev, "unsupported chip id\n"); error = -ENXIO; - goto err_free_mem; + goto err_class_sysfs; } idev->name = "MPU3050"; @@ -762,22 +808,19 @@ static int mpu3050_probe(struct i2c_client *client, disable_irq(client->irq); } + sensor->enable = 0; + mpu3050_set_power_mode(client, 0); + error = input_register_device(idev); if (error) { dev_err(&client->dev, "failed to register input device\n"); goto err_free_irq; } - error = sensors_classdev_register(&client->dev, &sensors_cdev); - if (error < 0) { - dev_err(&client->dev, "failed to create class device\n"); - goto err_input_cleanup; - } - error = create_sysfs_interfaces(&idev->dev); if (error < 0) { dev_err(&client->dev, "failed to create sysfs\n"); - goto err_class_sysfs; + goto err_input_cleanup; } pm_runtime_enable(&client->dev); @@ -785,8 +828,6 @@ static int mpu3050_probe(struct i2c_client *client, return 0; -err_class_sysfs: - sensors_classdev_unregister(&sensors_cdev); err_input_cleanup: input_unregister_device(idev); err_free_irq: @@ -798,6 +839,8 @@ err_free_gpio: gpio_free(sensor->platform_data->gpio_int); err_pm_set_suspended: pm_runtime_set_suspended(&client->dev); +err_class_sysfs: + sensors_classdev_unregister(&sensor->cdev); err_free_mem: input_free_device(idev); kfree(sensor); @@ -842,10 +885,11 @@ static int mpu3050_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct mpu3050_sensor *sensor = i2c_get_clientdata(client); - if (!sensor->use_poll) - disable_irq(client->irq); - - mpu3050_set_power_mode(client, 0); + if (sensor->enable) { + if (!sensor->use_poll) + disable_irq(client->irq); + mpu3050_set_power_mode(client, 0); + } return 0; } @@ -861,16 +905,64 @@ static int mpu3050_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct mpu3050_sensor *sensor = i2c_get_clientdata(client); - mpu3050_set_power_mode(client, 1); + if (sensor->enable) { + mpu3050_set_power_mode(client, 1); + mpu3050_hw_init(sensor); + if (!sensor->use_poll) + enable_irq(client->irq); + } + + return 0; +} + +/** + * mpu3050_runtime_suspend - called on device enters runtime suspend + * @dev: device being suspended + * + * Put the device into sleep mode. + */ +static int mpu3050_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mpu3050_sensor *sensor = i2c_get_clientdata(client); - if (!sensor->use_poll) - enable_irq(client->irq); + if (sensor->enable) + mpu3050_set_power_mode(client, 0); + + return 0; +} + +/** + * mpu3050_runtime_resume - called on device enters runtime resume + * @dev: device being resumed + * + * Put the device into powered mode. + */ +static int mpu3050_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mpu3050_sensor *sensor = i2c_get_clientdata(client); + + if (sensor->enable) { + mpu3050_set_power_mode(client, 1); + mpu3050_hw_init(sensor); + } return 0; } #endif -static UNIVERSAL_DEV_PM_OPS(mpu3050_pm, mpu3050_suspend, mpu3050_resume, NULL); +static const struct dev_pm_ops mpu3050_pm = { + .runtime_suspend = mpu3050_runtime_suspend, + .runtime_resume = mpu3050_runtime_resume, + .runtime_idle = NULL, + .suspend = mpu3050_suspend, + .resume = mpu3050_resume, + .freeze = mpu3050_suspend, + .thaw = mpu3050_resume, + .poweroff = mpu3050_suspend, + .restore = mpu3050_resume, +}; static const struct i2c_device_id mpu3050_ids[] = { { "mpu3050", 0 }, diff --git a/drivers/input/misc/stk3x1x.c b/drivers/input/misc/stk3x1x.c index 8fde72d773ae..8750858a39a2 100644 --- a/drivers/input/misc/stk3x1x.c +++ b/drivers/input/misc/stk3x1x.c @@ -64,8 +64,6 @@ #define STK_POLL_PS #define STK_POLL_ALS /* ALS interrupt is valid only when STK_PS_INT_MODE = 1 or 4*/ -#define STK_DEBUG_PRINTF - /* Define Register Map */ #define STK_STATE_REG 0x00 #define STK_PSCTRL_REG 0x01 @@ -192,9 +190,13 @@ static struct sensors_classdev sensors_light_cdev = { .max_range = "6500", .resolution = "0.0625", .sensor_power = "0.09", - .min_delay = 0, + .min_delay = (MIN_ALS_POLL_DELAY_NS / 1000), /* us */ .fifo_reserved_event_count = 0, .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, }; static struct sensors_classdev sensors_proximity_cdev = { @@ -209,6 +211,10 @@ static struct sensors_classdev sensors_proximity_cdev = { .min_delay = 0, .fifo_reserved_event_count = 0, .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, }; struct data_filter { @@ -221,6 +227,8 @@ struct data_filter { struct stk3x1x_data { struct i2c_client *client; struct stk3x1x_platform_data *pdata; + struct sensors_classdev als_cdev; + struct sensors_classdev ps_cdev; #if (!defined(STK_POLL_PS) || !defined(STK_POLL_ALS)) int32_t irq; struct work_struct stk_work; @@ -238,7 +246,7 @@ struct stk3x1x_data { int32_t ps_distance_last; bool ps_enabled; struct wake_lock ps_wakelock; - struct work_struct stk_ps_work; + struct work_struct stk_ps_work; struct workqueue_struct *stk_ps_wq; #ifdef STK_POLL_PS struct wake_lock ps_nosuspend_wl; @@ -321,11 +329,11 @@ static void stk_init_code_threshold_table(struct stk3x1x_data *ps_data) for (i=1,j=0;i<LUX_THD_TABLE_SIZE;i++,j++) { alscode = stk_lux2alscode(ps_data, lux_threshold_table[j]); - printk(KERN_INFO "alscode[%d]=%d\n",i,alscode); + dev_dbg(&ps_data->client->dev, "alscode[%d]=%d\n", i, alscode); code_threshold_table[i] = (uint16_t)(alscode); } code_threshold_table[i] = 0xffff; - printk(KERN_INFO "alscode[%d]=%d\n",i,alscode); + dev_dbg(&ps_data->client->dev, "alscode[%d]=%d\n", i, alscode); } static uint32_t stk_get_lux_interval_index(uint16_t alscode) @@ -458,7 +466,6 @@ static int32_t stk3x1x_check_pid(struct stk3x1x_data *ps_data) printk(KERN_ERR "%s: read i2c error, err=%d\n", __func__, err2); return -1; } - printk(KERN_INFO "%s: PID=0x%x, RID=0x%x\n", __func__, err1, err2); if(err2 == 0xC0) printk(KERN_INFO "%s: RID=0xC0!!!!!!!!!!!!!\n", __func__); @@ -685,7 +692,9 @@ static int32_t stk3x1x_enable_ps(struct stk3x1x_data *ps_data, uint8_t enable) input_sync(ps_data->ps_input_dev); wake_lock_timeout(&ps_data->ps_wakelock, 3*HZ); reading = stk3x1x_get_ps_reading(ps_data); - printk(KERN_INFO "%s: ps input event=%d, ps code = %d\n",__func__, near_far_state, reading); + dev_dbg(&ps_data->client->dev, + "%s: ps input event=%d, ps code = %d\n", + __func__, near_far_state, reading); #endif /* #ifndef STK_POLL_PS */ } else @@ -904,6 +913,21 @@ static ssize_t stk_als_code_show(struct device *dev, struct device_attribute *at return scnprintf(buf, PAGE_SIZE, "%d\n", reading); } +static ssize_t stk_als_enable_set(struct sensors_classdev *sensors_cdev, + unsigned int enabled) +{ + struct stk3x1x_data *als_data = container_of(sensors_cdev, + struct stk3x1x_data, als_cdev); + int err; + + mutex_lock(&als_data->io_lock); + err = stk3x1x_enable_als(als_data, enabled); + mutex_unlock(&als_data->io_lock); + + if (err < 0) + return err; + return 0; +} static ssize_t stk_als_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -935,7 +959,7 @@ static ssize_t stk_als_enable_store(struct device *dev, struct device_attribute printk(KERN_ERR "%s, invalid value %d\n", __func__, *buf); return -EINVAL; } - printk(KERN_INFO "%s: Enable ALS : %d\n", __func__, en); + dev_dbg(dev, "%s: Enable ALS : %d\n", __func__, en); mutex_lock(&ps_data->io_lock); stk3x1x_enable_als(ps_data, en); mutex_unlock(&ps_data->io_lock); @@ -971,7 +995,7 @@ static ssize_t stk_als_lux_store(struct device *dev, struct device_attribute *at input_report_abs(ps_data->als_input_dev, ABS_MISC, value); input_sync(ps_data->als_input_dev); mutex_unlock(&ps_data->io_lock); - printk(KERN_INFO "%s: als input event %ld lux\n",__func__, value); + dev_dbg(dev, "%s: als input event %ld lux\n", __func__, value); return size; } @@ -1009,7 +1033,8 @@ static ssize_t stk_als_transmittance_store(struct device *dev, struct device_att static ssize_t stk_als_delay_show(struct device *dev, struct device_attribute *attr, char *buf) { struct stk3x1x_data *ps_data = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%lld\n", ktime_to_ns(ps_data->als_poll_delay)); + return scnprintf(buf, PAGE_SIZE, "%u\n", + (u32)ktime_to_ms(ps_data->als_poll_delay)); } static inline void stk_als_delay_store_fir(struct stk3x1x_data *ps_data) @@ -1018,34 +1043,48 @@ static inline void stk_als_delay_store_fir(struct stk3x1x_data *ps_data) ps_data->fir.idx = 0; ps_data->fir.sum = 0; } + +static ssize_t stk_als_poll_delay_set(struct sensors_classdev *sensors_cdev, + unsigned int delay_msec) +{ + struct stk3x1x_data *als_data = container_of(sensors_cdev, + struct stk3x1x_data, als_cdev); + uint64_t value = 0; + + value = delay_msec * 1000000; + + if (value < MIN_ALS_POLL_DELAY_NS) + value = MIN_ALS_POLL_DELAY_NS; + + mutex_lock(&als_data->io_lock); + if (value != ktime_to_ns(als_data->als_poll_delay)) + als_data->als_poll_delay = ns_to_ktime(value); + + if (als_data->use_fir) + stk_als_delay_store_fir(als_data); + + mutex_unlock(&als_data->io_lock); + + return 0; +} + static ssize_t stk_als_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { uint64_t value = 0; int ret; - struct stk3x1x_data *ps_data = dev_get_drvdata(dev); + struct stk3x1x_data *als_data = dev_get_drvdata(dev); ret = kstrtoull(buf, 10, &value); if(ret < 0) { - printk(KERN_ERR "%s:kstrtoull failed, ret=0x%x\n", - __func__, ret); + dev_err(dev, "%s:kstrtoull failed, ret=0x%x\n", __func__, ret); return ret; } #ifdef STK_DEBUG_PRINTF - printk(KERN_INFO "%s: set als poll delay=%lld\n", __func__, value); + dev_dbg(dev, "%s: set als poll delay=%lld\n", __func__, value); #endif - if(value < MIN_ALS_POLL_DELAY_NS) - { - printk(KERN_ERR "%s: delay is too small\n", __func__); - value = MIN_ALS_POLL_DELAY_NS; - } - mutex_lock(&ps_data->io_lock); - if(value != ktime_to_ns(ps_data->als_poll_delay)) - ps_data->als_poll_delay = ns_to_ktime(value); - - if (ps_data->use_fir) - stk_als_delay_store_fir(ps_data); - - mutex_unlock(&ps_data->io_lock); + ret = stk_als_poll_delay_set(&als_data->als_cdev, value); + if (ret < 0) + return ret; return size; } @@ -1136,6 +1175,22 @@ static ssize_t stk_ps_code_show(struct device *dev, struct device_attribute *att return scnprintf(buf, PAGE_SIZE, "%d\n", reading); } +static ssize_t stk_ps_enable_set(struct sensors_classdev *sensors_cdev, + unsigned int enabled) +{ + struct stk3x1x_data *ps_data = container_of(sensors_cdev, + struct stk3x1x_data, ps_cdev); + int err; + + mutex_lock(&ps_data->io_lock); + err = stk3x1x_enable_ps(ps_data, enabled); + mutex_unlock(&ps_data->io_lock); + + if (err < 0) + return err; + return 0; +} + static ssize_t stk_ps_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { int32_t enable, ret; @@ -1166,7 +1221,7 @@ static ssize_t stk_ps_enable_store(struct device *dev, struct device_attribute * printk(KERN_ERR "%s, invalid value %d\n", __func__, *buf); return -EINVAL; } - printk(KERN_INFO "%s: Enable PS : %d\n", __func__, en); + dev_dbg(dev, "%s: Enable PS : %d\n", __func__, en); mutex_lock(&ps_data->io_lock); stk3x1x_enable_ps(ps_data, en); mutex_unlock(&ps_data->io_lock); @@ -1200,7 +1255,7 @@ static ssize_t stk_ps_enable_aso_store(struct device *dev, struct device_attribu printk(KERN_ERR "%s, invalid value %d\n", __func__, *buf); return -EINVAL; } - printk(KERN_INFO "%s: Enable PS ASO : %d\n", __func__, en); + dev_dbg(dev, "%s: Enable PS ASO : %d\n", __func__, en); ret = i2c_smbus_read_byte_data(ps_data->client, STK_STATE_REG); if (ret < 0) @@ -1288,7 +1343,7 @@ static ssize_t stk_ps_distance_show(struct device *dev, struct device_attribute input_sync(ps_data->ps_input_dev); mutex_unlock(&ps_data->io_lock); wake_lock_timeout(&ps_data->ps_wakelock, 3*HZ); - printk(KERN_INFO "%s: ps input event %d cm\n",__func__, dist); + dev_dbg(dev, "%s: ps input event %d cm\n", __func__, dist); return scnprintf(buf, PAGE_SIZE, "%d\n", dist); } @@ -1311,7 +1366,7 @@ static ssize_t stk_ps_distance_store(struct device *dev, struct device_attribute input_sync(ps_data->ps_input_dev); mutex_unlock(&ps_data->io_lock); wake_lock_timeout(&ps_data->ps_wakelock, 3*HZ); - printk(KERN_INFO "%s: ps input event %ld cm\n",__func__, value); + dev_dbg(dev, "%s: ps input event %ld cm\n", __func__, value); return size; } @@ -1507,7 +1562,7 @@ static ssize_t stk_all_reg_show(struct device *dev, struct device_attribute *att } else { - printk(KERN_INFO "reg[0x%2X]=0x%2X\n", cnt, ps_reg[cnt]); + dev_dbg(dev, "reg[0x%2X]=0x%2X\n", cnt, ps_reg[cnt]); } } ps_reg[cnt] = i2c_smbus_read_byte_data(ps_data->client, STK_PDT_ID_REG); @@ -1517,7 +1572,7 @@ static ssize_t stk_all_reg_show(struct device *dev, struct device_attribute *att printk( KERN_ERR "all_reg_show:i2c_smbus_read_byte_data fail, ret=%d", ps_reg[cnt]); return -EINVAL; } - printk( KERN_INFO "reg[0x%x]=0x%2X\n", STK_PDT_ID_REG, ps_reg[cnt]); + dev_dbg(dev, "reg[0x%x]=0x%2X\n", STK_PDT_ID_REG, ps_reg[cnt]); cnt++; ps_reg[cnt] = i2c_smbus_read_byte_data(ps_data->client, STK_RSRVD_REG); if(ps_reg[cnt] < 0) @@ -1526,7 +1581,7 @@ static ssize_t stk_all_reg_show(struct device *dev, struct device_attribute *att printk( KERN_ERR "all_reg_show:i2c_smbus_read_byte_data fail, ret=%d", ps_reg[cnt]); return -EINVAL; } - printk( KERN_INFO "reg[0x%x]=0x%2X\n", STK_RSRVD_REG, ps_reg[cnt]); + dev_dbg(dev, "reg[0x%x]=0x%2X\n", STK_RSRVD_REG, ps_reg[cnt]); mutex_unlock(&ps_data->io_lock); return scnprintf(buf, PAGE_SIZE, "%2X %2X %2X %2X %2X,%2X %2X %2X %2X %2X,%2X %2X %2X %2X %2X,%2X %2X %2X %2X %2X,%2X %2X %2X %2X %2X,%2X %2X\n", @@ -1589,8 +1644,7 @@ static ssize_t stk_send_store(struct device *dev, struct device_attribute *attr, __func__, ret); return ret; } - printk(KERN_INFO "%s: write reg 0x%x=0x%x\n", __func__, addr, cmd); - + dev_dbg(dev, "%s: write reg 0x%x=0x%x\n", __func__, addr, cmd); addr_u8 = (u8) addr; cmd_u8 = (u8) cmd; //mutex_lock(&ps_data->io_lock); @@ -1688,7 +1742,6 @@ static void stk_als_work_func(struct work_struct *work) input_report_abs(ps_data->als_input_dev, ABS_MISC, ps_data->als_lux_last); input_sync(ps_data->als_input_dev); mutex_unlock(&ps_data->io_lock); - //printk(KERN_INFO "%s: als input event %d lux\n",__func__, ps_data->als_lux_last); } #endif @@ -1948,7 +2001,6 @@ static void stk3x1x_early_suspend(struct early_suspend *h) int err; #endif - printk(KERN_INFO "%s", __func__); mutex_lock(&ps_data->io_lock); if(ps_data->als_enabled) { @@ -1976,7 +2028,6 @@ static void stk3x1x_late_resume(struct early_suspend *h) int err; #endif - printk(KERN_INFO "%s", __func__); mutex_lock(&ps_data->io_lock); if(ps_data->als_enabled) stk3x1x_enable_als(ps_data, 1); @@ -2399,10 +2450,16 @@ static int stk3x1x_probe(struct i2c_client *client, register_early_suspend(&ps_data->stk_early_suspend); #endif /* make sure everything is ok before registering the class device */ - err = sensors_classdev_register(&client->dev, &sensors_light_cdev); + ps_data->als_cdev = sensors_light_cdev; + ps_data->als_cdev.sensors_enable = stk_als_enable_set; + ps_data->als_cdev.sensors_poll_delay = stk_als_poll_delay_set; + err = sensors_classdev_register(&client->dev, &ps_data->als_cdev); if (err) goto err_power_on; - err = sensors_classdev_register(&client->dev, &sensors_proximity_cdev); + + ps_data->ps_cdev = sensors_proximity_cdev; + ps_data->ps_cdev.sensors_enable = stk_ps_enable_set; + err = sensors_classdev_register(&client->dev, &ps_data->ps_cdev); if (err) goto err_class_sysfs; @@ -2416,9 +2473,9 @@ static int stk3x1x_probe(struct i2c_client *client, err_init_all_setting: stk3x1x_power_ctl(ps_data, false); - sensors_classdev_unregister(&sensors_proximity_cdev); + sensors_classdev_unregister(&ps_data->ps_cdev); err_class_sysfs: - sensors_classdev_unregister(&sensors_light_cdev); + sensors_classdev_unregister(&ps_data->als_cdev); err_power_on: stk3x1x_power_init(ps_data, false); err_power_init: diff --git a/drivers/input/touchscreen/atmel_maxtouch_ts.c b/drivers/input/touchscreen/atmel_maxtouch_ts.c new file mode 100755 index 000000000000..f2f9fd047c79 --- /dev/null +++ b/drivers/input/touchscreen/atmel_maxtouch_ts.c @@ -0,0 +1,3286 @@ +/* + * Atmel maXTouch Touchscreen driver + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Copyright (C) 2011-2012 Atmel Corporation + * Copyright (C) 2012 Google, Inc. + * + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/i2c/atmel_mxt_ts.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/regulator/consumer.h> +#include <linux/gpio.h> + +/* Configuration file */ +#define MXT_CFG_MAGIC "OBP_RAW V1" + +/* Registers */ +#define MXT_OBJECT_START 0x07 +#define MXT_OBJECT_SIZE 6 +#define MXT_INFO_CHECKSUM_SIZE 3 +#define MXT_MAX_BLOCK_WRITE 256 + +/* Object types */ +#define MXT_DEBUG_DIAGNOSTIC_T37 37 +#define MXT_GEN_MESSAGE_T5 5 +#define MXT_GEN_COMMAND_T6 6 +#define MXT_GEN_POWER_T7 7 +#define MXT_GEN_ACQUIRE_T8 8 +#define MXT_GEN_DATASOURCE_T53 53 +#define MXT_TOUCH_MULTI_T9 9 +#define MXT_TOUCH_KEYARRAY_T15 15 +#define MXT_TOUCH_PROXIMITY_T23 23 +#define MXT_TOUCH_PROXKEY_T52 52 +#define MXT_PROCI_GRIPFACE_T20 20 +#define MXT_PROCG_NOISE_T22 22 +#define MXT_PROCI_ONETOUCH_T24 24 +#define MXT_PROCI_TWOTOUCH_T27 27 +#define MXT_PROCI_GRIP_T40 40 +#define MXT_PROCI_PALM_T41 41 +#define MXT_PROCI_TOUCHSUPPRESSION_T42 42 +#define MXT_PROCI_STYLUS_T47 47 +#define MXT_PROCG_NOISESUPPRESSION_T48 48 +#define MXT_SPT_COMMSCONFIG_T18 18 +#define MXT_SPT_GPIOPWM_T19 19 +#define MXT_SPT_SELFTEST_T25 25 +#define MXT_SPT_CTECONFIG_T28 28 +#define MXT_SPT_USERDATA_T38 38 +#define MXT_SPT_DIGITIZER_T43 43 +#define MXT_SPT_MESSAGECOUNT_T44 44 +#define MXT_SPT_CTECONFIG_T46 46 +#define MXT_PROCI_ACTIVE_STYLUS_T63 63 +#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100 + +/* MXT_GEN_MESSAGE_T5 object */ +#define MXT_RPTID_NOMSG 0xff + +/* MXT_GEN_COMMAND_T6 field */ +#define MXT_COMMAND_RESET 0 +#define MXT_COMMAND_BACKUPNV 1 +#define MXT_COMMAND_CALIBRATE 2 +#define MXT_COMMAND_REPORTALL 3 +#define MXT_COMMAND_DIAGNOSTIC 5 + +/* Define for T6 status byte */ +#define MXT_T6_STATUS_RESET (1 << 7) +#define MXT_T6_STATUS_OFL (1 << 6) +#define MXT_T6_STATUS_SIGERR (1 << 5) +#define MXT_T6_STATUS_CAL (1 << 4) +#define MXT_T6_STATUS_CFGERR (1 << 3) +#define MXT_T6_STATUS_COMSERR (1 << 2) + +/* MXT_GEN_POWER_T7 field */ +struct t7_config { + u8 idle; + u8 active; +} __packed; + +#define MXT_POWER_CFG_RUN 0 +#define MXT_POWER_CFG_DEEPSLEEP 1 + +/* MXT_TOUCH_MULTI_T9 field */ +#define MXT_T9_ORIENT 9 +#define MXT_T9_RANGE 18 + +/* MXT_TOUCH_MULTI_T9 status */ +#define MXT_T9_UNGRIP (1 << 0) +#define MXT_T9_SUPPRESS (1 << 1) +#define MXT_T9_AMP (1 << 2) +#define MXT_T9_VECTOR (1 << 3) +#define MXT_T9_MOVE (1 << 4) +#define MXT_T9_RELEASE (1 << 5) +#define MXT_T9_PRESS (1 << 6) +#define MXT_T9_DETECT (1 << 7) + +struct t9_range { + u16 x; + u16 y; +} __packed; + +/* MXT_TOUCH_MULTI_T9 orient */ +#define MXT_T9_ORIENT_SWITCH (1 << 0) + +/* MXT_SPT_COMMSCONFIG_T18 */ +#define MXT_COMMS_CTRL 0 +#define MXT_COMMS_CMD 1 +#define MXT_COMMS_RETRIGEN (1 << 6) + +/* Define for MXT_GEN_COMMAND_T6 */ +#define MXT_BOOT_VALUE 0xa5 +#define MXT_RESET_VALUE 0x01 +#define MXT_BACKUP_VALUE 0x55 + +/* Define for MXT_PROCI_TOUCHSUPPRESSION_T42 */ +#define MXT_T42_MSG_TCHSUP (1 << 0) + +/* T47 Stylus */ +#define MXT_TOUCH_MAJOR_T47_STYLUS 1 + +/* T63 Stylus */ +#define MXT_T63_STYLUS_PRESS (1 << 0) +#define MXT_T63_STYLUS_RELEASE (1 << 1) +#define MXT_T63_STYLUS_MOVE (1 << 2) +#define MXT_T63_STYLUS_SUPPRESS (1 << 3) + +#define MXT_T63_STYLUS_DETECT (1 << 4) +#define MXT_T63_STYLUS_TIP (1 << 5) +#define MXT_T63_STYLUS_ERASER (1 << 6) +#define MXT_T63_STYLUS_BARREL (1 << 7) + +#define MXT_T63_STYLUS_PRESSURE_MASK 0x3F + +/* T100 Multiple Touch Touchscreen */ +#define MXT_T100_CTRL 0 +#define MXT_T100_CFG1 1 +#define MXT_T100_TCHAUX 3 +#define MXT_T100_XRANGE 13 +#define MXT_T100_YRANGE 24 + +#define MXT_T100_CFG_SWITCHXY (1 << 5) + +#define MXT_T100_TCHAUX_VECT (1 << 0) +#define MXT_T100_TCHAUX_AMPL (1 << 1) +#define MXT_T100_TCHAUX_AREA (1 << 2) + +#define MXT_T100_DETECT (1 << 7) +#define MXT_T100_TYPE_MASK 0x70 +#define MXT_T100_TYPE_STYLUS 0x20 + +/* Delay times */ +#define MXT_BACKUP_TIME 50 /* msec */ +#define MXT_RESET_TIME 200 /* msec */ +#define MXT_RESET_TIMEOUT 3000 /* msec */ +#define MXT_CRC_TIMEOUT 1000 /* msec */ +#define MXT_FW_RESET_TIME 3000 /* msec */ +#define MXT_FW_CHG_TIMEOUT 300 /* msec */ +#define MXT_WAKEUP_TIME 25 /* msec */ +#define MXT_REGULATOR_DELAY 150 /* msec */ +#define MXT_POWERON_DELAY 150 /* msec */ + +/* Command to unlock bootloader */ +#define MXT_UNLOCK_CMD_MSB 0xaa +#define MXT_UNLOCK_CMD_LSB 0xdc + +/* Bootloader mode status */ +#define MXT_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */ +#define MXT_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */ +#define MXT_FRAME_CRC_CHECK 0x02 +#define MXT_FRAME_CRC_FAIL 0x03 +#define MXT_FRAME_CRC_PASS 0x04 +#define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ +#define MXT_BOOT_STATUS_MASK 0x3f +#define MXT_BOOT_EXTENDED_ID (1 << 5) +#define MXT_BOOT_ID_MASK 0x1f + +/* Touchscreen absolute values */ +#define MXT_MAX_AREA 0xff + +#define MXT_PIXELS_PER_MM 20 + +#define DEBUG_MSG_MAX 200 + +struct mxt_info { + u8 family_id; + u8 variant_id; + u8 version; + u8 build; + u8 matrix_xsize; + u8 matrix_ysize; + u8 object_num; +}; + +struct mxt_object { + u8 type; + u16 start_address; + u8 size_minus_one; + u8 instances_minus_one; + u8 num_report_ids; +} __packed; + +/* Each client has this additional data */ +struct mxt_data { + struct i2c_client *client; + struct input_dev *input_dev; + char phys[64]; /* device physical location */ + struct mxt_platform_data *pdata; + struct mxt_object *object_table; + struct mxt_info *info; + void *raw_info_block; + unsigned int irq; + unsigned int max_x; + unsigned int max_y; + bool in_bootloader; + u16 mem_size; + u8 t100_aux_ampl; + u8 t100_aux_area; + u8 t100_aux_vect; + struct bin_attribute mem_access_attr; + bool debug_enabled; + bool debug_v2_enabled; + u8 *debug_msg_data; + u16 debug_msg_count; + struct bin_attribute debug_msg_attr; + struct mutex debug_msg_lock; + u8 max_reportid; + u32 config_crc; + u32 info_crc; + u8 bootloader_addr; + struct t7_config t7_cfg; + u8 *msg_buf; + u8 t6_status; + bool update_input; + u8 last_message_count; + u8 num_touchids; + u8 num_stylusids; + unsigned long t15_keystatus; + bool use_retrigen_workaround; + bool use_regulator; + struct regulator *reg_vdd; + struct regulator *reg_avdd; + char *fw_name; + char *cfg_name; + + /* Cached parameters from object table */ + u16 T5_address; + u8 T5_msg_size; + u8 T6_reportid; + u16 T6_address; + u16 T7_address; + u8 T9_reportid_min; + u8 T9_reportid_max; + u8 T15_reportid_min; + u8 T15_reportid_max; + u16 T18_address; + u8 T19_reportid; + u8 T42_reportid_min; + u8 T42_reportid_max; + u16 T44_address; + u8 T48_reportid; + u8 T63_reportid_min; + u8 T63_reportid_max; + u8 T100_reportid_min; + u8 T100_reportid_max; + + /* for fw update in bootloader */ + struct completion bl_completion; + + /* for reset handling */ + struct completion reset_completion; + + /* for reset handling */ + struct completion crc_completion; + + /* Enable reporting of input events */ + bool enable_reporting; + + /* Indicates whether device is in suspend */ + bool suspended; +}; + +static inline size_t mxt_obj_size(const struct mxt_object *obj) +{ + return obj->size_minus_one + 1; +} + +static inline size_t mxt_obj_instances(const struct mxt_object *obj) +{ + return obj->instances_minus_one + 1; +} + +static bool mxt_object_readable(unsigned int type) +{ + switch (type) { + case MXT_GEN_COMMAND_T6: + case MXT_GEN_POWER_T7: + case MXT_GEN_ACQUIRE_T8: + case MXT_GEN_DATASOURCE_T53: + case MXT_TOUCH_MULTI_T9: + case MXT_TOUCH_KEYARRAY_T15: + case MXT_TOUCH_PROXIMITY_T23: + case MXT_TOUCH_PROXKEY_T52: + case MXT_PROCI_GRIPFACE_T20: + case MXT_PROCG_NOISE_T22: + case MXT_PROCI_ONETOUCH_T24: + case MXT_PROCI_TWOTOUCH_T27: + case MXT_PROCI_GRIP_T40: + case MXT_PROCI_PALM_T41: + case MXT_PROCI_TOUCHSUPPRESSION_T42: + case MXT_PROCI_STYLUS_T47: + case MXT_PROCG_NOISESUPPRESSION_T48: + case MXT_SPT_COMMSCONFIG_T18: + case MXT_SPT_GPIOPWM_T19: + case MXT_SPT_SELFTEST_T25: + case MXT_SPT_CTECONFIG_T28: + case MXT_SPT_USERDATA_T38: + case MXT_SPT_DIGITIZER_T43: + case MXT_SPT_CTECONFIG_T46: + return true; + default: + return false; + } +} + +static void mxt_dump_message(struct mxt_data *data, u8 *message) +{ + print_hex_dump(KERN_DEBUG, "MXT MSG:", DUMP_PREFIX_NONE, 16, 1, + message, data->T5_msg_size, false); +} + +static void mxt_debug_msg_enable(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + + if (data->debug_v2_enabled) + return; + + mutex_lock(&data->debug_msg_lock); + + data->debug_msg_data = kcalloc(DEBUG_MSG_MAX, + data->T5_msg_size, GFP_KERNEL); + if (!data->debug_msg_data) { + dev_err(&data->client->dev, "Failed to allocate buffer\n"); + return; + } + + data->debug_v2_enabled = true; + mutex_unlock(&data->debug_msg_lock); + + dev_info(dev, "Enabled message output\n"); +} + +static void mxt_debug_msg_disable(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + + if (!data->debug_v2_enabled) + return; + + dev_info(dev, "disabling message output\n"); + data->debug_v2_enabled = false; + + mutex_lock(&data->debug_msg_lock); + kfree(data->debug_msg_data); + data->debug_msg_data = NULL; + data->debug_msg_count = 0; + mutex_unlock(&data->debug_msg_lock); + dev_info(dev, "Disabled message output\n"); +} + +static void mxt_debug_msg_add(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + + mutex_lock(&data->debug_msg_lock); + + if (!data->debug_msg_data) { + dev_err(dev, "No buffer!\n"); + return; + } + + if (data->debug_msg_count < DEBUG_MSG_MAX) { + memcpy(data->debug_msg_data + data->debug_msg_count * data->T5_msg_size, + msg, + data->T5_msg_size); + data->debug_msg_count++; + } else { + dev_dbg(dev, "Discarding %u messages\n", data->debug_msg_count); + data->debug_msg_count = 0; + } + + mutex_unlock(&data->debug_msg_lock); + + sysfs_notify(&data->client->dev.kobj, NULL, "debug_notify"); +} + +static ssize_t mxt_debug_msg_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t count) +{ + return -EIO; +} + +static ssize_t mxt_debug_msg_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t bytes) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct mxt_data *data = dev_get_drvdata(dev); + int count; + size_t bytes_read; + + if (!data->debug_msg_data) { + dev_err(dev, "No buffer!\n"); + return 0; + } + + count = bytes / data->T5_msg_size; + + if (count > DEBUG_MSG_MAX) + count = DEBUG_MSG_MAX; + + mutex_lock(&data->debug_msg_lock); + + if (count > data->debug_msg_count) + count = data->debug_msg_count; + + bytes_read = count * data->T5_msg_size; + + memcpy(buf, data->debug_msg_data, bytes_read); + data->debug_msg_count = 0; + + mutex_unlock(&data->debug_msg_lock); + + return bytes_read; +} + +static int mxt_debug_msg_init(struct mxt_data *data) +{ + sysfs_bin_attr_init(&data->debug_msg_attr); + data->debug_msg_attr.attr.name = "debug_msg"; + data->debug_msg_attr.attr.mode = 0666; + data->debug_msg_attr.read = mxt_debug_msg_read; + data->debug_msg_attr.write = mxt_debug_msg_write; + data->debug_msg_attr.size = data->T5_msg_size * DEBUG_MSG_MAX; + + if (sysfs_create_bin_file(&data->client->dev.kobj, + &data->debug_msg_attr) < 0) { + dev_err(&data->client->dev, "Failed to create %s\n", + data->debug_msg_attr.attr.name); + return -EINVAL; + } + + return 0; +} + +static void mxt_debug_msg_remove(struct mxt_data *data) +{ + if (data->debug_msg_attr.attr.name) + sysfs_remove_bin_file(&data->client->dev.kobj, + &data->debug_msg_attr); +} + +static int mxt_wait_for_completion(struct mxt_data *data, + struct completion *comp, unsigned int timeout_ms) +{ + struct device *dev = &data->client->dev; + unsigned long timeout = msecs_to_jiffies(timeout_ms); + long ret; + + ret = wait_for_completion_interruptible_timeout(comp, timeout); + if (ret < 0) { + dev_err(dev, "Wait for completion interrupted.\n"); + return -EINTR; + } else if (ret == 0) { + dev_err(dev, "Wait for completion timed out.\n"); + return -ETIMEDOUT; + } + return 0; +} + +static int mxt_bootloader_read(struct mxt_data *data, + u8 *val, unsigned int count) +{ + int ret; + struct i2c_msg msg; + + msg.addr = data->bootloader_addr; + msg.flags = data->client->flags & I2C_M_TEN; + msg.flags |= I2C_M_RD; + msg.len = count; + msg.buf = val; + + ret = i2c_transfer(data->client->adapter, &msg, 1); + + if (ret == 1) { + ret = 0; + } else { + ret = (ret < 0) ? ret : -EIO; + dev_err(&data->client->dev, "%s: i2c recv failed (%d)\n", + __func__, ret); + } + + return ret; +} + +static int mxt_bootloader_write(struct mxt_data *data, + const u8 * const val, unsigned int count) +{ + int ret; + struct i2c_msg msg; + + msg.addr = data->bootloader_addr; + msg.flags = data->client->flags & I2C_M_TEN; + msg.len = count; + msg.buf = (u8 *)val; + + ret = i2c_transfer(data->client->adapter, &msg, 1); + if (ret == 1) { + ret = 0; + } else { + ret = (ret < 0) ? ret : -EIO; + dev_err(&data->client->dev, "%s: i2c send failed (%d)\n", + __func__, ret); + } + + return ret; +} + +static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry) +{ + u8 appmode = data->client->addr; + u8 bootloader; + u8 family_id = 0; + + if (data->info) + family_id = data->info->family_id; + + switch (appmode) { + case 0x4a: + case 0x4b: + /* Chips after 1664S use different scheme */ + if (retry || family_id >= 0xa2) { + bootloader = appmode - 0x24; + break; + } + /* Fall through for normal case */ + case 0x4c: + case 0x4d: + case 0x5a: + case 0x5b: + bootloader = appmode - 0x26; + break; + default: + dev_err(&data->client->dev, + "Appmode i2c address 0x%02x not found\n", + appmode); + return -EINVAL; + } + + data->bootloader_addr = bootloader; + return 0; +} + +static int mxt_probe_bootloader(struct mxt_data *data, bool retry) +{ + struct device *dev = &data->client->dev; + int ret; + u8 val; + bool crc_failure; + + ret = mxt_lookup_bootloader_address(data, retry); + if (ret) + return ret; + + ret = mxt_bootloader_read(data, &val, 1); + if (ret) + return ret; + + /* Check app crc fail mode */ + crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL; + + dev_err(dev, "Detected bootloader, status:%02X%s\n", + val, crc_failure ? ", APP_CRC_FAIL" : ""); + + return 0; +} + +static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val) +{ + struct device *dev = &data->client->dev; + u8 buf[3]; + + if (val & MXT_BOOT_EXTENDED_ID) { + if (mxt_bootloader_read(data, &buf[0], 3) != 0) { + dev_err(dev, "%s: i2c failure\n", __func__); + return -EIO; + } + + dev_info(dev, "Bootloader ID:%d Version:%d\n", buf[1], buf[2]); + + return buf[0]; + } else { + dev_info(dev, "Bootloader ID:%d\n", val & MXT_BOOT_ID_MASK); + + return val; + } +} + +static int mxt_check_bootloader(struct mxt_data *data, unsigned int state, + bool wait) +{ + struct device *dev = &data->client->dev; + u8 val; + int ret; + +recheck: + if (wait) { + /* + * In application update mode, the interrupt + * line signals state transitions. We must wait for the + * CHG assertion before reading the status byte. + * Once the status byte has been read, the line is deasserted. + */ + ret = mxt_wait_for_completion(data, &data->bl_completion, + MXT_FW_CHG_TIMEOUT); + if (ret) { + /* + * TODO: handle -EINTR better by terminating fw update + * process before returning to userspace by writing + * length 0x000 to device (iff we are in + * WAITING_FRAME_DATA state). + */ + dev_err(dev, "Update wait error %d\n", ret); + return ret; + } + } + + ret = mxt_bootloader_read(data, &val, 1); + if (ret) + return ret; + + if (state == MXT_WAITING_BOOTLOAD_CMD) + val = mxt_get_bootloader_version(data, val); + + switch (state) { + case MXT_WAITING_BOOTLOAD_CMD: + case MXT_WAITING_FRAME_DATA: + case MXT_APP_CRC_FAIL: + val &= ~MXT_BOOT_STATUS_MASK; + break; + case MXT_FRAME_CRC_PASS: + if (val == MXT_FRAME_CRC_CHECK) { + goto recheck; + } else if (val == MXT_FRAME_CRC_FAIL) { + dev_err(dev, "Bootloader CRC fail\n"); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + if (val != state) { + dev_err(dev, "Invalid bootloader state %02X != %02X\n", + val, state); + return -EINVAL; + } + + return 0; +} + +static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock) +{ + int ret; + u8 buf[2]; + + if (unlock) { + buf[0] = MXT_UNLOCK_CMD_LSB; + buf[1] = MXT_UNLOCK_CMD_MSB; + } else { + buf[0] = 0x01; + buf[1] = 0x01; + } + + ret = mxt_bootloader_write(data, buf, 2); + if (ret) + return ret; + + return 0; +} + +static int __mxt_read_reg(struct i2c_client *client, + u16 reg, u16 len, void *val) +{ + struct i2c_msg xfer[2]; + u8 buf[2]; + int ret; + bool retry = false; + + buf[0] = reg & 0xff; + buf[1] = (reg >> 8) & 0xff; + + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 2; + xfer[0].buf = buf; + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = len; + xfer[1].buf = val; + +retry_read: + ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer)); + if (ret != ARRAY_SIZE(xfer)) { + if (!retry) { + dev_dbg(&client->dev, "%s: i2c retry\n", __func__); + msleep(MXT_WAKEUP_TIME); + retry = true; + goto retry_read; + } else { + dev_err(&client->dev, "%s: i2c transfer failed (%d)\n", + __func__, ret); + return -EIO; + } + } + + return 0; +} + +static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len, + const void *val) +{ + u8 *buf; + size_t count; + int ret; + bool retry = false; + + count = len + 2; + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] = reg & 0xff; + buf[1] = (reg >> 8) & 0xff; + memcpy(&buf[2], val, len); + +retry_write: + ret = i2c_master_send(client, buf, count); + if (ret != count) { + if (!retry) { + dev_dbg(&client->dev, "%s: i2c retry\n", __func__); + msleep(MXT_WAKEUP_TIME); + retry = true; + goto retry_write; + } else { + dev_err(&client->dev, "%s: i2c send failed (%d)\n", + __func__, ret); + ret = -EIO; + } + } else { + ret = 0; + } + + kfree(buf); + return ret; +} + +static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val) +{ + return __mxt_write_reg(client, reg, 1, &val); +} + +static struct mxt_object * +mxt_get_object(struct mxt_data *data, u8 type) +{ + struct mxt_object *object; + int i; + + for (i = 0; i < data->info->object_num; i++) { + object = data->object_table + i; + if (object->type == type) + return object; + } + + dev_warn(&data->client->dev, "Invalid object type T%u\n", type); + return NULL; +} + +static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + u8 status = msg[1]; + u32 crc = msg[2] | (msg[3] << 8) | (msg[4] << 16); + + if (crc != data->config_crc) { + data->config_crc = crc; + dev_dbg(dev, "T6 Config Checksum: 0x%06X\n", crc); + complete(&data->crc_completion); + } + + /* Detect transition out of reset */ + if ((data->t6_status & MXT_T6_STATUS_RESET) && + !(status & MXT_T6_STATUS_RESET)) + complete(&data->reset_completion); + + /* Output debug if status has changed */ + if (status != data->t6_status) + dev_dbg(dev, "T6 Status 0x%02X%s%s%s%s%s%s%s\n", + status, + (status == 0) ? " OK" : "", + (status & MXT_T6_STATUS_RESET) ? " RESET" : "", + (status & MXT_T6_STATUS_OFL) ? " OFL" : "", + (status & MXT_T6_STATUS_SIGERR) ? " SIGERR" : "", + (status & MXT_T6_STATUS_CAL) ? " CAL" : "", + (status & MXT_T6_STATUS_CFGERR) ? " CFGERR" : "", + (status & MXT_T6_STATUS_COMSERR) ? " COMSERR" : ""); + + /* Save current status */ + data->t6_status = status; +} + +static void mxt_input_button(struct mxt_data *data, u8 *message) +{ + struct input_dev *input = data->input_dev; + const struct mxt_platform_data *pdata = data->pdata; + bool button; + int i; + + /* do not report events if input device not yet registered */ + if (!data->enable_reporting) + return; + + /* Active-low switch */ + for (i = 0; i < pdata->t19_num_keys; i++) { + if (pdata->t19_keymap[i] == KEY_RESERVED) + continue; + button = !(message[1] & (1 << i)); + input_report_key(input, pdata->t19_keymap[i], button); + } +} + +static void mxt_input_sync(struct input_dev *input_dev) +{ + input_mt_report_pointer_emulation(input_dev, false); + input_sync(input_dev); +} + +static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) +{ + struct device *dev = &data->client->dev; + struct input_dev *input_dev = data->input_dev; + int id; + u8 status; + int x; + int y; + int area; + int amplitude; + u8 vector; + int tool; + + /* do not report events if input device not yet registered */ + if (!data->enable_reporting) + return; + + id = message[0] - data->T9_reportid_min; + status = message[1]; + x = (message[2] << 4) | ((message[4] >> 4) & 0xf); + y = (message[3] << 4) | ((message[4] & 0xf)); + + /* Handle 10/12 bit switching */ + if (data->max_x < 1024) + x >>= 2; + if (data->max_y < 1024) + y >>= 2; + + area = message[5]; + + amplitude = message[6]; + vector = message[7]; + + dev_dbg(dev, + "[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u vector: %02X\n", + id, + (status & MXT_T9_DETECT) ? 'D' : '.', + (status & MXT_T9_PRESS) ? 'P' : '.', + (status & MXT_T9_RELEASE) ? 'R' : '.', + (status & MXT_T9_MOVE) ? 'M' : '.', + (status & MXT_T9_VECTOR) ? 'V' : '.', + (status & MXT_T9_AMP) ? 'A' : '.', + (status & MXT_T9_SUPPRESS) ? 'S' : '.', + (status & MXT_T9_UNGRIP) ? 'U' : '.', + x, y, area, amplitude, vector); + + input_mt_slot(input_dev, id); + + if (status & MXT_T9_DETECT) { + /* Multiple bits may be set if the host is slow to read the + * status messages, indicating all the events that have + * happened */ + if (status & MXT_T9_RELEASE) { + input_mt_report_slot_state(input_dev, + MT_TOOL_FINGER, 0); + mxt_input_sync(input_dev); + } + + /* A reported size of zero indicates that the reported touch + * is a stylus from a linked Stylus T47 object. */ + if (area == 0) { + area = MXT_TOUCH_MAJOR_T47_STYLUS; + tool = MT_TOOL_PEN; + } else { + tool = MT_TOOL_FINGER; + } + + /* Touch active */ + input_mt_report_slot_state(input_dev, tool, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area); + input_report_abs(input_dev, ABS_MT_ORIENTATION, vector); + } else { + /* Touch no longer active, close out slot */ + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); + } + + data->update_input = true; +} + +static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) +{ + struct device *dev = &data->client->dev; + struct input_dev *input_dev = data->input_dev; + int id; + u8 status; + int x; + int y; + int tool; + + /* do not report events if input device not yet registered */ + if (!data->enable_reporting) + return; + + id = message[0] - data->T100_reportid_min - 2; + + /* ignore SCRSTATUS events */ + if (id < 0) + return; + + status = message[1]; + x = (message[3] << 8) | message[2]; + y = (message[5] << 8) | message[4]; + + dev_dbg(dev, + "[%u] status:%02X x:%u y:%u area:%02X amp:%02X vec:%02X\n", + id, + status, + x, y, + (data->t100_aux_area) ? message[data->t100_aux_area] : 0, + (data->t100_aux_ampl) ? message[data->t100_aux_ampl] : 0, + (data->t100_aux_vect) ? message[data->t100_aux_vect] : 0); + + input_mt_slot(input_dev, id); + + if (status & MXT_T100_DETECT) { + /* A reported size of zero indicates that the reported touch + * is a stylus from a linked Stylus T47 object. */ + if ((status & MXT_T100_TYPE_MASK) == MXT_T100_TYPE_STYLUS) + tool = MT_TOOL_PEN; + else + tool = MT_TOOL_FINGER; + + /* Touch active */ + input_mt_report_slot_state(input_dev, tool, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + + if (data->t100_aux_ampl) + input_report_abs(input_dev, ABS_MT_PRESSURE, + message[data->t100_aux_ampl]); + + if (data->t100_aux_area) { + if (tool == MT_TOOL_PEN) + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + MXT_TOUCH_MAJOR_T47_STYLUS); + else + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + message[data->t100_aux_area]); + } + + if (data->t100_aux_vect) + input_report_abs(input_dev, ABS_MT_ORIENTATION, + message[data->t100_aux_vect]); + } else { + /* Touch no longer active, close out slot */ + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); + } + + data->update_input = true; +} + + +static void mxt_proc_t15_messages(struct mxt_data *data, u8 *msg) +{ + struct input_dev *input_dev = data->input_dev; + struct device *dev = &data->client->dev; + int key; + bool curr_state, new_state; + bool sync = false; + unsigned long keystates = le32_to_cpu(msg[2]); + + /* do not report events if input device not yet registered */ + if (!data->enable_reporting) + return; + + for (key = 0; key < data->pdata->t15_num_keys; key++) { + curr_state = test_bit(key, &data->t15_keystatus); + new_state = test_bit(key, &keystates); + + if (!curr_state && new_state) { + dev_dbg(dev, "T15 key press: %u\n", key); + __set_bit(key, &data->t15_keystatus); + input_event(input_dev, EV_KEY, + data->pdata->t15_keymap[key], 1); + sync = true; + } else if (curr_state && !new_state) { + dev_dbg(dev, "T15 key release: %u\n", key); + __clear_bit(key, &data->t15_keystatus); + input_event(input_dev, EV_KEY, + data->pdata->t15_keymap[key], 0); + sync = true; + } + } + + if (sync) + input_sync(input_dev); +} + +static void mxt_proc_t42_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + u8 status = msg[1]; + + if (status & MXT_T42_MSG_TCHSUP) + dev_info(dev, "T42 suppress\n"); + else + dev_info(dev, "T42 normal\n"); +} + +static int mxt_proc_t48_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + u8 status, state; + + status = msg[1]; + state = msg[4]; + + dev_dbg(dev, "T48 state %d status %02X %s%s%s%s%s\n", + state, + status, + (status & 0x01) ? "FREQCHG " : "", + (status & 0x02) ? "APXCHG " : "", + (status & 0x04) ? "ALGOERR " : "", + (status & 0x10) ? "STATCHG " : "", + (status & 0x20) ? "NLVLCHG " : ""); + + return 0; +} + +static void mxt_proc_t63_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + struct input_dev *input_dev = data->input_dev; + u8 id; + u16 x, y; + u8 pressure; + + /* do not report events if input device not yet registered */ + if (!data->enable_reporting) + return; + + /* stylus slots come after touch slots */ + id = data->num_touchids + (msg[0] - data->T63_reportid_min); + + if (id < 0 || id > (data->num_touchids + data->num_stylusids)) { + dev_err(dev, "invalid stylus id %d, max slot is %d\n", + id, data->num_stylusids); + return; + } + + x = msg[3] | (msg[4] << 8); + y = msg[5] | (msg[6] << 8); + pressure = msg[7] & MXT_T63_STYLUS_PRESSURE_MASK; + + dev_dbg(dev, + "[%d] %c%c%c%c x: %d y: %d pressure: %d stylus:%c%c%c%c\n", + id, + (msg[1] & MXT_T63_STYLUS_SUPPRESS) ? 'S' : '.', + (msg[1] & MXT_T63_STYLUS_MOVE) ? 'M' : '.', + (msg[1] & MXT_T63_STYLUS_RELEASE) ? 'R' : '.', + (msg[1] & MXT_T63_STYLUS_PRESS) ? 'P' : '.', + x, y, pressure, + (msg[2] & MXT_T63_STYLUS_BARREL) ? 'B' : '.', + (msg[2] & MXT_T63_STYLUS_ERASER) ? 'E' : '.', + (msg[2] & MXT_T63_STYLUS_TIP) ? 'T' : '.', + (msg[2] & MXT_T63_STYLUS_DETECT) ? 'D' : '.'); + + input_mt_slot(input_dev, id); + + if (msg[2] & MXT_T63_STYLUS_DETECT) { + input_mt_report_slot_state(input_dev, MT_TOOL_PEN, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(input_dev, ABS_MT_PRESSURE, pressure); + } else { + input_mt_report_slot_state(input_dev, MT_TOOL_PEN, 0); + } + + input_report_key(input_dev, BTN_STYLUS, + (msg[2] & MXT_T63_STYLUS_ERASER)); + input_report_key(input_dev, BTN_STYLUS2, + (msg[2] & MXT_T63_STYLUS_BARREL)); + + mxt_input_sync(input_dev); +} + +static int mxt_proc_message(struct mxt_data *data, u8 *message) +{ + u8 report_id = message[0]; + bool dump = data->debug_enabled; + + if (report_id == MXT_RPTID_NOMSG) + return 0; + + if (report_id == data->T6_reportid) { + mxt_proc_t6_messages(data, message); + } else if (report_id >= data->T9_reportid_min + && report_id <= data->T9_reportid_max) { + mxt_proc_t9_message(data, message); + } else if (report_id >= data->T100_reportid_min + && report_id <= data->T100_reportid_max) { + mxt_proc_t100_message(data, message); + } else if (report_id == data->T19_reportid) { + mxt_input_button(data, message); + data->update_input = true; + } else if (report_id >= data->T63_reportid_min + && report_id <= data->T63_reportid_max) { + mxt_proc_t63_messages(data, message); + } else if (report_id >= data->T42_reportid_min + && report_id <= data->T42_reportid_max) { + mxt_proc_t42_messages(data, message); + } else if (report_id == data->T48_reportid) { + mxt_proc_t48_messages(data, message); + } else if (report_id >= data->T15_reportid_min + && report_id <= data->T15_reportid_max) { + mxt_proc_t15_messages(data, message); + } else { + dump = true; + } + + if (dump) + mxt_dump_message(data, message); + + if (data->debug_v2_enabled) + mxt_debug_msg_add(data, message); + + return 1; +} + +static int mxt_read_and_process_messages(struct mxt_data *data, u8 count) +{ + struct device *dev = &data->client->dev; + int ret; + int i; + u8 num_valid = 0; + + /* Safety check for msg_buf */ + if (count > data->max_reportid) + return -EINVAL; + + /* Process remaining messages if necessary */ + ret = __mxt_read_reg(data->client, data->T5_address, + data->T5_msg_size * count, data->msg_buf); + if (ret) { + dev_err(dev, "Failed to read %u messages (%d)\n", count, ret); + return ret; + } + + for (i = 0; i < count; i++) { + ret = mxt_proc_message(data, + data->msg_buf + data->T5_msg_size * i); + + if (ret == 1) + num_valid++; + } + + /* return number of messages read */ + return num_valid; +} + +static irqreturn_t mxt_process_messages_t44(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + u8 count, num_left; + + /* Read T44 and T5 together */ + ret = __mxt_read_reg(data->client, data->T44_address, + data->T5_msg_size + 1, data->msg_buf); + if (ret) { + dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret); + return IRQ_NONE; + } + + count = data->msg_buf[0]; + + if (count == 0) { + dev_warn(dev, "Interrupt triggered but zero messages\n"); + return IRQ_NONE; + } else if (count > data->max_reportid) { + dev_err(dev, "T44 count %d exceeded max report id\n", count); + count = data->max_reportid; + } + + /* Process first message */ + ret = mxt_proc_message(data, data->msg_buf + 1); + if (ret < 0) { + dev_warn(dev, "Unexpected invalid message\n"); + return IRQ_NONE; + } + + num_left = count - 1; + + /* Process remaining messages if necessary */ + if (num_left) { + ret = mxt_read_and_process_messages(data, num_left); + if (ret < 0) + goto end; + else if (ret != num_left) + dev_warn(dev, "Unexpected invalid message\n"); + } + +end: + if (data->update_input) { + mxt_input_sync(data->input_dev); + data->update_input = false; + } + + return IRQ_HANDLED; +} + +static int mxt_process_messages_until_invalid(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int count, read; + u8 tries = 2; + + count = data->max_reportid; + + /* Read messages until we force an invalid */ + do { + read = mxt_read_and_process_messages(data, count); + if (read < count) + return 0; + } while (--tries); + + if (data->update_input) { + mxt_input_sync(data->input_dev); + data->update_input = false; + } + + dev_err(dev, "CHG pin isn't cleared\n"); + return -EBUSY; +} + +static irqreturn_t mxt_process_messages(struct mxt_data *data) +{ + int total_handled, num_handled; + u8 count = data->last_message_count; + + if (count < 1 || count > data->max_reportid) + count = 1; + + /* include final invalid message */ + total_handled = mxt_read_and_process_messages(data, count + 1); + if (total_handled < 0) + return IRQ_NONE; + /* if there were invalid messages, then we are done */ + else if (total_handled <= count) + goto update_count; + + /* read two at a time until an invalid message or else we reach + * reportid limit */ + do { + num_handled = mxt_read_and_process_messages(data, 2); + if (num_handled < 0) + return IRQ_NONE; + + total_handled += num_handled; + + if (num_handled < 2) + break; + } while (total_handled < data->num_touchids); + +update_count: + data->last_message_count = total_handled; + + if (data->enable_reporting && data->update_input) { + mxt_input_sync(data->input_dev); + data->update_input = false; + } + + return IRQ_HANDLED; +} + +static irqreturn_t mxt_interrupt(int irq, void *dev_id) +{ + struct mxt_data *data = dev_id; + + if (data->in_bootloader) { + /* bootloader state transition completion */ + complete(&data->bl_completion); + return IRQ_HANDLED; + } + + if (!data->object_table) + return IRQ_NONE; + + if (data->T44_address) { + return mxt_process_messages_t44(data); + } else { + return mxt_process_messages(data); + } +} + +static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset, + u8 value, bool wait) +{ + u16 reg; + u8 command_register; + int timeout_counter = 0; + int ret; + + reg = data->T6_address + cmd_offset; + + ret = mxt_write_reg(data->client, reg, value); + if (ret) + return ret; + + if (!wait) + return 0; + + do { + msleep(20); + ret = __mxt_read_reg(data->client, reg, 1, &command_register); + if (ret) + return ret; + } while ((command_register != 0) && (timeout_counter++ <= 100)); + + if (timeout_counter > 100) { + dev_err(&data->client->dev, "Command failed!\n"); + return -EIO; + } + + return 0; +} + +static int mxt_soft_reset(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int ret = 0; + + dev_info(dev, "Resetting chip\n"); + + INIT_COMPLETION(data->reset_completion); + + ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_RESET_VALUE, false); + if (ret) + return ret; + + ret = mxt_wait_for_completion(data, &data->reset_completion, + MXT_RESET_TIMEOUT); + if (ret) + return ret; + + return 0; +} + +static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value) +{ + /* on failure, CRC is set to 0 and config will always be downloaded */ + data->config_crc = 0; + INIT_COMPLETION(data->crc_completion); + + mxt_t6_command(data, cmd, value, true); + + /* Wait for crc message. On failure, CRC is set to 0 and config will + * always be downloaded */ + mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT); +} + +static void mxt_calc_crc24(u32 *crc, u8 firstbyte, u8 secondbyte) +{ + static const unsigned int crcpoly = 0x80001B; + u32 result; + u32 data_word; + + data_word = (secondbyte << 8) | firstbyte; + result = ((*crc << 1) ^ data_word); + + if (result & 0x1000000) + result ^= crcpoly; + + *crc = result; +} + +static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off) +{ + u32 crc = 0; + u8 *ptr = base + start_off; + u8 *last_val = base + end_off - 1; + + if (end_off < start_off) + return -EINVAL; + + while (ptr < last_val) { + mxt_calc_crc24(&crc, *ptr, *(ptr + 1)); + ptr += 2; + } + + /* if len is odd, fill the last byte with 0 */ + if (ptr == last_val) + mxt_calc_crc24(&crc, *ptr, 0); + + /* Mask to 24-bit */ + crc &= 0x00FFFFFF; + + return crc; +} + +static int mxt_check_retrigen(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + int val; + + if (data->pdata->irqflags & IRQF_TRIGGER_LOW) + return 0; + + if (data->T18_address) { + error = __mxt_read_reg(client, + data->T18_address + MXT_COMMS_CTRL, + 1, &val); + if (error) + return error; + + if (val & MXT_COMMS_RETRIGEN) + return 0; + } + + dev_warn(&client->dev, "Enabling RETRIGEN workaround\n"); + data->use_retrigen_workaround = true; + return 0; +} + +static int mxt_init_t7_power_cfg(struct mxt_data *data); + +/* + * mxt_check_reg_init - download configuration to chip + * + * Atmel Raw Config File Format + * + * The first four lines of the raw config file contain: + * 1) Version + * 2) Chip ID Information (first 7 bytes of device memory) + * 3) Chip Information Block 24-bit CRC Checksum + * 4) Chip Configuration 24-bit CRC Checksum + * + * The rest of the file consists of one line per object instance: + * <TYPE> <INSTANCE> <SIZE> <CONTENTS> + * + * <TYPE> - 2-byte object type as hex + * <INSTANCE> - 2-byte object instance number as hex + * <SIZE> - 2-byte object size as hex + * <CONTENTS> - array of <SIZE> 1-byte hex values + */ +static int mxt_check_reg_init(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + struct mxt_info cfg_info; + struct mxt_object *object; + const struct firmware *cfg = NULL; + int ret; + int offset; + int data_pos; + int byte_offset; + int i; + int cfg_start_ofs; + u32 info_crc, config_crc, calculated_crc; + u8 *config_mem; + size_t config_mem_size; + unsigned int type, instance, size; + u8 val; + u16 reg; + + if (!data->cfg_name) { + dev_dbg(dev, "Skipping cfg download\n"); + return 0; + } + + ret = request_firmware(&cfg, data->cfg_name, dev); + if (ret < 0) { + dev_err(dev, "Failure to request config file %s\n", + data->cfg_name); + return 0; + } + + mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); + + if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { + dev_err(dev, "Unrecognised config file\n"); + ret = -EINVAL; + goto release; + } + + data_pos = strlen(MXT_CFG_MAGIC); + + /* Load information block and check */ + for (i = 0; i < sizeof(struct mxt_info); i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + (unsigned char *)&cfg_info + i, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format\n"); + ret = -EINVAL; + goto release; + } + + data_pos += offset; + } + + if (cfg_info.family_id != data->info->family_id) { + dev_err(dev, "Family ID mismatch!\n"); + ret = -EINVAL; + goto release; + } + + if (cfg_info.variant_id != data->info->variant_id) { + dev_err(dev, "Variant ID mismatch!\n"); + ret = -EINVAL; + goto release; + } + + /* Read CRCs */ + ret = sscanf(cfg->data + data_pos, "%x%n", &info_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format: failed to parse Info CRC\n"); + ret = -EINVAL; + goto release; + } + data_pos += offset; + + ret = sscanf(cfg->data + data_pos, "%x%n", &config_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format: failed to parse Config CRC\n"); + ret = -EINVAL; + goto release; + } + data_pos += offset; + + /* The Info Block CRC is calculated over mxt_info and the object table + * If it does not match then we are trying to load the configuration + * from a different chip or firmware version, so the configuration CRC + * is invalid anyway. */ + if (info_crc == data->info_crc) { + if (config_crc == 0 || data->config_crc == 0) { + dev_info(dev, "CRC zero, attempting to apply config\n"); + } else if (config_crc == data->config_crc) { + dev_info(dev, "Config CRC 0x%06X: OK\n", + data->config_crc); + ret = 0; + goto release; + } else { + dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", + data->config_crc, config_crc); + } + } else { + dev_warn(dev, + "Warning: Info CRC error - device=0x%06X file=0x%06X\n", + data->info_crc, info_crc); + } + + /* Malloc memory to store configuration */ + cfg_start_ofs = MXT_OBJECT_START + + data->info->object_num * sizeof(struct mxt_object) + + MXT_INFO_CHECKSUM_SIZE; + config_mem_size = data->mem_size - cfg_start_ofs; + config_mem = kzalloc(config_mem_size, GFP_KERNEL); + if (!config_mem) { + dev_err(dev, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto release; + } + + while (data_pos < cfg->size) { + /* Read type, instance, length */ + ret = sscanf(cfg->data + data_pos, "%x %x %x%n", + &type, &instance, &size, &offset); + if (ret == 0) { + /* EOF */ + break; + } else if (ret != 3) { + dev_err(dev, "Bad format: failed to parse object\n"); + ret = -EINVAL; + goto release_mem; + } + data_pos += offset; + + object = mxt_get_object(data, type); + if (!object) { + /* Skip object */ + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + &val, + &offset); + data_pos += offset; + } + continue; + } + + if (size > mxt_obj_size(object)) { + /* Either we are in fallback mode due to wrong + * config or config from a later fw version, + * or the file is corrupt or hand-edited */ + dev_warn(dev, "Discarding %u byte(s) in T%u\n", + size - mxt_obj_size(object), type); + } else if (mxt_obj_size(object) > size) { + /* If firmware is upgraded, new bytes may be added to + * end of objects. It is generally forward compatible + * to zero these bytes - previous behaviour will be + * retained. However this does invalidate the CRC and + * will force fallback mode until the configuration is + * updated. We warn here but do nothing else - the + * malloc has zeroed the entire configuration. */ + dev_warn(dev, "Zeroing %u byte(s) in T%d\n", + mxt_obj_size(object) - size, type); + } + + if (instance >= mxt_obj_instances(object)) { + dev_err(dev, "Object instances exceeded!\n"); + ret = -EINVAL; + goto release_mem; + } + + reg = object->start_address + mxt_obj_size(object) * instance; + + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + &val, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format in T%d\n", type); + ret = -EINVAL; + goto release_mem; + } + data_pos += offset; + + if (i > mxt_obj_size(object)) + continue; + + byte_offset = reg + i - cfg_start_ofs; + + if ((byte_offset >= 0) + && (byte_offset <= config_mem_size)) { + *(config_mem + byte_offset) = val; + } else { + dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n", + reg, object->type, byte_offset); + ret = -EINVAL; + goto release_mem; + } + } + } + + /* calculate crc of the received configs (not the raw config file) */ + if (data->T7_address < cfg_start_ofs) { + dev_err(dev, "Bad T7 address, T7addr = %x, config offset %x\n", + data->T7_address, cfg_start_ofs); + ret = 0; + goto release_mem; + } + + calculated_crc = mxt_calculate_crc(config_mem, + data->T7_address - cfg_start_ofs, + config_mem_size); + + if (config_crc > 0 && (config_crc != calculated_crc)) + dev_warn(dev, "Config CRC error, calculated=%06X, file=%06X\n", + calculated_crc, config_crc); + + /* Write configuration as blocks */ + byte_offset = 0; + while (byte_offset < config_mem_size) { + size = config_mem_size - byte_offset; + + if (size > MXT_MAX_BLOCK_WRITE) + size = MXT_MAX_BLOCK_WRITE; + + ret = __mxt_write_reg(data->client, + cfg_start_ofs + byte_offset, + size, config_mem + byte_offset); + if (ret != 0) { + dev_err(dev, "Config write error, ret=%d\n", ret); + goto release_mem; + } + + byte_offset += size; + } + + mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); + + ret = mxt_check_retrigen(data); + if (ret) + goto release_mem; + + ret = mxt_soft_reset(data); + if (ret) + goto release_mem; + + dev_info(dev, "Config written\n"); + + /* T7 config may have changed */ + mxt_init_t7_power_cfg(data); + +release_mem: + kfree(config_mem); +release: + release_firmware(cfg); + return ret; +} + +static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep) +{ + struct device *dev = &data->client->dev; + int error; + struct t7_config *new_config; + struct t7_config deepsleep = { .active = 0, .idle = 0 }; + + if (sleep == MXT_POWER_CFG_DEEPSLEEP) + new_config = &deepsleep; + else + new_config = &data->t7_cfg; + + error = __mxt_write_reg(data->client, data->T7_address, + sizeof(data->t7_cfg), + new_config); + if (error) + return error; + + dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n", + new_config->active, new_config->idle); + + return 0; +} + +static int mxt_init_t7_power_cfg(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int error; + bool retry = false; + +recheck: + error = __mxt_read_reg(data->client, data->T7_address, + sizeof(data->t7_cfg), &data->t7_cfg); + if (error) + return error; + + if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) { + if (!retry) { + dev_info(dev, "T7 cfg zero, resetting\n"); + mxt_soft_reset(data); + retry = true; + goto recheck; + } else { + dev_dbg(dev, "T7 cfg zero after reset, overriding\n"); + data->t7_cfg.active = 20; + data->t7_cfg.idle = 100; + return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + } + } else { + dev_info(dev, "Initialised power cfg: ACTV %d, IDLE %d\n", + data->t7_cfg.active, data->t7_cfg.idle); + return 0; + } +} + +static int mxt_acquire_irq(struct mxt_data *data) +{ + int error; + + enable_irq(data->irq); + + if (data->use_retrigen_workaround) { + error = mxt_process_messages_until_invalid(data); + if (error) + return error; + } + + return 0; +} + +static void mxt_free_input_device(struct mxt_data *data) +{ + if (data->input_dev) { + input_unregister_device(data->input_dev); + data->input_dev = NULL; + } +} + +static void mxt_free_object_table(struct mxt_data *data) +{ + mxt_debug_msg_remove(data); + + kfree(data->raw_info_block); + data->object_table = NULL; + data->info = NULL; + data->raw_info_block = NULL; + kfree(data->msg_buf); + data->msg_buf = NULL; + + mxt_free_input_device(data); + + data->enable_reporting = false; + data->T5_address = 0; + data->T5_msg_size = 0; + data->T6_reportid = 0; + data->T7_address = 0; + data->T9_reportid_min = 0; + data->T9_reportid_max = 0; + data->T15_reportid_min = 0; + data->T15_reportid_max = 0; + data->T18_address = 0; + data->T19_reportid = 0; + data->T42_reportid_min = 0; + data->T42_reportid_max = 0; + data->T44_address = 0; + data->T48_reportid = 0; + data->T63_reportid_min = 0; + data->T63_reportid_max = 0; + data->T100_reportid_min = 0; + data->T100_reportid_max = 0; + data->max_reportid = 0; +} + +static int mxt_parse_object_table(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int i; + u8 reportid; + u16 end_address; + + /* Valid Report IDs start counting from 1 */ + reportid = 1; + data->mem_size = 0; + for (i = 0; i < data->info->object_num; i++) { + struct mxt_object *object = data->object_table + i; + u8 min_id, max_id; + + le16_to_cpus(&object->start_address); + + if (object->num_report_ids) { + min_id = reportid; + reportid += object->num_report_ids * + mxt_obj_instances(object); + max_id = reportid - 1; + } else { + min_id = 0; + max_id = 0; + } + + dev_dbg(&data->client->dev, + "T%u Start:%u Size:%u Instances:%u Report IDs:%u-%u\n", + object->type, object->start_address, + mxt_obj_size(object), mxt_obj_instances(object), + min_id, max_id); + + switch (object->type) { + case MXT_GEN_MESSAGE_T5: + if (data->info->family_id == 0x80) { + /* On mXT224 read and discard unused CRC byte + * otherwise DMA reads are misaligned */ + data->T5_msg_size = mxt_obj_size(object); + } else { + /* CRC not enabled, so skip last byte */ + data->T5_msg_size = mxt_obj_size(object) - 1; + } + data->T5_address = object->start_address; + case MXT_GEN_COMMAND_T6: + data->T6_reportid = min_id; + data->T6_address = object->start_address; + break; + case MXT_GEN_POWER_T7: + data->T7_address = object->start_address; + break; + case MXT_TOUCH_MULTI_T9: + /* Only handle messages from first T9 instance */ + data->T9_reportid_min = min_id; + data->T9_reportid_max = min_id + + object->num_report_ids - 1; + data->num_touchids = object->num_report_ids; + break; + case MXT_TOUCH_KEYARRAY_T15: + data->T15_reportid_min = min_id; + data->T15_reportid_max = max_id; + break; + case MXT_SPT_COMMSCONFIG_T18: + data->T18_address = object->start_address; + break; + case MXT_PROCI_TOUCHSUPPRESSION_T42: + data->T42_reportid_min = min_id; + data->T42_reportid_max = max_id; + break; + case MXT_SPT_MESSAGECOUNT_T44: + data->T44_address = object->start_address; + break; + case MXT_SPT_GPIOPWM_T19: + data->T19_reportid = min_id; + break; + case MXT_PROCG_NOISESUPPRESSION_T48: + data->T48_reportid = min_id; + break; + case MXT_PROCI_ACTIVE_STYLUS_T63: + /* Only handle messages from first T63 instance */ + data->T63_reportid_min = min_id; + data->T63_reportid_max = min_id; + data->num_stylusids = 1; + break; + case MXT_TOUCH_MULTITOUCHSCREEN_T100: + data->T100_reportid_min = min_id; + data->T100_reportid_max = max_id; + /* first two report IDs reserved */ + data->num_touchids = object->num_report_ids - 2; + break; + } + + end_address = object->start_address + + mxt_obj_size(object) * mxt_obj_instances(object) - 1; + + if (end_address >= data->mem_size) + data->mem_size = end_address + 1; + } + + /* Store maximum reportid */ + data->max_reportid = reportid; + + /* If T44 exists, T5 position has to be directly after */ + if (data->T44_address && (data->T5_address != data->T44_address + 1)) { + dev_err(&client->dev, "Invalid T44 position\n"); + return -EINVAL; + } + + data->msg_buf = kcalloc(data->max_reportid, + data->T5_msg_size, GFP_KERNEL); + if (!data->msg_buf) { + dev_err(&client->dev, "Failed to allocate message buffer\n"); + return -ENOMEM; + } + + return 0; +} + +static int mxt_read_info_block(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + size_t size; + void *buf; + uint8_t num_objects; + u32 calculated_crc; + u8 *crc_ptr; + + /* If info block already allocated, free it */ + if (data->raw_info_block != NULL) + mxt_free_object_table(data); + + /* Read 7-byte ID information block starting at address 0 */ + size = sizeof(struct mxt_info); + buf = kzalloc(size, GFP_KERNEL); + if (!buf) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + error = __mxt_read_reg(client, 0, size, buf); + if (error) + goto err_free_mem; + + /* Resize buffer to give space for rest of info block */ + num_objects = ((struct mxt_info *)buf)->object_num; + size += (num_objects * sizeof(struct mxt_object)) + + MXT_INFO_CHECKSUM_SIZE; + + buf = krealloc(buf, size, GFP_KERNEL); + if (!buf) { + dev_err(&client->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + /* Read rest of info block */ + error = __mxt_read_reg(client, MXT_OBJECT_START, + size - MXT_OBJECT_START, + buf + MXT_OBJECT_START); + if (error) + goto err_free_mem; + + /* Extract & calculate checksum */ + crc_ptr = buf + size - MXT_INFO_CHECKSUM_SIZE; + data->info_crc = crc_ptr[0] | (crc_ptr[1] << 8) | (crc_ptr[2] << 16); + + calculated_crc = mxt_calculate_crc(buf, 0, + size - MXT_INFO_CHECKSUM_SIZE); + + /* CRC mismatch can be caused by data corruption due to I2C comms + * issue or else device is not using Object Based Protocol */ + if ((data->info_crc == 0) || (data->info_crc != calculated_crc)) { + dev_err(&client->dev, + "Info Block CRC error calculated=0x%06X read=0x%06X\n", + data->info_crc, calculated_crc); + return -EIO; + } + + /* Save pointers in device data structure */ + data->raw_info_block = buf; + data->info = (struct mxt_info *)buf; + data->object_table = (struct mxt_object *)(buf + MXT_OBJECT_START); + + dev_info(&client->dev, + "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", + data->info->family_id, data->info->variant_id, + data->info->version >> 4, data->info->version & 0xf, + data->info->build, data->info->object_num); + + /* Parse object table information */ + error = mxt_parse_object_table(data); + if (error) { + dev_err(&client->dev, "Error %d reading object table\n", error); + mxt_free_object_table(data); + return error; + } + + return 0; + +err_free_mem: + kfree(buf); + return error; +} + +static int mxt_read_t9_resolution(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + struct t9_range range; + unsigned char orient; + struct mxt_object *object; + + object = mxt_get_object(data, MXT_TOUCH_MULTI_T9); + if (!object) + return -EINVAL; + + error = __mxt_read_reg(client, + object->start_address + MXT_T9_RANGE, + sizeof(range), &range); + if (error) + return error; + + le16_to_cpus(range.x); + le16_to_cpus(range.y); + + error = __mxt_read_reg(client, + object->start_address + MXT_T9_ORIENT, + 1, &orient); + if (error) + return error; + + /* Handle default values */ + if (range.x == 0) + range.x = 1023; + + if (range.y == 0) + range.y = 1023; + + if (orient & MXT_T9_ORIENT_SWITCH) { + data->max_x = range.y; + data->max_y = range.x; + } else { + data->max_x = range.x; + data->max_y = range.y; + } + + dev_info(&client->dev, + "Touchscreen size X%uY%u\n", data->max_x, data->max_y); + + return 0; +} + +static void mxt_regulator_enable(struct mxt_data *data) +{ + gpio_set_value(data->pdata->gpio_reset, 0); + + regulator_enable(data->reg_vdd); + regulator_enable(data->reg_avdd); + msleep(MXT_REGULATOR_DELAY); + + INIT_COMPLETION(data->bl_completion); + gpio_set_value(data->pdata->gpio_reset, 1); + mxt_wait_for_completion(data, &data->bl_completion, MXT_POWERON_DELAY); +} + +static void mxt_regulator_disable(struct mxt_data *data) +{ + regulator_disable(data->reg_vdd); + regulator_disable(data->reg_avdd); +} + +static void mxt_probe_regulators(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int error; + + /* According to maXTouch power sequencing specification, RESET line + * must be kept low until some time after regulators come up to + * voltage */ + if (!data->pdata->gpio_reset) { + dev_warn(dev, "Must have reset GPIO to use regulator support\n"); + goto fail; + } + + data->reg_vdd = regulator_get(dev, "vdd"); + if (IS_ERR(data->reg_vdd)) { + error = PTR_ERR(data->reg_vdd); + dev_err(dev, "Error %d getting vdd regulator\n", error); + goto fail; + } + + data->reg_avdd = regulator_get(dev, "avdd"); + if (IS_ERR(data->reg_vdd)) { + error = PTR_ERR(data->reg_vdd); + dev_err(dev, "Error %d getting avdd regulator\n", error); + goto fail_release; + } + + data->use_regulator = true; + mxt_regulator_enable(data); + + dev_dbg(dev, "Initialised regulators\n"); + return; + +fail_release: + regulator_put(data->reg_vdd); +fail: + data->reg_vdd = NULL; + data->reg_avdd = NULL; + data->use_regulator = false; +} + +static int mxt_read_t100_config(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + struct mxt_object *object; + u16 range_x, range_y; + u8 cfg, tchaux; + u8 aux; + + object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100); + if (!object) + return -EINVAL; + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_XRANGE, + sizeof(range_x), &range_x); + if (error) + return error; + + le16_to_cpus(range_x); + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_YRANGE, + sizeof(range_y), &range_y); + if (error) + return error; + + le16_to_cpus(range_y); + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_CFG1, + 1, &cfg); + if (error) + return error; + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_TCHAUX, + 1, &tchaux); + if (error) + return error; + + /* Handle default values */ + if (range_x == 0) + range_x = 1023; + + /* Handle default values */ + if (range_x == 0) + range_x = 1023; + + if (range_y == 0) + range_y = 1023; + + if (cfg & MXT_T100_CFG_SWITCHXY) { + data->max_x = range_y; + data->max_y = range_x; + } else { + data->max_x = range_x; + data->max_y = range_y; + } + + /* allocate aux bytes */ + aux = 6; + + if (tchaux & MXT_T100_TCHAUX_VECT) + data->t100_aux_vect = aux++; + + if (tchaux & MXT_T100_TCHAUX_AMPL) + data->t100_aux_ampl = aux++; + + if (tchaux & MXT_T100_TCHAUX_AREA) + data->t100_aux_area = aux++; + + dev_info(&client->dev, + "T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y); + + return 0; +} + +static int mxt_input_open(struct input_dev *dev); +static void mxt_input_close(struct input_dev *dev); + +static int mxt_initialize_t100_input_device(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + struct input_dev *input_dev; + int error; + + error = mxt_read_t100_config(data); + if (error) + dev_warn(dev, "Failed to initialize T9 resolution\n"); + + input_dev = input_allocate_device(); + if (!data || !input_dev) { + dev_err(dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + input_dev->name = "atmel_mxt_ts T100 touchscreen"; + + input_dev->phys = data->phys; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &data->client->dev; + input_dev->open = mxt_input_open; + input_dev->close = mxt_input_close; + + set_bit(EV_ABS, input_dev->evbit); + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + + /* For single touch */ + input_set_abs_params(input_dev, ABS_X, + 0, data->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, data->max_y, 0, 0); + + if (data->t100_aux_ampl) + input_set_abs_params(input_dev, ABS_PRESSURE, + 0, 255, 0, 0); + + /* For multi touch */ + error = input_mt_init_slots(input_dev, data->num_touchids); + if (error) { + dev_err(dev, "Error %d initialising slots\n", error); + goto err_free_mem; + } + + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, data->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, data->max_y, 0, 0); + + if (data->t100_aux_area) + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, MXT_MAX_AREA, 0, 0); + + if (data->t100_aux_ampl) + input_set_abs_params(input_dev, ABS_MT_PRESSURE, + 0, 255, 0, 0); + + if (data->t100_aux_vect) + input_set_abs_params(input_dev, ABS_MT_ORIENTATION, + 0, 255, 0, 0); + + input_set_drvdata(input_dev, data); + + error = input_register_device(input_dev); + if (error) { + dev_err(dev, "Error %d registering input device\n", error); + goto err_free_mem; + } + + data->input_dev = input_dev; + + return 0; + +err_free_mem: + input_free_device(input_dev); + return error; +} + +static int mxt_initialize_t9_input_device(struct mxt_data *data); +static int mxt_configure_objects(struct mxt_data *data); + +static int mxt_initialize(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + bool alt_bootloader_addr = false; + bool retry = false; + +retry_info: + error = mxt_read_info_block(data); + if (error) { +retry_bootloader: + error = mxt_probe_bootloader(data, alt_bootloader_addr); + if (error) { + if (alt_bootloader_addr) { + /* Chip is not in appmode or bootloader mode */ + return error; + } + + dev_info(&client->dev, "Trying alternate bootloader address\n"); + alt_bootloader_addr = true; + goto retry_bootloader; + } else { + if (retry) { + dev_err(&client->dev, + "Could not recover device from " + "bootloader mode\n"); + /* this is not an error state, we can reflash + * from here */ + data->in_bootloader = true; + return 0; + } + + /* Attempt to exit bootloader into app mode */ + mxt_send_bootloader_cmd(data, false); + msleep(MXT_FW_RESET_TIME); + retry = true; + goto retry_info; + } + } + + error = mxt_check_retrigen(data); + if (error) + return error; + + error = mxt_acquire_irq(data); + if (error) + return error; + + error = mxt_configure_objects(data); + if (error) + return error; + + return 0; +} + +static int mxt_configure_objects(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + + error = mxt_debug_msg_init(data); + if (error) + return error; + + error = mxt_init_t7_power_cfg(data); + if (error) { + dev_err(&client->dev, "Failed to initialize power cfg\n"); + return error; + } + + /* Check register init values */ + error = mxt_check_reg_init(data); + if (error) { + dev_err(&client->dev, "Error %d initialising configuration\n", + error); + return error; + } + + if (data->T9_reportid_min) { + error = mxt_initialize_t9_input_device(data); + if (error) + return error; + } else if (data->T100_reportid_min) { + error = mxt_initialize_t100_input_device(data); + if (error) + return error; + } else { + dev_warn(&client->dev, "No touch object detected\n"); + } + + data->enable_reporting = true; + return 0; +} + +/* Firmware Version is returned as Major.Minor.Build */ +static ssize_t mxt_fw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n", + data->info->version >> 4, data->info->version & 0xf, + data->info->build); +} + +/* Hardware Version is returned as FamilyID.VariantID */ +static ssize_t mxt_hw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%u.%u\n", + data->info->family_id, data->info->variant_id); +} + +static ssize_t mxt_show_instance(char *buf, int count, + struct mxt_object *object, int instance, + const u8 *val) +{ + int i; + + if (mxt_obj_instances(object) > 1) + count += scnprintf(buf + count, PAGE_SIZE - count, + "Instance %u\n", instance); + + for (i = 0; i < mxt_obj_size(object); i++) + count += scnprintf(buf + count, PAGE_SIZE - count, + "\t[%2u]: %02x (%d)\n", i, val[i], val[i]); + count += scnprintf(buf + count, PAGE_SIZE - count, "\n"); + + return count; +} + +static ssize_t mxt_object_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + struct mxt_object *object; + int count = 0; + int i, j; + int error; + u8 *obuf; + + /* Pre-allocate buffer large enough to hold max sized object. */ + obuf = kmalloc(256, GFP_KERNEL); + if (!obuf) + return -ENOMEM; + + error = 0; + for (i = 0; i < data->info->object_num; i++) { + object = data->object_table + i; + + if (!mxt_object_readable(object->type)) + continue; + + count += scnprintf(buf + count, PAGE_SIZE - count, + "T%u:\n", object->type); + + for (j = 0; j < mxt_obj_instances(object); j++) { + u16 size = mxt_obj_size(object); + u16 addr = object->start_address + j * size; + + error = __mxt_read_reg(data->client, addr, size, obuf); + if (error) + goto done; + + count = mxt_show_instance(buf, count, object, j, obuf); + } + } + +done: + kfree(obuf); + return error ?: count; +} + +static int mxt_check_firmware_format(struct device *dev, + const struct firmware *fw) +{ + unsigned int pos = 0; + char c; + + while (pos < fw->size) { + c = *(fw->data + pos); + + if (c < '0' || (c > '9' && c < 'A') || c > 'F') + return 0; + + pos++; + } + + /* To convert file try + * xxd -r -p mXTXXX__APP_VX-X-XX.enc > maxtouch.fw */ + dev_err(dev, "Aborting: firmware file must be in binary format\n"); + + return -1; +} + +static int mxt_load_fw(struct device *dev) +{ + struct mxt_data *data = dev_get_drvdata(dev); + const struct firmware *fw = NULL; + unsigned int frame_size; + unsigned int pos = 0; + unsigned int retry = 0; + unsigned int frame = 0; + int ret; + + ret = request_firmware(&fw, data->fw_name, dev); + if (ret) { + dev_err(dev, "Unable to open firmware %s\n", data->fw_name); + return ret; + } + + /* Check for incorrect enc file */ + ret = mxt_check_firmware_format(dev, fw); + if (ret) + goto release_firmware; + + if (data->suspended) { + if (data->use_regulator) + mxt_regulator_enable(data); + + enable_irq(data->irq); + data->suspended = false; + } + + if (!data->in_bootloader) { + /* Change to the bootloader mode */ + data->in_bootloader = true; + + ret = mxt_t6_command(data, MXT_COMMAND_RESET, + MXT_BOOT_VALUE, false); + if (ret) + goto release_firmware; + + msleep(MXT_RESET_TIME); + + /* At this stage, do not need to scan since we know + * family ID */ + ret = mxt_lookup_bootloader_address(data, 0); + if (ret) + goto release_firmware; + } else { + enable_irq(data->irq); + } + + mxt_free_object_table(data); + INIT_COMPLETION(data->bl_completion); + + ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); + if (ret) { + /* Bootloader may still be unlocked from previous update + * attempt */ + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false); + if (ret) + goto disable_irq; + } else { + dev_info(dev, "Unlocking bootloader\n"); + + /* Unlock bootloader */ + ret = mxt_send_bootloader_cmd(data, true); + if (ret) + goto disable_irq; + } + + while (pos < fw->size) { + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true); + if (ret) + goto disable_irq; + + frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); + + /* Take account of CRC bytes */ + frame_size += 2; + + /* Write one frame to device */ + ret = mxt_bootloader_write(data, fw->data + pos, frame_size); + if (ret) + goto disable_irq; + + ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true); + if (ret) { + retry++; + + /* Back off by 20ms per retry */ + msleep(retry * 20); + + if (retry > 20) { + dev_err(dev, "Retry count exceeded\n"); + goto disable_irq; + } + } else { + retry = 0; + pos += frame_size; + frame++; + } + + if (frame % 50 == 0) + dev_info(dev, "Sent %d frames, %d/%zd bytes\n", + frame, pos, fw->size); + } + + /* Wait for flash. */ + ret = mxt_wait_for_completion(data, &data->bl_completion, + MXT_FW_RESET_TIME); + if (ret) + goto disable_irq; + + dev_info(dev, "Sent %d frames, %zd bytes\n", frame, pos); + + /* Wait for device to reset. Some bootloader versions do not assert + * the CHG line after bootloading has finished, so ignore error */ + mxt_wait_for_completion(data, &data->bl_completion, + MXT_FW_RESET_TIME); + + data->in_bootloader = false; + +disable_irq: + disable_irq(data->irq); +release_firmware: + release_firmware(fw); + return ret; +} + +static int mxt_update_file_name(struct device *dev, char **file_name, + const char *buf, size_t count) +{ + char *file_name_tmp; + + /* Simple sanity check */ + if (count > 64) { + dev_warn(dev, "File name too long\n"); + return -EINVAL; + } + + file_name_tmp = krealloc(*file_name, count + 1, GFP_KERNEL); + if (!file_name_tmp) { + dev_warn(dev, "no memory\n"); + return -ENOMEM; + } + + *file_name = file_name_tmp; + memcpy(*file_name, buf, count); + + /* Echo into the sysfs entry may append newline at the end of buf */ + if (buf[count - 1] == '\n') + (*file_name)[count - 1] = '\0'; + else + (*file_name)[count] = '\0'; + + return 0; +} + +static ssize_t mxt_update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int error; + + error = mxt_update_file_name(dev, &data->fw_name, buf, count); + if (error) + return error; + + error = mxt_load_fw(dev); + if (error) { + dev_err(dev, "The firmware update failed(%d)\n", error); + count = error; + } else { + dev_info(dev, "The firmware update succeeded\n"); + + data->suspended = false; + + error = mxt_initialize(data); + if (error) + return error; + } + + return count; +} + +static ssize_t mxt_update_cfg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int ret; + + if (data->in_bootloader) { + dev_err(dev, "Not in appmode\n"); + return -EINVAL; + } + + ret = mxt_update_file_name(dev, &data->cfg_name, buf, count); + if (ret) + return ret; + + data->enable_reporting = false; + mxt_free_input_device(data); + + if (data->suspended) { + if (data->use_regulator) + mxt_regulator_enable(data); + else + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + + mxt_acquire_irq(data); + + data->suspended = false; + } + + ret = mxt_configure_objects(data); + if (ret) + goto out; + + ret = count; +out: + return ret; +} + +static ssize_t mxt_debug_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + char c; + + c = data->debug_enabled ? '1' : '0'; + return scnprintf(buf, PAGE_SIZE, "%c\n", c); +} + +static ssize_t mxt_debug_notify_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "0\n"); +} + +static ssize_t mxt_debug_v2_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int i; + + if (sscanf(buf, "%u", &i) == 1 && i < 2) { + if (i == 1) + mxt_debug_msg_enable(data); + else + mxt_debug_msg_disable(data); + + return count; + } else { + dev_dbg(dev, "debug_enabled write error\n"); + return -EINVAL; + } +} + +static ssize_t mxt_debug_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int i; + + if (sscanf(buf, "%u", &i) == 1 && i < 2) { + data->debug_enabled = (i == 1); + + dev_dbg(dev, "%s\n", i ? "debug enabled" : "debug disabled"); + return count; + } else { + dev_dbg(dev, "debug_enabled write error\n"); + return -EINVAL; + } +} + +static int mxt_check_mem_access_params(struct mxt_data *data, loff_t off, + size_t *count) +{ + if (off >= data->mem_size) + return -EIO; + + if (off + *count > data->mem_size) + *count = data->mem_size - off; + + if (*count > MXT_MAX_BLOCK_WRITE) + *count = MXT_MAX_BLOCK_WRITE; + + return 0; +} + +static ssize_t mxt_mem_access_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct mxt_data *data = dev_get_drvdata(dev); + int ret = 0; + + ret = mxt_check_mem_access_params(data, off, &count); + if (ret < 0) + return ret; + + if (count > 0) + ret = __mxt_read_reg(data->client, off, count, buf); + + return ret == 0 ? count : ret; +} + +static ssize_t mxt_mem_access_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, + size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct mxt_data *data = dev_get_drvdata(dev); + int ret = 0; + + ret = mxt_check_mem_access_params(data, off, &count); + if (ret < 0) + return ret; + + if (count > 0) + ret = __mxt_write_reg(data->client, off, count, buf); + + return ret == 0 ? count : 0; +} + +static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL); +static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL); +static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL); +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store); +static DEVICE_ATTR(update_cfg, S_IWUSR, NULL, mxt_update_cfg_store); +static DEVICE_ATTR(debug_v2_enable, S_IWUSR | S_IRUSR, NULL, mxt_debug_v2_enable_store); +static DEVICE_ATTR(debug_notify, S_IRUGO, mxt_debug_notify_show, NULL); +static DEVICE_ATTR(debug_enable, S_IWUSR | S_IRUSR, mxt_debug_enable_show, + mxt_debug_enable_store); + +static struct attribute *mxt_attrs[] = { + &dev_attr_fw_version.attr, + &dev_attr_hw_version.attr, + &dev_attr_object.attr, + &dev_attr_update_fw.attr, + &dev_attr_update_cfg.attr, + &dev_attr_debug_enable.attr, + &dev_attr_debug_v2_enable.attr, + &dev_attr_debug_notify.attr, + NULL +}; + +static const struct attribute_group mxt_attr_group = { + .attrs = mxt_attrs, +}; + +static void mxt_reset_slots(struct mxt_data *data) +{ + struct input_dev *input_dev = data->input_dev; + unsigned int num_mt_slots; + int id; + + num_mt_slots = data->num_touchids + data->num_stylusids; + + for (id = 0; id < num_mt_slots; id++) { + input_mt_slot(input_dev, id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); + } + + mxt_input_sync(input_dev); +} + +static void mxt_start(struct mxt_data *data) +{ + if (!data->suspended || data->in_bootloader) + return; + + if (data->use_regulator) { + mxt_regulator_enable(data); + } else { + /* Discard any messages still in message buffer from before + * chip went to sleep */ + mxt_process_messages_until_invalid(data); + + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + + /* Recalibrate since chip has been in deep sleep */ + mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false); + } + + mxt_acquire_irq(data); + data->enable_reporting = true; + data->suspended = false; +} + +static void mxt_stop(struct mxt_data *data) +{ + if (data->suspended || data->in_bootloader) + return; + + data->enable_reporting = false; + disable_irq(data->irq); + + if (data->use_regulator) + mxt_regulator_disable(data); + else + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); + + mxt_reset_slots(data); + data->suspended = true; +} + +static int mxt_input_open(struct input_dev *dev) +{ + struct mxt_data *data = input_get_drvdata(dev); + + mxt_start(data); + + return 0; +} + +static void mxt_input_close(struct input_dev *dev) +{ + struct mxt_data *data = input_get_drvdata(dev); + + mxt_stop(data); +} + +static int mxt_handle_pdata(struct mxt_data *data) +{ + data->pdata = dev_get_platdata(&data->client->dev); + + /* Use provided platform data if present */ + if (data->pdata) { + if (data->pdata->cfg_name) + mxt_update_file_name(&data->client->dev, + &data->cfg_name, + data->pdata->cfg_name, + strlen(data->pdata->cfg_name)); + + return 0; + } + + data->pdata = kzalloc(sizeof(*data->pdata), GFP_KERNEL); + if (!data->pdata) { + dev_err(&data->client->dev, "Failed to allocate pdata\n"); + return -ENOMEM; + } + + /* Set default parameters */ + data->pdata->irqflags = IRQF_TRIGGER_FALLING; + + return 0; +} + +static int mxt_initialize_t9_input_device(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + const struct mxt_platform_data *pdata = data->pdata; + struct input_dev *input_dev; + int error; + unsigned int num_mt_slots; + int i; + + error = mxt_read_t9_resolution(data); + if (error) + dev_warn(dev, "Failed to initialize T9 resolution\n"); + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + input_dev->name = "Atmel maXTouch Touchscreen"; + input_dev->phys = data->phys; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = dev; + input_dev->open = mxt_input_open; + input_dev->close = mxt_input_close; + + __set_bit(EV_ABS, input_dev->evbit); + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + + if (pdata->t19_num_keys) { + __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + + for (i = 0; i < pdata->t19_num_keys; i++) + if (pdata->t19_keymap[i] != KEY_RESERVED) + input_set_capability(input_dev, EV_KEY, + pdata->t19_keymap[i]); + + __set_bit(BTN_TOOL_FINGER, input_dev->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); + __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); + __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit); + + input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_X, + MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_Y, + MXT_PIXELS_PER_MM); + + input_dev->name = "Atmel maXTouch Touchpad"; + } + + /* For single touch */ + input_set_abs_params(input_dev, ABS_X, + 0, data->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, data->max_y, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + 0, 255, 0, 0); + + /* For multi touch */ + num_mt_slots = data->num_touchids + data->num_stylusids; + error = input_mt_init_slots(input_dev, num_mt_slots); + if (error) { + dev_err(dev, "Error %d initialising slots\n", error); + goto err_free_mem; + } + + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, MXT_MAX_AREA, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, data->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, data->max_y, 0, 0); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, + 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_ORIENTATION, + 0, 255, 0, 0); + + /* For T63 active stylus */ + if (data->T63_reportid_min) { + input_set_capability(input_dev, EV_KEY, BTN_STYLUS); + input_set_capability(input_dev, EV_KEY, BTN_STYLUS2); + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, + 0, MT_TOOL_MAX, 0, 0); + } + + /* For T15 key array */ + if (data->T15_reportid_min) { + data->t15_keystatus = 0; + + for (i = 0; i < data->pdata->t15_num_keys; i++) + input_set_capability(input_dev, EV_KEY, + data->pdata->t15_keymap[i]); + } + + input_set_drvdata(input_dev, data); + + error = input_register_device(input_dev); + if (error) { + dev_err(dev, "Error %d registering input device\n", error); + goto err_free_mem; + } + + data->input_dev = input_dev; + + return 0; + +err_free_mem: + input_free_device(input_dev); + return error; +} + +static int __devinit mxt_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mxt_data *data; + int error; + + data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); + if (!data) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0", + client->adapter->nr, client->addr); + + data->client = client; + data->irq = client->irq; + i2c_set_clientdata(client, data); + + error = mxt_handle_pdata(data); + if (error) + goto err_free_mem; + + init_completion(&data->bl_completion); + init_completion(&data->reset_completion); + init_completion(&data->crc_completion); + mutex_init(&data->debug_msg_lock); + + error = request_threaded_irq(data->irq, NULL, mxt_interrupt, + data->pdata->irqflags | IRQF_ONESHOT, + client->name, data); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_pdata; + } + + mxt_probe_regulators(data); + + disable_irq(data->irq); + + error = mxt_initialize(data); + if (error) + goto err_free_irq; + + error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); + if (error) { + dev_err(&client->dev, "Failure %d creating sysfs group\n", + error); + goto err_free_object; + } + + sysfs_bin_attr_init(&data->mem_access_attr); + data->mem_access_attr.attr.name = "mem_access"; + data->mem_access_attr.attr.mode = S_IRUGO | S_IWUSR; + data->mem_access_attr.read = mxt_mem_access_read; + data->mem_access_attr.write = mxt_mem_access_write; + data->mem_access_attr.size = data->mem_size; + + if (sysfs_create_bin_file(&client->dev.kobj, + &data->mem_access_attr) < 0) { + dev_err(&client->dev, "Failed to create %s\n", + data->mem_access_attr.attr.name); + goto err_remove_sysfs_group; + } + + return 0; + +err_remove_sysfs_group: + sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); +err_free_object: + mxt_free_object_table(data); +err_free_irq: + free_irq(data->irq, data); +err_free_pdata: + if (!dev_get_platdata(&data->client->dev)) + kfree(data->pdata); +err_free_mem: + kfree(data); + return error; +} + +static int __devexit mxt_remove(struct i2c_client *client) +{ + struct mxt_data *data = i2c_get_clientdata(client); + + if (data->mem_access_attr.attr.name) + sysfs_remove_bin_file(&client->dev.kobj, + &data->mem_access_attr); + + sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); + free_irq(data->irq, data); + regulator_put(data->reg_avdd); + regulator_put(data->reg_vdd); + mxt_free_object_table(data); + if (!dev_get_platdata(&data->client->dev)) + kfree(data->pdata); + kfree(data); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mxt_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mxt_data *data = i2c_get_clientdata(client); + struct input_dev *input_dev = data->input_dev; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + mxt_stop(data); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int mxt_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mxt_data *data = i2c_get_clientdata(client); + struct input_dev *input_dev = data->input_dev; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + mxt_start(data); + + mutex_unlock(&input_dev->mutex); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume); + +static void mxt_shutdown(struct i2c_client *client) +{ + struct mxt_data *data = i2c_get_clientdata(client); + + disable_irq(data->irq); +} + +static const struct i2c_device_id mxt_id[] = { + { "qt602240_ts", 0 }, + { "atmel_mxt_ts", 0 }, + { "atmel_mxt_tp", 0 }, + { "mXT224", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mxt_id); + +static struct i2c_driver mxt_driver = { + .driver = { + .name = "atmel_mxt_ts", + .owner = THIS_MODULE, + .pm = &mxt_pm_ops, + }, + .probe = mxt_probe, + .remove = __devexit_p(mxt_remove), + .shutdown = mxt_shutdown, + .id_table = mxt_id, +}; + +static int __init mxt_init(void) +{ + return i2c_add_driver(&mxt_driver); +} + +static void __exit mxt_exit(void) +{ + i2c_del_driver(&mxt_driver); +} + +module_init(mxt_init); +module_exit(mxt_exit); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); +MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index b658cc52b920..897e8ff92fc2 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2121,15 +2121,17 @@ static ssize_t mxt_secure_touch_show(struct device *dev, return scnprintf(buf, PAGE_SIZE, "%u", val); } -static DEVICE_ATTR(secure_touch_enable, 0666, mxt_secure_touch_enable_show, - mxt_secure_touch_enable_store); -static DEVICE_ATTR(secure_touch, 0444, mxt_secure_touch_show, NULL); +static DEVICE_ATTR(secure_touch_enable, S_IRUGO | S_IWUSR | S_IWGRP , + mxt_secure_touch_enable_show, + mxt_secure_touch_enable_store); +static DEVICE_ATTR(secure_touch, S_IRUGO, mxt_secure_touch_show, NULL); #endif -static DEVICE_ATTR(object, 0444, mxt_object_show, NULL); -static DEVICE_ATTR(update_fw, 0664, NULL, mxt_update_fw_store); -static DEVICE_ATTR(force_cfg_update, 0664, NULL, mxt_force_cfg_update_store); -static DEVICE_ATTR(update_object_byte, 0664, NULL, +static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL); +static DEVICE_ATTR(update_fw, S_IWUSR | S_IWGRP , NULL, mxt_update_fw_store); +static DEVICE_ATTR(force_cfg_update, S_IWUSR | S_IWGRP , + NULL, mxt_force_cfg_update_store); +static DEVICE_ATTR(update_object_byte, S_IWUSR | S_IWGRP, NULL, mxt_update_single_byte_store); static struct attribute *mxt_attrs[] = { diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index d95c68c2fa80..2f781f45acbf 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -1550,8 +1550,9 @@ static int ft5x06_ts_probe(struct i2c_client *client, data->family_id = pdata->family_id; err = request_threaded_irq(client->irq, NULL, - ft5x06_ts_interrupt, pdata->irqflags, - client->dev.driver->name, data); + ft5x06_ts_interrupt, + pdata->irqflags | IRQF_ONESHOT, + client->dev.driver->name, data); if (err) { dev_err(&client->dev, "request irq failed\n"); goto free_reset_gpio; diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.c b/drivers/input/touchscreen/gt9xx/gt9xx.c index 3c040a85c280..d819d23c56ca 100644 --- a/drivers/input/touchscreen/gt9xx/gt9xx.c +++ b/drivers/input/touchscreen/gt9xx/gt9xx.c @@ -605,26 +605,6 @@ exit_work_func: /******************************************************* Function: - Timer interrupt service routine for polling mode. -Input: - timer: timer struct pointer -Output: - Timer work mode. - HRTIMER_NORESTART: no restart mode -*********************************************************/ -static enum hrtimer_restart goodix_ts_timer_handler(struct hrtimer *timer) -{ - struct goodix_ts_data - *ts = container_of(timer, struct goodix_ts_data, timer); - - queue_work(ts->goodix_wq, &ts->work); - hrtimer_start(&ts->timer, ktime_set(0, (GTP_POLL_TIME + 6) * 1000000), - HRTIMER_MODE_REL); - return HRTIMER_NORESTART; -} - -/******************************************************* -Function: External interrupt service routine for interrupt mode. Input: irq: interrupt number. @@ -740,16 +720,13 @@ static s8 gtp_enter_doze(struct goodix_ts_data *ts) return ret; } #else -/******************************************************* -Function: - Enter sleep mode. -Input: - ts: private data. -Output: - Executive outcomes. - >0: succeed, otherwise failed. -*******************************************************/ -static s8 gtp_enter_sleep(struct goodix_ts_data *ts) +/** + * gtp_enter_sleep - Enter sleep mode + * @ts: driver private data + * + * Returns zero on success, else an error. + */ +static u8 gtp_enter_sleep(struct goodix_ts_data *ts) { int ret = -1; s8 retry = 0; @@ -771,16 +748,16 @@ static s8 gtp_enter_sleep(struct goodix_ts_data *ts) ret = goodix_power_off(ts); if (ret) { dev_err(&ts->client->dev, "GTP power off failed.\n"); - return 0; + return ret; } - return 1; + return 0; } else { usleep(5000); while (retry++ < GTP_I2C_RETRY_5) { ret = gtp_i2c_write(ts->client, i2c_control_buf, 3); if (ret == 1) { dev_dbg(&ts->client->dev, "GTP enter sleep!"); - return ret; + return 0; } msleep(20); } @@ -1204,7 +1181,7 @@ Output: *******************************************************/ static int gtp_request_irq(struct goodix_ts_data *ts) { - int ret; + int ret = 0; const u8 irq_table[] = GTP_IRQ_TAB; ret = request_threaded_irq(ts->client->irq, NULL, @@ -1212,21 +1189,12 @@ static int gtp_request_irq(struct goodix_ts_data *ts) irq_table[ts->int_trigger_type], ts->client->name, ts); if (ret) { - dev_err(&ts->client->dev, "Request IRQ failed!ERRNO:%d.\n", - ret); - gpio_direction_input(ts->pdata->irq_gpio); - - hrtimer_init(&ts->timer, CLOCK_MONOTONIC, - HRTIMER_MODE_REL); - ts->timer.function = goodix_ts_timer_handler; - hrtimer_start(&ts->timer, ktime_set(1, 0), - HRTIMER_MODE_REL); ts->use_irq = false; return ret; } else { gtp_irq_disable(ts); ts->use_irq = true; - return 0; + return ret; } } @@ -1903,8 +1871,8 @@ static int goodix_ts_probe(struct i2c_client *client, INIT_WORK(&ts->work, goodix_ts_work_func); ret = gtp_request_irq(ts); - if (ret < 0) - dev_info(&client->dev, "GTP works in polling mode.\n"); + if (ret) + dev_info(&client->dev, "GTP request irq failed %d.\n", ret); else dev_info(&client->dev, "GTP works in interrupt mode.\n"); @@ -2084,7 +2052,7 @@ static int goodix_ts_suspend(struct device *dev) ret = gtp_enter_sleep(ts); #endif - if (ret <= 0) + if (ret < 0) dev_err(&ts->client->dev, "GTP early suspend failed.\n"); /* to avoid waking up while not sleeping, * delay 48 + 10ms to ensure reliability @@ -2327,8 +2295,15 @@ static void gtp_esd_check_func(struct work_struct *work) } #endif -static SIMPLE_DEV_PM_OPS(goodix_ts_dev_pm_ops, goodix_ts_suspend, - goodix_ts_resume); +#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)) +static const struct dev_pm_ops goodix_ts_dev_pm_ops = { + .suspend = goodix_ts_suspend, + .resume = goodix_ts_resume, +}; +#else +static const struct dev_pm_ops goodix_ts_dev_pm_ops = { +}; +#endif static const struct i2c_device_id goodix_ts_id[] = { { GTP_I2C_NAME, 0 }, diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.h b/drivers/input/touchscreen/gt9xx/gt9xx.h index 6e59b29c9215..4d656ddc6056 100644 --- a/drivers/input/touchscreen/gt9xx/gt9xx.h +++ b/drivers/input/touchscreen/gt9xx/gt9xx.h @@ -111,11 +111,6 @@ extern u16 total_len; #define GTP_CHANGE_X2Y 0 #define GTP_DRIVER_SEND_CFG 1 #define GTP_HAVE_TOUCH_KEY 1 - -/* auto updated by head_fw_array in gt9xx_firmware.h, - * function together with CONFIG_GT9XX_TOUCHPANEL_UPDATE */ -#define GTP_HEADER_FW_UPDATE 0 - #define GTP_ESD_PROTECT 0 #define GTP_WITH_PEN 0 diff --git a/drivers/input/touchscreen/gt9xx/gt9xx_update.c b/drivers/input/touchscreen/gt9xx/gt9xx_update.c index d7ca2a8992d1..71e8a55a72ff 100644 --- a/drivers/input/touchscreen/gt9xx/gt9xx_update.c +++ b/drivers/input/touchscreen/gt9xx/gt9xx_update.c @@ -36,11 +36,6 @@ #include <linux/workqueue.h> #include <linux/kernel.h> -#if GTP_HEADER_FW_UPDATE -#include <linux/namei.h> -#include <linux/mount.h> -#endif - #define FIRMWARE_NAME_LEN_MAX 256 #define GUP_REG_HW_INFO 0x4220 @@ -641,26 +636,6 @@ static s8 gup_update_config(struct i2c_client *client, return ret; } -#if GTP_HEADER_FW_UPDATE -static u32 gup_get firmware_file(struct i2c_client, - struct st_update_msg *msg, u8 *path) -{ - if (sizeiof(header_fw_array) < (FW_HEAD_LENGTH + - FW_SECTION_LENGTH * 4 + - FW_DSP_ISP_LENGTH + - FW_DSP_LENGTH + - FW_BOOT_LENGTH)) { - dev_err(&client->dev, - "INVALID header_fw_array!"); - return -EINVAL; - } - msg->fw_data = (u8 *)header_fw_array; - msg->fw_len = sizeof(header_fw_array); - dev_dbg(&client->dev, "Found firmware from header file, len=%d", - msg->fw_len); - return 0; -} -#else static s32 gup_get_firmware_file(struct i2c_client *client, struct st_update_msg *msg, u8 *path) { @@ -690,7 +665,6 @@ static s32 gup_get_firmware_file(struct i2c_client *client, release_firmware(fw); return 0; } -#endif static u8 gup_check_firmware_name(struct i2c_client *client, u8 **path_p) diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi4.c b/drivers/input/touchscreen/synaptics_i2c_rmi4.c index f859b98756a5..adfb72c29569 100644 --- a/drivers/input/touchscreen/synaptics_i2c_rmi4.c +++ b/drivers/input/touchscreen/synaptics_i2c_rmi4.c @@ -2424,6 +2424,8 @@ static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data) } } + INIT_LIST_HEAD(&rmi->support_fn_list); + retval = synaptics_rmi4_query_device(rmi4_data); if (retval < 0) { dev_err(&rmi4_data->i2c_client->dev, diff --git a/drivers/iommu/msm_iommu-v0.c b/drivers/iommu/msm_iommu-v0.c index d1cd6994f78e..bc59d6ca5192 100644 --- a/drivers/iommu/msm_iommu-v0.c +++ b/drivers/iommu/msm_iommu-v0.c @@ -31,9 +31,10 @@ #include <mach/iommu_hw-v0.h> #include <mach/msm_iommu_priv.h> #include <mach/iommu.h> -#include <mach/msm_smem.h> #include <mach/msm_bus.h> +#include <soc/qcom/smem.h> + /* Sharability attributes of MSM IOMMU mappings */ #define MSM_IOMMU_ATTR_NON_SH 0x0 #define MSM_IOMMU_ATTR_SH 0x4 diff --git a/drivers/iommu/msm_iommu-v1.c b/drivers/iommu/msm_iommu-v1.c index d7552586e8c0..d47210099320 100644 --- a/drivers/iommu/msm_iommu-v1.c +++ b/drivers/iommu/msm_iommu-v1.c @@ -708,7 +708,6 @@ static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) goto unlock; } } - SET_MICRO_MMU_CTRL_RESERVED(iommu_drvdata->base, 0x3); program_iommu_bfb_settings(iommu_drvdata->base, iommu_drvdata->bfb_settings); set_m2v = true; diff --git a/drivers/iommu/msm_iommu_dev-v0.c b/drivers/iommu/msm_iommu_dev-v0.c index d9c24ae8d218..bf3bf4b5e95f 100644 --- a/drivers/iommu/msm_iommu_dev-v0.c +++ b/drivers/iommu/msm_iommu_dev-v0.c @@ -185,11 +185,11 @@ static int msm_iommu_parse_dt(struct platform_device *pdev, } drvdata->glb_base = drvdata->base; - if (!of_property_read_u32(pdev->dev.of_node, "qti,glb-offset", + if (!of_property_read_u32(pdev->dev.of_node, "qcom,glb-offset", &glb_offset)) { drvdata->glb_base += glb_offset; } else { - pr_err("%s: Missing property qti,glb-offset\n", __func__); + pr_err("%s: Missing property qcom,glb-offset\n", __func__); ret = -EINVAL; goto fail; } @@ -206,7 +206,7 @@ static int msm_iommu_parse_dt(struct platform_device *pdev, } needs_alt_core_clk = of_property_read_bool(pdev->dev.of_node, - "qti,needs-alt-core-clk"); + "qcom,needs-alt-core-clk"); ret = __get_clocks(pdev, drvdata, needs_alt_core_clk); @@ -217,7 +217,7 @@ static int msm_iommu_parse_dt(struct platform_device *pdev, drvdata->ttbr_split = 0; drvdata->needs_rem_spinlock = of_property_read_bool(pdev->dev.of_node, - "qti,msm-enable-remote-spinlock"); + "qcom,msm-enable-remote-spinlock"); if (drvdata->needs_rem_spinlock) pr_info("%s enabled remote spinlock\n", drvdata->name); @@ -288,24 +288,24 @@ static int msm_iommu_pmon_parse_dt(struct platform_device *pdev, pmon_info->iommu.evt_irq = platform_get_irq(pdev, 0); ret = of_property_read_u32(pdev->dev.of_node, - "qti,iommu-pmu-ngroups", + "qcom,iommu-pmu-ngroups", &pmon_info->num_groups); if (ret) { - pr_err("Error reading qti,iommu-pmu-ngroups\n"); + pr_err("Error reading qcom,iommu-pmu-ngroups\n"); goto fail; } ret = of_property_read_u32(pdev->dev.of_node, - "qti,iommu-pmu-ncounters", + "qcom,iommu-pmu-ncounters", &pmon_info->num_counters); if (ret) { - pr_err("Error reading qti,iommu-pmu-ncounters\n"); + pr_err("Error reading qcom,iommu-pmu-ncounters\n"); goto fail; } if (!of_get_property(pdev->dev.of_node, - "qti,iommu-pmu-event-classes", + "qcom,iommu-pmu-event-classes", &cls_prop_size)) { - pr_err("Error reading qti,iommu-pmu-event-classes\n"); + pr_err("Error reading qcom,iommu-pmu-event-classes\n"); return -EINVAL; } @@ -320,11 +320,11 @@ static int msm_iommu_pmon_parse_dt(struct platform_device *pdev, pmon_info->nevent_cls_supported = cls_prop_size / sizeof(u32); ret = of_property_read_u32_array(pdev->dev.of_node, - "qti,iommu-pmu-event-classes", + "qcom,iommu-pmu-event-classes", pmon_info->event_cls_supported, pmon_info->nevent_cls_supported); if (ret) { - pr_err("Error reading qti,iommu-pmu-event-classes\n"); + pr_err("Error reading qcom,iommu-pmu-event-classes\n"); return ret; } } else { @@ -516,7 +516,7 @@ static int msm_iommu_ctx_parse_dt(struct platform_device *pdev, goto out; } - if (!of_get_property(pdev->dev.of_node, "qti,iommu-ctx-mids", + if (!of_get_property(pdev->dev.of_node, "qcom,iommu-ctx-mids", &nmid_array_size)) { pr_err("Could not find iommu-ctx-mids property\n"); ret = -EINVAL; @@ -530,7 +530,7 @@ static int msm_iommu_ctx_parse_dt(struct platform_device *pdev, } nmid = nmid_array_size / sizeof(*ctx_drvdata->sids); - if (of_property_read_u32_array(pdev->dev.of_node, "qti,iommu-ctx-mids", + if (of_property_read_u32_array(pdev->dev.of_node, "qcom,iommu-ctx-mids", ctx_drvdata->sids, nmid)) { pr_err("Could not find iommu-ctx-mids property\n"); ret = -EINVAL; @@ -666,7 +666,7 @@ static int msm_iommu_ctx_remove(struct platform_device *pdev) static struct of_device_id msm_iommu_match_table[] = { - { .compatible = "qti,msm-smmu-v0", }, + { .compatible = "qcom,msm-smmu-v0", }, {} }; @@ -680,7 +680,7 @@ static struct platform_driver msm_iommu_driver = { }; static struct of_device_id msm_iommu_v0_ctx_match_table[] = { - { .compatible = "qti,msm-smmu-v0-ctx", }, + { .compatible = "qcom,msm-smmu-v0-ctx", }, {} }; diff --git a/drivers/iommu/msm_iommu_dev-v1.c b/drivers/iommu/msm_iommu_dev-v1.c index 3200d7e51c25..0799f8792864 100644 --- a/drivers/iommu/msm_iommu_dev-v1.c +++ b/drivers/iommu/msm_iommu_dev-v1.c @@ -33,11 +33,11 @@ static struct of_device_id msm_iommu_ctx_match_table[]; #ifdef CONFIG_IOMMU_LPAE -static const char *BFB_REG_NODE_NAME = "qti,iommu-lpae-bfb-regs"; -static const char *BFB_DATA_NODE_NAME = "qti,iommu-lpae-bfb-data"; +static const char *BFB_REG_NODE_NAME = "qcom,iommu-lpae-bfb-regs"; +static const char *BFB_DATA_NODE_NAME = "qcom,iommu-lpae-bfb-data"; #else -static const char *BFB_REG_NODE_NAME = "qti,iommu-bfb-regs"; -static const char *BFB_DATA_NODE_NAME = "qti,iommu-bfb-data"; +static const char *BFB_REG_NODE_NAME = "qcom,iommu-bfb-regs"; +static const char *BFB_DATA_NODE_NAME = "qcom,iommu-bfb-data"; #endif static int msm_iommu_parse_bfb_settings(struct platform_device *pdev, @@ -141,14 +141,14 @@ static inline void get_secure_ctx(struct device_node *node, static void get_secure_id(struct device_node *node, struct msm_iommu_drvdata *drvdata) { - of_property_read_u32(node, "qti,iommu-secure-id", &drvdata->sec_id); + of_property_read_u32(node, "qcom,iommu-secure-id", &drvdata->sec_id); } static void get_secure_ctx(struct device_node *node, struct msm_iommu_ctx_drvdata *ctx_drvdata) { ctx_drvdata->secure_context = - of_property_read_bool(node, "qti,secure-context"); + of_property_read_bool(node, "qcom,secure-context"); } #endif @@ -203,7 +203,7 @@ static int msm_iommu_parse_dt(struct platform_device *pdev, } drvdata->halt_enabled = of_property_read_bool(pdev->dev.of_node, - "qti,iommu-enable-halt"); + "qcom,iommu-enable-halt"); ret = of_platform_populate(pdev->dev.of_node, msm_iommu_ctx_match_table, @@ -232,24 +232,24 @@ static int msm_iommu_pmon_parse_dt(struct platform_device *pdev, pmon_info->iommu.evt_irq = platform_get_irq(pdev, 0); ret = of_property_read_u32(pdev->dev.of_node, - "qti,iommu-pmu-ngroups", + "qcom,iommu-pmu-ngroups", &pmon_info->num_groups); if (ret) { - pr_err("Error reading qti,iommu-pmu-ngroups\n"); + pr_err("Error reading qcom,iommu-pmu-ngroups\n"); goto fail; } ret = of_property_read_u32(pdev->dev.of_node, - "qti,iommu-pmu-ncounters", + "qcom,iommu-pmu-ncounters", &pmon_info->num_counters); if (ret) { - pr_err("Error reading qti,iommu-pmu-ncounters\n"); + pr_err("Error reading qcom,iommu-pmu-ncounters\n"); goto fail; } if (!of_get_property(pdev->dev.of_node, - "qti,iommu-pmu-event-classes", + "qcom,iommu-pmu-event-classes", &cls_prop_size)) { - pr_err("Error reading qti,iommu-pmu-event-classes\n"); + pr_err("Error reading qcom,iommu-pmu-event-classes\n"); return -EINVAL; } @@ -264,11 +264,11 @@ static int msm_iommu_pmon_parse_dt(struct platform_device *pdev, pmon_info->nevent_cls_supported = cls_prop_size / sizeof(u32); ret = of_property_read_u32_array(pdev->dev.of_node, - "qti,iommu-pmu-event-classes", + "qcom,iommu-pmu-event-classes", pmon_info->event_cls_supported, pmon_info->nevent_cls_supported); if (ret) { - pr_err("Error reading qti,iommu-pmu-event-classes\n"); + pr_err("Error reading qcom,iommu-pmu-event-classes\n"); return ret; } } else { @@ -309,7 +309,7 @@ static int msm_iommu_probe(struct platform_device *pdev) return PTR_ERR(drvdata->gdsc); drvdata->alt_gdsc = devm_regulator_get(&pdev->dev, - "qti,alt-vdd"); + "qcom,alt-vdd"); if (IS_ERR(drvdata->alt_gdsc)) drvdata->alt_gdsc = NULL; } else { @@ -325,7 +325,7 @@ static int msm_iommu_probe(struct platform_device *pdev) return PTR_ERR(drvdata->clk); needs_alt_core_clk = of_property_read_bool(pdev->dev.of_node, - "qti,needs-alt-core-clk"); + "qcom,needs-alt-core-clk"); if (needs_alt_core_clk) { drvdata->aclk = devm_clk_get(&pdev->dev, "alt_core_clk"); if (IS_ERR(drvdata->aclk)) @@ -333,7 +333,7 @@ static int msm_iommu_probe(struct platform_device *pdev) } needs_alt_iface_clk = of_property_read_bool(pdev->dev.of_node, - "qti,needs-alt-iface-clk"); + "qcom,needs-alt-iface-clk"); if (needs_alt_iface_clk) { drvdata->aiclk = devm_clk_get(&pdev->dev, "alt_iface_clk"); if (IS_ERR(drvdata->aclk)) @@ -341,7 +341,7 @@ static int msm_iommu_probe(struct platform_device *pdev) } drvdata->no_atos_support = of_property_read_bool(pdev->dev.of_node, - "qti,no-atos-support"); + "qcom,no-atos-support"); if (clk_get_rate(drvdata->clk) == 0) { ret = clk_round_rate(drvdata->clk, 1000); @@ -495,7 +495,7 @@ static int msm_iommu_ctx_parse_dt(struct platform_device *pdev, &ctx_drvdata->name)) ctx_drvdata->name = dev_name(&pdev->dev); - if (!of_get_property(pdev->dev.of_node, "qti,iommu-ctx-sids", &nsid)) { + if (!of_get_property(pdev->dev.of_node, "qcom,iommu-ctx-sids", &nsid)) { ret = -EINVAL; goto out; } @@ -504,7 +504,7 @@ static int msm_iommu_ctx_parse_dt(struct platform_device *pdev, goto out; } - if (of_property_read_u32_array(pdev->dev.of_node, "qti,iommu-ctx-sids", + if (of_property_read_u32_array(pdev->dev.of_node, "qcom,iommu-ctx-sids", ctx_drvdata->sids, nsid / sizeof(*ctx_drvdata->sids))) { ret = -EINVAL; @@ -551,8 +551,8 @@ static int msm_iommu_ctx_remove(struct platform_device *pdev) } static struct of_device_id msm_iommu_match_table[] = { - { .compatible = "qti,msm-smmu-v1", }, - { .compatible = "qti,msm-smmu-v2", }, + { .compatible = "qcom,msm-smmu-v1", }, + { .compatible = "qcom,msm-smmu-v2", }, {} }; @@ -566,8 +566,8 @@ static struct platform_driver msm_iommu_driver = { }; static struct of_device_id msm_iommu_ctx_match_table[] = { - { .compatible = "qti,msm-smmu-v1-ctx", }, - { .compatible = "qti,msm-smmu-v2-ctx", }, + { .compatible = "qcom,msm-smmu-v1-ctx", }, + { .compatible = "qcom,msm-smmu-v2-ctx", }, {} }; diff --git a/drivers/iommu/msm_iommu_domains.c b/drivers/iommu/msm_iommu_domains.c index 1281167b01d3..6b9ecad6165d 100644 --- a/drivers/iommu/msm_iommu_domains.c +++ b/drivers/iommu/msm_iommu_domains.c @@ -573,7 +573,7 @@ static int find_and_add_contexts(struct iommu_group *group, for (i = 0; i < num_contexts; ++i) { ctx_node = of_parse_phandle((struct device_node *) node, - "qti,iommu-contexts", i); + "qcom,iommu-contexts", i); if (!ctx_node) { pr_err("Unable to parse phandle #%u\n", i); ret_val = -EINVAL; @@ -613,7 +613,7 @@ static int create_and_add_domain(struct iommu_group *group, int secure_domain; int l2_redirect; - if (of_get_property(node, "qti,virtual-addr-pool", &array_size)) { + if (of_get_property(node, "qcom,virtual-addr-pool", &array_size)) { l.npartitions = array_size / sizeof(unsigned int) / 2; part = kmalloc( sizeof(struct msm_iova_partition) * l.npartitions, @@ -633,7 +633,7 @@ static int create_and_add_domain(struct iommu_group *group, } ret_val = of_property_read_u32_array(node, - "qti,virtual-addr-pool", + "qcom,virtual-addr-pool", addr_array, array_size/sizeof(unsigned int)); if (ret_val) { @@ -663,10 +663,10 @@ static int create_and_add_domain(struct iommu_group *group, l.client_name = name; l.partitions = part; - secure_domain = of_property_read_bool(node, "qti,secure-domain"); + secure_domain = of_property_read_bool(node, "qcom,secure-domain"); l.is_secure = (secure_domain) ? MSM_IOMMU_DOMAIN_SECURE : 0; - l2_redirect = of_property_read_bool(node, "qti,l2-redirect"); + l2_redirect = of_property_read_bool(node, "qcom,l2-redirect"); l.domain_flags = (l2_redirect) ? MSM_IOMMU_DOMAIN_PT_CACHEABLE : 0; domain_no = msm_register_domain(&l); @@ -756,8 +756,8 @@ static int iommu_domain_parse_dt(const struct device_node *dt_node) } iommu_group_set_name(group, name); - if (!of_get_property(node, "qti,iommu-contexts", &sz)) { - pr_err("Could not find qti,iommu-contexts property\n"); + if (!of_get_property(node, "qcom,iommu-contexts", &sz)) { + pr_err("Could not find qcom,iommu-contexts property\n"); ret_val = -EINVAL; goto free_group; } @@ -873,7 +873,7 @@ static int iommu_domain_exit(struct platform_device *pdev) } static struct of_device_id msm_iommu_domain_match_table[] = { - { .name = "qti,iommu-domains", }, + { .name = "qcom,iommu-domains", }, {} }; diff --git a/drivers/iommu/msm_iommu_sec.c b/drivers/iommu/msm_iommu_sec.c index 9ae950b8776c..65dcb42fcf70 100644 --- a/drivers/iommu/msm_iommu_sec.c +++ b/drivers/iommu/msm_iommu_sec.c @@ -283,8 +283,8 @@ static int msm_iommu_sec_ptbl_init(void) unsigned int spare; int ret, ptbl_ret = 0; - for_each_compatible_node(np, NULL, "qti,msm-smmu-v1") - if (of_find_property(np, "qti,iommu-secure-id", NULL)) + for_each_compatible_node(np, NULL, "qcom,msm-smmu-v1") + if (of_find_property(np, "qcom,iommu-secure-id", NULL)) break; if (!np) diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c index 9a85d2d0925a..b18f0e43e534 100644 --- a/drivers/leds/leds-qpnp.c +++ b/drivers/leds/leds-qpnp.c @@ -848,7 +848,7 @@ static int qpnp_mpp_set(struct qpnp_led_data *led) duty_us = (led->mpp_cfg->pwm_cfg->pwm_period_us * led->cdev.brightness) / LED_FULL; /*config pwm for brightness scaling*/ - rc = pwm_config(led->mpp_cfg->pwm_cfg->pwm_dev, + rc = pwm_config_us(led->mpp_cfg->pwm_cfg->pwm_dev, duty_us, led->mpp_cfg->pwm_cfg->pwm_period_us); if (rc < 0) { @@ -1402,7 +1402,7 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led) if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) { duty_us = (led->kpdbl_cfg->pwm_cfg->pwm_period_us * led->cdev.brightness) / KPDBL_MAX_LEVEL; - rc = pwm_config(led->kpdbl_cfg->pwm_cfg->pwm_dev, + rc = pwm_config_us(led->kpdbl_cfg->pwm_cfg->pwm_dev, duty_us, led->kpdbl_cfg->pwm_cfg->pwm_period_us); if (rc < 0) { @@ -1424,7 +1424,7 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led) led->kpdbl_cfg->pwm_cfg->default_mode; if (led->kpdbl_cfg->always_on) { - rc = pwm_config(led->kpdbl_cfg->pwm_cfg->pwm_dev, 0, + rc = pwm_config_us(led->kpdbl_cfg->pwm_cfg->pwm_dev, 0, led->kpdbl_cfg->pwm_cfg->pwm_period_us); if (rc < 0) { dev_err(&led->spmi_dev->dev, @@ -1473,7 +1473,8 @@ static int qpnp_rgb_set(struct qpnp_led_data *led) if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) { duty_us = (led->rgb_cfg->pwm_cfg->pwm_period_us * led->cdev.brightness) / LED_FULL; - rc = pwm_config(led->rgb_cfg->pwm_cfg->pwm_dev, duty_us, + rc = pwm_config_us(led->rgb_cfg->pwm_cfg->pwm_dev, + duty_us, led->rgb_cfg->pwm_cfg->pwm_period_us); if (rc < 0) { dev_err(&led->spmi_dev->dev, @@ -3693,10 +3694,15 @@ static int qpnp_leds_remove(struct spmi_device *spmi) return 0; } + +#ifdef CONFIG_OF static struct of_device_id spmi_match_table[] = { - { .compatible = "qcom,leds-qpnp", - } + { .compatible = "qcom,leds-qpnp",}, + { }, }; +#else +#define spmi_match_table NULL +#endif static struct spmi_driver qpnp_leds_driver = { .driver = { diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c index 33bb369a8e79..d278d7cfcc1f 100644 --- a/drivers/media/dvb-core/dmxdev.c +++ b/drivers/media/dvb-core/dmxdev.c @@ -2862,7 +2862,7 @@ static int dvb_dmxdev_ts_event_cb(struct dmx_ts_feed *feed, event.type = DMX_EVENT_EOS; dvb_dmxdev_add_event(events, &event); spin_unlock(&dmxdevfilter->dev->lock); - wake_up_all(&dmxdevfilter->buffer.queue); + wake_up_all(&buffer->queue); return 0; } @@ -2873,7 +2873,7 @@ static int dvb_dmxdev_ts_event_cb(struct dmx_ts_feed *feed, event.params.marker.id = dmx_data_ready->marker.id; dvb_dmxdev_add_event(events, &event); spin_unlock(&dmxdevfilter->dev->lock); - wake_up_all(&dmxdevfilter->buffer.queue); + wake_up_all(&buffer->queue); return 0; } @@ -2957,7 +2957,7 @@ static int dvb_dmxdev_ts_event_cb(struct dmx_ts_feed *feed, return 0; } - free = dvb_ringbuffer_free(&dmxdevfilter->buffer); + free = dvb_ringbuffer_free(buffer); if ((DMX_OVERRUN_ERROR == dmx_data_ready->status) || (dmx_data_ready->data_length > free)) { @@ -2982,8 +2982,7 @@ static int dvb_dmxdev_ts_event_cb(struct dmx_ts_feed *feed, if (dmxdevfilter->params.pes.output == DMX_OUT_TAP) { if ((dmx_data_ready->status == DMX_OK) && (!events->current_event_data_size)) { - events->current_event_start_offset = - dmxdevfilter->buffer.pwrite; + events->current_event_start_offset = buffer->pwrite; } else if (dmx_data_ready->status == DMX_OK_PES_END) { event.type = DMX_EVENT_NEW_PES; @@ -2992,7 +2991,7 @@ static int dvb_dmxdev_ts_event_cb(struct dmx_ts_feed *feed, event.params.pes.start_offset = (events->current_event_start_offset + dmx_data_ready->pes_end.start_gap) % - dmxdevfilter->buffer.size; + buffer->size; event.params.pes.actual_length = dmx_data_ready->pes_end.actual_length; @@ -3021,12 +3020,11 @@ static int dvb_dmxdev_ts_event_cb(struct dmx_ts_feed *feed, } } else { if (!events->current_event_data_size) - events->current_event_start_offset = - dmxdevfilter->buffer.pwrite; + events->current_event_start_offset = buffer->pwrite; } events->current_event_data_size += dmx_data_ready->data_length; - DVB_RINGBUFFER_PUSH(&dmxdevfilter->buffer, dmx_data_ready->data_length); + DVB_RINGBUFFER_PUSH(buffer, dmx_data_ready->data_length); if ((dmxdevfilter->params.pes.output == DMX_OUT_TS_TAP) || (dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)) { @@ -3396,12 +3394,15 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter) * even if user sets the buffer in deocder * filter as external buffer. */ - if ((filter->type == DMXDEV_TYPE_PES) && - (filter->params.pes.output == DMX_OUT_DECODER)) + if (filter->type == DMXDEV_TYPE_PES && + (filter->params.pes.output == DMX_OUT_DECODER || + filter->params.pes.output == DMX_OUT_TS_TAP)) filter->buffer_mode = DMX_BUFFER_MODE_INTERNAL; - if ((filter->buffer_mode == DMX_BUFFER_MODE_EXTERNAL) || - dvb_filter_external_buffer_only(dmxdev, filter)) + if (!(filter->type == DMXDEV_TYPE_PES && + filter->params.pes.output == DMX_OUT_TS_TAP) && + (filter->buffer_mode == DMX_BUFFER_MODE_EXTERNAL || + dvb_filter_external_buffer_only(dmxdev, filter))) return -ENOMEM; mem = vmalloc_user(filter->buffer.size); @@ -4623,6 +4624,7 @@ static int dvb_dmxdev_dbgfs_print(struct seq_file *s, void *p) struct dmx_buffer_status buffer_status; struct dmx_scrambling_bits scrambling_bits; const char *pes_feeds[] = {"DEC", "PES", "DVR", "REC"}; + int ret; if (!dmxdev) return 0; @@ -4650,8 +4652,14 @@ static int dvb_dmxdev_dbgfs_print(struct seq_file *s, void *p) dvb_dmxdev_get_scrambling_bits(filter, &scrambling_bits); - if (0 == dvb_dmxdev_get_buffer_status( - filter, &buffer_status)) { + if (filter->type == DMXDEV_TYPE_PES && + filter->params.pes.output == DMX_OUT_TS_TAP) + ret = dvb_dvr_get_buffer_status(dmxdev, + O_RDONLY, &buffer_status); + else + ret = dvb_dmxdev_get_buffer_status(filter, + &buffer_status); + if (!ret) { seq_printf(s, "size: %08d, ", buffer_status.size); seq_printf(s, "fullness: %08d, ", diff --git a/drivers/media/platform/msm/broadcast/ensigma_uccp330.c b/drivers/media/platform/msm/broadcast/ensigma_uccp330.c index 5b8a1553885f..e0fb662b3f71 100644 --- a/drivers/media/platform/msm/broadcast/ensigma_uccp330.c +++ b/drivers/media/platform/msm/broadcast/ensigma_uccp330.c @@ -654,7 +654,7 @@ static const struct dev_pm_ops demod_dev_pm_ops = { /* Platform driver information */ static struct of_device_id msm_demod_match_table[] = { - {.compatible = "qti,msm-demod"} + {.compatible = "qcom,msm-demod"} }; static struct platform_driver msm_demod_driver = { diff --git a/drivers/media/platform/msm/broadcast/tsc.c b/drivers/media/platform/msm/broadcast/tsc.c index e4f08aa59fa6..39b9ba906253 100644 --- a/drivers/media/platform/msm/broadcast/tsc.c +++ b/drivers/media/platform/msm/broadcast/tsc.c @@ -67,6 +67,9 @@ module_param(tsc_iommu_bypass, int, S_IRUGO | S_IWUSR | S_IWGRP); #define CICAM_CLK_RATE_12MHZ 12000000 #define CICAM_CLK_RATE_9MHZ 8971962 #define CICAM_CLK_RATE_7MHZ 7218045 +/* Rates for TSC serial and parallel clocks */ +#define TSC_SER_CLK_RATE 192000000 +#define TSC_PAR_CLK_RATE 24000000 /* * TSC register offsets @@ -189,13 +192,15 @@ enum transaction_state { }; /** - * enum pcmcia_state - states for the pcmcia pinctrl states - */ +* enum pcmcia_state - states for the pcmcia pinctrl states +* Note: the numbers here corresponds to the numbers of enum tsc_cam_personality +* in tsc.h file. +*/ enum pcmcia_state { - DISABLE, - PC_CARD, - CI_CARD, - CI_PLUS + PCMCIA_STATE_DISABLE = 0, + PCMCIA_STATE_CI_CARD = 1, + PCMCIA_STATE_CI_PLUS = 2, + PCMCIA_STATE_PC_CARD = 3 }; /** @@ -206,6 +211,7 @@ enum pcmcia_state { * @domain_num: TSC IOMMU domain number. * @partition_num: TSC iommu partition number. * @ion_client: TSC IOMMU client. + * @iommu_group_name TSC IOMMU group name. */ struct iommu_info { struct iommu_group *group; @@ -213,6 +219,7 @@ struct iommu_info { int domain_num; int partition_num; struct ion_client *ion_client; + const char *iommu_group_name; }; /** @@ -402,21 +409,6 @@ struct tsc_device { struct dentry *debugfs_entry; }; -/** - * struct msm_tsc_platform_data - TSC platform data - * - * @iommu_group: TSC IOMMU group name. - * @iommu_partition: TSC IOMMU partition number. - * @ts0_config: The TS0 configuration (1=A, 2=B). - * @ts1_config: The TS1 configuration (1=A, 2=B). - */ -struct msm_tsc_platform_data { - const char *iommu_group; - u32 iommu_partition; - u32 ts0_config; - u32 ts1_config; -}; - /* Global TSC device class */ static struct class *tsc_class; @@ -821,16 +813,20 @@ static int tsc_suspend_ts_pins(enum tsc_source source) struct pinctrl_info *ppinctrl = &tsc_device->pinctrl_info; struct pinctrl_current_state *pcurr_state = &ppinctrl->curr_state; + if (mutex_lock_interruptible(&tsc_device->mutex)) + return -ERESTARTSYS; + if (source == TSC_SOURCE_EXTERNAL0) { if (!ppinctrl->is_ts0) { pr_err("%s: No TS0-in pinctrl definitions were found in the TSC devicetree\n", __func__); + mutex_unlock(&tsc_device->mutex); return -EPERM; } /* Transition from current pinctrl state to curr + ts0 sleep */ switch (pcurr_state->pcmcia_state) { - case DISABLE: + case PCMCIA_STATE_DISABLE: if (pcurr_state->ts1) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->ts1); @@ -838,7 +834,7 @@ static int tsc_suspend_ts_pins(enum tsc_source source) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->disable); break; - case PC_CARD: + case PCMCIA_STATE_PC_CARD: if (pcurr_state->ts1) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->ts1_pc_card); @@ -846,7 +842,7 @@ static int tsc_suspend_ts_pins(enum tsc_source source) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->pc_card); break; - case CI_CARD: + case PCMCIA_STATE_CI_CARD: if (pcurr_state->ts1) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->ts1_ci_card); @@ -854,7 +850,7 @@ static int tsc_suspend_ts_pins(enum tsc_source source) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->ci_card); break; - case CI_PLUS: + case PCMCIA_STATE_CI_PLUS: if (pcurr_state->ts1) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->ts1_ci_plus); @@ -867,12 +863,13 @@ static int tsc_suspend_ts_pins(enum tsc_source source) if (!ppinctrl->is_ts1) { pr_err("%s: No TS1-in pinctrl definitions were found in the TSC devicetree\n", __func__); + mutex_unlock(&tsc_device->mutex); return -EPERM; } /* Transition from current pinctrl state to curr + ts1 sleep */ switch (pcurr_state->pcmcia_state) { - case DISABLE: + case PCMCIA_STATE_DISABLE: if (pcurr_state->ts0) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->ts0); @@ -880,7 +877,7 @@ static int tsc_suspend_ts_pins(enum tsc_source source) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->disable); break; - case PC_CARD: + case PCMCIA_STATE_PC_CARD: if (pcurr_state->ts0) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->ts0_pc_card); @@ -888,7 +885,7 @@ static int tsc_suspend_ts_pins(enum tsc_source source) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->pc_card); break; - case CI_CARD: + case PCMCIA_STATE_CI_CARD: if (pcurr_state->ts0) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->ts0_ci_card); @@ -896,7 +893,7 @@ static int tsc_suspend_ts_pins(enum tsc_source source) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->ci_card); break; - case CI_PLUS: + case PCMCIA_STATE_CI_PLUS: if (pcurr_state->ts0) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->ts0_ci_plus); @@ -910,6 +907,7 @@ static int tsc_suspend_ts_pins(enum tsc_source source) if (ret != 0) { pr_err("%s: error disabling TS-in pins. ret value = %d\n", __func__, ret); + mutex_unlock(&tsc_device->mutex); return -EINVAL; } @@ -919,6 +917,8 @@ static int tsc_suspend_ts_pins(enum tsc_source source) else pcurr_state->ts1 = false; + mutex_unlock(&tsc_device->mutex); + return 0; } @@ -938,16 +938,20 @@ static int tsc_activate_ts_pins(enum tsc_source source) struct pinctrl_info *ppinctrl = &tsc_device->pinctrl_info; struct pinctrl_current_state *pcurr_state = &ppinctrl->curr_state; + if (mutex_lock_interruptible(&tsc_device->mutex)) + return -ERESTARTSYS; + if (source == TSC_SOURCE_EXTERNAL0) { if (!ppinctrl->is_ts0) { pr_err("%s: No TS0-in pinctrl definitions were found in the TSC devicetree\n", __func__); + mutex_unlock(&tsc_device->mutex); return -EPERM; } /* Transition from current pinctrl state to curr + ts0 active */ switch (pcurr_state->pcmcia_state) { - case DISABLE: + case PCMCIA_STATE_DISABLE: if (pcurr_state->ts1) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->dual_ts); @@ -955,7 +959,7 @@ static int tsc_activate_ts_pins(enum tsc_source source) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->ts0); break; - case PC_CARD: + case PCMCIA_STATE_PC_CARD: if (pcurr_state->ts1) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->dual_ts_pc_card); @@ -963,7 +967,7 @@ static int tsc_activate_ts_pins(enum tsc_source source) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->ts0_pc_card); break; - case CI_CARD: + case PCMCIA_STATE_CI_CARD: if (pcurr_state->ts1) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->dual_ts_ci_card); @@ -971,7 +975,7 @@ static int tsc_activate_ts_pins(enum tsc_source source) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->ts0_ci_card); break; - case CI_PLUS: + case PCMCIA_STATE_CI_PLUS: if (pcurr_state->ts1) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->dual_ts_ci_plus); @@ -984,12 +988,13 @@ static int tsc_activate_ts_pins(enum tsc_source source) if (!ppinctrl->is_ts1) { pr_err("%s: No TS1-in pinctrl definitions were found in the TSC devicetree\n", __func__); + mutex_unlock(&tsc_device->mutex); return -EPERM; } /* Transition from current pinctrl state to curr + ts1 active */ switch (pcurr_state->pcmcia_state) { - case DISABLE: + case PCMCIA_STATE_DISABLE: if (pcurr_state->ts0) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->dual_ts); @@ -997,7 +1002,7 @@ static int tsc_activate_ts_pins(enum tsc_source source) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->ts1); break; - case PC_CARD: + case PCMCIA_STATE_PC_CARD: if (pcurr_state->ts0) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->dual_ts_pc_card); @@ -1005,7 +1010,7 @@ static int tsc_activate_ts_pins(enum tsc_source source) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->ts1_pc_card); break; - case CI_CARD: + case PCMCIA_STATE_CI_CARD: if (pcurr_state->ts0) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->dual_ts_ci_card); @@ -1013,7 +1018,7 @@ static int tsc_activate_ts_pins(enum tsc_source source) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->ts1_ci_card); break; - case CI_PLUS: + case PCMCIA_STATE_CI_PLUS: if (pcurr_state->ts0) ret = pinctrl_select_state(ppinctrl->pinctrl, ppinctrl->dual_ts_ci_plus); @@ -1027,6 +1032,7 @@ static int tsc_activate_ts_pins(enum tsc_source source) if (ret != 0) { pr_err("%s: error activating TS-in pins. ret value = %d\n", __func__, ret); + mutex_unlock(&tsc_device->mutex); return -EINVAL; } @@ -1036,6 +1042,8 @@ static int tsc_activate_ts_pins(enum tsc_source source) else pcurr_state->ts1 = true; + mutex_unlock(&tsc_device->mutex); + return 0; } @@ -1496,6 +1504,96 @@ err_copy_arg: } /** + * tsc_personality_change() - change the PCMCIA pins state. + * + * @pcmcia_state: The new state of the PCMCIA pins. + * + * Configure the TLMM pins of the PCMCIA according to received state and + * the current pinctrl configuration of the other pins. This function assums the + * PCMCIA pinctrl definitions were successfully parsed from the devicetree (this + * check is done at open device). + * + * Return 0 on success, error value otherwise. + */ +static int tsc_personality_change(enum tsc_cam_personality pcmcia_state) +{ + int ret = 0; + struct pinctrl_info *ppinctrl = &tsc_device->pinctrl_info; + struct pinctrl_current_state *pcurr_state = &ppinctrl->curr_state; + + if (mutex_lock_interruptible(&tsc_device->mutex)) + return -ERESTARTSYS; + + if (pcmcia_state == (enum tsc_cam_personality)pcurr_state->pcmcia_state) + goto exit; + + + /* Transition from current pinctrl state to curr + new pcmcia state */ + switch (pcmcia_state) { + case TSC_CICAM_PERSONALITY_CI: + if (pcurr_state->ts0 && pcurr_state->ts1) + ret = pinctrl_select_state(ppinctrl->pinctrl, + ppinctrl->dual_ts_ci_card); + else if (pcurr_state->ts0 && !pcurr_state->ts1) + ret = pinctrl_select_state(ppinctrl->pinctrl, + ppinctrl->ts0_ci_card); + else if (!pcurr_state->ts0 && pcurr_state->ts1) + ret = pinctrl_select_state(ppinctrl->pinctrl, + ppinctrl->ts1_ci_card); + else + ret = pinctrl_select_state(ppinctrl->pinctrl, + ppinctrl->ci_card); + break; + case TSC_CICAM_PERSONALITY_CIPLUS: + if (pcurr_state->ts0 && pcurr_state->ts1) + ret = pinctrl_select_state(ppinctrl->pinctrl, + ppinctrl->dual_ts_ci_plus); + else if (pcurr_state->ts0 && !pcurr_state->ts1) + ret = pinctrl_select_state(ppinctrl->pinctrl, + ppinctrl->ts0_ci_plus); + else if (!pcurr_state->ts0 && pcurr_state->ts1) + ret = pinctrl_select_state(ppinctrl->pinctrl, + ppinctrl->ts1_ci_plus); + else + ret = pinctrl_select_state(ppinctrl->pinctrl, + ppinctrl->ci_plus); + break; + case TSC_CICAM_PERSONALITY_DISABLE: + if (pcurr_state->ts0 && pcurr_state->ts1) + ret = pinctrl_select_state(ppinctrl->pinctrl, + ppinctrl->dual_ts); + else if (pcurr_state->ts0 && !pcurr_state->ts1) + ret = pinctrl_select_state(ppinctrl->pinctrl, + ppinctrl->ts0); + else if (!pcurr_state->ts0 && pcurr_state->ts1) + ret = pinctrl_select_state(ppinctrl->pinctrl, + ppinctrl->ts1); + else + ret = pinctrl_select_state(ppinctrl->pinctrl, + ppinctrl->disable); + break; + default: + pr_err("%s: Wrong personality parameter\n", __func__); + ret = -EINVAL; + goto exit; + } + + if (ret != 0) { + pr_err("%s: error changing PCMCIA pins. ret value = %d\n", + __func__, ret); + ret = -EINVAL; + goto exit; + } + + /* Update the current pcmcia state in the internal struct */ + pcurr_state->pcmcia_state = (enum pcmcia_state)pcmcia_state; + +exit: + mutex_unlock(&tsc_device->mutex); + return ret; +} + +/** * tsc_reset_cam() - HW reset to the CAM. * * Toggle the reset pin of the pcmcia to make a HW reset. @@ -1667,6 +1765,20 @@ static int tsc_mux_power_on_clocks(void) goto err_set_rate; } + /* Setting the TSC serial clock rate */ + ret = clk_set_rate(tsc_device->ser_clk, TSC_SER_CLK_RATE); + if (ret != 0) { + pr_err("%s: Can't set rate for tsc serial clock", __func__); + goto err_set_rate; + } + + /* Setting the TSC parallel clock rate */ + ret = clk_set_rate(tsc_device->par_clk, TSC_PAR_CLK_RATE); + if (ret != 0) { + pr_err("%s: Can't set rate for tsc parallel clock", __func__); + goto err_set_rate; + } + /* Enabling the clocks */ ret = clk_prepare_enable(tsc_device->ser_clk); if (ret != 0) { @@ -1758,7 +1870,8 @@ static int tsc_device_power_up(void) /* Update the current pinctrl state in the internal struct */ tsc_device->pinctrl_info.curr_state.ts0 = false; tsc_device->pinctrl_info.curr_state.ts1 = false; - tsc_device->pinctrl_info.curr_state.pcmcia_state = DISABLE; + tsc_device->pinctrl_info.curr_state.pcmcia_state = + TSC_CICAM_PERSONALITY_DISABLE; /* Reset TSC registers to a default known state */ tsc_reset_registers(); @@ -2167,8 +2280,10 @@ static long tsc_mux_ioctl(struct file *filp, switch (cmd) { case TSC_CONFIG_ROUTE: if (!arg || copy_from_user(&tsc_route, (void *)arg, - sizeof(struct tsc_route))) - return -EFAULT; + sizeof(struct tsc_route))) { + ret = -EFAULT; + goto err; + } ret = tsc_route_mux(tsc_mux, tsc_route.source, tsc_route.dest); break; case TSC_ENABLE_INPUT: @@ -2179,8 +2294,10 @@ static long tsc_mux_ioctl(struct file *filp, break; case TSC_SET_TSIF_CONFIG: if (!arg || copy_from_user(&tsif_params, (void *)arg, - sizeof(struct tsc_tsif_params))) - return -EFAULT; + sizeof(struct tsc_tsif_params))) { + ret = -EFAULT; + goto err; + } ret = tsc_config_tsif(tsc_mux, &tsif_params); break; case TSC_CLEAR_RATE_MISMATCH_IRQ: @@ -2194,6 +2311,7 @@ static long tsc_mux_ioctl(struct file *filp, pr_err("%s: Unknown ioctl %i", __func__, cmd); } +err: mutex_unlock(&tsc_mux->mutex); return ret; } @@ -2231,7 +2349,7 @@ static long tsc_ci_ioctl(struct file *filp, ret = tsc_reset_cam(); break; case TSC_CICAM_PERSONALITY_CHANGE: - /* TODO: set the pcmcia pins accordingly */ + ret = tsc_personality_change(arg); break; case TSC_GET_CARD_STATUS: spin_lock_irqsave(&tsc_ci->spinlock, flags); @@ -2700,7 +2818,6 @@ static void tsc_free_iommu_info(void) static int tsc_get_iommu_info(struct platform_device *pdev) { int ret = 0; - struct msm_tsc_platform_data *pdata = pdev->dev.platform_data; /* Create a new ION client used by tsc ci to allocate memory */ tsc_device->iommu_info.ion_client = msm_ion_client_create(UINT_MAX, @@ -2715,7 +2832,8 @@ static int tsc_get_iommu_info(struct platform_device *pdev) } /* Find the iommu group by the name obtained from the device tree */ - tsc_device->iommu_info.group = iommu_group_find(pdata->iommu_group); + tsc_device->iommu_info.group = + iommu_group_find(tsc_device->iommu_info.iommu_group_name); if (!tsc_device->iommu_info.group) { pr_err("%s: error in iommu_group_find", __func__); ret = -EINVAL; @@ -2735,9 +2853,6 @@ static int tsc_get_iommu_info(struct platform_device *pdev) tsc_device->iommu_info.domain_num = msm_find_domain_no(tsc_device->iommu_info.domain); - /* Save the partition number */ - tsc_device->iommu_info.partition_num = pdata->iommu_partition; - return ret; err_domain: @@ -2751,78 +2866,60 @@ err_client: } /** - * msm_tsc_dt_to_pdata() - Copy device-tree data to platfrom data structure. + * tsc_parse_dt() - Parse device-tree data and save it. * * @pdev: A pointer to the TSC platform device. * - * Return pointer to allocated platform data on success, NULL on failure. + * Return 0 on success, error value otherwise. */ -static struct msm_tsc_platform_data *msm_tsc_dt_to_pdata - (struct platform_device *pdev) +static int tsc_parse_dt(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; - struct msm_tsc_platform_data *pdata; struct device_node *iommu_pnode; int ret; - /* Note: memory allocated by devm_kzalloc is freed automatically */ - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(&pdev->dev, "%s: Unable to allocate platform data\n", - __func__); - return NULL; - } - /* Check that power regulator property exist */ if (!of_get_property(node, "vdd-supply", NULL)) { dev_err(&pdev->dev, "%s: Could not find vdd-supply property\n", __func__); - return NULL; + return -EINVAL; } /* Reading IOMMU group label by obtaining the group's phandle */ - iommu_pnode = of_parse_phandle(node, "qti,iommu-group", 0); + iommu_pnode = of_parse_phandle(node, "qcom,iommu-group", 0); if (!iommu_pnode) { dev_err(&pdev->dev, "%s: Couldn't find iommu-group property\n", __func__); - return NULL; + return -EINVAL; } ret = of_property_read_string(iommu_pnode, "label", - &pdata->iommu_group); + &tsc_device->iommu_info.iommu_group_name); of_node_put(iommu_pnode); if (ret) { dev_err(&pdev->dev, "%s: Couldn't find label property of the IOMMU group, err=%d\n", __func__, ret); - return NULL; + return -EINVAL; } /* Reading IOMMU partition */ - ret = of_property_read_u32(node, "qti,iommu-partition", - &pdata->iommu_partition); + ret = of_property_read_u32(node, "qcom,iommu-partition", + &tsc_device->iommu_info.partition_num); if (ret) { dev_err(&pdev->dev, "%s: Couldn't find iommu-partition property, err=%d\n", __func__, ret); - return NULL; + return -EINVAL; } /* Reading reset cam gpio */ tsc_device->reset_cam_gpio = of_get_named_gpio(node, - "qti,tsc-reset-cam-gpio", 0); + "qcom,tsc-reset-cam-gpio", 0); if (tsc_device->reset_cam_gpio < 0) { - dev_err(&pdev->dev, "%s: Couldn't find qti,tsc-reset-cam-gpio property\n", + dev_err(&pdev->dev, "%s: Couldn't find qcom,tsc-reset-cam-gpio property\n", __func__); - return NULL; + return -EINVAL; } - /* Reading the a/b configuration - if exist */ - ret = of_property_read_u32(node, "qti,ts0-config", &pdata->ts0_config); - if (ret) - pdata->ts0_config = 0; - ret = of_property_read_u32(node, "qti,ts1-config", &pdata->ts1_config); - if (ret) - pdata->ts1_config = 0; - - return pdata; + return 0; } /* TSC Mux file operations */ @@ -2848,7 +2945,6 @@ static const struct file_operations tsc_ci_fops = { static int msm_tsc_probe(struct platform_device *pdev) { int ret; - struct msm_tsc_platform_data *pdata = NULL; tsc_device = kzalloc(sizeof(struct tsc_device), GFP_KERNEL); if (!tsc_device) { @@ -2858,16 +2954,16 @@ static int msm_tsc_probe(struct platform_device *pdev) /* get information from device tree */ if (pdev->dev.of_node) { - pdata = msm_tsc_dt_to_pdata(pdev); - pdev->dev.platform_data = pdata; - } else { /* else - must have platform data */ - pdata = pdev->dev.platform_data; - } - - if (!pdata) { - pr_err("%s: Platform data not available", __func__); + ret = tsc_parse_dt(pdev); + if (ret != 0) { + pr_err("%s: devicetree data not available", __func__); + ret = -EINVAL; + goto err_dt; + } + } else { /* else - devicetree is not found */ + pr_err("%s: devicetree data is missing", __func__); ret = -EINVAL; - goto err_pdata; + goto err_dt; } /* set up references */ @@ -2962,7 +3058,7 @@ err_map_io: tsc_clocks_put(); err_clocks_get: tsc_free_iommu_info(); -err_pdata: +err_dt: kfree(tsc_device); return ret; @@ -3018,7 +3114,7 @@ static int msm_tsc_remove(struct platform_device *pdev) /*********************** Platform driver information ***********************/ static struct of_device_id msm_match_table[] = { - {.compatible = "qti,msm-tsc"}, + {.compatible = "qcom,msm-tsc"}, {} }; diff --git a/drivers/media/platform/msm/broadcast/tspp.c b/drivers/media/platform/msm/broadcast/tspp.c index 9b58edbd0b54..6f1ea5f602ba 100644 --- a/drivers/media/platform/msm/broadcast/tspp.c +++ b/drivers/media/platform/msm/broadcast/tspp.c @@ -34,6 +34,7 @@ #include <linux/tspp.h> /* tspp functions */ #include <linux/bitops.h> /* BIT() macro */ #include <linux/regulator/consumer.h> +#include <linux/regulator/rpm-smd-regulator.h> #include <mach/sps.h> /* BAM stuff */ #include <mach/gpio.h> #include <linux/wakelock.h> /* Locking functions */ @@ -41,7 +42,6 @@ #include <linux/jiffies.h> /* Jiffies counter */ #include <mach/dma.h> #include <mach/msm_tspp.h> -#include <mach/rpm-regulator-smd.h> #include <linux/debugfs.h> #include <linux/of.h> #include <linux/of_gpio.h> diff --git a/drivers/media/platform/msm/broadcast/tspp2.c b/drivers/media/platform/msm/broadcast/tspp2.c index af96642953e6..c30723860632 100644 --- a/drivers/media/platform/msm/broadcast/tspp2.c +++ b/drivers/media/platform/msm/broadcast/tspp2.c @@ -31,6 +31,7 @@ #include <linux/msm_iommu_domains.h> #include <mach/msm_bus.h> #include <mach/msm_tspp2.h> +#include <linux/clk/msm-clk.h> #define TSPP2_MODULUS_OP(val, mod) ((val) & ((mod) - 1)) @@ -46,6 +47,13 @@ #define TSPP2_NUM_EVENT_WORK_ELEMENTS 256 /* + * Based on the hardware programming guide, HW requires we wait for up to 2ms + * before closing the pipes used by the filter. + * This is required to avoid unexpected pipe reset interrupts. + */ +#define TSPP2_HW_DELAY_USEC 2000 + +/* * Default source configuration: * Sync byte 0x47, check sync byte, * Do not monitor scrambling bits, @@ -798,6 +806,8 @@ struct tspp2_iommu_info { * @tspp2_ahb_clk: TSPP2 AHB clock. * @tspp2_core_clk: TSPP2 core clock. * @tspp2_vbif_clk: TSPP2 VBIF clock. + * @vbif_ahb_clk: VBIF AHB clock. + * @vbif_axi_clk: VBIF AXI clock. * @tspp2_klm_ahb_clk: TSPP2 KLM AHB clock. * @tsif_ref_clk: TSIF reference clock. * @batches: An array of filter batch objects. @@ -844,6 +854,8 @@ struct tspp2_device { struct clk *tspp2_ahb_clk; struct clk *tspp2_core_clk; struct clk *tspp2_vbif_clk; + struct clk *vbif_ahb_clk; + struct clk *vbif_axi_clk; struct clk *tspp2_klm_ahb_clk; struct clk *tsif_ref_clk; struct tspp2_filter_batch batches[TSPP2_NUM_BATCHES]; @@ -982,7 +994,7 @@ static void tspp2_tsif_debugfs_init(struct tspp2_tsif_device *tsif_device) "stat_pkt_read_err", S_IRUGO | S_IWUSR | S_IWGRP, dentry, - &tsif_device->stat_pkt_write_err); + &tsif_device->stat_pkt_read_err); debugfs_create_u32( "stat_overflow", @@ -1004,6 +1016,286 @@ static void tspp2_tsif_debugfs_init(struct tspp2_tsif_device *tsif_device) } } +static char *op_to_string(enum tspp2_operation_type op) +{ + switch (op) { + case TSPP2_OP_PES_ANALYSIS: + return "TSPP2_OP_PES_ANALYSIS"; + case TSPP2_OP_RAW_TRANSMIT: + return "TSPP2_OP_RAW_TRANSMIT"; + case TSPP2_OP_PES_TRANSMIT: + return "TSPP2_OP_PES_TRANSMIT"; + case TSPP2_OP_PCR_EXTRACTION: + return "TSPP2_OP_PCR_EXTRACTION"; + case TSPP2_OP_CIPHER: + return "TSPP2_OP_CIPHER"; + case TSPP2_OP_INDEXING: + return "TSPP2_OP_INDEXING"; + case TSPP2_OP_COPY_PACKET: + return "TSPP2_OP_COPY_PACKET"; + default: + return "Invalid Operation"; + } +} + +static char *src_input_to_string(enum tspp2_src_input src_input) +{ + switch (src_input) { + case TSPP2_INPUT_TSIF0: + return "TSPP2_INPUT_TSIF0"; + case TSPP2_INPUT_TSIF1: + return "TSPP2_INPUT_TSIF1"; + case TSPP2_INPUT_MEMORY: + return "TSPP2_INPUT_MEMORY"; + default: + return "Unknown source input type"; + } +} + +static char *pkt_format_to_string(enum tspp2_packet_format pkt_format) +{ + switch (pkt_format) { + case TSPP2_PACKET_FORMAT_188_RAW: + return "TSPP2_PACKET_FORMAT_188_RAW"; + case TSPP2_PACKET_FORMAT_192_HEAD: + return "TSPP2_PACKET_FORMAT_192_HEAD"; + case TSPP2_PACKET_FORMAT_192_TAIL: + return "TSPP2_PACKET_FORMAT_192_TAIL"; + default: + return "Unknown packet format"; + } +} + +/** + * debugfs service to print device information. + */ +static int tspp2_device_debugfs_print(struct seq_file *s, void *p) +{ + int count; + int exist_flag = 0; + struct tspp2_device *device = (struct tspp2_device *)s->private; + + seq_printf(s, "dev_id: %d\n", device->dev_id); + seq_puts(s, "Enabled filters:"); + for (count = 0; count < TSPP2_NUM_HW_FILTERS; count++) + if (device->filters[count].enabled) { + seq_printf(s, "\n\tfilter%3d", count); + exist_flag = 1; + } + if (!exist_flag) + seq_puts(s, " none\n"); + else + seq_puts(s, "\n"); + + exist_flag = 0; + seq_puts(s, "Opened filters:"); + for (count = 0; count < TSPP2_NUM_HW_FILTERS; count++) + if (device->filters[count].opened) { + seq_printf(s, "\n\tfilter%3d", count); + exist_flag = 1; + } + if (!exist_flag) + seq_puts(s, " none\n"); + else + seq_puts(s, "\n"); + + exist_flag = 0; + seq_puts(s, "Opened pipes:\n"); + for (count = 0; count < TSPP2_NUM_PIPES; count++) + if (device->pipes[count].opened) { + seq_printf(s, "\tpipe%2d\n", count); + exist_flag = 1; + } + if (!exist_flag) + seq_puts(s, " none\n"); + else + seq_puts(s, "\n"); + + return 0; +} + +/** + * debugfs service to print source information. + */ +static int tspp2_src_debugfs_print(struct seq_file *s, void *p) +{ + struct tspp2_filter_batch *batch; + struct tspp2_filter *filter; + struct tspp2_output_pipe *output_pipe; + struct tspp2_src *src = (struct tspp2_src *)s->private; + + if (!src) { + seq_puts(s, "error\n"); + return 1; + } + seq_printf(s, "Status: %s\n", src->enabled ? "enabled" : "disabled"); + seq_printf(s, "hw_index: %d\n", src->hw_index); + seq_printf(s, "event_bitmask: 0x%08X\n", src->event_bitmask); + if (src->input_pipe) + seq_printf(s, "input_pipe hw_index: %d\n", + src->input_pipe->hw_index); + seq_printf(s, "tspp2_src_input: %s\n", src_input_to_string(src->input)); + seq_printf(s, "pkt_format: %s\n", + pkt_format_to_string(src->pkt_format)); + seq_printf(s, "num_associated_batches: %d\n", + src->num_associated_batches); + + if (src->num_associated_batches) { + seq_puts(s, "batch_ids: "); + list_for_each_entry(batch, &src->batches_list, link) + seq_printf(s, "%d ", batch->batch_id); + seq_puts(s, "\n"); + } + + seq_printf(s, "num_associated_pipes: %d\n", src->num_associated_pipes); + if (src->num_associated_pipes) { + seq_puts(s, "pipes_hw_idxs: "); + list_for_each_entry(output_pipe, &src->output_pipe_list, link) { + seq_printf(s, "%d ", output_pipe->pipe->hw_index); + } + seq_puts(s, "\n"); + } + + seq_printf(s, "reserved_filter_hw_index: %d\n", + src->reserved_filter_hw_index); + + seq_printf(s, "num_associated_filters: %d\n", + src->num_associated_filters); + if (src->num_associated_filters) { + int i; + seq_puts(s, "Open filters:\n"); + list_for_each_entry(filter, &src->filters_list, link) { + if (!filter->opened) + continue; + seq_printf(s, "\thw_index: %d\n", + filter->hw_index); + seq_printf(s, "\tStatus: %s\n", + filter->enabled ? "enabled" + : "disabled"); + seq_printf(s, "\tpid_value: 0x%08X\n", + filter->pid_value); + seq_printf(s, "\tmask: 0x%08X\n", filter->mask); + seq_printf(s, "\tnum_user_operations: %d\n", + filter->num_user_operations); + if (filter->num_user_operations) { + seq_puts( + s, "\tTypes of operations:\n"); + for (i = 0; + i < filter->num_user_operations; i++) { + seq_printf(s, "\t\t%s\n", op_to_string( + filter->operations[i].type)); + } + } + } + + } else { + seq_puts(s, "no filters\n"); + } + + return 0; +} + +/** + * debugfs service to print filter information. + */ +static int filter_debugfs_print(struct seq_file *s, void *p) +{ + int i; + struct tspp2_filter *filter = (struct tspp2_filter *)s->private; + + seq_printf(s, "Status: %s\n", filter->opened ? "opened" : "closed"); + if (filter->batch) + seq_printf(s, "Located in batch %d\n", filter->batch->batch_id); + if (filter->src) + seq_printf(s, "Associated with src %d\n", + filter->src->hw_index); + seq_printf(s, "hw_index: %d\n", filter->hw_index); + seq_printf(s, "pid_value: 0x%08X\n", filter->pid_value); + seq_printf(s, "mask: 0x%08X\n", filter->mask); + seq_printf(s, "context: %d\n", filter->context); + seq_printf(s, "indexing_table_id: %d\n", filter->indexing_table_id); + seq_printf(s, "num_user_operations: %d\n", filter->num_user_operations); + seq_puts(s, "Types of operations:\n"); + for (i = 0; i < filter->num_user_operations; i++) + seq_printf(s, "\t%s\n", op_to_string( + filter->operations[i].type)); + seq_printf(s, "indexing_op_set: %d\n", filter->indexing_op_set); + seq_printf(s, "raw_op_with_indexing: %d\n", + filter->raw_op_with_indexing); + seq_printf(s, "pes_analysis_op_set: %d\n", filter->pes_analysis_op_set); + seq_printf(s, "raw_op_set: %d\n", filter->raw_op_set); + seq_printf(s, "pes_tx_op_set: %d\n", filter->pes_tx_op_set); + seq_printf(s, "Status: %s\n", filter->enabled ? "enabled" : "disabled"); + return 0; +} + +/** + * debugfs service to print pipe information. + */ +static int pipe_debugfs_print(struct seq_file *s, void *p) +{ + struct tspp2_pipe *pipe = (struct tspp2_pipe *)s->private; + seq_printf(s, "hw_index: %d\n", pipe->hw_index); + seq_printf(s, "iova: 0x%08X\n", pipe->iova); + seq_printf(s, "threshold: %d\n", pipe->threshold); + seq_printf(s, "Status: %s\n", pipe->opened ? "opened" : "closed"); + seq_printf(s, "ref_cnt: %d\n", pipe->ref_cnt); + return 0; +} + +static int tspp2_dev_dbgfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, tspp2_device_debugfs_print, + inode->i_private); +} + +static int tspp2_filter_dbgfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, filter_debugfs_print, inode->i_private); +} + +static int tspp2_pipe_dbgfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, pipe_debugfs_print, inode->i_private); +} + +static int tspp2_src_dbgfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, tspp2_src_debugfs_print, inode->i_private); +} + +static const struct file_operations dbgfs_tspp2_device_fops = { + .open = tspp2_dev_dbgfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations dbgfs_filter_fops = { + .open = tspp2_filter_dbgfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations dbgfs_pipe_fops = { + .open = tspp2_pipe_dbgfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations dbgfs_src_fops = { + .open = tspp2_src_dbgfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + /** * tspp2_tsif_debugfs_exit() - TSIF device debugfs teardown. * @@ -1299,6 +1591,41 @@ static void tspp2_debugfs_init(struct tspp2_device *device) } } } + dir = debugfs_create_dir("software", device->debugfs_entry); + debugfs_create_file("device", S_IRUGO, dir, device, + &dbgfs_tspp2_device_fops); + + dentry = debugfs_create_dir("filters", dir); + if (dentry) { + for (i = 0; i < TSPP2_NUM_HW_FILTERS; i++) { + snprintf(name, 20, "filter%03i", i); + debugfs_create_file(name, S_IRUGO, dentry, + &(device->filters[i]), &dbgfs_filter_fops); + } + } + + dentry = debugfs_create_dir("pipes", dir); + if (dentry) { + for (i = 0; i < TSPP2_NUM_PIPES; i++) { + snprintf(name, 20, "pipe%02i", i); + debugfs_create_file(name, S_IRUGO, dentry, + &(device->pipes[i]), &dbgfs_pipe_fops); + } + } + + dentry = debugfs_create_dir("sources", dir); + if (dentry) { + for (i = 0; i < TSPP2_NUM_TSIF_INPUTS; i++) { + snprintf(name, 20, "tsif%d", i); + debugfs_create_file(name, S_IRUGO, dentry, + &(device->tsif_sources[i]), &dbgfs_src_fops); + } + for (i = 0; i < TSPP2_NUM_MEM_INPUTS; i++) { + snprintf(name, 20, "mem%d", i); + debugfs_create_file(name, S_IRUGO, dentry, + &(device->mem_sources[i]), &dbgfs_src_fops); + } + } } /** @@ -1381,6 +1708,58 @@ static int tspp2_tsif_start(struct tspp2_tsif_device *tsif_device) return (ctl & TSIF_STS_CTL_START) ? 0 : -EBUSY; } + +static int tspp2_vbif_clock_start(struct tspp2_device *device) +{ + int ret; + + if (device->tspp2_vbif_clk) { + ret = clk_prepare_enable(device->tspp2_vbif_clk); + if (ret) { + pr_err("%s: Can't start tspp2_vbif_clk\n", __func__); + return ret; + } + } + + if (device->vbif_ahb_clk) { + ret = clk_prepare_enable(device->vbif_ahb_clk); + if (ret) { + pr_err("%s: Can't start vbif_ahb_clk\n", __func__); + goto disable_vbif_tspp2; + } + } + if (device->vbif_axi_clk) { + ret = clk_prepare_enable(device->vbif_axi_clk); + if (ret) { + pr_err("%s: Can't start vbif_ahb_clk\n", __func__); + goto disable_vbif_ahb; + } + } + + return 0; + +disable_vbif_ahb: + if (device->vbif_ahb_clk) + clk_disable_unprepare(device->vbif_ahb_clk); +disable_vbif_tspp2: + if (device->tspp2_vbif_clk) + clk_disable_unprepare(device->tspp2_vbif_clk); + + return ret; +} + +static void tspp2_vbif_clock_stop(struct tspp2_device *device) +{ + if (device->tspp2_vbif_clk) + clk_disable_unprepare(device->tspp2_vbif_clk); + + if (device->vbif_ahb_clk) + clk_disable_unprepare(device->vbif_ahb_clk); + + if (device->vbif_axi_clk) + clk_disable_unprepare(device->vbif_axi_clk); +} + /** * tspp2_tsif_stop() - Stop TSIF device HW. * @@ -1492,14 +1871,6 @@ static int tspp2_clock_start(struct tspp2_device *device) tspp2_core_clk = 1; } - if (device->tspp2_vbif_clk) { - if (clk_prepare_enable(device->tspp2_vbif_clk) != 0) { - pr_err("%s: Can't start tspp2_vbif_clk\n", __func__); - goto err_clocks; - } - tspp2_vbif_clk = 1; - } - if (device->tspp2_klm_ahb_clk) { if (clk_prepare_enable(device->tspp2_klm_ahb_clk) != 0) { pr_err("%s: Can't start tspp2_klm_ahb_clk\n", __func__); @@ -1566,9 +1937,6 @@ static void tspp2_clock_stop(struct tspp2_device *device) if (device->tspp2_klm_ahb_clk) clk_disable_unprepare(device->tspp2_klm_ahb_clk); - if (device->tspp2_vbif_clk) - clk_disable_unprepare(device->tspp2_vbif_clk); - if (device->tspp2_core_clk) clk_disable_unprepare(device->tspp2_core_clk); @@ -1820,17 +2188,21 @@ static void tspp2_event_work_handler(struct work_struct *work) { struct tspp2_event_work *event_work = container_of(work, struct tspp2_event_work, work); + struct tspp2_event_work cb_info = *event_work; if (mutex_lock_interruptible(&event_work->device->mutex)) return; - if (event_work->callback) - event_work->callback(event_work->cookie, - event_work->event_bitmask); - list_add_tail(&event_work->link, &event_work->device->free_work_list); mutex_unlock(&event_work->device->mutex); + + /* + * Must run callback with tspp2 device mutex unlocked, + * as callback might call tspp2 driver API and cause a deadlock. + */ + if (cb_info.callback) + cb_info.callback(cb_info.cookie, cb_info.event_bitmask); } /** @@ -2013,10 +2385,10 @@ static int tspp2_src_disable_internal(struct tspp2_src *src) } /* - * HW requires we wait for up to 1ms here before closing the pipes + * HW requires we wait for up to 2ms here before closing the pipes * attached to (and used by) this source */ - udelay(1000); + udelay(TSPP2_HW_DELAY_USEC); src->enabled = 0; src->device->num_enabled_sources--; @@ -2069,6 +2441,11 @@ int tspp2_device_open(u32 dev_id) if (rc) goto err_mutex_unlock; + /* Reset TSPP2 core */ + clk_reset(device->tspp2_core_clk, CLK_RESET_ASSERT); + udelay(10); + clk_reset(device->tspp2_core_clk, CLK_RESET_DEASSERT); + /* Start HW clocks before accessing registers */ rc = tspp2_reg_clock_start(device); if (rc) @@ -2625,6 +3002,7 @@ static int tspp2_pipe_memory_init(struct tspp2_pipe *pipe) int partition = 0; int hlos_group_attached = 0; int cpz_group_attached = 0; + int vbif_clk_started = 0; if (pipe->cfg.is_secure) { domain = pipe->device->iommu_info.cpz_domain_num; @@ -2647,6 +3025,18 @@ static int tspp2_pipe_memory_init(struct tspp2_pipe *pipe) __func__, ret); return ret; } + + if ((pipe->device->num_secured_opened_pipes + + pipe->device->num_non_secured_opened_pipes) == 0) { + ret = tspp2_vbif_clock_start(pipe->device); + if (ret) { + pr_err( + "%s: tspp2_vbif_clock_start failed, ret=%d\n", + __func__, ret); + return ret; + } + vbif_clk_started = 1; + } } else { /* * We need to attach the group to enable the IOMMU and support @@ -2661,6 +3051,14 @@ static int tspp2_pipe_memory_init(struct tspp2_pipe *pipe) */ if ((pipe->device->num_secured_opened_pipes + pipe->device->num_non_secured_opened_pipes) == 0) { + ret = tspp2_vbif_clock_start(pipe->device); + if (ret) { + pr_err("%s: tspp2_vbif_clock_start failed, ret=%d\n", + __func__, ret); + goto err_out; + } + vbif_clk_started = 1; + pr_debug("%s: attaching HLOS group\n", __func__); ret = iommu_attach_group( pipe->device->iommu_info.hlos_domain, @@ -2725,6 +3123,9 @@ err_out: pipe->device->iommu_info.cpz_group); } + if (vbif_clk_started) + tspp2_vbif_clock_stop(pipe->device); + return ret; } @@ -2773,7 +3174,11 @@ static void tspp2_pipe_memory_terminate(struct tspp2_pipe *pipe) iommu_detach_group( pipe->device->iommu_info.hlos_domain, pipe->device->iommu_info.hlos_group); + tspp2_vbif_clock_stop(pipe->device); } + } else if ((pipe->device->num_secured_opened_pipes + + pipe->device->num_non_secured_opened_pipes) == 0) { + tspp2_vbif_clock_stop(pipe->device); } pipe->iova = 0; @@ -5227,10 +5632,10 @@ int tspp2_filter_disable(u32 filter_handle) TSPP2_FILTER_ENTRY0(filter->hw_index)); /* - * HW requires we wait for up to 1ms here before closing the pipes + * HW requires we wait for up to 2ms here before closing the pipes * used by this filter */ - udelay(1000); + udelay(TSPP2_HW_DELAY_USEC); filter->enabled = 0; @@ -5957,10 +6362,10 @@ static int tspp2_filter_ops_update(struct tspp2_filter *filter, TSPP2_FILTER_ENTRY0(filter->hw_index)); /* - * HW requires we wait for up to 1ms here before removing the + * HW requires we wait for up to 2ms here before removing the * operations used by this filter. */ - udelay(1000); + udelay(TSPP2_HW_DELAY_USEC); tspp2_filter_ops_clear(filter); @@ -6947,6 +7352,69 @@ int tspp2_filter_event_notification_register(u32 filter_handle, } EXPORT_SYMBOL(tspp2_filter_event_notification_register); +/** + * tspp2_get_filter_hw_index() - Get a filter's hardware index. + * + * @filter_handle: Filter handle. + * + * This is an helper function to support tspp2 auto-testing. + * + * Return the filter's hardware index on success, error value otherwise. + */ +int tspp2_get_filter_hw_index(u32 filter_handle) +{ + struct tspp2_filter *filter = (struct tspp2_filter *)filter_handle; + if (!filter_handle) + return -EINVAL; + return filter->hw_index; +} +EXPORT_SYMBOL(tspp2_get_filter_hw_index); + +/** + * tspp2_get_reserved_hw_index() - Get a source's reserved hardware index. + * + * @src_handle: Source handle. + * + * This is an helper function to support tspp2 auto-testing. + * + * Return the source's reserved hardware index on success, + * error value otherwise. + */ +int tspp2_get_reserved_hw_index(u32 src_handle) +{ + struct tspp2_src *src = (struct tspp2_src *)src_handle; + if (!src_handle) + return -EINVAL; + return src->reserved_filter_hw_index; +} +EXPORT_SYMBOL(tspp2_get_reserved_hw_index); + +/** + * tspp2_get_ops_array() - Get filter's operations. + * + * @filter_handle: Filter handle. + * @ops_array: The filter's operations. + * @num_of_ops: The filter's number of operations. + * + * This is an helper function to support tspp2 auto-testing. + * + * Return 0 on success, error value otherwise. + */ +int tspp2_get_ops_array(u32 filter_handle, + struct tspp2_operation ops_array[TSPP2_MAX_OPS_PER_FILTER], + u8 *num_of_ops) +{ + int i; + struct tspp2_filter *filter = (struct tspp2_filter *)filter_handle; + if (!filter_handle || !num_of_ops) + return -EINVAL; + *num_of_ops = filter->num_user_operations; + for (i = 0; i < *num_of_ops; i++) + ops_array[i] = filter->operations[i]; + return 0; +} +EXPORT_SYMBOL(tspp2_get_ops_array); + /* Platform driver related functions: */ /** @@ -7014,28 +7482,28 @@ msm_tspp2_dt_to_pdata(struct platform_device *pdev) } /* Get IOMMU information */ - rc = of_property_read_string(node, "qti,iommu-hlos-group", + rc = of_property_read_string(node, "qcom,iommu-hlos-group", &data->hlos_group); if (rc) { pr_err("%s: Could not find iommu-hlos-group property, err = %d\n", __func__, rc); return NULL; } - rc = of_property_read_string(node, "qti,iommu-cpz-group", + rc = of_property_read_string(node, "qcom,iommu-cpz-group", &data->cpz_group); if (rc) { pr_err("%s: Could not find iommu-cpz-group property, err = %d\n", __func__, rc); return NULL; } - rc = of_property_read_u32(node, "qti,iommu-hlos-partition", + rc = of_property_read_u32(node, "qcom,iommu-hlos-partition", &data->hlos_partition); if (rc) { pr_err("%s: Could not find iommu-hlos-partition property, err = %d\n", __func__, rc); return NULL; } - rc = of_property_read_u32(node, "qti,iommu-cpz-partition", + rc = of_property_read_u32(node, "qcom,iommu-cpz-partition", &data->cpz_partition); if (rc) { pr_err("%s: Could not find iommu-cpz-partition property, err = %d\n", @@ -7154,6 +7622,12 @@ static void tspp2_clocks_put(struct tspp2_device *device) if (device->tspp2_vbif_clk) clk_put(device->tspp2_vbif_clk); + if (device->vbif_ahb_clk) + clk_put(device->vbif_ahb_clk); + + if (device->vbif_axi_clk) + clk_put(device->vbif_axi_clk); + if (device->tspp2_core_clk) clk_put(device->tspp2_core_clk); @@ -7163,6 +7637,8 @@ static void tspp2_clocks_put(struct tspp2_device *device) device->tspp2_ahb_clk = NULL; device->tspp2_core_clk = NULL; device->tspp2_vbif_clk = NULL; + device->vbif_ahb_clk = NULL; + device->vbif_axi_clk = NULL; device->tspp2_klm_ahb_clk = NULL; device->tsif_ref_clk = NULL; } @@ -7196,6 +7672,8 @@ static int msm_tspp2_clocks_setup(struct platform_device *pdev, device->tspp2_ahb_clk = NULL; device->tspp2_core_clk = NULL; device->tspp2_vbif_clk = NULL; + device->vbif_ahb_clk = NULL; + device->vbif_axi_clk = NULL; device->tspp2_klm_ahb_clk = NULL; device->tsif_ref_clk = NULL; @@ -7235,6 +7713,22 @@ static int msm_tspp2_clocks_setup(struct platform_device *pdev, } } + device->vbif_ahb_clk = clk_get(&pdev->dev, "iface_vbif_clk"); + if (IS_ERR(device->vbif_ahb_clk)) { + pr_err("%s: Failed to get %s", __func__, "iface_vbif_clk"); + ret = PTR_ERR(device->vbif_ahb_clk); + device->vbif_ahb_clk = NULL; + goto err_clocks; + } + + device->vbif_axi_clk = clk_get(&pdev->dev, "vbif_core_clk"); + if (IS_ERR(device->vbif_axi_clk)) { + pr_err("%s: Failed to get %s", __func__, "vbif_core_clk"); + ret = PTR_ERR(device->vbif_axi_clk); + device->vbif_axi_clk = NULL; + goto err_clocks; + } + if (data->tspp2_klm_ahb_clk) { device->tspp2_klm_ahb_clk = clk_get(&pdev->dev, data->tspp2_klm_ahb_clk); diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c index fc746f2721a3..1e1d59995af7 100644 --- a/drivers/media/platform/msm/camera_v2/camera/camera.c +++ b/drivers/media/platform/msm/camera_v2/camera/camera.c @@ -64,8 +64,13 @@ static int camera_check_event_status(struct v4l2_event *event) struct msm_v4l2_event_data *event_data = (struct msm_v4l2_event_data *)&event->u.data[0]; - if (event_data->status > MSM_CAMERA_ERR_EVT_BASE) + if (event_data->status > MSM_CAMERA_ERR_EVT_BASE) { + pr_err("%s : event_data status out of bounds\n", + __func__); + pr_err("%s : Line %d event_data->status 0X%x\n", + __func__, __LINE__, event_data->status); return -EFAULT; + } return 0; } @@ -462,8 +467,10 @@ static int camera_v4l2_fh_open(struct file *filep) unsigned int stream_id; sp = kzalloc(sizeof(*sp), GFP_KERNEL); - if (!sp) + if (!sp) { + pr_err("%s : memory not available\n", __func__); return -ENOMEM; + } filep->private_data = &sp->fh; @@ -502,8 +509,10 @@ static int camera_v4l2_vb2_q_init(struct file *filep) /* free up this buffer when stream is done */ q->drv_priv = kzalloc(sizeof(struct msm_v4l2_format_data), GFP_KERNEL); - if (!q->drv_priv) + if (!q->drv_priv) { + pr_err("%s : memory not available\n", __func__); return -ENOMEM; + } q->mem_ops = msm_vb2_get_q_mem_ops(); q->ops = msm_vb2_get_q_ops(); @@ -534,44 +543,67 @@ static int camera_v4l2_open(struct file *filep) BUG_ON(!pvdev); rc = camera_v4l2_fh_open(filep); - if (rc < 0) + if (rc < 0) { + pr_err("%s : camera_v4l2_fh_open failed Line %d rc %d\n", + __func__, __LINE__, rc); goto fh_open_fail; + } opn_idx = atomic_read(&pvdev->opened); idx = opn_idx; /* every stream has a vb2 queue */ rc = camera_v4l2_vb2_q_init(filep); - if (rc < 0) + if (rc < 0) { + pr_err("%s : vb2 queue init fails Line %d rc %d\n", + __func__, __LINE__, rc); goto vb2_q_fail; + } if (!atomic_read(&pvdev->opened)) { pm_stay_awake(&pvdev->vdev->dev); /* create a new session when first opened */ rc = msm_create_session(pvdev->vdev->num, pvdev->vdev); - if (rc < 0) + if (rc < 0) { + pr_err("%s : session creation failed Line %d rc %d\n", + __func__, __LINE__, rc); goto session_fail; + } rc = msm_create_command_ack_q(pvdev->vdev->num, find_first_zero_bit(&opn_idx, MSM_CAMERA_STREAM_CNT_BITS)); - if (rc < 0) + if (rc < 0) { + pr_err("%s : creation of command_ack queue failed\n", + __func__); + pr_err("%s : Line %d rc %d\n", __func__, __LINE__, rc); goto command_ack_q_fail; + } camera_pack_event(filep, MSM_CAMERA_NEW_SESSION, 0, -1, &event); rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT); - if (rc < 0) + if (rc < 0) { + pr_err("%s : posting of NEW_SESSION event failed\n", + __func__); + pr_err("%s : Line %d rc %d\n", __func__, __LINE__, rc); goto post_fail; + } rc = camera_check_event_status(&event); - if (rc < 0) + if (rc < 0) { + pr_err("%s : checking event status fails Line %d rc %d\n", + __func__, __LINE__, rc); goto post_fail; + } } else { rc = msm_create_command_ack_q(pvdev->vdev->num, find_first_zero_bit(&opn_idx, MSM_CAMERA_STREAM_CNT_BITS)); - if (rc < 0) + if (rc < 0) { + pr_err("%s : creation of command_ack queue failed Line %d rc %d\n", + __func__, __LINE__, rc); goto session_fail; + } } pr_debug("%s: Open stream_id=%d\n", __func__, find_first_zero_bit(&opn_idx, MSM_CAMERA_STREAM_CNT_BITS)); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index 7de0dacc444c..557333355956 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -491,7 +491,7 @@ long msm_isp_ioctl(struct v4l2_subdev *sd, break; default: - pr_err("%s: Invalid ISP command\n", __func__); + pr_err_ratelimited("%s: Invalid ISP command\n", __func__); rc = -EINVAL; } return rc; diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c index 26ec2829977b..be955f60560a 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c @@ -200,7 +200,7 @@ static int msm_ispif_reset(struct ispif_device *ispif) ispif->base + ISPIF_VFE_m_INTF_CMD_0(i)); msm_camera_io_w(ISPIF_STOP_INTF_IMMEDIATELY, ispif->base + ISPIF_VFE_m_INTF_CMD_1(i)); - pr_debug("%s: base %x", __func__, (unsigned int)ispif->base); + pr_debug("%s: base %lx", __func__, (unsigned long)ispif->base); msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_PIX_INTF_n_CID_MASK(i, 0)); msm_camera_io_w(0, ispif->base + @@ -965,6 +965,7 @@ end: static void msm_ispif_release(struct ispif_device *ispif) { BUG_ON(!ispif); + BUG_ON(!ispif->base); if (ispif->ispif_state != ISPIF_POWER_UP) { pr_err("%s: ispif invalid state %d\n", __func__, @@ -1049,7 +1050,8 @@ static long msm_ispif_subdev_ioctl(struct v4l2_subdev *sd, return 0; } default: - pr_err("%s: invalid cmd 0x%x received\n", __func__, cmd); + pr_err_ratelimited("%s: invalid cmd 0x%x received\n", + __func__, cmd); return -ENOIOCTLCMD; } } @@ -1114,29 +1116,6 @@ static int ispif_probe(struct platform_device *pdev) return -ENOMEM; } - v4l2_subdev_init(&ispif->msm_sd.sd, &msm_ispif_subdev_ops); - ispif->msm_sd.sd.internal_ops = &msm_ispif_internal_ops; - ispif->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - snprintf(ispif->msm_sd.sd.name, - ARRAY_SIZE(ispif->msm_sd.sd.name), MSM_ISPIF_DRV_NAME); - v4l2_set_subdevdata(&ispif->msm_sd.sd, ispif); - - platform_set_drvdata(pdev, &ispif->msm_sd.sd); - mutex_init(&ispif->mutex); - - media_entity_init(&ispif->msm_sd.sd.entity, 0, NULL, 0); - ispif->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; - ispif->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_ISPIF; - ispif->msm_sd.sd.entity.name = pdev->name; - ispif->msm_sd.close_seq = MSM_SD_CLOSE_1ST_CATEGORY | 0x1; - rc = msm_sd_register(&ispif->msm_sd); - if (rc) { - pr_err("%s: msm_sd_register error = %d\n", __func__, rc); - goto error_sd_register; - } - - if (pdev->dev.of_node) { of_property_read_u32((&pdev->dev)->of_node, "cell-index", &pdev->id); @@ -1149,6 +1128,7 @@ static int ispif_probe(struct platform_device *pdev) rc = 0; } + mutex_init(&ispif->mutex); ispif->mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ispif"); if (!ispif->mem) { @@ -1181,15 +1161,33 @@ static int ispif_probe(struct platform_device *pdev) pr_err("%s: no valid csi_mux region\n", __func__); } + v4l2_subdev_init(&ispif->msm_sd.sd, &msm_ispif_subdev_ops); + ispif->msm_sd.sd.internal_ops = &msm_ispif_internal_ops; + ispif->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + snprintf(ispif->msm_sd.sd.name, + ARRAY_SIZE(ispif->msm_sd.sd.name), MSM_ISPIF_DRV_NAME); + v4l2_set_subdevdata(&ispif->msm_sd.sd, ispif); + + platform_set_drvdata(pdev, &ispif->msm_sd.sd); + + media_entity_init(&ispif->msm_sd.sd.entity, 0, NULL, 0); + ispif->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + ispif->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_ISPIF; + ispif->msm_sd.sd.entity.name = pdev->name; + ispif->msm_sd.close_seq = MSM_SD_CLOSE_1ST_CATEGORY | 0x1; + rc = msm_sd_register(&ispif->msm_sd); + if (rc) { + pr_err("%s: msm_sd_register error = %d\n", __func__, rc); + goto error; + } + ispif->pdev = pdev; ispif->ispif_state = ISPIF_POWER_DOWN; ispif->open_cnt = 0; - return 0; error: - msm_sd_unregister(&ispif->msm_sd); -error_sd_register: mutex_destroy(&ispif->mutex); kfree(ispif); return rc; diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c index 44a40144067b..ddf9261e727c 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c +++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c @@ -398,7 +398,7 @@ void msm_jpeg_io_dump(void *base, int size) p_str = line_str; for (i = 0; i < size/4; i++) { if (i % 4 == 0) { - snprintf(p_str, 12, "%08x: ", (u32) p); + snprintf(p_str, 12, "%08lx: ", (unsigned long)p); p_str += 10; } data = readl_relaxed(p++); diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c index 8604b1079f18..064f52c04e12 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c +++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c @@ -31,14 +31,19 @@ int msm_jpeg_platform_set_clk_rate(struct msm_jpeg_device *pgmn_dev, long clk_rate) { - struct msm_cam_clk_info jpeg_core_clk_info[] = { - {"core_clk", JPEG_CLK_RATE, 0} - }; + int rc = 0; + struct clk *jpeg_clk; + + jpeg_clk = clk_get(&pgmn_dev->pdev->dev, "core_clk"); + if (IS_ERR(jpeg_clk)) { + JPEG_PR_ERR("%s get failed\n", "core_clk"); + rc = PTR_ERR(jpeg_clk); + return rc; + } - jpeg_core_clk_info[0].clk_rate = clk_rate; + rc = clk_set_rate(jpeg_clk, clk_rate); - return msm_cam_clk_enable(&pgmn_dev->pdev->dev, jpeg_core_clk_info, - pgmn_dev->jpeg_clk, ARRAY_SIZE(jpeg_core_clk_info), 1); + return rc; } void msm_jpeg_platform_p2v(struct msm_jpeg_device *pgmn_dev, struct file *file, @@ -170,6 +175,50 @@ static struct msm_bus_scale_pdata msm_jpeg_bus_client_pdata = { .name = "msm_jpeg", }; +#ifdef CONFIG_MSM_IOMMU +static int msm_jpeg_attach_iommu(struct msm_jpeg_device *pgmn_dev) +{ + int i; + + for (i = 0; i < pgmn_dev->iommu_cnt; i++) { + int rc = iommu_attach_device(pgmn_dev->domain, + pgmn_dev->iommu_ctx_arr[i]); + if (rc < 0) { + JPEG_PR_ERR("%s: Device attach failed\n", __func__); + return -ENODEV; + } + JPEG_DBG("%s:%d] dom 0x%lx ctx 0x%lx", __func__, __LINE__, + (unsigned long)pgmn_dev->domain, + (unsigned long)pgmn_dev->iommu_ctx_arr[i]); + } + return 0; +} +static int msm_jpeg_detach_iommu(struct msm_jpeg_device *pgmn_dev) +{ + int i; + + for (i = 0; i < pgmn_dev->iommu_cnt; i++) { + JPEG_DBG("%s:%d] dom 0x%lx ctx 0x%lx", __func__, __LINE__, + (unsigned long)pgmn_dev->domain, + (unsigned long)pgmn_dev->iommu_ctx_arr[i]); + iommu_detach_device(pgmn_dev->domain, + pgmn_dev->iommu_ctx_arr[i]); + } + return 0; +} +#else +static int msm_jpeg_attach_iommu(struct msm_jpeg_device *pgmn_dev) +{ + return 0; +} +static int msm_jpeg_detach_iommu(struct msm_jpeg_device *pgmn_dev) +{ + return 0; +} +#endif + + + int msm_jpeg_platform_init(struct platform_device *pdev, struct resource **mem, void **base, @@ -178,7 +227,6 @@ int msm_jpeg_platform_init(struct platform_device *pdev, void *context) { int rc = -1; - int i = 0; int jpeg_irq; struct resource *jpeg_mem, *jpeg_io, *jpeg_irq_res; void *jpeg_base; @@ -255,20 +303,10 @@ int msm_jpeg_platform_init(struct platform_device *pdev, JPEG_DBG("%s:%d] jpeg_vbif 0x%x", __func__, __LINE__, (uint32_t)pgmn_dev->jpeg_vbif); -#ifdef CONFIG_MSM_IOMMU - for (i = 0; i < pgmn_dev->iommu_cnt; i++) { - rc = iommu_attach_device(pgmn_dev->domain, - pgmn_dev->iommu_ctx_arr[i]); - if (rc < 0) { - rc = -ENODEV; - JPEG_PR_ERR("%s: Device attach failed\n", __func__); - goto fail_iommu; - } - JPEG_DBG("%s:%d] dom 0x%x ctx 0x%x", __func__, __LINE__, - (uint32_t)pgmn_dev->domain, - (uint32_t)pgmn_dev->iommu_ctx_arr[i]); - } -#endif + rc = msm_jpeg_attach_iommu(pgmn_dev); + if (rc < 0) + goto fail_iommu; + set_vbif_params(pgmn_dev, pgmn_dev->jpeg_vbif); rc = request_irq(jpeg_irq, handler, IRQF_TRIGGER_RISING, "jpeg", @@ -290,19 +328,12 @@ int msm_jpeg_platform_init(struct platform_device *pdev, return rc; fail_request_irq: -#ifdef CONFIG_MSM_IOMMU - for (i = 0; i < pgmn_dev->iommu_cnt; i++) { - JPEG_PR_ERR("%s:%d] dom 0x%x ctx 0x%x", __func__, __LINE__, - (uint32_t)pgmn_dev->domain, - (uint32_t)pgmn_dev->iommu_ctx_arr[i]); - iommu_detach_device(pgmn_dev->domain, - pgmn_dev->iommu_ctx_arr[i]); - } -#endif + msm_jpeg_detach_iommu(pgmn_dev); fail_iommu: iounmap(pgmn_dev->jpeg_vbif); + fail_vbif: msm_cam_clk_enable(&pgmn_dev->pdev->dev, jpeg_8x_clk_info, pgmn_dev->jpeg_clk, ARRAY_SIZE(jpeg_8x_clk_info), 0); @@ -329,19 +360,13 @@ int msm_jpeg_platform_release(struct resource *mem, void *base, int irq, void *context) { int result = 0; - int i = 0; + struct msm_jpeg_device *pgmn_dev = (struct msm_jpeg_device *) context; free_irq(irq, context); -#ifdef CONFIG_MSM_IOMMU - for (i = 0; i < pgmn_dev->iommu_cnt; i++) { - iommu_detach_device(pgmn_dev->domain, - pgmn_dev->iommu_ctx_arr[i]); - JPEG_DBG("%s:%d]", __func__, __LINE__); - } -#endif + msm_jpeg_detach_iommu(pgmn_dev); msm_bus_scale_unregister_client(pgmn_dev->jpeg_bus_client); msm_cam_clk_enable(&pgmn_dev->pdev->dev, jpeg_8x_clk_info, diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c index 5cc51ffa5733..9ec7d87e698b 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c +++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c @@ -105,7 +105,7 @@ inline int msm_jpeg_q_in_buf(struct msm_jpeg_q *q_p, inline int msm_jpeg_q_wait(struct msm_jpeg_q *q_p) { - int tm = MAX_SCHEDULE_TIMEOUT; /* 500ms */ + long tm = MAX_SCHEDULE_TIMEOUT; /* 500ms */ int rc; JPEG_DBG("%s:%d] %s wait\n", __func__, __LINE__, q_p->name); @@ -807,7 +807,7 @@ int msm_jpeg_ioctl_test_dump_region(struct msm_jpeg_device *pgmn_dev, } int msm_jpeg_ioctl_set_clk_rate(struct msm_jpeg_device *pgmn_dev, - unsigned long arg) + void * __user arg) { long clk_rate; int rc; @@ -817,7 +817,7 @@ int msm_jpeg_ioctl_set_clk_rate(struct msm_jpeg_device *pgmn_dev, JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__); return -EFAULT; } - if (get_user(clk_rate, (long __user *)arg)) { + if (get_user(clk_rate, (unsigned int __user *)arg)) { JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__); return -EFAULT; } @@ -906,7 +906,7 @@ long __msm_jpeg_ioctl(struct msm_jpeg_device *pgmn_dev, break; case MSM_JPEG_IOCTL_SET_CLK_RATE: - rc = msm_jpeg_ioctl_set_clk_rate(pgmn_dev, arg); + rc = msm_jpeg_ioctl_set_clk_rate(pgmn_dev, (void __user *) arg); break; default: JPEG_PR_ERR(KERN_INFO "%s:%d] cmd = %d not supported\n", @@ -916,7 +916,7 @@ long __msm_jpeg_ioctl(struct msm_jpeg_device *pgmn_dev, } return rc; } - +#ifdef CONFIG_MSM_IOMMU static int camera_register_domain(void) { struct msm_iova_partition camera_fw_partition = { @@ -932,13 +932,17 @@ static int camera_register_domain(void) }; return msm_register_domain(&camera_fw_layout); } +#endif int __msm_jpeg_init(struct msm_jpeg_device *pgmn_dev) { - int rc = 0, i = 0, j = 0; + int rc = 0; int idx = 0; +#ifdef CONFIG_MSM_IOMMU + int i = 0, j = 0; char *iommu_name[JPEG_DEV_CNT] = {"jpeg_enc0", "jpeg_enc1", "jpeg_dec"}; +#endif mutex_init(&pgmn_dev->lock); @@ -987,7 +991,9 @@ int __msm_jpeg_init(struct msm_jpeg_device *pgmn_dev) #endif return rc; +#ifdef CONFIG_MSM_IOMMU error: +#endif mutex_destroy(&pgmn_dev->lock); return -EFAULT; } diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c index a7f135b28097..4ac2b226ebb7 100644 --- a/drivers/media/platform/msm/camera_v2/msm.c +++ b/drivers/media/platform/msm/camera_v2/msm.c @@ -341,17 +341,26 @@ int msm_create_session(unsigned int session_id, struct video_device *vdev) { struct msm_session *session = NULL; - if (!msm_session_q) + if (!msm_session_q) { + pr_err("%s : session queue not available Line %d\n", + __func__, __LINE__); return -ENODEV; + } session = msm_queue_find(msm_session_q, struct msm_session, list, __msm_queue_find_session, &session_id); - if (session) + if (session) { + pr_err("%s : Session not found Line %d\n", + __func__, __LINE__); return -EINVAL; + } session = kzalloc(sizeof(*session), GFP_KERNEL); - if (!session) + if (!session) { + pr_err("%s : Memory not available Line %d\n", + __func__, __LINE__); return -ENOMEM; + } session->session_id = session_id; session->event_q.vdev = vdev; @@ -367,17 +376,25 @@ int msm_create_command_ack_q(unsigned int session_id, unsigned int stream_id) struct msm_session *session; struct msm_command_ack *cmd_ack; - if (!msm_session_q) + if (!msm_session_q) { + pr_err("%s : Session queue not available Line %d\n", + __func__, __LINE__); return -ENODEV; + } session = msm_queue_find(msm_session_q, struct msm_session, list, __msm_queue_find_session, &session_id); - if (!session) + if (!session) { + pr_err("%s : Session not found Line %d\n", + __func__, __LINE__); return -EINVAL; + } mutex_lock(&session->lock); cmd_ack = kzalloc(sizeof(*cmd_ack), GFP_KERNEL); if (!cmd_ack) { mutex_unlock(&session->lock); + pr_err("%s : memory not available Line %d\n", + __func__, __LINE__); return -ENOMEM; } @@ -664,6 +681,8 @@ int msm_post_event(struct v4l2_event *event, int timeout) spin_lock_irqsave(&msm_eventq_lock, flags); if (!msm_eventq) { spin_unlock_irqrestore(&msm_eventq_lock, flags); + pr_err("%s : msm event queue not available Line %d\n", + __func__, __LINE__); return -ENODEV; } spin_unlock_irqrestore(&msm_eventq_lock, flags); @@ -673,14 +692,19 @@ int msm_post_event(struct v4l2_event *event, int timeout) /* send to imaging server and wait for ACK */ session = msm_queue_find(msm_session_q, struct msm_session, list, __msm_queue_find_session, &session_id); - if (WARN_ON(!session)) + if (WARN_ON(!session)) { + pr_err("%s : session not found Line %d\n", + __func__, __LINE__); return -EIO; + } mutex_lock(&session->lock); cmd_ack = msm_queue_find(&session->command_ack_q, struct msm_command_ack, list, __msm_queue_find_command_ack_q, &stream_id); if (WARN_ON(!cmd_ack)) { mutex_unlock(&session->lock); + pr_err("%s : cmd_ack not found Line %d\n", + __func__, __LINE__); return -EIO; } @@ -688,6 +712,8 @@ int msm_post_event(struct v4l2_event *event, int timeout) if (timeout < 0) { mutex_unlock(&session->lock); + pr_err("%s : timeout cannot be negative Line %d\n", + __func__, __LINE__); return rc; } @@ -718,6 +744,8 @@ int msm_post_event(struct v4l2_event *event, int timeout) struct msm_command, list); if (!cmd) { mutex_unlock(&session->lock); + pr_err("%s : cmd dequeue failed Line %d\n", + __func__, __LINE__); return -EINVAL; } @@ -725,8 +753,15 @@ int msm_post_event(struct v4l2_event *event, int timeout) /* compare cmd_ret and event */ if (WARN_ON(event->type != cmd->event.type) || - WARN_ON(event->id != cmd->event.id)) + WARN_ON(event->id != cmd->event.id)) { + pr_err("%s : Either event type or id didnot match Line %d\n", + __func__, __LINE__); + pr_err("%s : event->type %d event->id %d\n", __func__, + event->type, event->id); + pr_err("%s : cmd->event.type %d cmd->event.id %d\n", __func__, + cmd->event.type, cmd->event.id); rc = -EINVAL; + } *event = cmd->event; diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c index 2a72845a4581..40e51647583a 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c @@ -18,7 +18,7 @@ struct v4l2_subdev *msm_buf_mngr_get_subdev(void) return &msm_buf_mngr_dev->subdev.sd; } -static int msm_buf_mngr_get_buf(struct msm_buf_mngr_device *buf_mngr_dev, +static int32_t msm_buf_mngr_get_buf(struct msm_buf_mngr_device *buf_mngr_dev, void __user *argp) { unsigned long flags; @@ -48,12 +48,12 @@ static int msm_buf_mngr_get_buf(struct msm_buf_mngr_device *buf_mngr_dev, return 0; } -static int msm_buf_mngr_buf_done(struct msm_buf_mngr_device *buf_mngr_dev, +static int32_t msm_buf_mngr_buf_done(struct msm_buf_mngr_device *buf_mngr_dev, struct msm_buf_mngr_info *buf_info) { unsigned long flags; struct msm_get_bufs *bufs, *save; - int ret = -EINVAL; + int32_t ret = -EINVAL; spin_lock_irqsave(&buf_mngr_dev->buf_q_spinlock, flags); list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) { @@ -77,12 +77,12 @@ static int msm_buf_mngr_buf_done(struct msm_buf_mngr_device *buf_mngr_dev, } -static int msm_buf_mngr_put_buf(struct msm_buf_mngr_device *buf_mngr_dev, +static int32_t msm_buf_mngr_put_buf(struct msm_buf_mngr_device *buf_mngr_dev, struct msm_buf_mngr_info *buf_info) { unsigned long flags; struct msm_get_bufs *bufs, *save; - int ret = -EINVAL; + int32_t ret = -EINVAL; spin_lock_irqsave(&buf_mngr_dev->buf_q_spinlock, flags); list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) { @@ -109,8 +109,9 @@ static void msm_buf_mngr_sd_shutdown(struct msm_buf_mngr_device *buf_mngr_dev) if (!list_empty(&buf_mngr_dev->buf_qhead)) { list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) { - pr_err("%s: Error: Delete invalid bufs =%x\n", __func__, - (unsigned int)bufs); + pr_err("%s: Error delete invalid bufs =%x, ses_id=%d, str_id=%d, idx=%d\n", + __func__, (unsigned int)bufs, bufs->session_id, + bufs->stream_id, bufs->vb2_buf->v4l2_buf.index); list_del_init(&bufs->entry); kfree(bufs); } @@ -118,10 +119,40 @@ static void msm_buf_mngr_sd_shutdown(struct msm_buf_mngr_device *buf_mngr_dev) spin_unlock_irqrestore(&buf_mngr_dev->buf_q_spinlock, flags); } +static int msm_generic_buf_mngr_open(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + int rc = 0; + struct msm_buf_mngr_device *buf_mngr_dev = v4l2_get_subdevdata(sd); + if (!buf_mngr_dev) { + pr_err("%s buf manager device NULL\n", __func__); + rc = -ENODEV; + return rc; + } + buf_mngr_dev->msm_buf_mngr_open_cnt++; + return rc; +} + +static int msm_generic_buf_mngr_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + int rc = 0; + struct msm_buf_mngr_device *buf_mngr_dev = v4l2_get_subdevdata(sd); + if (!buf_mngr_dev) { + pr_err("%s buf manager device NULL\n", __func__); + rc = -ENODEV; + return rc; + } + buf_mngr_dev->msm_buf_mngr_open_cnt--; + if (buf_mngr_dev->msm_buf_mngr_open_cnt == 0) + msm_buf_mngr_sd_shutdown(buf_mngr_dev); + return rc; +} + static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { - int rc = 0; + int32_t rc = 0; struct msm_buf_mngr_device *buf_mngr_dev = v4l2_get_subdevdata(sd); void __user *argp = (void __user *)arg; @@ -141,6 +172,12 @@ static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd, case VIDIOC_MSM_BUF_MNGR_PUT_BUF: rc = msm_buf_mngr_put_buf(buf_mngr_dev, argp); break; + case VIDIOC_MSM_BUF_MNGR_INIT: + rc = msm_generic_buf_mngr_open(sd, NULL); + break; + case VIDIOC_MSM_BUF_MNGR_DEINIT: + rc = msm_generic_buf_mngr_close(sd, NULL); + break; case MSM_SD_SHUTDOWN: msm_buf_mngr_sd_shutdown(buf_mngr_dev); break; @@ -154,6 +191,12 @@ static struct v4l2_subdev_core_ops msm_buf_mngr_subdev_core_ops = { .ioctl = msm_buf_mngr_subdev_ioctl, }; +static const struct v4l2_subdev_internal_ops + msm_generic_buf_mngr_subdev_internal_ops = { + .open = msm_generic_buf_mngr_open, + .close = msm_generic_buf_mngr_close, +}; + static const struct v4l2_subdev_ops msm_buf_mngr_subdev_ops = { .core = &msm_buf_mngr_subdev_core_ops, }; @@ -163,9 +206,27 @@ static const struct of_device_id msm_buf_mngr_dt_match[] = { {} }; -static int __init msm_buf_mngr_init(void) +static struct v4l2_file_operations msm_buf_v4l2_subdev_fops; + +static long msm_bmgr_subdev_do_ioctl( + struct file *file, unsigned int cmd, void *arg) { - int rc = 0; + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + + return v4l2_subdev_call(sd, core, ioctl, cmd, arg); +} + + +static long msm_buf_subdev_fops_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return video_usercopy(file, cmd, arg, msm_bmgr_subdev_do_ioctl); +} + +static int32_t __init msm_buf_mngr_init(void) +{ + int32_t rc = 0; msm_buf_mngr_dev = kzalloc(sizeof(*msm_buf_mngr_dev), GFP_KERNEL); if (WARN_ON(!msm_buf_mngr_dev)) { @@ -175,6 +236,13 @@ static int __init msm_buf_mngr_init(void) /* Sub-dev */ v4l2_subdev_init(&msm_buf_mngr_dev->subdev.sd, &msm_buf_mngr_subdev_ops); + + msm_buf_v4l2_subdev_fops.owner = v4l2_subdev_fops.owner; + msm_buf_v4l2_subdev_fops.open = v4l2_subdev_fops.open; + msm_buf_v4l2_subdev_fops.unlocked_ioctl = msm_buf_subdev_fops_ioctl; + msm_buf_v4l2_subdev_fops.release = v4l2_subdev_fops.release; + msm_buf_v4l2_subdev_fops.poll = v4l2_subdev_fops.poll; + snprintf(msm_buf_mngr_dev->subdev.sd.name, ARRAY_SIZE(msm_buf_mngr_dev->subdev.sd.name), "msm_buf_mngr"); msm_buf_mngr_dev->subdev.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -184,6 +252,8 @@ static int __init msm_buf_mngr_init(void) msm_buf_mngr_dev->subdev.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; msm_buf_mngr_dev->subdev.sd.entity.group_id = MSM_CAMERA_SUBDEV_BUF_MNGR; + msm_buf_mngr_dev->subdev.sd.internal_ops = + &msm_generic_buf_mngr_subdev_internal_ops; msm_buf_mngr_dev->subdev.close_seq = MSM_SD_CLOSE_4TH_CATEGORY; rc = msm_sd_register(&msm_buf_mngr_dev->subdev); if (rc != 0) { @@ -191,6 +261,8 @@ static int __init msm_buf_mngr_init(void) goto end; } + msm_buf_mngr_dev->subdev.sd.devnode->fops = &msm_buf_v4l2_subdev_fops; + v4l2_subdev_notify(&msm_buf_mngr_dev->subdev.sd, MSM_SD_NOTIFY_REQ_CB, &msm_buf_mngr_dev->vb2_ops); diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h index 56886cd94843..49fad229ca0c 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h @@ -36,5 +36,6 @@ struct msm_buf_mngr_device { spinlock_t buf_q_spinlock; struct msm_sd_subdev subdev; struct msm_sd_req_vb2_q vb2_ops; + uint32_t msm_buf_mngr_open_cnt; }; #endif diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c index cfb9321eb30e..a93f02b7f892 100644 --- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c +++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c @@ -70,6 +70,9 @@ #define CPP_CLK_INFO_MAX 16 +static int msm_cpp_buffer_ops(struct cpp_device *cpp_dev, + uint32_t buff_mgr_ops, struct msm_buf_mngr_info *buff_mgr_info); + #if CONFIG_MSM_CPP_DBG #define CPP_DBG(fmt, args...) pr_err(fmt, ##args) #else @@ -739,6 +742,14 @@ static int cpp_init_hardware(struct cpp_device *cpp_dev) goto req_irq_fail; } cpp_dev->buf_mgr_subdev = msm_buf_mngr_get_subdev(); + + rc = msm_cpp_buffer_ops(cpp_dev, + VIDIOC_MSM_BUF_MNGR_INIT, NULL); + if (rc < 0) { + pr_err("buf mngr init failed\n"); + free_irq(cpp_dev->irq->start, cpp_dev); + goto req_irq_fail; + } } cpp_dev->hw_info.cpp_hw_version = @@ -783,7 +794,14 @@ bus_scale_register_failed: static void cpp_release_hardware(struct cpp_device *cpp_dev) { + int32_t rc; if (cpp_dev->state != CPP_STATE_BOOT) { + rc = msm_cpp_buffer_ops(cpp_dev, + VIDIOC_MSM_BUF_MNGR_DEINIT, NULL); + if (rc < 0) { + pr_err("error in buf mngr deinit\n"); + rc = -EINVAL; + } free_irq(cpp_dev->irq->start, cpp_dev); tasklet_kill(&cpp_dev->cpp_tasklet); atomic_set(&cpp_dev->irq_cnt, 0); @@ -1249,7 +1267,7 @@ static int msm_cpp_cfg(struct cpp_device *cpp_dev, uint32_t *cpp_frame_msg; unsigned long in_phyaddr, out_phyaddr0, out_phyaddr1; uint16_t num_stripes = 0; - struct msm_buf_mngr_info buff_mgr_info; + struct msm_buf_mngr_info buff_mgr_info, dup_buff_mgr_info; struct msm_cpp_frame_info_t *u_frame_info = (struct msm_cpp_frame_info_t *)ioctl_ptr->ioctl_ptr; int32_t status = 0; @@ -1341,19 +1359,20 @@ static int msm_cpp_cfg(struct cpp_device *cpp_dev, new_frame->duplicate_identity); memset(&new_frame->output_buffer_info[1], 0, sizeof(struct msm_cpp_buffer_info_t)); - memset(&buff_mgr_info, 0, sizeof(struct msm_buf_mngr_info)); - buff_mgr_info.session_id = + memset(&dup_buff_mgr_info, 0, sizeof(struct msm_buf_mngr_info)); + dup_buff_mgr_info.session_id = ((new_frame->duplicate_identity >> 16) & 0xFFFF); - buff_mgr_info.stream_id = + dup_buff_mgr_info.stream_id = (new_frame->duplicate_identity & 0xFFFF); rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_GET_BUF, - &buff_mgr_info); + &dup_buff_mgr_info); if (rc < 0) { rc = -EAGAIN; pr_debug("error getting buffer rc:%d\n", rc); - goto ERROR2; + goto ERROR3; } - new_frame->output_buffer_info[1].index = buff_mgr_info.index; + new_frame->output_buffer_info[1].index = + dup_buff_mgr_info.index; out_phyaddr1 = msm_cpp_fetch_buffer_info(cpp_dev, &new_frame->output_buffer_info[1], ((new_frame->duplicate_identity >> 16) & 0xFFFF), @@ -1362,6 +1381,8 @@ static int msm_cpp_cfg(struct cpp_device *cpp_dev, if (!out_phyaddr1) { pr_err("error gettting output physical address\n"); rc = -EINVAL; + msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_PUT_BUF, + &dup_buff_mgr_info); goto ERROR3; } /* set duplicate enable bit */ @@ -1780,7 +1801,7 @@ long msm_cpp_subdev_ioctl(struct v4l2_subdev *sd, break; } default: - pr_err("invalid value: cmd=0x%x\n", cmd); + pr_err_ratelimited("invalid value: cmd=0x%x\n", cmd); break; } mutex_unlock(&cpp_dev->mutex); diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c index 89bb6bf6684f..08148362882d 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c +++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c @@ -634,7 +634,11 @@ ERROR: static int msm_cci_subdev_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { - BUG_ON(!chip); + if (!chip) { + pr_err("%s:%d: NULL pointer supplied for chip ident\n", + __func__, __LINE__); + return -EINVAL; + } chip->ident = V4L2_IDENT_CCI; chip->revision = 0; return 0; diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c index a92fc7875e98..4841370f12c9 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c +++ b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c @@ -285,56 +285,16 @@ static int msm_csid_release(struct csid_device *csid_dev) disable_irq(csid_dev->irq->start); - if (csid_dev->hw_version == CSID_VERSION_V20) { - msm_cam_clk_enable(&csid_dev->pdev->dev, csid_clk_info, - csid_dev->csid_clk, ARRAY_SIZE(csid_clk_info), 0); + msm_cam_clk_enable(&csid_dev->pdev->dev, csid_clk_info, + csid_dev->csid_clk, ARRAY_SIZE(csid_clk_info), 0); - msm_camera_enable_vreg(&csid_dev->pdev->dev, - csid_vreg_info, ARRAY_SIZE(csid_vreg_info), - NULL, 0, &csid_dev->csi_vdd, 0); - - msm_camera_config_vreg(&csid_dev->pdev->dev, - csid_vreg_info, ARRAY_SIZE(csid_vreg_info), - NULL, 0, &csid_dev->csi_vdd, 0); - } else if (csid_dev->hw_version == CSID_VERSION_V22) { - msm_cam_clk_enable(&csid_dev->pdev->dev, - csid_clk_info, - csid_dev->csid_clk, - ARRAY_SIZE(csid_clk_info), 0); - - msm_camera_enable_vreg(&csid_dev->pdev->dev, - csid_vreg_info, ARRAY_SIZE(csid_vreg_info), - NULL, 0, &csid_dev->csi_vdd, 0); - - msm_camera_config_vreg(&csid_dev->pdev->dev, - csid_vreg_info, ARRAY_SIZE(csid_vreg_info), - NULL, 0, &csid_dev->csi_vdd, 0); - } else if ((csid_dev->hw_version >= CSID_VERSION_V30 && - csid_dev->hw_version < CSID_VERSION_V31) || - csid_dev->hw_version == CSID_VERSION_V40) { - msm_cam_clk_enable(&csid_dev->pdev->dev, csid_clk_info, - csid_dev->csid_clk, ARRAY_SIZE(csid_clk_info), 0); - msm_camera_enable_vreg(&csid_dev->pdev->dev, - csid_vreg_info, ARRAY_SIZE(csid_vreg_info), - NULL, 0, &csid_dev->csi_vdd, 0); - msm_camera_config_vreg(&csid_dev->pdev->dev, - csid_vreg_info, ARRAY_SIZE(csid_vreg_info), - NULL, 0, &csid_dev->csi_vdd, 0); - } else if (csid_dev->hw_version == CSID_VERSION_V31) { - msm_cam_clk_enable(&csid_dev->pdev->dev, csid_clk_info, - csid_dev->csid_clk, ARRAY_SIZE(csid_clk_info), 0); - msm_camera_enable_vreg(&csid_dev->pdev->dev, - csid_vreg_info, ARRAY_SIZE(csid_vreg_info), - NULL, 0, &csid_dev->csi_vdd, 0); + msm_camera_enable_vreg(&csid_dev->pdev->dev, + csid_vreg_info, ARRAY_SIZE(csid_vreg_info), + NULL, 0, &csid_dev->csi_vdd, 0); - msm_camera_config_vreg(&csid_dev->pdev->dev, - csid_vreg_info, ARRAY_SIZE(csid_vreg_info), - NULL, 0, &csid_dev->csi_vdd, 0); - } else { - pr_err("%s:%d, invalid hw version : 0x%x", __func__, __LINE__, - csid_dev->hw_version); - return -EINVAL; - } + msm_camera_config_vreg(&csid_dev->pdev->dev, + csid_vreg_info, ARRAY_SIZE(csid_vreg_info), + NULL, 0, &csid_dev->csi_vdd, 0); iounmap(csid_dev->base); csid_dev->base = NULL; @@ -448,7 +408,7 @@ static long msm_csid_subdev_ioctl(struct v4l2_subdev *sd, rc = msm_csid_release(csid_dev); break; default: - pr_err("%s: command not found\n", __func__); + pr_err_ratelimited("%s: command not found\n", __func__); } CDBG("%s:%d\n", __func__, __LINE__); mutex_unlock(&csid_dev->mutex); diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c index b5abc43d5b9a..aa2c534aaefc 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c @@ -608,7 +608,7 @@ static long msm_csiphy_subdev_ioctl(struct v4l2_subdev *sd, rc = msm_csiphy_release(csiphy_dev, arg); break; default: - pr_err("%s: command not found\n", __func__); + pr_err_ratelimited("%s: command not found\n", __func__); } mutex_unlock(&csiphy_dev->mutex); CDBG("%s:%d\n", __func__, __LINE__); diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c index 2de17c95cba8..149d00cd8d36 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c +++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c @@ -46,7 +46,7 @@ static long msm_led_flash_subdev_ioctl(struct v4l2_subdev *sd, *(int *)argp = MSM_CAMERA_LED_RELEASE; return fctrl->func_tbl->flash_led_config(fctrl, argp); default: - pr_err("invalid cmd %d\n", cmd); + pr_err_ratelimited("invalid cmd %d\n", cmd); return -ENOIOCTLCMD; } } diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c index 5e2cc9e9a17b..ba65c202e26f 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c +++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c @@ -378,6 +378,7 @@ int msm_camera_get_dt_power_setting_data(struct device_node *of_node, return -ENOMEM; } power_setting = ps; + power_info->power_setting = ps; for (i = 0; i < count; i++) { rc = of_property_read_string_index(of_node, diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c index 38a47a8dd69b..5007e076daee 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c @@ -17,7 +17,7 @@ #include "msm_camera_io_util.h" #include "msm_camera_i2c_mux.h" #include <mach/rpm-regulator.h> -#include <mach/rpm-regulator-smd.h> +#include <linux/regulator/rpm-smd-regulator.h> #include <linux/regulator/consumer.h> /*#define CONFIG_MSMB_CAMERA_DEBUG*/ @@ -426,6 +426,7 @@ int msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl) struct msm_camera_i2c_client *sensor_i2c_client; struct msm_camera_slave_info *slave_info; const char *sensor_name; + uint32_t retry = 0; if (!s_ctrl) { pr_err("%s:%d failed: %p\n", @@ -446,14 +447,21 @@ int msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl) return -EINVAL; } - rc = msm_camera_power_up(power_info, s_ctrl->sensor_device_type, - sensor_i2c_client); - if (rc < 0) - return rc; - rc = msm_sensor_check_id(s_ctrl); - if (rc < 0) - msm_camera_power_down(power_info, s_ctrl->sensor_device_type, - sensor_i2c_client); + for (retry = 0; retry < 3; retry++) { + rc = msm_camera_power_up(power_info, s_ctrl->sensor_device_type, + sensor_i2c_client); + if (rc < 0) + return rc; + rc = msm_sensor_check_id(s_ctrl); + if (rc < 0) { + msm_camera_power_down(power_info, + s_ctrl->sensor_device_type, sensor_i2c_client); + msleep(20); + continue; + } else { + break; + } + } return rc; } diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c index ae44f2d484df..c252e3d6bdd7 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c @@ -123,7 +123,7 @@ static long msm_sensor_init_subdev_ioctl(struct v4l2_subdev *sd, break; default: - pr_err("default"); + pr_err_ratelimited("default\n"); break; } diff --git a/drivers/media/platform/msm/dvb/adapter/mpq_stream_buffer.c b/drivers/media/platform/msm/dvb/adapter/mpq_stream_buffer.c index 6e123c70fa6a..819476fa6807 100644 --- a/drivers/media/platform/msm/dvb/adapter/mpq_stream_buffer.c +++ b/drivers/media/platform/msm/dvb/adapter/mpq_stream_buffer.c @@ -51,6 +51,7 @@ int mpq_streambuffer_init( sbuff->buffers = data_buffers; sbuff->pending_buffers_count = 0; sbuff->buffers_num = data_buff_num; + sbuff->cb = NULL; dvb_ringbuffer_init(&sbuff->packet_data, packet_buff, packet_buff_size); return 0; @@ -182,7 +183,7 @@ int mpq_streambuffer_pkt_dispose( (dispose_data)) { /* Advance the read pointer in the raw-data buffer first */ ret = mpq_streambuffer_data_read_dispose(sbuff, - packet.raw_data_len); + packet.raw_data_len); if (ret != 0) return ret; } @@ -222,9 +223,6 @@ int mpq_streambuffer_pkt_dispose( spin_unlock(&sbuff->raw_data.lock); spin_unlock(&sbuff->packet_data.lock); - if (sbuff->cb) - sbuff->cb(sbuff, &packet, sbuff->cb_user_data); - return 0; } EXPORT_SYMBOL(mpq_streambuffer_pkt_dispose); @@ -288,7 +286,7 @@ int mpq_streambuffer_pkt_write( spin_unlock(&sbuff->packet_data.lock); wake_up_all(&sbuff->packet_data.queue); - return 0; + return idx; } EXPORT_SYMBOL(mpq_streambuffer_pkt_write); @@ -414,6 +412,7 @@ ssize_t mpq_streambuffer_data_read( u8 *buf, size_t len) { ssize_t actual_len = 0; + u32 offset; if ((NULL == sbuff) || (NULL == buf)) return -EINVAL; @@ -436,6 +435,7 @@ ssize_t mpq_streambuffer_data_read( return -EPERM; } + offset = sbuff->raw_data.pread; actual_len = dvb_ringbuffer_avail(&sbuff->raw_data); if (actual_len < len) len = actual_len; @@ -463,10 +463,15 @@ ssize_t mpq_streambuffer_data_read( if (actual_len < len) len = actual_len; memcpy(buf, desc->base + desc->read_ptr, len); + offset = desc->read_ptr; desc->read_ptr += len; } spin_unlock(&sbuff->raw_data.lock); + + if (sbuff->cb) + sbuff->cb(sbuff, offset, len, sbuff->cb_user_data); + return len; } EXPORT_SYMBOL(mpq_streambuffer_data_read); @@ -477,6 +482,7 @@ ssize_t mpq_streambuffer_data_read_user( u8 __user *buf, size_t len) { ssize_t actual_len = 0; + u32 offset; if ((NULL == sbuff) || (NULL == buf)) return -EINVAL; @@ -493,6 +499,7 @@ ssize_t mpq_streambuffer_data_read_user( if (NULL == sbuff->raw_data.data) return -EPERM; + offset = sbuff->raw_data.pread; actual_len = dvb_ringbuffer_avail(&sbuff->raw_data); if (actual_len < len) len = actual_len; @@ -519,9 +526,13 @@ ssize_t mpq_streambuffer_data_read_user( if (copy_to_user(buf, desc->base + desc->read_ptr, len)) return -EFAULT; + offset = desc->read_ptr; desc->read_ptr += len; } + if (sbuff->cb) + sbuff->cb(sbuff, offset, len, sbuff->cb_user_data); + return len; } EXPORT_SYMBOL(mpq_streambuffer_data_read_user); @@ -530,6 +541,8 @@ int mpq_streambuffer_data_read_dispose( struct mpq_streambuffer *sbuff, size_t len) { + u32 offset; + if (NULL == sbuff) return -EINVAL; @@ -547,6 +560,7 @@ int mpq_streambuffer_data_read_dispose( return -EINVAL; } + offset = sbuff->raw_data.pread; DVB_RINGBUFFER_SKIP(&sbuff->raw_data, len); wake_up_all(&sbuff->raw_data.queue); } else { @@ -554,6 +568,8 @@ int mpq_streambuffer_data_read_dispose( desc = (struct mpq_streambuffer_buffer_desc *) &sbuff->raw_data.data[sbuff->raw_data.pread]; + offset = desc->read_ptr; + if ((desc->read_ptr + len) > desc->size) desc->read_ptr = desc->size; else @@ -562,6 +578,9 @@ int mpq_streambuffer_data_read_dispose( spin_unlock(&sbuff->raw_data.lock); + if (sbuff->cb) + sbuff->cb(sbuff, offset, len, sbuff->cb_user_data); + return 0; } EXPORT_SYMBOL(mpq_streambuffer_data_read_dispose); @@ -604,9 +623,9 @@ int mpq_streambuffer_get_buffer_handle( EXPORT_SYMBOL(mpq_streambuffer_get_buffer_handle); -int mpq_streambuffer_register_pkt_dispose( +int mpq_streambuffer_register_data_dispose( struct mpq_streambuffer *sbuff, - mpq_streambuffer_pkt_dispose_cb cb_func, + mpq_streambuffer_dispose_cb cb_func, void *user_data) { if ((NULL == sbuff) || (NULL == cb_func)) @@ -617,7 +636,7 @@ int mpq_streambuffer_register_pkt_dispose( return 0; } -EXPORT_SYMBOL(mpq_streambuffer_register_pkt_dispose); +EXPORT_SYMBOL(mpq_streambuffer_register_data_dispose); ssize_t mpq_streambuffer_data_free( diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c index 1d7a1509a48e..8675ac15c464 100644 --- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c +++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c @@ -807,7 +807,7 @@ void mpq_dmx_plugin_exit(void) &mpq_demux->demux.dmx, &mpq_demux->fe_memory); - if (mpq_sdmx_is_loaded()) + if (mpq_dmx_info.secure_demux_app_loaded) mpq_sdmx_close_session(mpq_demux); mutex_destroy(&mpq_demux->mutex); dvb_dmxdev_release(&mpq_demux->dmxdev); @@ -1477,7 +1477,6 @@ int mpq_dmx_init_video_feed(struct mpq_feed *mpq_feed) feed_data->continuity_errs = 0; feed_data->ts_packets_num = 0; feed_data->ts_dropped_bytes = 0; - feed_data->last_pkt_index = -1; mpq_demux->decoder_stat[feed_data->stream_interface].drop_count = 0; mpq_demux->decoder_stat[feed_data->stream_interface].out_count = 0; @@ -2199,9 +2198,9 @@ static inline void mpq_dmx_prepare_es_event_data( struct mpq_adapter_video_meta_data *meta_data, struct mpq_video_feed_info *feed_data, struct mpq_streambuffer *stream_buffer, - struct dmx_data_ready *data) + struct dmx_data_ready *data, + int cookie) { - size_t len = 0; struct dmx_pts_dts_info *pts_dts; if (meta_data->packet_type == DMX_PES_PACKET) { @@ -2218,15 +2217,7 @@ static inline void mpq_dmx_prepare_es_event_data( data->data_length = 0; data->buf.handle = packet->raw_data_handle; - - /* this has to succeed when called here, after packet was written */ - data->buf.cookie = mpq_streambuffer_pkt_next(stream_buffer, - feed_data->last_pkt_index, &len); - if (data->buf.cookie < 0) - MPQ_DVB_DBG_PRINT( - "%s: received invalid packet index %d\n", - __func__, data->buf.cookie); - + data->buf.cookie = cookie; data->buf.offset = packet->raw_data_offset; data->buf.len = packet->raw_data_len; data->buf.pts_exists = pts_dts->pts_exist; @@ -2239,9 +2230,6 @@ static inline void mpq_dmx_prepare_es_event_data( data->buf.ts_dropped_bytes = feed_data->ts_dropped_bytes; data->status = DMX_OK_DECODER_BUF; - /* save for next time: */ - feed_data->last_pkt_index = data->buf.cookie; - MPQ_DVB_DBG_PRINT("%s: cookie=%d\n", __func__, data->buf.cookie); /* reset counters */ @@ -2301,6 +2289,7 @@ static void mpq_dmx_decoder_frame_closure(struct mpq_demux *mpq_demux, struct mpq_video_feed_info *feed_data; struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed; struct dmx_data_ready data; + int cookie; feed_data = &mpq_feed->video_info; @@ -2349,14 +2338,17 @@ static void mpq_dmx_decoder_frame_closure(struct mpq_demux *mpq_demux, mpq_dmx_update_decoder_stat(mpq_feed); /* Writing meta-data that includes the framing information */ - if (mpq_streambuffer_pkt_write(stream_buffer, &packet, - (u8 *)&meta_data) < 0) - MPQ_DVB_ERR_PRINT("%s: Couldn't write packet\n", - __func__); - - mpq_dmx_prepare_es_event_data(&packet, &meta_data, feed_data, - stream_buffer, &data); - feed->data_ready_cb.ts(&feed->feed.ts, &data); + cookie = mpq_streambuffer_pkt_write(stream_buffer, &packet, + (u8 *)&meta_data); + if (cookie >= 0) { + mpq_dmx_prepare_es_event_data(&packet, &meta_data, + feed_data, stream_buffer, &data, cookie); + feed->data_ready_cb.ts(&feed->feed.ts, &data); + } else { + MPQ_DVB_ERR_PRINT( + "%s: mpq_streambuffer_pkt_write failed, ret=%d\n", + __func__, cookie); + } } spin_unlock(&feed_data->video_buffer_lock); @@ -2378,6 +2370,7 @@ static void mpq_dmx_decoder_pes_closure(struct mpq_demux *mpq_demux, struct mpq_video_feed_info *feed_data; struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed; struct dmx_data_ready data; + int cookie; feed_data = &mpq_feed->video_info; @@ -2416,18 +2409,20 @@ static void mpq_dmx_decoder_pes_closure(struct mpq_demux *mpq_demux, mpq_dmx_update_decoder_stat(mpq_feed); - if (mpq_streambuffer_pkt_write(stream_buffer, &packet, - (u8 *)&meta_data) < 0) - MPQ_DVB_ERR_PRINT("%s: Couldn't write packet\n", - __func__); - - /* Save write offset where new PES will begin */ - mpq_streambuffer_get_data_rw_offset(stream_buffer, NULL, - &feed_data->frame_offset); - - mpq_dmx_prepare_es_event_data(&packet, &meta_data, feed_data, - stream_buffer, &data); - feed->data_ready_cb.ts(&feed->feed.ts, &data); + cookie = mpq_streambuffer_pkt_write(stream_buffer, &packet, + (u8 *)&meta_data); + if (cookie >= 0) { + /* Save write offset where new PES will begin */ + mpq_streambuffer_get_data_rw_offset(stream_buffer, NULL, + &feed_data->frame_offset); + mpq_dmx_prepare_es_event_data(&packet, &meta_data, + feed_data, stream_buffer, &data, cookie); + feed->data_ready_cb.ts(&feed->feed.ts, &data); + } else { + MPQ_DVB_ERR_PRINT( + "%s: mpq_streambuffer_pkt_write failed, ret=%d\n", + __func__, cookie); + } } /* Reset PES info */ feed->peslen = 0; @@ -2809,26 +2804,24 @@ static int mpq_dmx_process_video_packet_framing( * writing meta-data that includes * the framing information */ - if (mpq_streambuffer_pkt_write(stream_buffer, - &packet, - (u8 *)&meta_data) < 0) { + ret = mpq_streambuffer_pkt_write(stream_buffer, &packet, + (u8 *)&meta_data); + if (ret < 0) { MPQ_DVB_ERR_PRINT( - "%s: " - "Couldn't write packet. " - "Should never happen\n", - __func__); - } - - mpq_dmx_prepare_es_event_data( - &packet, &meta_data, feed_data, - stream_buffer, &data); + "%s: mpq_streambuffer_pkt_write failed, ret=%d\n", + __func__, ret); + } else { + mpq_dmx_prepare_es_event_data( + &packet, &meta_data, feed_data, + stream_buffer, &data, ret); - feed->data_ready_cb.ts(&feed->feed.ts, &data); + feed->data_ready_cb.ts(&feed->feed.ts, &data); - mpq_streambuffer_get_data_rw_offset( - feed_data->video_buffer, - NULL, - &feed_data->frame_offset); + mpq_streambuffer_get_data_rw_offset( + feed_data->video_buffer, + NULL, + &feed_data->frame_offset); + } /* * In linear buffers, after writing the packet @@ -2936,6 +2929,7 @@ static int mpq_dmx_process_video_packet_no_framing( struct mpq_feed *mpq_feed; int discontinuity_indicator = 0; struct dmx_data_ready data; + int cookie; mpq_demux = feed->demux->priv; mpq_feed = feed->priv; @@ -3006,34 +3000,34 @@ static int mpq_dmx_process_video_packet_no_framing( mpq_dmx_update_decoder_stat(mpq_feed); - if (mpq_streambuffer_pkt_write( - stream_buffer, - &packet, - (u8 *)&meta_data) < 0) + cookie = mpq_streambuffer_pkt_write( + stream_buffer, &packet, + (u8 *)&meta_data); + if (cookie < 0) { MPQ_DVB_ERR_PRINT( - "%s: " - "Couldn't write packet. " - "Should never happen\n", - __func__); - - /* Save write offset where new PES will begin */ - mpq_streambuffer_get_data_rw_offset( - stream_buffer, - NULL, - &feed_data->frame_offset); + "%s: mpq_streambuffer_pkt_write failed, ret=%d\n", + __func__, cookie); + } else { + /* + * Save write offset where new PES + * will begin + */ + mpq_streambuffer_get_data_rw_offset( + stream_buffer, + NULL, + &feed_data->frame_offset); - mpq_dmx_prepare_es_event_data( - &packet, &meta_data, - feed_data, - stream_buffer, &data); + mpq_dmx_prepare_es_event_data( + &packet, &meta_data, + feed_data, + stream_buffer, &data, cookie); - feed->data_ready_cb.ts( - &feed->feed.ts, &data); + feed->data_ready_cb.ts(&feed->feed.ts, + &data); + } } else { MPQ_DVB_ERR_PRINT( - "%s: received PUSI" - "while handling PES header" - "of previous PES\n", + "%s: received PUSI while handling PES header of previous PES\n", __func__); } @@ -3318,7 +3312,7 @@ int mpq_dmx_decoder_eos_cmd(struct mpq_feed *mpq_feed) (u8 *)&oob_meta_data); spin_unlock(&feed_data->video_buffer_lock); - return ret; + return (ret < 0) ? ret : 0; } void mpq_dmx_convert_tts(struct dvb_demux_feed *feed, @@ -4577,18 +4571,19 @@ static void mpq_sdmx_decoder_filter_results(struct mpq_demux *mpq_demux, __func__, ret); } mpq_dmx_update_decoder_stat(mpq_feed); - if (mpq_streambuffer_pkt_write(sbuf, - &packet, - (u8 *)&meta_data) < 0) + ret = mpq_streambuffer_pkt_write(sbuf, &packet, + (u8 *)&meta_data); + if (ret < 0) { MPQ_DVB_ERR_PRINT( - "%s: Couldn't write packet. Should never happen\n", - __func__); - - mpq_dmx_prepare_es_event_data( - &packet, &meta_data, &mpq_feed->video_info, - sbuf, &data); - MPQ_DVB_DBG_PRINT("%s: Notify ES Event\n", __func__); - feed->data_ready_cb.ts(&feed->feed.ts, &data); + "%s: mpq_streambuffer_pkt_write failed, ret=%d\n", + __func__, ret); + } else { + mpq_dmx_prepare_es_event_data( + &packet, &meta_data, &mpq_feed->video_info, + sbuf, &data, ret); + MPQ_DVB_DBG_PRINT("%s: Notify ES Event\n", __func__); + feed->data_ready_cb.ts(&feed->feed.ts, &data); + } spin_unlock(&mpq_feed->video_info.video_buffer_lock); } diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h index d5ba93ad511f..b8e3685e4bd8 100644 --- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h +++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h @@ -297,8 +297,6 @@ struct mpq_decoder_buffers_desc { * @ts_packets_num: TS packets counter. * @ts_dropped_bytes: counts the number of bytes dropped due to insufficient * buffer space. - * @last_pkt_index: used to save the last streambuffer packet index reported in - * a new elementary stream data event. * @prev_stc: STC attached to the previous video TS packet */ struct mpq_video_feed_info { @@ -332,7 +330,6 @@ struct mpq_video_feed_info { u32 continuity_errs; u32 ts_packets_num; u32 ts_dropped_bytes; - int last_pkt_index; u64 prev_stc; }; diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c index ec37b7d97120..17701179b717 100644 --- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c +++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c @@ -77,7 +77,6 @@ static struct struct dentry *debugfs_filters_file; struct dentry *debugfs_sources_file; struct dentry *debugfs_index_tables_file; - } mpq_dmx_tspp2_info; /** @@ -92,7 +91,6 @@ static void pipe_work_queue_init(struct pipe_work_queue *queue) spin_lock_init(&queue->lock); INIT_LIST_HEAD(&queue->work_list); INIT_LIST_HEAD(&queue->free_list); - init_waitqueue_head(&queue->wait_queue); for (i = 0; i < TSPP2_DMX_PIPE_WORK_POOL_SIZE; i++) list_add_tail(&queue->work_pool[i].next, &queue->free_list); @@ -144,7 +142,6 @@ static void pipe_work_queue_push(struct pipe_work_queue *queue, spin_lock_irqsave(&queue->lock, flags); list_add_tail(&work->next, &queue->work_list); - wake_up_all(&queue->wait_queue); spin_unlock_irqrestore(&queue->lock, flags); } @@ -704,13 +701,13 @@ static int mpq_dmx_tspp2_ts_event_check(struct dvb_demux_feed *feed, ret = feed->demux->buffer_ctrl.ts(&feed->feed.ts, 0, 1); mutex_lock(&pipe_info->mutex); if (ret) { - MPQ_DVB_ERR_PRINT( + MPQ_DVB_DBG_PRINT( "%s: buffer_ctrl.ts callback failed, ret=%d!\n", __func__, ret); return ret; } if (pipe_info->session_id != session_id || !pipe_info->ref_count) { - MPQ_DVB_ERR_PRINT( + MPQ_DVB_DBG_PRINT( "%s: pipe was closed / reopened: ref. count=%u, session_id=%u (expected %u)\n", __func__, pipe_info->ref_count, pipe_info->session_id, session_id); @@ -720,6 +717,48 @@ static int mpq_dmx_tspp2_ts_event_check(struct dvb_demux_feed *feed, return 0; } +static int mpq_dmx_tspp2_stream_buffer_event_check(struct dvb_demux_feed *feed, + struct pipe_info *pipe_info) +{ + int ret; + u32 session_id; + + if (feed->demux->playback_mode != DMX_PB_MODE_PULL) + return 0; + + if (!mutex_is_locked(&pipe_info->mutex)) + return -EINVAL; + + /* + * For pull mode need to wait for sufficient room to write the + * meta-data packet in the mpq_streambuffer object. + * Data itself was already written by TSPPv2 hardware (so required_space + * argument is 0). + * Since this may block waiting for the metadata buffer, pipe mutex + * needs to be released, and when returning verify the pipe was not + * closed / re-opened in the meantime. + */ + session_id = pipe_info->session_id; + mutex_unlock(&pipe_info->mutex); + ret = mpq_dmx_decoder_fullness_wait(feed, 0); + mutex_lock(&pipe_info->mutex); + if (ret) { + MPQ_DVB_DBG_PRINT( + "%s: mpq_dmx_decoder_fullness_wait failed, ret=%d\n", + __func__, ret); + return ret; + } + if (pipe_info->session_id != session_id || !pipe_info->ref_count) { + MPQ_DVB_DBG_PRINT( + "%s: pipe was closed / re-opened: ref. count=%u, session_id=%u (expected %u)\n", + __func__, pipe_info->ref_count, + pipe_info->session_id, session_id); + return -ENODEV; + } + + return 0; +} + /** * mpq_dmx_tspp2_filter_event_cb() - filter event notification handler * @@ -1259,8 +1298,8 @@ static int mpq_dmx_tspp2_terminate_filter(struct dvb_demux_feed *feed, mpq_tspp2_feed->op_count = 0; /* Remove PES analysis operation */ - if ((feed->ts_type & (TS_PAYLOAD_ONLY | TS_DECODER)) || - feed->idx_params.enable) + if ((feed->ts_type & (TS_PAYLOAD_ONLY | TS_DECODER) && + !dvb_dmx_is_pcr_feed(feed)) || feed->idx_params.enable) mpq_dmx_tspp2_del_pes_analysis_op(mpq_tspp2_feed->filter); /* Remove indexing operation */ @@ -1667,6 +1706,8 @@ static int mpq_dmx_init_out_pipe(struct mpq_demux *mpq_demux, pipe_info->source_info = source_info; pipe_info->eos_pending = 0; pipe_info->session_id++; + pipe_info->hw_missed_notif = 0; + pipe_info->handler_count = 0; return 0; close_pipe: @@ -1732,8 +1773,7 @@ static int mpq_dmx_terminate_out_pipe(struct pipe_info *pipe_info) tspp2_pipe_close(pipe_info->handle); pipe_info->handle = TSPP2_INVALID_HANDLE; - pipe_work_queue_cancel_work(&source_info->demux_src.work_queue, - pipe_info); + pipe_work_queue_cancel_work(&pipe_info->work_queue, pipe_info); if (pipe_info->buffer.kernel_map) ion_unmap_kernel(mpq_demux->ion_client, @@ -2084,25 +2124,51 @@ static void mpq_dmx_tspp2_update_pipe_stats(struct pipe_info *pipe_info) pipe_info->hw_notif_count++; } -static void mpq_dmx_tspp2_queue_pipe_handler(struct pipe_info *pipe_info, +static int mpq_dmx_tspp2_queue_pipe_handler(struct pipe_info *pipe_info, enum mpq_dmx_tspp2_pipe_event event) { - struct source_info *src; + struct source_info *src = pipe_info->source_info; + struct pipe_work_queue *queue = &pipe_info->work_queue; struct pipe_work *pipe_work; + unsigned long flags; + + /* Try to merge data events into 1 pipe work object */ + spin_lock_irqsave(&queue->lock, flags); + if (!list_empty(&queue->work_list)) { + pipe_work = list_first_entry(&queue->work_list, + struct pipe_work, next); + if (event == PIPE_DATA_EVENT && + pipe_work->event == PIPE_DATA_EVENT && + pipe_work->event_count && + pipe_work->pipe_info == pipe_info && + pipe_work->session_id == pipe_info->session_id) { + pipe_work->event_count++; + spin_unlock_irqrestore(&queue->lock, flags); + wake_up_all(&src->demux_src.wait_queue); + return 0; + } + } + spin_unlock_irqrestore(&queue->lock, flags); - src = pipe_info->source_info; - pipe_work = pipe_work_queue_allocate(&src->demux_src.work_queue); + pipe_work = pipe_work_queue_allocate(&pipe_info->work_queue); if (pipe_work == NULL) { + int pipe_idx = (pipe_info - mpq_dmx_tspp2_info.pipes) / + sizeof(*pipe_info); MPQ_DVB_ERR_PRINT( - "%s: Cannot allocate pipe work for pipe %d\n", - __func__, pipe_info->type); - return; + "%s: Cannot allocate pipe work for pipe %d, type %d\n", + __func__, pipe_idx, pipe_info->type); + return -ENOSPC; } pipe_work->pipe_info = pipe_info; pipe_work->event = event; pipe_work->session_id = pipe_info->session_id; - pipe_work_queue_push(&src->demux_src.work_queue, pipe_work); + pipe_work->event_count = 1; + + pipe_work_queue_push(&pipe_info->work_queue, pipe_work); + wake_up_all(&src->demux_src.wait_queue); + + return 0; } /** @@ -2113,9 +2179,7 @@ static void mpq_dmx_tspp2_queue_pipe_handler(struct pipe_info *pipe_info, static void mpq_dmx_sps_producer_cb(struct sps_event_notify *notify) { struct pipe_info *pipe_info = notify->user; - - MPQ_DVB_DBG_PRINT("%s: Notification event id=%d, handle=%p, type=%d\n", - __func__, notify->event_id, pipe_info, pipe_info->type); + int ret; if (unlikely(pipe_info == NULL)) return; @@ -2123,12 +2187,17 @@ static void mpq_dmx_sps_producer_cb(struct sps_event_notify *notify) mpq_dmx_tspp2_update_pipe_stats(pipe_info); /* Schedule a new work to relevant source workqueue */ - if (notify->event_id == SPS_EVENT_OUT_OF_DESC) - mpq_dmx_tspp2_queue_pipe_handler(pipe_info, + if (notify->event_id == SPS_EVENT_OUT_OF_DESC) { + MPQ_DVB_ERR_PRINT("%s: SPS_EVENT_OUT_OF_DESC!\n", __func__); + ret = mpq_dmx_tspp2_queue_pipe_handler(pipe_info, PIPE_OVERFLOW_EVENT); - else - mpq_dmx_tspp2_queue_pipe_handler(pipe_info, + } else { + ret = mpq_dmx_tspp2_queue_pipe_handler(pipe_info, PIPE_DATA_EVENT); + } + + if (ret) + pipe_info->hw_missed_notif++; } /** @@ -2173,10 +2242,10 @@ static void mpq_dmx_timer_cb(unsigned long param) pipe_info = &mpq_dmx_tspp2_info.pipes[i]; spin_lock(&pipe_info->lock); - if ((!pipe_info->ref_count) || - ((pipe_info->type != CLEAR_SECTION_PIPE) && - (pipe_info->type != SCRAMBLED_SECTION_PIPE) && - (pipe_info->type != REC_PIPE))) { + if (!pipe_info->ref_count || + (pipe_info->type != CLEAR_SECTION_PIPE && + pipe_info->type != SCRAMBLED_SECTION_PIPE && + pipe_info->type != REC_PIPE)) { spin_unlock(&pipe_info->lock); continue; } @@ -2190,13 +2259,14 @@ static void mpq_dmx_timer_cb(unsigned long param) /* Schedule a new work to relevant source workqueue */ pipe_work = pipe_work_queue_allocate( - &source_info->demux_src.work_queue); + &pipe_info->work_queue); if (pipe_work != NULL) { pipe_work->session_id = pipe_info->session_id; pipe_work->pipe_info = pipe_info; pipe_work->event = PIPE_DATA_EVENT; - pipe_work_queue_push(&source_info->demux_src.work_queue, - pipe_work); + pipe_work->event_count = 1; + pipe_work_queue_push(&pipe_info->work_queue, pipe_work); + wake_up_all(&source_info->demux_src.wait_queue); } else { MPQ_DVB_ERR_PRINT( "%s: Cannot allocate pipe work\n", __func__); @@ -2569,7 +2639,6 @@ static int mpq_dmx_tspp2_section_pipe_handler(struct pipe_info *pipe_info, u16 curr_pid; ssize_t data_size; size_t packet_offset; - u32 tspp_write_offset; u32 tspp_last_addr = 0; struct dvb_demux *dvb_demux = &pipe_info->source_info->demux_src.mpq_demux->demux; @@ -2585,6 +2654,13 @@ static int mpq_dmx_tspp2_section_pipe_handler(struct pipe_info *pipe_info, tspp2_pipe_last_address_used_get(pipe_info->handle, &tspp_last_addr); data_size = mpq_dmx_tspp2_calc_pipe_data(pipe_info, tspp_last_addr); + if (data_size) { + pipe_info->tspp_write_offset += data_size; + if (pipe_info->tspp_write_offset >= pipe_info->buffer.size) + pipe_info->tspp_write_offset -= pipe_info->buffer.size; + } + data_size = mpq_dmx_calc_fullness(pipe_info->tspp_write_offset, + pipe_info->tspp_read_offset, pipe_info->buffer.size); if (data_size == 0) return 0; @@ -2598,8 +2674,8 @@ static int mpq_dmx_tspp2_section_pipe_handler(struct pipe_info *pipe_info, * will stall because there are no free descriptors. */ spin_lock(&dvb_demux->lock); - for (i = 0; i < num_packets && !should_stall; i++) { - packet_offset = pipe_info->tspp_write_offset + + for (i = 0; i < num_packets && !should_stall;) { + packet_offset = pipe_info->tspp_read_offset + i * TSPP2_DMX_SPS_SECTION_DESC_SIZE; if (packet_offset >= pipe_info->buffer.size) packet_offset -= pipe_info->buffer.size; @@ -2619,17 +2695,16 @@ static int mpq_dmx_tspp2_section_pipe_handler(struct pipe_info *pipe_info, break; } } + if (!should_stall) + i++; } spin_unlock(&dvb_demux->lock); pipe_info->tspp_last_addr = tspp_last_addr; - tspp_write_offset = pipe_info->tspp_write_offset; - pipe_info->tspp_write_offset += i * TSPP2_DMX_SPS_SECTION_DESC_SIZE; /* Release the data for the packets that were processed */ - data_size = mpq_dmx_calc_fullness(tspp_write_offset, - pipe_info->tspp_read_offset, pipe_info->buffer.size); - ret = mpq_dmx_release_data(pipe_info, data_size); + ret = mpq_dmx_release_data(pipe_info, + i * TSPP2_DMX_SPS_SECTION_DESC_SIZE); if (event == PIPE_EOS_EVENT && should_stall) pipe_info->eos_pending = 1; @@ -2640,6 +2715,94 @@ static int mpq_dmx_tspp2_section_pipe_handler(struct pipe_info *pipe_info, return ret; } +static int mpq_dmx_tspp2_process_full_pes_desc(struct pipe_info *pipe_info, + struct dvb_demux_feed *feed, struct sps_iovec *iovec) +{ + u8 pes_status; + u8 *data_buffer = NULL; + struct dmx_data_ready data; + int ret; + + data_buffer = mpq_dmx_get_kernel_addr(pipe_info, iovec->addr); + if (unlikely(!data_buffer)) { + /* Should NEVER happen! */ + MPQ_DVB_ERR_PRINT( + "%s: mpq_dmx_get_kernel_addr failed\n", + __func__); + return -EFAULT; + } + + /* + * Extract STC value for this PES: + * Descriptor data starts with 8 bytes of STC: + * 7 bytes STC@27MHz and 1 zero byte for padding. + */ + if (feed->peslen < PES_STC_FIELD_LENGTH) { + if (iovec->size < PES_STC_FIELD_LENGTH) { + /* Descriptor too small to hold STC */ + MPQ_DVB_ERR_PRINT( + "%s: descriptor size %d is too small (peslen=%d)\n", + __func__, iovec->size, feed->peslen); + return -EINVAL; + } + + feed->prev_stc = mpq_dmx_tspp2_get_stc(&data_buffer[0], 7); + } + + /* + * Report the whole descriptor allocated size to dmxdev so that + * DMX_OK_PES_END event total size will be correct. + */ + data.status = DMX_OK; + data.data_length = TSPP2_DMX_SPS_NON_VID_PES_DESC_SIZE; + feed->data_ready_cb.ts(&feed->feed.ts, &data); + + if (iovec->flags & SPS_IOVEC_FLAG_EOT) { + /* PES assembly status is the last byte in the descriptor */ + pes_status = + data_buffer[iovec->size - PES_ASM_STATUS_FIELD_LENGTH]; + + /* + * EOT descriptor might not have been used completely. + * The next PES will begin on the next descriptor and + * not immediately following this PES. Need to account + * for this gap when PES is reported. + */ + feed->peslen += iovec->size; + + data.status = DMX_OK_PES_END; + data.data_length = 0; + data.pes_end.start_gap = PES_STC_FIELD_LENGTH; + data.pes_end.actual_length = + feed->peslen - PES_STC_FIELD_LENGTH - + PES_ASM_STATUS_FIELD_LENGTH; + data.pes_end.stc = feed->prev_stc; + data.pes_end.disc_indicator_set = + pes_status & PES_ASM_STATUS_DCI; + data.pes_end.pes_length_mismatch = + pes_status & PES_ASM_STATUS_SIZE_MISMATCH; + /* TSPPv2 does not report the following */ + data.pes_end.tei_counter = 0; + data.pes_end.cont_err_counter = 0; + data.pes_end.ts_packets_num = 0; + + ret = mpq_dmx_tspp2_ts_event_check(feed, pipe_info); + if (ret) + return ret; + feed->data_ready_cb.ts(&feed->feed.ts, &data); + + /* Reset accumulated PES length for next iteration */ + feed->peslen = 0; + + return 0; + } + + /* DESC_DONE case */ + feed->peslen += iovec->size; + + return 0; +} + /** * mpq_dmx_tspp2_pes_pipe_handler() - Handler for non-video full PES * pipe notifications. @@ -2653,17 +2816,13 @@ static int mpq_dmx_tspp2_pes_pipe_handler(struct pipe_info *pipe_info, enum mpq_dmx_tspp2_pipe_event event) { int ret; - u64 stc = 0; - u32 pes_length; u32 tspp_write_offset = 0; u32 tspp_last_addr; size_t pes_leftover = 0; struct sps_iovec iovec; u8 *data_buffer = NULL; - u8 pes_status; struct dmx_data_ready data; struct dvb_demux_feed *feed; - int found_pes; feed = pipe_info->parent->mpq_feed->dvb_demux_feed; if (unlikely(!feed)) { @@ -2686,13 +2845,11 @@ static int mpq_dmx_tspp2_pes_pipe_handler(struct pipe_info *pipe_info, } /* - * Read all filled up descriptors (DESC_DONE) until EOT. + * Read pending descriptors (DESC_DONE and EOT). * In case PES is very short and fits in 1 descriptor, only EOT will * be received. */ - found_pes = 0; - pes_length = 0; - while (!found_pes) { + while (1) { ret = tspp2_pipe_descriptor_get(pipe_info->handle, &iovec); if (ret) { /* should NEVER happen! */ @@ -2702,132 +2859,35 @@ static int mpq_dmx_tspp2_pes_pipe_handler(struct pipe_info *pipe_info, return -EINVAL; } + /* No more descriptors */ if (!iovec.size) break; - if (iovec.flags & SPS_IOVEC_FLAG_EOT) { - if (!data_buffer) { - /* - * PES start address in payload buffer. This is - * also where STC is located. - */ - data_buffer = mpq_dmx_get_kernel_addr( - pipe_info, iovec.addr); - } - - if (unlikely(!data_buffer)) { - /* Should NEVER happen! */ - MPQ_DVB_ERR_PRINT( - "%s: mpq_dmx_get_kernel_addr failed\n", - __func__); - continue; - } - - /* - * At this point data_buffer always points to beginning - * of payload data, which starts with 8 bytes of STC: - * 7 bytes STC@27MHz and 1 zero byte for padding. - */ - stc = mpq_dmx_tspp2_get_stc(&data_buffer[0], 7); - - /* - * Now data_buffer will point to the beginning of the - * last descriptor. The last byte there is the PES - * assembly status flags. - */ - data_buffer = mpq_dmx_get_kernel_addr(pipe_info, - iovec.addr); - if (unlikely(!data_buffer)) { - /* should NEVER happen! */ - MPQ_DVB_ERR_PRINT( - "%s: mpq_dmx_get_kernel_addr failed\n", - __func__); - continue; - } - - pes_status = data_buffer[iovec.size - - PES_ASM_STATUS_FIELD_LENGTH]; - - /* - * EOT descriptor might not have been used completely. - * The next PES will begin on the next descriptor and - * not immediately following this PES. Need to account - * for this gap when PES is reported. - */ - pes_leftover = TSPP2_DMX_SPS_NON_VID_PES_DESC_SIZE - - iovec.size; - pes_length += iovec.size; - pipe_info->tspp_write_offset += - TSPP2_DMX_SPS_NON_VID_PES_DESC_SIZE; - if (pipe_info->tspp_write_offset >= - pipe_info->buffer.size) - pipe_info->tspp_write_offset -= - pipe_info->buffer.size; - - /* - * Must first report the last chunk of data for - * DMX_OK_PES_END event total size to be correct. - */ - data.status = DMX_OK; - data.data_length = TSPP2_DMX_SPS_NON_VID_PES_DESC_SIZE; - feed->data_ready_cb.ts(&feed->feed.ts, &data); - - data.status = DMX_OK_PES_END; - data.data_length = 0; - data.pes_end.start_gap = PES_STC_FIELD_LENGTH; - data.pes_end.actual_length = - pes_length - PES_STC_FIELD_LENGTH - - PES_ASM_STATUS_FIELD_LENGTH; - data.pes_end.stc = stc; - data.pes_end.disc_indicator_set = - pes_status & PES_ASM_STATUS_DCI; - data.pes_end.pes_length_mismatch = - pes_status & PES_ASM_STATUS_SIZE_MISMATCH; - /* TSPPv2 does not report the following */ - data.pes_end.tei_counter = 0; - data.pes_end.cont_err_counter = 0; - data.pes_end.ts_packets_num = 0; - - ret = mpq_dmx_tspp2_ts_event_check(feed, pipe_info); - if (ret) - return ret; - feed->data_ready_cb.ts(&feed->feed.ts, &data); - found_pes = 1; - } else { - if (!data_buffer) { - /* - * PES start address in payload buffer. This is - * also where STC is located. - */ - data_buffer = mpq_dmx_get_kernel_addr( - pipe_info, iovec.addr); - } + pipe_info->tspp_write_offset += + TSPP2_DMX_SPS_NON_VID_PES_DESC_SIZE; + if (pipe_info->tspp_write_offset >= pipe_info->buffer.size) + pipe_info->tspp_write_offset -= pipe_info->buffer.size; - pes_length += iovec.size; - pipe_info->tspp_write_offset += iovec.size; - if (pipe_info->tspp_write_offset >= - pipe_info->buffer.size) - pipe_info->tspp_write_offset -= - pipe_info->buffer.size; - - data.status = DMX_OK; - data.data_length = iovec.size; - feed->data_ready_cb.ts(&feed->feed.ts, &data); + ret = mpq_dmx_tspp2_process_full_pes_desc(pipe_info, feed, + &iovec); + if (ret) { + MPQ_DVB_DBG_PRINT( + "%s: mpq_dmx_tspp2_process_full_pes_desc failed, ret=%d\n", + __func__, ret); + return ret; } } if (event == PIPE_EOS_EVENT) { /* * There are 3 cases for End-of-Stream: - * 1. No new data so no need to notify on PES end - * (pes_leftover is 0) - * 2. New data is in a single partial descriptor so no - * descriptor done events were received and stc is in this - * partial descriptor - * (pes_length = 0, pes_leftover < 256) - * 3. New data spreads across several descriptors and a - * partial descriptor. - * (pes_length > 0, pes_leftover < 256) + * 1. No new data so just need to notify on end of partial PES + * consisting of previous DESC_DONE descriptors. + * (feed->peslen > 0, pes_leftover = 0) + * 2. New PES begins in this partial descriptor with stc + * (feed->peslen = 0, pes_leftover < 256) + * 3. New PES began is a previous descriptors, stc is valid + * (feed->peslen > 0, pes_leftover < 256) */ tspp2_pipe_last_address_used_get(pipe_info->handle, &tspp_last_addr); @@ -2840,41 +2900,60 @@ static int mpq_dmx_tspp2_pes_pipe_handler(struct pipe_info *pipe_info, pipe_info->buffer.size); } + + if (feed->peslen < PES_STC_FIELD_LENGTH && + pes_leftover < PES_STC_FIELD_LENGTH) { + /* Insufficient data to report, just report EOS event */ + MPQ_DVB_DBG_PRINT( + "%s: PES leftover too small = %d bytes\n", + __func__, pes_leftover); + + data.status = DMX_OK_EOS; + data.data_length = 0; + feed->data_ready_cb.ts(&feed->feed.ts, &data); + + return 0; + } + if (pes_leftover) { MPQ_DVB_DBG_PRINT("%s: PES leftover %d bytes\n", __func__, pes_leftover); - - if (!data_buffer) - data_buffer = pipe_info->buffer.mem + - tspp_write_offset; + data_buffer = pipe_info->buffer.mem + tspp_write_offset; /* Notify there is more data */ data.status = DMX_OK; data.data_length = pes_leftover; feed->data_ready_cb.ts(&feed->feed.ts, &data); - - /* Notify PES has ended */ - data.status = DMX_OK_PES_END; - data.data_length = 0; - data.pes_end.start_gap = PES_STC_FIELD_LENGTH; - /* In EOS case there is no PES assembly status byte */ - data.pes_end.actual_length = pes_length + pes_leftover - - PES_STC_FIELD_LENGTH; - data.pes_end.stc = - mpq_dmx_tspp2_get_stc(data_buffer, 7); - data.pes_end.disc_indicator_set = 0; - data.pes_end.pes_length_mismatch = 0; - data.pes_end.tei_counter = 0; - data.pes_end.cont_err_counter = 0; - data.pes_end.ts_packets_num = 0; - pipe_info->tspp_write_offset = tspp_write_offset; - - ret = mpq_dmx_tspp2_ts_event_check(feed, pipe_info); - if (ret) - return ret; - feed->data_ready_cb.ts(&feed->feed.ts, &data); } + if (feed->peslen < PES_STC_FIELD_LENGTH) + feed->prev_stc = + mpq_dmx_tspp2_get_stc(&data_buffer[0], 7); + + feed->peslen += pes_leftover; + + /* Notify PES has ended */ + data.status = DMX_OK_PES_END; + data.data_length = 0; + data.pes_end.start_gap = PES_STC_FIELD_LENGTH; + /* In EOS case there is no PES assembly status byte */ + data.pes_end.actual_length = + feed->peslen - PES_STC_FIELD_LENGTH; + data.pes_end.stc = feed->prev_stc; + data.pes_end.disc_indicator_set = 0; + data.pes_end.pes_length_mismatch = 0; + data.pes_end.tei_counter = 0; + data.pes_end.cont_err_counter = 0; + data.pes_end.ts_packets_num = 0; + pipe_info->tspp_write_offset = tspp_write_offset; + + ret = mpq_dmx_tspp2_ts_event_check(feed, pipe_info); + if (ret) + return ret; + feed->data_ready_cb.ts(&feed->feed.ts, &data); + + feed->peslen = 0; + data.status = DMX_OK_EOS; data.data_length = 0; feed->data_ready_cb.ts(&feed->feed.ts, &data); @@ -2955,6 +3034,16 @@ static int mpq_dmx_tspp2_process_video_headers(struct mpq_feed *mpq_feed, mpq_dmx_parse_video_header_suffix(buffer, partial_header, &stc, &pes_payload_sa, &pes_payload_ea, &status_flags); + if (status_flags) + MPQ_DVB_DBG_PRINT( + "%s: status_flags=0x%x, sa=%u, ea=%u\n", __func__, + status_flags, pes_payload_sa, pes_payload_ea); + + if (pes_payload_sa == ULONG_MAX || pes_payload_sa == ULONG_MAX) { + MPQ_DVB_DBG_PRINT("%s: Data was not written to payload pipe\n", + __func__); + return 0; + } ts_header = (struct ts_packet_header *)buffer; @@ -3012,8 +3101,7 @@ static int mpq_dmx_tspp2_process_video_headers(struct mpq_feed *mpq_feed, * Verify we have received at least the mandatory * fields within the PES header. */ - if (unlikely(feed_data->pes_header_offset < - PES_MANDATORY_FIELDS_LEN)) { + if (unlikely(feed_data->pes_header_offset < PES_MANDATORY_FIELDS_LEN)) { MPQ_DVB_ERR_PRINT( "%s: Invalid header size %d\n", __func__, feed_data->pes_header_offset); @@ -3043,7 +3131,7 @@ static int mpq_dmx_tspp2_process_video_headers(struct mpq_feed *mpq_feed, return ret; } - packet.raw_data_offset = payload_pipe->tspp_write_offset; + packet.raw_data_offset = pes_payload_sa - payload_pipe->buffer.iova; if (partial_header) { tspp2_pipe_last_address_used_get(payload_pipe->handle, @@ -3056,28 +3144,34 @@ static int mpq_dmx_tspp2_process_video_headers(struct mpq_feed *mpq_feed, packet.raw_data_len++; packet.user_data_len = sizeof(meta_data); - payload_pipe->tspp_write_offset += packet.raw_data_len; - if (payload_pipe->tspp_write_offset >= payload_pipe->buffer.size) - payload_pipe->tspp_write_offset -= payload_pipe->buffer.size; + payload_pipe->tspp_write_offset = + mpq_dmx_tspp2_addr_to_offset(payload_pipe, pes_payload_ea); ret = mpq_streambuffer_data_write_deposit(stream_buffer, packet.raw_data_len); if (ret) { - MPQ_DVB_ERR_PRINT( + MPQ_DVB_DBG_PRINT( "%s: mpq_streambuffer_data_write_deposit failed, ret=%d\n", __func__, ret); return ret; } + ret = mpq_dmx_tspp2_stream_buffer_event_check(feed, header_pipe); + if (ret) { + MPQ_DVB_DBG_PRINT( + "%s: mpq_dmx_tspp2_stream_buffer_event_check failed, ret=%d\n", + __func__, ret); + return ret; + } + ret = mpq_streambuffer_pkt_write(stream_buffer, &packet, (u8 *)&meta_data); - if (ret) { + if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: mpq_streambuffer_pkt_write failed, ret=%d\n", __func__, ret); } else { struct dmx_data_ready data; - size_t len = 0; mpq_dmx_update_decoder_stat(mpq_feed); @@ -3088,35 +3182,24 @@ static int mpq_dmx_tspp2_process_video_headers(struct mpq_feed *mpq_feed, * The following has to succeed when called here, * after packet was written */ - data.buf.cookie = mpq_streambuffer_pkt_next(stream_buffer, - feed_data->last_pkt_index, &len); - if (data.buf.cookie < 0) { - MPQ_DVB_ERR_PRINT( - "%s: received invalid packet index %d\n", - __func__, data.buf.cookie); - } else { - data.buf.offset = packet.raw_data_offset; - data.buf.len = packet.raw_data_len; - data.buf.pts_exists = pts_dts_info->pts_exist; - data.buf.pts = pts_dts_info->pts; - data.buf.dts_exists = pts_dts_info->dts_exist; - data.buf.dts = pts_dts_info->dts; - data.buf.tei_counter = 0; - data.buf.cont_err_counter = 0; - data.buf.ts_packets_num = 0; - data.buf.ts_dropped_bytes = 0; - data.status = DMX_OK_DECODER_BUF; - - /* save for next time: */ - feed_data->last_pkt_index = data.buf.cookie; - - MPQ_DVB_DBG_PRINT("%s: cookie=%d\n", - __func__, data.buf.cookie); - - ret = mpq_dmx_tspp2_ts_event_check(feed, header_pipe); - if (!ret) - feed->data_ready_cb.ts(&feed->feed.ts, &data); - } + data.buf.cookie = ret; + data.buf.offset = packet.raw_data_offset; + data.buf.len = packet.raw_data_len; + data.buf.pts_exists = pts_dts_info->pts_exist; + data.buf.pts = pts_dts_info->pts; + data.buf.dts_exists = pts_dts_info->dts_exist; + data.buf.dts = pts_dts_info->dts; + data.buf.tei_counter = 0; + data.buf.cont_err_counter = 0; + data.buf.ts_packets_num = 0; + data.buf.ts_dropped_bytes = 0; + data.status = DMX_OK_DECODER_BUF; + + MPQ_DVB_DBG_PRINT("%s: cookie=%d\n", __func__, data.buf.cookie); + + ret = mpq_dmx_tspp2_ts_event_check(feed, header_pipe); + if (!ret) + feed->data_ready_cb.ts(&feed->feed.ts, &data); } return ret ? ret : 1; @@ -3180,11 +3263,18 @@ static int mpq_dmx_tspp2_video_pipe_handler(struct pipe_info *pipe_info, feed = mpq_feed->dvb_demux_feed; feed_data = &mpq_feed->video_info; - found_pes = 0; - feed->peslen = 0; - feed_data->pes_header_offset = 0; - feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN; - while (!found_pes) { + /* + * Read all pending header descriptors. Typically only one descriptor + * will be read for each call of the pipe handler, but producer + * notifications might be missed so need to pick up the lost + * descriptors. + */ + while (1) { + found_pes = 0; + feed->peslen = 0; + feed_data->pes_header_offset = 0; + feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN; + ret = tspp2_pipe_descriptor_get(pipe_info->handle, &iovec); if (ret) { /* should NEVER happen! */ @@ -3194,36 +3284,36 @@ static int mpq_dmx_tspp2_video_pipe_handler(struct pipe_info *pipe_info, return -EINVAL; } + /* No more descriptors */ if (iovec.size == 0) break; - if (unlikely(iovec.size != - TSPP2_DMX_SPS_VPES_HEADER_DESC_SIZE)) { - /* should NEVER happen! */ - MPQ_DVB_ERR_PRINT( - "%s: invalid VPES header desc size %d\n", - __func__, iovec.size); - return -EINVAL; - } - data_buffer = mpq_dmx_get_kernel_addr(pipe_info, iovec.addr); - if (unlikely(!data_buffer)) { + if (unlikely(!data_buffer || iovec.size != + TSPP2_DMX_SPS_VPES_HEADER_DESC_SIZE)) { /* should NEVER happen! */ - MPQ_DVB_ERR_PRINT( - "%s: mpq_dmx_get_kernel_addr failed\n", - __func__); - return -EFAULT; + if (!data_buffer) { + MPQ_DVB_ERR_PRINT( + "%s: mpq_dmx_get_kernel_addr failed\n", + __func__); + ret = -EFAULT; + } else { + MPQ_DVB_ERR_PRINT( + "%s: invalid VPES header desc size %d, expected %d\n", + __func__, iovec.size, + TSPP2_DMX_SPS_VPES_HEADER_DESC_SIZE); + ret = -EINVAL; + } + return ret; } ret = mpq_dmx_tspp2_process_video_headers(mpq_feed, data_buffer, 0, pipe_info, main_pipe); - if (ret < 0) { - MPQ_DVB_ERR_PRINT( + found_pes = (ret == 1); + if (ret < 0) + MPQ_DVB_DBG_PRINT( "%s: mpq_dmx_tspp2_process_video_pes failed, ret=%d\n", __func__, ret); - return ret; - } - found_pes = (ret == 1); /* re-queue buffer holding TS packet of PES header */ ret = tspp2_pipe_descriptor_put(pipe_info->handle, @@ -3234,15 +3324,11 @@ static int mpq_dmx_tspp2_video_pipe_handler(struct pipe_info *pipe_info, __func__, ret); pipe_info->tspp_write_offset += iovec.size; - if (pipe_info->tspp_write_offset >= - pipe_info->buffer.size) - pipe_info->tspp_write_offset -= - pipe_info->buffer.size; + if (pipe_info->tspp_write_offset >= pipe_info->buffer.size) + pipe_info->tspp_write_offset -= pipe_info->buffer.size; - pipe_info->tspp_read_offset = - pipe_info->tspp_write_offset; - pipe_info->bam_read_offset = - pipe_info->tspp_write_offset; + pipe_info->tspp_read_offset = pipe_info->tspp_write_offset; + pipe_info->bam_read_offset = pipe_info->tspp_write_offset; } if (event == PIPE_EOS_EVENT) { @@ -4558,8 +4644,7 @@ static int mpq_dmx_tspp2_notify_data_read(struct dmx_ts_feed *ts_feed, } static void mpq_dmx_tspp2_streambuffer_cb(struct mpq_streambuffer *sbuff, - struct mpq_streambuffer_packet_header *packet, - void *user_data) + u32 offset, size_t len, void *user_data) { int ret; struct pipe_info *pipe_info = user_data; @@ -4573,7 +4658,7 @@ static void mpq_dmx_tspp2_streambuffer_cb(struct mpq_streambuffer *sbuff, return; } - ret = mpq_dmx_release_data(pipe_info, packet->raw_data_len); + ret = mpq_dmx_release_data(pipe_info, len); if (ret) MPQ_DVB_ERR_PRINT("%s: mpq_dmx_release_data failed, ret=%d\n", __func__, ret); @@ -4596,8 +4681,7 @@ static int mpq_dmx_tspp2_eos_cmd(struct mpq_tspp2_feed *tspp2_feed) source_info = pipe_info->source_info; - pipe_work = pipe_work_queue_allocate( - &source_info->demux_src.work_queue); + pipe_work = pipe_work_queue_allocate(&pipe_info->work_queue); if (pipe_work == NULL) { MPQ_DVB_ERR_PRINT("%s: Cannot allocate pipe work\n", __func__); mutex_unlock(&mpq_dmx_tspp2_info.mutex); @@ -4606,9 +4690,11 @@ static int mpq_dmx_tspp2_eos_cmd(struct mpq_tspp2_feed *tspp2_feed) pipe_work->pipe_info = pipe_info; pipe_work->event = PIPE_EOS_EVENT; + pipe_work->event_count = 1; pipe_work->session_id = pipe_info->session_id; - pipe_work_queue_push(&source_info->demux_src.work_queue, pipe_work); + pipe_work_queue_push(&pipe_info->work_queue, pipe_work); + wake_up_all(&source_info->demux_src.wait_queue); mutex_unlock(&mpq_dmx_tspp2_info.mutex); @@ -5367,7 +5453,7 @@ static int mpq_dmx_tspp2_start_filtering(struct dvb_demux_feed *feed) } if (dvb_dmx_is_video_feed(feed)) { - ret = mpq_streambuffer_register_pkt_dispose( + ret = mpq_streambuffer_register_data_dispose( mpq_feed->video_info.video_buffer, mpq_dmx_tspp2_streambuffer_cb, tspp2_feed->main_pipe); @@ -5669,6 +5755,8 @@ static int mpq_dmx_tspp2_write(struct dmx_demux *demux, /* Process only whole TS packets */ data_length = (count / dvbdemux->ts_packet_size) * dvbdemux->ts_packet_size; + if (!data_length) + return 0; if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE)) return -EINVAL; @@ -5793,6 +5881,9 @@ static int mpq_dmx_tspp2_write(struct dmx_demux *demux, pipe_info->bam_read_offset = 0; pipe_info->eos_pending = 0; pipe_info->session_id++; + pipe_info->handler_count = 0; + pipe_info->hw_notif_count = 0; + pipe_info->hw_missed_notif = 0; if (!source_info->enabled) { MPQ_DVB_DBG_PRINT( @@ -5853,6 +5944,21 @@ static int mpq_dmx_tspp2_write_cancel(struct dmx_demux *demux) return 0; } +static bool mpq_dmx_tspp2_pipe_do_work(struct source_info *source_info) +{ + struct pipe_info *pipe_info; + int i; + + for (i = 0; i < TSPP2_NUM_PIPES; i++) { + pipe_info = &mpq_dmx_tspp2_info.pipes[i]; + if (pipe_info->source_info == source_info && + (!pipe_work_queue_empty(&pipe_info->work_queue))) + return true; + } + + return false; +} + static int mpq_dmx_tspp2_thread(void *arg) { struct source_info *source_info = arg; @@ -5860,12 +5966,13 @@ static int mpq_dmx_tspp2_thread(void *arg) struct pipe_info *pipe_info; int ret; unsigned long flags; + int i; + int j; while (1) { ret = wait_event_interruptible( - source_info->demux_src.work_queue.wait_queue, - !pipe_work_queue_empty( - &source_info->demux_src.work_queue) || + source_info->demux_src.wait_queue, + mpq_dmx_tspp2_pipe_do_work(source_info) || kthread_should_stop()); if (ret) { MPQ_DVB_ERR_PRINT( @@ -5878,40 +5985,56 @@ static int mpq_dmx_tspp2_thread(void *arg) break; } - pipe_work = pipe_work_queue_pop( - &source_info->demux_src.work_queue); + for (i = 0; i < TSPP2_NUM_PIPES; i++) { + pipe_info = &mpq_dmx_tspp2_info.pipes[i]; - /* - * The pipe work might have been flushed - * if filter was stopped - */ - if (pipe_work == NULL) { - MPQ_DVB_DBG_PRINT("%s: pipe was flushed\n", __func__); - continue; - } + /* + * Lock pipe mutex to protect against pipe being closed + * during its processing + */ + if (mutex_lock_interruptible(&pipe_info->mutex)) + continue; - pipe_info = pipe_work->pipe_info; + if (pipe_info->source_info != source_info || + pipe_work_queue_empty(&pipe_info->work_queue)) { + mutex_unlock(&pipe_info->mutex); + continue; + } - /* - * Lock pipe mutex to protect against pipe being closed - * during its processing - */ - if (mutex_lock_interruptible(&pipe_info->mutex)) - continue; + pipe_work = pipe_work_queue_pop(&pipe_info->work_queue); - spin_lock_irqsave(&pipe_info->lock, flags); - if (pipe_info->ref_count && pipe_info->pipe_handler && - pipe_work->session_id == pipe_info->session_id) { - spin_unlock_irqrestore(&pipe_info->lock, flags); - pipe_info->pipe_handler(pipe_info, pipe_work->event); - } else { - spin_unlock_irqrestore(&pipe_info->lock, flags); - } + /* + * The pipe work might have been flushed + * if filter was stopped + */ + if (pipe_work == NULL) { + MPQ_DVB_DBG_PRINT( + "%s: pipe was flushed\n", __func__); + mutex_unlock(&pipe_info->mutex); + continue; + } - mutex_unlock(&pipe_work->pipe_info->mutex); + spin_lock_irqsave(&pipe_info->lock, flags); + if (pipe_info->ref_count && pipe_info->pipe_handler && + pipe_work->session_id == + pipe_info->session_id) { + spin_unlock_irqrestore(&pipe_info->lock, flags); + MPQ_DVB_DBG_PRINT( + "%s: calling pipe %d handler %d times\n", + __func__, i, pipe_work->event_count); + for (j = 0; j < pipe_work->event_count; j++) + pipe_info->pipe_handler(pipe_info, + pipe_work->event); + pipe_info->handler_count += j; + } else { + spin_unlock_irqrestore(&pipe_info->lock, flags); + } - pipe_work_queue_release(&source_info->demux_src.work_queue, - pipe_work); + mutex_unlock(&pipe_info->mutex); + + pipe_work_queue_release(&pipe_info->work_queue, + pipe_work); + } } /* Terminate thread gracefully */ @@ -6143,6 +6266,10 @@ static int mpq_dmx_tspp2_pipes_print(struct seq_file *s, void *p) pipe_info->hw_notif_rate_hz); seq_printf(s, "interrupt count: %d\n", pipe_info->hw_notif_count); + seq_printf(s, "int. miss count: %d\n", + pipe_info->hw_missed_notif); + seq_printf(s, "handler count : %d\n", + pipe_info->handler_count); seq_printf(s, "buffer address : 0x%p(0x%p)\n", pipe_info->buffer.mem, (void *)pipe_info->buffer.iova); @@ -6346,6 +6473,7 @@ static const struct file_operations dbgfs_index_tables_fops = { .release = single_release, .owner = THIS_MODULE, }; + static const struct file_operations dbgfs_sources_fops = { .open = mpq_dmx_tspp2_sources_open, .read = seq_read, @@ -6354,6 +6482,7 @@ static const struct file_operations dbgfs_sources_fops = { .owner = THIS_MODULE, }; + /** * Initialize a single demux device. * @@ -6533,7 +6662,7 @@ static int __init mpq_dmx_tspp2_plugin_init(void) init_completion(&source_info->completion); - pipe_work_queue_init(&source_info->demux_src.work_queue); + init_waitqueue_head(&source_info->demux_src.wait_queue); /* Initialize source processing thread */ source_info->demux_src.thread = @@ -6564,6 +6693,7 @@ static int __init mpq_dmx_tspp2_plugin_init(void) for (i = 0; i < TSPP2_NUM_PIPES; i++) { mpq_dmx_tspp2_info.pipes[i].handle = TSPP2_INVALID_HANDLE; mutex_init(&mpq_dmx_tspp2_info.pipes[i].mutex); + pipe_work_queue_init(&mpq_dmx_tspp2_info.pipes[i].work_queue); spin_lock_init(&mpq_dmx_tspp2_info.pipes[i].lock); } mpq_dmx_tspp2_info.user_count = 0; @@ -6619,7 +6749,6 @@ static int __init mpq_dmx_tspp2_plugin_init(void) mpq_dmx_tspp2_info.debugfs_dmx_dir, NULL, &dbgfs_sources_fops); - } return ret; diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.h b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.h index 2353dc1433c4..075a730f46d1 100644 --- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.h +++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.h @@ -34,7 +34,7 @@ #define TSPP2_DMX_MAX_FEED_OPS 4 -#define TSPP2_DMX_PIPE_WORK_POOL_SIZE 100 +#define TSPP2_DMX_PIPE_WORK_POOL_SIZE 500 /* Max number of section filters */ #define TSPP2_DMX_MAX_SECTION_FILTER_NUM 64 @@ -218,15 +218,18 @@ enum mpq_dmx_tspp2_pipe_event { /** * struct pipe_work - Work scheduled each time we receive data from a pipe * - * @pipe_info: Associated pipe - * @event: Source event type for this work item - * @session_id: pipe_info.session_id cached value at time of pipe work creation - * @next: List node field for pipe_work_queue lists + * @pipe_info: Associated pipe + * @event: Source event type for this work item + * @session_id: pipe_info.session_id cached value at time of pipe + * work creation. + * @event_count: Number of events included in this work + * @next: List node field for pipe_work_queue lists */ struct pipe_work { struct pipe_info *pipe_info; enum mpq_dmx_tspp2_pipe_event event; u32 session_id; + u32 event_count; struct list_head next; }; @@ -237,14 +240,12 @@ struct pipe_work { * @work_list: Queue of pipe_work element source thread should process * @free_list: List of free pipe_work objects * @lock: Lock to protect modifications to lists - * @wait_queue: Processing thread wait queue */ struct pipe_work_queue { struct pipe_work work_pool[TSPP2_DMX_PIPE_WORK_POOL_SIZE]; struct list_head work_list; struct list_head free_list; spinlock_t lock; - wait_queue_head_t wait_queue; }; /** @@ -315,6 +316,7 @@ struct mpq_dmx_tspp2_pipe_buffer { * @mutex: Mutex for protecting access to pipe info * @eos_pending: Flag specifying whether the pipe handler has an * end of stream notification that should be handled. + * @work_queue: pipe_work queue of work pending for this pipe * @hw_notif_count: Total number of HW notifications * @hw_notif_rate_hz: Rate of HW notifications in unit of Hz * @hw_notif_last_time: Time at which previous HW notification was received @@ -337,10 +339,13 @@ struct pipe_info { enum mpq_dmx_tspp2_pipe_event event); struct mutex mutex; int eos_pending; + struct pipe_work_queue work_queue; /* debug-fs */ u32 hw_notif_count; u32 hw_notif_rate_hz; + u32 hw_missed_notif; + u32 handler_count; struct timespec hw_notif_last_time; }; @@ -490,7 +495,7 @@ struct buffer_insertion_source { * struct demuxing_source - demuxing source related resources * * @thread: Source processing thread - * @work_queue: Pipe work queue object + * @wait_queue: Processing thread wait queue * @mpq_demux: Pointer to the demux connected to this source * @clear_section_pipe: Pipe opened to hold clear TS packets of sections * @scrambled_section_pipe: Pipe opened to hold scrambled TS packets of @@ -498,7 +503,7 @@ struct buffer_insertion_source { */ struct demuxing_source { struct task_struct *thread; - struct pipe_work_queue work_queue; + wait_queue_head_t wait_queue; struct mpq_demux *mpq_demux; struct pipe_info *clear_section_pipe; struct pipe_info *scrambled_section_pipe; diff --git a/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h b/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h index d70555ad101d..a03c94ecc0cc 100644 --- a/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h +++ b/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h @@ -103,19 +103,19 @@ * - Disposal of packets: * mpq_streambuffer_pkt_dispose(...) * - * For linear buffer mode, disposing of a packet with data size > 0, causes - * the current buffer to be marked as free for writing, and triggers moving to + * For linear buffer mode, disposing of a packet with data size > 0, + * regardless of the 'dispose_data' parameter, causes the current buffer's + * data to be disposed and marked as free for writing, and triggers moving to * the next available buffer, that shall now be the current read buffer. - - * */ struct mpq_streambuffer; struct mpq_streambuffer_packet_header; -typedef void (*mpq_streambuffer_pkt_dispose_cb) ( +typedef void (*mpq_streambuffer_dispose_cb) ( struct mpq_streambuffer *sbuff, - struct mpq_streambuffer_packet_header *packet, + u32 offset, + size_t len, void *user_data); enum mpq_streambuffer_mode { @@ -143,7 +143,7 @@ struct mpq_streambuffer { enum mpq_streambuffer_mode mode; u32 buffers_num; u32 pending_buffers_count; - mpq_streambuffer_pkt_dispose_cb cb; + mpq_streambuffer_dispose_cb cb; void *cb_user_data; }; @@ -357,8 +357,8 @@ ssize_t mpq_streambuffer_data_read_user( * mpq_streambuffer_data_read_dispose - Advances the raw-buffer read pointer. * Assumes the raw-data was read by the user directly. * - * @sbuff: The stream buffer - * @len: The length of the raw-data to be disposed + * @sbuff: The stream buffer + * @len: The length of the raw-data to be disposed * * Return error status, -EINVAL if buffer there's no enough data to * be disposed @@ -422,9 +422,9 @@ ssize_t mpq_streambuffer_data_avail( * Returns error status * -EINVAL if arguments are invalid */ -int mpq_streambuffer_register_pkt_dispose( +int mpq_streambuffer_register_data_dispose( struct mpq_streambuffer *sbuff, - mpq_streambuffer_pkt_dispose_cb cb_func, + mpq_streambuffer_dispose_cb cb_func, void *user_data); /** diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c index d469d8f92c0b..a8df20c039fb 100644 --- a/drivers/media/platform/msm/vidc/hfi_packetization.c +++ b/drivers/media/platform/msm/vidc/hfi_packetization.c @@ -267,7 +267,7 @@ int create_pkt_set_cmd_sys_resource( break; } default: - dprintk(VIDC_ERR, "Invalid resource_id %d", + dprintk(VIDC_ERR, "Invalid resource_id %d\n", resource_hdr->resource_id); rc = -EINVAL; } @@ -912,7 +912,7 @@ int create_pkt_cmd_session_set_property( HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT; hfi = (struct hfi_nal_stream_format_select *) &pkt->rg_property_data[1]; - dprintk(VIDC_DBG, "data is :%d", + dprintk(VIDC_DBG, "data is :%d\n", prop->nal_stream_format_select); hfi->nal_stream_format_select = hal_to_hfi_type( HAL_PARAM_NAL_STREAM_FORMAT_SELECT, @@ -934,7 +934,7 @@ int create_pkt_cmd_session_set_property( pkt->rg_property_data[1] = HFI_OUTPUT_ORDER_DISPLAY; break; default: - dprintk(VIDC_ERR, "invalid output order: 0x%x", + dprintk(VIDC_ERR, "invalid output order: 0x%x\n", *data); break; } @@ -1023,7 +1023,7 @@ int create_pkt_cmd_session_set_property( pkt->rg_property_data[1] = HFI_DIVX_FORMAT_6; break; default: - dprintk(VIDC_ERR, "Invalid divx format: 0x%x", *data); + dprintk(VIDC_ERR, "Invalid divx format: 0x%x\n", *data); break; } pkt->size += sizeof(u32) * 2; @@ -1118,14 +1118,14 @@ int create_pkt_cmd_session_set_property( if (hfi->profile <= 0) { hfi->profile = HFI_H264_PROFILE_HIGH; dprintk(VIDC_WARN, - "Profile %d not supported, falling back to high", + "Profile %d not supported, falling back to high\n", prop->profile); } if (!hfi->level) { hfi->level = 1; dprintk(VIDC_WARN, - "Level %d not supported, falling back to high", + "Level %d not supported, falling back to high\n", prop->level); } @@ -1175,8 +1175,9 @@ int create_pkt_cmd_session_set_property( pkt->rg_property_data[1] = HFI_RATE_CONTROL_VBR_VFR; break; default: - dprintk(VIDC_ERR, "Invalid Rate control setting: 0x%x", - (int) pdata); + dprintk(VIDC_ERR, + "Invalid Rate control setting: 0x%x\n", + (int)pdata); break; } pkt->size += sizeof(u32) * 2; @@ -1225,7 +1226,7 @@ int create_pkt_cmd_session_set_property( hfi->mode = HFI_H264_DB_MODE_ALL_BOUNDARY; break; default: - dprintk(VIDC_ERR, "Invalid deblocking mode: 0x%x", + dprintk(VIDC_ERR, "Invalid deblocking mode: 0x%x\n", prop->mode); break; } @@ -1358,7 +1359,7 @@ int create_pkt_cmd_session_set_property( hfi->rotation = HFI_ROTATE_270; break; default: - dprintk(VIDC_ERR, "Invalid rotation setting: 0x%x", + dprintk(VIDC_ERR, "Invalid rotation setting: 0x%x\n", prop->rotate); rc = -EINVAL; break; @@ -1374,7 +1375,7 @@ int create_pkt_cmd_session_set_property( hfi->flip = HFI_FLIP_VERTICAL; break; default: - dprintk(VIDC_ERR, "Invalid flip setting: 0x%x", + dprintk(VIDC_ERR, "Invalid flip setting: 0x%x\n", prop->flip); rc = -EINVAL; break; @@ -1407,8 +1408,9 @@ int create_pkt_cmd_session_set_property( hfi->mode = HFI_INTRA_REFRESH_RANDOM; break; default: - dprintk(VIDC_ERR, "Invalid intra refresh setting: 0x%x", - prop->mode); + dprintk(VIDC_ERR, + "Invalid intra refresh setting: 0x%x\n", + prop->mode); break; } hfi->air_mbs = prop->air_mbs; @@ -1440,7 +1442,7 @@ int create_pkt_cmd_session_set_property( hfi->multi_slice = HFI_MULTI_SLICE_BY_BYTE_COUNT; break; default: - dprintk(VIDC_ERR, "Invalid slice settings: 0x%x", + dprintk(VIDC_ERR, "Invalid slice settings: 0x%x\n", prop->multi_slice); break; } @@ -1630,7 +1632,7 @@ int create_pkt_cmd_session_set_property( case HAL_CONFIG_VENC_TIMESTAMP_SCALE: case HAL_PARAM_VENC_LOW_LATENCY: default: - dprintk(VIDC_ERR, "DEFAULT: Calling 0x%x", ptype); + dprintk(VIDC_ERR, "DEFAULT: Calling 0x%x\n", ptype); rc = -ENOTSUPP; break; } diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c index e7c0afebe1da..8b98c4be10db 100644 --- a/drivers/media/platform/msm/vidc/hfi_response_handler.c +++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c @@ -14,7 +14,7 @@ #include <linux/slab.h> #include <linux/list.h> #include <linux/interrupt.h> -#include <mach/msm_smem.h> +#include <soc/qcom/smem.h> #include "vidc_hfi_helper.h" #include "vidc_hfi_io.h" #include "msm_vidc_debug.h" @@ -112,10 +112,11 @@ static void hfi_process_sess_evt_seq_changed( struct hfi_profile_level *profile_level; u8 *data_ptr; int prop_id; - dprintk(VIDC_DBG, "RECEIVED:EVENT_NOTIFY"); + dprintk(VIDC_DBG, "RECEIVED: EVENT_NOTIFY\n"); if (sizeof(struct hfi_msg_event_notify_packet) > pkt->size) { - dprintk(VIDC_ERR, "hal_process_session_init_done:bad_pkt_size"); + dprintk(VIDC_ERR, + "hal_process_session_init_done: bad_pkt_size\n"); return; } @@ -152,7 +153,7 @@ static void hfi_process_sess_evt_seq_changed( (struct hfi_frame_size *) data_ptr; event_notify.width = frame_sz->width; event_notify.height = frame_sz->height; - dprintk(VIDC_DBG, "height:%d width:%d\n", + dprintk(VIDC_DBG, "height: %d width: %d\n", frame_sz->height, frame_sz->width); data_ptr += sizeof(struct hfi_frame_size); @@ -161,14 +162,15 @@ static void hfi_process_sess_evt_seq_changed( data_ptr = data_ptr + sizeof(u32); profile_level = (struct hfi_profile_level *) data_ptr; - dprintk(VIDC_DBG, "profile:%d level:%d\n", + dprintk(VIDC_DBG, "profile: %d level: %d\n", profile_level->profile, profile_level->level); data_ptr += sizeof(struct hfi_profile_level); break; default: - dprintk(VIDC_ERR, "%s cmd:0x%x not supported\n", + dprintk(VIDC_ERR, + "%s cmd: 0x%x not supported\n", __func__, prop_id); break; } @@ -188,10 +190,12 @@ static void hfi_process_evt_release_buffer_ref( struct hfi_msg_release_buffer_ref_event_packet *data; - dprintk(VIDC_DBG, "RECEIVED:EVENT_NOTIFY - release_buffer_reference"); + dprintk(VIDC_DBG, + "RECEIVED: EVENT_NOTIFY - release_buffer_reference\n"); if (sizeof(struct hfi_msg_event_notify_packet) > pkt->size) { - dprintk(VIDC_ERR, "hal_process_session_init_done:bad_pkt_size"); + dprintk(VIDC_ERR, + "hal_process_session_init_done: bad_pkt_size\n"); return; } @@ -248,11 +252,11 @@ static void hfi_process_event_notify( struct list_head *sessions, struct mutex *session_lock) { struct hal_session *sess = NULL; - dprintk(VIDC_DBG, "RECVD:EVENT_NOTIFY"); + dprintk(VIDC_DBG, "Received: EVENT_NOTIFY\n"); if (!callback || !pkt || pkt->size < sizeof(struct hfi_msg_event_notify_packet)) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return; } sess = (struct hal_session *)pkt->session_id; @@ -264,25 +268,28 @@ static void hfi_process_event_notify( hfi_process_sys_error(callback, device_id); break; case HFI_EVENT_SESSION_ERROR: - dprintk(VIDC_INFO, "HFI_EVENT_SESSION_ERROR"); + dprintk(VIDC_INFO, "HFI_EVENT_SESSION_ERROR\n"); if (!validate_session_pkt(sessions, sess, session_lock)) hfi_process_session_error(callback, device_id, pkt); break; case HFI_EVENT_SESSION_SEQUENCE_CHANGED: - dprintk(VIDC_INFO, "HFI_EVENT_SESSION_SEQUENCE_CHANGED"); + dprintk(VIDC_INFO, "HFI_EVENT_SESSION_SEQUENCE_CHANGED\n"); if (!validate_session_pkt(sessions, sess, session_lock)) hfi_process_sess_evt_seq_changed(callback, device_id, pkt); break; case HFI_EVENT_SESSION_PROPERTY_CHANGED: - dprintk(VIDC_INFO, "HFI_EVENT_SESSION_PROPERTY_CHANGED"); + dprintk(VIDC_INFO, "HFI_EVENT_SESSION_PROPERTY_CHANGED\n"); break; case HFI_EVENT_RELEASE_BUFFER_REFERENCE: dprintk(VIDC_INFO, "HFI_EVENT_RELEASE_BUFFER_REFERENCE\n"); - hfi_process_evt_release_buffer_ref(callback, device_id, pkt); + if (!validate_session_pkt(sessions, sess, session_lock)) + hfi_process_evt_release_buffer_ref(callback, + device_id, pkt); break; default: - dprintk(VIDC_WARN, "hal_process_event_notify:unkown_event_id"); + dprintk(VIDC_WARN, + "hal_process_event_notify: unknown_event_id\n"); break; } } @@ -297,9 +304,10 @@ static void hfi_process_sys_init_done( int prop_id; enum vidc_status status = VIDC_ERR_NONE; - dprintk(VIDC_DBG, "RECEIVED:SYS_INIT_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SYS_INIT_DONE\n"); if (sizeof(struct hfi_msg_sys_init_done_packet) > pkt->size) { - dprintk(VIDC_ERR, "hal_process_sys_init_done:bad_pkt_size: %d", + dprintk(VIDC_ERR, + "hal_process_sys_init_done: bad_pkt_size: %d\n", pkt->size); return; } @@ -308,8 +316,8 @@ static void hfi_process_sys_init_done( if (!status) { if (pkt->num_properties == 0) { - dprintk(VIDC_ERR, "hal_process_sys_init_done:" - "no_properties"); + dprintk(VIDC_ERR, + "hal_process_sys_init_done: no_properties\n"); status = VIDC_ERR_FAIL; goto err_no_prop; } @@ -318,8 +326,8 @@ static void hfi_process_sys_init_done( hfi_msg_sys_init_done_packet) + sizeof(u32); if (rem_bytes == 0) { - dprintk(VIDC_ERR, "hal_process_sys_init_done:" - "missing_prop_info"); + dprintk(VIDC_ERR, + "hal_process_sys_init_done: missing_prop_info\n"); status = VIDC_ERR_FAIL; goto err_no_prop; } @@ -351,8 +359,8 @@ static void hfi_process_sys_init_done( break; } default: - dprintk(VIDC_ERR, "hal_process_sys_init_done:" - "bad_prop_id"); + dprintk(VIDC_ERR, + "hal_process_sys_init_done: bad_prop_id\n"); status = VIDC_ERR_BAD_PARAM; break; } @@ -380,11 +388,11 @@ static void hfi_process_sys_rel_resource_done( enum vidc_status status = VIDC_ERR_NONE; u32 pkt_size; memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done)); - dprintk(VIDC_DBG, "RECEIVED:SYS_RELEASE_RESOURCE_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SYS_RELEASE_RESOURCE_DONE\n"); pkt_size = sizeof(struct hfi_msg_sys_release_resource_done_packet); if (pkt_size > pkt->size) { dprintk(VIDC_ERR, - "hal_process_sys_rel_resource_done:bad size:%d", + "hal_process_sys_rel_resource_done: bad size: %d\n", pkt->size); return; } @@ -460,7 +468,7 @@ enum vidc_status hfi_process_sess_init_done_prop_read( if (rem_bytes == 0) { dprintk(VIDC_ERR, - "hfi_msg_sys_session_init_done:missing_prop_info"); + "hfi_msg_sys_session_init_done: missing_prop_info\n"); return VIDC_ERR_FAIL; } @@ -567,7 +575,7 @@ enum vidc_status hfi_process_sess_init_done_prop_read( (struct hfi_profile_level_supported *) (data_ptr + next_offset); ptr = (char *) &prop->rg_profile_level[0]; - dprintk(VIDC_DBG, "prop->profile_count:%d\n", + dprintk(VIDC_DBG, "prop->profile_count: %d\n", prop->profile_count); prop_count = prop->profile_count; while (prop_count) { @@ -652,7 +660,7 @@ enum vidc_status hfi_process_sess_init_done_prop_read( } default: dprintk(VIDC_DBG, - "%s default case - 0x%x", __func__, prop_id); + "%s default case - 0x%x\n", __func__, prop_id); } rem_bytes -= next_offset; data_ptr += next_offset; @@ -669,7 +677,7 @@ static void hfi_process_sess_get_prop_profile_level( dprintk(VIDC_DBG, "Entered %s\n", __func__); if (!prop) { dprintk(VIDC_ERR, - "hal_process_sess_get_profile_level:bad_prop: %p", + "hal_process_sess_get_profile_level: bad_prop: %p\n", prop); return; } @@ -678,7 +686,7 @@ static void hfi_process_sess_get_prop_profile_level( if (!req_bytes || (req_bytes % sizeof(struct hfi_profile_level))) { dprintk(VIDC_ERR, - "hal_process_sess_get_profile_level:bad_pkt: %d", + "hal_process_sess_get_profile_level: bad_pkt: %d\n", req_bytes); return; } @@ -686,7 +694,7 @@ static void hfi_process_sess_get_prop_profile_level( &prop->rg_property_data[1]; profile_level->profile = hfi_profile_level->profile; profile_level->level = hfi_profile_level->level; - dprintk(VIDC_DBG, "%s profile:%d level:%d\n", + dprintk(VIDC_DBG, "%s profile: %d level: %d\n", __func__, profile_level->profile, profile_level->level); } @@ -698,10 +706,9 @@ static void hfi_process_sess_get_prop_buf_req( struct hfi_buffer_requirements *hfi_buf_req; u32 req_bytes; - dprintk(VIDC_DBG, "Entered "); if (!prop) { dprintk(VIDC_ERR, - "hal_process_sess_get_prop_buf_req:bad_prop: %p", + "hal_process_sess_get_prop_buf_req: bad_prop: %p\n", prop); return; } @@ -712,7 +719,7 @@ static void hfi_process_sess_get_prop_buf_req( struct hfi_buffer_requirements)) || (!prop->rg_property_data[1])) { dprintk(VIDC_ERR, - "hal_process_sess_get_prop_buf_req:bad_pkt: %d", + "hal_process_sess_get_prop_buf_req: bad_pkt: %d\n", req_bytes); return; } @@ -726,9 +733,9 @@ static void hfi_process_sess_get_prop_buf_req( buffer_count_actual))) dprintk(VIDC_WARN, "hal_process_sess_get_prop_buf_req:" - "bad_buf_req"); + "bad_buf_req\n"); - dprintk(VIDC_DBG, "got buffer requirements for: %d", + dprintk(VIDC_DBG, "got buffer requirements for: %d\n", hfi_buf_req->buffer_type); switch (hfi_buf_req->buffer_type) { case HFI_BUFFER_INPUT: @@ -796,7 +803,7 @@ static void hfi_process_sess_get_prop_buf_req( break; default: dprintk(VIDC_ERR, - "hal_process_sess_get_prop_buf_req: bad_buffer_type: %d", + "hal_process_sess_get_prop_buf_req: bad_buffer_type: %d\n", hfi_buf_req->buffer_type); break; } @@ -816,13 +823,14 @@ static void hfi_process_session_prop_info( dprintk(VIDC_DBG, "Received SESSION_PROPERTY_INFO\n"); if (pkt->size < sizeof(struct hfi_msg_session_property_info_packet)) { - dprintk(VIDC_ERR, "hal_process_session_prop_info:bad_pkt_size"); + dprintk(VIDC_ERR, + "hal_process_session_prop_info: bad_pkt_size\n"); return; } if (pkt->num_properties == 0) { dprintk(VIDC_ERR, - "hal_process_session_prop_info:no_properties"); + "hal_process_session_prop_info: no_properties\n"); return; } @@ -849,8 +857,8 @@ static void hfi_process_session_prop_info( callback(SESSION_PROPERTY_INFO, &cmd_done); break; default: - dprintk(VIDC_ERR, "hal_process_session_prop_info:" - "unknown_prop_id: %d", + dprintk(VIDC_ERR, + "hal_process_session_prop_info: unknown_prop_id: %d\n", pkt->rg_property_data[0]); break; } @@ -863,10 +871,11 @@ static void hfi_process_session_init_done( struct msm_vidc_cb_cmd_done cmd_done; struct vidc_hal_session_init_done session_init_done; struct hal_session *sess_close = NULL; - dprintk(VIDC_DBG, "RECEIVED:SESSION_INIT_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_INIT_DONE\n"); if (sizeof(struct hfi_msg_sys_session_init_done_packet) > pkt->size) { - dprintk(VIDC_ERR, "hal_process_session_init_done:bad_pkt_size"); + dprintk(VIDC_ERR, + "hal_process_session_init_done: bad_pkt_size\n"); return; } @@ -886,7 +895,7 @@ static void hfi_process_session_init_done( sess_close = (struct hal_session *)pkt->session_id; if (sess_close) { dprintk(VIDC_INFO, - "Sess init failed: Deleting session: 0x%x 0x%p", + "Sess init failed: Deleting session: 0x%x 0x%p\n", sess_close->session_id, sess_close); list_del(&sess_close->list); kfree(sess_close); @@ -902,12 +911,13 @@ static void hfi_process_session_load_res_done( struct hfi_msg_session_load_resources_done_packet *pkt) { struct msm_vidc_cb_cmd_done cmd_done; - dprintk(VIDC_DBG, "RECEIVED:SESSION_LOAD_RESOURCES_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_LOAD_RESOURCES_DONE\n"); if (sizeof(struct hfi_msg_session_load_resources_done_packet) != pkt->size) { - dprintk(VIDC_ERR, "hal_process_session_load_res_done:" - " bad packet size: %d", pkt->size); + dprintk(VIDC_ERR, + "hal_process_session_load_res_done: bad packet size: %d\n", + pkt->size); return; } @@ -928,11 +938,12 @@ static void hfi_process_session_flush_done( { struct msm_vidc_cb_cmd_done cmd_done; - dprintk(VIDC_DBG, "RECEIVED:SESSION_FLUSH_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_FLUSH_DONE\n"); if (sizeof(struct hfi_msg_session_flush_done_packet) != pkt->size) { - dprintk(VIDC_ERR, "hal_process_session_flush_done: " - "bad packet size: %d", pkt->size); + dprintk(VIDC_ERR, + "hal_process_session_flush_done: bad packet size: %d\n", + pkt->size); return; } @@ -952,11 +963,12 @@ static void hfi_process_session_etb_done( { struct msm_vidc_cb_data_done data_done; - dprintk(VIDC_DBG, "RECEIVED:SESSION_ETB_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_ETB_DONE\n"); if (!pkt || pkt->size < sizeof(struct hfi_msg_session_empty_buffer_done_packet)) { - dprintk(VIDC_ERR, "hal_process_session_etb_done:bad_pkt_size"); + dprintk(VIDC_ERR, + "hal_process_session_etb_done: bad_pkt_size\n"); return; } @@ -988,13 +1000,13 @@ static void hfi_process_session_ftb_done( struct hal_session *session; if (!msg_hdr) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return; } session = (struct hal_session *) ((struct hal_session *) pack->session_id)->session_id; - dprintk(VIDC_DBG, "RECEIVED:SESSION_FTB_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_FTB_DONE\n"); memset(&data_done, 0, sizeof(struct msm_vidc_cb_data_done)); @@ -1006,11 +1018,11 @@ static void hfi_process_session_ftb_done( hfi_msg_session_fill_buffer_done_compressed_packet) > pkt->size) { dprintk(VIDC_ERR, - "hal_process_session_ftb_done: bad_pkt_size"); + "hal_process_session_ftb_done: bad_pkt_size\n"); return; } else if (pkt->error_type != HFI_ERR_NONE) { dprintk(VIDC_ERR, - "got buffer back with error %x", + "got buffer back with error %x\n", pkt->error_type); /* Proceed with the FBD */ } @@ -1045,8 +1057,8 @@ static void hfi_process_session_ftb_done( if (sizeof(struct hfi_msg_session_fbd_uncompressed_plane0_packet) > pkt->size) { - dprintk(VIDC_ERR, "hal_process_session_ftb_done:" - "bad_pkt_size"); + dprintk(VIDC_ERR, + "hal_process_session_ftb_done: bad_pkt_size\n"); return; } @@ -1092,12 +1104,13 @@ static void hfi_process_session_start_done( { struct msm_vidc_cb_cmd_done cmd_done; - dprintk(VIDC_DBG, "RECEIVED:SESSION_START_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_START_DONE\n"); if (!pkt || pkt->size != sizeof(struct hfi_msg_session_start_done_packet)) { - dprintk(VIDC_ERR, "hal_process_session_start_done:" - "bad packet/packet size: %d", pkt->size); + dprintk(VIDC_ERR, + "hal_process_session_start_done: bad packet/packet size: %d\n", + pkt->size); return; } @@ -1117,12 +1130,13 @@ static void hfi_process_session_stop_done( { struct msm_vidc_cb_cmd_done cmd_done; - dprintk(VIDC_DBG, "RECEIVED:SESSION_STOP_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_STOP_DONE\n"); if (!pkt || pkt->size != sizeof(struct hfi_msg_session_stop_done_packet)) { - dprintk(VIDC_ERR, "hal_process_session_stop_done:" - "bad packet/packet size: %d", pkt->size); + dprintk(VIDC_ERR, + "hal_process_session_stop_done: bad packet/packet size: %d\n", + pkt->size); return; } @@ -1142,12 +1156,13 @@ static void hfi_process_session_rel_res_done( { struct msm_vidc_cb_cmd_done cmd_done; - dprintk(VIDC_DBG, "RECEIVED:SESSION_RELEASE_RESOURCES_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_RELEASE_RESOURCES_DONE\n"); if (!pkt || pkt->size != sizeof(struct hfi_msg_session_release_resources_done_packet)) { - dprintk(VIDC_ERR, "hal_process_session_rel_res_done:" - "bad packet/packet size: %d", pkt->size); + dprintk(VIDC_ERR, + "hal_process_session_rel_res_done: bad packet/packet size: %d\n", + pkt->size); return; } @@ -1169,7 +1184,7 @@ static void hfi_process_session_rel_buf_done( if (!pkt || pkt->size != sizeof(struct hfi_msg_session_release_buffers_done_packet)) { - dprintk(VIDC_ERR, "bad packet/packet size: %d", pkt->size); + dprintk(VIDC_ERR, "bad packet/packet size: %d\n", pkt->size); return; } memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done)); @@ -1193,12 +1208,13 @@ static void hfi_process_session_end_done( { struct msm_vidc_cb_cmd_done cmd_done; - dprintk(VIDC_DBG, "RECEIVED:SESSION_END_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_END_DONE\n"); if (!pkt || pkt->size != sizeof(struct hfi_msg_sys_session_end_done_packet)) { - dprintk(VIDC_ERR, "hal_process_session_end_done: " - "bad packet/packet size: %d", pkt->size); + dprintk(VIDC_ERR, + "hal_process_session_end_done: bad packet/packet size: %d\n", + pkt->size); return; } @@ -1218,11 +1234,11 @@ static void hfi_process_session_abort_done( { struct msm_vidc_cb_cmd_done cmd_done; - dprintk(VIDC_DBG, "RECEIVED:SESSION_ABORT_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_ABORT_DONE\n"); if (!pkt || pkt->size != sizeof(struct hfi_msg_sys_session_abort_done_packet)) { - dprintk(VIDC_ERR, "%s: bad packet/packet size: %d", + dprintk(VIDC_ERR, "%s: bad packet/packet size: %d\n", __func__, pkt ? pkt->size : 0); return; } @@ -1245,7 +1261,7 @@ static void hfi_process_session_get_seq_hdr_done( if (!pkt || pkt->size != sizeof(struct hfi_msg_session_get_sequence_header_done_packet)) { - dprintk(VIDC_ERR, "bad packet/packet size: %d", pkt->size); + dprintk(VIDC_ERR, "bad packet/packet size: %d\n", pkt->size); return; } memset(&data_done, 0, sizeof(struct msm_vidc_cb_data_done)); @@ -1256,7 +1272,7 @@ static void hfi_process_session_get_seq_hdr_done( data_done.status = hfi_map_err_status((u32)pkt->error_type); data_done.output_done.packet_buffer1 = pkt->sequence_header; data_done.output_done.filled_len1 = pkt->header_len; - dprintk(VIDC_INFO, "seq_hdr: %p, Length: %d", + dprintk(VIDC_INFO, "seq_hdr: %p, Length: %d\n", pkt->sequence_header, pkt->header_len); callback(SESSION_GET_SEQ_HDR_DONE, &data_done); } @@ -1278,7 +1294,7 @@ static void hfi_process_sys_get_prop_image_version( !pkt->rg_property_data[1] || pkt->num_properties > 1) { dprintk(VIDC_ERR, - "hfi_process_sys_get_prop_image_version:bad_pkt: %d", + "hfi_process_sys_get_prop_image_version: bad_pkt: %d\n", req_bytes); return; } @@ -1330,7 +1346,7 @@ static void hfi_process_sys_property_info( break; default: dprintk(VIDC_ERR, - "hfi_process_sys_property_info:unknown_prop_id: %d\n", + "hfi_process_sys_property_info: unknown_prop_id: %d\n", pkt->rg_property_data[0]); } } @@ -1344,13 +1360,14 @@ u32 hfi_process_msg_packet( struct hal_session *sess = NULL; if (!callback || !msg_hdr || msg_hdr->size < VIDC_IFACEQ_MIN_PKT_SIZE) { - dprintk(VIDC_ERR, "hal_process_msg_packet:bad" - "packet/packet size: %d", msg_hdr->size); + dprintk(VIDC_ERR, + "hal_process_msg_packet: bad packet/packet size: %d\n", + msg_hdr->size); rc = -EINVAL; return rc; } - dprintk(VIDC_INFO, "Received: 0x%x in ", msg_hdr->packet); + dprintk(VIDC_INFO, "Received: 0x%x\n", msg_hdr->packet); rc = (u32) msg_hdr->packet; sess = (struct hal_session *)((struct vidc_hal_session_cmd_pkt*) msg_hdr)->session_id; @@ -1458,7 +1475,7 @@ u32 hfi_process_msg_packet( msg_hdr); break; default: - dprintk(VIDC_DBG, "UNKNOWN_MSG_TYPE : %d", msg_hdr->packet); + dprintk(VIDC_DBG, "UNKNOWN_MSG_TYPE : %d\n", msg_hdr->packet); break; } return rc; diff --git a/drivers/media/platform/msm/vidc/msm_smem.c b/drivers/media/platform/msm/vidc/msm_smem.c index 8ef6f99aacf7..f4eb59b267a8 100644 --- a/drivers/media/platform/msm/vidc/msm_smem.c +++ b/drivers/media/platform/msm/vidc/msm_smem.c @@ -63,14 +63,15 @@ static int get_device_address(struct smem_client *smem_client, clnt = smem_client->clnt; if (!clnt) { - dprintk(VIDC_ERR, "Invalid client"); + dprintk(VIDC_ERR, "Invalid client\n"); return -EINVAL; } rc = msm_smem_get_domain_partition(smem_client, flags, buffer_type, &domain, &partition); if (rc) { - dprintk(VIDC_ERR, "Failed to get domain and partition: %d", rc); + dprintk(VIDC_ERR, "Failed to get domain and partition: %d\n", + rc); goto mem_domain_get_failed; } @@ -84,16 +85,16 @@ static int get_device_address(struct smem_client *smem_client, } if (is_iommu_present(smem_client->res)) { dprintk(VIDC_DBG, - "Calling ion_map_iommu - domain: %d, partition: %d", + "Calling ion_map_iommu - domain: %d, partition: %d\n", domain, partition); rc = ion_map_iommu(clnt, hndl, domain, partition, align, 0, iova, buffer_size, 0, 0); } else { - dprintk(VIDC_DBG, "Using physical memory address"); + dprintk(VIDC_DBG, "Using physical memory address\n"); rc = ion_phys(clnt, hndl, iova, (size_t *)buffer_size); } if (rc) { - dprintk(VIDC_ERR, "ion memory map failed - %d", rc); + dprintk(VIDC_ERR, "ion memory map failed - %d\n", rc); goto mem_map_failed; } @@ -118,12 +119,12 @@ static void put_device_address(struct smem_client *smem_client, clnt = smem_client->clnt; if (!clnt) { - dprintk(VIDC_WARN, "Invalid client"); + dprintk(VIDC_WARN, "Invalid client\n"); return; } if (is_iommu_present(smem_client->res)) { dprintk(VIDC_DBG, - "Calling ion_unmap_iommu - domain: %d, parition: %d", + "Calling ion_unmap_iommu - domain: %d, parition: %d\n", domain_num, partition_num); ion_unmap_iommu(clnt, hndl, domain_num, partition_num); } @@ -270,7 +271,7 @@ static void free_ion_mem(struct smem_client *client, struct msm_smem *mem) rc = msm_smem_get_domain_partition((void *)client, mem->flags, mem->buffer_type, &domain, &partition); if (rc) { - dprintk(VIDC_ERR, "Failed to get domain, partition: %d", rc); + dprintk(VIDC_ERR, "Failed to get domain, partition: %d\n", rc); return; } @@ -514,7 +515,7 @@ int msm_smem_get_domain_partition(void *clt, u32 flags, enum hal_buffer bool is_secure = (flags & SMEM_SECURE); struct iommu_info *iommu_map; if (!domain_num || !partition_num) { - dprintk(VIDC_DBG, "passed null to get domain partition!"); + dprintk(VIDC_DBG, "passed null to get domain partition!\n"); return -EINVAL; } diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c index 65164a8b0be5..a46095682a9f 100644 --- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c +++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c @@ -35,7 +35,7 @@ struct msm_vidc_drv *vidc_driver; -uint32_t msm_vidc_pwr_collapse_delay = INT_MAX; +uint32_t msm_vidc_pwr_collapse_delay = 2000; static inline struct msm_vidc_inst *get_vidc_inst(struct file *filp, void *fh) { diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index 7e91efabae17..2f8465996a55 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -21,7 +21,7 @@ #define MSM_VDEC_DVC_NAME "msm_vdec_8974" #define MIN_NUM_OUTPUT_BUFFERS 4 #define MAX_NUM_OUTPUT_BUFFERS VIDEO_MAX_FRAME -#define DEFAULT_CONCEAL_COLOR 0x0 +#define DEFAULT_VIDEO_CONCEAL_COLOR_BLACK 0x8080 #define TZ_INFO_GET_FEATURE_VERSION_ID 0x3 #define TZ_DYNAMIC_BUFFER_FEATURE_ID 12 @@ -508,6 +508,16 @@ static struct msm_vidc_ctrl msm_vdec_ctrls[] = { .qmenu = mpeg_vidc_video_h264_mvc_layout, .cluster = 0, }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR, + .name = "Picture concealed color", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0x0, + .maximum = 0xffffff, + .default_value = DEFAULT_VIDEO_CONCEAL_COLOR_BLACK, + .step = 1, + .cluster = 0, + }, }; #define NUM_CTRLS ARRAY_SIZE(msm_vdec_ctrls) @@ -531,7 +541,7 @@ static int is_ctrl_valid_for_codec(struct msm_vidc_inst *inst, switch (ctrl->id) { case V4L2_CID_MPEG_VIDC_VIDEO_MVC_BUFFER_LAYOUT: if (inst->fmts[OUTPUT_PORT]->fourcc != V4L2_PIX_FMT_H264_MVC) { - dprintk(VIDC_ERR, "Control 0x%x only valid for MVC", + dprintk(VIDC_ERR, "Control 0x%x only valid for MVC\n", ctrl->id); rc = -ENOTSUPP; break; @@ -540,7 +550,8 @@ static int is_ctrl_valid_for_codec(struct msm_vidc_inst *inst, case V4L2_CID_MPEG_VIDEO_H264_PROFILE: if (inst->fmts[OUTPUT_PORT]->fourcc == V4L2_PIX_FMT_H264_MVC && ctrl->val != V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH) { - dprintk(VIDC_ERR, "Profile 0x%x not supported for MVC", + dprintk(VIDC_ERR, + "Profile 0x%x not supported for MVC\n", ctrl->val); rc = -ENOTSUPP; break; @@ -549,7 +560,7 @@ static int is_ctrl_valid_for_codec(struct msm_vidc_inst *inst, case V4L2_CID_MPEG_VIDEO_H264_LEVEL: if (inst->fmts[OUTPUT_PORT]->fourcc == V4L2_PIX_FMT_H264_MVC && ctrl->val >= V4L2_MPEG_VIDEO_H264_LEVEL_5_2) { - dprintk(VIDC_ERR, "Level 0x%x not supported for MVC", + dprintk(VIDC_ERR, "Level 0x%x not supported for MVC\n", ctrl->val); rc = -ENOTSUPP; break; @@ -717,7 +728,7 @@ int msm_vdec_prepare_buf(struct msm_vidc_inst *inst, struct hfi_device *hdev; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } hdev = inst->core->device; @@ -765,7 +776,7 @@ int msm_vdec_prepare_buf(struct msm_vidc_inst *inst, (void *)inst->session, &buffer_info); if (rc) dprintk(VIDC_ERR, - "vidc_hal_session_set_buffers failed"); + "vidc_hal_session_set_buffers failed\n"); break; default: dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type); @@ -785,7 +796,7 @@ int msm_vdec_release_buf(struct msm_vidc_inst *inst, struct hfi_device *hdev; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } @@ -844,7 +855,7 @@ int msm_vdec_release_buf(struct msm_vidc_inst *inst, (void *)inst->session, &buffer_info); if (rc) dprintk(VIDC_ERR, - "vidc_hal_session_release_buffers failed"); + "vidc_hal_session_release_buffers failed\n"); break; default: dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type); @@ -1105,7 +1116,7 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) int max_input_size = 0; if (!inst || !f) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { @@ -1312,7 +1323,7 @@ static int msm_vdec_queue_setup(struct vb2_queue *q, inst = q->drv_priv; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } @@ -1470,7 +1481,7 @@ static inline int start_streaming(struct msm_vidc_inst *inst) HAL_VIDEO_DECODER_SECONDARY) rc = msm_vidc_check_scaling_supported(inst); if (rc) { - dprintk(VIDC_ERR, "H/w scaling is not in valid range"); + dprintk(VIDC_ERR, "H/w scaling is not in valid range\n"); return -EINVAL; } rc = msm_comm_set_scratch_buffers(inst); @@ -1548,7 +1559,6 @@ static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count) { struct msm_vidc_inst *inst; int rc = 0; - int pdata = DEFAULT_CONCEAL_COLOR; struct hfi_device *hdev; if (!q || !q->drv_priv) { dprintk(VIDC_ERR, "Invalid input, q = %p\n", q); @@ -1556,7 +1566,7 @@ static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count) } inst = q->drv_priv; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } hdev = inst->core->device; @@ -1566,10 +1576,6 @@ static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count) case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: if (inst->bufq[CAPTURE_PORT].vb2_bufq.streaming) rc = start_streaming(inst); - rc = call_hfi_op(hdev, session_set_property, - (void *) inst->session, - HAL_PARAM_VDEC_CONCEAL_COLOR, - (void *) &pdata); break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: if (inst->bufq[OUTPUT_PORT].vb2_bufq.streaming) @@ -1634,12 +1640,24 @@ int msm_vdec_cmd(struct msm_vidc_inst *inst, struct v4l2_decoder_cmd *dec) struct msm_vidc_core *core = inst->core; if (!dec || !inst || !inst->core) { - dprintk(VIDC_ERR, "%s invalid params", __func__); + dprintk(VIDC_ERR, "%s invalid params\n", __func__); return -EINVAL; } switch (dec->cmd) { case V4L2_DEC_QCOM_CMD_FLUSH: + if (core->state != VIDC_CORE_INVALID && + inst->state == MSM_VIDC_CORE_INVALID) { + rc = msm_comm_recover_from_session_error(inst); + if (rc) + dprintk(VIDC_ERR, + "Failed to recover from session_error: %d\n", + rc); + } rc = msm_comm_flush(inst, dec->flags); + if (rc) { + dprintk(VIDC_ERR, + "Failed to flush buffers: %d\n", rc); + } break; case V4L2_DEC_CMD_STOP: if (core->state != VIDC_CORE_INVALID && @@ -1733,7 +1751,7 @@ static inline enum buffer_mode_type get_buf_type(int val) case V4L2_MPEG_VIDC_VIDEO_DYNAMIC: return HAL_BUFFER_MODE_DYNAMIC; default: - dprintk(VIDC_ERR, "%s: invalid buf type: %d", __func__, val); + dprintk(VIDC_ERR, "%s: invalid buf type: %d\n", __func__, val); } return 0; } @@ -1765,7 +1783,7 @@ static int try_get_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) union hal_get_property hprop; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } @@ -1831,7 +1849,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) struct hal_mvc_buffer_layout layout; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } hdev = inst->core->device; @@ -1914,7 +1932,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) inst->flags |= VIDC_TURBO; break; default: - dprintk(VIDC_ERR, "Perf mode %x not supported", + dprintk(VIDC_ERR, "Perf mode %x not supported\n", ctrl->val); rc = -ENOTSUPP; break; @@ -1963,7 +1981,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE: if (ctrl->val && !(inst->capability.pixelprocess_capabilities & HAL_VIDEO_DECODER_MULTI_STREAM_CAPABILITY)) { - dprintk(VIDC_ERR, "Downscaling not supported: 0x%x", + dprintk(VIDC_ERR, "Downscaling not supported: 0x%x\n", ctrl->id); rc = -ENOTSUPP; break; @@ -2038,6 +2056,11 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) pdata = &layout; break; } + case V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR: + property_id = HAL_PARAM_VDEC_CONCEAL_COLOR; + property_val = ctrl->val; + pdata = &property_val; + break; default: break; } @@ -2059,7 +2082,7 @@ static int msm_vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) struct msm_vidc_inst *inst = container_of(ctrl->handler, struct msm_vidc_inst, ctrl_handler); if (!inst) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE); @@ -2073,7 +2096,7 @@ static int msm_vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) if (ctrl->cluster[c]->is_new) { rc = try_set_ctrl(inst, ctrl->cluster[c]); if (rc) { - dprintk(VIDC_ERR, "Failed setting %x", + dprintk(VIDC_ERR, "Failed setting %x\n", ctrl->cluster[c]->id); break; } @@ -2082,7 +2105,8 @@ static int msm_vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) failed_open_done: if (rc) - dprintk(VIDC_ERR, "Failed to set hal property for framesize\n"); + dprintk(VIDC_ERR, "Failed setting control: %x (%s)", + ctrl->id, v4l2_ctrl_get_name(ctrl->id)); return rc; } @@ -2103,7 +2127,7 @@ static int msm_vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) if (master->cluster[c]->id == ctrl->id) { rc = try_get_ctrl(inst, ctrl); if (rc) { - dprintk(VIDC_ERR, "Failed getting %x", + dprintk(VIDC_ERR, "Failed getting %x\n", ctrl->id); return rc; } @@ -2246,7 +2270,8 @@ int msm_vdec_ctrl_init(struct msm_vidc_inst *inst) cluster = get_cluster(idx, &cluster_size); if (!cluster || !cluster_size) { - dprintk(VIDC_WARN, "Failed to setup cluster of type %d", + dprintk(VIDC_WARN, + "Failed to setup cluster of type %d\n", idx); continue; } diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c index 664cea0567a1..3c99756874e9 100644 --- a/drivers/media/platform/msm/vidc/msm_venc.c +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -478,6 +478,26 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { .cluster = MSM_VENC_CTRL_CLUSTER_QP, }, { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP, + .name = "VP8 Minimum QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 128, + .default_value = 1, + .step = 1, + .cluster = MSM_VENC_CTRL_CLUSTER_QP, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP, + .name = "VP8 Maximum QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 128, + .default_value = 128, + .step = 1, + .cluster = MSM_VENC_CTRL_CLUSTER_QP, + }, + { .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, .name = "Slice Mode", .type = V4L2_CTRL_TYPE_MENU, @@ -885,7 +905,7 @@ static int msm_venc_queue_setup(struct vb2_queue *q, inst = q->drv_priv; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } hdev = inst->core->device; @@ -990,7 +1010,7 @@ static inline int start_streaming(struct msm_vidc_inst *inst) HAL_VIDEO_ENCODER_SCALING_CAPABILITY) rc = msm_vidc_check_scaling_supported(inst); if (rc) { - dprintk(VIDC_ERR, "H/w scaling is not in valid range"); + dprintk(VIDC_ERR, "H/w scaling is not in valid range\n"); return -EINVAL; } rc = msm_comm_try_get_bufreqs(inst); @@ -1323,7 +1343,7 @@ static inline int venc_v4l2_to_hal(int id, int value) } unknown_value: - dprintk(VIDC_WARN, "Unknown control (%x, %d)", id, value); + dprintk(VIDC_WARN, "Unknown control (%x, %d)\n", id, value); return -EINVAL; } @@ -1354,7 +1374,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) struct hal_mpeg4_time_resolution time_res; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } hdev = inst->core->device; @@ -1366,7 +1386,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) __ctrl_id, \ ctrl->cluster, ctrl->ncontrols); \ if (!__temp) { \ - dprintk(VIDC_ERR, "Can't find %s (%x) in cluster", \ + dprintk(VIDC_ERR, "Can't find %s (%x) in cluster\n", \ #__ctrl_id, __ctrl_id); \ /* Clusters are hardcoded, if we can't find */ \ /* something then things are massively screwed up */ \ @@ -1393,7 +1413,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) inst->fmts[CAPTURE_PORT]->fourcc != V4L2_PIX_FMT_H264 && inst->fmts[CAPTURE_PORT]->fourcc != V4L2_PIX_FMT_H264_NO_SC) { - dprintk(VIDC_ERR, "Control 0x%x only valid for H264", + dprintk(VIDC_ERR, "Control 0x%x only valid for H264\n", ctrl->id); rc = -ENOTSUPP; break; @@ -1438,7 +1458,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) (void *)inst->session, property_id, pdata); if (rc) { dprintk(VIDC_ERR, - "Failed : Setprop MAX_NUM_B_FRAMES %d", + "Failed : Setprop MAX_NUM_B_FRAMES %d\n", rc); break; } @@ -1522,13 +1542,13 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) if (ctrl->val < avg_bitrate->val) { dprintk(VIDC_ERR, - "Peak bitrate (%d) is lower than average bitrate (%d)", + "Peak bitrate (%d) is lower than average bitrate (%d)\n", ctrl->val, avg_bitrate->val); rc = -EINVAL; break; } else if (ctrl->val < avg_bitrate->val * 2) { dprintk(VIDC_WARN, - "Peak bitrate (%d) ideally should be twice the average bitrate (%d)", + "Peak bitrate (%d) ideally should be twice the average bitrate (%d)\n", ctrl->val, avg_bitrate->val); } @@ -1653,7 +1673,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) struct v4l2_ctrl *deinterlace = NULL; if (!(inst->capability.pixelprocess_capabilities & HAL_VIDEO_ENCODER_ROTATION_CAPABILITY)) { - dprintk(VIDC_ERR, "Rotation not supported: 0x%x", + dprintk(VIDC_ERR, "Rotation not supported: 0x%x\n", ctrl->id); rc = -ENOTSUPP; break; @@ -1663,7 +1683,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) if (ctrl->val && deinterlace && deinterlace->val != V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_DISABLED) { dprintk(VIDC_ERR, - "Rotation not supported with deinterlacing"); + "Rotation not supported with deinterlacing\n"); rc = -EINVAL; break; } @@ -1729,7 +1749,8 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_MAX_QP); if (ctrl->val >= qp_max->val) { - dprintk(VIDC_ERR, "Bad range: Min QP (%d) > Max QP(%d)", + dprintk(VIDC_ERR, + "Bad range: Min QP (%d) > Max QP(%d)\n", ctrl->val, qp_max->val); rc = -ERANGE; break; @@ -1748,7 +1769,8 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_MIN_QP); if (ctrl->val <= qp_min->val) { - dprintk(VIDC_ERR, "Bad range: Max QP (%d) < Min QP(%d)", + dprintk(VIDC_ERR, + "Bad range: Max QP (%d) < Min QP(%d)\n", ctrl->val, qp_min->val); rc = -ERANGE; break; @@ -1762,6 +1784,26 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) pdata = &qp_range; break; } + case V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP: { + struct v4l2_ctrl *qp_max; + qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP); + property_id = HAL_PARAM_VENC_SESSION_QP_RANGE; + qp_range.layer_id = 0; + qp_range.max_qp = qp_max->val; + qp_range.min_qp = ctrl->val; + pdata = &qp_range; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP: { + struct v4l2_ctrl *qp_min; + qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP); + property_id = HAL_PARAM_VENC_SESSION_QP_RANGE; + qp_range.layer_id = 0; + qp_range.max_qp = ctrl->val; + qp_range.min_qp = qp_min->val; + pdata = &qp_range; + break; + } case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: { int temp = 0; @@ -1816,7 +1858,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) } else { dprintk(VIDC_WARN, "Failed : slice delivery mode is valid "\ - "only for H264 encoder and MB based slicing"); + "only for H264 encoder and MB based slicing\n"); enable.enable = false; } pdata = &enable; @@ -2011,7 +2053,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) inst->flags |= VIDC_TURBO; break; default: - dprintk(VIDC_ERR, "Perf mode %x not supported", + dprintk(VIDC_ERR, "Perf mode %x not supported\n", ctrl->val); rc = -ENOTSUPP; break; @@ -2039,7 +2081,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) struct v4l2_ctrl *rotation = NULL; if (!(inst->capability.pixelprocess_capabilities & HAL_VIDEO_ENCODER_DEINTERLACE_CAPABILITY)) { - dprintk(VIDC_ERR, "Deinterlace not supported: 0x%x", + dprintk(VIDC_ERR, "Deinterlace not supported: 0x%x\n", ctrl->id); rc = -ENOTSUPP; break; @@ -2094,7 +2136,7 @@ static int msm_venc_op_s_ctrl(struct v4l2_ctrl *ctrl) struct msm_vidc_inst, ctrl_handler); if (!inst) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } @@ -2112,7 +2154,7 @@ static int msm_venc_op_s_ctrl(struct v4l2_ctrl *ctrl) rc = try_set_ctrl(inst, temp); if (rc) { - dprintk(VIDC_ERR, "Failed setting %s (%x)", + dprintk(VIDC_ERR, "Failed setting %s (%x)\n", v4l2_ctrl_get_name(temp->id), temp->id); break; @@ -2121,7 +2163,8 @@ static int msm_venc_op_s_ctrl(struct v4l2_ctrl *ctrl) } failed_open_done: if (rc) - dprintk(VIDC_ERR, "Failed to set hal property\n"); + dprintk(VIDC_ERR, "Failed setting control: %x (%s)", + ctrl->id, v4l2_ctrl_get_name(ctrl->id)); return rc; } @@ -2264,7 +2307,7 @@ int msm_venc_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a) struct hfi_device *hdev; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } @@ -2337,7 +2380,7 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) } if (!inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } hdev = inst->core->device; @@ -2570,7 +2613,7 @@ int msm_venc_prepare_buf(struct msm_vidc_inst *inst, int extra_idx = 0; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } @@ -2614,7 +2657,7 @@ int msm_venc_prepare_buf(struct msm_vidc_inst *inst, (void *)inst->session, &buffer_info); if (rc) dprintk(VIDC_ERR, - "vidc_hal_session_set_buffers failed"); + "vidc_hal_session_set_buffers failed\n"); break; default: dprintk(VIDC_ERR, @@ -2632,7 +2675,7 @@ int msm_venc_release_buf(struct msm_vidc_inst *inst, struct hfi_device *hdev; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } @@ -2854,7 +2897,8 @@ int msm_venc_ctrl_init(struct msm_vidc_inst *inst) cluster = get_cluster(idx, &cluster_size); if (!cluster || !cluster_size) { - dprintk(VIDC_WARN, "Failed to setup cluster of type %d", + dprintk(VIDC_WARN, + "Failed to setup cluster of type %d\n", idx); continue; } diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c index f99c3b980bcc..b85b0c729e37 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc.c +++ b/drivers/media/platform/msm/vidc/msm_vidc.c @@ -686,7 +686,8 @@ int output_buffer_cache_invalidate(struct msm_vidc_inst *inst, return -EINVAL; } } else - dprintk(VIDC_ERR, "%s: WARN: NULL handle", __func__); + dprintk(VIDC_DBG, "%s: NULL handle for plane %d\n", + __func__, i); } return 0; } @@ -732,6 +733,8 @@ int msm_vidc_release_buffers(void *instance, int buffer_type) return -EINVAL; list_for_each_safe(ptr, next, &inst->registered_bufs) { + bool release_buf = false; + mutex_lock(&inst->lock); bi = list_entry(ptr, struct buffer_info, list); if (bi->type == buffer_type) { buffer_info.type = bi->type; @@ -749,19 +752,28 @@ int msm_vidc_release_buffers(void *instance, int buffer_type) buffer_info.m.planes[i].length); } buffer_info.length = bi->num_planes; - if (inst->session_type == MSM_VIDC_DECODER) - rc = msm_vdec_release_buf(instance, - &buffer_info); - if (inst->session_type == MSM_VIDC_ENCODER) - rc = msm_venc_release_buf(instance, - &buffer_info); - if (rc) - dprintk(VIDC_ERR, - "Failed Release buffer: %d, %d, %d\n", - buffer_info.m.planes[0].reserved[0], - buffer_info.m.planes[0].reserved[1], - buffer_info.m.planes[0].length); - + release_buf = true; + } + mutex_unlock(&inst->lock); + if (!release_buf) + continue; + if (inst->session_type == MSM_VIDC_DECODER) + rc = msm_vdec_release_buf(instance, + &buffer_info); + if (inst->session_type == MSM_VIDC_ENCODER) + rc = msm_venc_release_buf(instance, + &buffer_info); + if (rc) + dprintk(VIDC_ERR, + "Failed Release buffer: %d, %d, %d\n", + buffer_info.m.planes[0].reserved[0], + buffer_info.m.planes[0].reserved[1], + buffer_info.m.planes[0].length); + } + mutex_lock(&inst->lock); + list_for_each_safe(ptr, next, &inst->registered_bufs) { + bi = list_entry(ptr, struct buffer_info, list); + if (bi->type == buffer_type) { list_del(&bi->list); for (i = 0; i < bi->num_planes; i++) { if (bi->handle[i] && bi->mapped[i]) { @@ -777,6 +789,7 @@ int msm_vidc_release_buffers(void *instance, int buffer_type) kfree(bi); } } + mutex_unlock(&inst->lock); return rc; } EXPORT_SYMBOL(msm_vidc_release_buffers); @@ -1183,7 +1196,7 @@ void *msm_vidc_open(int core_id, int session_type) } pr_info(VIDC_DBG_TAG "Opening video instance: %p, %d\n", - VIDC_INFO, inst, session_type); + VIDC_MSG_PRIO2STRING(VIDC_INFO), inst, session_type); mutex_init(&inst->sync_lock); mutex_init(&inst->bufq[CAPTURE_PORT].lock); mutex_init(&inst->bufq[OUTPUT_PORT].lock); @@ -1379,7 +1392,8 @@ int msm_vidc_close(void *instance) dprintk(VIDC_ERR, "Failed to move video instance to uninit state\n"); - pr_info(VIDC_DBG_TAG "Closed video instance: %p\n", VIDC_INFO, inst); + pr_info(VIDC_DBG_TAG "Closed video instance: %p\n", + VIDC_MSG_PRIO2STRING(VIDC_INFO), inst); kfree(inst); return 0; } diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 7c39a1f600db..5101a0c463ab 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -173,7 +173,7 @@ static void msm_comm_unvote_buses(struct msm_vidc_core *core, struct hfi_device *hdev; if (!core || !core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return; } hdev = core->device; @@ -317,7 +317,7 @@ static void handle_session_release_buf_done(enum command_response cmd, list_for_each_safe(ptr, next, &inst->internalbufs) { buf = list_entry(ptr, struct internal_buf, list); if (address == buf->handle->device_addr) { - dprintk(VIDC_DBG, "releasing scratch: 0x%x", + dprintk(VIDC_DBG, "releasing scratch: 0x%x\n", (u32) buf->handle->device_addr); buf_found = true; } @@ -326,14 +326,14 @@ static void handle_session_release_buf_done(enum command_response cmd, list_for_each_safe(ptr, next, &inst->persistbufs) { buf = list_entry(ptr, struct internal_buf, list); if (address == (u32) buf->handle->device_addr) { - dprintk(VIDC_DBG, "releasing persist: 0x%x", + dprintk(VIDC_DBG, "releasing persist: 0x%x\n", (u32) buf->handle->device_addr); buf_found = true; } } if (!buf_found) - dprintk(VIDC_ERR, "invalid buffer received from firmware"); + dprintk(VIDC_ERR, "invalid buffer received from firmware\n"); complete(&inst->completions[SESSION_MSG_INDEX(cmd)]); } @@ -359,13 +359,13 @@ static void change_inst_state(struct msm_vidc_inst *inst, enum instance_state state) { if (!inst) { - dprintk(VIDC_ERR, "Invalid parameter %s", __func__); + dprintk(VIDC_ERR, "Invalid parameter %s\n", __func__); return; } mutex_lock(&inst->lock); if (inst->state == MSM_VIDC_CORE_INVALID) { dprintk(VIDC_DBG, - "Inst: %p is in bad state can't change state", + "Inst: %p is in bad state can't change state\n", inst); goto exit; } @@ -395,7 +395,8 @@ static int wait_for_sess_signal_receipt(struct msm_vidc_inst *inst, &inst->completions[SESSION_MSG_INDEX(cmd)], msecs_to_jiffies(msm_vidc_hw_rsp_timeout)); if (!rc) { - dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n", rc); + dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n", + SESSION_MSG_INDEX(cmd)); msm_comm_recover_from_session_error(inst); rc = -EIO; } else { @@ -433,7 +434,7 @@ void msm_vidc_queue_v4l2_event(struct msm_vidc_inst *inst, int event_type) static void msm_comm_generate_session_error(struct msm_vidc_inst *inst) { if (!inst) { - dprintk(VIDC_ERR, "%s: invalid input parameters", __func__); + dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__); return; } mutex_lock(&inst->lock); @@ -455,7 +456,7 @@ static void handle_session_init_done(enum command_response cmd, void *data) response->data; inst = (struct msm_vidc_inst *)response->session_id; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return; } @@ -475,7 +476,7 @@ static void handle_session_init_done(enum command_response cmd, void *data) session_init_done->alloc_mode_out; } else { dprintk(VIDC_ERR, - "Session init response from FW : 0x%x", + "Session init response from FW : 0x%x\n", response->status); msm_comm_generate_session_error(inst); } @@ -524,6 +525,14 @@ static void handle_event_change(enum command_response cmd, void *data) event_notify->packet_buffer, event_notify->exra_data_buffer); + if (inst->state == MSM_VIDC_CORE_INVALID || + inst->core->state == + VIDC_CORE_INVALID) { + dprintk(VIDC_DBG, + "Event release buf ref received in invalid state - discard\n"); + return; + } + /* * Get the buffer_info entry for the * device address. @@ -635,7 +644,7 @@ static void handle_load_resource_done(enum command_response cmd, void *data) inst = (struct msm_vidc_inst *)response->session_id; if (response->status) { dprintk(VIDC_ERR, - "Load resource response from FW : 0x%x", + "Load resource response from FW : 0x%x\n", response->status); msm_comm_generate_session_error(inst); } @@ -710,7 +719,7 @@ void validate_output_buffers(struct msm_vidc_inst *inst) } if (buffers_owned_by_driver != output_buf->buffer_count_actual) dprintk(VIDC_ERR, - "OUTPUT Buffer count mismatch %d of %d", + "OUTPUT Buffer count mismatch %d of %d\n", buffers_owned_by_driver, output_buf->buffer_count_actual); @@ -779,12 +788,12 @@ static void handle_sys_error(enum command_response cmd, void *data) hdev = inst->core->device; if (hdev && inst->session) { dprintk(VIDC_DBG, - "cleaning up inst: 0x%p", inst); + "cleaning up inst: 0x%p\n", inst); rc = call_hfi_op(hdev, session_clean, (void *) inst->session); if (rc) dprintk(VIDC_ERR, - "Sess clean failed :%p", + "Sess clean failed :%p\n", inst); } inst->session = NULL; @@ -795,7 +804,7 @@ static void handle_sys_error(enum command_response cmd, void *data) mutex_unlock(&core->sync_lock); } else { dprintk(VIDC_ERR, - "Got SYS_ERR but unable to identify core"); + "Got SYS_ERR but unable to identify core\n"); } } else { dprintk(VIDC_ERR, @@ -834,7 +843,7 @@ static void handle_sys_watchdog_timeout(enum command_response cmd, void *data) (void *) inst->session); if (rc) dprintk(VIDC_ERR, - "Sess clean failed :%p", + "Sess clean failed :%p\n", inst); } @@ -860,7 +869,7 @@ static void handle_session_close(enum command_response cmd, void *data) hdev = inst->core->device; mutex_lock(&inst->lock); if (inst->session) { - dprintk(VIDC_DBG, "cleaning up inst: 0x%p", inst); + dprintk(VIDC_DBG, "cleaning up inst: 0x%p\n", inst); call_hfi_op(hdev, session_clean, (void *) inst->session); } @@ -1057,7 +1066,7 @@ static void handle_dynamic_buffer(struct msm_vidc_inst *inst, } if (flags & HAL_BUFFERFLAG_READONLY) { dprintk(VIDC_DBG, - "_F_B_D_ fd[0] = %d -> Reference with f/w, addr: 0x%x", + "FBD fd[0] = %d -> Reference with f/w, addr: 0x%x\n", binfo->fd[0], device_addr); } else { dprintk(VIDC_DBG, @@ -1293,7 +1302,7 @@ static void handle_seq_hdr_done(enum command_response cmd, void *data) (u32)fill_buf_done->packet_buffer1); if (!vb) { dprintk(VIDC_ERR, - "Failed to find video buffer for seq_hdr_done"); + "Failed to find video buffer for seq_hdr_done\n"); return; } @@ -1444,7 +1453,7 @@ static int msm_comm_unset_ocmem(struct msm_vidc_core *core) struct hfi_device *hdev; if (!core || !core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } hdev = core->device; @@ -1467,7 +1476,8 @@ static int msm_comm_unset_ocmem(struct msm_vidc_core *core) &core->completions[SYS_MSG_INDEX(RELEASE_RESOURCE_DONE)], msecs_to_jiffies(msm_vidc_hw_rsp_timeout)); if (!rc) { - dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n", rc); + dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n", + SYS_MSG_INDEX(RELEASE_RESOURCE_DONE)); rc = -EIO; } release_ocmem_failed: @@ -1489,7 +1499,8 @@ static int msm_comm_init_core_done(struct msm_vidc_inst *inst) &core->completions[SYS_MSG_INDEX(SYS_INIT_DONE)], msecs_to_jiffies(msm_vidc_hw_rsp_timeout)); if (!rc) { - dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n", rc); + dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n", + SYS_MSG_INDEX(SYS_INIT_DONE)); rc = -EIO; goto exit; } else { @@ -1569,7 +1580,7 @@ static int msm_vidc_deinit_core(struct msm_vidc_inst *inst) struct hfi_device *hdev; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } @@ -1638,7 +1649,7 @@ static enum hal_domain get_hal_domain(int session_type) enum hal_video_codec get_hal_codec_type(int fourcc) { enum hal_video_codec codec; - dprintk(VIDC_DBG, "codec is 0x%x", fourcc); + dprintk(VIDC_DBG, "codec is 0x%x\n", fourcc); switch (fourcc) { case V4L2_PIX_FMT_H264: case V4L2_PIX_FMT_H264_NO_SC: @@ -1693,7 +1704,7 @@ static int msm_comm_session_init(int flipped_state, struct hfi_device *hdev; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } hdev = inst->core->device; @@ -1759,13 +1770,13 @@ static int msm_vidc_load_resources(int flipped_state, int height, width; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } if (inst->state == MSM_VIDC_CORE_INVALID || inst->core->state == VIDC_CORE_INVALID) { dprintk(VIDC_ERR, - "Core is in bad state can't do load res"); + "Core is in bad state can't do load res\n"); return -EINVAL; } @@ -1831,13 +1842,13 @@ static int msm_vidc_start(int flipped_state, struct msm_vidc_inst *inst) struct hfi_device *hdev; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } if (inst->state == MSM_VIDC_CORE_INVALID || inst->core->state == VIDC_CORE_INVALID) { dprintk(VIDC_ERR, - "Core is in bad state can't do start"); + "Core is in bad state can't do start\n"); return -EINVAL; } @@ -1868,7 +1879,7 @@ static int msm_vidc_stop(int flipped_state, struct msm_vidc_inst *inst) struct hfi_device *hdev; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } hdev = inst->core->device; @@ -1898,7 +1909,7 @@ static int msm_vidc_release_res(int flipped_state, struct msm_vidc_inst *inst) struct hfi_device *hdev; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } hdev = inst->core->device; @@ -1931,7 +1942,7 @@ static int msm_comm_session_close(int flipped_state, struct hfi_device *hdev; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid params", __func__); + dprintk(VIDC_ERR, "%s invalid params\n", __func__); return -EINVAL; } hdev = inst->core->device; @@ -2063,16 +2074,16 @@ static int set_output_buffers(struct msm_vidc_inst *inst, buffer_info.extradata_addr = handle->device_addr + output_buf->buffer_size; buffer_info.extradata_size = extradata_buf->buffer_size; - dprintk(VIDC_DBG, "Output buffer address: %x", + dprintk(VIDC_DBG, "Output buffer address: %x\n", buffer_info.align_device_addr); - dprintk(VIDC_DBG, "Output extradata address: %x", + dprintk(VIDC_DBG, "Output extradata address: %x\n", buffer_info.extradata_addr); rc = call_hfi_op(hdev, session_set_buffers, (void *) inst->session, &buffer_info); mutex_unlock(&inst->lock); if (rc) { dprintk(VIDC_ERR, - "%s : session_set_buffers failed", + "%s : session_set_buffers failed\n", __func__); goto fail_set_buffers; } @@ -2149,13 +2160,13 @@ static int set_scratch_buffers(struct msm_vidc_inst *inst, binfo->buffer_type = buffer_type; buffer_info.num_buffers = 1; buffer_info.align_device_addr = handle->device_addr; - dprintk(VIDC_DBG, "Scratch buffer address: %x", + dprintk(VIDC_DBG, "Scratch buffer address: %x\n", buffer_info.align_device_addr); rc = call_hfi_op(hdev, session_set_buffers, (void *) inst->session, &buffer_info); if (rc) { dprintk(VIDC_ERR, - "vidc_hal_session_set_buffers failed"); + "vidc_hal_session_set_buffers failed\n"); goto fail_set_buffers; } mutex_lock(&inst->lock); @@ -2236,13 +2247,13 @@ static int set_persist_buffers(struct msm_vidc_inst *inst, binfo->buffer_type = buffer_type; buffer_info.num_buffers = 1; buffer_info.align_device_addr = handle->device_addr; - dprintk(VIDC_DBG, "Persist buffer address: %x", + dprintk(VIDC_DBG, "Persist buffer address: %x\n", buffer_info.align_device_addr); rc = call_hfi_op(hdev, session_set_buffers, (void *) inst->session, &buffer_info); if (rc) { dprintk(VIDC_ERR, - "vidc_hal_session_set_buffers failed"); + "vidc_hal_session_set_buffers failed\n"); goto fail_set_buffers; } mutex_lock(&inst->lock); @@ -2282,7 +2293,7 @@ int msm_comm_try_state(struct msm_vidc_inst *inst, int state) if (inst->state == MSM_VIDC_CORE_INVALID || core->state == VIDC_CORE_INVALID) { dprintk(VIDC_ERR, - "Core is in bad state can't change the state"); + "Core is in bad state can't change the state\n"); rc = -EINVAL; goto exit; } @@ -2396,7 +2407,7 @@ int msm_comm_qbuf(struct vb2_buffer *vb) } hdev = core->device; if (!hdev) { - dprintk(VIDC_ERR, "Invalid input: %p", hdev); + dprintk(VIDC_ERR, "Invalid input: %p\n", hdev); return -EINVAL; } @@ -2448,7 +2459,13 @@ int msm_comm_qbuf(struct vb2_buffer *vb) dprintk(VIDC_DBG, "Received EOS on output capability\n"); } - + if (vb->v4l2_buf.flags & + V4L2_MSM_BUF_FLAG_YUV_601_709_CLAMP) { + frame_data.flags |= + HAL_BUFFERFLAG_YUV_601_709_CSC_CLAMP; + dprintk(VIDC_DBG, + "Received buff with 601to709 clamp\n"); + } if (vb->v4l2_buf.flags & V4L2_QCOM_BUF_FLAG_CODECCONFIG) { frame_data.flags |= HAL_BUFFERFLAG_CODECCONFIG; @@ -2564,7 +2581,7 @@ int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst) HAL_PARAM_GET_BUFFER_REQUIREMENTS, &hprop); if (rc) { - dprintk(VIDC_ERR, "%s Error rc:%d", __func__, rc); + dprintk(VIDC_ERR, "%s Error rc:%d\n", __func__, rc); return rc; } buf_req = hprop.buf_req; @@ -2591,7 +2608,7 @@ int msm_comm_try_get_prop(struct msm_vidc_inst *inst, enum hal_property ptype, struct getprop_buf *buf; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } @@ -2641,7 +2658,8 @@ int msm_comm_try_get_prop(struct msm_vidc_inst *inst, enum hal_property ptype, msecs_to_jiffies(msm_vidc_hw_rsp_timeout)); if (!rc) { dprintk(VIDC_ERR, - "Wait interrupted or timeout: %d\n", rc); + "Wait interrupted or timeout: %d\n", + SESSION_MSG_INDEX(SESSION_PROPERTY_INFO)); inst->state = MSM_VIDC_CORE_INVALID; msm_comm_recover_from_session_error(inst); rc = -EIO; @@ -2707,7 +2725,7 @@ int msm_comm_release_output_buffers(struct msm_vidc_inst *inst) (void *)inst->session, &buffer_info); if (rc) dprintk(VIDC_WARN, - "Rel output buf fail:0x%x, %d", + "Rel output buf fail:0x%x, %d\n", buffer_info.align_device_addr, buffer_info.buffer_size); } @@ -2767,7 +2785,7 @@ int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst) (void *)inst->session, &buffer_info); if (rc) dprintk(VIDC_WARN, - "Rel scrtch buf fail:0x%x, %d", + "Rel scrtch buf fail:0x%x, %d\n", buffer_info.align_device_addr, buffer_info.buffer_size); mutex_unlock(&inst->lock); @@ -2838,7 +2856,7 @@ int msm_comm_release_persist_buffers(struct msm_vidc_inst *inst) (void *)inst->session, &buffer_info); if (rc) dprintk(VIDC_WARN, - "Rel prst buf fail:0x%x, %d", + "Rel prst buf fail:0x%x, %d\n", buffer_info.align_device_addr, buffer_info.buffer_size); mutex_unlock(&inst->lock); @@ -2875,7 +2893,7 @@ int msm_comm_try_set_prop(struct msm_vidc_inst *inst, } if (!inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } hdev = inst->core->device; @@ -2899,7 +2917,7 @@ int msm_comm_set_output_buffers(struct msm_vidc_inst *inst) { int rc = 0; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } @@ -2919,7 +2937,7 @@ int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst) { int rc = 0; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } @@ -2948,7 +2966,7 @@ int msm_comm_set_persist_buffers(struct msm_vidc_inst *inst) { int rc = 0; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } @@ -3096,7 +3114,7 @@ int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags) } hdev = core->device; if (!hdev) { - dprintk(VIDC_ERR, "Invalid device pointer = %p", hdev); + dprintk(VIDC_ERR, "Invalid device pointer = %p\n", hdev); return -EINVAL; } @@ -3107,6 +3125,9 @@ int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags) dprintk(VIDC_INFO, "Input only flush not supported\n"); return 0; } + mutex_lock(&inst->sync_lock); + msm_comm_flush_dynamic_buffers(inst); + mutex_unlock(&inst->sync_lock); if (inst->state == MSM_VIDC_CORE_INVALID || core->state == VIDC_CORE_INVALID) { dprintk(VIDC_ERR, @@ -3117,7 +3138,6 @@ int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags) } mutex_lock(&inst->sync_lock); - msm_comm_flush_dynamic_buffers(inst); if (inst->in_reconfig && !ip_flush && op_flush) { if (!list_empty(&inst->pendingq)) { /*Execution can never reach here since port reconfig @@ -3339,7 +3359,7 @@ int msm_vidc_check_scaling_supported(struct msm_vidc_inst *inst) if (!inst->capability.scale_x.min || !inst->capability.scale_x.max || !inst->capability.scale_y.min || !inst->capability.scale_y.max) { - dprintk(VIDC_ERR, "%s : Invalid scaling ratios", + dprintk(VIDC_ERR, "%s : Invalid scaling ratios\n", __func__); return -ENOTSUPP; } @@ -3355,8 +3375,8 @@ int msm_vidc_check_scaling_supported(struct msm_vidc_inst *inst) if (!input_height || !input_width || !output_height || !output_width) { dprintk(VIDC_ERR, - "Invalid : Input Height = %d Width = %d" - " Output Height = %d Width = %d", + "Invalid : Input height = %d width = %d" + " output height = %d width = %d\n", input_height, input_width, output_height, output_width); return -ENOTSUPP; @@ -3365,14 +3385,14 @@ int msm_vidc_check_scaling_supported(struct msm_vidc_inst *inst) if (input_height > output_height) { if (input_height/output_height > x_min) { dprintk(VIDC_ERR, - "Unsupported Height Downscale ratio %d Vs %d", + "Unsupported height downscale ratio %d vs %d\n", input_height/output_height, x_min); return -ENOTSUPP; } } else { if (input_height/output_height > x_max) { dprintk(VIDC_ERR, - "Unsupported Height Upscale ratio %d Vs %d", + "Unsupported height upscale ratio %d vs %d\n", input_height/output_height, x_max); return -ENOTSUPP; } @@ -3380,14 +3400,14 @@ int msm_vidc_check_scaling_supported(struct msm_vidc_inst *inst) if (input_width > output_width) { if (input_width/output_width > y_min) { dprintk(VIDC_ERR, - "Unsupported Width Downscale ratio %d Vs %d", + "Unsupported width downscale ratio %d vs %d\n", input_width/output_width, y_min); return -ENOTSUPP; } } else { if (input_width/output_width > y_max) { dprintk(VIDC_ERR, - "Unsupported Width Upscale ratio %d Vs %d", + "Unsupported width upscale ratio %d vs %d\n", input_width/output_width, y_max); return -ENOTSUPP; } @@ -3418,7 +3438,7 @@ int msm_vidc_check_session_supported(struct msm_vidc_inst *inst) * inst->prop.width[CAPTURE_PORT] > capability->width.max * capability->height.max)) { dprintk(VIDC_ERR, - "Unsupported WxH = (%u)x(%u), Max supported is - (%u)x(%u)", + "Unsupported WxH = (%u)x(%u), max supported is - (%u)x(%u)\n", inst->prop.width[CAPTURE_PORT], inst->prop.height[CAPTURE_PORT], capability->width.max, capability->height.max); @@ -3441,7 +3461,7 @@ static void msm_comm_generate_sys_error(struct msm_vidc_inst *inst) enum command_response cmd = SYS_ERROR; struct msm_vidc_cb_cmd_done response = {0}; if (!inst || !inst->core) { - dprintk(VIDC_ERR, "%s: invalid input parameters", __func__); + dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__); return; } core = inst->core; @@ -3455,12 +3475,12 @@ int msm_comm_recover_from_session_error(struct msm_vidc_inst *inst) int rc = 0; if (!inst || !inst->core || !inst->core->device) { - dprintk(VIDC_ERR, "%s: invalid input parameters", __func__); + dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__); return -EINVAL; } if (!inst->session || inst->state < MSM_VIDC_OPEN_DONE) { dprintk(VIDC_WARN, - "No corresponding FW session. No need to send Abort"); + "No corresponding FW session. No need to send Abort\n"); return rc; } hdev = inst->core->device; @@ -3481,7 +3501,7 @@ int msm_comm_recover_from_session_error(struct msm_vidc_inst *inst) msecs_to_jiffies(msm_vidc_hw_rsp_timeout)); if (!rc) { dprintk(VIDC_ERR, "%s: Wait interrupted or timeout: %d\n", - __func__, rc); + __func__, SESSION_MSG_INDEX(SESSION_ABORT_DONE)); msm_comm_generate_sys_error(inst); } else change_inst_state(inst, MSM_VIDC_CLOSE_DONE); diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c index 575111340261..8c4d30aa04b1 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c @@ -20,7 +20,6 @@ int msm_vidc_debug_out = VIDC_OUT_PRINTK; int msm_fw_debug = 0x18; int msm_fw_debug_mode = 0x1; int msm_fw_low_power_mode = 0x1; -int msm_vp8_low_tier = 0x1; int msm_vidc_hw_rsp_timeout = 1000; struct debug_buffer { @@ -155,11 +154,6 @@ struct dentry *msm_vidc_debugfs_init_drv(void) dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); goto failed_create_dir; } - if (!debugfs_create_u32("vp8_low_tier", S_IRUGO | S_IWUSR, - dir, &msm_vp8_low_tier)) { - dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); - goto failed_create_dir; - } if (!debugfs_create_u32("debug_output", S_IRUGO | S_IWUSR, dir, &msm_vidc_debug_out)) { dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.h b/drivers/media/platform/msm/vidc/msm_vidc_debug.h index de55166ca41c..74139c78ccfc 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_debug.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.h @@ -17,7 +17,7 @@ #include <linux/delay.h> #include "msm_vidc_internal.h" -#define VIDC_DBG_TAG "msm_vidc: %d: " +#define VIDC_DBG_TAG "msm_vidc: %4s: " /* To enable messages OR these values and * echo the result to debugfs file. @@ -52,23 +52,58 @@ extern int msm_vidc_debug_out; extern int msm_fw_debug; extern int msm_fw_debug_mode; extern int msm_fw_low_power_mode; -extern int msm_vp8_low_tier; extern int msm_vidc_hw_rsp_timeout; +#define VIDC_MSG_PRIO2STRING(__level) ({ \ + char *__str; \ + \ + switch (__level) { \ + case VIDC_ERR: \ + __str = "err"; \ + break; \ + case VIDC_WARN: \ + __str = "warn"; \ + break; \ + case VIDC_INFO: \ + __str = "info"; \ + break; \ + case VIDC_DBG: \ + __str = "dbg"; \ + break; \ + case VIDC_PROF: \ + __str = "prof"; \ + break; \ + case VIDC_PKT: \ + __str = "pkt"; \ + break; \ + case VIDC_FW: \ + __str = "fw"; \ + break; \ + default: \ + __str = "????"; \ + break; \ + } \ + \ + __str; \ + }) + #define dprintk(__level, __fmt, arg...) \ do { \ if (msm_vidc_debug & __level) { \ if (msm_vidc_debug_out == VIDC_OUT_PRINTK) { \ - printk(KERN_DEBUG VIDC_DBG_TAG \ - __fmt, __level, ## arg); \ + pr_info(VIDC_DBG_TAG __fmt, \ + VIDC_MSG_PRIO2STRING(__level), \ + ## arg); \ } else if (msm_vidc_debug_out == VIDC_OUT_FTRACE) { \ - trace_printk(KERN_DEBUG VIDC_DBG_TAG \ - __fmt, __level, ## arg); \ + trace_printk(KERN_DEBUG VIDC_DBG_TAG __fmt, \ + VIDC_MSG_PRIO2STRING(__level), \ + ## arg); \ } \ } \ } while (0) + struct dentry *msm_vidc_debugfs_init_drv(void); struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core, struct dentry *parent); @@ -116,7 +151,7 @@ static inline void show_stats(struct msm_vidc_inst *i) i->debug.pdata[x].name, i->debug.pdata[x].cumulative / i->debug.samples); - dprintk(VIDC_PROF, "%s Samples: %d", + dprintk(VIDC_PROF, "%s Samples: %d\n", i->debug.pdata[x].name, i->debug.samples); } diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c index 2eea58d10dd6..6687628b7d5b 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c @@ -494,7 +494,7 @@ static int msm_vidc_load_iommu_groups(struct msm_vidc_platform_resources *res) domain_idx, iommu_map->name); - if (!of_get_property(ctx_node, "qti,virtual-addr-pool", + if (!of_get_property(ctx_node, "qcom,virtual-addr-pool", &array_size)) { dprintk(VIDC_ERR, "Could not find any addr pool for group : %s\n", @@ -511,7 +511,7 @@ static int msm_vidc_load_iommu_groups(struct msm_vidc_platform_resources *res) domain_idx); rc = of_property_read_u32_array(ctx_node, - "qti,virtual-addr-pool", + "qcom,virtual-addr-pool", (u32 *)iommu_map->addr_range, iommu_map->npartitions * 2); if (rc) { @@ -523,7 +523,7 @@ static int msm_vidc_load_iommu_groups(struct msm_vidc_platform_resources *res) } iommu_map->is_secure = - of_property_read_bool(ctx_node, "qti,secure-domain"); + of_property_read_bool(ctx_node, "qcom,secure-domain"); dprintk(VIDC_DBG, "domain %s : secure = %d\n", diff --git a/drivers/media/platform/msm/vidc/q6_hfi.c b/drivers/media/platform/msm/vidc/q6_hfi.c index e53e48ca53f2..7f029c696748 100644 --- a/drivers/media/platform/msm/vidc/q6_hfi.c +++ b/drivers/media/platform/msm/vidc/q6_hfi.c @@ -14,7 +14,7 @@ #include <linux/slab.h> #include <linux/iommu.h> #include <linux/msm_iommu_domains.h> -#include <mach/qdsp6v2/apr.h> +#include <linux/qdsp6v2/apr.h> #include <mach/subsystem_restart.h> #include "hfi_packetization.h" #include "msm_vidc_debug.h" @@ -33,7 +33,7 @@ static int write_queue(void *info, u8 *packet) u32 *write_ptr; if (!info || !packet) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } @@ -42,7 +42,7 @@ static int write_queue(void *info, u8 *packet) packet_size_in_words = (*(u32 *)packet) >> 2; if (packet_size_in_words == 0) { - dprintk(VIDC_ERR, "Zero packet size"); + dprintk(VIDC_ERR, "Zero packet size\n"); return -ENODATA; } @@ -52,7 +52,7 @@ static int write_queue(void *info, u8 *packet) (qinfo->q_size - (qinfo->write_idx - read_idx)) : (read_idx - qinfo->write_idx); if (empty_space <= packet_size_in_words) { - dprintk(VIDC_ERR, "Insufficient size (%d) to write (%d)", + dprintk(VIDC_ERR, "Insufficient size (%d) to write (%d)\n", empty_space, packet_size_in_words); return -ENOTEMPTY; } @@ -80,7 +80,7 @@ static int read_queue(void *info, u8 *packet) struct q6_iface_q_info *qinfo; if (!info || !packet) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } @@ -92,7 +92,7 @@ static int read_queue(void *info, u8 *packet) read_ptr = (u32 *)(qinfo->buffer + (qinfo->read_idx << 2)); packet_size_in_words = (*read_ptr) >> 2; if (packet_size_in_words == 0) { - dprintk(VIDC_ERR, "Zero packet size"); + dprintk(VIDC_ERR, "Zero packet size\n"); return -ENODATA; } @@ -121,13 +121,13 @@ static int q6_hfi_iface_eventq_write(struct q6_hfi_device *device, void *pkt) unsigned long flags = 0; if (!device || !pkt) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } q_info = &device->event_queue; if (!q_info->buffer) { - dprintk(VIDC_ERR, "cannot write to shared Q"); + dprintk(VIDC_ERR, "cannot write to shared Q\n"); rc = -ENODATA; goto err_q_write; } @@ -135,7 +135,7 @@ static int q6_hfi_iface_eventq_write(struct q6_hfi_device *device, void *pkt) spin_lock_irqsave(&q_info->lock, flags); rc = write_queue(q_info, (u8 *)pkt); if (rc) - dprintk(VIDC_ERR, "q6_hfi_iface_eventq_write: queue_full"); + dprintk(VIDC_ERR, "q6_hfi_iface_eventq_write: queue_full\n"); spin_unlock_irqrestore(&q_info->lock, flags); err_q_write: @@ -149,14 +149,14 @@ static int q6_hfi_iface_eventq_read(struct q6_hfi_device *device, void *pkt) unsigned long flags = 0; if (!pkt) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } q_info = &device->event_queue; if (!q_info->buffer) { - dprintk(VIDC_ERR, "cannot read from shared Q"); + dprintk(VIDC_ERR, "cannot read from shared Q\n"); rc = -ENODATA; goto read_error; } @@ -164,7 +164,7 @@ static int q6_hfi_iface_eventq_read(struct q6_hfi_device *device, void *pkt) spin_lock_irqsave(&q_info->lock, flags); rc = read_queue(q_info, (u8 *)pkt); if (rc) { - dprintk(VIDC_INFO, "q6_hfi_iface_eventq_read:queue_empty"); + dprintk(VIDC_INFO, "q6_hfi_iface_eventq_read:queue_empty\n"); rc = -ENODATA; } spin_unlock_irqrestore(&q_info->lock, flags); @@ -191,7 +191,7 @@ static void q6_hfi_core_work_handler(struct work_struct *work) } while (!rc); if (rc != -ENODATA) - dprintk(VIDC_ERR, "Failed to read from event queue"); + dprintk(VIDC_ERR, "Failed to read from event queue\n"); } static int q6_hfi_register_iommu_domains(struct q6_hfi_device *device) @@ -202,7 +202,7 @@ static int q6_hfi_register_iommu_domains(struct q6_hfi_device *device) struct iommu_info *iommu_map; if (!device || !device->res) { - dprintk(VIDC_ERR, "Invalid parameter: %p", device); + dprintk(VIDC_ERR, "Invalid parameter: %p\n", device); return -EINVAL; } @@ -220,7 +220,7 @@ static int q6_hfi_register_iommu_domains(struct q6_hfi_device *device) domain = iommu_group_get_iommudata(iommu_map->group); if (IS_ERR_OR_NULL(domain)) { dprintk(VIDC_ERR, - "Failed to get domain data for group %p", + "Failed to get domain data for group %p\n", iommu_map->group); rc = -EINVAL; goto fail_group; @@ -228,7 +228,7 @@ static int q6_hfi_register_iommu_domains(struct q6_hfi_device *device) iommu_map->domain = msm_find_domain_no(domain); if (iommu_map->domain < 0) { dprintk(VIDC_ERR, - "Failed to get domain index for domain %p", + "Failed to get domain index for domain %p\n", domain); rc = -EINVAL; goto fail_group; @@ -254,7 +254,7 @@ static void q6_hfi_deregister_iommu_domains(struct q6_hfi_device *device) int i = 0; if (!device || !device->res) { - dprintk(VIDC_ERR, "Invalid parameter: %p", device); + dprintk(VIDC_ERR, "Invalid parameter: %p\n", device); return; } @@ -274,14 +274,18 @@ static int q6_hfi_init_resources(struct q6_hfi_device *device, int rc = 0; if (!device || !res) { - dprintk(VIDC_ERR, "Invalid device or resources"); + dprintk(VIDC_ERR, "Invalid device or resources\n"); return -EINVAL; } device->res = res; rc = q6_hfi_register_iommu_domains(device); - if (rc) - dprintk(VIDC_ERR, "Failed to register iommu domains: %d\n", rc); + if (rc) { + if (rc != -EPROBE_DEFER) { + dprintk(VIDC_ERR, + "Failed to register iommu domains: %d\n", rc); + } + } return rc; } @@ -297,14 +301,14 @@ static void *q6_hfi_add_device(u32 device_id, struct q6_hfi_device *hdevice = NULL; if (!callback) { - dprintk(VIDC_ERR, "Invalid Paramters"); + dprintk(VIDC_ERR, "Invalid Paramters\n"); return NULL; } hdevice = (struct q6_hfi_device *) kzalloc(sizeof(struct q6_hfi_device), GFP_KERNEL); if (!hdevice) { - dprintk(VIDC_ERR, "failed to allocate new device"); + dprintk(VIDC_ERR, "failed to allocate new device\n"); goto err_alloc; } @@ -356,14 +360,15 @@ static void *q6_hfi_get_device(u32 device_id, rc = q6_hfi_init_resources(device, res); if (rc) { - dprintk(VIDC_ERR, "Failed to init resources: %d\n", rc); + if (rc != -EPROBE_DEFER) + dprintk(VIDC_ERR, "Failed to init resources: %d\n", rc); goto err_fail_init_res; } return device; err_fail_init_res: q6_hfi_delete_device(device); - return NULL; + return ERR_PTR(rc); } void q6_hfi_delete_device(void *device) @@ -411,15 +416,15 @@ static int q6_hfi_apr_callback(struct apr_client_data *data, void *priv) int rc = 0; if (!data || !device) { - dprintk(VIDC_ERR, "%s - Invalid arguments", __func__); + dprintk(VIDC_ERR, "%s - Invalid arguments\n", __func__); return -EINVAL; } - dprintk(VIDC_DBG, "%s opcode = %u payload size = %u", __func__, + dprintk(VIDC_DBG, "%s opcode = %u payload size = %u\n", __func__, data->opcode, data->payload_size); if (data->opcode == RESET_EVENTS) { - dprintk(VIDC_ERR, "%s Received subsystem reset event: %d", + dprintk(VIDC_ERR, "%s Received subsystem reset event: %d\n", __func__, data->reset_event); pkt.packet_type = HFI_MSG_EVENT_NOTIFY; pkt.size = sizeof(pkt); @@ -430,13 +435,13 @@ static int q6_hfi_apr_callback(struct apr_client_data *data, void *priv) } else if (data->payload_size > 0) { payload = data->payload; } else { - dprintk(VIDC_ERR, "%s - Invalid payload size", __func__); + dprintk(VIDC_ERR, "%s - Invalid payload size\n", __func__); return -EINVAL; } rc = q6_hfi_iface_eventq_write(device, payload); if (rc) { - dprintk(VIDC_ERR, "%s failed to write to event queue", + dprintk(VIDC_ERR, "%s failed to write to event queue\n", __func__); return rc; } @@ -458,14 +463,14 @@ static int q6_init_event_queue(struct q6_hfi_device *dev) struct q6_iface_q_info *iface_q; if (!dev) { - dprintk(VIDC_ERR, "Invalid device"); + dprintk(VIDC_ERR, "Invalid device\n"); return -EINVAL; } iface_q = &dev->event_queue; iface_q->buffer = kzalloc(Q6_IFACEQ_QUEUE_SIZE, GFP_KERNEL); if (!iface_q->buffer) { - dprintk(VIDC_ERR, "iface_q alloc failed"); + dprintk(VIDC_ERR, "iface_q alloc failed\n"); q6_release_event_queue(dev); return -ENOMEM; } else { @@ -494,11 +499,11 @@ static int q6_hfi_core_init(void *device) if (!dev->event_queue.buffer) { rc = q6_init_event_queue(dev); if (rc) { - dprintk(VIDC_ERR, "q6_init_event_queue failed"); + dprintk(VIDC_ERR, "q6_init_event_queue failed\n"); goto err_core_init; } } else { - dprintk(VIDC_ERR, "queue buffer exists"); + dprintk(VIDC_ERR, "queue buffer exists\n"); rc = -EEXIST; goto err_core_init; } @@ -507,13 +512,13 @@ static int q6_hfi_core_init(void *device) rc = create_pkt_cmd_sys_init(&apr.pkt, HFI_VIDEO_ARCH_OX); if (rc) { - dprintk(VIDC_ERR, "Failed to create sys init pkt"); + dprintk(VIDC_ERR, "Failed to create sys init pkt\n"); goto err_core_init; } rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); if (rc != apr.hdr.pkt_size) { - dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", __func__, rc); rc = -EBADE; } else @@ -562,7 +567,7 @@ static void *q6_hfi_session_init(void *device, u32 session_id, if (create_pkt_cmd_sys_session_init(&apr.pkt, (u32)new_session, session_type, codec_type)) { - dprintk(VIDC_ERR, "session_init: failed to create packet"); + dprintk(VIDC_ERR, "session_init: failed to create packet\n"); goto err_session_init; } /* @@ -577,7 +582,7 @@ static void *q6_hfi_session_init(void *device, u32 session_id, rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); if (rc != apr.hdr.pkt_size) { - dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", __func__, rc); /* Delete the session id as the send pkt is not successful */ mutex_lock(&dev->session_lock); @@ -612,13 +617,13 @@ static int q6_hal_send_session_cmd(void *sess, rc = create_pkt_cmd_session_cmd(&apr.pkt, pkt_type, (u32)session); if (rc) { - dprintk(VIDC_ERR, "send session cmd: create pkt failed"); + dprintk(VIDC_ERR, "send session cmd: create pkt failed\n"); goto err_create_pkt; } rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); if (rc != apr.hdr.pkt_size) { - dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", __func__, rc); rc = -EBADE; } else @@ -643,11 +648,11 @@ static int q6_hfi_session_clean(void *session) { struct hal_session *sess_close; if (!session) { - dprintk(VIDC_ERR, "Invalid Params %s", __func__); + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); return -EINVAL; } sess_close = session; - dprintk(VIDC_DBG, "deleted the session: 0x%x", + dprintk(VIDC_DBG, "deleted the session: 0x%x\n", sess_close->session_id); mutex_lock(&((struct q6_hfi_device *) sess_close->device)->session_lock); @@ -682,14 +687,14 @@ static int q6_hfi_session_set_buffers(void *sess, rc = create_pkt_cmd_session_set_buffers(&apr->pkt, (u32)session, buffer_info); if (rc) { - dprintk(VIDC_ERR, "set buffers: failed to create packet"); + dprintk(VIDC_ERR, "set buffers: failed to create packet\n"); goto err_create_pkt; } - dprintk(VIDC_INFO, "set buffers: 0x%x", buffer_info->buffer_type); + dprintk(VIDC_INFO, "set buffers: 0x%x\n", buffer_info->buffer_type); rc = apr_send_pkt(dev->apr, (uint32_t *)apr); if (rc != apr->hdr.pkt_size) { - dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", __func__, rc); rc = -EBADE; } else @@ -724,15 +729,15 @@ static int q6_hfi_session_release_buffers(void *sess, rc = create_pkt_cmd_session_release_buffers(&apr->pkt, (u32)session, buffer_info); if (rc) { - dprintk(VIDC_ERR, "release buffers: failed to create packet"); + dprintk(VIDC_ERR, "release buffers: failed to create packet\n"); goto err_create_pkt; } - dprintk(VIDC_INFO, "Release buffers: 0x%x", buffer_info->buffer_type); + dprintk(VIDC_INFO, "Release buffers: 0x%x\n", buffer_info->buffer_type); rc = apr_send_pkt(dev->apr, (uint32_t *)apr); if (rc != apr->hdr.pkt_size) { - dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", __func__, rc); rc = -EBADE; } else @@ -785,7 +790,7 @@ static int q6_hfi_session_etb(void *sess, struct q6_hfi_device *dev; if (!session || !input_frame || !session->device) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } @@ -799,15 +804,15 @@ static int q6_hfi_session_etb(void *sess, (u32)session, input_frame); if (rc) { dprintk(VIDC_ERR, - "Session etb decoder: failed to create pkt"); + "Session etb decoder: failed to create pkt\n"); goto err_create_pkt; } - dprintk(VIDC_DBG, "Q DECODER INPUT BUFFER"); - dprintk(VIDC_DBG, "addr = 0x%x ts = %lld", + dprintk(VIDC_DBG, "Q DECODER INPUT BUFFER\n"); + dprintk(VIDC_DBG, "addr = 0x%x ts = %lld\n", input_frame->device_addr, input_frame->timestamp); rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); if (rc != apr.hdr.pkt_size) { - dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", __func__, rc); rc = -EBADE; } else @@ -821,13 +826,13 @@ static int q6_hfi_session_etb(void *sess, (u32)session, input_frame); if (rc) { dprintk(VIDC_ERR, - "Session etb encoder: failed to create pkt"); + "Session etb encoder: failed to create pkt\n"); goto err_create_pkt; } - dprintk(VIDC_DBG, "Q ENCODER INPUT BUFFER"); + dprintk(VIDC_DBG, "Q ENCODER INPUT BUFFER\n"); rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); if (rc != apr.hdr.pkt_size) { - dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", __func__, rc); rc = -EBADE; } else @@ -846,7 +851,7 @@ static int q6_hfi_session_ftb(void *sess, struct q6_hfi_device *dev; if (!session || !output_frame || !session->device) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } dev = session->device; @@ -855,14 +860,14 @@ static int q6_hfi_session_ftb(void *sess, rc = create_pkt_cmd_session_ftb(&apr.pkt, (u32)session, output_frame); if (rc) { - dprintk(VIDC_ERR, "Session ftb: failed to create pkt"); + dprintk(VIDC_ERR, "Session ftb: failed to create pkt\n"); goto err_create_pkt; } - dprintk(VIDC_INFO, "Q OUTPUT BUFFER"); + dprintk(VIDC_INFO, "Q OUTPUT BUFFER\n"); rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); if (rc != apr.hdr.pkt_size) { - dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", __func__, rc); rc = -EBADE; } else @@ -881,7 +886,7 @@ static int q6_hfi_session_parse_seq_hdr(void *sess, struct q6_hfi_device *dev; if (!session || !seq_hdr || !session->device) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } dev = session->device; @@ -894,13 +899,13 @@ static int q6_hfi_session_parse_seq_hdr(void *sess, (u32)session, seq_hdr); if (rc) { dprintk(VIDC_ERR, - "Session parse seq hdr: failed to create pkt"); + "Session parse seq hdr: failed to create pkt\n"); goto err_create_pkt; } rc = apr_send_pkt(dev->apr, (uint32_t *)apr); if (rc != apr->hdr.pkt_size) { - dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", __func__, rc); rc = -EBADE; } else @@ -919,7 +924,7 @@ static int q6_hfi_session_get_seq_hdr(void *sess, struct q6_hfi_device *dev; if (!session || !seq_hdr || !session->device) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } dev = session->device; @@ -931,13 +936,13 @@ static int q6_hfi_session_get_seq_hdr(void *sess, rc = create_pkt_cmd_session_get_seq_hdr(&apr->pkt, (u32)session, seq_hdr); if (rc) { - dprintk(VIDC_ERR, "Session get seq hdr: failed to create pkt"); + dprintk(VIDC_ERR, "Session get seqhdr: failed to create pkt\n"); goto err_create_pkt; } rc = apr_send_pkt(dev->apr, (uint32_t *)apr); if (rc != apr->hdr.pkt_size) { - dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", __func__, rc); rc = -EBADE; } else @@ -955,7 +960,7 @@ static int q6_hfi_session_get_buf_req(void *sess) struct q6_hfi_device *dev; if (!session || !session->device) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } dev = session->device; @@ -964,13 +969,13 @@ static int q6_hfi_session_get_buf_req(void *sess) rc = create_pkt_cmd_session_get_buf_req(&apr.pkt, (u32)session); if (rc) { - dprintk(VIDC_ERR, "Session get buf req: failed to create pkt"); + dprintk(VIDC_ERR, "Session get bufreq: failed to create pkt\n"); goto err_create_pkt; } rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); if (rc != apr.hdr.pkt_size) { - dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", __func__, rc); rc = -EBADE; } else @@ -987,7 +992,7 @@ static int q6_hfi_session_flush(void *sess, enum hal_flush flush_mode) struct q6_hfi_device *dev; if (!session || !session->device) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } dev = session->device; @@ -996,13 +1001,13 @@ static int q6_hfi_session_flush(void *sess, enum hal_flush flush_mode) rc = create_pkt_cmd_session_flush(&apr.pkt, (u32)session, flush_mode); if (rc) { - dprintk(VIDC_ERR, "Session flush: failed to create pkt"); + dprintk(VIDC_ERR, "Session flush: failed to create pkt\n"); goto err_create_pkt; } rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); if (rc != apr.hdr.pkt_size) { - dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", __func__, rc); rc = -EBADE; } else @@ -1023,24 +1028,24 @@ static int q6_hfi_session_set_property(void *sess, struct q6_hfi_device *dev; if (!session || !pdata || !session->device) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } dev = session->device; - dprintk(VIDC_DBG, "in set_prop,with prop id: 0x%x", ptype); + dprintk(VIDC_DBG, "in set_prop,with prop id: 0x%x\n", ptype); q6_hfi_add_apr_hdr(dev, &apr->hdr, VIDC_IFACEQ_VAR_LARGE_PKT_SIZE); rc = create_pkt_cmd_session_set_property(&apr->pkt, (u32)session, ptype, pdata); if (rc) { - dprintk(VIDC_ERR, "set property: failed to create packet"); + dprintk(VIDC_ERR, "set property: failed to create packet\n"); goto err_create_pkt; } rc = apr_send_pkt(dev->apr, (uint32_t *)apr); if (rc != apr->hdr.pkt_size) { - dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", __func__, rc); rc = -EBADE; } else @@ -1057,12 +1062,12 @@ static int q6_hfi_session_get_property(void *sess, struct q6_hfi_device *dev; if (!session || !session->device) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } dev = session->device; - dprintk(VIDC_DBG, "IN func: , with property id: %d", ptype); + dprintk(VIDC_DBG, "IN func: , with property id: %d\n", ptype); switch (ptype) { case HAL_CONFIG_FRAME_RATE: @@ -1168,7 +1173,7 @@ static int q6_hfi_session_get_property(void *sess, case HAL_CONFIG_VENC_TIMESTAMP_SCALE: case HAL_PARAM_VENC_LOW_LATENCY: default: - dprintk(VIDC_INFO, "DEFAULT: Calling 0x%x", ptype); + dprintk(VIDC_INFO, "DEFAULT: Calling 0x%x\n", ptype); break; } return 0; @@ -1187,7 +1192,7 @@ static int q6_hfi_iommu_get_domain_partition(void *dev, u32 flags, { (void)dev; - dprintk(VIDC_ERR, "Not implemented: %s", __func__); + dprintk(VIDC_ERR, "Not implemented: %s\n", __func__); return -ENOTSUPP; } @@ -1202,7 +1207,7 @@ static int q6_hfi_iommu_attach(struct q6_hfi_device *device) struct iommu_info *iommu_map; if (!device || !device->res) { - dprintk(VIDC_ERR, "Invalid parameter: %p", device); + dprintk(VIDC_ERR, "Invalid parameter: %p\n", device); return -EINVAL; } @@ -1212,16 +1217,16 @@ static int q6_hfi_iommu_attach(struct q6_hfi_device *device) group = iommu_map->group; domain = msm_get_iommu_domain(iommu_map->domain); if (IS_ERR_OR_NULL(domain)) { - dprintk(VIDC_ERR, "Failed to get domain: %s", + dprintk(VIDC_ERR, "Failed to get domain: %s\n", iommu_map->name); rc = IS_ERR(domain) ? PTR_ERR(domain) : -EINVAL; break; } - dprintk(VIDC_DBG, "Attaching domain(id:%d) %p to group %p", + dprintk(VIDC_DBG, "Attaching domain(id:%d) %p to group %p\n", iommu_map->domain, domain, group); rc = iommu_attach_group(domain, group); if (rc) { - dprintk(VIDC_ERR, "IOMMU attach failed: %s", + dprintk(VIDC_ERR, "IOMMU attach failed: %s\n", iommu_map->name); break; } @@ -1248,7 +1253,7 @@ static void q6_hfi_iommu_detach(struct q6_hfi_device *device) int i; if (!device || !device->res) { - dprintk(VIDC_ERR, "Invalid parameter: %p", device); + dprintk(VIDC_ERR, "Invalid parameter: %p\n", device); return; } @@ -1288,14 +1293,14 @@ static int q6_hfi_load_fw(void *dev) device); if (device->apr == NULL) { - dprintk(VIDC_ERR, "Failed to register with QDSP6"); + dprintk(VIDC_ERR, "Failed to register with QDSP6\n"); rc = -EINVAL; goto fail_apr_register; } rc = q6_hfi_iommu_attach(device); if (rc) { - dprintk(VIDC_ERR, "Failed to attach iommu"); + dprintk(VIDC_ERR, "Failed to attach iommu\n"); goto fail_iommu_attach; } @@ -1326,7 +1331,7 @@ static void q6_hfi_unload_fw(void *hfi_device_data) if (device->apr) { if (apr_deregister(device->apr)) - dprintk(VIDC_ERR, "Failed to deregister APR"); + dprintk(VIDC_ERR, "Failed to deregister APR\n"); device->apr = NULL; } } @@ -1377,13 +1382,19 @@ int q6_hfi_initialize(struct hfi_device *hdev, u32 device_id, int rc = 0; if (!hdev || !res || !callback) { - dprintk(VIDC_ERR, "Invalid params: %p %p %p", + dprintk(VIDC_ERR, "Invalid params: %p %p %p\n", hdev, res, callback); rc = -EINVAL; goto err_hfi_init; } hdev->hfi_device_data = q6_hfi_get_device(device_id, res, callback); + if (IS_ERR_OR_NULL(hdev->hfi_device_data)) { + rc = PTR_ERR(hdev->hfi_device_data); + rc = !rc ? -EINVAL : rc; + goto err_hfi_init; + } + q6_init_hfi_callbacks(hdev); err_hfi_init: diff --git a/drivers/media/platform/msm/vidc/q6_hfi.h b/drivers/media/platform/msm/vidc/q6_hfi.h index 5f48a5126b97..e63d9ef30e1b 100644 --- a/drivers/media/platform/msm/vidc/q6_hfi.h +++ b/drivers/media/platform/msm/vidc/q6_hfi.h @@ -13,7 +13,7 @@ #ifndef __Q6_HFI_H__ #define __Q6_HFI_H__ -#include <mach/qdsp6v2/apr.h> +#include <linux/qdsp6v2/apr.h> #include "vidc_hfi.h" #include "vidc_hfi_helper.h" #include "msm_vidc_resources.h" diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c index 4345eb833348..e251ad0ab4d1 100644 --- a/drivers/media/platform/msm/vidc/venus_hfi.c +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -23,7 +23,7 @@ #include <mach/ocmem.h> #include <mach/scm.h> #include <mach/subsystem_restart.h> -#include <mach/msm_smem.h> +#include <soc/qcom/smem.h> #include <asm/memory.h> #include <linux/iopoll.h> #include "hfi_packetization.h" @@ -107,7 +107,7 @@ static void venus_hfi_sim_modify_cmd_packet(u8 *packet) u8 i; if (!packet) { - dprintk(VIDC_ERR, "Invalid Param"); + dprintk(VIDC_ERR, "Invalid Param\n"); return; } @@ -232,7 +232,7 @@ static int venus_hfi_write_queue(void *info, u8 *packet, u32 *rx_req_is_set) u32 *write_ptr; if (!info || !packet || !rx_req_is_set) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } @@ -245,7 +245,7 @@ static int venus_hfi_write_queue(void *info, u8 *packet, u32 *rx_req_is_set) queue = (struct hfi_queue_header *) qinfo->q_hdr; if (!queue) { - dprintk(VIDC_ERR, "queue not present"); + dprintk(VIDC_ERR, "queue not present\n"); return -ENOENT; } @@ -257,10 +257,10 @@ static int venus_hfi_write_queue(void *info, u8 *packet, u32 *rx_req_is_set) } packet_size_in_words = (*(u32 *)packet) >> 2; - dprintk(VIDC_DBG, "Packet_size in words: %d", packet_size_in_words); + dprintk(VIDC_DBG, "Packet_size in words: %d\n", packet_size_in_words); if (packet_size_in_words == 0) { - dprintk(VIDC_ERR, "Zero packet size"); + dprintk(VIDC_ERR, "Zero packet size\n"); return -ENODATA; } @@ -269,10 +269,10 @@ static int venus_hfi_write_queue(void *info, u8 *packet, u32 *rx_req_is_set) empty_space = (queue->qhdr_write_idx >= read_idx) ? (queue->qhdr_q_size - (queue->qhdr_write_idx - read_idx)) : (read_idx - queue->qhdr_write_idx); - dprintk(VIDC_DBG, "Empty_space: %d", empty_space); + dprintk(VIDC_DBG, "Empty_space: %d\n", empty_space); if (empty_space <= packet_size_in_words) { queue->qhdr_tx_req = 1; - dprintk(VIDC_ERR, "Insufficient size (%d) to write (%d)", + dprintk(VIDC_ERR, "Insufficient size (%d) to write (%d)\n", empty_space, packet_size_in_words); return -ENOTEMPTY; } @@ -282,7 +282,7 @@ static int venus_hfi_write_queue(void *info, u8 *packet, u32 *rx_req_is_set) new_write_idx = (queue->qhdr_write_idx + packet_size_in_words); write_ptr = (u32 *)((qinfo->q_array.align_virtual_addr) + (queue->qhdr_write_idx << 2)); - dprintk(VIDC_DBG, "Write Ptr: %d", (u32) write_ptr); + dprintk(VIDC_DBG, "Write Ptr: %d\n", (u32) write_ptr); if (new_write_idx < queue->qhdr_q_size) { memcpy(write_ptr, packet, packet_size_in_words << 2); } else { @@ -301,7 +301,6 @@ static int venus_hfi_write_queue(void *info, u8 *packet, u32 *rx_req_is_set) /*Memory barrier to make sure write index is updated before an * interupt is raised on venus.*/ mb(); - dprintk(VIDC_DBG, "Out : "); return 0; } @@ -311,7 +310,7 @@ static void venus_hfi_hal_sim_modify_msg_packet(u8 *packet) struct hal_session *sess; if (!packet) { - dprintk(VIDC_ERR, "Invalid Param: "); + dprintk(VIDC_ERR, "Invalid Param\n"); return; } @@ -367,7 +366,7 @@ static int venus_hfi_read_queue(void *info, u8 *packet, u32 *pb_tx_req_is_set) int rc = 0; if (!info || !packet || !pb_tx_req_is_set) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } @@ -395,14 +394,14 @@ static int venus_hfi_read_queue(void *info, u8 *packet, u32 *pb_tx_req_is_set) read_ptr = (u32 *)((qinfo->q_array.align_virtual_addr) + (queue->qhdr_read_idx << 2)); packet_size_in_words = (*read_ptr) >> 2; - dprintk(VIDC_DBG, "packet_size_in_words: %d", packet_size_in_words); + dprintk(VIDC_DBG, "packet_size_in_words: %d\n", packet_size_in_words); if (packet_size_in_words == 0) { - dprintk(VIDC_ERR, "Zero packet size"); + dprintk(VIDC_ERR, "Zero packet size\n"); return -ENODATA; } new_read_idx = queue->qhdr_read_idx + packet_size_in_words; - dprintk(VIDC_DBG, "Read Ptr: %d", (u32) new_read_idx); + dprintk(VIDC_DBG, "Read Ptr: %d\n", (u32) new_read_idx); if (((packet_size_in_words << 2) <= VIDC_IFACEQ_MED_PKT_SIZE) && queue->qhdr_read_idx <= queue->qhdr_q_size) { if (new_read_idx < queue->qhdr_q_size) { @@ -439,7 +438,6 @@ static int venus_hfi_read_queue(void *info, u8 *packet, u32 *pb_tx_req_is_set) dprintk(VIDC_PKT, "%s: %p\n", __func__, qinfo); venus_hfi_dump_packet(packet); } - dprintk(VIDC_DBG, "Out : "); return rc; } @@ -451,23 +449,23 @@ static int venus_hfi_alloc(struct venus_hfi_device *dev, void *mem, int rc = 0; if (!dev || !dev->hal_client || !mem || !size) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } vmem = (struct vidc_mem_addr *)mem; - dprintk(VIDC_INFO, "start to alloc: size:%d, Flags: %d", size, flags); + dprintk(VIDC_INFO, "start to alloc: size:%d, Flags: %d\n", size, flags); venus_hfi_power_enable(dev); alloc = msm_smem_alloc(dev->hal_client, size, align, flags, usage, 1); - dprintk(VIDC_DBG, "Alloc done"); + dprintk(VIDC_DBG, "Alloc done\n"); if (!alloc) { dprintk(VIDC_ERR, "Alloc failed\n"); rc = -ENOMEM; goto fail_smem_alloc; } - dprintk(VIDC_DBG, "venus_hfi_alloc:ptr=%p,size=%d", + dprintk(VIDC_DBG, "venus_hfi_alloc: ptr = %p, size = %d\n", alloc->kvaddr, size); rc = msm_smem_cache_operations(dev->hal_client, alloc, SMEM_CACHE_CLEAN); @@ -529,7 +527,7 @@ static void venus_hfi_write_register(struct venus_hfi_device *device, u32 reg, } hwiosymaddr = ((u32)base_addr + (hwiosymaddr)); - dprintk(VIDC_DBG, "Base addr: 0x%x, written to: 0x%x, Value: 0x%x...", + dprintk(VIDC_DBG, "Base addr: 0x%x, written to: 0x%x, Value: 0x%x...\n", (u32)base_addr, hwiosymaddr, value); writel_relaxed(value, hwiosymaddr); wmb(); @@ -555,6 +553,25 @@ static int venus_hfi_read_register(struct venus_hfi_device *device, u32 reg) return rc; } +static void venus_hfi_set_registers(struct venus_hfi_device *device) +{ + struct reg_set *reg_set; + int i; + + if (!device->res) { + dprintk(VIDC_ERR, + "device resources null, cannot set registers\n"); + return; + } + + reg_set = &device->res->reg_set; + for (i = 0; i < reg_set->count; i++) { + venus_hfi_write_register(device, + reg_set->reg_tbl[i].reg, + reg_set->reg_tbl[i].value, 0); + } +} + static int venus_hfi_core_start_cpu(struct venus_hfi_device *device) { u32 ctrl_status = 0, count = 0, rc = 0; @@ -655,7 +672,7 @@ static int venus_hfi_unvote_bus(void *dev, struct venus_hfi_device *device = dev; if (!device) { - dprintk(VIDC_ERR, "%s invalid device handle %p", + dprintk(VIDC_ERR, "%s invalid device handle %p\n", __func__, device); return -EINVAL; } @@ -684,7 +701,7 @@ static void venus_hfi_unvote_buses(void *dev, enum mem_type mtype) struct venus_hfi_device *device = dev; if (!device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return; } @@ -719,7 +736,7 @@ static int venus_hfi_get_bus_vector(struct venus_hfi_device *device, int load, if (!device || (mtype != DDR_MEM && mtype != OCMEM_MEM) || (type != MSM_VIDC_ENCODER && type != MSM_VIDC_DECODER)) { - dprintk(VIDC_ERR, "%s invalid params", __func__); + dprintk(VIDC_ERR, "%s invalid params\n", __func__); return -EINVAL; } @@ -752,7 +769,7 @@ static int venus_hfi_scale_bus(void *dev, int load, int bus_vector = 0; if (!device) { - dprintk(VIDC_ERR, "%s invalid device handle %p", + dprintk(VIDC_ERR, "%s invalid device handle %p\n", __func__, device); return -EINVAL; } @@ -788,7 +805,7 @@ static int venus_hfi_scale_buses(void *dev, enum mem_type mtype) struct venus_hfi_device *device = dev; if (!device) { - dprintk(VIDC_ERR, "%s invalid parameters", __func__); + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } for (i = 0; i < MSM_VIDC_MAX_DEVICES; i++) { @@ -848,7 +865,7 @@ static inline int venus_hfi_reset_core(struct venus_hfi_device *device) VIDC_CTRL_INIT, 0x1, 0); rc = venus_hfi_core_start_cpu(device); if (rc) - dprintk(VIDC_ERR, "Failed to start core"); + dprintk(VIDC_ERR, "Failed to start core\n"); return rc; } @@ -864,7 +881,7 @@ static inline int venus_hfi_clk_enable(struct venus_hfi_device *device) return -EINVAL; } if (device->clocks_enabled) { - dprintk(VIDC_DBG, "Clocks already enabled"); + dprintk(VIDC_DBG, "Clocks already enabled\n"); return 0; } @@ -908,7 +925,7 @@ static inline void venus_hfi_clk_disable(struct venus_hfi_device *device) return; } if (!device->clocks_enabled) { - dprintk(VIDC_DBG, "Clocks already disabled"); + dprintk(VIDC_DBG, "Clocks already disabled\n"); return; } @@ -960,14 +977,14 @@ static inline int venus_hfi_power_off(struct venus_hfi_device *device) return -EINVAL; } if (!device->power_enabled) { - dprintk(VIDC_DBG, "Power already disabled"); + dprintk(VIDC_DBG, "Power already disabled\n"); goto already_disabled; } /*Temporarily enable clocks to make TZ call.*/ rc = venus_hfi_clk_enable(device); if (rc) { - dprintk(VIDC_ERR, "Failed to enable clocks before TZ call"); + dprintk(VIDC_ERR, "Failed to enable clocks before TZ call\n"); return rc; } rc = venus_hfi_tzbsp_set_video_state(TZBSP_VIDEO_STATE_SUSPEND); @@ -980,7 +997,7 @@ static inline int venus_hfi_power_off(struct venus_hfi_device *device) venus_hfi_iommu_detach(device); rc = regulator_disable(venus_hfi_get_regulator(device, "venus")); if (rc) { - dprintk(VIDC_ERR, "Failed to disable GDSC, %d", rc); + dprintk(VIDC_ERR, "Failed to disable GDSC, %d\n", rc); return rc; } if (device->res->has_ocmem) @@ -989,6 +1006,7 @@ static inline int venus_hfi_power_off(struct venus_hfi_device *device) venus_hfi_unvote_buses(device, DDR_MEM); device->power_enabled = 0; + dprintk(VIDC_INFO, "entering power collapse\n"); already_disabled: return rc; } @@ -1006,13 +1024,13 @@ static inline int venus_hfi_power_on(struct venus_hfi_device *device) else rc = venus_hfi_scale_buses(device, DDR_MEM); if (rc) { - dprintk(VIDC_ERR, "Failed to scale buses"); + dprintk(VIDC_ERR, "Failed to scale buses\n"); goto err_scale_buses; } rc = regulator_enable(venus_hfi_get_regulator(device, "venus")); if (rc) { - dprintk(VIDC_ERR, "Failed to enable GDSC %d", rc); + dprintk(VIDC_ERR, "Failed to enable GDSC %d\n", rc); goto err_enable_gdsc; } @@ -1024,21 +1042,48 @@ static inline int venus_hfi_power_on(struct venus_hfi_device *device) rc = venus_hfi_clk_enable(device); if (rc) { - dprintk(VIDC_ERR, "Failed to enable clocks"); + dprintk(VIDC_ERR, "Failed to enable clocks\n"); goto err_enable_clk; } + + /* + * Re-program all of the registers that get reset as a result of + * regulator_disable() and _enable() + */ + venus_hfi_set_registers(device); + + venus_hfi_write_register(device, VIDC_UC_REGION_ADDR, + (u32)device->iface_q_table.align_device_addr, 0); + venus_hfi_write_register(device, + VIDC_UC_REGION_SIZE, SHARED_QSIZE, 0); + venus_hfi_write_register(device, VIDC_CPU_CS_SCIACMDARG2, + (u32)device->iface_q_table.align_device_addr, + device->iface_q_table.align_virtual_addr); + + if (!IS_ERR_OR_NULL(device->sfr.align_device_addr)) + venus_hfi_write_register(device, VIDC_SFR_ADDR, + (u32)device->sfr.align_device_addr, 0); + if (!IS_ERR_OR_NULL(device->qdss.align_device_addr)) + venus_hfi_write_register(device, VIDC_MMAP_ADDR, + (u32)device->qdss.align_device_addr, 0); + + /* Reboot the firmware */ rc = venus_hfi_tzbsp_set_video_state(TZBSP_VIDEO_STATE_RESUME); if (rc) { dprintk(VIDC_ERR, "Failed to resume video core %d\n", rc); goto err_set_video_state; } + + /* Wait for boot completion */ rc = venus_hfi_reset_core(device); if (rc) { - dprintk(VIDC_ERR, "Failed to reset venus core"); + dprintk(VIDC_ERR, "Failed to reset venus core\n"); goto err_reset_core; } + device->power_enabled = 1; + dprintk(VIDC_INFO, "resuming from power collapse\n"); return rc; err_reset_core: venus_hfi_tzbsp_set_video_state(TZBSP_VIDEO_STATE_SUSPEND); @@ -1084,7 +1129,7 @@ static inline int venus_hfi_clk_gating_off(struct venus_hfi_device *device) return -EINVAL; } if (device->clocks_enabled) { - dprintk(VIDC_DBG, "Clocks are already enabled"); + dprintk(VIDC_DBG, "Clocks are already enabled\n"); goto already_enabled; } cancel_delayed_work(&venus_hfi_pm_work); @@ -1098,7 +1143,7 @@ static inline int venus_hfi_clk_gating_off(struct venus_hfi_device *device) } else { rc = venus_hfi_clk_enable(device); if (rc) { - dprintk(VIDC_ERR, "Failed venus clock enable"); + dprintk(VIDC_ERR, "Failed venus clock enable\n"); goto fail_clk_power_on; } venus_hfi_write_register(device, @@ -1163,14 +1208,14 @@ static int venus_hfi_iface_cmdq_write(struct venus_hfi_device *device, struct vidc_iface_q_info *q_info; int result = -EPERM; if (!device || !pkt) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } mutex_lock(&device->write_lock); q_info = &device->iface_queues[VIDC_IFACEQ_CMDQ_IDX]; if (!q_info) { - dprintk(VIDC_ERR, "cannot write to shared Q's"); + dprintk(VIDC_ERR, "cannot write to shared Q's\n"); goto err_q_null; } if (!venus_hfi_write_queue(q_info, (u8 *)pkt, &rx_req_is_set)) { @@ -1196,7 +1241,7 @@ static int venus_hfi_iface_cmdq_write(struct venus_hfi_device *device, result = 0; mutex_unlock(&device->clk_pwr_lock); } else { - dprintk(VIDC_ERR, "venus_hfi_iface_cmdq_write:queue_full"); + dprintk(VIDC_ERR, "venus_hfi_iface_cmdq_write:queue_full\n"); } err_q_write: err_q_null: @@ -1211,13 +1256,13 @@ static int venus_hfi_iface_msgq_read(struct venus_hfi_device *device, void *pkt) struct vidc_iface_q_info *q_info; if (!pkt) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } mutex_lock(&device->read_lock); if (device->iface_queues[VIDC_IFACEQ_MSGQ_IDX]. q_array.align_virtual_addr == 0) { - dprintk(VIDC_ERR, "cannot read from shared MSG Q's"); + dprintk(VIDC_ERR, "cannot read from shared MSG Q's\n"); rc = -ENODATA; goto read_error_null; } @@ -1239,7 +1284,7 @@ static int venus_hfi_iface_msgq_read(struct venus_hfi_device *device, void *pkt) rc = 0; mutex_unlock(&device->clk_pwr_lock); } else { - dprintk(VIDC_INFO, "venus_hfi_iface_msgq_read:queue_empty"); + dprintk(VIDC_INFO, "venus_hfi_iface_msgq_read:queue_empty\n"); rc = -ENODATA; } read_error: @@ -1255,13 +1300,13 @@ static int venus_hfi_iface_dbgq_read(struct venus_hfi_device *device, void *pkt) struct vidc_iface_q_info *q_info; if (!pkt) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } mutex_lock(&device->read_lock); if (device->iface_queues[VIDC_IFACEQ_DBGQ_IDX]. q_array.align_virtual_addr == 0) { - dprintk(VIDC_ERR, "cannot read from shared DBG Q's"); + dprintk(VIDC_ERR, "cannot read from shared DBG Q's\n"); rc = -ENODATA; goto dbg_error_null; } @@ -1283,7 +1328,7 @@ static int venus_hfi_iface_dbgq_read(struct venus_hfi_device *device, void *pkt) rc = 0; mutex_unlock(&device->clk_pwr_lock); } else { - dprintk(VIDC_INFO, "venus_hfi_iface_dbgq_read:queue_empty"); + dprintk(VIDC_INFO, "venus_hfi_iface_dbgq_read:queue_empty\n"); rc = -ENODATA; } dbg_error: @@ -1375,7 +1420,7 @@ static int venus_hfi_get_qdss_iommu_virtual_addr(struct hfi_mem_map *mem_map, SZ_4K, 0, &iova); if (rc) { dprintk(VIDC_ERR, - "IOMMU QDSS mapping failed for addr 0x%x", + "IOMMU QDSS mapping failed for addr 0x%x\n", venus_qdss_entries[i][0]); rc = -ENOMEM; break; @@ -1387,7 +1432,7 @@ static int venus_hfi_get_qdss_iommu_virtual_addr(struct hfi_mem_map *mem_map, } if (i < num_entries) { dprintk(VIDC_ERR, - "IOMMU QDSS mapping failed, Freeing entries %d", i); + "IOMMU QDSS mapping failed, Freeing entries %d\n", i); for (--i; i >= 0; i--) { msm_iommu_unmap_contig_buffer( (unsigned long)(mem_map[i].virtual_addr), @@ -1416,7 +1461,7 @@ static int venus_hfi_interface_queues_init(struct venus_hfi_device *dev) QUEUE_SIZE, 1, 0, HAL_BUFFER_INTERNAL_CMD_QUEUE); if (rc) { - dprintk(VIDC_ERR, "iface_q_table_alloc_fail"); + dprintk(VIDC_ERR, "iface_q_table_alloc_fail\n"); goto fail_alloc_queue; } dev->iface_q_table.align_virtual_addr = mem_addr->align_virtual_addr; @@ -1444,7 +1489,7 @@ static int venus_hfi_interface_queues_init(struct venus_hfi_device *dev) HAL_BUFFER_INTERNAL_CMD_QUEUE); if (rc) { dprintk(VIDC_WARN, - "qdss_alloc_fail: QDSS messages logging will not work"); + "qdss_alloc_fail: QDSS messages logging will not work\n"); dev->qdss.align_device_addr = NULL; } else { dev->qdss.align_device_addr = mem_addr->align_device_addr; @@ -1456,7 +1501,7 @@ static int venus_hfi_interface_queues_init(struct venus_hfi_device *dev) SFR_SIZE, 1, 0, HAL_BUFFER_INTERNAL_CMD_QUEUE); if (rc) { - dprintk(VIDC_WARN, "sfr_alloc_fail: SFR not will work"); + dprintk(VIDC_WARN, "sfr_alloc_fail: SFR not will work\n"); dev->sfr.align_device_addr = NULL; } else { dev->sfr.align_device_addr = mem_addr->align_device_addr; @@ -1517,7 +1562,7 @@ static int venus_hfi_interface_queues_init(struct venus_hfi_device *dev) rc = venus_hfi_get_qdss_iommu_virtual_addr(mem_map, domain, partition); if (rc) { dprintk(VIDC_ERR, - "IOMMU mapping failed, Freeing qdss memdata"); + "IOMMU mapping failed, Freeing qdss memdata\n"); venus_hfi_free(dev, dev->qdss.mem_data); dev->qdss.mem_data = NULL; } @@ -1536,25 +1581,6 @@ fail_alloc_queue: return -ENOMEM; } -static void venus_hfi_set_registers(struct venus_hfi_device *device) -{ - struct reg_set *reg_set; - int i; - - if (!device->res) { - dprintk(VIDC_ERR, - "device resources null, cannot set registers\n"); - return; - } - - reg_set = &device->res->reg_set; - for (i = 0; i < reg_set->count; i++) { - venus_hfi_write_register(device, - reg_set->reg_tbl[i].reg, - reg_set->reg_tbl[i].value, 0); - } -} - static int venus_hfi_sys_set_debug(struct venus_hfi_device *device, u32 debug) { u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; @@ -1620,7 +1646,7 @@ static int venus_hfi_core_init(void *device) if (device) { dev = device; } else { - dprintk(VIDC_ERR, "Invalid device"); + dprintk(VIDC_ERR, "Invalid device\n"); return -ENODEV; } @@ -1634,23 +1660,23 @@ static int venus_hfi_core_init(void *device) if (!dev->hal_client) { dev->hal_client = msm_smem_new_client(SMEM_ION, dev->res); if (dev->hal_client == NULL) { - dprintk(VIDC_ERR, "Failed to alloc ION_Client"); + dprintk(VIDC_ERR, "Failed to alloc ION_Client\n"); rc = -ENODEV; goto err_core_init; } - dprintk(VIDC_DBG, "Dev_Virt: 0x%x, Reg_Virt: 0x%x", + dprintk(VIDC_DBG, "Dev_Virt: 0x%x, Reg_Virt: 0x%x\n", dev->hal_data->device_base_addr, (u32) dev->hal_data->register_base_addr); rc = venus_hfi_interface_queues_init(dev); if (rc) { - dprintk(VIDC_ERR, "failed to init queues"); + dprintk(VIDC_ERR, "failed to init queues\n"); rc = -ENOMEM; goto err_core_init; } } else { - dprintk(VIDC_ERR, "hal_client exists"); + dprintk(VIDC_ERR, "hal_client exists\n"); rc = -EEXIST; goto err_core_init; } @@ -1659,14 +1685,14 @@ static int venus_hfi_core_init(void *device) VIDC_CTRL_INIT, 0x1, 0); rc = venus_hfi_core_start_cpu(dev); if (rc) { - dprintk(VIDC_ERR, "Failed to start core"); + dprintk(VIDC_ERR, "Failed to start core\n"); rc = -ENODEV; goto err_core_init; } rc = create_pkt_cmd_sys_init(&pkt, HFI_VIDEO_ARCH_OX); if (rc) { - dprintk(VIDC_ERR, "Failed to create sys init pkt"); + dprintk(VIDC_ERR, "Failed to create sys init pkt\n"); goto err_core_init; } if (venus_hfi_iface_cmdq_write(dev, &pkt)) { @@ -1675,7 +1701,7 @@ static int venus_hfi_core_init(void *device) } rc = create_pkt_cmd_sys_image_version(&version_pkt); if (rc || venus_hfi_iface_cmdq_write(dev, &version_pkt)) - dprintk(VIDC_WARN, "Failed to send image version pkt to f/w"); + dprintk(VIDC_WARN, "Failed to send image version pkt to f/w\n"); return rc; err_core_init: @@ -1690,7 +1716,7 @@ static int venus_hfi_core_release(void *device) if (device) { dev = device; } else { - dprintk(VIDC_ERR, "invalid device"); + dprintk(VIDC_ERR, "invalid device\n"); return -ENODEV; } if (dev->hal_client) { @@ -1719,14 +1745,17 @@ static int venus_hfi_is_cmd_pending(struct venus_hfi_device *dev) struct vidc_iface_q_info *q_info; u32 write_ptr, read_ptr; u32 rc = 0; + q_info = &dev->iface_queues[VIDC_IFACEQ_CMDQ_IDX]; if (!q_info) - dprintk(VIDC_ERR, "cannot read shared Q's"); - queue = (struct hfi_queue_header *) q_info->q_hdr; + dprintk(VIDC_ERR, "cannot read shared Q's\n"); + + queue = (struct hfi_queue_header *)q_info->q_hdr; if (!queue) { - dprintk(VIDC_ERR, "queue not present"); + dprintk(VIDC_ERR, "queue not present\n"); return -ENOENT; } + write_ptr = (u32)queue->qhdr_write_idx; read_ptr = (u32)queue->qhdr_read_idx; rc = read_ptr - write_ptr; @@ -1740,7 +1769,7 @@ static inline void venus_hfi_clk_gating_on(struct venus_hfi_device *device) return; } if (!device->clocks_enabled) { - dprintk(VIDC_DBG, "Clocks are already disabled"); + dprintk(VIDC_DBG, "Clocks are already disabled\n"); goto already_disabled; } /*SYS Idle should be last message so mask any further interrupts @@ -1773,6 +1802,7 @@ static void venus_hfi_core_clear_interrupt(struct venus_hfi_device *device) "%s : Clock enable failed\n", __func__); goto err_clk_gating_off; } + intr_status = venus_hfi_read_register( device, VIDC_WRAPPER_INTR_STATUS); @@ -1782,19 +1812,20 @@ static void venus_hfi_core_clear_interrupt(struct venus_hfi_device *device) (intr_status & VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK)) { device->intr_status |= intr_status; - dprintk(VIDC_DBG, "INTERRUPT for device: 0x%x: " - "times: %d interrupt_status: %d", - (u32) device, ++device->reg_count, intr_status); + dprintk(VIDC_DBG, + "INTERRUPT for device: 0x%x: times: %d interrupt_status: %d\n", + (u32)device, ++device->reg_count, intr_status); } else { - dprintk(VIDC_INFO, "SPURIOUS_INTR for device: 0x%x: " - "times: %d interrupt_status: %d", - (u32) device, ++device->spur_count, intr_status); + dprintk(VIDC_INFO, + "SPURIOUS_INTR for device: 0x%x: times: %d interrupt_status: %d\n", + (u32)device, ++device->spur_count, intr_status); } + venus_hfi_write_register(device, VIDC_CPU_CS_A2HSOFTINTCLR, 1, 0); venus_hfi_write_register(device, VIDC_WRAPPER_INTR_CLEAR, intr_status, 0); - dprintk(VIDC_DBG, "Cleared WRAPPER/A2H interrupt"); + dprintk(VIDC_DBG, "Cleared WRAPPER/A2H interrupt\n"); err_clk_gating_off: mutex_unlock(&device->clk_pwr_lock); mutex_unlock(&device->write_lock); @@ -1809,7 +1840,7 @@ static int venus_hfi_core_set_resource(void *device, struct venus_hfi_device *dev; if (!device || !resource_hdr || !resource_value) { - dprintk(VIDC_ERR, "set_res: Invalid Params"); + dprintk(VIDC_ERR, "set_res: Invalid Params\n"); return -EINVAL; } else { dev = device; @@ -1820,7 +1851,7 @@ static int venus_hfi_core_set_resource(void *device, rc = create_pkt_set_cmd_sys_resource(pkt, resource_hdr, resource_value); if (rc) { - dprintk(VIDC_ERR, "set_res: failed to create packet"); + dprintk(VIDC_ERR, "set_res: failed to create packet\n"); goto err_create_pkt; } if (venus_hfi_iface_cmdq_write(dev, pkt)) @@ -1838,7 +1869,7 @@ static int venus_hfi_core_release_resource(void *device, struct venus_hfi_device *dev; if (!device || !resource_hdr) { - dprintk(VIDC_ERR, "Inv-Params in rel_res"); + dprintk(VIDC_ERR, "Inv-Params in rel_res\n"); return -EINVAL; } else { dev = device; @@ -1846,7 +1877,7 @@ static int venus_hfi_core_release_resource(void *device, rc = create_pkt_cmd_sys_release_resource(&pkt, resource_hdr); if (rc) { - dprintk(VIDC_ERR, "release_res: failed to create packet"); + dprintk(VIDC_ERR, "release_res: failed to create packet\n"); goto err_create_pkt; } @@ -1866,13 +1897,13 @@ static int venus_hfi_core_ping(void *device) if (device) { dev = device; } else { - dprintk(VIDC_ERR, "invalid device"); + dprintk(VIDC_ERR, "invalid device\n"); return -ENODEV; } rc = create_pkt_cmd_sys_ping(&pkt); if (rc) { - dprintk(VIDC_ERR, "core_ping: failed to create packet"); + dprintk(VIDC_ERR, "core_ping: failed to create packet\n"); goto err_create_pkt; } @@ -1893,13 +1924,13 @@ static int venus_hfi_core_trigger_ssr(void *device, if (device) { dev = device; } else { - dprintk(VIDC_ERR, "invalid device"); + dprintk(VIDC_ERR, "invalid device\n"); return -ENODEV; } rc = create_pkt_ssr_cmd(type, &pkt); if (rc) { - dprintk(VIDC_ERR, "core_ping: failed to create packet"); + dprintk(VIDC_ERR, "core_ping: failed to create packet\n"); goto err_create_pkt; } @@ -1920,17 +1951,17 @@ static int venus_hfi_session_set_property(void *sess, int rc = 0; if (!sess || !pdata) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } else { session = sess; } - dprintk(VIDC_INFO, "in set_prop,with prop id: 0x%x", ptype); + dprintk(VIDC_INFO, "in set_prop,with prop id: 0x%x\n", ptype); if (create_pkt_cmd_session_set_property(pkt, (u32)session, ptype, pdata)) { - dprintk(VIDC_ERR, "set property: failed to create packet"); + dprintk(VIDC_ERR, "set property: failed to create packet\n"); return -EINVAL; } @@ -1947,17 +1978,17 @@ static int venus_hfi_session_get_property(void *sess, struct hal_session *session; int rc = 0; if (!sess) { - dprintk(VIDC_ERR, "Invalid Params in "); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } else { session = sess; } - dprintk(VIDC_INFO, "%s: property id: %d", __func__, ptype); + dprintk(VIDC_INFO, "%s: property id: %d\n", __func__, ptype); rc = create_pkt_cmd_session_get_property( &pkt, (u32)session, ptype); if (rc) { - dprintk(VIDC_ERR, "get property profile: pkt failed"); + dprintk(VIDC_ERR, "get property profile: pkt failed\n"); goto err_create_pkt; } if (venus_hfi_iface_cmdq_write(session->device, &pkt)) { @@ -1989,7 +2020,7 @@ static void *venus_hfi_session_init(void *device, u32 session_id, if (device) { dev = device; } else { - dprintk(VIDC_ERR, "invalid device"); + dprintk(VIDC_ERR, "invalid device\n"); return NULL; } @@ -2010,7 +2041,7 @@ static void *venus_hfi_session_init(void *device, u32 session_id, if (create_pkt_cmd_sys_session_init(&pkt, (u32)new_session, session_type, codec_type)) { - dprintk(VIDC_ERR, "session_init: failed to create packet"); + dprintk(VIDC_ERR, "session_init: failed to create packet\n"); goto err_session_init_fail; } @@ -2039,7 +2070,7 @@ static int venus_hfi_send_session_cmd(void *session_id, rc = create_pkt_cmd_session_cmd(&pkt, pkt_type, (u32)session); if (rc) { - dprintk(VIDC_ERR, "send session cmd: create pkt failed"); + dprintk(VIDC_ERR, "send session cmd: create pkt failed\n"); goto err_create_pkt; } @@ -2066,11 +2097,11 @@ static int venus_hfi_session_clean(void *session) { struct hal_session *sess_close; if (!session) { - dprintk(VIDC_ERR, "Invalid Params %s", __func__); + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); return -EINVAL; } sess_close = session; - dprintk(VIDC_DBG, "deleted the session: 0x%p", + dprintk(VIDC_DBG, "deleted the session: 0x%p\n", sess_close); mutex_lock(&((struct venus_hfi_device *) sess_close->device)->session_lock); @@ -2090,7 +2121,7 @@ static int venus_hfi_session_set_buffers(void *sess, struct hal_session *session; if (!sess || !buffer_info) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } else { session = sess; @@ -2104,11 +2135,11 @@ static int venus_hfi_session_set_buffers(void *sess, rc = create_pkt_cmd_session_set_buffers(pkt, (u32)session, buffer_info); if (rc) { - dprintk(VIDC_ERR, "set buffers: failed to create packet"); + dprintk(VIDC_ERR, "set buffers: failed to create packet\n"); goto err_create_pkt; } - dprintk(VIDC_INFO, "set buffers: 0x%x", buffer_info->buffer_type); + dprintk(VIDC_INFO, "set buffers: 0x%x\n", buffer_info->buffer_type); if (venus_hfi_iface_cmdq_write(session->device, pkt)) rc = -ENOTEMPTY; err_create_pkt: @@ -2124,7 +2155,7 @@ static int venus_hfi_session_release_buffers(void *sess, struct hal_session *session; if (!sess || !buffer_info) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } else { session = sess; @@ -2138,11 +2169,11 @@ static int venus_hfi_session_release_buffers(void *sess, rc = create_pkt_cmd_session_release_buffers(pkt, (u32)session, buffer_info); if (rc) { - dprintk(VIDC_ERR, "release buffers: failed to create packet"); + dprintk(VIDC_ERR, "release buffers: failed to create packet\n"); goto err_create_pkt; } - dprintk(VIDC_INFO, "Release buffers: 0x%x", buffer_info->buffer_type); + dprintk(VIDC_INFO, "Release buffers: 0x%x\n", buffer_info->buffer_type); if (venus_hfi_iface_cmdq_write(session->device, pkt)) rc = -ENOTEMPTY; err_create_pkt: @@ -2192,7 +2223,7 @@ static int venus_hfi_session_etb(void *sess, struct hal_session *session; if (!sess || !input_frame) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } else { session = sess; @@ -2205,10 +2236,10 @@ static int venus_hfi_session_etb(void *sess, (u32)session, input_frame); if (rc) { dprintk(VIDC_ERR, - "Session etb decoder: failed to create pkt"); + "Session etb decoder: failed to create pkt\n"); goto err_create_pkt; } - dprintk(VIDC_DBG, "Q DECODER INPUT BUFFER"); + dprintk(VIDC_DBG, "Q DECODER INPUT BUFFER\n"); if (venus_hfi_iface_cmdq_write(session->device, &pkt)) rc = -ENOTEMPTY; } else { @@ -2219,10 +2250,10 @@ static int venus_hfi_session_etb(void *sess, (u32)session, input_frame); if (rc) { dprintk(VIDC_ERR, - "Session etb encoder: failed to create pkt"); + "Session etb encoder: failed to create pkt\n"); goto err_create_pkt; } - dprintk(VIDC_DBG, "Q ENCODER INPUT BUFFER"); + dprintk(VIDC_DBG, "Q ENCODER INPUT BUFFER\n"); if (venus_hfi_iface_cmdq_write(session->device, &pkt)) rc = -ENOTEMPTY; } @@ -2238,7 +2269,7 @@ static int venus_hfi_session_ftb(void *sess, struct hal_session *session; if (!sess || !output_frame) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } else { session = sess; @@ -2246,7 +2277,7 @@ static int venus_hfi_session_ftb(void *sess, rc = create_pkt_cmd_session_ftb(&pkt, (u32)session, output_frame); if (rc) { - dprintk(VIDC_ERR, "Session ftb: failed to create pkt"); + dprintk(VIDC_ERR, "Session ftb: failed to create pkt\n"); goto err_create_pkt; } @@ -2265,7 +2296,7 @@ static int venus_hfi_session_parse_seq_hdr(void *sess, struct hal_session *session; if (!sess || !seq_hdr) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } else { session = sess; @@ -2277,7 +2308,7 @@ static int venus_hfi_session_parse_seq_hdr(void *sess, seq_hdr); if (rc) { dprintk(VIDC_ERR, - "Session parse seq hdr: failed to create pkt"); + "Session parse seq hdr: failed to create pkt\n"); goto err_create_pkt; } @@ -2296,7 +2327,7 @@ static int venus_hfi_session_get_seq_hdr(void *sess, struct hal_session *session; if (!sess || !seq_hdr) { - dprintk(VIDC_ERR, "Invalid Params"); + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } else { session = sess; @@ -2305,7 +2336,8 @@ static int venus_hfi_session_get_seq_hdr(void *sess, pkt = (struct hfi_cmd_session_get_sequence_header_packet *) packet; rc = create_pkt_cmd_session_get_seq_hdr(pkt, (u32)session, seq_hdr); if (rc) { - dprintk(VIDC_ERR, "Session get seq hdr: failed to create pkt"); + dprintk(VIDC_ERR, + "Session get seq hdr: failed to create pkt\n"); goto err_create_pkt; } @@ -2330,7 +2362,8 @@ static int venus_hfi_session_get_buf_req(void *sess) rc = create_pkt_cmd_session_get_buf_req(&pkt, (u32)session); if (rc) { - dprintk(VIDC_ERR, "Session get buf req: failed to create pkt"); + dprintk(VIDC_ERR, + "Session get buf req: failed to create pkt\n"); goto err_create_pkt; } @@ -2355,7 +2388,7 @@ static int venus_hfi_session_flush(void *sess, enum hal_flush flush_mode) rc = create_pkt_cmd_session_flush(&pkt, (u32)session, flush_mode); if (rc) { - dprintk(VIDC_ERR, "Session flush: failed to create pkt"); + dprintk(VIDC_ERR, "Session flush: failed to create pkt\n"); goto err_create_pkt; } @@ -2405,12 +2438,12 @@ static int venus_hfi_check_core_registered( FIRMWARE_SIZE))) { return 0; } else { - dprintk(VIDC_INFO, "Device not registered"); + dprintk(VIDC_INFO, "Device not registered\n"); return -EINVAL; } } } else { - dprintk(VIDC_INFO, "no device Registered"); + dprintk(VIDC_INFO, "no device Registered\n"); } return -EINVAL; } @@ -2433,13 +2466,13 @@ static int venus_hfi_core_pc_prep(void *device) if (device) { dev = device; } else { - dprintk(VIDC_ERR, "invalid device"); + dprintk(VIDC_ERR, "invalid device\n"); return -ENODEV; } rc = create_pkt_cmd_sys_pc_prep(&pkt); if (rc) { - dprintk(VIDC_ERR, "Failed to create sys pc prep pkt"); + dprintk(VIDC_ERR, "Failed to create sys pc prep pkt\n"); goto err_create_pkt; } @@ -2467,26 +2500,26 @@ static void venus_hfi_pm_hndlr(struct work_struct *work) init_completion(&pc_prep_done); rc = venus_hfi_core_pc_prep(device); if (rc) { - dprintk(VIDC_ERR, "Failed to prepare venus for power off"); + dprintk(VIDC_ERR, "Failed to prepare venus for power off\n"); return; } rc = wait_for_completion_timeout(&pc_prep_done, msecs_to_jiffies(msm_vidc_hw_rsp_timeout)); if (!rc) { - dprintk(VIDC_ERR, "Wait interrupted or timeout: %d", rc); + dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n", rc); return; } mutex_lock(&device->clk_pwr_lock); if (device->clocks_enabled) { dprintk(VIDC_ERR, - "Clocks are still enabled after PC_PREP_DONE, ignore power off"); + "Clocks are still enabled after PC_PREP_DONE, ignore power off\n"); goto clks_enabled; } rc = venus_hfi_power_off(device); if (rc) - dprintk(VIDC_ERR, "Failed venus power off"); + dprintk(VIDC_ERR, "Failed venus power off\n"); clks_enabled: mutex_unlock(&device->clk_pwr_lock); } @@ -2496,7 +2529,7 @@ static int venus_hfi_try_clk_gating(struct venus_hfi_device *device) int rc = 0; u32 ctrl_status = 0; if (!device) { - dprintk(VIDC_ERR, "invalid device"); + dprintk(VIDC_ERR, "invalid device\n"); return -ENODEV; } mutex_lock(&device->write_lock); @@ -2506,14 +2539,14 @@ static int venus_hfi_try_clk_gating(struct venus_hfi_device *device) device, VIDC_CPU_CS_SCIACMDARG0); dprintk(VIDC_DBG, - "venus_hfi_try_clk_gating - rc %d, ctrl_status 0x%x", + "venus_hfi_try_clk_gating - rc %d, ctrl_status 0x%x\n", rc, ctrl_status); if (((ctrl_status & VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK) || (ctrl_status & VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_PC_READY)) && !rc) venus_hfi_clk_gating_on(device); else - dprintk(VIDC_DBG, "Ignore clock gating"); + dprintk(VIDC_DBG, "Ignore clock gating\n"); mutex_unlock(&device->clk_pwr_lock); mutex_unlock(&device->write_lock); return rc; @@ -2533,7 +2566,7 @@ static void venus_hfi_process_msg_event_notify( vsfr = (struct hfi_sfr_struct *) device->sfr.align_virtual_addr; if (vsfr) - dprintk(VIDC_ERR, "SFR Message from FW : %s", + dprintk(VIDC_ERR, "SFR Message from FW : %s\n", vsfr->rg_data); } } @@ -2546,13 +2579,13 @@ static void venus_hfi_response_handler(struct venus_hfi_device *device) if (device) { if ((device->intr_status & VIDC_WRAPPER_INTR_CLEAR_A2HWD_BMSK)) { - dprintk(VIDC_ERR, "Received: Watchdog timeout %s", + dprintk(VIDC_ERR, "Received: Watchdog timeout %s\n", __func__); vsfr = (struct hfi_sfr_struct *) device->sfr.align_virtual_addr; if (vsfr) dprintk(VIDC_ERR, - "SFR Message from FW : %s", + "SFR Message from FW : %s\n", vsfr->rg_data); venus_hfi_process_sys_watchdog_timeout(device); } @@ -2569,7 +2602,7 @@ static void venus_hfi_response_handler(struct venus_hfi_device *device) while (!venus_hfi_iface_dbgq_read(device, packet)) { struct hfi_msg_sys_debug_packet *pkt = (struct hfi_msg_sys_debug_packet *) packet; - dprintk(VIDC_FW, "FW-SAYS: %s", pkt->rg_msg_data); + dprintk(VIDC_FW, "%s", pkt->rg_msg_data); } if (rc == HFI_MSG_SYS_IDLE) { dprintk(VIDC_DBG, "Received HFI_MSG_SYS_IDLE\n"); @@ -2580,11 +2613,11 @@ static void venus_hfi_response_handler(struct venus_hfi_device *device) rc = venus_hfi_try_clk_gating(device); if (rc) dprintk(VIDC_ERR, - "Failed clk gating after PC_PREP_DONE"); + "Failed clk gating after PC_PREP_DONE\n"); complete(&pc_prep_done); } } else { - dprintk(VIDC_ERR, "SPURIOUS_INTERRUPT"); + dprintk(VIDC_ERR, "SPURIOUS_INTERRUPT\n"); } } @@ -2593,7 +2626,7 @@ static void venus_hfi_core_work_handler(struct work_struct *work) struct venus_hfi_device *device = list_first_entry( &hal_ctxt.dev_head, struct venus_hfi_device, list); - dprintk(VIDC_INFO, " GOT INTERRUPT () "); + dprintk(VIDC_INFO, "GOT INTERRUPT\n"); if (!device->callback) { dprintk(VIDC_ERR, "No interrupt callback function: %p\n", device); @@ -2609,10 +2642,9 @@ static DECLARE_WORK(venus_hfi_work, venus_hfi_core_work_handler); static irqreturn_t venus_hfi_isr(int irq, void *dev) { struct venus_hfi_device *device = dev; - dprintk(VIDC_INFO, "vidc_hal_isr() %d ", irq); + dprintk(VIDC_INFO, "vidc_hal_isr %d\n", irq); disable_irq_nosync(irq); queue_work(device->vidc_workq, &venus_hfi_work); - dprintk(VIDC_INFO, "vidc_hal_isr() %d ", irq); return IRQ_HANDLED; } @@ -2632,16 +2664,16 @@ static int venus_hfi_init_regs_and_interrupts( device->register_base, device->register_size, device->irq); if (!rc) { - dprintk(VIDC_ERR, "Core present/Already added"); + dprintk(VIDC_ERR, "Core present/Already added\n"); rc = -EEXIST; goto err_core_init; } - dprintk(VIDC_DBG, "HAL_DATA will be assigned now"); + dprintk(VIDC_DBG, "HAL_DATA will be assigned now\n"); hal = (struct hal_data *) kzalloc(sizeof(struct hal_data), GFP_KERNEL); if (!hal) { - dprintk(VIDC_ERR, "Failed to alloc"); + dprintk(VIDC_ERR, "Failed to alloc\n"); rc = -ENOMEM; goto err_core_init; } @@ -2651,7 +2683,7 @@ static int venus_hfi_init_regs_and_interrupts( ioremap_nocache(device->register_base, device->register_size); if (!hal->register_base_addr) { dprintk(VIDC_ERR, - "could not map reg addr %d of size %d", + "could not map reg addr %d of size %d\n", device->register_base, device->register_size); goto error_irq_fail; } @@ -2712,7 +2744,7 @@ static inline int venus_hfi_init_clocks(struct msm_vidc_platform_resources *res, venus_hfi_for_each_clock(device, cl, { if (!strcmp(cl->name, "mem_clk") && !res->has_ocmem) { dprintk(VIDC_ERR, - "Found %s on a target that doesn't support ocmem", + "Found %s on a target that doesn't support ocmem\n", cl->name); rc = -ENOENT; goto err_found_bad_ocmem; @@ -2767,7 +2799,7 @@ static inline void venus_hfi_disable_clks(struct venus_hfi_device *device) venus_hfi_for_each_clock(device, cl, { if (!device->clocks_enabled && cl->has_sw_power_collapse) { dprintk(VIDC_DBG, - "Omitting clk_disable of %s in %s as it's already disabled", + "Omitting clk_disable of %s in %s as it's already disabled\n", cl->name, __func__); clk_unprepare(cl->clk); } else { @@ -2841,7 +2873,7 @@ static int venus_hfi_register_iommu_domains(struct venus_hfi_device *device, domain = iommu_group_get_iommudata(iommu_map->group); if (!domain) { dprintk(VIDC_ERR, - "Failed to get domain data for group %p", + "Failed to get domain data for group %p\n", iommu_map->group); rc = -EINVAL; goto fail_group; @@ -2849,7 +2881,7 @@ static int venus_hfi_register_iommu_domains(struct venus_hfi_device *device, iommu_map->domain = msm_find_domain_no(domain); if (iommu_map->domain < 0) { dprintk(VIDC_ERR, - "Failed to get domain index for domain %p", + "Failed to get domain index for domain %p\n", domain); rc = -EINVAL; goto fail_group; @@ -2999,7 +3031,7 @@ static int venus_hfi_unset_ocmem(void *dev) goto ocmem_unset_failed; } if (!device->resources.ocmem.buf) { - dprintk(VIDC_INFO, "%s Trying to free OCMEM which is not set", + dprintk(VIDC_INFO, "%s Trying to free OCMEM which is not set\n", __func__); rc = -EINVAL; goto ocmem_unset_failed; @@ -3100,7 +3132,7 @@ static int venus_hfi_free_ocmem(void *dev) int rc = 0; if (!device) { - dprintk(VIDC_ERR, "%s invalid device handle %p", + dprintk(VIDC_ERR, "%s invalid device handle %p\n", __func__, device); return -EINVAL; } @@ -3409,14 +3441,14 @@ static int venus_hfi_load_fw(void *dev) mutex_init(&device->clk_pwr_lock); rc = venus_hfi_iommu_attach(device); if (rc) { - dprintk(VIDC_ERR, "Failed to attach iommu"); + dprintk(VIDC_ERR, "Failed to attach iommu\n"); goto fail_iommu_attach; } mutex_lock(&device->clk_pwr_lock); rc = venus_hfi_enable_regulators(device); if (rc) { - dprintk(VIDC_ERR, "Failed to enable GDSC %d", rc); + dprintk(VIDC_ERR, "Failed to enable GDSC %d\n", rc); mutex_unlock(&device->clk_pwr_lock); goto fail_enable_gdsc; } @@ -3527,7 +3559,7 @@ static int venus_hfi_get_fw_info(void *dev, enum fw_info info) break; default: - dprintk(VIDC_ERR, "Invalid fw info requested"); + dprintk(VIDC_ERR, "Invalid fw info requested\n"); } return rc; } @@ -3582,13 +3614,9 @@ int venus_hfi_capability_check(u32 fourcc, u32 width, return -EINVAL; } - if (msm_vp8_low_tier && fourcc == V4L2_PIX_FMT_VP8) { - *max_width = DEFAULT_WIDTH; - *max_height = DEFAULT_HEIGHT; - } if (width > *max_width) { dprintk(VIDC_ERR, - "Unsupported width = %u supported max width = %u", + "Unsupported width = %u supported max width = %u\n", width, *max_width); rc = -ENOTSUPP; } @@ -3602,17 +3630,17 @@ static void *venus_hfi_add_device(u32 device_id, struct venus_hfi_device *hdevice = NULL; int rc = 0; - if (device_id || !res || !callback) { - dprintk(VIDC_ERR, "Invalid Paramters"); + if (!res || !callback) { + dprintk(VIDC_ERR, "Invalid Parameters\n"); return NULL; } - dprintk(VIDC_INFO, "entered , device_id: %d", device_id); + dprintk(VIDC_INFO, "entered , device_id: %d\n", device_id); hdevice = (struct venus_hfi_device *) kzalloc(sizeof(struct venus_hfi_device), GFP_KERNEL); if (!hdevice) { - dprintk(VIDC_ERR, "failed to allocate new device"); + dprintk(VIDC_ERR, "failed to allocate new device\n"); goto err_alloc; } @@ -3766,6 +3794,7 @@ int venus_hfi_initialize(struct hfi_device *hdev, u32 device_id, if (IS_ERR_OR_NULL(hdev->hfi_device_data)) { rc = PTR_ERR(hdev->hfi_device_data); + rc = !rc ? -EINVAL : rc; goto err_venus_hfi_init; } diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h index 9a2dc4802bff..f79d43c20304 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h @@ -43,6 +43,7 @@ #define HAL_BUFFERFLAG_READONLY 0x00000200 #define HAL_BUFFERFLAG_ENDOFSUBFRAME 0x00000400 #define HAL_BUFFERFLAG_EOSEQ 0x00200000 +#define HAL_BUFFERFLAG_YUV_601_709_CSC_CLAMP 0x10000000 #define HAL_BUFFERFLAG_DROP_FRAME 0x20000000 #define HAL_BUFFERFLAG_TS_DISCONTINUITY 0x40000000 #define HAL_BUFFERFLAG_TS_ERROR 0x80000000 diff --git a/drivers/media/platform/msm/vpu/vpu_hfi.c b/drivers/media/platform/msm/vpu/vpu_hfi.c index 7485df490edf..7b9b49c7a868 100644 --- a/drivers/media/platform/msm/vpu/vpu_hfi.c +++ b/drivers/media/platform/msm/vpu/vpu_hfi.c @@ -128,9 +128,23 @@ struct vpu_hfi_device { struct vpu_platform_resources *platform_resouce; }; +struct addr_range { + u32 start; + u32 end; +}; + /* global */ static struct vpu_hfi_device g_hfi_device; +static struct addr_range csr_skip_addrs[] = { + /* start and end offsets of inaccessible address ranges */ + { 0x0000, 0x000F }, + { 0x0018, 0x001B }, + { 0x0020, 0x0037 }, + { 0x00C0, 0x00DF }, + { 0x01A0, 0x01AF }, +}; + /* * write a packet into the IPC memory * caller needs to lock the queue @@ -422,9 +436,9 @@ static int vpu_hfi_queues_init(struct vpu_hfi_device *dev, void *start_addr, (u32) dev->mem_base; dev->rxqs[i].q_data_size = vpu_hfi_q_size(RX_Q_IDX_TO_Q_ID(i)); dev->rxqs[i].state = HFI_QUEUE_STATE_DISABLED; - vpu_hfi_init_qhdr(dev->txqs[i].q_hdr, false, - dev->txqs[i].q_data_offset, - dev->txqs[i].q_data_size); + vpu_hfi_init_qhdr(dev->rxqs[i].q_hdr, false, + dev->rxqs[i].q_data_offset, + dev->rxqs[i].q_data_size); qmem_size_sum += vpu_hfi_q_size(RX_Q_IDX_TO_Q_ID(i)); mutex_init(&dev->rxqs[i].lock); @@ -519,7 +533,7 @@ static int vpu_hfi_boot(struct vpu_hfi_device *hdevice) /* wait for VPU FW up (poll status register) */ timeout = vpu_pil_timeout/20; - while ((raw_hfi_status_read((u32)(hdevice->reg_base)) & 1) == 0) { + while (!raw_hfi_fw_ready((u32)hdevice->reg_base)) { if (timeout-- <= 0) { /* FW bootup timed out */ pr_err("VPU FW bootup timeout\n"); @@ -791,9 +805,9 @@ void vpu_hfi_stop(void) free_irq(hdevice->irq_wd, hdevice); if (!hdevice->watchdog_bited) { - if (!raw_hfi_fw_halted((u32)(hdevice->reg_base))) { + if (!raw_hfi_fw_halted((u32)hdevice->reg_base)) { msleep(20); - if (!raw_hfi_fw_halted((u32)(hdevice->reg_base))) + if (!raw_hfi_fw_halted((u32)hdevice->reg_base)) pr_warn("firmware not halted!\n"); } } @@ -1144,15 +1158,48 @@ size_t vpu_hfi_print_queues(char *buf, size_t buf_size) return strlcat(buf, "", buf_size); } - int vpu_hfi_dump_csr_regs(char *buf, size_t buf_size) { struct vpu_hfi_device *hdevice = &g_hfi_device; u32 v_base = (u32)(hdevice->reg_base); u32 p_base = (u32)(hdevice->platform_resouce->register_base_phy); + u32 last_addr = v_base + + csr_skip_addrs[ARRAY_SIZE(csr_skip_addrs) - 1].end; + u32 addr; + char temp[32]; + int i = 0, skip = 0, temp_size = 32; strlcpy(buf, "", buf_size); - raw_hfi_dump_csr_regs(v_base, p_base, buf, buf_size); + + /* read one at a time. Print 4 registers per line */ + for (addr = v_base; addr <= last_addr; addr += sizeof(u32)) { + if ((addr % 0x10) == 0) { + snprintf(temp, temp_size, "@0x%08x -", + (addr - v_base + p_base)); + strlcat(buf, temp, buf_size); + } + + if ((addr - v_base) >= csr_skip_addrs[i].start && + (addr - v_base) <= csr_skip_addrs[i].end) { + skip = 1; + snprintf(temp, temp_size, " xxxxxxxxxx"); + strlcat(buf, temp, buf_size); + } else { + if (skip) { + i++; + skip = 0; + } + + snprintf(temp, temp_size, " 0x%08x", + readl_relaxed(addr + 0 * sizeof(u32))); + strlcat(buf, temp, buf_size); + } + + if ((addr % 0x10) == 0xc) { + snprintf(temp, temp_size, "\n"); + strlcat(buf, temp, buf_size); + } + } return strlcat(buf, "\n", buf_size); } diff --git a/drivers/media/platform/msm/vpu/vpu_hfi_intf.h b/drivers/media/platform/msm/vpu/vpu_hfi_intf.h index ab0621eadcc2..046c54b21233 100644 --- a/drivers/media/platform/msm/vpu/vpu_hfi_intf.h +++ b/drivers/media/platform/msm/vpu/vpu_hfi_intf.h @@ -44,10 +44,9 @@ struct hfi_queue_table_header { #define VPU_MAX_QUEUE_NUM (VPU_MAX_TXQ_NUM + VPU_MAX_RXQ_NUM) /* qhdr_q_size in bytes */ -#define VPU_SYS_QUEUE_SIZE 1024 -#define VPU_SESSION_QUEUE_SIZE 2048 -#define VPU_LOGGING_QUEUE_SIZE 2048 /* assume this size */ -#define VPU_TUNE_QUEUE_SIZE 20480 +#define VPU_SYS_QUEUE_SIZE SZ_1K +#define VPU_SESSION_QUEUE_SIZE SZ_2K +#define VPU_LOGGING_QUEUE_SIZE SZ_64K #define TX_Q_IDX_TO_Q_ID(idx) (VPU_SYSTEM_CMD_QUEUE_ID + (idx * 2)) #define RX_Q_IDX_TO_Q_ID(idx) (VPU_SYSTEM_MSG_QUEUE_ID + (idx * 2)) @@ -146,8 +145,6 @@ static inline u32 vpu_hfi_q_size(int q_id) * VPU CSR register offsets */ -/* A5 control */ -#define VPU_CSR_A5_CONTROL 0x018 /* fw -> apps sgi interrupt */ #define VPU_CSR_APPS_SGI_STS 0x050 #define VPU_CSR_APPS_SGI_CLR 0x054 @@ -226,43 +223,18 @@ static inline u32 raw_hfi_status_read(u32 regbase) return val; } -static inline bool raw_hfi_fw_halted(u32 regbase) +static inline bool raw_hfi_fw_ready(u32 regbase) { - u32 val; + u32 val = raw_hfi_status_read(regbase); - val = readl_relaxed(regbase + VPU_CSR_A5_CONTROL); - - /* bit 7 is Firmware WFI status */ - if (val & (1 << 7)) - return true; - else - return false; + return (val & 0x1) ? true : false; } -#define add2buf(dest, dest_size, temp, temp_size, __fmt, arg...) \ - do { \ - snprintf(temp, temp_size, __fmt, ## arg); \ - strlcat(dest, temp, dest_size); \ - } while (0) - -static inline void raw_hfi_dump_csr_regs(u32 v_base, u32 p_base, - char *buf, size_t buf_size) +static inline bool raw_hfi_fw_halted(u32 regbase) { - /* temporary buffer */ - size_t t_s = SZ_128; - char t[t_s]; - u32 addr; - u32 last_addr = v_base + VPU_HW_VERSION; - - /* read each register (4 per line) */ - for (addr = v_base; addr <= last_addr; addr += 4 * sizeof(u32)) - add2buf(buf, buf_size, t, t_s, - "@0x%08x - 0x%08x 0x%08x 0x%08x 0x%08x\n", - (addr - v_base + p_base), - readl_relaxed(addr + 0 * sizeof(u32)), - readl_relaxed(addr + 1 * sizeof(u32)), - readl_relaxed(addr + 2 * sizeof(u32)), - readl_relaxed(addr + 3 * sizeof(u32))); + u32 val = raw_hfi_status_read(regbase); + + return (val & 0x2) ? true : false; } #endif /* __H_VPU_HFI_INTF_H__ */ diff --git a/drivers/media/platform/msm/vpu/vpu_resources.c b/drivers/media/platform/msm/vpu/vpu_resources.c index d770e72604f6..520d75d08f54 100644 --- a/drivers/media/platform/msm/vpu/vpu_resources.c +++ b/drivers/media/platform/msm/vpu/vpu_resources.c @@ -79,9 +79,9 @@ static struct vpu_iommu_map iommus_array[VPU_MAX_IOMMU_DOMAIN] = { }; static const char * const clk_table_dt_entries[] = { - [VPU_BUS_CLK] = "qti,bus-clk-load-freq-tbl", - [VPU_MAPLE_CLK] = "qti,maple-clk-load-freq-tbl", - [VPU_VDP_CLK] = "qti,vdp-clk-load-freq-tbl", + [VPU_BUS_CLK] = "qcom,bus-clk-load-freq-tbl", + [VPU_MAPLE_CLK] = "qcom,maple-clk-load-freq-tbl", + [VPU_VDP_CLK] = "qcom,vdp-clk-load-freq-tbl", }; struct bus_pdata_config { @@ -93,7 +93,7 @@ struct bus_pdata_config { static struct bus_pdata_config bus_pdata_config = { .masters = {MSM_BUS_MASTER_VPU, MSM_BUS_MASTER_VPU}, .slaves = {MSM_BUS_SLAVE_EBI_CH0, MSM_BUS_SLAVE_EBI_CH0}, - .name = "qti,bus-load-vector-tbl", + .name = "qcom,bus-load-vector-tbl", }; static int __get_u32_array_num_elements(struct platform_device *pdev, @@ -267,7 +267,7 @@ static int __vpu_load_iommu_maps(struct vpu_platform_resources *res) u32 count = 0; num_elements = of_property_count_strings(pdev->dev.of_node, - "qti,enabled-iommu-maps"); + "qcom,enabled-iommu-maps"); if (num_elements <= 0) { pr_warn("No list of IOMMUs to be enabled\n"); return ret; @@ -278,7 +278,7 @@ static int __vpu_load_iommu_maps(struct vpu_platform_resources *res) for (i = 0; i < num_elements; i++) { ret = of_property_read_string_index(pdev->dev.of_node, - "qti,enabled-iommu-maps", i, &name); + "qcom,enabled-iommu-maps", i, &name); if (ret) return ret; diff --git a/drivers/media/platform/msm/vpu/vpu_v4l2.c b/drivers/media/platform/msm/vpu/vpu_v4l2.c index 3f2aba7b8b48..7ce992614fd2 100644 --- a/drivers/media/platform/msm/vpu/vpu_v4l2.c +++ b/drivers/media/platform/msm/vpu/vpu_v4l2.c @@ -618,7 +618,7 @@ static int vpu_remove(struct platform_device *pdev) static const struct of_device_id vpu_dt_match[] = { - {.compatible = "qti,vpu"}, + {.compatible = "qcom,vpu"}, {} }; diff --git a/drivers/media/platform/msm/wfd/wfd-ioctl.c b/drivers/media/platform/msm/wfd/wfd-ioctl.c index 26b526e05e92..40e12937a09c 100644 --- a/drivers/media/platform/msm/wfd/wfd-ioctl.c +++ b/drivers/media/platform/msm/wfd/wfd-ioctl.c @@ -1425,6 +1425,7 @@ static struct vb2_mem_ops wfd_vb2_mem_ops = { int wfd_initialize_vb2_queue(struct vb2_queue *q, void *priv) { + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_USERPTR; q->ops = &wfd_vidbuf_ops; diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 66f80973596b..ed79d7b78e7d 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -1724,9 +1724,9 @@ static long round_clock_rate(u8 clock, unsigned long rate) /* CPU FREQ table, may be changed due to if MAX_OPP is supported. */ static struct cpufreq_frequency_table db8500_cpufreq_table[] = { - { .frequency = 200000, .index = ARM_EXTCLK,}, - { .frequency = 400000, .index = ARM_50_OPP,}, - { .frequency = 800000, .index = ARM_100_OPP,}, + { .frequency = 200000, .driver_data = ARM_EXTCLK,}, + { .frequency = 400000, .driver_data = ARM_50_OPP,}, + { .frequency = 800000, .driver_data = ARM_100_OPP,}, { .frequency = CPUFREQ_TABLE_END,}, /* To be used for MAX_OPP. */ { .frequency = CPUFREQ_TABLE_END,}, }; @@ -1901,7 +1901,7 @@ static int set_armss_rate(unsigned long rate) return -EINVAL; /* Set the new arm opp. */ - return db8500_prcmu_set_arm_opp(db8500_cpufreq_table[i].index); + return db8500_prcmu_set_arm_opp(db8500_cpufreq_table[i].driver_data); } static int set_plldsi_rate(unsigned long rate) @@ -3105,7 +3105,7 @@ static void db8500_prcmu_update_cpufreq(void) { if (prcmu_has_arm_maxopp()) { db8500_cpufreq_table[3].frequency = 1000000; - db8500_cpufreq_table[3].index = ARM_MAX_OPP; + db8500_cpufreq_table[3].driver_data = ARM_MAX_OPP; } } diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 872e9bed53f1..ae0174d09790 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -618,4 +618,5 @@ source "drivers/misc/carma/Kconfig" source "drivers/misc/altera-stapl/Kconfig" source "drivers/misc/mei/Kconfig" source "drivers/misc/vmw_vmci/Kconfig" +source "drivers/misc/qcom/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 7c1e40658af6..a9a93e5e24cf 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -62,3 +62,4 @@ obj-$(CONFIG_QFP_FUSE) += qfp_fuse.o obj-$(CONFIG_TI_DRV2667) += ti_drv2667.o obj-$(CONFIG_QPNP_MISC) += qpnp-misc.o obj-$(CONFIG_APQ8084_DOCKING_STATION) += apq8084_dock.o +obj-y += qcom/ diff --git a/drivers/misc/apq8084_dock.c b/drivers/misc/apq8084_dock.c index bfd19baa84e3..fdf18602a77a 100644 --- a/drivers/misc/apq8084_dock.c +++ b/drivers/misc/apq8084_dock.c @@ -80,7 +80,7 @@ static int apq8084_dock_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dock); INIT_WORK(&dock->dock_work, dock_detected_work); - dock->dock_detect = of_get_named_gpio(node, "qti,dock-detect-gpio", 0); + dock->dock_detect = of_get_named_gpio(node, "qcom,dock-detect-gpio", 0); if (dock->dock_detect < 0) { dev_err(dock->dev, "unable to get dock-detect-gpio\n"); return dock->dock_detect; @@ -94,7 +94,7 @@ static int apq8084_dock_probe(struct platform_device *pdev) return ret; dock->dock_hub_reset = of_get_named_gpio(node, - "qti,dock-hub-reset-gpio", 0); + "qcom,dock-hub-reset-gpio", 0); if (dock->dock_hub_reset < 0) { dev_err(dock->dev, "unable to get dock-hub-reset-gpio\n"); return dock->dock_hub_reset; @@ -106,7 +106,7 @@ static int apq8084_dock_probe(struct platform_device *pdev) return ret; dock->dock_eth_reset = of_get_named_gpio(node, - "qti,dock-eth-reset-gpio", 0); + "qcom,dock-eth-reset-gpio", 0); if (dock->dock_eth_reset < 0) { dev_err(dock->dev, "unable to get dock-eth-reset-gpio\n"); return dock->dock_eth_reset; @@ -117,7 +117,7 @@ static int apq8084_dock_probe(struct platform_device *pdev) if (ret) return ret; - dock->dock_enable = of_get_named_gpio(node, "qti,dock-enable-gpio", 0); + dock->dock_enable = of_get_named_gpio(node, "qcom,dock-enable-gpio", 0); if (dock->dock_enable < 0) { dev_err(dock->dev, "unable to get dock-enable-gpio\n"); return dock->dock_enable; @@ -145,7 +145,7 @@ static int apq8084_dock_remove(struct platform_device *pdev) } static struct of_device_id of_match_table[] = { - { .compatible = "qti,apq8084-dock", + { .compatible = "qcom,apq8084-dock", } }; diff --git a/drivers/misc/qcom/Kconfig b/drivers/misc/qcom/Kconfig new file mode 100644 index 000000000000..237854828a2d --- /dev/null +++ b/drivers/misc/qcom/Kconfig @@ -0,0 +1,8 @@ +config MSM_QDSP6V2_CODECS + bool "Audio QDSP6V2 APR support" + depends on MSM_SMD + help + Enable Audio codecs with APR IPC protocol support between + application processor and QDSP6 for B-family. APR is + used by audio driver to configure QDSP6's + ASM, ADM and AFE. diff --git a/drivers/misc/qcom/Makefile b/drivers/misc/qcom/Makefile new file mode 100644 index 000000000000..120bdddcbc84 --- /dev/null +++ b/drivers/misc/qcom/Makefile @@ -0,0 +1 @@ +obj-y += qdsp6v2/ diff --git a/drivers/misc/qcom/qdsp6v2/Makefile b/drivers/misc/qcom/qdsp6v2/Makefile new file mode 100644 index 000000000000..26f3c404e705 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_MSM_QDSP6V2_CODECS) += aac_in.o qcelp_in.o evrc_in.o amrnb_in.o audio_utils.o +obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_wma.o audio_wmapro.o audio_aac.o audio_multi_aac.o audio_utils_aio.o +obj-$(CONFIG_MSM_QDSP6V2_CODECS) += q6audio_v2.o q6audio_v2_aio.o +obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_amrwbplus.o audio_evrc.o audio_qcelp.o amrwb_in.o diff --git a/drivers/misc/qcom/qdsp6v2/aac_in.c b/drivers/misc/qcom/qdsp6v2/aac_in.c new file mode 100644 index 000000000000..14dc7348e0b3 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/aac_in.c @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2010-2013, The Linux Foundation. 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 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. + * + */ +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/msm_audio_aac.h> +#include <asm/atomic.h> +#include <asm/ioctls.h> +#include "audio_utils.h" + + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 5 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((1536+sizeof(struct meta_out_dsp)) * 5)) + +#define AAC_FORMAT_ADTS 65535 + +/* ------------------- device --------------------- */ +static long aac_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_aac_enc_config *enc_cfg; + struct msm_audio_aac_config *aac_config; + uint32_t aac_mode = AAC_ENC_MODE_AAC_LC; + + enc_cfg = audio->enc_cfg; + aac_config = audio->codec_cfg; + /* ENCODE CFG (after new set of API's are published )bharath*/ + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + + if (audio->opened) { + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + } else { + if (audio->feedback == NON_TUNNEL_MODE) { + pr_debug("%s: starting in non_tunnel mode", + __func__); + rc = q6asm_open_read_write(audio->ac, + FORMAT_MPEG4_AAC, FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:open read write failed\n", + __func__); + break; + } + } + if (audio->feedback == TUNNEL_MODE) { + pr_debug("%s: starting in tunnel mode", + __func__); + rc = q6asm_open_read(audio->ac, + FORMAT_MPEG4_AAC); + + if (rc < 0) { + pr_err("%s:open read failed\n", + __func__); + break; + } + } + audio->stopped = 0; + } + + pr_debug("%s:sbr_ps_flag = %d, sbr_flag = %d\n", __func__, + aac_config->sbr_ps_on_flag, aac_config->sbr_on_flag); + if (aac_config->sbr_ps_on_flag) + aac_mode = AAC_ENC_MODE_EAAC_P; + else if (aac_config->sbr_on_flag) + aac_mode = AAC_ENC_MODE_AAC_P; + else + aac_mode = AAC_ENC_MODE_AAC_LC; + + rc = q6asm_enc_cfg_blk_aac(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->sample_rate, + enc_cfg->channels, + enc_cfg->bit_rate, + aac_mode, + enc_cfg->stream_format); + if (rc < 0) { + pr_err("%s:session id %d: cmd media format block" + "failed\n", __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("%s:session id %d: media format block" + "failed\n", __func__, audio->ac->session); + break; + } + } + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure" + "failed rc=%d\n", __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: Rxed AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed" + "rc=%d\n", __func__, audio->ac->session, rc); + break; + } + break; + } + case AUDIO_GET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config cfg; + struct msm_audio_aac_enc_config *enc_cfg; + memset(&cfg, 0, sizeof(cfg)); + enc_cfg = audio->enc_cfg; + if (enc_cfg->channels == CH_MODE_MONO) + cfg.channels = 1; + else + cfg.channels = 2; + cfg.sample_rate = enc_cfg->sample_rate; + cfg.bit_rate = enc_cfg->bit_rate; + /* ADTS(-1) to ADTS(0x00), RAW(0x00) to RAW(0x03) */ + cfg.stream_format = ((enc_cfg->stream_format == \ + 0x00) ? AUDIO_AAC_FORMAT_ADTS : AUDIO_AAC_FORMAT_RAW); + pr_debug("%s:session id %d: Get-aac-cfg: format=%d sr=%d" + "bitrate=%d\n", __func__, audio->ac->session, + cfg.stream_format, cfg.sample_rate, cfg.bit_rate); + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + break; + } + case AUDIO_SET_AAC_ENC_CONFIG: { + struct msm_audio_aac_enc_config cfg; + struct msm_audio_aac_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + pr_debug("%s:session id %d: Set-aac-cfg: stream=%d\n", __func__, + audio->ac->session, cfg.stream_format); + + if ((cfg.stream_format != AUDIO_AAC_FORMAT_RAW) && + (cfg.stream_format != AAC_FORMAT_ADTS)) { + pr_err("%s:session id %d: unsupported AAC format\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + + if (cfg.channels == 1) { + cfg.channels = CH_MODE_MONO; + } else if (cfg.channels == 2) { + cfg.channels = CH_MODE_STEREO; + } else { + rc = -EINVAL; + break; + } + if ((cfg.sample_rate < 8000) && (cfg.sample_rate > 48000)) { + pr_err("%s: ERROR in setting samplerate = %d\n", + __func__, cfg.sample_rate); + rc = -EINVAL; + break; + } + /* For aac-lc, min_bit_rate = min(24Kbps, 0.5*SR*num_chan); + max_bi_rate = min(192Kbps, 6*SR*num_chan); + min_sample_rate = 8000Hz, max_rate=48000 */ + if ((cfg.bit_rate < 4000) || (cfg.bit_rate > 192000)) { + pr_err("%s: ERROR in setting bitrate = %d\n", + __func__, cfg.bit_rate); + rc = -EINVAL; + break; + } + enc_cfg->sample_rate = cfg.sample_rate; + enc_cfg->channels = cfg.channels; + enc_cfg->bit_rate = cfg.bit_rate; + enc_cfg->stream_format = + ((cfg.stream_format == AUDIO_AAC_FORMAT_RAW) ? \ + 0x03 : 0x00); + pr_debug("%s:session id %d: Set-aac-cfg:SR= 0x%x ch=0x%x" + "bitrate=0x%x, format(adts/raw) = %d\n", + __func__, audio->ac->session, enc_cfg->sample_rate, + enc_cfg->channels, enc_cfg->bit_rate, + enc_cfg->stream_format); + break; + } + case AUDIO_GET_AAC_CONFIG: { + if (copy_to_user((void *)arg, &audio->codec_cfg, + sizeof(struct msm_audio_aac_config))) { + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config aac_cfg; + struct msm_audio_aac_config *audio_aac_cfg; + struct msm_audio_aac_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + audio_aac_cfg = audio->codec_cfg; + + if (copy_from_user(&aac_cfg, (void *)arg, + sizeof(struct msm_audio_aac_config))) { + rc = -EFAULT; + break; + } + pr_debug("%s:session id %d: AUDIO_SET_AAC_CONFIG: sbr_flag = %d" + " sbr_ps_flag = %d\n", __func__, + audio->ac->session, aac_cfg.sbr_on_flag, + aac_cfg.sbr_ps_on_flag); + audio_aac_cfg->sbr_on_flag = aac_cfg.sbr_on_flag; + audio_aac_cfg->sbr_ps_on_flag = aac_cfg.sbr_ps_on_flag; + if ((audio_aac_cfg->sbr_on_flag == 1) || + (audio_aac_cfg->sbr_ps_on_flag == 1)) { + if (enc_cfg->sample_rate < 24000) { + pr_err("%s: ERROR in setting samplerate = %d" + "\n", __func__, enc_cfg->sample_rate); + rc = -EINVAL; + break; + } + } + break; + } + default: + rc = -EINVAL; + } + return rc; +} + +static int aac_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_aac_enc_config *enc_cfg; + struct msm_audio_aac_config *aac_config; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) { + pr_err("%s: Could not allocate memory for aac" + "driver\n", __func__); + return -ENOMEM; + } + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_aac_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + pr_err("%s:session id %d: Could not allocate memory for aac" + "config param\n", __func__, audio->ac->session); + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + pr_err("%s:session id %d: Could not allocate memory for aac" + "config\n", __func__, audio->ac->session); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + aac_config = audio->codec_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 1536; + audio->max_frames_per_buf = 5; + enc_cfg->sample_rate = 8000; + enc_cfg->channels = 1; + enc_cfg->bit_rate = 16000; + enc_cfg->stream_format = 0x00;/* 0:ADTS, 3:RAW */ + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + aac_config->format = AUDIO_AAC_FORMAT_ADTS; + aac_config->audio_object = AUDIO_AAC_OBJECT_LC; + aac_config->sbr_on_flag = 0; + aac_config->sbr_ps_on_flag = 0; + aac_config->channel_configuration = 1; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for" + "audio client\n", __func__); + kfree(audio->enc_cfg); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + /* open aac encoder in tunnel mode */ + audio->buf_cfg.frames_per_buf = 0x01; + + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_MPEG4_AAC, + FORMAT_LINEAR_PCM); + + if (rc < 0) { + pr_err("%s:session id %d: NT Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + audio->buf_cfg.meta_info_enable = 0x01; + pr_info("%s:session id %d: NT mode encoder success\n", __func__, + audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_MPEG4_AAC); + + if (rc < 0) { + pr_err("%s:session id %d: Tunnel Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration" + "failed rc=%d\n", __func__, + audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + audio->buf_cfg.meta_info_enable = 0x00; + pr_info("%s:session id %d: T mode encoder success\n", __func__, + audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_ioctl = aac_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = aac_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +}; + +struct miscdevice audio_aac_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aac_in", + .fops = &audio_in_fops, +}; + +static int __init aac_in_init(void) +{ + return misc_register(&audio_aac_in_misc); +} +device_initcall(aac_in_init); diff --git a/drivers/misc/qcom/qdsp6v2/amrnb_in.c b/drivers/misc/qcom/qdsp6v2/amrnb_in.c new file mode 100644 index 000000000000..91c588c064d5 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/amrnb_in.c @@ -0,0 +1,285 @@ +/* Copyright (c) 2010-2012, The Linux Foundation. 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 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. + * +*/ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/dma-mapping.h> +#include <linux/msm_audio_amrnb.h> +#include <asm/atomic.h> +#include <asm/ioctls.h> +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((32+sizeof(struct meta_out_dsp)) * 10)) + +/* ------------------- device --------------------- */ +static long amrnb_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_amrnb_enc_config_v2 *enc_cfg; + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + rc = q6asm_enc_cfg_blk_amrnb(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->band_mode, + enc_cfg->dtx_enable); + + if (rc < 0) { + pr_err("%s:session id %d: cmd amrnb media format block" + "failed\n", __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block" + "failed\n", __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", + __func__, audio->ac->session, + audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed" + "rc=%d\n", __func__, + audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:AUDIO_STOP\n", __func__); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed" + "rc=%d\n", __func__, + audio->ac->session, rc); + break; + } + break; + } + case AUDIO_GET_AMRNB_ENC_CONFIG_V2: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_amrnb_enc_config_v2))) + rc = -EFAULT; + break; + } + case AUDIO_SET_AMRNB_ENC_CONFIG_V2: { + struct msm_audio_amrnb_enc_config_v2 cfg; + struct msm_audio_amrnb_enc_config_v2 *enc_cfg; + enc_cfg = audio->enc_cfg; + if (copy_from_user(&cfg, (void *) arg, + sizeof(struct msm_audio_amrnb_enc_config_v2))) { + rc = -EFAULT; + break; + } + if (cfg.band_mode > 8 || + cfg.band_mode < 1) { + pr_err("%s:session id %d: invalid band mode\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + /* AMR NB encoder accepts values between 0-7 + while openmax provides value between 1-8 + as per spec */ + enc_cfg->band_mode = (cfg.band_mode - 1); + enc_cfg->dtx_enable = (cfg.dtx_enable ? 1 : 0); + enc_cfg->frame_format = 0; + pr_debug("%s:session id %d: band_mode = 0x%x dtx_enable=0x%x\n", + __func__, audio->ac->session, + enc_cfg->band_mode, enc_cfg->dtx_enable); + break; + } + default: + rc = -EINVAL; + } + return rc; +} + +static int amrnb_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_amrnb_enc_config_v2 *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) { + pr_err("%s Could not allocate memory for amrnb" + "driver\n", __func__); + return -ENOMEM; + } + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrnb_enc_config_v2), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + pr_err("%s:session id %d: Could not allocate memory for aac" + "config param\n", __func__, audio->ac->session); + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 32; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->band_mode = 7; + enc_cfg->dtx_enable = 0; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for audio" + "client\n", __func__); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open amrnb encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_AMRNB, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: NT mode encoder success\n", + __func__, audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_AMRNB); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration" + "failed rc=%d\n", __func__, audio->ac->session, + rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: T mode encoder success\n", + __func__, audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_ioctl = amrnb_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = amrnb_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +}; + +struct miscdevice audio_amrnb_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrnb_in", + .fops = &audio_in_fops, +}; + +static int __init amrnb_in_init(void) +{ + return misc_register(&audio_amrnb_in_misc); +} + +device_initcall(amrnb_in_init); diff --git a/drivers/misc/qcom/qdsp6v2/amrwb_in.c b/drivers/misc/qcom/qdsp6v2/amrwb_in.c new file mode 100644 index 000000000000..ea3fe5b84068 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/amrwb_in.c @@ -0,0 +1,282 @@ +/* Copyright (c) 2011-2012, The Linux Foundation. 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 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. + */ + +#include <linux/dma-mapping.h> +#include <linux/fs.h> +#include <linux/module.h> +#include <linux/miscdevice.h> +#include <linux/msm_audio_amrwb.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/wait.h> +#include <asm/atomic.h> +#include <asm/ioctls.h> +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((61+sizeof(struct meta_out_dsp)) * 10)) + +/* ------------------- device --------------------- */ +static long amrwb_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_amrwb_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + rc = q6asm_enc_cfg_blk_amrwb(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->band_mode, + enc_cfg->dtx_enable); + + if (rc < 0) { + pr_err("%s:session id %d: cmd amrwb media format block" + "failed\n", __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block" + "failed\n", __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", + __func__, audio->ac->session, + audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed" + "rc=%d\n", __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:AUDIO_STOP\n", __func__); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed" + "rc=%d\n", __func__, audio->ac->session, rc); + break; + } + break; + } + case AUDIO_GET_AMRWB_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_amrwb_enc_config))) + rc = -EFAULT; + break; + } + case AUDIO_SET_AMRWB_ENC_CONFIG: { + struct msm_audio_amrwb_enc_config cfg; + struct msm_audio_amrwb_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + if (copy_from_user(&cfg, (void *) arg, + sizeof(struct msm_audio_amrwb_enc_config))) { + rc = -EFAULT; + break; + } + if (cfg.band_mode > 8) { + pr_err("%s:session id %d: invalid band mode\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + /* ToDo: AMR WB encoder accepts values between 0-8 + while openmax provides value between 9-17 + as per spec */ + enc_cfg->band_mode = cfg.band_mode; + enc_cfg->dtx_enable = (cfg.dtx_enable ? 1 : 0); + /* Currently DSP does not support different frameformat */ + enc_cfg->frame_format = 0; + pr_debug("%s:session id %d: band_mode = 0x%x dtx_enable=0x%x\n", + __func__, audio->ac->session, + enc_cfg->band_mode, enc_cfg->dtx_enable); + break; + } + default: + rc = -EINVAL; + } + return rc; +} + +static int amrwb_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_amrwb_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) { + pr_err("%s: Could not allocate memory for amrwb driver\n", + __func__); + return -ENOMEM; + } + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrwb_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + pr_err("%s:session id %d: Could not allocate memory for amrwb" + "config param\n", __func__, audio->ac->session); + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 32; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->band_mode = 8; + enc_cfg->dtx_enable = 0; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 16000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s:audio[%p]: Could not allocate memory for audio" + "client\n", __func__, audio); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open amrwb encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_AMRWB, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: NT mode encoder success\n", + __func__, audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_AMRWB); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration" + "failed rc=%d\n", __func__, audio->ac->session, + rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: T mode encoder success\n", + __func__, audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_ioctl = amrwb_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = amrwb_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +}; + +struct miscdevice audio_amrwb_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrwb_in", + .fops = &audio_in_fops, +}; + +static int __init amrwb_in_init(void) +{ + return misc_register(&audio_amrwb_in_misc); +} + +device_initcall(amrwb_in_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_aac.c b/drivers/misc/qcom/qdsp6v2/audio_aac.c new file mode 100644 index 000000000000..caeb79d22880 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_aac.c @@ -0,0 +1,309 @@ +/* aac audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. + * + * 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 <linux/msm_audio_aac.h> +#include "audio_utils_aio.h" + +#define AUDIO_AAC_DUAL_MONO_INVALID -1 +#define PCM_BUFSZ_MIN_AAC ((8*1024) + sizeof(struct dec_meta_out)) + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_aac_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + switch (cmd) { + case AUDIO_START: { + struct asm_aac_cfg aac_cfg; + struct msm_audio_aac_config *aac_config; + uint32_t sbr_ps = 0x00; + pr_debug("%s: AUDIO_START session_id[%d]\n", __func__, + audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, 0, 0); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + /* turn on both sbr and ps */ + rc = q6asm_enable_sbrps(audio->ac, sbr_ps); + if (rc < 0) + pr_err("sbr-ps enable failed\n"); + aac_config = (struct msm_audio_aac_config *)audio->codec_cfg; + if (aac_config->sbr_ps_on_flag) + aac_cfg.aot = AAC_ENC_MODE_EAAC_P; + else if (aac_config->sbr_on_flag) + aac_cfg.aot = AAC_ENC_MODE_AAC_P; + else + aac_cfg.aot = AAC_ENC_MODE_AAC_LC; + + switch (aac_config->format) { + case AUDIO_AAC_FORMAT_ADTS: + aac_cfg.format = 0x00; + break; + case AUDIO_AAC_FORMAT_LOAS: + aac_cfg.format = 0x01; + break; + case AUDIO_AAC_FORMAT_ADIF: + aac_cfg.format = 0x02; + break; + default: + case AUDIO_AAC_FORMAT_RAW: + aac_cfg.format = 0x03; + } + aac_cfg.ep_config = aac_config->ep_config; + aac_cfg.section_data_resilience = + aac_config->aac_section_data_resilience_flag; + aac_cfg.scalefactor_data_resilience = + aac_config->aac_scalefactor_data_resilience_flag; + aac_cfg.spectral_data_resilience = + aac_config->aac_spectral_data_resilience_flag; + aac_cfg.ch_cfg = audio->pcm_cfg.channel_count; + if (audio->feedback == TUNNEL_MODE) { + aac_cfg.sample_rate = aac_config->sample_rate; + aac_cfg.ch_cfg = aac_config->channel_configuration; + } else { + aac_cfg.sample_rate = audio->pcm_cfg.sample_rate; + aac_cfg.ch_cfg = audio->pcm_cfg.channel_count; + } + + pr_debug("%s:format=%x aot=%d ch=%d sr=%d\n", + __func__, aac_cfg.format, + aac_cfg.aot, aac_cfg.ch_cfg, + aac_cfg.sample_rate); + + /* Configure Media format block */ + rc = q6asm_media_format_block_aac(audio->ac, &aac_cfg); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + rc = enable_volume_ramp(audio); + if (rc < 0) { + pr_err("%s: Failed to enable volume ramp\n", + __func__); + } + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + case AUDIO_GET_AAC_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_aac_config))) { + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config *aac_config; + pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__); + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_aac_config))) { + rc = -EFAULT; + break; + } else { + uint16_t sce_left = 1, sce_right = 2; + aac_config = audio->codec_cfg; + /* PL_PR is 0 only need to check PL_SR */ + if (aac_config->dual_mono_mode > + AUDIO_AAC_DUAL_MONO_PL_SR) { + pr_err("%s:AUDIO_SET_AAC_CONFIG: Invalid" + "dual_mono mode =%d\n", __func__, + aac_config->dual_mono_mode); + } else { + /* convert the data from user into sce_left + * and sce_right based on the definitions + */ + pr_debug("%s: AUDIO_SET_AAC_CONFIG: modify" + "dual_mono mode =%d\n", __func__, + aac_config->dual_mono_mode); + switch (aac_config->dual_mono_mode) { + case AUDIO_AAC_DUAL_MONO_PL_PR: + sce_left = 1; + sce_right = 1; + break; + case AUDIO_AAC_DUAL_MONO_SL_SR: + sce_left = 2; + sce_right = 2; + break; + case AUDIO_AAC_DUAL_MONO_SL_PR: + sce_left = 2; + sce_right = 1; + break; + case AUDIO_AAC_DUAL_MONO_PL_SR: + default: + sce_left = 1; + sce_right = 2; + break; + } + rc = q6asm_cfg_dual_mono_aac(audio->ac, + sce_left, sce_right); + if (rc < 0) + pr_err("%s: asm cmd dualmono failed" + " rc=%d\n", __func__, rc); + } + } + break; + } + default: + pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err("%s[%p]:Failed in utils_ioctl: %d\n", + __func__, audio, rc); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + struct msm_audio_aac_config *aac_config = NULL; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_aac_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for aac decode driver\n"); + return -ENOMEM; + } + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + pr_err("%s:Could not allocate memory for aac" + "config\n", __func__); + kfree(audio); + return -ENOMEM; + } + aac_config = audio->codec_cfg; + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AAC; + aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_MPEG4_AAC); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open AAC decoder, expected frames is always 1 + audio->buf_cfg.frames_per_buf = 0x01;*/ + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_MPEG4_AAC); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("audio_aio_open rc=%d\n", rc); + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_aac_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_aac_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:aacdec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_aac_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_aac_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aac", + .fops = &audio_aac_fops, +}; + +static int __init audio_aac_init(void) +{ + return misc_register(&audio_aac_misc); +} + +device_initcall(audio_aac_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrnb.c b/drivers/misc/qcom/qdsp6v2/audio_amrnb.c new file mode 100644 index 000000000000..fc023c1b825f --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_amrnb.c @@ -0,0 +1,165 @@ +/* amrnb audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * + * 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 "audio_utils_aio.h" + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_amrnb_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_amrnb_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for wma decode driver\n"); + return -ENOMEM; + } + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_AMRNB); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_AMRNB); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("audio_aio_open rc=%d\n", rc); + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_amrnb_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_amrnb_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:amrnb decoder open success, session_id = %d\n", __func__, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_amrnb_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_amrnb_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrnb", + .fops = &audio_amrnb_fops, +}; + +static int __init audio_amrnb_init(void) +{ + return misc_register(&audio_amrnb_misc); +} + +device_initcall(audio_amrnb_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrwb.c b/drivers/misc/qcom/qdsp6v2/audio_amrwb.c new file mode 100644 index 000000000000..256da4d37912 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_amrwb.c @@ -0,0 +1,168 @@ +/* amrwb audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * + * 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 "audio_utils_aio.h" + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_amrwb_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_amrwb_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for aac decode driver\n"); + return -ENOMEM; + } + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_AMRWB); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_AMRWB); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("audio_aio_open rc=%d\n", rc); + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_amrwb_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_amrwb_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s: AMRWB dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_amrwb_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_amrwb_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrwb", + .fops = &audio_amrwb_fops, +}; + +static int __init audio_amrwb_init(void) +{ + return misc_register(&audio_amrwb_misc); +} + +device_initcall(audio_amrwb_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c b/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c new file mode 100644 index 000000000000..544bf9ca8a25 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c @@ -0,0 +1,238 @@ +/* amr-wbplus audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. + * + * 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 <linux/msm_audio_amrwbplus.h> +#include "audio_utils_aio.h" + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_amrwbplus_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +static void config_debug_fs(struct q6audio_aio *audio) +{ + if (audio != NULL) { + char name[sizeof("msm_amrwbplus_") + 5]; + snprintf(name, sizeof(name), "msm_amrwbplus_%04x", + audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_amrwbplus_debug_fops); + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); + } +} +#else +static void config_debug_fs(struct q6audio_aio *audio) +{ +} +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct asm_amrwbplus_cfg q6_amrwbplus_cfg; + struct msm_audio_amrwbplus_config_v2 *amrwbplus_drv_config; + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_err("%s[%p]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + amrwbplus_drv_config = + (struct msm_audio_amrwbplus_config_v2 *)audio->codec_cfg; + + q6_amrwbplus_cfg.size_bytes = + amrwbplus_drv_config->size_bytes; + q6_amrwbplus_cfg.version = + amrwbplus_drv_config->version; + q6_amrwbplus_cfg.num_channels = + amrwbplus_drv_config->num_channels; + q6_amrwbplus_cfg.amr_band_mode = + amrwbplus_drv_config->amr_band_mode; + q6_amrwbplus_cfg.amr_dtx_mode = + amrwbplus_drv_config->amr_dtx_mode; + q6_amrwbplus_cfg.amr_frame_fmt = + amrwbplus_drv_config->amr_frame_fmt; + q6_amrwbplus_cfg.amr_lsf_idx = + amrwbplus_drv_config->amr_lsf_idx; + + rc = q6asm_media_format_block_amrwbplus(audio->ac, + &q6_amrwbplus_cfg); + if (rc < 0) { + pr_err("q6asm_media_format_block_amrwb+ failed...\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("%s:AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + case AUDIO_GET_AMRWBPLUS_CONFIG_V2: { + if ((audio) && (arg) && (audio->codec_cfg)) { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_amrwbplus_config_v2))) { + rc = -EFAULT; + pr_err("wb+ config get copy_to_user failed"); + break; + } + } else { + pr_err("wb+ config v2 invalid parameters.."); + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AMRWBPLUS_CONFIG_V2: { + if ((audio) && (arg) && (audio->codec_cfg)) { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_amrwbplus_config_v2))) { + rc = -EFAULT; + pr_err("wb+ config set copy_to_user_failed"); + break; + } + } else { + pr_err("wb+ config invalid parameters.."); + rc = -EFAULT; + break; + } + break; + } + default: + pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("kzalloc failed for amrwb+ decode driver\n"); + return -ENOMEM; + } + audio->codec_cfg = + kzalloc(sizeof(struct msm_audio_amrwbplus_config_v2), GFP_KERNEL); + if (audio->codec_cfg == NULL) { + pr_err("%s:failed kzalloc for amrwb+ config structure", + __func__); + kfree(audio); + return -ENOMEM; + } + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + + audio->ac = + q6asm_audio_client_alloc((app_cb) q6_audio_cb, (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_AMR_WB_PLUS); + if (rc < 0) { + pr_err("amrwbplus NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_AMR_WB_PLUS); + if (rc < 0) { + pr_err("wb+ T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("audio_amrwbplus Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("audio_aio_open rc=%d\n", rc); + goto fail; + } + + config_debug_fs(audio); + pr_debug("%s: AMRWBPLUS dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_amrwbplus_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_amrwbplus_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrwbplus", + .fops = &audio_amrwbplus_fops, +}; + +static int __init audio_amrwbplus_init(void) +{ + return misc_register(&audio_amrwbplus_misc); +} + +device_initcall(audio_amrwbplus_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_evrc.c b/drivers/misc/qcom/qdsp6v2/audio_evrc.c new file mode 100644 index 000000000000..3498e6927a29 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_evrc.c @@ -0,0 +1,174 @@ +/* evrc audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * + * 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 "audio_utils_aio.h" + + + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_evrc_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_evrc_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for aac decode driver\n"); + return -ENOMEM; + } + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_EVRC); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_EVRC); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("audio_aio_open rc=%d\n", rc); + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_evrc_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_evrc_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_evrc_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_evrc_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_evrc", + .fops = &audio_evrc_fops, +}; + +static int __init audio_evrc_init(void) +{ + return misc_register(&audio_evrc_misc); +} + +device_initcall(audio_evrc_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_mp3.c b/drivers/misc/qcom/qdsp6v2/audio_mp3.c new file mode 100644 index 000000000000..d2f02702bb3e --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_mp3.c @@ -0,0 +1,174 @@ +/* mp3 audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * + * 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 "audio_utils_aio.h" + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_mp3_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + rc = enable_volume_ramp(audio); + if (rc < 0) { + pr_err("%s: Failed to enable volume ramp\n", + __func__); + } + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_mp3_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for mp3 decode driver\n"); + return -ENOMEM; + } + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_MP3); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open MP3 decoder, expected frames is always 1 + audio->buf_cfg.frames_per_buf = 0x01;*/ + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_MP3); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("audio_aio_open rc=%d\n", rc); + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_mp3_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_mp3_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:mp3dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_mp3_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_mp3_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_mp3", + .fops = &audio_mp3_fops, +}; + +static int __init audio_mp3_init(void) +{ + return misc_register(&audio_mp3_misc); +} + +device_initcall(audio_mp3_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c b/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c new file mode 100644 index 000000000000..0a8ce8e4adc6 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c @@ -0,0 +1,328 @@ +/* aac audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * + * 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 <linux/msm_audio_aac.h> +#include <mach/socinfo.h> +#include "audio_utils_aio.h" + +#define AUDIO_AAC_DUAL_MONO_INVALID -1 + + +/* Default number of pre-allocated event packets */ +#define PCM_BUFSZ_MIN_AACM ((8*1024) + sizeof(struct dec_meta_out)) + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_aac_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_aac_cfg aac_cfg; + struct msm_audio_aac_config *aac_config; + uint32_t sbr_ps = 0x00; + aac_config = (struct msm_audio_aac_config *)audio->codec_cfg; + if (audio->feedback == TUNNEL_MODE) { + aac_cfg.sample_rate = aac_config->sample_rate; + aac_cfg.ch_cfg = aac_config->channel_configuration; + } else { + aac_cfg.sample_rate = audio->pcm_cfg.sample_rate; + aac_cfg.ch_cfg = audio->pcm_cfg.channel_count; + } + pr_debug("%s: AUDIO_START session_id[%d]\n", __func__, + audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm_native(audio->ac, + aac_cfg.sample_rate, + aac_cfg.ch_cfg); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + /* turn on both sbr and ps */ + rc = q6asm_enable_sbrps(audio->ac, sbr_ps); + if (rc < 0) + pr_err("sbr-ps enable failed\n"); + if (aac_config->sbr_ps_on_flag) + aac_cfg.aot = AAC_ENC_MODE_EAAC_P; + else if (aac_config->sbr_on_flag) + aac_cfg.aot = AAC_ENC_MODE_AAC_P; + else + aac_cfg.aot = AAC_ENC_MODE_AAC_LC; + + switch (aac_config->format) { + case AUDIO_AAC_FORMAT_ADTS: + aac_cfg.format = 0x00; + break; + case AUDIO_AAC_FORMAT_LOAS: + aac_cfg.format = 0x01; + break; + case AUDIO_AAC_FORMAT_ADIF: + aac_cfg.format = 0x02; + break; + default: + case AUDIO_AAC_FORMAT_RAW: + aac_cfg.format = 0x03; + } + aac_cfg.ep_config = aac_config->ep_config; + aac_cfg.section_data_resilience = + aac_config->aac_section_data_resilience_flag; + aac_cfg.scalefactor_data_resilience = + aac_config->aac_scalefactor_data_resilience_flag; + aac_cfg.spectral_data_resilience = + aac_config->aac_spectral_data_resilience_flag; + + pr_debug("%s:format=%x aot=%d ch=%d sr=%d\n", + __func__, aac_cfg.format, + aac_cfg.aot, aac_cfg.ch_cfg, + aac_cfg.sample_rate); + + /* Configure Media format block */ + rc = q6asm_media_format_block_multi_aac(audio->ac, &aac_cfg); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + if (!cpu_is_msm8x60()) { + rc = q6asm_set_encdec_chan_map(audio->ac, 2); + if (rc < 0) { + pr_err("%s: cmd set encdec_chan_map failed\n", + __func__); + break; + } + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + case AUDIO_GET_AAC_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_aac_config))) { + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_AAC_CONFIG: { + struct msm_audio_aac_config *aac_config; + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_aac_config))) { + rc = -EFAULT; + } else { + uint16_t sce_left = 1, sce_right = 2; + aac_config = audio->codec_cfg; + if (aac_config->dual_mono_mode > + AUDIO_AAC_DUAL_MONO_PL_SR) { + pr_err("%s:AUDIO_SET_AAC_CONFIG: Invalid dual_mono mode =%d\n", + __func__, aac_config->dual_mono_mode); + } else { + /* convert the data from user into sce_left + * and sce_right based on the definitions + */ + pr_debug("%s: AUDIO_SET_AAC_CONFIG: modify dual_mono mode =%d\n", + __func__, aac_config->dual_mono_mode); + switch (aac_config->dual_mono_mode) { + case AUDIO_AAC_DUAL_MONO_PL_PR: + sce_left = 1; + sce_right = 1; + break; + case AUDIO_AAC_DUAL_MONO_SL_SR: + sce_left = 2; + sce_right = 2; + break; + case AUDIO_AAC_DUAL_MONO_SL_PR: + sce_left = 2; + sce_right = 1; + break; + case AUDIO_AAC_DUAL_MONO_PL_SR: + default: + sce_left = 1; + sce_right = 2; + break; + } + rc = q6asm_cfg_dual_mono_aac(audio->ac, + sce_left, sce_right); + if (rc < 0) + pr_err("%s: asm cmd dualmono failed rc=%d\n", + __func__, rc); + } break; + } + break; + } + case AUDIO_SET_AAC_MIX_CONFIG: { + pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG", __func__); + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(unsigned long))) { + rc = -EFAULT; + break; + } else { + unsigned long *mix_coeff = + (unsigned long *)audio->codec_cfg; + pr_debug("%s, value of coeff = %lu", + __func__, *mix_coeff); + q6asm_cfg_aac_sel_mix_coef(audio->ac, *mix_coeff); + if (rc < 0) + pr_err("%s asm aac_sel_mix_coef failed rc=%d\n", + __func__, rc); + break; + } + break; + } + default: + pr_debug("Calling utils ioctl\n"); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + struct msm_audio_aac_config *aac_config = NULL; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_multi_aac_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for aac decode driver\n"); + return -ENOMEM; + } + + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + pr_err("%s: Could not allocate memory for aac config\n", + __func__); + kfree(audio); + return -ENOMEM; + } + + aac_config = audio->codec_cfg; + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AACM; + aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_MPEG4_MULTI_AAC); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open AAC decoder, expected frames is always 1 + audio->buf_cfg.frames_per_buf = 0x01;*/ + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_MPEG4_MULTI_AAC); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("audio_aio_open rc=%d\n", rc); + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_multi_aac_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_aac_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:AAC 5.1 Decoder OPEN success mode[%d]session[%d]\n", + __func__, audio->feedback, audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_aac_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_multiaac_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_multi_aac", + .fops = &audio_aac_fops, +}; + +static int __init audio_aac_init(void) +{ + return misc_register(&audio_multiaac_misc); +} + +device_initcall(audio_aac_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_qcelp.c b/drivers/misc/qcom/qdsp6v2/audio_qcelp.c new file mode 100644 index 000000000000..4993226d61b3 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_qcelp.c @@ -0,0 +1,179 @@ +/* qcelp(v13k) audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * + * 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 "audio_utils_aio.h" + +#define FRAME_SIZE_DEC_QCELP ((32) + sizeof(struct dec_meta_in)) + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_qcelp_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__, + audio->ac->session, + audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + default: + pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_qcelp_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for aac decode driver\n"); + return -ENOMEM; + } + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE_DEC_QCELP; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + audio->pcm_cfg.sample_rate = 8000; + audio->pcm_cfg.channel_count = 1; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio); + return -ENOMEM; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_V13K); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_V13K); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("audio_aio_open rc=%d\n", rc); + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_qcelp_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_qcelp_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:dec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio); + return rc; +} + +static const struct file_operations audio_qcelp_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_qcelp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_qcelp", + .fops = &audio_qcelp_fops, +}; + +static int __init audio_qcelp_init(void) +{ + return misc_register(&audio_qcelp_misc); +} + +device_initcall(audio_qcelp_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils.c b/drivers/misc/qcom/qdsp6v2/audio_utils.c new file mode 100644 index 000000000000..042b77b9e722 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_utils.c @@ -0,0 +1,656 @@ +/* Copyright (c) 2010-2013, The Linux Foundation. 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 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. + * +*/ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/atomic.h> +#include <asm/ioctls.h> +#include "audio_utils.h" + +static int audio_in_pause(struct q6audio_in *audio) +{ + int rc; + + rc = q6asm_cmd(audio->ac, CMD_PAUSE); + if (rc < 0) + pr_err("%s:session id %d: pause cmd failed rc=%d\n", __func__, + audio->ac->session, rc); + + return rc; +} + +static int audio_in_flush(struct q6audio_in *audio) +{ + int rc; + + pr_debug("%s:session id %d: flush\n", __func__, audio->ac->session); + /* Flush if session running */ + if (audio->enabled) { + /* Implicitly issue a pause to the encoder before flushing */ + rc = audio_in_pause(audio); + if (rc < 0) { + pr_err("%s:session id %d: pause cmd failed rc=%d\n", + __func__, audio->ac->session, rc); + return rc; + } + + rc = q6asm_cmd(audio->ac, CMD_FLUSH); + if (rc < 0) { + pr_err("%s:session id %d: flush cmd failed rc=%d\n", + __func__, audio->ac->session, rc); + return rc; + } + /* 2nd arg: 0 -> run immediately + 3rd arg: 0 -> msw_ts, 4th arg: 0 ->lsw_ts */ + q6asm_run(audio->ac, 0x00, 0x00, 0x00); + pr_debug("Rerun the session\n"); + } + audio->rflush = 1; + audio->wflush = 1; + memset(audio->out_frame_info, 0, sizeof(audio->out_frame_info)); + wake_up(&audio->read_wait); + /* get read_lock to ensure no more waiting read thread */ + mutex_lock(&audio->read_lock); + audio->rflush = 0; + mutex_unlock(&audio->read_lock); + wake_up(&audio->write_wait); + /* get write_lock to ensure no more waiting write thread */ + mutex_lock(&audio->write_lock); + audio->wflush = 0; + mutex_unlock(&audio->write_lock); + pr_debug("%s:session id %d: in_bytes %d\n", __func__, + audio->ac->session, atomic_read(&audio->in_bytes)); + pr_debug("%s:session id %d: in_samples %d\n", __func__, + audio->ac->session, atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); + atomic_set(&audio->out_count, 0); + return 0; +} + +/* must be called with audio->lock held */ +int audio_in_enable(struct q6audio_in *audio) +{ + if (audio->enabled) + return 0; + + /* 2nd arg: 0 -> run immediately + 3rd arg: 0 -> msw_ts, 4th arg: 0 ->lsw_ts */ + return q6asm_run(audio->ac, 0x00, 0x00, 0x00); +} + +/* must be called with audio->lock held */ +int audio_in_disable(struct q6audio_in *audio) +{ + int rc = 0; + if (!audio->stopped) { + audio->enabled = 0; + audio->opened = 0; + pr_debug("%s:session id %d: inbytes[%d] insamples[%d]\n", + __func__, audio->ac->session, + atomic_read(&audio->in_bytes), + atomic_read(&audio->in_samples)); + + rc = q6asm_cmd(audio->ac, CMD_CLOSE); + if (rc < 0) + pr_err("%s:session id %d: Failed to close the session rc=%d\n", + __func__, audio->ac->session, + rc); + audio->stopped = 1; + memset(audio->out_frame_info, 0, + sizeof(audio->out_frame_info)); + wake_up(&audio->read_wait); + wake_up(&audio->write_wait); + } + pr_debug("%s:session id %d: enabled[%d]\n", __func__, + audio->ac->session, audio->enabled); + return rc; +} + +int audio_in_buf_alloc(struct q6audio_in *audio) +{ + int rc = 0; + + switch (audio->buf_alloc) { + case NO_BUF_ALLOC: + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_audio_client_buf_alloc(IN, + audio->ac, + ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size), + audio->pcm_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed\n", + __func__, + audio->ac->session); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_IN; + } + rc = q6asm_audio_client_buf_alloc(OUT, audio->ac, + ALIGN_BUF_SIZE(audio->str_cfg.buffer_size), + audio->str_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_OUT; + break; + case BUF_ALLOC_IN: + rc = q6asm_audio_client_buf_alloc(OUT, audio->ac, + ALIGN_BUF_SIZE(audio->str_cfg.buffer_size), + audio->str_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_OUT; + break; + case BUF_ALLOC_OUT: + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_audio_client_buf_alloc(IN, audio->ac, + ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size), + audio->pcm_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed\n", + __func__, + audio->ac->session); + rc = -ENOMEM; + break; + } + audio->buf_alloc |= BUF_ALLOC_IN; + } + break; + default: + pr_debug("%s:session id %d: buf[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + } + + return rc; +} +/* ------------------- device --------------------- */ +long audio_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + memset(&stats, 0, sizeof(stats)); + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return rc; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_FLUSH: { + /* Make sure we're stopped and we wake any threads + * that might be blocked holding the read_lock. + * While audio->stopped read threads will always + * exit immediately. + */ + rc = audio_in_flush(audio); + if (rc < 0) + pr_err("%s:session id %d: Flush Fail rc=%d\n", + __func__, audio->ac->session, rc); + else { /* Register back the flushed read buffer with DSP */ + int cnt = 0; + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + pr_debug("register the read buffer\n"); + } + break; + } + case AUDIO_PAUSE: { + pr_debug("%s:session id %d: AUDIO_PAUSE\n", __func__, + audio->ac->session); + if (audio->enabled) + audio_in_pause(audio); + break; + } + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->str_cfg.buffer_size; + cfg.buffer_count = audio->str_cfg.buffer_count; + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + pr_debug("%s:session id %d: AUDIO_GET_STREAM_CONFIG %d %d\n", + __func__, audio->ac->session, cfg.buffer_size, + cfg.buffer_count); + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + /* Minimum single frame size, + but with in maximum frames number */ + if ((cfg.buffer_size < (audio->min_frame_size+ \ + sizeof(struct meta_out_dsp))) || + (cfg.buffer_count < FRAME_NUM)) { + rc = -EINVAL; + break; + } + audio->str_cfg.buffer_size = cfg.buffer_size; + audio->str_cfg.buffer_count = cfg.buffer_count; + if (audio->opened) { + rc = q6asm_audio_client_buf_alloc(OUT, audio->ac, + ALIGN_BUF_SIZE(audio->str_cfg.buffer_size), + audio->str_cfg.buffer_count); + if (rc < 0) { + pr_err("%s: session id %d: Buffer Alloc failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENOMEM; + break; + } + } + audio->buf_alloc |= BUF_ALLOC_OUT; + rc = 0; + pr_debug("%s:session id %d: AUDIO_SET_STREAM_CONFIG %d %d\n", + __func__, audio->ac->session, + audio->str_cfg.buffer_size, + audio->str_cfg.buffer_count); + break; + } + case AUDIO_GET_SESSION_ID: { + if (copy_to_user((void *) arg, &audio->ac->session, + sizeof(unsigned short))) { + rc = -EFAULT; + } + break; + } + case AUDIO_SET_BUF_CFG: { + struct msm_audio_buf_cfg cfg; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if ((audio->feedback == NON_TUNNEL_MODE) && + !cfg.meta_info_enable) { + rc = -EFAULT; + break; + } + + /* Restrict the num of frames per buf to coincide with + * default buf size */ + if (cfg.frames_per_buf > audio->max_frames_per_buf) { + rc = -EFAULT; + break; + } + audio->buf_cfg.meta_info_enable = cfg.meta_info_enable; + audio->buf_cfg.frames_per_buf = cfg.frames_per_buf; + pr_debug("%s:session id %d: Set-buf-cfg: meta[%d] framesperbuf[%d]\n", + __func__, + audio->ac->session, cfg.meta_info_enable, + cfg.frames_per_buf); + break; + } + case AUDIO_GET_BUF_CFG: { + pr_debug("%s:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n", + __func__, + audio->ac->session, audio->buf_cfg.meta_info_enable, + audio->buf_cfg.frames_per_buf); + + if (copy_to_user((void *)arg, &audio->buf_cfg, + sizeof(struct msm_audio_buf_cfg))) + rc = -EFAULT; + break; + } + case AUDIO_GET_CONFIG: { + if (copy_to_user((void *)arg, &audio->pcm_cfg, + sizeof(struct msm_audio_config))) + rc = -EFAULT; + break; + + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config cfg; + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if (audio->feedback != NON_TUNNEL_MODE) { + pr_err("%s:session id %d: Not sufficient permission to change the record mode\n", + __func__, + audio->ac->session); + rc = -EACCES; + break; + } + if ((cfg.buffer_count > PCM_BUF_COUNT) || + (cfg.buffer_count == 1)) + cfg.buffer_count = PCM_BUF_COUNT; + + audio->pcm_cfg.buffer_count = cfg.buffer_count; + audio->pcm_cfg.buffer_size = cfg.buffer_size; + audio->pcm_cfg.channel_count = cfg.channel_count; + audio->pcm_cfg.sample_rate = cfg.sample_rate; + if (audio->opened && audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_audio_client_buf_alloc(IN, audio->ac, + ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size), + audio->pcm_cfg.buffer_count); + if (rc < 0) { + pr_err("%s:session id %d: Buffer Alloc failed\n", + __func__, audio->ac->session); + rc = -ENOMEM; + break; + } + } + audio->buf_alloc |= BUF_ALLOC_IN; + rc = 0; + pr_debug("%s:session id %d: AUDIO_SET_CONFIG %d %d\n", __func__, + audio->ac->session, audio->pcm_cfg.buffer_count, + audio->pcm_cfg.buffer_size); + break; + } + default: + /* call codec specific ioctl */ + rc = audio->enc_ioctl(file, cmd, arg); + } + mutex_unlock(&audio->lock); + return rc; +} + +ssize_t audio_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) +{ + struct q6audio_in *audio = file->private_data; + const char __user *start = buf; + unsigned char *data; + uint32_t offset = 0; + uint32_t size = 0; + int rc = 0; + uint32_t idx; + struct meta_out_dsp meta; + uint32_t bytes_to_copy = 0; + uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 : + (sizeof(unsigned char) + + (sizeof(struct meta_out_dsp)*(audio->buf_cfg.frames_per_buf))); + + memset(&meta, 0, sizeof(meta)); + pr_debug("%s:session id %d: read - %d\n", __func__, audio->ac->session, + count); + if (!audio->enabled) + return -EFAULT; + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible( + audio->read_wait, + ((atomic_read(&audio->out_count) > 0) || + (audio->stopped) || + audio->rflush || audio->eos_rsp || + audio->event_abort)); + + if (audio->event_abort) { + rc = -EIO; + break; + } + + + if (rc < 0) + break; + + if ((audio->stopped && !(atomic_read(&audio->out_count))) || + audio->rflush) { + pr_debug("%s:session id %d: driver in stop state or flush,No more buf to read", + __func__, + audio->ac->session); + rc = 0;/* End of File */ + break; + } + if (!(atomic_read(&audio->out_count)) && + (audio->eos_rsp == 1) && + (count >= (sizeof(unsigned char) + + sizeof(struct meta_out_dsp)))) { + unsigned char num_of_frames; + pr_info("%s:session id %d: eos %d at output\n", + __func__, audio->ac->session, audio->eos_rsp); + if (buf != start) + break; + num_of_frames = 0xFF; + if (copy_to_user(buf, &num_of_frames, + sizeof(unsigned char))) { + rc = -EFAULT; + break; + } + buf += sizeof(unsigned char); + meta.frame_size = 0xFFFF; + meta.encoded_pcm_samples = 0xFFFF; + meta.msw_ts = 0x00; + meta.lsw_ts = 0x00; + meta.nflags = AUD_EOS_SET; + audio->eos_rsp = 0; + if (copy_to_user(buf, &meta, sizeof(meta))) { + rc = -EFAULT; + break; + } + buf += sizeof(meta); + break; + } + data = (unsigned char *)q6asm_is_cpu_buf_avail(OUT, audio->ac, + &size, &idx); + if ((count >= (size + mfield_size)) && data) { + if (audio->buf_cfg.meta_info_enable) { + if (copy_to_user(buf, + &audio->out_frame_info[idx][0], + sizeof(unsigned char))) { + rc = -EFAULT; + break; + } + bytes_to_copy = + (size + audio->out_frame_info[idx][1]); + /* Number of frames information copied */ + buf += sizeof(unsigned char); + count -= sizeof(unsigned char); + } else { + offset = audio->out_frame_info[idx][1]; + bytes_to_copy = size; + } + + pr_debug("%s:session id %d: offset=%d nr of frames= %d\n", + __func__, audio->ac->session, + audio->out_frame_info[idx][1], + audio->out_frame_info[idx][0]); + + if (copy_to_user(buf, &data[offset], bytes_to_copy)) { + rc = -EFAULT; + break; + } + count -= bytes_to_copy; + buf += bytes_to_copy; + } else { + pr_err("%s:session id %d: short read data[%p] bytesavail[%d]bytesrequest[%d]\n", + __func__, + audio->ac->session, + data, size, count); + } + atomic_dec(&audio->out_count); + q6asm_read(audio->ac); + break; + } + mutex_unlock(&audio->read_lock); + + pr_debug("%s:session id %d: read: %d bytes\n", __func__, + audio->ac->session, (buf-start)); + if (buf > start) + return buf - start; + return rc; +} + +static int extract_meta_info(char *buf, unsigned long *msw_ts, + unsigned long *lsw_ts, unsigned int *flags) +{ + struct meta_in *meta = (struct meta_in *)buf; + *msw_ts = meta->ntimestamp.highpart; + *lsw_ts = meta->ntimestamp.lowpart; + *flags = meta->nflags; + return 0; +} + +ssize_t audio_in_write(struct file *file, + const char __user *buf, + size_t count, loff_t *pos) +{ + struct q6audio_in *audio = file->private_data; + const char __user *start = buf; + size_t xfer = 0; + char *cpy_ptr; + int rc = 0; + unsigned char *data; + uint32_t size = 0; + uint32_t idx = 0; + uint32_t nflags = 0; + unsigned long msw_ts = 0; + unsigned long lsw_ts = 0; + uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 : + sizeof(struct meta_in); + + pr_debug("%s:session id %d: to write[%d]\n", __func__, + audio->ac->session, count); + if (!audio->enabled) + return -EFAULT; + mutex_lock(&audio->write_lock); + + while (count > 0) { + rc = wait_event_interruptible(audio->write_wait, + ((atomic_read(&audio->in_count) > 0) || + (audio->stopped) || + (audio->wflush) || (audio->event_abort))); + + if (audio->event_abort) { + rc = -EIO; + break; + } + + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + pr_debug("%s: session id %d: stop or flush\n", __func__, + audio->ac->session); + rc = -EBUSY; + break; + } + /* if no PCM data, might have only eos buffer + such case do not hold cpu buffer */ + if ((buf == start) && (count == mfield_size)) { + char eos_buf[sizeof(struct meta_in)]; + /* Processing begining of user buffer */ + if (copy_from_user(eos_buf, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + extract_meta_info(eos_buf, &msw_ts, &lsw_ts, + &nflags); + buf += mfield_size; + /* send the EOS and return */ + pr_debug("%s:session id %d: send EOS 0x%8x\n", + __func__, + audio->ac->session, nflags); + break; + } + data = (unsigned char *)q6asm_is_cpu_buf_avail(IN, audio->ac, + &size, &idx); + if (!data) { + pr_debug("%s:session id %d: No buf available\n", + __func__, audio->ac->session); + continue; + } + cpy_ptr = data; + if (audio->buf_cfg.meta_info_enable) { + if (buf == start) { + /* Processing beginning of user buffer */ + if (copy_from_user(cpy_ptr, buf, mfield_size)) { + rc = -EFAULT; + break; + } + /* Check if EOS flag is set and buffer has + * contains just meta field + */ + extract_meta_info(cpy_ptr, &msw_ts, &lsw_ts, + &nflags); + buf += mfield_size; + count -= mfield_size; + } else { + pr_debug("%s:session id %d: continuous buffer\n", + __func__, audio->ac->session); + } + } + xfer = (count > (audio->pcm_cfg.buffer_size)) ? + (audio->pcm_cfg.buffer_size) : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + rc = q6asm_write(audio->ac, xfer, msw_ts, lsw_ts, 0x00); + if (rc < 0) { + rc = -EFAULT; + break; + } + atomic_dec(&audio->in_count); + count -= xfer; + buf += xfer; + } + mutex_unlock(&audio->write_lock); + pr_debug("%s:session id %d: eos_condition 0x%8x buf[0x%x] start[0x%x]\n", + __func__, audio->ac->session, + nflags, (int) buf, (int) start); + if (nflags & AUD_EOS_SET) { + rc = q6asm_cmd(audio->ac, CMD_EOS); + pr_info("%s:session id %d: eos %d at input\n", __func__, + audio->ac->session, audio->eos_rsp); + } + pr_debug("%s:session id %d: Written %d Avail Buf[%d]", __func__, + audio->ac->session, (buf - start - mfield_size), + atomic_read(&audio->in_count)); + if (!rc) { + if (buf > start) + return buf - start; + } + return rc; +} + +int audio_in_release(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = file->private_data; + pr_info("%s: session id %d\n", __func__, audio->ac->session); + mutex_lock(&audio->lock); + audio_in_disable(audio); + q6asm_audio_client_free(audio->ac); + mutex_unlock(&audio->lock); + kfree(audio->enc_cfg); + kfree(audio->codec_cfg); + kfree(audio); + return 0; +} + diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils.h b/drivers/misc/qcom/qdsp6v2/audio_utils.h new file mode 100644 index 000000000000..7209724f484d --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_utils.h @@ -0,0 +1,104 @@ +/* Copyright (c) 2010-2013, The Linux Foundation. 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 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. + * +*/ +#include <linux/msm_audio.h> +#include "q6audio_common.h" + +#define FRAME_NUM (8) + +#define PCM_BUF_COUNT (2) + +#define AUD_EOS_SET 0x01 +#define TUNNEL_MODE 0x0000 +#define NON_TUNNEL_MODE 0x0001 + +#define NO_BUF_ALLOC 0x00 +#define BUF_ALLOC_IN 0x01 +#define BUF_ALLOC_OUT 0x02 +#define BUF_ALLOC_INOUT 0x03 +#define ALIGN_BUF_SIZE(size) ((size + 4095) & (~4095)) + +struct timestamp { + unsigned long lowpart; + unsigned long highpart; +} __packed; + +struct meta_in { + unsigned short offset; + struct timestamp ntimestamp; + unsigned int nflags; +} __packed; + +struct meta_out_dsp { + u32 offset_to_frame; + u32 frame_size; + u32 encoded_pcm_samples; + u32 msw_ts; + u32 lsw_ts; + u32 nflags; +} __packed; + +struct meta_out { + unsigned char num_of_frames; + struct meta_out_dsp meta_out_dsp[]; +} __packed; + +struct q6audio_in { + spinlock_t dsp_lock; + atomic_t in_bytes; + atomic_t in_samples; + + struct mutex lock; + struct mutex read_lock; + struct mutex write_lock; + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; + + struct audio_client *ac; + struct msm_audio_stream_config str_cfg; + void *enc_cfg; + struct msm_audio_buf_cfg buf_cfg; + struct msm_audio_config pcm_cfg; + void *codec_cfg; + + /* number of buffers available to read/write */ + atomic_t in_count; + atomic_t out_count; + + /* first idx: num of frames per buf, second idx: offset to frame */ + uint32_t out_frame_info[FRAME_NUM][2]; + int eos_rsp; + int opened; + int enabled; + int stopped; + int event_abort; + int feedback; /* Flag indicates whether used + in Non Tunnel mode */ + int rflush; + int wflush; + int buf_alloc; + uint16_t min_frame_size; + uint16_t max_frames_per_buf; + long (*enc_ioctl)(struct file *, unsigned int, unsigned long); +}; + +int audio_in_enable(struct q6audio_in *audio); +int audio_in_disable(struct q6audio_in *audio); +int audio_in_buf_alloc(struct q6audio_in *audio); +long audio_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); +ssize_t audio_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos); +ssize_t audio_in_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos); +int audio_in_release(struct inode *inode, struct file *file); + diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c new file mode 100644 index 000000000000..b1e5a30bc374 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c @@ -0,0 +1,1483 @@ +/* Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved. + * + * 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 <linux/module.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/atomic.h> +#include <asm/ioctls.h> +#include <linux/debugfs.h> +#include <linux/msm_audio_ion.h> +#include "audio_utils_aio.h" +#ifdef CONFIG_USE_DEV_CTRL_VOLUME +#include <linux/qdsp6v2/audio_dev_ctl.h> +#endif /*CONFIG_USE_DEV_CTRL_VOLUME*/ + +#ifdef CONFIG_DEBUG_FS +ssize_t audio_aio_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +ssize_t audio_aio_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0; + struct q6audio_aio *audio = file->private_data; + + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "feedback %d\n", audio->feedback); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "inqueue empty %d\n", list_empty(&audio->in_queue)); + n += scnprintf(buffer + n, debug_bufmax - n, + "outqueue empty %d\n", list_empty(&audio->out_queue)); + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} +#endif + +int insert_eos_buf(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node) +{ + struct dec_meta_out *eos_buf = buf_node->kvaddr; + pr_debug("%s[%p]:insert_eos_buf\n", __func__, audio); + eos_buf->num_of_frames = 0xFFFFFFFF; + eos_buf->meta_out_dsp[0].offset_to_frame = 0x0; + eos_buf->meta_out_dsp[0].nflags = AUDIO_DEC_EOS_SET; + return sizeof(struct dec_meta_out) + + sizeof(eos_buf->meta_out_dsp[0]); +} + +/* Routine which updates read buffers of driver/dsp, + for flush operation as DSP output might not have proper + value set */ +static int insert_meta_data_flush(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node) +{ + struct dec_meta_out *meta_data = buf_node->kvaddr; + meta_data->num_of_frames = 0x0; + meta_data->meta_out_dsp[0].offset_to_frame = 0x0; + meta_data->meta_out_dsp[0].nflags = 0x0; + return sizeof(struct dec_meta_out) + + sizeof(meta_data->meta_out_dsp[0]); +} + +static int audio_aio_ion_lookup_vaddr(struct q6audio_aio *audio, void *addr, + unsigned long len, + struct audio_aio_ion_region **region) +{ + struct audio_aio_ion_region *region_elt; + + int match_count = 0; + + *region = NULL; + + /* returns physical address or zero */ + list_for_each_entry(region_elt, &audio->ion_region_queue, list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) { + /* offset since we could pass vaddr inside a registerd + * ion buffer + */ + + match_count++; + if (!*region) + *region = region_elt; + } + } + + if (match_count > 1) { + pr_err("%s[%p]:multiple hits for vaddr %p, len %ld\n", + __func__, audio, addr, len); + list_for_each_entry(region_elt, &audio->ion_region_queue, + list) { + if (addr >= region_elt->vaddr && + addr < region_elt->vaddr + region_elt->len && + addr + len <= region_elt->vaddr + region_elt->len) + pr_err("\t%s[%p]:%p, %ld --> %p\n", + __func__, audio, + region_elt->vaddr, + region_elt->len, + (void *)region_elt->paddr); + } + } + + return *region ? 0 : -1; +} + +static unsigned long audio_aio_ion_fixup(struct q6audio_aio *audio, void *addr, + unsigned long len, int ref_up, void **kvaddr) +{ + struct audio_aio_ion_region *region; + unsigned long paddr; + int ret; + + ret = audio_aio_ion_lookup_vaddr(audio, addr, len, ®ion); + if (ret) { + pr_err("%s[%p]:lookup (%p, %ld) failed\n", + __func__, audio, addr, len); + return 0; + } + if (ref_up) + region->ref_cnt++; + else + region->ref_cnt--; + pr_debug("%s[%p]:found region %p ref_cnt %d\n", + __func__, audio, region, region->ref_cnt); + paddr = region->paddr + (addr - region->vaddr); + /* provide kernel virtual address for accessing meta information */ + if (kvaddr) + *kvaddr = (void *) (region->kvaddr + (addr - region->vaddr)); + return paddr; +} + +static int audio_aio_pause(struct q6audio_aio *audio) +{ + int rc = -EINVAL; + + pr_debug("%s[%p], enabled = %d\n", __func__, audio, + audio->enabled); + if (audio->enabled) { + rc = q6asm_cmd(audio->ac, CMD_PAUSE); + if (rc < 0) + pr_err("%s[%p]: pause cmd failed rc=%d\n", + __func__, audio, rc); + + if (rc == 0) { + /* Send suspend only if pause was successful */ + rc = q6asm_cmd(audio->ac, CMD_SUSPEND); + if (rc < 0) + pr_err("%s[%p]: suspend cmd failed rc=%d\n", + __func__, audio, rc); + } else + pr_err("%s[%p]: not sending suspend since pause failed\n", + __func__, audio); + + } else + pr_err("%s[%p]: Driver not enabled\n", __func__, audio); + return rc; +} + +static int audio_aio_flush(struct q6audio_aio *audio) +{ + int rc; + + if (audio->enabled) { + /* Implicitly issue a pause to the decoder before flushing if + it is not in pause state */ + if (!(audio->drv_status & ADRV_STATUS_PAUSE)) { + rc = audio_aio_pause(audio); + if (rc < 0) + pr_err("%s[%p}: pause cmd failed rc=%d\n", + __func__, audio, + rc); + else + audio->drv_status |= ADRV_STATUS_PAUSE; + } + rc = q6asm_cmd(audio->ac, CMD_FLUSH); + if (rc < 0) + pr_err("%s[%p]: flush cmd failed rc=%d\n", + __func__, audio, rc); + /* Not in stop state, reenable the stream */ + if (audio->stopped == 0) { + rc = audio_aio_enable(audio); + if (rc) + pr_err("%s[%p]:audio re-enable failed\n", + __func__, audio); + else { + audio->enabled = 1; + if (audio->drv_status & ADRV_STATUS_PAUSE) + audio->drv_status &= ~ADRV_STATUS_PAUSE; + } + } + } + pr_debug("%s[%p]:in_bytes %d\n", + __func__, audio, atomic_read(&audio->in_bytes)); + pr_debug("%s[%p]:in_samples %d\n", + __func__, audio, atomic_read(&audio->in_samples)); + atomic_set(&audio->in_bytes, 0); + atomic_set(&audio->in_samples, 0); + return 0; +} + +static int audio_aio_outport_flush(struct q6audio_aio *audio) +{ + int rc; + + rc = q6asm_cmd(audio->ac, CMD_OUT_FLUSH); + if (rc < 0) + pr_err("%s[%p}: output port flush cmd failed rc=%d\n", + __func__, audio, rc); + return rc; +} + +/* Write buffer to DSP / Handle Ack from DSP */ +void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token, + uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload event_payload; + struct audio_aio_buffer_node *used_buf; + + /* No active flush in progress */ + if (audio->wflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (list_empty(&audio->out_queue)) { + pr_warning("%s: ingore unexpected event from dsp\n", __func__); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + return; + } + used_buf = list_first_entry(&audio->out_queue, + struct audio_aio_buffer_node, list); + if (token == used_buf->token) { + list_del(&used_buf->list); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + pr_debug("%s[%p]:consumed buffer\n", __func__, audio); + event_payload.aio_buf = used_buf->buf; + audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE, + event_payload); + kfree(used_buf); + if (list_empty(&audio->out_queue) && + (audio->drv_status & ADRV_STATUS_FSYNC)) { + pr_debug("%s[%p]: list is empty, reached EOS in Tunnel\n", + __func__, audio); + wake_up(&audio->write_wait); + } + } else { + pr_err("%s[%p]:expected=%lx ret=%x\n", + __func__, audio, used_buf->token, token); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } +} + +/* ------------------- device --------------------- */ +void audio_aio_async_out_flush(struct q6audio_aio *audio) +{ + struct audio_aio_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + unsigned long flags; + + pr_debug("%s[%p}\n", __func__, audio); + /* EOS followed by flush, EOS response not guranteed, free EOS i/p + buffer */ + spin_lock_irqsave(&audio->dsp_lock, flags); + + if (audio->eos_flag && (audio->eos_write_payload.aio_buf.buf_addr)) { + pr_debug("%s[%p]: EOS followed by flush received,acknowledge"\ + " eos i/p buffer immediately\n", __func__, audio); + audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE, + audio->eos_write_payload); + memset(&audio->eos_write_payload , 0, + sizeof(union msm_audio_event_payload)); + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); + list_for_each_safe(ptr, next, &audio->out_queue) { + buf_node = list_entry(ptr, struct audio_aio_buffer_node, list); + list_del(&buf_node->list); + payload.aio_buf = buf_node->buf; + audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE, payload); + kfree(buf_node); + pr_debug("%s[%p]: Propagate WRITE_DONE during flush\n", + __func__, audio); + } +} + +void audio_aio_async_in_flush(struct q6audio_aio *audio) +{ + struct audio_aio_buffer_node *buf_node; + struct list_head *ptr, *next; + union msm_audio_event_payload payload; + + pr_debug("%s[%p]\n", __func__, audio); + list_for_each_safe(ptr, next, &audio->in_queue) { + buf_node = list_entry(ptr, struct audio_aio_buffer_node, list); + list_del(&buf_node->list); + /* Forcefull send o/p eos buffer after flush, if no eos response + * received by dsp even after sending eos command */ + if ((audio->eos_rsp != 1) && audio->eos_flag) { + pr_debug("%s[%p]: send eos on o/p buffer during flush\n", + __func__, audio); + payload.aio_buf = buf_node->buf; + payload.aio_buf.data_len = + insert_eos_buf(audio, buf_node); + audio->eos_flag = 0; + } else { + payload.aio_buf = buf_node->buf; + payload.aio_buf.data_len = + insert_meta_data_flush(audio, buf_node); + } + audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE, payload); + kfree(buf_node); + pr_debug("%s[%p]: Propagate READ_DONE during flush\n", + __func__, audio); + } +} + +int audio_aio_enable(struct q6audio_aio *audio) +{ + /* 2nd arg: 0 -> run immediately + 3rd arg: 0 -> msw_ts, 4th arg: 0 ->lsw_ts */ + return q6asm_run(audio->ac, 0x00, 0x00, 0x00); +} + +int audio_aio_disable(struct q6audio_aio *audio) +{ + int rc = 0; + if (audio->opened) { + audio->enabled = 0; + audio->opened = 0; + pr_debug("%s[%p]: inbytes[%d] insamples[%d]\n", __func__, + audio, atomic_read(&audio->in_bytes), + atomic_read(&audio->in_samples)); + /* Close the session */ + rc = q6asm_cmd(audio->ac, CMD_CLOSE); + if (rc < 0) + pr_err("%s[%p]:Failed to close the session rc=%d\n", + __func__, audio, rc); + audio->stopped = 1; + wake_up(&audio->write_wait); + wake_up(&audio->cmd_wait); + } + pr_debug("%s[%p]:enabled[%d]\n", __func__, audio, audio->enabled); + return rc; +} + +void audio_aio_reset_ion_region(struct q6audio_aio *audio) +{ + struct audio_aio_ion_region *region; + struct list_head *ptr, *next; + + list_for_each_safe(ptr, next, &audio->ion_region_queue) { + region = list_entry(ptr, struct audio_aio_ion_region, list); + list_del(®ion->list); + msm_audio_ion_free_legacy(audio->client, region->handle); + kfree(region); + } + + return; +} + +void audio_aio_reset_event_queue(struct q6audio_aio *audio) +{ + unsigned long flags; + struct audio_aio_event *drv_evt; + struct list_head *ptr, *next; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + list_for_each_safe(ptr, next, &audio->event_queue) { + drv_evt = list_first_entry(&audio->event_queue, + struct audio_aio_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + list_for_each_safe(ptr, next, &audio->free_event_queue) { + drv_evt = list_first_entry(&audio->free_event_queue, + struct audio_aio_event, list); + list_del(&drv_evt->list); + kfree(drv_evt); + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + return; +} + +static void audio_aio_unmap_ion_region(struct q6audio_aio *audio) +{ + struct audio_aio_ion_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + pr_debug("%s[%p]:\n", __func__, audio); + list_for_each_safe(ptr, next, &audio->ion_region_queue) { + region = list_entry(ptr, struct audio_aio_ion_region, list); + pr_debug("%s[%p]: phy_address = 0x%lx\n", + __func__, audio, region->paddr); + if (region != NULL) { + rc = q6asm_memory_unmap(audio->ac, + (uint32_t)region->paddr, IN); + if (rc < 0) + pr_err("%s[%p]: memory unmap failed\n", + __func__, audio); + } + } +} + +#ifdef CONFIG_USE_DEV_CTRL_VOLUME + +static void audio_aio_listner(u32 evt_id, union auddev_evt_data *evt_payload, + void *private_data) +{ + struct q6audio_aio *audio = (struct q6audio_aio *) private_data; + int rc = 0; + + switch (evt_id) { + case AUDDEV_EVT_STREAM_VOL_CHG: + audio->volume = evt_payload->session_vol; + pr_debug("%s[%p]: AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d, enabled = %d\n", + __func__, audio, audio->volume, audio->enabled); + if (audio->enabled == 1) { + if (audio->ac) { + rc = q6asm_set_volume(audio->ac, audio->volume); + if (rc < 0) { + pr_err("%s[%p]: Send Volume command failed rc=%d\n", + __func__, audio, rc); + } + } + } + break; + default: + pr_err("%s[%p]:ERROR:wrong event\n", __func__, audio); + break; + } +} + +int register_volume_listener(struct q6audio_aio *audio) +{ + int rc = 0; + audio->device_events = AUDDEV_EVT_STREAM_VOL_CHG; + audio->drv_status &= ~ADRV_STATUS_PAUSE; + + rc = auddev_register_evt_listner(audio->device_events, + AUDDEV_CLNT_DEC, + audio->ac->session, + audio_aio_listner, + (void *)audio); + if (rc < 0) { + pr_err("%s[%p]: Event listener failed\n", __func__, audio); + rc = -EACCES; + } + return rc; +} +void unregister_volume_listener(struct q6audio_aio *audio) +{ + auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->ac->session); +} + +int enable_volume_ramp(struct q6audio_aio *audio) +{ + int rc = 0; + struct asm_softpause_params softpause; + struct asm_softvolume_params softvol; + + if (audio->ac == NULL) + return -EINVAL; + pr_debug("%s[%p]\n", __func__, audio); + softpause.enable = SOFT_PAUSE_ENABLE; + softpause.period = SOFT_PAUSE_PERIOD; + softpause.step = SOFT_PAUSE_STEP; + softpause.rampingcurve = SOFT_PAUSE_CURVE_LINEAR; + + softvol.period = SOFT_VOLUME_PERIOD; + softvol.step = SOFT_VOLUME_STEP; + softvol.rampingcurve = SOFT_VOLUME_CURVE_LINEAR; + + if (softpause.rampingcurve == SOFT_PAUSE_CURVE_LINEAR) + softpause.step = SOFT_PAUSE_STEP_LINEAR; + if (softvol.rampingcurve == SOFT_VOLUME_CURVE_LINEAR) + softvol.step = SOFT_VOLUME_STEP_LINEAR; + rc = q6asm_set_volume(audio->ac, audio->volume); + if (rc < 0) { + pr_err("%s: Send Volume command failed rc=%d\n", + __func__, rc); + return rc; + } + rc = q6asm_set_softpause(audio->ac, &softpause); + if (rc < 0) { + pr_err("%s: Send SoftPause Param failed rc=%d\n", + __func__, rc); + return rc; + } + rc = q6asm_set_softvolume(audio->ac, &softvol); + if (rc < 0) { + pr_err("%s: Send SoftVolume Param failed rc=%d\n", + __func__, rc); + return rc; + } + /* disable mute by default */ + rc = q6asm_set_mute(audio->ac, 0); + if (rc < 0) { + pr_err("%s: Send mute command failed rc=%d\n", + __func__, rc); + return rc; + } + return rc; +} + +#else /*CONFIG_USE_DEV_CTRL_VOLUME*/ +int register_volume_listener(struct q6audio_aio *audio) +{ + return 0;/* do nothing */ +} +void unregister_volume_listener(struct q6audio_aio *audio) +{ + return;/* do nothing */ +} +int enable_volume_ramp(struct q6audio_aio *audio) +{ + return 0; /* do nothing */ +} +#endif /*CONFIG_USE_DEV_CTRL_VOLUME*/ + +int audio_aio_release(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = file->private_data; + pr_debug("%s[%p]\n", __func__, audio); + mutex_lock(&audio->lock); + audio->wflush = 1; + if (audio->enabled) + audio_aio_flush(audio); + audio->wflush = 0; + audio->drv_ops.out_flush(audio); + audio->drv_ops.in_flush(audio); + audio_aio_disable(audio); + audio_aio_unmap_ion_region(audio); + audio_aio_reset_ion_region(audio); + msm_audio_ion_client_destroy(audio->client); + audio->event_abort = 1; + wake_up(&audio->event_wait); + audio_aio_reset_event_queue(audio); + q6asm_audio_client_free(audio->ac); + mutex_unlock(&audio->lock); + mutex_destroy(&audio->lock); + mutex_destroy(&audio->read_lock); + mutex_destroy(&audio->write_lock); + mutex_destroy(&audio->get_event_lock); + unregister_volume_listener(audio); + +#ifdef CONFIG_DEBUG_FS + if (audio->dentry) + debugfs_remove(audio->dentry); +#endif + kfree(audio->codec_cfg); + kfree(audio); + return 0; +} + +int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync) +{ + int rc = 0; + struct q6audio_aio *audio = file->private_data; + + if (!audio->enabled || audio->feedback) + return -EINVAL; + + /* Blocking client sends more data */ + mutex_lock(&audio->lock); + audio->drv_status |= ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + pr_debug("%s[%p]:\n", __func__, audio); + + audio->eos_rsp = 0; + + pr_debug("%s[%p]Wait for write done from DSP\n", __func__, audio); + rc = wait_event_interruptible(audio->write_wait, + (list_empty(&audio->out_queue)) || + audio->wflush || audio->stopped); + + if (audio->stopped || audio->wflush) { + pr_debug("%s[%p]: Audio Flushed or Stopped,this is not EOS\n" + , __func__, audio); + audio->wflush = 0; + rc = -EBUSY; + } + + if (rc < 0) { + pr_err("%s[%p]: wait event for list_empty failed, rc = %d\n", + __func__, audio, rc); + goto done; + } + + rc = q6asm_cmd(audio->ac, CMD_EOS); + pr_debug("%s[%p]: EOS cmd sent to DSP\n", __func__, audio); + + if (rc < 0) + pr_err("%s[%p]: q6asm_cmd failed, rc = %d", + __func__, audio, rc); + + pr_debug("%s[%p]: wait for RENDERED_EOS from DSP\n" + , __func__, audio); + rc = wait_event_interruptible(audio->write_wait, + (audio->eos_rsp || audio->wflush || + audio->stopped)); + + if (rc < 0) { + pr_err("%s[%p]: wait event for eos_rsp failed, rc = %d\n", + __func__, audio, rc); + goto done; + } + + if (audio->stopped || audio->wflush) { + audio->wflush = 0; + pr_debug("%s[%p]: Audio Flushed or Stopped,this is not EOS\n" + , __func__, audio); + rc = -EBUSY; + } + + if (audio->eos_rsp == 1) + pr_debug("%s[%p]: EOS\n", __func__, audio); + + +done: + mutex_lock(&audio->lock); + audio->drv_status &= ~ADRV_STATUS_FSYNC; + mutex_unlock(&audio->lock); + + return rc; +} + +static int audio_aio_events_pending(struct q6audio_aio *audio) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + empty = !list_empty(&audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return empty || audio->event_abort; +} + +static long audio_aio_process_event_req(struct q6audio_aio *audio, + void __user *arg) +{ + long rc; + struct msm_audio_event usr_evt; + struct audio_aio_event *drv_evt = NULL; + int timeout; + unsigned long flags; + + if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) + return -EFAULT; + + timeout = (int)usr_evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout(audio->event_wait, + audio_aio_events_pending + (audio), + msecs_to_jiffies + (timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible(audio->event_wait, + audio_aio_events_pending(audio)); + } + if (rc < 0) + return rc; + + if (audio->event_abort) { + audio->event_abort = 0; + return -ENODEV; + } + + rc = 0; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + if (!list_empty(&audio->event_queue)) { + drv_evt = list_first_entry(&audio->event_queue, + struct audio_aio_event, list); + list_del(&drv_evt->list); + } + if (drv_evt) { + usr_evt.event_type = drv_evt->event_type; + usr_evt.event_payload = drv_evt->payload; + list_add_tail(&drv_evt->list, &audio->free_event_queue); + } else { + pr_err("%s[%p]:Unexpected path\n", __func__, audio); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return -EPERM; + } + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + + if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE) { + pr_debug("%s[%p]:posted AUDIO_EVENT_WRITE_DONE to user\n", + __func__, audio); + mutex_lock(&audio->write_lock); + audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0, 0); + mutex_unlock(&audio->write_lock); + } else if (drv_evt->event_type == AUDIO_EVENT_READ_DONE) { + pr_debug("%s[%p]:posted AUDIO_EVENT_READ_DONE to user\n", + __func__, audio); + mutex_lock(&audio->read_lock); + audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr, + drv_evt->payload.aio_buf.buf_len, 0, 0); + mutex_unlock(&audio->read_lock); + } + + /* Some read buffer might be held up in DSP,release all + * Once EOS indicated + */ + if (audio->eos_rsp && !list_empty(&audio->in_queue)) { + pr_debug("%s[%p]:Send flush command to release read buffers"\ + " held up in DSP\n", __func__, audio); + audio_aio_flush(audio); + } + + if (copy_to_user(arg, &usr_evt, sizeof(usr_evt))) + rc = -EFAULT; + + return rc; +} + +static int audio_aio_ion_check(struct q6audio_aio *audio, + void *vaddr, unsigned long len) +{ + struct audio_aio_ion_region *region_elt; + struct audio_aio_ion_region t = {.vaddr = vaddr, .len = len }; + + list_for_each_entry(region_elt, &audio->ion_region_queue, list) { + if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) || + OVERLAPS(region_elt, &t)) { + pr_err("%s[%p]:region (vaddr %p len %ld) clashes with registered region (vaddr %p paddr %p len %ld)\n", + __func__, audio, vaddr, len, + region_elt->vaddr, + (void *)region_elt->paddr, region_elt->len); + return -EINVAL; + } + } + + return 0; +} + +static int audio_aio_ion_add(struct q6audio_aio *audio, + struct msm_audio_ion_info *info) +{ + ion_phys_addr_t paddr = 0; + size_t len = 0; + struct audio_aio_ion_region *region; + int rc = -EINVAL; + struct ion_handle *handle = NULL; + unsigned long ionflag; + void *kvaddr = NULL; + + pr_debug("%s[%p]:\n", __func__, audio); + region = kmalloc(sizeof(*region), GFP_KERNEL); + + if (!region) { + rc = -ENOMEM; + goto end; + } + + rc = msm_audio_ion_import_legacy("Audio_Dec_Client", audio->client, + &handle, info->fd, &ionflag, + 0, &paddr, &len, &kvaddr); + if (rc) { + pr_err("%s: msm audio ion alloc failed\n", __func__); + goto import_error; + } + + rc = audio_aio_ion_check(audio, info->vaddr, len); + if (rc < 0) { + pr_err("%s: audio_aio_ion_check failed\n", __func__); + goto ion_error; + } + + region->handle = handle; + region->vaddr = info->vaddr; + region->fd = info->fd; + region->paddr = paddr; + region->kvaddr = (unsigned long)kvaddr; + region->len = len; + region->ref_cnt = 0; + pr_debug("%s[%p]:add region paddr %lx vaddr %p, len %lu kvaddr %lx\n", + __func__, audio, + region->paddr, region->vaddr, region->len, region->kvaddr); + list_add_tail(®ion->list, &audio->ion_region_queue); + rc = q6asm_memory_map(audio->ac, (uint32_t) paddr, IN, (uint32_t) len, + 1); + if (rc < 0) { + pr_err("%s[%p]: memory map failed\n", __func__, audio); + goto mmap_error; + } else { + goto end; + } +mmap_error: + list_del(®ion->list); +ion_error: + msm_audio_ion_free_legacy(audio->client, handle); +import_error: + kfree(region); +end: + return rc; +} + +static int audio_aio_ion_remove(struct q6audio_aio *audio, + struct msm_audio_ion_info *info) +{ + struct audio_aio_ion_region *region; + struct list_head *ptr, *next; + int rc = -EINVAL; + + pr_debug("%s[%p]:info fd %d vaddr %p\n", + __func__, audio, info->fd, info->vaddr); + + list_for_each_safe(ptr, next, &audio->ion_region_queue) { + region = list_entry(ptr, struct audio_aio_ion_region, list); + + if ((region->fd == info->fd) && + (region->vaddr == info->vaddr)) { + if (region->ref_cnt) { + pr_debug("%s[%p]:region %p in use ref_cnt %d\n", + __func__, audio, region, + region->ref_cnt); + break; + } + pr_debug("%s[%p]:remove region fd %d vaddr %p\n", + __func__, audio, info->fd, info->vaddr); + rc = q6asm_memory_unmap(audio->ac, + (uint32_t) region->paddr, IN); + if (rc < 0) + pr_err("%s[%p]: memory unmap failed\n", + __func__, audio); + + list_del(®ion->list); + msm_audio_ion_free_legacy(audio->client, + region->handle); + kfree(region); + rc = 0; + break; + } + } + + return rc; +} + +static void audio_aio_async_write(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node) +{ + int rc; + struct audio_client *ac; + struct audio_aio_write_param param; + + if (!audio || !buf_node) { + pr_err("%s NULL pointer audio=[0x%p], buf_node=[0x%p]\n", + __func__, audio, buf_node); + return; + } + pr_debug("%s[%p]: Send write buff %p phy %lx len %d meta_enable = %d\n", + __func__, audio, buf_node, buf_node->paddr, + buf_node->buf.data_len, + audio->buf_cfg.meta_info_enable); + pr_debug("%s[%p]: flags = 0x%x\n", __func__, audio, + buf_node->meta_info.meta_in.nflags); + + ac = audio->ac; + /* Offset with appropriate meta */ + if (audio->feedback) { + /* Non Tunnel mode */ + param.paddr = buf_node->paddr + sizeof(struct dec_meta_in); + param.len = buf_node->buf.data_len - sizeof(struct dec_meta_in); + } else { + /* Tunnel mode */ + param.paddr = buf_node->paddr; + param.len = buf_node->buf.data_len; + } + param.msw_ts = buf_node->meta_info.meta_in.ntimestamp.highpart; + param.lsw_ts = buf_node->meta_info.meta_in.ntimestamp.lowpart; + param.flags = buf_node->meta_info.meta_in.nflags; + /* If no meta_info enaled, indicate no time stamp valid */ + if (!audio->buf_cfg.meta_info_enable) + param.flags = 0xFF00; + + if (buf_node->meta_info.meta_in.nflags & AUDIO_DEC_EOF_SET) + param.flags |= AUDIO_DEC_EOF_SET; + + param.uid = param.paddr; + /* Read command will populate paddr as token */ + buf_node->token = param.paddr; + rc = q6asm_async_write(ac, ¶m); + if (rc < 0) + pr_err("%s[%p]:failed\n", __func__, audio); +} + +void audio_aio_post_event(struct q6audio_aio *audio, int type, + union msm_audio_event_payload payload) +{ + struct audio_aio_event *e_node = NULL; + unsigned long flags; + + spin_lock_irqsave(&audio->event_queue_lock, flags); + + if (!list_empty(&audio->free_event_queue)) { + e_node = list_first_entry(&audio->free_event_queue, + struct audio_aio_event, list); + list_del(&e_node->list); + } else { + e_node = kmalloc(sizeof(struct audio_aio_event), GFP_ATOMIC); + if (!e_node) { + pr_err("%s[%p]:No mem to post event %d\n", + __func__, audio, type); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + return; + } + } + + e_node->event_type = type; + e_node->payload = payload; + + list_add_tail(&e_node->list, &audio->event_queue); + spin_unlock_irqrestore(&audio->event_queue_lock, flags); + wake_up(&audio->event_wait); +} + +static void audio_aio_async_read(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node) +{ + struct audio_client *ac; + struct audio_aio_read_param param; + int rc; + + pr_debug("%s[%p]: Send read buff %p phy %lx len %d\n", + __func__, audio, buf_node, + buf_node->paddr, buf_node->buf.buf_len); + ac = audio->ac; + /* Provide address so driver can append nr frames information */ + param.paddr = buf_node->paddr + + sizeof(struct dec_meta_out); + param.len = buf_node->buf.buf_len - + sizeof(struct dec_meta_out); + param.uid = param.paddr; + /* Write command will populate paddr as token */ + buf_node->token = param.paddr; + rc = q6asm_async_read(ac, ¶m); + if (rc < 0) + pr_err("%s[%p]:failed\n", __func__, audio); +} + +static int audio_aio_buf_add(struct q6audio_aio *audio, unsigned dir, + void __user *arg) +{ + unsigned long flags; + struct audio_aio_buffer_node *buf_node; + + + buf_node = kzalloc(sizeof(*buf_node), GFP_KERNEL); + + if (!buf_node) + return -ENOMEM; + + if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) { + kfree(buf_node); + return -EFAULT; + } + + pr_debug("%s[%p]:node %p dir %x buf_addr %p buf_len %d data_len %d\n", + __func__, audio, buf_node, dir, buf_node->buf.buf_addr, + buf_node->buf.buf_len, buf_node->buf.data_len); + buf_node->paddr = audio_aio_ion_fixup(audio, buf_node->buf.buf_addr, + buf_node->buf.buf_len, 1, + &buf_node->kvaddr); + if (dir) { + /* write */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (!audio->feedback && !buf_node->buf.data_len)) { + kfree(buf_node); + return -EINVAL; + } + extract_meta_out_info(audio, buf_node, 1); + /* Not a EOS buffer */ + if (!(buf_node->meta_info.meta_in.nflags & AUDIO_DEC_EOS_SET)) { + spin_lock_irqsave(&audio->dsp_lock, flags); + audio_aio_async_write(audio, buf_node); + /* EOS buffer handled in driver */ + list_add_tail(&buf_node->list, &audio->out_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } else if (buf_node->meta_info.meta_in.nflags + & AUDIO_DEC_EOS_SET) { + if (!audio->wflush) { + pr_debug("%s[%p]:Send EOS cmd at i/p\n", + __func__, audio); + /* Driver will forcefully post writedone event + * once eos ack recived from DSP + */ + audio->eos_write_payload.aio_buf =\ + buf_node->buf; + audio->eos_flag = 1; + audio->eos_rsp = 0; + q6asm_cmd(audio->ac, CMD_EOS); + kfree(buf_node); + } else { /* Flush in progress, send back i/p + * EOS buffer as is + */ + union msm_audio_event_payload event_payload; + event_payload.aio_buf = buf_node->buf; + audio_aio_post_event(audio, + AUDIO_EVENT_WRITE_DONE, + event_payload); + kfree(buf_node); + } + } + } else { + /* read */ + if (!buf_node->paddr || + (buf_node->paddr & 0x1) || + (buf_node->buf.buf_len < PCM_BUFSZ_MIN)) { + kfree(buf_node); + return -EINVAL; + } + /* No EOS reached */ + if (!audio->eos_rsp) { + spin_lock_irqsave(&audio->dsp_lock, flags); + audio_aio_async_read(audio, buf_node); + /* EOS buffer handled in driver */ + list_add_tail(&buf_node->list, &audio->in_queue); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } + /* EOS reached at input side fake all upcoming read buffer to + * indicate the same + */ + else { + union msm_audio_event_payload event_payload; + event_payload.aio_buf = buf_node->buf; + event_payload.aio_buf.data_len = + insert_eos_buf(audio, buf_node); + pr_debug("%s[%p]: propagate READ_DONE as EOS done\n",\ + __func__, audio); + audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE, + event_payload); + kfree(buf_node); + } + } + return 0; +} + +void audio_aio_ioport_reset(struct q6audio_aio *audio) +{ + if (audio->drv_status & ADRV_STATUS_AIO_INTF) { + /* If fsync is in progress, make sure + * return value of fsync indicates + * abort due to flush + */ + if (audio->drv_status & ADRV_STATUS_FSYNC) { + pr_debug("%s[%p]:fsync in progress\n", __func__, audio); + audio->drv_ops.out_flush(audio); + } else + audio->drv_ops.out_flush(audio); + if (audio->feedback == NON_TUNNEL_MODE) + audio->drv_ops.in_flush(audio); + } +} + +int audio_aio_open(struct q6audio_aio *audio, struct file *file) +{ + int rc = 0; + int i; + struct audio_aio_event *e_node = NULL; + struct list_head *ptr, *next; + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + audio->pcm_cfg.sample_rate = 48000; + audio->pcm_cfg.channel_count = 2; + + /* Only AIO interface */ + if (file->f_flags & O_NONBLOCK) { + pr_debug("%s[%p]:set to aio interface\n", __func__, audio); + audio->drv_status |= ADRV_STATUS_AIO_INTF; + audio->drv_ops.out_flush = audio_aio_async_out_flush; + audio->drv_ops.in_flush = audio_aio_async_in_flush; + q6asm_set_io_mode(audio->ac, ASYNC_IO_MODE); + } else { + pr_err("%s[%p]:SIO interface not supported\n", + __func__, audio); + rc = -EACCES; + goto fail; + } + + /* Initialize all locks of audio instance */ + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + mutex_init(&audio->get_event_lock); + spin_lock_init(&audio->dsp_lock); + spin_lock_init(&audio->event_queue_lock); + init_waitqueue_head(&audio->cmd_wait); + init_waitqueue_head(&audio->write_wait); + init_waitqueue_head(&audio->event_wait); + INIT_LIST_HEAD(&audio->out_queue); + INIT_LIST_HEAD(&audio->in_queue); + INIT_LIST_HEAD(&audio->ion_region_queue); + INIT_LIST_HEAD(&audio->free_event_queue); + INIT_LIST_HEAD(&audio->event_queue); + + audio->drv_ops.out_flush(audio); + audio->opened = 1; + file->private_data = audio; + audio->codec_ioctl = audio_aio_ioctl; + + for (i = 0; i < AUDIO_EVENT_NUM; i++) { + e_node = kmalloc(sizeof(struct audio_aio_event), GFP_KERNEL); + if (e_node) + list_add_tail(&e_node->list, &audio->free_event_queue); + else { + pr_err("%s[%p]:event pkt alloc failed\n", + __func__, audio); + rc = -ENOMEM; + goto cleanup; + } + } + audio->client = msm_audio_ion_client_create(UINT_MAX, + "Audio_Dec_Client"); + if (IS_ERR_OR_NULL(audio->client)) { + pr_err("Unable to create ION client\n"); + rc = -ENOMEM; + goto cleanup; + } + pr_debug("Ion client create in audio_aio_open %p", audio->client); + + rc = register_volume_listener(audio); + if (rc < 0) + goto ion_cleanup; + + return 0; +ion_cleanup: + msm_audio_ion_client_destroy(audio->client); + audio->client = NULL; +cleanup: + list_for_each_safe(ptr, next, &audio->free_event_queue) { + e_node = list_first_entry(&audio->free_event_queue, + struct audio_aio_event, list); + list_del(&e_node->list); + kfree(e_node); + } +fail: + return rc; +} + +long audio_aio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_GET_STATS: { + struct msm_audio_stats stats; + uint64_t timestamp; + memset(&stats, 0, sizeof(struct msm_audio_stats)); + stats.byte_count = atomic_read(&audio->in_bytes); + stats.sample_count = atomic_read(&audio->in_samples); + rc = q6asm_get_session_time(audio->ac, ×tamp); + if (rc >= 0) + memcpy(&stats.unused[0], ×tamp, sizeof(timestamp)); + else + pr_debug("Error while getting timestamp\n"); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + rc = -EFAULT; + break; + } + case AUDIO_GET_EVENT: { + pr_debug("%s[%p]:AUDIO_GET_EVENT\n", __func__, audio); + if (mutex_trylock(&audio->get_event_lock)) { + rc = audio_aio_process_event_req(audio, + (void __user *)arg); + mutex_unlock(&audio->get_event_lock); + } else + rc = -EBUSY; + break; + } + case AUDIO_ABORT_GET_EVENT: { + audio->event_abort = 1; + wake_up(&audio->event_wait); + break; + } + case AUDIO_ASYNC_WRITE: { + mutex_lock(&audio->write_lock); + if (audio->drv_status & ADRV_STATUS_FSYNC) + rc = -EBUSY; + else { + if (audio->enabled) + rc = audio_aio_buf_add(audio, 1, + (void __user *)arg); + else + rc = -EPERM; + } + mutex_unlock(&audio->write_lock); + break; + } + case AUDIO_ASYNC_READ: { + mutex_lock(&audio->read_lock); + if ((audio->feedback) && (audio->enabled)) + rc = audio_aio_buf_add(audio, 0, + (void __user *)arg); + else + rc = -EPERM; + mutex_unlock(&audio->read_lock); + break; + } + case AUDIO_OUTPORT_FLUSH: { + pr_debug("%s[%p]:AUDIO_OUTPORT_FLUSH\n", __func__, audio); + mutex_lock(&audio->read_lock); + rc = audio_aio_outport_flush(audio); + if (rc < 0) { + pr_err("%s[%p]: AUDIO_OUTPORT_FLUSH failed\n", + __func__, audio); + rc = -EINTR; + } + mutex_unlock(&audio->read_lock); + break; + } + case AUDIO_STOP: { + pr_debug("%s[%p]: AUDIO_STOP session_id[%d]\n", __func__, + audio, audio->ac->session); + mutex_lock(&audio->lock); + audio->stopped = 1; + rc = audio_aio_flush(audio); + if (rc < 0) { + pr_err("%s[%p]:Audio Stop procedure failed rc=%d\n", + __func__, audio, rc); + mutex_unlock(&audio->lock); + break; + } + audio->enabled = 0; + audio->drv_status &= ~ADRV_STATUS_PAUSE; + if (audio->drv_status & ADRV_STATUS_FSYNC) { + pr_debug("%s[%p] Waking up the audio_aio_fsync\n", + __func__, audio); + wake_up(&audio->write_wait); + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_PAUSE: { + pr_debug("%s[%p]:AUDIO_PAUSE %ld\n", __func__, audio, arg); + mutex_lock(&audio->lock); + if (arg == 1) { + rc = audio_aio_pause(audio); + if (rc < 0) { + pr_err("%s[%p]: pause FAILED rc=%d\n", + __func__, audio, rc); + mutex_unlock(&audio->lock); + break; + } + audio->drv_status |= ADRV_STATUS_PAUSE; + } else if (arg == 0) { + if (audio->drv_status & ADRV_STATUS_PAUSE) { + rc = audio_aio_enable(audio); + if (rc) + pr_err("%s[%p]: audio enable failed\n", + __func__, audio); + else { + audio->drv_status &= ~ADRV_STATUS_PAUSE; + audio->enabled = 1; + } + } + } + mutex_unlock(&audio->lock); + break; + } + case AUDIO_FLUSH: { + pr_debug("%s[%p]: AUDIO_FLUSH sessionid[%d]\n", __func__, + audio, audio->ac->session); + mutex_lock(&audio->lock); + audio->rflush = 1; + audio->wflush = 1; + if (audio->drv_status & ADRV_STATUS_FSYNC) { + pr_debug("%s[%p] Waking up the audio_aio_fsync\n", + __func__, audio); + wake_up(&audio->write_wait); + } + /* Flush DSP */ + rc = audio_aio_flush(audio); + /* Flush input / Output buffer in software*/ + audio_aio_ioport_reset(audio); + if (rc < 0) { + pr_err("%s[%p]:AUDIO_FLUSH interrupted\n", + __func__, audio); + rc = -EINTR; + } else { + audio->rflush = 0; + if (audio->drv_status & ADRV_STATUS_FSYNC) + wake_up(&audio->write_wait); + else + audio->wflush = 0; + + } + audio->eos_flag = 0; + audio->eos_rsp = 0; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_REGISTER_ION: { + struct msm_audio_ion_info info; + pr_debug("%s[%p]:AUDIO_REGISTER_ION\n", __func__, audio); + mutex_lock(&audio->lock); + if (copy_from_user(&info, (void *)arg, sizeof(info))) + rc = -EFAULT; + else + rc = audio_aio_ion_add(audio, &info); + mutex_unlock(&audio->lock); + break; + } + case AUDIO_DEREGISTER_ION: { + struct msm_audio_ion_info info; + mutex_lock(&audio->lock); + pr_debug("%s[%p]:AUDIO_DEREGISTER_ION\n", __func__, audio); + if (copy_from_user(&info, (void *)arg, sizeof(info))) + rc = -EFAULT; + else + rc = audio_aio_ion_remove(audio, &info); + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + mutex_lock(&audio->lock); + memset(&cfg, 0, sizeof(cfg)); + cfg.buffer_size = audio->str_cfg.buffer_size; + cfg.buffer_count = audio->str_cfg.buffer_count; + pr_debug("%s[%p]:GET STREAM CFG %d %d\n", + __func__, audio, cfg.buffer_size, cfg.buffer_count); + if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_STREAM_CONFIG: { + struct msm_audio_stream_config cfg; + pr_debug("%s[%p]:SET STREAM CONFIG\n", __func__, audio); + mutex_lock(&audio->lock); + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + rc = 0; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + mutex_lock(&audio->lock); + if (copy_to_user((void *)arg, &audio->pcm_cfg, sizeof(cfg))) + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + pr_err("%s[%p]:AUDIO_SET_CONFIG\n", __func__, audio); + mutex_lock(&audio->lock); + if (copy_from_user(&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + if (audio->feedback != NON_TUNNEL_MODE) { + pr_err("%s[%p]:Not sufficient permission to change the playback mode\n", + __func__, audio); + rc = -EACCES; + mutex_unlock(&audio->lock); + break; + } + if ((config.buffer_count > PCM_BUF_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + audio->pcm_cfg.buffer_count = config.buffer_count; + audio->pcm_cfg.buffer_size = config.buffer_size; + audio->pcm_cfg.channel_count = config.channel_count; + audio->pcm_cfg.sample_rate = config.sample_rate; + rc = 0; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_SET_BUF_CFG: { + struct msm_audio_buf_cfg cfg; + mutex_lock(&audio->lock); + if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) { + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + if ((audio->feedback == NON_TUNNEL_MODE) && + !cfg.meta_info_enable) { + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + + audio->buf_cfg.meta_info_enable = cfg.meta_info_enable; + pr_debug("%s[%p]:session id %d: Set-buf-cfg: meta[%d]", + __func__, audio, + audio->ac->session, cfg.meta_info_enable); + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_BUF_CFG: { + pr_debug("%s[%p]:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n", + __func__, audio, + audio->ac->session, audio->buf_cfg.meta_info_enable, + audio->buf_cfg.frames_per_buf); + + mutex_lock(&audio->lock); + if (copy_to_user((void *)arg, &audio->buf_cfg, + sizeof(struct msm_audio_buf_cfg))) + rc = -EFAULT; + mutex_unlock(&audio->lock); + break; + } + case AUDIO_GET_SESSION_ID: { + mutex_lock(&audio->lock); + if (copy_to_user((void *)arg, &audio->ac->session, + sizeof(unsigned short))) { + rc = -EFAULT; + } + mutex_unlock(&audio->lock); + break; + } + default: + rc = -EINVAL; + } + return rc; +} diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h new file mode 100644 index 000000000000..0efcd647f9d6 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h @@ -0,0 +1,221 @@ +/* Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved. + * + * 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 <linux/fs.h> +#include <linux/module.h> +#include <linux/miscdevice.h> +#include <linux/mutex.h> +#include <linux/sched.h> +#include <linux/uaccess.h> +#include <linux/wait.h> +#include <linux/msm_audio.h> +#include <linux/debugfs.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/msm_ion.h> +#include <asm/ioctls.h> +#include <asm/atomic.h> +#include "q6audio_common.h" + +#define TUNNEL_MODE 0x0000 +#define NON_TUNNEL_MODE 0x0001 + +#define ADRV_STATUS_AIO_INTF 0x00000001 /* AIO interface */ +#define ADRV_STATUS_FSYNC 0x00000008 +#define ADRV_STATUS_PAUSE 0x00000010 +#define AUDIO_DEC_EOS_SET 0x00000001 +#define AUDIO_DEC_EOF_SET 0x00000010 +#define AUDIO_EVENT_NUM 10 + +#define __CONTAINS(r, v, l) ({ \ + typeof(r) __r = r; \ + typeof(v) __v = v; \ + typeof(v) __e = __v + l; \ + int res = ((__v >= __r->vaddr) && \ + (__e <= __r->vaddr + __r->len)); \ + res; \ +}) + +#define CONTAINS(r1, r2) ({ \ + typeof(r2) __r2 = r2; \ + __CONTAINS(r1, __r2->vaddr, __r2->len); \ +}) + +#define IN_RANGE(r, v) ({ \ + typeof(r) __r = r; \ + typeof(v) __vv = v; \ + int res = ((__vv >= __r->vaddr) && \ + (__vv < (__r->vaddr + __r->len))); \ + res; \ +}) + +#define OVERLAPS(r1, r2) ({ \ + typeof(r1) __r1 = r1; \ + typeof(r2) __r2 = r2; \ + typeof(__r2->vaddr) __v = __r2->vaddr; \ + typeof(__v) __e = __v + __r2->len - 1; \ + int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ + res; \ +}) + +struct timestamp { + unsigned long lowpart; + unsigned long highpart; +} __packed; + +struct meta_out_dsp { + u32 offset_to_frame; + u32 frame_size; + u32 encoded_pcm_samples; + u32 msw_ts; + u32 lsw_ts; + u32 nflags; +} __packed; + +struct dec_meta_in { + unsigned char reserved[18]; + unsigned short offset; + struct timestamp ntimestamp; + unsigned int nflags; +} __packed; + +struct dec_meta_out { + unsigned int reserved[7]; + unsigned int num_of_frames; + struct meta_out_dsp meta_out_dsp[]; +} __packed; + +/* General meta field to store meta info +locally */ +union meta_data { + struct dec_meta_out meta_out; + struct dec_meta_in meta_in; +} __packed; + +#define PCM_BUF_COUNT (2) +/* Buffer with meta */ +#define PCM_BUFSZ_MIN ((4*1024) + sizeof(struct dec_meta_out)) + +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM (2) +#define FRAME_SIZE ((4*1536) + sizeof(struct dec_meta_in)) + +struct audio_aio_ion_region { + struct list_head list; + struct ion_handle *handle; + int fd; + void *vaddr; + unsigned long paddr; + unsigned long kvaddr; + unsigned long len; + unsigned ref_cnt; +}; + +struct audio_aio_event { + struct list_head list; + int event_type; + union msm_audio_event_payload payload; +}; + +struct audio_aio_buffer_node { + struct list_head list; + struct msm_audio_aio_buf buf; + unsigned long paddr; + unsigned long token; + void *kvaddr; + union meta_data meta_info; +}; + +struct q6audio_aio; +struct audio_aio_drv_operations { + void (*out_flush) (struct q6audio_aio *); + void (*in_flush) (struct q6audio_aio *); +}; + +struct q6audio_aio { + atomic_t in_bytes; + atomic_t in_samples; + + struct msm_audio_stream_config str_cfg; + struct msm_audio_buf_cfg buf_cfg; + struct msm_audio_config pcm_cfg; + void *codec_cfg; + + struct audio_client *ac; + + struct mutex lock; + struct mutex read_lock; + struct mutex write_lock; + struct mutex get_event_lock; + wait_queue_head_t cmd_wait; + wait_queue_head_t write_wait; + wait_queue_head_t event_wait; + spinlock_t dsp_lock; + spinlock_t event_queue_lock; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; +#endif + struct list_head out_queue; /* queue to retain output buffers */ + struct list_head in_queue; /* queue to retain input buffers */ + struct list_head free_event_queue; + struct list_head event_queue; + struct list_head ion_region_queue; /* protected by lock */ + struct ion_client *client; + struct audio_aio_drv_operations drv_ops; + union msm_audio_event_payload eos_write_payload; + uint32_t device_events; + uint16_t volume; + uint32_t drv_status; + int event_abort; + int eos_rsp; + int eos_flag; + int opened; + int enabled; + int stopped; + int feedback; + int rflush; /* Read flush */ + int wflush; /* Write flush */ + long (*codec_ioctl)(struct file *, unsigned int, unsigned long); +}; + +void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token, + uint32_t *payload); + +void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token, + uint32_t *payload); + +int insert_eos_buf(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node); + +void extract_meta_out_info(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node, int dir); + +int audio_aio_open(struct q6audio_aio *audio, struct file *file); +int audio_aio_enable(struct q6audio_aio *audio); +void audio_aio_post_event(struct q6audio_aio *audio, int type, + union msm_audio_event_payload payload); +int audio_aio_release(struct inode *inode, struct file *file); +long audio_aio_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync); +void audio_aio_async_out_flush(struct q6audio_aio *audio); +void audio_aio_async_in_flush(struct q6audio_aio *audio); +void audio_aio_ioport_reset(struct q6audio_aio *audio); +int enable_volume_ramp(struct q6audio_aio *audio); +#ifdef CONFIG_DEBUG_FS +ssize_t audio_aio_debug_open(struct inode *inode, struct file *file); +ssize_t audio_aio_debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos); +#endif diff --git a/drivers/misc/qcom/qdsp6v2/audio_wma.c b/drivers/misc/qcom/qdsp6v2/audio_wma.c new file mode 100644 index 000000000000..5e3de8609803 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_wma.c @@ -0,0 +1,214 @@ +/* wma audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved. + * + * 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 <linux/types.h> +#include <linux/msm_audio_wma.h> +#include "audio_utils_aio.h" + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_wma_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_wma_cfg wma_cfg; + struct msm_audio_wma_config_v2 *wma_config; + pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__, + audio, audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg; + wma_cfg.format_tag = wma_config->format_tag; + wma_cfg.ch_cfg = wma_config->numchannels; + wma_cfg.sample_rate = wma_config->samplingrate; + wma_cfg.avg_bytes_per_sec = wma_config->avgbytespersecond; + wma_cfg.block_align = wma_config->block_align; + wma_cfg.valid_bits_per_sample = + wma_config->validbitspersample; + wma_cfg.ch_mask = wma_config->channelmask; + wma_cfg.encode_opt = wma_config->encodeopt; + /* Configure Media format block */ + rc = q6asm_media_format_block_wma(audio->ac, &wma_cfg); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + case AUDIO_GET_WMA_CONFIG_V2: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_wma_config_v2))) { + rc = -EFAULT; + break; + } + break; + } + case AUDIO_SET_WMA_CONFIG_V2: { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_wma_config_v2))) { + rc = -EFAULT; + break; + } + break; + } + default: + pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err("Failed in utils_ioctl: %d\n", rc); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_wma_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for wma decode driver\n"); + return -ENOMEM; + } + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wma_config_v2), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + pr_err("%s:Could not allocate memory for wma" + "config\n", __func__); + kfree(audio); + return -ENOMEM; + } + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_WMA_V9); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open WMA decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_WMA_V9); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("audio_aio_open rc=%d\n", rc); + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_wma_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_wma_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:wmadec success mode[%d]session[%d]\n", __func__, + audio->feedback, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_wma_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_wma_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_wma", + .fops = &audio_wma_fops, +}; + +static int __init audio_wma_init(void) +{ + return misc_register(&audio_wma_misc); +} + +device_initcall(audio_wma_init); diff --git a/drivers/misc/qcom/qdsp6v2/audio_wmapro.c b/drivers/misc/qcom/qdsp6v2/audio_wmapro.c new file mode 100644 index 000000000000..ce49cac47fa5 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_wmapro.c @@ -0,0 +1,273 @@ +/* wmapro audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved. + * + * 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 <linux/types.h> +#include <linux/msm_audio_wmapro.h> +#include "audio_utils_aio.h" + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations audio_wmapro_debug_fops = { + .read = audio_aio_debug_read, + .open = audio_aio_debug_open, +}; +#endif + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct q6audio_aio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_START: { + struct asm_wmapro_cfg wmapro_cfg; + struct msm_audio_wmapro_config *wmapro_config; + pr_debug("%s: AUDIO_START session_id[%d]\n", __func__, + audio->ac->session); + if (audio->feedback == NON_TUNNEL_MODE) { + /* Configure PCM output block */ + rc = q6asm_enc_cfg_blk_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + if (rc < 0) { + pr_err("pcm output block config failed\n"); + break; + } + } + wmapro_config = (struct msm_audio_wmapro_config *) + audio->codec_cfg; + if ((wmapro_config->formattag == 0x162) || + (wmapro_config->formattag == 0x163) || + (wmapro_config->formattag == 0x166) || + (wmapro_config->formattag == 0x167)) { + wmapro_cfg.format_tag = wmapro_config->formattag; + } else { + pr_err("%s:AUDIO_START failed: formattag = %d\n", + __func__, wmapro_config->formattag); + rc = -EINVAL; + break; + } + if ((wmapro_config->numchannels == 1) || + (wmapro_config->numchannels == 2)) { + wmapro_cfg.ch_cfg = wmapro_config->numchannels; + } else { + pr_err("%s:AUDIO_START failed: channels = %d\n", + __func__, wmapro_config->numchannels); + rc = -EINVAL; + break; + } + if ((wmapro_config->samplingrate <= 48000) || + (wmapro_config->samplingrate > 0)) { + wmapro_cfg.sample_rate = + wmapro_config->samplingrate; + } else { + pr_err("%s:AUDIO_START failed: sample_rate = %d\n", + __func__, wmapro_config->samplingrate); + rc = -EINVAL; + break; + } + wmapro_cfg.avg_bytes_per_sec = + wmapro_config->avgbytespersecond; + if ((wmapro_config->asfpacketlength <= 13376) || + (wmapro_config->asfpacketlength > 0)) { + wmapro_cfg.block_align = + wmapro_config->asfpacketlength; + } else { + pr_err("%s:AUDIO_START failed: block_align = %d\n", + __func__, wmapro_config->asfpacketlength); + rc = -EINVAL; + break; + } + if ((wmapro_config->validbitspersample == 16) || + (wmapro_config->validbitspersample == 24)) { + wmapro_cfg.valid_bits_per_sample = + wmapro_config->validbitspersample; + } else { + pr_err("%s:AUDIO_START failed: bitspersample = %d\n", + __func__, + wmapro_config->validbitspersample); + rc = -EINVAL; + break; + } + if ((wmapro_config->channelmask == 4) || + (wmapro_config->channelmask == 3)) { + wmapro_cfg.ch_mask = wmapro_config->channelmask; + } else { + pr_err("%s:AUDIO_START failed: channel_mask = %d\n", + __func__, wmapro_config->channelmask); + rc = -EINVAL; + break; + } + wmapro_cfg.encode_opt = wmapro_config->encodeopt; + wmapro_cfg.adv_encode_opt = + wmapro_config->advancedencodeopt; + wmapro_cfg.adv_encode_opt2 = + wmapro_config->advancedencodeopt2; + /* Configure Media format block */ + rc = q6asm_media_format_block_wmapro(audio->ac, &wmapro_cfg); + if (rc < 0) { + pr_err("cmd media format block failed\n"); + break; + } + rc = audio_aio_enable(audio); + audio->eos_rsp = 0; + audio->eos_flag = 0; + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("Audio Start procedure failed rc=%d\n", rc); + break; + } + pr_debug("AUDIO_START success enable[%d]\n", audio->enabled); + if (audio->stopped == 1) + audio->stopped = 0; + break; + } + case AUDIO_GET_WMAPRO_CONFIG: { + if (copy_to_user((void *)arg, audio->codec_cfg, + sizeof(struct msm_audio_wmapro_config))) { + rc = -EFAULT; + } + break; + } + case AUDIO_SET_WMAPRO_CONFIG: { + if (copy_from_user(audio->codec_cfg, (void *)arg, + sizeof(struct msm_audio_wmapro_config))) { + rc = -EFAULT; + break; + } + break; + } + default: + pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio); + rc = audio->codec_ioctl(file, cmd, arg); + if (rc) + pr_err("Failed in utils_ioctl: %d\n", rc); + } + return rc; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct q6audio_aio *audio = NULL; + int rc = 0; + +#ifdef CONFIG_DEBUG_FS + /* 4 bytes represents decoder number, 1 byte for terminate string */ + char name[sizeof "msm_wmapro_" + 5]; +#endif + audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); + + if (audio == NULL) { + pr_err("Could not allocate memory for wma decode driver\n"); + return -ENOMEM; + } + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wmapro_config), + GFP_KERNEL); + if (audio->codec_cfg == NULL) { + pr_err("%s: Could not allocate memory for wmapro" + "config\n", __func__); + kfree(audio); + return -ENOMEM; + } + + + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; + + audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("Could not allocate memory for audio client\n"); + kfree(audio->codec_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, + FORMAT_WMA_V10PRO); + if (rc < 0) { + pr_err("NT mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = NON_TUNNEL_MODE; + /* open WMA decoder, expected frames is always 1*/ + audio->buf_cfg.frames_per_buf = 0x01; + audio->buf_cfg.meta_info_enable = 0x01; + } else if ((file->f_mode & FMODE_WRITE) && + !(file->f_mode & FMODE_READ)) { + rc = q6asm_open_write(audio->ac, FORMAT_WMA_V10PRO); + if (rc < 0) { + pr_err("T mode Open failed rc=%d\n", rc); + rc = -ENODEV; + goto fail; + } + audio->feedback = TUNNEL_MODE; + audio->buf_cfg.meta_info_enable = 0x00; + } else { + pr_err("Not supported mode\n"); + rc = -EACCES; + goto fail; + } + rc = audio_aio_open(audio, file); + if (rc < 0) { + pr_err("audio_aio_open rc=%d\n", rc); + goto fail; + } + +#ifdef CONFIG_DEBUG_FS + snprintf(name, sizeof name, "msm_wmapro_%04x", audio->ac->session); + audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO, + NULL, (void *)audio, + &audio_wmapro_debug_fops); + + if (IS_ERR(audio->dentry)) + pr_debug("debugfs_create_file failed\n"); +#endif + pr_info("%s:wmapro decoder open success, session_id = %d\n", __func__, + audio->ac->session); + return rc; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->codec_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_wmapro_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_aio_release, + .unlocked_ioctl = audio_ioctl, + .fsync = audio_aio_fsync, +}; + +struct miscdevice audio_wmapro_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_wmapro", + .fops = &audio_wmapro_fops, +}; + +static int __init audio_wmapro_init(void) +{ + return misc_register(&audio_wmapro_misc); +} + +device_initcall(audio_wmapro_init); diff --git a/drivers/misc/qcom/qdsp6v2/evrc_in.c b/drivers/misc/qcom/qdsp6v2/evrc_in.c new file mode 100644 index 000000000000..a7852662d546 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/evrc_in.c @@ -0,0 +1,293 @@ +/* Copyright (c) 2010-2013, The Linux Foundation. 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 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. + * +*/ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/msm_audio_qcp.h> +#include <linux/atomic.h> +#include <asm/ioctls.h> +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((23+sizeof(struct meta_out_dsp)) * 10)) + +/* ------------------- device --------------------- */ +static long evrc_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_evrc_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + /* rate_modulation_cmd set to zero + currently not configurable from user space */ + rc = q6asm_enc_cfg_blk_evrc(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate, 0); + + if (rc < 0) { + pr_err("%s:session id %d: cmd evrc media format block failed\n", + __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block failed\n", + __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + break; + } + case AUDIO_GET_EVRC_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_evrc_enc_config))) + rc = -EFAULT; + break; + } + case AUDIO_SET_EVRC_ENC_CONFIG: { + struct msm_audio_evrc_enc_config cfg; + struct msm_audio_evrc_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + + if (copy_from_user(&cfg, (void *) arg, + sizeof(struct msm_audio_evrc_enc_config))) { + rc = -EFAULT; + break; + } + + if (cfg.min_bit_rate > 4 || + cfg.min_bit_rate < 1 || + (cfg.min_bit_rate == 2)) { + pr_err("%s:session id %d: invalid min bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + if (cfg.max_bit_rate > 4 || + cfg.max_bit_rate < 1 || + (cfg.max_bit_rate == 2)) { + pr_err("%s:session id %d: invalid max bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + enc_cfg->min_bit_rate = cfg.min_bit_rate; + enc_cfg->max_bit_rate = cfg.max_bit_rate; + pr_debug("%s:session id %d: min_bit_rate= 0x%x max_bit_rate=0x%x\n", + __func__, + audio->ac->session, enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate); + break; + } + default: + rc = -EINVAL; + } + return rc; +} + +static int evrc_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_evrc_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) { + pr_err("%s: Could not allocate memory for evrc driver\n", + __func__); + return -ENOMEM; + } + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_evrc_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + pr_err("%s:session id %d: Could not allocate memory for aac config param\n", + __func__, audio->ac->session); + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 23; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->min_bit_rate = 4; + enc_cfg->max_bit_rate = 4; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + audio->event_abort = 0; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for audio client\n", + __func__); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open evrc encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_EVRC, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: NT mode encoder success\n", + __func__, audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_EVRC); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n", + __func__, + audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: T mode encoder success\n", __func__, + audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_ioctl = evrc_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = evrc_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +}; + +struct miscdevice audio_evrc_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_evrc_in", + .fops = &audio_in_fops, +}; + +static int __init evrc_in_init(void) +{ + return misc_register(&audio_evrc_in_misc); +} + +device_initcall(evrc_in_init); diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_common.h b/drivers/misc/qcom/qdsp6v2/q6audio_common.h new file mode 100644 index 000000000000..7fcab43b8922 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/q6audio_common.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2012-2013, The Linux Foundation. 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 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. + * +*/ + + +/* For Decoders */ +#ifndef __Q6_AUDIO_COMMON_H__ +#define __Q6_AUDIO_COMMON_H__ + +#if defined(CONFIG_ARCH_MSM8974) \ + || defined(CONFIG_ARCH_MSM8226) || defined(CONFIG_ARCH_MSM8610) \ + || defined(CONFIG_ARCH_APQ8084) || defined(CONFIG_ARCH_MPQ8092) + +#include <sound/apr_audio-v2.h> +#include <sound/q6asm-v2.h> +#else +#include <sound/apr_audio.h> +#include <sound/q6asm.h> +#endif + +void q6_audio_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv); + +void audio_aio_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *audio); + + +/* For Encoders */ +void q6asm_in_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv); + +void audio_in_get_dsp_frames(void *audio, + uint32_t token, uint32_t *payload); + +#endif /*__Q6_AUDIO_COMMON_H__*/ diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_v2.c b/drivers/misc/qcom/qdsp6v2/q6audio_v2.c new file mode 100644 index 000000000000..7786fc01bcfd --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/q6audio_v2.c @@ -0,0 +1,98 @@ +/* Copyright (c) 2012-2013, The Linux Foundation. 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 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. + * +*/ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/atomic.h> +#include <asm/ioctls.h> +#include "audio_utils.h" + +void q6asm_in_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6audio_in *audio = (struct q6audio_in *)priv; + unsigned long flags; + + pr_debug("%s:session id %d: opcode[0x%x]\n", __func__, + audio->ac->session, opcode); + + spin_lock_irqsave(&audio->dsp_lock, flags); + switch (opcode) { + case ASM_DATA_EVENT_READ_DONE_V2: + audio_in_get_dsp_frames(audio, token, payload); + break; + case ASM_DATA_EVENT_WRITE_DONE_V2: + atomic_inc(&audio->in_count); + wake_up(&audio->write_wait); + break; + case ASM_DATA_EVENT_RENDERED_EOS: + audio->eos_rsp = 1; + wake_up(&audio->read_wait); + break; + case ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2: + break; + case ASM_SESSION_EVENTX_OVERFLOW: + pr_err("%s:session id %d: ASM_SESSION_EVENT_TX_OVERFLOW\n", + __func__, audio->ac->session); + break; + case RESET_EVENTS: + pr_debug("%s:received RESET EVENTS\n", __func__); + audio->enabled = 0; + audio->stopped = 1; + audio->event_abort = 1; + wake_up(&audio->read_wait); + wake_up(&audio->write_wait); + break; + default: + pr_debug("%s:session id %d: Ignore opcode[0x%x]\n", __func__, + audio->ac->session, opcode); + break; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +void audio_in_get_dsp_frames(void *priv, + uint32_t token, uint32_t *payload) +{ + struct q6audio_in *audio = (struct q6audio_in *)priv; + uint32_t index; + + index = token; + pr_debug("%s:session id %d: index=%d nr frames=%d offset[%d]\n", + __func__, audio->ac->session, token, payload[9], + payload[5]); + pr_debug("%s:session id %d: timemsw=%d lsw=%d\n", __func__, + audio->ac->session, payload[7], payload[6]); + pr_debug("%s:session id %d: uflags=0x%8x uid=0x%8x\n", __func__, + audio->ac->session, payload[8], payload[10]); + pr_debug("%s:session id %d: enc_framesotal_size=0x%8x\n", __func__, + audio->ac->session, payload[4]); + + audio->out_frame_info[index][0] = payload[9]; + audio->out_frame_info[index][1] = payload[5]; + + /* statistics of read */ + atomic_add(payload[4], &audio->in_bytes); + atomic_add(payload[9], &audio->in_samples); + + if (atomic_read(&audio->out_count) <= audio->str_cfg.buffer_count) { + atomic_inc(&audio->out_count); + wake_up(&audio->read_wait); + } +} diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c b/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c new file mode 100644 index 000000000000..21040b15e1ce --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c @@ -0,0 +1,219 @@ +/* Copyright (c) 2012-2013, The Linux Foundation. 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 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. + * +*/ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/atomic.h> +#include <asm/ioctls.h> +#include "audio_utils_aio.h" + +void q6_audio_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6audio_aio *audio = (struct q6audio_aio *)priv; + + pr_debug("%s:opcode = %x token = 0x%x\n", __func__, opcode, token); + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE_V2: + case ASM_DATA_EVENT_READ_DONE_V2: + case ASM_DATA_EVENT_RENDERED_EOS: + case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2: + case ASM_STREAM_CMD_SET_ENCDEC_PARAM: + case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY: + case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY: + case RESET_EVENTS: + audio_aio_cb(opcode, token, payload, audio); + break; + default: + pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode); + break; + } +} + +void audio_aio_cb(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv/*struct q6audio_aio *audio*/) +{ + struct q6audio_aio *audio = (struct q6audio_aio *)priv; + union msm_audio_event_payload e_payload; + + switch (opcode) { + case ASM_DATA_EVENT_WRITE_DONE_V2: + pr_debug("%s[%p]:ASM_DATA_EVENT_WRITE_DONE token = 0x%x\n", + __func__, audio, token); + audio_aio_async_write_ack(audio, token, payload); + break; + case ASM_DATA_EVENT_READ_DONE_V2: + pr_debug("%s[%p]:ASM_DATA_EVENT_READ_DONE token = 0x%x\n", + __func__, audio, token); + audio_aio_async_read_ack(audio, token, payload); + break; + case ASM_DATA_EVENT_RENDERED_EOS: + /* EOS Handle */ + pr_debug("%s[%p]:ASM_DATA_CMDRSP_EOS\n", __func__, audio); + if (audio->feedback) { /* Non-Tunnel mode */ + audio->eos_rsp = 1; + /* propagate input EOS i/p buffer, + after receiving DSP acknowledgement */ + if (audio->eos_flag && + (audio->eos_write_payload.aio_buf.buf_addr)) { + audio_aio_post_event(audio, + AUDIO_EVENT_WRITE_DONE, + audio->eos_write_payload); + memset(&audio->eos_write_payload , 0, + sizeof(union msm_audio_event_payload)); + audio->eos_flag = 0; + } + } else { /* Tunnel mode */ + audio->eos_rsp = 1; + wake_up(&audio->write_wait); + wake_up(&audio->cmd_wait); + } + break; + case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2: + case ASM_STREAM_CMD_SET_ENCDEC_PARAM: + pr_debug("%s[%p]:payload0[%x] payloa1d[%x]opcode= 0x%x\n", + __func__, audio, payload[0], payload[1], opcode); + break; + case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY: + case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY: + pr_debug("%s[%p]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0]-sr = %d, payload[1]-chl = %d, payload[2] = %d, payload[3] = %d\n", + __func__, audio, payload[0], + payload[1], payload[2], payload[3]); + + pr_debug("%s[%p]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, sr(prev) = %d, chl(prev) = %d,", + __func__, audio, audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + audio->pcm_cfg.sample_rate = payload[0]; + audio->pcm_cfg.channel_count = payload[1] & 0xFFFF; + e_payload.stream_info.chan_info = audio->pcm_cfg.channel_count; + e_payload.stream_info.sample_rate = audio->pcm_cfg.sample_rate; + audio_aio_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload); + break; + case RESET_EVENTS: + pr_debug("%s: Received opcode:0x%x\n", __func__, opcode); + audio->stopped = 1; + wake_up(&audio->event_wait); + break; + default: + break; + } +} + +void extract_meta_out_info(struct q6audio_aio *audio, + struct audio_aio_buffer_node *buf_node, int dir) +{ + struct dec_meta_out *meta_data = buf_node->kvaddr; + uint32_t temp; + + if (dir) { /* input buffer - Write */ + if (audio->buf_cfg.meta_info_enable) + memcpy(&buf_node->meta_info.meta_in, + (char *)buf_node->kvaddr, sizeof(struct dec_meta_in)); + else + memset(&buf_node->meta_info.meta_in, + 0, sizeof(struct dec_meta_in)); + pr_debug("%s[%p]:i/p: msw_ts 0x%lx lsw_ts 0x%lx nflags 0x%8x\n", + __func__, audio, + buf_node->meta_info.meta_in.ntimestamp.highpart, + buf_node->meta_info.meta_in.ntimestamp.lowpart, + buf_node->meta_info.meta_in.nflags); + } else { /* output buffer - Read */ + memcpy((char *)buf_node->kvaddr, + &buf_node->meta_info.meta_out, + sizeof(struct dec_meta_out)); + meta_data->meta_out_dsp[0].nflags = 0x00000000; + temp = meta_data->meta_out_dsp[0].msw_ts; + meta_data->meta_out_dsp[0].msw_ts = + meta_data->meta_out_dsp[0].lsw_ts; + meta_data->meta_out_dsp[0].lsw_ts = temp; + + pr_debug("%s[%p]:o/p: msw_ts 0x%8x lsw_ts 0x%8x nflags 0x%8x, num_frames = %d\n", + __func__, audio, + ((struct dec_meta_out *)buf_node->kvaddr)->\ + meta_out_dsp[0].msw_ts, + ((struct dec_meta_out *)buf_node->kvaddr)->\ + meta_out_dsp[0].lsw_ts, + ((struct dec_meta_out *)buf_node->kvaddr)->\ + meta_out_dsp[0].nflags, + ((struct dec_meta_out *)buf_node->kvaddr)->num_of_frames); + } +} + +/* Read buffer from DSP / Handle Ack from DSP */ +void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token, + uint32_t *payload) +{ + unsigned long flags; + union msm_audio_event_payload event_payload; + struct audio_aio_buffer_node *filled_buf; + pr_debug("%s\n", __func__); + + /* No active flush in progress */ + if (audio->rflush) + return; + + /* Statistics of read */ + atomic_add(payload[4], &audio->in_bytes); + atomic_add(payload[9], &audio->in_samples); + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (list_empty(&audio->in_queue)) { + spin_unlock_irqrestore(&audio->dsp_lock, flags); + pr_warning("%s unexpected ack from dsp\n", __func__); + return; + } + filled_buf = list_first_entry(&audio->in_queue, + struct audio_aio_buffer_node, list); + + pr_debug("%s token: 0x[%d], filled_buf->token: 0x[%lu]", + __func__, token, filled_buf->token); + if (token == (filled_buf->token)) { + list_del(&filled_buf->list); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + event_payload.aio_buf = filled_buf->buf; + /* Read done Buffer due to flush/normal condition + after EOS event, so append EOS buffer */ + if (audio->eos_rsp == 0x1) { + event_payload.aio_buf.data_len = + insert_eos_buf(audio, filled_buf); + /* Reset flag back to indicate eos intimated */ + audio->eos_rsp = 0; + } else { + filled_buf->meta_info.meta_out.num_of_frames\ + = payload[9]; + event_payload.aio_buf.data_len = payload[4]\ + + payload[5] + sizeof(struct dec_meta_out); + pr_debug("%s[%p]:nr of frames 0x%8x len=%d\n", + __func__, audio, + filled_buf->meta_info.meta_out.num_of_frames, + event_payload.aio_buf.data_len); + extract_meta_out_info(audio, filled_buf, 0); + audio->eos_rsp = 0; + } + pr_debug("%s, posting read done to the app here\n", __func__); + audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE, + event_payload); + kfree(filled_buf); + } else { + pr_err("%s[%p]:expected=%lx ret=%x\n", + __func__, audio, filled_buf->token, token); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } +} diff --git a/drivers/misc/qcom/qdsp6v2/qcelp_in.c b/drivers/misc/qcom/qdsp6v2/qcelp_in.c new file mode 100644 index 000000000000..3a5411ed2625 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/qcelp_in.c @@ -0,0 +1,291 @@ +/* Copyright (c) 2010-2013, The Linux Foundation. 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 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. + * +*/ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/msm_audio_qcp.h> +#include <linux/atomic.h> +#include <asm/ioctls.h> +#include "audio_utils.h" + +/* Buffer with meta*/ +#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in)) + +/* Maximum 10 frames in buffer with meta */ +#define FRAME_SIZE (1 + ((35+sizeof(struct meta_out_dsp)) * 10)) + +/* ------------------- device --------------------- */ +static long qcelp_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct q6audio_in *audio = file->private_data; + int rc = 0; + int cnt = 0; + + switch (cmd) { + case AUDIO_START: { + struct msm_audio_qcelp_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__, + audio->ac->session, audio->buf_alloc); + if (audio->enabled == 1) { + pr_info("%s:AUDIO_START already over\n", __func__); + rc = 0; + break; + } + rc = audio_in_buf_alloc(audio); + if (rc < 0) { + pr_err("%s:session id %d: buffer allocation failed\n", + __func__, audio->ac->session); + break; + } + + /* reduced_rate_level, rate_modulation_cmd set to zero + currently not configurable from user space */ + rc = q6asm_enc_cfg_blk_qcelp(audio->ac, + audio->buf_cfg.frames_per_buf, + enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate, 0, 0); + + if (rc < 0) { + pr_err("%s:session id %d: cmd qcelp media format block failed\n", + __func__, audio->ac->session); + break; + } + if (audio->feedback == NON_TUNNEL_MODE) { + rc = q6asm_media_format_block_pcm(audio->ac, + audio->pcm_cfg.sample_rate, + audio->pcm_cfg.channel_count); + + if (rc < 0) { + pr_err("%s:session id %d: media format block failed\n", + __func__, audio->ac->session); + break; + } + } + pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__, + audio->ac->session, audio->enabled); + rc = audio_in_enable(audio); + if (!rc) { + audio->enabled = 1; + } else { + audio->enabled = 0; + pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n", + __func__, audio->ac->session, rc); + break; + } + while (cnt++ < audio->str_cfg.buffer_count) + q6asm_read(audio->ac); /* Push buffer to DSP */ + rc = 0; + pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n", + __func__, audio->ac->session, audio->enabled); + break; + } + case AUDIO_STOP: { + pr_debug("%s:session id %d: AUDIO_STOP\n", __func__, + audio->ac->session); + rc = audio_in_disable(audio); + if (rc < 0) { + pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n", + __func__, audio->ac->session, + rc); + break; + } + break; + } + case AUDIO_GET_QCELP_ENC_CONFIG: { + if (copy_to_user((void *)arg, audio->enc_cfg, + sizeof(struct msm_audio_qcelp_enc_config))) + rc = -EFAULT; + break; + } + case AUDIO_SET_QCELP_ENC_CONFIG: { + struct msm_audio_qcelp_enc_config cfg; + struct msm_audio_qcelp_enc_config *enc_cfg; + enc_cfg = audio->enc_cfg; + if (copy_from_user(&cfg, (void *) arg, + sizeof(struct msm_audio_qcelp_enc_config))) { + rc = -EFAULT; + break; + } + + if (cfg.min_bit_rate > 4 || + cfg.min_bit_rate < 1) { + pr_err("%s:session id %d: invalid min bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + if (cfg.max_bit_rate > 4 || + cfg.max_bit_rate < 1) { + pr_err("%s:session id %d: invalid max bitrate\n", + __func__, audio->ac->session); + rc = -EINVAL; + break; + } + enc_cfg->min_bit_rate = cfg.min_bit_rate; + enc_cfg->max_bit_rate = cfg.max_bit_rate; + pr_debug("%s:session id %d: min_bit_rate= 0x%x max_bit_rate=0x%x\n", + __func__, + audio->ac->session, enc_cfg->min_bit_rate, + enc_cfg->max_bit_rate); + break; + } + default: + rc = -EINVAL; + } + return rc; +} + +static int qcelp_in_open(struct inode *inode, struct file *file) +{ + struct q6audio_in *audio = NULL; + struct msm_audio_qcelp_enc_config *enc_cfg; + int rc = 0; + + audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL); + + if (audio == NULL) { + pr_err("%s: Could not allocate memory for qcelp driver\n", + __func__); + return -ENOMEM; + } + /* Allocate memory for encoder config param */ + audio->enc_cfg = kzalloc(sizeof(struct msm_audio_qcelp_enc_config), + GFP_KERNEL); + if (audio->enc_cfg == NULL) { + pr_err("%s:session id %d: Could not allocate memory for aac config param\n", + __func__, audio->ac->session); + kfree(audio); + return -ENOMEM; + } + enc_cfg = audio->enc_cfg; + + mutex_init(&audio->lock); + mutex_init(&audio->read_lock); + mutex_init(&audio->write_lock); + spin_lock_init(&audio->dsp_lock); + init_waitqueue_head(&audio->read_wait); + init_waitqueue_head(&audio->write_wait); + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->str_cfg.buffer_size = FRAME_SIZE; + audio->str_cfg.buffer_count = FRAME_NUM; + audio->min_frame_size = 35; + audio->max_frames_per_buf = 10; + audio->pcm_cfg.buffer_size = PCM_BUF_SIZE; + audio->pcm_cfg.buffer_count = PCM_BUF_COUNT; + enc_cfg->min_bit_rate = 4; + enc_cfg->max_bit_rate = 4; + audio->pcm_cfg.channel_count = 1; + audio->pcm_cfg.sample_rate = 8000; + audio->buf_cfg.meta_info_enable = 0x01; + audio->buf_cfg.frames_per_buf = 0x01; + audio->event_abort = 0; + + audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb, + (void *)audio); + + if (!audio->ac) { + pr_err("%s: Could not allocate memory for audio client\n", + __func__); + kfree(audio->enc_cfg); + kfree(audio); + return -ENOMEM; + } + + /* open qcelp encoder in T/NT mode */ + if ((file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = NON_TUNNEL_MODE; + rc = q6asm_open_read_write(audio->ac, FORMAT_V13K, + FORMAT_LINEAR_PCM); + if (rc < 0) { + pr_err("%s:session id %d: NT mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: NT mode encoder success\n", __func__, + audio->ac->session); + } else if (!(file->f_mode & FMODE_WRITE) && + (file->f_mode & FMODE_READ)) { + audio->feedback = TUNNEL_MODE; + rc = q6asm_open_read(audio->ac, FORMAT_V13K); + if (rc < 0) { + pr_err("%s:session id %d: T mode Open failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + /* register for tx overflow (valid for tunnel mode only) */ + rc = q6asm_reg_tx_overflow(audio->ac, 0x01); + if (rc < 0) { + pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n", + __func__, audio->ac->session, rc); + rc = -ENODEV; + goto fail; + } + pr_info("%s:session id %d: T mode encoder success\n", __func__, + audio->ac->session); + } else { + pr_err("%s:session id %d: Unexpected mode\n", __func__, + audio->ac->session); + rc = -EACCES; + goto fail; + } + + audio->opened = 1; + atomic_set(&audio->in_count, PCM_BUF_COUNT); + atomic_set(&audio->out_count, 0x00); + audio->enc_ioctl = qcelp_in_ioctl; + file->private_data = audio; + + pr_info("%s:session id %d: success\n", __func__, audio->ac->session); + return 0; +fail: + q6asm_audio_client_free(audio->ac); + kfree(audio->enc_cfg); + kfree(audio); + return rc; +} + +static const struct file_operations audio_in_fops = { + .owner = THIS_MODULE, + .open = qcelp_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +}; + +struct miscdevice audio_qcelp_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_qcelp_in", + .fops = &audio_in_fops, +}; + +static int __init qcelp_in_init(void) +{ + return misc_register(&audio_qcelp_in_misc); +} + +device_initcall(qcelp_in_init); diff --git a/drivers/misc/qfp_fuse.c b/drivers/misc/qfp_fuse.c index 428d9ffced93..9c97df998f31 100644 --- a/drivers/misc/qfp_fuse.c +++ b/drivers/misc/qfp_fuse.c @@ -355,7 +355,7 @@ static int qfp_get_resource(struct platform_device *pdev, if (pdev->dev.of_node) { struct device_node *np = pdev->dev.of_node; - if (of_property_read_u32(np, "qti,blow-status-offset", + if (of_property_read_u32(np, "qcom,blow-status-offset", &blow_status_offset) == 0) { if ((res->start + blow_status_offset) > res->end) { pr_err("Invalid blow-status-offset\n"); @@ -372,7 +372,7 @@ static int qfp_get_resource(struct platform_device *pdev, return -EINVAL; } - of_property_read_u32(np, "qti,blow-timer", &blow_timer); + of_property_read_u32(np, "qcom,blow-timer", &blow_timer); } else { regulator_name = pdev->dev.platform_data; @@ -460,7 +460,7 @@ static int qfp_fuse_remove(struct platform_device *plat) } static struct of_device_id __attribute__ ((unused)) qfp_fuse_of_match[] = { - { .compatible = "qti,qfp-fuse", }, + { .compatible = "qcom,qfp-fuse", }, {} }; diff --git a/drivers/misc/qpnp-misc.c b/drivers/misc/qpnp-misc.c index c5621f8244d7..4b5d7a03aa18 100644 --- a/drivers/misc/qpnp-misc.c +++ b/drivers/misc/qpnp-misc.c @@ -106,9 +106,9 @@ int qpnp_misc_irqs_available(struct device *consumer_dev) return -EINVAL; } - misc_node = of_parse_phandle(consumer_dev->of_node, "qti,misc-ref", 0); + misc_node = of_parse_phandle(consumer_dev->of_node, "qcom,misc-ref", 0); if (!misc_node) { - pr_debug("Could not find qti,misc-ref property in %s\n", + pr_debug("Could not find qcom,misc-ref property in %s\n", consumer_dev->of_node->full_name); return 0; } diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 0d2f30b4a1df..91400d1a8f02 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -34,6 +34,7 @@ #include <linux/firmware.h> #include <linux/freezer.h> #include <linux/scatterlist.h> +#include <linux/delay.h> #include <mach/board.h> #include <mach/msm_bus.h> #include <mach/msm_bus_board.h> @@ -156,6 +157,7 @@ struct qseecom_control { uint32_t qsee_version; struct device *pdev; bool commonlib_loaded; + struct ion_handle *cmnlib_ion_handle; struct ce_hw_usage_info ce_info; int qsee_bw_count; @@ -203,25 +205,15 @@ struct qseecom_dev_handle { enum qseecom_bandwidth_request_mode mode; }; -enum qseecom_set_clear_key_flag { - QSEECOM_CLEAR_CE_KEY_CMD = 0, - QSEECOM_SET_CE_KEY_CMD, -}; - -struct qseecom_set_key_parameter { - uint32_t ce_hw; - uint32_t pipe; - uint32_t flags; - uint8_t key_id[QSEECOM_KEY_ID_SIZE]; - unsigned char hash32[QSEECOM_HASH_SIZE]; - enum qseecom_set_clear_key_flag set_clear_key_flag; -}; - struct qseecom_sg_entry { uint32_t phys_addr; uint32_t len; }; +uint8_t *key_id_array[QSEECOM_KEY_ID_SIZE] = { + "Disk Encryption" +}; + /* Function proto types */ static int qsee_vote_for_clock(struct qseecom_dev_handle *, int32_t); static void qsee_disable_clock_vote(struct qseecom_dev_handle *, int32_t); @@ -1467,7 +1459,7 @@ static int qseecom_receive_req(struct qseecom_dev_handle *data) if (wait_event_freezable(this_lstnr->rcv_req_wq, __qseecom_listener_has_rcvd_req(data, this_lstnr))) { - pr_warning("Interrupted: exiting Listener Service = %d\n", + pr_debug("Interrupted: exiting Listener Service = %d\n", (uint32_t)data->listener.id); /* woken up for different reason */ return -ERESTARTSYS; @@ -1669,32 +1661,52 @@ static int __qseecom_load_fw(struct qseecom_dev_handle *data, char *appname) static int qseecom_load_commonlib_image(struct qseecom_dev_handle *data) { - int32_t ret = 0; + int ret = 0; + int len = 0; uint32_t fw_size = 0; struct qseecom_load_app_ireq load_req = {0, 0, 0, 0}; struct qseecom_command_scm_resp resp; u8 *img_data = NULL; + ion_phys_addr_t pa; if (__qseecom_get_fw_size("cmnlib", &fw_size)) return -EIO; - img_data = kzalloc(fw_size, GFP_KERNEL); - if (!img_data) { - pr_err("Mem allocation for lib image data failed\n"); + qseecom.cmnlib_ion_handle = ion_alloc(qseecom.ion_clnt, fw_size, + SZ_4K, ION_HEAP(ION_QSECOM_HEAP_ID), 0); + if (IS_ERR_OR_NULL(qseecom.cmnlib_ion_handle)) { + pr_err("ION alloc failed\n"); return -ENOMEM; } + + img_data = (u8 *)ion_map_kernel(qseecom.ion_clnt, + qseecom.cmnlib_ion_handle); + if (IS_ERR_OR_NULL(img_data)) { + pr_err("ION memory mapping for cmnlib failed\n"); + ret = -ENOMEM; + goto exit_ion_free; + } ret = __qseecom_get_fw_data("cmnlib", img_data, &load_req); if (ret) { - kzfree(img_data); - return -EIO; + ret = -EIO; + goto exit_ion_unmap_kernel; + } + /* Get the physical address of the ION BUF */ + ret = ion_phys(qseecom.ion_clnt, qseecom.cmnlib_ion_handle, + &pa, &len); + load_req.phy_addr = (s32)pa; + if (ret) { + pr_err("physical memory retrieval failure\n"); + ret = -EIO; + goto exit_ion_unmap_kernel; } /* Populate the remaining parameters */ load_req.qsee_cmd_id = QSEOS_LOAD_SERV_IMAGE_COMMAND; /* Vote for the SFPB clock */ ret = __qseecom_enable_clk_scale_up(data); if (ret) { - kzfree(img_data); - return -EIO; + ret = -EIO; + goto exit_ion_unmap_kernel; } __cpuc_flush_dcache_area((void *)img_data, fw_size); @@ -1705,31 +1717,41 @@ static int qseecom_load_commonlib_image(struct qseecom_dev_handle *data) if (ret) { pr_err("scm_call to load failed : ret %d\n", ret); ret = -EIO; - } else { - switch (resp.result) { - case QSEOS_RESULT_SUCCESS: - break; - case QSEOS_RESULT_FAILURE: - pr_err("scm call failed w/response result%d\n", - resp.result); - ret = -EINVAL; - break; - case QSEOS_RESULT_INCOMPLETE: - ret = __qseecom_process_incomplete_cmd(data, &resp); - if (ret) - pr_err("process_incomplete_cmd failed err: %d\n", - ret); - break; - default: - pr_err("scm call return unknown response %d\n", - resp.result); - ret = -EINVAL; - break; + goto exit_disable_clk_vote; + } + + switch (resp.result) { + case QSEOS_RESULT_SUCCESS: + break; + case QSEOS_RESULT_FAILURE: + pr_err("scm call failed w/response result%d\n", resp.result); + ret = -EINVAL; + goto exit_disable_clk_vote; + case QSEOS_RESULT_INCOMPLETE: + ret = __qseecom_process_incomplete_cmd(data, &resp); + if (ret) { + pr_err("process_incomplete_cmd failed err: %d\n", ret); + goto exit_disable_clk_vote; } + break; + default: + pr_err("scm call return unknown response %d\n", resp.result); + ret = -EINVAL; + goto exit_disable_clk_vote; } - kzfree(img_data); __qseecom_disable_clk_scale_down(data); return ret; + +exit_disable_clk_vote: + __qseecom_disable_clk_scale_down(data); + +exit_ion_unmap_kernel: + ion_unmap_kernel(qseecom.ion_clnt, qseecom.cmnlib_ion_handle); + +exit_ion_free: + ion_free(qseecom.ion_clnt, qseecom.cmnlib_ion_handle); + qseecom.cmnlib_ion_handle = NULL; + return ret; } static int qseecom_unload_commonlib_image(void) @@ -1761,6 +1783,11 @@ static int qseecom_unload_commonlib_image(void) break; } } + + ion_unmap_kernel(qseecom.ion_clnt, qseecom.cmnlib_ion_handle); + ion_free(qseecom.ion_clnt, qseecom.cmnlib_ion_handle); + qseecom.cmnlib_ion_handle = NULL; + return ret; } @@ -2575,25 +2602,20 @@ static int __qseecom_get_ce_pipe_info( static int __qseecom_generate_and_save_key(struct qseecom_dev_handle *data, enum qseecom_key_management_usage_type usage, - uint8_t *key_id, uint32_t flags) + struct qseecom_key_generate_ireq *ireq) { - struct qseecom_key_generate_ireq ireq; struct qseecom_command_scm_resp resp; int ret; - if (usage != QSEOS_KM_USAGE_DISK_ENCRYPTION) { + if (usage < QSEOS_KM_USAGE_DISK_ENCRYPTION || + usage >= QSEOS_KM_USAGE_MAX) { pr_err("Error:: unsupported usage %d\n", usage); return -EFAULT; } - - memcpy(ireq.key_id, key_id, QSEECOM_KEY_ID_SIZE); - ireq.flags = flags; - ireq.qsee_command_id = QSEOS_GENERATE_KEY; - __qseecom_enable_clk(CLK_QSEE); ret = scm_call(SCM_SVC_TZSCHEDULER, 1, - &ireq, sizeof(struct qseecom_key_generate_ireq), + ireq, sizeof(struct qseecom_key_generate_ireq), &resp, sizeof(resp)); if (ret) { pr_err("scm call to generate key failed : %d\n", ret); @@ -2631,25 +2653,20 @@ static int __qseecom_generate_and_save_key(struct qseecom_dev_handle *data, static int __qseecom_delete_saved_key(struct qseecom_dev_handle *data, enum qseecom_key_management_usage_type usage, - uint8_t *key_id, uint32_t flags) + struct qseecom_key_delete_ireq *ireq) { - struct qseecom_key_delete_ireq ireq; struct qseecom_command_scm_resp resp; int ret; - if (usage != QSEOS_KM_USAGE_DISK_ENCRYPTION) { + if (usage < QSEOS_KM_USAGE_DISK_ENCRYPTION || + usage >= QSEOS_KM_USAGE_MAX) { pr_err("Error:: unsupported usage %d\n", usage); return -EFAULT; } - - memcpy(ireq.key_id, key_id, QSEECOM_KEY_ID_SIZE); - ireq.flags = flags; - ireq.qsee_command_id = QSEOS_DELETE_KEY; - __qseecom_enable_clk(CLK_QSEE); ret = scm_call(SCM_SVC_TZSCHEDULER, 1, - &ireq, sizeof(struct qseecom_key_delete_ireq), + ireq, sizeof(struct qseecom_key_delete_ireq), &resp, sizeof(struct qseecom_command_scm_resp)); if (ret) { pr_err("scm call to delete key failed : %d\n", ret); @@ -2679,39 +2696,23 @@ static int __qseecom_delete_saved_key(struct qseecom_dev_handle *data, static int __qseecom_set_clear_ce_key(struct qseecom_dev_handle *data, enum qseecom_key_management_usage_type usage, - struct qseecom_set_key_parameter *set_key_para) + struct qseecom_key_select_ireq *ireq) { - struct qseecom_key_select_ireq ireq; struct qseecom_command_scm_resp resp; int ret; - if (usage != QSEOS_KM_USAGE_DISK_ENCRYPTION) { - pr_err("Error:: unsupported usage %d\n", usage); - return -EFAULT; + if (usage < QSEOS_KM_USAGE_DISK_ENCRYPTION || + usage >= QSEOS_KM_USAGE_MAX) { + pr_err("Error:: unsupported usage %d\n", usage); + return -EFAULT; } __qseecom_enable_clk(CLK_QSEE); if (qseecom.qsee.instance != qseecom.ce_drv.instance) __qseecom_enable_clk(CLK_CE_DRV); - memcpy(ireq.key_id, set_key_para->key_id, QSEECOM_KEY_ID_SIZE); - ireq.qsee_command_id = QSEOS_SET_KEY; - ireq.ce = set_key_para->ce_hw; - ireq.pipe = set_key_para->pipe; - ireq.flags = set_key_para->flags; - - /* set both PIPE_ENC and PIPE_ENC_XTS*/ - ireq.pipe_type = QSEOS_PIPE_ENC|QSEOS_PIPE_ENC_XTS; - - if (set_key_para->set_clear_key_flag == - QSEECOM_SET_CE_KEY_CMD) - memcpy((void *)ireq.hash, (void *)set_key_para->hash32, - QSEECOM_HASH_SIZE); - else - memset((void *)ireq.hash, 0, QSEECOM_HASH_SIZE); - ret = scm_call(SCM_SVC_TZSCHEDULER, 1, - &ireq, sizeof(struct qseecom_key_select_ireq), + ireq, sizeof(struct qseecom_key_select_ireq), &resp, sizeof(struct qseecom_command_scm_resp)); if (ret) { pr_err("scm call to set QSEOS_PIPE_ENC key failed : %d\n", ret); @@ -2744,16 +2745,63 @@ static int __qseecom_set_clear_ce_key(struct qseecom_dev_handle *data, return ret; } +static int __qseecom_update_current_key_user_info( + struct qseecom_dev_handle *data, + enum qseecom_key_management_usage_type usage, + struct qseecom_key_userinfo_update_ireq *ireq) +{ + struct qseecom_command_scm_resp resp; + int ret; + + if (usage < QSEOS_KM_USAGE_DISK_ENCRYPTION || + usage >= QSEOS_KM_USAGE_MAX) { + pr_err("Error:: unsupported usage %d\n", usage); + return -EFAULT; + } + + __qseecom_enable_clk(CLK_QSEE); + + ret = scm_call(SCM_SVC_TZSCHEDULER, 1, + ireq, sizeof(struct qseecom_key_userinfo_update_ireq), + &resp, sizeof(struct qseecom_command_scm_resp)); + if (ret) { + pr_err("scm call to update key userinfo failed : %d\n", ret); + __qseecom_disable_clk(CLK_QSEE); + if (qseecom.qsee.instance != qseecom.ce_drv.instance) + __qseecom_disable_clk(CLK_CE_DRV); + return ret; + } + + switch (resp.result) { + case QSEOS_RESULT_SUCCESS: + break; + case QSEOS_RESULT_INCOMPLETE: + ret = __qseecom_process_incomplete_cmd(data, &resp); + if (ret) + pr_err("process_incomplete_cmd FAILED, resp.result %d\n", + resp.result); + break; + case QSEOS_RESULT_FAILURE: + default: + pr_err("Set key scm call failed resp.result %d\n", resp.result); + ret = -EINVAL; + break; + } + + __qseecom_disable_clk(CLK_QSEE); + return ret; +} + static int qseecom_create_key(struct qseecom_dev_handle *data, void __user *argp) { uint32_t ce_hw = 0; uint32_t pipe = 0; - uint8_t key_id[QSEECOM_KEY_ID_SIZE] = {0}; int ret = 0; uint32_t flags = 0; - struct qseecom_set_key_parameter set_key_para; struct qseecom_create_key_req create_key_req; + struct qseecom_key_generate_ireq generate_key_ireq; + struct qseecom_key_select_ireq set_key_ireq; ret = copy_from_user(&create_key_req, argp, sizeof(create_key_req)); if (ret) { @@ -2761,7 +2809,8 @@ static int qseecom_create_key(struct qseecom_dev_handle *data, return ret; } - if (create_key_req.usage != QSEOS_KM_USAGE_DISK_ENCRYPTION) { + if (create_key_req.usage < QSEOS_KM_USAGE_DISK_ENCRYPTION || + create_key_req.usage >= QSEOS_KM_USAGE_MAX) { pr_err("Error:: unsupported usage %d\n", create_key_req.usage); return -EFAULT; } @@ -2772,23 +2821,41 @@ static int qseecom_create_key(struct qseecom_dev_handle *data, return -EINVAL; } + generate_key_ireq.flags = flags; + generate_key_ireq.qsee_command_id = QSEOS_GENERATE_KEY; + memset((void *)generate_key_ireq.key_id, 0, QSEECOM_KEY_ID_SIZE); + memset((void *)generate_key_ireq.hash32, 0, QSEECOM_HASH_SIZE); + memcpy((void *)generate_key_ireq.key_id, + (void *)key_id_array[create_key_req.usage - 1], + QSEECOM_KEY_ID_SIZE); + memcpy((void *)generate_key_ireq.hash32, + (void *)create_key_req.hash32, QSEECOM_HASH_SIZE); + ret = __qseecom_generate_and_save_key(data, create_key_req.usage, - key_id, flags); + &generate_key_ireq); if (ret) { pr_err("Failed to generate key on storage: %d\n", ret); return -EFAULT; } - set_key_para.ce_hw = ce_hw; - set_key_para.pipe = pipe; - memcpy(set_key_para.key_id, key_id, QSEECOM_KEY_ID_SIZE); - set_key_para.flags = flags; - set_key_para.set_clear_key_flag = QSEECOM_SET_CE_KEY_CMD; - memcpy((void *)set_key_para.hash32, (void *)create_key_req.hash32, + set_key_ireq.qsee_command_id = QSEOS_SET_KEY; + set_key_ireq.ce = ce_hw; + set_key_ireq.pipe = pipe; + set_key_ireq.flags = flags; + + /* set both PIPE_ENC and PIPE_ENC_XTS*/ + set_key_ireq.pipe_type = QSEOS_PIPE_ENC|QSEOS_PIPE_ENC_XTS; + memset((void *)set_key_ireq.key_id, 0, QSEECOM_KEY_ID_SIZE); + memset((void *)set_key_ireq.hash32, 0, QSEECOM_HASH_SIZE); + memcpy((void *)set_key_ireq.key_id, + (void *)key_id_array[create_key_req.usage - 1], + QSEECOM_KEY_ID_SIZE); + memcpy((void *)set_key_ireq.hash32, (void *)create_key_req.hash32, QSEECOM_HASH_SIZE); ret = __qseecom_set_clear_ce_key(data, create_key_req.usage, - &set_key_para); + &set_key_ireq); + msleep(2000); if (ret) { pr_err("Failed to create key: pipe %d, ce %d: %d\n", pipe, ce_hw, ret); @@ -2803,12 +2870,12 @@ static int qseecom_wipe_key(struct qseecom_dev_handle *data, { uint32_t ce_hw = 0; uint32_t pipe = 0; - uint8_t key_id[QSEECOM_KEY_ID_SIZE] = {0}; int ret = 0; uint32_t flags = 0; int i; struct qseecom_wipe_key_req wipe_key_req; - struct qseecom_set_key_parameter clear_key_para; + struct qseecom_key_delete_ireq delete_key_ireq; + struct qseecom_key_select_ireq clear_key_ireq; ret = copy_from_user(&wipe_key_req, argp, sizeof(wipe_key_req)); if (ret) { @@ -2816,7 +2883,8 @@ static int qseecom_wipe_key(struct qseecom_dev_handle *data, return ret; } - if (wipe_key_req.usage != QSEOS_KM_USAGE_DISK_ENCRYPTION) { + if (wipe_key_req.usage < QSEOS_KM_USAGE_DISK_ENCRYPTION || + wipe_key_req.usage >= QSEOS_KM_USAGE_MAX) { pr_err("Error:: unsupported usage %d\n", wipe_key_req.usage); return -EFAULT; } @@ -2827,22 +2895,32 @@ static int qseecom_wipe_key(struct qseecom_dev_handle *data, return -EINVAL; } - ret = __qseecom_delete_saved_key(data, wipe_key_req.usage, key_id, - flags); + delete_key_ireq.flags = flags; + delete_key_ireq.qsee_command_id = QSEOS_DELETE_KEY; + memset((void *)delete_key_ireq.key_id, 0, QSEECOM_KEY_ID_SIZE); + memcpy((void *)delete_key_ireq.key_id, + (void *)key_id_array[wipe_key_req.usage - 1], + QSEECOM_KEY_ID_SIZE); + memset((void *)delete_key_ireq.hash32, 0, QSEECOM_HASH_SIZE); + + ret = __qseecom_delete_saved_key(data, wipe_key_req.usage, + &delete_key_ireq); if (ret) { pr_err("Failed to delete key from ssd storage: %d\n", ret); return -EFAULT; } - /* an invalid key_id 0xff is used to indicate clear key*/ + clear_key_ireq.qsee_command_id = QSEOS_SET_KEY; + clear_key_ireq.ce = ce_hw; + clear_key_ireq.pipe = pipe; + clear_key_ireq.flags = flags; + clear_key_ireq.pipe_type = QSEOS_PIPE_ENC|QSEOS_PIPE_ENC_XTS; for (i = 0; i < QSEECOM_KEY_ID_SIZE; i++) - clear_key_para.key_id[i] = 0xff; - clear_key_para.ce_hw = ce_hw; - clear_key_para.pipe = pipe; - clear_key_para.flags = flags; - clear_key_para.set_clear_key_flag = QSEECOM_CLEAR_CE_KEY_CMD; + clear_key_ireq.key_id[i] = 0xff; + memset((void *)clear_key_ireq.hash32, 0, QSEECOM_HASH_SIZE); + ret = __qseecom_set_clear_ce_key(data, wipe_key_req.usage, - &clear_key_para); + &clear_key_ireq); if (ret) { pr_err("Failed to wipe key: pipe %d, ce %d: %d\n", pipe, ce_hw, ret); @@ -2852,6 +2930,48 @@ static int qseecom_wipe_key(struct qseecom_dev_handle *data, return ret; } +static int qseecom_update_key_user_info(struct qseecom_dev_handle *data, + void __user *argp) +{ + int ret = 0; + uint32_t flags = 0; + struct qseecom_update_key_userinfo_req update_key_req; + struct qseecom_key_userinfo_update_ireq ireq; + + ret = copy_from_user(&update_key_req, argp, sizeof(update_key_req)); + if (ret) { + pr_err("copy_from_user failed\n"); + return ret; + } + + if (update_key_req.usage < QSEOS_KM_USAGE_DISK_ENCRYPTION || + update_key_req.usage >= QSEOS_KM_USAGE_MAX) { + pr_err("Error:: unsupported usage %d\n", update_key_req.usage); + return -EFAULT; + } + + ireq.qsee_command_id = QSEOS_UPDATE_KEY_USERINFO; + ireq.flags = flags; + memset(ireq.key_id, 0, QSEECOM_KEY_ID_SIZE); + memset((void *)ireq.current_hash32, 0, QSEECOM_HASH_SIZE); + memset((void *)ireq.new_hash32, 0, QSEECOM_HASH_SIZE); + memcpy(ireq.key_id, key_id_array[update_key_req.usage - 1], + QSEECOM_KEY_ID_SIZE); + memcpy((void *)ireq.current_hash32, + (void *)update_key_req.current_hash32, QSEECOM_HASH_SIZE); + memcpy((void *)ireq.new_hash32, + (void *)update_key_req.new_hash32, QSEECOM_HASH_SIZE); + + ret = __qseecom_update_current_key_user_info(data, update_key_req.usage, + &ireq); + msleep(2000); + if (ret) { + pr_err("Failed to update key info: %d\n", ret); + return -EFAULT; + } + return ret; + +} static int qseecom_is_es_activated(void __user *argp) { struct qseecom_is_es_activated_req req; @@ -3024,7 +3144,7 @@ static long qseecom_ioctl(struct file *file, unsigned cmd, ret = qseecom_receive_req(data); atomic_dec(&data->ioctl_count); wake_up_all(&data->abort_wq); - if (ret) + if (ret && (ret != -ERESTARTSYS)) pr_err("failed qseecom_receive_req: %d\n", ret); break; } @@ -3259,14 +3379,12 @@ static long qseecom_ioctl(struct file *file, unsigned cmd, return -EINVAL; } data->released = true; - mutex_lock(&app_access_lock); atomic_inc(&data->ioctl_count); ret = qseecom_create_key(data, argp); if (ret) pr_err("failed to create encryption key: %d\n", ret); atomic_dec(&data->ioctl_count); - mutex_unlock(&app_access_lock); break; } case QSEECOM_IOCTL_WIPE_KEY_REQ: { @@ -3282,13 +3400,31 @@ static long qseecom_ioctl(struct file *file, unsigned cmd, return -EINVAL; } data->released = true; - mutex_lock(&app_access_lock); atomic_inc(&data->ioctl_count); ret = qseecom_wipe_key(data, argp); if (ret) pr_err("failed to wipe encryption key: %d\n", ret); atomic_dec(&data->ioctl_count); - mutex_unlock(&app_access_lock); + break; + } + case QSEECOM_IOCTL_UPDATE_KEY_USER_INFO_REQ: { + if (data->type != QSEECOM_GENERIC) { + pr_err("update key req: invalid handle (%d)\n", + data->type); + ret = -EINVAL; + break; + } + if (qseecom.qsee_version < QSEE_VERSION_05) { + pr_err("Update Key feature unsupported in qsee ver %u\n", + qseecom.qsee_version); + return -EINVAL; + } + data->released = true; + atomic_inc(&data->ioctl_count); + ret = qseecom_update_key_user_info(data, argp); + if (ret) + pr_err("failed to update key user info: %d\n", ret); + atomic_dec(&data->ioctl_count); break; } case QSEECOM_IOCTL_SAVE_PARTITION_HASH_REQ: { diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 44e7c40dc24b..629661a62bab 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -3151,6 +3151,8 @@ static const struct mmc_fixup blk_fixups[] = MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + MMC_FIXUP(CID_NAME_ANY, CID_MANFID_HYNIX, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_BROKEN_DATA_TIMEOUT), /* Some INAND MCP devices advertise incorrect timeout values */ MMC_FIXUP("SEM04G", 0x45, CID_OEMID_ANY, add_quirk_mmc, diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 3e2bf62d9385..aa32d4401854 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1341,6 +1341,11 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card) data->timeout_ns = 4000000000u; /* 4s */ data->timeout_clks = 0; } + /* Some emmc cards require a longer read/write time */ + if (card->quirks & MMC_QUIRK_BROKEN_DATA_TIMEOUT) { + if (data->timeout_ns < 4000000000u) + data->timeout_ns = 4000000000u; /* 4s */ + } } EXPORT_SYMBOL(mmc_set_data_timeout); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 09ca1750cb06..b386dd333ea8 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1249,8 +1249,7 @@ static int mmc_change_bus_speed(struct mmc_host *host, unsigned long *freq) mmc_set_clock(host, (unsigned int) (*freq)); } - if ((mmc_card_hs400(card) || mmc_card_hs200(card)) - && card->host->ops->execute_tuning) { + if (mmc_card_hs200(card) && card->host->ops->execute_tuning) { /* * We try to probe host driver for tuning for any * frequency, it is host driver responsibility to diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 035011bff8bd..51f7571db870 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -48,6 +48,7 @@ #include <linux/pm_qos.h> #include <linux/iopoll.h> #include <linux/clk/msm-clk.h> +#include <linux/irqchip/msm-mpm-irq.h> #include <asm/cacheflush.h> #include <asm/div64.h> @@ -57,7 +58,6 @@ #include <mach/msm_iomap.h> #include <mach/dma.h> #include <mach/sdio_al.h> -#include <mach/mpm.h> #include <mach/msm_bus.h> #include "msm_sdcc.h" diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index d20368a38c9d..93abfe181563 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -37,9 +37,9 @@ #include <linux/pm_runtime.h> #include <linux/mmc/slot-gpio.h> #include <linux/dma-mapping.h> +#include <linux/irqchip/msm-mpm-irq.h> #include <mach/gpio.h> #include <mach/msm_bus.h> -#include <mach/mpm.h> #include <linux/iopoll.h> #include "sdhci-pltfm.h" diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 61b5805e9bc7..c705d0197114 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -812,12 +812,6 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) if (host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL) return 0xE; - /* During initialization, don't use max timeout as the clock is slow */ - if ((host->quirks2 & SDHCI_QUIRK2_USE_RESERVED_MAX_TIMEOUT) && - (host->clock > 400000)) { - return 0xF; - } - /* Unspecified timeout, assume max */ if (!data && !cmd->cmd_timeout_ms) return 0xE; @@ -1724,7 +1718,10 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) * is no on-going data transfer. If so, we need to execute * tuning procedure before sending command. */ - if ((host->flags & SDHCI_NEEDS_RETUNING) && + if ((mrq->cmd->opcode != MMC_SEND_TUNING_BLOCK) && + (mrq->cmd->opcode != MMC_SEND_TUNING_BLOCK_HS400) && + (mrq->cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) && + (host->flags & SDHCI_NEEDS_RETUNING) && !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) { if (mmc->card) { /* eMMC uses cmd21 but sd and sdio use cmd19 */ @@ -1732,6 +1729,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) mmc->card->type == MMC_TYPE_MMC ? MMC_SEND_TUNING_BLOCK_HS200 : MMC_SEND_TUNING_BLOCK; + host->mrq = NULL; spin_unlock_irqrestore(&host->lock, flags); sdhci_execute_tuning(mmc, tuning_opcode); spin_lock_irqsave(&host->lock, flags); @@ -2808,6 +2806,8 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) } if (host->cmd->error) { + if (host->cmd->error == -EILSEQ) + host->flags |= SDHCI_NEEDS_RETUNING; tasklet_schedule(&host->finish_tasklet); return; } @@ -2945,8 +2945,11 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) SDHCI_COMMAND)); if ((command != MMC_SEND_TUNING_BLOCK_HS400) && (command != MMC_SEND_TUNING_BLOCK_HS200) && - (command != MMC_SEND_TUNING_BLOCK)) + (command != MMC_SEND_TUNING_BLOCK)) { pr_msg = true; + if (intmask & SDHCI_INT_DATA_CRC) + host->flags |= SDHCI_NEEDS_RETUNING; + } } else { pr_msg = true; } diff --git a/drivers/mtd/devices/msm_qpic_nand.c b/drivers/mtd/devices/msm_qpic_nand.c index 8521f2fed308..eb671295aba3 100644 --- a/drivers/mtd/devices/msm_qpic_nand.c +++ b/drivers/mtd/devices/msm_qpic_nand.c @@ -32,8 +32,8 @@ #include <linux/of.h> #include <linux/ctype.h> #include <mach/sps.h> -#include <mach/msm_smem.h> #include <mach/msm_bus.h> +#include <soc/qcom/smem.h> #define PAGE_SIZE_2K 2048 #define PAGE_SIZE_4K 4096 diff --git a/drivers/net/ethernet/msm/ecm_ipa.c b/drivers/net/ethernet/msm/ecm_ipa.c index 315abed3ccbc..5fd2f3df557e 100644 --- a/drivers/net/ethernet/msm/ecm_ipa.c +++ b/drivers/net/ethernet/msm/ecm_ipa.c @@ -243,7 +243,7 @@ int ecm_ipa_init(struct ecm_ipa_params *params) ecm_ipa_ctx->net = net; ecm_ipa_ctx->tx_enable = true; ecm_ipa_ctx->rx_enable = true; - ecm_ipa_ctx->rm_enable = true; + ecm_ipa_ctx->rm_enable = false; ecm_ipa_ctx->outstanding_high = DEFAULT_OUTSTANDING_HIGH; ecm_ipa_ctx->outstanding_low = DEFAULT_OUTSTANDING_LOW; atomic_set(&ecm_ipa_ctx->outstanding_pkts, 0); diff --git a/drivers/net/ethernet/msm/emac/emac_ethtool.c b/drivers/net/ethernet/msm/emac/emac_ethtool.c index 28b7b3791692..ab928f3fe4f6 100644 --- a/drivers/net/ethernet/msm/emac/emac_ethtool.c +++ b/drivers/net/ethernet/msm/emac/emac_ethtool.c @@ -93,7 +93,7 @@ static int emac_get_settings(struct net_device *netdev, ecmd->advertising |= hw->autoneg_advertised; ecmd->port = PORT_TP; - ecmd->phy_address = 0; + ecmd->phy_address = hw->phy_addr; ecmd->transceiver = XCVR_INTERNAL; ecmd->autoneg = AUTONEG_ENABLE; diff --git a/drivers/net/ethernet/msm/emac/emac_hw.c b/drivers/net/ethernet/msm/emac/emac_hw.c index 7061e149eb33..bdbc501cd020 100644 --- a/drivers/net/ethernet/msm/emac/emac_hw.c +++ b/drivers/net/ethernet/msm/emac/emac_hw.c @@ -100,9 +100,11 @@ int emac_hw_read_phy_reg(struct emac_hw *hw, bool ext, u8 dev, bool fast, *phy_data = 0; clk_sel = fast ? MDIO_CLK_25_4 : MDIO_CLK_25_28; - retval = emac_disable_mdio_autopoll(hw); - if (retval) - return retval; + if (hw->adpt->no_ephy == false) { + retval = emac_disable_mdio_autopoll(hw); + if (retval) + return retval; + } emac_reg_update32(hw, EMAC, EMAC_PHY_STS, PHY_ADDR_BMSK, (dev << PHY_ADDR_SHFT)); @@ -142,7 +144,9 @@ int emac_hw_read_phy_reg(struct emac_hw *hw, bool ext, u8 dev, bool fast, if (i == MDIO_WAIT_TIMES) retval = -EIO; - emac_enable_mdio_autopoll(hw); + if (hw->adpt->no_ephy == false) + emac_enable_mdio_autopoll(hw); + return retval; } @@ -154,9 +158,11 @@ int emac_hw_write_phy_reg(struct emac_hw *hw, bool ext, u8 dev, clk_sel = fast ? MDIO_CLK_25_4 : MDIO_CLK_25_28; - retval = emac_disable_mdio_autopoll(hw); - if (retval) - return retval; + if (hw->adpt->no_ephy == false) { + retval = emac_disable_mdio_autopoll(hw); + if (retval) + return retval; + } emac_reg_update32(hw, EMAC, EMAC_PHY_STS, PHY_ADDR_BMSK, (dev << PHY_ADDR_SHFT)); @@ -195,17 +201,20 @@ int emac_hw_write_phy_reg(struct emac_hw *hw, bool ext, u8 dev, if (i == MDIO_WAIT_TIMES) retval = -EIO; - emac_enable_mdio_autopoll(hw); + if (hw->adpt->no_ephy == false) + emac_enable_mdio_autopoll(hw); + return retval; } -int emac_read_phy_reg(struct emac_hw *hw, u16 reg_addr, u16 *phy_data) +int emac_read_phy_reg(struct emac_hw *hw, u16 phy_addr, + u16 reg_addr, u16 *phy_data) { unsigned long flags; int retval; spin_lock_irqsave(&hw->mdio_lock, flags); - retval = emac_hw_read_phy_reg(hw, false, hw->phy_addr, true, + retval = emac_hw_read_phy_reg(hw, false, phy_addr, true, reg_addr, phy_data); spin_unlock_irqrestore(&hw->mdio_lock, flags); @@ -218,13 +227,14 @@ int emac_read_phy_reg(struct emac_hw *hw, u16 reg_addr, u16 *phy_data) return retval; } -int emac_write_phy_reg(struct emac_hw *hw, u16 reg_addr, u16 phy_data) +int emac_write_phy_reg(struct emac_hw *hw, u16 phy_addr, + u16 reg_addr, u16 phy_data) { unsigned long flags; int retval; spin_lock_irqsave(&hw->mdio_lock, flags); - retval = emac_hw_write_phy_reg(hw, false, hw->phy_addr, true, + retval = emac_hw_write_phy_reg(hw, false, phy_addr, true, reg_addr, phy_data); spin_unlock_irqrestore(&hw->mdio_lock, flags); @@ -409,15 +419,19 @@ int emac_hw_init_phy(struct emac_hw *hw) spin_lock_init(&hw->mdio_lock); if (hw->adpt->no_ephy == false) { - retval = emac_read_phy_reg(hw, MII_PHYSID1, &phy_id[0]); + retval = emac_read_phy_reg(hw, hw->phy_addr, + MII_PHYSID1, &phy_id[0]); if (retval) return retval; - retval = emac_read_phy_reg(hw, MII_PHYSID2, &phy_id[1]); + retval = emac_read_phy_reg(hw, hw->phy_addr, + MII_PHYSID2, &phy_id[1]); if (retval) return retval; hw->phy_id[0] = phy_id[0]; hw->phy_id[1] = phy_id[1]; + } else { + emac_disable_mdio_autopoll(hw); } hw->autoneg_advertised = EMAC_LINK_SPEED_DEFAULT; @@ -483,11 +497,13 @@ static int emac_hw_setup_phy_link(struct emac_hw *hw, u32 speed, bool autoneg, if (speed & EMAC_LINK_SPEED_1GB_FULL) ctrl1000 |= ADVERTISE_1000FULL; - retval |= emac_write_phy_reg(hw, MII_ADVERTISE, adv); - retval |= emac_write_phy_reg(hw, MII_CTRL1000, ctrl1000); + retval |= emac_write_phy_reg(hw, hw->phy_addr, + MII_ADVERTISE, adv); + retval |= emac_write_phy_reg(hw, hw->phy_addr, + MII_CTRL1000, ctrl1000); bmcr = BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART; - retval |= emac_write_phy_reg(hw, MII_BMCR, bmcr); + retval |= emac_write_phy_reg(hw, hw->phy_addr, MII_BMCR, bmcr); } else { bmcr = BMCR_RESET; switch (speed) { @@ -507,7 +523,7 @@ static int emac_hw_setup_phy_link(struct emac_hw *hw, u32 speed, bool autoneg, return -EINVAL; } - retval |= emac_write_phy_reg(hw, MII_BMCR, bmcr); + retval |= emac_write_phy_reg(hw, hw->phy_addr, MII_BMCR, bmcr); } return retval; @@ -558,7 +574,7 @@ int emac_check_phy_link(struct emac_hw *hw, u32 *speed, bool *link_up) } } - retval = emac_read_phy_reg(hw, MII_BMSR, &bmsr); + retval = emac_read_phy_reg(hw, hw->phy_addr, MII_BMSR, &bmsr); if (retval) return retval; @@ -568,7 +584,7 @@ int emac_check_phy_link(struct emac_hw *hw, u32 *speed, bool *link_up) return 0; } *link_up = true; - retval = emac_read_phy_reg(hw, MII_PSSR, &pssr); + retval = emac_read_phy_reg(hw, hw->phy_addr, MII_PSSR, &pssr); if (retval) return retval; @@ -621,8 +637,8 @@ int emac_hw_get_lpa_speed(struct emac_hw *hw, u32 *speed) } } - retval = emac_read_phy_reg(hw, MII_LPA, &lpa); - retval |= emac_read_phy_reg(hw, MII_STAT1000, &stat1000); + retval = emac_read_phy_reg(hw, hw->phy_addr, MII_LPA, &lpa); + retval |= emac_read_phy_reg(hw, hw->phy_addr, MII_STAT1000, &stat1000); if (retval) return retval; @@ -1147,12 +1163,13 @@ static int emac_get_fc_mode(struct emac_hw *hw, enum emac_fc_mode *mode) int retval = 0; for (i = 0; i < EMAC_MAX_SETUP_LNK_CYCLE; i++) { - retval = emac_read_phy_reg(hw, MII_BMSR, &bmsr); + retval = emac_read_phy_reg(hw, hw->phy_addr, MII_BMSR, &bmsr); if (retval) return retval; if (bmsr & BMSR_LSTATUS) { - retval = emac_read_phy_reg(hw, MII_PSSR, &pssr); + retval = emac_read_phy_reg(hw, hw->phy_addr, + MII_PSSR, &pssr); if (retval) return retval; @@ -1322,7 +1339,7 @@ void emac_hw_start_mac(struct emac_hw *hw) mac |= (CRCE | PCRCE); mac |= ((hw->preamble << PRLEN_SHFT) & PRLEN_BMSK); mac |= BROAD_EN; - mac |= (FLCHK | PROM_MODE | RX_CHKSUM_EN); + mac |= (FLCHK | RX_CHKSUM_EN); mac &= ~(HUGEN | VLAN_STRIP | TPAUSE | SIMR | HUGE | MULTI_ALL | DEBUG_MODE | SINGLE_PAUSE_MODE); diff --git a/drivers/net/ethernet/msm/emac/emac_hw.h b/drivers/net/ethernet/msm/emac/emac_hw.h index 3802b11b20cb..35ea8ee606f5 100644 --- a/drivers/net/ethernet/msm/emac/emac_hw.h +++ b/drivers/net/ethernet/msm/emac/emac_hw.h @@ -33,8 +33,10 @@ extern int emac_hw_read_phy_reg(struct emac_hw *hw, bool ext, u8 dev, bool fast, u16 reg_addr, u16 *phy_data); extern int emac_hw_write_phy_reg(struct emac_hw *hw, bool ext, u8 dev, bool fast, u16 reg_addr, u16 phy_data); -extern int emac_read_phy_reg(struct emac_hw *hw, u16 reg_addr, u16 *phy_data); -extern int emac_write_phy_reg(struct emac_hw *hw, u16 reg_addr, u16 phy_data); +extern int emac_read_phy_reg(struct emac_hw *hw, u16 phy_addr, + u16 reg_addr, u16 *phy_data); +extern int emac_write_phy_reg(struct emac_hw *hw, u16 phy_addr, + u16 reg_addr, u16 phy_data); extern int emac_setup_phy_link(struct emac_hw *hw, u32 speed, bool autoneg, bool fc); extern int emac_setup_phy_link_speed(struct emac_hw *hw, u32 speed, diff --git a/drivers/net/ethernet/msm/emac/emac_main.c b/drivers/net/ethernet/msm/emac/emac_main.c index 61c0868592cf..ca0af609724d 100644 --- a/drivers/net/ethernet/msm/emac/emac_main.c +++ b/drivers/net/ethernet/msm/emac/emac_main.c @@ -86,8 +86,8 @@ static struct emac_irq_info emac_irq[EMAC_NUM_IRQ] = { }; static struct emac_gpio_info emac_gpio[EMAC_NUM_GPIO] = { - { 0, "qti,emac-gpio-mdc" }, - { 0, "qti,emac-gpio-mdio" }, + { 0, "qcom,emac-gpio-mdc" }, + { 0, "qcom,emac-gpio-mdio" }, }; static struct emac_clk_info emac_clk[EMAC_NUM_CLK] = { @@ -110,6 +110,8 @@ void emac_reinit_locked(struct emac_adapter *adpt) } emac_down(adpt, EMAC_HW_CTRL_RESET_MAC); + if (adpt->phy_mode == PHY_INTERFACE_MODE_SGMII) + emac_hw_reset_sgmii(&adpt->hw); emac_up(adpt); CLI_ADPT_FLAG(STATE_RESETTING); @@ -961,7 +963,15 @@ static irqreturn_t emac_sgmii_interrupt(int irq, void *data) if (status & SGMII_ISR_AN_MASK) emac_check_lsc(adpt); - emac_hw_clear_sgmii_intr_status(hw, status); + if (emac_hw_clear_sgmii_intr_status(hw, status) != 0) { + emac_warn(adpt, intr, + "failed to clear sgmii intr, status=0x%x\n", + status); + /* reset */ + SET_ADPT_FLAG(TASK_REINIT_REQ); + emac_task_schedule(adpt); + break; + } } while (1); return IRQ_HANDLED; @@ -1598,12 +1608,9 @@ static int emac_mii_ioctl(struct net_device *netdev, struct mii_ioctl_data *data = if_mii(ifr); int retval = 0; - if (!netif_running(netdev)) - return -EINVAL; - switch (cmd) { case SIOCGMIIPHY: - data->phy_id = 0; + data->phy_id = hw->phy_addr; break; case SIOCGMIIREG: @@ -1617,7 +1624,18 @@ static int emac_mii_ioctl(struct net_device *netdev, break; } - retval = emac_read_phy_reg(hw, data->reg_num, &data->val_out); + if (data->phy_id >= PHY_MAX_ADDR) { + retval = -EFAULT; + break; + } + + if (adpt->no_ephy == false && data->phy_id != hw->phy_addr) { + retval = -EFAULT; + break; + } + + retval = emac_read_phy_reg(hw, data->phy_id, + data->reg_num, &data->val_out); break; case SIOCSMIIREG: @@ -1631,7 +1649,19 @@ static int emac_mii_ioctl(struct net_device *netdev, break; } - retval = emac_write_phy_reg(hw, data->reg_num, data->val_in); + if (data->phy_id >= PHY_MAX_ADDR) { + retval = -EFAULT; + break; + } + + if (adpt->no_ephy == false && data->phy_id != hw->phy_addr) { + retval = -EFAULT; + break; + } + + retval = emac_write_phy_reg(hw, data->phy_id, + data->reg_num, data->val_in); + break; } @@ -2280,10 +2310,10 @@ static int emac_get_resources(struct platform_device *pdev, return retval; /* get time stamp enable flag */ - adpt->tstamp_en = of_property_read_bool(node, "qti,emac-tstamp-en"); + adpt->tstamp_en = of_property_read_bool(node, "qcom,emac-tstamp-en"); /* get no_ephy attribute */ - adpt->no_ephy = of_property_read_bool(node, "qti,no-external-phy"); + adpt->no_ephy = of_property_read_bool(node, "qcom,no-external-phy"); /* get phy address on MDIO bus */ if (adpt->no_ephy == false) { @@ -2506,8 +2536,6 @@ static int emac_probe(struct platform_device *pdev) netdev->vlan_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO | NETIF_F_TSO6; - netdev->flags |= IFF_PROMISC; - setup_timer(&adpt->emac_timer, &emac_timer_routine, (unsigned long)adpt); INIT_WORK(&adpt->emac_task, emac_task_routine); @@ -2582,7 +2610,7 @@ static const struct dev_pm_ops emac_pm_ops = { static struct of_device_id emac_dt_match[] = { { - .compatible = "qti,emac", + .compatible = "qcom,emac", }, {} }; diff --git a/drivers/net/ethernet/msm/rndis_ipa.c b/drivers/net/ethernet/msm/rndis_ipa.c index 8e788e365cb7..1fb7745621ff 100644 --- a/drivers/net/ethernet/msm/rndis_ipa.c +++ b/drivers/net/ethernet/msm/rndis_ipa.c @@ -154,8 +154,10 @@ struct rndis_loopback_pipe { * @directory: debugfs directory for various debugging switches * @tx_filter: flag that enable/disable Tx path to continue to IPA * @tx_dropped: number of filtered out Tx packets + * @tx_dump_enable: dump all Tx packets * @rx_filter: flag that enable/disable Rx path to continue to IPA * @rx_dropped: number of filtered out Rx packets + * @rx_dump_enable: dump all Rx packets * @icmp_filter: allow all ICMP packet to pass through the filters * @rm_enable: flag that enable/disable Resource manager request prior to Tx * @loopback_enable: flag that enable/disable USB stub loopback @@ -180,8 +182,10 @@ struct rndis_ipa_dev { struct net_device *net; u32 tx_filter; u32 tx_dropped; + u32 tx_dump_enable; u32 rx_filter; u32 rx_dropped; + u32 rx_dump_enable; u32 icmp_filter; u32 rm_enable; bool loopback_enable; @@ -270,6 +274,7 @@ static ssize_t rndis_ipa_debugfs_loopback_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos); static ssize_t rndis_ipa_debugfs_atomic_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos); +static void rndis_ipa_dump_skb(struct sk_buff *skb); static int rndis_ipa_debugfs_init(struct rndis_ipa_dev *rndis_ipa_ctx); static void rndis_ipa_debugfs_destroy(struct rndis_ipa_dev *rndis_ipa_ctx); static int rndis_ipa_ep_registers_cfg(u32 usb_to_ipa_hdl, @@ -312,7 +317,7 @@ const struct file_operations rndis_ipa_aggr_ops = { static struct ipa_ep_cfg ipa_to_usb_ep_cfg = { .mode = { .mode = IPA_BASIC, - .dst = IPA_CLIENT_A5_LAN_WAN_CONS, + .dst = IPA_CLIENT_APPS_LAN_CONS, }, .hdr = { .hdr_len = ETH_ALEN + sizeof(struct rndis_pkt_hdr), @@ -357,7 +362,7 @@ static struct ipa_ep_cfg ipa_to_usb_ep_cfg = { static struct ipa_ep_cfg usb_to_ipa_ep_cfg = { .mode = { .mode = IPA_BASIC, - .dst = IPA_CLIENT_A5_LAN_WAN_CONS, + .dst = IPA_CLIENT_APPS_LAN_CONS, }, .hdr = { .hdr_len = ETH_ALEN, @@ -470,9 +475,11 @@ int rndis_ipa_init(struct ipa_usb_init_params *params) rndis_ipa_ctx->tx_filter = false; rndis_ipa_ctx->rx_filter = false; rndis_ipa_ctx->icmp_filter = true; - rndis_ipa_ctx->rm_enable = true; + rndis_ipa_ctx->rm_enable = false; rndis_ipa_ctx->tx_dropped = 0; rndis_ipa_ctx->rx_dropped = 0; + rndis_ipa_ctx->tx_dump_enable = false; + rndis_ipa_ctx->rx_dump_enable = false; rndis_ipa_ctx->outstanding_high = DEFAULT_OUTSTANDING_HIGH; rndis_ipa_ctx->outstanding_low = DEFAULT_OUTSTANDING_LOW; atomic_set(&rndis_ipa_ctx->outstanding_pkts, 0); @@ -747,6 +754,9 @@ static netdev_tx_t rndis_ipa_start_xmit(struct sk_buff *skb, goto out; } + if (unlikely(rndis_ipa_ctx->tx_dump_enable)) + rndis_ipa_dump_skb(skb); + if (unlikely(rndis_ipa_ctx->state != RNDIS_IPA_CONNECTED_AND_UP)) { RNDIS_IPA_ERROR("Missing pipe connected and/or iface up\n"); return -NETDEV_TX_BUSY; @@ -820,6 +830,9 @@ static void rndis_ipa_tx_complete_notify(void *private, NULL_CHECK_NO_RETVAL(private); + RNDIS_IPA_DEBUG("packet Tx-complete, len=%d, skb->protocol=%d", + skb->len, skb->protocol); + if (unlikely((evt != IPA_WRITE_DONE))) { RNDIS_IPA_ERROR("unsupported event on TX call-back\n"); return; @@ -929,6 +942,9 @@ static void rndis_ipa_packet_receive_notify(void *private, RNDIS_IPA_DEBUG("packet Rx, len=%d", skb->len); + if (unlikely(rndis_ipa_ctx->rx_dump_enable)) + rndis_ipa_dump_skb(skb); + if (unlikely(rndis_ipa_ctx->state != RNDIS_IPA_CONNECTED_AND_UP)) { RNDIS_IPA_DEBUG("use connect()/up() before receive()\n"); RNDIS_IPA_DEBUG("packet dropped (length=%d)\n", @@ -1752,6 +1768,24 @@ static const char *rndis_ipa_state_string(enum rndis_ipa_state state) } } +static void rndis_ipa_dump_skb(struct sk_buff *skb) +{ + int i; + u32 *cur = (u32 *)skb->data; + u8 *byte; + + RNDIS_IPA_DEBUG("packet dump start for skb->len=%d\n", + skb->len); + + for (i = 0; i < (skb->len/4); i++) { + byte = (u8 *)(cur + i); + pr_info("%2d %08x %02x %02x %02x %02x\n", + i, *(cur + i), + byte[0], byte[1], byte[2], byte[3]); + } + RNDIS_IPA_DEBUG("packet dump ended for skb->len=%d\n", + skb->len); +} /** * Creates the root folder for the driver @@ -1909,6 +1943,22 @@ static int rndis_ipa_debugfs_init(struct rndis_ipa_dev *rndis_ipa_ctx) goto fail_file; } + file = debugfs_create_bool("tx_dump_enable", flags_read_write, + rndis_ipa_ctx->directory, + &rndis_ipa_ctx->tx_dump_enable); + if (!file) { + RNDIS_IPA_ERROR("fail to create tx_dump_enable file\n"); + goto fail_file; + } + + file = debugfs_create_bool("rx_dump_enable", flags_read_write, + rndis_ipa_ctx->directory, + &rndis_ipa_ctx->rx_dump_enable); + if (!file) { + RNDIS_IPA_ERROR("fail to create rx_dump_enable file\n"); + goto fail_file; + } + RNDIS_IPA_LOG_EXIT(); return 0; diff --git a/drivers/net/wireless/cnss/Makefile b/drivers/net/wireless/cnss/Makefile index 70855ad02876..daf6f261088d 100644 --- a/drivers/net/wireless/cnss/Makefile +++ b/drivers/net/wireless/cnss/Makefile @@ -1,3 +1,4 @@ -# Makefile for cnss platform driver +# Makefile for CNSS platform driver -obj-$(CONFIG_CNSS) +=cnss.o +cnsscore-objs += cnss.o ../wcnss/qcomwlan_secif.o +obj-$(CONFIG_CNSS) += cnsscore.o diff --git a/drivers/net/wireless/cnss/cnss.c b/drivers/net/wireless/cnss/cnss.c index 19d45d2a6b01..967196773cfb 100644 --- a/drivers/net/wireless/cnss/cnss.c +++ b/drivers/net/wireless/cnss/cnss.c @@ -37,6 +37,9 @@ #define QCA6174_VENDOR_ID (0x168C) #define QCA6174_DEVICE_ID (0x003E) +#define QCA6174_REV_ID_OFFSET (0x08) +#define QCA6174_FW_1_1 (0x11) +#define QCA6174_FW_1_3 (0x13) #define WLAN_VREG_NAME "vdd-wlan" #define WLAN_EN_GPIO_NAME "wlan-en-gpio" @@ -75,6 +78,8 @@ static struct cnss_data { struct cnss_wlan_gpio_info gpio_info; bool pcie_link_state; struct pci_saved_state *saved_state; + u16 revision_id; + struct cnss_fw_files fw_files; } *penv; static int cnss_wlan_vreg_set(struct cnss_wlan_vreg_info *vreg_info, bool state) @@ -223,6 +228,56 @@ static u8 cnss_get_pci_dev_bus_number(struct pci_dev *pdev) return pdev->bus->number; } +void cnss_setup_fw_files(u16 revision) +{ + switch (revision) { + + case QCA6174_FW_1_1: + strlcpy(penv->fw_files.image_file, "athwlan11.bin", + CNSS_MAX_FILE_NAME); + strlcpy(penv->fw_files.board_data, "bdatawlan11.bin", + CNSS_MAX_FILE_NAME); + strlcpy(penv->fw_files.otp_data, "otp11.bin", + CNSS_MAX_FILE_NAME); + strlcpy(penv->fw_files.utf_file, "utf11.bin", + CNSS_MAX_FILE_NAME); + break; + + case QCA6174_FW_1_3: + strlcpy(penv->fw_files.image_file, "athwlan13.bin", + CNSS_MAX_FILE_NAME); + strlcpy(penv->fw_files.board_data, "bdatawlan13.bin", + CNSS_MAX_FILE_NAME); + strlcpy(penv->fw_files.otp_data, "otp13.bin", + CNSS_MAX_FILE_NAME); + strlcpy(penv->fw_files.utf_file, "utf13.bin", + CNSS_MAX_FILE_NAME); + break; + + default: + strlcpy(penv->fw_files.image_file, "athwlan.bin", + CNSS_MAX_FILE_NAME); + strlcpy(penv->fw_files.board_data, "bdatawlan.bin", + CNSS_MAX_FILE_NAME); + strlcpy(penv->fw_files.otp_data, "otp.bin", + CNSS_MAX_FILE_NAME); + strlcpy(penv->fw_files.utf_file, "utf.bin", + CNSS_MAX_FILE_NAME); + break; + } +} + +int cnss_get_fw_files(struct cnss_fw_files *pfw_files) +{ + if (!penv || !pfw_files) + return -ENODEV; + + *pfw_files = penv->fw_files; + + return 0; +} +EXPORT_SYMBOL(cnss_get_fw_files); + static int cnss_wlan_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -233,6 +288,9 @@ static int cnss_wlan_pci_probe(struct pci_dev *pdev, penv->pdev = pdev; penv->id = id; + pci_read_config_word(pdev, QCA6174_REV_ID_OFFSET, &penv->revision_id); + cnss_setup_fw_files(penv->revision_id); + if (penv->pcie_link_state) { pci_save_state(pdev); penv->saved_state = pci_store_saved_state(pdev); @@ -750,7 +808,7 @@ static int cnss_remove(struct platform_device *pdev) } static const struct of_device_id cnss_dt_match[] = { - {.compatible = "qti,cnss"}, + {.compatible = "qcom,cnss"}, {} }; diff --git a/drivers/net/wireless/wcnss/qcomwlan_secif.c b/drivers/net/wireless/wcnss/qcomwlan_secif.c index c16e48fc09aa..fd63b62c5ec8 100644 --- a/drivers/net/wireless/wcnss/qcomwlan_secif.c +++ b/drivers/net/wireless/wcnss/qcomwlan_secif.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2013, The Linux Foundation. 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 and @@ -61,3 +61,15 @@ void wcnss_wlan_crypto_free_ablkcipher(struct crypto_ablkcipher *tfm) } EXPORT_SYMBOL(wcnss_wlan_crypto_free_ablkcipher); +void wcnss_wlan_crypto_free_cipher(struct crypto_cipher *tfm) +{ + crypto_free_cipher(tfm); +} +EXPORT_SYMBOL(wcnss_wlan_crypto_free_cipher); + +struct crypto_cipher * +wcnss_wlan_crypto_alloc_cipher(const char *alg_name, u32 type, u32 mask) +{ + return crypto_alloc_cipher(alg_name, type, mask); +} +EXPORT_SYMBOL(wcnss_wlan_crypto_alloc_cipher); diff --git a/drivers/net/wireless/wcnss/wcnss_vreg.c b/drivers/net/wireless/wcnss/wcnss_vreg.c index abbfd2fc997c..1955cfaf3409 100644 --- a/drivers/net/wireless/wcnss/wcnss_vreg.c +++ b/drivers/net/wireless/wcnss/wcnss_vreg.c @@ -16,6 +16,7 @@ #include <linux/gpio.h> #include <linux/delay.h> #include <linux/regulator/consumer.h> +#include <linux/regulator/rpm-smd-regulator.h> #include <linux/mfd/pm8xxx/pm8921.h> #include <linux/mfd/pm8xxx/gpio.h> #include <linux/wcnss_wlan.h> @@ -25,7 +26,6 @@ #include <linux/clk.h> #include <mach/msm_xo.h> #include <mach/msm_iomap.h> -#include <mach/rpm-regulator-smd.h> static void __iomem *msm_wcnss_base; diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c index b520a46bac27..37c64e028577 100644 --- a/drivers/net/wireless/wcnss/wcnss_wlan.c +++ b/drivers/net/wireless/wcnss/wcnss_wlan.c @@ -45,6 +45,7 @@ #endif #define DEVICE "wcnss_wlan" +#define CTRL_DEVICE "wcnss_ctrl" #define VERSION "1.01" #define WCNSS_PIL_DEVICE "wcnss" @@ -147,6 +148,9 @@ static DEFINE_SPINLOCK(reg_spinlock); #define MSM_PRONTO_TXP_PHY_ABORT 0xfb080488 #define MSM_PRONTO_BRDG_ERR_SRC 0xfb080fb0 +#define MSM_PRONTO_ALARMS_TXCTL 0xfb0120a8 +#define MSM_PRONTO_ALARMS_TACTL 0xfb012448 + #define WCNSS_DEF_WLAN_RX_BUFF_COUNT 1024 #define WCNSS_VBATT_THRESHOLD 3500000 #define WCNSS_VBATT_GUARD 200 @@ -157,6 +161,16 @@ static DEFINE_SPINLOCK(reg_spinlock); #define WCNSS_MAX_FRAME_SIZE (4*1024) #define WCNSS_VERSION_LEN 30 #define WCNSS_MAX_BUILD_VER_LEN 256 +#define WCNSS_MAX_CMD_LEN (128) +#define WCNSS_MIN_CMD_LEN (3) +#define WCNSS_MIN_SERIAL_LEN (6) + +/* control messages from userspace */ +#define WCNSS_USR_CTRL_MSG_START 0x00000000 +#define WCNSS_USR_SERIAL_NUM (WCNSS_USR_CTRL_MSG_START + 1) +#define WCNSS_USR_HAS_CAL_DATA (WCNSS_USR_CTRL_MSG_START + 2) + +#define MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x" /* message types */ #define WCNSS_CTRL_MSG_START 0x01000000 @@ -172,6 +186,8 @@ static DEFINE_SPINLOCK(reg_spinlock); #define WCNSS_BUILD_VER_REQ (WCNSS_CTRL_MSG_START + 9) #define WCNSS_BUILD_VER_RSP (WCNSS_CTRL_MSG_START + 10) +/* max 20mhz channel count */ +#define WCNSS_MAX_CH_NUM 45 #define VALID_VERSION(version) \ ((strncmp(version, "INVALID", WCNSS_VERSION_LEN)) ? 1 : 0) @@ -338,6 +354,8 @@ static struct { void __iomem *wlan_tx_status; void __iomem *wlan_tx_phy_aborts; void __iomem *wlan_brdg_err_source; + void __iomem *alarms_txctl; + void __iomem *alarms_tactl; void __iomem *fiq_reg; int nv_downloaded; unsigned char *fw_cal_data; @@ -352,13 +370,62 @@ static struct { int device_opened; int iris_xo_mode_set; int fw_vbatt_state; + char wlan_nv_macAddr[WLAN_MAC_ADDR_SIZE]; + int ctrl_device_opened; struct mutex dev_lock; + struct mutex ctrl_lock; wait_queue_head_t read_wait; struct qpnp_adc_tm_btm_param vbat_monitor_params; struct qpnp_adc_tm_chip *adc_tm_dev; struct mutex vbat_monitor_mutex; + u16 unsafe_ch_count; + u16 unsafe_ch_list[WCNSS_MAX_CH_NUM]; } *penv = NULL; +static ssize_t wcnss_wlan_macaddr_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char macAddr[WLAN_MAC_ADDR_SIZE]; + + if (!penv) + return -ENODEV; + + pr_debug("%s: Receive MAC Addr From user space: %s\n", __func__, buf); + + if (WLAN_MAC_ADDR_SIZE != sscanf(buf, MAC_ADDRESS_STR, + (int *)&macAddr[0], (int *)&macAddr[1], + (int *)&macAddr[2], (int *)&macAddr[3], + (int *)&macAddr[4], (int *)&macAddr[5])) { + + pr_err("%s: Failed to Copy MAC\n", __func__); + return -EINVAL; + } + + memcpy(penv->wlan_nv_macAddr, macAddr, sizeof(penv->wlan_nv_macAddr)); + + pr_info("%s: Write MAC Addr:" MAC_ADDRESS_STR "\n", __func__, + penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1], + penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3], + penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]); + + return count; +} + +static ssize_t wcnss_wlan_macaddr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (!penv) + return -ENODEV; + + return scnprintf(buf, PAGE_SIZE, MAC_ADDRESS_STR, + penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1], + penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3], + penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]); +} + +static DEVICE_ATTR(wcnss_mac_addr, S_IRUSR | S_IWUSR, + wcnss_wlan_macaddr_show, wcnss_wlan_macaddr_store); + static ssize_t wcnss_serial_number_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -485,45 +552,39 @@ void wcnss_pronto_log_debug_regs(void) reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SPARE_OFFSET; reg = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: PRONTO_PMU_SPARE %08x\n", __func__, reg); + pr_err("PRONTO_PMU_SPARE %08x\n", reg); reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CPU_CBCR_OFFSET; reg = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: PRONTO_PMU_COM_CPU_CBCR %08x\n", - __func__, reg); + pr_err("PRONTO_PMU_COM_CPU_CBCR %08x\n", reg); reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_AHB_CBCR_OFFSET; reg = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: PRONTO_PMU_COM_AHB_CBCR %08x\n", - __func__, reg); + pr_err("PRONTO_PMU_COM_AHB_CBCR %08x\n", reg); reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CFG_OFFSET; reg = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: PRONTO_PMU_CFG %08x\n", __func__, reg); + pr_err("PRONTO_PMU_CFG %08x\n", reg); reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CSR_OFFSET; reg = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: PRONTO_PMU_COM_CSR %08x\n", - __func__, reg); + pr_err("PRONTO_PMU_COM_CSR %08x\n", reg); reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SOFT_RESET_OFFSET; reg = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: PRONTO_PMU_SOFT_RESET %08x\n", - __func__, reg); + pr_err("PRONTO_PMU_SOFT_RESET %08x\n", reg); reg_addr = penv->pronto_saw2_base + PRONTO_SAW2_SPM_STS_OFFSET; reg = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: PRONTO_SAW2_SPM_STS %08x\n", __func__, reg); + pr_err("PRONTO_SAW2_SPM_STS %08x\n", reg); reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_GDSCR_OFFSET; reg = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: PRONTO_PMU_COM_GDSCR %08x\n", - __func__, reg); + pr_err("PRONTO_PMU_COM_GDSCR %08x\n", reg); reg >>= 31; if (!reg) { - pr_info_ratelimited("%s: Cannot log, Pronto common SS is power collapsed\n", - __func__); + pr_err("Cannot log, Pronto common SS is power collapsed\n"); return; } reg &= ~(PRONTO_PMU_COM_GDSCR_SW_COLLAPSE @@ -537,35 +598,35 @@ void wcnss_pronto_log_debug_regs(void) reg_addr = penv->pronto_a2xb_base + A2XB_CFG_OFFSET; reg = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: A2XB_CFG_OFFSET %08x\n", __func__, reg); + pr_err("A2XB_CFG_OFFSET %08x\n", reg); reg_addr = penv->pronto_a2xb_base + A2XB_INT_SRC_OFFSET; reg = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: A2XB_INT_SRC_OFFSET %08x\n", __func__, reg); + pr_err("A2XB_INT_SRC_OFFSET %08x\n", reg); reg_addr = penv->pronto_a2xb_base + A2XB_ERR_INFO_OFFSET; reg = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: A2XB_ERR_INFO_OFFSET %08x\n", __func__, reg); + pr_err("A2XB_ERR_INFO_OFFSET %08x\n", reg); reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_INVALID_ADDR_OFFSET; reg = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: CCU_CCPU_INVALID_ADDR %08x\n", __func__, reg); + pr_err("CCU_CCPU_INVALID_ADDR %08x\n", reg); reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR0_OFFSET; reg = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR0 %08x\n", __func__, reg); + pr_err("CCU_CCPU_LAST_ADDR0 %08x\n", reg); reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR1_OFFSET; reg = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR1 %08x\n", __func__, reg); + pr_err("CCU_CCPU_LAST_ADDR1 %08x\n", reg); reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR2_OFFSET; reg = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg); + pr_err("CCU_CCPU_LAST_ADDR2 %08x\n", reg); reg_addr = penv->pronto_pll_base + PRONTO_PLL_STATUS_OFFSET; reg = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: PRONTO_PLL_STATUS %08x\n", __func__, reg); + pr_err("PRONTO_PLL_STATUS %08x\n", reg); tst_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_OFFSET; tst_ctrl_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_CTRL_OFFSET; @@ -575,24 +636,21 @@ void wcnss_pronto_log_debug_regs(void) reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_RDFIFO; writel_relaxed(reg, tst_ctrl_addr); reg = readl_relaxed(tst_addr); - pr_info_ratelimited("%s: Read data FIFO testbus %08x\n", - __func__, reg); + pr_err("Read data FIFO testbus %08x\n", reg); /* command FIFO */ reg = 0; reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CMDFIFO; writel_relaxed(reg, tst_ctrl_addr); reg = readl_relaxed(tst_addr); - pr_info_ratelimited("%s: Command FIFO testbus %08x\n", - __func__, reg); + pr_err("Command FIFO testbus %08x\n", reg); /* write data FIFO */ reg = 0; reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_WRFIFO; writel_relaxed(reg, tst_ctrl_addr); reg = readl_relaxed(tst_addr); - pr_info_ratelimited("%s: Rrite data FIFO testbus %08x\n", - __func__, reg); + pr_err("Rrite data FIFO testbus %08x\n", reg); /* AXIM SEL CFG0 */ reg = 0; @@ -600,8 +658,7 @@ void wcnss_pronto_log_debug_regs(void) WCNSS_TSTBUS_CTRL_AXIM_CFG0; writel_relaxed(reg, tst_ctrl_addr); reg = readl_relaxed(tst_addr); - pr_info_ratelimited("%s: AXIM SEL CFG0 testbus %08x\n", - __func__, reg); + pr_err("AXIM SEL CFG0 testbus %08x\n", reg); /* AXIM SEL CFG1 */ reg = 0; @@ -609,8 +666,7 @@ void wcnss_pronto_log_debug_regs(void) WCNSS_TSTBUS_CTRL_AXIM_CFG1; writel_relaxed(reg, tst_ctrl_addr); reg = readl_relaxed(tst_addr); - pr_info_ratelimited("%s: AXIM SEL CFG1 testbus %08x\n", - __func__, reg); + pr_err("AXIM SEL CFG1 testbus %08x\n", reg); /* CTRL SEL CFG0 */ reg = 0; @@ -618,8 +674,7 @@ void wcnss_pronto_log_debug_regs(void) WCNSS_TSTBUS_CTRL_CTRL_CFG0; writel_relaxed(reg, tst_ctrl_addr); reg = readl_relaxed(tst_addr); - pr_info_ratelimited("%s: CTRL SEL CFG0 testbus %08x\n", - __func__, reg); + pr_err("CTRL SEL CFG0 testbus %08x\n", reg); /* CTRL SEL CFG1 */ reg = 0; @@ -627,7 +682,7 @@ void wcnss_pronto_log_debug_regs(void) WCNSS_TSTBUS_CTRL_CTRL_CFG1; writel_relaxed(reg, tst_ctrl_addr); reg = readl_relaxed(tst_addr); - pr_info_ratelimited("%s: CTRL SEL CFG1 testbus %08x\n", __func__, reg); + pr_err("CTRL SEL CFG1 testbus %08x\n", reg); reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_BCR_OFFSET; @@ -638,38 +693,62 @@ void wcnss_pronto_log_debug_regs(void) reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_AHB_CBCR_OFFSET; reg3 = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: PMU_WLAN_AHB_CBCR %08x\n", __func__, reg3); + pr_err("PMU_WLAN_AHB_CBCR %08x\n", reg3); reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CPU_AHB_CMD_RCGR_OFFSET; reg4 = readl_relaxed(reg_addr); - pr_info_ratelimited("%s: PMU_CPU_CMD_RCGR %08x\n", __func__, reg4); + pr_err("PMU_CPU_CMD_RCGR %08x\n", reg4); if ((reg & PRONTO_PMU_WLAN_BCR_BLK_ARES) || (reg2 & PRONTO_PMU_WLAN_GDSCR_SW_COLLAPSE) || (!(reg4 & PRONTO_PMU_CPU_AHB_CMD_RCGR_ROOT_EN)) || (reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_OFF) || (!(reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_EN))) { - pr_info_ratelimited("%s: Cannot log, wlan domain is power collapsed\n", - __func__); + pr_err("Cannot log, wlan domain is power collapsed\n"); return; } reg = readl_relaxed(penv->wlan_tx_phy_aborts); - pr_info_ratelimited("%s: WLAN_TX_PHY_ABORTS %08x\n", __func__, reg); + pr_err("WLAN_TX_PHY_ABORTS %08x\n", reg); reg = readl_relaxed(penv->wlan_brdg_err_source); - pr_info_ratelimited("%s: WLAN_BRDG_ERR_SOURCE %08x\n", __func__, reg); + pr_err("WLAN_BRDG_ERR_SOURCE %08x\n", reg); reg = readl_relaxed(penv->wlan_tx_status); - pr_info_ratelimited("%s: WLAN_TXP_STATUS %08x\n", __func__, reg); + pr_err("WLAN_TXP_STATUS %08x\n", reg); + + reg = readl_relaxed(penv->alarms_txctl); + pr_err("ALARMS_TXCTL %08x\n", reg); + + reg = readl_relaxed(penv->alarms_tactl); + pr_err("ALARMS_TACTL %08x\n", reg); } EXPORT_SYMBOL(wcnss_pronto_log_debug_regs); #ifdef CONFIG_WCNSS_REGISTER_DUMP_ON_BITE void wcnss_log_debug_regs_on_bite(void) { - if (wcnss_hardware_type() == WCNSS_PRONTO_HW) - wcnss_pronto_log_debug_regs(); + struct platform_device *pdev = wcnss_get_platform_device(); + struct clk *measure; + struct clk *wcnss_debug_mux; + unsigned long clk_rate; + + if (wcnss_hardware_type() != WCNSS_PRONTO_HW) + return; + + measure = clk_get(&pdev->dev, "measure"); + wcnss_debug_mux = clk_get(&pdev->dev, "wcnss_debug"); + + if (!IS_ERR(measure) && !IS_ERR(wcnss_debug_mux)) { + clk_set_parent(measure, wcnss_debug_mux); + clk_rate = clk_get_rate(measure); + pr_debug("wcnss: clock frequency is: %luHz\n", clk_rate); + + if (clk_rate) + wcnss_pronto_log_debug_regs(); + else + pr_err("clock frequency is zero, cannot access PMU or other registers\n"); + } } #endif @@ -707,8 +786,14 @@ static int wcnss_create_sysfs(struct device *dev) if (ret) goto remove_thermal; + ret = device_create_file(dev, &dev_attr_wcnss_mac_addr); + if (ret) + goto remove_version; + return 0; +remove_version: + device_remove_file(dev, &dev_attr_wcnss_version); remove_thermal: device_remove_file(dev, &dev_attr_thermal_mitigation); remove_serial: @@ -723,6 +808,7 @@ static void wcnss_remove_sysfs(struct device *dev) device_remove_file(dev, &dev_attr_serial_number); device_remove_file(dev, &dev_attr_thermal_mitigation); device_remove_file(dev, &dev_attr_wcnss_version); + device_remove_file(dev, &dev_attr_wcnss_mac_addr); } } static void wcnss_smd_notify_event(void *data, unsigned int event) @@ -994,6 +1080,20 @@ unsigned int wcnss_get_serial_number(void) } EXPORT_SYMBOL(wcnss_get_serial_number); +int wcnss_get_wlan_mac_address(char mac_addr[WLAN_MAC_ADDR_SIZE]) +{ + if (!penv) + return -ENODEV; + + memcpy(mac_addr, penv->wlan_nv_macAddr, WLAN_MAC_ADDR_SIZE); + pr_debug("%s: Get MAC Addr:" MAC_ADDRESS_STR "\n", __func__, + penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1], + penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3], + penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]); + return 0; +} +EXPORT_SYMBOL(wcnss_get_wlan_mac_address); + static int enable_wcnss_suspend_notify; static int enable_wcnss_suspend_notify_set(const char *val, @@ -1137,6 +1237,35 @@ u32 wcnss_get_wlan_rx_buff_count(void) } EXPORT_SYMBOL(wcnss_get_wlan_rx_buff_count); +int wcnss_set_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 ch_count) +{ + if (penv && unsafe_ch_list && + (ch_count <= WCNSS_MAX_CH_NUM)) { + memcpy((char *)penv->unsafe_ch_list, + (char *)unsafe_ch_list, ch_count * sizeof(u16)); + penv->unsafe_ch_count = ch_count; + return 0; + } else + return -ENODEV; +} +EXPORT_SYMBOL(wcnss_set_wlan_unsafe_channel); + +int wcnss_get_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 buffer_size, + u16 *ch_count) +{ + if (penv) { + if (buffer_size < penv->unsafe_ch_count * sizeof(u16)) + return -ENODEV; + memcpy((char *)unsafe_ch_list, + (char *)penv->unsafe_ch_list, + penv->unsafe_ch_count * sizeof(u16)); + *ch_count = penv->unsafe_ch_count; + return 0; + } else + return -ENODEV; +} +EXPORT_SYMBOL(wcnss_get_wlan_unsafe_channel); + static int wcnss_smd_tx(void *data, int len) { int ret = 0; @@ -1793,6 +1922,80 @@ static struct notifier_block wcnss_pm_notifier = { .notifier_call = wcnss_pm_notify, }; +static int wcnss_ctrl_open(struct inode *inode, struct file *file) +{ + int rc = 0; + + if (!penv || penv->ctrl_device_opened) + return -EFAULT; + + penv->ctrl_device_opened = 1; + + return rc; +} + + +void process_usr_ctrl_cmd(u8 *buf, size_t len) +{ + u16 cmd = buf[0] << 8 | buf[1]; + + switch (cmd) { + + case WCNSS_USR_SERIAL_NUM: + if (WCNSS_MIN_SERIAL_LEN > len) { + pr_err("%s: Invalid serial number\n", __func__); + return; + } + penv->serial_number = buf[2] << 24 | buf[3] << 16 + | buf[4] << 8 | buf[5]; + break; + + case WCNSS_USR_HAS_CAL_DATA: + if (1 < buf[2]) + pr_err("%s: Invalid data for cal %d\n", __func__, + buf[2]); + has_calibrated_data = buf[2]; + break; + + default: + pr_err("%s: Invalid command %d\n", __func__, cmd); + break; + } +} + +static ssize_t wcnss_ctrl_write(struct file *fp, const char __user + *user_buffer, size_t count, loff_t *position) +{ + int rc = 0; + u8 buf[WCNSS_MAX_CMD_LEN]; + + if (!penv || !penv->ctrl_device_opened || WCNSS_MAX_CMD_LEN < count + || WCNSS_MIN_CMD_LEN > count) + return -EFAULT; + + mutex_lock(&penv->ctrl_lock); + rc = copy_from_user(buf, user_buffer, count); + if (0 == rc) + process_usr_ctrl_cmd(buf, count); + + mutex_unlock(&penv->ctrl_lock); + + return rc; +} + + +static const struct file_operations wcnss_ctrl_fops = { + .owner = THIS_MODULE, + .open = wcnss_ctrl_open, + .write = wcnss_ctrl_write, +}; + +static struct miscdevice wcnss_usr_ctrl = { + .minor = MISC_DYNAMIC_MINOR, + .name = CTRL_DEVICE, + .fops = &wcnss_ctrl_fops, +}; + static int wcnss_trigger_config(struct platform_device *pdev) { @@ -1961,6 +2164,18 @@ wcnss_trigger_config(struct platform_device *pdev) pr_err("%s: ioremap wlan TX STATUS failed\n", __func__); goto fail_ioremap9; } + penv->alarms_txctl = ioremap(MSM_PRONTO_ALARMS_TXCTL, SZ_8); + if (!penv->alarms_txctl) { + ret = -ENOMEM; + pr_err("%s: ioremap alarms TXCTL failed\n", __func__); + goto fail_ioremap10; + } + penv->alarms_tactl = ioremap(MSM_PRONTO_ALARMS_TACTL, SZ_8); + if (!penv->alarms_tactl) { + ret = -ENOMEM; + pr_err("%s: ioremap alarms TACTL failed\n", __func__); + goto fail_ioremap11; + } } penv->adc_tm_dev = qpnp_get_adc_tm(&penv->pdev->dev, "wcnss"); if (IS_ERR(penv->adc_tm_dev)) { @@ -1986,6 +2201,12 @@ wcnss_trigger_config(struct platform_device *pdev) fail_pil: if (penv->riva_ccu_base) iounmap(penv->riva_ccu_base); + if (penv->alarms_tactl) + iounmap(penv->alarms_tactl); +fail_ioremap11: + if (penv->alarms_txctl) + iounmap(penv->alarms_txctl); +fail_ioremap10: if (penv->wlan_tx_status) iounmap(penv->wlan_tx_status); fail_ioremap9: @@ -2183,6 +2404,7 @@ wcnss_wlan_probe(struct platform_device *pdev) } mutex_init(&penv->dev_lock); + mutex_init(&penv->ctrl_lock); mutex_init(&penv->vbat_monitor_mutex); init_waitqueue_head(&penv->read_wait); @@ -2195,6 +2417,9 @@ wcnss_wlan_probe(struct platform_device *pdev) * place */ pr_info(DEVICE " probed in built-in mode\n"); + + misc_register(&wcnss_usr_ctrl); + return misc_register(&wcnss_misc); } diff --git a/drivers/of/of_batterydata.c b/drivers/of/of_batterydata.c index c591e138dfe0..06f8fbe50018 100644 --- a/drivers/of/of_batterydata.c +++ b/drivers/of/of_batterydata.c @@ -222,6 +222,14 @@ static int of_batterydata_load_battery_data(struct device_node *node, if (rc) return rc; + rc = of_property_read_string(node, "qcom,battery-type", + &batt_data->battery_type); + if (rc) { + pr_err("Error reading qcom,battery-type property rc=%d\n", rc); + batt_data->battery_type = NULL; + return rc; + } + OF_PROP_READ(batt_data->fcc, "fcc-mah", node, rc, false); OF_PROP_READ(batt_data->default_rbatt_mohm, "default-rbatt-mohm", node, rc, false); @@ -269,6 +277,7 @@ int of_batterydata_read_data(struct device_node *batterydata_container_node, { struct device_node *node, *best_node; struct batt_ids batt_ids; + const char *battery_type = NULL; int delta, best_delta, batt_id_kohm, rpull_up_kohm, vadc_vdd_uv, best_id_kohm, i, rc = 0; @@ -307,7 +316,12 @@ int of_batterydata_read_data(struct device_node *batterydata_container_node, pr_err("No battery data found\n"); return -ENODATA; } - pr_info("%s loaded\n", best_node->name); + rc = of_property_read_string(best_node, "qcom,battery-type", + &battery_type); + if (!rc) + pr_info("%s loaded\n", battery_type); + else + pr_info("%s loaded\n", best_node->name); return of_batterydata_load_battery_data(best_node, best_id_kohm, batt_data); diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig new file mode 100644 index 000000000000..cfa8f895ee2e --- /dev/null +++ b/drivers/phy/Kconfig @@ -0,0 +1,24 @@ +# +# PHY +# + +menu "PHY Subsystem" + +config GENERIC_PHY + tristate "PHY Core" + help + Generic PHY support. + + This framework is designed to provide a generic interface for PHY + devices present in the kernel. This layer will have the generic + API by which phy drivers can create PHY using the phy framework and + phy users can obtain reference to the PHY. All the users of this + framework should select this config. + +config PHY_MSM_SATA + tristate "MSM SoC SATA 6Gbps PHY driver" + depends on OF && ARCH_MSM + select GENERIC_PHY + help + Support for 6Gbps SATA PHY on MSM chipsets. +endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile new file mode 100644 index 000000000000..f3ace083f7de --- /dev/null +++ b/drivers/phy/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the phy drivers. +# + +obj-$(CONFIG_GENERIC_PHY) += phy-core.o +obj-$(CONFIG_PHY_MSM_SATA) += phy-msm-sata.o diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c new file mode 100644 index 000000000000..03cf8fb81554 --- /dev/null +++ b/drivers/phy/phy-core.c @@ -0,0 +1,698 @@ +/* + * phy-core.c -- Generic Phy framework. + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Kishon Vijay Abraham I <kishon@ti.com> + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/idr.h> +#include <linux/pm_runtime.h> + +static struct class *phy_class; +static DEFINE_MUTEX(phy_provider_mutex); +static LIST_HEAD(phy_provider_list); +static DEFINE_IDA(phy_ida); + +static void devm_phy_release(struct device *dev, void *res) +{ + struct phy *phy = *(struct phy **)res; + + phy_put(phy); +} + +static void devm_phy_provider_release(struct device *dev, void *res) +{ + struct phy_provider *phy_provider = *(struct phy_provider **)res; + + of_phy_provider_unregister(phy_provider); +} + +static void devm_phy_consume(struct device *dev, void *res) +{ + struct phy *phy = *(struct phy **)res; + + phy_destroy(phy); +} + +static int devm_phy_match(struct device *dev, void *res, void *match_data) +{ + return res == match_data; +} + +static struct phy *phy_lookup(struct device *device, const char *port) +{ + unsigned int count; + struct phy *phy; + struct device *dev; + struct phy_consumer *consumers; + struct class_dev_iter iter; + + class_dev_iter_init(&iter, phy_class, NULL, NULL); + while ((dev = class_dev_iter_next(&iter))) { + phy = to_phy(dev); + count = phy->init_data->num_consumers; + consumers = phy->init_data->consumers; + while (count--) { + if (!strcmp(consumers->dev_name, dev_name(device)) && + !strcmp(consumers->port, port)) { + class_dev_iter_exit(&iter); + return phy; + } + consumers++; + } + } + + class_dev_iter_exit(&iter); + return ERR_PTR(-ENODEV); +} + +static struct phy_provider *of_phy_provider_lookup(struct device_node *node) +{ + struct phy_provider *phy_provider; + + list_for_each_entry(phy_provider, &phy_provider_list, list) { + if (phy_provider->dev->of_node == node) + return phy_provider; + } + + return ERR_PTR(-EPROBE_DEFER); +} + +int phy_pm_runtime_get(struct phy *phy) +{ + if (!pm_runtime_enabled(&phy->dev)) + return -ENOTSUPP; + + return pm_runtime_get(&phy->dev); +} +EXPORT_SYMBOL_GPL(phy_pm_runtime_get); + +int phy_pm_runtime_get_sync(struct phy *phy) +{ + if (!pm_runtime_enabled(&phy->dev)) + return -ENOTSUPP; + + return pm_runtime_get_sync(&phy->dev); +} +EXPORT_SYMBOL_GPL(phy_pm_runtime_get_sync); + +int phy_pm_runtime_put(struct phy *phy) +{ + if (!pm_runtime_enabled(&phy->dev)) + return -ENOTSUPP; + + return pm_runtime_put(&phy->dev); +} +EXPORT_SYMBOL_GPL(phy_pm_runtime_put); + +int phy_pm_runtime_put_sync(struct phy *phy) +{ + if (!pm_runtime_enabled(&phy->dev)) + return -ENOTSUPP; + + return pm_runtime_put_sync(&phy->dev); +} +EXPORT_SYMBOL_GPL(phy_pm_runtime_put_sync); + +void phy_pm_runtime_allow(struct phy *phy) +{ + if (!pm_runtime_enabled(&phy->dev)) + return; + + pm_runtime_allow(&phy->dev); +} +EXPORT_SYMBOL_GPL(phy_pm_runtime_allow); + +void phy_pm_runtime_forbid(struct phy *phy) +{ + if (!pm_runtime_enabled(&phy->dev)) + return; + + pm_runtime_forbid(&phy->dev); +} +EXPORT_SYMBOL_GPL(phy_pm_runtime_forbid); + +int phy_init(struct phy *phy) +{ + int ret; + + ret = phy_pm_runtime_get_sync(phy); + if (ret < 0 && ret != -ENOTSUPP) + return ret; + + mutex_lock(&phy->mutex); + if (phy->init_count++ == 0 && phy->ops->init) { + ret = phy->ops->init(phy); + if (ret < 0) { + dev_err(&phy->dev, "phy init failed --> %d\n", ret); + goto out; + } + } + +out: + mutex_unlock(&phy->mutex); + phy_pm_runtime_put(phy); + return ret; +} +EXPORT_SYMBOL_GPL(phy_init); + +int phy_exit(struct phy *phy) +{ + int ret; + + ret = phy_pm_runtime_get_sync(phy); + if (ret < 0 && ret != -ENOTSUPP) + return ret; + + mutex_lock(&phy->mutex); + if (--phy->init_count == 0 && phy->ops->exit) { + ret = phy->ops->exit(phy); + if (ret < 0) { + dev_err(&phy->dev, "phy exit failed --> %d\n", ret); + goto out; + } + } + +out: + mutex_unlock(&phy->mutex); + phy_pm_runtime_put(phy); + return ret; +} +EXPORT_SYMBOL_GPL(phy_exit); + +int phy_power_on(struct phy *phy) +{ + int ret = -ENOTSUPP; + + ret = phy_pm_runtime_get_sync(phy); + if (ret < 0 && ret != -ENOTSUPP) + return ret; + + mutex_lock(&phy->mutex); + if (phy->power_count++ == 0 && phy->ops->power_on) { + ret = phy->ops->power_on(phy); + if (ret < 0) { + dev_err(&phy->dev, "phy poweron failed --> %d\n", ret); + goto out; + } + } + +out: + mutex_unlock(&phy->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(phy_power_on); + +int phy_power_off(struct phy *phy) +{ + int ret = -ENOTSUPP; + + mutex_lock(&phy->mutex); + if (--phy->power_count == 0 && phy->ops->power_off) { + ret = phy->ops->power_off(phy); + if (ret < 0) { + dev_err(&phy->dev, "phy poweroff failed --> %d\n", ret); + goto out; + } + } + +out: + mutex_unlock(&phy->mutex); + phy_pm_runtime_put(phy); + + return ret; +} +EXPORT_SYMBOL_GPL(phy_power_off); + +/** + * of_phy_get() - lookup and obtain a reference to a phy by phandle + * @dev: device that requests this phy + * @index: the index of the phy + * + * Returns the phy associated with the given phandle value, + * after getting a refcount to it or -ENODEV if there is no such phy or + * -EPROBE_DEFER if there is a phandle to the phy, but the device is + * not yet loaded. This function uses of_xlate call back function provided + * while registering the phy_provider to find the phy instance. + */ +static struct phy *of_phy_get(struct device *dev, int index) +{ + int ret; + struct phy_provider *phy_provider; + struct phy *phy = NULL; + struct of_phandle_args args; + + ret = of_parse_phandle_with_args(dev->of_node, "phys", "#phy-cells", + index, &args); + if (ret) { + dev_dbg(dev, "failed to get phy in %s node\n", + dev->of_node->full_name); + return ERR_PTR(-ENODEV); + } + + mutex_lock(&phy_provider_mutex); + phy_provider = of_phy_provider_lookup(args.np); + if (IS_ERR(phy_provider) || !try_module_get(phy_provider->owner)) { + phy = ERR_PTR(-EPROBE_DEFER); + goto err0; + } + + phy = phy_provider->of_xlate(phy_provider->dev, &args); + module_put(phy_provider->owner); + +err0: + mutex_unlock(&phy_provider_mutex); + of_node_put(args.np); + + return phy; +} + +/** + * phy_put() - release the PHY + * @phy: the phy returned by phy_get() + * + * Releases a refcount the caller received from phy_get(). + */ +void phy_put(struct phy *phy) +{ + if (IS_ERR(phy)) + return; + + module_put(phy->ops->owner); + put_device(&phy->dev); +} +EXPORT_SYMBOL_GPL(phy_put); + +/** + * devm_phy_put() - release the PHY + * @dev: device that wants to release this phy + * @phy: the phy returned by devm_phy_get() + * + * destroys the devres associated with this phy and invokes phy_put + * to release the phy. + */ +void devm_phy_put(struct device *dev, struct phy *phy) +{ + int r; + + r = devres_destroy(dev, devm_phy_release, devm_phy_match, phy); + dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n"); +} +EXPORT_SYMBOL_GPL(devm_phy_put); + +/** + * of_phy_simple_xlate() - returns the phy instance from phy provider + * @dev: the PHY provider device + * @args: of_phandle_args (not used here) + * + * Intended to be used by phy provider for the common case where #phy-cells is + * 0. For other cases where #phy-cells is greater than '0', the phy provider + * should provide a custom of_xlate function that reads the *args* and returns + * the appropriate phy. + */ +struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args + *args) +{ + struct phy *phy; + struct class_dev_iter iter; + struct device_node *node = dev->of_node; + + class_dev_iter_init(&iter, phy_class, NULL, NULL); + while ((dev = class_dev_iter_next(&iter))) { + phy = to_phy(dev); + if (node != phy->dev.of_node) + continue; + + class_dev_iter_exit(&iter); + return phy; + } + + class_dev_iter_exit(&iter); + return ERR_PTR(-ENODEV); +} +EXPORT_SYMBOL_GPL(of_phy_simple_xlate); + +/** + * phy_get() - lookup and obtain a reference to a phy. + * @dev: device that requests this phy + * @string: the phy name as given in the dt data or the name of the controller + * port for non-dt case + * + * Returns the phy driver, after getting a refcount to it; or + * -ENODEV if there is no such phy. The caller is responsible for + * calling phy_put() to release that count. + */ +struct phy *phy_get(struct device *dev, const char *string) +{ + int index = 0; + struct phy *phy = NULL; + + if (string == NULL) { + dev_WARN(dev, "missing string\n"); + return ERR_PTR(-EINVAL); + } + + if (dev->of_node) { + index = of_property_match_string(dev->of_node, "phy-names", + string); + phy = of_phy_get(dev, index); + if (IS_ERR(phy)) { + dev_err(dev, "unable to find phy\n"); + return phy; + } + } else { + phy = phy_lookup(dev, string); + if (IS_ERR(phy)) { + dev_err(dev, "unable to find phy\n"); + return phy; + } + } + + if (!try_module_get(phy->ops->owner)) + return ERR_PTR(-EPROBE_DEFER); + + get_device(&phy->dev); + + return phy; +} +EXPORT_SYMBOL_GPL(phy_get); + +/** + * devm_phy_get() - lookup and obtain a reference to a phy. + * @dev: device that requests this phy + * @string: the phy name as given in the dt data or phy device name + * for non-dt case + * + * Gets the phy using phy_get(), and associates a device with it using + * devres. On driver detach, release function is invoked on the devres data, + * then, devres data is freed. + */ +struct phy *devm_phy_get(struct device *dev, const char *string) +{ + struct phy **ptr, *phy; + + ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + phy = phy_get(dev, string); + if (!IS_ERR(phy)) { + *ptr = phy; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return phy; +} +EXPORT_SYMBOL_GPL(devm_phy_get); + +/** + * phy_create() - create a new phy + * @dev: device that is creating the new phy + * @ops: function pointers for performing phy operations + * @init_data: contains the list of PHY consumers or NULL + * + * Called to create a phy using phy framework. + */ +struct phy *phy_create(struct device *dev, const struct phy_ops *ops, + struct phy_init_data *init_data) +{ + int ret; + int id; + struct phy *phy; + + if (!dev) { + dev_WARN(dev, "no device provided for PHY\n"); + ret = -EINVAL; + goto err0; + } + + phy = kzalloc(sizeof(*phy), GFP_KERNEL); + if (!phy) { + ret = -ENOMEM; + goto err0; + } + + id = ida_simple_get(&phy_ida, 0, 0, GFP_KERNEL); + if (id < 0) { + dev_err(dev, "unable to get id\n"); + ret = id; + goto err0; + } + + device_initialize(&phy->dev); + mutex_init(&phy->mutex); + + phy->dev.class = phy_class; + phy->dev.parent = dev; + phy->dev.of_node = dev->of_node; + phy->id = id; + phy->ops = ops; + phy->init_data = init_data; + + ret = dev_set_name(&phy->dev, "phy-%s.%d", dev_name(dev), id); + if (ret) + goto err1; + + ret = device_add(&phy->dev); + if (ret) + goto err1; + + if (pm_runtime_enabled(dev)) { + pm_runtime_enable(&phy->dev); + pm_runtime_no_callbacks(&phy->dev); + } + + return phy; + +err1: + ida_remove(&phy_ida, phy->id); + put_device(&phy->dev); + kfree(phy); + +err0: + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(phy_create); + +/** + * devm_phy_create() - create a new phy + * @dev: device that is creating the new phy + * @ops: function pointers for performing phy operations + * @init_data: contains the list of PHY consumers or NULL + * + * Creates a new PHY device adding it to the PHY class. + * While at that, it also associates the device with the phy using devres. + * On driver detach, release function is invoked on the devres data, + * then, devres data is freed. + */ +struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops, + struct phy_init_data *init_data) +{ + struct phy **ptr, *phy; + + ptr = devres_alloc(devm_phy_consume, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + phy = phy_create(dev, ops, init_data); + if (!IS_ERR(phy)) { + *ptr = phy; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return phy; +} +EXPORT_SYMBOL_GPL(devm_phy_create); + +/** + * phy_destroy() - destroy the phy + * @phy: the phy to be destroyed + * + * Called to destroy the phy. + */ +void phy_destroy(struct phy *phy) +{ + pm_runtime_disable(&phy->dev); + device_unregister(&phy->dev); +} +EXPORT_SYMBOL_GPL(phy_destroy); + +/** + * devm_phy_destroy() - destroy the PHY + * @dev: device that wants to release this phy + * @phy: the phy returned by devm_phy_get() + * + * destroys the devres associated with this phy and invokes phy_destroy + * to destroy the phy. + */ +void devm_phy_destroy(struct device *dev, struct phy *phy) +{ + int r; + + r = devres_destroy(dev, devm_phy_consume, devm_phy_match, phy); + dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n"); +} +EXPORT_SYMBOL_GPL(devm_phy_destroy); + +/** + * __of_phy_provider_register() - create/register phy provider with the framework + * @dev: struct device of the phy provider + * @owner: the module owner containing of_xlate + * @of_xlate: function pointer to obtain phy instance from phy provider + * + * Creates struct phy_provider from dev and of_xlate function pointer. + * This is used in the case of dt boot for finding the phy instance from + * phy provider. + */ +struct phy_provider *__of_phy_provider_register(struct device *dev, + struct module *owner, struct phy * (*of_xlate)(struct device *dev, + struct of_phandle_args *args)) +{ + struct phy_provider *phy_provider; + + phy_provider = kzalloc(sizeof(*phy_provider), GFP_KERNEL); + if (!phy_provider) + return ERR_PTR(-ENOMEM); + + phy_provider->dev = dev; + phy_provider->owner = owner; + phy_provider->of_xlate = of_xlate; + + mutex_lock(&phy_provider_mutex); + list_add_tail(&phy_provider->list, &phy_provider_list); + mutex_unlock(&phy_provider_mutex); + + return phy_provider; +} +EXPORT_SYMBOL_GPL(__of_phy_provider_register); + +/** + * __devm_of_phy_provider_register() - create/register phy provider with the + * framework + * @dev: struct device of the phy provider + * @owner: the module owner containing of_xlate + * @of_xlate: function pointer to obtain phy instance from phy provider + * + * Creates struct phy_provider from dev and of_xlate function pointer. + * This is used in the case of dt boot for finding the phy instance from + * phy provider. While at that, it also associates the device with the + * phy provider using devres. On driver detach, release function is invoked + * on the devres data, then, devres data is freed. + */ +struct phy_provider *__devm_of_phy_provider_register(struct device *dev, + struct module *owner, struct phy * (*of_xlate)(struct device *dev, + struct of_phandle_args *args)) +{ + struct phy_provider **ptr, *phy_provider; + + ptr = devres_alloc(devm_phy_provider_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + phy_provider = __of_phy_provider_register(dev, owner, of_xlate); + if (!IS_ERR(phy_provider)) { + *ptr = phy_provider; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return phy_provider; +} +EXPORT_SYMBOL_GPL(__devm_of_phy_provider_register); + +/** + * of_phy_provider_unregister() - unregister phy provider from the framework + * @phy_provider: phy provider returned by of_phy_provider_register() + * + * Removes the phy_provider created using of_phy_provider_register(). + */ +void of_phy_provider_unregister(struct phy_provider *phy_provider) +{ + if (IS_ERR(phy_provider)) + return; + + mutex_lock(&phy_provider_mutex); + list_del(&phy_provider->list); + kfree(phy_provider); + mutex_unlock(&phy_provider_mutex); +} +EXPORT_SYMBOL_GPL(of_phy_provider_unregister); + +/** + * devm_of_phy_provider_unregister() - remove phy provider from the framework + * @dev: struct device of the phy provider + * + * destroys the devres associated with this phy provider and invokes + * of_phy_provider_unregister to unregister the phy provider. + */ +void devm_of_phy_provider_unregister(struct device *dev, + struct phy_provider *phy_provider) { + int r; + + r = devres_destroy(dev, devm_phy_provider_release, devm_phy_match, + phy_provider); + dev_WARN_ONCE(dev, r, "couldn't find PHY provider device resource\n"); +} +EXPORT_SYMBOL_GPL(devm_of_phy_provider_unregister); + +/** + * phy_release() - release the phy + * @dev: the dev member within phy + * + * When the last reference to the device is removed, it is called + * from the embedded kobject as release method. + */ +static void phy_release(struct device *dev) +{ + struct phy *phy; + + phy = to_phy(dev); + dev_vdbg(dev, "releasing '%s'\n", dev_name(dev)); + ida_remove(&phy_ida, phy->id); + kfree(phy); +} + +static int __init phy_core_init(void) +{ + phy_class = class_create(THIS_MODULE, "phy"); + if (IS_ERR(phy_class)) { + pr_err("failed to create phy class --> %ld\n", + PTR_ERR(phy_class)); + return PTR_ERR(phy_class); + } + + phy_class->dev_release = phy_release; + + return 0; +} +module_init(phy_core_init); + +static void __exit phy_core_exit(void) +{ + class_destroy(phy_class); +} +module_exit(phy_core_exit); + +MODULE_DESCRIPTION("Generic PHY Framework"); +MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-msm-sata.c b/drivers/phy/phy-msm-sata.c new file mode 100644 index 000000000000..02a480010796 --- /dev/null +++ b/drivers/phy/phy-msm-sata.c @@ -0,0 +1,663 @@ +/* + * Copyright (c) 2013, The Linux Foundation. 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 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. + */ + +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/time.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/phy/phy.h> +#include <linux/iopoll.h> +#include <linux/regulator/consumer.h> + +/* QSERDES COMMON registers */ +#define QSERDES_COM_SYS_CLK_CTRL 0x000 +#define QSERDES_COM_PLL_IP_SETI 0x018 +#define QSERDES_COM_PLL_CP_SETI 0x024 +#define QSERDES_COM_PLL_IP_SETP 0x028 +#define QSERDES_COM_PLL_CP_SETP 0x02c +#define QSERDES_COM_SYSCLK_EN_SEL 0x038 +#define QSERDES_COM_RESETSM_CNTRL 0x040 +#define QSERDES_COM_PLLLOCK_CMP1 0x044 +#define QSERDES_COM_PLLLOCK_CMP2 0x048 +#define QSERDES_COM_PLLLOCK_CMP3 0x04c +#define QSERDES_COM_PLLLOCK_CMP_EN 0x050 +#define QSERDES_COM_DEC_START1 0x064 +#define QSERDES_COM_SSC_EN_CENTER 0x06c +#define QSERDES_COM_SSC_ADJ_PER1 0x070 +#define QSERDES_COM_SSC_ADJ_PER2 0x074 +#define QSERDES_COM_SSC_PER1 0x078 +#define QSERDES_COM_SSC_PER2 0x07c +#define QSERDES_COM_SSC_STEP_SIZE1 0x080 +#define QSERDES_COM_SSC_STEP_SIZE2 0x084 +#define QSERDES_COM_DIV_FRAC_START1 0x098 +#define QSERDES_COM_DIV_FRAC_START2 0x09c +#define QSERDES_COM_DIV_FRAC_START3 0x0a0 +#define QSERDES_COM_DEC_START2 0x0a4 +#define QSERDES_COM_PLL_CRCTRL 0x0ac +#define QSERDES_COM_RESET_SM 0x0bc + +/* QSERDES TX registers */ +#define QSERDES_TX_BIST_MODE_LANENO 0x100 +#define QSERDES_TX_TX_EMP_POST1_LVL 0x108 +#define QSERDES_TX_TX_DRV_LVL 0x10c + +/* QSERDES RX registers */ +#define QSERDES_RX_CDR_CONTROL 0x200 +#define QSERDES_RX_SIGDET_CNTRL 0x234 +#define QSERDES_RX_PWM_CNTRL1 0x280 +#define QSERDES_RX_PWM_CNTRL2 0x284 +#define QSERDES_RX_CDR_CONTROL_QUARTER 0x29c +#define QSERDES_RX_RX_SIGDET_PWMDECSTATUS 0x2D8 + +/* SATA PHY registers */ +#define SATA_PHY_SERDES_START 0x300 +#define SATA_PHY_CMN_PWR_CTRL 0x304 +#define SATA_PHY_RX_PWR_CTRL 0x308 +#define SATA_PHY_TX_PWR_CTRL 0x30c +#define SATA_PHY_LANE_CTRL1 0x318 +#define SATA_PHY_CDR_CTRL0 0x358 +#define SATA_PHY_TX_DRV_WAKEUP 0x360 +#define SATA_PHY_CLK_BUF_SETTLING 0x364 +#define SATA_PHY_SPDNEG_CFG0 0x370 +#define SATA_PHY_SPDNEG_CFG1 0x374 +#define SATA_PHY_POW_DWN_CTRL0 0x380 +#define SATA_PHY_ALIGNP 0x3a4 + +#define MAX_PROP_NAME 32 +#define VDDA_PHY_MIN_UV 1000000 +#define VDDA_PHY_MAX_UV 1000000 +#define VDDA_PLL_MIN_UV 1800000 +#define VDDA_PLL_MAX_UV 1800000 + +struct msm_sata_phy_vreg { + const char *name; + struct regulator *reg; + int max_uA; + int min_uV; + int max_uV; + bool enabled; +}; + +struct msm_sata_phy { + struct device *dev; + void __iomem *mmio; + void __iomem *phy_sel; + struct clk *ref_clk_src; + struct clk *ref_clk_parent; + struct clk *ref_clk; + struct clk *rxoob_clk; + bool is_ref_clk_enabled; + bool is_rxoob_clk_enabled; + struct msm_sata_phy_vreg vdda_pll; + struct msm_sata_phy_vreg vdda_phy; + bool is_powered_on; +}; + +static int msm_sata_enable_phy_rxoob_clk(struct msm_sata_phy *phy) +{ + int err = 0; + + if (phy->is_rxoob_clk_enabled) + goto out; + + /* set max. 100MHz */ + err = clk_set_rate(phy->rxoob_clk, 100000000); + if (err) { + dev_err(phy->dev, "%s: rxoob_clk set rate failed %d\n", + __func__, err); + goto out; + } + + err = clk_prepare_enable(phy->rxoob_clk); + if (err) { + dev_err(phy->dev, "%s: rxoob_clk enable failed %d\n", + __func__, err); + goto out; + } + + phy->is_rxoob_clk_enabled = true; +out: + return err; +} + +static void msm_sata_disable_phy_rxoob_clk(struct msm_sata_phy *phy) +{ + if (phy->is_rxoob_clk_enabled) { + clk_disable_unprepare(phy->rxoob_clk); + phy->is_rxoob_clk_enabled = false; + } +} + +static int msm_sata_enable_phy_ref_clk(struct msm_sata_phy *phy) +{ + int err = 0; + + if (phy->is_ref_clk_enabled) + goto out; + + /* + * reference clock is propagated in a daisy-chained manner from + * source to phy, so ungate them at each stage. + */ + err = clk_prepare_enable(phy->ref_clk_src); + if (err) { + dev_err(phy->dev, "%s: ref_clk_src enable failed %d\n", + __func__, err); + goto out; + } + + err = clk_prepare_enable(phy->ref_clk_parent); + if (err) { + dev_err(phy->dev, "%s: ref_clk_parent enable failed %d\n", + __func__, err); + goto out_disable_src; + } + + err = clk_prepare_enable(phy->ref_clk); + if (err) { + dev_err(phy->dev, "%s: ref_clk enable failed %d\n", + __func__, err); + goto out_disable_parent; + } + + phy->is_ref_clk_enabled = true; + goto out; + +out_disable_parent: + clk_disable_unprepare(phy->ref_clk_parent); +out_disable_src: + clk_disable_unprepare(phy->ref_clk_src); +out: + return err; +} + +static void msm_sata_disable_phy_ref_clk(struct msm_sata_phy *phy) +{ + if (phy->is_ref_clk_enabled) { + clk_disable_unprepare(phy->ref_clk); + clk_disable_unprepare(phy->ref_clk_parent); + clk_disable_unprepare(phy->ref_clk_src); + phy->is_ref_clk_enabled = false; + } +} + +static int msm_sata_phy_cfg_vreg(struct device *dev, + struct msm_sata_phy_vreg *vreg, bool on) +{ + int err = 0; + struct regulator *reg = vreg->reg; + const char *name = vreg->name; + int min_uV, uA_load; + + BUG_ON(!vreg); + + if (regulator_count_voltages(reg) > 0) { + min_uV = on ? vreg->min_uV : 0; + err = regulator_set_voltage(reg, min_uV, vreg->max_uV); + if (err) { + dev_err(dev, "%s: %s set voltage failed, err=%d\n", + __func__, name, err); + goto out; + } + + uA_load = on ? vreg->max_uA : 0; + err = regulator_set_optimum_mode(reg, uA_load); + if (err >= 0) { + /* + * regulator_set_optimum_mode() returns new regulator + * mode upon success. + */ + err = 0; + } else { + dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n", + __func__, name, uA_load, err); + goto out; + } + } +out: + return err; +} + +static int msm_sata_phy_enable_vreg(struct msm_sata_phy *phy, + struct msm_sata_phy_vreg *vreg) +{ + struct device *dev = phy->dev; + int err = 0; + + if (!vreg || vreg->enabled) + goto out; + + err = msm_sata_phy_cfg_vreg(dev, vreg, true); + if (!err) + err = regulator_enable(vreg->reg); + + if (!err) + vreg->enabled = true; + else + dev_err(dev, "%s: %s enable failed, err=%d\n", + __func__, vreg->name, err); +out: + return err; +} + +static int msm_sata_phy_disable_vreg(struct msm_sata_phy *phy, + struct msm_sata_phy_vreg *vreg) +{ + struct device *dev = phy->dev; + int err = 0; + + if (!vreg || !vreg->enabled) + goto out; + + err = regulator_disable(vreg->reg); + + if (!err) { + /* ignore errors on applying disable config */ + msm_sata_phy_cfg_vreg(dev, vreg, false); + vreg->enabled = false; + } else { + dev_err(dev, "%s: %s disable failed, err=%d\n", + __func__, vreg->name, err); + } +out: + return err; +} + +static int msm_sata_phy_init_vreg(struct device *dev, + struct msm_sata_phy_vreg *vreg, const char *name) +{ + int err = 0; + char prop_name[MAX_PROP_NAME]; + + vreg->name = kstrdup(name, GFP_KERNEL); + if (!vreg->name) { + err = -ENOMEM; + goto out; + } + + vreg->reg = devm_regulator_get(dev, name); + if (IS_ERR(vreg->reg)) { + err = PTR_ERR(vreg->reg); + dev_err(dev, "failed to get %s, %d\n", name, err); + goto out; + } + + if (dev->of_node) { + snprintf(prop_name, MAX_PROP_NAME, "%s-max-microamp", name); + err = of_property_read_u32(dev->of_node, + prop_name, &vreg->max_uA); + if (err && err != -EINVAL) { + dev_err(dev, "%s: failed to read %s\n", + __func__, prop_name); + goto out; + } else if (err == -EINVAL || !vreg->max_uA) { + if (regulator_count_voltages(vreg->reg) > 0) { + dev_err(dev, "%s: %s is mandatory\n", + __func__, prop_name); + goto out; + } + err = 0; + } + } + + if (!strcmp(name, "vdda-pll")) { + vreg->max_uV = VDDA_PLL_MAX_UV; + vreg->min_uV = VDDA_PLL_MIN_UV; + } else if (!strcmp(name, "vdda-phy")) { + vreg->max_uV = VDDA_PHY_MAX_UV; + vreg->min_uV = VDDA_PHY_MIN_UV; + } + +out: + if (err) + kfree(vreg->name); + return err; +} + +static int msm_sata_phy_clk_get(struct device *dev, + const char *name, struct clk **clk_out) +{ + struct clk *clk; + int err = 0; + + clk = devm_clk_get(dev, name); + if (IS_ERR(clk)) { + err = PTR_ERR(clk); + dev_err(dev, "failed to get %s err %d", name, err); + } else { + *clk_out = clk; + } + + return err; +} + +static int msm_sata_phy_power_up(struct msm_sata_phy *phy) +{ + int err = 0; + u32 reg; + struct device *dev = phy->dev; + + if (phy->phy_sel) { + /* Select SATA PHY */ + writel_relaxed(0x0, phy->phy_sel); + + /* + * SATA PHY must be selected before configuring the PHY. + * The phy_sel and phy_mmio may be in different register space + * and *_relaxed version doesn't ensure ordering in such case. + */ + mb(); + } + + /* SATA PHY powerup sequence */ + + /* PWM configurations */ + writel_relaxed(0x08, phy->mmio + QSERDES_RX_PWM_CNTRL1); + writel_relaxed(0x40, phy->mmio + QSERDES_RX_PWM_CNTRL2); + + /* Configure PHY power control to operate in mission mode */ + writel_relaxed(0x01, phy->mmio + SATA_PHY_POW_DWN_CTRL0); + + /* CDR counter selected between 30-40 */ + writel_relaxed(0x25, phy->mmio + SATA_PHY_CDR_CTRL0); + + /* Wakeup counter values are set to Maximum */ + writel_relaxed(0x0f, phy->mmio + SATA_PHY_CLK_BUF_SETTLING); + writel_relaxed(0xff, phy->mmio + SATA_PHY_TX_DRV_WAKEUP); + writel_relaxed(0xff, phy->mmio + SATA_PHY_SPDNEG_CFG0); + writel_relaxed(0x25, phy->mmio + SATA_PHY_CDR_CTRL0); + + /* PLL register settings */ + writel_relaxed(0xec, phy->mmio + QSERDES_COM_PLL_CRCTRL); + writel_relaxed(0x01, phy->mmio + QSERDES_COM_PLL_IP_SETI); + writel_relaxed(0x3f, phy->mmio + QSERDES_COM_PLL_CP_SETI); + writel_relaxed(0x0f, phy->mmio + QSERDES_COM_PLL_IP_SETP); + writel_relaxed(0x13, phy->mmio + QSERDES_COM_PLL_CP_SETP); + + /* PCS settings for COMMON, TX, RX paths */ + writel_relaxed(0x5b, phy->mmio + SATA_PHY_CMN_PWR_CTRL); + writel_relaxed(0x32, phy->mmio + SATA_PHY_TX_PWR_CTRL); + writel_relaxed(0x83, phy->mmio + SATA_PHY_RX_PWR_CTRL); + writel_relaxed(0x7b, phy->mmio + SATA_PHY_CMN_PWR_CTRL); + + /* Ref clk frequency select - 19.2Mhz selected */ + writel_relaxed(0x08, phy->mmio + QSERDES_COM_SYSCLK_EN_SEL); + writel_relaxed(0x06, phy->mmio + QSERDES_COM_SYS_CLK_CTRL); + + /* Decimal and Fractional dividers configuration */ + writel_relaxed(0x9c, phy->mmio + QSERDES_COM_DEC_START1); + writel_relaxed(0x03, phy->mmio + QSERDES_COM_DEC_START2); + writel_relaxed(0xff, phy->mmio + QSERDES_COM_DIV_FRAC_START1); + writel_relaxed(0xff, phy->mmio + QSERDES_COM_DIV_FRAC_START2); + writel_relaxed(0x13, phy->mmio + QSERDES_COM_DIV_FRAC_START3); + + /* PLL configurations */ + writel_relaxed(0xff, phy->mmio + QSERDES_COM_PLLLOCK_CMP1); + writel_relaxed(0x7c, phy->mmio + QSERDES_COM_PLLLOCK_CMP2); + writel_relaxed(0x00, phy->mmio + QSERDES_COM_PLLLOCK_CMP3); + writel_relaxed(0x01, phy->mmio + QSERDES_COM_PLLLOCK_CMP_EN); + + /* Other Resetsm configurations - FAST_VCO_TUNE */ + writel_relaxed(0x10, phy->mmio + QSERDES_COM_RESETSM_CNTRL); + + /* PI configurations- First Order threshold and Second order gain */ + writel_relaxed(0xeb, phy->mmio + QSERDES_RX_CDR_CONTROL); + writel_relaxed(0x1a, phy->mmio + QSERDES_RX_CDR_CONTROL_QUARTER); + + /* TX configurations */ + writel_relaxed(0x1f, phy->mmio + QSERDES_TX_TX_DRV_LVL); + writel_relaxed(0x00, phy->mmio + QSERDES_TX_BIST_MODE_LANENO); + writel_relaxed(0x30, phy->mmio + QSERDES_TX_TX_EMP_POST1_LVL); + + /* SSC Configurations and Serdes start */ + writel_relaxed(0x00, phy->mmio + QSERDES_COM_SSC_EN_CENTER); + writel_relaxed(0x31, phy->mmio + QSERDES_COM_SSC_PER1); + writel_relaxed(0x01, phy->mmio + QSERDES_COM_SSC_PER2); + writel_relaxed(0x01, phy->mmio + QSERDES_COM_SSC_ADJ_PER1); + writel_relaxed(0x00, phy->mmio + QSERDES_COM_SSC_ADJ_PER2); + writel_relaxed(0x3f, phy->mmio + QSERDES_COM_SSC_STEP_SIZE1); + writel_relaxed(0x05, phy->mmio + QSERDES_COM_SSC_STEP_SIZE2); + writel_relaxed(0x01, phy->mmio + QSERDES_COM_SSC_EN_CENTER); + + /* + * Flush all delayed writes before sleeping to ensure that PHY + * configuration is applied. + */ + mb(); + + /* Sleep for 1ms before starting serdes */ + usleep(1000); + + /* Start serdes */ + writel_relaxed(0x01, phy->mmio + SATA_PHY_SERDES_START); + + /* + * Read RESETSM status until SERDES is ready, + * timeout after 1 sec + */ + err = readl_poll_timeout(phy->mmio + QSERDES_COM_RESET_SM, reg, + (reg & (1 << 5)), 100, 1000000); + if (err) { + dev_err(dev, "%s: poll timeout QSERDES_COM_RESET_SM, status: 0x%x\n", + __func__, readl_relaxed(phy->mmio + + QSERDES_COM_RESET_SM)); + goto out; + } + + /* RX configurations */ + writel_relaxed(0x5f, phy->mmio + SATA_PHY_LANE_CTRL1); + writel_relaxed(0x43, phy->mmio + SATA_PHY_ALIGNP); + + dev_dbg(dev, "SATA PHY powered up in functional mode\n"); +out: + /* power down PHY in case of failure */ + if (err) + writel_relaxed(0x0, phy->mmio + SATA_PHY_POW_DWN_CTRL0); + + return err; +} + +static int msm_sata_phy_power_off(struct phy *generic_phy) +{ + struct msm_sata_phy *phy = phy_get_drvdata(generic_phy); + + writel_relaxed(0x0, phy->mmio + SATA_PHY_POW_DWN_CTRL0); + /* + * Ensure that the PHY is power down before gating power. + * It is possible that PHY regulators might be turned on if + * other PHY's share the same regulators. + */ + mb(); + + msm_sata_disable_phy_rxoob_clk(phy); + msm_sata_disable_phy_ref_clk(phy); + + msm_sata_phy_disable_vreg(phy, &phy->vdda_pll); + msm_sata_phy_disable_vreg(phy, &phy->vdda_phy); + phy->is_powered_on = false; + + return 0; +} + +static int msm_sata_phy_power_on(struct phy *generic_phy) +{ + int err; + struct msm_sata_phy *phy = phy_get_drvdata(generic_phy); + + err = msm_sata_phy_enable_vreg(phy, &phy->vdda_phy); + if (err) + goto out; + + /* vdda_pll also enables ref clock LDOs so enable it first */ + err = msm_sata_phy_enable_vreg(phy, &phy->vdda_pll); + if (err) + goto out_disable_phy; + + err = msm_sata_enable_phy_ref_clk(phy); + if (err) + goto out_disable_pll; + + err = msm_sata_enable_phy_rxoob_clk(phy); + if (err) + goto out_disable_ref; + + err = msm_sata_phy_power_up(phy); + if (err) + goto out_disable_rxoob; + + phy->is_powered_on = true; + goto out; + +out_disable_rxoob: + msm_sata_disable_phy_rxoob_clk(phy); +out_disable_ref: + msm_sata_disable_phy_ref_clk(phy); +out_disable_pll: + msm_sata_phy_disable_vreg(phy, &phy->vdda_pll); +out_disable_phy: + msm_sata_phy_disable_vreg(phy, &phy->vdda_phy); +out: + return err; +} + +static int msm_sata_phy_init(struct phy *generic_phy) +{ + int err; + struct msm_sata_phy *phy = phy_get_drvdata(generic_phy); + struct device *dev = phy->dev; + + err = msm_sata_phy_clk_get(dev, "ref_clk_src", &phy->ref_clk_src); + if (err) + goto out; + + err = msm_sata_phy_clk_get(dev, "ref_clk_parent", &phy->ref_clk_parent); + if (err) + goto out; + + err = msm_sata_phy_clk_get(dev, "ref_clk", &phy->ref_clk); + if (err) + goto out; + + err = msm_sata_phy_clk_get(dev, "rxoob_clk", &phy->rxoob_clk); + if (err) + goto out; + + err = msm_sata_phy_init_vreg(dev, &phy->vdda_pll, "vdda-pll"); + if (err) + goto out; + + err = msm_sata_phy_init_vreg(dev, &phy->vdda_phy, "vdda-phy"); + if (err) + goto out; +out: + return err; +} + +static int msm_sata_phy_exit(struct phy *generic_phy) +{ + struct msm_sata_phy *phy = phy_get_drvdata(generic_phy); + + if (phy->is_powered_on) + msm_sata_phy_power_off(generic_phy); + + return 0; +} + +static struct phy_ops msm_sata_phy_ops = { + .init = msm_sata_phy_init, + .exit = msm_sata_phy_exit, + .power_on = msm_sata_phy_power_on, + .power_off = msm_sata_phy_power_off, + .owner = THIS_MODULE, +}; + +static int msm_sata_phy_probe(struct platform_device *pdev) +{ + int err = 0; + struct msm_sata_phy *phy; + struct device *dev = &pdev->dev; + struct resource *res; + struct phy_provider *phy_provider; + struct phy *generic_phy; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) { + err = -ENOMEM; + dev_err(dev, "%s: failed to allocate phy\n", __func__); + goto out; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_sel"); + phy->phy_sel = devm_ioremap_resource(dev, res); + if (IS_ERR(phy->phy_sel)) { + err = PTR_ERR(phy->phy_sel); + /* phy_sel resource is optional */ + dev_dbg(dev, "%s: phy select resource get failed %d\n", + __func__, err); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem"); + phy->mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(phy->mmio)) { + err = PTR_ERR(phy->mmio); + dev_err(dev, "%s: phy mmio get resource failed %d\n", + __func__, err); + goto out; + } + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + err = PTR_ERR(phy_provider); + dev_err(dev, "%s: failed to register phy %d\n", __func__, err); + goto out; + } + + generic_phy = devm_phy_create(dev, &msm_sata_phy_ops, NULL); + if (IS_ERR(generic_phy)) { + err = PTR_ERR(generic_phy); + dev_err(dev, "%s: failed to create phy %d\n", __func__, err); + goto out; + } + + phy->dev = dev; + phy_set_drvdata(generic_phy, phy); + + return 0; +out: + return err; +} + +static const struct of_device_id msm_sata_phy_of_match[] = { + { .compatible = "qcom,sataphy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, msm_sata_phy_of_match); + +static struct platform_driver msm_sata_phy_driver = { + .probe = msm_sata_phy_probe, + .driver = { + .name = "msm-sata-phy", + .owner = THIS_MODULE, + .of_match_table = msm_sata_phy_of_match, + } +}; +module_platform_driver(msm_sata_phy_driver); + +MODULE_DESCRIPTION("MSM 6Gbps SATA PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig index 604526e65c08..8ed898c4b3a9 100644 --- a/drivers/platform/msm/Kconfig +++ b/drivers/platform/msm/Kconfig @@ -48,7 +48,7 @@ config SPS_SUPPORT_NDP_BAM config QPNP_POWER_ON tristate "QPNP PMIC POWER-ON Driver" - depends on OF_SPMI && SPMI && MSM_QPNP_INT + depends on OF_SPMI && SPMI && MSM_QPNP_INT && INPUT help This driver supports the power-on functionality on Qualcomm PNP PMIC. It currently supports reporting the change in status of diff --git a/drivers/platform/msm/avtimer.c b/drivers/platform/msm/avtimer.c index 00b8c238e941..354ddff601b5 100644 --- a/drivers/platform/msm/avtimer.c +++ b/drivers/platform/msm/avtimer.c @@ -24,7 +24,7 @@ #include <linux/of.h> #include <linux/wait.h> #include <linux/sched.h> -#include <mach/qdsp6v2/apr.h> +#include <linux/qdsp6v2/apr.h> #define DEVICE_NAME "avtimer" #define TIMEOUT_MS 1000 diff --git a/drivers/platform/msm/ipa/ipa.c b/drivers/platform/msm/ipa/ipa.c index 37d8004ec82f..ba4a180f0a08 100644 --- a/drivers/platform/msm/ipa/ipa.c +++ b/drivers/platform/msm/ipa/ipa.c @@ -54,7 +54,7 @@ static struct ipa_plat_drv_res ipa_res = {0, }; static struct of_device_id ipa_plat_drv_match[] = { { - .compatible = "qti,ipa", + .compatible = "qcom,ipa", }, { @@ -1247,55 +1247,55 @@ static int ipa_update_connections_info(struct device_node *node, if (!pipe_connection || !node) return -EINVAL; - key = "qti,src-bam-physical-address"; + key = "qcom,src-bam-physical-address"; rc = of_property_read_u32(node, key, &val); if (rc) goto err; pipe_connection->src_phy_addr = val; - key = "qti,ipa-bam-mem-type"; + key = "qcom,ipa-bam-mem-type"; rc = of_property_read_u32(node, key, &mem_type); if (rc) goto err; pipe_connection->mem_type = mem_type; - key = "qti,src-bam-pipe-index"; + key = "qcom,src-bam-pipe-index"; rc = of_property_read_u32(node, key, &val); if (rc) goto err; pipe_connection->src_pipe_index = val; - key = "qti,dst-bam-physical-address"; + key = "qcom,dst-bam-physical-address"; rc = of_property_read_u32(node, key, &val); if (rc) goto err; pipe_connection->dst_phy_addr = val; - key = "qti,dst-bam-pipe-index"; + key = "qcom,dst-bam-pipe-index"; rc = of_property_read_u32(node, key, &val); if (rc) goto err; pipe_connection->dst_pipe_index = val; - key = "qti,data-fifo-offset"; + key = "qcom,data-fifo-offset"; rc = of_property_read_u32(node, key, &val); if (rc) goto err; pipe_connection->data_fifo_base_offset = val; - key = "qti,data-fifo-size"; + key = "qcom,data-fifo-size"; rc = of_property_read_u32(node, key, &val); if (rc) goto err; pipe_connection->data_fifo_size = val; - key = "qti,descriptor-fifo-offset"; + key = "qcom,descriptor-fifo-offset"; rc = of_property_read_u32(node, key, &val); if (rc) goto err; pipe_connection->desc_fifo_base_offset = val; - key = "qti,descriptor-fifo-size"; + key = "qcom,descriptor-fifo-size"; rc = of_property_read_u32(node, key, &val); if (rc) goto err; @@ -2211,7 +2211,7 @@ static int get_ipa_dts_configuration(struct platform_device *pdev, ipa_drv_res->ipa_hw_mode = 0; /* Get IPA HW Version */ - result = of_property_read_u32(pdev->dev.of_node, "qti,ipa-hw-ver", + result = of_property_read_u32(pdev->dev.of_node, "qcom,ipa-hw-ver", &ipa_drv_res->ipa_hw_type); if ((result) || (ipa_drv_res->ipa_hw_type == 0)) { IPAERR(":get resource failed for ipa-hw-ver!\n"); @@ -2220,7 +2220,7 @@ static int get_ipa_dts_configuration(struct platform_device *pdev, IPADBG(": ipa_hw_type = %d", ipa_drv_res->ipa_hw_type); /* Get IPA HW mode */ - result = of_property_read_u32(pdev->dev.of_node, "qti,ipa-hw-mode", + result = of_property_read_u32(pdev->dev.of_node, "qcom,ipa-hw-mode", &ipa_drv_res->ipa_hw_mode); if (result) IPADBG("using default (IPA_MODE_NORMAL) for ipa-hw-mode\n"); @@ -2230,20 +2230,20 @@ static int get_ipa_dts_configuration(struct platform_device *pdev, ipa_drv_res->use_ipa_bamdma_a2_bridge = of_property_read_bool(pdev->dev.of_node, - "qti,use-ipa-bamdma-a2-bridge"); + "qcom,use-ipa-bamdma-a2-bridge"); IPADBG(": using A2-BAMDMA bridge = %s", ipa_drv_res->use_ipa_bamdma_a2_bridge ? "True" : "False"); ipa_drv_res->use_a2_service = of_property_read_bool(pdev->dev.of_node, - "qti,use-a2-service"); + "qcom,use-a2-service"); IPADBG(": using A2 service = %s", ipa_drv_res->use_a2_service ? "True" : "False"); ipa_drv_res->use_ipa_teth_bridge = of_property_read_bool(pdev->dev.of_node, - "qti,use-ipa-tethering-bridge"); + "qcom,use-ipa-tethering-bridge"); IPADBG(": using TBDr = %s", ipa_drv_res->use_ipa_teth_bridge ? "True" : "False"); @@ -2317,7 +2317,7 @@ static int get_ipa_dts_configuration(struct platform_device *pdev, return -ENODEV; } - result = of_property_read_u32(pdev->dev.of_node, "qti,ee", + result = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ipa_drv_res->ee); if (result) ipa_drv_res->ee = 0; diff --git a/drivers/platform/msm/ipa/ipa_bridge.c b/drivers/platform/msm/ipa/ipa_bridge.c index ecf58e7c72be..3cb889cab1cd 100644 --- a/drivers/platform/msm/ipa/ipa_bridge.c +++ b/drivers/platform/msm/ipa/ipa_bridge.c @@ -12,7 +12,7 @@ #include <linux/delay.h> #include <linux/ratelimit.h> -#include <mach/msm_smem.h> +#include <soc/qcom/smem.h> #include "ipa_i.h" /* diff --git a/drivers/platform/msm/ipa/ipa_dp.c b/drivers/platform/msm/ipa/ipa_dp.c index 11bc02379df0..9e4d8d064602 100644 --- a/drivers/platform/msm/ipa/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_dp.c @@ -53,8 +53,10 @@ static void ipa_wq_write_done_common(struct ipa_sys_context *sys, u32 cnt) int i; for (i = 0; i < cnt; i++) { - if (unlikely(list_empty(&sys->head_desc_list))) + if (unlikely(list_empty(&sys->head_desc_list))) { WARN_ON(1); + return; + } tx_pkt_expected = list_first_entry(&sys->head_desc_list, struct ipa_tx_pkt_wrapper, link); @@ -87,14 +89,17 @@ static void ipa_wq_write_done_status(int src_pipe) return; } - spin_lock(&sys->spinlock); - if (unlikely(list_empty(&sys->head_desc_list))) + spin_lock_bh(&sys->spinlock); + if (unlikely(list_empty(&sys->head_desc_list))) { WARN_ON(1); + spin_unlock_bh(&sys->spinlock); + return; + } tx_pkt_expected = list_first_entry(&sys->head_desc_list, struct ipa_tx_pkt_wrapper, link); ipa_wq_write_done_common(sys, tx_pkt_expected->cnt); - spin_unlock(&sys->spinlock); + spin_unlock_bh(&sys->spinlock); } /** @@ -120,9 +125,9 @@ static void ipa_wq_write_done(struct work_struct *work) cnt = tx_pkt->cnt; sys = tx_pkt->sys; - spin_lock(&sys->spinlock); + spin_lock_bh(&sys->spinlock); ipa_wq_write_done_common(sys, cnt); - spin_unlock(&sys->spinlock); + spin_unlock_bh(&sys->spinlock); } static int ipa_handle_tx_core(struct ipa_sys_context *sys, bool process_all, @@ -145,9 +150,9 @@ static int ipa_handle_tx_core(struct ipa_sys_context *sys, bool process_all, if (iov.addr == 0) break; - spin_lock(&sys->spinlock); + spin_lock_bh(&sys->spinlock); ipa_wq_write_done_common(sys, 1); - spin_unlock(&sys->spinlock); + spin_unlock_bh(&sys->spinlock); cnt++; }; @@ -314,7 +319,7 @@ int ipa_send_one(struct ipa_sys_context *sys, struct ipa_desc *desc, INIT_WORK(&tx_pkt->work, ipa_wq_write_done); - spin_lock(&sys->spinlock); + spin_lock_bh(&sys->spinlock); list_add_tail(&tx_pkt->link, &sys->head_desc_list); result = sps_transfer_one(sys->ep->ep_hdl, dma_address, len, tx_pkt, sps_flags); @@ -323,13 +328,13 @@ int ipa_send_one(struct ipa_sys_context *sys, struct ipa_desc *desc, goto fail_sps_send; } - spin_unlock(&sys->spinlock); + spin_unlock_bh(&sys->spinlock); return 0; fail_sps_send: list_del(&tx_pkt->link); - spin_unlock(&sys->spinlock); + spin_unlock_bh(&sys->spinlock); if (unlikely(ipa_ctx->ipa_hw_type == IPA_HW_v1_0)) dma_pool_free(ipa_ctx->dma_pool, tx_pkt->bounce, dma_address); @@ -384,7 +389,7 @@ int ipa_send(struct ipa_sys_context *sys, u32 num_desc, struct ipa_desc *desc, transfer.iovec = dma_pool_alloc(ipa_ctx->dma_pool, mem_flag, &dma_addr); transfer.iovec_phys = dma_addr; transfer.iovec_count = num_desc; - spin_lock(&sys->spinlock); + spin_lock_bh(&sys->spinlock); if (!transfer.iovec) { IPAERR("fail to alloc DMA mem for sps xfr buff\n"); goto failure_coherent; @@ -491,7 +496,7 @@ int ipa_send(struct ipa_sys_context *sys, u32 num_desc, struct ipa_desc *desc, goto failure; } - spin_unlock(&sys->spinlock); + spin_unlock_bh(&sys->spinlock); return 0; failure: @@ -518,7 +523,7 @@ failure: dma_pool_free(ipa_ctx->dma_pool, transfer.iovec, transfer.iovec_phys); failure_coherent: - spin_unlock(&sys->spinlock); + spin_unlock_bh(&sys->spinlock); return -EFAULT; } @@ -1913,8 +1918,10 @@ static void ipa_wq_rx_common(struct ipa_sys_context *sys, u32 size) struct ipa_rx_pkt_wrapper *rx_pkt_expected; struct sk_buff *rx_skb; - if (unlikely(list_empty(&sys->head_desc_list))) + if (unlikely(list_empty(&sys->head_desc_list))) { WARN_ON(1); + return; + } rx_pkt_expected = list_first_entry(&sys->head_desc_list, struct ipa_rx_pkt_wrapper, link); @@ -1930,9 +1937,8 @@ static void ipa_wq_rx_common(struct ipa_sys_context *sys, u32 size) rx_skb->truesize = rx_pkt_expected->len + sizeof(struct sk_buff); sys->pyld_hdlr(rx_skb, sys); - ipa_replenish_rx_cache(sys); - kmem_cache_free(ipa_ctx->rx_pkt_wrapper_cache, rx_pkt_expected); - + ipa_replenish_rx_cache(sys); + kmem_cache_free(ipa_ctx->rx_pkt_wrapper_cache, rx_pkt_expected); } static void ipa_wq_rx_avail(struct work_struct *work) diff --git a/drivers/platform/msm/ipa/ipa_nat.c b/drivers/platform/msm/ipa/ipa_nat.c index f9ae85f7860c..98c2a0ffe87a 100644 --- a/drivers/platform/msm/ipa/ipa_nat.c +++ b/drivers/platform/msm/ipa/ipa_nat.c @@ -79,6 +79,12 @@ static int ipa_nat_mmap(struct file *filp, struct vm_area_struct *vma) } else { IPADBG("Mapping shared(local) memory\n"); IPADBG("map sz=0x%lx\n", vsize); + + if ((IPA_NAT_PHYS_MEM_SIZE == 0) || + (vsize > IPA_NAT_PHYS_MEM_SIZE)) { + result = -EINVAL; + goto bail; + } phys_addr = ipa_ctx->ipa_wrapper_base + IPA_REG_BASE_OFST + IPA_SRAM_DIRECT_ACCESS_N_OFST(IPA_NAT_PHYS_MEM_OFFSET); @@ -225,6 +231,7 @@ int ipa_nat_init_cmd(struct ipa_ioc_v4_nat_init *init) struct ipa_ip_v4_nat_init *cmd; u16 size = sizeof(struct ipa_ip_v4_nat_init); int result; + u32 offset = 0; IPADBG("\n"); if (init->tbl_index < 0 || init->table_entries <= 0) { @@ -245,6 +252,26 @@ int ipa_nat_init_cmd(struct ipa_ioc_v4_nat_init *init) cmd->index_table_addr_type = IPA_NAT_SYSTEM_MEMORY; cmd->index_table_expansion_addr_type = IPA_NAT_SYSTEM_MEMORY; + offset = UINT_MAX - ipa_ctx->nat_mem.dma_handle; + + if ((init->ipv4_rules_offset > offset) || + (init->expn_rules_offset > offset) || + (init->index_offset > offset) || + (init->index_expn_offset > offset)) { + IPAERR("Failed due to integer overflow\n"); + IPAERR("nat.mem.dma_handle: 0x%x\n", + ipa_ctx->nat_mem.dma_handle); + IPAERR("ipv4_rules_offset: 0x%x\n", + init->ipv4_rules_offset); + IPAERR("expn_rules_offset: 0x%x\n", + init->expn_rules_offset); + IPAERR("index_offset: 0x%x\n", + init->index_offset); + IPAERR("index_expn_offset: 0x%x\n", + init->index_expn_offset); + result = -EPERM; + goto free_cmd; + } cmd->ipv4_rules_addr = ipa_ctx->nat_mem.dma_handle + init->ipv4_rules_offset; IPADBG("ipv4_rules_offset:0x%x\n", init->ipv4_rules_offset); diff --git a/drivers/platform/msm/ipa/rmnet_ipa.c b/drivers/platform/msm/ipa/rmnet_ipa.c index d6321c115f9d..a763c8667d6b 100644 --- a/drivers/platform/msm/ipa/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/rmnet_ipa.c @@ -387,6 +387,7 @@ static int wwan_add_ul_flt_rule_to_ipa(void) { u32 pyld_sz; int i, retval = 0; + int num_v4_rule = 0, num_v6_rule = 0; struct ipa_ioc_add_flt_rule *param; struct ipa_flt_rule_add flt_rule_entry; struct ipa_fltr_installed_notif_req_msg_v01 req; @@ -436,9 +437,15 @@ static int wwan_add_ul_flt_rule_to_ipa(void) req.install_status = QMI_RESULT_SUCCESS_V01; req.filter_index_list_len = num_q6_rule; for (i = 0; i < num_q6_rule; i++) { + if (q6_ul_filter_rule[i].ip == IPA_IP_v4) { + req.filter_index_list[i].filter_index = num_v4_rule; + num_v4_rule++; + } else { + req.filter_index_list[i].filter_index = num_v6_rule; + num_v6_rule++; + } req.filter_index_list[i].filter_handle = q6_ul_filter_rule[i].filter_hdl; - req.filter_index_list[i].filter_index = i; } if (qmi_filter_notify_send(&req)) { IPAWANDBG("add filter rule index on A7-RX failed\n"); diff --git a/drivers/platform/msm/qca1530.c b/drivers/platform/msm/qca1530.c index 593d3d8fc85a..60e4c9a3eee3 100644 --- a/drivers/platform/msm/qca1530.c +++ b/drivers/platform/msm/qca1530.c @@ -42,6 +42,10 @@ #define QCA1530_OF_CLK_GPIO_NAME "qca,clk-gpio" /* xLNA power regulator prefix for DTS */ #define QCA1530_OF_XLNA_REG_NAME "qca,xlna" +/* xLNA voltage property name in DTS */ +#define QCA1530_OF_XLNA_POWER_VOLTAGE "qca,xlna-voltage-level" +/* xLNA current property name in DTS */ +#define QCA1530_OF_XLNA_POWER_CURRENT "qca,xlna-current-level" /* xLNA pin name for DTS */ #define QCA1530_OF_XLNA_GPIO_NAME "qca,xlna-gpio" @@ -51,7 +55,6 @@ * @reset_gpio: Number of GPIO pin for reset control, or -1 * @reset_reg: Handle to power regulator for reset control, or NULL * @rtc_clk: RTC clock handle (32KHz) - * @rtc_clk_state: RTC clock state * @rtc_clk_gpio: RTC clock gppio, or -1 * @tcxo_clk: TCXO clock handle * @pwr_reg: Main SoC power regulator handle, or NULL @@ -66,7 +69,6 @@ struct qca1530_static { int reset_gpio; struct regulator *reset_reg; struct clk *rtc_clk; - int rtc_clk_state; int rtc_clk_gpio; struct clk *tcxo_clk; struct regulator *pwr_reg; @@ -80,7 +82,6 @@ struct qca1530_static { */ static struct qca1530_static qca1530_data = { .reset_gpio = -1, - .rtc_clk_state = -1, .rtc_clk_gpio = -1, .pwr_gpio = -1, .xlna_gpio = -1, @@ -88,6 +89,7 @@ static struct qca1530_static qca1530_data = { /** * qca1530_deinit_gpio() - release GPIO resource + * @pdev: platform device data * @pgpio: pointer to GPIO handle * * Function releases GPIO handle allocated by the driver. By default the @@ -96,16 +98,15 @@ static struct qca1530_static qca1530_data = { * * GPIO handle is set to -1. */ -static void qca1530_deinit_gpio(int *pgpio) +static void qca1530_deinit_gpio(struct platform_device *pdev, int *pgpio) { int gpio = *pgpio; if (gpio >= 0) { pr_debug("Releasing GPIO: %d", gpio); - gpio_unexport(gpio); gpio_direction_input(gpio); - gpio_free(gpio); + devm_gpio_free(&pdev->dev, gpio); *pgpio = -1; } } @@ -119,21 +120,21 @@ static void qca1530_deinit_gpio(int *pgpio) static void qca1530_deinit_regulator(struct regulator **ppwr) { if (*ppwr) { - regulator_put(*ppwr); + devm_regulator_put(*ppwr); *ppwr = NULL; } } /** - * qca1530_clk_set_rtc() - enable or disable clock + * qca1530_clk_prepare() - prepare or unprepare clock * @clk: clock handle - * @mode: 0 to disable, 1 to enable + * @mode: 0 to unprepare, 1 to prepare * - * Function turns clock on or off and logs the result. + * Function prepares or unprepares clock and logs the result. * * Return: 0 on success, error code on failure */ -static int qca1530_clk_set_rtc(struct clk *clk, int mode) +static int qca1530_clk_prepare(struct clk *clk, int mode) { int ret = 0; @@ -161,12 +162,12 @@ static void qca1530_clk_set_gpio(int mode) } /** - * qca1530_clk_set() - start/stop RTC clocks + * qca1530_clk_set() - start/stop initialized clocks * @mode: 1 to start clocks, 0 to stop * * Function turns clocks on or off. Clock configuration is optional, however - * when configured, the following order is used: GPIO pin, RTC clock, TCXO - * clock. When switching off, the order is reveresed. + * when configured, the following order is used: RTC GPIO pin, RTC clock, + * TCXO clock. When switching off, the order is reveresed. * * Return: 0 on success, error code on failure. */ @@ -181,18 +182,16 @@ static int qca1530_clk_set(int mode) if (qca1530_data.rtc_clk_gpio >= 0) qca1530_clk_set_gpio(1); if (qca1530_data.rtc_clk) - ret = qca1530_clk_set_rtc(qca1530_data.rtc_clk, 1); + ret = qca1530_clk_prepare(qca1530_data.rtc_clk, 1); if (!ret && qca1530_data.tcxo_clk) - ret = qca1530_clk_set_rtc(qca1530_data.tcxo_clk, 1); - qca1530_data.rtc_clk_state = ret ? 0 : 1; + ret = qca1530_clk_prepare(qca1530_data.tcxo_clk, 1); } else { if (qca1530_data.tcxo_clk) - ret = qca1530_clk_set_rtc(qca1530_data.tcxo_clk, 0); + ret = qca1530_clk_prepare(qca1530_data.tcxo_clk, 0); if (!ret && qca1530_data.rtc_clk) - ret = qca1530_clk_set_rtc(qca1530_data.rtc_clk, 0); + ret = qca1530_clk_prepare(qca1530_data.rtc_clk, 0); if (!ret && qca1530_data.rtc_clk_gpio >= 0) qca1530_clk_set_gpio(0); - qca1530_data.rtc_clk_state = 0; } pr_debug("Configured clk: mode=%d ret=%d", mode, ret); @@ -201,21 +200,24 @@ static int qca1530_clk_set(int mode) } /** - * qca1530_clk_deinit_rtc() - release clocks + * qca1530_clk_release_clocks() - release clocks * @pdev: platform device data * - * Function sets clock handle references to NULL. + * Function releases initialized clocks and sets clock handle + * references to NULL. */ -static void qca1530_clk_deinit_rtc(struct platform_device *pdev) +static void qca1530_clk_release_clocks(struct platform_device *pdev) { if (qca1530_data.tcxo_clk) { pr_debug("Unregistering CLK: device=%s name=%s", dev_name(&pdev->dev), QCA1530_TCXO_CLK_ID); + devm_clk_put(&pdev->dev, qca1530_data.tcxo_clk); qca1530_data.tcxo_clk = NULL; } if (qca1530_data.rtc_clk) { pr_debug("Unregistering CLK: device=%s name=%s", dev_name(&pdev->dev), QCA1530_RTC_CLK_ID); + devm_clk_put(&pdev->dev, qca1530_data.rtc_clk); qca1530_data.rtc_clk = NULL; } } @@ -306,12 +308,10 @@ static int qca1530_clk_init(struct platform_device *pdev) return 0; err_2: - qca1530_deinit_gpio(&qca1530_data.rtc_clk_gpio); + qca1530_deinit_gpio(pdev, &qca1530_data.rtc_clk_gpio); err_1: - qca1530_clk_deinit_rtc(pdev); + qca1530_clk_release_clocks(pdev); err_0: - qca1530_data.rtc_clk_state = -1; - pr_err("init error: ret=%d", ret); return ret; @@ -326,9 +326,8 @@ err_0: static void qca1530_clk_deinit(struct platform_device *pdev) { qca1530_clk_set(0); - qca1530_deinit_gpio(&qca1530_data.rtc_clk_gpio); - qca1530_clk_deinit_rtc(pdev); - qca1530_data.rtc_clk_state = -1; + qca1530_deinit_gpio(pdev, &qca1530_data.rtc_clk_gpio); + qca1530_clk_release_clocks(pdev); } /** @@ -360,33 +359,26 @@ static int qca1530_pwr_set_regulator(struct regulator *reg, int mode) pr_debug("Setting regulator: mode=%d regulator=%p", mode, reg); if (mode) { - if (regulator_is_enabled(reg)) { - ret = 0; - goto done; - } - - ret = regulator_set_mode(reg, REGULATOR_MODE_NORMAL); + ret = regulator_enable(reg); if (ret) - pr_warn("failed to set regulator mode, ret=%d", ret); + pr_err("Failed to enable regulator, rc=%d", ret); + else + pr_debug("Regulator %p was enabled (%d)", reg, ret); - ret = regulator_enable(reg); - if (ret) { - pr_err("failed to enable regulator, rc=%d", ret); - goto done; - } } else { if (!regulator_is_enabled(reg)) { ret = 0; - goto done; - } - - ret = regulator_disable(reg); - if (ret) { - pr_err("failed to disable regulator, rc=%d", ret); - goto done; + } else { + ret = regulator_disable(reg); + if (ret) + pr_err("Failed to disable regulator, rc=%d", + ret); + else + pr_debug("Regulator %p was disabled (%d)", reg, + ret); } } -done: + pr_debug("Regulator result: regulator=%p mode=%d ret=%d", reg, mode, ret); @@ -403,7 +395,8 @@ static int qca1530_pwr_init_gpio(struct platform_device *pdev) { int ret; - ret = of_get_named_gpio(pdev->dev.of_node, QCA1530_OF_PWR_GPIO_NAME, 0); + ret = of_get_named_gpio(pdev->dev.of_node, + QCA1530_OF_PWR_GPIO_NAME, 0); if (ret == -ENOENT) { qca1530_data.pwr_gpio = ret; ret = 0; @@ -428,7 +421,8 @@ static int qca1530_pwr_init_gpio(struct platform_device *pdev) * Return: 0 on success, error code otherwise */ static int qca1530_pwr_init_regulator( - struct platform_device *pdev, const char *name, struct regulator **ppwr) + struct platform_device *pdev, + const char *name, struct regulator **ppwr) { int ret; struct regulator *pwr; @@ -517,7 +511,7 @@ static int qca1530_pwr_init(struct platform_device *pdev) return ret; err_2: - qca1530_deinit_gpio(&qca1530_data.pwr_gpio); + qca1530_deinit_gpio(pdev, &qca1530_data.pwr_gpio); err_1: qca1530_deinit_regulator(&qca1530_data.pwr_reg); err_0: @@ -526,13 +520,14 @@ err_0: /** * qca1530_pwr_deinit() - release main SoC power control + * @pdev: platform device data * * Function releases power regulator and power GPIO pin. */ -static void qca1530_pwr_deinit(void) +static void qca1530_pwr_deinit(struct platform_device *pdev) { qca1530_pwr_set(0); - qca1530_deinit_gpio(&qca1530_data.pwr_gpio); + qca1530_deinit_gpio(pdev, &qca1530_data.pwr_gpio); qca1530_deinit_regulator(&qca1530_data.pwr_reg); } @@ -597,21 +592,22 @@ static int qca1530_reset_init(struct platform_device *pdev) err_supply_configure: qca1530_deinit_regulator(&qca1530_data.reset_reg); err_gpio_configure: - qca1530_deinit_gpio(&qca1530_data.reset_gpio); + qca1530_deinit_gpio(pdev, &qca1530_data.reset_gpio); err_gpio_get: return ret; } /** * qca1530_reset_deinit() - release reset control resources + * @pdev: platform device data * * Function releases reset line GPIO and switches off and releases power * regulator. */ -static void qca1530_reset_deinit(void) +static void qca1530_reset_deinit(struct platform_device *pdev) { pr_debug("reset control: releasing"); - qca1530_deinit_gpio(&qca1530_data.reset_gpio); + qca1530_deinit_gpio(pdev, &qca1530_data.reset_gpio); if (qca1530_data.reset_reg) { qca1530_pwr_set_regulator(qca1530_data.reset_reg, 0); qca1530_deinit_regulator(&qca1530_data.reset_reg); @@ -661,9 +657,11 @@ static int qca1530_xlna_set(int mode) if (!qca1530_data.xlna_reg && qca1530_data.xlna_gpio < 0) ret = -ENOSYS; else if (mode) { - if (qca1530_data.xlna_reg) + if (qca1530_data.xlna_reg) { ret = qca1530_pwr_set_regulator( qca1530_data.xlna_reg, mode); + } + if (!ret && qca1530_data.xlna_gpio >= 0) gpio_set_value(qca1530_data.xlna_gpio, 1); } else { @@ -677,11 +675,45 @@ static int qca1530_xlna_set(int mode) } /** + * qca1530_read_u32_arr_property() - read u32 values property + * @pdev: platform device data + * @pname: property name + * @num_values: array size + * @values: array to put results to + * + * Function reads property with given name. Filles passed array of + * u32 values. + * + * Return: 0 on successful read, error code on error. + */ +static int qca1530_read_u32_arr_property(struct platform_device *pdev, + const char *pname, const int num_values, u32 *values) +{ + int len = 0; + int ret = 0; + + if (pdev->dev.of_node) { + const void *rp = of_get_property(pdev->dev.of_node, + pname, &len); + if (NULL != rp && len == sizeof(u32)*num_values) + ret = of_property_read_u32_array(pdev->dev.of_node, + pname, values, num_values); + else + ret = -ENODATA; + } else{ + ret = -EINVAL; + } + if (ret) + pr_err("Error reading property %s, ret:%d", pname, ret); + return ret; +} + +/** * qca1530_xlna_init() - allocates xLNA resources * @pdev: platform device data * - * Function allocates xLNA resources according to DTS configuration. When - * configured, the following facilities are used: + * Function allocates xLNA resources according to DTS configuration. + * When configured, the following facilities are used: * - power regulator (optionally) * - GPIO pin (optionally) * @@ -692,6 +724,7 @@ static int qca1530_xlna_set(int mode) static int qca1530_xlna_init(struct platform_device *pdev) { int ret = 0; + u32 tmp[2]; pr_debug("xLNA control: initializing"); @@ -700,6 +733,33 @@ static int qca1530_xlna_init(struct platform_device *pdev) if (ret) goto err_0; + if (qca1530_data.xlna_reg) { + ret = qca1530_read_u32_arr_property(pdev, + QCA1530_OF_XLNA_POWER_VOLTAGE, 2, tmp); + if (!ret) { + ret = regulator_set_voltage(qca1530_data.xlna_reg, + tmp[0], tmp[1]); + if (ret) + pr_warn("Failed to set voltage, ret=%d", ret); + else + pr_debug("Regulator %p voltage was set (%d)", + qca1530_data.xlna_reg, ret); + } + + ret = qca1530_read_u32_arr_property(pdev, + QCA1530_OF_XLNA_POWER_CURRENT, 2, tmp); + if (!ret) { + ret = regulator_set_optimum_mode(qca1530_data.xlna_reg, + tmp[0]); + if (ret < 0) + pr_warn("Failed to set optimum mode, ret=%d", + ret); + else + pr_debug("Optimum mode for %p was set (%d)", + qca1530_data.xlna_reg, ret); + } + } + ret = qca1530_xlna_init_gpio(pdev); if (ret) goto err_1; @@ -719,7 +779,7 @@ static int qca1530_xlna_init(struct platform_device *pdev) return ret; err_2: - qca1530_deinit_gpio(&qca1530_data.xlna_gpio); + qca1530_deinit_gpio(pdev, &qca1530_data.xlna_gpio); err_1: qca1530_deinit_regulator(&qca1530_data.xlna_reg); err_0: @@ -728,13 +788,14 @@ err_0: /** * qca1530_xlna_deinit() - release all xLNA resources + * @pdev: platform device data * * Function switches off xLNA and releases all allocated resources. */ -static void qca1530_xlna_deinit(void) +static void qca1530_xlna_deinit(struct platform_device *pdev) { qca1530_xlna_set(0); - qca1530_deinit_gpio(&qca1530_data.xlna_gpio); + qca1530_deinit_gpio(pdev, &qca1530_data.xlna_gpio); qca1530_deinit_regulator(&qca1530_data.xlna_reg); } @@ -778,18 +839,18 @@ static int qca1530_probe(struct platform_device *pdev) } qca1530_data.pdev = pdev; - pr_debug("Probe OK"); - return ret; err_pwr_init: - qca1530_xlna_deinit(); + qca1530_xlna_deinit(pdev); err_xlna_init: qca1530_clk_deinit(pdev); err_clk_init: - qca1530_reset_deinit(); + qca1530_reset_deinit(pdev); err_reset_init: + + pr_debug("Probe ret=%d", ret); return ret; } @@ -805,10 +866,10 @@ err_reset_init: */ static int qca1530_remove(struct platform_device *pdev) { - qca1530_pwr_deinit(); - qca1530_xlna_deinit(); + qca1530_pwr_deinit(pdev); + qca1530_xlna_deinit(pdev); qca1530_clk_deinit(pdev); - qca1530_reset_deinit(); + qca1530_reset_deinit(pdev); qca1530_data.pdev = NULL; return 0; } diff --git a/drivers/platform/msm/qpnp-power-on.c b/drivers/platform/msm/qpnp-power-on.c index dfb2935c2f51..27efde1bbc62 100644 --- a/drivers/platform/msm/qpnp-power-on.c +++ b/drivers/platform/msm/qpnp-power-on.c @@ -51,6 +51,8 @@ #define QPNP_PON_KPDPWR_RESIN_S2_CNTL2(base) (base + 0x4B) #define QPNP_PON_PS_HOLD_RST_CTL(base) (base + 0x5A) #define QPNP_PON_PS_HOLD_RST_CTL2(base) (base + 0x5B) +#define QPNP_PON_WD_RST_S2_CTL(base) (base + 0x56) +#define QPNP_PON_WD_RST_S2_CTL2(base) (base + 0x57) #define QPNP_PON_TRIGGER_EN(base) (base + 0x80) #define QPNP_PON_S3_DBC_CTL(base) (base + 0x75) @@ -74,6 +76,7 @@ #define QPNP_PON_RESIN_BARK_N_SET BIT(4) #define QPNP_PON_KPDPWR_RESIN_BARK_N_SET BIT(5) +#define QPNP_PON_WD_EN BIT(7) #define QPNP_PON_RESET_EN BIT(7) #define QPNP_PON_POWER_OFF_MASK 0xF @@ -358,6 +361,32 @@ int qpnp_pon_is_warm_reset(void) EXPORT_SYMBOL(qpnp_pon_is_warm_reset); /** + * qpnp_pon_wd_config - Disable the wd in a warm reset. + * @enable: to enable or disable the PON watch dog + * + * Returns = 0 for operate successfully, < 0 for errors + */ +int qpnp_pon_wd_config(bool enable) +{ + struct qpnp_pon *pon = sys_reset_dev; + int rc = 0; + + if (!pon) + return -EPROBE_DEFER; + + rc = qpnp_pon_masked_write(pon, QPNP_PON_WD_RST_S2_CTL2(pon->base), + QPNP_PON_WD_EN, enable ? QPNP_PON_WD_EN : 0); + if (rc) + dev_err(&pon->spmi->dev, + "Unable to write to addr=%x, rc(%d)\n", + QPNP_PON_WD_RST_S2_CTL2(pon->base), rc); + + return rc; +} +EXPORT_SYMBOL(qpnp_pon_wd_config); + + +/** * qpnp_pon_trigger_config - Configures (enable/disable) the PON trigger source * @pon_src: PON source to be configured * @enable: to enable or disable the PON trigger @@ -1204,6 +1233,8 @@ static int qpnp_pon_probe(struct spmi_device *spmi) return rc; } + boot_reason = ffs(pon_sts); + index = ffs(pon_sts) - 1; cold_boot = !qpnp_pon_is_warm_reset(); if (index >= ARRAY_SIZE(qpnp_pon_reason) || index < 0) diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c index ba46146cfe48..ae75c0bff1d0 100644 --- a/drivers/platform/msm/usb_bam.c +++ b/drivers/platform/msm/usb_bam.c @@ -105,37 +105,69 @@ static char *bam_enable_strings[MAX_BAMS] = { [HSIC_BAM] = "hsic", }; -static enum usb_bam ipa_rm_bams[] = {HSUSB_BAM, HSIC_BAM}; +struct ipa_rm_bam { + enum usb_bam bam; + char *str; + bool initialized; +}; + +static struct ipa_rm_bam ipa_rm_bams[] = { + { + .bam = SSUSB_BAM, + .initialized = false + }, + { + .bam = HSUSB_BAM, + .initialized = false + }, + { + .bam = HSIC_BAM, + .initialized = false + } +}; +/* + * HSUSB_BAM & SSUSB_BAM shouldn't be used simultaneously + * since both share the same prod & cons rm resourses + */ static enum ipa_client_type ipa_rm_resource_prod[MAX_BAMS] = { [HSUSB_BAM] = IPA_RM_RESOURCE_USB_PROD, [HSIC_BAM] = IPA_RM_RESOURCE_HSIC_PROD, + [SSUSB_BAM] = IPA_RM_RESOURCE_USB_PROD, }; static enum ipa_client_type ipa_rm_resource_cons[MAX_BAMS] = { [HSUSB_BAM] = IPA_RM_RESOURCE_USB_CONS, [HSIC_BAM] = IPA_RM_RESOURCE_HSIC_CONS, + [SSUSB_BAM] = IPA_RM_RESOURCE_USB_CONS, }; static int usb_cons_request_resource(void); static int usb_cons_release_resource(void); +static int ss_usb_cons_request_resource(void); +static int ss_usb_cons_release_resource(void); static int hsic_cons_request_resource(void); static int hsic_cons_release_resource(void); + static int (*request_resource_cb[MAX_BAMS])(void) = { [HSUSB_BAM] = usb_cons_request_resource, [HSIC_BAM] = hsic_cons_request_resource, + [SSUSB_BAM] = ss_usb_cons_request_resource, }; static int (*release_resource_cb[MAX_BAMS])(void) = { [HSUSB_BAM] = usb_cons_release_resource, [HSIC_BAM] = hsic_cons_release_resource, + [SSUSB_BAM] = ss_usb_cons_release_resource, }; struct usb_bam_ipa_handshake_info { enum ipa_rm_event cur_prod_state; enum ipa_rm_event cur_cons_state; + enum usb_bam_mode cur_bam_mode; + enum usb_bam bam_type; bool lpm_wait_handshake; int connect_complete; bool lpm_wait_pipes; @@ -166,7 +198,7 @@ struct usb_bam_ipa_handshake_info { struct work_struct finish_suspend_work; }; -struct usb_bam_hsic_host_info { +struct usb_bam_host_info { struct device *dev; bool in_lpm; }; @@ -178,7 +210,8 @@ static struct usb_bam_peer_handshake_info peer_handshake_info; static spinlock_t usb_bam_lock; /* Protect ctx and usb_bam_connections */ static struct usb_bam_pipe_connect *usb_bam_connections; static struct usb_bam_ctx_type ctx; -static struct usb_bam_hsic_host_info hsic_host_info; +static struct usb_bam_host_info host_info[MAX_BAMS]; +static struct device *usb_device; static int __usb_bam_register_wake_cb(int idx, int (*callback)(void *user), void *param, bool trigger_cb_per_pipe); @@ -192,16 +225,28 @@ void msm_bam_set_hsic_host_dev(struct device *dev) pr_debug("%s: Getting hsic device %x\n", __func__, (int)dev); pm_runtime_get(dev); - } else if (hsic_host_info.dev) { + } else if (host_info[HSIC_BAM].dev) { pr_debug("%s: Putting hsic device %x\n", __func__, - (int)hsic_host_info.dev); + (int)host_info[HSIC_BAM].dev); /* Just free previous device*/ info[HSIC_BAM].in_lpm = true; - pm_runtime_put(hsic_host_info.dev); + pm_runtime_put(host_info[HSIC_BAM].dev); } - hsic_host_info.dev = dev; - hsic_host_info.in_lpm = false; + host_info[HSIC_BAM].dev = dev; + host_info[HSIC_BAM].in_lpm = false; +} + +void msm_bam_set_usb_dev(struct device *dev) +{ + pr_debug("%s: Updating usb device for power managment\n", __func__); + usb_device = dev; +} + +void msm_bam_set_usb_host_dev(struct device *dev) +{ + host_info[HSUSB_BAM].dev = dev; + host_info[HSUSB_BAM].in_lpm = false; } static int get_bam_type_from_core_name(const char *name) @@ -541,6 +586,7 @@ static int connect_pipe_ipa(u8 idx, sps_connection->destination = sps_out_params.ipa_bam_hdl; sps_connection->src_pipe_index = pipe_connect->src_pipe_index; sps_connection->dest_pipe_index = sps_out_params.ipa_ep_idx; + ipa_params->ipa_cons_ep_idx = sps_out_params.ipa_ep_idx; *(ipa_params->src_pipe) = sps_connection->src_pipe_index; pipe_connect->dst_pipe_index = sps_out_params.ipa_ep_idx; pr_debug("%s: BAM pipe usb[%x]->ipa[%x] connection\n", @@ -555,6 +601,7 @@ static int connect_pipe_ipa(u8 idx, sps_connection->source = sps_out_params.ipa_bam_hdl; sps_connection->destination = usb_handle; sps_connection->src_pipe_index = sps_out_params.ipa_ep_idx; + ipa_params->ipa_prod_ep_idx = sps_out_params.ipa_ep_idx; sps_connection->dest_pipe_index = pipe_connect->dst_pipe_index; *(ipa_params->dst_pipe) = sps_connection->dest_pipe_index; pipe_connect->src_pipe_index = sps_out_params.ipa_ep_idx; @@ -565,6 +612,9 @@ static int connect_pipe_ipa(u8 idx, sps_connection->options = 0; } + pipe_connect->data_mem_buf = sps_out_params.data; + pipe_connect->desc_mem_buf = sps_out_params.desc; + sps_connection->data = sps_out_params.data; sps_connection->desc = sps_out_params.desc; sps_connection->event_thresh = 16; @@ -632,37 +682,131 @@ static int disconnect_pipe(u8 idx) return 0; } -static void usb_bam_resume_core(enum usb_bam cur_bam) +static bool _usb_bam_resume_core(void) { - struct usb_phy *phy = usb_get_phy(USB_PHY_TYPE_USB2); + pr_debug("%s: Resuming usb peripheral/host device", __func__); - if (cur_bam != HSUSB_BAM) - return; - BUG_ON(IS_ERR_OR_NULL(phy)); - pr_debug("%s: resume core", __func__); - pm_runtime_resume(phy->dev); + if (usb_device) + pm_runtime_resume(usb_device); + else { + pr_err("%s: usb device is not initialized\n", __func__); + return false; + } + + return true; } -static void usb_bam_start_lpm(bool disconnect) +static bool _hsic_host_bam_resume_core(void) { - struct usb_phy *phy = usb_get_phy(USB_PHY_TYPE_USB2); + pr_debug("%s: enter\n", __func__); - BUG_ON(IS_ERR_OR_NULL(phy)); + /* Exit from "full suspend" in case of hsic host */ + if (host_info[HSIC_BAM].dev && info[HSIC_BAM].in_lpm) { + pr_debug("%s: Getting hsic device %x\n", __func__, + (int)host_info[HSIC_BAM].dev); + pm_runtime_get(host_info[HSIC_BAM].dev); + info[HSIC_BAM].in_lpm = false; + return true; + } + return false; +} + +static bool _hsic_device_bam_resume_core(void) +{ + pr_debug("%s: enter\n", __func__); + + /* Not supported yet */ + return false; +} + +static void _usb_bam_suspend_core(enum usb_bam bam_type, bool disconnect) +{ + + pr_debug("%s: enter bam=%s\n", __func__, bam_enable_strings[bam_type]); + + if (!usb_device) { + pr_err("%s: usb device is not initialized\n", __func__); + return; + } spin_lock(&usb_bam_ipa_handshake_info_lock); - info[HSUSB_BAM].lpm_wait_handshake = false; - info[HSUSB_BAM].lpm_wait_pipes = 0; + info[bam_type].lpm_wait_handshake = false; + info[bam_type].lpm_wait_pipes = 0; if (disconnect) - pm_runtime_put_noidle(phy->dev); + pm_runtime_put_noidle(usb_device); - if (info[HSUSB_BAM].pending_lpm) { - info[HSUSB_BAM].pending_lpm = 0; + if (info[bam_type].pending_lpm) { + info[bam_type].pending_lpm = 0; spin_unlock(&usb_bam_ipa_handshake_info_lock); pr_debug("%s: Going to LPM\n", __func__); - pm_runtime_suspend(phy->dev); + pm_runtime_suspend(usb_device); } else spin_unlock(&usb_bam_ipa_handshake_info_lock); +} + +static void _hsic_device_bam_suspend_core(void) +{ + /* Not supported yet */ + pr_debug("%s: enter\n", __func__); +} + +static void _hsic_host_bam_suspend_core(void) +{ + pr_debug("%s: enter\n", __func__); + + if (host_info[HSIC_BAM].dev && !info[HSIC_BAM].in_lpm) { + pr_debug("%s: Putting hsic host device %x\n", __func__, + (int)host_info[HSIC_BAM].dev); + pm_runtime_put(host_info[HSIC_BAM].dev); + info[HSIC_BAM].in_lpm = true; + } +} + +static void usb_bam_suspend_core(enum usb_bam bam_type, + enum usb_bam_mode bam_mode, + bool disconnect) +{ + pr_debug("%s: enter bam=%s\n", __func__, bam_enable_strings[bam_type]); + + switch (bam_type) { + case HSUSB_BAM: + /* TODO: This needs the correct handling for SSUSB_BAM */ + case SSUSB_BAM: + _usb_bam_suspend_core(bam_type, disconnect); + break; + case HSIC_BAM: + if (bam_mode == USB_BAM_DEVICE) + _hsic_device_bam_suspend_core(); + else /* USB_BAM_HOST */ + _hsic_host_bam_suspend_core(); + break; + default: + pr_err("%s: Invalid BAM type %d\n", __func__, bam_type); + return; + } +} + +static bool usb_bam_resume_core(enum usb_bam bam_type, + enum usb_bam_mode bam_mode) +{ + pr_debug("%s: enter bam=%s\n", __func__, bam_enable_strings[bam_type]); + switch (bam_type) { + case HSUSB_BAM: + /* TODO: This needs the correct handling for SSUSB_BAM */ + case SSUSB_BAM: + return _usb_bam_resume_core(); + break; + case HSIC_BAM: + if (bam_mode == USB_BAM_DEVICE) + return _hsic_device_bam_resume_core(); + else /* USB_BAM_HOST */ + return _hsic_host_bam_resume_core(); + break; + default: + pr_err("%s: Invalid BAM type %d\n", __func__, bam_type); + return false; + } } int usb_bam_connect(int idx, u32 *bam_pipe_idx) @@ -670,6 +814,8 @@ int usb_bam_connect(int idx, u32 *bam_pipe_idx) int ret; struct usb_bam_pipe_connect *pipe_connect = &usb_bam_connections[idx]; struct msm_usb_bam_platform_data *pdata; + enum usb_bam cur_bam; + enum usb_bam_mode cur_mode; if (!ctx.usb_bam_pdev) { pr_err("%s: usb_bam device not found\n", __func__); @@ -703,6 +849,12 @@ int usb_bam_connect(int idx, u32 *bam_pipe_idx) } spin_unlock(&usb_bam_lock); + cur_bam = pipe_connect->bam_type; + cur_mode = pipe_connect->bam_mode; + + /* Set the BAM mode (host/device) according to connected pipe */ + info[cur_bam].cur_bam_mode = pipe_connect->bam_mode; + ret = connect_pipe(idx, bam_pipe_idx); if (ret) { pr_err("%s: pipe connection[%d] failure\n", __func__, idx); @@ -761,13 +913,13 @@ static void stop_cons_transfers(struct usb_bam_pipe_connect *pipe_connect) } } -static int ipa_suspend_pipes(u32 idx) +static int ipa_suspend_pipes(enum usb_bam cur_bam, u32 idx) { struct usb_bam_pipe_connect *dst_pipe, *src_pipe; int ret1, ret2; - dst_pipe = &usb_bam_connections[info[HSUSB_BAM].suspend_dst_idx[idx]]; - src_pipe = &usb_bam_connections[info[HSUSB_BAM].suspend_src_idx[idx]]; + dst_pipe = &usb_bam_connections[info[cur_bam].suspend_dst_idx[idx]]; + src_pipe = &usb_bam_connections[info[cur_bam].suspend_src_idx[idx]]; if (dst_pipe->ipa_clnt_hdl == -1 || src_pipe->ipa_clnt_hdl == -1) { @@ -785,13 +937,13 @@ static int ipa_suspend_pipes(u32 idx) return ret1 | ret2; } -static int ipa_resume_pipes(u32 idx) +static int ipa_resume_pipes(enum usb_bam cur_bam, u32 idx) { struct usb_bam_pipe_connect *dst_pipe, *src_pipe; int ret1, ret2; - src_pipe = &usb_bam_connections[info[HSUSB_BAM].resume_src_idx[idx]]; - dst_pipe = &usb_bam_connections[info[HSUSB_BAM].resume_dst_idx[idx]]; + src_pipe = &usb_bam_connections[info[cur_bam].resume_src_idx[idx]]; + dst_pipe = &usb_bam_connections[info[cur_bam].resume_dst_idx[idx]]; if (dst_pipe->ipa_clnt_hdl == -1 || src_pipe->ipa_clnt_hdl == -1) { @@ -821,7 +973,7 @@ static void resume_suspended_pipes(enum usb_bam cur_bam) pipe_connect = &usb_bam_connections[dst_idx]; if (pipe_connect->cons_stopped) { spin_unlock(&usb_bam_ipa_handshake_info_lock); - ipa_resume_pipes(idx); + ipa_resume_pipes(cur_bam, idx); spin_lock(&usb_bam_ipa_handshake_info_lock); pr_debug("%s: Starting CONS on %d", __func__, dst_idx); start_cons_transfers(pipe_connect); @@ -844,13 +996,12 @@ static inline int all_pipes_suspended(enum usb_bam cur_bam) ctx.pipes_enabled_per_bam[cur_bam]); } -static void usb_bam_finish_suspend(void) +static void usb_bam_finish_suspend(enum usb_bam cur_bam) { int ret; u32 cons_empty, idx, dst_idx; struct sps_pipe *cons_pipe; struct usb_bam_pipe_connect *pipe_connect; - enum usb_bam cur_bam = HSUSB_BAM; mutex_lock(&info[cur_bam].suspend_resume_mutex); @@ -909,7 +1060,7 @@ static void usb_bam_finish_suspend(void) IPA_RM_RESOURCE_RELEASED, ipa_rm_resource_cons[cur_bam]); } - ipa_suspend_pipes(idx); + ipa_suspend_pipes(cur_bam, idx); spin_lock(&usb_bam_ipa_handshake_info_lock); info[cur_bam].resume_src_idx[idx] = info[cur_bam].suspend_src_idx[idx]; @@ -927,7 +1078,9 @@ static void usb_bam_finish_suspend(void) info[cur_bam].pipes_resumed = 0; spin_unlock(&usb_bam_ipa_handshake_info_lock); pr_debug("%s: Starting LPM on Bus Suspend\n", __func__); - usb_bam_start_lpm(0); + + usb_bam_suspend_core(cur_bam, USB_BAM_DEVICE, 0); + mutex_unlock(&info[cur_bam].suspend_resume_mutex); return; @@ -949,7 +1102,16 @@ no_lpm: void usb_bam_finish_suspend_(struct work_struct *w) { - usb_bam_finish_suspend(); + enum usb_bam cur_bam; + struct usb_bam_ipa_handshake_info *info_ptr; + + info_ptr = container_of(w, struct usb_bam_ipa_handshake_info, + finish_suspend_work); + cur_bam = info_ptr->cur_bam_mode; + + pr_debug("%s: Finishing suspend sequence(BAM=%s)\n", __func__, + bam_enable_strings[cur_bam]); + usb_bam_finish_suspend(cur_bam); } static void usb_prod_notify_cb(void *user_data, enum ipa_rm_event event, @@ -977,28 +1139,24 @@ static void usb_prod_notify_cb(void *user_data, enum ipa_rm_event event, } /** - * usb_bam_resume_hsic_host: vote for hsic host core resume. - * In addition also resume all hsic pipes that are connected to - * the ipa peer bam. + * usb_bam_resume_host: vote for hsic host core resume. In + * addition also resume all hsic pipes that are connected to the + * ipa peer bam. * * NOTE: This function should be called in a context that hold * usb_bam_lock. */ -static void usb_bam_resume_hsic_host(void) +static void usb_bam_resume_host(enum usb_bam bam_type) { int i; struct usb_bam_pipe_connect *pipe_iter; - /* Exit from "full suspend" in case of hsic host */ - if (hsic_host_info.dev && info[HSIC_BAM].in_lpm) { - pr_debug("%s: Getting hsic device %x\n", __func__, - (int)hsic_host_info.dev); - pm_runtime_get(hsic_host_info.dev); - info[HSIC_BAM].in_lpm = false; + pr_debug("%s: enter bam=%s\n", __func__, bam_enable_strings[bam_type]); + if (usb_bam_resume_core(bam_type, USB_BAM_HOST)) for (i = 0; i < ctx.max_connections; i++) { pipe_iter = &usb_bam_connections[i]; - if (pipe_iter->bam_type == HSIC_BAM && + if (pipe_iter->bam_type == bam_type && pipe_iter->enabled && pipe_iter->suspended) { spin_unlock(&usb_bam_lock); @@ -1007,7 +1165,6 @@ static void usb_bam_resume_hsic_host(void) spin_lock(&usb_bam_lock); } } - } } static int cons_request_resource(enum usb_bam cur_bam) @@ -1022,11 +1179,11 @@ static int cons_request_resource(enum usb_bam cur_bam) spin_lock(&usb_bam_lock); - switch (cur_bam) { - case HSUSB_BAM: - if (ctx.pipes_enabled_per_bam[HSUSB_BAM] && + switch (info[cur_bam].cur_bam_mode) { + case USB_BAM_DEVICE: + if (ctx.pipes_enabled_per_bam[cur_bam] && info[cur_bam].connect_complete) { - if (!all_pipes_suspended(HSUSB_BAM) && + if (!all_pipes_suspended(cur_bam) && !info[cur_bam].bus_suspend) { pr_debug("%s: ACK on cons_request", __func__); ret = 0; @@ -1040,7 +1197,7 @@ static int cons_request_resource(enum usb_bam cur_bam) } break; - case HSIC_BAM: + case USB_BAM_HOST: /* * Vote for hsic resume, however the core * resume may not be completed yet or on the other hand @@ -1048,7 +1205,7 @@ static int cons_request_resource(enum usb_bam cur_bam) * by other driver, in this case we will just renew our * vote here. */ - usb_bam_resume_hsic_host(); + usb_bam_resume_host(cur_bam); /* * Return sucess if there are pipes connected @@ -1057,12 +1214,12 @@ static int cons_request_resource(enum usb_bam cur_bam) * finish (see msm_bam_hsic_notify_on_resume) */ if (ctx.pipes_enabled_per_bam[cur_bam] && - !hsic_host_info.in_lpm) { + !host_info[cur_bam].in_lpm) { ret = 0; } break; - case SSUSB_BAM: + default: break; } @@ -1076,6 +1233,12 @@ static int cons_request_resource(enum usb_bam cur_bam) return ret; } +static int ss_usb_cons_request_resource(void) +{ + return cons_request_resource(SSUSB_BAM); +} + + static int usb_cons_request_resource(void) { return cons_request_resource(HSUSB_BAM); @@ -1101,7 +1264,7 @@ static int cons_release_resource(enum usb_bam cur_bam) } spin_unlock(&usb_bam_lock); - if (cur_bam == HSUSB_BAM) { + if (info[cur_bam].cur_bam_mode == USB_BAM_DEVICE) { spin_lock(&usb_bam_ipa_handshake_info_lock); if (info[cur_bam].bus_suspend) { queue_work(ctx.usb_bam_wq, @@ -1111,18 +1274,13 @@ static int cons_release_resource(enum usb_bam cur_bam) pr_debug("%s: EINPROGRESS cons_release", __func__); return -EINPROGRESS; - } else if (cur_bam == HSIC_BAM) { - + } else if (info[cur_bam].cur_bam_mode == USB_BAM_HOST) { /* * Allow to go to lpm for now. Actual state will be checked - * in msm_bam_hsic_lpm_ok() just before going to lpm. + * in msm_bam_hsic_lpm_ok() / msm_bam_lpm_ok() just before + * going to lpm. */ - if (hsic_host_info.dev && !info[HSIC_BAM].in_lpm) { - pr_debug("%s: Putting hsic device %x\n", __func__, - (int)hsic_host_info.dev); - pm_runtime_put(hsic_host_info.dev); - info[HSIC_BAM].in_lpm = true; - } + usb_bam_suspend_core(cur_bam, info[cur_bam].cur_bam_mode, 1); } return 0; @@ -1138,6 +1296,11 @@ static int usb_cons_release_resource(void) return cons_release_resource(HSUSB_BAM); } +static int ss_usb_cons_release_resource(void) +{ + return cons_release_resource(SSUSB_BAM); +} + static void usb_bam_ipa_create_resources(void) { struct ipa_rm_create_params usb_prod_create_params; @@ -1146,15 +1309,20 @@ static void usb_bam_ipa_create_resources(void) int ret, i; for (i = 0; i < ARRAY_SIZE(ipa_rm_bams); i++) { + /* Only initialized bams should be regitsterd with RM */ + if (!ipa_rm_bams[i].initialized) + continue; + /* Create USB/HSIC_PROD entity */ - cur_bam = ipa_rm_bams[i]; + cur_bam = ipa_rm_bams[i].bam; memset(&usb_prod_create_params, 0, sizeof(usb_prod_create_params)); usb_prod_create_params.name = ipa_rm_resource_prod[cur_bam]; usb_prod_create_params.reg_params.notify_cb = usb_prod_notify_cb; - usb_prod_create_params.reg_params.user_data = &ipa_rm_bams[i]; + usb_prod_create_params.reg_params.user_data = + &ipa_rm_bams[i].bam; ret = ipa_rm_create_resource(&usb_prod_create_params); if (ret) { pr_err("%s: Failed to create USB_PROD resource\n", @@ -1214,7 +1382,7 @@ void notify_usb_connected(enum usb_bam cur_bam) pr_debug("%s: enter\n", __func__); spin_lock(&usb_bam_ipa_handshake_info_lock); - if (cur_bam == HSUSB_BAM) + if (info[cur_bam].cur_bam_mode == USB_BAM_DEVICE) info[cur_bam].connect_complete = 1; spin_unlock(&usb_bam_ipa_handshake_info_lock); @@ -1289,8 +1457,11 @@ void usb_bam_suspend(struct usb_bam_connect_ipa_params *ipa_params) { struct usb_bam_pipe_connect *pipe_connect; enum usb_bam cur_bam; + enum usb_bam_mode bam_mode; u8 src_idx, dst_idx; + pr_debug("%s: enter\n", __func__); + if (!ipa_params) { pr_err("%s: Invalid ipa params\n", __func__); return; @@ -1306,7 +1477,8 @@ void usb_bam_suspend(struct usb_bam_connect_ipa_params *ipa_params) pipe_connect = &usb_bam_connections[src_idx]; cur_bam = pipe_connect->bam_type; - if (cur_bam != HSUSB_BAM) + bam_mode = pipe_connect->bam_mode; + if (bam_mode != USB_BAM_DEVICE) return; pr_debug("%s: Starting suspend sequence(BAM=%s)\n", __func__, @@ -1336,11 +1508,17 @@ void usb_bam_suspend(struct usb_bam_connect_ipa_params *ipa_params) static void usb_bam_start_suspend(struct work_struct *w) { struct usb_bam_pipe_connect *pipe_connect; - enum usb_bam cur_bam = HSUSB_BAM; + struct usb_bam_ipa_handshake_info *info_ptr; + enum usb_bam cur_bam; u8 src_idx, dst_idx; int pipes_to_suspend; - pr_debug("%s: enter", __func__); + info_ptr = container_of(w, struct usb_bam_ipa_handshake_info, + suspend_work); + cur_bam = info_ptr->bam_type; + pr_debug("%s: Starting suspend sequence(BAM=%s)\n", __func__, + bam_enable_strings[cur_bam]); + mutex_lock(&info[cur_bam].suspend_resume_mutex); spin_lock(&usb_bam_ipa_handshake_info_lock); @@ -1384,26 +1562,29 @@ static void usb_bam_start_suspend(struct work_struct *w) if (info[cur_bam].pipes_to_suspend * 2 == ctx.pipes_enabled_per_bam[cur_bam]) { spin_unlock(&usb_bam_ipa_handshake_info_lock); - wait_for_prod_release(HSUSB_BAM); + wait_for_prod_release(cur_bam); } else spin_unlock(&usb_bam_ipa_handshake_info_lock); mutex_unlock(&info[cur_bam].suspend_resume_mutex); if (info[cur_bam].cur_cons_state == IPA_RM_RESOURCE_RELEASED) - usb_bam_finish_suspend(); + usb_bam_finish_suspend(cur_bam); else pr_debug("Consumer not released yet\n"); } static void usb_bam_finish_resume(struct work_struct *w) { - struct usb_phy *phy = usb_get_phy(USB_PHY_TYPE_USB2); - enum usb_bam cur_bam = HSUSB_BAM; + /* TODO: Change this when HSIC device support is introduced */ + enum usb_bam cur_bam; + struct usb_bam_ipa_handshake_info *info_ptr; struct usb_bam_pipe_connect *pipe_connect; u32 idx, dst_idx, suspended; - BUG_ON(IS_ERR_OR_NULL(phy)); - pr_debug("%s: enter", __func__); + info_ptr = container_of(w, struct usb_bam_ipa_handshake_info, + resume_work); + cur_bam = info_ptr->bam_type; + pr_debug("%s: enter bam=%s\n", __func__, bam_enable_strings[cur_bam]); mutex_lock(&info[cur_bam].suspend_resume_mutex); /* Suspend happened in the meantime */ @@ -1427,7 +1608,7 @@ static void usb_bam_finish_resume(struct work_struct *w) pipe_connect = &usb_bam_connections[dst_idx]; if (pipe_connect->cons_stopped) { spin_unlock(&usb_bam_ipa_handshake_info_lock); - ipa_resume_pipes(idx); + ipa_resume_pipes(cur_bam, idx); spin_lock(&usb_bam_ipa_handshake_info_lock); pr_debug("%s: Starting CONS on %d", __func__, dst_idx); start_cons_transfers(pipe_connect); @@ -1437,7 +1618,7 @@ static void usb_bam_finish_resume(struct work_struct *w) if (info[cur_bam].cur_cons_state == IPA_RM_RESOURCE_GRANTED) { pr_debug("%s: Notify CONS_GRANTED\n", __func__); ipa_rm_notify_completion(IPA_RM_RESOURCE_GRANTED, - ipa_rm_resource_cons[HSUSB_BAM]); + ipa_rm_resource_cons[cur_bam]); } spin_unlock(&usb_bam_ipa_handshake_info_lock); @@ -1495,7 +1676,9 @@ void usb_bam_resume(struct usb_bam_connect_ipa_params *ipa_params) pipe_connect = &usb_bam_connections[src_idx]; cur_bam = pipe_connect->bam_type; - if (cur_bam != HSUSB_BAM) + pr_debug("%s: bam=%s mode =%d\n", __func__, + bam_enable_strings[cur_bam], pipe_connect->bam_mode); + if (pipe_connect->bam_mode != USB_BAM_DEVICE) return; info[cur_bam].in_lpm = false; @@ -1505,29 +1688,38 @@ void usb_bam_resume(struct usb_bam_connect_ipa_params *ipa_params) queue_work(ctx.usb_bam_wq, &info[cur_bam].resume_work); } -void msm_bam_wait_for_hsic_prod_granted(void) +void _msm_bam_wait_for_host_prod_granted(enum usb_bam bam_type) { spin_lock(&usb_bam_lock); - ctx.is_bam_inactivity[HSIC_BAM] = false; + pr_debug("%s: enter bam=%s\n", __func__, bam_enable_strings[bam_type]); + ctx.is_bam_inactivity[bam_type] = false; /* Get back to resume state including wakeup ipa */ - usb_bam_resume_hsic_host(); + usb_bam_resume_core(bam_type, USB_BAM_HOST); /* Ensure getting the producer resource */ - wait_for_prod_granted(HSIC_BAM); + wait_for_prod_granted(bam_type); spin_unlock(&usb_bam_lock); + } -void msm_bam_hsic_notify_on_resume(void) +void msm_bam_wait_for_hsic_host_prod_granted(void) +{ + pr_debug("%s: start\n", __func__); + _msm_bam_wait_for_host_prod_granted(HSIC_BAM); +} + +void _msm_bam_host_notify_on_resume(enum usb_bam bam_type) { spin_lock(&usb_bam_lock); + pr_debug("%s: enter bam=%s\n", __func__, bam_enable_strings[bam_type]); - hsic_host_info.in_lpm = false; + host_info[bam_type].in_lpm = false; /* HSIC resume completed. Notify CONS grant if CONS was requested */ - notify_usb_connected(HSIC_BAM); + notify_usb_connected(bam_type); /* * This function is called to notify the usb bam driver @@ -1535,18 +1727,20 @@ void msm_bam_hsic_notify_on_resume(void) * and clocked on. Therefore we can now set the inactivity * timer to the hsic bam hw. */ - if (ctx.inactivity_timer_ms[HSIC_BAM]) - usb_bam_set_inactivity_timer(HSIC_BAM); + if (ctx.inactivity_timer_ms[bam_type]) + usb_bam_set_inactivity_timer(bam_type); spin_unlock(&usb_bam_lock); } -bool msm_bam_hsic_lpm_ok(void) +bool msm_bam_host_lpm_ok(enum usb_bam bam_type) { int i; struct usb_bam_pipe_connect *pipe_iter; - if (hsic_host_info.dev) { + pr_debug("%s: enter bam=%s\n", __func__, bam_enable_strings[bam_type]); + + if (host_info[bam_type].dev) { pr_debug("%s: Starting hsic full suspend sequence\n", __func__); @@ -1560,20 +1754,20 @@ bool msm_bam_hsic_lpm_ok(void) */ spin_lock(&usb_bam_lock); - if (info[HSIC_BAM].cur_cons_state == + if (info[bam_type].cur_cons_state == IPA_RM_RESOURCE_RELEASED && - info[HSIC_BAM].cur_prod_state == + info[bam_type].cur_prod_state == IPA_RM_RESOURCE_RELEASED && - ctx.is_bam_inactivity[HSIC_BAM] && info[HSIC_BAM].in_lpm) { + ctx.is_bam_inactivity[bam_type] && info[bam_type].in_lpm) { /* HSIC host will go now to lpm */ pr_debug("%s: vote for suspend hsic %x\n", - __func__, (int)hsic_host_info.dev); + __func__, (int)host_info[bam_type].dev); for (i = 0; i < ctx.max_connections; i++) { pipe_iter = &usb_bam_connections[i]; - if (pipe_iter->bam_type == HSIC_BAM && + if (pipe_iter->bam_type == bam_type && pipe_iter->enabled && !pipe_iter->suspended) { spin_unlock(&usb_bam_lock); @@ -1584,23 +1778,23 @@ bool msm_bam_hsic_lpm_ok(void) } } - hsic_host_info.in_lpm = true; + host_info[bam_type].in_lpm = true; spin_unlock(&usb_bam_lock); return true; } /* We don't allow lpm, therefore renew our vote here */ - if (info[HSIC_BAM].in_lpm) { + if (info[bam_type].in_lpm) { pr_debug("%s: Not allow lpm while ref count=0\n", __func__); pr_debug("%s: inactivity=%d, c_s=%d p_s=%d lpm=%d\n", - __func__, ctx.is_bam_inactivity[HSIC_BAM], - info[HSIC_BAM].cur_cons_state, - info[HSIC_BAM].cur_prod_state, - info[HSIC_BAM].in_lpm); - pm_runtime_get(hsic_host_info.dev); - info[HSIC_BAM].in_lpm = false; + __func__, ctx.is_bam_inactivity[bam_type], + info[bam_type].cur_cons_state, + info[bam_type].cur_prod_state, + info[bam_type].in_lpm); + pm_runtime_get(host_info[bam_type].dev); + info[bam_type].in_lpm = false; spin_unlock(&usb_bam_lock); } else spin_unlock(&usb_bam_lock); @@ -1611,15 +1805,23 @@ bool msm_bam_hsic_lpm_ok(void) return true; } +void msm_bam_hsic_host_notify_on_resume(void) +{ + _msm_bam_host_notify_on_resume(HSIC_BAM); +} + int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params) { u8 idx; enum usb_bam cur_bam; + enum usb_bam_mode cur_mode; struct usb_bam_pipe_connect *pipe_connect; int ret; struct msm_usb_bam_platform_data *pdata = ctx.usb_bam_pdev->dev.platform_data; + pr_debug("%s: start\n", __func__); + if (!ipa_params) { pr_err("%s: Invalid ipa params\n", __func__); @@ -1638,6 +1840,10 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params) } pipe_connect = &usb_bam_connections[idx]; cur_bam = pipe_connect->bam_type; + cur_mode = pipe_connect->bam_mode; + + /* Set the BAM mode (host/device) according to connected pipe */ + info[cur_bam].cur_bam_mode = pipe_connect->bam_mode; if (pipe_connect->enabled) { pr_err("%s: connection %d was already established\n", @@ -1647,11 +1853,11 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params) pr_debug("%s: enter", __func__); - if (cur_bam == HSUSB_BAM) { - mutex_lock(&info[HSUSB_BAM].suspend_resume_mutex); + if (cur_mode == USB_BAM_DEVICE) { + mutex_lock(&info[cur_bam].suspend_resume_mutex); spin_lock(&usb_bam_lock); - if (ctx.pipes_enabled_per_bam[HSUSB_BAM] == 0) { + if (ctx.pipes_enabled_per_bam[cur_bam] == 0) { spin_unlock(&usb_bam_lock); spin_lock(&usb_bam_ipa_handshake_info_lock); info[cur_bam].lpm_wait_handshake = true; @@ -1666,7 +1872,7 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params) pipe_connect->cons_stopped = 0; pipe_connect->prod_stopped = 0; spin_unlock(&usb_bam_ipa_handshake_info_lock); - usb_bam_resume_core(cur_bam); + usb_bam_resume_core(cur_bam, USB_BAM_DEVICE); } else spin_unlock(&usb_bam_lock); } @@ -1677,21 +1883,17 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params) (ctx.pipes_enabled_per_bam[cur_bam] == 0)) { spin_unlock(&usb_bam_lock); - if (cur_bam == HSUSB_BAM) + if (cur_bam != HSIC_BAM) msm_hw_bam_disable(1); sps_device_reset(ctx.h_bam[cur_bam]); - if (cur_bam == HSUSB_BAM) + if (cur_bam != HSIC_BAM) msm_hw_bam_disable(0); - /* On re-connect assume out from lpm for HSIC BAM */ - if (cur_bam == HSIC_BAM && hsic_host_info.dev && - info[HSIC_BAM].in_lpm) { - pr_err("%s: Getting hsic device %x\n", - __func__, (int)hsic_host_info.dev); - pm_runtime_get(hsic_host_info.dev); - } + /* On re-connect assume out from lpm for HOST BAM */ + if (cur_mode == USB_BAM_HOST) + usb_bam_resume_core(cur_bam, cur_mode); /* On re-connect assume out from lpm for all BAMs */ info[cur_bam].in_lpm = false; @@ -1707,8 +1909,8 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params) ret = connect_pipe_ipa(idx, ipa_params); if (ret) { pr_err("%s: pipe connection failure\n", __func__); - if (cur_bam == HSUSB_BAM) - mutex_unlock(&info[HSUSB_BAM].suspend_resume_mutex); + if (cur_mode == USB_BAM_DEVICE) + mutex_unlock(&info[cur_bam].suspend_resume_mutex); return ret; } pr_debug("%s: pipe connection success\n", __func__); @@ -1730,8 +1932,8 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params) notify_usb_connected(cur_bam); spin_unlock(&usb_bam_lock); - if (cur_bam == HSUSB_BAM) - mutex_unlock(&info[HSUSB_BAM].suspend_resume_mutex); + if (cur_mode == USB_BAM_DEVICE) + mutex_unlock(&info[cur_bam].suspend_resume_mutex); pr_debug("%s: done", __func__); @@ -1764,8 +1966,7 @@ int usb_bam_client_ready(bool ready) pr_debug("%s: enters pending_work\n", __func__); } - pr_debug("%s: success\n", - __func__); + pr_debug("%s: success\n", __func__); return 0; } @@ -1792,9 +1993,10 @@ static void usb_bam_work(struct work_struct *w) * wakeup hsic host class driver (done by the callback below) */ if (pipe_connect->peer_bam == IPA_P_BAM && - pipe_connect->bam_type == HSIC_BAM && - info[HSIC_BAM].cur_prod_state != IPA_RM_RESOURCE_GRANTED) { - wait_for_prod_granted(HSIC_BAM); + pipe_connect->bam_mode == USB_BAM_HOST && + info[pipe_connect->bam_type].cur_prod_state + != IPA_RM_RESOURCE_GRANTED) { + wait_for_prod_granted(pipe_connect->bam_type); } /* @@ -1809,8 +2011,8 @@ static void usb_bam_work(struct work_struct *w) * resources against the ipa resource manager. */ spin_lock(&usb_bam_lock); - if (pipe_connect->bam_type == HSIC_BAM) - usb_bam_resume_hsic_host(); + if (pipe_connect->bam_mode == USB_BAM_HOST) + usb_bam_resume_host(pipe_connect->bam_type); spin_unlock(&usb_bam_lock); /* Notify about wakeup / activity of the bam */ @@ -1826,7 +2028,7 @@ static void usb_bam_work(struct work_struct *w) usb_bam_set_inactivity_timer(pipe_connect->bam_type); spin_unlock(&usb_bam_lock); - if (pipe_connect->bam_type == HSUSB_BAM) { + if (pipe_connect->bam_mode == USB_BAM_DEVICE) { /* A2 wakeup not from LPM (CONS was up) */ wait_for_prod_granted(pipe_connect->bam_type); if (pipe_connect->start) { @@ -1888,13 +2090,12 @@ static void usb_bam_work(struct work_struct *w) * If consumer is up, we will wait to the release consumer * notification. */ - if (hsic_host_info.dev && - info[HSIC_BAM].cur_cons_state == - IPA_RM_RESOURCE_RELEASED && !info[HSIC_BAM].in_lpm) { - pr_debug("%s: Putting hsic device %x\n", __func__, - (int)hsic_host_info.dev); - pm_runtime_put(hsic_host_info.dev); - info[HSIC_BAM].in_lpm = true; + if (host_info[pipe_connect->bam_type].dev && + info[pipe_connect->bam_type].cur_cons_state == + IPA_RM_RESOURCE_RELEASED && + !info[pipe_connect->bam_type].in_lpm) { + usb_bam_suspend_core(pipe_connect->bam_type, + pipe_connect->bam_mode, 1); } break; @@ -2069,8 +2270,10 @@ static int __usb_bam_register_wake_cb(int idx, int (*callback)(void *user), int usb_bam_register_wake_cb(u8 idx, int (*callback)(void *user), void *param) { - info[HSUSB_BAM].wake_cb = callback; - info[HSUSB_BAM].wake_param = param; + struct usb_bam_pipe_connect *pipe_connect = &usb_bam_connections[idx]; + + info[pipe_connect->bam_type].wake_cb = callback; + info[pipe_connect->bam_type].wake_param = param; return __usb_bam_register_wake_cb(idx, callback, param, true); } @@ -2156,6 +2359,7 @@ int usb_bam_disconnect_ipa(struct usb_bam_connect_ipa_params *ipa_params) struct usb_bam_pipe_connect *pipe_connect; struct sps_connect *sps_connection; enum usb_bam cur_bam; + enum usb_bam_mode bam_mode; if (!ipa_params->prod_clnt_hdl && !ipa_params->cons_clnt_hdl) { pr_err("%s: Both of the handles is missing\n", __func__); @@ -2170,6 +2374,7 @@ int usb_bam_disconnect_ipa(struct usb_bam_connect_ipa_params *ipa_params) idx = ipa_params->src_idx; pipe_connect = &usb_bam_connections[idx]; cur_bam = pipe_connect->bam_type; + bam_mode = pipe_connect->bam_mode; mutex_lock(&info[cur_bam].suspend_resume_mutex); /* Delay USB core to go into lpm before we finish our handshake */ @@ -2183,7 +2388,7 @@ int usb_bam_disconnect_ipa(struct usb_bam_connect_ipa_params *ipa_params) /* Do the release handshake with the A2 via RM */ spin_lock(&usb_bam_ipa_handshake_info_lock); - if (cur_bam == HSUSB_BAM) { + if (bam_mode == USB_BAM_DEVICE) { info[cur_bam].connect_complete = 0; info[cur_bam].lpm_wait_pipes = 1; info[cur_bam].disconnected = 1; @@ -2193,7 +2398,8 @@ int usb_bam_disconnect_ipa(struct usb_bam_connect_ipa_params *ipa_params) /* Start release handshake on the last producer pipe */ if (info[cur_bam].prod_pipes_enabled_per_bam == 1) wait_for_prod_release(cur_bam); - usb_bam_resume_core(cur_bam); + if (pipe_connect->bam_mode == USB_BAM_DEVICE) + usb_bam_resume_core(cur_bam, pipe_connect->bam_mode); /* close USB -> IPA pipe */ ret = ipa_disconnect(ipa_params->prod_clnt_hdl); if (ret) { @@ -2259,10 +2465,15 @@ int usb_bam_disconnect_ipa(struct usb_bam_connect_ipa_params *ipa_params) IPA_RM_RESOURCE_RELEASED, ipa_rm_resource_cons[cur_bam]); } - if (cur_bam == HSUSB_BAM) { + /* TODO: Make also for SSUSB_BAM, when correct suspend + * in place + */ + if (cur_bam == HSUSB_BAM && + pipe_connect->bam_mode == USB_BAM_DEVICE) { pr_debug("%s Ended disconnect sequence\n", __func__); - usb_bam_start_lpm(1); + usb_bam_suspend_core(cur_bam, + USB_BAM_DEVICE, 1); } } } @@ -2497,6 +2708,18 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata( } usb_bam_connections[i].bam_type = bam; + rc = of_property_read_u32(node, "qcom,bam-mode", + &usb_bam_connections[i].bam_mode); + if (rc) { + pr_debug("%s: bam mode is missing in device tree\n", + __func__); + /* + * In cases where bam_mode is not set, the default + * will be set to device + */ + usb_bam_connections[i].bam_mode = USB_BAM_DEVICE; + } + rc = of_property_read_u32(node, "qcom,peer-bam", &usb_bam_connections[i].peer_bam); if (rc) { @@ -2563,9 +2786,9 @@ err: return NULL; } -static int usb_bam_init(int bam_idx) +static int usb_bam_init(int bam_type) { - int ret, irq; + int ret, irq, i; void *usb_virt_addr; struct msm_usb_bam_platform_data *pdata = ctx.usb_bam_pdev->dev.platform_data; @@ -2573,16 +2796,16 @@ static int usb_bam_init(int bam_idx) struct sps_bam_props *props = &ctx.usb_bam_sps.usb_props; pr_debug("%s: usb_bam_init - %s\n", __func__, - bam_enable_strings[bam_idx]); + bam_enable_strings[bam_type]); res = platform_get_resource_byname(ctx.usb_bam_pdev, IORESOURCE_MEM, - bam_enable_strings[bam_idx]); + bam_enable_strings[bam_type]); if (!res) { dev_dbg(&ctx.usb_bam_pdev->dev, "bam not initialized\n"); return 0; } irq = platform_get_irq_byname(ctx.usb_bam_pdev, - bam_enable_strings[bam_idx]); + bam_enable_strings[bam_type]); if (irq < 0) { dev_err(&ctx.usb_bam_pdev->dev, "Unable to get IRQ resource\n"); return irq; @@ -2596,9 +2819,9 @@ static int usb_bam_init(int bam_idx) } /* Check if USB3 pipe memory needs to be enabled */ - if (bam_idx == SSUSB_BAM && bam_use_private_mem(bam_idx)) { + if (bam_type == SSUSB_BAM && bam_use_private_mem(bam_type)) { pr_debug("%s: Enabling USB private memory for: %s\n", __func__, - bam_enable_strings[bam_idx]); + bam_enable_strings[bam_type]); ram_resource = platform_get_resource_byname(ctx.usb_bam_pdev, IORESOURCE_MEM, "qscratch_ram1_reg"); @@ -2626,26 +2849,33 @@ static int usb_bam_init(int bam_idx) props->event_threshold = pdata->override_threshold; props->num_pipes = pdata->usb_bam_num_pipes; props->callback = usb_bam_sps_events; - props->user = bam_enable_strings[bam_idx]; + props->user = bam_enable_strings[bam_type]; props->options = SPS_BAM_OPT_IRQ_WAKEUP; /* * HSUSB and HSIC Cores don't support RESET ACK signal to BAMs * Hence, let BAM to ignore acknowledge from USB while resetting PIPE */ - if (pdata->ignore_core_reset_ack && bam_idx != SSUSB_BAM) + if (pdata->ignore_core_reset_ack && bam_type != SSUSB_BAM) props->options = SPS_BAM_NO_EXT_P_RST; if (pdata->disable_clk_gating) props->options |= SPS_BAM_NO_LOCAL_CLK_GATING; - ret = sps_register_bam_device(props, &(ctx.h_bam[bam_idx])); + ret = sps_register_bam_device(props, &(ctx.h_bam[bam_type])); if (ret < 0) { pr_err("%s: register bam error %d\n", __func__, ret); ret = -EFAULT; goto free_qscratch_reg; } + /* Mark this bam as initilaized */ + for (i = 0; i < ARRAY_SIZE(ipa_rm_bams); i++) + if (ipa_rm_bams[i].bam == bam_type) { + ipa_rm_bams[i].initialized = true; + break; + } + return 0; free_qscratch_reg: @@ -2829,6 +3059,7 @@ static int usb_bam_probe(struct platform_device *pdev) info[i].pipes_to_suspend = 0; info[i].pipes_suspended = 0; info[i].pipes_resumed = 0; + info[i].bam_type = i; INIT_WORK(&info[i].resume_work, usb_bam_finish_resume); INIT_WORK(&info[i].suspend_work, usb_bam_start_suspend); INIT_WORK(&info[i].finish_suspend_work, @@ -2863,7 +3094,7 @@ static int usb_bam_probe(struct platform_device *pdev) int usb_bam_get_qdss_idx(u8 num) { return usb_bam_get_connection_idx(ctx.qdss_core_name, QDSS_P_BAM, - PEER_PERIPHERAL_TO_USB, num); + PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, num); } EXPORT_SYMBOL(usb_bam_get_qdss_idx); @@ -2902,7 +3133,7 @@ EXPORT_SYMBOL(get_bam2bam_connection_info); int usb_bam_get_connection_idx(const char *core_name, enum peer_bam client, - enum usb_bam_pipe_dir dir, u32 num) + enum usb_bam_pipe_dir dir, enum usb_bam_mode bam_mode, u32 num) { u8 i; int bam_type; @@ -2918,6 +3149,7 @@ int usb_bam_get_connection_idx(const char *core_name, enum peer_bam client, if (usb_bam_connections[i].bam_type == bam_type && usb_bam_connections[i].peer_bam == client && usb_bam_connections[i].dir == dir && + usb_bam_connections[i].bam_mode == bam_mode && usb_bam_connections[i].pipe_num == num) { pr_debug("%s: index %d was found\n", __func__, i); return i; @@ -2928,25 +3160,51 @@ int usb_bam_get_connection_idx(const char *core_name, enum peer_bam client, } EXPORT_SYMBOL(usb_bam_get_connection_idx); -bool msm_bam_lpm_ok(void) +bool msm_bam_device_lpm_ok(enum usb_bam bam_type) { + pr_debug("%s: enter bam%s\n", __func__, bam_enable_strings[bam_type]); + spin_lock(&usb_bam_ipa_handshake_info_lock); - if (info[HSUSB_BAM].lpm_wait_handshake || - info[HSUSB_BAM].lpm_wait_pipes) { - info[HSUSB_BAM].pending_lpm = 1; + if (info[bam_type].lpm_wait_handshake || + info[bam_type].lpm_wait_pipes) { + info[bam_type].pending_lpm = 1; spin_unlock(&usb_bam_ipa_handshake_info_lock); pr_err("%s: Scheduling LPM for later\n", __func__); return 0; } else { - info[HSUSB_BAM].pending_lpm = 0; - info[HSUSB_BAM].in_lpm = true; + info[bam_type].pending_lpm = 0; + info[bam_type].in_lpm = true; spin_unlock(&usb_bam_ipa_handshake_info_lock); pr_err("%s: Going to LPM now\n", __func__); return 1; } } -EXPORT_SYMBOL(msm_bam_lpm_ok); +bool msm_bam_usb_lpm_ok(void) +{ + pr_debug("%s: enter mode %d\n", __func__, info[HSUSB_BAM].cur_bam_mode); + + if (info[HSUSB_BAM].cur_bam_mode == USB_BAM_DEVICE) + return msm_bam_device_lpm_ok(HSUSB_BAM); + else /* USB_BAM_HOST */ { + return msm_bam_host_lpm_ok(HSUSB_BAM); + } +} +EXPORT_SYMBOL(msm_bam_usb_lpm_ok); + +bool msm_bam_hsic_lpm_ok(void) +{ + pr_debug("%s: enter\n", __func__); + + if (info[HSIC_BAM].cur_bam_mode == USB_BAM_DEVICE) + return msm_bam_device_lpm_ok(HSIC_BAM); + else /* USB_BAM_HOST */ { + return msm_bam_host_lpm_ok(HSIC_BAM); + } +} +EXPORT_SYMBOL(msm_bam_hsic_lpm_ok); + +/* TODO: make this for SSUSB_BAM when lpm support is in place */ void msm_bam_notify_lpm_resume() { /* diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index ef86091a8385..251f43575463 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -207,6 +207,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(model_name), POWER_SUPPLY_ATTR(manufacturer), POWER_SUPPLY_ATTR(serial_number), + POWER_SUPPLY_ATTR(battery_type), }; static struct attribute * diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c index a95389f6a348..63af1ab05a41 100644 --- a/drivers/power/qpnp-bms.c +++ b/drivers/power/qpnp-bms.c @@ -408,6 +408,14 @@ static void disable_bms_irq(struct bms_irq *irq) } } +static void disable_bms_irq_nosync(struct bms_irq *irq) +{ + if (!__test_and_set_bit(0, &irq->disabled)) { + disable_irq_nosync(irq->irq); + pr_debug("disabled irq %d\n", irq->irq); + } +} + #define HOLD_OREG_DATA BIT(0) static int lock_output_data(struct qpnp_bms_chip *chip) { @@ -1007,14 +1015,14 @@ static int read_soc_params_raw(struct qpnp_bms_chip *chip, chip->base + BMS1_OCV_FOR_SOC_DATA0, 2); if (rc) { pr_err("Error reading ocv: rc = %d\n", rc); - return -ENXIO; + goto param_err; } rc = read_cc_raw(chip, &raw->cc, CC); rc = read_cc_raw(chip, &raw->shdw_cc, SHDW_CC); if (rc) { pr_err("Failed to read raw cc data, rc = %d\n", rc); - return rc; + goto param_err; } unlock_output_data(chip); @@ -1072,6 +1080,11 @@ static int read_soc_params_raw(struct qpnp_bms_chip *chip, raw->last_good_ocv_raw, raw->last_good_ocv_uv); pr_debug("cc_raw= 0x%llx\n", raw->cc); return 0; + +param_err: + unlock_output_data(chip); + mutex_unlock(&chip->bms_output_lock); + return rc; } static int calculate_pc(struct qpnp_bms_chip *chip, int ocv_uv, @@ -1478,7 +1491,7 @@ static int get_prop_bms_charge_counter(struct qpnp_bms_chip *chip) mutex_lock(&chip->bms_output_lock); lock_output_data(chip); - read_cc_raw(chip, &cc_raw, false); + read_cc_raw(chip, &cc_raw, CC); unlock_output_data(chip); mutex_unlock(&chip->bms_output_lock); @@ -1492,7 +1505,7 @@ static int get_prop_bms_charge_counter_shadow(struct qpnp_bms_chip *chip) mutex_lock(&chip->bms_output_lock); lock_output_data(chip); - read_cc_raw(chip, &cc_raw, true); + read_cc_raw(chip, &cc_raw, SHDW_CC); unlock_output_data(chip); mutex_unlock(&chip->bms_output_lock); @@ -3548,7 +3561,7 @@ static irqreturn_t bms_sw_cc_thr_irq_handler(int irq, void *_chip) struct qpnp_bms_chip *chip = _chip; pr_debug("sw_cc_thr irq triggered\n"); - disable_bms_irq(&chip->sw_cc_thr_irq); + disable_bms_irq_nosync(&chip->sw_cc_thr_irq); bms_stay_awake(&chip->soc_wake_source); schedule_work(&chip->recalc_work); return IRQ_HANDLED; diff --git a/drivers/power/smb135x-charger.c b/drivers/power/smb135x-charger.c index d8e0de3ad135..63d4db871b20 100644 --- a/drivers/power/smb135x-charger.c +++ b/drivers/power/smb135x-charger.c @@ -200,6 +200,11 @@ #define IRQ_D_TIMEOUT_BIT BIT(2) #define IRQ_E_REG 0x54 +#define IRQ_E_DC_OV_BIT BIT(6) +#define IRQ_E_DC_UV_BIT BIT(4) +#define IRQ_E_USB_OV_BIT BIT(2) +#define IRQ_E_USB_UV_BIT BIT(0) + #define IRQ_F_REG 0x55 #define IRQ_F_POWER_OK_BIT BIT(0) @@ -237,6 +242,7 @@ struct smb135x_chg { bool usb_present; bool dc_present; + bool dc_ov; bool bmd_algo_disabled; bool iterm_disabled; @@ -375,34 +381,6 @@ static bool is_usb100_broken(struct smb135x_chg *chip) return !!(reg & CHECK_USB100_GOOD_BIT); } -static bool is_dc_present(struct smb135x_chg *chip) -{ - int rc; - u8 reg; - - rc = smb135x_read(chip, STATUS_8_REG, ®); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc); - return false; - } - - return !!(reg & (DCIN_9V | DCIN_UNREG | DCIN_LV)); -} - -static bool is_usb_present(struct smb135x_chg *chip) -{ - int rc; - u8 reg; - - rc = smb135x_read(chip, STATUS_8_REG, ®); - if (rc < 0) { - dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc); - return false; - } - - return !!(reg & (USBIN_9V | USBIN_UNREG | USBIN_LV)); -} - static char *usb_type_str[] = { "ACA_DOCK", /* bit 0 */ "ACA_C", /* bit 1 */ @@ -428,11 +406,11 @@ static enum power_supply_type usb_type_enum[] = { POWER_SUPPLY_TYPE_USB_ACA, /* bit 1 */ POWER_SUPPLY_TYPE_USB_ACA, /* bit 2 */ POWER_SUPPLY_TYPE_USB_ACA, /* bit 3 */ - POWER_SUPPLY_TYPE_UNKNOWN, /* bit 5 */ POWER_SUPPLY_TYPE_USB, /* bit 4 */ + POWER_SUPPLY_TYPE_UNKNOWN, /* bit 5 */ POWER_SUPPLY_TYPE_USB_DCP, /* bit 6 */ POWER_SUPPLY_TYPE_USB_CDP, /* bit 7 */ - POWER_SUPPLY_TYPE_USB, /* bit 8 error case, report SDP */ + POWER_SUPPLY_TYPE_UNKNOWN, /* bit 8 error case, report UNKNWON */ }; /* helper to return enum power_supply_type of USB type */ @@ -505,18 +483,6 @@ static int smb135x_get_prop_batt_present(struct smb135x_chg *chip) return chip->batt_present; } -static int smb135x_get_charging_status(struct smb135x_chg *chip) -{ - int rc; - u8 reg = 0; - - rc = smb135x_read(chip, STATUS_4_REG, ®); - if (rc < 0) - return 0; - - return (reg & CHG_EN_BIT) ? 1 : 0; -} - static int smb135x_get_prop_charge_type(struct smb135x_chg *chip) { int rc; @@ -915,7 +881,7 @@ static int smb135x_battery_get_property(struct power_supply *psy, val->intval = smb135x_get_prop_batt_present(chip); break; case POWER_SUPPLY_PROP_CHARGING_ENABLED: - val->intval = smb135x_get_charging_status(chip); + val->intval = chip->chg_enabled; break; case POWER_SUPPLY_PROP_CHARGE_TYPE: val->intval = smb135x_get_prop_charge_type(chip); @@ -937,6 +903,7 @@ static int smb135x_battery_get_property(struct power_supply *psy, static enum power_supply_property smb135x_dc_properties[] = { POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_HEALTH, }; static int smb135x_dc_get_property(struct power_supply *psy, @@ -948,8 +915,10 @@ static int smb135x_dc_get_property(struct power_supply *psy, switch (prop) { case POWER_SUPPLY_PROP_ONLINE: - /* return if dc is charging the battery */ - val->intval = is_dc_present(chip); + val->intval = chip->dc_present; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = chip->dc_present; break; default: return -EINVAL; @@ -1222,15 +1191,42 @@ static int power_ok_handler(struct smb135x_chg *chip, u8 rt_stat) return 0; } +static int handle_dc_removal(struct smb135x_chg *chip) +{ + if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIRELESS) { + cancel_delayed_work_sync(&chip->wireless_insertion_work); + smb135x_dc_suspend(chip, true); + } + + if (chip->dc_psy_type != -EINVAL) + power_supply_set_online(&chip->dc_psy, chip->dc_present); + return 0; +} + +#define DCIN_UNSUSPEND_DELAY_MS 1000 +static int handle_dc_insertion(struct smb135x_chg *chip) +{ + if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIRELESS) + schedule_delayed_work(&chip->wireless_insertion_work, + msecs_to_jiffies(DCIN_UNSUSPEND_DELAY_MS)); + if (chip->dc_psy_type != -EINVAL) + power_supply_set_online(&chip->dc_psy, + chip->dc_present); + + return 0; +} /** * dcin_uv_handler() - called when the dc voltage crosses the uv threshold * @chip: pointer to smb135x_chg chip * @rt_stat: the status bit indicating whether dc voltage is uv */ -#define DCIN_UNSUSPEND_DELAY_MS 1000 static int dcin_uv_handler(struct smb135x_chg *chip, u8 rt_stat) { - bool dc_present = is_dc_present(chip); + /* + * rt_stat indicates if dc is undervolted. If so dc_present + * should be marked removed + */ + bool dc_present = !rt_stat; pr_debug("chip->dc_present = %d dc_present = %d\n", chip->dc_present, dc_present); @@ -1238,30 +1234,45 @@ static int dcin_uv_handler(struct smb135x_chg *chip, u8 rt_stat) if (chip->dc_present && !dc_present) { /* dc removed */ chip->dc_present = dc_present; - if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIRELESS) { - cancel_delayed_work_sync( - &chip->wireless_insertion_work); - smb135x_dc_suspend(chip, true); - } - if (chip->dc_psy_type != -EINVAL) - power_supply_set_online(&chip->dc_psy, - chip->dc_present); + handle_dc_removal(chip); } if (!chip->dc_present && dc_present) { /* dc inserted */ chip->dc_present = dc_present; - if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIRELESS) - schedule_delayed_work(&chip->wireless_insertion_work, - msecs_to_jiffies(DCIN_UNSUSPEND_DELAY_MS)); - if (chip->dc_psy_type != -EINVAL) - power_supply_set_online(&chip->dc_psy, - chip->dc_present); + handle_dc_insertion(chip); } return 0; } +static int dcin_ov_handler(struct smb135x_chg *chip, u8 rt_stat) +{ + /* + * rt_stat indicates if dc is overvolted. If so dc_present + * should be marked removed + */ + bool dc_present = !rt_stat; + + pr_debug("chip->dc_present = %d dc_present = %d\n", + chip->dc_present, dc_present); + + chip->dc_ov = !!rt_stat; + + if (chip->dc_present && !dc_present) { + /* dc removed */ + chip->dc_present = dc_present; + handle_dc_removal(chip); + } + + if (!chip->dc_present && dc_present) { + /* dc inserted */ + chip->dc_present = dc_present; + handle_dc_insertion(chip); + } + return 0; +} + static int handle_usb_removal(struct smb135x_chg *chip) { if (chip->usb_psy) { @@ -1288,15 +1299,16 @@ static int handle_usb_insertion(struct smb135x_chg *chip) dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc); return rc; } - usb_type_name = get_usb_type_name(reg); - usb_supply_type = get_usb_supply_type(reg); /* * Report the charger type as UNKNOWN if the * apsd-fail flag is set. This nofifies the USB driver * to initiate a s/w based charger type detection. */ if (chip->workaround_flags & WRKARND_APSD_FAIL) - usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN; + reg = 0; + + usb_type_name = get_usb_type_name(reg); + usb_supply_type = get_usb_supply_type(reg); pr_debug("inserted %s, usb psy type = %d stat_5 = 0x%02x\n", usb_type_name, usb_supply_type, reg); if (chip->usb_psy) { @@ -1315,7 +1327,30 @@ static int handle_usb_insertion(struct smb135x_chg *chip) */ static int usbin_uv_handler(struct smb135x_chg *chip, u8 rt_stat) { - bool usb_present = is_usb_present(chip); + /* + * rt_stat indicates if usb is undervolted. If so usb_present + * should be marked removed + */ + bool usb_present = !rt_stat; + + pr_debug("chip->usb_present = %d usb_present = %d\n", + chip->usb_present, usb_present); + if (chip->usb_present && !usb_present) { + /* USB removed */ + chip->usb_present = usb_present; + handle_usb_removal(chip); + } + return 0; +} + +static int usbin_ov_handler(struct smb135x_chg *chip, u8 rt_stat) +{ + /* + * rt_stat indicates if usb is overvolted. If so usb_present + * should be marked removed + */ + bool usb_present = !rt_stat; + int health; pr_debug("chip->usb_present = %d usb_present = %d\n", chip->usb_present, usb_present); @@ -1324,18 +1359,25 @@ static int usbin_uv_handler(struct smb135x_chg *chip, u8 rt_stat) chip->usb_present = usb_present; handle_usb_removal(chip); } + + if (chip->usb_psy) { + health = rt_stat ? POWER_SUPPLY_HEALTH_OVERVOLTAGE + : POWER_SUPPLY_HEALTH_GOOD; + power_supply_set_health_state(chip->usb_psy, health); + } + return 0; } /** * src_detect_handler() - this is called when USB charger type is detected, use - * it for handling USB charger insertion + * it for handling USB charger insertion/removal * @chip: pointer to smb135x_chg chip * @rt_stat: the status bit indicating chg insertion/removal */ static int src_detect_handler(struct smb135x_chg *chip, u8 rt_stat) { - bool usb_present = is_usb_present(chip); + bool usb_present = !!rt_stat; pr_debug("chip->usb_present = %d usb_present = %d\n", chip->usb_present, usb_present); @@ -1466,6 +1508,7 @@ static struct irq_handler_info handlers[] = { }, { .name = "usbin_ov", + .smb_irq = usbin_ov_handler, }, { .name = "dcin_uv", @@ -1473,6 +1516,7 @@ static struct irq_handler_info handlers[] = { }, { .name = "dcin_ov", + .smb_irq = dcin_ov_handler, }, }, }, @@ -1774,6 +1818,11 @@ static int force_rechg_set(void *data, u64 val) int rc; struct smb135x_chg *chip = data; + if (!chip->chg_enabled) { + pr_debug("Charging Disabled force recharge not allowed\n"); + return -EINVAL; + } + rc = smb135x_masked_write(chip, CFG_14_REG, EN_CHG_INHIBIT_BIT, 0); if (rc) dev_err(chip->dev, @@ -1883,8 +1932,14 @@ static int determine_initial_status(struct smb135x_chg *chip) if (reg & IRQ_C_TERM_BIT) chip->chg_done_batt_full = true; - chip->usb_present = is_usb_present(chip); - chip->dc_present = is_dc_present(chip); + rc = smb135x_read(chip, IRQ_E_REG, ®); + if (rc < 0) { + dev_err(chip->dev, "Couldn't read irq E rc = %d\n", rc); + return rc; + } + chip->usb_present = !(reg & IRQ_E_USB_OV_BIT) + && !(reg & IRQ_E_USB_UV_BIT); + chip->dc_present = !(reg & IRQ_E_DC_OV_BIT) && !(reg & IRQ_E_DC_UV_BIT); if (chip->usb_present) handle_usb_insertion(chip); @@ -2510,7 +2565,8 @@ static int smb135x_charger_probe(struct i2c_client *client, "Couldn't create count debug file rc = %d\n", rc); - ent = debugfs_create_file("force_recharge", S_IFREG | S_IRUGO, + ent = debugfs_create_file("force_recharge", + S_IFREG | S_IWUSR | S_IRUGO, chip->debug_root, chip, &force_rechg_ops); if (!ent) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 25ed1fc7e226..179c87fb892d 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -544,6 +544,17 @@ config REGULATOR_STUB Clients can use the real regulator device names with proper constraint checking while the real driver is being developed. +config REGULATOR_RPM_SMD + bool "RPM SMD regulator driver" + depends on OF + depends on MSM_RPM_SMD + help + Compile in support for the RPM SMD regulator driver which is used for + setting voltages and other parameters of the various power rails + supplied by some Qualcomm PMICs. The RPM SMD regulator driver should + be used on systems which contain an RPM which communicates with the + application processor over SMD. + config REGULATOR_QPNP depends on SPMI depends on OF_SPMI diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 064ea0a6161d..5eb98aca1bcd 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o +obj-$(CONFIG_REGULATOR_RPM_SMD) += rpm-smd-regulator.o obj-$(CONFIG_REGULATOR_QPNP) += qpnp-regulator.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/qpnp-regulator.c b/drivers/regulator/qpnp-regulator.c index 740732f42287..2487bd2f6df3 100644 --- a/drivers/regulator/qpnp-regulator.c +++ b/drivers/regulator/qpnp-regulator.c @@ -1105,7 +1105,7 @@ static irqreturn_t qpnp_regulator_vs_ocp_isr(int irq, void *data) return IRQ_HANDLED; } -static const char const *qpnp_print_actions[] = { +static const char * const qpnp_print_actions[] = { [QPNP_REGULATOR_ACTION_INIT] = "initial ", [QPNP_REGULATOR_ACTION_ENABLE] = "enable ", [QPNP_REGULATOR_ACTION_DISABLE] = "disable ", diff --git a/drivers/regulator/rpm-smd-regulator.c b/drivers/regulator/rpm-smd-regulator.c new file mode 100644 index 000000000000..ffc4776b799e --- /dev/null +++ b/drivers/regulator/rpm-smd-regulator.c @@ -0,0 +1,1722 @@ +/* Copyright (c) 2012-2013, The Linux Foundation. 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 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/module.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/regulator/rpm-smd-regulator.h> +#include <mach/rpm-smd.h> + +/* Debug Definitions */ + +enum { + RPM_VREG_DEBUG_REQUEST = BIT(0), + RPM_VREG_DEBUG_FULL_REQUEST = BIT(1), + RPM_VREG_DEBUG_DUPLICATE = BIT(2), +}; + +static int rpm_vreg_debug_mask; +module_param_named( + debug_mask, rpm_vreg_debug_mask, int, S_IRUSR | S_IWUSR +); + +#define vreg_err(req, fmt, ...) \ + pr_err("%s: " fmt, req->rdesc.name, ##__VA_ARGS__) + +/* RPM regulator request types */ +enum rpm_regulator_type { + RPM_REGULATOR_TYPE_LDO, + RPM_REGULATOR_TYPE_SMPS, + RPM_REGULATOR_TYPE_VS, + RPM_REGULATOR_TYPE_NCP, + RPM_REGULATOR_TYPE_MAX, +}; + +/* RPM resource parameters */ +enum rpm_regulator_param_index { + RPM_REGULATOR_PARAM_ENABLE, + RPM_REGULATOR_PARAM_VOLTAGE, + RPM_REGULATOR_PARAM_CURRENT, + RPM_REGULATOR_PARAM_MODE_LDO, + RPM_REGULATOR_PARAM_MODE_SMPS, + RPM_REGULATOR_PARAM_PIN_CTRL_ENABLE, + RPM_REGULATOR_PARAM_PIN_CTRL_MODE, + RPM_REGULATOR_PARAM_FREQUENCY, + RPM_REGULATOR_PARAM_HEAD_ROOM, + RPM_REGULATOR_PARAM_QUIET_MODE, + RPM_REGULATOR_PARAM_FREQ_REASON, + RPM_REGULATOR_PARAM_CORNER, + RPM_REGULATOR_PARAM_BYPASS, + RPM_REGULATOR_PARAM_FLOOR_CORNER, + RPM_REGULATOR_PARAM_MAX, +}; + +enum rpm_regulator_smps_mode { + RPM_REGULATOR_SMPS_MODE_AUTO = 0, + RPM_REGULATOR_SMPS_MODE_IPEAK = 1, + RPM_REGULATOR_SMPS_MODE_PWM = 2, +}; + +enum rpm_regulator_ldo_mode { + RPM_REGULATOR_LDO_MODE_IPEAK = 0, + RPM_REGULATOR_LDO_MODE_HPM = 1, +}; + +#define RPM_SET_CONFIG_ACTIVE BIT(0) +#define RPM_SET_CONFIG_SLEEP BIT(1) +#define RPM_SET_CONFIG_BOTH (RPM_SET_CONFIG_ACTIVE \ + | RPM_SET_CONFIG_SLEEP) +struct rpm_regulator_param { + char *name; + char *property_name; + u32 key; + u32 min; + u32 max; + u32 supported_regulator_types; +}; + +#define PARAM(_idx, _support_ldo, _support_smps, _support_vs, _support_ncp, \ + _name, _min, _max, _property_name) \ + [RPM_REGULATOR_PARAM_##_idx] = { \ + .name = _name, \ + .property_name = _property_name, \ + .min = _min, \ + .max = _max, \ + .supported_regulator_types = \ + _support_ldo << RPM_REGULATOR_TYPE_LDO | \ + _support_smps << RPM_REGULATOR_TYPE_SMPS | \ + _support_vs << RPM_REGULATOR_TYPE_VS | \ + _support_ncp << RPM_REGULATOR_TYPE_NCP, \ + } + +static struct rpm_regulator_param params[RPM_REGULATOR_PARAM_MAX] = { + /* ID LDO SMPS VS NCP name min max property-name */ + PARAM(ENABLE, 1, 1, 1, 1, "swen", 0, 1, "qcom,init-enable"), + PARAM(VOLTAGE, 1, 1, 0, 1, "uv", 0, 0x7FFFFFF, "qcom,init-voltage"), + PARAM(CURRENT, 1, 1, 0, 0, "ma", 0, 0x1FFF, "qcom,init-current"), + PARAM(MODE_LDO, 1, 0, 0, 0, "lsmd", 0, 1, "qcom,init-ldo-mode"), + PARAM(MODE_SMPS, 0, 1, 0, 0, "ssmd", 0, 2, "qcom,init-smps-mode"), + PARAM(PIN_CTRL_ENABLE, 1, 1, 1, 0, "pcen", 0, 0xF, "qcom,init-pin-ctrl-enable"), + PARAM(PIN_CTRL_MODE, 1, 1, 1, 0, "pcmd", 0, 0x1F, "qcom,init-pin-ctrl-mode"), + PARAM(FREQUENCY, 0, 1, 0, 1, "freq", 0, 31, "qcom,init-frequency"), + PARAM(HEAD_ROOM, 1, 0, 0, 1, "hr", 0, 0x7FFFFFFF, "qcom,init-head-room"), + PARAM(QUIET_MODE, 0, 1, 0, 0, "qm", 0, 2, "qcom,init-quiet-mode"), + PARAM(FREQ_REASON, 0, 1, 0, 1, "resn", 0, 8, "qcom,init-freq-reason"), + PARAM(CORNER, 1, 1, 0, 0, "corn", 0, 6, "qcom,init-voltage-corner"), + PARAM(BYPASS, 1, 0, 0, 0, "bypa", 0, 1, "qcom,init-disallow-bypass"), + PARAM(FLOOR_CORNER, 1, 1, 0, 0, "vfc", 0, 6, "qcom,init-voltage-floor-corner"), +}; + +struct rpm_regulator_mode_map { + int ldo_mode; + int smps_mode; +}; + +static struct rpm_regulator_mode_map mode_mapping[] = { + [RPM_REGULATOR_MODE_AUTO] + = {-1, RPM_REGULATOR_SMPS_MODE_AUTO}, + [RPM_REGULATOR_MODE_IPEAK] + = {RPM_REGULATOR_LDO_MODE_IPEAK, RPM_REGULATOR_SMPS_MODE_IPEAK}, + [RPM_REGULATOR_MODE_HPM] + = {RPM_REGULATOR_LDO_MODE_HPM, RPM_REGULATOR_SMPS_MODE_PWM}, +}; + +struct rpm_vreg_request { + u32 param[RPM_REGULATOR_PARAM_MAX]; + u32 valid; + u32 modified; +}; + +struct rpm_vreg { + struct rpm_vreg_request aggr_req_active; + struct rpm_vreg_request aggr_req_sleep; + struct list_head reg_list; + const char *resource_name; + u32 resource_id; + bool allow_atomic; + int regulator_type; + int hpm_min_load; + int enable_time; + spinlock_t slock; + struct mutex mlock; + unsigned long flags; + bool sleep_request_sent; + bool apps_only; + struct msm_rpm_request *handle_active; + struct msm_rpm_request *handle_sleep; +}; + +struct rpm_regulator { + struct regulator_desc rdesc; + struct regulator_dev *rdev; + struct rpm_vreg *rpm_vreg; + struct list_head list; + bool set_active; + bool set_sleep; + bool always_send_voltage; + bool always_send_current; + struct rpm_vreg_request req; + int system_load; + int min_uV; + int max_uV; +}; + +/* + * This voltage in uV is returned by get_voltage functions when there is no way + * to determine the current voltage level. It is needed because the regulator + * framework treats a 0 uV voltage as an error. + */ +#define VOLTAGE_UNKNOWN 1 + +/* + * Regulator requests sent in the active set take effect immediately. Requests + * sent in the sleep set take effect when the Apps processor transitions into + * RPM assisted power collapse. For any given regulator, if an active set + * request is present, but not a sleep set request, then the active set request + * is used at all times, even when the Apps processor is power collapsed. + * + * The rpm-regulator-smd takes advantage of this default usage of the active set + * request by only sending a sleep set request if it differs from the + * corresponding active set request. + */ +#define RPM_SET_ACTIVE MSM_RPM_CTX_ACTIVE_SET +#define RPM_SET_SLEEP MSM_RPM_CTX_SLEEP_SET + +static u32 rpm_vreg_string_to_int(const u8 *str) +{ + int i, len; + u32 output = 0; + + len = strnlen(str, sizeof(u32)); + for (i = 0; i < len; i++) + output |= str[i] << (i * 8); + + return output; +} + +static inline void rpm_vreg_lock(struct rpm_vreg *rpm_vreg) +{ + if (rpm_vreg->allow_atomic) + spin_lock_irqsave(&rpm_vreg->slock, rpm_vreg->flags); + else + mutex_lock(&rpm_vreg->mlock); +} + +static inline void rpm_vreg_unlock(struct rpm_vreg *rpm_vreg) +{ + if (rpm_vreg->allow_atomic) + spin_unlock_irqrestore(&rpm_vreg->slock, rpm_vreg->flags); + else + mutex_unlock(&rpm_vreg->mlock); +} + +static inline bool rpm_vreg_active_or_sleep_enabled(struct rpm_vreg *rpm_vreg) +{ + return (rpm_vreg->aggr_req_active.param[RPM_REGULATOR_PARAM_ENABLE] + && (rpm_vreg->aggr_req_active.valid + & BIT(RPM_REGULATOR_PARAM_ENABLE))) + || ((rpm_vreg->aggr_req_sleep.param[RPM_REGULATOR_PARAM_ENABLE]) + && (rpm_vreg->aggr_req_sleep.valid + & BIT(RPM_REGULATOR_PARAM_ENABLE))); +} + +static inline bool rpm_vreg_shared_active_or_sleep_enabled_valid + (struct rpm_vreg *rpm_vreg) +{ + return !rpm_vreg->apps_only && + ((rpm_vreg->aggr_req_active.valid + & BIT(RPM_REGULATOR_PARAM_ENABLE)) + || (rpm_vreg->aggr_req_sleep.valid + & BIT(RPM_REGULATOR_PARAM_ENABLE))); +} + +/* + * This is used when voting for LPM or HPM by subtracting or adding to the + * hpm_min_load of a regulator. It has units of uA. + */ +#define LOAD_THRESHOLD_STEP 1000 + +static inline int rpm_vreg_hpm_min_uA(struct rpm_vreg *rpm_vreg) +{ + return rpm_vreg->hpm_min_load; +} + +static inline int rpm_vreg_lpm_max_uA(struct rpm_vreg *rpm_vreg) +{ + return rpm_vreg->hpm_min_load - LOAD_THRESHOLD_STEP; +} + +#define MICRO_TO_MILLI(uV) ((uV) / 1000) +#define MILLI_TO_MICRO(uV) ((uV) * 1000) + +#define DEBUG_PRINT_BUFFER_SIZE 512 +#define REQ_SENT 0 +#define REQ_PREV 1 +#define REQ_CACHED 2 +#define REQ_TYPES 3 + +static void rpm_regulator_req(struct rpm_regulator *regulator, int set, + bool sent) +{ + char buf[DEBUG_PRINT_BUFFER_SIZE]; + size_t buflen = DEBUG_PRINT_BUFFER_SIZE; + struct rpm_vreg *rpm_vreg = regulator->rpm_vreg; + struct rpm_vreg_request *aggr; + bool first; + u32 mask[REQ_TYPES] = {0, 0, 0}; + const char *req_names[REQ_TYPES] = {"sent", "prev", "cached"}; + int pos = 0; + int i, j; + + aggr = (set == RPM_SET_ACTIVE) + ? &rpm_vreg->aggr_req_active : &rpm_vreg->aggr_req_sleep; + + if (rpm_vreg_debug_mask & RPM_VREG_DEBUG_DUPLICATE) { + mask[REQ_SENT] = aggr->modified; + mask[REQ_PREV] = aggr->valid & ~aggr->modified; + } else if (sent + && (rpm_vreg_debug_mask & RPM_VREG_DEBUG_FULL_REQUEST)) { + mask[REQ_SENT] = aggr->modified; + mask[REQ_PREV] = aggr->valid & ~aggr->modified; + } else if (sent && (rpm_vreg_debug_mask & RPM_VREG_DEBUG_REQUEST)) { + mask[REQ_SENT] = aggr->modified; + } + + if (!(mask[REQ_SENT] | mask[REQ_PREV])) + return; + + if (set == RPM_SET_SLEEP && !rpm_vreg->sleep_request_sent) { + mask[REQ_CACHED] = mask[REQ_SENT] | mask[REQ_PREV]; + mask[REQ_SENT] = 0; + mask[REQ_PREV] = 0; + } + + pos += scnprintf(buf + pos, buflen - pos, "%s%s: ", + KERN_INFO, __func__); + + pos += scnprintf(buf + pos, buflen - pos, "%s %u (%s): s=%s", + rpm_vreg->resource_name, rpm_vreg->resource_id, + regulator->rdesc.name, + (set == RPM_SET_ACTIVE ? "act" : "slp")); + + for (i = 0; i < REQ_TYPES; i++) { + if (mask[i]) + pos += scnprintf(buf + pos, buflen - pos, "; %s: ", + req_names[i]); + + first = true; + for (j = 0; j < RPM_REGULATOR_PARAM_MAX; j++) { + if (mask[i] & BIT(j)) { + pos += scnprintf(buf + pos, buflen - pos, + "%s%s=%u", (first ? "" : ", "), + params[j].name, aggr->param[j]); + first = false; + } + } + } + + pos += scnprintf(buf + pos, buflen - pos, "\n"); + printk(buf); +} + +#define RPM_VREG_SET_PARAM(_regulator, _param, _val) \ +{ \ + (_regulator)->req.param[RPM_REGULATOR_PARAM_##_param] = _val; \ + (_regulator)->req.modified |= BIT(RPM_REGULATOR_PARAM_##_param); \ +} \ + +static int rpm_vreg_add_kvp_to_request(struct rpm_vreg *rpm_vreg, + const u32 *param, int idx, u32 set) +{ + struct msm_rpm_request *handle; + + handle = (set == RPM_SET_ACTIVE ? rpm_vreg->handle_active + : rpm_vreg->handle_sleep); + + if (rpm_vreg->allow_atomic) + return msm_rpm_add_kvp_data_noirq(handle, params[idx].key, + (u8 *)¶m[idx], 4); + else + return msm_rpm_add_kvp_data(handle, params[idx].key, + (u8 *)¶m[idx], 4); +} + +static void rpm_vreg_check_modified_requests(const u32 *prev_param, + const u32 *param, u32 prev_valid, u32 *modified) +{ + u32 value_changed = 0; + int i; + + for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) { + if (param[i] != prev_param[i]) + value_changed |= BIT(i); + } + + /* + * Only keep bits that are for changed parameters or previously + * invalid parameters. + */ + *modified &= value_changed | ~prev_valid; +} + +static int rpm_vreg_add_modified_requests(struct rpm_regulator *regulator, + u32 set, const u32 *param, u32 modified) +{ + struct rpm_vreg *rpm_vreg = regulator->rpm_vreg; + int rc = 0; + int i; + + for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) { + /* Only send requests for modified parameters. */ + if (modified & BIT(i)) { + rc = rpm_vreg_add_kvp_to_request(rpm_vreg, param, i, + set); + if (rc) { + vreg_err(regulator, + "add KVP failed: %s %u; %s, rc=%d\n", + rpm_vreg->resource_name, + rpm_vreg->resource_id, params[i].name, + rc); + return rc; + } + } + } + + return rc; +} + +static int rpm_vreg_send_request(struct rpm_regulator *regulator, u32 set) +{ + struct rpm_vreg *rpm_vreg = regulator->rpm_vreg; + struct msm_rpm_request *handle + = (set == RPM_SET_ACTIVE ? rpm_vreg->handle_active + : rpm_vreg->handle_sleep); + int rc; + + if (rpm_vreg->allow_atomic) + rc = msm_rpm_wait_for_ack_noirq(msm_rpm_send_request_noirq( + handle)); + else + rc = msm_rpm_wait_for_ack(msm_rpm_send_request(handle)); + + if (rc) + vreg_err(regulator, + "msm rpm send failed: %s %u; set=%s, rc=%d\n", + rpm_vreg->resource_name, + rpm_vreg->resource_id, + (set == RPM_SET_ACTIVE ? "act" : "slp"), rc); + + return rc; +} + +#define RPM_VREG_AGGR_MIN(_idx, _param_aggr, _param_reg) \ +{ \ + _param_aggr[RPM_REGULATOR_PARAM_##_idx] \ + = min(_param_aggr[RPM_REGULATOR_PARAM_##_idx], \ + _param_reg[RPM_REGULATOR_PARAM_##_idx]); \ +} + +#define RPM_VREG_AGGR_MAX(_idx, _param_aggr, _param_reg) \ +{ \ + _param_aggr[RPM_REGULATOR_PARAM_##_idx] \ + = max(_param_aggr[RPM_REGULATOR_PARAM_##_idx], \ + _param_reg[RPM_REGULATOR_PARAM_##_idx]); \ +} + +#define RPM_VREG_AGGR_SUM(_idx, _param_aggr, _param_reg) \ +{ \ + _param_aggr[RPM_REGULATOR_PARAM_##_idx] \ + += _param_reg[RPM_REGULATOR_PARAM_##_idx]; \ +} + +#define RPM_VREG_AGGR_OR(_idx, _param_aggr, _param_reg) \ +{ \ + _param_aggr[RPM_REGULATOR_PARAM_##_idx] \ + |= _param_reg[RPM_REGULATOR_PARAM_##_idx]; \ +} + +/* + * Aggregation is performed on each parameter based on the way that the RPM + * aggregates that type internally between RPM masters. + */ +static void rpm_vreg_aggregate_params(u32 *param_aggr, const u32 *param_reg) +{ + RPM_VREG_AGGR_MAX(ENABLE, param_aggr, param_reg); + RPM_VREG_AGGR_MAX(VOLTAGE, param_aggr, param_reg); + RPM_VREG_AGGR_SUM(CURRENT, param_aggr, param_reg); + RPM_VREG_AGGR_MAX(MODE_LDO, param_aggr, param_reg); + RPM_VREG_AGGR_MAX(MODE_SMPS, param_aggr, param_reg); + RPM_VREG_AGGR_OR(PIN_CTRL_ENABLE, param_aggr, param_reg); + RPM_VREG_AGGR_OR(PIN_CTRL_MODE, param_aggr, param_reg); + RPM_VREG_AGGR_MIN(FREQUENCY, param_aggr, param_reg); + RPM_VREG_AGGR_MAX(HEAD_ROOM, param_aggr, param_reg); + RPM_VREG_AGGR_MAX(QUIET_MODE, param_aggr, param_reg); + RPM_VREG_AGGR_MAX(FREQ_REASON, param_aggr, param_reg); + RPM_VREG_AGGR_MAX(CORNER, param_aggr, param_reg); + RPM_VREG_AGGR_MAX(BYPASS, param_aggr, param_reg); + RPM_VREG_AGGR_MAX(FLOOR_CORNER, param_aggr, param_reg); +} + +static int rpm_vreg_aggregate_requests(struct rpm_regulator *regulator) +{ + struct rpm_vreg *rpm_vreg = regulator->rpm_vreg; + u32 param_active[RPM_REGULATOR_PARAM_MAX]; + u32 param_sleep[RPM_REGULATOR_PARAM_MAX]; + u32 modified_active, modified_sleep; + struct rpm_regulator *reg; + bool sleep_set_differs = false; + bool send_active = false; + bool send_sleep = false; + int rc = 0; + int i; + + memset(param_active, 0, sizeof(param_active)); + memset(param_sleep, 0, sizeof(param_sleep)); + modified_active = rpm_vreg->aggr_req_active.modified; + modified_sleep = rpm_vreg->aggr_req_sleep.modified; + + /* + * Aggregate all of the requests for this regulator in both active + * and sleep sets. + */ + list_for_each_entry(reg, &rpm_vreg->reg_list, list) { + if (reg->set_active) { + rpm_vreg_aggregate_params(param_active, reg->req.param); + modified_active |= reg->req.modified; + } + if (reg->set_sleep) { + rpm_vreg_aggregate_params(param_sleep, reg->req.param); + modified_sleep |= reg->req.modified; + } + } + + /* + * Check if the aggregated sleep set parameter values differ from the + * aggregated active set parameter values. + */ + if (!rpm_vreg->sleep_request_sent) { + for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) { + if ((param_active[i] != param_sleep[i]) + && (modified_sleep & BIT(i))) { + sleep_set_differs = true; + break; + } + } + } + + /* Add KVPs to the active set RPM request if they have new values. */ + rpm_vreg_check_modified_requests(rpm_vreg->aggr_req_active.param, + param_active, rpm_vreg->aggr_req_active.valid, + &modified_active); + rc = rpm_vreg_add_modified_requests(regulator, RPM_SET_ACTIVE, + param_active, modified_active); + if (rc) + return rc; + send_active = modified_active; + + /* + * Sleep set configurations are only sent if they differ from the + * active set values. This is because the active set values will take + * effect during rpm assisted power collapse in the absence of sleep set + * values. + * + * However, once a sleep set request is sent for a given regulator, + * additional sleep set requests must be sent in the future even if they + * match the corresponding active set requests. + */ + if (rpm_vreg->sleep_request_sent || sleep_set_differs) { + /* Add KVPs to the sleep set RPM request if they are new. */ + rpm_vreg_check_modified_requests(rpm_vreg->aggr_req_sleep.param, + param_sleep, rpm_vreg->aggr_req_sleep.valid, + &modified_sleep); + rc = rpm_vreg_add_modified_requests(regulator, RPM_SET_SLEEP, + param_sleep, modified_sleep); + if (rc) + return rc; + send_sleep = modified_sleep; + } + + /* Send active set request to the RPM if it contains new KVPs. */ + if (send_active) { + rc = rpm_vreg_send_request(regulator, RPM_SET_ACTIVE); + if (rc) + return rc; + rpm_vreg->aggr_req_active.valid |= modified_active; + } + /* Store the results of the aggregation. */ + rpm_vreg->aggr_req_active.modified = modified_active; + memcpy(rpm_vreg->aggr_req_active.param, param_active, + sizeof(param_active)); + + /* Handle debug printing of the active set request. */ + rpm_regulator_req(regulator, RPM_SET_ACTIVE, send_active); + if (send_active) + rpm_vreg->aggr_req_active.modified = 0; + + /* Send sleep set request to the RPM if it contains new KVPs. */ + if (send_sleep) { + rc = rpm_vreg_send_request(regulator, RPM_SET_SLEEP); + if (rc) + return rc; + else + rpm_vreg->sleep_request_sent = true; + rpm_vreg->aggr_req_sleep.valid |= modified_sleep; + } + /* Store the results of the aggregation. */ + rpm_vreg->aggr_req_sleep.modified = modified_sleep; + memcpy(rpm_vreg->aggr_req_sleep.param, param_sleep, + sizeof(param_sleep)); + + /* Handle debug printing of the sleep set request. */ + rpm_regulator_req(regulator, RPM_SET_SLEEP, send_sleep); + if (send_sleep) + rpm_vreg->aggr_req_sleep.modified = 0; + + /* + * Loop over all requests for this regulator to update the valid and + * modified values for use in future aggregation. + */ + list_for_each_entry(reg, &rpm_vreg->reg_list, list) { + reg->req.valid |= reg->req.modified; + reg->req.modified = 0; + } + + return rc; +} + +static int rpm_vreg_is_enabled(struct regulator_dev *rdev) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + + return reg->req.param[RPM_REGULATOR_PARAM_ENABLE]; +} + +static int rpm_vreg_enable(struct regulator_dev *rdev) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + int rc; + u32 prev_enable; + + rpm_vreg_lock(reg->rpm_vreg); + + prev_enable = reg->req.param[RPM_REGULATOR_PARAM_ENABLE]; + RPM_VREG_SET_PARAM(reg, ENABLE, 1); + rc = rpm_vreg_aggregate_requests(reg); + if (rc) { + vreg_err(reg, "enable failed, rc=%d", rc); + RPM_VREG_SET_PARAM(reg, ENABLE, prev_enable); + } + + rpm_vreg_unlock(reg->rpm_vreg); + + return rc; +} + +static int rpm_vreg_disable(struct regulator_dev *rdev) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + int rc; + u32 prev_enable; + + rpm_vreg_lock(reg->rpm_vreg); + + prev_enable = reg->req.param[RPM_REGULATOR_PARAM_ENABLE]; + RPM_VREG_SET_PARAM(reg, ENABLE, 0); + rc = rpm_vreg_aggregate_requests(reg); + if (rc) { + vreg_err(reg, "enable failed, rc=%d", rc); + RPM_VREG_SET_PARAM(reg, ENABLE, prev_enable); + } + + rpm_vreg_unlock(reg->rpm_vreg); + + return rc; +} + +static int rpm_vreg_set_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV, unsigned *selector) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + int rc = 0; + u32 prev_voltage; + + rpm_vreg_lock(reg->rpm_vreg); + + prev_voltage = reg->req.param[RPM_REGULATOR_PARAM_VOLTAGE]; + RPM_VREG_SET_PARAM(reg, VOLTAGE, min_uV); + + /* + * Only send a new voltage if the regulator is currently enabled or + * if the regulator has been configured to always send voltage updates. + */ + if (reg->always_send_voltage + || rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg) + || rpm_vreg_shared_active_or_sleep_enabled_valid(reg->rpm_vreg)) + rc = rpm_vreg_aggregate_requests(reg); + + if (rc) { + vreg_err(reg, "set voltage failed, rc=%d", rc); + RPM_VREG_SET_PARAM(reg, VOLTAGE, prev_voltage); + } + + rpm_vreg_unlock(reg->rpm_vreg); + + return rc; +} + +static int rpm_vreg_get_voltage(struct regulator_dev *rdev) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + int uV; + + uV = reg->req.param[RPM_REGULATOR_PARAM_VOLTAGE]; + if (uV == 0) + uV = VOLTAGE_UNKNOWN; + + return uV; +} + +static int rpm_vreg_set_voltage_corner(struct regulator_dev *rdev, int min_uV, + int max_uV, unsigned *selector) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + int rc = 0; + int corner; + u32 prev_corner; + + /* + * Translate from values which work as inputs in the + * regulator_set_voltage function to the actual corner values + * sent to the RPM. + */ + corner = min_uV - RPM_REGULATOR_CORNER_NONE; + + if (corner < params[RPM_REGULATOR_PARAM_CORNER].min + || corner > params[RPM_REGULATOR_PARAM_CORNER].max) { + vreg_err(reg, "corner=%d is not within allowed range: [%u, %u]\n", + corner, params[RPM_REGULATOR_PARAM_CORNER].min, + params[RPM_REGULATOR_PARAM_CORNER].max); + return -EINVAL; + } + + rpm_vreg_lock(reg->rpm_vreg); + + prev_corner = reg->req.param[RPM_REGULATOR_PARAM_CORNER]; + RPM_VREG_SET_PARAM(reg, CORNER, corner); + + /* + * Only send a new voltage corner if the regulator is currently enabled + * or if the regulator has been configured to always send voltage + * updates. + */ + if (reg->always_send_voltage + || rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg) + || rpm_vreg_shared_active_or_sleep_enabled_valid(reg->rpm_vreg)) + rc = rpm_vreg_aggregate_requests(reg); + + if (rc) { + vreg_err(reg, "set voltage corner failed, rc=%d", rc); + RPM_VREG_SET_PARAM(reg, CORNER, prev_corner); + } + + rpm_vreg_unlock(reg->rpm_vreg); + + return rc; +} + +static int rpm_vreg_get_voltage_corner(struct regulator_dev *rdev) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + + return reg->req.param[RPM_REGULATOR_PARAM_CORNER] + + RPM_REGULATOR_CORNER_NONE; +} + +static int rpm_vreg_set_voltage_floor_corner(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + int rc = 0; + int corner; + u32 prev_corner; + + /* + * Translate from values which work as inputs in the + * regulator_set_voltage function to the actual corner values + * sent to the RPM. + */ + corner = min_uV - RPM_REGULATOR_CORNER_NONE; + + if (corner < params[RPM_REGULATOR_PARAM_FLOOR_CORNER].min + || corner > params[RPM_REGULATOR_PARAM_FLOOR_CORNER].max) { + vreg_err(reg, "corner=%d is not within allowed range: [%u, %u]\n", + corner, params[RPM_REGULATOR_PARAM_FLOOR_CORNER].min, + params[RPM_REGULATOR_PARAM_FLOOR_CORNER].max); + return -EINVAL; + } + + rpm_vreg_lock(reg->rpm_vreg); + + prev_corner = reg->req.param[RPM_REGULATOR_PARAM_FLOOR_CORNER]; + RPM_VREG_SET_PARAM(reg, FLOOR_CORNER, corner); + + /* + * Only send a new voltage floor corner if the regulator is currently + * enabled or if the regulator has been configured to always send + * voltage updates. + */ + if (reg->always_send_voltage + || rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg) + || rpm_vreg_shared_active_or_sleep_enabled_valid(reg->rpm_vreg)) + rc = rpm_vreg_aggregate_requests(reg); + + if (rc) { + vreg_err(reg, "set voltage corner failed, rc=%d", rc); + RPM_VREG_SET_PARAM(reg, FLOOR_CORNER, prev_corner); + } + + rpm_vreg_unlock(reg->rpm_vreg); + + return rc; +} + +static int rpm_vreg_get_voltage_floor_corner(struct regulator_dev *rdev) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + + return reg->req.param[RPM_REGULATOR_PARAM_FLOOR_CORNER] + + RPM_REGULATOR_CORNER_NONE; +} + +static int rpm_vreg_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + int rc = 0; + u32 prev_current; + int prev_uA; + + rpm_vreg_lock(reg->rpm_vreg); + + prev_current = reg->req.param[RPM_REGULATOR_PARAM_CURRENT]; + prev_uA = MILLI_TO_MICRO(prev_current); + + if (mode == REGULATOR_MODE_NORMAL) { + /* Make sure that request current is in HPM range. */ + if (prev_uA < rpm_vreg_hpm_min_uA(reg->rpm_vreg)) + RPM_VREG_SET_PARAM(reg, CURRENT, + MICRO_TO_MILLI(rpm_vreg_hpm_min_uA(reg->rpm_vreg))); + } else if (REGULATOR_MODE_IDLE) { + /* Make sure that request current is in LPM range. */ + if (prev_uA > rpm_vreg_lpm_max_uA(reg->rpm_vreg)) + RPM_VREG_SET_PARAM(reg, CURRENT, + MICRO_TO_MILLI(rpm_vreg_lpm_max_uA(reg->rpm_vreg))); + } else { + vreg_err(reg, "invalid mode: %u\n", mode); + rpm_vreg_unlock(reg->rpm_vreg); + return -EINVAL; + } + + /* + * Only send a new load current value if the regulator is currently + * enabled or if the regulator has been configured to always send + * current updates. + */ + if (reg->always_send_current + || rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg) + || rpm_vreg_shared_active_or_sleep_enabled_valid(reg->rpm_vreg)) + rc = rpm_vreg_aggregate_requests(reg); + + if (rc) { + vreg_err(reg, "set mode failed, rc=%d", rc); + RPM_VREG_SET_PARAM(reg, CURRENT, prev_current); + } + + rpm_vreg_unlock(reg->rpm_vreg); + + return rc; +} + +static unsigned int rpm_vreg_get_mode(struct regulator_dev *rdev) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + + return (reg->req.param[RPM_REGULATOR_PARAM_CURRENT] + >= MICRO_TO_MILLI(reg->rpm_vreg->hpm_min_load)) + ? REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE; +} + +static unsigned int rpm_vreg_get_optimum_mode(struct regulator_dev *rdev, + int input_uV, int output_uV, int load_uA) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + u32 load_mA; + + load_uA += reg->system_load; + + load_mA = MICRO_TO_MILLI(load_uA); + if (load_mA > params[RPM_REGULATOR_PARAM_CURRENT].max) + load_mA = params[RPM_REGULATOR_PARAM_CURRENT].max; + + rpm_vreg_lock(reg->rpm_vreg); + RPM_VREG_SET_PARAM(reg, CURRENT, load_mA); + rpm_vreg_unlock(reg->rpm_vreg); + + return (load_uA >= reg->rpm_vreg->hpm_min_load) + ? REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE; +} + +static int rpm_vreg_enable_time(struct regulator_dev *rdev) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + + return reg->rpm_vreg->enable_time; +} + +/** + * rpm_regulator_get() - lookup and obtain a handle to an RPM regulator + * @dev: device for regulator consumer + * @supply: supply name + * + * Returns a struct rpm_regulator corresponding to the regulator producer, + * or ERR_PTR() containing errno. + * + * This function may only be called from nonatomic context. + */ +struct rpm_regulator *rpm_regulator_get(struct device *dev, const char *supply) +{ + struct rpm_regulator *framework_reg; + struct rpm_regulator *priv_reg = NULL; + struct regulator *regulator; + struct rpm_vreg *rpm_vreg; + + regulator = regulator_get(dev, supply); + if (IS_ERR(regulator)) { + pr_err("could not find regulator for: dev=%s, supply=%s, rc=%ld\n", + (dev ? dev_name(dev) : ""), (supply ? supply : ""), + PTR_ERR(regulator)); + return ERR_CAST(regulator); + } + + framework_reg = regulator_get_drvdata(regulator); + if (framework_reg == NULL) { + pr_err("regulator structure not found.\n"); + regulator_put(regulator); + return ERR_PTR(-ENODEV); + } + regulator_put(regulator); + + rpm_vreg = framework_reg->rpm_vreg; + + priv_reg = kzalloc(sizeof(struct rpm_regulator), GFP_KERNEL); + if (priv_reg == NULL) { + vreg_err(framework_reg, + "could not allocate memory for regulator\n"); + return ERR_PTR(-ENOMEM); + } + + /* + * Allocate a regulator_dev struct so that framework callback functions + * can be called from the private API functions. + */ + priv_reg->rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL); + if (priv_reg->rdev == NULL) { + vreg_err(framework_reg, + "could not allocate memory for regulator_dev\n"); + kfree(priv_reg); + return ERR_PTR(-ENOMEM); + } + priv_reg->rdev->reg_data = priv_reg; + priv_reg->rpm_vreg = rpm_vreg; + priv_reg->rdesc.name = framework_reg->rdesc.name; + priv_reg->rdesc.ops = framework_reg->rdesc.ops; + priv_reg->set_active = framework_reg->set_active; + priv_reg->set_sleep = framework_reg->set_sleep; + priv_reg->min_uV = framework_reg->min_uV; + priv_reg->max_uV = framework_reg->max_uV; + priv_reg->system_load = framework_reg->system_load; + + might_sleep_if(!rpm_vreg->allow_atomic); + rpm_vreg_lock(rpm_vreg); + list_add(&priv_reg->list, &rpm_vreg->reg_list); + rpm_vreg_unlock(rpm_vreg); + + return priv_reg; +} +EXPORT_SYMBOL(rpm_regulator_get); + +static int rpm_regulator_check_input(struct rpm_regulator *regulator) +{ + if (IS_ERR_OR_NULL(regulator) || regulator->rpm_vreg == NULL) { + pr_err("invalid rpm_regulator pointer\n"); + return -EINVAL; + } + + might_sleep_if(!regulator->rpm_vreg->allow_atomic); + + return 0; +} + +/** + * rpm_regulator_put() - free the RPM regulator handle + * @regulator: RPM regulator handle + * + * Parameter reaggregation does not take place when rpm_regulator_put is called. + * Therefore, regulator enable state and voltage must be configured + * appropriately before calling rpm_regulator_put. + * + * This function may be called from either atomic or nonatomic context. If this + * function is called from atomic context, then the regulator being operated on + * must be configured via device tree with qcom,allow-atomic == 1. + */ +void rpm_regulator_put(struct rpm_regulator *regulator) +{ + struct rpm_vreg *rpm_vreg; + int rc = rpm_regulator_check_input(regulator); + + if (rc) + return; + + rpm_vreg = regulator->rpm_vreg; + + might_sleep_if(!rpm_vreg->allow_atomic); + rpm_vreg_lock(rpm_vreg); + list_del(®ulator->list); + rpm_vreg_unlock(rpm_vreg); + + kfree(regulator->rdev); + kfree(regulator); +} +EXPORT_SYMBOL(rpm_regulator_put); + +/** + * rpm_regulator_enable() - enable regulator output + * @regulator: RPM regulator handle + * + * Returns 0 on success or errno on failure. + * + * This function may be called from either atomic or nonatomic context. If this + * function is called from atomic context, then the regulator being operated on + * must be configured via device tree with qcom,allow-atomic == 1. + */ +int rpm_regulator_enable(struct rpm_regulator *regulator) +{ + int rc = rpm_regulator_check_input(regulator); + + if (rc) + return rc; + + return rpm_vreg_enable(regulator->rdev); +} +EXPORT_SYMBOL(rpm_regulator_enable); + +/** + * rpm_regulator_disable() - disable regulator output + * @regulator: RPM regulator handle + * + * Returns 0 on success or errno on failure. + * + * The enable state of the regulator is determined by aggregating the requests + * of all consumers. Therefore, it is possible that the regulator will remain + * enabled even after rpm_regulator_disable is called. + * + * This function may be called from either atomic or nonatomic context. If this + * function is called from atomic context, then the regulator being operated on + * must be configured via device tree with qcom,allow-atomic == 1. + */ +int rpm_regulator_disable(struct rpm_regulator *regulator) +{ + int rc = rpm_regulator_check_input(regulator); + + if (rc) + return rc; + + return rpm_vreg_disable(regulator->rdev); +} +EXPORT_SYMBOL(rpm_regulator_disable); + +/** + * rpm_regulator_set_voltage() - set regulator output voltage + * @regulator: RPM regulator handle + * @min_uV: minimum required voltage in uV + * @max_uV: maximum acceptable voltage in uV + * + * Sets a voltage regulator to the desired output voltage. This can be set + * while the regulator is disabled or enabled. If the regulator is enabled then + * the voltage will change to the new value immediately; otherwise, if the + * regulator is disabled, then the regulator will output at the new voltage when + * enabled. + * + * The min_uV to max_uV voltage range requested must intersect with the + * voltage constraint range configured for the regulator. + * + * Returns 0 on success or errno on failure. + * + * The final voltage value that is sent to the RPM is aggregated based upon the + * values requested by all consumers of the regulator. This corresponds to the + * maximum min_uV value. + * + * This function may be called from either atomic or nonatomic context. If this + * function is called from atomic context, then the regulator being operated on + * must be configured via device tree with qcom,allow-atomic == 1. + */ +int rpm_regulator_set_voltage(struct rpm_regulator *regulator, int min_uV, + int max_uV) +{ + int rc = rpm_regulator_check_input(regulator); + int uV = min_uV; + + if (rc) + return rc; + + if (regulator->rpm_vreg->regulator_type == RPM_REGULATOR_TYPE_VS) { + vreg_err(regulator, "unsupported regulator type: %d\n", + regulator->rpm_vreg->regulator_type); + return -EINVAL; + } + + if (min_uV > max_uV) { + vreg_err(regulator, "min_uV=%d must be less than max_uV=%d\n", + min_uV, max_uV); + return -EINVAL; + } + + if (uV < regulator->min_uV && max_uV >= regulator->min_uV) + uV = regulator->min_uV; + + if (uV < regulator->min_uV || uV > regulator->max_uV) { + vreg_err(regulator, + "request v=[%d, %d] is outside allowed v=[%d, %d]\n", + min_uV, max_uV, regulator->min_uV, regulator->max_uV); + return -EINVAL; + } + + return regulator->rdesc.ops->set_voltage(regulator->rdev, uV, uV, NULL); +} +EXPORT_SYMBOL(rpm_regulator_set_voltage); + +/** + * rpm_regulator_set_mode() - set regulator operating mode + * @regulator: RPM regulator handle + * @mode: operating mode requested for the regulator + * + * Requests that the mode of the regulator be set to the mode specified. This + * parameter is aggregated using a max function such that AUTO < IPEAK < HPM. + * + * Returns 0 on success or errno on failure. + */ +int rpm_regulator_set_mode(struct rpm_regulator *regulator, + enum rpm_regulator_mode mode) +{ + int index = 0; + u32 new_mode, prev_mode; + int rc; + + rc = rpm_regulator_check_input(regulator); + if (rc) + return rc; + + if (mode < 0 || mode >= ARRAY_SIZE(mode_mapping)) { + vreg_err(regulator, "invalid mode requested: %d\n", mode); + return -EINVAL; + } + + switch (regulator->rpm_vreg->regulator_type) { + case RPM_REGULATOR_TYPE_SMPS: + index = RPM_REGULATOR_PARAM_MODE_SMPS; + new_mode = mode_mapping[mode].smps_mode; + break; + case RPM_REGULATOR_TYPE_LDO: + index = RPM_REGULATOR_PARAM_MODE_LDO; + new_mode = mode_mapping[mode].ldo_mode; + break; + default: + vreg_err(regulator, "unsupported regulator type: %d\n", + regulator->rpm_vreg->regulator_type); + return -EINVAL; + }; + + if (new_mode < params[index].min || new_mode > params[index].max) { + vreg_err(regulator, "invalid mode requested: %d for type: %d\n", + mode, regulator->rpm_vreg->regulator_type); + return -EINVAL; + } + + rpm_vreg_lock(regulator->rpm_vreg); + + prev_mode = regulator->req.param[index]; + regulator->req.param[index] = new_mode; + regulator->req.modified |= BIT(index); + + rc = rpm_vreg_aggregate_requests(regulator); + if (rc) { + vreg_err(regulator, "set mode failed, rc=%d", rc); + regulator->req.param[index] = prev_mode; + } + + rpm_vreg_unlock(regulator->rpm_vreg); + + return rc; +} +EXPORT_SYMBOL(rpm_regulator_set_mode); + +static struct regulator_ops ldo_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = rpm_vreg_is_enabled, + .set_voltage = rpm_vreg_set_voltage, + .get_voltage = rpm_vreg_get_voltage, + .set_mode = rpm_vreg_set_mode, + .get_mode = rpm_vreg_get_mode, + .get_optimum_mode = rpm_vreg_get_optimum_mode, + .enable_time = rpm_vreg_enable_time, +}; + +static struct regulator_ops ldo_corner_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = rpm_vreg_is_enabled, + .set_voltage = rpm_vreg_set_voltage_corner, + .get_voltage = rpm_vreg_get_voltage_corner, + .set_mode = rpm_vreg_set_mode, + .get_mode = rpm_vreg_get_mode, + .get_optimum_mode = rpm_vreg_get_optimum_mode, + .enable_time = rpm_vreg_enable_time, +}; + +static struct regulator_ops ldo_floor_corner_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = rpm_vreg_is_enabled, + .set_voltage = rpm_vreg_set_voltage_floor_corner, + .get_voltage = rpm_vreg_get_voltage_floor_corner, + .set_mode = rpm_vreg_set_mode, + .get_mode = rpm_vreg_get_mode, + .get_optimum_mode = rpm_vreg_get_optimum_mode, + .enable_time = rpm_vreg_enable_time, +}; + +static struct regulator_ops smps_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = rpm_vreg_is_enabled, + .set_voltage = rpm_vreg_set_voltage, + .get_voltage = rpm_vreg_get_voltage, + .set_mode = rpm_vreg_set_mode, + .get_mode = rpm_vreg_get_mode, + .get_optimum_mode = rpm_vreg_get_optimum_mode, + .enable_time = rpm_vreg_enable_time, +}; + +static struct regulator_ops smps_corner_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = rpm_vreg_is_enabled, + .set_voltage = rpm_vreg_set_voltage_corner, + .get_voltage = rpm_vreg_get_voltage_corner, + .set_mode = rpm_vreg_set_mode, + .get_mode = rpm_vreg_get_mode, + .get_optimum_mode = rpm_vreg_get_optimum_mode, + .enable_time = rpm_vreg_enable_time, +}; + +static struct regulator_ops smps_floor_corner_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = rpm_vreg_is_enabled, + .set_voltage = rpm_vreg_set_voltage_floor_corner, + .get_voltage = rpm_vreg_get_voltage_floor_corner, + .set_mode = rpm_vreg_set_mode, + .get_mode = rpm_vreg_get_mode, + .get_optimum_mode = rpm_vreg_get_optimum_mode, + .enable_time = rpm_vreg_enable_time, +}; + +static struct regulator_ops switch_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = rpm_vreg_is_enabled, + .enable_time = rpm_vreg_enable_time, +}; + +static struct regulator_ops ncp_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = rpm_vreg_is_enabled, + .set_voltage = rpm_vreg_set_voltage, + .get_voltage = rpm_vreg_get_voltage, + .enable_time = rpm_vreg_enable_time, +}; + +static struct regulator_ops *vreg_ops[] = { + [RPM_REGULATOR_TYPE_LDO] = &ldo_ops, + [RPM_REGULATOR_TYPE_SMPS] = &smps_ops, + [RPM_REGULATOR_TYPE_VS] = &switch_ops, + [RPM_REGULATOR_TYPE_NCP] = &ncp_ops, +}; + +static int rpm_vreg_device_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rpm_regulator *reg; + struct rpm_vreg *rpm_vreg; + + reg = platform_get_drvdata(pdev); + if (reg) { + rpm_vreg = reg->rpm_vreg; + rpm_vreg_lock(rpm_vreg); + regulator_unregister(reg->rdev); + list_del(®->list); + kfree(reg); + rpm_vreg_unlock(rpm_vreg); + } else { + dev_err(dev, "%s: drvdata missing\n", __func__); + return -EINVAL; + } + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static int rpm_vreg_resource_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rpm_regulator *reg, *reg_temp; + struct rpm_vreg *rpm_vreg; + + rpm_vreg = platform_get_drvdata(pdev); + if (rpm_vreg) { + rpm_vreg_lock(rpm_vreg); + list_for_each_entry_safe(reg, reg_temp, &rpm_vreg->reg_list, + list) { + /* Only touch data for private consumers. */ + if (reg->rdev->desc == NULL) { + list_del(®->list); + kfree(reg->rdev); + kfree(reg); + } else { + dev_err(dev, "%s: not all child devices have been removed\n", + __func__); + } + } + rpm_vreg_unlock(rpm_vreg); + + msm_rpm_free_request(rpm_vreg->handle_active); + msm_rpm_free_request(rpm_vreg->handle_sleep); + + kfree(rpm_vreg); + } else { + dev_err(dev, "%s: drvdata missing\n", __func__); + return -EINVAL; + } + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +/* + * This probe is called for child rpm-regulator devices which have + * properties which are required to configure individual regulator + * framework regulators for a given RPM regulator resource. + */ +static int rpm_vreg_device_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct regulator_init_data *init_data; + struct rpm_vreg *rpm_vreg; + struct rpm_regulator *reg; + struct regulator_config reg_config = {}; + int rc = 0; + int i, regulator_type; + u32 val; + + if (!dev->of_node) { + dev_err(dev, "%s: device tree information missing\n", __func__); + return -ENODEV; + } + + if (pdev->dev.parent == NULL) { + dev_err(dev, "%s: parent device missing\n", __func__); + return -ENODEV; + } + + rpm_vreg = dev_get_drvdata(pdev->dev.parent); + if (rpm_vreg == NULL) { + dev_err(dev, "%s: rpm_vreg not found in parent device\n", + __func__); + return -ENODEV; + } + + reg = kzalloc(sizeof(struct rpm_regulator), GFP_KERNEL); + if (reg == NULL) { + dev_err(dev, "%s: could not allocate memory for reg\n", + __func__); + return -ENOMEM; + } + + regulator_type = rpm_vreg->regulator_type; + reg->rpm_vreg = rpm_vreg; + reg->rdesc.ops = vreg_ops[regulator_type]; + reg->rdesc.owner = THIS_MODULE; + reg->rdesc.type = REGULATOR_VOLTAGE; + + /* + * Switch to voltage corner regulator ops if qcom,use-voltage-corner + * is specified in the device node (SMPS and LDO only). + */ + if (of_property_read_bool(node, "qcom,use-voltage-corner")) { + if (of_property_read_bool(node, + "qcom,use-voltage-floor-corner")) { + dev_err(dev, "%s: invalid properties: both qcom,use-voltage-corner and qcom,use-voltage-floor-corner specified\n", + __func__); + goto fail_free_reg; + } + + if (regulator_type == RPM_REGULATOR_TYPE_SMPS) + reg->rdesc.ops = &smps_corner_ops; + else if (regulator_type == RPM_REGULATOR_TYPE_LDO) + reg->rdesc.ops = &ldo_corner_ops; + } else if (of_property_read_bool(node, + "qcom,use-voltage-floor-corner")) { + if (regulator_type == RPM_REGULATOR_TYPE_SMPS) + reg->rdesc.ops = &smps_floor_corner_ops; + else if (regulator_type == RPM_REGULATOR_TYPE_LDO) + reg->rdesc.ops = &ldo_floor_corner_ops; + } + + reg->always_send_voltage + = of_property_read_bool(node, "qcom,always-send-voltage"); + reg->always_send_current + = of_property_read_bool(node, "qcom,always-send-current"); + + if (regulator_type == RPM_REGULATOR_TYPE_VS) + reg->rdesc.n_voltages = 0; + else + reg->rdesc.n_voltages = 2; + + rc = of_property_read_u32(node, "qcom,set", &val); + if (rc) { + dev_err(dev, "%s: sleep set and/or active set must be configured via qcom,set property, rc=%d\n", + __func__, rc); + goto fail_free_reg; + } else if (!(val & RPM_SET_CONFIG_BOTH)) { + dev_err(dev, "%s: qcom,set=%u property is invalid\n", __func__, + val); + rc = -EINVAL; + goto fail_free_reg; + } + + reg->set_active = !!(val & RPM_SET_CONFIG_ACTIVE); + reg->set_sleep = !!(val & RPM_SET_CONFIG_SLEEP); + + init_data = of_get_regulator_init_data(dev, node); + if (init_data == NULL) { + dev_err(dev, "%s: unable to allocate memory\n", __func__); + rc = -ENOMEM; + goto fail_free_reg; + } + if (init_data->constraints.name == NULL) { + dev_err(dev, "%s: regulator name not specified\n", __func__); + rc = -EINVAL; + goto fail_free_reg; + } + + init_data->constraints.input_uV = init_data->constraints.max_uV; + + if (of_get_property(node, "parent-supply", NULL)) + init_data->supply_regulator = "parent"; + + /* + * Fill in ops and mode masks based on callbacks specified for + * this type of regulator. + */ + if (reg->rdesc.ops->enable) + init_data->constraints.valid_ops_mask + |= REGULATOR_CHANGE_STATUS; + if (reg->rdesc.ops->get_voltage) + init_data->constraints.valid_ops_mask + |= REGULATOR_CHANGE_VOLTAGE; + if (reg->rdesc.ops->get_mode) { + init_data->constraints.valid_ops_mask + |= REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_DRMS; + init_data->constraints.valid_modes_mask + |= REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE; + } + + reg->rdesc.name = init_data->constraints.name; + reg->min_uV = init_data->constraints.min_uV; + reg->max_uV = init_data->constraints.max_uV; + + /* Initialize the param array based on optional properties. */ + for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) { + rc = of_property_read_u32(node, params[i].property_name, &val); + if (rc == 0) { + if (params[i].supported_regulator_types + & BIT(regulator_type)) { + if (val < params[i].min + || val > params[i].max) { + pr_warn("%s: device tree property: %s=%u is outsided allowed range [%u, %u]\n", + reg->rdesc.name, + params[i].property_name, val, + params[i].min, params[i].max); + continue; + } + reg->req.param[i] = val; + reg->req.modified |= BIT(i); + } else { + pr_warn("%s: regulator type=%d does not support device tree property: %s\n", + reg->rdesc.name, regulator_type, + params[i].property_name); + } + } + } + + of_property_read_u32(node, "qcom,system-load", ®->system_load); + + rpm_vreg_lock(rpm_vreg); + list_add(®->list, &rpm_vreg->reg_list); + rpm_vreg_unlock(rpm_vreg); + + reg_config.dev = dev; + reg_config.init_data = init_data; + reg_config.of_node = node; + reg_config.driver_data = reg; + reg->rdev = regulator_register(®->rdesc, ®_config); + if (IS_ERR(reg->rdev)) { + rc = PTR_ERR(reg->rdev); + reg->rdev = NULL; + pr_err("regulator_register failed: %s, rc=%d\n", + reg->rdesc.name, rc); + goto fail_remove_from_list; + } + + platform_set_drvdata(pdev, reg); + + pr_debug("successfully probed: %s\n", reg->rdesc.name); + + return 0; + +fail_remove_from_list: + rpm_vreg_lock(rpm_vreg); + list_del(®->list); + rpm_vreg_unlock(rpm_vreg); + +fail_free_reg: + kfree(reg); + return rc; +} + +/* + * This probe is called for parent rpm-regulator devices which have + * properties which are required to identify a given RPM resource. + */ +static int rpm_vreg_resource_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct rpm_vreg *rpm_vreg; + int val = 0; + u32 resource_type; + int rc; + + if (!dev->of_node) { + dev_err(dev, "%s: device tree information missing\n", __func__); + return -ENODEV; + } + + /* Create new rpm_vreg entry. */ + rpm_vreg = kzalloc(sizeof(struct rpm_vreg), GFP_KERNEL); + if (rpm_vreg == NULL) { + dev_err(dev, "%s: could not allocate memory for vreg\n", + __func__); + return -ENOMEM; + } + + /* Required device tree properties: */ + rc = of_property_read_string(node, "qcom,resource-name", + &rpm_vreg->resource_name); + if (rc) { + dev_err(dev, "%s: qcom,resource-name missing in DT node\n", + __func__); + goto fail_free_vreg; + } + resource_type = rpm_vreg_string_to_int(rpm_vreg->resource_name); + + rc = of_property_read_u32(node, "qcom,resource-id", + &rpm_vreg->resource_id); + if (rc) { + dev_err(dev, "%s: qcom,resource-id missing in DT node\n", + __func__); + goto fail_free_vreg; + } + + rc = of_property_read_u32(node, "qcom,regulator-type", + &rpm_vreg->regulator_type); + if (rc) { + dev_err(dev, "%s: qcom,regulator-type missing in DT node\n", + __func__); + goto fail_free_vreg; + } + + if ((rpm_vreg->regulator_type < 0) + || (rpm_vreg->regulator_type >= RPM_REGULATOR_TYPE_MAX)) { + dev_err(dev, "%s: invalid regulator type: %d\n", __func__, + rpm_vreg->regulator_type); + rc = -EINVAL; + goto fail_free_vreg; + } + + /* Optional device tree properties: */ + of_property_read_u32(node, "qcom,allow-atomic", &val); + rpm_vreg->allow_atomic = !!val; + of_property_read_u32(node, "qcom,enable-time", &rpm_vreg->enable_time); + of_property_read_u32(node, "qcom,hpm-min-load", + &rpm_vreg->hpm_min_load); + rpm_vreg->apps_only = of_property_read_bool(node, "qcom,apps-only"); + + rpm_vreg->handle_active = msm_rpm_create_request(RPM_SET_ACTIVE, + resource_type, rpm_vreg->resource_id, RPM_REGULATOR_PARAM_MAX); + if (rpm_vreg->handle_active == NULL + || IS_ERR(rpm_vreg->handle_active)) { + rc = PTR_ERR(rpm_vreg->handle_active); + dev_err(dev, "%s: failed to create active RPM handle, rc=%d\n", + __func__, rc); + goto fail_free_vreg; + } + + rpm_vreg->handle_sleep = msm_rpm_create_request(RPM_SET_SLEEP, + resource_type, rpm_vreg->resource_id, RPM_REGULATOR_PARAM_MAX); + if (rpm_vreg->handle_sleep == NULL || IS_ERR(rpm_vreg->handle_sleep)) { + rc = PTR_ERR(rpm_vreg->handle_sleep); + dev_err(dev, "%s: failed to create sleep RPM handle, rc=%d\n", + __func__, rc); + goto fail_free_handle_active; + } + + INIT_LIST_HEAD(&rpm_vreg->reg_list); + + if (rpm_vreg->allow_atomic) + spin_lock_init(&rpm_vreg->slock); + else + mutex_init(&rpm_vreg->mlock); + + platform_set_drvdata(pdev, rpm_vreg); + + rc = of_platform_populate(node, NULL, NULL, dev); + if (rc) { + dev_err(dev, "%s: failed to add child nodes, rc=%d\n", __func__, + rc); + goto fail_unset_drvdata; + } + + pr_debug("successfully probed: %s (%08X) %u\n", rpm_vreg->resource_name, + resource_type, rpm_vreg->resource_id); + + return rc; + +fail_unset_drvdata: + platform_set_drvdata(pdev, NULL); + msm_rpm_free_request(rpm_vreg->handle_sleep); + +fail_free_handle_active: + msm_rpm_free_request(rpm_vreg->handle_active); + +fail_free_vreg: + kfree(rpm_vreg); + + return rc; +} + +static struct of_device_id rpm_vreg_match_table_device[] = { + { .compatible = "qcom,rpm-smd-regulator", }, + {} +}; + +static struct of_device_id rpm_vreg_match_table_resource[] = { + { .compatible = "qcom,rpm-smd-regulator-resource", }, + {} +}; + +static struct platform_driver rpm_vreg_device_driver = { + .probe = rpm_vreg_device_probe, + .remove = rpm_vreg_device_remove, + .driver = { + .name = "qcom,rpm-smd-regulator", + .owner = THIS_MODULE, + .of_match_table = rpm_vreg_match_table_device, + }, +}; + +static struct platform_driver rpm_vreg_resource_driver = { + .probe = rpm_vreg_resource_probe, + .remove = rpm_vreg_resource_remove, + .driver = { + .name = "qcom,rpm-smd-regulator-resource", + .owner = THIS_MODULE, + .of_match_table = rpm_vreg_match_table_resource, + }, +}; + +/** + * rpm_smd_regulator_driver_init() - initialize the RPM SMD regulator drivers + * + * This function registers the RPM SMD regulator platform drivers. + * + * Returns 0 on success or errno on failure. + */ +int __init rpm_smd_regulator_driver_init(void) +{ + static bool initialized; + int i, rc; + + if (initialized) + return 0; + else + initialized = true; + + /* Store parameter string names as integers */ + for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) + params[i].key = rpm_vreg_string_to_int(params[i].name); + + rc = platform_driver_register(&rpm_vreg_device_driver); + if (rc) + return rc; + + return platform_driver_register(&rpm_vreg_resource_driver); +} +EXPORT_SYMBOL(rpm_smd_regulator_driver_init); + +static void __exit rpm_vreg_exit(void) +{ + platform_driver_unregister(&rpm_vreg_device_driver); + platform_driver_unregister(&rpm_vreg_resource_driver); +} + +module_init(rpm_smd_regulator_driver_init); +module_exit(rpm_vreg_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM RPM SMD regulator driver"); diff --git a/drivers/rtc/qpnp-rtc.c b/drivers/rtc/qpnp-rtc.c index 876482de73aa..11a593cb9161 100644 --- a/drivers/rtc/qpnp-rtc.c +++ b/drivers/rtc/qpnp-rtc.c @@ -355,6 +355,7 @@ qpnp_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) unsigned long irq_flags; struct qpnp_rtc *rtc_dd = dev_get_drvdata(dev); u8 ctrl_reg; + u8 value[4] = {0}; spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags); ctrl_reg = rtc_dd->alarm_ctrl_reg1; @@ -370,6 +371,15 @@ qpnp_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) rtc_dd->alarm_ctrl_reg1 = ctrl_reg; + /* Clear Alarm register */ + if (!enabled) { + rc = qpnp_write_wrapper(rtc_dd, value, + rtc_dd->alarm_base + REG_OFFSET_ALARM_RW, + NUM_8_BIT_RTC_REGS); + if (rc) + dev_err(dev, "Clear ALARM value reg failed\n"); + } + rtc_rw_fail: spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags); return rc; diff --git a/drivers/scsi/ufs/debugfs.c b/drivers/scsi/ufs/debugfs.c index 9664120533f6..57110039eaff 100644 --- a/drivers/scsi/ufs/debugfs.c +++ b/drivers/scsi/ufs/debugfs.c @@ -302,8 +302,10 @@ static int ufsdbg_host_regs_show(struct seq_file *file, void *data) { struct ufs_hba *hba = (struct ufs_hba *)file->private; + ufshcd_hold(hba, false); ufsdbg_pr_buf_to_std(file, hba->mmio_base, UFSHCI_REG_SPACE_SIZE, "host regs"); + ufshcd_release(hba); return 0; } @@ -408,6 +410,8 @@ static int ufsdbg_show_hba_show(struct seq_file *file, void *data) hba->auto_bkops_enabled); seq_printf(file, "hba->ufshcd_state = 0x%x\n", hba->ufshcd_state); + seq_printf(file, "hba->clk_gating.state = 0x%x\n", + hba->clk_gating.state); seq_printf(file, "hba->eh_flags = 0x%x\n", hba->eh_flags); seq_printf(file, "hba->intr_mask = 0x%x\n", hba->intr_mask); seq_printf(file, "hba->ee_ctrl_mask = 0x%x\n", hba->ee_ctrl_mask); diff --git a/drivers/scsi/ufs/ufs-msm.c b/drivers/scsi/ufs/ufs-msm.c index 2827ec543773..82520b1e865b 100644 --- a/drivers/scsi/ufs/ufs-msm.c +++ b/drivers/scsi/ufs/ufs-msm.c @@ -32,7 +32,7 @@ #define FAST 2 #define UFS_MSM_LIMIT_NUM_LANES_RX 2 -#define UFS_MSM_LIMIT_NUM_LANES_TX 1 +#define UFS_MSM_LIMIT_NUM_LANES_TX 2 #define UFS_MSM_LIMIT_HSGEAR_RX UFS_HS_G2 #define UFS_MSM_LIMIT_HSGEAR_TX UFS_HS_G2 #define UFS_MSM_LIMIT_PWMGEAR_RX UFS_PWM_G4 @@ -1116,14 +1116,8 @@ static int msm_ufs_phy_power_on(struct msm_ufs_phy *phy) if (err) goto out_disable_pll; - err = msm_ufs_enable_phy_iface_clk(phy); - if (err) - goto out_disable_ref; - goto out; -out_disable_ref: - msm_ufs_disable_phy_ref_clk(phy); out_disable_pll: msm_ufs_phy_disable_vreg(phy, &phy->vdda_pll); out_disable_phy: @@ -1137,7 +1131,6 @@ static int msm_ufs_phy_power_off(struct msm_ufs_phy *phy) writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); mb(); - msm_ufs_disable_phy_iface_clk(phy); msm_ufs_disable_phy_ref_clk(phy); msm_ufs_phy_disable_vreg(phy, &phy->vdda_pll); @@ -1212,6 +1205,108 @@ static int msm_ufs_hce_enable_notify(struct ufs_hba *hba, bool status) return err; } +/** + * Returns non-zero for success (which rate of core_clk) and 0 + * in case of a failure + */ +static unsigned long +msm_ufs_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate) +{ + struct ufs_clk_info *clki; + u32 core_clk_period_in_ns; + u32 tx_clk_cycles_per_us = 0; + unsigned long core_clk_rate = 0; + + static u32 pwm_fr_table[][2] = { + {UFS_PWM_G1, 0x1}, + {UFS_PWM_G2, 0x1}, + {UFS_PWM_G3, 0x1}, + {UFS_PWM_G4, 0x1}, + }; + + static u32 hs_fr_table_rA[][2] = { + {UFS_HS_G1, 0x1F}, + {UFS_HS_G2, 0x3e}, + }; + + static u32 hs_fr_table_rB[][2] = { + {UFS_HS_G1, 0x24}, + {UFS_HS_G2, 0x49}, + }; + + if (gear == 0) { + dev_err(hba->dev, "%s: invalid gear = %d\n", __func__, gear); + goto out_error; + } + + list_for_each_entry(clki, &hba->clk_list_head, list) { + if (!strcmp(clki->name, "core_clk")) + core_clk_rate = clk_get_rate(clki->clk); + } + + /* If frequency is smaller than 1MHz, set to 1MHz */ + if (core_clk_rate < DEFAULT_CLK_RATE_HZ) + core_clk_rate = DEFAULT_CLK_RATE_HZ; + + core_clk_period_in_ns = NSEC_PER_SEC / core_clk_rate; + core_clk_period_in_ns <<= OFFSET_CLK_NS_REG; + core_clk_period_in_ns &= MASK_CLK_NS_REG; + + switch (hs) { + case FASTAUTO_MODE: + case FAST_MODE: + if (rate == PA_HS_MODE_A) { + if (gear >= ARRAY_SIZE(hs_fr_table_rA)) { + dev_err(hba->dev, + "%s: index %d exceeds table size %d\n", + __func__, gear, + ARRAY_SIZE(hs_fr_table_rA)); + goto out_error; + } + tx_clk_cycles_per_us = hs_fr_table_rA[gear-1][1]; + } else if (rate == PA_HS_MODE_B) { + if (gear >= ARRAY_SIZE(hs_fr_table_rB)) { + dev_err(hba->dev, + "%s: index %d exceeds table size %d\n", + __func__, gear, + ARRAY_SIZE(hs_fr_table_rB)); + goto out_error; + } + tx_clk_cycles_per_us = hs_fr_table_rB[gear-1][1]; + } else { + dev_err(hba->dev, "%s: invalid rate = %d\n", + __func__, rate); + goto out_error; + } + break; + case SLOWAUTO_MODE: + case SLOW_MODE: + if (gear >= ARRAY_SIZE(pwm_fr_table)) { + dev_err(hba->dev, + "%s: index %d exceeds table size %d\n", + __func__, gear, + ARRAY_SIZE(pwm_fr_table)); + goto out_error; + } + tx_clk_cycles_per_us = pwm_fr_table[gear-1][1]; + break; + case UNCHANGED: + default: + dev_err(hba->dev, "%s: invalid mode = %d\n", __func__, hs); + goto out_error; + } + + /* this register 2 fields shall be written at once */ + ufshcd_writel(hba, core_clk_period_in_ns | tx_clk_cycles_per_us, + REG_UFS_TX_SYMBOL_CLK_NS_US); + goto out; + +out_error: + core_clk_rate = 0; +out: + return core_clk_rate; +} + static int msm_ufs_link_startup_notify(struct ufs_hba *hba, bool status) { unsigned long core_clk_rate = 0; @@ -1219,7 +1314,13 @@ static int msm_ufs_link_startup_notify(struct ufs_hba *hba, bool status) switch (status) { case PRE_CHANGE: - core_clk_rate = msm_ufs_cfg_timers(hba, 0, 0); + core_clk_rate = msm_ufs_cfg_timers(hba, UFS_PWM_G1, + SLOWAUTO_MODE, 0); + if (!core_clk_rate) { + dev_err(hba->dev, "%s: msm_ufs_cfg_timers() failed\n", + __func__); + return -EINVAL; + } core_clk_cycles_per_100ms = (core_clk_rate / MSEC_PER_SEC) * 100; ufshcd_writel(hba, core_clk_cycles_per_100ms, @@ -1255,20 +1356,14 @@ static int msm_ufs_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) goto out; } - /* M-PHY RMMI interface clocks can be turned off */ - msm_ufs_disable_phy_iface_clk(phy); - /* - * If UniPro link is not active, PHY ref_clk and main PHY analog power - * can be switched off. + * If UniPro link is not active, PHY ref_clk, main PHY analog power + * rail and low noise analog power rail for PLL can be switched off. */ if (!ufshcd_is_link_active(hba)) { msm_ufs_disable_phy_ref_clk(phy); - ret = msm_ufs_phy_disable_vreg(phy, &phy->vdda_phy); - /* - * TODO: Check if "vdda_pll" can voted off when link is hibern8 - * or power off state? - */ + msm_ufs_phy_disable_vreg(phy, &phy->vdda_phy); + msm_ufs_phy_disable_vreg(phy, &phy->vdda_pll); } out: @@ -1301,24 +1396,6 @@ struct ufs_msm_dev_params { u32 desired_working_mode; }; -enum ufs_pwm_gear_tag { - UFS_PWM_DONT_CHANGE, /* Don't change Gear */ - UFS_PWM_G1, /* PWM Gear 1 (default for reset) */ - UFS_PWM_G2, /* PWM Gear 2 */ - UFS_PWM_G3, /* PWM Gear 3 */ - UFS_PWM_G4, /* PWM Gear 4 */ - UFS_PWM_G5, /* PWM Gear 5 */ - UFS_PWM_G6, /* PWM Gear 6 */ - UFS_PWM_G7, /* PWM Gear 7 */ -}; - -enum ufs_hs_gear_tag { - UFS_HS_DONT_CHANGE, /* Don't change Gear */ - UFS_HS_G1, /* HS Gear 1 (default for reset) */ - UFS_HS_G2, /* HS Gear 2 */ - UFS_HS_G3, /* HS Gear 3 */ -}; - /** * as every power mode, according to the UFS spec, have a defined * number that are not corresponed to their order or power @@ -1473,65 +1550,6 @@ static int get_pwr_dev_param(struct ufs_msm_dev_params *msm_param, return 0; } -static unsigned long -msm_ufs_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs) -{ - struct ufs_clk_info *clki; - u32 core_clk_period_in_ns; - u32 tx_clk_cycles_per_us = 0; - unsigned long core_clk_rate = 0; - - static u32 pwm_fr_table[][2] = { - {UFS_PWM_G1, 0x1}, - {UFS_PWM_G2, 0x1}, - {UFS_PWM_G3, 0x1}, - {UFS_PWM_G4, 0x1}, - }; - - static u32 hs_fr_table_rA[][2] = { - {UFS_HS_G1, 0x1F}, - {UFS_HS_G2, 0x3e}, - }; - - if (gear == 0 && hs == 0) { - gear = UFS_PWM_G1; - hs = SLOWAUTO_MODE; - } - - list_for_each_entry(clki, &hba->clk_list_head, list) { - if (!strcmp(clki->name, "core_clk")) - core_clk_rate = clk_get_rate(clki->clk); - } - - /* If frequency is smaller than 1MHz, set to 1MHz */ - if (core_clk_rate < DEFAULT_CLK_RATE_HZ) - core_clk_rate = DEFAULT_CLK_RATE_HZ; - - core_clk_period_in_ns = NSEC_PER_SEC / core_clk_rate; - core_clk_period_in_ns <<= OFFSET_CLK_NS_REG; - core_clk_period_in_ns &= MASK_CLK_NS_REG; - - switch (hs) { - case FASTAUTO_MODE: - case FAST_MODE: - tx_clk_cycles_per_us = hs_fr_table_rA[gear-1][1]; - break; - case SLOWAUTO_MODE: - case SLOW_MODE: - tx_clk_cycles_per_us = pwm_fr_table[gear-1][1]; - break; - case UNCHANGED: - default: - pr_err("%s: power parameter not valid\n", __func__); - return core_clk_rate; - } - - /* this register 2 fields shall be written at once */ - ufshcd_writel(hba, core_clk_period_in_ns | tx_clk_cycles_per_us, - REG_UFS_TX_SYMBOL_CLK_NS_US); - return core_clk_rate; -} - static int msm_ufs_pwr_change_notify(struct ufs_hba *hba, bool status, struct ufs_pa_layer_attr *dev_max_params, @@ -1551,8 +1569,12 @@ static int msm_ufs_pwr_change_notify(struct ufs_hba *hba, switch (status) { case PRE_CHANGE: + if (hba->quirks & UFSHCD_QUIRK_BROKEN_2_TX_LANES) + ufs_msm_cap.tx_lanes = 1; + else + ufs_msm_cap.tx_lanes = UFS_MSM_LIMIT_NUM_LANES_TX; + ufs_msm_cap.rx_lanes = UFS_MSM_LIMIT_NUM_LANES_RX; - ufs_msm_cap.tx_lanes = UFS_MSM_LIMIT_NUM_LANES_TX; ufs_msm_cap.hs_rx_gear = UFS_MSM_LIMIT_HSGEAR_RX; ufs_msm_cap.hs_tx_gear = UFS_MSM_LIMIT_HSGEAR_TX; ufs_msm_cap.pwm_rx_gear = UFS_MSM_LIMIT_PWMGEAR_RX; @@ -1575,8 +1597,18 @@ static int msm_ufs_pwr_change_notify(struct ufs_hba *hba, break; case POST_CHANGE: - msm_ufs_cfg_timers(hba, dev_req_params->gear_rx, - dev_req_params->pwr_rx); + if (!msm_ufs_cfg_timers(hba, dev_req_params->gear_rx, + dev_req_params->pwr_rx, + dev_req_params->hs_rate)) { + dev_err(hba->dev, "%s: msm_ufs_cfg_timers() failed\n", + __func__); + /* + * we return error code at the end of the routine, + * but continue to configure UFS_PHY_TX_LANE_ENABLE + * and bus voting as usual + */ + ret = -EINVAL; + } val = ~(MAX_U32 << dev_req_params->lane_tx); writel_relaxed(val, phy->mmio + UFS_PHY_TX_LANE_ENABLE); @@ -1631,6 +1663,7 @@ static void msm_ufs_advertise_quirks(struct ufs_hba *hba) | UFSHCD_QUIRK_BROKEN_VER_REG_1_1 | UFSHCD_QUIRK_BROKEN_CAP_64_BIT_0 | UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS + | UFSHCD_QUIRK_BROKEN_2_TX_LANES | UFSHCD_QUIRK_BROKEN_SUSPEND); } @@ -1745,11 +1778,26 @@ static int msm_ufs_setup_clocks(struct ufs_hba *hba, bool on) int err; int vote; + /* + * In case msm_ufs_init() is not yet done, simply ignore. + * This msm_ufs_setup_clocks() shall be called from + * msm_ufs_init() after init is done. + */ + if (!host) + return 0; + if (on) { + err = msm_ufs_enable_phy_iface_clk(host->phy); + if (err) + goto out; + vote = host->bus_vote.saved_vote; if (vote == host->bus_vote.min_bw_vote) msm_ufs_update_bus_bw_vote(host); } else { + /* M-PHY RMMI interface clocks can be turned off */ + msm_ufs_disable_phy_iface_clk(host->phy); + vote = host->bus_vote.min_bw_vote; } @@ -1758,6 +1806,7 @@ static int msm_ufs_setup_clocks(struct ufs_hba *hba, bool on) dev_err(hba->dev, "%s: set bus vote failed %d\n", __func__, err); +out: return err; } @@ -1903,6 +1952,9 @@ static int msm_ufs_init(struct ufs_hba *hba) hba->spm_lvl = UFS_PM_LVL_3; } + hba->caps |= UFSHCD_CAP_CLK_GATING | + UFSHCD_CAP_HIBERN8_WITH_CLK_GATING; + msm_ufs_setup_clocks(hba, true); goto out; out_disable_phy: diff --git a/drivers/scsi/ufs/ufs-msm.h b/drivers/scsi/ufs/ufs-msm.h index 37ec02971d1f..c1ecff82ba1b 100644 --- a/drivers/scsi/ufs/ufs-msm.h +++ b/drivers/scsi/ufs/ufs-msm.h @@ -100,8 +100,6 @@ struct msm_ufs_host { bool is_lane_clks_enabled; }; -static unsigned long -msm_ufs_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs); static int msm_ufs_update_bus_bw_vote(struct msm_ufs_host *host); /* MSM UFS PHY control registers */ diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 4a84dcd3a84b..ab42aa553bd3 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -177,6 +177,18 @@ enum unit_desc_param { UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22, }; +/* + * Logical Unit Write Protect + * 00h: LU not write protected + * 01h: LU write protected when fPowerOnWPEn =1 + * 02h: LU permanently write protected when fPermanentWPEn =1 + */ +enum ufs_lu_wp_type { + UFS_LU_NO_WP = 0x00, + UFS_LU_POWER_ON_WP = 0x01, + UFS_LU_PERM_WP = 0x02, +}; + /* bActiveICCLevel parameter current units */ enum { UFSHCD_NANO_AMP = 0, @@ -415,6 +427,12 @@ struct ufs_query_res { #define UFS_VREG_VCCQ2_MIN_UV 1650000 /* uV */ #define UFS_VREG_VCCQ2_MAX_UV 1950000 /* uV */ +/* + * VCCQ & VCCQ2 current requirement when UFS device is in sleep state + * and link is in Hibern8 state. + */ +#define UFS_VREG_LPM_LOAD_UA 1000 /* uA */ + struct ufs_vreg { struct regulator *reg; const char *name; @@ -431,4 +449,10 @@ struct ufs_vreg_info { struct ufs_vreg *vccq2; }; +struct ufs_dev_info { + bool f_power_on_wp_en; + /* Keeps information if any of the LU is power on write protected */ + bool is_lu_power_on_wp; +}; + #endif /* End of Header */ diff --git a/drivers/scsi/ufs/ufs_test.c b/drivers/scsi/ufs/ufs_test.c index 3fd52b7848ac..35f841c183ee 100644 --- a/drivers/scsi/ufs/ufs_test.c +++ b/drivers/scsi/ufs/ufs_test.c @@ -934,9 +934,6 @@ static int run_long_seq_test(struct test_data *td) /* NUM_OF_BLOCK * (BLOCK_SIZE / SECTOR_SIZE) */ sector += TEST_MAX_BIOS_PER_REQ * (PAGE_SIZE / td->req_q->limits.logical_block_size); - td->test_info.test_byte_count += - (TEST_MAX_BIOS_PER_REQ * sizeof(unsigned int) * - BIO_U32_SIZE); } while (inserted_requests < LONG_SEQ_TEST_NUM_REQS); diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c index 8f3d6e5cff7a..c92fa656f1f8 100644 --- a/drivers/scsi/ufs/ufshcd-pci.c +++ b/drivers/scsi/ufs/ufshcd-pci.c @@ -92,7 +92,7 @@ static int ufshcd_pci_runtime_idle(struct device *dev) */ static void ufshcd_pci_shutdown(struct pci_dev *pdev) { - ufshcd_hba_stop((struct ufs_hba *)pci_get_drvdata(pdev), true); + ufshcd_shutdown((struct ufs_hba *)pci_get_drvdata(pdev)); } /** diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 5cba57c48759..9e0f018f7394 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -258,6 +258,11 @@ static int ufshcd_pltfrm_runtime_idle(struct device *dev) #define ufshcd_pltfrm_runtime_idle NULL #endif /* CONFIG_PM_RUNTIME */ +static void ufshcd_pltfrm_shutdown(struct platform_device *pdev) +{ + ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev)); +} + /** * ufshcd_pltfrm_probe - probe routine of the driver * @pdev: pointer to Platform device handle @@ -362,6 +367,7 @@ static const struct dev_pm_ops ufshcd_dev_pm_ops = { static struct platform_driver ufshcd_pltfrm_driver = { .probe = ufshcd_pltfrm_probe, .remove = ufshcd_pltfrm_remove, + .shutdown = ufshcd_pltfrm_shutdown, .driver = { .name = "ufshcd", .owner = THIS_MODULE, diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 6e130282f492..fd1708595b0f 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -3,6 +3,7 @@ * * This code is based on drivers/scsi/ufs/ufshcd.c * Copyright (C) 2011-2013 Samsung India Software Operations + * Copyright (c) 2013, The Linux Foundation. All rights reserved. * * Authors: * Santosh Yaraganavi <santosh.sy@samsung.com> @@ -31,6 +32,9 @@ * circumstances will the contributor of this Program be liable for * any damages of any kind arising from your use or distribution of * this program. + * + * The Linux Foundation chooses to take subject only to the GPLv2 + * license terms, and distributes only under these terms. */ #include <linux/async.h> @@ -96,6 +100,9 @@ /* default value of auto suspend is 3 seconds */ #define UFSHCD_AUTO_SUSPEND_DELAY_MS 3000 /* millisecs */ +/* IOCTL opcode for command - ufs set device read only */ +#define UFS_IOCTL_BLKROSET BLKROSET + #define ufshcd_toggle_vreg(_dev, _vreg, _on) \ ({ \ int _ret; \ @@ -209,6 +216,11 @@ static inline int ufshcd_read_unit_desc_param(struct ufs_hba *hba, enum unit_desc_param param_offset, u8 *param_read_buf, u32 param_size); +static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on, + bool skip_ref_clk); +static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on); +static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba); +static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba); static inline void ufshcd_enable_irq(struct ufs_hba *hba) { @@ -618,6 +630,254 @@ static inline int ufshcd_is_hba_active(struct ufs_hba *hba) return (ufshcd_readl(hba, REG_CONTROLLER_ENABLE) & 0x1) ? 0 : 1; } +static void ufshcd_ungate_work(struct work_struct *work) +{ + int ret; + unsigned long flags; + struct ufs_hba *hba = container_of(work, struct ufs_hba, + clk_gating.ungate_work); + + cancel_delayed_work_sync(&hba->clk_gating.gate_work); + + spin_lock_irqsave(hba->host->host_lock, flags); + if (hba->clk_gating.state == CLKS_ON) { + spin_unlock_irqrestore(hba->host->host_lock, flags); + goto unblock_reqs; + } + + spin_unlock_irqrestore(hba->host->host_lock, flags); + ufshcd_setup_clocks(hba, true); + + /* Exit from hibern8 */ + if (ufshcd_can_hibern8_during_gating(hba)) { + /* Prevent gating in this path */ + hba->clk_gating.is_suspended = true; + if (ufshcd_is_link_hibern8(hba)) { + ret = ufshcd_uic_hibern8_exit(hba); + if (ret) + dev_err(hba->dev, "%s: hibern8 exit failed %d\n", + __func__, ret); + else + ufshcd_set_link_active(hba); + } + hba->clk_gating.is_suspended = false; + } +unblock_reqs: + scsi_unblock_requests(hba->host); +} + +static const char *to_string(enum clk_gating_state state) +{ + switch (state) { + case CLKS_OFF: return "CLKS_OFF"; + case CLKS_ON: return "CLKS_ON"; + case REQ_CLKS_OFF: return "REQ_CLKS_OFF"; + case REQ_CLKS_ON: return "REQ_CLKS_ON"; + default: return "UNKNOWN_STATE"; + } +} + +/** + * ufshcd_hold - Enable clocks that were gated earlier due to ufshcd_release. + * Also, exit from hibern8 mode and set the link as active. + * @hba: per adapter instance + * @async: This indicates whether caller should ungate clocks asynchronously. + */ +int ufshcd_hold(struct ufs_hba *hba, bool async) +{ + int rc = 0; + unsigned long flags; + + if (!ufshcd_is_clkgating_allowed(hba)) + goto out; +start: + spin_lock_irqsave(hba->host->host_lock, flags); + hba->clk_gating.active_reqs++; + + switch (hba->clk_gating.state) { + case CLKS_ON: + break; + case REQ_CLKS_OFF: + if (cancel_delayed_work(&hba->clk_gating.gate_work)) { + hba->clk_gating.state = CLKS_ON; + trace_ufshcd_clk_gating(dev_name(hba->dev), + to_string(hba->clk_gating.state)); + break; + } + /* + * If we here, it means gating work is either done or + * currently running. Hence, fall through to cancel gating + * work and to enable clocks. + */ + case CLKS_OFF: + scsi_block_requests(hba->host); + hba->clk_gating.state = REQ_CLKS_ON; + trace_ufshcd_clk_gating(dev_name(hba->dev), + to_string(hba->clk_gating.state)); + schedule_work(&hba->clk_gating.ungate_work); + /* + * fall through to check if we should wait for this + * work to be done or not. + */ + case REQ_CLKS_ON: + if (async) { + rc = -EAGAIN; + hba->clk_gating.active_reqs--; + break; + } else { + spin_unlock_irqrestore(hba->host->host_lock, flags); + flush_work(&hba->clk_gating.ungate_work); + /* Make sure state is CLKS_ON before returning */ + goto start; + } + default: + dev_err(hba->dev, "%s: clk gating is in invalid state %d\n", + __func__, hba->clk_gating.state); + break; + } + spin_unlock_irqrestore(hba->host->host_lock, flags); +out: + return rc; +} + +static void ufshcd_gate_work(struct work_struct *work) +{ + struct ufs_hba *hba = container_of(work, struct ufs_hba, + clk_gating.gate_work.work); + unsigned long flags; + + spin_lock_irqsave(hba->host->host_lock, flags); + if (hba->clk_gating.is_suspended) { + hba->clk_gating.state = CLKS_ON; + trace_ufshcd_clk_gating(dev_name(hba->dev), + to_string(hba->clk_gating.state)); + goto rel_lock; + } + + if (hba->clk_gating.active_reqs + || hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL + || hba->lrb_in_use || hba->outstanding_tasks + || hba->active_uic_cmd || hba->uic_async_done) + goto rel_lock; + + spin_unlock_irqrestore(hba->host->host_lock, flags); + + /* put the link into hibern8 mode before turning off clocks */ + if (ufshcd_can_hibern8_during_gating(hba)) { + if (ufshcd_uic_hibern8_enter(hba)) { + hba->clk_gating.state = CLKS_ON; + trace_ufshcd_clk_gating(dev_name(hba->dev), + to_string(hba->clk_gating.state)); + goto out; + } + ufshcd_set_link_hibern8(hba); + } + + if (!ufshcd_is_link_active(hba)) + ufshcd_setup_clocks(hba, false); + else + /* If link is active, device ref_clk can't be switched off */ + __ufshcd_setup_clocks(hba, false, true); + + /* + * In case you are here to cancel this work the gating state + * would be marked as REQ_CLKS_ON. In this case keep the state + * as REQ_CLKS_ON which would anyway imply that clocks are off + * and a request to turn them on is pending. By doing this way, + * we keep the state machine in tact and this would ultimately + * prevent from doing cancel work multiple times when there are + * new requests arriving before the current cancel work is done. + */ + spin_lock_irqsave(hba->host->host_lock, flags); + if (hba->clk_gating.state == REQ_CLKS_OFF) { + hba->clk_gating.state = CLKS_OFF; + trace_ufshcd_clk_gating(dev_name(hba->dev), + to_string(hba->clk_gating.state)); + } +rel_lock: + spin_unlock_irqrestore(hba->host->host_lock, flags); +out: + return; +} + +/* host lock must be held before calling this variant */ +static void __ufshcd_release(struct ufs_hba *hba) +{ + if (!ufshcd_is_clkgating_allowed(hba)) + return; + + hba->clk_gating.active_reqs--; + + if (hba->clk_gating.active_reqs || hba->clk_gating.is_suspended + || hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL + || hba->lrb_in_use || hba->outstanding_tasks + || hba->active_uic_cmd || hba->uic_async_done) + return; + + hba->clk_gating.state = REQ_CLKS_OFF; + trace_ufshcd_clk_gating(dev_name(hba->dev), + to_string(hba->clk_gating.state)); + schedule_delayed_work(&hba->clk_gating.gate_work, + msecs_to_jiffies(hba->clk_gating.delay_ms)); +} + +void ufshcd_release(struct ufs_hba *hba) +{ + unsigned long flags; + + spin_lock_irqsave(hba->host->host_lock, flags); + __ufshcd_release(hba); + spin_unlock_irqrestore(hba->host->host_lock, flags); +} + +static ssize_t ufshcd_clkgate_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%lu\n", hba->clk_gating.delay_ms); +} + +static ssize_t ufshcd_clkgate_delay_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + unsigned long flags, value; + + if (kstrtoul(buf, 0, &value)) + return -EINVAL; + + spin_lock_irqsave(hba->host->host_lock, flags); + hba->clk_gating.delay_ms = value; + spin_unlock_irqrestore(hba->host->host_lock, flags); + return count; +} + +static void ufshcd_init_clk_gating(struct ufs_hba *hba) +{ + if (!ufshcd_is_clkgating_allowed(hba)) + return; + + hba->clk_gating.delay_ms = 150; + INIT_DELAYED_WORK(&hba->clk_gating.gate_work, ufshcd_gate_work); + INIT_WORK(&hba->clk_gating.ungate_work, ufshcd_ungate_work); + + hba->clk_gating.delay_attr.show = ufshcd_clkgate_delay_show; + hba->clk_gating.delay_attr.store = ufshcd_clkgate_delay_store; + sysfs_attr_init(&hba->clk_gating.delay_attr.attr); + hba->clk_gating.delay_attr.attr.name = "clkgate_delay_ms"; + hba->clk_gating.delay_attr.attr.mode = S_IRUGO | S_IWUSR; + if (device_create_file(hba->dev, &hba->clk_gating.delay_attr)) + dev_err(hba->dev, "Failed to create sysfs for clkgate_delay\n"); +} + +static void ufshcd_exit_clk_gating(struct ufs_hba *hba) +{ + if (!ufshcd_is_clkgating_allowed(hba)) + return; + device_remove_file(hba->dev, &hba->clk_gating.delay_attr); +} + /** * ufshcd_send_command - Send SCSI or device management commands * @hba: per adapter instance @@ -823,10 +1083,12 @@ ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) { int ret; + ufshcd_hold(hba, false); mutex_lock(&hba->uic_cmd_mutex); ret = __ufshcd_send_uic_cmd(hba, uic_cmd); mutex_unlock(&hba->uic_cmd_mutex); + ufshcd_release(hba); return ret; } @@ -1142,6 +1404,14 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) goto out; } + err = ufshcd_hold(hba, true); + if (err) { + err = SCSI_MLQUEUE_HOST_BUSY; + clear_bit_unlock(tag, &hba->lrb_in_use); + goto out; + } + WARN_ON(hba->clk_gating.state != CLKS_ON); + lrbp = &hba->lrb[tag]; WARN_ON(lrbp->cmd); @@ -1417,6 +1687,7 @@ int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, BUG_ON(!hba); + ufshcd_hold(hba, false); mutex_lock(&hba->dev_cmd.lock); ufshcd_init_query(hba, &request, &response, opcode, idn, index, selector); @@ -1460,6 +1731,7 @@ int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, out_unlock: mutex_unlock(&hba->dev_cmd.lock); + ufshcd_release(hba); return err; } @@ -1483,6 +1755,7 @@ int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, BUG_ON(!hba); + ufshcd_hold(hba, false); if (!attr_val) { dev_err(hba->dev, "%s: attribute value required for opcode 0x%x\n", __func__, opcode); @@ -1522,6 +1795,7 @@ int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, out_unlock: mutex_unlock(&hba->dev_cmd.lock); out: + ufshcd_release(hba); return err; } @@ -1586,6 +1860,7 @@ int ufshcd_query_descriptor(struct ufs_hba *hba, BUG_ON(!hba); + ufshcd_hold(hba, false); if (!desc_buf) { dev_err(hba->dev, "%s: descriptor buffer required for opcode 0x%x\n", __func__, opcode); @@ -1635,6 +1910,7 @@ int ufshcd_query_descriptor(struct ufs_hba *hba, out_unlock: mutex_unlock(&hba->dev_cmd.lock); out: + ufshcd_release(hba); return err; } @@ -2071,6 +2347,7 @@ int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd) u8 status; int ret; + ufshcd_hold(hba, false); mutex_lock(&hba->uic_cmd_mutex); init_completion(&uic_async_done); @@ -2107,6 +2384,8 @@ out: hba->uic_async_done = NULL; spin_unlock_irqrestore(hba->host->host_lock, flags); mutex_unlock(&hba->uic_cmd_mutex); + + ufshcd_release(hba); return ret; } @@ -2147,6 +2426,48 @@ static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba) return ufshcd_uic_pwr_ctrl(hba, &uic_cmd); } + /** + * ufshcd_print_pwr_info - print power params as saved in hba + * power info + * @hba: per-adapter instance + */ +static void ufshcd_print_pwr_info(struct ufs_hba *hba) +{ + char *names[] = { + "INVALID MODE", + "FAST MODE", + "SLOW_MODE", + "INVALID MODE", + "FASTAUTO_MODE", + "SLOWAUTO_MODE", + "INVALID MODE", + }; + + dev_info(hba->dev, "%s:[RX, TX]: gear=[%d, %d], lane[%d, %d], pwr[%s, %s], rate = %d\n", + __func__, + hba->pwr_info.gear_rx, hba->pwr_info.gear_tx, + hba->pwr_info.lane_rx, hba->pwr_info.lane_tx, + names[hba->pwr_info.pwr_rx], + names[hba->pwr_info.pwr_tx], + hba->pwr_info.hs_rate); +} + + /** + * ufshcd_init_pwr_info - setting the POR (power on reset) + * values in hba power info + * @hba: per-adapter instance + */ +static void ufshcd_init_pwr_info(struct ufs_hba *hba) +{ + hba->pwr_info.gear_rx = UFS_PWM_G1; + hba->pwr_info.gear_tx = UFS_PWM_G1; + hba->pwr_info.lane_rx = 1; + hba->pwr_info.lane_tx = 1; + hba->pwr_info.pwr_rx = SLOWAUTO_MODE; + hba->pwr_info.pwr_tx = SLOWAUTO_MODE; + hba->pwr_info.hs_rate = 0; +} + /** * ufshcd_config_max_pwr_mode - Set & Change power mode with * maximum capability attribute information. @@ -2169,6 +2490,12 @@ static int ufshcd_config_max_pwr_mode(struct ufs_hba *hba) ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDRXDATALANES), &lanes[RX]); ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), &lanes[TX]); + if (!lanes[RX] || !lanes[TX]) { + dev_err(hba->dev, "%s: invalid connected lanes value. rx=%d, tx=%d\n", + __func__, lanes[RX], lanes[TX]); + return -EINVAL; + } + /* * First, get the maximum gears of HS speed. * If a zero value, it means there is no HSGEAR capability. @@ -2177,6 +2504,11 @@ static int ufshcd_config_max_pwr_mode(struct ufs_hba *hba) ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[RX]); if (!gear[RX]) { ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR), &gear[RX]); + if (!gear[RX]) { + dev_err(hba->dev, "%s: invalid rx gear read = %d\n", + __func__, gear[RX]); + return -EINVAL; + } pwr[RX] = SLOWAUTO_MODE; } @@ -2184,6 +2516,11 @@ static int ufshcd_config_max_pwr_mode(struct ufs_hba *hba) if (!gear[TX]) { ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR), &gear[TX]); + if (!gear[TX]) { + dev_err(hba->dev, "%s: invalid tx gear read = %d\n", + __func__, gear[TX]); + return -EINVAL; + } pwr[TX] = SLOWAUTO_MODE; } @@ -2244,8 +2581,18 @@ static int ufshcd_config_max_pwr_mode(struct ufs_hba *hba) if (hba->vops->pwr_change_notify) hba->vops->pwr_change_notify(hba, POST_CHANGE, NULL, &dev_required_params); + + hba->pwr_info.gear_rx = gear[RX]; + hba->pwr_info.gear_tx = gear[TX]; + hba->pwr_info.lane_rx = lanes[RX]; + hba->pwr_info.lane_tx = lanes[TX]; + hba->pwr_info.pwr_rx = pwr[RX]; + hba->pwr_info.pwr_tx = pwr[TX]; + hba->pwr_info.hs_rate = hs_rate; } + ufshcd_print_pwr_info(hba); + if (hba->quirks & UFSHCD_QUIRK_BROKEN_PWR_MODE_CHANGE) msleep(1000); @@ -2501,6 +2848,7 @@ static int ufshcd_verify_dev_init(struct ufs_hba *hba) int err = 0; int retries; + ufshcd_hold(hba, false); mutex_lock(&hba->dev_cmd.lock); for (retries = NOP_OUT_RETRIES; retries > 0; retries--) { err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_NOP, @@ -2512,6 +2860,7 @@ static int ufshcd_verify_dev_init(struct ufs_hba *hba) dev_dbg(hba->dev, "%s: error %d retrying\n", __func__, err); } mutex_unlock(&hba->dev_cmd.lock); + ufshcd_release(hba); if (err) dev_err(hba->dev, "%s: NOP OUT failed %d\n", __func__, err); @@ -2564,6 +2913,62 @@ static void ufshcd_set_queue_depth(struct scsi_device *sdev) scsi_activate_tcq(sdev, lun_qdepth); } +/* + * ufshcd_get_lu_wp - returns the "b_lu_write_protect" from UNIT DESCRIPTOR + * @hba: per-adapter instance + * @lun: UFS device lun id + * @b_lu_write_protect: pointer to buffer to hold the LU's write protect info + * + * Returns 0 in case of success and b_lu_write_protect status would be returned + * @b_lu_write_protect parameter. + * Returns -ENOTSUPP if reading b_lu_write_protect is not supported. + * Returns -EINVAL in case of invalid parameters passed to this function. + */ +static int ufshcd_get_lu_wp(struct ufs_hba *hba, + u8 lun, + u8 *b_lu_write_protect) +{ + int ret; + + if (!b_lu_write_protect) + ret = -EINVAL; + /* + * According to UFS device spec, RPMB LU can't be write + * protected so skip reading bLUWriteProtect parameter for + * it. For other W-LUs, UNIT DESCRIPTOR is not available. + */ + else if (lun >= UFS_UPIU_MAX_GENERAL_LUN) + ret = -ENOTSUPP; + else + ret = ufshcd_read_unit_desc_param(hba, + lun, + UNIT_DESC_PARAM_LU_WR_PROTECT, + b_lu_write_protect, + sizeof(*b_lu_write_protect)); + return ret; +} + +/** + * ufshcd_get_lu_power_on_wp_status - get LU's power on write protect + * status + * @hba: per-adapter instance + * @sdev: pointer to SCSI device + * + */ +static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, + struct scsi_device *sdev) +{ + if (hba->dev_info.f_power_on_wp_en && + !hba->dev_info.is_lu_power_on_wp) { + u8 b_lu_write_protect; + + if (!ufshcd_get_lu_wp(hba, ufshcd_scsi_to_upiu_lun(sdev->lun), + &b_lu_write_protect) && + (b_lu_write_protect == UFS_LU_POWER_ON_WP)) + hba->dev_info.is_lu_power_on_wp = true; + } +} + /** * ufshcd_slave_alloc - handle initial SCSI device configurations * @sdev: pointer to SCSI device @@ -2595,6 +3000,8 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev) ufshcd_set_queue_depth(sdev); + ufshcd_get_lu_power_on_wp_status(hba, sdev); + /* * For selecting the UFS device power mode (Active / UFS_Sleep / * UFS_PowerDown), SCSI power management command (START STOP UNIT) @@ -2886,6 +3293,7 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba) clear_bit_unlock(index, &hba->lrb_in_use); /* Do not touch lrbp after scsi done */ cmd->scsi_done(cmd); + __ufshcd_release(hba); } else if (lrbp->command_type == UTP_CMD_TYPE_DEV_MANAGE) { if (hba->dev_cmd.complete) complete(hba->dev_cmd.complete); @@ -3170,6 +3578,7 @@ static void ufshcd_err_handler(struct work_struct *work) hba = container_of(work, struct ufs_hba, eh_work); pm_runtime_get_sync(hba->dev); + ufshcd_hold(hba, false); spin_lock_irqsave(hba->host->host_lock, flags); if (hba->ufshcd_state == UFSHCD_STATE_RESET) { @@ -3223,6 +3632,7 @@ static void ufshcd_err_handler(struct work_struct *work) out: scsi_unblock_requests(hba->host); + ufshcd_release(hba); pm_runtime_put_sync(hba->dev); } @@ -3303,6 +3713,7 @@ static void ufshcd_check_errors(struct ufs_hba *hba) SYSTEM_BUS_FATAL_ERROR); ufshcd_print_host_regs(hba); + ufshcd_print_pwr_info(hba); ufshcd_print_tmrs(hba, hba->outstanding_tasks); ufshcd_print_trs(hba, hba->outstanding_reqs, pr_prdt); @@ -3430,6 +3841,7 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id, * the maximum wait time is bounded by %TM_CMD_TIMEOUT. */ wait_event(hba->tm_tag_wq, ufshcd_get_tm_free_slot(hba, &free_slot)); + ufshcd_hold(hba, false); spin_lock_irqsave(host->host_lock, flags); task_req_descp = hba->utmrdl_base_addr; @@ -3481,6 +3893,7 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id, ufshcd_put_tm_slot(hba, free_slot); wake_up(&hba->tm_tag_wq); + ufshcd_release(hba); return err; } @@ -3563,6 +3976,7 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) hba = shost_priv(host); tag = cmd->request->tag; + ufshcd_hold(hba, false); /* If command is already aborted/completed, return SUCCESS */ if (!(test_bit(tag, &hba->outstanding_reqs))) goto out; @@ -3578,6 +3992,7 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) dev_err(hba->dev, "%s: Device abort task at tag %d", __func__, tag); scsi_print_command(cmd); ufshcd_print_host_regs(hba); + ufshcd_print_pwr_info(hba); ufshcd_print_trs(hba, 1 << tag, true); lrbp = &hba->lrb[tag]; @@ -3633,6 +4048,11 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) clear_bit_unlock(tag, &hba->lrb_in_use); wake_up(&hba->dev_cmd.tag_wq); + /* + * This ufshcd_release() corresponds to the original scsi cmd that got + * aborted here (as we won't get any IRQ for it). + */ + ufshcd_release(hba); out: if (!err) { err = SUCCESS; @@ -3640,7 +4060,7 @@ out: dev_err(hba->dev, "%s: failed with err %d\n", __func__, err); err = FAILED; } - + ufshcd_release(hba); return err; } @@ -3725,6 +4145,7 @@ static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd) hba = shost_priv(cmd->device->host); + ufshcd_hold(hba, false); /* * Check if there is any race with fatal error handling. * If so, wait for it to complete. Even though fatal error @@ -3758,6 +4179,7 @@ static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd) ufshcd_clear_eh_in_progress(hba); spin_unlock_irqrestore(hba->host->host_lock, flags); + ufshcd_release(hba); return err; } @@ -3919,6 +4341,9 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) if (ret) goto out; + ufshcd_init_pwr_info(hba); + ufshcd_print_pwr_info(hba); + /* UniPro link is active now */ ufshcd_set_link_active(hba); @@ -3943,13 +4368,24 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) ufshcd_force_reset_auto_bkops(hba); hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL; - ufshcd_config_max_pwr_mode(hba); + if (ufshcd_config_max_pwr_mode(hba)) + dev_err(hba->dev, + "%s: Failed configuring max supported power mode\n", + __func__); /* * If we are in error handling context or in power management callbacks * context, no need to scan the host */ if (!ufshcd_eh_in_progress(hba) && !hba->pm_op_in_progress) { + bool flag; + + /* clear any previous UFS device information */ + memset(&hba->dev_info, 0, sizeof(hba->dev_info)); + if (!ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_READ_FLAG, + QUERY_FLAG_IDN_PWR_ON_WPE, &flag)) + hba->dev_info.f_power_on_wp_en = flag; + ufshcd_init_icc_levels(hba); scsi_scan_host(hba->host); pm_runtime_put_sync(hba->dev); @@ -4186,6 +4622,9 @@ static int ufshcd_ioctl(struct scsi_device *dev, int cmd, void __user *buffer) buffer); pm_runtime_put_sync(hba->dev); break; + case UFS_IOCTL_BLKROSET: + err = -ENOIOCTLCMD; + break; default: err = -EINVAL; dev_err(hba->dev, "%s: Illegal ufs-IOCTL cmd %d\n", __func__, @@ -4213,8 +4652,45 @@ static struct scsi_host_template ufshcd_driver_template = { .sg_tablesize = SG_ALL, .cmd_per_lun = UFSHCD_CMD_PER_LUN, .can_queue = UFSHCD_CAN_QUEUE, + .max_host_blocked = 1, }; +static int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg, + int ua) +{ + int ret = 0; + struct regulator *reg = vreg->reg; + const char *name = vreg->name; + + BUG_ON(!vreg); + + ret = regulator_set_optimum_mode(reg, ua); + if (ret >= 0) { + /* + * regulator_set_optimum_mode() returns new regulator + * mode upon success. + */ + ret = 0; + } else { + dev_err(dev, "%s: %s set optimum mode(ua=%d) failed, err=%d\n", + __func__, name, ua, ret); + } + + return ret; +} + +static inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba, + struct ufs_vreg *vreg) +{ + return ufshcd_config_vreg_load(hba->dev, vreg, UFS_VREG_LPM_LOAD_UA); +} + +static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba, + struct ufs_vreg *vreg) +{ + return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA); +} + static int ufshcd_config_vreg(struct device *dev, struct ufs_vreg *vreg, bool on) { @@ -4235,18 +4711,9 @@ static int ufshcd_config_vreg(struct device *dev, } uA_load = on ? vreg->max_uA : 0; - ret = regulator_set_optimum_mode(reg, uA_load); - if (ret >= 0) { - /* - * regulator_set_optimum_mode() returns new regulator - * mode upon success. - */ - ret = 0; - } else { - dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n", - __func__, name, uA_load, ret); + ret = ufshcd_config_vreg_load(dev, vreg, uA_load); + if (ret) goto out; - } } out: return ret; @@ -4368,6 +4835,7 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on, int ret = 0; struct ufs_clk_info *clki; struct list_head *head = &hba->clk_list_head; + unsigned long flags; if (!head || list_empty(head)) goto out; @@ -4392,12 +4860,21 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on, clki->name, on ? "en" : "dis"); } } + + if (hba->vops && hba->vops->setup_clocks) + ret = hba->vops->setup_clocks(hba, on); out: if (ret) { list_for_each_entry(clki, head, list) { if (!IS_ERR_OR_NULL(clki->clk) && clki->enabled) clk_disable_unprepare(clki->clk); } + } else if (!ret && on) { + spin_lock_irqsave(hba->host->host_lock, flags); + hba->clk_gating.state = CLKS_ON; + trace_ufshcd_clk_gating(dev_name(hba->dev), + to_string(hba->clk_gating.state)); + spin_unlock_irqrestore(hba->host->host_lock, flags); } return ret; } @@ -4458,23 +4935,14 @@ static int ufshcd_variant_hba_init(struct ufs_hba *hba) goto out; } - if (hba->vops->setup_clocks) { - err = hba->vops->setup_clocks(hba, true); - if (err) - goto out_exit; - } - if (hba->vops->setup_regulators) { err = hba->vops->setup_regulators(hba, true); if (err) - goto out_clks; + goto out_exit; } goto out; -out_clks: - if (hba->vops->setup_clocks) - hba->vops->setup_clocks(hba, false); out_exit: if (hba->vops->exit) hba->vops->exit(hba); @@ -4672,18 +5140,72 @@ out: return ret; } +static void ufshcd_vreg_set_lpm(struct ufs_hba *hba) +{ + /* + * If UFS device is either in UFS_Sleep turn off VCC rail to save some + * power. + * + * If UFS device and link is in OFF state, all power supplies (VCC, + * VCCQ, VCCQ2) can be turned off if power on write protect is not + * required. If UFS link is inactive (Hibern8 or OFF state) and device + * is in sleep state, put VCCQ & VCCQ2 rails in LPM mode. + * + * Ignore the error returned by ufshcd_toggle_vreg() as device is anyway + * in low power state which would save some power. + */ + if (ufshcd_is_ufs_dev_poweroff(hba) && + !hba->dev_info.is_lu_power_on_wp) { + ufshcd_setup_vreg(hba, false); + } else if (!ufshcd_is_ufs_dev_active(hba)) { + ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false); + if (!ufshcd_is_link_active(hba)) { + ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq); + ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq2); + } + } +} + +static int ufshcd_vreg_set_hpm(struct ufs_hba *hba) +{ + int ret = 0; + + if (ufshcd_is_ufs_dev_poweroff(hba) && + !hba->dev_info.is_lu_power_on_wp) { + ret = ufshcd_setup_vreg(hba, true); + } else if (!ufshcd_is_ufs_dev_active(hba)) { + ret = ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, true); + if (!ret && !ufshcd_is_link_active(hba)) { + ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq); + if (ret) + goto vcc_disable; + ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq2); + if (ret) + goto vccq_lpm; + } + } + goto out; + +vccq_lpm: + ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq); +vcc_disable: + ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false); +out: + return ret; +} + /** * ufshcd_suspend - helper function for suspend operations * @hba: per adapter instance - * @pm_op: runtime PM or system PM - * - * This is common function called by both ufshcd_system_suspend() and - * ufshcd_runtime_suspend(). + * @pm_op: desired low power operation type * * This function will try to put the UFS device and link into low power * mode based on the "rpm_lvl" (Runtime PM level) or "spm_lvl" * (System PM level). * + * If this function is called during shutdown, it will make sure that + * both UFS device and UFS link is powered off. + * * NOTE: UFS device & link must be active before we enter in this function. * * Returns 0 for success and non-zero for failure @@ -4696,14 +5218,23 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) enum uic_link_state req_link_state; hba->pm_op_in_progress = 1; - pm_lvl = ufshcd_is_runtime_pm(pm_op) ? hba->rpm_lvl : hba->spm_lvl; - req_dev_pwr_mode = ufs_get_pm_lvl_to_dev_pwr_mode(pm_lvl); - req_link_state = ufs_get_pm_lvl_to_link_pwr_state(pm_lvl); + if (!ufshcd_is_shutdown_pm(pm_op)) { + pm_lvl = ufshcd_is_runtime_pm(pm_op) ? + hba->rpm_lvl : hba->spm_lvl; + req_dev_pwr_mode = ufs_get_pm_lvl_to_dev_pwr_mode(pm_lvl); + req_link_state = ufs_get_pm_lvl_to_link_pwr_state(pm_lvl); + } else { + req_dev_pwr_mode = UFS_POWERDOWN_PWR_MODE; + req_link_state = UIC_LINK_OFF_STATE; + } /* * If we can't transition into any of the low power modes * just gate the clocks. */ + ufshcd_hold(hba, false); + hba->clk_gating.is_suspended = true; + if (req_dev_pwr_mode == UFS_ACTIVE_PWR_MODE && req_link_state == UIC_LINK_ACTIVE_STATE) { goto disable_clks; @@ -4726,35 +5257,24 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) */ ret = ufshcd_bkops_ctrl(hba, BKOPS_STATUS_NON_CRITICAL); if (ret) - goto out; + goto enable_gating; } if ((req_dev_pwr_mode != hba->curr_dev_pwr_mode) && ((ufshcd_is_runtime_pm(pm_op) && !hba->auto_bkops_enabled) || - ufshcd_is_system_pm(pm_op))) { + !ufshcd_is_runtime_pm(pm_op))) { /* ensure that bkops is disabled */ ufshcd_disable_auto_bkops(hba); ret = ufshcd_set_dev_pwr_mode(hba, req_dev_pwr_mode); if (ret) - goto out; + goto enable_gating; } ret = ufshcd_link_state_transition(hba, req_link_state, 1); if (ret) goto set_dev_active; - /* - * If UFS device is either in UFS_Sleep turn off VCC rail to - * save some power. - * If UFS device is in UFS_Poweroff state, all power supplies - * (VCC, VCCQ, VCCQ2) can be turned off. - * Ignore the error returned by ufshcd_toggle_vreg() as device - * is anyway in low power state which would save some power. - */ - if (ufshcd_is_ufs_dev_poweroff(hba)) - ufshcd_setup_vreg(hba, false); - else if (ufshcd_is_ufs_dev_sleep(hba)) - ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false); + ufshcd_vreg_set_lpm(hba); disable_clks: /* @@ -4780,6 +5300,9 @@ disable_clks: /* If link is active, device ref_clk can't be switched off */ __ufshcd_setup_clocks(hba, false, true); + hba->clk_gating.state = CLKS_OFF; + trace_ufshcd_clk_gating(dev_name(hba->dev), + to_string(hba->clk_gating.state)); /* * Disable the host irq as host controller as there won't be any * host controller trasanction expected till resume. @@ -4791,10 +5314,7 @@ vops_resume: if (hba->vops && hba->vops->resume) hba->vops->resume(hba, pm_op); set_link_active: - if (ufshcd_is_ufs_dev_poweroff(hba)) - ufshcd_setup_vreg(hba, true); - else if (ufshcd_is_ufs_dev_sleep(hba)) - ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, true); + ufshcd_vreg_set_hpm(hba); if (ufshcd_is_link_hibern8(hba) && !ufshcd_uic_hibern8_exit(hba)) ufshcd_set_link_active(hba); else if (ufshcd_is_link_off(hba)) @@ -4802,6 +5322,9 @@ set_link_active: set_dev_active: if (!ufshcd_set_dev_pwr_mode(hba, UFS_ACTIVE_PWR_MODE)) ufshcd_disable_auto_bkops(hba); +enable_gating: + hba->clk_gating.is_suspended = false; + ufshcd_release(hba); out: hba->pm_op_in_progress = 0; return ret; @@ -4829,21 +5352,10 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) if (ret) goto out; - if (hba->vops && hba->vops->setup_clocks) { - ret = hba->vops->setup_clocks(hba, true); - if (ret) - goto disable_clks; - } - /* enable the host irq as host controller would be active soon */ ufshcd_enable_irq(hba); - /* Bring regulators back online if its turned off during suspend. */ - if (ufshcd_is_ufs_dev_poweroff(hba)) - ret = ufshcd_setup_vreg(hba, true); - else if (ufshcd_is_ufs_dev_sleep(hba)) - ret = ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, true); - + ret = ufshcd_vreg_set_hpm(hba); if (ret) goto disable_irq_and_vops_clks; @@ -4881,6 +5393,10 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) } ufshcd_disable_auto_bkops(hba); + hba->clk_gating.is_suspended = false; + + /* Schedule clock gating in case of no access to UFS device yet */ + ufshcd_release(hba); goto out; set_old_link_state: @@ -4889,15 +5405,9 @@ vendor_suspend: if (hba->vops && hba->vops->suspend) hba->vops->suspend(hba, pm_op); disable_vreg: - if (ufshcd_is_ufs_dev_poweroff(hba)) - ufshcd_setup_vreg(hba, false); - else if (ufshcd_is_ufs_dev_sleep(hba)) - ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false); + ufshcd_vreg_set_lpm(hba); disable_irq_and_vops_clks: ufshcd_disable_irq(hba); - if (hba->vops && hba->vops->setup_clocks) - ret = hba->vops->setup_clocks(hba, false); -disable_clks: ufshcd_setup_clocks(hba, false); out: hba->pm_op_in_progress = 0; @@ -5047,6 +5557,36 @@ int ufshcd_runtime_idle(struct ufs_hba *hba) EXPORT_SYMBOL(ufshcd_runtime_idle); /** + * ufshcd_shutdown - shutdown routine + * @hba: per adapter instance + * + * This function would power off both UFS device and UFS link. + * + * Returns 0 always to allow force shutdown even in case of errors. + */ +int ufshcd_shutdown(struct ufs_hba *hba) +{ + int ret = 0; + + if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba)) + goto out; + + if (pm_runtime_suspended(hba->dev)) { + ret = ufshcd_runtime_resume(hba); + if (ret) + goto out; + } + + ret = ufshcd_suspend(hba, UFS_SHUTDOWN_PM); +out: + if (ret) + dev_err(hba->dev, "%s failed, err %d\n", __func__, ret); + /* allow force shutdown even in case of errors */ + return 0; +} +EXPORT_SYMBOL(ufshcd_shutdown); + +/** * ufshcd_remove - de-allocate SCSI host and host memory space * data structure memory * @hba - per adapter instance @@ -5060,6 +5600,7 @@ void ufshcd_remove(struct ufs_hba *hba) scsi_host_put(hba->host); + ufshcd_exit_clk_gating(hba); ufshcd_hba_exit(hba); } EXPORT_SYMBOL_GPL(ufshcd_remove); @@ -5205,11 +5746,12 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) /* Initialize device management tag acquire wait queue */ init_waitqueue_head(&hba->dev_cmd.tag_wq); + ufshcd_init_clk_gating(hba); /* IRQ registration */ err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba); if (err) { dev_err(hba->dev, "request irq failed\n"); - goto out_disable; + goto exit_gating; } else { hba->is_irq_enabled = true; } @@ -5218,13 +5760,13 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) err = scsi_init_shared_tag_map(host, host->can_queue); if (err) { dev_err(hba->dev, "init shared queue failed\n"); - goto out_disable; + goto exit_gating; } err = scsi_add_host(host, hba->dev); if (err) { dev_err(hba->dev, "scsi_add_host failed\n"); - goto out_disable; + goto exit_gating; } /* Host controller enable */ @@ -5246,6 +5788,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) out_remove_scsi_host: scsi_remove_host(hba->host); +exit_gating: + ufshcd_exit_clk_gating(hba); out_disable: hba->is_irq_enabled = false; scsi_host_put(host); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 5f518b522170..1c356c17db12 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -102,10 +102,12 @@ struct uic_command { enum ufs_pm_op { UFS_RUNTIME_PM, UFS_SYSTEM_PM, + UFS_SHUTDOWN_PM, }; #define ufshcd_is_runtime_pm(op) ((op) == UFS_RUNTIME_PM) #define ufshcd_is_system_pm(op) ((op) == UFS_SYSTEM_PM) +#define ufshcd_is_shutdown_pm(op) ((op) == UFS_SHUTDOWN_PM) /* Host <-> Device UniPro Link state */ enum uic_link_state { @@ -281,6 +283,38 @@ struct ufs_hba_variant_ops { int (*resume)(struct ufs_hba *, enum ufs_pm_op); }; +/* clock gating state */ +enum clk_gating_state { + CLKS_OFF, + CLKS_ON, + REQ_CLKS_OFF, + REQ_CLKS_ON, +}; + +/** + * struct ufs_clk_gating - UFS clock gating related info + * @gate_work: worker to turn off clocks after some delay as specified in + * delay_ms + * @ungate_work: worker to turn on clocks that will be used in case of + * interrupt context + * @state: the current clocks state + * @delay_ms: gating delay in ms + * @is_suspended: clk gating is suspended when set to 1 which can be used + * during suspend/resume + * @delay_attr: sysfs attribute to control delay_attr + * @active_reqs: number of requests that are pending and should be waited for + * completion before gating clocks. + */ +struct ufs_clk_gating { + struct delayed_work gate_work; + struct work_struct ungate_work; + enum clk_gating_state state; + unsigned long delay_ms; + bool is_suspended; + struct device_attribute delay_attr; + int active_reqs; +}; + /** * struct ufs_hba - per adapter private structure * @mmio_base: UFSHCI base register address @@ -408,6 +442,8 @@ struct ufs_hba { */ #define UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS (1 << 7) + #define UFSHCD_QUIRK_BROKEN_2_TX_LANES (1 << 8) + wait_queue_head_t tm_wq; wait_queue_head_t tm_tag_wq; unsigned long tm_condition; @@ -436,16 +472,39 @@ struct ufs_hba { /* Device management request data */ struct ufs_dev_cmd dev_cmd; + /* Keeps information of the UFS device connected to this host */ + struct ufs_dev_info dev_info; bool auto_bkops_enabled; struct ufs_vreg_info vreg_info; struct list_head clk_list_head; + struct ufs_pa_layer_attr pwr_info; + struct ufs_clk_gating clk_gating; + /* Control to enable/disable host capabilities */ + u32 caps; + /* Allow dynamic clk gating */ +#define UFSHCD_CAP_CLK_GATING (1 << 0) + /* Allow hiberb8 with clk gating */ +#define UFSHCD_CAP_HIBERN8_WITH_CLK_GATING (1 << 1) + #ifdef CONFIG_DEBUG_FS struct ufs_stats ufs_stats; struct debugfs_files debugfs_files; #endif }; +/* Returns true if clocks can be gated. Otherwise false */ +static inline bool ufshcd_is_clkgating_allowed(struct ufs_hba *hba) +{ + return hba->caps & UFSHCD_CAP_CLK_GATING; +} +static inline bool ufshcd_can_hibern8_during_gating(struct ufs_hba *hba) +{ + if (!(hba->quirks & UFSHCD_QUIRK_BROKEN_HIBERN8)) + return hba->caps & UFSHCD_CAP_HIBERN8_WITH_CLK_GATING; + else + return false; +} #define ufshcd_writel(hba, val, reg) \ writel((val), (hba)->mmio_base + (reg)) #define ufshcd_readl(hba, reg) \ @@ -502,6 +561,7 @@ extern int ufshcd_runtime_resume(struct ufs_hba *hba); extern int ufshcd_runtime_idle(struct ufs_hba *hba); extern int ufshcd_system_suspend(struct ufs_hba *hba); extern int ufshcd_system_resume(struct ufs_hba *hba); +extern int ufshcd_shutdown(struct ufs_hba *hba); extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel, u8 attr_set, u32 mib_val, u8 peer); extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, @@ -572,4 +632,6 @@ int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, int ufshcd_query_descriptor(struct ufs_hba *hba, enum query_opcode opcode, enum attr_idn idn, u8 index, u8 selector, u8 *desc_buf, int *buf_len); +int ufshcd_hold(struct ufs_hba *hba, bool async); +void ufshcd_release(struct ufs_hba *hba); #endif /* End of Header */ diff --git a/drivers/scsi/ufs/unipro.h b/drivers/scsi/ufs/unipro.h index baf45aeece05..fb572aa301ee 100644 --- a/drivers/scsi/ufs/unipro.h +++ b/drivers/scsi/ufs/unipro.h @@ -92,6 +92,24 @@ enum { PA_HS_MODE_B = 2, }; +enum ufs_pwm_gear_tag { + UFS_PWM_DONT_CHANGE, /* Don't change Gear */ + UFS_PWM_G1, /* PWM Gear 1 (default for reset) */ + UFS_PWM_G2, /* PWM Gear 2 */ + UFS_PWM_G3, /* PWM Gear 3 */ + UFS_PWM_G4, /* PWM Gear 4 */ + UFS_PWM_G5, /* PWM Gear 5 */ + UFS_PWM_G6, /* PWM Gear 6 */ + UFS_PWM_G7, /* PWM Gear 7 */ +}; + +enum ufs_hs_gear_tag { + UFS_HS_DONT_CHANGE, /* Don't change Gear */ + UFS_HS_G1, /* HS Gear 1 (default for reset) */ + UFS_HS_G2, /* HS Gear 2 */ + UFS_HS_G3, /* HS Gear 3 */ +}; + /* * Data Link Layer Attributes */ diff --git a/drivers/sensors/sensors_class.c b/drivers/sensors/sensors_class.c index 74e0d8d3551f..bbf168c10623 100644 --- a/drivers/sensors/sensors_class.c +++ b/drivers/sensors/sensors_class.c @@ -105,6 +105,78 @@ static ssize_t sensors_fifo_max_show(struct device *dev, sensors_cdev->fifo_max_event_count); } +static ssize_t sensors_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + unsigned long data = 0; + + ret = kstrtoul(buf, 10, &data); + if (ret) + return ret; + if (data > 1) { + dev_err(dev, "Invalid value of input, input=%ld\n", data); + return -EINVAL; + } + + if (sensors_cdev->sensors_enable == NULL) { + dev_err(dev, "Invalid sensor class enable handle\n"); + return -EINVAL; + } + ret = sensors_cdev->sensors_enable(sensors_cdev, data); + if (ret) + return ret; + + sensors_cdev->enabled = data; + return size; +} + + +static ssize_t sensors_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%u\n", + sensors_cdev->enabled); +} + +static ssize_t sensors_delay_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + unsigned long data = 0; + + ret = kstrtoul(buf, 10, &data); + if (ret) + return ret; + /* The data unit is millisecond, the min_delay unit is microseconds. */ + if ((data * 1000) < sensors_cdev->min_delay) { + dev_err(dev, "Invalid value of delay, delay=%ld\n", data); + return -EINVAL; + } + if (sensors_cdev->sensors_poll_delay == NULL) { + dev_err(dev, "Invalid sensor class delay handle\n"); + return -EINVAL; + } + ret = sensors_cdev->sensors_poll_delay(sensors_cdev, data); + if (ret) + return ret; + + sensors_cdev->delay_msec = data; + return size; +} + +static ssize_t sensors_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%u\n", + sensors_cdev->delay_msec); +} + + static struct device_attribute sensors_class_attrs[] = { __ATTR(name, 0444, sensors_name_show, NULL), __ATTR(vendor, 0444, sensors_vendor_show, NULL), @@ -117,6 +189,8 @@ static struct device_attribute sensors_class_attrs[] = { __ATTR(min_delay, 0444, sensors_min_delay_show, NULL), __ATTR(fifo_reserved_event_count, 0444, sensors_fifo_event_show, NULL), __ATTR(fifo_max_event_count, 0444, sensors_fifo_max_show, NULL), + __ATTR(enable, 0664, sensors_enable_show, sensors_enable_store), + __ATTR(poll_delay, 0664, sensors_delay_show, sensors_delay_store), __ATTR_NULL, }; diff --git a/drivers/sh/clk/core.c b/drivers/sh/clk/core.c index 7715de2629c1..74727851820d 100644 --- a/drivers/sh/clk/core.c +++ b/drivers/sh/clk/core.c @@ -63,12 +63,12 @@ void clk_rate_table_build(struct clk *clk, else freq = clk->parent->rate * mult / div; - freq_table[i].index = i; + freq_table[i].driver_data = i; freq_table[i].frequency = freq; } /* Termination entry */ - freq_table[i].index = i; + freq_table[i].driver_data = i; freq_table[i].frequency = CPUFREQ_TABLE_END; } diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c index 74a4fd953bfa..7f09ba780045 100644 --- a/drivers/slimbus/slim-msm-ngd.c +++ b/drivers/slimbus/slim-msm-ngd.c @@ -27,7 +27,7 @@ #include <linux/timer.h> #include <mach/sps.h> #include "slim-msm.h" -#include <mach/qdsp6v2/apr.h> +#include <linux/qdsp6v2/apr.h> #define NGD_SLIM_NAME "ngd_msm_ctrl" #define SLIM_LA_MGR 0xFF diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig new file mode 100644 index 000000000000..b6f0ff198396 --- /dev/null +++ b/drivers/soc/Kconfig @@ -0,0 +1 @@ +source "drivers/soc/qcom/Kconfig" diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile new file mode 100644 index 000000000000..9d8ecdc6a875 --- /dev/null +++ b/drivers/soc/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ARCH_MSM) += qcom/ diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig new file mode 100644 index 000000000000..1ea417e3b313 --- /dev/null +++ b/drivers/soc/qcom/Kconfig @@ -0,0 +1,43 @@ +# When adding new entries keep the list in alphabetical order + +if ARCH_MSM + +config MSM_SMEM + depends on REMOTE_SPINLOCK_MSM + bool "MSM Shared Memory (SMEM)" + help + Support for the shared memory interface between the various + processors in the System on a Chip (SoC) which allows basic + inter-processor communication. + +config MSM_QDSP6_APRV2 + bool "Audio QDSP6 APRv2 support" + depends on MSM_SMD + help + Enable APRv2 IPC protocol support between + application processor and QDSP6. APR is + used by audio driver to configure QDSP6's + ASM, ADM and AFE. + +config MSM_QDSP6_APRV3 + bool "Audio QDSP6 APRv3 support" + depends on MSM_SMD + help + Enable APRv2 IPC protocol support between + application processor and QDSP6. APR is + used by audio driver to configure QDSP6v2's + ASM, ADM and AFE. + +config MSM_ADSP_LOADER + tristate "ADSP loader support" + select SND_SOC_MSM_APRV2_INTF + depends on MSM_QDSP6_APRV2 || MSM_QDSP6_APRV3 + help + Enable ADSP image loader. + The ADSP loader brings ADSP out of reset + for the platforms that use APRv2. + Say M if you want to enable this module. + +source "drivers/soc/qcom/memshare/Kconfig" + +endif # ARCH_MSM diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile new file mode 100644 index 000000000000..83ec3bab5f99 --- /dev/null +++ b/drivers/soc/qcom/Makefile @@ -0,0 +1,6 @@ +# When adding new entries keep the list in alphabetical order + +obj-y += qdsp6v2/ + +obj-$(CONFIG_MSM_SMEM) += smem.o smem_debug.o +obj-$(CONFIG_MSM_QMI_INTERFACE) += memshare/ diff --git a/drivers/soc/qcom/memshare/Kconfig b/drivers/soc/qcom/memshare/Kconfig new file mode 100644 index 000000000000..7eb1415b350b --- /dev/null +++ b/drivers/soc/qcom/memshare/Kconfig @@ -0,0 +1,9 @@ +config MEM_SHARE_QMI_SERVICE + depends on MSM_QMI_INTERFACE + bool "Shared Heap for external processors" + help + Memory Share Kernel Qualcomm Messaging Interface Service + receives requests from Modem Processor Sub System + for heap alloc/free from Application Processor + Sub System and send a response back to client with + proper handle/address. diff --git a/drivers/soc/qcom/memshare/Makefile b/drivers/soc/qcom/memshare/Makefile new file mode 100644 index 000000000000..c119ad317ed7 --- /dev/null +++ b/drivers/soc/qcom/memshare/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MSM_QMI_INTERFACE) := heap_mem_ext_v01.o msm_memshare.o
\ No newline at end of file diff --git a/drivers/soc/qcom/memshare/heap_mem_ext_v01.c b/drivers/soc/qcom/memshare/heap_mem_ext_v01.c new file mode 100644 index 000000000000..3f9fe995fc87 --- /dev/null +++ b/drivers/soc/qcom/memshare/heap_mem_ext_v01.c @@ -0,0 +1,138 @@ +/* Copyright (c) 2013, The Linux Foundation. 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 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. + * + */ + +#include <linux/qmi_encdec.h> +#include <mach/msm_qmi_interface.h> +#include "heap_mem_ext_v01.h" + +struct elem_info mem_alloc_req_msg_data_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct mem_alloc_req_msg_v01, + num_bytes), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct mem_alloc_req_msg_v01, + block_alignment_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct mem_alloc_req_msg_v01, + block_alignment), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info mem_alloc_resp_msg_data_v01_ei[] = { + { + .data_type = QMI_SIGNED_2_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(uint16_t), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct mem_alloc_resp_msg_v01, + resp), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct mem_alloc_resp_msg_v01, + handle_valid), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint64_t), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct mem_alloc_resp_msg_v01, + handle), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct mem_alloc_resp_msg_v01, + num_bytes_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct mem_alloc_resp_msg_v01, + num_bytes), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info mem_free_req_msg_data_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint64_t), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct mem_free_req_msg_v01, + handle), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info mem_free_resp_msg_data_v01_ei[] = { + { + .data_type = QMI_SIGNED_2_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(uint16_t), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct mem_free_resp_msg_v01, + resp), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; diff --git a/drivers/soc/qcom/memshare/heap_mem_ext_v01.h b/drivers/soc/qcom/memshare/heap_mem_ext_v01.h new file mode 100644 index 000000000000..bc2a8cd69b98 --- /dev/null +++ b/drivers/soc/qcom/memshare/heap_mem_ext_v01.h @@ -0,0 +1,143 @@ +/* Copyright (c) 2013, The Linux Foundation. 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 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 HEAP_MEM_EXT_SERVICE_01_H +#define HEAP_MEM_EXT_SERVICE_01_H + +#include <mach/msm_qmi_interface.h> + +#define MEM_ALLOC_REQ_MAX_MSG_LEN_V01 255 +#define MEM_FREE_REQ_MAX_MSG_LEN_V01 255 + +enum dhms_mem_block_align_enum_v01 { + /* To force a 32 bit signed enum. Do not change or use + */ + DHMS_MEM_BLOCK_ALIGN_ENUM_MIN_ENUM_VAL_V01 = -2147483647, + /* Align allocated memory by 2 bytes */ + DHMS_MEM_BLOCK_ALIGN_2_V01 = 0, + /* Align allocated memory by 4 bytes */ + DHMS_MEM_BLOCK_ALIGN_4_V01 = 1, + /**< Align allocated memory by 8 bytes */ + DHMS_MEM_BLOCK_ALIGN_8_V01 = 2, + /**< Align allocated memory by 16 bytes */ + DHMS_MEM_BLOCK_ALIGN_16_V01 = 3, + /**< Align allocated memory by 32 bytes */ + DHMS_MEM_BLOCK_ALIGN_32_V01 = 4, + /**< Align allocated memory by 64 bytes */ + DHMS_MEM_BLOCK_ALIGN_64_V01 = 5, + /**< Align allocated memory by 128 bytes */ + DHMS_MEM_BLOCK_ALIGN_128_V01 = 6, + /**< Align allocated memory by 256 bytes */ + DHMS_MEM_BLOCK_ALIGN_256_V01 = 7, + /**< Align allocated memory by 512 bytes */ + DHMS_MEM_BLOCK_ALIGN_512_V01 = 8, + /**< Align allocated memory by 1024 bytes */ + DHMS_MEM_BLOCK_ALIGN_1K_V01 = 9, + /**< Align allocated memory by 2048 bytes */ + DHMS_MEM_BLOCK_ALIGN_2K_V01 = 10, + /**< Align allocated memory by 4096 bytes */ + DHMS_MEM_BLOCK_ALIGN_4K_V01 = 11, + DHMS_MEM_BLOCK_ALIGN_ENUM_MAX_ENUM_VAL_V01 = 2147483647 + /* To force a 32 bit signed enum. Do not change or use + */ +}; + +/* Request Message; This command is used for getting + * the multiple physically contiguous + * memory blocks from the server memory subsystem + */ +struct mem_alloc_req_msg_v01 { + + /* Mandatory */ + /*requested size*/ + uint32_t num_bytes; + + /* Optional */ + /* Must be set to true if block_alignment + * is being passed + */ + uint8_t block_alignment_valid; + /* The block alignment for the memory block to be allocated + */ + enum dhms_mem_block_align_enum_v01 block_alignment; +}; /* Message */ + +/* Response Message; This command is used for getting + * the multiple physically contiguous memory blocks + * from the server memory subsystem + */ +struct mem_alloc_resp_msg_v01 { + + /* Mandatory */ + /* Result Code */ + /* The result of the requested memory operation + */ + enum qmi_result_type_v01 resp; + /* Optional */ + /* Memory Block Handle + */ + /* Must be set to true if handle is being passed + */ + uint8_t handle_valid; + /* The physical address of the memory allocated on the HLOS + */ + uint64_t handle; + /* Optional */ + /* Memory block size */ + /* Must be set to true if num_bytes is being passed + */ + uint8_t num_bytes_valid; + /* The number of bytes actually allocated for the request. + * This value can be smaller than the size requested in + * QMI_DHMS_MEM_ALLOC_REQ_MSG. + */ + uint32_t num_bytes; +}; /* Message */ + +/* Request Message; This command is used for releasing + * the multiple physically contiguous + * memory blocks to the server memory subsystem + */ +struct mem_free_req_msg_v01 { + + /* Mandatory */ + /* Physical address of memory to be freed + */ + uint32_t handle; +}; /* Message */ + +/* Response Message; This command is used for releasing + * the multiple physically contiguous + * memory blocks to the server memory subsystem + */ +struct mem_free_resp_msg_v01 { + + /* Mandatory */ + /* Result of the requested memory operation, todo, + * need to check the async operation for free + */ + enum qmi_result_type_v01 resp; +}; /* Message */ + +extern struct elem_info mem_alloc_req_msg_data_v01_ei[]; +extern struct elem_info mem_alloc_resp_msg_data_v01_ei[]; +extern struct elem_info mem_free_req_msg_data_v01_ei[]; +extern struct elem_info mem_free_resp_msg_data_v01_ei[]; + +/*Service Message Definition*/ +#define MEM_ALLOC_REQ_MSG_V01 0x0020 +#define MEM_ALLOC_RESP_MSG_V01 0x0020 +#define MEM_FREE_REQ_MSG_V01 0x0021 +#define MEM_FREE_RESP_MSG_V01 0x0021 + +#endif diff --git a/drivers/soc/qcom/memshare/msm_memshare.c b/drivers/soc/qcom/memshare/msm_memshare.c new file mode 100644 index 000000000000..7371717f268a --- /dev/null +++ b/drivers/soc/qcom/memshare/msm_memshare.c @@ -0,0 +1,312 @@ +/* Copyright (c) 2013, The Linux Foundation. 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 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. + * + */ +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/mutex.h> +#include <mach/scm.h> +#include <mach/msm_qmi_interface.h> +#include "msm_memshare.h" +#include "heap_mem_ext_v01.h" +#define MEM_SHARE_SERVICE_SVC_ID 0x00000034 +#define MEM_SHARE_SERVICE_INS_ID 1 +#define MEM_SHARE_SERVICE_VERS 1 + +static struct qmi_handle *mem_share_svc_handle; +static void mem_share_svc_recv_msg(struct work_struct *work); +static DECLARE_DELAYED_WORK(work_recv_msg, mem_share_svc_recv_msg); +static struct workqueue_struct *mem_share_svc_workqueue; +static void *curr_conn; +struct mutex connection; +static struct mem_blocks memblock; +struct mutex mem_share; +struct mutex mem_free; +static uint32_t size; + +static struct msg_desc mem_share_svc_alloc_req_desc = { + .max_msg_len = MEM_ALLOC_REQ_MAX_MSG_LEN_V01, + .msg_id = MEM_ALLOC_REQ_MSG_V01, + .ei_array = mem_alloc_req_msg_data_v01_ei, +}; + +static struct msg_desc mem_share_svc_alloc_resp_desc = { + .max_msg_len = MEM_ALLOC_REQ_MAX_MSG_LEN_V01, + .msg_id = MEM_ALLOC_RESP_MSG_V01, + .ei_array = mem_alloc_resp_msg_data_v01_ei, +}; + +static struct msg_desc mem_share_svc_free_req_desc = { + .max_msg_len = MEM_FREE_REQ_MAX_MSG_LEN_V01, + .msg_id = MEM_FREE_REQ_MSG_V01, + .ei_array = mem_free_req_msg_data_v01_ei, +}; + +static struct msg_desc mem_share_svc_free_resp_desc = { + .max_msg_len = MEM_FREE_REQ_MAX_MSG_LEN_V01, + .msg_id = MEM_FREE_RESP_MSG_V01, + .ei_array = mem_free_resp_msg_data_v01_ei, +}; + + +static int handle_alloc_req(void *req_h, void *req) +{ + struct mem_alloc_req_msg_v01 *alloc_req; + struct mem_alloc_resp_msg_v01 alloc_resp; + int rc; + + alloc_req = (struct mem_alloc_req_msg_v01 *)req; + pr_debug("%s: Received Alloc Request\n", __func__); + pr_debug("%s: req->num_bytes = %d\n", __func__, alloc_req->num_bytes); + alloc_resp.resp = QMI_RESULT_FAILURE_V01; + mutex_lock(&mem_share); + memset(&alloc_resp, 0, sizeof(struct mem_alloc_resp_msg_v01)); + rc = memshare_alloc(alloc_req->num_bytes, + alloc_req->block_alignment, + &memblock); + if (rc) { + mutex_unlock(&mem_share); + return -ENOMEM; + } + + alloc_resp.num_bytes_valid = 1; + alloc_resp.num_bytes = alloc_req->num_bytes; + size = alloc_req->num_bytes; + alloc_resp.handle_valid = 1; + alloc_resp.handle = memblock.phy_addr; + alloc_resp.resp = QMI_RESULT_SUCCESS_V01; + mutex_unlock(&mem_share); + pr_debug("alloc_resp.num_bytes :%d, alloc_resp.handle :%lx, alloc_resp.mem_req_result :%lx\n", + alloc_resp.num_bytes, + (unsigned long int)alloc_resp.handle, + (unsigned long int)alloc_resp.resp); + rc = qmi_send_resp_from_cb(mem_share_svc_handle, curr_conn, req_h, + &mem_share_svc_alloc_resp_desc, &alloc_resp, + sizeof(alloc_resp)); + return rc; +} +static int handle_free_req(void *req_h, void *req) +{ + struct mem_free_req_msg_v01 *free_req; + struct mem_free_resp_msg_v01 free_resp; + int rc; + + free_req = (struct mem_free_req_msg_v01 *)req; + pr_debug("%s: Received Free Request\n", __func__); + mutex_lock(&mem_free); + free_resp.resp = QMI_RESULT_FAILURE_V01; + + memset(&free_resp, 0, sizeof(struct mem_free_resp_msg_v01)); + pr_debug("In %s: pblk->virtual_addr :%lx, pblk->phy_addr %lx\n,size: %d", + __func__, + (unsigned long int)memblock.virtual_addr, + (unsigned long int)free_req->handle, size); + dma_free_coherent(NULL, size, + memblock.virtual_addr, free_req->handle); + mutex_unlock(&mem_free); + free_resp.resp = QMI_RESULT_SUCCESS_V01; + + rc = qmi_send_resp_from_cb(mem_share_svc_handle, curr_conn, req_h, + &mem_share_svc_free_resp_desc, &free_resp, + sizeof(free_resp)); + + return rc; +} + +static int mem_share_svc_connect_cb(struct qmi_handle *handle, + void *conn_h) +{ + if (mem_share_svc_handle != handle || !conn_h) + return -EINVAL; + mutex_lock(&connection); + if (curr_conn) { + pr_err("%s: Service is busy\n", __func__); + mutex_unlock(&connection); + return -EBUSY; + } + curr_conn = conn_h; + mutex_unlock(&connection); + return 0; +} + +static int mem_share_svc_disconnect_cb(struct qmi_handle *handle, + void *conn_h) +{ + mutex_lock(&connection); + if (mem_share_svc_handle != handle || curr_conn != conn_h) { + mutex_unlock(&connection); + return -EINVAL; + } + curr_conn = NULL; + mutex_unlock(&connection); + return 0; +} + +static int mem_share_svc_req_desc_cb(unsigned int msg_id, + struct msg_desc **req_desc) +{ + int rc; + + pr_debug("memshare: In %s\n", __func__); + switch (msg_id) { + case MEM_ALLOC_REQ_MSG_V01: + *req_desc = &mem_share_svc_alloc_req_desc; + rc = sizeof(struct mem_alloc_req_msg_v01); + break; + + case MEM_FREE_REQ_MSG_V01: + *req_desc = &mem_share_svc_free_req_desc; + rc = sizeof(struct mem_free_req_msg_v01); + break; + + default: + rc = -ENOTSUPP; + break; + } + return rc; +} + +static int mem_share_svc_req_cb(struct qmi_handle *handle, void *conn_h, + void *req_h, unsigned int msg_id, void *req) +{ + int rc; + + pr_debug("memshare: In %s\n", __func__); + if (mem_share_svc_handle != handle || curr_conn != conn_h) + return -EINVAL; + + switch (msg_id) { + case MEM_ALLOC_REQ_MSG_V01: + rc = handle_alloc_req(req_h, req); + break; + + case MEM_FREE_REQ_MSG_V01: + rc = handle_free_req(req_h, req); + break; + + default: + rc = -ENOTSUPP; + break; + } + return rc; +} + +static void mem_share_svc_recv_msg(struct work_struct *work) +{ + int rc; + + pr_debug("memshare: In %s\n", __func__); + do { + pr_debug("%s: Notified about a Receive Event", __func__); + } while ((rc = qmi_recv_msg(mem_share_svc_handle)) == 0); + + if (rc != -ENOMSG) + pr_err("%s: Error receiving message\n", __func__); +} + +static void qmi_mem_share_svc_ntfy(struct qmi_handle *handle, + enum qmi_event_type event, void *priv) +{ + pr_debug("memshare: In %s\n", __func__); + switch (event) { + case QMI_RECV_MSG: + queue_delayed_work(mem_share_svc_workqueue, + &work_recv_msg, 0); + break; + default: + break; + } +} + +static struct qmi_svc_ops_options mem_share_svc_ops_options = { + .version = 1, + .service_id = MEM_SHARE_SERVICE_SVC_ID, + .service_vers = MEM_SHARE_SERVICE_VERS, + .service_ins = MEM_SHARE_SERVICE_INS_ID, + .connect_cb = mem_share_svc_connect_cb, + .disconnect_cb = mem_share_svc_disconnect_cb, + .req_desc_cb = mem_share_svc_req_desc_cb, + .req_cb = mem_share_svc_req_cb, +}; + +int memshare_alloc(unsigned int block_size, + unsigned int block_algn, + struct mem_blocks *pblk) +{ + + int ret; + + pr_debug("%s: memshare_alloc called", __func__); + if (!pblk) { + pr_err("%s: Failed to alloc\n", __func__); + return -ENOMEM; + } + + pblk->virtual_addr = dma_alloc_coherent(NULL, block_size, + &pblk->phy_addr, GFP_KERNEL); + if (pblk->virtual_addr == NULL) { + pr_err("allocation failed, %d\n", block_size); + ret = -ENOMEM; + return ret; + } + pr_debug("pblk->phy_addr :%lx, pblk->virtual_addr %lx\n", + (unsigned long int)pblk->phy_addr, + (unsigned long int)pblk->virtual_addr); + return 0; +} + +static int __init memshare_init(void) +{ + int rc; + + mem_share_svc_workqueue = + create_singlethread_workqueue("mem_share_svc"); + if (!mem_share_svc_workqueue) + return -ENOMEM; + + mem_share_svc_handle = qmi_handle_create(qmi_mem_share_svc_ntfy, NULL); + if (!mem_share_svc_handle) { + pr_err("%s: Creating mem_share_svc qmi handle failed\n", + __func__); + destroy_workqueue(mem_share_svc_workqueue); + return -ENOMEM; + } + rc = qmi_svc_register(mem_share_svc_handle, &mem_share_svc_ops_options); + if (rc < 0) { + pr_err("%s: Registering mem share svc failed %d\n", + __func__, rc); + qmi_handle_destroy(mem_share_svc_handle); + destroy_workqueue(mem_share_svc_workqueue); + return rc; + } + mutex_init(&connection); + mutex_init(&mem_share); + mutex_init(&mem_free); + pr_info("memshare: memshare_init successful\n"); + + return 0; +} + +static void __exit memshare_exit(void) +{ + qmi_svc_unregister(mem_share_svc_handle); + flush_workqueue(mem_share_svc_workqueue); + qmi_handle_destroy(mem_share_svc_handle); + destroy_workqueue(mem_share_svc_workqueue); +} + +module_init(memshare_init); +module_exit(memshare_exit); + +MODULE_DESCRIPTION("Mem Share QMI Service Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/memshare/msm_memshare.h b/drivers/soc/qcom/memshare/msm_memshare.h new file mode 100644 index 000000000000..9f12f3a267a3 --- /dev/null +++ b/drivers/soc/qcom/memshare/msm_memshare.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2013, The Linux Foundation. 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 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_MEM_SHARE_H +#define _LINUX_MEM_SHARE_H + + +struct mem_blocks { + /* start address of the memory block reserved by server memory + * subsystem to client + */ + phys_addr_t phy_addr; + /* virtual address during allocation of the physical memory + */ + void *virtual_addr; +}; +int memshare_alloc(unsigned int block_size, + unsigned int block_algn, + struct mem_blocks *pblk); +void memshare_free(unsigned int block_size, + struct mem_blocks *pblk); +#endif /* _LINUX_MEM_SHARE_H */ diff --git a/drivers/soc/qcom/qdsp6v2/Makefile b/drivers/soc/qcom/qdsp6v2/Makefile new file mode 100644 index 000000000000..f4770d11596f --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_MSM_QDSP6_APRV2) += apr.o apr_v2.o apr_tal.o dsp_debug.o +obj-$(CONFIG_MSM_QDSP6_APRV3) += apr.o apr_v3.o apr_tal.o +obj-$(CONFIG_MSM_QDSP6V2_CODECS) += msm_audio_ion.o +obj-$(CONFIG_MSM_ADSP_LOADER) += adsp-loader.o diff --git a/drivers/soc/qcom/qdsp6v2/adsp-loader.c b/drivers/soc/qcom/qdsp6v2/adsp-loader.c new file mode 100644 index 000000000000..d2a463194353 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/adsp-loader.c @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2012-2013, The Linux Foundation. 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 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. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <mach/subsystem_restart.h> +#include <linux/qdsp6v2/apr.h> +#include <linux/of_device.h> +#include <linux/sysfs.h> + +#define Q6_PIL_GET_DELAY_MS 100 +#define BOOT_CMD 1 + +static ssize_t adsp_boot_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count); + +struct adsp_loader_private { + void *pil_h; + struct kobject *boot_adsp_obj; + struct attribute_group *attr_group; +}; + +static struct kobj_attribute adsp_boot_attribute = + __ATTR(boot, 0220, NULL, adsp_boot_store); + +static struct attribute *attrs[] = { + &adsp_boot_attribute.attr, + NULL, +}; + +static struct platform_device *adsp_private; + +static void adsp_loader_do(struct platform_device *pdev) +{ + + struct adsp_loader_private *priv = NULL; + + const char *adsp_dt = "qcom,adsp-state"; + int rc = 0; + u32 adsp_state; + + if (!pdev) { + dev_err(&pdev->dev, "%s: Platform device null\n", __func__); + goto fail; + } + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, + "%s: Device tree information missing\n", __func__); + goto fail; + } + + rc = of_property_read_u32(pdev->dev.of_node, adsp_dt, &adsp_state); + if (rc) { + dev_err(&pdev->dev, + "%s: ADSP state = %x\n", __func__, adsp_state); + goto fail; + } + + if (adsp_state == APR_SUBSYS_DOWN) { + priv = platform_get_drvdata(pdev); + if (!priv) { + dev_err(&pdev->dev, + " %s: Private data get failed\n", __func__); + goto fail; + } + + + priv->pil_h = subsystem_get("adsp"); + if (IS_ERR(priv->pil_h)) { + dev_err(&pdev->dev, "%s: pil get failed,\n", + __func__); + goto fail; + } + + /* Set the state of the ADSP in APR driver */ + apr_set_q6_state(APR_SUBSYS_LOADED); + } else if (adsp_state == APR_SUBSYS_LOADED) { + dev_dbg(&pdev->dev, + "%s: ADSP state = %x\n", __func__, adsp_state); + apr_set_q6_state(APR_SUBSYS_LOADED); + } + + + dev_info(&pdev->dev, "%s: Q6/ADSP image is loaded\n", __func__); + return; +fail: + + dev_err(&pdev->dev, "%s: Q6/ADSP image loading failed\n", __func__); + return; +} + + +static ssize_t adsp_boot_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t count) +{ + int boot = 0; + sscanf(buf, "%du", &boot); + + if (boot == BOOT_CMD) { + pr_debug("%s:going to call adsp_loader_do", __func__); + adsp_loader_do(adsp_private); + } + return count; +} + +static int adsp_loader_init_sysfs(struct platform_device *pdev) +{ + int ret = -EINVAL; + struct adsp_loader_private *priv = NULL; + adsp_private = NULL; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "%s: memory alloc failed\n", __func__); + ret = -ENOMEM; + return ret; + } + + platform_set_drvdata(pdev, priv); + + priv->pil_h = NULL; + priv->boot_adsp_obj = NULL; + priv->attr_group = devm_kzalloc(&pdev->dev, + sizeof(*(priv->attr_group)), + GFP_KERNEL); + if (!priv->attr_group) { + dev_err(&pdev->dev, "%s: malloc attr_group failed\n", + __func__); + ret = -ENOMEM; + goto error_return; + } + + priv->attr_group->attrs = attrs; + + priv->boot_adsp_obj = kobject_create_and_add("boot_adsp", kernel_kobj); + if (!priv->boot_adsp_obj) { + dev_err(&pdev->dev, "%s: sysfs create and add failed\n", + __func__); + ret = -ENOMEM; + goto error_return; + } + + ret = sysfs_create_group(priv->boot_adsp_obj, priv->attr_group); + if (ret) { + dev_err(&pdev->dev, "%s: sysfs create group failed %d\n", + __func__, ret); + goto error_return; + } + + adsp_private = pdev; + + return 0; + +error_return: + + if (priv->boot_adsp_obj) { + kobject_del(priv->boot_adsp_obj); + priv->boot_adsp_obj = NULL; + } + + return ret; +} + +static int adsp_loader_remove(struct platform_device *pdev) +{ + struct adsp_loader_private *priv = NULL; + + priv = platform_get_drvdata(pdev); + + if (!priv) + return 0; + + if (priv->pil_h) { + subsystem_put(priv->pil_h); + priv->pil_h = NULL; + } + + if (priv->boot_adsp_obj) { + sysfs_remove_group(priv->boot_adsp_obj, priv->attr_group); + kobject_del(priv->boot_adsp_obj); + priv->boot_adsp_obj = NULL; + } + + return 0; +} + +static int adsp_loader_probe(struct platform_device *pdev) +{ + int ret = adsp_loader_init_sysfs(pdev); + if (ret != 0) { + dev_err(&pdev->dev, "%s: Error in initing sysfs\n", __func__); + return ret; + } + + return 0; +} + +static const struct of_device_id adsp_loader_dt_match[] = { + { .compatible = "qcom,adsp-loader" }, + { } +}; +MODULE_DEVICE_TABLE(of, adsp_loader_dt_match); + +static struct platform_driver adsp_loader_driver = { + .driver = { + .name = "adsp-loader", + .owner = THIS_MODULE, + .of_match_table = adsp_loader_dt_match, + }, + .probe = adsp_loader_probe, + .remove = adsp_loader_remove, +}; + +static int __init adsp_loader_init(void) +{ + return platform_driver_register(&adsp_loader_driver); +} +module_init(adsp_loader_init); + +static void __exit adsp_loader_exit(void) +{ + platform_driver_unregister(&adsp_loader_driver); +} +module_exit(adsp_loader_exit); + +MODULE_DESCRIPTION("ADSP Loader module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/qdsp6v2/apr.c b/drivers/soc/qcom/qdsp6v2/apr.c new file mode 100644 index 000000000000..c3dd3d367099 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/apr.c @@ -0,0 +1,834 @@ +/* Copyright (c) 2010-2013, The Linux Foundation. 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 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <linux/spinlock.h> +#include <linux/list.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/delay.h> +#include <linux/debugfs.h> +#include <linux/platform_device.h> +#include <linux/sysfs.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <sound/apr_audio-v2.h> +#include <mach/subsystem_restart.h> +#include <mach/msm_smd.h> +#include <linux/qdsp6v2/apr.h> +#include <linux/qdsp6v2/apr_tal.h> +#include <linux/qdsp6v2/dsp_debug.h> +#include <mach/subsystem_notif.h> +#include <mach/subsystem_restart.h> + +static struct apr_q6 q6; +static struct apr_client client[APR_DEST_MAX][APR_CLIENT_MAX]; + +static wait_queue_head_t dsp_wait; +static wait_queue_head_t modem_wait; +/* Subsystem restart: QDSP6 data, functions */ +static struct workqueue_struct *apr_reset_workqueue; +static void apr_reset_deregister(struct work_struct *work); +struct apr_reset_work { + void *handle; + struct work_struct work; +}; + +struct apr_svc_table { + char name[64]; + int idx; + int id; + int client_id; +}; + +static const struct apr_svc_table svc_tbl_qdsp6[] = { + { + .name = "AFE", + .idx = 0, + .id = APR_SVC_AFE, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "ASM", + .idx = 1, + .id = APR_SVC_ASM, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "ADM", + .idx = 2, + .id = APR_SVC_ADM, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "CORE", + .idx = 3, + .id = APR_SVC_ADSP_CORE, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "TEST", + .idx = 4, + .id = APR_SVC_TEST_CLIENT, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "MVM", + .idx = 5, + .id = APR_SVC_ADSP_MVM, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "CVS", + .idx = 6, + .id = APR_SVC_ADSP_CVS, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "CVP", + .idx = 7, + .id = APR_SVC_ADSP_CVP, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "USM", + .idx = 8, + .id = APR_SVC_USM, + .client_id = APR_CLIENT_AUDIO, + }, + { + .name = "VIDC", + .idx = 9, + .id = APR_SVC_VIDC, + }, + { + .name = "LSM", + .idx = 10, + .id = APR_SVC_LSM, + .client_id = APR_CLIENT_AUDIO, + }, +}; + +static struct apr_svc_table svc_tbl_voice[] = { + { + .name = "VSM", + .idx = 0, + .id = APR_SVC_VSM, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "VPM", + .idx = 1, + .id = APR_SVC_VPM, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "MVS", + .idx = 2, + .id = APR_SVC_MVS, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "MVM", + .idx = 3, + .id = APR_SVC_MVM, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "CVS", + .idx = 4, + .id = APR_SVC_CVS, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "CVP", + .idx = 5, + .id = APR_SVC_CVP, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "SRD", + .idx = 6, + .id = APR_SVC_SRD, + .client_id = APR_CLIENT_VOICE, + }, + { + .name = "TEST", + .idx = 7, + .id = APR_SVC_TEST_CLIENT, + .client_id = APR_CLIENT_VOICE, + }, +}; + +enum apr_subsys_state apr_get_modem_state(void) +{ + return atomic_read(&q6.modem_state); +} + +void apr_set_modem_state(enum apr_subsys_state state) +{ + atomic_set(&q6.modem_state, state); +} + +enum apr_subsys_state apr_cmpxchg_modem_state(enum apr_subsys_state prev, + enum apr_subsys_state new) +{ + return atomic_cmpxchg(&q6.modem_state, prev, new); +} + +enum apr_subsys_state apr_get_q6_state(void) +{ + return atomic_read(&q6.q6_state); +} +EXPORT_SYMBOL_GPL(apr_get_q6_state); + +int apr_set_q6_state(enum apr_subsys_state state) +{ + pr_debug("%s: setting adsp state %d\n", __func__, state); + if (state < APR_SUBSYS_DOWN || state > APR_SUBSYS_LOADED) + return -EINVAL; + atomic_set(&q6.q6_state, state); + return 0; +} +EXPORT_SYMBOL_GPL(apr_set_q6_state); + +enum apr_subsys_state apr_cmpxchg_q6_state(enum apr_subsys_state prev, + enum apr_subsys_state new) +{ + return atomic_cmpxchg(&q6.q6_state, prev, new); +} + +int apr_wait_for_device_up(int dest_id) +{ + int rc = -1; + if (dest_id == APR_DEST_MODEM) + rc = wait_event_interruptible_timeout(modem_wait, + (apr_get_modem_state() == APR_SUBSYS_UP), + (1 * HZ)); + else if (dest_id == APR_DEST_QDSP6) + rc = wait_event_interruptible_timeout(dsp_wait, + (apr_get_q6_state() == APR_SUBSYS_UP), + (1 * HZ)); + else + pr_err("%s: unknown dest_id %d\n", __func__, dest_id); + /* returns left time */ + return rc; +} + +int apr_load_adsp_image(void) +{ + int rc = 0; + mutex_lock(&q6.lock); + if (apr_get_q6_state() == APR_SUBSYS_UP) { + q6.pil = subsystem_get("adsp"); + if (IS_ERR(q6.pil)) { + rc = PTR_ERR(q6.pil); + pr_err("APR: Unable to load q6 image, error:%d\n", rc); + } else { + apr_set_q6_state(APR_SUBSYS_LOADED); + pr_debug("APR: Image is loaded, stated\n"); + } + } else if (apr_get_q6_state() == APR_SUBSYS_LOADED) { + pr_debug("APR: q6 image already loaded\n"); + } else { + pr_debug("APR: cannot load state %d\n", apr_get_q6_state()); + } + mutex_unlock(&q6.lock); + return rc; +} + +struct apr_client *apr_get_client(int dest_id, int client_id) +{ + return &client[dest_id][client_id]; +} + +int apr_send_pkt(void *handle, uint32_t *buf) +{ + struct apr_svc *svc = handle; + struct apr_client *clnt; + struct apr_hdr *hdr; + uint16_t dest_id; + uint16_t client_id; + uint16_t w_len; + unsigned long flags; + + if (!handle || !buf) { + pr_err("APR: Wrong parameters\n"); + return -EINVAL; + } + if (svc->need_reset) { + pr_err("apr: send_pkt service need reset\n"); + return -ENETRESET; + } + + if ((svc->dest_id == APR_DEST_QDSP6) && + (apr_get_q6_state() != APR_SUBSYS_LOADED)) { + pr_err("%s: Still dsp is not Up\n", __func__); + return -ENETRESET; + } else if ((svc->dest_id == APR_DEST_MODEM) && + (apr_get_modem_state() == APR_SUBSYS_DOWN)) { + pr_err("apr: Still Modem is not Up\n"); + return -ENETRESET; + } + + spin_lock_irqsave(&svc->w_lock, flags); + dest_id = svc->dest_id; + client_id = svc->client_id; + clnt = &client[dest_id][client_id]; + + if (!client[dest_id][client_id].handle) { + pr_err("APR: Still service is not yet opened\n"); + spin_unlock_irqrestore(&svc->w_lock, flags); + return -EINVAL; + } + hdr = (struct apr_hdr *)buf; + + hdr->src_domain = APR_DOMAIN_APPS; + hdr->src_svc = svc->id; + hdr->dest_domain = svc->dest_domain; + hdr->dest_svc = svc->id; + + w_len = apr_tal_write(clnt->handle, buf, hdr->pkt_size); + if (w_len != hdr->pkt_size) + pr_err("Unable to write APR pkt successfully: %d\n", w_len); + spin_unlock_irqrestore(&svc->w_lock, flags); + + return w_len; +} + +struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn, + uint32_t src_port, void *priv) +{ + struct apr_client *clnt; + int client_id = 0; + int svc_idx = 0; + int svc_id = 0; + int dest_id = 0; + int domain_id = 0; + int temp_port = 0; + struct apr_svc *svc = NULL; + int rc = 0; + + if (!dest || !svc_name || !svc_fn) + return NULL; + + if (!strcmp(dest, "ADSP")) + domain_id = APR_DOMAIN_ADSP; + else if (!strcmp(dest, "MODEM")) + domain_id = APR_DOMAIN_MODEM; + else { + pr_err("APR: wrong destination\n"); + goto done; + } + + dest_id = apr_get_dest_id(dest); + + if (dest_id == APR_DEST_QDSP6) { + if (apr_get_q6_state() != APR_SUBSYS_LOADED) { + pr_err("%s: adsp not up\n", __func__); + return NULL; + } + pr_debug("%s: adsp Up\n", __func__); + } else if (dest_id == APR_DEST_MODEM) { + if (apr_get_modem_state() == APR_SUBSYS_DOWN) { + pr_debug("%s: Wait for modem to bootup\n", __func__); + rc = apr_wait_for_device_up(APR_DEST_MODEM); + if (rc == 0) { + pr_err("%s: Modem is not Up\n", __func__); + return NULL; + } + } + pr_debug("%s: modem Up\n", __func__); + } + + if (apr_get_svc(svc_name, domain_id, &client_id, &svc_idx, &svc_id)) { + pr_err("%s: apr_get_svc failed\n", __func__); + goto done; + } + + clnt = &client[dest_id][client_id]; + mutex_lock(&clnt->m_lock); + if (!clnt->handle) { + clnt->handle = apr_tal_open(client_id, dest_id, + APR_DL_SMD, apr_cb_func, NULL); + if (!clnt->handle) { + svc = NULL; + pr_err("APR: Unable to open handle\n"); + mutex_unlock(&clnt->m_lock); + goto done; + } + } + mutex_unlock(&clnt->m_lock); + svc = &clnt->svc[svc_idx]; + mutex_lock(&svc->m_lock); + clnt->id = client_id; + if (svc->need_reset) { + mutex_unlock(&svc->m_lock); + pr_err("APR: Service needs reset\n"); + goto done; + } + svc->priv = priv; + svc->id = svc_id; + svc->dest_id = dest_id; + svc->client_id = client_id; + svc->dest_domain = domain_id; + if (src_port != 0xFFFFFFFF) { + temp_port = ((src_port >> 8) * 8) + (src_port & 0xFF); + pr_debug("port = %d t_port = %d\n", src_port, temp_port); + if (temp_port >= APR_MAX_PORTS || temp_port < 0) { + pr_err("APR: temp_port out of bounds\n"); + mutex_unlock(&svc->m_lock); + return NULL; + } + if (!svc->port_cnt && !svc->svc_cnt) + clnt->svc_cnt++; + svc->port_cnt++; + svc->port_fn[temp_port] = svc_fn; + svc->port_priv[temp_port] = priv; + } else { + if (!svc->fn) { + if (!svc->port_cnt && !svc->svc_cnt) + clnt->svc_cnt++; + svc->fn = svc_fn; + if (svc->port_cnt) + svc->svc_cnt++; + } + } + + mutex_unlock(&svc->m_lock); +done: + return svc; +} + + +void apr_cb_func(void *buf, int len, void *priv) +{ + struct apr_client_data data; + struct apr_client *apr_client; + struct apr_svc *c_svc; + struct apr_hdr *hdr; + uint16_t hdr_size; + uint16_t msg_type; + uint16_t ver; + uint16_t src; + uint16_t svc; + uint16_t clnt; + int i; + int temp_port = 0; + uint32_t *ptr; + + pr_debug("APR2: len = %d\n", len); + ptr = buf; + pr_debug("\n*****************\n"); + for (i = 0; i < len/4; i++) + pr_debug("%x ", ptr[i]); + pr_debug("\n"); + pr_debug("\n*****************\n"); + + if (!buf || len <= APR_HDR_SIZE) { + pr_err("APR: Improper apr pkt received:%p %d\n", buf, len); + return; + } + hdr = buf; + + ver = hdr->hdr_field; + ver = (ver & 0x000F); + if (ver > APR_PKT_VER + 1) { + pr_err("APR: Wrong version: %d\n", ver); + return; + } + + hdr_size = hdr->hdr_field; + hdr_size = ((hdr_size & 0x00F0) >> 0x4) * 4; + if (hdr_size < APR_HDR_SIZE) { + pr_err("APR: Wrong hdr size:%d\n", hdr_size); + return; + } + + if (hdr->pkt_size < APR_HDR_SIZE) { + pr_err("APR: Wrong paket size\n"); + return; + } + msg_type = hdr->hdr_field; + msg_type = (msg_type >> 0x08) & 0x0003; + if (msg_type >= APR_MSG_TYPE_MAX && msg_type != APR_BASIC_RSP_RESULT) { + pr_err("APR: Wrong message type: %d\n", msg_type); + return; + } + + if (hdr->src_domain >= APR_DOMAIN_MAX || + hdr->dest_domain >= APR_DOMAIN_MAX || + hdr->src_svc >= APR_SVC_MAX || + hdr->dest_svc >= APR_SVC_MAX) { + pr_err("APR: Wrong APR header\n"); + return; + } + + svc = hdr->dest_svc; + if (hdr->src_domain == APR_DOMAIN_MODEM) { + if (svc == APR_SVC_MVS || svc == APR_SVC_MVM || + svc == APR_SVC_CVS || svc == APR_SVC_CVP || + svc == APR_SVC_TEST_CLIENT) + clnt = APR_CLIENT_VOICE; + else { + pr_err("APR: Wrong svc :%d\n", svc); + return; + } + } else if (hdr->src_domain == APR_DOMAIN_ADSP) { + if (svc == APR_SVC_AFE || svc == APR_SVC_ASM || + svc == APR_SVC_VSM || svc == APR_SVC_VPM || + svc == APR_SVC_ADM || svc == APR_SVC_ADSP_CORE || + svc == APR_SVC_USM || + svc == APR_SVC_TEST_CLIENT || svc == APR_SVC_ADSP_MVM || + svc == APR_SVC_ADSP_CVS || svc == APR_SVC_ADSP_CVP || + svc == APR_SVC_LSM) + clnt = APR_CLIENT_AUDIO; + else if (svc == APR_SVC_VIDC) + clnt = APR_CLIENT_AUDIO; + else { + pr_err("APR: Wrong svc :%d\n", svc); + return; + } + } else { + pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain); + return; + } + + src = apr_get_data_src(hdr); + if (src == APR_DEST_MAX) + return; + + pr_debug("src =%d clnt = %d\n", src, clnt); + apr_client = &client[src][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) + if (apr_client->svc[i].id == svc) { + pr_debug("%d\n", apr_client->svc[i].id); + c_svc = &apr_client->svc[i]; + break; + } + + if (i == APR_SVC_MAX) { + pr_err("APR: service is not registered\n"); + return; + } + pr_debug("svc_idx = %d\n", i); + pr_debug("%x %x %x %p %p\n", c_svc->id, c_svc->dest_id, + c_svc->client_id, c_svc->fn, c_svc->priv); + data.payload_size = hdr->pkt_size - hdr_size; + data.opcode = hdr->opcode; + data.src = src; + data.src_port = hdr->src_port; + data.dest_port = hdr->dest_port; + data.token = hdr->token; + data.msg_type = msg_type; + if (data.payload_size > 0) + data.payload = (char *)hdr + hdr_size; + + temp_port = ((data.src_port >> 8) * 8) + (data.src_port & 0xFF); + pr_debug("port = %d t_port = %d\n", data.src_port, temp_port); + if (c_svc->port_cnt && c_svc->port_fn[temp_port]) + c_svc->port_fn[temp_port](&data, c_svc->port_priv[temp_port]); + else if (c_svc->fn) + c_svc->fn(&data, c_svc->priv); + else + pr_err("APR: Rxed a packet for NULL callback\n"); +} + +int apr_get_svc(const char *svc_name, int domain_id, int *client_id, + int *svc_idx, int *svc_id) +{ + int i; + int size; + struct apr_svc_table *tbl; + int ret = 0; + + if ((domain_id == APR_DOMAIN_ADSP)) { + tbl = (struct apr_svc_table *)&svc_tbl_qdsp6; + size = ARRAY_SIZE(svc_tbl_qdsp6); + } else { + tbl = (struct apr_svc_table *)&svc_tbl_voice; + size = ARRAY_SIZE(svc_tbl_voice); + } + + for (i = 0; i < size; i++) { + if (!strcmp(svc_name, tbl[i].name)) { + *client_id = tbl[i].client_id; + *svc_idx = tbl[i].idx; + *svc_id = tbl[i].id; + break; + } + } + + pr_debug("%s: svc_name = %s c_id = %d domain_id = %d\n", + __func__, svc_name, *client_id, domain_id); + if (i == size) { + pr_err("%s: APR: Wrong svc name %s\n", __func__, svc_name); + ret = -EINVAL; + } + + return ret; +} + +static void apr_reset_deregister(struct work_struct *work) +{ + struct apr_svc *handle = NULL; + struct apr_reset_work *apr_reset = + container_of(work, struct apr_reset_work, work); + + handle = apr_reset->handle; + pr_debug("%s:handle[%p]\n", __func__, handle); + apr_deregister(handle); + kfree(apr_reset); +} + +int apr_deregister(void *handle) +{ + struct apr_svc *svc = handle; + struct apr_client *clnt; + uint16_t dest_id; + uint16_t client_id; + + if (!handle) + return -EINVAL; + + mutex_lock(&svc->m_lock); + dest_id = svc->dest_id; + client_id = svc->client_id; + clnt = &client[dest_id][client_id]; + + if (svc->port_cnt > 0 || svc->svc_cnt > 0) { + if (svc->port_cnt) + svc->port_cnt--; + else if (svc->svc_cnt) + svc->svc_cnt--; + if (!svc->port_cnt && !svc->svc_cnt) { + client[dest_id][client_id].svc_cnt--; + svc->need_reset = 0x0; + } + } else if (client[dest_id][client_id].svc_cnt > 0) { + client[dest_id][client_id].svc_cnt--; + if (!client[dest_id][client_id].svc_cnt) { + svc->need_reset = 0x0; + pr_debug("%s: service is reset %p\n", __func__, svc); + } + } + + if (!svc->port_cnt && !svc->svc_cnt) { + svc->priv = NULL; + svc->id = 0; + svc->fn = NULL; + svc->dest_id = 0; + svc->client_id = 0; + svc->need_reset = 0x0; + } + if (client[dest_id][client_id].handle && + !client[dest_id][client_id].svc_cnt) { + apr_tal_close(client[dest_id][client_id].handle); + client[dest_id][client_id].handle = NULL; + } + mutex_unlock(&svc->m_lock); + + return 0; +} + +void apr_reset(void *handle) +{ + struct apr_reset_work *apr_reset_worker = NULL; + + if (!handle) + return; + pr_debug("%s: handle[%p]\n", __func__, handle); + + if (apr_reset_workqueue == NULL) { + pr_err("%s: apr_reset_workqueue is NULL\n", __func__); + return; + } + + apr_reset_worker = kzalloc(sizeof(struct apr_reset_work), + GFP_ATOMIC); + + if (apr_reset_worker == NULL) { + pr_err("%s: mem failure\n", __func__); + return; + } + + apr_reset_worker->handle = handle; + INIT_WORK(&apr_reset_worker->work, apr_reset_deregister); + queue_work(apr_reset_workqueue, &apr_reset_worker->work); +} + +/* Dispatch the Reset events to Modem and audio clients */ +void dispatch_event(unsigned long code, unsigned short proc) +{ + struct apr_client *apr_client; + struct apr_client_data data; + struct apr_svc *svc; + uint16_t clnt; + int i, j; + + data.opcode = RESET_EVENTS; + data.reset_event = code; + data.reset_proc = proc; + + clnt = APR_CLIENT_AUDIO; + apr_client = &client[proc][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) { + mutex_lock(&apr_client->svc[i].m_lock); + if (apr_client->svc[i].fn) { + apr_client->svc[i].need_reset = 0x1; + apr_client->svc[i].fn(&data, apr_client->svc[i].priv); + } + if (apr_client->svc[i].port_cnt) { + svc = &(apr_client->svc[i]); + svc->need_reset = 0x1; + for (j = 0; j < APR_MAX_PORTS; j++) + if (svc->port_fn[j]) + svc->port_fn[j](&data, + svc->port_priv[j]); + } + mutex_unlock(&apr_client->svc[i].m_lock); + } + + clnt = APR_CLIENT_VOICE; + apr_client = &client[proc][clnt]; + for (i = 0; i < APR_SVC_MAX; i++) { + mutex_lock(&apr_client->svc[i].m_lock); + if (apr_client->svc[i].fn) { + apr_client->svc[i].need_reset = 0x1; + apr_client->svc[i].fn(&data, apr_client->svc[i].priv); + } + if (apr_client->svc[i].port_cnt) { + svc = &(apr_client->svc[i]); + svc->need_reset = 0x1; + for (j = 0; j < APR_MAX_PORTS; j++) + if (svc->port_fn[j]) + svc->port_fn[j](&data, + svc->port_priv[j]); + } + mutex_unlock(&apr_client->svc[i].m_lock); + } +} + +static int modem_notifier_cb(struct notifier_block *this, unsigned long code, + void *_cmd) +{ + static int boot_count = 2; + + if (boot_count) { + boot_count--; + return NOTIFY_OK; + } + + switch (code) { + case SUBSYS_BEFORE_SHUTDOWN: + pr_debug("M-Notify: Shutdown started\n"); + apr_set_modem_state(APR_SUBSYS_DOWN); + dispatch_event(code, APR_DEST_MODEM); + break; + case SUBSYS_AFTER_SHUTDOWN: + pr_debug("M-Notify: Shutdown Completed\n"); + break; + case SUBSYS_BEFORE_POWERUP: + pr_debug("M-notify: Bootup started\n"); + break; + case SUBSYS_AFTER_POWERUP: + if (apr_cmpxchg_modem_state(APR_SUBSYS_DOWN, APR_SUBSYS_UP) == + APR_SUBSYS_DOWN) + wake_up(&modem_wait); + pr_debug("M-Notify: Bootup Completed\n"); + break; + default: + pr_err("M-Notify: General: %lu\n", code); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block mnb = { + .notifier_call = modem_notifier_cb, +}; + +static int lpass_notifier_cb(struct notifier_block *this, unsigned long code, + void *_cmd) +{ + static int boot_count = 2; + + if (boot_count) { + boot_count--; + return NOTIFY_OK; + } + + switch (code) { + case SUBSYS_BEFORE_SHUTDOWN: + pr_debug("L-Notify: Shutdown started\n"); + apr_set_q6_state(APR_SUBSYS_DOWN); + dispatch_event(code, APR_DEST_QDSP6); + break; + case SUBSYS_AFTER_SHUTDOWN: + pr_debug("L-Notify: Shutdown Completed\n"); + break; + case SUBSYS_BEFORE_POWERUP: + pr_debug("L-notify: Bootup started\n"); + break; + case SUBSYS_AFTER_POWERUP: + if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN, + APR_SUBSYS_LOADED) == APR_SUBSYS_DOWN) + wake_up(&dsp_wait); + pr_debug("L-Notify: Bootup Completed\n"); + break; + default: + pr_err("L-Notify: Generel: %lu\n", code); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block lnb = { + .notifier_call = lpass_notifier_cb, +}; + + +static int __init apr_init(void) +{ + int i, j, k; + + for (i = 0; i < APR_DEST_MAX; i++) + for (j = 0; j < APR_CLIENT_MAX; j++) { + mutex_init(&client[i][j].m_lock); + for (k = 0; k < APR_SVC_MAX; k++) { + mutex_init(&client[i][j].svc[k].m_lock); + spin_lock_init(&client[i][j].svc[k].w_lock); + } + } + apr_set_subsys_state(); + mutex_init(&q6.lock); + apr_reset_workqueue = create_singlethread_workqueue("apr_driver"); + if (!apr_reset_workqueue) + return -ENOMEM; + return 0; +} +device_initcall(apr_init); + +static int __init apr_late_init(void) +{ + int ret = 0; + init_waitqueue_head(&dsp_wait); + init_waitqueue_head(&modem_wait); + subsys_notif_register(&mnb, &lnb); + return ret; +} +late_initcall(apr_late_init); diff --git a/drivers/soc/qcom/qdsp6v2/apr_tal.c b/drivers/soc/qcom/qdsp6v2/apr_tal.c new file mode 100644 index 000000000000..684bc5c76161 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/apr_tal.c @@ -0,0 +1,283 @@ +/* Copyright (c) 2010-2011, 2013 The Linux Foundation. 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 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <linux/spinlock.h> +#include <linux/mutex.h> +#include <linux/list.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/debugfs.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <mach/msm_smd.h> +#include <linux/qdsp6v2/apr_tal.h> + +static char *svc_names[APR_DEST_MAX][APR_CLIENT_MAX] = { + { + "apr_audio_svc", + "apr_voice_svc", + }, + { + "apr_audio_svc", + "apr_voice_svc", + }, +}; + +struct apr_svc_ch_dev apr_svc_ch[APR_DL_MAX][APR_DEST_MAX][APR_CLIENT_MAX]; + +int __apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, int len) +{ + int w_len; + unsigned long flags; + + + spin_lock_irqsave(&apr_ch->w_lock, flags); + if (smd_write_avail(apr_ch->ch) < len) { + spin_unlock_irqrestore(&apr_ch->w_lock, flags); + return -EAGAIN; + } + + w_len = smd_write(apr_ch->ch, data, len); + spin_unlock_irqrestore(&apr_ch->w_lock, flags); + pr_debug("apr_tal:w_len = %d\n", w_len); + + if (w_len != len) { + pr_err("apr_tal: Error in write\n"); + return -ENETRESET; + } + return w_len; +} + +int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, int len) +{ + int rc = 0, retries = 0; + + if (!apr_ch->ch) + return -EINVAL; + + do { + if (rc == -EAGAIN) + udelay(50); + + rc = __apr_tal_write(apr_ch, data, len); + } while (rc == -EAGAIN && retries++ < 300); + + if (rc == -EAGAIN) + pr_err("apr_tal: TIMEOUT for write\n"); + + return rc; +} + +static void apr_tal_notify(void *priv, unsigned event) +{ + struct apr_svc_ch_dev *apr_ch = priv; + int len, r_len, sz; + int pkt_cnt = 0; + unsigned long flags; + + pr_debug("event = %d\n", event); + switch (event) { + case SMD_EVENT_DATA: + pkt_cnt = 0; + spin_lock_irqsave(&apr_ch->lock, flags); +check_pending: + len = smd_read_avail(apr_ch->ch); + if (len < 0) { + pr_err("apr_tal: Invalid Read Event :%d\n", len); + spin_unlock_irqrestore(&apr_ch->lock, flags); + return; + } + sz = smd_cur_packet_size(apr_ch->ch); + if (sz < 0) { + pr_debug("pkt size is zero\n"); + spin_unlock_irqrestore(&apr_ch->lock, flags); + return; + } + if (!len && !sz && !pkt_cnt) + goto check_write_avail; + if (!len) { + pr_debug("len = %d pkt_cnt = %d\n", len, pkt_cnt); + spin_unlock_irqrestore(&apr_ch->lock, flags); + return; + } + r_len = smd_read_from_cb(apr_ch->ch, apr_ch->data, len); + if (len != r_len) { + pr_err("apr_tal: Invalid Read\n"); + spin_unlock_irqrestore(&apr_ch->lock, flags); + return; + } + pkt_cnt++; + pr_debug("%d %d %d\n", len, sz, pkt_cnt); + if (apr_ch->func) + apr_ch->func(apr_ch->data, r_len, apr_ch->priv); + goto check_pending; +check_write_avail: + if (smd_write_avail(apr_ch->ch)) + wake_up(&apr_ch->wait); + spin_unlock_irqrestore(&apr_ch->lock, flags); + break; + case SMD_EVENT_OPEN: + pr_debug("apr_tal: SMD_EVENT_OPEN\n"); + apr_ch->smd_state = 1; + wake_up(&apr_ch->wait); + break; + case SMD_EVENT_CLOSE: + pr_debug("apr_tal: SMD_EVENT_CLOSE\n"); + break; + } +} + +struct apr_svc_ch_dev *apr_tal_open(uint32_t svc, uint32_t dest, + uint32_t dl, apr_svc_cb_fn func, void *priv) +{ + int rc; + + if ((svc >= APR_CLIENT_MAX) || (dest >= APR_DEST_MAX) || + (dl >= APR_DL_MAX)) { + pr_err("apr_tal: Invalid params\n"); + return NULL; + } + + if (apr_svc_ch[dl][dest][svc].ch) { + pr_err("apr_tal: This channel alreday openend\n"); + return NULL; + } + + mutex_lock(&apr_svc_ch[dl][dest][svc].m_lock); + if (!apr_svc_ch[dl][dest][svc].dest_state) { + rc = wait_event_timeout(apr_svc_ch[dl][dest][svc].dest, + apr_svc_ch[dl][dest][svc].dest_state, + msecs_to_jiffies(APR_OPEN_TIMEOUT_MS)); + if (rc == 0) { + pr_err("apr_tal:open timeout\n"); + mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock); + return NULL; + } + pr_debug("apr_tal:Wakeup done\n"); + apr_svc_ch[dl][dest][svc].dest_state = 0; + } + rc = smd_named_open_on_edge(svc_names[dest][svc], dest, + &apr_svc_ch[dl][dest][svc].ch, + &apr_svc_ch[dl][dest][svc], + apr_tal_notify); + if (rc < 0) { + pr_err("apr_tal: smd_open failed %s\n", + svc_names[dest][svc]); + mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock); + return NULL; + } + rc = wait_event_timeout(apr_svc_ch[dl][dest][svc].wait, + (apr_svc_ch[dl][dest][svc].smd_state == 1), 5 * HZ); + if (rc == 0) { + pr_err("apr_tal:TIMEOUT for OPEN event\n"); + mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock); + apr_tal_close(&apr_svc_ch[dl][dest][svc]); + return NULL; + } + if (!apr_svc_ch[dl][dest][svc].dest_state) { + apr_svc_ch[dl][dest][svc].dest_state = 1; + pr_debug("apr_tal:Waiting for apr svc init\n"); + msleep(200); + pr_debug("apr_tal:apr svc init done\n"); + } + apr_svc_ch[dl][dest][svc].smd_state = 0; + + apr_svc_ch[dl][dest][svc].func = func; + apr_svc_ch[dl][dest][svc].priv = priv; + mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock); + + return &apr_svc_ch[dl][dest][svc]; +} + +int apr_tal_close(struct apr_svc_ch_dev *apr_ch) +{ + int r; + + if (!apr_ch->ch) + return -EINVAL; + + mutex_lock(&apr_ch->m_lock); + r = smd_close(apr_ch->ch); + apr_ch->ch = NULL; + apr_ch->func = NULL; + apr_ch->priv = NULL; + mutex_unlock(&apr_ch->m_lock); + return r; +} + +static int apr_smd_probe(struct platform_device *pdev) +{ + int dest; + int clnt; + + if (pdev->id == APR_DEST_MODEM) { + pr_info("apr_tal:Modem Is Up\n"); + dest = APR_DEST_MODEM; + if (!strcmp(pdev->name, "apr_audio_svc")) + clnt = APR_CLIENT_AUDIO; + else + clnt = APR_CLIENT_VOICE; + apr_svc_ch[APR_DL_SMD][dest][clnt].dest_state = 1; + wake_up(&apr_svc_ch[APR_DL_SMD][dest][clnt].dest); + } else if (pdev->id == APR_DEST_QDSP6) { + pr_info("apr_tal:Q6 Is Up\n"); + dest = APR_DEST_QDSP6; + clnt = APR_CLIENT_AUDIO; + apr_svc_ch[APR_DL_SMD][dest][clnt].dest_state = 1; + wake_up(&apr_svc_ch[APR_DL_SMD][dest][clnt].dest); + } else + pr_err("apr_tal:Invalid Dest Id: %d\n", pdev->id); + + return 0; +} + +static struct platform_driver apr_q6_driver = { + .probe = apr_smd_probe, + .driver = { + .name = "apr_audio_svc", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver apr_modem_driver = { + .probe = apr_smd_probe, + .driver = { + .name = "apr_voice_svc", + .owner = THIS_MODULE, + }, +}; + +static int __init apr_tal_init(void) +{ + int i, j, k; + + for (i = 0; i < APR_DL_MAX; i++) + for (j = 0; j < APR_DEST_MAX; j++) + for (k = 0; k < APR_CLIENT_MAX; k++) { + init_waitqueue_head(&apr_svc_ch[i][j][k].wait); + init_waitqueue_head(&apr_svc_ch[i][j][k].dest); + spin_lock_init(&apr_svc_ch[i][j][k].lock); + spin_lock_init(&apr_svc_ch[i][j][k].w_lock); + mutex_init(&apr_svc_ch[i][j][k].m_lock); + } + platform_driver_register(&apr_q6_driver); + platform_driver_register(&apr_modem_driver); + return 0; +} +device_initcall(apr_tal_init); diff --git a/drivers/soc/qcom/qdsp6v2/apr_v2.c b/drivers/soc/qcom/qdsp6v2/apr_v2.c new file mode 100644 index 000000000000..4770a8a31ba8 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/apr_v2.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2012, 2013 The Linux Foundation. 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 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. + */ + +#include <linux/types.h> +#include <linux/uaccess.h> +#include <linux/spinlock.h> +#include <linux/kernel.h> +#include <linux/qdsp6v2/apr.h> +#include <linux/qdsp6v2/apr_tal.h> +#include <linux/qdsp6v2/dsp_debug.h> + +static const char *lpass_subsys_name = "adsp"; + +void apr_set_subsys_state(void) +{ + apr_set_q6_state(APR_SUBSYS_DOWN); + apr_set_modem_state(APR_SUBSYS_UP); +} + +const char *apr_get_lpass_subsys_name(void) +{ + return lpass_subsys_name; +} + +uint16_t apr_get_data_src(struct apr_hdr *hdr) +{ + if (hdr->src_domain == APR_DOMAIN_MODEM) + return APR_DEST_MODEM; + else if (hdr->src_domain == APR_DOMAIN_ADSP) + return APR_DEST_QDSP6; + else { + pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain); + return APR_DEST_MAX; /*RETURN INVALID VALUE*/ + } +} + +int apr_get_dest_id(char *dest) +{ + if (!strcmp(dest, "ADSP")) + return APR_DEST_QDSP6; + else + return APR_DEST_MODEM; +} + +void subsys_notif_register(struct notifier_block *mod_notif, + struct notifier_block *lp_notif) +{ + subsys_notif_register_notifier("modem", mod_notif); + subsys_notif_register_notifier(apr_get_lpass_subsys_name(), lp_notif); +} diff --git a/drivers/soc/qcom/qdsp6v2/apr_v3.c b/drivers/soc/qcom/qdsp6v2/apr_v3.c new file mode 100644 index 000000000000..bbef6a5af809 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/apr_v3.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013, The Linux Foundation. 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 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. + */ + +#include <linux/types.h> +#include <linux/uaccess.h> +#include <linux/spinlock.h> +#include <linux/kernel.h> +#include <mach/qdsp6v2/apr.h> +#include <mach/qdsp6v2/apr_tal.h> +#include <mach/qdsp6v2/dsp_debug.h> + +#define DEST_ID APR_DEST_MODEM + +void apr_set_subsys_state(void) +{ + apr_set_modem_state(APR_SUBSYS_UP); +} + +uint16_t apr_get_data_src(struct apr_hdr *hdr) +{ + return DEST_ID; +} + +int apr_get_dest_id(char *dest) +{ + return DEST_ID; +} + +void subsys_notif_register(struct notifier_block *mod_notif, + struct notifier_block *lp_notif) +{ + subsys_notif_register_notifier("modem", mod_notif); +} + diff --git a/drivers/soc/qcom/qdsp6v2/dsp_debug.c b/drivers/soc/qcom/qdsp6v2/dsp_debug.c new file mode 100644 index 000000000000..be47f587da40 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/dsp_debug.c @@ -0,0 +1,261 @@ +/* arch/arm/mach-msm/qdsp6/dsp_dump.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland <swetland@google.com> + * + * 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 <linux/io.h> +#include <linux/fs.h> +#include <linux/module.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <asm/atomic.h> + +#include <mach/proc_comm.h> +#include <mach/debug_mm.h> +#include <linux/qdsp6v2/dsp_debug.h> + +static wait_queue_head_t dsp_wait; +static int dsp_has_crashed; +static int dsp_wait_count; + +static atomic_t dsp_crash_count = ATOMIC_INIT(0); +dsp_state_cb cb_ptr; + +void q6audio_dsp_not_responding(void) +{ + int i; + + if (cb_ptr) + cb_ptr(DSP_STATE_CRASHED); + if (atomic_add_return(1, &dsp_crash_count) != 1) { + pr_err("q6audio_dsp_not_responding() \ + - parking additional crasher...\n"); + for (i = 0; i < 600; i++) + msleep(1000); + } + if (dsp_wait_count) { + dsp_has_crashed = 1; + wake_up(&dsp_wait); + + while (dsp_has_crashed != 2) + wait_event(dsp_wait, dsp_has_crashed == 2); + } else { + pr_err("q6audio_dsp_not_responding() - no waiter?\n"); + } + if (cb_ptr) + cb_ptr(DSP_STATE_CRASH_DUMP_DONE); +} + +static int dsp_open(struct inode *inode, struct file *file) +{ + return 0; +} + +#define DSP_NMI_ADDR 0x28800010 + +static ssize_t dsp_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + char cmd[32]; + void __iomem *ptr; + void *mem_buffer; + + if (count >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buf, count)) + return -EFAULT; + cmd[count] = 0; + + if ((count > 1) && (cmd[count-1] == '\n')) + cmd[count-1] = 0; + + if (!strcmp(cmd, "wait-for-crash")) { + while (!dsp_has_crashed) { + int res; + dsp_wait_count++; + res = wait_event_interruptible(dsp_wait, + dsp_has_crashed); + if (res < 0) { + dsp_wait_count--; + return res; + } + } + /* assert DSP NMI */ + mem_buffer = ioremap(DSP_NMI_ADDR, 0x16); + if (IS_ERR((void *)mem_buffer)) { + pr_err("%s:map_buffer failed, error = %ld\n", __func__, + PTR_ERR((void *)mem_buffer)); + return -ENOMEM; + } + ptr = mem_buffer; + if (!ptr) { + pr_err("Unable to map DSP NMI\n"); + return -EFAULT; + } + writel(0x1, (void *)ptr); + iounmap(mem_buffer); + } else if (!strcmp(cmd, "boom")) { + q6audio_dsp_not_responding(); + } else if (!strcmp(cmd, "continue-crash")) { + dsp_has_crashed = 2; + wake_up(&dsp_wait); + } else { + pr_err("[%s:%s] unknown dsp_debug command: %s\n", __MM_FILE__, + __func__, cmd); + } + + return count; +} + +static unsigned copy_ok_count; +static uint32_t dsp_ram_size; +static uint32_t dsp_ram_base; + +static ssize_t dsp_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + size_t actual = 0; + size_t mapsize = PAGE_SIZE; + unsigned addr; + void __iomem *ptr; + void *mem_buffer; + + if ((dsp_ram_base == 0) || (dsp_ram_size == 0)) { + pr_err("[%s:%s] Memory Invalid or not initialized, Base = 0x%x," + " size = 0x%x\n", __MM_FILE__, + __func__, dsp_ram_base, dsp_ram_size); + return -EINVAL; + } + + if (*pos >= dsp_ram_size) + return 0; + + if (*pos & (PAGE_SIZE - 1)) + return -EINVAL; + + addr = (*pos + dsp_ram_base); + + /* don't blow up if we're unaligned */ + if (addr & (PAGE_SIZE - 1)) + mapsize *= 2; + + while (count >= PAGE_SIZE) { + mem_buffer = ioremap(addr, mapsize); + if (IS_ERR((void *)mem_buffer)) { + pr_err("%s:map_buffer failed, error = %ld\n", + __func__, PTR_ERR((void *)mem_buffer)); + return -ENOMEM; + } + ptr = mem_buffer; + if (!ptr) { + pr_err("[%s:%s] map error @ %x\n", __MM_FILE__, + __func__, addr); + return -EFAULT; + } + if (copy_to_user(buf, ptr, PAGE_SIZE)) { + iounmap(mem_buffer); + pr_err("[%s:%s] copy error @ %p\n", __MM_FILE__, + __func__, buf); + return -EFAULT; + } + copy_ok_count += PAGE_SIZE; + iounmap(mem_buffer); + addr += PAGE_SIZE; + buf += PAGE_SIZE; + actual += PAGE_SIZE; + count -= PAGE_SIZE; + } + + *pos += actual; + return actual; +} + +static int dsp_release(struct inode *inode, struct file *file) +{ + return 0; +} + +int dsp_debug_register(dsp_state_cb ptr) +{ + if (ptr == NULL) + return -EINVAL; + cb_ptr = ptr; + + return 0; +} + +static int dspcrashd_probe(struct platform_device *pdev) +{ + int rc = 0; + struct resource *res; + int *pdata; + + pdata = pdev->dev.platform_data; + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, + "msm_dspcrashd"); + if (!res) { + pr_err("%s: failed to get resources for dspcrashd\n", __func__); + return -ENODEV; + } + + dsp_ram_base = res->start; + dsp_ram_size = res->end - res->start; + pr_info("%s: Platform driver values: Base = 0x%x, Size = 0x%x," + "pdata = 0x%x\n", __func__, + dsp_ram_base, dsp_ram_size, *pdata); + return rc; +} + +static const struct file_operations dsp_fops = { + .owner = THIS_MODULE, + .open = dsp_open, + .read = dsp_read, + .write = dsp_write, + .release = dsp_release, +}; + +static struct miscdevice dsp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "dsp_debug", + .fops = &dsp_fops, +}; + +static struct platform_driver dspcrashd_driver = { + .probe = dspcrashd_probe, + .driver = { .name = "msm_dspcrashd"} +}; + +static int __init dsp_init(void) +{ + int rc = 0; + init_waitqueue_head(&dsp_wait); + rc = platform_driver_register(&dspcrashd_driver); + if (IS_ERR_VALUE(rc)) { + pr_err("%s: platform_driver_register for dspcrashd failed\n", + __func__); + } + return misc_register(&dsp_misc); +} + +static int __exit dsp_exit(void) +{ + platform_driver_unregister(&dspcrashd_driver); + return 0; +} + +device_initcall(dsp_init); diff --git a/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c b/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c new file mode 100644 index 000000000000..c63cc62c0e24 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c @@ -0,0 +1,577 @@ +/* + * Copyright (c) 2013, The Linux Foundation. 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 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. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <mach/subsystem_restart.h> +#include <linux/qdsp6v2/apr.h> +#include <linux/of_device.h> +#include <linux/msm_audio_ion.h> + +#include <linux/iommu.h> +#include <linux/msm_iommu_domains.h> + +struct msm_audio_ion_private { + bool smmu_enabled; + bool audioheap_enabled; + struct iommu_group *group; + u32 domain_id; + struct iommu_domain *domain; +}; + +static struct msm_audio_ion_private msm_audio_ion_data = {0,}; + + +static int msm_audio_ion_get_phys(struct ion_client *client, + struct ion_handle *handle, + ion_phys_addr_t *addr, size_t *len); + + + +int msm_audio_ion_alloc(const char *name, struct ion_client **client, + struct ion_handle **handle, size_t bufsz, + ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr) +{ + int rc = 0; + + if ((msm_audio_ion_data.smmu_enabled == true) && + (msm_audio_ion_data.group == NULL)) { + pr_debug("%s:probe is not done, deferred\n", __func__); + return -EPROBE_DEFER; + } + if (!name || !client || !handle || !paddr || !vaddr + || !bufsz || !pa_len) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + *client = msm_audio_ion_client_create(UINT_MAX, name); + if (IS_ERR_OR_NULL((void *)(*client))) { + pr_err("%s: ION create client for AUDIO failed\n", __func__); + goto err; + } + + *handle = ion_alloc(*client, bufsz, SZ_4K, + ION_HEAP(ION_AUDIO_HEAP_ID), 0); + if (IS_ERR_OR_NULL((void *) (*handle))) { + pr_debug("system heap is used"); + msm_audio_ion_data.audioheap_enabled = 0; + *handle = ion_alloc(*client, bufsz, SZ_4K, + ION_HEAP(ION_SYSTEM_HEAP_ID), 0); + + } else { + pr_debug("audio heap is used"); + msm_audio_ion_data.audioheap_enabled = 1; + } + + if (IS_ERR_OR_NULL((void *) (*handle))) { + pr_err("%s: ION memory allocation for AUDIO failed rc=%d, smmu_enabled=%d\n", + __func__, rc, msm_audio_ion_data.smmu_enabled); + goto err_ion_client; + } + + rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len); + if (rc) { + pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", + __func__, rc); + goto err_ion_handle; + } + + *vaddr = ion_map_kernel(*client, *handle); + if (IS_ERR_OR_NULL((void *)*vaddr)) { + pr_err("%s: ION memory mapping for AUDIO failed\n", __func__); + goto err_ion_handle; + } + pr_debug("%s: mapped address = %p, size=%d\n", __func__, *vaddr, bufsz); + + if (bufsz != 0) { + pr_debug("%s: memset to 0 %p %d\n", __func__, *vaddr, bufsz); + memset((void *)*vaddr, 0, bufsz); + } + + return 0; + +err_ion_handle: + ion_free(*client, *handle); +err_ion_client: + msm_audio_ion_client_destroy(*client); + *handle = NULL; + *client = NULL; +err: + return -EINVAL; +} + +int msm_audio_ion_import(const char *name, struct ion_client **client, + struct ion_handle **handle, int fd, + unsigned long *ionflag, size_t bufsz, + ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr) +{ + int rc = 0; + if (!name || !client || !handle || !paddr || !vaddr || !pa_len) { + pr_err("%s: Invalid params\n", __func__); + rc = -EINVAL; + goto err; + } + + if ((msm_audio_ion_data.smmu_enabled == true) && + (msm_audio_ion_data.group == NULL)) { + pr_debug("%s:probe is not done, deferred\n", __func__); + return -EPROBE_DEFER; + } + + *client = msm_audio_ion_client_create(UINT_MAX, name); + if (IS_ERR_OR_NULL((void *)(*client))) { + pr_err("%s: ION create client for AUDIO failed\n", __func__); + rc = -EINVAL; + goto err; + } + + /* name should be audio_acdb_client or Audio_Dec_Client, + bufsz should be 0 and fd shouldn't be 0 as of now + */ + *handle = ion_import_dma_buf(*client, fd); + pr_err("%s: DMA Buf name=%s, fd=%d handle=%p\n", __func__, + name, fd, *handle); + if (IS_ERR_OR_NULL((void *) (*handle))) { + pr_err("%s: ion import dma buffer failed\n", + __func__); + rc = -EINVAL; + goto err_destroy_client; + } + + if (ionflag != NULL) { + rc = ion_handle_get_flags(*client, *handle, ionflag); + if (rc) { + pr_err("%s: could not get flags for the handle\n", + __func__); + goto err_ion_handle; + } + } + + rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len); + if (rc) { + pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", + __func__, rc); + goto err_ion_handle; + } + + *vaddr = ion_map_kernel(*client, *handle); + if (IS_ERR_OR_NULL((void *)*vaddr)) { + pr_err("%s: ION memory mapping for AUDIO failed\n", __func__); + rc = -ENOMEM; + goto err_ion_handle; + } + pr_debug("%s: mapped address = %p, size=%d\n", __func__, *vaddr, bufsz); + + return 0; + +err_ion_handle: + ion_free(*client, *handle); +err_destroy_client: + msm_audio_ion_client_destroy(*client); + *client = NULL; + *handle = NULL; +err: + return rc; +} + +int msm_audio_ion_free(struct ion_client *client, struct ion_handle *handle) +{ + if (!client || !handle) { + pr_err("%s Invalid params\n", __func__); + return -EINVAL; + } + if (msm_audio_ion_data.smmu_enabled) { + /* Need to populate book kept infomation */ + pr_debug("client=%p, domain=%p, domain_id=%d, group=%p", + client, msm_audio_ion_data.domain, + msm_audio_ion_data.domain_id, msm_audio_ion_data.group); + + ion_unmap_iommu(client, handle, + msm_audio_ion_data.domain_id, 0); + } + + ion_unmap_kernel(client, handle); + + ion_free(client, handle); + msm_audio_ion_client_destroy(client); + return 0; +} + +int msm_audio_ion_mmap(struct audio_buffer *ab, + struct vm_area_struct *vma) +{ + struct sg_table *table; + unsigned long addr = vma->vm_start; + unsigned long offset = vma->vm_pgoff * PAGE_SIZE; + struct scatterlist *sg; + unsigned int i; + struct page *page; + int ret; + + pr_debug("%s\n", __func__); + + table = ion_sg_table(ab->client, ab->handle); + + if (IS_ERR(table)) { + pr_err("%s: Unable to get sg_table from ion: %ld\n", + __func__, PTR_ERR(table)); + return PTR_ERR(table); + } else if (!table) { + pr_err("%s: sg_list is NULL\n", __func__); + return -EINVAL; + } + + /* uncached */ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + /* We need to check if a page is associated with this sg list because: + * If the allocation came from a carveout we currently don't have + * pages associated with carved out memory. This might change in the + * future and we can remove this check and the else statement. + */ + page = sg_page(table->sgl); + if (page) { + pr_debug("%s: page is NOT null\n", __func__); + for_each_sg(table->sgl, sg, table->nents, i) { + unsigned long remainder = vma->vm_end - addr; + unsigned long len = sg_dma_len(sg); + + page = sg_page(sg); + + if (offset >= sg_dma_len(sg)) { + offset -= sg_dma_len(sg); + continue; + } else if (offset) { + page += offset / PAGE_SIZE; + len = sg_dma_len(sg) - offset; + offset = 0; + } + len = min(len, remainder); + pr_debug("vma=%p, addr=%x len=%ld vm_start=%x vm_end=%x vm_page_prot=%ld\n", + vma, (unsigned int)addr, len, + (unsigned int)vma->vm_start, + (unsigned int)vma->vm_end, + (unsigned long int)vma->vm_page_prot); + remap_pfn_range(vma, addr, page_to_pfn(page), len, + vma->vm_page_prot); + addr += len; + if (addr >= vma->vm_end) + return 0; + } + } else { + ion_phys_addr_t phys_addr; + size_t phys_len; + size_t va_len = 0; + pr_debug("%s: page is NULL\n", __func__); + + ret = ion_phys(ab->client, ab->handle, &phys_addr, &phys_len); + if (ret) { + pr_err("%s: Unable to get phys address from ION buffer: %d\n" + , __func__ , ret); + return ret; + } + pr_debug("phys=%x len=%d\n", (unsigned int)phys_addr, phys_len); + pr_debug("vma=%p, vm_start=%x vm_end=%x vm_pgoff=%ld vm_page_prot=%ld\n", + vma, (unsigned int)vma->vm_start, + (unsigned int)vma->vm_end, vma->vm_pgoff, + (unsigned long int)vma->vm_page_prot); + va_len = vma->vm_end - vma->vm_start; + if ((offset > phys_len) || (va_len > phys_len-offset)) { + pr_err("wrong offset size %ld, lens= %d, va_len=%d\n", + offset, phys_len, va_len); + return -EINVAL; + } + ret = remap_pfn_range(vma, vma->vm_start, + __phys_to_pfn(phys_addr) + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); + } + return 0; +} + + +bool msm_audio_ion_is_smmu_available(void) +{ + return msm_audio_ion_data.smmu_enabled; +} + +/* move to static section again */ +struct ion_client *msm_audio_ion_client_create(unsigned int heap_mask, + const char *name) +{ + struct ion_client *pclient = NULL; + /*IOMMU group and domain are moved to probe()*/ + pclient = msm_ion_client_create(heap_mask, name); + return pclient; +} + + +void msm_audio_ion_client_destroy(struct ion_client *client) +{ + pr_debug("%s: client = %p smmu_enabled = %d\n", __func__, + client, msm_audio_ion_data.smmu_enabled); + + ion_client_destroy(client); +} + +int msm_audio_ion_import_legacy(const char *name, struct ion_client *client, + struct ion_handle **handle, int fd, + unsigned long *ionflag, size_t bufsz, + ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr) +{ + int rc = 0; + if (!name || !client || !handle || !paddr || !vaddr || !pa_len) { + pr_err("%s: Invalid params\n", __func__); + rc = -EINVAL; + goto err; + } + /* client is already created for legacy and given*/ + /* name should be audio_acdb_client or Audio_Dec_Client, + bufsz should be 0 and fd shouldn't be 0 as of now + */ + *handle = ion_import_dma_buf(client, fd); + pr_debug("%s: DMA Buf name=%s, fd=%d handle=%p\n", __func__, + name, fd, *handle); + if (IS_ERR_OR_NULL((void *)(*handle))) { + pr_err("%s: ion import dma buffer failed\n", + __func__); + rc = -EINVAL; + goto err_destroy_client; + } + + if (ionflag != NULL) { + rc = ion_handle_get_flags(client, *handle, ionflag); + if (rc) { + pr_err("%s: could not get flags for the handle\n", + __func__); + rc = -EINVAL; + goto err_ion_handle; + } + } + + rc = msm_audio_ion_get_phys(client, *handle, paddr, pa_len); + if (rc) { + pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n", + __func__, rc); + rc = -EINVAL; + goto err_ion_handle; + } + + /*Need to add condition SMMU enable or not */ + *vaddr = ion_map_kernel(client, *handle); + if (IS_ERR_OR_NULL((void *)*vaddr)) { + pr_err("%s: ION memory mapping for AUDIO failed\n", __func__); + rc = -EINVAL; + goto err_ion_handle; + } + + if (bufsz != 0) + memset((void *)*vaddr, 0, bufsz); + + return 0; + +err_ion_handle: + ion_free(client, *handle); +err_destroy_client: + msm_audio_ion_client_destroy(client); + client = NULL; + *handle = NULL; +err: + return rc; +} + +int msm_audio_ion_free_legacy(struct ion_client *client, + struct ion_handle *handle) +{ + /* To add condition for SMMU enabled */ + ion_unmap_kernel(client, handle); + + ion_free(client, handle); + /* no client_destrody in legacy*/ + return 0; +} + +int msm_audio_ion_cache_operations(struct audio_buffer *abuff, int cache_op) +{ + unsigned long ionflag = 0; + int rc = 0; + int msm_cache_ops = 0; + + if (!abuff) { + pr_err("Invalid params: %p, %p\n", __func__, abuff); + return -EINVAL; + } + rc = ion_handle_get_flags(abuff->client, abuff->handle, + &ionflag); + if (rc) { + pr_err("ion_handle_get_flags failed: %d\n", rc); + goto cache_op_failed; + } + + /* has to be CACHED */ + if (ION_IS_CACHED(ionflag)) { + /* ION_IOC_INV_CACHES or ION_IOC_CLEAN_CACHES */ + msm_cache_ops = cache_op; + rc = msm_ion_do_cache_op(abuff->client, + abuff->handle, + (unsigned long *) abuff->data, + (unsigned long)abuff->size, + msm_cache_ops); + if (rc) { + pr_err("cache operation failed %d\n", rc); + goto cache_op_failed; + } + } +cache_op_failed: + return rc; +} + + +static int msm_audio_ion_get_phys(struct ion_client *client, + struct ion_handle *handle, + ion_phys_addr_t *addr, size_t *len) +{ + int rc = 0; + pr_debug("%s: smmu_enabled = %d\n", __func__, + msm_audio_ion_data.smmu_enabled); + + if (msm_audio_ion_data.smmu_enabled) { + rc = ion_map_iommu(client, handle, msm_audio_ion_data.domain_id, + 0 /*partition_num*/, SZ_4K /*align*/, 0/*iova_length*/, + addr, (unsigned long *)len, + 0, 0); + if (rc) { + pr_err("%s: ION map iommu failed %d\n", __func__, rc); + return rc; + } + pr_debug("client=%p, domain=%p, domain_id=%d, group=%p", + client, msm_audio_ion_data.domain, + msm_audio_ion_data.domain_id, msm_audio_ion_data.group); + } else { + /* SMMU is disabled*/ + rc = ion_phys(client, handle, addr, len); + } + pr_debug("phys=%x, len=%d, rc=%d\n", (unsigned int)*addr, *len, rc); + return rc; +} + +static int msm_audio_ion_probe(struct platform_device *pdev) +{ + int rc = 0; + const char *msm_audio_ion_dt = "qcom,smmu-enabled"; + bool smmu_enabled; + enum apr_subsys_state q6_state; + + if (pdev->dev.of_node == NULL) { + pr_err("%s: device tree is not found\n", __func__); + msm_audio_ion_data.smmu_enabled = 0; + return 0; + } + + smmu_enabled = of_property_read_bool(pdev->dev.of_node, + msm_audio_ion_dt); + msm_audio_ion_data.smmu_enabled = smmu_enabled; + + if (smmu_enabled) { + q6_state = apr_get_q6_state(); + if (q6_state == APR_SUBSYS_DOWN) { + pr_debug("defering %s, adsp_state %d\n", __func__, + q6_state); + return -EPROBE_DEFER; + } else + pr_debug("%s: adsp is ready\n", __func__); + + msm_audio_ion_data.group = iommu_group_find("lpass_audio"); + if (!msm_audio_ion_data.group) { + pr_debug("Failed to find group lpass_audio deferred\n"); + goto fail_group; + } + msm_audio_ion_data.domain = + iommu_group_get_iommudata(msm_audio_ion_data.group); + if (IS_ERR_OR_NULL(msm_audio_ion_data.domain)) { + pr_err("Failed to get domain data for group %p", + msm_audio_ion_data.group); + goto fail_group; + } + msm_audio_ion_data.domain_id = + msm_find_domain_no(msm_audio_ion_data.domain); + if (msm_audio_ion_data.domain_id < 0) { + pr_err("Failed to get domain index for domain %p", + msm_audio_ion_data.domain); + goto fail_group; + } + pr_debug("domain=%p, domain_id=%d, group=%p", + msm_audio_ion_data.domain, + msm_audio_ion_data.domain_id, msm_audio_ion_data.group); + + /* iommu_attach_group() will make AXI clock ON. For future PL + this will require to be called in once per session */ + rc = iommu_attach_group(msm_audio_ion_data.domain, + msm_audio_ion_data.group); + if (rc) { + pr_err("%s:ION attach group failed %d\n", __func__, rc); + return rc; + } + + } + + pr_debug("%s: SMMU-Enabled = %d\n", __func__, smmu_enabled); + return rc; + +fail_group: + return -EPROBE_DEFER; +} + +static int msm_audio_ion_remove(struct platform_device *pdev) +{ + pr_debug("%s: msm audio ion is unloaded, domain=%p, group=%p\n", + __func__, msm_audio_ion_data.domain, msm_audio_ion_data.group); + iommu_detach_group(msm_audio_ion_data.domain, msm_audio_ion_data.group); + + return 0; +} + +static const struct of_device_id msm_audio_ion_dt_match[] = { + { .compatible = "qcom,msm-audio-ion" }, + { } +}; +MODULE_DEVICE_TABLE(of, msm_audio_ion_dt_match); + +static struct platform_driver msm_audio_ion_driver = { + .driver = { + .name = "msm-audio-ion", + .owner = THIS_MODULE, + .of_match_table = msm_audio_ion_dt_match, + }, + .probe = msm_audio_ion_probe, + .remove = msm_audio_ion_remove, +}; + +static int __init msm_audio_ion_init(void) +{ + return platform_driver_register(&msm_audio_ion_driver); +} +module_init(msm_audio_ion_init); + +static void __exit msm_audio_ion_exit(void) +{ + platform_driver_unregister(&msm_audio_ion_driver); +} +module_exit(msm_audio_ion_exit); + +MODULE_DESCRIPTION("MSM Audio ION module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c new file mode 100644 index 000000000000..ffbb47573523 --- /dev/null +++ b/drivers/soc/qcom/smem.c @@ -0,0 +1,1375 @@ +/* Copyright (c) 2013, The Linux Foundation. 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 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. + */ + +#include <linux/export.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/ipc_logging.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/notifier.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/printk.h> +#include <linux/slab.h> +#include <linux/stat.h> + +#include <soc/qcom/smem.h> + +#include <mach/ramdump.h> +#include <mach/subsystem_notif.h> + +#include "smem_private.h" + +/** + * OVERFLOW_ADD_UNSIGNED() - check for unsigned overflow + * + * @type: type to check for overflow + * @a: left value to use + * @b: right value to use + * @returns: true if a + b will result in overflow; false otherwise + */ +#define OVERFLOW_ADD_UNSIGNED(type, a, b) \ + (((type)~0 - (a)) < (b) ? true : false) + +#define MODEM_SBL_VERSION_INDEX 7 +#define SMEM_VERSION_INFO_SIZE (32 * 4) +#define SMEM_VERSION 0x000B + +enum { + MSM_SMEM_DEBUG = 1U << 0, + MSM_SMEM_INFO = 1U << 1, +}; + +static int msm_smem_debug_mask = MSM_SMEM_INFO; +module_param_named(debug_mask, msm_smem_debug_mask, + int, S_IRUGO | S_IWUSR | S_IWGRP); +static void *smem_ipc_log_ctx; +#define NUM_LOG_PAGES 4 + +#define IPC_LOG(x...) do { \ + if (smem_ipc_log_ctx) \ + ipc_log_string(smem_ipc_log_ctx, x); \ + } while (0) + + +#define LOG_ERR(x...) do { \ + pr_err(x); \ + IPC_LOG(x); \ + } while (0) +#define SMEM_DBG(x...) do { \ + if (msm_smem_debug_mask & MSM_SMEM_DEBUG) \ + IPC_LOG(x); \ + } while (0) +#define SMEM_INFO(x...) do { \ + if (msm_smem_debug_mask & MSM_SMEM_INFO) \ + IPC_LOG(x); \ + } while (0) + +#define SMEM_SPINLOCK_SMEM_ALLOC "S:3" + +static void *smem_ram_base; +static resource_size_t smem_ram_size; +static phys_addr_t smem_ram_phys; +static remote_spinlock_t remote_spinlock; +static uint32_t num_smem_areas; +static struct smem_area *smem_areas; +static struct ramdump_segment *smem_ramdump_segments; +static int spinlocks_initialized; +static void *smem_ramdump_dev; +static DEFINE_MUTEX(spinlock_init_lock); +static DEFINE_SPINLOCK(smem_init_check_lock); +static int smem_module_inited; +static RAW_NOTIFIER_HEAD(smem_module_init_notifier_list); +static DEFINE_MUTEX(smem_module_init_notifier_lock); + +/* smem security feature components */ +#define SMEM_TOC_IDENTIFIER 0x434f5424 /* "$TOC" */ +#define SMEM_TOC_MAX_EXCLUSIONS 4 +#define SMEM_PART_HDR_IDENTIFIER 0x54525024 /* "$PRT" */ +#define SMEM_ALLOCATION_CANARY 0xa5a5 + +struct smem_toc_entry { + uint32_t offset; + uint32_t size; + uint32_t flags; + uint16_t host0; + uint16_t host1; + uint32_t size_cacheline; + uint32_t reserved[3]; + uint32_t exclusion_sizes[SMEM_TOC_MAX_EXCLUSIONS]; +}; + +struct smem_toc { + /* Identifier is a constant, set to SMEM_TOC_IDENTIFIER. */ + uint32_t identifier; + uint32_t version; + uint32_t num_entries; + uint32_t reserved[5]; + struct smem_toc_entry entry[]; +}; + +struct smem_partition_header { + /* Identifier is a constant, set to SMEM_PART_HDR_IDENTIFIER. */ + uint32_t identifier; + uint16_t host0; + uint16_t host1; + uint32_t size; + uint32_t offset_free_uncached; + uint32_t offset_free_cached; + uint32_t reserved[3]; +}; + +struct smem_partition_allocation_header { + /* Canary is a constant, set to SMEM_ALLOCATION_CANARY */ + uint16_t canary; + uint16_t smem_type; + uint32_t size; /* includes padding bytes */ + uint16_t padding_data; + uint16_t padding_hdr; + uint32_t reserved[1]; +}; + +struct smem_partition_info { + uint32_t partition_num; + uint32_t offset; + uint32_t size_cacheline; +}; + +static struct smem_partition_info partitions[NUM_SMEM_SUBSYSTEMS]; +/* end smem security feature components */ + +struct restart_notifier_block { + unsigned processor; + char *name; + struct notifier_block nb; +}; + +static int restart_notifier_cb(struct notifier_block *this, + unsigned long code, + void *data); + +static struct restart_notifier_block restart_notifiers[] = { + {SMEM_MODEM, "modem", .nb.notifier_call = restart_notifier_cb}, + {SMEM_Q6, "lpass", .nb.notifier_call = restart_notifier_cb}, + {SMEM_WCNSS, "wcnss", .nb.notifier_call = restart_notifier_cb}, + {SMEM_DSPS, "dsps", .nb.notifier_call = restart_notifier_cb}, + {SMEM_MODEM, "gss", .nb.notifier_call = restart_notifier_cb}, + {SMEM_Q6, "adsp", .nb.notifier_call = restart_notifier_cb}, +}; + +static int init_smem_remote_spinlock(void); + +/** + * smem_phys_to_virt() - Convert a physical base and offset to virtual address + * + * @base: physical base address to check + * @offset: offset from the base to get the final address + * @returns: virtual SMEM address; NULL for failure + * + * Takes a physical address and an offset and checks if the resulting physical + * address would fit into one of the smem regions. If so, returns the + * corresponding virtual address. Otherwise returns NULL. + */ +static void *smem_phys_to_virt(phys_addr_t base, unsigned offset) +{ + int i; + phys_addr_t phys_addr; + resource_size_t size; + + if (OVERFLOW_ADD_UNSIGNED(phys_addr_t, base, offset)) + return NULL; + + if (!smem_areas) { + /* + * Early boot - no area configuration yet, so default + * to using the main memory region. + * + * To remove the MSM_SHARED_RAM_BASE and the static + * mapping of SMEM in the future, add dump_stack() + * to identify the early callers of smem_get_entry() + * (which calls this function) and replace those calls + * with a new function that knows how to lookup the + * SMEM base address before SMEM has been probed. + */ + phys_addr = smem_ram_phys; + size = smem_ram_size; + + if (base >= phys_addr && base + offset < phys_addr + size) { + if (OVERFLOW_ADD_UNSIGNED(uintptr_t, + (uintptr_t)smem_ram_base, offset)) { + SMEM_INFO("%s: overflow %p %x\n", __func__, + smem_ram_base, offset); + return NULL; + } + + return smem_ram_base + offset; + } else { + return NULL; + } + } + for (i = 0; i < num_smem_areas; ++i) { + phys_addr = smem_areas[i].phys_addr; + size = smem_areas[i].size; + + if (base < phys_addr || base + offset >= phys_addr + size) + continue; + + if (OVERFLOW_ADD_UNSIGNED(uintptr_t, + (uintptr_t)smem_areas[i].virt_addr, offset)) { + SMEM_INFO("%s: overflow %p %x\n", __func__, + smem_areas[i].virt_addr, offset); + return NULL; + } + + return smem_areas[i].virt_addr + offset; + } + + return NULL; +} + +/** + * smem_virt_to_phys() - Convert SMEM address to physical address. + * + * @smem_address: Address of SMEM item (returned by smem_alloc(), etc) + * @returns: Physical address (or NULL if there is a failure) + * + * This function should only be used if an SMEM item needs to be handed + * off to a DMA engine. + */ +phys_addr_t smem_virt_to_phys(void *smem_address) +{ + phys_addr_t phys_addr = 0; + int i; + void *vend; + + if (!smem_areas) + return phys_addr; + + for (i = 0; i < num_smem_areas; ++i) { + vend = (void *)(smem_areas[i].virt_addr + smem_areas[i].size); + + if (smem_address >= smem_areas[i].virt_addr && + smem_address < vend) { + phys_addr = smem_address - smem_areas[i].virt_addr; + phys_addr += smem_areas[i].phys_addr; + break; + } + } + + return phys_addr; +} +EXPORT_SYMBOL(smem_virt_to_phys); + +/** + * __smem_get_entry_nonsecure - Get pointer and size of existing SMEM item + * + * @id: ID of SMEM item + * @size: Pointer to size variable for storing the result + * @skip_init_check: True means do not verify that SMEM has been initialized + * @use_rspinlock: True to use the remote spinlock + * @returns: Pointer to SMEM item or NULL if it doesn't exist + */ +static void *__smem_get_entry_nonsecure(unsigned id, unsigned *size, + bool skip_init_check, bool use_rspinlock) +{ + struct smem_shared *shared = smem_ram_base; + struct smem_heap_entry *toc = shared->heap_toc; + int use_spinlocks = spinlocks_initialized && use_rspinlock; + void *ret = 0; + unsigned long flags = 0; + + if (!skip_init_check && !smem_initialized_check()) + return ret; + + if (id >= SMEM_NUM_ITEMS) + return ret; + + if (use_spinlocks) + remote_spin_lock_irqsave(&remote_spinlock, flags); + /* toc is in device memory and cannot be speculatively accessed */ + if (toc[id].allocated) { + phys_addr_t phys_base; + + *size = toc[id].size; + barrier(); + + phys_base = toc[id].reserved & BASE_ADDR_MASK; + if (!phys_base) + phys_base = smem_ram_phys; + ret = smem_phys_to_virt(phys_base, toc[id].offset); + } else { + *size = 0; + } + if (use_spinlocks) + remote_spin_unlock_irqrestore(&remote_spinlock, flags); + + return ret; +} + +/** + * __smem_get_entry_secure - Get pointer and size of existing SMEM item with + * security support + * + * @id: ID of SMEM item + * @size: Pointer to size variable for storing the result + * @to_proc: SMEM host that shares the item with apps + * @flags: Item attribute flags + * @skip_init_check: True means do not verify that SMEM has been initialized + * @use_rspinlock: True to use the remote spinlock + * @returns: Pointer to SMEM item or NULL if it doesn't exist + */ +static void *__smem_get_entry_secure(unsigned id, + unsigned *size, + unsigned to_proc, + unsigned flags, + bool skip_init_check, + bool use_rspinlock) +{ + struct smem_partition_header *hdr; + unsigned long lflags = 0; + void *item = NULL; + struct smem_partition_allocation_header *alloc_hdr; + uint32_t partition_num; + uint32_t a_hdr_size; + int rc; + + SMEM_DBG("%s(%u, %u, %u, %u, %d, %d)\n", __func__, id, *size, to_proc, + flags, skip_init_check, use_rspinlock); + + if (!skip_init_check && !smem_initialized_check()) + return NULL; + + if (id >= SMEM_NUM_ITEMS) { + SMEM_INFO("%s: invalid id %d\n", __func__, id); + return NULL; + } + + if (!(flags & SMEM_ANY_HOST_FLAG) && to_proc >= NUM_SMEM_SUBSYSTEMS) { + SMEM_INFO("%s: id %u invalid to_proc %d\n", __func__, id, + to_proc); + return NULL; + } + + if (flags & SMEM_ANY_HOST_FLAG || !partitions[to_proc].offset) + return __smem_get_entry_nonsecure(id, size, skip_init_check, + use_rspinlock); + + partition_num = partitions[to_proc].partition_num; + hdr = smem_areas[0].virt_addr + partitions[to_proc].offset; + if (unlikely(!spinlocks_initialized)) { + rc = init_smem_remote_spinlock(); + if (unlikely(rc)) { + SMEM_INFO( + "%s: id:%u remote spinlock init failed %d\n", + __func__, id, rc); + return NULL; + } + } + if (use_rspinlock) + remote_spin_lock_irqsave(&remote_spinlock, lflags); + if (hdr->identifier != SMEM_PART_HDR_IDENTIFIER) { + LOG_ERR( + "%s: SMEM corruption detected. Partition %d to %d at %p\n", + __func__, + partition_num, + to_proc, + hdr); + BUG(); + } + + if (flags & SMEM_ITEM_CACHED_FLAG) { + a_hdr_size = ALIGN(sizeof(*alloc_hdr), + partitions[to_proc].size_cacheline); + for (alloc_hdr = (void *)(hdr) + hdr->size - a_hdr_size; + (void *)(alloc_hdr) > (void *)(hdr) + + hdr->offset_free_cached; + alloc_hdr = (void *)(alloc_hdr) - + alloc_hdr->size - a_hdr_size) { + if (alloc_hdr->canary != SMEM_ALLOCATION_CANARY) { + LOG_ERR( + "%s: SMEM corruption detected. Partition %d to %d at %p\n", + __func__, + partition_num, + to_proc, + alloc_hdr); + BUG(); + + } + if (alloc_hdr->smem_type == id) { + /* 8 byte alignment to match legacy */ + *size = ALIGN(alloc_hdr->size - + alloc_hdr->padding_data, 8); + item = (void *)(alloc_hdr) - alloc_hdr->size; + break; + } + } + } else { + for (alloc_hdr = (void *)(hdr) + sizeof(*hdr); + (void *)(alloc_hdr) < (void *)(hdr) + + hdr->offset_free_uncached; + alloc_hdr = (void *)(alloc_hdr) + + sizeof(*alloc_hdr) + + alloc_hdr->padding_hdr + + alloc_hdr->size) { + if (alloc_hdr->canary != SMEM_ALLOCATION_CANARY) { + LOG_ERR( + "%s: SMEM corruption detected. Partition %d to %d at %p\n", + __func__, + partition_num, + to_proc, + alloc_hdr); + BUG(); + + } + if (alloc_hdr->smem_type == id) { + /* 8 byte alignment to match legacy */ + *size = ALIGN(alloc_hdr->size - + alloc_hdr->padding_data, 8); + item = (void *)(alloc_hdr) + + sizeof(*alloc_hdr) + + alloc_hdr->padding_hdr; + break; + } + } + } + if (use_rspinlock) + remote_spin_unlock_irqrestore(&remote_spinlock, lflags); + + return item; +} + +static void *__smem_find(unsigned id, unsigned size_in, bool skip_init_check) +{ + unsigned size; + void *ptr; + + ptr = __smem_get_entry_nonsecure(id, &size, skip_init_check, true); + if (!ptr) + return 0; + + size_in = ALIGN(size_in, 8); + if (size_in != size) { + SMEM_INFO("smem_find(%u, %u): wrong size %u\n", + id, size_in, size); + return 0; + } + + return ptr; +} + +/** + * smem_find - Find existing item with security support + * + * @id: ID of SMEM item + * @size_in: Size of the SMEM item + * @to_proc: SMEM host that shares the item with apps + * @flags: Item attribute flags + * @returns: Pointer to SMEM item or NULL if it doesn't exist + */ +void *smem_find(unsigned id, unsigned size_in, unsigned to_proc, unsigned flags) +{ + unsigned size; + void *ptr; + + SMEM_DBG("%s(%u, %u, %u, %u)\n", __func__, id, size_in, to_proc, + flags); + + ptr = smem_get_entry(id, &size, to_proc, flags); + if (!ptr) + return 0; + + size_in = ALIGN(size_in, 8); + if (size_in != size) { + SMEM_INFO("smem_find(%u, %u, %u, %u): wrong size %u\n", + id, size_in, to_proc, flags, size); + return 0; + } + + return ptr; +} +EXPORT_SYMBOL(smem_find); + +/** + * alloc_item_nonsecure - Allocate an SMEM item in the nonsecure partition + * + * @id: ID of SMEM item + * @size_in: Size to allocate + * @returns: Pointer to SMEM item or NULL for error + * + * Assumes the id parameter is valid and does not already exist. Assumes + * size_in is already adjusted for alignment, if necessary. Requires the + * remote spinlock to already be locked. + */ +static void *alloc_item_nonsecure(unsigned id, unsigned size_in) +{ + void *smem_base = smem_ram_base; + struct smem_shared *shared = smem_base; + struct smem_heap_entry *toc = shared->heap_toc; + void *ret = NULL; + + if (shared->heap_info.heap_remaining >= size_in) { + toc[id].offset = shared->heap_info.free_offset; + toc[id].size = size_in; + /* + * wmb() is necessary to ensure the allocation data is + * consistent before setting the allocated flag to prevent race + * conditions with remote processors + */ + wmb(); + toc[id].allocated = 1; + + shared->heap_info.free_offset += size_in; + shared->heap_info.heap_remaining -= size_in; + ret = smem_base + toc[id].offset; + /* + * wmb() is necessary to ensure the heap data is consistent + * before continuing to prevent race conditions with remote + * processors + */ + wmb(); + } else { + SMEM_INFO("%s: id %u not enough memory %u (required %u)\n", + __func__, id, shared->heap_info.heap_remaining, + size_in); + } + + return ret; +} + +/** + * alloc_item_secure - Allocate an SMEM item in a secure partition + * + * @id: ID of SMEM item + * @size_in: Size to allocate + * @to_proc: SMEM host that shares the item with apps + * @flags: Item attribute flags + * @returns: Pointer to SMEM item or NULL for error + * + * Assumes the id parameter is valid and does not already exist. Assumes + * size_in is the raw size requested by the client. Assumes to_proc is a valid + * host, and a valid partition to that host exists. Requires the remote + * spinlock to already be locked. + */ +static void *alloc_item_secure(unsigned id, unsigned size_in, unsigned to_proc, + unsigned flags) +{ + void *smem_base = smem_ram_base; + struct smem_partition_header *hdr; + struct smem_partition_allocation_header *alloc_hdr; + uint32_t a_hdr_size; + uint32_t a_data_size; + uint32_t size_cacheline; + uint32_t free_space; + uint32_t partition_num; + void *ret = NULL; + + hdr = smem_base + partitions[to_proc].offset; + partition_num = partitions[to_proc].partition_num; + + if (hdr->identifier != SMEM_PART_HDR_IDENTIFIER) { + LOG_ERR( + "%s: SMEM corruption detected. Partition %d to %d at %p\n", + __func__, + partition_num, + to_proc, + hdr); + BUG(); + } + + size_cacheline = partitions[to_proc].size_cacheline; + free_space = hdr->offset_free_cached - + hdr->offset_free_uncached; + + if (flags & SMEM_ITEM_CACHED_FLAG) { + a_hdr_size = ALIGN(sizeof(*alloc_hdr), size_cacheline); + a_data_size = ALIGN(size_in, size_cacheline); + if (free_space < a_hdr_size + a_data_size) { + SMEM_INFO( + "%s: id %u not enough memory %u (required %u)\n", + __func__, id, free_space, + a_hdr_size + a_data_size); + return ret; + } + alloc_hdr = (void *)(hdr) + hdr->offset_free_cached - + a_hdr_size; + alloc_hdr->canary = SMEM_ALLOCATION_CANARY; + alloc_hdr->smem_type = id; + alloc_hdr->size = a_data_size; + alloc_hdr->padding_data = a_data_size - size_in; + alloc_hdr->padding_hdr = a_hdr_size - sizeof(*alloc_hdr); + hdr->offset_free_cached = hdr->offset_free_cached - + a_hdr_size - a_data_size; + ret = (void *)(alloc_hdr) - a_data_size; + /* + * The SMEM protocol currently does not support cacheable + * areas within the smem region, but if it ever does in the + * future, then cache management needs to be done here. + * The area of memory this item is allocated from will need to + * be dynamically made cachable, and a cache flush of the + * allocation header using __cpuc_flush_dcache_area and + * outer_flush_area will need to be done. + */ + } else { + a_hdr_size = sizeof(*alloc_hdr); + a_data_size = ALIGN(size_in, 8); + if (free_space < a_hdr_size + a_data_size) { + SMEM_INFO( + "%s: id %u not enough memory %u (required %u)\n", + __func__, id, free_space, + a_hdr_size + a_data_size); + return ret; + } + alloc_hdr = (void *)(hdr) + hdr->offset_free_uncached; + alloc_hdr->canary = SMEM_ALLOCATION_CANARY; + alloc_hdr->smem_type = id; + alloc_hdr->size = a_data_size; + alloc_hdr->padding_data = a_data_size - size_in; + alloc_hdr->padding_hdr = a_hdr_size - sizeof(*alloc_hdr); + hdr->offset_free_uncached = hdr->offset_free_uncached + + a_hdr_size + a_data_size; + ret = alloc_hdr + 1; + } + /* + * wmb() is necessary to ensure the heap and allocation data is + * consistent before continuing to prevent race conditions with remote + * processors + */ + wmb(); + + return ret; +} + +/** + * smem_alloc - Find an existing item, otherwise allocate it with security + * support + * + * @id: ID of SMEM item + * @size_in: Size of the SMEM item + * @to_proc: SMEM host that shares the item with apps + * @flags: Item attribute flags + * @returns: Pointer to SMEM item or NULL if it couldn't be found/allocated + */ +void *smem_alloc(unsigned id, unsigned size_in, unsigned to_proc, + unsigned flags) +{ + unsigned long lflags; + void *ret = NULL; + int rc; + unsigned size_out; + unsigned a_size_in; + + SMEM_DBG("%s(%u, %u, %u, %u)\n", __func__, id, size_in, to_proc, + flags); + + if (!smem_initialized_check()) + return NULL; + + if (id >= SMEM_NUM_ITEMS) { + SMEM_INFO("%s: invalid id %u\n", __func__, id); + return NULL; + } + + if (!(flags & SMEM_ANY_HOST_FLAG) && to_proc >= NUM_SMEM_SUBSYSTEMS) { + SMEM_INFO("%s: invalid to_proc %u for id %u\n", __func__, + to_proc, id); + return NULL; + } + + if (unlikely(!spinlocks_initialized)) { + rc = init_smem_remote_spinlock(); + if (unlikely(rc)) { + SMEM_INFO("%s: id:%u remote spinlock init failed %d\n", + __func__, id, rc); + return NULL; + } + } + + a_size_in = ALIGN(size_in, 8); + remote_spin_lock_irqsave(&remote_spinlock, lflags); + + ret = __smem_get_entry_secure(id, &size_out, to_proc, flags, true, + false); + if (ret) { + SMEM_INFO("%s: %u already allocated\n", __func__, id); + if (a_size_in == size_out) { + remote_spin_unlock_irqrestore(&remote_spinlock, lflags); + return ret; + } else { + remote_spin_unlock_irqrestore(&remote_spinlock, lflags); + SMEM_INFO("%s: id %u wrong size %u (expected %u)\n", + __func__, id, size_out, a_size_in); + return NULL; + } + } + + if (id > SMEM_FIXED_ITEM_LAST) { + SMEM_INFO("%s: allocating %u size %u to_proc %u flags %u\n", + __func__, id, size_in, to_proc, flags); + if (flags & SMEM_ANY_HOST_FLAG || !partitions[to_proc].offset) + ret = alloc_item_nonsecure(id, a_size_in); + else + ret = alloc_item_secure(id, size_in, to_proc, flags); + + } else { + SMEM_INFO("%s: attempted to allocate non-dynamic item %u\n", + __func__, id); + } + + remote_spin_unlock_irqrestore(&remote_spinlock, lflags); + return ret; +} +EXPORT_SYMBOL(smem_alloc); + +/** + * smem_get_entry - Get existing item with security support + * + * @id: ID of SMEM item + * @size: Pointer to size variable for storing the result + * @to_proc: SMEM host that shares the item with apps + * @flags: Item attribute flags + * @returns: Pointer to SMEM item or NULL if it doesn't exist + */ +void *smem_get_entry(unsigned id, unsigned *size, unsigned to_proc, + unsigned flags) +{ + SMEM_DBG("%s(%u, %u, %u, %u)\n", __func__, id, *size, to_proc, flags); + + return __smem_get_entry_secure(id, size, to_proc, flags, false, true); +} +EXPORT_SYMBOL(smem_get_entry); + +/** + * smem_get_entry_no_rlock - Get existing item without using remote spinlock + * + * @id: ID of SMEM item + * @size_out: Pointer to size variable for storing the result + * @to_proc: SMEM host that shares the item with apps + * @flags: Item attribute flags + * @returns: Pointer to SMEM item or NULL if it doesn't exist + * + * This function does not lock the remote spinlock and should only be used in + * failure-recover cases such as retrieving the subsystem failure reason during + * subsystem restart. + */ +void *smem_get_entry_no_rlock(unsigned id, unsigned *size_out, unsigned to_proc, + unsigned flags) +{ + return __smem_get_entry_secure(id, size_out, to_proc, flags, false, + false); +} +EXPORT_SYMBOL(smem_get_entry_no_rlock); + +/** + * smem_get_remote_spinlock - Remote spinlock pointer for unit testing. + * + * @returns: pointer to SMEM remote spinlock + */ +remote_spinlock_t *smem_get_remote_spinlock(void) +{ + if (unlikely(!spinlocks_initialized)) + init_smem_remote_spinlock(); + return &remote_spinlock; +} +EXPORT_SYMBOL(smem_get_remote_spinlock); + +/** + * smem_get_free_space() - Get the available allocation free space for a + * partition + * + * @to_proc: remote SMEM host. Determines the applicable partition + * @returns: size in bytes available to allocate + * + * Helper function for SMD so that SMD only scans the channel allocation + * table for a partition when it is reasonably certain that a channel has + * actually been created, because scanning can be expensive. Creating a channel + * will consume some of the free space in a partition, so SMD can compare the + * last free space size against the current free space size to determine if + * a channel may have been created. SMD can't do this directly, because the + * necessary partition internals are restricted to just SMEM. + */ +unsigned smem_get_free_space(unsigned to_proc) +{ + struct smem_partition_header *hdr; + struct smem_shared *shared; + + if (to_proc >= NUM_SMEM_SUBSYSTEMS) { + pr_err("%s: invalid to_proc:%d\n", __func__, to_proc); + return UINT_MAX; + } + + if (partitions[to_proc].offset) { + if (unlikely(OVERFLOW_ADD_UNSIGNED(uintptr_t, + (uintptr_t)smem_areas[0].virt_addr, + partitions[to_proc].offset))) { + pr_err("%s: unexpected overflow detected\n", __func__); + return UINT_MAX; + } + hdr = smem_areas[0].virt_addr + partitions[to_proc].offset; + return hdr->offset_free_cached - hdr->offset_free_uncached; + } else { + shared = smem_ram_base; + return shared->heap_info.heap_remaining; + } +} +EXPORT_SYMBOL(smem_get_free_space); + +/** + * smem_get_version() - Get the smem user version number + * + * @idx: SMEM user idx in SMEM_VERSION_INFO table. + * @returns: smem version number if success otherwise zero. + */ +unsigned smem_get_version(unsigned idx) +{ + int *version_array; + + if (idx > 32) { + pr_err("%s: invalid idx:%d\n", __func__, idx); + return 0; + } + + version_array = __smem_find(SMEM_VERSION_INFO, SMEM_VERSION_INFO_SIZE, + true); + if (version_array == NULL) + return 0; + + return version_array[idx]; +} +EXPORT_SYMBOL(smem_get_version); + +/** + * init_smem_remote_spinlock - Reentrant remote spinlock initialization + * + * @returns: success or error code for failure + */ +static int init_smem_remote_spinlock(void) +{ + int rc = 0; + + /* + * Optimistic locking. Init only needs to be done once by the first + * caller. After that, serializing inits between different callers + * is unnecessary. The second check after the lock ensures init + * wasn't previously completed by someone else before the lock could + * be grabbed. + */ + if (!spinlocks_initialized) { + mutex_lock(&spinlock_init_lock); + if (!spinlocks_initialized) { + rc = remote_spin_lock_init(&remote_spinlock, + SMEM_SPINLOCK_SMEM_ALLOC); + if (!rc) + spinlocks_initialized = 1; + } + mutex_unlock(&spinlock_init_lock); + } + return rc; +} + +/** + * smem_initialized_check - Reentrant check that smem has been initialized + * + * @returns: true if initialized, false if not. + */ +bool smem_initialized_check(void) +{ + static int checked; + static int is_inited; + unsigned long flags; + struct smem_shared *smem; + + if (likely(checked)) { + if (unlikely(!is_inited)) + LOG_ERR("%s: smem not initialized\n", __func__); + return is_inited; + } + + spin_lock_irqsave(&smem_init_check_lock, flags); + if (checked) { + spin_unlock_irqrestore(&smem_init_check_lock, flags); + if (unlikely(!is_inited)) + LOG_ERR("%s: smem not initialized\n", __func__); + return is_inited; + } + + smem = smem_ram_base; + + if (smem->heap_info.initialized != 1) + goto failed; + if (smem->heap_info.reserved != 0) + goto failed; + + /* + * The Modem SBL is now the Master SBL version and is required to + * pre-initialize SMEM and fill in any necessary configuration + * structures. Without the extra configuration data, the SMEM driver + * cannot be properly initialized. + */ + if (smem_get_version(MODEM_SBL_VERSION_INDEX) != SMEM_VERSION << 16) { + pr_err("%s: SBL version not correct\n", __func__); + goto failed; + } + + is_inited = 1; + checked = 1; + spin_unlock_irqrestore(&smem_init_check_lock, flags); + return is_inited; + +failed: + is_inited = 0; + checked = 1; + spin_unlock_irqrestore(&smem_init_check_lock, flags); + LOG_ERR( + "%s: shared memory needs to be initialized by SBL before booting\n", + __func__); + return is_inited; +} +EXPORT_SYMBOL(smem_initialized_check); + +static int restart_notifier_cb(struct notifier_block *this, + unsigned long code, + void *data) +{ + if (code == SUBSYS_AFTER_SHUTDOWN) { + struct restart_notifier_block *notifier; + + notifier = container_of(this, + struct restart_notifier_block, nb); + SMEM_INFO("%s: ssrestart for processor %d ('%s')\n", + __func__, notifier->processor, + notifier->name); + + remote_spin_release(&remote_spinlock, notifier->processor); + remote_spin_release_all(notifier->processor); + + if (smem_ramdump_dev) { + int ret; + + SMEM_DBG("%s: saving ramdump\n", __func__); + /* + * XPU protection does not currently allow the + * auxiliary memory regions to be dumped. If this + * changes, then num_smem_areas + 1 should be passed + * into do_elf_ramdump() to dump all regions. + */ + ret = do_elf_ramdump(smem_ramdump_dev, + smem_ramdump_segments, 1); + if (ret < 0) + LOG_ERR("%s: unable to dump smem %d\n", + __func__, ret); + } + } + + return NOTIFY_DONE; +} + +static __init int modem_restart_late_init(void) +{ + int i; + void *handle; + struct restart_notifier_block *nb; + + smem_ramdump_dev = create_ramdump_device("smem", NULL); + if (IS_ERR_OR_NULL(smem_ramdump_dev)) { + LOG_ERR("%s: Unable to create smem ramdump device.\n", + __func__); + smem_ramdump_dev = NULL; + } + + for (i = 0; i < ARRAY_SIZE(restart_notifiers); i++) { + nb = &restart_notifiers[i]; + handle = subsys_notif_register_notifier(nb->name, &nb->nb); + SMEM_DBG("%s: registering notif for '%s', handle=%p\n", + __func__, nb->name, handle); + } + + return 0; +} +late_initcall(modem_restart_late_init); + +int smem_module_init_notifier_register(struct notifier_block *nb) +{ + int ret; + if (!nb) + return -EINVAL; + mutex_lock(&smem_module_init_notifier_lock); + ret = raw_notifier_chain_register(&smem_module_init_notifier_list, nb); + if (smem_module_inited) + nb->notifier_call(nb, 0, NULL); + mutex_unlock(&smem_module_init_notifier_lock); + return ret; +} +EXPORT_SYMBOL(smem_module_init_notifier_register); + +int smem_module_init_notifier_unregister(struct notifier_block *nb) +{ + int ret; + if (!nb) + return -EINVAL; + mutex_lock(&smem_module_init_notifier_lock); + ret = raw_notifier_chain_unregister(&smem_module_init_notifier_list, + nb); + mutex_unlock(&smem_module_init_notifier_lock); + return ret; +} +EXPORT_SYMBOL(smem_module_init_notifier_unregister); + +static void smem_module_init_notify(uint32_t state, void *data) +{ + mutex_lock(&smem_module_init_notifier_lock); + smem_module_inited = 1; + raw_notifier_call_chain(&smem_module_init_notifier_list, + state, data); + mutex_unlock(&smem_module_init_notifier_lock); +} + +/** + * smem_init_security_partition - Init local structures for a secured smem + * partition that has apps as one of the hosts + * + * @entry: Entry in the security TOC for the partition to init + * @num: Partition ID + * + * Initialize local data structures to point to a secured smem partition + * that is accessible by apps and another processor. Assumes that one of the + * listed hosts is apps. Verifiess that the partition is valid, otherwise will + * skip. Checks for memory corruption and will BUG() if detected. Assumes + * smem_areas is already initialized and that smem_areas[0] corresponds to the + * smem region with the secured partitions. + */ +static void smem_init_security_partition(struct smem_toc_entry *entry, + uint32_t num) +{ + uint16_t remote_host; + struct smem_partition_header *hdr; + + if (!entry->offset) { + SMEM_INFO("Skipping smem partition %d - bad offset\n", num); + return; + } + if (!entry->size) { + SMEM_INFO("Skipping smem partition %d - bad size\n", num); + return; + } + if (!entry->size_cacheline) { + SMEM_INFO("Skipping smem partition %d - bad cacheline\n", num); + return; + } + + if (entry->host0 == SMEM_APPS) + remote_host = entry->host1; + else + remote_host = entry->host0; + + if (remote_host >= NUM_SMEM_SUBSYSTEMS) { + SMEM_INFO("Skipping smem partition %d - bad remote:%d\n", num, + remote_host); + return; + } + if (partitions[remote_host].offset) { + SMEM_INFO("Skipping smem partition %d - duplicate of %d\n", num, + partitions[remote_host].partition_num); + return; + } + + hdr = smem_areas[0].virt_addr + entry->offset; + + if (hdr->identifier != SMEM_PART_HDR_IDENTIFIER) { + LOG_ERR("Smem partition %d hdr magic is bad\n", num); + BUG(); + } + if (!hdr->size) { + LOG_ERR("Smem partition %d size is 0\n", num); + BUG(); + } + if (hdr->offset_free_uncached > hdr->size) { + LOG_ERR("Smem partition %d uncached heap exceeds size\n", num); + BUG(); + } + if (hdr->offset_free_cached > hdr->size) { + LOG_ERR("Smem partition %d cached heap exceeds size\n", num); + BUG(); + } + if (hdr->host0 != SMEM_APPS && hdr->host1 != SMEM_APPS) { + LOG_ERR("Smem partition %d hosts don't match TOC\n", num); + BUG(); + } + if (hdr->host0 != remote_host && hdr->host1 != remote_host) { + LOG_ERR("Smem partition %d hosts don't match TOC\n", num); + BUG(); + } + + partitions[remote_host].partition_num = num; + partitions[remote_host].offset = entry->offset; + partitions[remote_host].size_cacheline = entry->size_cacheline; + SMEM_INFO("Partition %d offset:%x remote:%d\n", num, entry->offset, + remote_host); +} + +/** + * smem_init_security - Init local support for secured smem + * + * Looks for a valid security TOC, and if one is found, parses it looking for + * partitions that apps can access. If any such partitions are found, do the + * required local initialization to support them. Assumes smem_areas is inited + * and smem_area[0] corresponds to the smem region with the TOC. + */ +static void smem_init_security(void) +{ + struct smem_toc *toc; + uint32_t i; + + SMEM_DBG("%s\n", __func__); + + toc = smem_areas[0].virt_addr + smem_areas[0].size - 4 * 1024; + + if (toc->identifier != SMEM_TOC_IDENTIFIER) { + LOG_ERR("%s failed: invalid TOC magic\n", __func__); + return; + } + + for (i = 0; i < toc->num_entries; ++i) { + SMEM_DBG("Partition %d host0:%d host1:%d\n", i, + toc->entry[i].host0, + toc->entry[i].host1); + + if (toc->entry[i].host0 == SMEM_APPS || + toc->entry[i].host1 == SMEM_APPS) + smem_init_security_partition(&toc->entry[i], i); + } + + SMEM_DBG("%s done\n", __func__); +} + +static int msm_smem_probe(struct platform_device *pdev) +{ + char *key; + struct resource *r; + phys_addr_t aux_mem_base; + resource_size_t aux_mem_size; + int temp_string_size = 11; /* max 3 digit count */ + char temp_string[temp_string_size]; + int ret; + struct ramdump_segment *ramdump_segments_tmp = NULL; + struct smem_area *smem_areas_tmp = NULL; + int smem_idx = 0; + bool security_enabled; + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smem"); + if (!r) { + LOG_ERR("%s: missing reg\n", __func__); + return -ENODEV; + } + + smem_ram_base = ioremap(r->start, resource_size(r)); + smem_ram_size = resource_size(r); + smem_ram_phys = r->start; + if (!smem_ram_base) { + LOG_ERR("%s: ioremap_nocache() of addr:%pa size: %pa\n", + __func__, + &smem_ram_phys, &smem_ram_size); + return -ENODEV; + } + + if (!smem_initialized_check()) + return -ENODEV; + + /* + * The software implementation requires smem_find(), which needs + * smem_ram_base to be intitialized. The remote spinlock item is + * guarenteed to be allocated by the bootloader, so this is the + * safest and earliest place to init the spinlock. + */ + ret = init_smem_remote_spinlock(); + if (ret) { + LOG_ERR("%s: remote spinlock init failed %d\n", __func__, ret); + return ret; + } + + key = "irq-reg-base"; + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, key); + if (!r) { + LOG_ERR("%s: missing '%s'\n", __func__, key); + return -ENODEV; + } + + num_smem_areas = 1; + while (1) { + scnprintf(temp_string, temp_string_size, "aux-mem%d", + num_smem_areas); + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, + temp_string); + if (!r) + break; + + ++num_smem_areas; + if (num_smem_areas > 999) { + LOG_ERR("%s: max num aux mem regions reached\n", + __func__); + break; + } + } + /* Initialize main SMEM region and SSR ramdump region */ + key = "smem"; + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, key); + if (!r) { + LOG_ERR("%s: missing '%s'\n", __func__, key); + return -ENODEV; + } + + smem_areas_tmp = kmalloc_array(num_smem_areas, sizeof(struct smem_area), + GFP_KERNEL); + if (!smem_areas_tmp) { + LOG_ERR("%s: smem areas kmalloc failed\n", __func__); + ret = -ENOMEM; + goto free_smem_areas; + } + + ramdump_segments_tmp = kmalloc_array(num_smem_areas, + sizeof(struct ramdump_segment), GFP_KERNEL); + if (!ramdump_segments_tmp) { + LOG_ERR("%s: ramdump segment kmalloc failed\n", __func__); + ret = -ENOMEM; + goto free_smem_areas; + } + smem_areas_tmp[smem_idx].phys_addr = r->start; + smem_areas_tmp[smem_idx].size = resource_size(r); + smem_areas_tmp[smem_idx].virt_addr = smem_ram_base; + + ramdump_segments_tmp[smem_idx].address = r->start; + ramdump_segments_tmp[smem_idx].size = resource_size(r); + ++smem_idx; + + /* Configure auxiliary SMEM regions */ + while (1) { + scnprintf(temp_string, temp_string_size, "aux-mem%d", + smem_idx); + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, + temp_string); + if (!r) + break; + aux_mem_base = r->start; + aux_mem_size = resource_size(r); + + ramdump_segments_tmp[smem_idx].address = aux_mem_base; + ramdump_segments_tmp[smem_idx].size = aux_mem_size; + + smem_areas_tmp[smem_idx].phys_addr = aux_mem_base; + smem_areas_tmp[smem_idx].size = aux_mem_size; + smem_areas_tmp[smem_idx].virt_addr = ioremap_nocache( + (unsigned long)(smem_areas_tmp[smem_idx].phys_addr), + smem_areas_tmp[smem_idx].size); + SMEM_DBG("%s: %s = %pa %pa -> %p", __func__, temp_string, + &aux_mem_base, &aux_mem_size, + smem_areas_tmp[smem_idx].virt_addr); + + if (!smem_areas_tmp[smem_idx].virt_addr) { + LOG_ERR("%s: ioremap_nocache() of addr:%pa size: %pa\n", + __func__, + &smem_areas_tmp[smem_idx].phys_addr, + &smem_areas_tmp[smem_idx].size); + ret = -ENOMEM; + goto free_smem_areas; + } + + if (OVERFLOW_ADD_UNSIGNED(uintptr_t, + (uintptr_t)smem_areas_tmp[smem_idx].virt_addr, + smem_areas_tmp[smem_idx].size)) { + LOG_ERR( + "%s: invalid virtual address block %i: %p:%pa\n", + __func__, smem_idx, + smem_areas_tmp[smem_idx].virt_addr, + &smem_areas_tmp[smem_idx].size); + ++smem_idx; + ret = -EINVAL; + goto free_smem_areas; + } + + ++smem_idx; + if (smem_idx > 999) { + LOG_ERR("%s: max num aux mem regions reached\n", + __func__); + break; + } + } + + smem_areas = smem_areas_tmp; + smem_ramdump_segments = ramdump_segments_tmp; + + key = "qcom,mpu-enabled"; + security_enabled = of_property_read_bool(pdev->dev.of_node, key); + if (security_enabled) { + SMEM_INFO("smem security enabled\n"); + smem_init_security(); + } + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret) + LOG_ERR("%s: of_platform_populate failed %d\n", __func__, ret); + + return 0; + +free_smem_areas: + for (smem_idx = smem_idx - 1; smem_idx >= 1; --smem_idx) + iounmap(smem_areas_tmp[smem_idx].virt_addr); + + num_smem_areas = 0; + kfree(ramdump_segments_tmp); + kfree(smem_areas_tmp); + return ret; +} + +static struct of_device_id msm_smem_match_table[] = { + { .compatible = "qcom,smem" }, + {}, +}; + +static struct platform_driver msm_smem_driver = { + .probe = msm_smem_probe, + .driver = { + .name = "msm_smem", + .owner = THIS_MODULE, + .of_match_table = msm_smem_match_table, + }, +}; + +int __init msm_smem_init(void) +{ + static bool registered; + int rc; + + if (registered) + return 0; + + registered = true; + + smem_ipc_log_ctx = ipc_log_context_create(NUM_LOG_PAGES, "smem"); + if (!smem_ipc_log_ctx) { + pr_err("%s: unable to create logging context\n", __func__); + msm_smem_debug_mask = 0; + } + + rc = platform_driver_register(&msm_smem_driver); + if (rc) { + LOG_ERR("%s: msm_smem_driver register failed %d\n", + __func__, rc); + return rc; + } + + smem_module_init_notify(0, NULL); + + return 0; +} + +module_init(msm_smem_init); diff --git a/drivers/soc/qcom/smem_debug.c b/drivers/soc/qcom/smem_debug.c new file mode 100644 index 000000000000..ace89afb614c --- /dev/null +++ b/drivers/soc/qcom/smem_debug.c @@ -0,0 +1,139 @@ +/* arch/arm/mach-msm/smem_debug.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved. + * Author: Brian Swetland <swetland@google.com> + * + * 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 <linux/debugfs.h> +#include <linux/list.h> +#include <linux/ctype.h> +#include <linux/jiffies.h> + +#include <soc/qcom/smem.h> + +#include "smem_private.h" + +#if defined(CONFIG_DEBUG_FS) + +#define SZ_SMEM_ALLOCATION_TABLE 8192 + +static void debug_read_mem(struct seq_file *s) +{ + unsigned n; + struct smem_heap_info *heap_info; + struct smem_heap_entry *toc; + + heap_info = smem_find(SMEM_HEAP_INFO, sizeof(struct smem_heap_info), + 0, + SMEM_ANY_HOST_FLAG); + if (!heap_info) { + seq_puts(s, "SMEM_HEAP_INFO is NULL\n"); + return; + } + toc = smem_find(SMEM_ALLOCATION_TABLE, SZ_SMEM_ALLOCATION_TABLE, + 0, SMEM_ANY_HOST_FLAG); + if (!toc) { + seq_puts(s, "SMEM_ALLOCATION_TABLE is NULL\n"); + return; + } + + seq_printf(s, "heap: init=%d free=%d remain=%d\n", + heap_info->initialized, + heap_info->free_offset, + heap_info->heap_remaining); + + for (n = 0; n < SMEM_NUM_ITEMS; n++) { + if (toc[n].allocated == 0) + continue; + seq_printf(s, "%04d: offset %08x size %08x\n", + n, toc[n].offset, toc[n].size); + } +} + +static void debug_read_smem_version(struct seq_file *s) +{ + uint32_t n, version; + + for (n = 0; n < 32; n++) { + version = smem_get_version(n); + seq_printf(s, "entry %d: smem = %d proc_comm = %d\n", n, + version >> 16, + version & 0xffff); + } +} + +static void debug_read_build_id(struct seq_file *s) +{ + unsigned size; + void *data; + + data = smem_get_entry(SMEM_HW_SW_BUILD_ID, &size, 0, + SMEM_ANY_HOST_FLAG); + if (!data) + return; + + seq_write(s, data, size); +} + +static int debugfs_show(struct seq_file *s, void *data) +{ + void (*show)(struct seq_file *) = s->private; + + show(s); + + return 0; +} + +static int debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, debugfs_show, inode->i_private); +} + +static const struct file_operations debug_ops = { + .open = debug_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +static void debug_create(const char *name, umode_t mode, + struct dentry *dent, + void (*show)(struct seq_file *)) +{ + struct dentry *file; + + file = debugfs_create_file(name, mode, dent, show, &debug_ops); + if (!file) + pr_err("%s: unable to create file '%s'\n", __func__, name); +} + +static int __init smem_debugfs_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("smem", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debug_create("mem", 0444, dent, debug_read_mem); + debug_create("version", 0444, dent, debug_read_smem_version); + + /* NNV: this is google only stuff */ + debug_create("build", 0444, dent, debug_read_build_id); + + return 0; +} + +late_initcall(smem_debugfs_init); +#endif diff --git a/drivers/soc/qcom/smem_private.h b/drivers/soc/qcom/smem_private.h new file mode 100644 index 000000000000..02e2f0e77c13 --- /dev/null +++ b/drivers/soc/qcom/smem_private.h @@ -0,0 +1,104 @@ +/* Copyright (c) 2013, The Linux Foundation. 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 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 _ARCH_ARM_MACH_MSM_SMEM_PRIVATE_H_ +#define _ARCH_ARM_MACH_MSM_SMEM_PRIVATE_H_ + +#include <linux/remote_spinlock.h> + +#include <mach/ramdump.h> + +#define SMD_HEAP_SIZE 512 + +struct smem_heap_info { + unsigned initialized; + unsigned free_offset; + unsigned heap_remaining; + unsigned reserved; +}; + +struct smem_heap_entry { + unsigned allocated; + unsigned offset; + unsigned size; + unsigned reserved; /* bits 1:0 reserved, bits 31:2 aux smem base addr */ +}; +#define BASE_ADDR_MASK 0xfffffffc + +struct smem_proc_comm { + unsigned command; + unsigned status; + unsigned data1; + unsigned data2; +}; + +struct smem_shared { + struct smem_proc_comm proc_comm[4]; + unsigned version[32]; + struct smem_heap_info heap_info; + struct smem_heap_entry heap_toc[SMD_HEAP_SIZE]; +}; + +struct smem_area { + phys_addr_t phys_addr; + resource_size_t size; + void __iomem *virt_addr; +}; + +/* used for unit testing spinlocks */ +remote_spinlock_t *smem_get_remote_spinlock(void); + +bool smem_initialized_check(void); + +/** + * smem_module_init_notifier_register() - Register a smem module + * init notifier block + * @nb: Notifier block to be registered + * + * In order to mark the dependency on SMEM Driver module initialization + * register a notifier using this API. Once the smem module_init is + * done, notification will be passed to the registered module. + */ +int smem_module_init_notifier_register(struct notifier_block *nb); + +/** + * smem_module_init_notifier_register() - Unregister a smem module + * init notifier block + * @nb: Notifier block to be unregistered + */ +int smem_module_init_notifier_unregister(struct notifier_block *nb); + +/** + * smem_get_free_space() - Get the available allocation free space for a + * partition + * + * @to_proc: remote SMEM host. Determines the applicable partition + * @returns: size in bytes available to allocate + * + * Helper function for SMD so that SMD only scans the channel allocation + * table for a partition when it is reasonably certain that a channel has + * actually been created, because scanning can be expensive. Creating a channel + * will consume some of the free space in a partition, so SMD can compare the + * last free space size against the current free space size to determine if + * a channel may have been created. SMD can't do this directly, because the + * necessary partition internals are restricted to just SMEM. + */ +unsigned smem_get_free_space(unsigned to_proc); + +/** + * smem_get_version() - Get the smem user version number + * + * @idx: SMEM user idx in SMEM_VERSION_INFO table. + * @returns: smem version number if success otherwise zero. + */ +unsigned smem_get_version(unsigned idx); +#endif /* _ARCH_ARM_MACH_MSM_SMEM_PRIVATE_H_ */ diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c index c185d2f06066..712b621ee16b 100644 --- a/drivers/spi/spi_qsd.c +++ b/drivers/spi/spi_qsd.c @@ -48,7 +48,7 @@ static int msm_spi_pm_resume_runtime(struct device *device); static int msm_spi_pm_suspend_runtime(struct device *device); - +static inline void msm_spi_dma_unmap_buffers(struct msm_spi *dd); static inline int msm_spi_configure_gsbi(struct msm_spi *dd, struct platform_device *pdev) @@ -133,6 +133,40 @@ static inline void msm_spi_free_gpios(struct msm_spi *dd) } } +static inline int msm_spi_request_cs_gpio(struct msm_spi *dd) +{ + int cs_num; + int rc; + + cs_num = dd->cur_msg->spi->chip_select; + if ((!(dd->cur_msg->spi->mode & SPI_LOOP)) && + (!(dd->cs_gpios[cs_num].valid)) && + (dd->cs_gpios[cs_num].gpio_num >= 0)) { + rc = gpio_request(dd->cs_gpios[cs_num].gpio_num, + spi_cs_rsrcs[cs_num]); + if (rc) { + dev_err(dd->dev, + "gpio_request for pin %d failed,error %d\n", + dd->cs_gpios[cs_num].gpio_num, rc); + return rc; + } + dd->cs_gpios[cs_num].valid = 1; + } + return 0; +} + +static inline void msm_spi_free_cs_gpio(struct msm_spi *dd) +{ + int cs_num; + + cs_num = dd->cur_msg->spi->chip_select; + if (dd->cs_gpios[cs_num].valid) { + gpio_free(dd->cs_gpios[cs_num].gpio_num); + dd->cs_gpios[cs_num].valid = 0; + } +} + + /** * msm_spi_clk_max_rate: finds the nearest lower rate for a clk * @clk the clock for which to find nearest lower rate @@ -783,117 +817,190 @@ static void msm_spi_bam_flush(struct msm_spi *dd) msm_spi_bam_pipe_flush(dd, SPI_BAM_PRODUCER_PIPE); } -/** - * msm_spi_bam_begin_transfer: transfer dd->tx_bytes_remaining bytes - * using BAM. - * @brief BAM can transfer SPI_MAX_TRFR_BTWN_RESETS byte at a single - * transfer. Between transfer QUP must change to reset state. A loop is - * issuing a single BAM transfer at a time. If another tsranfer is - * required, it waits for the trasfer to finish, then moving to reset - * state, and back to run state to issue the next transfer. - * The function dose not wait for the last transfer to end, or if only - * a single transfer is required, the function dose not wait for it to - * end. - * @timeout max time in jiffies to wait for a transfer to finish. - * @return zero on success - */ static int -msm_spi_bam_begin_transfer(struct msm_spi *dd, u32 timeout, u8 bpw) +msm_spi_bam_process_rx(struct msm_spi *dd, u32 *bytes_to_send, u32 desc_cnt) { - u32 bytes_to_send, bytes_sent, n_words_xfr, cons_flags, prod_flags; - int ret; + int ret = 0; + u32 data_xfr_size = 0, rem_bc = 0; + u32 prod_flags = 0; + + rem_bc = dd->cur_rx_transfer->len - dd->bam.curr_rx_bytes_recvd; + data_xfr_size = (rem_bc < *bytes_to_send) ? rem_bc : *bytes_to_send; + /* - * QUP must move to reset mode every 64K-1 bytes of transfer - * (counter is 16 bit) + * set flags for last descriptor only */ - if (dd->tx_bytes_remaining > SPI_MAX_TRFR_BTWN_RESETS) { - /* assert chip select unconditionally */ - u32 spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL); - if (!(spi_ioc & SPI_IO_C_FORCE_CS)) - writel_relaxed(spi_ioc | SPI_IO_C_FORCE_CS, - dd->base + SPI_IO_CONTROL); - } + if ((desc_cnt == 1) + || (*bytes_to_send == data_xfr_size)) + prod_flags = (dd->write_buf) + ? 0 : (SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_NWD); - /* Following flags are required since we are waiting on all transfers */ - cons_flags = SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_NWD; /* - * on a balanced transaction, BAM will set the flags on the producer - * pipe based on the flags set on the consumer pipe + * enqueue read buffer in BAM */ - prod_flags = (dd->write_buf) ? 0 : cons_flags; + ret = sps_transfer_one(dd->bam.prod.handle, + dd->cur_rx_transfer->rx_dma + + dd->bam.curr_rx_bytes_recvd, + data_xfr_size, dd, prod_flags); + if (ret < 0) { + dev_err(dd->dev, + "%s: Failed to queue producer BAM transfer", + __func__); + return ret; + } - while (dd->tx_bytes_remaining > 0) { - bytes_sent = dd->cur_transfer->len - dd->tx_bytes_remaining; - bytes_to_send = min_t(u32, dd->tx_bytes_remaining - , SPI_MAX_TRFR_BTWN_RESETS); - n_words_xfr = DIV_ROUND_UP(bytes_to_send - , dd->bytes_per_word); + dd->bam.curr_rx_bytes_recvd += data_xfr_size; + *bytes_to_send -= data_xfr_size; + dd->bam.bam_rx_len -= data_xfr_size; + + if (!(dd->cur_rx_transfer->len - dd->bam.curr_rx_bytes_recvd)) { + struct spi_transfer *t = dd->cur_rx_transfer; + struct spi_transfer *next; + if (t->transfer_list.next != &dd->cur_msg->transfers) { + next = list_entry(t->transfer_list.next, + struct spi_transfer, + transfer_list); + dd->read_buf = next->rx_buf; + dd->cur_rx_transfer = next; + dd->bam.curr_rx_bytes_recvd = 0; + } + } + return data_xfr_size; +} - msm_spi_set_mx_counts(dd, n_words_xfr); +static int +msm_spi_bam_process_tx(struct msm_spi *dd, u32 *bytes_to_send, u32 desc_cnt) +{ + int ret = 0; + u32 data_xfr_size = 0, rem_bc = 0; + u32 cons_flags = 0; - ret = msm_spi_set_state(dd, SPI_OP_STATE_RUN); - if (ret < 0) { - dev_err(dd->dev, - "%s: Failed to set QUP state to run", - __func__); - goto xfr_err; - } + rem_bc = dd->cur_tx_transfer->len - dd->bam.curr_tx_bytes_sent; + data_xfr_size = (rem_bc < *bytes_to_send) ? rem_bc : *bytes_to_send; - /* enqueue read buffer in BAM */ - if (dd->read_buf) { - ret = sps_transfer_one(dd->bam.prod.handle, - dd->cur_transfer->rx_dma + bytes_sent, - bytes_to_send, dd, prod_flags); - if (ret < 0) { - dev_err(dd->dev, - "%s: Failed to queue producer BAM transfer", - __func__); - goto xfr_err; - } - } + /* + * set flags for last descriptor only + */ + if ((desc_cnt == 1) + || (*bytes_to_send == data_xfr_size)) + cons_flags = SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_NWD; - /* enqueue write buffer in BAM */ - if (dd->write_buf) { - ret = sps_transfer_one(dd->bam.cons.handle, - dd->cur_transfer->tx_dma + bytes_sent, - bytes_to_send, dd, cons_flags); - if (ret < 0) { - dev_err(dd->dev, - "%s: Failed to queue consumer BAM transfer", - __func__); - goto xfr_err; - } + /* + * enqueue write buffer in BAM + */ + ret = sps_transfer_one(dd->bam.cons.handle, + dd->cur_tx_transfer->tx_dma + + dd->bam.curr_tx_bytes_sent, + data_xfr_size, dd, cons_flags); + if (ret < 0) { + dev_err(dd->dev, + "%s: Failed to queue consumer BAM transfer", + __func__); + return ret; + } + + dd->bam.curr_tx_bytes_sent += data_xfr_size; + *bytes_to_send -= data_xfr_size; + dd->bam.bam_tx_len -= data_xfr_size; + + if (!(dd->cur_tx_transfer->len - dd->bam.curr_tx_bytes_sent)) { + struct spi_transfer *t = dd->cur_tx_transfer; + struct spi_transfer *next; + if (t->transfer_list.next != &dd->cur_msg->transfers) { + next = list_entry(t->transfer_list.next, + struct spi_transfer, + transfer_list); + dd->write_buf = next->tx_buf; + dd->cur_tx_transfer = next; + dd->bam.curr_tx_bytes_sent = 0; } + } + return data_xfr_size; +} - dd->tx_bytes_remaining -= bytes_to_send; - /* move to reset state after SPI_MAX_TRFR_BTWN_RESETS */ - if (dd->tx_bytes_remaining > 0) { - if (!wait_for_completion_timeout( - &dd->transfer_complete, timeout)) { - dev_err(dd->dev, - "%s: SPI transaction timeout", - __func__); - dd->cur_msg->status = -EIO; - ret = -EIO; +/** + * msm_spi_bam_begin_transfer: transfer dd->tx_bytes_remaining bytes + * using BAM. + * @brief BAM can transfer SPI_MAX_TRFR_BTWN_RESETS byte at a single + * transfer. Between transfer QUP must change to reset state. A loop is + * issuing a single BAM transfer at a time. + * @return zero on success + */ +static int +msm_spi_bam_begin_transfer(struct msm_spi *dd) +{ + u32 tx_bytes_to_send = 0, rx_bytes_to_recv = 0; + u32 n_words_xfr; + s32 ret = 0; + u32 prod_desc_cnt = SPI_BAM_MAX_DESC_NUM - 1; + u32 cons_desc_cnt = SPI_BAM_MAX_DESC_NUM - 1; + u32 byte_count = 0; + + + rx_bytes_to_recv = min_t(u32, dd->bam.bam_rx_len, + SPI_MAX_TRFR_BTWN_RESETS); + tx_bytes_to_send = min_t(u32, dd->bam.bam_tx_len, + SPI_MAX_TRFR_BTWN_RESETS); + n_words_xfr = DIV_ROUND_UP(rx_bytes_to_recv, + dd->bytes_per_word); + + msm_spi_set_mx_counts(dd, n_words_xfr); + ret = msm_spi_set_state(dd, SPI_OP_STATE_RUN); + if (ret < 0) { + dev_err(dd->dev, + "%s: Failed to set QUP state to run", + __func__); + goto xfr_err; + } + + while ((rx_bytes_to_recv + tx_bytes_to_send) && + ((cons_desc_cnt + prod_desc_cnt) > 0)) { + if (dd->read_buf && (prod_desc_cnt > 0)) { + ret = msm_spi_bam_process_rx(dd, &rx_bytes_to_recv, + prod_desc_cnt); + if (ret < 0) goto xfr_err; - } - ret = msm_spi_set_state(dd, SPI_OP_STATE_RESET); - if (ret < 0) { - dev_err(dd->dev, - "%s: Failed to set QUP state to reset", - __func__); + prod_desc_cnt--; + } + + if (dd->write_buf && (cons_desc_cnt > 0)) { + ret = msm_spi_bam_process_tx(dd, &tx_bytes_to_send, + cons_desc_cnt); + if (ret < 0) goto xfr_err; - } - init_completion(&dd->transfer_complete); + cons_desc_cnt--; } + byte_count += ret; } - return 0; + dd->tx_bytes_remaining -= min_t(u32, byte_count, + SPI_MAX_TRFR_BTWN_RESETS); + return 0; xfr_err: return ret; } +static int +msm_spi_bam_next_transfer(struct msm_spi *dd) +{ + if (dd->mode != SPI_BAM_MODE) + return 0; + + if (dd->tx_bytes_remaining > 0) { + init_completion(&dd->transfer_complete); + if (msm_spi_set_state(dd, SPI_OP_STATE_RESET)) + return 0; + if ((msm_spi_bam_begin_transfer(dd)) < 0) { + dev_err(dd->dev, "%s: BAM transfer setup failed\n", + __func__); + return 0; + } + return 1; + } + return 0; +} + static void msm_spi_setup_dm_transfer(struct msm_spi *dd) { dmov_box *box; @@ -1096,6 +1203,16 @@ static int msm_spi_dm_send_next(struct msm_spi *dd) return 0; } +static int msm_spi_dma_send_next(struct msm_spi *dd) +{ + int ret = 0; + if (dd->mode == SPI_DMOV_MODE) + ret = msm_spi_dm_send_next(dd); + if (dd->mode == SPI_BAM_MODE) + ret = msm_spi_bam_next_transfer(dd); + return ret; +} + static inline void msm_spi_ack_transfer(struct msm_spi *dd) { writel_relaxed(SPI_OP_MAX_INPUT_DONE_FLAG | @@ -1165,10 +1282,8 @@ static irqreturn_t msm_spi_input_irq(int irq, void *dev_id) if ((!dd->read_buf || op & SPI_OP_MAX_INPUT_DONE_FLAG) && (!dd->write_buf || op & SPI_OP_MAX_OUTPUT_DONE_FLAG)) { msm_spi_ack_transfer(dd); - if (dd->rx_unaligned_len == 0) { if (atomic_inc_return(&dd->rx_irq_called) == 1) return IRQ_HANDLED; - } msm_spi_complete(dd); return IRQ_HANDLED; } @@ -1297,14 +1412,14 @@ static irqreturn_t msm_spi_error_irq(int irq, void *dev_id) } /** - * msm_spi_dma_map_buffers: prepares buffer for DMA transfer + * msm_spi_dmov_map_buffers: prepares buffer for DMA transfer * @return zero on success or negative error code * * calls dma_map_single() on the read/write buffers, effectively invalidating * their cash entries. for For WR-WR and WR-RD transfers, allocates temporary * buffer and copy the data to/from the client buffers */ -static int msm_spi_dma_map_buffers(struct msm_spi *dd) +static int msm_spi_dmov_map_buffers(struct msm_spi *dd) { struct device *dev; struct spi_transfer *first_xfr; @@ -1323,7 +1438,7 @@ static int msm_spi_dma_map_buffers(struct msm_spi *dd) * For WR-WR and WR-RD transfers, we allocate our own temporary * buffer and copy the data to/from the client buffers. */ - if (dd->multi_xfr) { + if (!dd->qup_ver && dd->multi_xfr) { dd->temp_buf = kzalloc(dd->cur_msg_len, GFP_KERNEL | __GFP_DMA); if (!dd->temp_buf) @@ -1384,6 +1499,70 @@ error: return ret; } +static int msm_spi_bam_map_buffers(struct msm_spi *dd) +{ + int ret = -EINVAL; + struct device *dev; + struct spi_transfer *first_xfr; + struct spi_transfer *nxt_xfr; + void *tx_buf, *rx_buf; + u32 tx_len, rx_len; + int num_xfrs_grped = dd->num_xfrs_grped; + + dev = &dd->cur_msg->spi->dev; + first_xfr = dd->cur_transfer; + + do { + tx_buf = (void *)first_xfr->tx_buf; + rx_buf = first_xfr->rx_buf; + tx_len = rx_len = first_xfr->len; + if (tx_buf != NULL) { + first_xfr->tx_dma = dma_map_single(dev, tx_buf, + tx_len, DMA_TO_DEVICE); + if (dma_mapping_error(NULL, first_xfr->tx_dma)) { + ret = -ENOMEM; + goto error; + } + } + + if (rx_buf != NULL) { + first_xfr->rx_dma = dma_map_single(dev, rx_buf, rx_len, + DMA_FROM_DEVICE); + if (dma_mapping_error(NULL, first_xfr->rx_dma)) { + if (tx_buf != NULL) + dma_unmap_single(NULL, + first_xfr->tx_dma, + tx_len, DMA_TO_DEVICE); + ret = -ENOMEM; + goto error; + } + } + + nxt_xfr = list_entry(first_xfr->transfer_list.next, + struct spi_transfer, transfer_list); + + if (nxt_xfr == NULL) + break; + num_xfrs_grped--; + first_xfr = nxt_xfr; + } while (num_xfrs_grped > 0); + + return 0; +error: + msm_spi_dma_unmap_buffers(dd); + return ret; +} + +static int msm_spi_dma_map_buffers(struct msm_spi *dd) +{ + int ret = 0; + if (dd->mode == SPI_DMOV_MODE) + ret = msm_spi_dmov_map_buffers(dd); + else if (dd->mode == SPI_BAM_MODE) + ret = msm_spi_bam_map_buffers(dd); + return ret; +} + static void msm_spi_dmov_unmap_buffers(struct msm_spi *dd) { struct device *dev; @@ -1455,21 +1634,39 @@ unmap_end: static void msm_spi_bam_unmap_buffers(struct msm_spi *dd) { struct device *dev; + int num_xfrs_grped = dd->num_xfrs_grped; + struct spi_transfer *first_xfr; + struct spi_transfer *nxt_xfr; + void *tx_buf, *rx_buf; + u32 tx_len, rx_len; + + dev = &dd->cur_msg->spi->dev; + first_xfr = dd->cur_transfer; /* mapped by client */ if (dd->cur_msg->is_dma_mapped) return; - dev = &dd->cur_msg->spi->dev; - if (dd->cur_transfer->rx_buf) - dma_unmap_single(dev, dd->cur_transfer->rx_dma, - dd->cur_transfer->len, - DMA_FROM_DEVICE); + do { + tx_buf = (void *)first_xfr->tx_buf; + rx_buf = first_xfr->rx_buf; + tx_len = rx_len = first_xfr->len; + if (tx_buf != NULL) + dma_unmap_single(dev, first_xfr->tx_dma, + tx_len, DMA_TO_DEVICE); - if (dd->cur_transfer->tx_buf) - dma_unmap_single(dev, dd->cur_transfer->tx_dma, - dd->cur_transfer->len, - DMA_TO_DEVICE); + if (rx_buf != NULL) + dma_unmap_single(dev, first_xfr->rx_dma, + rx_len, DMA_FROM_DEVICE); + + nxt_xfr = list_entry(first_xfr->transfer_list.next, + struct spi_transfer, transfer_list); + + if (nxt_xfr == NULL) + break; + num_xfrs_grped--; + first_xfr = nxt_xfr; + } while (num_xfrs_grped > 0); } static inline void msm_spi_dma_unmap_buffers(struct msm_spi *dd) @@ -1506,7 +1703,8 @@ msm_spi_use_dma(struct msm_spi *dd, struct spi_transfer *tr, u8 bpw) if (dd->cur_msg_len < 3*dd->input_block_size) return false; - if (dd->multi_xfr && !dd->read_len && !dd->write_len) + if ((dd->qup_ver != SPI_QUP_VERSION_BFAM) && + dd->multi_xfr && !dd->read_len && !dd->write_len) return false; if (dd->qup_ver == SPI_QUP_VERSION_NONE) { @@ -1688,11 +1886,17 @@ static void msm_spi_process_transfer(struct msm_spi *dd) msm_spi_set_transfer_mode(dd, bpw, read_count); msm_spi_set_mx_counts(dd, read_count); - if ((dd->mode == SPI_BAM_MODE) || (dd->mode == SPI_DMOV_MODE)) + if (dd->mode == SPI_DMOV_MODE) { if (msm_spi_dma_map_buffers(dd) < 0) { pr_err("Mapping DMA buffers\n"); return; + } + } else if (dd->mode == SPI_BAM_MODE) { + if (msm_spi_dma_map_buffers(dd) < 0) { + pr_err("Mapping DMA buffers\n"); + return; } + } msm_spi_set_qup_io_modes(dd); msm_spi_set_spi_config(dd, bpw); msm_spi_set_qup_config(dd, bpw); @@ -1712,7 +1916,7 @@ static void msm_spi_process_transfer(struct msm_spi *dd) goto transfer_end; msm_spi_start_write(dd, read_count); } else if (dd->mode == SPI_BAM_MODE) { - if ((msm_spi_bam_begin_transfer(dd, timeout, bpw)) < 0) + if ((msm_spi_bam_begin_transfer(dd)) < 0) dev_err(dd->dev, "%s: BAM transfer setup failed\n", __func__); } @@ -1749,9 +1953,10 @@ static void msm_spi_process_transfer(struct msm_spi *dd) msm_spi_bam_flush(dd); break; } - } while (msm_spi_dm_send_next(dd)); + } while (msm_spi_dma_send_next(dd)); + + msm_spi_udelay(dd->xfrs_delay_usec); - msm_spi_udelay(dd->cur_transfer->delay_usecs); transfer_end: msm_spi_dma_unmap_buffers(dd); dd->mode = SPI_MODE_NONE; @@ -1803,155 +2008,137 @@ static void get_transfer_length(struct msm_spi *dd) dd->multi_xfr = 1; } +static inline void write_force_cs(struct msm_spi *dd, bool set_flag) +{ + u32 spi_ioc; + u32 spi_ioc_orig; + + spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL); + spi_ioc_orig = spi_ioc; + if (set_flag) + spi_ioc |= SPI_IO_C_FORCE_CS; + else + spi_ioc &= ~SPI_IO_C_FORCE_CS; + + if (spi_ioc != spi_ioc_orig) + writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL); +} + static inline int combine_transfers(struct msm_spi *dd) { struct spi_transfer *t = dd->cur_transfer; struct spi_transfer *nxt; int xfrs_grped = 1; + dd->xfrs_delay_usec = 0; + + dd->bam.bam_rx_len = dd->bam.bam_tx_len = 0; dd->cur_msg_len = dd->cur_transfer->len; + + if (dd->cur_transfer->tx_buf) + dd->bam.bam_tx_len += dd->cur_transfer->len; + if (dd->cur_transfer->rx_buf) + dd->bam.bam_rx_len += dd->cur_transfer->len; + while (t->transfer_list.next != &dd->cur_msg->transfers) { nxt = list_entry(t->transfer_list.next, struct spi_transfer, transfer_list); if (t->cs_change != nxt->cs_change) return xfrs_grped; + if (t->delay_usecs) { + dd->xfrs_delay_usec = t->delay_usecs; + dev_info(dd->dev, "SPI slave requests delay per txn :%d usecs", + t->delay_usecs); + return xfrs_grped; + } + if (nxt->tx_buf) + dd->bam.bam_tx_len += nxt->len; + if (nxt->rx_buf) + dd->bam.bam_rx_len += nxt->len; + dd->cur_msg_len += nxt->len; xfrs_grped++; t = nxt; } - return xfrs_grped; -} -static inline void write_force_cs(struct msm_spi *dd, bool set_flag) -{ - u32 spi_ioc; - u32 spi_ioc_orig; - - spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL); - spi_ioc_orig = spi_ioc; - if (set_flag) - spi_ioc |= SPI_IO_C_FORCE_CS; - else - spi_ioc &= ~SPI_IO_C_FORCE_CS; + if (1 == xfrs_grped) + dd->xfrs_delay_usec = dd->cur_transfer->delay_usecs; - if (spi_ioc != spi_ioc_orig) - writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL); + return xfrs_grped; } static void msm_spi_process_message(struct msm_spi *dd) { int xfrs_grped = 0; - int cs_num; int rc; - bool xfer_delay = false; - struct spi_transfer *tr; + dd->num_xfrs_grped = 0; + dd->bam.curr_rx_bytes_recvd = dd->bam.curr_tx_bytes_sent = 0; dd->write_xfr_cnt = dd->read_xfr_cnt = 0; - cs_num = dd->cur_msg->spi->chip_select; - if ((!(dd->cur_msg->spi->mode & SPI_LOOP)) && - (!(dd->cs_gpios[cs_num].valid)) && - (dd->cs_gpios[cs_num].gpio_num >= 0)) { - rc = gpio_request(dd->cs_gpios[cs_num].gpio_num, - spi_cs_rsrcs[cs_num]); - if (rc) { - dev_err(dd->dev, "gpio_request for pin %d failed with " - "error %d\n", dd->cs_gpios[cs_num].gpio_num, - rc); - return; - } - dd->cs_gpios[cs_num].valid = 1; - } - - list_for_each_entry(tr, - &dd->cur_msg->transfers, - transfer_list) { - if (tr->delay_usecs) { - dev_info(dd->dev, "SPI slave requests delay per txn :%d", - tr->delay_usecs); - xfer_delay = true; - break; - } - } - - /* Don't combine xfers if delay is needed after every xfer */ - if (dd->qup_ver || xfer_delay) { - if (dd->qup_ver) - write_force_cs(dd, 0); - list_for_each_entry(dd->cur_transfer, - &dd->cur_msg->transfers, - transfer_list) { - struct spi_transfer *t = dd->cur_transfer; - struct spi_transfer *nxt; + rc = msm_spi_request_cs_gpio(dd); + if (rc) + return; - if (t->transfer_list.next != &dd->cur_msg->transfers) { - nxt = list_entry(t->transfer_list.next, + dd->cur_transfer = list_first_entry(&dd->cur_msg->transfers, struct spi_transfer, transfer_list); - if (dd->qup_ver && - t->cs_change == nxt->cs_change) - write_force_cs(dd, 1); - else if (dd->qup_ver) - write_force_cs(dd, 0); - } + get_transfer_length(dd); + if (dd->qup_ver || (dd->multi_xfr && !dd->read_len && !dd->write_len)) { - dd->cur_msg_len = dd->cur_transfer->len; - msm_spi_process_transfer(dd); - } - } else { - dd->cur_transfer = list_first_entry(&dd->cur_msg->transfers, - struct spi_transfer, - transfer_list); - get_transfer_length(dd); - if (dd->multi_xfr && !dd->read_len && !dd->write_len) { - /* - * Handling of multi-transfers. - * FIFO mode is used by default - */ - list_for_each_entry(dd->cur_transfer, - &dd->cur_msg->transfers, - transfer_list) { - if (!dd->cur_transfer->len) - goto error; - if (xfrs_grped) { - xfrs_grped--; - continue; - } else { - dd->read_len = dd->write_len = 0; - xfrs_grped = combine_transfers(dd); - } + if (dd->qup_ver) + write_force_cs(dd, 0); - dd->cur_tx_transfer = dd->cur_transfer; - dd->cur_rx_transfer = dd->cur_transfer; - msm_spi_process_transfer(dd); + /* + * Handling of multi-transfers. + * FIFO mode is used by default + */ + list_for_each_entry(dd->cur_transfer, + &dd->cur_msg->transfers, + transfer_list) { + if (!dd->cur_transfer->len) + goto error; + if (xfrs_grped) { xfrs_grped--; - } - } else { - /* Handling of a single transfer or - * WR-WR or WR-RD transfers - */ - if ((!dd->cur_msg->is_dma_mapped) && - (msm_spi_use_dma(dd, dd->cur_transfer, - dd->cur_transfer->bits_per_word))) { - /* Mapping of DMA buffers */ - int ret = msm_spi_dma_map_buffers(dd); - if (ret < 0) { - dd->cur_msg->status = ret; - goto error; - } + continue; + } else { + dd->read_len = dd->write_len = 0; + xfrs_grped = combine_transfers(dd); + dd->num_xfrs_grped = xfrs_grped; + if (dd->qup_ver) + write_force_cs(dd, 1); } dd->cur_tx_transfer = dd->cur_transfer; dd->cur_rx_transfer = dd->cur_transfer; msm_spi_process_transfer(dd); + if (dd->qup_ver && !dd->xfrs_delay_usec) + write_force_cs(dd, 0); + xfrs_grped--; } + } else { + /* Handling of a single transfer or + * WR-WR or WR-RD transfers + */ + if ((!dd->cur_msg->is_dma_mapped) && + (msm_spi_use_dma(dd, dd->cur_transfer, + dd->cur_transfer->bits_per_word))) { + /* Mapping of DMA buffers */ + int ret = msm_spi_dma_map_buffers(dd); + if (ret < 0) { + dd->cur_msg->status = ret; + goto error; + } + } + + dd->cur_tx_transfer = dd->cur_transfer; + dd->cur_rx_transfer = dd->cur_transfer; + dd->num_xfrs_grped = 1; + msm_spi_process_transfer(dd); } error: - if (dd->cs_gpios[cs_num].valid) { - gpio_free(dd->cs_gpios[cs_num].gpio_num); - dd->cs_gpios[cs_num].valid = 0; - } + msm_spi_free_cs_gpio(dd); return; } diff --git a/drivers/spi/spi_qsd.h b/drivers/spi/spi_qsd.h index 3fd8a90991ca..8d272a1c28e3 100644 --- a/drivers/spi/spi_qsd.h +++ b/drivers/spi/spi_qsd.h @@ -289,6 +289,10 @@ struct msm_spi_bam { struct msm_spi_bam_pipe prod; struct msm_spi_bam_pipe cons; bool deregister_required; + u32 curr_rx_bytes_recvd; + u32 curr_tx_bytes_sent; + u32 bam_rx_len; + u32 bam_tx_len; }; struct msm_spi { @@ -382,6 +386,8 @@ struct msm_spi { struct spi_cs_gpio cs_gpios[ARRAY_SIZE(spi_cs_rsrcs)]; enum msm_spi_qup_version qup_ver; int max_trfr_len; + int num_xfrs_grped; + u16 xfrs_delay_usec; }; /* Forward declaration */ diff --git a/drivers/spmi/qpnp-int.c b/drivers/spmi/qpnp-int.c index 9022cef35782..6379528f963d 100644 --- a/drivers/spmi/qpnp-int.c +++ b/drivers/spmi/qpnp-int.c @@ -27,10 +27,9 @@ #include <linux/slab.h> #include <linux/printk.h> #include <linux/ratelimit.h> +#include <linux/irqchip/qpnp-int.h> #include <asm/irq.h> -#include <asm/mach/irq.h> -#include <mach/qpnp-int.h> /* 16 slave_ids, 256 per_ids per slave, and 8 ints per per_id */ #define QPNPINT_NR_IRQS (16 * 256 * 8) diff --git a/drivers/spmi/spmi-dbgfs.h b/drivers/spmi/spmi-dbgfs.h index a419002830a2..255377b5beef 100644 --- a/drivers/spmi/spmi-dbgfs.h +++ b/drivers/spmi/spmi-dbgfs.h @@ -17,6 +17,9 @@ #ifdef CONFIG_DEBUG_FS int spmi_dfs_add_controller(struct spmi_controller *ctrl); int spmi_dfs_del_controller(struct spmi_controller *ctrl); +struct dentry *spmi_dfs_create_file(struct spmi_controller *ctrl, + const char *name, void *data, + const struct file_operations *fops); #else static inline int spmi_dfs_add_controller(struct spmi_controller *ctrl) { @@ -26,10 +29,13 @@ static inline int spmi_dfs_del_controller(struct spmi_controller *ctrl) { return 0; } -#endif -struct dentry *spmi_dfs_create_file(struct spmi_controller *ctrl, +static inline struct dentry *spmi_dfs_create_file(struct spmi_controller *ctrl, const char *name, void *data, - const struct file_operations *fops); + const struct file_operations *fops) +{ + return 0; +} +#endif #endif /* _SPMI_DBGFS_H */ diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 31b34d28bbe7..e4f13f19a5a5 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -25,7 +25,7 @@ #include <linux/module.h> #include <linux/seq_file.h> #include <linux/syscore_ops.h> -#include <mach/qpnp-int.h> +#include <linux/irqchip/qpnp-int.h> #include "spmi-dbgfs.h" #define SPMI_PMIC_ARB_NAME "spmi_pmic_arb" @@ -33,6 +33,9 @@ /* PMIC Arbiter configuration registers */ #define PMIC_ARB_VERSION 0x0000 #define PMIC_ARB_INT_EN 0x0004 +#define PMIC_ARB_GENI_CTRL 0x0024 +#define PMIC_ARB_GENI_STATUS 0x0028 +#define PMIC_ARB_PROTOCOL_IRQ_STATUS (0x700 + 0x820) /* PMIC Arbiter channel registers */ #define PMIC_ARB_CMD(N) (0x0800 + (0x80 * (N))) @@ -209,6 +212,17 @@ pa_write_data(struct spmi_pmic_arb_dev *dev, u8 *buf, u32 reg, u8 bc) pmic_arb_write(dev, reg, data); } +static void pmic_arb_dbg_dump_regs(struct spmi_pmic_arb_dev *pmic_arb, int ret, + const char *msg) +{ + u32 irq = readl_relaxed(pmic_arb->cnfg + PMIC_ARB_PROTOCOL_IRQ_STATUS); + u32 geni_stat = readl_relaxed(pmic_arb->cnfg + PMIC_ARB_GENI_STATUS); + u32 geni_ctrl = readl_relaxed(pmic_arb->cnfg + PMIC_ARB_GENI_CTRL); + dev_err(pmic_arb->dev, + "err:%d on %s PROTOCOL_IRQ_STATUS:0x%x GENI_STATUS:0x%x GENI_CTRL:0x%x\n", + ret, msg, irq, geni_stat, geni_ctrl); +} + /* Non-data command */ static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid) { @@ -230,6 +244,8 @@ static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid) rc = pmic_arb_wait_for_done(pmic_arb); spin_unlock_irqrestore(&pmic_arb->lock, flags); + if (rc) + pmic_arb_dbg_dump_regs(pmic_arb, rc, "cmd"); return rc; } @@ -277,6 +293,8 @@ static int pmic_arb_read_cmd(struct spmi_controller *ctrl, done: spin_unlock_irqrestore(&pmic_arb->lock, flags); + if (rc) + pmic_arb_dbg_dump_regs(pmic_arb, rc, "read_cmd"); return rc; } @@ -323,6 +341,8 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, rc = pmic_arb_wait_for_done(pmic_arb); spin_unlock_irqrestore(&pmic_arb->lock, flags); + if (rc) + pmic_arb_dbg_dump_regs(pmic_arb, rc, "write_cmd"); return rc; } diff --git a/drivers/staging/android/alarm-dev.c b/drivers/staging/android/alarm-dev.c index 6dc27dac679d..eb632161d0c9 100644 --- a/drivers/staging/android/alarm-dev.c +++ b/drivers/staging/android/alarm-dev.c @@ -40,7 +40,8 @@ do { \ #define ANDROID_ALARM_WAKEUP_MASK ( \ ANDROID_ALARM_RTC_WAKEUP_MASK | \ - ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK) + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK | \ + ANDROID_ALARM_RTC_POWEROFF_WAKEUP_MASK) static int alarm_opened; static DEFINE_SPINLOCK(alarm_slock); @@ -64,7 +65,8 @@ static struct devalarm alarms[ANDROID_ALARM_TYPE_COUNT]; static int is_wakeup(enum android_alarm_type type) { return (type == ANDROID_ALARM_RTC_WAKEUP || - type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP); + type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP || + type == ANDROID_ALARM_RTC_POWEROFF_WAKEUP); } @@ -92,7 +94,7 @@ static void devalarm_cancel(struct devalarm *alrm) hrtimer_cancel(&alrm->u.hrt); } -static void alarm_clear(enum android_alarm_type alarm_type) +static void alarm_clear(enum android_alarm_type alarm_type, struct timespec *ts) { uint32_t alarm_type_mask = 1U << alarm_type; unsigned long flags; @@ -108,6 +110,8 @@ static void alarm_clear(enum android_alarm_type alarm_type) alarm_enabled &= ~alarm_type_mask; spin_unlock_irqrestore(&alarm_slock, flags); + if (alarm_type == ANDROID_ALARM_RTC_POWEROFF_WAKEUP) + set_power_on_alarm(ts->tv_sec, 0); } static void alarm_set(enum android_alarm_type alarm_type, @@ -122,6 +126,9 @@ static void alarm_set(enum android_alarm_type alarm_type, alarm_enabled |= alarm_type_mask; devalarm_start(&alarms[alarm_type], timespec_to_ktime(*ts)); spin_unlock_irqrestore(&alarm_slock, flags); + + if (alarm_type == ANDROID_ALARM_RTC_POWEROFF_WAKEUP) + set_power_on_alarm(ts->tv_sec, 1); } static int alarm_wait(void) @@ -181,6 +188,7 @@ static int alarm_get_time(enum android_alarm_type alarm_type, switch (alarm_type) { case ANDROID_ALARM_RTC_WAKEUP: case ANDROID_ALARM_RTC: + case ANDROID_ALARM_RTC_POWEROFF_WAKEUP: getnstimeofday(ts); break; case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP: @@ -224,7 +232,7 @@ static long alarm_do_ioctl(struct file *file, unsigned int cmd, switch (ANDROID_ALARM_BASE_CMD(cmd)) { case ANDROID_ALARM_CLEAR(0): - alarm_clear(alarm_type); + alarm_clear(alarm_type, ts); break; case ANDROID_ALARM_SET(0): alarm_set(alarm_type, ts); @@ -258,6 +266,7 @@ static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case ANDROID_ALARM_SET_AND_WAIT(0): case ANDROID_ALARM_SET(0): case ANDROID_ALARM_SET_RTC: + case ANDROID_ALARM_CLEAR(0): if (copy_from_user(&ts, (void __user *)arg, sizeof(ts))) return -EFAULT; break; @@ -421,6 +430,8 @@ static int __init alarm_dev_init(void) CLOCK_BOOTTIME, HRTIMER_MODE_ABS); hrtimer_init(&alarms[ANDROID_ALARM_SYSTEMTIME].u.hrt, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + alarm_init(&alarms[ANDROID_ALARM_RTC_POWEROFF_WAKEUP].u.alrm, + ALARM_REALTIME, devalarm_alarmhandler); for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) { alarms[i].type = i; diff --git a/drivers/staging/android/android_alarm.h b/drivers/staging/android/android_alarm.h index 4fd32f337f9c..f11ff2952465 100644 --- a/drivers/staging/android/android_alarm.h +++ b/drivers/staging/android/android_alarm.h @@ -26,6 +26,7 @@ enum android_alarm_type { ANDROID_ALARM_RTC, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP, ANDROID_ALARM_ELAPSED_REALTIME, + ANDROID_ALARM_RTC_POWEROFF_WAKEUP, ANDROID_ALARM_SYSTEMTIME, ANDROID_ALARM_TYPE_COUNT, @@ -41,6 +42,8 @@ enum android_alarm_return_flags { 1U << ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP, ANDROID_ALARM_ELAPSED_REALTIME_MASK = 1U << ANDROID_ALARM_ELAPSED_REALTIME, + ANDROID_ALARM_RTC_POWEROFF_WAKEUP_MASK = + 1U << ANDROID_ALARM_RTC_POWEROFF_WAKEUP, ANDROID_ALARM_SYSTEMTIME_MASK = 1U << ANDROID_ALARM_SYSTEMTIME, ANDROID_ALARM_TIME_CHANGE_MASK = 1U << 16 }; diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index e6e5b1cf527b..bfb131fcd279 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -69,7 +69,6 @@ config THERMAL_GOV_USER_SPACE config CPU_THERMAL bool "generic cpu cooling support" depends on CPU_FREQ - select CPU_FREQ_TABLE help This implements the generic cpu cooling mechanism through frequency reduction. An ACPI version of this already exists diff --git a/drivers/thermal/msm8974-tsens.c b/drivers/thermal/msm8974-tsens.c index 695803ccc3b8..a3d0f64d80fb 100644 --- a/drivers/thermal/msm8974-tsens.c +++ b/drivers/thermal/msm8974-tsens.c @@ -1803,19 +1803,19 @@ static int tsens_calib_sensors(void) } static struct of_device_id tsens_match[] = { - { .compatible = "qti,msm-tsens", + { .compatible = "qcom,msm-tsens", .data = (void *)TSENS_CALIB_FUSE_MAP_8974, }, - { .compatible = "qti,msm8x26-tsens", + { .compatible = "qcom,msm8x26-tsens", .data = (void *)TSENS_CALIB_FUSE_MAP_8X26, }, - { .compatible = "qti,msm8x10-tsens", + { .compatible = "qcom,msm8x10-tsens", .data = (void *)TSENS_CALIB_FUSE_MAP_8X10, }, - { .compatible = "qti,fsm9900-tsens", + { .compatible = "qcom,fsm9900-tsens", .data = (void *)TSENS_CALIB_FUSE_MAP_9900, }, - { .compatible = "qti,msmkrypton-tsens", + { .compatible = "qcom,msmkrypton-tsens", .data = (void *)TSENS_CALIB_FUSE_MAP_KRYPTON, }, {} @@ -1830,7 +1830,7 @@ static int get_device_tree_data(struct platform_device *pdev) const struct of_device_id *id; rc = of_property_read_u32(of_node, - "qti,sensors", &tsens_num_sensors); + "qcom,sensors", &tsens_num_sensors); if (rc) { dev_err(&pdev->dev, "missing sensor number\n"); return -ENODEV; @@ -1844,7 +1844,7 @@ static int get_device_tree_data(struct platform_device *pdev) } rc = of_property_read_u32_array(of_node, - "qti,slope", tsens_slope_data, tsens_num_sensors); + "qcom,slope", tsens_slope_data, tsens_num_sensors); if (rc) { dev_err(&pdev->dev, "invalid or missing property: tsens-slope\n"); return rc; @@ -1871,9 +1871,9 @@ static int get_device_tree_data(struct platform_device *pdev) tmdev->tsens_factor = TSENS_SLOPE_FACTOR; tmdev->tsens_num_sensor = tsens_num_sensors; tmdev->calibration_less_mode = of_property_read_bool(of_node, - "qti,calibration-less-mode"); + "qcom,calibration-less-mode"); tmdev->tsens_local_init = of_property_read_bool(of_node, - "qti,tsens-local-init"); + "qcom,tsens-local-init"); tmdev->calib_mode = (u32) id->data; sensor_id = devm_kzalloc(&pdev->dev, @@ -1884,7 +1884,7 @@ static int get_device_tree_data(struct platform_device *pdev) } rc = of_property_read_u32_array(of_node, - "qti,sensor-id", sensor_id, tsens_num_sensors); + "qcom,sensor-id", sensor_id, tsens_num_sensors); if (rc) { pr_debug("Default sensor id mapping\n"); for (i = 0; i < tsens_num_sensors; i++) { @@ -1899,7 +1899,7 @@ static int get_device_tree_data(struct platform_device *pdev) } } - if (!strcmp(id->compatible, "qti,msmkrypton-tsens")) + if (!strcmp(id->compatible, "qcom,msmkrypton-tsens")) tmdev->tsens_type = TSENS_TYPE2; else tmdev->tsens_type = TSENS_TYPE0; diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c index 1ca54dc62d7e..510ad88303ff 100644 --- a/drivers/thermal/msm_thermal.c +++ b/drivers/thermal/msm_thermal.c @@ -32,7 +32,7 @@ #include <linux/types.h> #include <linux/thermal.h> #include <mach/rpm-regulator.h> -#include <mach/rpm-regulator-smd.h> +#include <linux/regulator/rpm-smd-regulator.h> #include <linux/regulator/consumer.h> #include <linux/msm_thermal_ioctl.h> @@ -1163,7 +1163,8 @@ static __ref int do_freq_mitigation(void *data) cpus[cpu].limited_min_freq = min_freq_req; update_cpu_freq(cpu); reset_threshold: - if (cpus[cpu].freq_thresh_clear) { + if (freq_mitigation_enabled && + cpus[cpu].freq_thresh_clear) { set_threshold(cpus[cpu].sensor_id, &cpus[cpu].threshold[FREQ_THRESHOLD_HIGH]); @@ -1225,8 +1226,10 @@ static void freq_mitigation_init(void) uint32_t cpu = 0; struct sensor_threshold *hi_thresh = NULL, *low_thresh = NULL; - if (!freq_mitigation_enabled || freq_mitigation_task) + if (freq_mitigation_task) return; + if (!freq_mitigation_enabled) + goto init_freq_thread; for_each_possible_cpu(cpu) { if (!(msm_thermal_info.freq_mitig_control_mask & BIT(cpu))) @@ -1245,7 +1248,7 @@ static void freq_mitigation_init(void) set_threshold(cpus[cpu].sensor_id, hi_thresh); } - +init_freq_thread: init_completion(&freq_mitigation_complete); freq_mitigation_task = kthread_run(do_freq_mitigation, NULL, "msm_thermal:freq_mitig"); diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index dc04b81918ec..44874f8755e1 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -1277,7 +1277,7 @@ unsigned int msm_hs_tx_empty(struct uart_port *uport) struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); msm_hs_clock_vote(msm_uport); - data = msm_hs_read(uport, UARTDM_SR_ADDR); + data = msm_hs_read(uport, UART_DM_SR); msm_hs_clock_unvote(msm_uport); if (data & UARTDM_SR_TXEMT_BMSK) @@ -2255,7 +2255,7 @@ void msm_hs_request_clock_off(struct uart_port *uport) { msm_uport->clk_state = MSM_HS_CLK_REQUEST_OFF; msm_uport->clk_req_off_state = CLK_REQ_OFF_START; msm_uport->imr_reg |= UARTDM_ISR_TXLEV_BMSK; - msm_hs_write(uport, UARTDM_IMR, msm_uport->imr_reg); + msm_hs_write(uport, UART_DM_IMR, msm_uport->imr_reg); /* * Complete device write before retuning back. * Hence mb() requires here. diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig index bb8b73682a70..4e8a4d9fc331 100644 --- a/drivers/usb/class/Kconfig +++ b/drivers/usb/class/Kconfig @@ -46,3 +46,15 @@ config USB_TMC To compile this driver as a module, choose M here: the module will be called usbtmc. + +config USB_CCID_BRIDGE + tristate "USB Smart Card Class (CCID) support" + help + Say Y here if you want to connect a USB Smart Card device that + follows the USB.org specification for Integrated Circuit(s) Cards + Interface Devices to your computer's USB port. This module + provides a character device interface to exchange the messages. + Ioctls facilitate control transfers and interrupt transfers. + + To compile this driver as a module, choose M here: the + module will be called ccid_bridge. diff --git a/drivers/usb/class/Makefile b/drivers/usb/class/Makefile index 32e85277b5cf..c2ee6f3bc0cf 100644 --- a/drivers/usb/class/Makefile +++ b/drivers/usb/class/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_USB_ACM) += cdc-acm.o obj-$(CONFIG_USB_PRINTER) += usblp.o obj-$(CONFIG_USB_WDM) += cdc-wdm.o obj-$(CONFIG_USB_TMC) += usbtmc.o +obj-$(CONFIG_USB_CCID_BRIDGE) += ccid_bridge.o diff --git a/drivers/usb/class/ccid_bridge.c b/drivers/usb/class/ccid_bridge.c new file mode 100644 index 000000000000..a3e100ab3fee --- /dev/null +++ b/drivers/usb/class/ccid_bridge.c @@ -0,0 +1,885 @@ +/* Copyright (c) 2013, The Linux Foundation. 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 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. + */ + +#define pr_fmt(fmt) "%s: " fmt "\n", __func__ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/uaccess.h> +#include <linux/usb.h> +#include <linux/wait.h> +#include <linux/cdev.h> + +#include <linux/usb/ccid_bridge.h> + +#define CCID_CLASS_DECRIPTOR_TYPE 0x21 +#define CCID_NOTIFY_SLOT_CHANGE 0x50 +#define CCID_NOTIFY_HARDWARE_ERROR 0x51 +#define CCID_ABORT_REQ 0x1 +#define CCID_GET_CLK_FREQ_REQ 0x2 +#define CCID_GET_DATA_RATES 0x3 + +#define CCID_BRIDGE_MSG_SZ 512 +#define CCID_BRIDGE_OPEN_TIMEOUT 500 /* msec */ +#define CCID_CONTROL_TIMEOUT 500 /* msec */ +#define CCID_BRIDGE_MSG_TIMEOUT 500 /* msec */ + +struct ccid_bridge { + struct usb_device *udev; + struct usb_interface *intf; + unsigned int in_pipe; + unsigned int out_pipe; + unsigned int int_pipe; + struct urb *inturb; + struct urb *readurb; + struct urb *writeurb; + + bool opened; + bool events_supported; + bool is_suspended; + struct mutex open_mutex; + struct mutex write_mutex; + struct mutex read_mutex; + struct mutex event_mutex; + int write_result; + int read_result; + int event_result; + wait_queue_head_t open_wq; + wait_queue_head_t write_wq; + wait_queue_head_t read_wq; + wait_queue_head_t event_wq; + struct usb_ccid_event cur_event; + void *intbuf; + + dev_t chrdev; + struct cdev cdev; + struct class *class; + struct device *device; +}; + +static struct ccid_bridge *__ccid_bridge_dev; + +static void ccid_bridge_out_cb(struct urb *urb) +{ + struct ccid_bridge *ccid = urb->context; + + if (urb->dev->state == USB_STATE_NOTATTACHED) + ccid->write_result = -ENODEV; + else + ccid->write_result = urb->status ? : urb->actual_length; + + pr_debug("write result = %d", ccid->write_result); + wake_up(&ccid->write_wq); +} + +static void ccid_bridge_in_cb(struct urb *urb) +{ + struct ccid_bridge *ccid = urb->context; + + if (urb->dev->state == USB_STATE_NOTATTACHED) + ccid->read_result = -ENODEV; + else + ccid->read_result = urb->status ? : urb->actual_length; + + pr_debug("read result = %d", ccid->read_result); + wake_up(&ccid->read_wq); +} + +static void ccid_bridge_int_cb(struct urb *urb) +{ + struct ccid_bridge *ccid = urb->context; + u8 *msg_type; + bool wakeup = true; + + if (urb->dev->state == USB_STATE_NOTATTACHED || (urb->status && + urb->status != -ENOENT)) { + ccid->event_result = -ENODEV; + wakeup = true; + goto out; + } + + /* + * Don't wakeup the event ioctl process during suspend. + * The suspend state is not visible to user space. + * we wake up the process after resume to send RESUME + * event if the device supports remote wakeup. + */ + if (urb->status == -ENOENT && !urb->actual_length) { + ccid->event_result = -ENOENT; + wakeup = false; + goto out; + } + + ccid->event_result = 0; + msg_type = urb->transfer_buffer; + switch (*msg_type) { + case CCID_NOTIFY_SLOT_CHANGE: + pr_debug("NOTIFY_SLOT_CHANGE event arrived"); + ccid->cur_event.event = USB_CCID_NOTIFY_SLOT_CHANGE_EVENT; + ccid->cur_event.u.notify.slot_icc_state = *(++msg_type); + break; + case CCID_NOTIFY_HARDWARE_ERROR: + pr_debug("NOTIFY_HARDWARE_ERROR event arrived"); + ccid->cur_event.event = USB_CCID_HARDWARE_ERROR_EVENT; + ccid->cur_event.u.error.slot = *(++msg_type); + ccid->cur_event.u.error.seq = *(++msg_type); + ccid->cur_event.u.error.error_code = *(++msg_type); + break; + default: + pr_err("UNKNOWN event arrived\n"); + ccid->event_result = -EINVAL; + } + +out: + pr_debug("returning %d", ccid->event_result); + if (wakeup) + wake_up(&ccid->event_wq); +} + +static int ccid_bridge_submit_inturb(struct ccid_bridge *ccid) +{ + int ret = 0; + + /* + * Don't resume the bus to submit an interrupt URB. + * We submit the URB in resume path. This is important. + * Because the device will be in suspend state during + * multiple system suspend/resume cycles. The user space + * process comes here during system resume after it is + * unfrozen. + */ + if (!ccid->int_pipe || ccid->is_suspended) + goto out; + + ret = usb_autopm_get_interface(ccid->intf); + if (ret < 0) { + pr_debug("fail to get autopm with %d\n", ret); + goto out; + } + ret = usb_submit_urb(ccid->inturb, GFP_KERNEL); + if (ret < 0) + pr_err("fail to submit int urb with %d\n", ret); + usb_autopm_put_interface(ccid->intf); + +out: + pr_debug("returning %d", ret); + return ret; +} + +static int ccid_bridge_get_event(struct ccid_bridge *ccid) +{ + int ret = 0; + + /* + * The first event returned after the device resume + * will be RESUME event. This event is set by + * the resume. + */ + if (ccid->cur_event.event) + goto out; + + ccid->event_result = -EINPROGRESS; + + ret = ccid_bridge_submit_inturb(ccid); + if (ret < 0) + goto out; + + /* + * Wait for the notification on interrupt endpoint + * or remote wakeup event from the resume. The + * int urb completion handler and resume callback + * take care of setting the current event. + */ + mutex_unlock(&ccid->event_mutex); + ret = wait_event_interruptible(ccid->event_wq, + (ccid->event_result != -EINPROGRESS)); + mutex_lock(&ccid->event_mutex); + + if (ret == -ERESTARTSYS) /* interrupted */ + usb_kill_urb(ccid->inturb); + else + ret = ccid->event_result; +out: + pr_debug("returning %d", ret); + return ret; +} + +static int ccid_bridge_open(struct inode *ip, struct file *fp) +{ + struct ccid_bridge *ccid = container_of(ip->i_cdev, + struct ccid_bridge, cdev); + int ret; + + pr_debug("called"); + + mutex_lock(&ccid->open_mutex); + if (ccid->opened) { + ret = -EBUSY; + goto out; + } + mutex_unlock(&ccid->open_mutex); + + ret = wait_event_interruptible_timeout(ccid->open_wq, + ccid->intf != NULL, msecs_to_jiffies( + CCID_BRIDGE_OPEN_TIMEOUT)); + + mutex_lock(&ccid->open_mutex); + + if (ret != -ERESTARTSYS && ccid->intf) { + fp->private_data = ccid; + ccid->opened = true; + ret = 0; + } else if (!ret) { /* timed out */ + ret = -ENODEV; + } +out: + mutex_unlock(&ccid->open_mutex); + pr_debug("returning %d", ret); + return ret; +} + +static ssize_t ccid_bridge_write(struct file *fp, const char __user *ubuf, + size_t count, loff_t *pos) +{ + struct ccid_bridge *ccid = fp->private_data; + int ret; + char *kbuf; + + pr_debug("called with %d", count); + + if (!ccid->intf) { + pr_debug("intf is not active"); + return -ENODEV; + } + + mutex_lock(&ccid->write_mutex); + + if (!count || count > CCID_BRIDGE_MSG_SZ) { + pr_err("invalid count"); + ret = -EINVAL; + goto out; + } + + kbuf = kmalloc(count, GFP_KERNEL); + if (!kbuf) { + pr_err("fail to allocate memory"); + ret = -ENOMEM; + goto out; + } + + ret = copy_from_user(kbuf, ubuf, count); + if (ret) { + pr_err("fail to copy user buf"); + ret = -EFAULT; + goto free_kbuf; + } + + ret = usb_autopm_get_interface(ccid->intf); + if (ret) { + pr_err("fail to get autopm with %d", ret); + goto free_kbuf; + } + + ccid->write_result = 0; + + usb_fill_bulk_urb(ccid->writeurb, ccid->udev, ccid->out_pipe, + kbuf, count, ccid_bridge_out_cb, ccid); + ret = usb_submit_urb(ccid->writeurb, GFP_KERNEL); + if (ret < 0) { + pr_err("urb submit fail with %d", ret); + goto put_pm; + } + + ret = wait_event_interruptible_timeout(ccid->write_wq, + ccid->write_result != 0, + msecs_to_jiffies(CCID_BRIDGE_MSG_TIMEOUT)); + if (!ret || ret == -ERESTARTSYS) { /* timedout or interrupted */ + usb_kill_urb(ccid->writeurb); + if (!ret) + ret = -ETIMEDOUT; + } else { + ret = ccid->write_result; + } + + pr_debug("returning %d", ret); + +put_pm: + if (ret != -ENODEV) + usb_autopm_put_interface(ccid->intf); +free_kbuf: + kfree(kbuf); +out: + mutex_unlock(&ccid->write_mutex); + return ret; + +} + +static ssize_t ccid_bridge_read(struct file *fp, char __user *ubuf, + size_t count, loff_t *pos) +{ + struct ccid_bridge *ccid = fp->private_data; + int ret; + char *kbuf; + + pr_debug("called with %d", count); + if (!ccid->intf) { + pr_debug("intf is not active"); + return -ENODEV; + } + + mutex_lock(&ccid->read_mutex); + + if (!count || count > CCID_BRIDGE_MSG_SZ) { + pr_err("invalid count"); + ret = -EINVAL; + goto out; + } + + kbuf = kmalloc(count, GFP_KERNEL); + if (!kbuf) { + pr_err("fail to allocate memory"); + ret = -ENOMEM; + goto out; + } + + ret = usb_autopm_get_interface(ccid->intf); + if (ret) { + pr_err("fail to get autopm with %d", ret); + goto free_kbuf; + } + + ccid->read_result = 0; + + usb_fill_bulk_urb(ccid->readurb, ccid->udev, ccid->in_pipe, + kbuf, count, ccid_bridge_in_cb, ccid); + ret = usb_submit_urb(ccid->readurb, GFP_KERNEL); + if (ret < 0) { + pr_err("urb submit fail with %d", ret); + if (ret != -ENODEV) + usb_autopm_put_interface(ccid->intf); + goto free_kbuf; + } + + + ret = wait_event_interruptible_timeout(ccid->read_wq, + ccid->read_result != 0, + msecs_to_jiffies(CCID_BRIDGE_MSG_TIMEOUT)); + if (!ret || ret == -ERESTARTSYS) { /* timedout or interrupted */ + usb_kill_urb(ccid->readurb); + if (!ret) + ret = -ETIMEDOUT; + } else { + ret = ccid->read_result; + } + + + if (ret > 0) { + if (copy_to_user(ubuf, kbuf, ret)) + ret = -EFAULT; + } + + usb_autopm_put_interface(ccid->intf); + pr_debug("returning %d", ret); + +free_kbuf: + kfree(kbuf); +out: + mutex_unlock(&ccid->read_mutex); + return ret; +} + +static long +ccid_bridge_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct ccid_bridge *ccid = fp->private_data; + char *buf; + struct usb_ccid_data data; + struct usb_ccid_abort abort; + struct usb_descriptor_header *header; + int ret; + struct usb_device *udev = ccid->udev; + __u8 intf = ccid->intf->cur_altsetting->desc.bInterfaceNumber; + __u8 breq = 0; + + if (!ccid->intf) { + pr_debug("intf is not active"); + return -ENODEV; + } + + mutex_lock(&ccid->event_mutex); + switch (cmd) { + case USB_CCID_GET_CLASS_DESC: + pr_debug("GET_CLASS_DESC ioctl called"); + ret = copy_from_user(&data, (void __user *)arg, sizeof(data)); + if (ret) { + ret = -EFAULT; + break; + } + ret = __usb_get_extra_descriptor(udev->rawdescriptors[0], + le16_to_cpu(udev->config[0].desc.wTotalLength), + CCID_CLASS_DECRIPTOR_TYPE, (void **) &buf); + if (ret) { + ret = -ENOENT; + break; + } + header = (struct usb_descriptor_header *) buf; + if (data.length != header->bLength) { + ret = -EINVAL; + break; + } + ret = copy_to_user((void __user *)data.data, buf, data.length); + if (ret) + ret = -EFAULT; + break; + case USB_CCID_GET_CLOCK_FREQUENCIES: + pr_debug("GET_CLOCK_FREQUENCIES ioctl called"); + breq = CCID_GET_CLK_FREQ_REQ; + /* fall through */ + case USB_CCID_GET_DATA_RATES: + if (!breq) { + pr_debug("GET_DATA_RATES ioctl called"); + breq = CCID_GET_DATA_RATES; + } + ret = copy_from_user(&data, (void __user *)arg, sizeof(data)); + if (ret) { + ret = -EFAULT; + break; + } + buf = kmalloc(data.length, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + break; + } + ret = usb_autopm_get_interface(ccid->intf); + if (ret < 0) { + pr_debug("fail to get autopm with %d", ret); + break; + } + ret = usb_control_msg(ccid->udev, + usb_rcvctrlpipe(ccid->udev, 0), + breq, (USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE), 0, intf, buf, + data.length, CCID_CONTROL_TIMEOUT); + usb_autopm_put_interface(ccid->intf); + if (ret == data.length) { + ret = copy_to_user((void __user *)data.data, buf, + data.length); + if (ret) + ret = -EFAULT; + } else { + if (ret > 0) + ret = -EPIPE; + } + kfree(buf); + break; + case USB_CCID_ABORT: + pr_debug("ABORT ioctl called"); + breq = CCID_ABORT_REQ; + ret = copy_from_user(&abort, (void __user *)arg, sizeof(abort)); + if (ret) { + ret = -EFAULT; + break; + } + ret = usb_autopm_get_interface(ccid->intf); + if (ret < 0) { + pr_debug("fail to get autopm with %d", ret); + break; + } + ret = usb_control_msg(ccid->udev, + usb_sndctrlpipe(ccid->udev, 0), + breq, (USB_DIR_OUT | USB_TYPE_CLASS | + USB_RECIP_INTERFACE), + (abort.seq << 8) | abort.slot, intf, NULL, + 0, CCID_CONTROL_TIMEOUT); + if (ret < 0) + pr_err("abort request failed with err %d\n", ret); + usb_autopm_put_interface(ccid->intf); + break; + case USB_CCID_GET_EVENT: + pr_debug("GET_EVENT ioctl called"); + if (!ccid->events_supported) { + ret = -ENOENT; + break; + } + ret = ccid_bridge_get_event(ccid); + if (ret == 0) { + ret = copy_to_user((void __user *)arg, &ccid->cur_event, + sizeof(ccid->cur_event)); + if (ret) + ret = -EFAULT; + } + ccid->cur_event.event = 0; + break; + default: + pr_err("UNKNOWN ioctl called"); + ret = -EINVAL; + break; + } + + mutex_unlock(&ccid->event_mutex); + pr_debug("returning %d", ret); + return ret; +} + +static int ccid_bridge_release(struct inode *ip, struct file *fp) +{ + struct ccid_bridge *ccid = fp->private_data; + + pr_debug("called"); + + usb_kill_urb(ccid->writeurb); + usb_kill_urb(ccid->readurb); + if (ccid->int_pipe) + usb_kill_urb(ccid->inturb); + + ccid->event_result = -EIO; + wake_up(&ccid->event_wq); + + mutex_lock(&ccid->open_mutex); + ccid->opened = false; + mutex_unlock(&ccid->open_mutex); + return 0; +} + +static const struct file_operations ccid_bridge_fops = { + .owner = THIS_MODULE, + .open = ccid_bridge_open, + .write = ccid_bridge_write, + .read = ccid_bridge_read, + .unlocked_ioctl = ccid_bridge_ioctl, + .release = ccid_bridge_release, +}; + +static int ccid_bridge_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct ccid_bridge *ccid = usb_get_intfdata(intf); + int ret = 0; + + pr_debug("called"); + + if (!ccid->opened) + goto out; + + mutex_lock(&ccid->event_mutex); + if (ccid->int_pipe) { + usb_kill_urb(ccid->inturb); + if (ccid->event_result != -ENOENT) { + ret = -EBUSY; + goto rel_mutex; + } + } + + ccid->is_suspended = true; +rel_mutex: + mutex_unlock(&ccid->event_mutex); +out: + pr_debug("returning %d", ret); + return ret; +} + +static int ccid_bridge_resume(struct usb_interface *intf) +{ + struct ccid_bridge *ccid = usb_get_intfdata(intf); + int ret; + + pr_debug("called"); + + if (!ccid->opened) + goto out; + + mutex_lock(&ccid->event_mutex); + + ccid->is_suspended = false; + + if (device_can_wakeup(&ccid->udev->dev)) { + ccid->event_result = 0; + ccid->cur_event.event = USB_CCID_RESUME_EVENT; + wake_up(&ccid->event_wq); + } else if (ccid->int_pipe) { + ccid->event_result = -EINPROGRESS; + ret = usb_submit_urb(ccid->inturb, GFP_KERNEL); + if (ret < 0) + pr_debug("fail to submit inturb with %d\n", ret); + } + + mutex_unlock(&ccid->event_mutex); +out: + return 0; +} + +static int +ccid_bridge_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct ccid_bridge *ccid = __ccid_bridge_dev; + struct usb_host_interface *intf_desc; + struct usb_endpoint_descriptor *ep_desc; + struct usb_host_endpoint *ep; + __u8 epin_addr = 0, epout_addr = 0, epint_addr = 0; + int i, ret; + + intf_desc = intf->cur_altsetting; + + if (intf_desc->desc.bNumEndpoints > 3) + return -ENODEV; + + for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { + ep_desc = &intf_desc->endpoint[i].desc; + + if (usb_endpoint_is_bulk_in(ep_desc)) + epin_addr = ep_desc->bEndpointAddress; + else if (usb_endpoint_is_bulk_out(ep_desc)) + epout_addr = ep_desc->bEndpointAddress; + else if (usb_endpoint_is_int_in(ep_desc)) + epint_addr = ep_desc->bEndpointAddress; + else + return -ENODEV; + } + + if (!epin_addr || !epout_addr) + return -ENODEV; + + ccid->udev = usb_get_dev(interface_to_usbdev(intf)); + ccid->in_pipe = usb_rcvbulkpipe(ccid->udev, epin_addr); + ccid->out_pipe = usb_sndbulkpipe(ccid->udev, epout_addr); + if (epint_addr) + ccid->int_pipe = usb_rcvbulkpipe(ccid->udev, epint_addr); + + ccid->writeurb = usb_alloc_urb(0, GFP_KERNEL); + if (!ccid->writeurb) { + pr_err("fail to allocate write urb"); + ret = -ENOMEM; + goto put_udev; + } + ccid->readurb = usb_alloc_urb(0, GFP_KERNEL); + if (!ccid->readurb) { + pr_err("fail to allocate read urb"); + ret = -ENOMEM; + goto free_writeurb; + } + + if (ccid->int_pipe) { + pr_debug("interrupt endpoint is present"); + ep = usb_pipe_endpoint(ccid->udev, ccid->int_pipe); + ccid->inturb = usb_alloc_urb(0, GFP_KERNEL); + if (!ccid->inturb) { + pr_err("fail to allocate int urb"); + ret = -ENOMEM; + goto free_readurb; + } + ccid->intbuf = kmalloc(usb_endpoint_maxp(&ep->desc), + GFP_KERNEL); + if (!ccid->intbuf) { + pr_err("fail to allocated int buf"); + ret = -ENOMEM; + goto free_inturb; + } + usb_fill_int_urb(ccid->inturb, ccid->udev, + usb_rcvintpipe(ccid->udev, epint_addr), + ccid->intbuf, usb_endpoint_maxp(&ep->desc), + ccid_bridge_int_cb, ccid, + ep->desc.bInterval); + } + + if (ccid->int_pipe || device_can_wakeup(&ccid->udev->dev)) { + pr_debug("event support is present"); + ccid->events_supported = true; + } + + usb_set_intfdata(intf, ccid); + + mutex_lock(&ccid->open_mutex); + ccid->intf = intf; + wake_up(&ccid->open_wq); + mutex_unlock(&ccid->open_mutex); + + pr_info("success"); + return 0; + +free_inturb: + if (ccid->int_pipe) + usb_free_urb(ccid->inturb); +free_readurb: + usb_free_urb(ccid->readurb); +free_writeurb: + usb_free_urb(ccid->writeurb); +put_udev: + usb_put_dev(ccid->udev); + return ret; +} + +static void ccid_bridge_disconnect(struct usb_interface *intf) +{ + struct ccid_bridge *ccid = usb_get_intfdata(intf); + + pr_debug("called"); + + usb_kill_urb(ccid->writeurb); + usb_kill_urb(ccid->readurb); + if (ccid->int_pipe) + usb_kill_urb(ccid->inturb); + + ccid->event_result = -ENODEV; + wake_up(&ccid->event_wq); + + /* + * This would synchronize any ongoing read/write/ioctl. + * After acquiring the mutex, we can safely set + * intf to NULL. + */ + mutex_lock(&ccid->open_mutex); + mutex_lock(&ccid->write_mutex); + mutex_lock(&ccid->read_mutex); + mutex_lock(&ccid->event_mutex); + + usb_free_urb(ccid->writeurb); + usb_free_urb(ccid->readurb); + if (ccid->int_pipe) { + usb_free_urb(ccid->inturb); + kfree(ccid->intbuf); + ccid->int_pipe = 0; + } + + ccid->intf = NULL; + + mutex_unlock(&ccid->event_mutex); + mutex_unlock(&ccid->read_mutex); + mutex_unlock(&ccid->write_mutex); + mutex_unlock(&ccid->open_mutex); + +} + +static const struct usb_device_id ccid_bridge_ids[] = { + { USB_INTERFACE_INFO(USB_CLASS_CSCID, 0, 0) }, + + {} /* terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, ccid_bridge_ids); + +static struct usb_driver ccid_bridge_driver = { + .name = "ccid_bridge", + .probe = ccid_bridge_probe, + .disconnect = ccid_bridge_disconnect, + .suspend = ccid_bridge_suspend, + .resume = ccid_bridge_resume, + .id_table = ccid_bridge_ids, + .supports_autosuspend = 1, +}; + +static int __init ccid_bridge_init(void) +{ + int ret; + struct ccid_bridge *ccid; + + ccid = kzalloc(sizeof(*ccid), GFP_KERNEL); + if (!ccid) { + pr_err("Fail to allocate ccid"); + ret = -ENOMEM; + goto out; + } + __ccid_bridge_dev = ccid; + + mutex_init(&ccid->open_mutex); + mutex_init(&ccid->write_mutex); + mutex_init(&ccid->read_mutex); + mutex_init(&ccid->event_mutex); + + init_waitqueue_head(&ccid->open_wq); + init_waitqueue_head(&ccid->write_wq); + init_waitqueue_head(&ccid->read_wq); + init_waitqueue_head(&ccid->event_wq); + + ret = usb_register(&ccid_bridge_driver); + if (ret < 0) { + pr_err("Fail to register ccid usb driver with %d", ret); + goto free_ccid; + } + + ret = alloc_chrdev_region(&ccid->chrdev, 0, 1, "ccid_bridge"); + if (ret < 0) { + pr_err("Fail to allocate ccid char dev region with %d", ret); + goto unreg_driver; + } + ccid->class = class_create(THIS_MODULE, "ccid_bridge"); + if (IS_ERR(ccid->class)) { + ret = PTR_ERR(ccid->class); + pr_err("Fail to create ccid class with %d", ret); + goto unreg_chrdev; + } + cdev_init(&ccid->cdev, &ccid_bridge_fops); + ccid->cdev.owner = THIS_MODULE; + + ret = cdev_add(&ccid->cdev, ccid->chrdev, 1); + if (ret < 0) { + pr_err("Fail to add ccid cdev with %d", ret); + goto destroy_class; + } + ccid->device = device_create(ccid->class, + NULL, ccid->chrdev, NULL, + "ccid_bridge"); + if (IS_ERR(ccid->device)) { + ret = PTR_ERR(ccid->device); + pr_err("Fail to create ccid device with %d", ret); + goto del_cdev; + } + + pr_info("success"); + + return 0; + +del_cdev: + cdev_del(&ccid->cdev); +destroy_class: + class_destroy(ccid->class); +unreg_chrdev: + unregister_chrdev_region(ccid->chrdev, 1); +unreg_driver: + usb_deregister(&ccid_bridge_driver); +free_ccid: + mutex_destroy(&ccid->open_mutex); + mutex_destroy(&ccid->write_mutex); + mutex_destroy(&ccid->read_mutex); + mutex_destroy(&ccid->event_mutex); + kfree(ccid); + __ccid_bridge_dev = NULL; +out: + return ret; +} + +static void __exit ccid_bridge_exit(void) +{ + struct ccid_bridge *ccid = __ccid_bridge_dev; + + pr_debug("called"); + device_destroy(ccid->class, ccid->chrdev); + cdev_del(&ccid->cdev); + class_destroy(ccid->class); + unregister_chrdev_region(ccid->chrdev, 1); + + usb_deregister(&ccid_bridge_driver); + + mutex_destroy(&ccid->open_mutex); + mutex_destroy(&ccid->write_mutex); + mutex_destroy(&ccid->read_mutex); + mutex_destroy(&ccid->event_mutex); + + kfree(ccid); + __ccid_bridge_dev = NULL; +} + +module_init(ccid_bridge_init); +module_exit(ccid_bridge_exit); + +MODULE_DESCRIPTION("USB CCID bridge driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 78e89d7b314a..1b13c6aabeef 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -648,6 +648,7 @@ struct dwc3_request { u8 epnum; struct dwc3_trb *trb; + struct dwc3_trb *ztrb; dma_addr_t trb_dma; unsigned direction:1; diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index aa6366a8831b..d2b5c86fd815 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -36,6 +36,7 @@ #include <linux/usb/msm_hsusb.h> #include <linux/usb/msm_ext_chg.h> #include <linux/regulator/consumer.h> +#include <linux/regulator/rpm-smd-regulator.h> #include <linux/pm_wakeup.h> #include <linux/power_supply.h> #include <linux/qpnp/qpnp-adc.h> @@ -44,7 +45,6 @@ #include <linux/clk/msm-clk.h> #include <mach/rpm-regulator.h> -#include <mach/rpm-regulator-smd.h> #include <mach/msm_bus.h> #include <mach/scm.h> @@ -2505,7 +2505,7 @@ static int dwc3_msm_probe(struct platform_device *pdev) ret = PTR_ERR(mdwc->ref_clk); goto disable_utmi_clk; } - ret = of_property_read_u32(node, "qti,ref-clk-rate", + ret = of_property_read_u32(node, "qcom,ref-clk-rate", (u32 *)&mdwc->ref_clk_rate); if (ret) mdwc->ref_clk_rate = 19200000; @@ -2514,15 +2514,15 @@ static int dwc3_msm_probe(struct platform_device *pdev) mdwc->id_state = mdwc->ext_xceiv.id = DWC3_ID_FLOAT; mdwc->ext_xceiv.otg_capability = of_property_read_bool(node, - "qti,otg-capability"); + "qcom,otg-capability"); mdwc->charger.charging_disabled = of_property_read_bool(node, - "qti,charging-disabled"); + "qcom,charging-disabled"); mdwc->charger.skip_chg_detect = of_property_read_bool(node, - "qti,skip-charger-detection"); + "qcom,skip-charger-detection"); mdwc->suspend_resume_no_support = of_property_read_bool(node, - "qti,no-suspend-resume"); + "qcom,no-suspend-resume"); /* * DWC3 has separate IRQ line for OTG events (ID/BSV) and for * DP and DM linestate transitions during low power mode. @@ -2623,7 +2623,7 @@ static int dwc3_msm_probe(struct platform_device *pdev) mdwc->io_res = res; /* used to calculate chg block offset */ - if (of_property_read_u32(node, "qti,dwc-usb3-msm-dbm-eps", + if (of_property_read_u32(node, "qcom,dwc-usb3-msm-dbm-eps", &mdwc->dbm_num_eps)) { dev_err(&pdev->dev, "unable to read platform data num of dbm eps\n"); @@ -2639,12 +2639,12 @@ static int dwc3_msm_probe(struct platform_device *pdev) goto disable_ref_clk; } - if (of_property_read_u32(node, "qti,dwc-usb3-msm-tx-fifo-size", + if (of_property_read_u32(node, "qcom,dwc-usb3-msm-tx-fifo-size", &mdwc->tx_fifo_size)) dev_err(&pdev->dev, "unable to read platform data tx fifo size\n"); - if (of_property_read_u32(node, "qti,dwc-usb3-msm-qdss-tx-fifo-size", + if (of_property_read_u32(node, "qcom,dwc-usb3-msm-qdss-tx-fifo-size", &mdwc->qdss_tx_fifo_size)) dev_err(&pdev->dev, "unable to read platform data qdss tx fifo size\n"); @@ -2677,15 +2677,6 @@ static int dwc3_msm_probe(struct platform_device *pdev) } } - if (node) { - ret = of_platform_populate(node, NULL, NULL, &pdev->dev); - if (ret) { - dev_err(&pdev->dev, - "failed to add create dwc3 core\n"); - goto put_psupply; - } - } - /* Assumes dwc3 is the only DT child of dwc3-msm */ dwc3_node = of_get_next_available_child(node, NULL); if (!dwc3_node) { @@ -2694,12 +2685,37 @@ static int dwc3_msm_probe(struct platform_device *pdev) } host_mode = of_property_read_bool(dwc3_node, "host-only-mode"); + if (host_mode && of_get_property(pdev->dev.of_node, "vbus_dwc3-supply", + NULL)) { + mdwc->vbus_otg = devm_regulator_get(&pdev->dev, "vbus_dwc3"); + if (IS_ERR(mdwc->vbus_otg)) { + dev_err(&pdev->dev, "Failed to get vbus regulator\n"); + ret = PTR_ERR(mdwc->vbus_otg); + of_node_put(dwc3_node); + goto put_psupply; + } + ret = regulator_enable(mdwc->vbus_otg); + if (ret) { + mdwc->vbus_otg = 0; + dev_err(&pdev->dev, "Failed to enable vbus_otg\n"); + of_node_put(dwc3_node); + goto put_psupply; + } + } + + ret = of_platform_populate(node, NULL, NULL, &pdev->dev); + if (ret) { + dev_err(&pdev->dev, + "failed to add create dwc3 core\n"); + of_node_put(dwc3_node); + goto disable_vbus; + } mdwc->dwc3 = of_find_device_by_node(dwc3_node); of_node_put(dwc3_node); if (!mdwc->dwc3) { dev_err(&pdev->dev, "failed to get dwc3 platform device\n"); - goto put_psupply; + goto put_dwc3; } mdwc->hs_phy = devm_usb_get_phy_by_phandle(&mdwc->dwc3->dev, @@ -2762,17 +2778,6 @@ static int dwc3_msm_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "No OTG, DWC3 running in host only mode\n"); mdwc->scope = POWER_SUPPLY_SCOPE_SYSTEM; mdwc->hs_phy->flags |= PHY_HOST_MODE; - mdwc->vbus_otg = devm_regulator_get(&pdev->dev, "vbus_dwc3"); - if (IS_ERR(mdwc->vbus_otg)) { - dev_dbg(&pdev->dev, "Failed to get vbus regulator\n"); - mdwc->vbus_otg = 0; - } else { - ret = regulator_enable(mdwc->vbus_otg); - if (ret) { - mdwc->vbus_otg = 0; - dev_err(&pdev->dev, "Failed to enable vbus_otg\n"); - } - } } else { dev_err(&pdev->dev, "DWC3 device-only mode not supported\n"); ret = -ENODEV; @@ -2785,7 +2790,7 @@ static int dwc3_msm_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Fail to setup dwc3 setup cdev\n"); } - ret = of_property_read_u32(node, "qti,restore-sec-cfg-for-scm-dev-id", + ret = of_property_read_u32(node, "qcom,restore-sec-cfg-for-scm-dev-id", &mdwc->scm_dev_id); if (ret && ret != -ENODATA) dev_dbg(&pdev->dev, "unable to read scm device id\n"); @@ -2797,7 +2802,7 @@ static int dwc3_msm_probe(struct platform_device *pdev) pm_runtime_set_active(mdwc->dev); pm_runtime_enable(mdwc->dev); - if (of_property_read_bool(node, "qti,reset_hsphy_sleep_clk_on_init")) { + if (of_property_read_bool(node, "qcom,reset_hsphy_sleep_clk_on_init")) { ret = clk_reset(mdwc->hsphy_sleep_clk, CLK_RESET_ASSERT); if (ret) { dev_err(&pdev->dev, @@ -2815,10 +2820,15 @@ static int dwc3_msm_probe(struct platform_device *pdev) } } + msm_bam_set_usb_dev(mdwc->dev); + return 0; put_dwc3: platform_device_put(mdwc->dwc3); +disable_vbus: + if (!IS_ERR_OR_NULL(mdwc->vbus_otg)) + regulator_disable(mdwc->vbus_otg); put_psupply: if (mdwc->usb_psy.dev) power_supply_unregister(&mdwc->usb_psy); @@ -2865,7 +2875,7 @@ static int dwc3_msm_remove(struct platform_device *pdev) dwc3_start_chg_det(&mdwc->charger, false); if (mdwc->usb_psy.dev) power_supply_unregister(&mdwc->usb_psy); - if (mdwc->vbus_otg) + if (!IS_ERR_OR_NULL(mdwc->vbus_otg)) regulator_disable(mdwc->vbus_otg); pm_runtime_disable(mdwc->dev); @@ -2987,7 +2997,7 @@ static const struct dev_pm_ops dwc3_msm_dev_pm_ops = { static const struct of_device_id of_dwc3_matach[] = { { - .compatible = "qti,dwc-usb3-msm", + .compatible = "qcom,dwc-usb3-msm", }, { }, }; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4adb44b429b3..ebaf9d162c17 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -294,6 +294,15 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, dep->busy_slot++; } while(++i < req->request.num_mapped_sgs); req->queued = false; + + if (req->request.zero && req->ztrb) { + dep->busy_slot++; + req->ztrb = NULL; + if (((dep->busy_slot & DWC3_TRB_MASK) == + DWC3_TRB_NUM - 1) && + usb_endpoint_xfer_isoc(dep->endpoint.desc)) + dep->busy_slot++; + } } list_del(&req->list); req->trb = NULL; @@ -863,6 +872,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, dep->free_slot++; +update_trb: trb->size = DWC3_TRB_SIZE_LENGTH(length); trb->bpl = lower_32_bits(dma); trb->bph = upper_32_bits(dma); @@ -897,8 +907,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI; trb->ctrl |= DWC3_TRB_CTRL_CSP; - } else if (last) { - trb->ctrl |= DWC3_TRB_CTRL_LST; } if (chain) @@ -908,6 +916,25 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id); trb->ctrl |= DWC3_TRB_CTRL_HWO; + + if (req->request.zero && length && + (length % usb_endpoint_maxp(dep->endpoint.desc) == 0)) { + /* Skip the LINK-TRB on ISOC */ + if (((dep->free_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) && + usb_endpoint_xfer_isoc(dep->endpoint.desc)) + dep->free_slot++; + + trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK]; + dep->free_slot++; + + req->ztrb = trb; + length = 0; + + goto update_trb; + } + + if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) && last) + trb->ctrl |= DWC3_TRB_CTRL_LST; } /* @@ -1011,12 +1038,25 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting) } dbg_queue(dep->number, &req->request, 0); } else { + struct dwc3_request *req1; + int maxpkt_size = usb_endpoint_maxp(dep->endpoint.desc); + dma = req->request.dma; length = req->request.length; trbs_left--; - if (!trbs_left) + if (req->request.zero && length && + (length % maxpkt_size == 0)) + trbs_left--; + + if (!trbs_left) { last_one = 1; + } else if (dep->direction && (trbs_left <= 1)) { + req1 = next_request(&req->list); + if (req1->request.zero && req1->request.length + && (req1->request.length % maxpkt_size == 0)) + last_one = 1; + } /* Is this the last request? */ if (list_is_last(&req->list, &dep->request_list)) @@ -2201,6 +2241,17 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, break; }while (++i < req->request.num_mapped_sgs); + if (req->ztrb) { + trb = req->ztrb; + if ((event->status & DEPEVT_STATUS_LST) && + (trb->ctrl & (DWC3_TRB_CTRL_LST | + DWC3_TRB_CTRL_HWO))) + ret = 1; + + if ((event->status & DEPEVT_STATUS_IOC) && + (trb->ctrl & DWC3_TRB_CTRL_IOC)) + ret = 1; + } dwc3_gadget_giveback(dep, req, status); if (ret) diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index 5a0987daa451..f8d9ab973f95 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -444,12 +444,23 @@ static void ffs_function_enable(struct android_usb_function *f) { struct android_dev *dev = f->android_dev; struct functionfs_config *config = f->config; + int ret = 0; config->enabled = true; /* Disable the gadget until the function is ready */ - if (!config->opened) + if (!config->opened) { android_disable(dev); + } else { + /* + * Call functionfs_bind to handle the case where userspace + * passed descriptors before updating enabled functions list + */ + ret = functionfs_bind(config->data, dev->cdev); + if (ret) + pr_err("%s: functionfs_bind failed (%d)\n", __func__, + ret); + } } static void ffs_function_disable(struct android_usb_function *f) @@ -3106,6 +3117,7 @@ android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) struct android_configuration *conf; int value = -EOPNOTSUPP; unsigned long flags; + bool do_work = false; req->zero = 0; req->length = 0; @@ -3135,13 +3147,14 @@ android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) spin_lock_irqsave(&cdev->lock, flags); if (!dev->connected) { dev->connected = 1; - schedule_work(&dev->work); + do_work = true; } else if (c->bRequest == USB_REQ_SET_CONFIGURATION && cdev->config) { - schedule_work(&dev->work); + do_work = true; } spin_unlock_irqrestore(&cdev->lock, flags); - + if (do_work) + schedule_work(&dev->work); return value; } diff --git a/drivers/usb/gadget/f_diag.c b/drivers/usb/gadget/f_diag.c index c36cbb788c57..d3c2a9f89afa 100644 --- a/drivers/usb/gadget/f_diag.c +++ b/drivers/usb/gadget/f_diag.c @@ -354,28 +354,6 @@ static void free_reqs(struct diag_context *ctxt) } /** - * usb_diag_free_req() - Free USB requests - * @ch: Channel handler - * - * This function free read and write USB requests for the interface - * associated with this channel. - * - */ -void usb_diag_free_req(struct usb_diag_ch *ch) -{ - struct diag_context *ctxt = ch->priv_usb; - unsigned long flags; - - if (ctxt) { - spin_lock_irqsave(&ctxt->lock, flags); - free_reqs(ctxt); - spin_unlock_irqrestore(&ctxt->lock, flags); - } - -} -EXPORT_SYMBOL(usb_diag_free_req); - -/** * usb_diag_alloc_req() - Allocate USB requests * @ch: Channel handler * @n_write: Number of requests for Tx diff --git a/drivers/usb/gadget/f_mbim.c b/drivers/usb/gadget/f_mbim.c index 4219bfa6d5ce..a93e392beb93 100644 --- a/drivers/usb/gadget/f_mbim.c +++ b/drivers/usb/gadget/f_mbim.c @@ -696,9 +696,9 @@ static int mbim_bam_connect(struct f_mbim *dev) pr_info("dev:%p portno:%d\n", dev, dev->port_num); src_connection_idx = usb_bam_get_connection_idx(gadget->name, bam_name, - USB_TO_PEER_PERIPHERAL, dev->port_num); + USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE, dev->port_num); dst_connection_idx = usb_bam_get_connection_idx(gadget->name, bam_name, - PEER_PERIPHERAL_TO_USB, dev->port_num); + PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, dev->port_num); if (src_connection_idx < 0 || dst_connection_idx < 0) { pr_err("%s: usb_bam_get_connection_idx failed\n", __func__); return ret; @@ -1695,8 +1695,10 @@ mbim_read(struct file *fp, char __user *buf, size_t count, loff_t *pos) return -EIO; } + spin_lock(&dev->lock); while (list_empty(&dev->cpkt_req_q)) { pr_debug("Requests list is empty. Wait.\n"); + spin_unlock(&dev->lock); ret = wait_event_interruptible(dev->read_wq, !list_empty(&dev->cpkt_req_q)); if (ret < 0) { @@ -1705,11 +1707,13 @@ mbim_read(struct file *fp, char __user *buf, size_t count, loff_t *pos) return -ERESTARTSYS; } pr_debug("Received request packet\n"); + spin_lock(&dev->lock); } cpkt = list_first_entry(&dev->cpkt_req_q, struct ctrl_pkt, list); if (cpkt->len > count) { + spin_unlock(&dev->lock); mbim_unlock(&dev->read_excl); pr_err("cpkt size too big:%d > buf size:%d\n", cpkt->len, count); @@ -1719,6 +1723,7 @@ mbim_read(struct file *fp, char __user *buf, size_t count, loff_t *pos) pr_debug("cpkt size:%d\n", cpkt->len); list_del(&cpkt->list); + spin_unlock(&dev->lock); mbim_unlock(&dev->read_excl); ret = copy_to_user(buf, cpkt->buf, cpkt->len); diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index 8008cf35c43d..75968f54db14 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -899,7 +899,10 @@ static void receive_file_work(struct work_struct *data) dev->rx_done || dev->state != STATE_BUSY); if (dev->state == STATE_CANCELED || dev->state == STATE_OFFLINE) { - r = -ECANCELED; + if (dev->state == STATE_OFFLINE) + r = -EIO; + else + r = -ECANCELED; if (!dev->rx_done) usb_ep_dequeue(dev->ep_out, read_req); break; diff --git a/drivers/usb/gadget/f_qc_ecm.c b/drivers/usb/gadget/f_qc_ecm.c index 4169a2ada57e..bd6a48b15a7a 100644 --- a/drivers/usb/gadget/f_qc_ecm.c +++ b/drivers/usb/gadget/f_qc_ecm.c @@ -395,9 +395,9 @@ static int ecm_qc_bam_connect(struct f_ecm_qc *dev) /* currently we use the first connection */ src_connection_idx = usb_bam_get_connection_idx(gadget->name, peer_bam, - USB_TO_PEER_PERIPHERAL, 0); + USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE, 0); dst_connection_idx = usb_bam_get_connection_idx(gadget->name, peer_bam, - PEER_PERIPHERAL_TO_USB, 0); + PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, 0); if (src_connection_idx < 0 || dst_connection_idx < 0) { pr_err("usb_bam_get_connection_idx failed\n"); return ret; @@ -662,6 +662,12 @@ static void ecm_qc_disable(struct usb_function *f) gether_qc_disconnect_name(&ecm->port, "ecm0"); } + if (ecm->xport == USB_GADGET_XPORT_BAM2BAM_IPA && + gadget_is_dwc3(cdev->gadget)) { + msm_ep_unconfig(ecm->port.out_ep); + msm_ep_unconfig(ecm->port.in_ep); + } + if (ecm->notify->driver_data) { usb_ep_disable(ecm->notify); ecm->notify->driver_data = NULL; diff --git a/drivers/usb/gadget/f_qc_rndis.c b/drivers/usb/gadget/f_qc_rndis.c index 15a6619a0a18..72aa1a686d43 100644 --- a/drivers/usb/gadget/f_qc_rndis.c +++ b/drivers/usb/gadget/f_qc_rndis.c @@ -440,9 +440,9 @@ static int rndis_qc_bam_connect(struct f_rndis_qc *dev) /* currently we use the first connection */ src_connection_idx = usb_bam_get_connection_idx(gadget->name, peer_bam, - USB_TO_PEER_PERIPHERAL, 0); + USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE, 0); dst_connection_idx = usb_bam_get_connection_idx(gadget->name, peer_bam, - PEER_PERIPHERAL_TO_USB, 0); + PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, 0); if (src_connection_idx < 0 || dst_connection_idx < 0) { pr_err("%s: usb_bam_get_connection_idx failed\n", __func__); return ret; @@ -781,6 +781,7 @@ fail: static void rndis_qc_disable(struct usb_function *f) { struct f_rndis_qc *rndis = func_to_rndis_qc(f); + struct usb_composite_dev *cdev = f->config->cdev; if (!rndis->notify->driver_data) return; @@ -794,6 +795,11 @@ static void rndis_qc_disable(struct usb_function *f) else rndis_ipa_supported = false; + if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA && + gadget_is_dwc3(cdev->gadget)) { + msm_ep_unconfig(rndis->port.out_ep); + msm_ep_unconfig(rndis->port.in_ep); + } usb_ep_disable(rndis->notify); rndis->notify->driver_data = NULL; } diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c index 5eabe21a79b4..ef551b7bc714 100644 --- a/drivers/usb/gadget/f_rmnet.c +++ b/drivers/usb/gadget/f_rmnet.c @@ -388,7 +388,7 @@ static int rmnet_gport_setup(void) return 0; } -static int gport_rmnet_connect(struct f_rmnet *dev) +static int gport_rmnet_connect(struct f_rmnet *dev, unsigned intf) { int ret; unsigned port_num; @@ -413,7 +413,7 @@ static int gport_rmnet_connect(struct f_rmnet *dev) } break; case USB_GADGET_XPORT_QTI: - ret = gqti_ctrl_connect(&dev->port, port_num); + ret = gqti_ctrl_connect(&dev->port, port_num, intf); if (ret) { pr_err("%s: gqti_ctrl_connect failed: err:%d\n", __func__, ret); @@ -449,9 +449,11 @@ static int gport_rmnet_connect(struct f_rmnet *dev) switch (dxport) { case USB_GADGET_XPORT_BAM2BAM: src_connection_idx = usb_bam_get_connection_idx(gadget->name, - A2_P_BAM, USB_TO_PEER_PERIPHERAL, port_num); + A2_P_BAM, USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE, + port_num); dst_connection_idx = usb_bam_get_connection_idx(gadget->name, - A2_P_BAM, PEER_PERIPHERAL_TO_USB, port_num); + A2_P_BAM, PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, + port_num); if (dst_connection_idx < 0 || src_connection_idx < 0) { pr_err("%s: usb_bam_get_connection_idx failed\n", __func__); @@ -470,9 +472,11 @@ static int gport_rmnet_connect(struct f_rmnet *dev) break; case USB_GADGET_XPORT_BAM2BAM_IPA: src_connection_idx = usb_bam_get_connection_idx(gadget->name, - IPA_P_BAM, USB_TO_PEER_PERIPHERAL, port_num); + IPA_P_BAM, USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE, + port_num); dst_connection_idx = usb_bam_get_connection_idx(gadget->name, - IPA_P_BAM, PEER_PERIPHERAL_TO_USB, port_num); + IPA_P_BAM, PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, + port_num); if (dst_connection_idx < 0 || src_connection_idx < 0) { pr_err("%s: usb_bam_get_connection_idx failed\n", __func__); @@ -696,6 +700,8 @@ static void frmnet_resume(struct usb_function *f) static void frmnet_disable(struct usb_function *f) { struct f_rmnet *dev = func_to_rmnet(f); + enum transport_type dxport = rmnet_ports[dev->port_num].data_xport; + struct usb_composite_dev *cdev = dev->cdev; pr_debug("%s: port#%d\n", __func__, dev->port_num); @@ -706,6 +712,11 @@ static void frmnet_disable(struct usb_function *f) frmnet_purge_responses(dev); + if (dxport == USB_GADGET_XPORT_BAM2BAM_IPA && + gadget_is_dwc3(cdev->gadget)) { + msm_ep_unconfig(dev->port.out); + msm_ep_unconfig(dev->port.in); + } gport_rmnet_disconnect(dev); } @@ -748,7 +759,7 @@ frmnet_set_alt(struct usb_function *f, unsigned intf, unsigned alt) dev->port.out->desc = NULL; return -EINVAL; } - ret = gport_rmnet_connect(dev); + ret = gport_rmnet_connect(dev, intf); } if (dxport == USB_GADGET_XPORT_BAM2BAM_IPA && diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c index fa589331e0f9..68cafd520cb3 100644 --- a/drivers/usb/gadget/u_bam.c +++ b/drivers/usb/gadget/u_bam.c @@ -25,6 +25,7 @@ #include <mach/usb_gadget_xport.h> #include <linux/usb/msm_hsusb.h> +#include <linux/usb/rmnet_ctrl_qti.h> #include <mach/usb_bam.h> #include "u_rmnet.h" @@ -862,7 +863,8 @@ static void gbam2bam_connect_work(struct work_struct *w) struct u_bam_data_connect_info bam_info; idx = usb_bam_get_connection_idx(gadget->name, - IPA_P_BAM, USB_TO_PEER_PERIPHERAL, 0); + IPA_P_BAM, USB_TO_PEER_PERIPHERAL, + USB_BAM_DEVICE, 0); if (idx < 0) { pr_err("%s: get_connection_idx failed\n", __func__); @@ -894,7 +896,8 @@ static void gbam2bam_connect_work(struct work_struct *w) struct u_bam_data_connect_info bam_info; idx = usb_bam_get_connection_idx(gadget->name, - IPA_P_BAM, PEER_PERIPHERAL_TO_USB, 0); + IPA_P_BAM, PEER_PERIPHERAL_TO_USB, + USB_BAM_DEVICE, 0); if (idx < 0) { pr_err("%s: get_connection_idx failed\n", __func__); @@ -912,6 +915,10 @@ static void gbam2bam_connect_work(struct work_struct *w) bam_info.usb_bam_pipe_idx); } + gqti_ctrl_update_ipa_pipes(port->port_usb, port->port_num, + d->ipa_params.ipa_prod_ep_idx , + d->ipa_params.ipa_cons_ep_idx); + connect_params.ipa_usb_pipe_hdl = d->ipa_params.prod_clnt_hdl; connect_params.usb_ipa_pipe_hdl = d->ipa_params.cons_clnt_hdl; connect_params.tethering_mode = TETH_TETHERING_MODE_RMNET; diff --git a/drivers/usb/gadget/u_bam_data.c b/drivers/usb/gadget/u_bam_data.c index 45bf145f9a9c..ce4a167b50a2 100644 --- a/drivers/usb/gadget/u_bam_data.c +++ b/drivers/usb/gadget/u_bam_data.c @@ -271,7 +271,8 @@ static void bam2bam_data_connect_work(struct work_struct *w) struct usb_bam_data_connect_info bam_info; idx = usb_bam_get_connection_idx(gadget->name, - IPA_P_BAM, USB_TO_PEER_PERIPHERAL, 0); + IPA_P_BAM, USB_TO_PEER_PERIPHERAL, + USB_BAM_DEVICE, 0); if (idx < 0) { pr_err("%s: get_connection_idx failed\n", __func__); @@ -310,7 +311,8 @@ static void bam2bam_data_connect_work(struct work_struct *w) struct usb_bam_data_connect_info bam_info; idx = usb_bam_get_connection_idx(gadget->name, - IPA_P_BAM, PEER_PERIPHERAL_TO_USB, 0); + IPA_P_BAM, PEER_PERIPHERAL_TO_USB, + USB_BAM_DEVICE, 0); if (idx < 0) { pr_err("%s: get_connection_idx failed\n", __func__); diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 65d4aea4fe66..67095d97eab2 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -866,9 +866,10 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, req->length = length; - /* throttle highspeed IRQ rate back slightly */ + /* throttle high/super speed IRQ rate back slightly */ if (gadget_is_dualspeed(dev->gadget) && - (dev->gadget->speed == USB_SPEED_HIGH)) { + (dev->gadget->speed == USB_SPEED_HIGH || + dev->gadget->speed == USB_SPEED_SUPER)) { dev->tx_qlen++; if (dev->tx_qlen == (qmult/2)) { req->no_interrupt = 0; diff --git a/drivers/usb/gadget/u_qdss.c b/drivers/usb/gadget/u_qdss.c index c6952d59c466..dea25ce8a78e 100644 --- a/drivers/usb/gadget/u_qdss.c +++ b/drivers/usb/gadget/u_qdss.c @@ -69,7 +69,7 @@ static int set_qdss_data_connection(struct usb_gadget *gadget, /* There is only one qdss pipe, so the pipe number can be set to 0 */ idx = usb_bam_get_connection_idx(gadget->name, QDSS_P_BAM, - PEER_PERIPHERAL_TO_USB, 0); + PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, 0); if (idx < 0) { pr_err("%s: usb_bam_get_connection_idx failed\n", __func__); return idx; diff --git a/drivers/usb/gadget/u_rmnet.h b/drivers/usb/gadget/u_rmnet.h index 549bba30da2b..86fa4e5761bc 100644 --- a/drivers/usb/gadget/u_rmnet.h +++ b/drivers/usb/gadget/u_rmnet.h @@ -69,7 +69,9 @@ int gsmd_ctrl_connect(struct grmnet *gr, int port_num); void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num); int gsmd_ctrl_setup(enum ctrl_client client_num, unsigned int count, u8 *first_port_idx); -int gqti_ctrl_connect(struct grmnet *gr, u8 port_num); +int gqti_ctrl_connect(struct grmnet *gr, u8 port_num, unsigned intf); void gqti_ctrl_disconnect(struct grmnet *gr, u8 port_num); +void gqti_ctrl_update_ipa_pipes(struct grmnet *gr, u8 port_num, + u32 ipa_prod, u32 ipa_cons); #endif /* __U_RMNET_H*/ diff --git a/drivers/usb/gadget/u_rmnet_ctrl_qti.c b/drivers/usb/gadget/u_rmnet_ctrl_qti.c index 7eb49722f5e1..f1ea710fee34 100644 --- a/drivers/usb/gadget/u_rmnet_ctrl_qti.c +++ b/drivers/usb/gadget/u_rmnet_ctrl_qti.c @@ -23,6 +23,9 @@ struct rmnet_ctrl_qti_port { bool is_open; int index; + unsigned intf; + int ipa_prod_idx; + int ipa_cons_idx; atomic_t connected; atomic_t line_state; @@ -152,7 +155,7 @@ gqti_ctrl_notify_modem(void *gptr, u8 portno, int val) rmnet_ctrl_queue_notify(port); } -int gqti_ctrl_connect(struct grmnet *gr, u8 port_num) +int gqti_ctrl_connect(struct grmnet *gr, u8 port_num, unsigned intf) { struct rmnet_ctrl_qti_port *port; unsigned long flags; @@ -177,6 +180,7 @@ int gqti_ctrl_connect(struct grmnet *gr, u8 port_num) spin_lock_irqsave(&port->lock, flags); port->port_usb = gr; + port->intf = intf; gr->send_encap_cmd = grmnet_ctrl_qti_send_cpkt_tomodem; gr->notify_modem = gqti_ctrl_notify_modem; spin_unlock_irqrestore(&port->lock, flags); @@ -233,6 +237,24 @@ void gqti_ctrl_disconnect(struct grmnet *gr, u8 port_num) rmnet_ctrl_queue_notify(port); } +void gqti_ctrl_update_ipa_pipes(struct grmnet *gr, u8 port_num, u32 ipa_prod, + u32 ipa_cons) +{ + struct rmnet_ctrl_qti_port *port; + + if (port_num >= NR_QTI_PORTS) { + pr_err("%s: Invalid QTI port %d\n", __func__, port_num); + return; + } + + port = ctrl_port[port_num]; + + port->ipa_prod_idx = ipa_prod; + port->ipa_cons_idx = ipa_cons; + +} + + static int rmnet_ctrl_open(struct inode *ip, struct file *fp) { unsigned long flags; @@ -423,6 +445,7 @@ rmnet_ctrl_write(struct file *fp, const char __user *buf, size_t count, static long rmnet_ctrl_ioctl(struct file *fp, unsigned cmd, unsigned long arg) { struct rmnet_ctrl_qti_port *port = fp->private_data; + struct ep_info info; int val, ret = 0; pr_debug("%s: Received command %d", __func__, cmd); @@ -441,6 +464,33 @@ static long rmnet_ctrl_ioctl(struct file *fp, unsigned cmd, unsigned long arg) pr_debug("%s: Sent line_state: %d", __func__, atomic_read(&port->line_state)); break; + case FRMNET_CTRL_EP_LOOKUP: + val = atomic_read(&port->connected); + if (!val) { + pr_err("EP_LOOKUP failed - not connected"); + ret = -EAGAIN; + break; + } + + if (port->ipa_prod_idx == -1 || port->ipa_cons_idx == -1) { + pr_err("EP_LOOKUP failed - ipa pipes were not updated"); + ret = -EAGAIN; + break; + + } + + info.ph_ep_info.ep_type = DATA_EP_TYPE_HSUSB; + info.ph_ep_info.peripheral_iface_id = port->intf; + info.ipa_ep_pair.cons_pipe_num = port->ipa_cons_idx; + info.ipa_ep_pair.prod_pipe_num = port->ipa_prod_idx; + + ret = copy_to_user((void __user *)arg, &info, + sizeof(info)); + if (ret) { + pr_err("copying to user space failed"); + ret = -EFAULT; + } + break; default: pr_err("wrong parameter"); ret = -EINVAL; @@ -527,6 +577,8 @@ static int __init gqti_ctrl_init(void) ctrl_port[i] = port; port->index = i; + port->ipa_prod_idx = -1; + port->ipa_cons_idx = -1; ret = misc_register(&rmnet_device[i]); if (ret) { diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index 837bedbffd98..f6c1e411a3c5 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -705,18 +705,20 @@ static void gs_free_requests(struct usb_ep *ep, struct list_head *head, } static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head, - int num, int size, void (*fn)(struct usb_ep *, struct usb_request *), + int queue_size, int req_size, + void (*fn)(struct usb_ep *, struct usb_request *), int *allocated) { int i; struct usb_request *req; + int n = allocated ? queue_size - *allocated : queue_size; /* Pre-allocate up to QUEUE_SIZE transfers, but if we can't * do quite that many this time, don't fail ... we just won't * be as speedy as we might otherwise be. */ - for (i = 0; i < num; i++) { - req = gs_alloc_req(ep, size, GFP_ATOMIC); + for (i = 0; i < n; i++) { + req = gs_alloc_req(ep, req_size, GFP_ATOMIC); if (!req) return list_empty(head) ? -ENOMEM : 0; req->complete = fn; @@ -963,22 +965,6 @@ static void gs_close(struct tty_struct *tty, struct file *file) port->port_num, tty, file); wake_up(&port->port.close_wait); - - /* - * Freeing the previously queued requests as they are - * allocated again as a part of gs_open() - */ - if (port->port_usb) { - spin_unlock_irq(&port->port_lock); - usb_ep_fifo_flush(gser->out); - usb_ep_fifo_flush(gser->in); - spin_lock_irq(&port->port_lock); - gs_free_requests(gser->out, &port->read_queue, NULL); - gs_free_requests(gser->out, &port->read_pool, NULL); - gs_free_requests(gser->in, &port->write_pool, NULL); - } - port->read_allocated = port->read_started = - port->write_allocated = port->write_started = 0; exit: spin_unlock_irq(&port->port_lock); } diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index a88a34ee0f60..8e0d8f887c5d 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -881,19 +881,14 @@ static int ehci_hub_control ( ) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); int ports = HCS_N_PORTS (ehci->hcs_params); - u32 __iomem *status_reg; - u32 __iomem *hostpc_reg; + u32 __iomem *status_reg = &ehci->regs->port_status[ + (wIndex & 0xff) - 1]; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[(wIndex & 0xff) - 1]; u32 temp, temp1, status; unsigned long flags; int retval = 0; unsigned selector; - if ((wIndex & 0xff) == 0x0) - return -EINVAL; - - status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1]; - hostpc_reg = &ehci->regs->hostpc[(wIndex & 0xff) - 1]; - /* * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. * HCS_INDICATOR may say we can change LEDs to off/amber/green. diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c index dab70e89586f..269fc80f70f7 100644 --- a/drivers/usb/host/ehci-msm-hsic.c +++ b/drivers/usb/host/ehci-msm-hsic.c @@ -891,7 +891,7 @@ static int msm_hsic_resume(struct msm_hsic_hcd *mehci) if (pdata->consider_ipa_handshake) { dev_dbg(mehci->dev, "%s:Wait for producer resource\n", __func__); - msm_bam_wait_for_hsic_prod_granted(); + msm_bam_wait_for_hsic_host_prod_granted(); dev_dbg(mehci->dev, "%s:Producer resource obtained\n", __func__); } @@ -986,7 +986,7 @@ skip_phy_resume: if (pdata->consider_ipa_handshake) { dev_dbg(mehci->dev, "%s:Notify usb bam on resume complete\n", __func__); - msm_bam_hsic_notify_on_resume(); + msm_bam_hsic_host_notify_on_resume(); } return 0; @@ -1523,7 +1523,7 @@ static int msm_hsic_init_clocks(struct msm_hsic_hcd *mehci, u32 init) /*core_clk is required for LINK protocol engine *clock rate appropriately set by target specific clock driver */ - mehci->core_clk = clk_get(mehci->dev, "core_clk"); + mehci->core_clk = devm_clk_get(mehci->dev, "core_clk"); if (IS_ERR(mehci->core_clk)) { ret = PTR_ERR(mehci->core_clk); mehci->core_clk = NULL; @@ -1532,48 +1532,35 @@ static int msm_hsic_init_clocks(struct msm_hsic_hcd *mehci, u32 init) return ret; } - /* alt_core_clk is for LINK to be used during PHY RESET in - * targets on which link does NOT use asynchronous reset methodology. - * clock rate appropriately set by target specific clock driver */ - mehci->alt_core_clk = clk_get(mehci->dev, "alt_core_clk"); - if (IS_ERR(mehci->alt_core_clk)) { - ret = PTR_ERR(mehci->alt_core_clk); - mehci->alt_core_clk = NULL; - if (ret != -EPROBE_DEFER) - dev_dbg(mehci->dev, "failed to get alt_core_clk\n"); - else - goto put_core_clk; - } - /* phy_clk is required for HSIC PHY operation * clock rate appropriately set by target specific clock driver */ - mehci->phy_clk = clk_get(mehci->dev, "phy_clk"); + mehci->phy_clk = devm_clk_get(mehci->dev, "phy_clk"); if (IS_ERR(mehci->phy_clk)) { ret = PTR_ERR(mehci->phy_clk); mehci->phy_clk = NULL; if (ret != -EPROBE_DEFER) dev_err(mehci->dev, "failed to get phy_clk\n"); - goto put_alt_core_clk; + return ret; } /* 10MHz cal_clk is required for calibration of I/O pads */ - mehci->cal_clk = clk_get(mehci->dev, "cal_clk"); + mehci->cal_clk = devm_clk_get(mehci->dev, "cal_clk"); if (IS_ERR(mehci->cal_clk)) { ret = PTR_ERR(mehci->cal_clk); mehci->cal_clk = NULL; if (ret != -EPROBE_DEFER) dev_err(mehci->dev, "failed to get cal_clk\n"); - goto put_phy_clk; + return ret; } /* ahb_clk is required for data transfers */ - mehci->ahb_clk = clk_get(mehci->dev, "iface_clk"); + mehci->ahb_clk = devm_clk_get(mehci->dev, "iface_clk"); if (IS_ERR(mehci->ahb_clk)) { ret = PTR_ERR(mehci->ahb_clk); mehci->ahb_clk = NULL; if (ret != -EPROBE_DEFER) dev_err(mehci->dev, "failed to get iface_clk\n"); - goto put_cal_clk; + return ret; } /* @@ -1581,10 +1568,19 @@ static int msm_hsic_init_clocks(struct msm_hsic_hcd *mehci, u32 init) * This clock is not compulsory and is defined in clock lookup * only for targets that need to use the inactivity timer feature. */ - mehci->inactivity_clk = clk_get(mehci->dev, "inactivity_clk"); + mehci->inactivity_clk = devm_clk_get(mehci->dev, "inactivity_clk"); if (IS_ERR(mehci->inactivity_clk)) dev_dbg(mehci->dev, "failed to get inactivity_clk\n"); + /* + * alt_core_clk is for LINK to be used during PHY RESET in + * targets on which link does NOT use asynchronous reset methodology. + * clock rate appropriately set by target specific clock driver + */ + mehci->alt_core_clk = devm_clk_get(mehci->dev, "alt_core_clk"); + if (IS_ERR(mehci->alt_core_clk)) + dev_dbg(mehci->dev, "failed to get alt_core_clk\n"); + clk_prepare_enable(mehci->core_clk); clk_prepare_enable(mehci->phy_clk); clk_prepare_enable(mehci->cal_clk); @@ -1603,21 +1599,10 @@ put_clocks: if (!IS_ERR(mehci->inactivity_clk)) clk_disable_unprepare(mehci->inactivity_clk); } - if (!IS_ERR(mehci->inactivity_clk)) - clk_put(mehci->inactivity_clk); - clk_put(mehci->ahb_clk); -put_cal_clk: - clk_put(mehci->cal_clk); -put_phy_clk: - clk_put(mehci->phy_clk); -put_alt_core_clk: - if (mehci->alt_core_clk) - clk_put(mehci->alt_core_clk); -put_core_clk: - clk_put(mehci->core_clk); - return ret; + return 0; } + static irqreturn_t hsic_peripheral_status_change(int irq, void *dev_id) { struct msm_hsic_hcd *mehci = dev_id; diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 655cf342400a..d63b5ba0d80a 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -150,7 +150,7 @@ static int ehci_msm_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 1); pm_runtime_enable(&pdev->dev); - /* FIXME: need to call usb_add_hcd() here? */ + msm_bam_set_usb_host_dev(&pdev->dev); return 0; diff --git a/drivers/usb/host/ehci-msm2.c b/drivers/usb/host/ehci-msm2.c index a875b04d6b98..c61de84b28f3 100644 --- a/drivers/usb/host/ehci-msm2.c +++ b/drivers/usb/host/ehci-msm2.c @@ -172,11 +172,11 @@ static int msm_ehci_init_vddcx(struct msm_hcd *mhcd, int init) if (mhcd->dev->of_node) { of_get_property(mhcd->dev->of_node, - "qti,vdd-voltage-level", + "qcom,vdd-voltage-level", &len); if (len == sizeof(tmp)) { of_property_read_u32_array(mhcd->dev->of_node, - "qti,vdd-voltage-level", + "qcom,vdd-voltage-level", tmp, len/sizeof(*tmp)); hsusb_vdd_val[mhcd->vdd_type][VDD_MIN_NONE] = tmp[0]; hsusb_vdd_val[mhcd->vdd_type][VDD_MIN_P50] = tmp[1]; @@ -1005,6 +1005,10 @@ static int msm_ehci_reset(struct usb_hcd *hcd) writel_relaxed(readl_relaxed(USB_PHY_CTRL2) | (1<<16), USB_PHY_CTRL2); + /* Disable ULPI_TX_PKT_EN_CLR_FIX which is valid only for HSIC */ + writel_relaxed(readl_relaxed(USB_GENCONFIG2) & ~(1<<19), + USB_GENCONFIG2); + return 0; } @@ -1311,15 +1315,15 @@ struct msm_usb_host_platform_data *ehci_msm2_dt_to_pdata( } pdata->use_sec_phy = of_property_read_bool(node, - "qti,usb2-enable-hsphy2"); - of_property_read_u32(node, "qti,usb2-power-budget", + "qcom,usb2-enable-hsphy2"); + of_property_read_u32(node, "qcom,usb2-power-budget", &pdata->power_budget); pdata->no_selective_suspend = of_property_read_bool(node, - "qti,no-selective-suspend"); - pdata->resume_gpio = of_get_named_gpio(node, "qti,resume-gpio", 0); + "qcom,no-selective-suspend"); + pdata->resume_gpio = of_get_named_gpio(node, "qcom,resume-gpio", 0); pdata->ext_hub_reset_gpio = of_get_named_gpio(node, - "qti,ext-hub-reset-gpio", 0); + "qcom,ext-hub-reset-gpio", 0); return pdata; } @@ -1746,7 +1750,7 @@ static const struct dev_pm_ops ehci_msm2_dev_pm_ops = { #endif static const struct of_device_id ehci_msm2_dt_match[] = { - { .compatible = "qti,ehci-host", + { .compatible = "qcom,ehci-host", }, {} }; diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index ae18ad64aa01..ecf0a4c87569 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -683,7 +683,7 @@ qh_urb_transaction ( if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) token ^= QTD_TOGGLE; - if (likely(sg && (this_sg_len <= 0))) { + if (likely(this_sg_len <= 0)) { if (--i <= 0 || len <= 0) break; sg = sg_next(sg); diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index 5b9f55eb3d00..2f8c5362a77e 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -613,11 +613,11 @@ static char *get_timestamp(char *tbuf) return tbuf; } -static int check_log_mask(struct dbg_data *d, int ep_addr) +static int check_log_mask(struct dbg_data *d, int ep_addr, struct urb *urb) { int dir, num; - dir = ep_addr & USB_DIR_IN ? USB_DIR_IN : USB_DIR_OUT; + dir = usb_urb_dir_in(urb) ? USB_DIR_IN : USB_DIR_OUT; num = ep_addr & ~USB_DIR_IN; num = 1 << num; @@ -629,9 +629,9 @@ static int check_log_mask(struct dbg_data *d, int ep_addr) return 0; } -static char *get_hex_data(char *dbuf, struct urb *urb, int event, int status) +static char * +get_hex_data(char *dbuf, struct urb *urb, int event, int status, size_t max_len) { - int ep_addr = urb->ep->desc.bEndpointAddress; char *ubuf = urb->transfer_buffer; size_t len = event ? urb->actual_length : urb->transfer_buffer_length; @@ -640,11 +640,10 @@ static char *get_hex_data(char *dbuf, struct urb *urb, int event, int status) status = 0; /*Only dump ep in completions and epout submissions*/ - if (len && !status && - (((ep_addr & USB_DIR_IN) && event) || - (!(ep_addr & USB_DIR_IN) && !event))) { - if (len >= 32) - len = 32; + if (len && !status && ((usb_urb_dir_in(urb) && event) || + (usb_urb_dir_in(urb) && !event))) { + if (len >= max_len) + len = max_len; hex_dump_to_buffer(ubuf, len, 32, 4, dbuf, HEX_DUMP_LEN, 0); } else { dbuf = ""; @@ -675,7 +674,7 @@ xhci_dbg_log_event(struct dbg_data *d, struct urb *urb, char *event, } ep_addr = urb->ep->desc.bEndpointAddress; - if (!check_log_mask(d, ep_addr)) + if (!check_log_mask(d, ep_addr, urb)) return; if ((ep_addr & 0x0f) == 0x0) { @@ -684,9 +683,9 @@ xhci_dbg_log_event(struct dbg_data *d, struct urb *urb, char *event, write_lock_irqsave(&d->ctrl_lck, flags); scnprintf(d->ctrl_buf[d->ctrl_idx], DBG_MSG_LEN, "%s: [%s : %p]:[%s] " - "%02x %02x %04x %04x %04x %u %d", + "%02x %02x %04x %04x %04x %u %d %s", get_timestamp(tbuf), event, urb, - (ep_addr & USB_DIR_IN) ? "in" : "out", + usb_urb_dir_in(urb) ? "in" : "out", urb->setup_packet[0], urb->setup_packet[1], (urb->setup_packet[3] << 8) | urb->setup_packet[2], @@ -694,17 +693,21 @@ xhci_dbg_log_event(struct dbg_data *d, struct urb *urb, char *event, urb->setup_packet[4], (urb->setup_packet[7] << 8) | urb->setup_packet[6], - urb->transfer_buffer_length, extra); + urb->transfer_buffer_length, extra, + d->log_payload ? get_hex_data(dbuf, urb, + xhci_str_to_event(event), extra, 16) : ""); dbg_inc(&d->ctrl_idx); write_unlock_irqrestore(&d->ctrl_lck, flags); } else { write_lock_irqsave(&d->ctrl_lck, flags); scnprintf(d->ctrl_buf[d->ctrl_idx], - DBG_MSG_LEN, "%s: [%s : %p]:[%s] %u %d", + DBG_MSG_LEN, "%s: [%s : %p]:[%s] %u %d %s", get_timestamp(tbuf), event, urb, - (ep_addr & USB_DIR_IN) ? "in" : "out", - urb->actual_length, extra); + usb_urb_dir_in(urb) ? "in" : "out", + urb->actual_length, extra, + d->log_payload ? get_hex_data(dbuf, urb, + xhci_str_to_event(event), extra, 16) : ""); dbg_inc(&d->ctrl_idx); write_unlock_irqrestore(&d->ctrl_lck, flags); @@ -714,11 +717,11 @@ xhci_dbg_log_event(struct dbg_data *d, struct urb *urb, char *event, scnprintf(d->data_buf[d->data_idx], DBG_MSG_LEN, "%s: [%s : %p]:ep%d[%s] %u %d %s", get_timestamp(tbuf), event, urb, ep_addr & 0x0f, - (ep_addr & USB_DIR_IN) ? "in" : "out", + usb_urb_dir_in(urb) ? "in" : "out", xhci_str_to_event(event) ? urb->actual_length : urb->transfer_buffer_length, extra, d->log_payload ? get_hex_data(dbuf, urb, - xhci_str_to_event(event), extra) : ""); + xhci_str_to_event(event), extra, 32) : ""); dbg_inc(&d->data_idx); write_unlock_irqrestore(&d->data_lck, flags); diff --git a/drivers/usb/host/xhci-msm-hsic.c b/drivers/usb/host/xhci-msm-hsic.c index 07f12f0abdbb..ef6e88b6f8f5 100644 --- a/drivers/usb/host/xhci-msm-hsic.c +++ b/drivers/usb/host/xhci-msm-hsic.c @@ -554,7 +554,9 @@ static int mxhci_hsic_bus_suspend(struct usb_hcd *hcd) /* don't miss connect bus state from peripheral for USB 2.0 root hub */ if (usb_hcd_is_primary_hcd(hcd) && !(readl_relaxed(MSM_HSIC_PORTSC) & PORT_PE)) { - dev_err(mxhci->dev, "%s: port is not enabled; skip suspend\n", + xhci_dbg_log_event(&dbg_hsic, NULL, + "port is not enabled; skip suspend", 0); + dev_dbg(mxhci->dev, "%s: port is not enabled; skip suspend\n", __func__); return -EAGAIN; } @@ -599,6 +601,10 @@ static int mxhci_hsic_suspend(struct mxhci_hsic_hcd *mxhci) init_completion(&mxhci->phy_in_lpm); + /* Don't poll the roothubs after bus suspend. */ + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); + del_timer_sync(&hcd->rh_timer); + clk_disable_unprepare(mxhci->core_clk); clk_disable_unprepare(mxhci->utmi_clk); clk_disable_unprepare(mxhci->hsic_clk); @@ -684,6 +690,10 @@ static int mxhci_hsic_resume(struct mxhci_hsic_hcd *mxhci) clk_prepare_enable(mxhci->utmi_clk); clk_prepare_enable(mxhci->core_clk); + /* Re-enable port polling. */ + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); + usb_hcd_poll_rh_status(hcd); + if (mxhci->wakeup_irq) usb_hcd_resume_root_hub(hcd); @@ -943,7 +953,7 @@ static int mxhci_hsic_probe(struct platform_device *pdev) reg |= CTRLREG_PLL_CTRL_SLEEP | CTRLREG_PLL_CTRL_SUSP; writel_relaxed(reg, MSM_HSIC_CTRL_REG); - if (of_property_read_bool(node, "qti,disable-hw-clk-gating")) { + if (of_property_read_bool(node, "qcom,disable-hw-clk-gating")) { reg = readl_relaxed(MSM_HSIC_GCTL); writel_relaxed((reg | GCTL_DSBLCLKGTNG), MSM_HSIC_GCTL); } diff --git a/drivers/usb/phy/phy-msm-hsusb.c b/drivers/usb/phy/phy-msm-hsusb.c index 0359d8894e43..52e8fe3f60e9 100644 --- a/drivers/usb/phy/phy-msm-hsusb.c +++ b/drivers/usb/phy/phy-msm-hsusb.c @@ -519,21 +519,21 @@ static int msm_hsphy_probe(struct platform_device *pdev) phy->tcsr); } - if (of_get_property(dev->of_node, "qti,primary-phy", NULL)) { + if (of_get_property(dev->of_node, "qcom,primary-phy", NULL)) { dev_dbg(dev, "secondary HSPHY\n"); phy->phy.flags |= ENABLE_SECONDARY_PHY; } - ret = of_property_read_u32_array(dev->of_node, "qti,vdd-voltage-level", + ret = of_property_read_u32_array(dev->of_node, "qcom,vdd-voltage-level", (u32 *) phy->vdd_levels, ARRAY_SIZE(phy->vdd_levels)); if (ret) { - dev_err(dev, "error reading qti,vdd-voltage-level property\n"); + dev_err(dev, "error reading qcom,vdd-voltage-level property\n"); goto err_ret; } phy->ext_vbus_id = of_property_read_bool(dev->of_node, - "qti,ext-vbus-id"); + "qcom,ext-vbus-id"); phy->phy.dev = dev; phy->vdd = devm_regulator_get(dev, "vdd"); @@ -575,18 +575,18 @@ static int msm_hsphy_probe(struct platform_device *pdev) goto disable_hs_vdd; } - if (of_property_read_u32(dev->of_node, "qti,hsphy-init", + if (of_property_read_u32(dev->of_node, "qcom,hsphy-init", &phy->hsphy_init_seq)) dev_dbg(dev, "unable to read hsphy init seq\n"); else if (!phy->hsphy_init_seq) dev_warn(dev, "hsphy init seq cannot be 0. Using POR value\n"); phy->set_pllbtune = of_property_read_bool(dev->of_node, - "qti,set-pllbtune"); + "qcom,set-pllbtune"); platform_set_drvdata(pdev, phy); - if (of_property_read_bool(dev->of_node, "qti,vbus-valid-override")) + if (of_property_read_bool(dev->of_node, "qcom,vbus-valid-override")) phy->phy.flags |= PHY_VBUS_VALID_OVERRIDE; phy->phy.init = msm_hsphy_init; @@ -630,7 +630,7 @@ static int msm_hsphy_remove(struct platform_device *pdev) static const struct of_device_id msm_usb_id_table[] = { { - .compatible = "qti,usb-hsphy", + .compatible = "qcom,usb-hsphy", }, { }, }; diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c index b80481566e4f..30cc23385dfe 100644 --- a/drivers/usb/phy/phy-msm-ssusb-qmp.c +++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c @@ -392,11 +392,11 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev) return -ENODEV; } - ret = of_property_read_u32_array(dev->of_node, "qti,vdd-voltage-level", + ret = of_property_read_u32_array(dev->of_node, "qcom,vdd-voltage-level", (u32 *) phy->vdd_levels, ARRAY_SIZE(phy->vdd_levels)); if (ret) { - dev_err(dev, "error reading qti,vdd-voltage-level property\n"); + dev_err(dev, "error reading qcom,vdd-voltage-level property\n"); return ret; } @@ -432,7 +432,7 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev) platform_set_drvdata(pdev, phy); - if (of_property_read_bool(dev->of_node, "qti,vbus-valid-override")) + if (of_property_read_bool(dev->of_node, "qcom,vbus-valid-override")) phy->phy.flags |= PHY_VBUS_VALID_OVERRIDE; phy->phy.dev = dev; @@ -485,7 +485,7 @@ static int msm_ssphy_qmp_remove(struct platform_device *pdev) static const struct of_device_id msm_usb_id_table[] = { { - .compatible = "qti,usb-ssphy-qmp", + .compatible = "qcom,usb-ssphy-qmp", }, { }, }; diff --git a/drivers/usb/phy/phy-msm-ssusb.c b/drivers/usb/phy/phy-msm-ssusb.c index feea5e0dcd8e..0259da87b5db 100644 --- a/drivers/usb/phy/phy-msm-ssusb.c +++ b/drivers/usb/phy/phy-msm-ssusb.c @@ -16,6 +16,8 @@ #include <linux/kernel.h> #include <linux/err.h> #include <linux/slab.h> +#include <linux/clk.h> +#include <linux/clk/msm-clk.h> #include <linux/delay.h> #include <linux/io.h> #include <linux/of.h> @@ -53,6 +55,8 @@ MODULE_PARM_DESC(ss_phy_override_deemphasis, "Override SSPHY demphasis value"); struct msm_ssphy { struct usb_phy phy; void __iomem *base; + struct clk *com_reset_clk; /* PHY common block reset */ + struct clk *reset_clk; /* SS PHY reset */ struct regulator *vdd; struct regulator *vdda18; bool suspended; @@ -214,19 +218,24 @@ static int msm_ssphy_init(struct usb_phy *uphy) /* read initial value */ val = readl_relaxed(phy->base + SS_PHY_CTRL_REG); + /* Use clk reset, if available; otherwise use SS_PHY_RESET bit */ + if (phy->com_reset_clk) { + clk_reset(phy->com_reset_clk, CLK_RESET_ASSERT); + clk_reset(phy->reset_clk, CLK_RESET_ASSERT); + udelay(10); /* 10us required before de-asserting */ + clk_reset(phy->com_reset_clk, CLK_RESET_DEASSERT); + clk_reset(phy->reset_clk, CLK_RESET_DEASSERT); + } else { + writel_relaxed(val | SS_PHY_RESET, phy->base + SS_PHY_CTRL_REG); + udelay(10); /* 10us required before de-asserting */ + writel_relaxed(val, phy->base + SS_PHY_CTRL_REG); + } + /* Use ref_clk from pads and set its parameters */ val |= REF_USE_PAD; writel_relaxed(val, phy->base + SS_PHY_CTRL_REG); msleep(30); - /* Assert SSPHY reset */ - writel_relaxed(val | SS_PHY_RESET, phy->base + SS_PHY_CTRL_REG); - usleep_range(2000, 2200); - - /* De-assert SSPHY reset - power and ref_clock must be ON */ - writel_relaxed(val, phy->base + SS_PHY_CTRL_REG); - usleep_range(2000, 2200); - /* Ref clock must be stable now, enable ref clock for HS mode */ val |= LANE0_PWR_PRESENT | REF_SS_PHY_EN; writel_relaxed(val, phy->base + SS_PHY_CTRL_REG); @@ -332,6 +341,13 @@ static int msm_ssphy_set_suspend(struct usb_phy *uphy, int suspend) /* Set TEST_POWERDOWN (enables PHY retention) */ msm_usb_write_readback(base, SS_PHY_CTRL_REG, TEST_POWERDOWN, TEST_POWERDOWN); + if (phy->com_reset_clk && + !(phy->phy.flags & ENABLE_SECONDARY_PHY)) { + /* leave these asserted until resuming */ + clk_reset(phy->com_reset_clk, CLK_RESET_ASSERT); + clk_reset(phy->reset_clk, CLK_RESET_ASSERT); + } + msm_ssusb_ldo_enable(phy, 0); msm_ssusb_config_vdd(phy, 0); } else { @@ -343,9 +359,15 @@ static int msm_ssphy_set_suspend(struct usb_phy *uphy, int suspend) goto done; } - /* Assert SS PHY RESET */ - msm_usb_write_readback(base, SS_PHY_CTRL_REG, SS_PHY_RESET, - SS_PHY_RESET); + if (phy->com_reset_clk) { + clk_reset(phy->com_reset_clk, CLK_RESET_DEASSERT); + clk_reset(phy->reset_clk, CLK_RESET_DEASSERT); + } else { + /* Assert SS PHY RESET */ + msm_usb_write_readback(base, SS_PHY_CTRL_REG, + SS_PHY_RESET, SS_PHY_RESET); + } + /* Set REF_USE_PAD */ msm_usb_write_readback(base, SS_PHY_CTRL_REG, REF_USE_PAD, REF_USE_PAD); @@ -355,9 +377,11 @@ static int msm_ssphy_set_suspend(struct usb_phy *uphy, int suspend) /* Clear TEST_POWERDOWN */ msm_usb_write_readback(base, SS_PHY_CTRL_REG, TEST_POWERDOWN, 0); - /* 10usec delay required before de-asserting SS PHY RESET */ - udelay(10); - msm_usb_write_readback(base, SS_PHY_CTRL_REG, SS_PHY_RESET, 0); + if (!phy->com_reset_clk) { + udelay(10); /* 10us required before de-asserting */ + msm_usb_write_readback(base, SS_PHY_CTRL_REG, + SS_PHY_RESET, 0); + } /* * Reinitialize SSPHY parameters as SS_PHY RESET will reset @@ -426,16 +450,28 @@ static int msm_ssphy_probe(struct platform_device *pdev) return -ENODEV; } - if (of_get_property(dev->of_node, "qti,primary-phy", NULL)) { + phy->com_reset_clk = devm_clk_get(dev, "com_reset_clk"); + if (IS_ERR(phy->com_reset_clk)) { + dev_dbg(dev, "com_reset_clk unavailable\n"); + phy->com_reset_clk = NULL; + } + + phy->reset_clk = devm_clk_get(dev, "reset_clk"); + if (IS_ERR(phy->reset_clk)) { + dev_dbg(dev, "reset_clk unavailable\n"); + phy->reset_clk = NULL; + } + + if (of_get_property(dev->of_node, "qcom,primary-phy", NULL)) { dev_dbg(dev, "secondary HSPHY\n"); phy->phy.flags |= ENABLE_SECONDARY_PHY; } - ret = of_property_read_u32_array(dev->of_node, "qti,vdd-voltage-level", + ret = of_property_read_u32_array(dev->of_node, "qcom,vdd-voltage-level", (u32 *) phy->vdd_levels, ARRAY_SIZE(phy->vdd_levels)); if (ret) { - dev_err(dev, "error reading qti,vdd-voltage-level property\n"); + dev_err(dev, "error reading qcom,vdd-voltage-level property\n"); return ret; } @@ -472,10 +508,10 @@ static int msm_ssphy_probe(struct platform_device *pdev) platform_set_drvdata(pdev, phy); - if (of_property_read_bool(dev->of_node, "qti,vbus-valid-override")) + if (of_property_read_bool(dev->of_node, "qcom,vbus-valid-override")) phy->phy.flags |= PHY_VBUS_VALID_OVERRIDE; - if (of_property_read_u32(dev->of_node, "qti,deemphasis-value", + if (of_property_read_u32(dev->of_node, "qcom,deemphasis-value", &phy->deemphasis_val)) dev_dbg(dev, "unable to read ssphy deemphasis value\n"); @@ -521,7 +557,7 @@ static int msm_ssphy_remove(struct platform_device *pdev) static const struct of_device_id msm_usb_id_table[] = { { - .compatible = "qti,usb-ssphy", + .compatible = "qcom,usb-ssphy", }, { }, }; diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index e3b574c7ceb0..37ed14d4b9c0 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -30,6 +30,7 @@ #include <linux/of.h> #include <linux/dma-mapping.h> #include <linux/clk/msm-clk.h> +#include <linux/irqchip/msm-mpm-irq.h> #include <linux/usb.h> #include <linux/usb/otg.h> @@ -46,7 +47,6 @@ #include <linux/mhl_8334.h> #include <mach/scm.h> -#include <mach/mpm.h> #include <mach/msm_xo.h> #include <mach/msm_bus.h> #include <mach/rpm-regulator.h> @@ -900,7 +900,8 @@ static int msm_otg_suspend(struct msm_otg *motg) if (atomic_read(&motg->in_lpm)) return 0; - if (motg->pdata->delay_lpm_hndshk_on_disconnect && !msm_bam_lpm_ok()) + if (motg->pdata->delay_lpm_hndshk_on_disconnect && + !msm_bam_usb_lpm_ok()) return -EBUSY; motg->ui_enabled = 0; @@ -4323,7 +4324,7 @@ struct msm_otg_platform_data *msm_otg_dt_to_pdata(struct platform_device *pdev) pdata->enable_ahb2ahb_bypass = of_property_read_bool(node, "qcom,ahb-async-bridge-bypass"); pdata->disable_retention_with_vdd_min = of_property_read_bool(node, - "qti,disable-retention-with-vdd-min"); + "qcom,disable-retention-with-vdd-min"); res_gpio = of_get_named_gpio(node, "qcom,hsusb-otg-vddmin-gpio", 0); if (res_gpio < 0) @@ -4345,6 +4346,7 @@ static int __init msm_otg_probe(struct platform_device *pdev) struct msm_otg *motg; struct usb_phy *phy; struct msm_otg_platform_data *pdata; + void __iomem *tcsr; dev_info(&pdev->dev, "msm_otg probe\n"); @@ -4394,28 +4396,12 @@ static int __init msm_otg_probe(struct platform_device *pdev) goto put_core_clk; } - /* - * Targets on which link uses asynchronous reset methodology, - * free running clock is not required during the reset. - */ - motg->clk = clk_get(&pdev->dev, "alt_core_clk"); - if (IS_ERR(motg->clk)) { - ret = PTR_ERR(motg->clk); - motg->clk = NULL; - if (ret != -EPROBE_DEFER) - dev_dbg(&pdev->dev, "alt_core_clk is not present\n"); - else - goto put_pclk; - } else { - clk_set_rate(motg->clk, 60000000); - } - motg->xo_clk = clk_get(&pdev->dev, "xo"); if (IS_ERR(motg->xo_clk)) { ret = PTR_ERR(motg->xo_clk); motg->xo_clk = NULL; if (ret == -EPROBE_DEFER) - goto put_clk; + goto put_pclk; } /* @@ -4439,6 +4425,18 @@ static int __init msm_otg_probe(struct platform_device *pdev) } } + /* + * Targets on which link uses asynchronous reset methodology, + * free running clock is not required during the reset. + */ + motg->clk = clk_get(&pdev->dev, "alt_core_clk"); + if (IS_ERR(motg->clk)) { + motg->clk = NULL; + dev_dbg(&pdev->dev, "alt_core_clk is not present\n"); + } else { + clk_set_rate(motg->clk, 60000000); + } + if (pdev->dev.of_node) { dev_dbg(&pdev->dev, "device tree enabled\n"); pdata = msm_otg_dt_to_pdata(pdev); @@ -4516,6 +4514,27 @@ static int __init msm_otg_probe(struct platform_device *pdev) } dev_info(&pdev->dev, "OTG regs = %p\n", motg->regs); + if (pdata->enable_sec_phy) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_dbg(&pdev->dev, "missing TCSR memory resource\n"); + } else { + tcsr = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + if (!tcsr) { + dev_dbg(&pdev->dev, "tcsr ioremap failed\n"); + } else { + /* Enable USB2 on secondary HSPHY. */ + writel_relaxed(0x1, tcsr); + /* + * Ensure that TCSR write is completed before + * USB registers initialization. + */ + mb(); + } + } + } + if (pdata->enable_sec_phy) motg->usb_phy_ctrl_reg = USB_PHY_CTRL2; else @@ -4851,9 +4870,6 @@ put_sleep_clk: put_xo_clk: if (motg->xo_clk) clk_put(motg->xo_clk); -put_clk: - if (motg->clk) - clk_put(motg->clk); put_pclk: if (motg->pclk) clk_put(motg->pclk); diff --git a/drivers/video/msm/mdss/dsi_host_v2.c b/drivers/video/msm/mdss/dsi_host_v2.c index 4d9e60fffe5b..1e09239fadcd 100644 --- a/drivers/video/msm/mdss/dsi_host_v2.c +++ b/drivers/video/msm/mdss/dsi_host_v2.c @@ -958,14 +958,18 @@ int msm_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp) } msm_dsi_clk_ctrl(&ctrl->panel_data, 1); - dsi_set_tx_power_mode(0); + + if (0 == (req->flags & CMD_REQ_LP_MODE)) + dsi_set_tx_power_mode(0); if (req->flags & CMD_REQ_RX) msm_dsi_cmdlist_rx(ctrl, req); else msm_dsi_cmdlist_tx(ctrl, req); - dsi_set_tx_power_mode(1); + if (0 == (req->flags & CMD_REQ_LP_MODE)) + dsi_set_tx_power_mode(1); + msm_dsi_clk_ctrl(&ctrl->panel_data, 0); mutex_unlock(&ctrl->cmd_mutex); @@ -1307,6 +1311,21 @@ static int dsi_get_panel_cfg(char *panel_cfg) return rc; } +static struct device_node *dsi_pref_prim_panel( + struct platform_device *pdev) +{ + struct device_node *dsi_pan_node = NULL; + + pr_debug("%s:%d: Select primary panel from dt\n", + __func__, __LINE__); + dsi_pan_node = of_parse_phandle(pdev->dev.of_node, + "qcom,dsi-pref-prim-pan", 0); + if (!dsi_pan_node) + pr_err("%s:can't find panel phandle\n", __func__); + + return dsi_pan_node; +} + /** * dsi_find_panel_of_node(): find device node of dsi panel * @pdev: platform_device of the dsi ctrl node @@ -1336,14 +1355,7 @@ static struct device_node *dsi_find_panel_of_node( /* no panel cfg chg, parse dt */ pr_debug("%s:%d: no cmd line cfg present\n", __func__, __LINE__); - dsi_pan_node = of_parse_phandle( - pdev->dev.of_node, - "qcom,dsi-pref-prim-pan", 0); - if (!dsi_pan_node) { - pr_err("%s:can't find panel phandle\n", - __func__); - return NULL; - } + dsi_pan_node = dsi_pref_prim_panel(pdev); } else { if (panel_cfg[0] != '0') { pr_err("%s:%d:ctrl id=[%d] not supported\n", @@ -1371,7 +1383,7 @@ static struct device_node *dsi_find_panel_of_node( if (!dsi_pan_node) { pr_err("%s: invalid pan node\n", __func__); - return NULL; + dsi_pan_node = dsi_pref_prim_panel(pdev); } } return dsi_pan_node; diff --git a/drivers/video/msm/mdss/mdp3.c b/drivers/video/msm/mdss/mdp3.c index 4dc7cc513d70..171d56ecb488 100644 --- a/drivers/video/msm/mdss/mdp3.c +++ b/drivers/video/msm/mdss/mdp3.c @@ -186,6 +186,7 @@ static irqreturn_t mdp3_irq_handler(int irq, void *ptr) int i = 0; struct mdp3_hw_resource *mdata = (struct mdp3_hw_resource *)ptr; u32 mdp_interrupt = 0; + u32 mdp_status = 0; spin_lock(&mdata->irq_lock); if (!mdata->irq_mask) @@ -194,8 +195,8 @@ static irqreturn_t mdp3_irq_handler(int irq, void *ptr) clk_enable(mdp3_res->clocks[MDP3_CLK_AHB]); clk_enable(mdp3_res->clocks[MDP3_CLK_CORE]); - mdp_interrupt = MDP3_REG_READ(MDP3_REG_INTR_STATUS); - MDP3_REG_WRITE(MDP3_REG_INTR_CLEAR, mdp_interrupt); + mdp_status = MDP3_REG_READ(MDP3_REG_INTR_STATUS); + mdp_interrupt = mdp_status; pr_debug("mdp3_irq_handler irq=%d\n", mdp_interrupt); mdp_interrupt &= mdata->irq_mask; @@ -206,6 +207,7 @@ static irqreturn_t mdp3_irq_handler(int irq, void *ptr) mdp_interrupt = mdp_interrupt >> 1; i++; } + MDP3_REG_WRITE(MDP3_REG_INTR_CLEAR, mdp_status); clk_disable(mdp3_res->clocks[MDP3_CLK_AHB]); clk_disable(mdp3_res->clocks[MDP3_CLK_CORE]); @@ -949,7 +951,7 @@ static int mdp3_get_pan_cfg(struct mdss_panel_cfg *pan_cfg) { char *t = NULL; char pan_intf_str[MDSS_MAX_PANEL_LEN]; - int rc, i; + int rc, i, panel_len; char pan_name[MDSS_MAX_PANEL_LEN]; if (!pan_cfg) @@ -986,6 +988,14 @@ static int mdp3_get_pan_cfg(struct mdss_panel_cfg *pan_cfg) strlcpy(&pan_cfg->arg_cfg[0], t, sizeof(pan_cfg->arg_cfg)); pr_debug("%s:%d: t=[%s] panel name=[%s]\n", __func__, __LINE__, t, pan_cfg->arg_cfg); + + panel_len = strlen(pan_cfg->arg_cfg); + if (!panel_len) { + pr_err("%s: Panel name is invalid\n", __func__); + pan_cfg->pan_intf = MDSS_PANEL_INTF_INVALID; + return -EINVAL; + } + rc = mdp3_get_pan_intf(pan_intf_str); pan_cfg->pan_intf = (rc < 0) ? MDSS_PANEL_INTF_INVALID : rc; return 0; @@ -1069,10 +1079,10 @@ static int mdp3_parse_bootarg(struct platform_device *pdev) of_node_put(chosen_node); rc = mdp3_get_pan_cfg(pan_cfg); - if (!rc) + if (!rc) { pan_cfg->init_done = true; - - return rc; + return rc; + } get_dt_pan: rc = mdp3_parse_dt_pan_intf(pdev); @@ -1444,7 +1454,9 @@ int mdp3_self_map_iommu(struct ion_client *client, struct ion_handle *handle, ret = 0; } else { ret = PTR_ERR(iommu_meta); - goto out_unlock; + mutex_unlock(&mdp3_res->iommu_lock); + pr_err("%s: meta_create failed err=%d", __func__, ret); + return ret; } } else { if (iommu_meta->flags != iommu_flags) { @@ -1729,7 +1741,7 @@ int mdp3_parse_dt_splash(struct msm_fb_data_type *mfd) u32 offsets[2]; rc = of_property_read_u32_array(pdev->dev.of_node, - "qti,memblock-reserve", offsets, 2); + "qcom,memblock-reserve", offsets, 2); if (rc) { pr_err("fail to get memblock-reserve property\n"); diff --git a/drivers/video/msm/mdss/mdp3.h b/drivers/video/msm/mdss/mdp3.h index 046593237045..8b4b1417f435 100644 --- a/drivers/video/msm/mdss/mdp3.h +++ b/drivers/video/msm/mdss/mdp3.h @@ -25,6 +25,7 @@ #include "mdss_fb.h" #define MDP_VSYNC_CLK_RATE 19200000 +#define KOFF_TIMEOUT msecs_to_jiffies(84) enum { MDP3_CLK_AHB, diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c index eefca918b14d..ea37899f60fd 100644 --- a/drivers/video/msm/mdss/mdp3_ctrl.c +++ b/drivers/video/msm/mdss/mdp3_ctrl.c @@ -28,7 +28,10 @@ #define MDP_CORE_CLK_RATE 100000000 #define VSYNC_EXPIRE_TICK 4 -static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd); +static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd, + struct mdp_overlay *req, + int image_size, + int *pipe_ndx); static int mdp3_overlay_unset(struct msm_fb_data_type *mfd, int ndx); static int mdp3_histogram_stop(struct mdp3_session_data *session, u32 block); @@ -91,6 +94,36 @@ static int mdp3_bufq_count(struct mdp3_buffer_queue *bufq) return bufq->count; } +void mdp3_ctrl_notifier_register(struct mdp3_session_data *ses, + struct notifier_block *notifier) +{ + blocking_notifier_chain_register(&ses->notifier_head, notifier); +} + +void mdp3_ctrl_notifier_unregister(struct mdp3_session_data *ses, + struct notifier_block *notifier) +{ + blocking_notifier_chain_unregister(&ses->notifier_head, notifier); +} + +int mdp3_ctrl_notify(struct mdp3_session_data *ses, int event) +{ + return blocking_notifier_call_chain(&ses->notifier_head, event, ses); +} + +static void mdp3_dispatch_dma_done(struct work_struct *work) +{ + struct mdp3_session_data *session; + + pr_debug("%s\n", __func__); + session = container_of(work, struct mdp3_session_data, + dma_done_work); + if (!session) + return; + + mdp3_ctrl_notify(session, MDP_NOTIFY_FRAME_DONE); +} + static void mdp3_dispatch_clk_off(struct work_struct *work) { struct mdp3_session_data *session; @@ -121,6 +154,12 @@ void vsync_notify_handler(void *arg) sysfs_notify_dirent(session->vsync_event_sd); } +void dma_done_notify_handler(void *arg) +{ + struct mdp3_session_data *session = (struct mdp3_session_data *)arg; + schedule_work(&session->dma_done_work); +} + void vsync_count_down(void *arg) { struct mdp3_session_data *session = (struct mdp3_session_data *)arg; @@ -140,8 +179,8 @@ void mdp3_ctrl_reset_countdown(struct mdp3_session_data *session, static int mdp3_ctrl_vsync_enable(struct msm_fb_data_type *mfd, int enable) { struct mdp3_session_data *mdp3_session; - struct mdp3_vsync_notification vsync_client; - struct mdp3_vsync_notification *arg = NULL; + struct mdp3_notification vsync_client; + struct mdp3_notification *arg = NULL; pr_debug("mdp3_ctrl_vsync_enable =%d\n", enable); mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; @@ -464,6 +503,7 @@ static int mdp3_ctrl_dma_init(struct msm_fb_data_type *mfd, int frame_rate = mfd->panel_info->mipi.frame_rate; int vbp, vfp, vspw; int vtotal, vporch; + struct mdp3_notification dma_done_callback; vbp = panel_info->lcdc.v_back_porch; vfp = panel_info->lcdc.v_front_porch; @@ -499,6 +539,13 @@ static int mdp3_ctrl_dma_init(struct msm_fb_data_type *mfd, rc = dma->dma_config(dma, &sourceConfig, &outputConfig); else rc = -EINVAL; + + if (outputConfig.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) { + dma_done_callback.handler = dma_done_notify_handler; + dma_done_callback.arg = mfd->mdp.private1; + dma->dma_done_notifier(dma, &dma_done_callback); + } + return rc; } @@ -527,6 +574,8 @@ static int mdp3_ctrl_on(struct msm_fb_data_type *mfd) } mdp3_batfet_ctrl(true); + mdp3_ctrl_notifier_register(mdp3_session, + &mdp3_session->mfd->mdp_sync_pt_data.notifier); rc = mdp3_iommu_enable(MDP3_CLIENT_DMA_P); if (rc) { @@ -658,6 +707,8 @@ static int mdp3_ctrl_off(struct msm_fb_data_type *mfd) if (rc) pr_err("fail to dettach MDP DMA SMMU\n"); + mdp3_ctrl_notifier_unregister(mdp3_session, + &mdp3_session->mfd->mdp_sync_pt_data.notifier); mdp3_batfet_ctrl(false); mdp3_session->vsync_enabled = 0; atomic_set(&mdp3_session->vsync_countdown, 0); @@ -677,7 +728,7 @@ static int mdp3_ctrl_reset_cmd(struct msm_fb_data_type *mfd) struct mdp3_session_data *mdp3_session; struct mdp3_dma *mdp3_dma; struct mdss_panel_data *panel; - struct mdp3_vsync_notification vsync_client; + struct mdp3_notification vsync_client; pr_debug("mdp3_ctrl_reset_cmd\n"); mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; @@ -728,7 +779,7 @@ static int mdp3_ctrl_reset(struct msm_fb_data_type *mfd) struct mdp3_session_data *mdp3_session; struct mdp3_dma *mdp3_dma; struct mdss_panel_data *panel; - struct mdp3_vsync_notification vsync_client; + struct mdp3_notification vsync_client; pr_debug("mdp3_ctrl_reset\n"); mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; @@ -971,17 +1022,31 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd, return -EPERM; } + mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_BEGIN); data = mdp3_bufq_pop(&mdp3_session->bufq_in); if (data) { mdp3_ctrl_reset_countdown(mdp3_session, mfd); mdp3_ctrl_clk_enable(mfd, 1); - mdp3_session->dma->update(mdp3_session->dma, + rc = mdp3_session->dma->update(mdp3_session->dma, (void *)(int)data->addr, mdp3_session->intf); + /* This is for the previous frame */ + if (rc < 0) { + mdp3_ctrl_notify(mdp3_session, + MDP_NOTIFY_FRAME_TIMEOUT); + } else { + if (mdp3_ctrl_get_intf_type(mfd) == + MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) { + mdp3_ctrl_notify(mdp3_session, + MDP_NOTIFY_FRAME_DONE); + } + } + + mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED); mdp3_bufq_push(&mdp3_session->bufq_out, data); } - if (mdp3_bufq_count(&mdp3_session->bufq_out) > 2) { + if (mdp3_bufq_count(&mdp3_session->bufq_out) > 1) { data = mdp3_bufq_pop(&mdp3_session->bufq_out); if (data) mdp3_put_img(data, MDP3_CLIENT_DMA_P); @@ -1001,16 +1066,20 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd, mdss_fb_update_notify_update(mfd); - return rc; + return 0; } -static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd) +static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd, + struct mdp_overlay *req, + int image_size, + int *pipe_ndx) { struct fb_info *fbi; struct mdp3_session_data *mdp3_session; u32 offset; int bpp; struct mdss_panel_info *panel_info; + int rc; pr_debug("mdp3_ctrl_pan_display\n"); if (!mfd || !mfd->mdp.private1) @@ -1049,10 +1118,23 @@ static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd) if (mfd->fbi->screen_base) { mdp3_ctrl_reset_countdown(mdp3_session, mfd); + mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_BEGIN); mdp3_ctrl_clk_enable(mfd, 1); - mdp3_session->dma->update(mdp3_session->dma, + rc = mdp3_session->dma->update(mdp3_session->dma, (void *)(int)(mfd->iova + offset), mdp3_session->intf); + /* This is for the previous frame */ + if (rc < 0) { + mdp3_ctrl_notify(mdp3_session, + MDP_NOTIFY_FRAME_TIMEOUT); + } else { + if (mdp3_ctrl_get_intf_type(mfd) == + MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) { + mdp3_ctrl_notify(mdp3_session, + MDP_NOTIFY_FRAME_DONE); + } + } + mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED); } else { pr_debug("mdp3_ctrl_pan_display no memory, stop interface"); mdp3_clk_enable(1, 0); @@ -1683,6 +1765,7 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd) memset(mdp3_session, 0, sizeof(struct mdp3_session_data)); mutex_init(&mdp3_session->lock); INIT_WORK(&mdp3_session->clk_off_work, mdp3_dispatch_clk_off); + INIT_WORK(&mdp3_session->dma_done_work, mdp3_dispatch_dma_done); atomic_set(&mdp3_session->vsync_countdown, 0); mutex_init(&mdp3_session->histo_lock); mdp3_session->dma = mdp3_get_dma_pipe(MDP3_DMA_CAP_ALL); @@ -1718,6 +1801,7 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd) mdp3_bufq_init(&mdp3_session->bufq_out); mdp3_session->histo_status = 0; mdp3_session->lut_sel = 0; + BLOCKING_INIT_NOTIFIER_HEAD(&mdp3_session->notifier_head); init_timer(&mdp3_session->vsync_timer); mdp3_session->vsync_timer.function = mdp3_vsync_timer_func; @@ -1746,8 +1830,11 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd) kobject_uevent(&dev->kobj, KOBJ_ADD); pr_debug("vsync kobject_uevent(KOBJ_ADD)\n"); - if (mdp3_get_cont_spash_en()) + if (mdp3_get_cont_spash_en()) { mdp3_session->clk_on = 1; + mdp3_ctrl_notifier_register(mdp3_session, + &mdp3_session->mfd->mdp_sync_pt_data.notifier); + } if (splash_mismatch) { pr_err("splash memory mismatch, stop splash\n"); diff --git a/drivers/video/msm/mdss/mdp3_ctrl.h b/drivers/video/msm/mdss/mdp3_ctrl.h index f2484ef951a4..cfad1d3c8f58 100644 --- a/drivers/video/msm/mdss/mdp3_ctrl.h +++ b/drivers/video/msm/mdss/mdp3_ctrl.h @@ -49,6 +49,7 @@ struct mdp3_session_data { struct mdp3_buffer_queue bufq_in; struct mdp3_buffer_queue bufq_out; struct work_struct clk_off_work; + struct work_struct dma_done_work; int histo_status; struct mutex histo_lock; int lut_sel; @@ -56,6 +57,7 @@ struct mdp3_session_data { bool vsync_before_commit; bool first_commit; int clk_on; + struct blocking_notifier_head notifier_head; int vsync_enabled; atomic_t vsync_countdown; /* Used to count down */ diff --git a/drivers/video/msm/mdss/mdp3_dma.c b/drivers/video/msm/mdss/mdp3_dma.c index 4d9bceab8691..13a453e01bbf 100644 --- a/drivers/video/msm/mdss/mdp3_dma.c +++ b/drivers/video/msm/mdss/mdp3_dma.c @@ -27,26 +27,38 @@ static void mdp3_vsync_intr_handler(int type, void *arg) { struct mdp3_dma *dma = (struct mdp3_dma *)arg; - struct mdp3_vsync_notification vsync_client; + struct mdp3_notification vsync_client; + unsigned int wait_for_next_vs; pr_debug("mdp3_vsync_intr_handler\n"); spin_lock(&dma->dma_lock); vsync_client = dma->vsync_client; - complete(&dma->vsync_comp); + wait_for_next_vs = !dma->vsync_status; + dma->vsync_status = 0; + if (wait_for_next_vs) + complete(&dma->vsync_comp); spin_unlock(&dma->dma_lock); - if (vsync_client.handler) + if (vsync_client.handler) { vsync_client.handler(vsync_client.arg); - else - mdp3_irq_disable_nosync(type); + } else { + if (wait_for_next_vs) + mdp3_irq_disable_nosync(type); + } } static void mdp3_dma_done_intr_handler(int type, void *arg) { struct mdp3_dma *dma = (struct mdp3_dma *)arg; + struct mdp3_notification dma_client; pr_debug("mdp3_dma_done_intr_handler\n"); + spin_lock(&dma->dma_lock); + dma_client = dma->dma_notifier_client; complete(&dma->dma_comp); + spin_unlock(&dma->dma_lock); mdp3_irq_disable_nosync(type); + if (dma_client.handler) + dma_client.handler(dma_client.arg); } static void mdp3_hist_done_intr_handler(int type, void *arg) @@ -189,7 +201,7 @@ static int mdp3_dma_callback_setup(struct mdp3_dma *dma) } static void mdp3_dma_vsync_enable(struct mdp3_dma *dma, - struct mdp3_vsync_notification *vsync_client) + struct mdp3_notification *vsync_client) { unsigned long flag; int updated = 0; @@ -220,6 +232,21 @@ static void mdp3_dma_vsync_enable(struct mdp3_dma *dma, } } +static void mdp3_dma_done_notifier(struct mdp3_dma *dma, + struct mdp3_notification *dma_client) +{ + unsigned long flag; + + spin_lock_irqsave(&dma->dma_lock, flag); + if (dma_client) { + dma->dma_notifier_client = *dma_client; + } else { + dma->dma_notifier_client.handler = NULL; + dma->dma_notifier_client.arg = NULL; + } + spin_unlock_irqrestore(&dma->dma_lock, flag); +} + static void mdp3_dma_clk_auto_gating(struct mdp3_dma *dma, int enable) { u32 cgc; @@ -546,13 +573,20 @@ static int mdp3_dmap_update(struct mdp3_dma *dma, void *buf, { unsigned long flag; int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC; + int rc = 0; pr_debug("mdp3_dmap_update\n"); if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) { cb_type = MDP3_DMA_CALLBACK_TYPE_DMA_DONE; - if (intf->active) - wait_for_completion_killable(&dma->dma_comp); + if (intf->active) { + rc = wait_for_completion_timeout(&dma->dma_comp, + KOFF_TIMEOUT); + if (rc <= 0) { + WARN(1, "cmd kickoff timed out (%d)\n", rc); + rc = -1; + } + } } spin_lock_irqsave(&dma->dma_lock, flag); MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_ADDR, (u32)buf); @@ -567,16 +601,22 @@ static int mdp3_dmap_update(struct mdp3_dma *dma, void *buf, intf->start(intf); } - wmb(); + mb(); + dma->vsync_status = MDP3_REG_READ(MDP3_REG_INTR_STATUS) & + (1 << MDP3_INTR_LCDC_START_OF_FRAME); init_completion(&dma->vsync_comp); spin_unlock_irqrestore(&dma->dma_lock, flag); mdp3_dma_callback_enable(dma, cb_type); pr_debug("mdp3_dmap_update wait for vsync_comp in\n"); - if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) - wait_for_completion_killable(&dma->vsync_comp); + if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) { + rc = wait_for_completion_timeout(&dma->vsync_comp, + KOFF_TIMEOUT); + if (rc <= 0) + rc = -1; + } pr_debug("mdp3_dmap_update wait for vsync_comp out\n"); - return 0; + return rc; } static int mdp3_dmas_update(struct mdp3_dma *dma, void *buf, @@ -870,6 +910,7 @@ int mdp3_dma_init(struct mdp3_dma *dma) dma->get_histo = mdp3_dmap_histo_get; dma->histo_op = mdp3_dmap_histo_op; dma->vsync_enable = mdp3_dma_vsync_enable; + dma->dma_done_notifier = mdp3_dma_done_notifier; dma->start = mdp3_dma_start; dma->stop = mdp3_dma_stop; dma->config_stride = mdp3_dma_stride_config; diff --git a/drivers/video/msm/mdss/mdp3_dma.h b/drivers/video/msm/mdss/mdp3_dma.h index 935025f2ea45..d914c712ef72 100644 --- a/drivers/video/msm/mdss/mdp3_dma.h +++ b/drivers/video/msm/mdss/mdp3_dma.h @@ -14,6 +14,7 @@ #ifndef MDP3_DMA_H #define MDP3_DMA_H +#include <linux/notifier.h> #include <linux/sched.h> #define MDP_HISTOGRAM_BL_SCALE_MAX 1024 @@ -227,7 +228,7 @@ struct mdp3_dma_histogram_data { u32 extra[2]; }; -struct mdp3_vsync_notification { +struct mdp3_notification { void (*handler)(void *arg); void *arg; }; @@ -245,7 +246,8 @@ struct mdp3_dma { struct completion vsync_comp; struct completion dma_comp; struct completion histo_comp; - struct mdp3_vsync_notification vsync_client; + struct mdp3_notification vsync_client; + struct mdp3_notification dma_notifier_client; struct mdp3_dma_output_config output_config; struct mdp3_dma_source source_config; @@ -256,6 +258,7 @@ struct mdp3_dma { struct mdp3_dma_histogram_config histogram_config; int histo_state; struct mdp3_dma_histogram_data histo_data; + unsigned int vsync_status; int (*dma_config)(struct mdp3_dma *dma, struct mdp3_dma_source *source_config, @@ -290,7 +293,10 @@ struct mdp3_dma { void (*config_stride)(struct mdp3_dma *dma, int stride); void (*vsync_enable)(struct mdp3_dma *dma, - struct mdp3_vsync_notification *vsync_client); + struct mdp3_notification *vsync_client); + + void (*dma_done_notifier)(struct mdp3_dma *dma, + struct mdp3_notification *dma_client); }; struct mdp3_video_intf_cfg { diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c index 5a66b8ab3366..9eb8643563a9 100644 --- a/drivers/video/msm/mdss/mdss_dsi.c +++ b/drivers/video/msm/mdss/mdss_dsi.c @@ -783,6 +783,21 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, return rc; } +static struct device_node *mdss_dsi_pref_prim_panel( + struct platform_device *pdev) +{ + struct device_node *dsi_pan_node = NULL; + + pr_debug("%s:%d: Select primary panel from dt\n", + __func__, __LINE__); + dsi_pan_node = of_parse_phandle(pdev->dev.of_node, + "qcom,dsi-pref-prim-pan", 0); + if (!dsi_pan_node) + pr_err("%s:can't find panel phandle\n", __func__); + + return dsi_pan_node; +} + /** * mdss_dsi_find_panel_of_node(): find device node of dsi panel * @pdev: platform_device of the dsi ctrl node @@ -810,14 +825,7 @@ static struct device_node *mdss_dsi_find_panel_of_node( /* no panel cfg chg, parse dt */ pr_debug("%s:%d: no cmd line cfg present\n", __func__, __LINE__); - dsi_pan_node = of_parse_phandle( - pdev->dev.of_node, - "qcom,dsi-pref-prim-pan", 0); - if (!dsi_pan_node) { - pr_err("%s:can't find panel phandle\n", - __func__); - return NULL; - } + dsi_pan_node = mdss_dsi_pref_prim_panel(pdev); } else { if (panel_cfg[0] == '0') { pr_debug("%s:%d: DSI ctrl 1\n", __func__, __LINE__); @@ -850,11 +858,12 @@ static struct device_node *mdss_dsi_find_panel_of_node( dsi_pan_node = of_find_node_by_name(mdss_node, panel_name); if (!dsi_pan_node) { - pr_err("%s: invalid pan node\n", + pr_err("%s: invalid pan node, selecting prim panel\n", __func__); - return NULL; + dsi_pan_node = mdss_dsi_pref_prim_panel(pdev); } } + return dsi_pan_node; } diff --git a/drivers/video/msm/mdss/mdss_dsi_cmd.h b/drivers/video/msm/mdss/mdss_dsi_cmd.h index c48075601310..f806e78e6aee 100644 --- a/drivers/video/msm/mdss/mdss_dsi_cmd.h +++ b/drivers/video/msm/mdss/mdss_dsi_cmd.h @@ -98,6 +98,7 @@ struct dsi_cmd_desc { #define CMD_REQ_COMMIT 0x0002 #define CMD_CLK_CTRL 0x0004 #define CMD_REQ_NO_MAX_PKT_SIZE 0x0008 +#define CMD_REQ_LP_MODE 0x0010 struct dcs_cmd_req { struct dsi_cmd_desc *cmds; diff --git a/drivers/video/msm/mdss/mdss_dsi_panel.c b/drivers/video/msm/mdss/mdss_dsi_panel.c index c9d6c4484818..fc63ce48b7be 100644 --- a/drivers/video/msm/mdss/mdss_dsi_panel.c +++ b/drivers/video/msm/mdss/mdss_dsi_panel.c @@ -19,7 +19,7 @@ #include <linux/delay.h> #include <linux/slab.h> #include <linux/leds.h> -#include <linux/pwm.h> +#include <linux/qpnp/pwm.h> #include <linux/err.h> #include "mdss_dsi.h" @@ -69,9 +69,9 @@ static void mdss_dsi_panel_bklt_pwm(struct mdss_dsi_ctrl_pdata *ctrl, int level) ctrl->pwm_enabled = 0; } - ret = pwm_config(ctrl->pwm_bl, duty, ctrl->pwm_period); + ret = pwm_config_us(ctrl->pwm_bl, duty, ctrl->pwm_period); if (ret) { - pr_err("%s: pwm_config() failed err=%d.\n", __func__, ret); + pr_err("%s: pwm_config_us() failed err=%d.\n", __func__, ret); return; } @@ -118,6 +118,11 @@ static void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl, cmdreq.cmds = pcmds->cmds; cmdreq.cmds_cnt = pcmds->cmd_cnt; cmdreq.flags = CMD_REQ_COMMIT; + + /*Panel ON/Off commands should be sent in DSI Low Power Mode*/ + if (pcmds->link_state == DSI_LP_MODE) + cmdreq.flags |= CMD_REQ_LP_MODE; + cmdreq.rlen = 0; cmdreq.cb = NULL; @@ -810,6 +815,8 @@ static int mdss_panel_parse_dt(struct device_node *np, ctrl_pdata->bklt_ctrl = BL_DCS_CMD; } } + rc = of_property_read_u32(np, "qcom,mdss-brightness-max-level", &tmp); + pinfo->brightness_max = (!rc ? tmp : MDSS_MAX_BL_BRIGHTNESS); rc = of_property_read_u32(np, "qcom,mdss-dsi-bl-min-level", &tmp); pinfo->bl_min = (!rc ? tmp : 0); rc = of_property_read_u32(np, "qcom,mdss-dsi-bl-max-level", &tmp); diff --git a/drivers/video/msm/mdss/mdss_edp.c b/drivers/video/msm/mdss/mdss_edp.c index 19bc1b144321..1b3f98db4f89 100644 --- a/drivers/video/msm/mdss/mdss_edp.c +++ b/drivers/video/msm/mdss/mdss_edp.c @@ -23,7 +23,7 @@ #include <linux/gpio.h> #include <linux/err.h> #include <linux/regulator/consumer.h> -#include <linux/pwm.h> +#include <linux/qpnp/pwm.h> #include <linux/clk.h> #include <linux/spinlock_types.h> #include <linux/kthread.h> @@ -206,11 +206,11 @@ void mdss_edp_set_backlight(struct mdss_panel_data *pdata, u32 bl_level) if (bl_level > bl_max) bl_level = bl_max; - ret = pwm_config(edp_drv->bl_pwm, + ret = pwm_config_us(edp_drv->bl_pwm, bl_level * edp_drv->pwm_period / bl_max, edp_drv->pwm_period); if (ret) { - pr_err("%s: pwm_config() failed err=%d.\n", __func__, + pr_err("%s: pwm_config_us() failed err=%d.\n", __func__, ret); return; } @@ -714,10 +714,16 @@ static int mdss_edp_remove(struct platform_device *pdev) static int mdss_edp_device_register(struct mdss_edp_drv_pdata *edp_drv) { int ret; + u32 tmp; mdss_edp_edid2pinfo(edp_drv); edp_drv->panel_data.panel_info.bl_min = 1; edp_drv->panel_data.panel_info.bl_max = 255; + ret = of_property_read_u32(edp_drv->pdev->dev.of_node, + "qcom,mdss-brightness-max-level", &tmp); + edp_drv->panel_data.panel_info.brightness_max = + (!ret ? tmp : MDSS_MAX_BL_BRIGHTNESS); + edp_drv->panel_data.panel_info.edp.frame_rate = DEFAULT_FRAME_RATE;/* 60 fps */ diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c index f7c7f99b0822..fe622f42d3e4 100644 --- a/drivers/video/msm/mdss/mdss_fb.c +++ b/drivers/video/msm/mdss/mdss_fb.c @@ -147,6 +147,47 @@ static int mdss_fb_notify_update(struct msm_fb_data_type *mfd, return ret; } +static int mdss_fb_splash_thread(void *data) +{ + struct msm_fb_data_type *mfd = data; + int ret = -EINVAL; + struct fb_info *fbi = NULL; + int ov_index[2]; + + if (!mfd || !mfd->fbi || !mfd->mdp.splash_fnc) { + pr_err("Invalid input parameter\n"); + goto end; + } + + fbi = mfd->fbi; + + ret = mdss_fb_open(fbi, current->tgid); + if (ret) { + pr_err("fb_open failed\n"); + goto end; + } + + mfd->bl_updated = true; + mdss_fb_set_backlight(mfd, mfd->panel_info->bl_max >> 1); + + ret = mfd->mdp.splash_fnc(mfd, ov_index, MDP_CREATE_SPLASH_OV); + if (ret) { + pr_err("Splash image failed\n"); + goto splash_err; + } + + do { + schedule_timeout_interruptible(SPLASH_THREAD_WAIT_TIMEOUT * HZ); + } while (!kthread_should_stop()); + + mfd->mdp.splash_fnc(mfd, ov_index, MDP_REMOVE_SPLASH_OV); + +splash_err: + mdss_fb_release(fbi, current->tgid); +end: + return ret; +} + static int lcd_backlight_registered; static void mdss_fb_set_bl_brightness(struct led_classdev *led_cdev, @@ -155,13 +196,13 @@ static void mdss_fb_set_bl_brightness(struct led_classdev *led_cdev, struct msm_fb_data_type *mfd = dev_get_drvdata(led_cdev->dev->parent); int bl_lvl; - if (value > MDSS_MAX_BL_BRIGHTNESS) - value = MDSS_MAX_BL_BRIGHTNESS; + if (value > mfd->panel_info->brightness_max) + value = mfd->panel_info->brightness_max; /* This maps android backlight level 0 to 255 into driver backlight level 0 to bl_max with rounding */ MDSS_BRIGHT_TO_BL(bl_lvl, value, mfd->panel_info->bl_max, - MDSS_MAX_BL_BRIGHTNESS); + mfd->panel_info->brightness_max); if (!bl_lvl && value) bl_lvl = 1; @@ -376,6 +417,8 @@ static int mdss_fb_probe(struct platform_device *pdev) /* android supports only one lcd-backlight/lcd for now */ if (!lcd_backlight_registered) { + backlight_led.brightness = mfd->panel_info->brightness_max; + backlight_led.max_brightness = mfd->panel_info->brightness_max; if (led_classdev_register(&pdev->dev, &backlight_led)) pr_err("led_classdev_register failed\n"); else @@ -405,6 +448,16 @@ static int mdss_fb_probe(struct platform_device *pdev) else mfd->mdp_sync_pt_data.threshold = 2; + if (mfd->index == 0) { + mfd->splash_thread = kthread_run(mdss_fb_splash_thread, mfd, + "mdss_fb_splash"); + if (IS_ERR(mfd->splash_thread)) { + pr_err("unable to start splash thread %d\n", + mfd->index); + mfd->splash_thread = NULL; + } + } + return rc; } @@ -1212,6 +1265,12 @@ static int mdss_fb_open(struct fb_info *info, int user) pinfo->ref_cnt++; mfd->ref_cnt++; + /* Stop the splash thread once userspace open the fb node */ + if (mfd->splash_thread && mfd->ref_cnt > 1) { + kthread_stop(mfd->splash_thread); + mfd->splash_thread = NULL; + } + return 0; blank_error: @@ -1591,7 +1650,7 @@ static int mdss_fb_pan_display_sub(struct fb_var_screeninfo *var, (var->yoffset / info->fix.ypanstep) * info->fix.ypanstep; if (mfd->mdp.dma_fnc) - mfd->mdp.dma_fnc(mfd); + mfd->mdp.dma_fnc(mfd, NULL, 0, NULL); else pr_warn("dma function not set for panel type=%d\n", mfd->panel.type); @@ -2221,13 +2280,6 @@ static int mdss_fb_register_extra_panel(struct platform_device *pdev, return -EEXIST; } - if ((fb_pdata->panel_info.type != MIPI_VIDEO_PANEL) || - (pdata->panel_info.type != MIPI_VIDEO_PANEL)) { - pr_err("Split panel not supported for panel type %d\n", - pdata->panel_info.type); - return -EINVAL; - } - fb_pdata->next = pdata; return 0; diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h index fc6208f75a15..7a7ddfac1b17 100644 --- a/drivers/video/msm/mdss/mdss_fb.h +++ b/drivers/video/msm/mdss/mdss_fb.h @@ -36,6 +36,8 @@ #define WAIT_DISP_OP_TIMEOUT ((WAIT_FENCE_FIRST_TIMEOUT + \ WAIT_FENCE_FINAL_TIMEOUT) * MDP_MAX_FENCE_FD) +#define SPLASH_THREAD_WAIT_TIMEOUT 3 + #ifndef MAX #define MAX(x, y) (((x) > (y)) ? (x) : (y)) #endif @@ -70,6 +72,11 @@ enum mdp_notify_event { MDP_NOTIFY_FRAME_TIMEOUT, }; +enum mdp_splash_event { + MDP_CREATE_SPLASH_OV = 0, + MDP_REMOVE_SPLASH_OV, +}; + struct disp_info_type_suspend { int op_enable; int panel_power_on; @@ -113,7 +120,8 @@ struct msm_mdp_interface { int (*kickoff_fnc)(struct msm_fb_data_type *mfd, struct mdp_display_commit *data); int (*ioctl_handler)(struct msm_fb_data_type *mfd, u32 cmd, void *arg); - void (*dma_fnc)(struct msm_fb_data_type *mfd); + void (*dma_fnc)(struct msm_fb_data_type *mfd, struct mdp_overlay *req, + int image_len, int *pipe_ndx); int (*cursor_update)(struct msm_fb_data_type *mfd, struct fb_cursor *cursor); int (*lut_update)(struct msm_fb_data_type *mfd, struct fb_cmap *cmap); @@ -122,6 +130,7 @@ struct msm_mdp_interface { int (*update_ad_input)(struct msm_fb_data_type *mfd); int (*panel_register_done)(struct mdss_panel_data *pdata); u32 (*fb_stride)(u32 fb_index, u32 xres, int bpp); + int (*splash_fnc) (struct msm_fb_data_type *mfd, int *index, int req); struct msm_sync_pt_data *(*get_sync_fnc)(struct msm_fb_data_type *mfd, const struct mdp_buf_sync *buf_sync); void *private1; @@ -204,6 +213,8 @@ struct msm_fb_data_type { wait_queue_head_t idle_wait_q; bool shutdown_pending; + struct task_struct *splash_thread; + struct msm_fb_backup_type msm_fb_backup; struct completion power_set_comp; u32 is_power_setting; diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c index 1036065a16b3..d83e95a97e53 100644 --- a/drivers/video/msm/mdss/mdss_mdp.c +++ b/drivers/video/msm/mdss/mdss_mdp.c @@ -30,6 +30,7 @@ #include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> +#include <linux/regulator/rpm-smd-regulator.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/sched.h> @@ -47,7 +48,6 @@ #include <linux/msm_iommu_domains.h> #include <mach/memory.h> #include <mach/msm_memtypes.h> -#include <mach/rpm-regulator-smd.h> #include "mdss.h" #include "mdss_fb.h" @@ -536,6 +536,8 @@ static int mdss_mdp_clk_update(u32 clk_idx, u32 enable) if (clk) { pr_debug("clk=%d en=%d\n", clk_idx, enable); if (enable) { + if (clk_idx == MDSS_CLK_MDP_VSYNC) + clk_set_rate(clk, 19200000); ret = clk_prepare_enable(clk); } else { clk_disable_unprepare(clk); @@ -1311,7 +1313,7 @@ static int mdss_mdp_get_pan_cfg(struct mdss_panel_cfg *pan_cfg) { char *t = NULL; char pan_intf_str[MDSS_MAX_PANEL_LEN]; - int rc, i; + int rc, i, panel_len; char pan_name[MDSS_MAX_PANEL_LEN]; if (!pan_cfg) @@ -1348,6 +1350,14 @@ static int mdss_mdp_get_pan_cfg(struct mdss_panel_cfg *pan_cfg) strlcpy(&pan_cfg->arg_cfg[0], t, sizeof(pan_cfg->arg_cfg)); pr_debug("%s:%d: t=[%s] panel name=[%s]\n", __func__, __LINE__, t, pan_cfg->arg_cfg); + + panel_len = strlen(pan_cfg->arg_cfg); + if (!panel_len) { + pr_err("%s: Panel name is invalid\n", __func__); + pan_cfg->pan_intf = MDSS_PANEL_INTF_INVALID; + return -EINVAL; + } + rc = mdss_mdp_get_pan_intf(pan_intf_str); pan_cfg->pan_intf = (rc < 0) ? MDSS_PANEL_INTF_INVALID : rc; return 0; @@ -1452,10 +1462,10 @@ static int mdss_mdp_parse_bootarg(struct platform_device *pdev) of_node_put(chosen_node); rc = mdss_mdp_get_pan_cfg(pan_cfg); - if (!rc) + if (!rc) { pan_cfg->init_done = true; - - return rc; + return rc; + } get_dt_pan: rc = mdss_mdp_parse_dt_pan_intf(pdev); diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h index 9e0e9cc4afc9..ae3f1a028733 100644 --- a/drivers/video/msm/mdss/mdss_mdp.h +++ b/drivers/video/msm/mdss/mdss_mdp.h @@ -154,6 +154,7 @@ struct mdss_mdp_ctl { u32 flush_bits; u32 flush_reg_data; + bool split_flush_en; bool is_video_mode; u32 play_cnt; u32 vsync_cnt; @@ -214,6 +215,8 @@ struct mdss_mdp_mixer { u16 height; struct mdss_mdp_img_rect roi; u8 cursor_enabled; + u16 cursor_hotx; + u16 cursor_hoty; u8 rotator_mode; struct mdss_mdp_ctl *ctl; @@ -270,11 +273,12 @@ struct pp_hist_col_info { u32 hist_cnt_sent; u32 hist_cnt_time; u32 frame_cnt; - u32 is_kick_ready; struct completion comp; u32 data[HIST_V_SIZE]; struct mutex hist_mutex; spinlock_t hist_lock; + char __iomem *base; + u32 intr_shift; }; struct mdss_mdp_ad { @@ -285,6 +289,7 @@ struct mdss_mdp_ad { struct mdss_ad_info { u8 num; u8 calc_hw_num; + u32 ops; u32 sts; u32 reg_sts; u32 state; @@ -364,7 +369,6 @@ struct mdss_mdp_pipe { u8 overfetch_disable; u32 transp; u32 bg_color; - u8 has_buf; struct msm_fb_data_type *mfd; struct mdss_mdp_mixer *mixer; diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c index 9a39573a3557..cf634775f4cc 100644 --- a/drivers/video/msm/mdss/mdss_mdp_ctl.c +++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c @@ -656,13 +656,19 @@ int mdss_mdp_ctl_splash_finish(struct mdss_mdp_ctl *ctl, bool handoff) static inline int mdss_mdp_set_split_ctl(struct mdss_mdp_ctl *ctl, struct mdss_mdp_ctl *split_ctl) { - if (!ctl || !split_ctl) + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + + if (!ctl || !split_ctl || !mdata) return -ENODEV; /* setup split ctl mixer as right mixer of original ctl so that * original ctl can work the same way as dual pipe solution */ ctl->mixer_right = split_ctl->mixer_left; + if ((mdata->mdp_rev >= MDSS_MDP_HW_REV_103) && + (ctl->opmode == MDSS_MDP_CTL_OP_VIDEO_MODE)) + ctl->split_flush_en = true; + return 0; } @@ -1035,7 +1041,7 @@ static void mdss_mdp_ctl_split_display_enable(int enable, MDSS_MDP_REG_WRITE(MDSS_MDP_REG_SPLIT_DISPLAY_LOWER_PIPE_CTRL, lower); MDSS_MDP_REG_WRITE(MDSS_MDP_REG_SPLIT_DISPLAY_EN, enable); - if (main_ctl->mdata->mdp_rev >= MDSS_MDP_HW_REV_103) + if (main_ctl->split_flush_en) MDSS_MDP_REG_WRITE(MMSS_MDP_MDP_SSPP_SPARE_0, enable ? 0x1 : 0x0); } @@ -1954,7 +1960,7 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg) /* postprocessing setup, including dspp */ mdss_mdp_pp_setup_locked(ctl); - if (sctl && ctl->mdata->mdp_rev >= MDSS_MDP_HW_REV_103) { + if (sctl && ctl->split_flush_en) { ctl->flush_bits |= sctl->flush_bits; sctl->flush_bits = 0; } diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_video.c b/drivers/video/msm/mdss/mdss_mdp_intf_video.c index 84643de78181..a469dead830b 100644 --- a/drivers/video/msm/mdss/mdss_mdp_intf_video.c +++ b/drivers/video/msm/mdss/mdss_mdp_intf_video.c @@ -62,6 +62,7 @@ struct mdss_mdp_video_ctx { atomic_t vsync_ref; spinlock_t vsync_lock; + struct mutex vsync_mtx; struct list_head vsync_handlers; }; @@ -216,19 +217,23 @@ static inline void video_vsync_irq_enable(struct mdss_mdp_ctl *ctl, bool clear) { struct mdss_mdp_video_ctx *ctx = ctl->priv_data; + mutex_lock(&ctx->vsync_mtx); if (atomic_inc_return(&ctx->vsync_ref) == 1) mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num); else if (clear) mdss_mdp_irq_clear(ctl->mdata, MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num); + mutex_unlock(&ctx->vsync_mtx); } static inline void video_vsync_irq_disable(struct mdss_mdp_ctl *ctl) { struct mdss_mdp_video_ctx *ctx = ctl->priv_data; + mutex_lock(&ctx->vsync_mtx); if (atomic_dec_return(&ctx->vsync_ref) == 0) mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num); + mutex_unlock(&ctx->vsync_mtx); } static int mdss_mdp_video_add_vsync_handler(struct mdss_mdp_ctl *ctl, @@ -687,6 +692,7 @@ int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl) ctx->intf_type = ctl->intf_type; init_completion(&ctx->vsync_comp); spin_lock_init(&ctx->vsync_lock); + mutex_init(&ctx->vsync_mtx); atomic_set(&ctx->vsync_ref, 0); mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num, diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c index a6debba9d371..474a6398c556 100644 --- a/drivers/video/msm/mdss/mdss_mdp_overlay.c +++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c @@ -34,6 +34,8 @@ #include "mdss_mdp.h" #include "mdss_mdp_rotator.h" +#include "splash.h" + #define VSYNC_PERIOD 16 #define BORDERFILL_NDX 0x0BF000BF #define CHECK_BOUNDS(offset, size, max_size) \ @@ -44,6 +46,8 @@ #define MEM_PROTECT_SD_CTRL 0xF +#define INVALID_PIPE_INDEX 0xFFFF + struct sd_ctrl_req { unsigned int enable; } __attribute__ ((__packed__)); @@ -150,6 +154,9 @@ int mdss_mdp_overlay_req_check(struct msm_fb_data_type *mfd, pr_err("Invalid decimation factors horz=%d vert=%d\n", req->horz_deci, req->vert_deci); return -EINVAL; + } else if (req->flags & MDP_BWC_EN) { + pr_err("Decimation can't be enabled with BWC\n"); + return -EINVAL; } } @@ -266,7 +273,8 @@ static int __mdp_pipe_tune_perf(struct mdss_mdp_pipe *pipe) * requirement by applying vertical decimation and reduce * mdp clock requirement */ - if (mdata->has_decimation && (pipe->vert_deci < MAX_DECIMATION)) + if (mdata->has_decimation && (pipe->vert_deci < MAX_DECIMATION) + && !pipe->bwc_mode) pipe->vert_deci++; else return -EPERM; @@ -608,7 +616,6 @@ static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, } pipe->params_changed++; - pipe->has_buf = 0; req->vert_deci = pipe->vert_deci; @@ -1257,6 +1264,9 @@ static int mdss_mdp_overlay_queue(struct msm_fb_data_type *mfd, pr_debug("ov queue pnum=%d\n", pipe->num); + if (pipe->flags & MDP_SOLID_FILL) + pr_warn("Unexpected buffer queue to a solid fill pipe\n"); + flags = (pipe->flags & MDP_SECURE_OVERLAY_SESSION); src_data = &pipe->back_buf; @@ -1270,7 +1280,6 @@ static int mdss_mdp_overlay_queue(struct msm_fb_data_type *mfd, if (IS_ERR_VALUE(ret)) { pr_err("src_data pmem error\n"); } - pipe->has_buf = 1; mdss_mdp_pipe_unmap(pipe); return ret; @@ -1377,18 +1386,18 @@ static int mdss_mdp_overlay_free_fb_pipe(struct msm_fb_data_type *mfd) static int mdss_mdp_overlay_get_fb_pipe(struct msm_fb_data_type *mfd, struct mdss_mdp_pipe **ppipe, - int mixer_mux) + int mixer_mux, + struct mdp_overlay *req_ov) { struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); struct mdss_mdp_pipe *pipe; + int ret; pipe = mdss_mdp_mixer_stage_pipe(mdp5_data->ctl, mixer_mux, MDSS_MDP_STAGE_BASE); + if (pipe == NULL) { - struct mdp_overlay req; - struct fb_info *fbi = mfd->fbi; struct mdss_mdp_mixer *mixer; - int ret, bpp; mixer = mdss_mdp_mixer_get(mdp5_data->ctl, MDSS_MDP_MIXER_MUX_LEFT); @@ -1397,49 +1406,73 @@ static int mdss_mdp_overlay_get_fb_pipe(struct msm_fb_data_type *mfd, return -ENODEV; } - memset(&req, 0, sizeof(req)); + if (req_ov == NULL) { + struct mdp_overlay req; + struct fb_info *fbi = mfd->fbi; + int bpp; + + memset(&req, 0, sizeof(req)); + + bpp = fbi->var.bits_per_pixel / 8; + req.id = MSMFB_NEW_REQUEST; + req.src.format = mfd->fb_imgType; + req.src.height = fbi->var.yres; + req.src.width = fbi->fix.line_length / bpp; + if (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT) { + if (req.src.width <= mixer->width) { + pr_warn("right fb pipe not needed\n"); + return -EINVAL; + } - bpp = fbi->var.bits_per_pixel / 8; - req.id = MSMFB_NEW_REQUEST; - req.src.format = mfd->fb_imgType; - req.src.height = fbi->var.yres; - req.src.width = fbi->fix.line_length / bpp; - if (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT) { - if (req.src.width <= mixer->width) { - pr_warn("right fb pipe not needed\n"); - return -EINVAL; + req.flags |= MDSS_MDP_RIGHT_MIXER; + req.src_rect.x = mixer->width; + req.src_rect.w = fbi->var.xres - mixer->width; + } else { + req.src_rect.x = 0; + req.src_rect.w = MIN(fbi->var.xres, + mixer->width); } - req.flags |= MDSS_MDP_RIGHT_MIXER; - req.src_rect.x = mixer->width; - req.src_rect.w = fbi->var.xres - mixer->width; - } else { - req.src_rect.x = 0; - req.src_rect.w = MIN(fbi->var.xres, mixer->width); - } - - req.src_rect.y = 0; - req.src_rect.h = req.src.height; - req.dst_rect.x = 0; - req.dst_rect.y = 0; - req.dst_rect.w = req.src_rect.w; - req.dst_rect.h = req.src_rect.h; - req.z_order = MDSS_MDP_STAGE_BASE; + req.src_rect.y = 0; + req.src_rect.h = req.src.height; + req.dst_rect.x = 0; + req.dst_rect.y = 0; + req.dst_rect.w = req.src_rect.w; + req.dst_rect.h = req.src_rect.h; + req.z_order = MDSS_MDP_STAGE_BASE; - pr_debug("allocating base pipe mux=%d\n", mixer_mux); + pr_debug("allocating base pipe mux=%d\n", mixer_mux); - ret = mdss_mdp_overlay_pipe_setup(mfd, &req, &pipe); - if (ret) - return ret; + ret = mdss_mdp_overlay_pipe_setup(mfd, &req, &pipe); + if (ret) + return ret; + } else { + if (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT) { + req_ov->id = MSMFB_NEW_REQUEST; + req_ov->flags |= MDSS_MDP_RIGHT_MIXER; + req_ov->src_rect.w = MIN(mixer->width, + req_ov->src_rect.w >> 1); + req_ov->dst_rect.w = req_ov->src_rect.w; + req_ov->src_rect.x = req_ov->src_rect.w; + req_ov->dst_rect.x = 0; + } - pr_debug("ctl=%d pnum=%d\n", mdp5_data->ctl->num, pipe->num); + ret = mdss_mdp_overlay_pipe_setup(mfd, req_ov, &pipe); + if (ret) + return ret; + } } + pr_debug("ctl=%d pnum=%d\n", mdp5_data->ctl->num, pipe->num); + *ppipe = pipe; return 0; } -static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd) +static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd, + struct mdp_overlay *req, + int image_size, + int *pipe_ndx) { struct mdss_mdp_data *buf; struct mdss_mdp_pipe *pipe; @@ -1487,8 +1520,8 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd) goto pan_display_error; } - - ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe, MDSS_MDP_MIXER_MUX_LEFT); + ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe, + MDSS_MDP_MIXER_MUX_LEFT, req); if (ret) { pr_err("unable to allocate base pipe\n"); goto pan_display_error; @@ -1498,12 +1531,14 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd) pr_err("unable to map base pipe\n"); goto pan_display_error; } + if (pipe_ndx) + pipe_ndx[0] = pipe->ndx; buf = &pipe->back_buf; if (is_mdss_iommu_attached()) { if (!mfd->iova) { pr_err("mfd iova is zero\n"); - goto pan_display_error; + goto attach_err; } buf->p[0].addr = mfd->iova; } else { @@ -1511,24 +1546,28 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd) } buf->p[0].addr += offset; - buf->p[0].len = fbi->fix.smem_len - offset; + if (image_size) + buf->p[0].len = image_size; + else + buf->p[0].len = fbi->fix.smem_len - offset; buf->num_planes = 1; - pipe->has_buf = 1; mdss_mdp_pipe_unmap(pipe); if (fbi->var.xres > MAX_MIXER_WIDTH || mfd->split_display) { ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe, - MDSS_MDP_MIXER_MUX_RIGHT); + MDSS_MDP_MIXER_MUX_RIGHT, req); if (ret) { pr_err("unable to allocate right base pipe\n"); - goto pan_display_error; + goto attach_err; } if (mdss_mdp_pipe_map(pipe)) { pr_err("unable to map right base pipe\n"); - goto pan_display_error; + goto attach_err; } + if (pipe_ndx) + pipe_ndx[1] = pipe->ndx; + pipe->back_buf = *buf; - pipe->has_buf = 1; mdss_mdp_pipe_unmap(pipe); } mutex_unlock(&mdp5_data->ov_lock); @@ -1539,6 +1578,12 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd) return; +attach_err: + mutex_unlock(&mdp5_data->ov_lock); + mdss_mdp_overlay_unset(mfd, pipe->ndx); + if (pipe_ndx) + pipe_ndx[0] = INVALID_PIPE_INDEX; + return; pan_display_error: mutex_unlock(&mdp5_data->ov_lock); } @@ -1798,6 +1843,19 @@ static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd, struct fb_image *img = &cursor->image; u32 blendcfg; int ret = 0; + u32 xres = mfd->fbi->var.xres; + u32 yres = mfd->fbi->var.yres; + u32 start_x = img->dx; + u32 start_y = img->dy; + u32 roi_x = 0; + u32 roi_y = 0; + int roi_w = 0; + int roi_h = 0; + int roi_size = 0; + + mixer = mdss_mdp_mixer_get(mdp5_data->ctl, MDSS_MDP_MIXER_MUX_DEFAULT); + if (!mixer) + return -ENODEV; if (!mfd->cursor_buf && (cursor->set & FB_CUR_SETIMAGE)) { mfd->cursor_buf = dma_alloc_coherent(NULL, MDSS_MDP_CURSOR_SIZE, @@ -1820,15 +1878,14 @@ static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd, ret); return ret; } - } - mixer = mdss_mdp_mixer_get(mdp5_data->ctl, MDSS_MDP_MIXER_MUX_DEFAULT); - if (!mixer) - return -ENODEV; + mixer->cursor_hotx = 0; + mixer->cursor_hoty = 0; + } if ((img->width > MDSS_MDP_CURSOR_WIDTH) || (img->height > MDSS_MDP_CURSOR_HEIGHT) || - (img->depth != 32)) + (img->depth != 32) || (start_x >= xres) || (start_y >= yres)) return -EINVAL; pr_debug("mixer=%d enable=%x set=%x\n", mixer->num, cursor->enable, @@ -1837,9 +1894,43 @@ static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd, mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); blendcfg = mdp_mixer_read(mixer, MDSS_MDP_REG_LM_CURSOR_BLEND_CONFIG); - if (cursor->set & FB_CUR_SETPOS) + if (cursor->set & FB_CUR_SETHOT) { + if ((cursor->hot.x < img->width) && + (cursor->hot.y < img->height)) { + mixer->cursor_hotx = cursor->hot.x; + mixer->cursor_hoty = cursor->hot.y; + /* Update cursor position */ + cursor->set |= FB_CUR_SETPOS; + } else { + pr_err("Invalid cursor hotspot coordinates\n"); + return -EINVAL; + } + } + + if (start_x > mixer->cursor_hotx) { + start_x -= mixer->cursor_hotx; + } else { + roi_x = mixer->cursor_hotx - start_x; + start_x = 0; + } + if (start_y > mixer->cursor_hoty) { + start_y -= mixer->cursor_hoty; + } else { + roi_y = mixer->cursor_hoty - start_y; + start_y = 0; + } + + roi_w = min(xres - start_x, img->width - roi_x); + roi_h = min(yres - start_y, img->height - roi_y); + roi_size = (roi_h << 16) | roi_w; + + if (cursor->set & FB_CUR_SETPOS) { + mdp_mixer_write(mixer, MDSS_MDP_REG_LM_CURSOR_XY, + (roi_y << 16) | roi_x); mdp_mixer_write(mixer, MDSS_MDP_REG_LM_CURSOR_START_XY, - (img->dy << 16) | img->dx); + (start_y << 16) | start_x); + mdp_mixer_write(mixer, MDSS_MDP_REG_LM_CURSOR_SIZE, roi_size); + } if (cursor->set & FB_CUR_SETIMAGE) { int calpha_en, transp_en, alpha, size; @@ -1877,7 +1968,7 @@ static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd, size = (img->height << 16) | img->width; mdp_mixer_write(mixer, MDSS_MDP_REG_LM_CURSOR_IMG_SIZE, size); - mdp_mixer_write(mixer, MDSS_MDP_REG_LM_CURSOR_SIZE, size); + mdp_mixer_write(mixer, MDSS_MDP_REG_LM_CURSOR_SIZE, roi_size); mdp_mixer_write(mixer, MDSS_MDP_REG_LM_CURSOR_STRIDE, img->width * 4); mdp_mixer_write(mixer, MDSS_MDP_REG_LM_CURSOR_BASE_ADDR, @@ -1910,6 +2001,9 @@ static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd, MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_HIGH1, ((img->bg_color & 0xff0000) >> 16)); } + + mixer->cursor_hotx = 0; + mixer->cursor_hoty = 0; } if (!cursor->enable != !(blendcfg & 0x1)) { @@ -2617,6 +2711,63 @@ error: return rc; } +static int mdss_mdp_overlay_splash_image(struct msm_fb_data_type *mfd, + int *pipe_ndx, int splash_event) +{ + struct mdp_overlay req; + int rc = 0; + struct fb_info *fbi = NULL; + int image_len = 0; + + if (!mfd || !mfd->fbi || !mfd->fbi->screen_base || !pipe_ndx) { + pr_err("Invalid input parameter\n"); + return -EINVAL; + } + + fbi = mfd->fbi; + image_len = SPLASH_IMAGE_WIDTH * SPLASH_IMAGE_HEIGHT * SPLASH_IMAGE_BPP; + + if (SPLASH_IMAGE_WIDTH > fbi->var.xres || + SPLASH_IMAGE_HEIGHT > fbi->var.yres || + SPLASH_IMAGE_BPP > fbi->var.bits_per_pixel / 8 || + image_len > fbi->fix.smem_len) { + pr_err("Invalid splash parameter configuration\n"); + return -EINVAL; + } + + if (splash_event == MDP_CREATE_SPLASH_OV) { + pipe_ndx[0] = INVALID_PIPE_INDEX; + pipe_ndx[1] = INVALID_PIPE_INDEX; + + memset(&req, 0, sizeof(struct mdp_overlay)); + req.src.width = req.dst_rect.w = req.src_rect.w = + SPLASH_IMAGE_WIDTH; + req.src.height = req.dst_rect.h = req.src_rect.h = + SPLASH_IMAGE_HEIGHT; + req.src.format = SPLASH_IMAGE_FORMAT; + req.id = MSMFB_NEW_REQUEST; + req.z_order = MDSS_MDP_STAGE_0; + req.is_fg = 1; + req.alpha = 0xff; + req.transp_mask = MDP_TRANSP_NOP; + req.dst_rect.x = + (fbi->var.xres >> 1) - (SPLASH_IMAGE_WIDTH >> 1); + req.dst_rect.y = + (fbi->var.yres >> 1) - (SPLASH_IMAGE_HEIGHT >> 1); + + memcpy(fbi->screen_base, splash_bgr888_image, image_len); + mdss_mdp_overlay_pan_display(mfd, &req, image_len, pipe_ndx); + + } else if (splash_event == MDP_REMOVE_SPLASH_OV) { + if (pipe_ndx[0] != INVALID_PIPE_INDEX) + mdss_mdp_overlay_unset(mfd, pipe_ndx[0]); + if (pipe_ndx[1] != INVALID_PIPE_INDEX) + mdss_mdp_overlay_unset(mfd, pipe_ndx[1]); + } + + return rc; +} + int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) { struct device *dev = mfd->fbi->dev; @@ -2634,6 +2785,7 @@ int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) mdp5_interface->panel_register_done = mdss_panel_register_done; mdp5_interface->kickoff_fnc = mdss_mdp_overlay_kickoff; mdp5_interface->get_sync_fnc = mdss_mdp_rotator_sync_pt_get; + mdp5_interface->splash_fnc = mdss_mdp_overlay_splash_image; mdp5_data = kmalloc(sizeof(struct mdss_overlay_private), GFP_KERNEL); if (!mdp5_data) { @@ -2728,7 +2880,7 @@ static __ref int mdss_mdp_overlay_splash_parse_dt(struct msm_fb_data_type *mfd) int len = 0, rc = 0; u32 offsets[2]; - of_find_property(pdev->dev.of_node, "qti,memblock-reserve", &len); + of_find_property(pdev->dev.of_node, "qcom,memblock-reserve", &len); if (len < 1) { pr_debug("mem reservation for splash screen fb not present\n"); @@ -2739,7 +2891,7 @@ static __ref int mdss_mdp_overlay_splash_parse_dt(struct msm_fb_data_type *mfd) len = len/sizeof(u32); rc = of_property_read_u32_array(pdev->dev.of_node, - "qti,memblock-reserve", offsets, len); + "qcom,memblock-reserve", offsets, len); if (rc) { pr_debug("Error reading mem reserve settings for fb\n"); goto error; diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c index 74c7df38cbda..4cbd65ba250e 100644 --- a/drivers/video/msm/mdss/mdss_mdp_pipe.c +++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c @@ -166,7 +166,7 @@ int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe) struct mdss_mdp_plane_sizes ps; int i; int rc = 0, rot_mode = 0; - u32 nlines, format; + u32 nlines, format, seg_w; u16 width; width = pipe->src.w >> pipe->horz_deci; @@ -178,19 +178,17 @@ int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe) return rc; /* * Override fetch strides with SMP buffer size for both the - * planes + * planes. BWC line buffer needs to be divided into 16 + * segments and every segment is aligned to format + * specific RAU size */ + seg_w = DIV_ROUND_UP(pipe->src.w, 16); if (pipe->src_fmt->fetch_planes == MDSS_MDP_PLANE_INTERLEAVED) { - /* - * BWC line buffer needs to be divided into 16 - * segments and every segment is aligned to format - * specific RAU size - */ - ps.ystride[0] = ALIGN(pipe->src.w / 16 , 32) * 16 * - ps.rau_h[0] * pipe->src_fmt->bpp; + ps.ystride[0] = ALIGN(seg_w, 32) * 16 * ps.rau_h[0] * + pipe->src_fmt->bpp; ps.ystride[1] = 0; } else { - u32 bwc_width = ALIGN(pipe->src.w / 16, 64) * 16; + u32 bwc_width = ALIGN(seg_w, 64) * 16; ps.ystride[0] = bwc_width * ps.rau_h[0]; ps.ystride[1] = bwc_width * ps.rau_h[1]; /* @@ -1008,7 +1006,7 @@ int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe, (pipe->mixer->type == MDSS_MDP_MIXER_TYPE_WRITEBACK) && (ctl->mdata->mixer_switched)) || ctl->roi_changed; - if (src_data == NULL || !pipe->has_buf) { + if (src_data == NULL || (pipe->flags & MDP_SOLID_FILL)) { pipe->params_changed = 0; mdss_mdp_pipe_solidfill_setup(pipe); goto update_nobuf; diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c index cd65bbe33c61..5b5e2de99de0 100644 --- a/drivers/video/msm/mdss/mdss_mdp_pp.c +++ b/drivers/video/msm/mdss/mdss_mdp_pp.c @@ -214,6 +214,10 @@ static u32 igc_limited[IGC_LUT_ENTRIES] = { #define PP_AD_BAD_HW_NUM 255 +#define MDSS_SIDE_NONE 0 +#define MDSS_SIDE_LEFT 1 +#define MDSS_SIDE_RIGHT 2 + #define PP_AD_STATE_INIT 0x2 #define PP_AD_STATE_CFG 0x4 #define PP_AD_STATE_DATA 0x8 @@ -302,8 +306,8 @@ struct mdss_pp_res_type { struct mdp_gamut_cfg_data gamut_disp_cfg[MDSS_BLOCK_DISP_NUM]; uint16_t gamut_tbl[MDSS_BLOCK_DISP_NUM][GAMUT_TOTAL_TABLE_SIZE]; u32 hist_data[MDSS_BLOCK_DISP_NUM][HIST_V_SIZE]; - /* physical info */ struct pp_sts_type pp_disp_sts[MDSS_BLOCK_DISP_NUM]; + /* physical info */ struct pp_hist_col_info dspp_hist[MDSS_MDP_MAX_DSPP]; }; @@ -313,8 +317,7 @@ static struct mdss_pp_res_type *mdss_pp_res; static u32 pp_hist_read(char __iomem *v_addr, struct pp_hist_col_info *hist_info); static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix); -static int pp_hist_disable(struct pp_hist_col_info *hist_info, - u32 done_bit, char __iomem *ctl_base); +static int pp_hist_disable(struct pp_hist_col_info *hist_info); static void pp_update_pcc_regs(char __iomem *addr, struct mdp_pcc_cfg_data *cfg_ptr); static void pp_update_igc_lut(struct mdp_igc_lut_data *cfg, @@ -350,8 +353,9 @@ static void pp_enhist_config(unsigned long flags, char __iomem *addr, static void pp_dither_config(char __iomem *addr, struct pp_sts_type *pp_sts, struct mdp_dither_cfg_data *dither_cfg); -static void pp_dspp_opmode_config(struct pp_sts_type *pp_sts, u32 *opmode, - int mdp_rev); +static void pp_dspp_opmode_config(struct mdss_mdp_ctl *ctl, u32 num, + struct pp_sts_type *pp_sts, int mdp_rev, + u32 *opmode); static void pp_sharp_config(char __iomem *addr, struct pp_sts_type *pp_sts, struct mdp_sharp_cfg *sharp_config); @@ -385,11 +389,15 @@ static void pp_ad_init_write(struct mdss_mdp_ad *ad_hw, struct mdss_ad_info *ad, struct mdss_mdp_ctl *ctl); static void pp_ad_input_write(struct mdss_mdp_ad *ad_hw, struct mdss_ad_info *ad); -static void pp_ad_bypass_config(struct mdss_ad_info *ad, u32 *opmode); static int pp_ad_setup_hw_nums(struct msm_fb_data_type *mfd, struct mdss_ad_info *ad); +static void pp_ad_bypass_config(struct mdss_ad_info *ad, + struct mdss_mdp_ctl *ctl, u32 num, u32 *opmode); static int mdss_mdp_ad_setup(struct msm_fb_data_type *mfd); static void pp_ad_cfg_lut(char __iomem *addr, u32 *data); +static int pp_num_to_side(struct mdss_mdp_ctl *ctl, u32 num); +static inline bool pp_sts_is_enabled(u32 sts, int side); +static inline void pp_sts_set_split_bits(u32 *sts, u32 bits); static u32 last_sts, last_state; @@ -528,6 +536,7 @@ static void pp_gamut_config(struct mdp_gamut_cfg_data *gamut_cfg, pp_sts->gamut_sts &= ~PP_STS_ENABLE; else if (gamut_cfg->flags & MDP_PP_OPS_ENABLE) pp_sts->gamut_sts |= PP_STS_ENABLE; + pp_sts_set_split_bits(&pp_sts->gamut_sts, gamut_cfg->flags); } static void pp_pa_config(unsigned long flags, char __iomem *addr, @@ -696,6 +705,7 @@ static void pp_update_pa_v2_sts(struct pp_sts_type *pp_sts, if (pa_v2_config->flags & MDP_PP_PA_SIX_ZONE_VAL_MASK) pp_sts->pa_sts |= PP_STS_PA_SIX_ZONE_VAL_MASK; + pp_sts_set_split_bits(&pp_sts->pa_sts, pa_v2_config->flags); } static void pp_pcc_config(unsigned long flags, char __iomem *addr, @@ -710,6 +720,7 @@ static void pp_pcc_config(unsigned long flags, char __iomem *addr, pp_sts->pcc_sts &= ~PP_STS_ENABLE; else if (pcc_config->ops & MDP_PP_OPS_ENABLE) pp_sts->pcc_sts |= PP_STS_ENABLE; + pp_sts_set_split_bits(&pp_sts->pcc_sts, pcc_config->ops); } } @@ -737,6 +748,7 @@ static void pp_igc_config(unsigned long flags, char __iomem *addr, pp_sts->igc_sts &= ~PP_STS_ENABLE; else if (igc_config->ops & MDP_PP_OPS_ENABLE) pp_sts->igc_sts |= PP_STS_ENABLE; + pp_sts_set_split_bits(&pp_sts->igc_sts, igc_config->ops); } } @@ -1086,7 +1098,7 @@ static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe) /*program pixel extn values for the SSPP*/ mdss_mdp_pipe_program_pixel_extn(pipe); - } else { + } else if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) { writel_relaxed(phasex_step, pipe->base + MDSS_MDP_REG_SCALE_PHASE_STEP_X); writel_relaxed(phasey_step, pipe->base + @@ -1095,6 +1107,11 @@ static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe) MDSS_MDP_REG_SCALE_INIT_PHASE_X); writel_relaxed(init_phasey, pipe->base + MDSS_MDP_REG_SCALE_INIT_PHASE_Y); + } else { + writel_relaxed(phasex_step, pipe->base + + MDSS_MDP_REG_SCALE_PHASE_STEP_X); + writel_relaxed(phasey_step, pipe->base + + MDSS_MDP_REG_SCALE_PHASE_STEP_Y); } writel_relaxed(scale_config, pipe->base + @@ -1121,17 +1138,12 @@ int mdss_mdp_pipe_pp_setup(struct mdss_mdp_pipe *pipe, u32 *op) void mdss_mdp_pipe_sspp_term(struct mdss_mdp_pipe *pipe) { - u32 done_bit; struct pp_hist_col_info *hist_info; - char __iomem *ctl_base; if (pipe) { if (pipe->pp_res.hist.col_en) { - done_bit = 3 << (pipe->num * 4); hist_info = &pipe->pp_res.hist; - ctl_base = pipe->base + - MDSS_MDP_REG_VIG_HIST_CTL_BASE; - pp_hist_disable(hist_info, done_bit, ctl_base); + pp_hist_disable(hist_info); } memset(&pipe->pp_cfg, 0, sizeof(struct mdp_overlay_pp_params)); memset(&pipe->pp_res, 0, sizeof(struct mdss_pipe_pp_res)); @@ -1271,19 +1283,21 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix) int ret = -EINVAL; char __iomem *base; u32 op_flags, kick_base, col_state; - struct mdss_data_type *mdata; struct mdss_mdp_pipe *pipe; struct pp_hist_col_info *hist_info; unsigned long flag; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + bool is_hist_v1 = !(mdata->mdp_rev >= MDSS_MDP_HW_REV_103); if (mix && (PP_LOCAT(block) == MDSS_PP_DSPP_CFG)) { /* HIST_EN & AUTO_CLEAR */ - op_flags = BIT(16) | BIT(17); + op_flags = BIT(16); + if (is_hist_v1) + op_flags |= BIT(17); hist_info = &mdss_pp_res->dspp_hist[mix->num]; base = mdss_mdp_get_dspp_addr_off(PP_BLOCK(block)); kick_base = MDSS_MDP_REG_DSPP_HIST_CTL_BASE; - } else if (PP_LOCAT(block) == MDSS_PP_SSPP_CFG) { - mdata = mdss_mdp_get_mdata(); + } else if (PP_LOCAT(block) == MDSS_PP_SSPP_CFG && is_hist_v1) { pipe = mdss_mdp_pipe_get(mdata, BIT(PP_BLOCK(block))); if (IS_ERR_OR_NULL(pipe)) { pr_debug("pipe DNE (%d)", (u32) BIT(PP_BLOCK(block))); @@ -1297,7 +1311,6 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix) kick_base = MDSS_MDP_REG_VIG_HIST_CTL_BASE; mdss_mdp_pipe_unmap(pipe); } else { - pr_warn("invalid histogram location (%d)", block); goto error; } @@ -1306,12 +1319,10 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix) mutex_lock(&hist_info->hist_mutex); spin_lock_irqsave(&hist_info->hist_lock, flag); col_state = hist_info->col_state; - if (hist_info->is_kick_ready && - ((col_state == HIST_IDLE) || - ((false == hist_info->read_request) && - col_state == HIST_READY))) { + if (col_state == HIST_IDLE) { /* Kick off collection */ - writel_relaxed(1, base + kick_base); + if (is_hist_v1) + writel_relaxed(1, base + kick_base); hist_info->col_state = HIST_START; } spin_unlock_irqrestore(&hist_info->hist_lock, flag); @@ -1348,12 +1359,20 @@ static void pp_dither_config(char __iomem *addr, pp_sts->dither_sts &= ~PP_STS_ENABLE; else if (dither_cfg->flags & MDP_PP_OPS_ENABLE) pp_sts->dither_sts |= PP_STS_ENABLE; + pp_sts_set_split_bits(&pp_sts->dither_sts, dither_cfg->flags); } -static void pp_dspp_opmode_config(struct pp_sts_type *pp_sts, u32 *opmode, - int mdp_rev) +static void pp_dspp_opmode_config(struct mdss_mdp_ctl *ctl, u32 num, + struct pp_sts_type *pp_sts, int mdp_rev, + u32 *opmode) { - if (pp_sts->pa_sts & PP_STS_ENABLE) + int side; + side = pp_num_to_side(ctl, num); + + if (side < 0) + return; + + if (pp_sts_is_enabled(pp_sts->pa_sts, side)) *opmode |= MDSS_MDP_DSPP_OP_PA_EN; /* PA_EN */ if (mdp_rev >= MDSS_MDP_HW_REV_103) { if (pp_sts->pa_sts & PP_STS_PA_HUE_MASK) @@ -1381,10 +1400,10 @@ static void pp_dspp_opmode_config(struct pp_sts_type *pp_sts, u32 *opmode, if (pp_sts->pa_sts & PP_STS_PA_SIX_ZONE_VAL_MASK) *opmode |= MDSS_MDP_DSPP_OP_PA_SIX_ZONE_VAL_MASK; } - if (pp_sts->pcc_sts & PP_STS_ENABLE) + if (pp_sts_is_enabled(pp_sts->pcc_sts, side)) *opmode |= MDSS_MDP_DSPP_OP_PCC_EN; /* PCC_EN */ - if (pp_sts->igc_sts & PP_STS_ENABLE) { + if (pp_sts_is_enabled(pp_sts->igc_sts, side)) { *opmode |= MDSS_MDP_DSPP_OP_IGC_LUT_EN | /* IGC_LUT_EN */ (pp_sts->igc_tbl_idx << 1); } @@ -1392,14 +1411,14 @@ static void pp_dspp_opmode_config(struct pp_sts_type *pp_sts, u32 *opmode, *opmode |= MDSS_MDP_DSPP_OP_HIST_LUTV_EN | /* HIST_LUT_EN */ MDSS_MDP_DSPP_OP_PA_EN; /* PA_EN */ } - if (pp_sts->dither_sts & PP_STS_ENABLE) + if (pp_sts_is_enabled(pp_sts->dither_sts, side)) *opmode |= MDSS_MDP_DSPP_OP_DST_DITHER_EN; /* DITHER_EN */ - if (pp_sts->gamut_sts & PP_STS_ENABLE) { + if (pp_sts_is_enabled(pp_sts->gamut_sts, side)) { *opmode |= MDSS_MDP_DSPP_OP_GAMUT_EN; /* GAMUT_EN */ if (pp_sts->gamut_sts & PP_STS_GAMUT_FIRST) *opmode |= MDSS_MDP_DSPP_OP_GAMUT_PCC_ORDER; } - if (pp_sts->pgc_sts & PP_STS_ENABLE) + if (pp_sts_is_enabled(pp_sts->pgc_sts, side)) *opmode |= MDSS_MDP_DSPP_OP_ARGC_LUT_EN; } @@ -1505,8 +1524,12 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) pp_sts->pgc_sts &= ~PP_STS_ENABLE; else if (pgc_config->flags & MDP_PP_OPS_ENABLE) pp_sts->pgc_sts |= PP_STS_ENABLE; + pp_sts_set_split_bits(&pp_sts->pgc_sts, pgc_config->flags); } + pp_dspp_opmode_config(ctl, dspp_num, pp_sts, mdata->mdp_rev, &opmode); + +flush_exit: if (ad_hw) { mutex_lock(&ad->lock); ad_flags = ad->reg_sts; @@ -1516,13 +1539,11 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) pp_ad_init_write(ad_hw, ad, ctl); if (ad_flags & PP_AD_STS_DIRTY_CFG) pp_ad_cfg_write(ad_hw, ad); - pp_ad_bypass_config(ad, &ad_bypass); + pp_ad_bypass_config(ad, ctl, ad_hw->num, &ad_bypass); writel_relaxed(ad_bypass, ad_hw->base); mutex_unlock(&ad->lock); } - pp_dspp_opmode_config(pp_sts, &opmode, mdata->mdp_rev); -flush_exit: writel_relaxed(opmode, base + MDSS_MDP_REG_DSPP_OP_MODE); if (dspp_num == MDSS_MDP_DSPP3) @@ -1868,6 +1889,12 @@ int mdss_mdp_pa_v2_config(struct mdp_pa_v2_cfg_data *config, (config->block >= MDP_BLOCK_MAX)) return -EINVAL; + if ((config->pa_v2_data.flags & MDSS_PP_SPLIT_MASK) == + MDSS_PP_SPLIT_MASK) { + pr_warn("Can't set both split bits\n"); + return -EINVAL; + } + mutex_lock(&mdss_pp_mutex); disp_num = config->block - MDP_LOGICAL_BLOCK_DISP_0; @@ -2157,6 +2184,11 @@ int mdss_mdp_pcc_config(struct mdp_pcc_cfg_data *config, (config->block >= MDP_BLOCK_MAX)) return -EINVAL; + if ((config->ops & MDSS_PP_SPLIT_MASK) == MDSS_PP_SPLIT_MASK) { + pr_warn("Can't set both split bits\n"); + return -EINVAL; + } + mutex_lock(&mdss_pp_mutex); disp_num = config->block - MDP_LOGICAL_BLOCK_DISP_0; @@ -2276,6 +2308,11 @@ int mdss_mdp_igc_lut_config(struct mdp_igc_lut_data *config, if (config->len != IGC_LUT_ENTRIES) return -EINVAL; + if ((config->ops & MDSS_PP_SPLIT_MASK) == MDSS_PP_SPLIT_MASK) { + pr_warn("Can't set both split bits\n"); + return -EINVAL; + } + mutex_lock(&mdss_pp_mutex); disp_num = config->block - MDP_LOGICAL_BLOCK_DISP_0; @@ -2475,6 +2512,11 @@ int mdss_mdp_argc_config(struct mdp_pgc_lut_data *config, (PP_BLOCK(config->block) >= MDP_BLOCK_MAX)) return -EINVAL; + if ((config->flags & MDSS_PP_SPLIT_MASK) == MDSS_PP_SPLIT_MASK) { + pr_warn("Can't set both split bits\n"); + return -EINVAL; + } + mutex_lock(&mdss_pp_mutex); disp_num = PP_BLOCK(config->block) - MDP_LOGICAL_BLOCK_DISP_0; @@ -2646,6 +2688,11 @@ int mdss_mdp_dither_config(struct mdp_dither_cfg_data *config, if (config->flags & MDP_PP_OPS_READ) return -ENOTSUPP; + if ((config->flags & MDSS_PP_SPLIT_MASK) == MDSS_PP_SPLIT_MASK) { + pr_warn("Can't set both split bits\n"); + return -EINVAL; + } + mutex_lock(&mdss_pp_mutex); disp_num = config->block - MDP_LOGICAL_BLOCK_DISP_0; mdss_pp_res->dither_disp_cfg[disp_num] = *config; @@ -2696,6 +2743,11 @@ int mdss_mdp_gamut_config(struct mdp_gamut_cfg_data *config, if (pp_gm_has_invalid_lut_size(config)) return -EINVAL; + if ((config->flags & MDSS_PP_SPLIT_MASK) == MDSS_PP_SPLIT_MASK) { + pr_warn("Can't set both split bits\n"); + return -EINVAL; + } + mutex_lock(&mdss_pp_mutex); disp_num = config->block - MDP_LOGICAL_BLOCK_DISP_0; @@ -2837,18 +2889,19 @@ static u32 pp_hist_read(char __iomem *v_addr, /* Assumes that relevant clocks are enabled */ static int pp_hist_enable(struct pp_hist_col_info *hist_info, - struct mdp_histogram_start_req *req, - u32 shift_bit, char __iomem *ctl_base) + struct mdp_histogram_start_req *req) { unsigned long flag; int ret = 0; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + bool is_hist_v2 = mdata->mdp_rev >= MDSS_MDP_HW_REV_103; + u32 intr_mask = is_hist_v2 ? 1 : 3; mutex_lock(&hist_info->hist_mutex); /* check if it is idle */ if (hist_info->col_en) { pr_info("%s Hist collection has already been enabled %d", - __func__, (u32) ctl_base); + __func__, (u32) hist_info->base); ret = -EINVAL; goto exit; } @@ -2858,15 +2911,23 @@ static int pp_hist_enable(struct pp_hist_col_info *hist_info, hist_info->hist_cnt_sent = 0; hist_info->hist_cnt_time = 0; spin_lock_irqsave(&hist_info->hist_lock, flag); - hist_info->read_request = false; - hist_info->col_state = HIST_RESET; + hist_info->read_request = 0; + if (is_hist_v2) + hist_info->col_state = HIST_IDLE; + else + hist_info->col_state = HIST_RESET; hist_info->col_en = true; spin_unlock_irqrestore(&hist_info->hist_lock, flag); - hist_info->is_kick_ready = true; - mdss_mdp_hist_intr_req(&mdata->hist_intr, 3 << shift_bit, true); - writel_relaxed(req->frame_cnt, ctl_base + 8); - /* Kick out reset start */ - writel_relaxed(1, ctl_base + 4); + mdss_mdp_hist_intr_req(&mdata->hist_intr, + intr_mask << hist_info->intr_shift, true); + if (is_hist_v2) { + /* if hist v2, make sure HW is unlocked */ + writel_relaxed(0, hist_info->base); + } else { + writel_relaxed(req->frame_cnt, hist_info->base + 8); + /* Kick out reset start */ + writel_relaxed(1, hist_info->base + 4); + } exit: mutex_unlock(&hist_info->hist_mutex); return ret; @@ -2875,8 +2936,6 @@ exit: #define MDSS_MAX_HIST_BIN_SIZE 16777215 int mdss_mdp_hist_start(struct mdp_histogram_start_req *req) { - u32 done_shift_bit; - char __iomem *ctl_base; struct pp_hist_col_info *hist_info; int i, ret = 0; u32 disp_num, dspp_num = 0; @@ -2884,6 +2943,7 @@ int mdss_mdp_hist_start(struct mdp_histogram_start_req *req) u32 frame_size; struct mdss_mdp_pipe *pipe; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + bool is_hist_v2 = mdata->mdp_rev >= MDSS_MDP_HW_REV_103; if (!mdss_is_ready()) return -EPROBE_DEFER; @@ -2917,6 +2977,12 @@ int mdss_mdp_hist_start(struct mdp_histogram_start_req *req) ret = -EINVAL; goto hist_exit; } + if (is_hist_v2 && (PP_LOCAT(req->block) == MDSS_PP_SSPP_CFG)) { + pr_warn("No histogram on SSPP\n"); + ret = -EINVAL; + goto hist_exit; + } + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); if (PP_LOCAT(req->block) == MDSS_PP_SSPP_CFG) { @@ -2938,23 +3004,21 @@ int mdss_mdp_hist_start(struct mdp_histogram_start_req *req) pr_warn("Invalid Hist pipe (%d)", i); goto hist_stop_clk; } - done_shift_bit = (pipe->num * 4); hist_info = &pipe->pp_res.hist; - ctl_base = pipe->base + + hist_info->intr_shift = (pipe->num * 4); + hist_info->base = pipe->base + MDSS_MDP_REG_VIG_HIST_CTL_BASE; - ret = pp_hist_enable(hist_info, req, - done_shift_bit, ctl_base); + ret = pp_hist_enable(hist_info, req); mdss_mdp_pipe_unmap(pipe); } } else if (PP_LOCAT(req->block) == MDSS_PP_DSPP_CFG) { for (i = 0; i < mixer_cnt; i++) { dspp_num = mixer_id[i]; - done_shift_bit = (dspp_num * 4) + 12; hist_info = &mdss_pp_res->dspp_hist[dspp_num]; - ctl_base = mdss_mdp_get_dspp_addr_off(dspp_num) + + hist_info->intr_shift = (dspp_num * 4) + 12; + hist_info->base = mdss_mdp_get_dspp_addr_off(dspp_num) + MDSS_MDP_REG_DSPP_HIST_CTL_BASE; - ret = pp_hist_enable(hist_info, req, - done_shift_bit, ctl_base); + ret = pp_hist_enable(hist_info, req); mdss_pp_res->pp_disp_flags[disp_num] |= PP_FLAGS_DIRTY_HIST_COL; } @@ -2965,16 +3029,18 @@ hist_exit: return ret; } -static int pp_hist_disable(struct pp_hist_col_info *hist_info, - u32 done_bit, char __iomem *ctl_base) +static int pp_hist_disable(struct pp_hist_col_info *hist_info) { int ret = 0; unsigned long flag; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + bool is_hist_v2 = mdata->mdp_rev >= MDSS_MDP_HW_REV_103; + u32 intr_mask = is_hist_v2 ? 1 : 3; mutex_lock(&hist_info->hist_mutex); if (hist_info->col_en == false) { - pr_debug("Histogram already disabled (%d)", (u32) ctl_base); + pr_debug("Histogram already disabled (%d)", + (u32) hist_info->base); ret = -EINVAL; goto exit; } @@ -2983,9 +3049,13 @@ static int pp_hist_disable(struct pp_hist_col_info *hist_info, hist_info->col_en = false; hist_info->col_state = HIST_UNKNOWN; spin_unlock_irqrestore(&hist_info->hist_lock, flag); - hist_info->is_kick_ready = false; - mdss_mdp_hist_intr_req(&mdata->hist_intr, done_bit, false); - writel_relaxed(BIT(1), ctl_base);/* cancel */ + mdss_mdp_hist_intr_req(&mdata->hist_intr, + intr_mask << hist_info->intr_shift, false); + /* if hist v2, make sure HW is unlocked */ + if (is_hist_v2) + writel_relaxed(0, hist_info->base); + else + writel_relaxed(BIT(1), hist_info->base);/* cancel */ ret = 0; exit: mutex_unlock(&hist_info->hist_mutex); @@ -2995,8 +3065,7 @@ exit: int mdss_mdp_hist_stop(u32 block) { int i, ret = 0; - char __iomem *ctl_base; - u32 dspp_num, disp_num, done_bit; + u32 dspp_num, disp_num; struct pp_hist_col_info *hist_info; u32 mixer_cnt, mixer_id[MDSS_MDP_INTF_MAX_LAYERMIXER]; struct mdss_mdp_pipe *pipe; @@ -3038,12 +3107,8 @@ int mdss_mdp_hist_stop(u32 block) pr_warn("Invalid Hist pipe (%d)", i); continue; } - done_bit = 3 << (pipe->num * 4); hist_info = &pipe->pp_res.hist; - ctl_base = pipe->base + - MDSS_MDP_REG_VIG_HIST_CTL_BASE; - ret = pp_hist_disable(hist_info, done_bit, - ctl_base); + ret = pp_hist_disable(hist_info); mdss_mdp_pipe_unmap(pipe); if (ret) goto hist_stop_clk; @@ -3051,12 +3116,8 @@ int mdss_mdp_hist_stop(u32 block) } else if (PP_LOCAT(block) == MDSS_PP_DSPP_CFG) { for (i = 0; i < mixer_cnt; i++) { dspp_num = mixer_id[i]; - done_bit = 3 << ((dspp_num * 4) + 12); hist_info = &mdss_pp_res->dspp_hist[dspp_num]; - ctl_base = mdss_mdp_get_dspp_addr_off(dspp_num) + - MDSS_MDP_REG_DSPP_HIST_CTL_BASE; - ret = pp_hist_disable(hist_info, done_bit, - ctl_base); + ret = pp_hist_disable(hist_info); if (ret) goto hist_stop_clk; mdss_pp_res->pp_disp_flags[disp_num] |= @@ -3223,6 +3284,8 @@ static int pp_hist_collect(struct mdp_histogram_data *hist, unsigned long flag; struct mdss_pipe_pp_res *res; struct mdss_mdp_pipe *pipe; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + bool is_hist_v2 = mdata->mdp_rev >= MDSS_MDP_HW_REV_103; mutex_lock(&hist_info->hist_mutex); if ((hist_info->col_en == 0) || @@ -3233,7 +3296,6 @@ static int pp_hist_collect(struct mdp_histogram_data *hist, spin_lock_irqsave(&hist_info->hist_lock, flag); /* wait for hist done if cache has no data */ if (hist_info->col_state != HIST_READY) { - hist_info->read_request = true; spin_unlock_irqrestore(&hist_info->hist_lock, flag); timeout = HIST_WAIT_TIMEOUT(hist_info->frame_cnt); mutex_unlock(&hist_info->hist_mutex); @@ -3273,9 +3335,11 @@ static int pp_hist_collect(struct mdp_histogram_data *hist, } if (hist_info->col_state != HIST_READY) { ret = -ENODATA; + spin_lock_irqsave(&hist_info->hist_lock, flag); + hist_info->col_state = HIST_READY; + spin_unlock_irqrestore(&hist_info->hist_lock, flag); pr_debug("%s: state is not ready: %d", __func__, hist_info->col_state); - goto hist_collect_exit; } } else { spin_unlock_irqrestore(&hist_info->hist_lock, flag); @@ -3286,11 +3350,12 @@ static int pp_hist_collect(struct mdp_histogram_data *hist, v_base = ctl_base + 0x1C; mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); sum = pp_hist_read(v_base, hist_info); + /* if hist_v2 unlock HW when done reading */ + if (is_hist_v2) + writel_relaxed(0, ctl_base); mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); spin_lock_irqsave(&hist_info->hist_lock, flag); - if (!expect_sum || sum == expect_sum) - hist_info->read_request = false; - else + if (expect_sum && sum != expect_sum) ret = -ENODATA; hist_info->col_state = HIST_IDLE; } @@ -3302,8 +3367,9 @@ hist_collect_exit: int mdss_mdp_hist_collect(struct mdp_histogram_data *hist) { - int i, j, off, ret = 0; + int i, j, off, ret = 0, temp_ret = 0; struct pp_hist_col_info *hist_info; + struct pp_hist_col_info *hists[MDSS_MDP_INTF_MAX_LAYERMIXER]; u32 dspp_num, disp_num; char __iomem *ctl_base; u32 hist_cnt, mixer_id[MDSS_MDP_INTF_MAX_LAYERMIXER]; @@ -3314,6 +3380,7 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist) u32 exp_sum = 0; struct mdss_mdp_pipe *pipe; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + unsigned long flag; if ((PP_BLOCK(hist->block) < MDP_LOGICAL_BLOCK_DISP_0) || (PP_BLOCK(hist->block) >= MDP_BLOCK_MAX)) @@ -3334,20 +3401,41 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist) ret = -EPERM; goto hist_collect_exit; } + if (PP_LOCAT(hist->block) == MDSS_PP_DSPP_CFG) { - hist_info = &mdss_pp_res->dspp_hist[disp_num]; for (i = 0; i < hist_cnt; i++) { dspp_num = mixer_id[i]; - hist_info = &mdss_pp_res->dspp_hist[dspp_num]; + hists[i] = &mdss_pp_res->dspp_hist[dspp_num]; + } + for (i = 0; i < hist_cnt; i++) { + spin_lock_irqsave(&hists[i]->hist_lock, flag); + /* mark that collect is ready to handle completions */ + hists[i]->read_request = 1; + spin_unlock_irqrestore(&hists[i]->hist_lock, flag); + } + for (i = 0; i < hist_cnt; i++) { + dspp_num = mixer_id[i]; ctl_base = mdss_mdp_get_dspp_addr_off(dspp_num) + MDSS_MDP_REG_DSPP_HIST_CTL_BASE; exp_sum = (mdata->mixer_intf[dspp_num].width * mdata->mixer_intf[dspp_num].height); - ret = pp_hist_collect(hist, hist_info, ctl_base, - exp_sum); if (ret) - goto hist_collect_exit; + temp_ret = ret; + ret = pp_hist_collect(hist, hists[i], ctl_base, + exp_sum); } + for (i = 0; i < hist_cnt; i++) { + /* reset read requests and re-intialize completions */ + spin_lock_irqsave(&hists[i]->hist_lock, flag); + hists[i]->read_request = 0; + INIT_COMPLETION(hists[i]->comp); + spin_unlock_irqrestore(&hists[i]->hist_lock, flag); + } + if (ret || temp_ret) { + ret = ret ? ret : temp_ret; + goto hist_collect_exit; + } + if (hist->bin_cnt != HIST_V_SIZE) { pr_err("User not expecting size %d output", HIST_V_SIZE); @@ -3363,19 +3451,19 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist) } memset(hist_concat, 0, HIST_V_SIZE * sizeof(u32)); for (i = 0; i < hist_cnt; i++) { - dspp_num = mixer_id[i]; - hist_info = &mdss_pp_res->dspp_hist[dspp_num]; - mutex_lock(&hist_info->hist_mutex); + mutex_lock(&hists[i]->hist_mutex); for (j = 0; j < HIST_V_SIZE; j++) - hist_concat[j] += hist_info->data[j]; - mutex_unlock(&hist_info->hist_mutex); + hist_concat[j] += hists[i]->data[j]; + mutex_unlock(&hists[i]->hist_mutex); } hist_data_addr = hist_concat; } else { - hist_data_addr = hist_info->data; + hist_data_addr = hists[0]->data; } - hist_info = &mdss_pp_res->dspp_hist[disp_num]; - hist_info->hist_cnt_sent++; + + for (i = 0; i < hist_cnt; i++) + hists[i]->hist_cnt_sent++; + } else if (PP_LOCAT(hist->block) == MDSS_PP_SSPP_CFG) { hist_cnt = MDSS_PP_ARG_MASK & hist->block; @@ -3411,14 +3499,50 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist) continue; } hist_info = &pipe->pp_res.hist; + spin_lock_irqsave(&hist_info->hist_lock, flag); + hist_info->read_request = 1; + spin_unlock_irqrestore(&hist_info->hist_lock, flag); + } + for (i = pipe_num; i < MDSS_PP_ARG_NUM; i++) { + if (!PP_ARG(i, hist->block)) + continue; + pipe_cnt++; + pipe = mdss_mdp_pipe_get(mdata, BIT(i)); + if (IS_ERR_OR_NULL(pipe) || + pipe->num > MDSS_MDP_SSPP_VIG2) { + pr_warn("Invalid Hist pipe (%d)", i); + continue; + } + hist_info = &pipe->pp_res.hist; ctl_base = pipe->base + MDSS_MDP_REG_VIG_HIST_CTL_BASE; + if (ret) + temp_ret = ret; ret = pp_hist_collect(hist, hist_info, ctl_base, exp_sum); mdss_mdp_pipe_unmap(pipe); - if (ret) - goto hist_collect_exit; } + for (i = pipe_num; i < MDSS_PP_ARG_NUM; i++) { + if (!PP_ARG(i, hist->block)) + continue; + pipe_cnt++; + pipe = mdss_mdp_pipe_get(mdata, BIT(i)); + if (IS_ERR_OR_NULL(pipe) || + pipe->num > MDSS_MDP_SSPP_VIG2) { + pr_warn("Invalid Hist pipe (%d)", i); + continue; + } + hist_info = &pipe->pp_res.hist; + spin_lock_irqsave(&hist_info->hist_lock, flag); + hist_info->read_request = 0; + INIT_COMPLETION(hist_info->comp); + spin_unlock_irqrestore(&hist_info->hist_lock, flag); + } + if (ret || temp_ret) { + ret = ret ? ret : temp_ret; + goto hist_collect_exit; + } + if (pipe_cnt != 0 && (hist->bin_cnt != (HIST_V_SIZE * pipe_cnt))) { pr_err("User not expecting size %d output", @@ -3472,6 +3596,8 @@ void mdss_mdp_hist_intr_done(u32 isr) struct pp_hist_col_info *hist_info = NULL; struct mdss_mdp_pipe *pipe; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + bool is_hist_v2 = mdata->mdp_rev >= MDSS_MDP_HW_REV_103; + bool need_complete = false; isr &= 0x333333; while (isr != 0) { if (isr & 0xFFF000) { @@ -3513,9 +3639,18 @@ void mdss_mdp_hist_intr_done(u32 isr) /* Histogram Done Interrupt */ if (hist_info && (isr_blk & 0x1) && (hist_info->col_en)) { spin_lock(&hist_info->hist_lock); - hist_info->col_state = HIST_READY; + if (!is_hist_v2) + hist_info->col_state = HIST_READY; + if (hist_info->read_request == 1) { + hist_info->read_request++; + if (is_hist_v2) { + hist_info->col_state = HIST_READY; + writel_relaxed(1, hist_info->base); + } + need_complete = true; + } spin_unlock(&hist_info->hist_lock); - if (hist_info->read_request) + if (need_complete) complete(&hist_info->comp); } /* Histogram Reset Done Interrupt */ @@ -3543,6 +3678,53 @@ static struct msm_fb_data_type *mdss_get_mfd_from_index(int index) return out; } +static int pp_num_to_side(struct mdss_mdp_ctl *ctl, u32 num) +{ + u32 mixer_id[MDSS_MDP_INTF_MAX_LAYERMIXER]; + u32 mixer_num; + + if (!ctl || !ctl->mfd) + return -EINVAL; + mixer_num = mdss_mdp_get_ctl_mixers(ctl->mfd->index, mixer_id); + if (mixer_num < 2) + return MDSS_SIDE_NONE; + else if (mixer_id[1] == num) + return MDSS_SIDE_RIGHT; + else if (mixer_id[0] == num) + return MDSS_SIDE_LEFT; + else + pr_err("invalid, not on any side"); + return -EINVAL; +} + +static inline void pp_sts_set_split_bits(u32 *sts, u32 bits) +{ + u32 tmp = *sts; + tmp &= ~MDSS_PP_SPLIT_MASK; + tmp |= bits & MDSS_PP_SPLIT_MASK; + *sts = tmp; +} + +static inline bool pp_sts_is_enabled(u32 sts, int side) +{ + bool ret = false; + /* + * If there are no sides, or if there are no split mode bits set, the + * side can't be disabled via split mode. + * + * Otherwise, if the side being checked opposes the split mode + * configuration, the side is disabled. + */ + if ((side == MDSS_SIDE_NONE) || !(sts & MDSS_PP_SPLIT_MASK)) + ret = true; + else if ((sts & MDSS_PP_SPLIT_RIGHT_ONLY) && (side == MDSS_SIDE_RIGHT)) + ret = true; + else if ((sts & MDSS_PP_SPLIT_LEFT_ONLY) && (side == MDSS_SIDE_LEFT)) + ret = true; + + return ret && (sts & PP_STS_ENABLE); +} + static int mdss_ad_init_checks(struct msm_fb_data_type *mfd) { u32 mixer_id[MDSS_MDP_INTF_MAX_LAYERMIXER]; @@ -3638,7 +3820,7 @@ int mdss_mdp_ad_config(struct msm_fb_data_type *mfd, struct mdss_ad_info *ad; struct msm_fb_data_type *bl_mfd; int lin_ret = -1, inv_ret = -1, ret = 0; - u32 ratio_temp, shift = 0; + u32 ratio_temp, shift = 0, last_ops; ret = mdss_mdp_get_ad(mfd, &ad); if (ret) @@ -3651,6 +3833,11 @@ int mdss_mdp_ad_config(struct msm_fb_data_type *mfd, bl_mfd = mfd; } + if ((init_cfg->ops & MDSS_PP_SPLIT_MASK) == MDSS_PP_SPLIT_MASK) { + pr_warn("Can't set both split bits\n"); + return -EINVAL; + } + mutex_lock(&ad->lock); if (init_cfg->ops & MDP_PP_AD_INIT) { memcpy(&ad->init, &init_cfg->params.init, @@ -3693,6 +3880,22 @@ int mdss_mdp_ad_config(struct msm_fb_data_type *mfd, ad->sts |= PP_AD_STS_DIRTY_CFG; } + last_ops = ad->ops & MDSS_PP_SPLIT_MASK; + ad->ops = init_cfg->ops & MDSS_PP_SPLIT_MASK; + /* + * if there is a change in the split mode config, the init values + * need to be re-written to hardware (if they have already been + * written or if there is data pending to be written). Check for + * pending data (DIRTY_INIT) is not checked here since it will not + * affect the outcome of this conditional (i.e. if init hasn't + * already been written (*_STATE_INIT is set), this conditional will + * only evaluate to true (and set the DIRTY bit) if the DIRTY bit has + * already been set). + */ + if ((last_ops ^ ad->ops) && (ad->state & PP_AD_STATE_INIT)) + ad->sts |= PP_AD_STS_DIRTY_INIT; + + if (!ret && (init_cfg->ops & MDP_PP_OPS_DISABLE)) { ad->sts &= ~PP_STS_ENABLE; mutex_unlock(&ad->lock); @@ -3778,7 +3981,7 @@ int mdss_mdp_ad_input(struct msm_fb_data_type *mfd, mutex_unlock(&ad->lock); mutex_lock(&mfd->bl_lock); MDSS_BRIGHT_TO_BL(bl, bl, mfd->panel_info->bl_max, - MDSS_MAX_BL_BRIGHTNESS); + mfd->panel_info->brightness_max); mdss_fb_set_backlight(mfd, bl); mutex_unlock(&mfd->bl_lock); mutex_lock(&ad->lock); @@ -3847,8 +4050,9 @@ static void pp_ad_init_write(struct mdss_mdp_ad *ad_hw, struct mdss_ad_info *ad, u32 temp; u32 frame_start, frame_end, procs_start, procs_end, tile_ctrl; u32 num; + int side; char __iomem *base; - bool is_calc, is_dual_pipe; + bool is_calc, is_dual_pipe, split_mode; u32 mixer_id[MDSS_MDP_INTF_MAX_LAYERMIXER]; u32 mixer_num; mixer_num = mdss_mdp_get_ctl_mixers(ctl->mfd->index, mixer_id); @@ -3859,6 +4063,7 @@ static void pp_ad_init_write(struct mdss_mdp_ad *ad_hw, struct mdss_ad_info *ad, base = ad_hw->base; is_calc = ad->calc_hw_num == ad_hw->num; + split_mode = !!(ad->ops & MDSS_PP_SPLIT_MASK); writel_relaxed(ad->init.i_control[0] & 0x1F, base + MDSS_MDP_REG_AD_CON_CTRL_0); @@ -3884,7 +4089,10 @@ static void pp_ad_init_write(struct mdss_mdp_ad *ad_hw, struct mdss_ad_info *ad, writel_relaxed(ad->init.format, base + MDSS_MDP_REG_AD_CTRL_0); writel_relaxed(ad->init.auto_size, base + MDSS_MDP_REG_AD_CTRL_1); - temp = ad->init.frame_w << 16; + if (split_mode) + temp = mdata->mixer_intf[ad_hw->num].width << 16; + else + temp = ad->init.frame_w << 16; temp |= ad->init.frame_h & 0xFFFF; writel_relaxed(temp, base + MDSS_MDP_REG_AD_FRAME_SIZE); @@ -3896,17 +4104,26 @@ static void pp_ad_init_write(struct mdss_mdp_ad *ad_hw, struct mdss_ad_info *ad, pp_ad_cfg_lut(base + MDSS_MDP_REG_AD_LUT_CC, ad->init.color_corr_lut); if (mdata->mdp_rev >= MDSS_MDP_HW_REV_103) { - if (is_dual_pipe) { + if (is_dual_pipe && !split_mode) { num = ad_hw->num; + side = pp_num_to_side(ctl, num); tile_ctrl = 0x5; - if (is_calc) { + if ((ad->calc_hw_num + 1) == num) + tile_ctrl |= 0x10; + + if (side <= MDSS_SIDE_NONE) { + WARN(1, "error finding sides, %d", side); + frame_start = 0; + procs_start = frame_start; + frame_end = 0; + procs_end = frame_end; + } else if (side == MDSS_SIDE_LEFT) { frame_start = 0; procs_start = 0; frame_end = mdata->mixer_intf[num].width + MDSS_AD_MERGED_WIDTH; procs_end = mdata->mixer_intf[num].width; } else { - tile_ctrl |= 0x10; procs_start = ad->init.frame_w - (mdata->mixer_intf[num].width); procs_end = ad->init.frame_w; @@ -3921,8 +4138,13 @@ static void pp_ad_init_write(struct mdss_mdp_ad *ad_hw, struct mdss_ad_info *ad, frame_end = 0xFFFF; procs_start = 0x0; procs_end = 0xFFFF; - tile_ctrl = 0x1; + if (split_mode) + tile_ctrl = 0x0; + else + tile_ctrl = 0x1; } + + writel_relaxed(frame_start, base + MDSS_MDP_REG_AD_FRAME_START); writel_relaxed(frame_end, base + MDSS_MDP_REG_AD_FRAME_END); writel_relaxed(procs_start, base + MDSS_MDP_REG_AD_PROCS_START); @@ -3986,12 +4208,17 @@ static void pp_ad_vsync_handler(struct mdss_mdp_ctl *ctl, ktime_t t) } #define MDSS_PP_AD_BYPASS_DEF 0x101 -static void pp_ad_bypass_config(struct mdss_ad_info *ad, u32 *opmode) +static void pp_ad_bypass_config(struct mdss_ad_info *ad, + struct mdss_mdp_ctl *ctl, u32 num, u32 *opmode) { - if (ad->reg_sts & PP_STS_ENABLE) + int side = pp_num_to_side(ctl, num); + + if (pp_sts_is_enabled(ad->reg_sts | (ad->ops & MDSS_PP_SPLIT_MASK), + side)) { *opmode = 0; - else + } else { *opmode = MDSS_PP_AD_BYPASS_DEF; + } } static int pp_ad_setup_hw_nums(struct msm_fb_data_type *mfd, @@ -4006,6 +4233,8 @@ static int pp_ad_setup_hw_nums(struct msm_fb_data_type *mfd, /* default to left mixer */ ad->calc_hw_num = mixer_id[0]; + if ((mixer_num > 1) && (ad->ops & MDSS_PP_SPLIT_RIGHT_ONLY)) + ad->calc_hw_num = mixer_id[1]; return 0; } @@ -4313,6 +4542,7 @@ int mdss_mdp_ad_addr_setup(struct mdss_data_type *mdata, u32 *ad_offsets) mdata->ad_off[i].base = mdata->mdp_base + ad_offsets[i]; mdata->ad_off[i].num = i; mdata->ad_cfgs[i].num = i; + mdata->ad_cfgs[i].ops = 0; mdata->ad_cfgs[i].reg_sts = 0; mdata->ad_cfgs[i].calc_itr = 0; mdata->ad_cfgs[i].last_str = 0xFFFFFFFF; @@ -4421,7 +4651,10 @@ static int is_valid_calib_vig_addr(char __iomem *ptr) pipe = mdss_res->vig_pipes + counter; base = pipe->base; - if (ptr == base + MDSS_MDP_REG_SSPP_SRC_FORMAT) { + if (ptr == base + MDSS_MDP_REG_VIG_OP_MODE) { + ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE; + break; + } else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_FORMAT) { ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE; break; } else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_CONSTANT_COLOR) { @@ -4430,6 +4663,9 @@ static int is_valid_calib_vig_addr(char __iomem *ptr) } else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN) { ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE; break; + } else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_OP_MODE) { + ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE; + break; } else if ((ptr == base + MDSS_MDP_REG_VIG_QSEED2_SHARP)) { ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE; break; @@ -4477,6 +4713,9 @@ static int is_valid_calib_rgb_addr(char __iomem *ptr) } else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN) { ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE; break; + } else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_OP_MODE) { + ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE; + break; /* IGC range */ } else if ((ptr >= base + MDSS_MDP_REG_IGC_RGB_BASE) && (ptr <= base + MDSS_MDP_REG_IGC_RGB_BASE + @@ -4508,6 +4747,9 @@ static int is_valid_calib_dma_addr(char __iomem *ptr) } else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN) { ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE; break; + } else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_OP_MODE) { + ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE; + break; /* IGC range */ } else if ((ptr >= base + MDSS_MDP_REG_IGC_DMA_BASE) && (ptr <= base + MDSS_MDP_REG_IGC_DMA_BASE + diff --git a/drivers/video/msm/mdss/mdss_panel.h b/drivers/video/msm/mdss/mdss_panel.h index 48ff538406ce..59ff4959f201 100644 --- a/drivers/video/msm/mdss/mdss_panel.h +++ b/drivers/video/msm/mdss/mdss_panel.h @@ -287,6 +287,7 @@ struct mdss_panel_info { u32 type; u32 wait_cycle; u32 pdest; + u32 brightness_max; u32 bl_max; u32 bl_min; u32 fb_num; diff --git a/drivers/video/msm/mdss/splash.h b/drivers/video/msm/mdss/splash.h new file mode 100644 index 000000000000..dc8a473cb56c --- /dev/null +++ b/drivers/video/msm/mdss/splash.h @@ -0,0 +1,5279 @@ +/* Copyright (c) 2013, The Linux Foundation. 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 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 __SPLASH_H_ +#define __SPLASH_H_ + +#define SPLASH_IMAGE_WIDTH 113 +#define SPLASH_IMAGE_HEIGHT 124 +#define SPLASH_IMAGE_FORMAT MDP_BGR_888 +#define SPLASH_IMAGE_BPP 3 + +char splash_bgr888_image[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, + 0x29, 0x19, 0x31, 0x31, + 0x29, 0x31, 0x31, 0x29, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x08, 0x10, 0x31, 0x31, + 0x29, 0x4a, 0x52, 0x4a, 0x6b, 0x5a, 0x73, 0x4a, 0x52, 0x4a, 0x10, 0x29, + 0x19, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, + 0x10, 0x31, 0x31, 0x29, 0x6b, 0x5a, 0x73, 0x6b, 0x7b, 0x73, 0x6b, 0x5a, + 0x4a, 0x31, 0x31, 0x29, + 0x3a, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x08, 0x10, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x10, 0x31, 0x31, 0x29, 0x6b, 0x5a, 0x4a, 0x6b, 0x5a, + 0x73, 0x3a, 0x31, 0x4a, + 0x31, 0x31, 0x29, 0x10, 0x29, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x31, 0x31, 0x29, 0x3a, 0x31, + 0x4a, 0x31, 0x31, 0x29, + 0x10, 0x29, 0x19, 0x08, 0x08, 0x10, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x10, 0x21, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x08, 0x08, + 0x10, 0x08, 0x08, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, + 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x08, 0x10, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0x00, 0x08, 0x08, 0x10, + 0x08, 0x08, 0x10, 0x10, + 0x29, 0x19, 0x10, 0x29, 0x19, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, + 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, + 0x4a, 0x52, 0x4a, 0x08, + 0x08, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x31, 0x31, 0x29, 0x08, + 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x29, 0x19, 0x4a, 0x52, 0x4a, 0x3a, 0x31, 0x4a, + 0x08, 0x00, 0x00, 0x08, + 0x08, 0x10, 0x4a, 0x52, 0x4a, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x08, 0x10, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4a, + 0x52, 0x4a, 0x9c, 0xa5, 0x94, 0x9c, 0x7b, 0x94, 0x08, 0x08, 0x10, 0x08, + 0x08, 0x10, 0x10, 0x29, + 0x19, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, + 0x10, 0x10, 0x21, 0x00, + 0x08, 0x08, 0x10, 0x6b, 0x7b, 0x73, 0x9c, 0x7b, 0x94, 0x9c, 0xa5, 0x94, + 0xce, 0xad, 0xad, 0xa5, + 0xb5, 0xb5, 0x31, 0x31, 0x29, 0x08, 0x08, 0x10, 0x10, 0x29, 0x19, 0x08, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x4a, + 0x52, 0x4a, 0xa5, 0x9c, 0xad, 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0x9c, + 0x7b, 0x94, 0x10, 0x29, + 0x19, 0x3a, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x08, 0x10, 0x9c, 0x7b, 0x94, 0x9c, 0xa5, 0x94, 0xa5, 0xb5, 0xb5, + 0xa5, 0xb5, 0xb5, 0xce, + 0xde, 0xce, 0xc5, 0xad, 0xd6, 0x9c, 0xa5, 0x94, 0x3a, 0x10, 0x21, 0x00, + 0x00, 0x00, 0x08, 0x08, + 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x08, 0x10, 0x9c, 0x7b, 0x94, 0xce, 0xad, 0xad, 0xce, 0xe6, 0xef, 0xce, + 0xe6, 0xef, 0xe6, 0xde, + 0xde, 0xa5, 0x9c, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6b, 0x5a, 0x73, 0xce, 0xde, 0xce, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0xde, 0xce, 0x6b, + 0x7b, 0x73, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x29, 0x19, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xff, 0x9c, + 0xa5, 0x94, 0xce, 0xde, + 0xce, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x21, 0x00, 0xce, 0xde, 0xce, 0xff, 0xf7, 0xff, + 0xff, 0xf7, 0xff, 0x9c, + 0xa5, 0x94, 0x00, 0x00, 0x00, 0x31, 0x31, 0x29, 0xef, 0xf7, 0xe6, 0xff, + 0xf7, 0xff, 0xef, 0xde, + 0xef, 0x08, 0x08, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x4a, 0x52, 0x4a, 0xff, 0xf7, 0xff, 0x10, + 0x29, 0x19, 0x08, 0x08, + 0x10, 0x4a, 0x52, 0x4a, 0xce, 0xad, 0xad, 0xff, 0xff, 0xff, 0x4a, 0x52, + 0x4a, 0x08, 0x08, 0x10, + 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x08, 0x08, 0x10, 0xce, 0xad, 0xad, + 0xef, 0xf7, 0xff, 0xce, + 0xde, 0xce, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x9c, 0x7b, 0x94, 0x31, + 0x31, 0x29, 0xef, 0xf7, + 0xe6, 0xff, 0xff, 0xff, 0x4a, 0x52, 0x4a, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x5a, 0x73, 0xef, + 0xf7, 0xe6, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x6b, 0x7b, 0x73, 0x08, 0x08, 0x10, 0xff, 0xff, + 0xff, 0x6b, 0x7b, 0x73, + 0x08, 0x08, 0x10, 0x31, 0x31, 0x29, 0x4a, 0x52, 0x4a, 0x08, 0x08, 0x10, + 0xce, 0xde, 0xce, 0xff, + 0xff, 0xff, 0x10, 0x29, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, + 0x31, 0x29, 0x4a, 0x52, + 0x4a, 0xa5, 0xb5, 0xb5, 0xff, 0xff, 0xff, 0x9c, 0x7b, 0x94, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x4a, + 0x52, 0x4a, 0xe6, 0xde, + 0xde, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x31, 0x31, 0x29, 0x08, 0x08, + 0x10, 0xff, 0xff, 0xff, + 0x4a, 0x52, 0x4a, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, + 0x08, 0x08, 0x10, 0x9c, + 0x7b, 0x94, 0xff, 0xff, 0xff, 0x3a, 0x10, 0x21, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, + 0x10, 0x08, 0x08, 0x10, 0x6b, 0x5a, 0x73, 0xff, 0xff, 0xff, 0xa5, 0xb5, + 0xb5, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x4a, 0x52, + 0x4a, 0xef, 0xf7, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xe6, 0xde, 0xde, 0x08, 0x31, 0x5a, 0x10, 0x7b, 0x9c, 0x10, 0x7b, 0x9c, + 0x10, 0x7b, 0x9c, 0x10, + 0x52, 0x7b, 0x31, 0x31, 0x29, 0xef, 0xf7, 0xff, 0x08, 0x08, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0xa5, 0x94, 0xff, 0xff, + 0xff, 0xa5, 0x9c, 0xad, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x21, 0x00, 0x08, + 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x08, 0x10, 0xff, 0xf7, 0xff, 0x4a, 0x52, 0x4a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x31, 0x5a, 0x00, 0x84, 0xbd, 0x08, 0xad, 0xd6, 0x00, 0xbd, 0xef, + 0x00, 0xbd, 0xef, 0x00, + 0x9c, 0xd6, 0x08, 0xa5, 0xad, 0x08, 0xad, 0xd6, 0x10, 0xce, 0xce, 0x6b, + 0x7b, 0x9c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, + 0x6b, 0x5a, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xce, 0xde, 0xce, 0xff, 0xf7, + 0xff, 0x10, 0x29, 0x19, + 0x10, 0x5a, 0x9c, 0x00, 0x9c, 0xd6, 0x19, 0x94, 0xce, 0x00, 0xbd, 0xf7, + 0x10, 0xc5, 0xef, 0x08, + 0xad, 0xd6, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xd6, 0x10, + 0xc5, 0xef, 0x00, 0xbd, + 0xef, 0x08, 0xa5, 0xad, 0x08, 0x31, 0x5a, 0x10, 0x29, 0x19, 0xff, 0xf7, + 0xff, 0xff, 0xff, 0xff, + 0xe6, 0xde, 0xde, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x5a, + 0x73, 0xef, 0xf7, 0xe6, + 0x19, 0x7b, 0xbd, 0x19, 0x7b, 0xbd, 0x08, 0xad, 0xd6, 0x00, 0xbd, 0xef, + 0x10, 0xc5, 0xef, 0x00, + 0xbd, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xc5, 0xef, 0x3a, 0xde, 0xef, 0x19, + 0xbd, 0xf7, 0x3a, 0xde, + 0xef, 0x3a, 0xde, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, + 0xd6, 0x19, 0x94, 0xce, + 0xa5, 0xb5, 0xb5, 0x4a, 0x5a, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x10, + 0x10, 0x7b, 0x9c, 0x00, 0x84, 0xbd, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xd6, + 0x08, 0xad, 0xef, 0x10, + 0xc5, 0xef, 0x08, 0xad, 0xd6, 0x10, 0xc5, 0xef, 0x10, 0xe6, 0xef, 0x19, + 0xbd, 0xf7, 0x3a, 0xde, + 0xef, 0x3a, 0xde, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xe6, 0xef, 0x10, 0xe6, + 0xef, 0x10, 0xc5, 0xef, + 0x10, 0xe6, 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xd6, 0x08, 0x31, 0x3a, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x10, + 0x10, 0x7b, 0x9c, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6, + 0x10, 0xc5, 0xef, 0x08, + 0xad, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xc5, 0xef, 0x10, + 0xc5, 0xef, 0x6b, 0xe6, + 0xef, 0x3a, 0xde, 0xef, 0x10, 0xe6, 0xef, 0x10, 0xe6, 0xef, 0x10, 0xc5, + 0xef, 0x10, 0xe6, 0xef, + 0x10, 0xc5, 0xef, 0x10, 0xe6, 0xef, 0x10, 0xc5, 0xef, 0x19, 0xbd, 0xf7, + 0x19, 0x7b, 0xbd, 0x00, + 0x00, 0x00, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x31, 0x5a, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x00, 0x9c, 0xd6, + 0x08, 0xad, 0xef, 0x08, + 0xad, 0xef, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xef, 0x10, 0xc5, 0xef, 0x10, + 0xc5, 0xef, 0x10, 0xe6, + 0xef, 0x3a, 0xde, 0xef, 0x19, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x10, 0xe6, + 0xef, 0x10, 0xc5, 0xef, + 0x10, 0xe6, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0x84, 0xbd, 0x10, 0x7b, 0x9c, + 0x00, 0xbd, 0xef, 0x10, + 0x73, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x52, 0x7b, 0x19, 0x7b, 0xbd, 0x00, 0x9c, 0xd6, + 0x19, 0x94, 0xce, 0x08, + 0xad, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xd6, 0x10, + 0xe6, 0xef, 0x19, 0xbd, + 0xf7, 0x6b, 0xe6, 0xef, 0x19, 0xbd, 0xf7, 0x10, 0xe6, 0xef, 0x10, 0xe6, + 0xef, 0x10, 0xe6, 0xef, + 0x10, 0xc5, 0xef, 0x00, 0x9c, 0xd6, 0x00, 0x84, 0xbd, 0x00, 0x9c, 0xd6, + 0x19, 0x94, 0xce, 0x00, + 0x9c, 0xd6, 0x10, 0x52, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x08, 0x31, 0x5a, + 0x08, 0xa5, 0xad, 0x08, + 0xad, 0xef, 0x00, 0xbd, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xc5, 0xef, 0x10, + 0xc5, 0xef, 0x10, 0xc5, + 0xef, 0x10, 0xe6, 0xef, 0x19, 0xbd, 0xf7, 0x10, 0xe6, 0xef, 0x10, 0xc5, + 0xef, 0x10, 0xc5, 0xef, + 0x19, 0xbd, 0xf7, 0x10, 0x7b, 0x9c, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, + 0x08, 0xad, 0xd6, 0x00, + 0x9c, 0xd6, 0x19, 0x94, 0xce, 0x08, 0x31, 0x3a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, + 0x10, 0x31, 0x31, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, + 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x31, 0x3a, 0x10, + 0x52, 0x7b, 0x10, 0x7b, 0x9c, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x10, + 0xc5, 0xef, 0x10, 0xc5, + 0xef, 0x3a, 0xde, 0xef, 0x10, 0xe6, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xe6, + 0xef, 0x08, 0xad, 0xd6, + 0x10, 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x00, 0x9c, 0xd6, 0x19, 0x94, 0xce, + 0x00, 0x9c, 0xd6, 0x00, + 0x9c, 0xd6, 0x19, 0x7b, 0xbd, 0x19, 0x7b, 0xbd, 0x08, 0x08, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x10, 0x9c, 0x7b, 0x73, 0x6b, 0x5a, 0x73, 0x10, 0x29, + 0x19, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x08, 0x10, 0x73, 0xa5, 0xad, 0x10, 0x52, 0x7b, 0x10, 0x52, 0x7b, 0x00, + 0x84, 0xbd, 0x08, 0xa5, + 0xad, 0x08, 0xad, 0xd6, 0x19, 0x94, 0xce, 0x00, 0x84, 0xbd, 0x10, 0x73, + 0x7b, 0x10, 0x5a, 0x9c, + 0x00, 0x84, 0xbd, 0x00, 0x9c, 0xd6, 0x00, 0x9c, 0xd6, 0x00, 0x9c, 0xd6, + 0x00, 0x9c, 0xd6, 0x00, + 0x84, 0xbd, 0x3a, 0xa5, 0xce, 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0x6b, + 0x5a, 0x73, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x52, 0x4a, 0x6b, 0x7b, + 0x9c, 0x9c, 0x7b, 0x73, + 0x31, 0x31, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x31, 0x31, 0x29, 0xc5, 0xad, 0xd6, 0x52, 0xa5, 0xa5, 0x10, + 0x5a, 0x9c, 0x10, 0x7b, + 0x9c, 0x19, 0x94, 0xce, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xd6, 0x19, 0x94, + 0xce, 0x00, 0x9c, 0xd6, + 0x00, 0x9c, 0xd6, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, + 0x00, 0x84, 0xbd, 0x19, + 0x7b, 0xbd, 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0xce, 0xde, 0xce, 0xa5, + 0xb5, 0xb5, 0xa5, 0xb5, + 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, + 0x00, 0x6b, 0x7b, 0x73, + 0x9c, 0x7b, 0x94, 0x6b, 0x7b, 0x73, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x10, 0x4a, 0x52, 0x4a, 0xa5, 0xd6, 0xad, 0xc5, + 0xad, 0xd6, 0x4a, 0x7b, + 0x9c, 0x10, 0x5a, 0x9c, 0x00, 0x84, 0xbd, 0x00, 0x9c, 0xd6, 0x00, 0x84, + 0xbd, 0x00, 0x9c, 0xd6, + 0x19, 0x94, 0xce, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x00, 0x84, 0xbd, + 0x19, 0x7b, 0xbd, 0x73, + 0xa5, 0xad, 0xa5, 0xb5, 0xb5, 0xc5, 0xad, 0xd6, 0xa5, 0xb5, 0xb5, 0xe6, + 0xde, 0xde, 0xce, 0xde, + 0xce, 0xce, 0xad, 0xad, 0x10, 0x29, 0x19, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x4a, 0x52, 0x4a, 0x6b, 0x7b, 0x73, 0x31, 0x31, 0x29, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x31, 0x4a, 0xce, + 0xad, 0xad, 0xce, 0xde, + 0xce, 0xa5, 0xb5, 0xb5, 0x73, 0xa5, 0xad, 0x10, 0x73, 0x7b, 0x10, 0x5a, + 0x9c, 0x10, 0x7b, 0x9c, + 0x10, 0x5a, 0xbd, 0x10, 0x7b, 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x5a, 0x9c, + 0x19, 0x7b, 0xbd, 0xa5, + 0xb5, 0xb5, 0xa5, 0xb5, 0xb5, 0xc5, 0xad, 0xd6, 0xa5, 0xd6, 0xad, 0xef, + 0xde, 0xef, 0xef, 0xf7, + 0xe6, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xa5, 0x9c, 0xad, 0x08, 0x08, + 0x10, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x4a, + 0x52, 0x4a, 0xe6, 0xde, + 0xde, 0xc5, 0xad, 0xd6, 0xa5, 0xb5, 0xb5, 0xce, 0xad, 0xad, 0x9c, 0xad, + 0xce, 0x52, 0xa5, 0xa5, + 0x10, 0x5a, 0x9c, 0x10, 0x7b, 0x9c, 0x10, 0x5a, 0x9c, 0x52, 0xa5, 0xa5, + 0x73, 0xa5, 0xad, 0xc5, + 0xad, 0xd6, 0xa5, 0xb5, 0xb5, 0xce, 0xde, 0xce, 0xce, 0xde, 0xce, 0xef, + 0xde, 0xef, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xef, 0xf7, + 0xe6, 0x08, 0x08, 0x10, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xef, 0xf7, + 0xe6, 0xef, 0xf7, 0xe6, 0xce, 0xde, 0xce, 0xce, 0xde, 0xce, 0xa5, 0xb5, + 0xb5, 0xce, 0xde, 0xce, + 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0xa5, 0xb5, 0xb5, 0xce, 0xde, 0xce, + 0xce, 0xad, 0xad, 0xc5, + 0xad, 0xd6, 0xa5, 0xd6, 0xad, 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0xe6, + 0xde, 0xde, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xa5, 0xb5, 0xb5, 0x08, 0x08, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x08, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x10, 0x21, 0x00, 0x00, + 0x00, 0x00, 0x9c, 0xa5, + 0x94, 0xff, 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xe6, 0xc5, 0xad, + 0xd6, 0xa5, 0xb5, 0xb5, + 0xce, 0xad, 0xad, 0xc5, 0xad, 0xd6, 0xa5, 0xb5, 0xb5, 0xce, 0xad, 0xad, + 0xc5, 0xad, 0xd6, 0xa5, + 0xd6, 0xad, 0xc5, 0xad, 0xd6, 0xce, 0xde, 0xce, 0xef, 0xde, 0xef, 0xef, + 0xf7, 0xe6, 0xff, 0xf7, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x31, 0x31, 0x29, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x08, 0x08, 0x10, 0x08, + 0x08, 0x10, 0x10, 0x29, + 0x19, 0xef, 0xde, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, + 0xc5, 0xad, 0xd6, 0xa5, 0xd6, 0xad, 0xa5, 0xb5, 0xb5, 0xce, 0xde, 0xce, + 0xa5, 0xb5, 0xb5, 0xce, + 0xde, 0xce, 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0xef, 0xf7, 0xe6, 0xef, + 0xf7, 0xff, 0xff, 0xf7, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0xde, 0xde, + 0x08, 0x08, 0x10, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x21, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0xe6, 0xde, 0xde, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xe6, 0xe6, 0xde, 0xde, 0xce, 0xad, 0xad, 0xc5, 0xad, 0xd6, + 0xa5, 0xb5, 0xb5, 0xce, + 0xad, 0xad, 0xce, 0xde, 0xce, 0xce, 0xe6, 0xef, 0xff, 0xf7, 0xff, 0xff, + 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x08, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4a, 0x52, 0x4a, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xef, 0xf7, 0xe6, + 0xe6, 0xde, 0xde, 0xef, + 0xf7, 0xe6, 0xce, 0xe6, 0xef, 0xff, 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xa5, 0x9c, 0xad, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0xde, 0xde, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4a, 0x52, 0x4a, 0xef, 0xf7, 0xe6, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, + 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0xce, 0xde, 0xce, 0xef, 0xde, + 0xef, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xef, + 0xf7, 0xff, 0x31, 0x31, + 0x29, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0xce, 0xad, + 0xad, 0xef, 0xf7, 0xe6, + 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xef, 0xf7, 0xe6, 0xef, + 0xde, 0xef, 0xef, 0xf7, + 0xe6, 0x9c, 0xa5, 0x94, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x31, + 0x29, 0xa5, 0xb5, 0xb5, + 0xce, 0xde, 0xce, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xff, 0xff, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xe6, 0xef, 0xde, 0xef, 0xef, + 0xf7, 0xe6, 0xe6, 0xde, + 0xde, 0xce, 0xe6, 0xef, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xf7, + 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xff, 0xef, 0xf7, 0xe6, + 0xef, 0xf7, 0xe6, 0xef, + 0xde, 0xef, 0xce, 0xe6, 0xef, 0xe6, 0xde, 0xde, 0xe6, 0xde, 0xde, 0xce, + 0xde, 0xce, 0xce, 0xde, + 0xce, 0xe6, 0xde, 0xde, 0xce, 0xad, 0xad, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x10, 0x21, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x08, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, + 0x10, 0x6b, 0x5a, 0x4a, + 0x73, 0xa5, 0xad, 0xce, 0xad, 0xad, 0xc5, 0xad, 0xd6, 0xe6, 0xde, 0xde, + 0xe6, 0xde, 0xde, 0xff, + 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xef, + 0xde, 0xef, 0xce, 0xde, + 0xce, 0xef, 0xde, 0xef, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xe6, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xde, 0xef, + 0xce, 0xde, 0xce, 0xc5, + 0xad, 0xd6, 0xce, 0xde, 0xce, 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0xa5, + 0xb5, 0xb5, 0xc5, 0xad, + 0xd6, 0xa5, 0xb5, 0xb5, 0xc5, 0xad, 0xd6, 0xef, 0xf7, 0xff, 0x4a, 0x52, + 0x4a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x10, 0x29, 0x19, + 0x6b, 0x5a, 0x73, 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0xce, 0xde, 0xce, + 0xef, 0xf7, 0xe6, 0xef, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xef, 0xde, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xf7, 0xff, 0xef, 0xf7, 0xe6, 0xe6, 0xde, 0xde, 0xce, 0xde, 0xce, 0xc5, + 0xad, 0xd6, 0xce, 0xde, + 0xce, 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0xa5, 0xb5, 0xb5, 0xce, 0xde, + 0xce, 0xff, 0xf7, 0xff, + 0x31, 0x31, 0x29, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x4a, 0x52, 0x4a, + 0x31, 0x31, 0x29, 0x08, + 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x08, 0x08, 0x10, 0xa5, 0x9c, 0xad, 0xce, 0xde, 0xce, 0xce, 0xe6, 0xef, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xf7, + 0xff, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xce, 0xe6, 0xef, 0xe6, 0xde, 0xde, 0xce, 0xde, 0xce, 0xc5, 0xad, + 0xd6, 0xce, 0xad, 0xad, + 0xe6, 0xde, 0xde, 0xff, 0xf7, 0xff, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x08, 0x10, 0x31, 0x31, 0x29, 0x3a, 0x10, 0x21, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x31, 0x31, + 0x29, 0x00, 0x00, 0x00, + 0x10, 0x21, 0x00, 0x6b, 0x5a, 0x73, 0xce, 0xde, 0xce, 0xff, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xe6, 0xce, 0xde, 0xce, + 0xce, 0xde, 0xce, 0x9c, 0xad, 0xce, 0xef, 0xf7, 0xe6, 0xa5, 0x9c, 0xad, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x3a, 0x31, 0x4a, 0x10, + 0x29, 0x19, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x52, + 0x4a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0xe6, 0xde, 0xde, 0xef, 0xf7, 0xe6, + 0xff, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xe6, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xe6, 0xff, 0xf7, 0xff, + 0xef, 0xf7, 0xff, 0xef, 0xde, 0xef, 0xce, 0xe6, 0xad, 0xc5, 0xad, 0xd6, + 0xff, 0xff, 0xff, 0x10, + 0x29, 0x19, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x31, 0x31, + 0x29, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x10, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x29, + 0x19, 0x08, 0x08, 0x10, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x5a, 0x73, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xde, 0xef, + 0xce, 0xde, 0xce, 0xce, + 0xde, 0xce, 0xce, 0xad, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x21, 0x00, 0x08, 0x08, + 0x10, 0x08, 0x08, 0x10, 0x4a, 0x52, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x4a, 0x52, 0x4a, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xf7, 0xe6, + 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xef, + 0xde, 0xef, 0xce, 0xde, 0xce, 0xef, 0xf7, 0xff, 0x08, 0x08, 0x10, 0x00, + 0x00, 0x00, 0x3a, 0x10, + 0x21, 0x31, 0x31, 0x29, 0x31, 0x31, 0x29, 0x08, 0x08, 0x10, 0x10, 0x29, + 0x19, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x29, 0x19, + 0x3a, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x7b, 0x94, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0x4a, + 0x52, 0x4a, 0x00, 0x00, + 0x00, 0x10, 0x29, 0x19, 0x4a, 0x52, 0x4a, 0x31, 0x31, 0x29, 0x08, 0x08, + 0x10, 0x3a, 0x31, 0x4a, + 0x08, 0x08, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x4a, 0x52, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, + 0xef, 0xf7, 0xe6, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xce, 0xde, + 0xce, 0x3a, 0x10, 0x21, 0x10, 0x29, 0x19, 0x3a, 0x31, 0x4a, 0x3a, 0x10, + 0x21, 0x10, 0x21, 0x00, + 0x08, 0x08, 0x10, 0x31, 0x31, 0x29, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x08, 0x10, 0x3a, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x7b, 0x94, 0xef, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xe6, 0xde, 0xde, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x4a, 0x52, 0x4a, 0x08, 0x08, 0x10, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4a, 0x52, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x08, 0x10, 0xef, + 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0xe6, 0xef, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, + 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0x3a, 0x10, 0x21, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x31, 0x29, + 0x08, 0x08, 0x10, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x31, 0x31, 0x29, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4a, + 0x52, 0x4a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xe6, 0xde, 0xde, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4a, 0x52, + 0x4a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x08, 0x10, 0x31, + 0x31, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x31, 0x29, 0x08, 0x08, 0x10, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe6, 0xde, 0xde, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, + 0xe6, 0xde, 0xde, 0xef, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf7, + 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf7, 0xff, 0xef, 0xf7, + 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xff, 0x9c, 0x7b, 0x94, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, + 0x08, 0x10, 0x10, 0x29, 0x19, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x52, 0x4a, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x29, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xe6, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xe6, 0xce, + 0xe6, 0xef, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xe6, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xe6, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xa5, 0xb5, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x08, 0x10, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x10, 0x3a, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x31, 0x31, 0x29, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x52, 0x4a, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xf7, 0xff, 0xef, + 0xf7, 0xe6, 0xe6, 0xde, 0xde, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xf7, + 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xce, 0xde, 0xce, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x29, 0x19, 0x08, 0x08, 0x10, 0x00, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x08, 0x10, 0x31, + 0x31, 0x29, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0xce, 0xad, 0xad, 0xef, + 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xef, 0xde, 0xef, 0xef, 0xf7, 0xe6, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x29, 0x19, 0x08, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, + 0x21, 0x00, 0x3a, 0x31, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xce, + 0xe6, 0xef, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf7, 0xff, 0xce, 0xe6, 0xef, 0xe6, 0xde, 0xde, 0xef, + 0xf7, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x3a, + 0x31, 0x4a, 0x08, 0x08, + 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x10, 0x4a, 0x52, 0x4a, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0xef, 0xf7, + 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xe6, 0xde, 0xde, 0xe6, + 0xde, 0xde, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xe6, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x4a, 0x52, + 0x4a, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x10, 0x21, 0x4a, 0x52, 0x4a, 0x00, + 0x00, 0x00, 0x08, 0x08, + 0x10, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, + 0xe6, 0xef, 0xe6, 0xde, + 0xde, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x10, 0x29, 0x19, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x3a, 0x31, 0x4a, 0x6b, + 0x7b, 0x73, 0x08, 0x00, + 0x00, 0x10, 0x29, 0x19, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xe6, 0xde, + 0xde, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, + 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x29, 0x19, 0x08, 0x08, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, + 0x29, 0x19, 0x6b, 0x5a, + 0x73, 0x08, 0x00, 0x00, 0x10, 0x29, 0x19, 0xff, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xe6, 0xde, 0xde, 0xe6, 0xde, 0xde, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xe6, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x10, 0x31, 0x31, 0x29, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x31, 0x31, 0x29, 0x08, 0x00, 0x00, 0x31, 0x31, 0x29, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf7, + 0xff, 0xef, 0xf7, 0xff, 0xe6, 0xde, 0xde, 0xef, 0xf7, 0xe6, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xe6, 0xde, 0xde, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x10, 0x29, 0x19, 0x08, 0x08, + 0x10, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x31, 0x4a, 0x31, 0x31, + 0x29, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xe6, 0xff, + 0xff, 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0xde, 0xde, 0xce, 0xe6, + 0xef, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xff, 0xff, 0xf7, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa5, + 0xb5, 0xb5, 0x00, 0x00, + 0x00, 0x10, 0x21, 0x00, 0x3a, 0x31, 0x4a, 0x10, 0x29, 0x19, 0x3a, 0x08, + 0x00, 0x08, 0x08, 0x10, + 0x08, 0x08, 0x10, 0x31, 0x31, 0x29, 0x31, 0x31, 0x29, 0x10, 0x29, 0x19, + 0x08, 0x08, 0x10, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x10, + 0x7b, 0x9c, 0x10, 0xc5, + 0xef, 0x10, 0xc5, 0xef, 0x10, 0x7b, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4a, 0x52, 0x4a, + 0xef, 0xf7, 0xe6, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xf7, + 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0xde, + 0xde, 0xef, 0xf7, 0xe6, + 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x6b, 0x5a, + 0x4a, 0x08, 0x08, 0x10, 0x08, 0x08, 0x10, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x08, 0x10, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, + 0x3a, 0x10, 0x21, 0x6b, + 0x5a, 0x73, 0x10, 0x29, 0x19, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, + 0x94, 0xce, 0x08, 0xad, + 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad, + 0xd6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4a, 0x5a, 0x73, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xe6, 0xde, 0xde, + 0xef, 0xde, 0xef, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xe6, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0xe6, 0xef, 0xce, + 0xe6, 0xef, 0xce, 0xe6, + 0xef, 0x08, 0x10, 0x42, 0x10, 0x29, 0x19, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x31, 0x31, 0x29, 0x4a, 0x52, 0x4a, 0x08, 0x08, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, + 0x7b, 0xbd, 0x00, 0x9c, + 0xd6, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0x9c, 0xd6, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x4a, 0x52, 0x4a, + 0xef, 0xde, 0xef, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xe6, 0xff, 0xff, 0xff, + 0xe6, 0xde, 0xde, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0x08, 0xad, 0xd6, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, + 0xef, 0x10, 0xc5, 0xef, 0x10, 0xc5, 0xef, 0x08, 0x08, 0x10, 0x08, 0x08, + 0x10, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x10, 0x31, 0x31, 0x29, 0x10, 0x29, 0x19, 0x3a, + 0x10, 0x21, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x84, + 0xbd, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, + 0xef, 0x00, 0xbd, 0xf7, + 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0x7b, 0xbd, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x31, + 0x31, 0x29, 0xe6, 0xde, 0xde, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xce, 0xe6, 0xef, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0xe6, 0xef, 0x08, + 0xad, 0xef, 0x00, 0xbd, + 0xf7, 0x10, 0xe6, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xe6, 0xef, 0x08, 0x08, + 0x10, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x29, 0x19, 0x10, 0x29, 0x19, 0x00, + 0x00, 0x00, 0x00, 0x84, + 0xbd, 0x10, 0xe6, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x19, 0x7b, + 0xbd, 0x00, 0x9c, 0xd6, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x19, 0xbd, + 0xf7, 0x00, 0xbd, 0xf7, + 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x10, 0xc5, 0xef, + 0x10, 0x5a, 0x9c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x29, 0x19, 0xce, 0xde, 0xce, 0xff, + 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xe6, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0xde, 0xde, 0xef, 0xde, 0xef, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xce, + 0xe6, 0xef, 0x00, 0x9c, + 0xd6, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, + 0xd6, 0x08, 0x08, 0x10, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x08, 0x10, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x08, 0x00, 0x00, 0x08, + 0x08, 0x10, 0x08, 0x31, + 0x3a, 0x10, 0xc5, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xc5, 0xef, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x84, + 0xbd, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, + 0xef, 0x00, 0xbd, 0xef, + 0x19, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, + 0x10, 0xc5, 0xef, 0x00, + 0xbd, 0xef, 0x08, 0x31, 0x3a, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x9c, 0xa5, + 0x94, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xe6, 0xef, + 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xce, 0xe6, + 0xef, 0x08, 0xad, 0xd6, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, + 0xef, 0x19, 0x94, 0xce, + 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x19, 0x94, 0xce, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xef, 0x10, 0xc5, + 0xef, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x84, 0xbd, 0x00, + 0x84, 0xbd, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x00, 0x84, 0xbd, 0x19, + 0x94, 0xce, 0x00, 0x84, + 0xbd, 0x08, 0xad, 0xd6, 0x00, 0x9c, 0xd6, 0x08, 0xad, 0xef, 0x10, 0xc5, + 0xef, 0x19, 0xbd, 0xf7, + 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x00, + 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xd6, 0x08, 0x08, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4a, 0x52, 0x4a, 0xff, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xe6, 0xef, 0xde, + 0xef, 0x9c, 0xde, 0xd6, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, + 0xef, 0x08, 0xad, 0xef, + 0x00, 0x84, 0xbd, 0x08, 0x31, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x10, 0x5a, 0x9c, 0x08, 0xad, 0xd6, 0x00, 0xbd, 0xef, 0x10, 0xc5, + 0xef, 0x10, 0xc5, 0xef, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x19, + 0x94, 0xce, 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef, 0x08, + 0xad, 0xef, 0x08, 0xad, + 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, + 0xef, 0x00, 0xbd, 0xf7, + 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, + 0x10, 0xc5, 0xef, 0x00, + 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, + 0x7b, 0xbd, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x31, + 0x29, 0xce, 0xe6, 0xef, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xce, 0xe6, + 0xef, 0xce, 0xad, 0xad, 0x9c, 0xad, 0xce, 0x19, 0x94, 0xce, 0x08, 0xad, + 0xef, 0x00, 0xbd, 0xef, + 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6, 0x10, 0x5a, 0x9c, 0x08, 0x08, 0x10, + 0x00, 0x00, 0x00, 0x08, + 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, + 0x00, 0x00, 0x08, 0x08, + 0x10, 0x10, 0x73, 0x7b, 0x00, 0x9c, 0xd6, 0x00, 0xbd, 0xef, 0x10, 0xc5, + 0xef, 0x08, 0xad, 0xef, + 0x00, 0xbd, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x84, 0xbd, 0x00, 0x9c, 0xd6, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, + 0xad, 0xef, 0x10, 0xc5, + 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x19, 0xbd, 0xf7, 0x19, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, + 0x19, 0xbd, 0xf7, 0x00, + 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00, + 0xbd, 0xef, 0x10, 0xce, + 0xce, 0x08, 0x31, 0x3a, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x08, 0x10, 0xce, 0xde, 0xce, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xe6, 0xc5, 0xad, 0xd6, 0xce, 0xde, 0xce, 0x9c, 0xad, 0xce, 0x08, 0xad, + 0xd6, 0x08, 0xad, 0xef, + 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x00, 0x9c, 0xd6, 0x19, 0x7b, 0xbd, + 0x10, 0x7b, 0x9c, 0x08, + 0x31, 0x5a, 0x08, 0x31, 0x3a, 0x08, 0x08, 0x10, 0x08, 0x31, 0x3a, 0x08, + 0x31, 0x3a, 0x10, 0x5a, + 0x9c, 0x00, 0x84, 0xbd, 0x00, 0x9c, 0xd6, 0x19, 0x94, 0xce, 0x00, 0xbd, + 0xef, 0x08, 0xad, 0xef, + 0x10, 0xc5, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x19, 0x7b, 0xbd, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x08, + 0xad, 0xef, 0x00, 0xbd, + 0xef, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x10, 0xc5, + 0xef, 0x00, 0xbd, 0xf7, + 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xf7, + 0x19, 0xbd, 0xf7, 0x00, + 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, + 0xbd, 0xf7, 0x10, 0xc5, + 0xef, 0x00, 0xbd, 0xf7, 0x19, 0x94, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x7b, 0x94, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xa5, 0xb5, 0xb5, 0xce, 0xde, 0xce, 0x9c, 0xad, + 0xce, 0x19, 0x94, 0xce, + 0x08, 0xad, 0xd6, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xd6, 0x00, 0x9c, 0xd6, + 0x00, 0x9c, 0xd6, 0x00, + 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x19, + 0x7b, 0xbd, 0x00, 0x84, + 0xbd, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x08, 0xad, 0xef, 0x08, 0xad, + 0xef, 0x10, 0xc5, 0xef, + 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xef, 0x08, 0xad, 0xef, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0xbd, 0x08, 0xad, 0xd6, 0x08, + 0xad, 0xef, 0x10, 0xc5, + 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x19, 0xbd, + 0xf7, 0x00, 0xbd, 0xf7, + 0x19, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xef, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00, + 0xbd, 0xf7, 0x19, 0xbd, + 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xef, 0x10, 0x52, + 0x7b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6b, + 0x7b, 0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xef, 0xf7, + 0xe6, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xce, 0xad, 0xad, 0xce, 0xde, + 0xce, 0xa5, 0xb5, 0xb5, + 0x00, 0x9c, 0xd6, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, + 0x19, 0x94, 0xce, 0x00, + 0x9c, 0xd6, 0x19, 0x94, 0xce, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x00, + 0x84, 0xbd, 0x00, 0x84, + 0xbd, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x08, 0xad, + 0xef, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, + 0x10, 0xc5, 0xef, 0x19, + 0x94, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x7b, 0xbd, 0x19, + 0x94, 0xce, 0x08, 0xad, + 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, + 0xf7, 0x00, 0xbd, 0xf7, + 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, + 0x00, 0xbd, 0xf7, 0x08, + 0xad, 0xef, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, + 0xbd, 0xf7, 0x19, 0xbd, + 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x10, 0xc5, + 0xef, 0x08, 0xad, 0xd6, + 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xce, 0xde, 0xce, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xce, 0xde, + 0xce, 0xc5, 0xad, 0xd6, + 0xa5, 0xb5, 0xb5, 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6, 0x08, 0xad, 0xd6, + 0x08, 0xad, 0xef, 0x08, + 0xad, 0xef, 0x08, 0xad, 0xef, 0x00, 0x9c, 0xd6, 0x00, 0x9c, 0xd6, 0x00, + 0x9c, 0xd6, 0x00, 0x9c, + 0xd6, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x08, 0xad, + 0xef, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, + 0x10, 0xc5, 0xef, 0x08, + 0xad, 0xef, 0x08, 0xad, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x84, 0xbd, 0x00, 0x9c, + 0xd6, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x19, 0xbd, + 0xf7, 0x00, 0xbd, 0xef, + 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, + 0xc5, 0xef, 0x00, 0xbd, + 0xf7, 0x10, 0xc5, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x00, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xef, 0x19, 0x7b, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x5a, 0x73, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xe6, 0xce, 0xde, 0xce, + 0xa5, 0xb5, 0xb5, 0x9c, 0xad, 0xce, 0x00, 0x84, 0xbd, 0x00, 0x9c, 0xd6, + 0x08, 0xad, 0xef, 0x10, + 0xc5, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef, 0x19, + 0x94, 0xce, 0x08, 0xad, + 0xef, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x08, 0xad, + 0xef, 0x00, 0xbd, 0xef, + 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, + 0x19, 0xbd, 0xf7, 0x00, + 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x7b, 0xe6, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef, 0x00, 0xbd, + 0xef, 0x00, 0xbd, 0xf7, + 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x08, + 0xad, 0xef, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, + 0xad, 0xef, 0x00, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x00, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, + 0x10, 0xc5, 0xef, 0x10, 0xe6, 0xef, 0x00, 0xbd, 0xef, 0x08, 0x31, 0x3a, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, + 0x52, 0x4a, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xe6, 0xff, + 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf7, 0xff, + 0xe6, 0xde, 0xde, 0xc5, 0xad, 0xd6, 0x29, 0x5a, 0x4a, 0x00, 0x84, 0xbd, + 0x19, 0x94, 0xce, 0x08, + 0xad, 0xd6, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00, + 0xbd, 0xef, 0x08, 0xad, + 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef, 0x08, 0xad, + 0xef, 0x08, 0xad, 0xef, + 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x00, + 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, + 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x19, 0x94, 0xce, 0x08, 0xad, 0xd6, 0x08, 0xad, + 0xef, 0x19, 0xbd, 0xf7, + 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, + 0x10, 0xc5, 0xef, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, + 0xc5, 0xef, 0x19, 0xbd, + 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, + 0xf7, 0x00, 0xbd, 0xf7, + 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, + 0x08, 0xad, 0xd6, 0x08, + 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x9c, 0xa5, + 0x94, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xf7, + 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xe6, 0xff, 0xf7, 0xff, + 0xef, 0xf7, 0xff, 0xce, 0xde, 0xce, 0x4a, 0x52, 0x4a, 0x08, 0x31, 0x5a, + 0x00, 0x84, 0xbd, 0x00, + 0x9c, 0xd6, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x10, + 0xc5, 0xef, 0x08, 0xad, + 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xef, 0x10, 0xc5, + 0xef, 0x08, 0xad, 0xef, + 0x00, 0xbd, 0xef, 0x10, 0xc5, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, + 0x19, 0xbd, 0xf7, 0x00, + 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, + 0xbd, 0xef, 0x00, 0xbd, + 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0xd6, 0x19, 0x94, + 0xce, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, + 0x08, 0xad, 0xef, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10, + 0xc5, 0xef, 0x00, 0xbd, + 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, + 0xf7, 0x00, 0xbd, 0xf7, + 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, + 0x10, 0xc5, 0xef, 0x10, + 0xc5, 0xef, 0x10, 0x5a, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x9c, 0x7b, + 0x94, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4a, 0x52, 0x4a, 0x08, 0x00, 0x00, + 0x08, 0x31, 0x5a, 0x00, + 0x84, 0xbd, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, + 0xad, 0xef, 0x19, 0xbd, + 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, + 0x00, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x94, + 0xce, 0x00, 0x9c, 0xd6, + 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, + 0x19, 0xbd, 0xf7, 0x00, + 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, + 0x08, 0xad, 0xef, 0x00, + 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xd6, 0x10, 0x73, 0x7b, 0xe6, + 0xde, 0xde, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0x31, 0x31, 0x29, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, + 0x52, 0x7b, 0x00, 0x84, 0xbd, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef, 0x08, + 0xad, 0xef, 0x00, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad, + 0xef, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, + 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, + 0xf7, 0x00, 0xbd, 0xf7, + 0x00, 0xbd, 0xef, 0x08, 0xad, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x84, 0xbd, + 0x00, 0x9c, 0xd6, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x08, 0xad, 0xef, + 0x19, 0xbd, 0xf7, 0x00, + 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, + 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xd6, 0x10, 0xc5, 0xef, 0x19, + 0x94, 0xce, 0x9c, 0xad, + 0xce, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xe6, 0xff, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xe6, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xff, 0xef, 0xde, 0xef, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x52, 0x7b, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x08, + 0xad, 0xef, 0x08, 0xad, + 0xef, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10, 0xc5, + 0xef, 0x00, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, + 0x00, 0xbd, 0xf7, 0x10, + 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x08, + 0xad, 0xef, 0x10, 0xc5, + 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10, 0xc5, + 0xef, 0x00, 0xbd, 0xf7, + 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x84, 0xbd, + 0x00, 0x84, 0xbd, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x00, + 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, + 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, + 0xf7, 0x10, 0xc5, 0xef, + 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, + 0x00, 0xbd, 0xf7, 0x10, + 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, + 0xc5, 0xef, 0x08, 0xad, + 0xd6, 0x10, 0x7b, 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, + 0xff, 0xef, 0xf7, 0xff, + 0xff, 0xff, 0xff, 0x9c, 0xa5, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x08, 0x10, 0x10, 0x52, 0x7b, 0x00, 0x84, 0xbd, 0x08, + 0xad, 0xd6, 0x00, 0xbd, + 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, + 0x08, 0xad, 0xef, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, + 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x19, 0x94, 0xce, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, + 0x00, 0xbd, 0xef, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, + 0xf7, 0x10, 0xc5, 0xef, + 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, + 0x08, 0xad, 0xef, 0x00, + 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x00, + 0xbd, 0xef, 0x10, 0xc5, + 0xef, 0x08, 0xad, 0xd6, 0x00, 0x84, 0xbd, 0x10, 0x52, 0x7b, 0xef, 0xf7, + 0xe6, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf7, 0xff, + 0xce, 0xde, 0xce, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x52, 0x7b, 0x00, + 0x84, 0xbd, 0x19, 0x94, + 0xce, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, + 0x19, 0xbd, 0xf7, 0x00, + 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, + 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, + 0xef, 0x19, 0xbd, 0xf7, + 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x00, 0x9c, 0xd6, 0x19, 0x7b, 0xbd, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x00, 0xbd, 0xf7, + 0x19, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, + 0xef, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x10, 0xc5, + 0xef, 0x08, 0xad, 0xef, + 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, + 0x00, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, + 0xbd, 0xf7, 0x19, 0xbd, + 0xf7, 0x00, 0xbd, 0xef, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xd6, 0x10, 0x52, + 0x7b, 0x3a, 0x31, 0x4a, + 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, + 0xff, 0xff, 0xff, 0xef, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xce, 0xde, 0xce, + 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x08, 0x08, 0x10, 0x10, + 0x52, 0x7b, 0x00, 0x84, + 0xbd, 0x00, 0x9c, 0xd6, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00, 0xbd, + 0xf7, 0x10, 0xc5, 0xef, + 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x00, 0xbd, 0xf7, + 0x19, 0xbd, 0xf7, 0x00, + 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, + 0xbd, 0xf7, 0x00, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, + 0xef, 0x08, 0xad, 0xef, + 0x08, 0xad, 0xef, 0x19, 0x94, 0xce, 0x19, 0x94, 0xce, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x19, 0x7b, 0xbd, 0x00, 0x84, 0xbd, 0x08, 0xad, 0xef, + 0x10, 0xc5, 0xef, 0x00, + 0xbd, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, + 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad, + 0xef, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x10, + 0xc5, 0xef, 0x00, 0xbd, + 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x00, 0x9c, + 0xd6, 0x10, 0x7b, 0x9c, + 0x08, 0x31, 0x5a, 0x00, 0x00, 0x00, 0x4a, 0x5a, 0x73, 0xce, 0xde, 0xce, + 0xef, 0xf7, 0xe6, 0xff, + 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xef, 0xf7, + 0xe6, 0xef, 0xf7, 0xff, 0xef, 0xf7, 0xe6, 0xa5, 0xb5, 0xb5, 0x6b, 0x5a, + 0x73, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x31, 0x3a, 0x10, 0x52, + 0x7b, 0x19, 0x7b, 0xbd, 0x00, 0x9c, 0xd6, 0x08, 0xad, 0xef, 0x10, 0xc5, + 0xef, 0x19, 0xbd, 0xf7, + 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, + 0x08, 0xad, 0xef, 0x10, + 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, + 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, + 0xef, 0x08, 0xad, 0xef, + 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6, 0x19, 0x7b, 0xbd, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0xbd, 0x00, 0x9c, 0xd6, + 0x19, 0x94, 0xce, 0x08, + 0xad, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00, + 0xbd, 0xf7, 0x10, 0xc5, + 0xef, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, + 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, + 0x00, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x19, + 0xbd, 0xf7, 0x08, 0xad, + 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x08, 0xad, + 0xd6, 0x19, 0x94, 0xce, + 0x00, 0x84, 0xbd, 0x10, 0x52, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x10, 0x10, 0x29, 0x19, 0x31, 0x31, 0x29, 0x31, + 0x31, 0x29, 0x08, 0x08, + 0x10, 0x08, 0x08, 0x10, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x31, + 0x3a, 0x10, 0x52, 0x7b, 0x00, 0x84, 0xbd, 0x00, 0x9c, 0xd6, 0x08, 0xad, + 0xef, 0x10, 0xc5, 0xef, + 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, + 0xbd, 0xf7, 0x08, 0xad, + 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, + 0xef, 0x00, 0x84, 0xbd, + 0x19, 0x94, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0xbd, + 0x19, 0x94, 0xce, 0x00, + 0x9c, 0xd6, 0x00, 0x9c, 0xd6, 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x08, + 0xad, 0xef, 0x08, 0xad, + 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, + 0xef, 0x08, 0xad, 0xef, + 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, + 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, + 0xef, 0x08, 0xad, 0xef, + 0x00, 0x9c, 0xd6, 0x10, 0x5a, 0x9c, 0x10, 0x52, 0x7b, 0x08, 0x31, 0x3a, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x08, 0x00, 0x00, 0x08, + 0x08, 0x10, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x31, 0x5a, 0x10, 0x5a, 0x9c, 0x00, 0x84, 0xbd, 0x19, 0x94, + 0xce, 0x08, 0xad, 0xef, + 0x08, 0xad, 0xef, 0x00, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, + 0x00, 0xbd, 0xf7, 0x10, + 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10, + 0xc5, 0xef, 0x00, 0xbd, + 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x19, 0x94, 0xce, 0x00, 0x9c, + 0xd6, 0x00, 0x84, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x7b, 0x9c, 0x00, + 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x00, 0x84, 0xbd, 0x19, + 0x94, 0xce, 0x00, 0x9c, + 0xd6, 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6, 0x08, 0xad, 0xd6, 0x08, 0xad, + 0xef, 0x08, 0xad, 0xd6, + 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, + 0x10, 0xc5, 0xef, 0x08, + 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, + 0xef, 0x10, 0xc5, 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x00, 0xbd, + 0xf7, 0x08, 0xad, 0xd6, + 0x19, 0x94, 0xce, 0x00, 0x84, 0xbd, 0x10, 0x7b, 0x9c, 0x10, 0x52, 0x7b, + 0x08, 0x31, 0x5a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x31, 0x3a, 0x10, 0x52, 0x7b, 0x00, 0x84, + 0xbd, 0x00, 0x84, 0xbd, + 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, + 0x00, 0xbd, 0xf7, 0x10, + 0xc5, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x00, + 0xbd, 0xf7, 0x08, 0xad, + 0xef, 0x19, 0x94, 0xce, 0x08, 0xad, 0xd6, 0x00, 0x9c, 0xd6, 0x00, 0x9c, + 0xd6, 0x10, 0x5a, 0x9c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x10, + 0x5a, 0x9c, 0x00, 0x84, + 0xbd, 0x19, 0x7b, 0xbd, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x19, 0x94, + 0xce, 0x00, 0x84, 0xbd, + 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, + 0x19, 0x94, 0xce, 0x08, + 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xef, 0x19, + 0xbd, 0xf7, 0x00, 0xbd, + 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xf7, 0x10, 0xc5, + 0xef, 0x08, 0xad, 0xef, + 0x08, 0xad, 0xef, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x10, 0x5a, 0x9c, + 0x10, 0x52, 0x7b, 0x08, + 0x31, 0x3a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x31, 0x5a, 0x10, 0x52, + 0x7b, 0x00, 0x84, 0xbd, + 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef, + 0x19, 0xbd, 0xf7, 0x08, + 0xad, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00, + 0xbd, 0xef, 0x08, 0xad, + 0xef, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x00, 0x84, 0xbd, 0x00, 0x84, + 0xbd, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x73, 0x7b, 0x10, 0x5a, 0x9c, 0x10, 0x7b, + 0x9c, 0x10, 0x5a, 0xbd, + 0x00, 0x84, 0xbd, 0x10, 0x7b, 0x9c, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, + 0x00, 0x84, 0xbd, 0x00, + 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6, 0x08, + 0xad, 0xef, 0x08, 0xad, + 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, + 0xef, 0x08, 0xad, 0xef, + 0x08, 0xad, 0xd6, 0x19, 0x94, 0xce, 0x00, 0x84, 0xbd, 0x10, 0x5a, 0x9c, + 0x10, 0x52, 0x7b, 0x10, + 0x52, 0x7b, 0x08, 0x31, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x21, 0x00, 0x08, + 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x31, + 0x3a, 0x10, 0x52, 0x7b, + 0x10, 0x5a, 0x9c, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x08, 0xad, 0xef, + 0x08, 0xad, 0xd6, 0x08, + 0xad, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x08, + 0xad, 0xef, 0x19, 0x94, + 0xce, 0x00, 0x9c, 0xd6, 0x00, 0x84, 0xbd, 0x19, 0x7b, 0xbd, 0x10, 0x5a, + 0x9c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x5a, 0x9c, 0x10, 0x52, 0x7b, 0x10, 0x5a, 0x9c, + 0x10, 0x5a, 0x9c, 0x10, + 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x7b, 0x9c, 0x00, 0x84, 0xbd, 0x00, + 0x84, 0xbd, 0x19, 0x94, + 0xce, 0x00, 0x9c, 0xd6, 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6, 0x08, 0xad, + 0xef, 0x08, 0xad, 0xd6, + 0x19, 0x94, 0xce, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x10, 0x5a, 0x9c, + 0x10, 0x52, 0x7b, 0x10, + 0x52, 0x7b, 0x08, 0x31, 0x5a, 0x08, 0x08, 0x10, 0x08, 0x08, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x31, 0x3a, + 0x08, 0x31, 0x5a, 0x10, 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x19, 0x7b, 0xbd, + 0x00, 0x84, 0xbd, 0x00, + 0x9c, 0xd6, 0x08, 0xad, 0xd6, 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6, 0x19, + 0x94, 0xce, 0x00, 0x9c, + 0xd6, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x10, 0x5a, + 0x9c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x52, 0x7b, 0x10, 0x52, 0x7b, 0x10, 0x5a, 0x9c, 0x10, + 0x5a, 0x9c, 0x10, 0x5a, + 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x7b, 0x9c, 0x00, 0x84, 0xbd, 0x19, 0x7b, + 0xbd, 0x00, 0x84, 0xbd, + 0x19, 0x7b, 0xbd, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x10, 0x5a, 0x9c, + 0x10, 0x52, 0x7b, 0x10, + 0x52, 0x7b, 0x08, 0x31, 0x5a, 0x08, 0x31, 0x5a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x31, 0x5a, 0x10, 0x5a, 0x9c, 0x10, 0x7b, 0x9c, + 0x00, 0x84, 0xbd, 0x00, + 0x84, 0xbd, 0x19, 0x7b, 0xbd, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x00, + 0x84, 0xbd, 0x00, 0x84, + 0xbd, 0x19, 0x7b, 0xbd, 0x10, 0x7b, 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x52, + 0x7b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x31, 0x5a, 0x10, 0x52, 0x7b, 0x10, 0x52, 0x7b, 0x10, 0x5a, + 0x9c, 0x10, 0x73, 0x7b, + 0x10, 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x52, 0x7b, + 0x10, 0x52, 0x7b, 0x10, + 0x52, 0x7b, 0x08, 0x31, 0x5a, 0x08, 0x31, 0x3a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x31, 0x5a, 0x08, 0x31, 0x5a, + 0x10, 0x52, 0x7b, 0x10, + 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x7b, 0x9c, 0x10, 0x5a, 0x9c, 0x00, + 0x84, 0xbd, 0x00, 0x84, + 0xbd, 0x10, 0x7b, 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x08, 0x31, + 0x5a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x31, + 0x5a, 0x10, 0x52, 0x7b, + 0x08, 0x31, 0x5a, 0x10, 0x52, 0x7b, 0x10, 0x52, 0x7b, 0x08, 0x31, 0x5a, + 0x10, 0x52, 0x7b, 0x08, + 0x31, 0x5a, 0x08, 0x31, 0x5a, 0x08, 0x31, 0x3a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x31, 0x5a, 0x08, + 0x31, 0x5a, 0x10, 0x52, 0x7b, 0x10, 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x10, + 0x5a, 0x9c, 0x10, 0x5a, + 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x52, 0x7b, 0x10, 0x52, 0x7b, 0x08, 0x31, + 0x5a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x31, 0x5a, 0x08, 0x31, 0x5a, + 0x08, 0x31, 0x5a, 0x08, + 0x31, 0x5a, 0x08, 0x31, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x31, 0x3a, 0x08, 0x31, 0x5a, 0x08, + 0x31, 0x5a, 0x10, 0x52, + 0x7b, 0x08, 0x31, 0x5a, 0x08, 0x31, 0x5a, 0x08, 0x31, 0x3a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +#endif |