Merge "iommu/msm: Add GDSC regulator control" into msm-3.4
diff --git a/arch/arm/boot/dts/msm9625.dts b/arch/arm/boot/dts/msm9625.dts
index b7b8be2..6c007fb 100644
--- a/arch/arm/boot/dts/msm9625.dts
+++ b/arch/arm/boot/dts/msm9625.dts
@@ -41,6 +41,15 @@
clock-frequency = <5000000>;
};
+ qcom,sps@f9980000 {
+ compatible = "qcom,msm_sps";
+ reg = <0xf9984000 0x15000>,
+ <0xf9999000 0xb000>,
+ <0xfe800000 0x4800>;
+ interrupts = <0 94 0>;
+ qcom,device-type = <2>;
+ };
+
serial@f991f000 {
compatible = "qcom,msm-lsuart-v14";
reg = <0xf991f000 0x1000>;
diff --git a/arch/arm/boot/dts/msmcopper.dtsi b/arch/arm/boot/dts/msmcopper.dtsi
index a37f4ff..79d6814 100644
--- a/arch/arm/boot/dts/msmcopper.dtsi
+++ b/arch/arm/boot/dts/msmcopper.dtsi
@@ -274,7 +274,7 @@
qcom,ssusb@F9200000 {
compatible = "qcom,dwc-usb3-msm";
- reg = <0xF9200000 0xCCFF>;
+ reg = <0xF9200000 0xFA000>;
interrupts = <0 131 0>;
SSUSB_VDDCX-supply = <&pm8841_s2>;
SSUSB_1p8-supply = <&pm8941_l6>;
@@ -296,6 +296,53 @@
qcom,firmware-name = "adsp";
};
+ qcom,msm-pcm {
+ compatible = "qcom,msm-pcm-dsp";
+ };
+
+ qcom,msm-pcm-routing {
+ compatible = "qcom,msm-pcm-routing";
+ };
+
+ qcom,msm-pcm-lpa {
+ compatible = "qcom,msm-pcm-lpa";
+ };
+
+ qcom,msm-voip-dsp {
+ compatible = "qcom,msm-voip-dsp";
+ };
+
+ qcom,msm-stub-codec {
+ compatible = "qcom,msm-stub-codec";
+ };
+
+ qcom,msm-dai-fe {
+ compatible = "qcom,msm-dai-fe";
+ };
+
+ qcom,msm-dai-q6 {
+ compatible = "qcom,msm-dai-q6";
+ qcom,msm-cpudai-auxpcm-clk = "pcm_clk";
+ qcom,msm-cpudai-auxpcm-mode = <0>;
+ qcom,msm-cpudai-auxpcm-sync = <1>;
+ qcom,msm-cpudai-auxpcm-frame = <5>;
+ qcom,msm-cpudai-auxpcm-quant = <2>;
+ qcom,msm-cpudai-auxpcm-slot = <1>;
+ qcom,msm-cpudai-auxpcm-data = <0>;
+ qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>;
+
+ qcom,msm-dai-q6-rx {
+ qcom,msm-dai-q6-id = <4106>;
+ };
+ qcom,msm-dai-q6-tx {
+ qcom,msm-dai-q6-id = <4107>;
+ };
+ };
+
+ qcom,msm-pcm-hostless {
+ compatible = "qcom,msm-pcm-hostless";
+ };
+
qcom,mss@fc880000 {
compatible = "qcom,pil-q6v5-mss";
reg = <0xfc880000 0x100>,
@@ -345,4 +392,18 @@
qcom,qseecom@fe806000 {
compatible = "qcom,qseecom";
};
+
+ qcom,mdss_mdp@fd900000 {
+ cell-index = <0>;
+ compatible = "qcom,mdss_mdp";
+ reg = <0xfd900000 0x22100>;
+ interrupts = <0 72 0>;
+ };
+
+ qcom,mdss_wb_panel {
+ cell-index = <1>;
+ compatible = "qcom,mdss_wb";
+ qcom,mdss_pan_res = <640 480>;
+ qcom,mdss_pan_bpp = <24>;
+ };
};
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 54a8392..b14ecf8 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -248,28 +248,17 @@
u32 enabled;
unsigned long pending[32];
void __iomem *base = gic_data_dist_base(gic);
-#ifdef CONFIG_ARCH_MSM8625
- unsigned long flags;
-#endif
if (!msm_show_resume_irq_mask)
return;
-#ifdef CONFIG_ARCH_MSM8625
- raw_spin_lock_irqsave(&irq_controller_lock, flags);
-#else
raw_spin_lock(&irq_controller_lock);
-#endif
for (i = 0; i * 32 < gic->max_irq; i++) {
enabled = readl_relaxed(base + GIC_DIST_ENABLE_CLEAR + i * 4);
pending[i] = readl_relaxed(base + GIC_DIST_PENDING_SET + i * 4);
pending[i] &= enabled;
}
-#ifdef CONFIG_ARCH_MSM8625
- raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
-#else
- raw_spin_lock(&irq_controller_lock);
-#endif
+ raw_spin_unlock(&irq_controller_lock);
for (i = find_first_bit(pending, gic->max_irq);
i < gic->max_irq;
@@ -283,22 +272,14 @@
{
unsigned int i;
void __iomem *base = gic_data_dist_base(gic);
-#ifdef CONFIG_ARCH_MSM8625
- unsigned long flags;
-#endif
+
gic_show_resume_irq(gic);
for (i = 0; i * 32 < gic->max_irq; i++) {
-#ifdef CONFIG_ARCH_MSM8625
- raw_spin_lock_irqsave(&irq_controller_lock, flags);
-#endif
/* disable all of them */
writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4);
/* enable the enabled set */
writel_relaxed(gic->enabled_irqs[i],
base + GIC_DIST_ENABLE_SET + i * 4);
-#ifdef CONFIG_ARCH_MSM8625
- raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
-#endif
}
mb();
}
@@ -1081,9 +1062,11 @@
return 0;
}
#endif
-/* before calling this function the interrupts should be disabled
- * and the irq must be disabled at gic to avoid spurious interrupts */
-bool gic_is_spi_pending(unsigned int irq)
+/*
+ * Before calling this function the interrupts should be disabled
+ * and the irq must be disabled at gic to avoid spurious interrupts
+ */
+bool gic_is_irq_pending(unsigned int irq)
{
struct irq_data *d = irq_get_irq_data(irq);
struct gic_chip_data *gic_data = &gic_data[0];
@@ -1102,9 +1085,11 @@
return (bool) (val & mask);
}
-/* before calling this function the interrupts should be disabled
- * and the irq must be disabled at gic to avoid spurious interrupts */
-void gic_clear_spi_pending(unsigned int irq)
+/*
+ * Before calling this function the interrupts should be disabled
+ * and the irq must be disabled at gic to avoid spurious interrupts
+ */
+void gic_clear_irq_pending(unsigned int irq)
{
struct gic_chip_data *gic_data = &gic_data[0];
struct irq_data *d = irq_get_irq_data(irq);
diff --git a/arch/arm/configs/msm-copper_defconfig b/arch/arm/configs/msm-copper_defconfig
index 49fae4a..78ab155 100644
--- a/arch/arm/configs/msm-copper_defconfig
+++ b/arch/arm/configs/msm-copper_defconfig
@@ -10,7 +10,6 @@
CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
CONFIG_CGROUP_SCHED=y
-# CONFIG_FAIR_GROUP_SCHED is not set
CONFIG_RT_GROUP_SCHED=y
CONFIG_NAMESPACES=y
# CONFIG_UTS_NS is not set
@@ -75,15 +74,26 @@
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
CONFIG_PM_RUNTIME=y
CONFIG_NET=y
+CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_INET=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_VERBOSE=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
CONFIG_IPV6=y
CONFIG_IPV6_PRIVACY=y
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_IPV6_ROUTE_INFO=y
CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
CONFIG_IPV6_MIP6=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_SUBTREES=y
@@ -98,9 +108,13 @@
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
CONFIG_NETDEVICES=y
CONFIG_KS8851=m
# CONFIG_MSM_RMNET is not set
+CONFIG_DUMMY=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_EVBUG=m
CONFIG_INPUT_JOYSTICK=y
@@ -141,7 +155,14 @@
CONFIG_ION=y
CONFIG_ION_MSM=y
CONFIG_FB=y
-CONFIG_FB_VIRTUAL=y
+CONFIG_FB_MSM=y
+# CONFIG_FB_MSM_BACKLIGHT is not set
+CONFIG_FB_MSM_MDSS=y
+CONFIG_FB_MSM_MDSS_WRITEBACK=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_GENERIC is not set
# CONFIG_HID_SUPPORT is not set
CONFIG_USB_GADGET=y
CONFIG_USB_CI13XXX_MSM=y
@@ -154,8 +175,6 @@
# CONFIG_MMC_BLOCK_BOUNCE is not set
CONFIG_MMC_TEST=m
CONFIG_MMC_MSM=y
-CONFIG_NEW_LEDS=y
-CONFIG_LEDS_CLASS=y
CONFIG_SWITCH=y
CONFIG_STAGING=y
CONFIG_ANDROID=y
@@ -174,6 +193,7 @@
CONFIG_EXT3_FS=y
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
CONFIG_EXT4_FS=y
+CONFIG_FUSE_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_NLS_CODEPAGE_437=y
@@ -198,6 +218,7 @@
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_MD5=y
CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_SHA256=y
CONFIG_CRYPTO_AES=y
CONFIG_CRYPTO_ARC4=y
CONFIG_CRYPTO_DES=y
@@ -205,3 +226,7 @@
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRC_CCITT=y
CONFIG_LIBCRC32C=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_MSM8974=y
diff --git a/arch/arm/configs/msm7630-perf_defconfig b/arch/arm/configs/msm7630-perf_defconfig
index 136dc46a..7de7fee8 100644
--- a/arch/arm/configs/msm7630-perf_defconfig
+++ b/arch/arm/configs/msm7630-perf_defconfig
@@ -261,6 +261,7 @@
# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
CONFIG_VIDEOBUF2_MSM_MEM=y
CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_RADIO_TAVARUA=y
CONFIG_MSM_KGSL=y
CONFIG_VIDEO_OUTPUT_CONTROL=y
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index 47d6ea8..57e644d 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -90,6 +90,7 @@
CONFIG_MSM_DLOAD_MODE=y
CONFIG_MSM_QDSS=y
CONFIG_MSM_SLEEP_STATS=y
+CONFIG_MSM_EBI_ERP=y
CONFIG_MSM_CACHE_ERP=y
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
CONFIG_MSM_DCVS=y
@@ -388,6 +389,7 @@
CONFIG_USB_EHCI_EHSET=y
CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_MSM_HSIC=y
+CONFIG_USB_EHCI_MSM_HOST4=y
CONFIG_USB_ACM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_STORAGE_DEBUG=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index a0636f9..ca8a909 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -91,6 +91,7 @@
CONFIG_MSM_QDSS_ETM_DEFAULT_ENABLE=y
CONFIG_MSM_RTB=y
CONFIG_MSM_RTB_SEPARATE_CPUS=y
+CONFIG_MSM_EBI_ERP=y
CONFIG_MSM_CACHE_ERP=y
CONFIG_MSM_L1_ERR_PANIC=y
CONFIG_MSM_L2_ERP_PRINT_ACCESS_ERRORS=y
@@ -391,6 +392,7 @@
CONFIG_USB_EHCI_EHSET=y
CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_MSM_HSIC=y
+CONFIG_USB_EHCI_MSM_HOST4=y
CONFIG_USB_ACM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_STORAGE_DEBUG=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index 1e45f66..bb3554b 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -85,6 +85,9 @@
CONFIG_DEBUG_MEMORY_INIT=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_BAMDMA=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_KEYS=y
CONFIG_CRYPTO_AUTHENC=y
CONFIG_CRYPTO_CBC=y
diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h
index 3fb0a1c..3783ff3 100644
--- a/arch/arm/include/asm/hardware/gic.h
+++ b/arch/arm/include/asm/hardware/gic.h
@@ -46,6 +46,8 @@
void gic_handle_irq(struct pt_regs *regs);
void gic_cascade_irq(unsigned int gic_nr, unsigned int irq);
void gic_raise_softirq(const struct cpumask *mask, unsigned int irq);
+bool gic_is_irq_pending(unsigned int irq);
+void gic_clear_irq_pending(unsigned int irq);
#ifdef CONFIG_ARM_GIC
void gic_set_irq_secure(unsigned int irq);
#else
@@ -56,8 +58,6 @@
{
gic_init_bases(nr, start, dist, cpu, 0);
}
-bool gic_is_spi_pending(unsigned int irq);
-void gic_clear_spi_pending(unsigned int irq);
void gic_set_irq_secure(unsigned int irq);
#endif
diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h
index a6ec7b2..4bcbfc2 100644
--- a/arch/arm/include/asm/mach/mmc.h
+++ b/arch/arm/include/asm/mach/mmc.h
@@ -148,7 +148,7 @@
bool nonremovable;
unsigned int mpm_sdiowakeup_int;
unsigned int wpswitch_gpio;
- unsigned char wpswitch_polarity;
+ bool is_wpswitch_active_low;
struct msm_mmc_slot_reg_data *vreg_data;
int is_sdio_al_client;
unsigned int *sup_clk_table;
diff --git a/arch/arm/include/asm/perf_event.h b/arch/arm/include/asm/perf_event.h
index 2fecc60..a40f81e 100644
--- a/arch/arm/include/asm/perf_event.h
+++ b/arch/arm/include/asm/perf_event.h
@@ -25,7 +25,9 @@
ARM_PERF_PMU_ID_CA7,
ARM_PERF_PMU_ID_SCORPION,
ARM_PERF_PMU_ID_SCORPIONMP,
+ ARM_PERF_PMU_ID_SCORPIONMP_L2,
ARM_PERF_PMU_ID_KRAIT,
+ ARM_PERF_PMU_ID_KRAIT_L2,
ARM_NUM_PMU_IDS,
};
diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h
index 1e54b58..37cbfcb 100644
--- a/arch/arm/include/asm/pmu.h
+++ b/arch/arm/include/asm/pmu.h
@@ -21,7 +21,7 @@
*/
enum arm_pmu_type {
ARM_PMU_DEVICE_CPU = 0,
- ARM_PMU_DEVICE_L2 = 1,
+ ARM_PMU_DEVICE_L2CC = 1,
ARM_NUM_PMU_DEVICES,
};
@@ -129,11 +129,13 @@
u64 max_period;
struct platform_device *plat_device;
struct pmu_hw_events *(*get_hw_events)(void);
+ int (*test_set_event_constraints)(struct perf_event *event);
+ int (*clear_event_constraints)(struct perf_event *event);
};
#define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu))
-int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type);
+int armpmu_register(struct arm_pmu *armpmu, char *name, int type);
u64 armpmu_event_update(struct perf_event *event,
struct hw_perf_event *hwc,
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index af6e7e6..e37b28b 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -28,6 +28,8 @@
#include <asm/pmu.h>
#include <asm/stacktrace.h>
+#include <linux/cpu_pm.h>
+
/*
* ARMv6 supports a maximum of 3 events, starting from index 0. If we add
* another platform that supports more, we need to increase this to be the
@@ -271,6 +273,10 @@
hw_events->events[idx] = NULL;
clear_bit(idx, hw_events->used_mask);
+ /* Clear event constraints. */
+ if (armpmu->clear_event_constraints)
+ armpmu->clear_event_constraints(event);
+
perf_event_update_userpage(event);
}
@@ -284,6 +290,17 @@
int err = 0;
perf_pmu_disable(event->pmu);
+ /*
+ * Tests if event is constrained. If not sets it so that next
+ * collision can be detected.
+ */
+ if (armpmu->test_set_event_constraints)
+ if (armpmu->test_set_event_constraints(event) < 0) {
+ pr_err("Event: %llx failed constraint check.\n",
+ event->attr.config);
+ event->state = PERF_EVENT_STATE_OFF;
+ goto out;
+ }
/* If we don't have a space for the counter then finish early. */
idx = armpmu->get_event_idx(hw_events, hwc);
@@ -383,10 +400,7 @@
{
int i, irq, irqs;
struct platform_device *pmu_device = armpmu->plat_device;
-#if 0
- struct arm_pmu_platdata *plat =
- dev_get_platdata(&pmu_device->dev);
-#endif
+
irqs = min(pmu_device->num_resources, num_possible_cpus());
for (i = 0; i < irqs; ++i) {
@@ -394,13 +408,6 @@
continue;
irq = platform_get_irq(pmu_device, i);
armpmu->free_pmu_irq(irq);
-#if 0
- if (irq >= 0) {
- if (plat && plat->disable_irq)
- plat->disable_irq(irq);
- free_irq(irq, armpmu);
- }
-#endif
}
release_pmu(armpmu->type);
@@ -462,19 +469,6 @@
return err;
}
-#if 0
- err = request_irq(irq, handle_irq,
- IRQF_DISABLED | IRQF_NOBALANCING,
- "arm-pmu", armpmu);
- if (err) {
- pr_err("unable to request IRQ%d for ARM PMU counters\n",
- irq);
- armpmu_release_hardware(armpmu);
- return err;
- } else if (plat && plat->enable_irq)
- plat->enable_irq(irq);
-#endif
-
cpumask_set_cpu(i, &armpmu->active_irqs);
}
@@ -538,6 +532,7 @@
return -EPERM;
}
+
/*
* Store the event encoding into the config_base field.
*/
@@ -616,7 +611,7 @@
armpmu->stop();
}
-static void __init armpmu_init(struct arm_pmu *armpmu)
+static void armpmu_init(struct arm_pmu *armpmu)
{
atomic_set(&armpmu->active_events, 0);
mutex_init(&armpmu->reserve_mutex);
@@ -633,7 +628,7 @@
};
}
-int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type)
+int armpmu_register(struct arm_pmu *armpmu, char *name, int type)
{
armpmu_init(armpmu);
return perf_pmu_register(&armpmu->pmu, name, type);
@@ -644,6 +639,7 @@
#include "perf_event_v6.c"
#include "perf_event_v7.c"
#include "perf_event_msm_krait.c"
+#include "perf_event_msm.c"
/*
* Ensure the PMU has sane values out of reset.
@@ -734,10 +730,76 @@
return NOTIFY_OK;
}
+static void armpmu_update_counters(void)
+{
+ struct pmu_hw_events *hw_events;
+ int idx;
+
+ if (!cpu_pmu)
+ return;
+
+ hw_events = cpu_pmu->get_hw_events();
+
+ for (idx = 0; idx <= cpu_pmu->num_events; ++idx) {
+ struct perf_event *event = hw_events->events[idx];
+
+ if (!event)
+ continue;
+
+ armpmu_read(event);
+ }
+}
+
+static int cpu_has_active_perf(void)
+{
+ struct pmu_hw_events *hw_events;
+ int enabled;
+
+ if (!cpu_pmu)
+ return 0;
+
+ hw_events = cpu_pmu->get_hw_events();
+ enabled = bitmap_weight(hw_events->used_mask, cpu_pmu->num_events);
+
+ if (enabled)
+ /*Even one event's existence is good enough.*/
+ return 1;
+
+ return 0;
+}
+
static struct notifier_block __cpuinitdata pmu_cpu_notifier = {
.notifier_call = pmu_cpu_notify,
};
+/*TODO: Unify with pending patch from ARM */
+static int perf_cpu_pm_notifier(struct notifier_block *self, unsigned long cmd,
+ void *v)
+{
+ switch (cmd) {
+ case CPU_PM_ENTER:
+ if (cpu_has_active_perf()) {
+ armpmu_update_counters();
+ perf_pmu_disable(&cpu_pmu->pmu);
+ }
+ break;
+
+ case CPU_PM_ENTER_FAILED:
+ case CPU_PM_EXIT:
+ if (cpu_has_active_perf() && cpu_pmu->reset) {
+ cpu_pmu->reset(NULL);
+ perf_pmu_enable(&cpu_pmu->pmu);
+ }
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block perf_cpu_pm_notifier_block = {
+ .notifier_call = perf_cpu_pm_notifier,
+};
+
/*
* CPU PMU identification and registration.
*/
@@ -790,11 +852,11 @@
} else if (0x51 == implementor) {
switch (part_number) {
case 0x00F0: /* 8x50 & 7x30*/
-// cpu_pmu = armv7_scorpion_pmu_init();
+ cpu_pmu = armv7_scorpion_pmu_init();
break;
case 0x02D0: /* 8x60 */
// fabricmon_pmu_init();
-// cpu_pmu = armv7_scorpionmp_pmu_init();
+ cpu_pmu = armv7_scorpionmp_pmu_init();
// scorpionmp_l2_pmu_init();
break;
case 0x0490: /* 8960 sim */
@@ -814,6 +876,7 @@
cpu_pmu_init(cpu_pmu);
register_cpu_notifier(&pmu_cpu_notifier);
armpmu_register(cpu_pmu, "cpu", PERF_TYPE_RAW);
+ cpu_pm_register_notifier(&perf_cpu_pm_notifier_block);
} else {
pr_info("no hardware support available\n");
}
diff --git a/arch/arm/kernel/perf_event_msm.c b/arch/arm/kernel/perf_event_msm.c
index 0ca5164..46fa8fe 100644
--- a/arch/arm/kernel/perf_event_msm.c
+++ b/arch/arm/kernel/perf_event_msm.c
@@ -12,7 +12,7 @@
*/
#include <linux/cpumask.h>
-
+#include <asm/cp15.h>
#include <asm/vfp.h>
#include <asm/system.h>
#include "../vfp/vfpinstr.h"
@@ -143,12 +143,16 @@
* combined.
*/
[C(OP_READ)] = {
- [C(RESULT_ACCESS)] = ARMV7_PERFCTR_DCACHE_ACCESS,
- [C(RESULT_MISS)] = ARMV7_PERFCTR_DCACHE_REFILL,
+ [C(RESULT_ACCESS)]
+ = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
+ [C(RESULT_MISS)]
+ = ARMV7_PERFCTR_L1_DCACHE_REFILL,
},
[C(OP_WRITE)] = {
- [C(RESULT_ACCESS)] = ARMV7_PERFCTR_DCACHE_ACCESS,
- [C(RESULT_MISS)] = ARMV7_PERFCTR_DCACHE_REFILL,
+ [C(RESULT_ACCESS)]
+ = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
+ [C(RESULT_MISS)]
+ = ARMV7_PERFCTR_L1_DCACHE_REFILL,
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
@@ -236,6 +240,13 @@
},
};
+static int msm_scorpion_map_event(struct perf_event *event)
+{
+ return map_cpu_event(event, &armv7_scorpion_perf_map,
+ &armv7_scorpion_perf_cache_map, 0xfffff);
+}
+
+
struct scorpion_evt {
/*
* The scorpion_evt_type field corresponds to the actual Scorpion
@@ -483,9 +494,9 @@
u32 v_orig_val;
u32 f_orig_val;
- /* CPACR Enable CP10 access */
+ /* CPACR Enable CP10 and CP11 access */
v_orig_val = get_copro_access();
- venum_new_val = v_orig_val | CPACC_SVC(10);
+ venum_new_val = v_orig_val | CPACC_SVC(10) | CPACC_SVC(11);
set_copro_access(venum_new_val);
/* Store orig venum val */
__get_cpu_var(venum_orig_val) = v_orig_val;
@@ -551,17 +562,13 @@
static void scorpion_clear_pmuregs(void)
{
- unsigned long flags;
-
scorpion_write_lpm0(0);
scorpion_write_lpm1(0);
scorpion_write_lpm2(0);
scorpion_write_l2lpm(0);
- raw_spin_lock_irqsave(&pmu_lock, flags);
scorpion_pre_vlpm();
scorpion_write_vlpm(0);
scorpion_post_vlpm();
- raw_spin_unlock_irqrestore(&pmu_lock, flags);
}
static void scorpion_clearpmu(u32 grp, u32 val, u32 evt_code)
@@ -585,9 +592,11 @@
u32 gr;
unsigned long event;
struct scorpion_evt evtinfo;
+ struct pmu_hw_events *events = cpu_pmu->get_hw_events();
+
/* Disable counter and interrupt */
- raw_spin_lock_irqsave(&pmu_lock, flags);
+ raw_spin_lock_irqsave(&events->pmu_lock, flags);
/* Disable counter */
armv7_pmnc_disable_counter(idx);
@@ -596,7 +605,7 @@
* Clear lpm code (if destined for PMNx counters)
* We don't need to set the event if it's a cycle count
*/
- if (idx != ARMV7_CYCLE_COUNTER) {
+ if (idx != ARMV7_IDX_CYCLE_COUNTER) {
val = hwc->config_base;
val &= SCORPION_EVTYPE_EVENT;
@@ -613,10 +622,11 @@
armv7_pmnc_disable_intens(idx);
scorpion_dis_out:
- raw_spin_unlock_irqrestore(&pmu_lock, flags);
+ raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}
-static void scorpion_pmu_enable_event(struct hw_perf_event *hwc, int idx)
+static void scorpion_pmu_enable_event(struct hw_perf_event *hwc,
+ int idx, int cpu)
{
unsigned long flags;
u32 val = 0;
@@ -624,12 +634,13 @@
unsigned long event;
struct scorpion_evt evtinfo;
unsigned long long prev_count = local64_read(&hwc->prev_count);
+ struct pmu_hw_events *events = cpu_pmu->get_hw_events();
/*
* Enable counter and interrupt, and set the counter to count
* the event that we're interested in.
*/
- raw_spin_lock_irqsave(&pmu_lock, flags);
+ raw_spin_lock_irqsave(&events->pmu_lock, flags);
/* Disable counter */
armv7_pmnc_disable_counter(idx);
@@ -638,7 +649,7 @@
* Set event (if destined for PMNx counters)
* We don't need to set the event if it's a cycle count
*/
- if (idx != ARMV7_CYCLE_COUNTER) {
+ if (idx != ARMV7_IDX_CYCLE_COUNTER) {
val = hwc->config_base;
val &= SCORPION_EVTYPE_EVENT;
@@ -673,59 +684,12 @@
armv7_pmnc_enable_counter(idx);
scorpion_out:
- raw_spin_unlock_irqrestore(&pmu_lock, flags);
-}
-
-static void enable_irq_callback(void *info)
-{
- int irq = *(unsigned int *)info;
-
- enable_percpu_irq(irq, IRQ_TYPE_EDGE_RISING);
-}
-
-static void disable_irq_callback(void *info)
-{
- int irq = *(unsigned int *)info;
-
- disable_percpu_irq(irq);
-}
-
-static int
-msm_request_irq(int irq, irq_handler_t *handle_irq)
-{
- int err = 0;
- int cpu;
-
- err = request_percpu_irq(irq, *handle_irq, "armpmu",
- &cpu_hw_events);
-
- if (!err) {
- for_each_cpu(cpu, cpu_online_mask) {
- smp_call_function_single(cpu,
- enable_irq_callback, &irq, 1);
- }
- }
-
- return err;
-}
-
-static void
-msm_free_irq(int irq)
-{
- int cpu;
-
- if (irq >= 0) {
- for_each_cpu(cpu, cpu_online_mask) {
- smp_call_function_single(cpu,
- disable_irq_callback, &irq, 1);
- }
- free_percpu_irq(irq, &cpu_hw_events);
- }
+ raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}
static void scorpion_pmu_reset(void *info)
{
- u32 idx, nb_cnt = armpmu->num_events;
+ u32 idx, nb_cnt = cpu_pmu->num_events;
/* Stop all counters and their interrupts */
for (idx = 1; idx < nb_cnt; ++idx) {
@@ -751,7 +715,7 @@
.disable = scorpion_pmu_disable_event,
.read_counter = armv7pmu_read_counter,
.write_counter = armv7pmu_write_counter,
- .raw_event_mask = 0xFFFFF,
+ .map_event = msm_scorpion_map_event,
.get_event_idx = armv7pmu_get_event_idx,
.start = armv7pmu_start,
.stop = armv7pmu_stop,
@@ -759,33 +723,29 @@
.max_period = (1LLU << 32) - 1,
};
-static const struct arm_pmu *__init armv7_scorpion_pmu_init(void)
+static struct arm_pmu *__init armv7_scorpion_pmu_init(void)
{
scorpion_pmu.id = ARM_PERF_PMU_ID_SCORPION;
scorpion_pmu.name = "ARMv7 Scorpion";
- scorpion_pmu.cache_map = &armv7_scorpion_perf_cache_map;
- scorpion_pmu.event_map = &armv7_scorpion_perf_map;
scorpion_pmu.num_events = armv7_read_num_pmnc_events();
scorpion_clear_pmuregs();
return &scorpion_pmu;
}
-static const struct arm_pmu *__init armv7_scorpionmp_pmu_init(void)
+static struct arm_pmu *__init armv7_scorpionmp_pmu_init(void)
{
scorpion_pmu.id = ARM_PERF_PMU_ID_SCORPIONMP;
scorpion_pmu.name = "ARMv7 Scorpion-MP";
- scorpion_pmu.cache_map = &armv7_scorpion_perf_cache_map;
- scorpion_pmu.event_map = &armv7_scorpion_perf_map;
scorpion_pmu.num_events = armv7_read_num_pmnc_events();
scorpion_clear_pmuregs();
return &scorpion_pmu;
}
#else
-static const struct arm_pmu *__init armv7_scorpion_pmu_init(void)
+static struct arm_pmu *__init armv7_scorpion_pmu_init(void)
{
return NULL;
}
-static const struct arm_pmu *__init armv7_scorpionmp_pmu_init(void)
+static struct arm_pmu *__init armv7_scorpionmp_pmu_init(void)
{
return NULL;
}
diff --git a/arch/arm/kernel/perf_event_msm_krait.c b/arch/arm/kernel/perf_event_msm_krait.c
index 8dbd047..1b115b4 100644
--- a/arch/arm/kernel/perf_event_msm_krait.c
+++ b/arch/arm/kernel/perf_event_msm_krait.c
@@ -58,6 +58,17 @@
static u32 krait_ver, evt_index;
static u32 krait_max_l1_reg;
+
+/*
+ * Every 4 bytes represents a prefix.
+ * Every nibble represents a register.
+ * Every bit represents a group within a register.
+ *
+ * This supports up to 4 groups per register, upto 8
+ * registers per prefix and upto 2 prefixes.
+ */
+static DEFINE_PER_CPU(u64, pmu_bitmap);
+
static const unsigned armv7_krait_perf_map[PERF_COUNT_HW_MAX] = {
[PERF_COUNT_HW_CPU_CYCLES] = ARMV7_PERFCTR_CPU_CYCLES,
[PERF_COUNT_HW_INSTRUCTIONS] = ARMV7_PERFCTR_INSTR_EXECUTED,
@@ -556,6 +567,58 @@
}
}
+/*
+ * We check for column exclusion constraints here.
+ * Two events cant have same reg and same group.
+ */
+static int msm_test_set_ev_constraint(struct perf_event *event)
+{
+ u32 krait_evt_type = event->attr.config & KRAIT_EVENT_MASK;
+ u8 prefix = (krait_evt_type & 0xF0000) >> 16;
+ u8 reg = (krait_evt_type & 0x0F000) >> 12;
+ u8 group = krait_evt_type & 0x0000F;
+ u64 cpu_pmu_bitmap = __get_cpu_var(pmu_bitmap);
+ u64 bitmap_t;
+
+ /* Return if non MSM event. */
+ if (!prefix)
+ return 0;
+
+ bitmap_t = 1 << (((prefix - 1) * 32) + (reg * 4) + group);
+
+ /* Set it if not already set. */
+ if (!(cpu_pmu_bitmap & bitmap_t)) {
+ cpu_pmu_bitmap |= bitmap_t;
+ __get_cpu_var(pmu_bitmap) = cpu_pmu_bitmap;
+ return 1;
+ }
+ /* Bit is already set. Constraint failed. */
+ return -EPERM;
+}
+
+static int msm_clear_ev_constraint(struct perf_event *event)
+{
+ u32 krait_evt_type = event->attr.config & KRAIT_EVENT_MASK;
+ u8 prefix = (krait_evt_type & 0xF0000) >> 16;
+ u8 reg = (krait_evt_type & 0x0F000) >> 12;
+ u8 group = krait_evt_type & 0x0000F;
+ u64 cpu_pmu_bitmap = __get_cpu_var(pmu_bitmap);
+ u64 bitmap_t;
+
+ /* Return if non MSM event. */
+ if (!prefix)
+ return 0;
+
+ bitmap_t = 1 << (((prefix - 1) * 32) + (reg * 4) + group);
+
+ /* Clear constraint bit. */
+ cpu_pmu_bitmap &= ~(bitmap_t);
+
+ __get_cpu_var(pmu_bitmap) = cpu_pmu_bitmap;
+
+ return 1;
+}
+
static struct arm_pmu krait_pmu = {
.handle_irq = armv7pmu_handle_irq,
.request_pmu_irq = msm_request_irq,
@@ -568,6 +631,8 @@
.start = armv7pmu_start,
.stop = armv7pmu_stop,
.reset = krait_pmu_reset,
+ .test_set_event_constraints = msm_test_set_ev_constraint,
+ .clear_event_constraints = msm_clear_ev_constraint,
.max_period = (1LLU << 32) - 1,
};
diff --git a/arch/arm/kernel/perf_event_msm_krait_l2.c b/arch/arm/kernel/perf_event_msm_krait_l2.c
deleted file mode 100644
index baa05ac..0000000
--- a/arch/arm/kernel/perf_event_msm_krait_l2.c
+++ /dev/null
@@ -1,667 +0,0 @@
-/*
- * Copyright (c) 2011, 2012 Code Aurora Forum. 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.
- */
-#ifdef CONFIG_ARCH_MSM_KRAIT
-
-#include <linux/irq.h>
-
-#include <mach/msm-krait-l2-accessors.h>
-
-#define MAX_L2_PERIOD ((1ULL << 32) - 1)
-#define MAX_KRAIT_L2_CTRS 5
-
-#define L2PMCCNTR 0x409
-#define L2PMCCNTCR 0x408
-#define L2PMCCNTSR 0x40A
-#define L2CYCLE_CTR_BIT 31
-#define L2CYCLE_CTR_EVENT_IDX 4
-#define L2CYCLE_CTR_RAW_CODE 0xfe
-
-#define L2PMOVSR 0x406
-
-#define L2PMCR 0x400
-#define L2PMCR_RESET_ALL 0x6
-#define L2PMCR_GLOBAL_ENABLE 0x1
-#define L2PMCR_GLOBAL_DISABLE 0x0
-
-#define L2PMCNTENSET 0x403
-#define L2PMCNTENCLR 0x402
-
-#define L2PMINTENSET 0x405
-#define L2PMINTENCLR 0x404
-
-#define IA_L2PMXEVCNTCR_BASE 0x420
-#define IA_L2PMXEVTYPER_BASE 0x424
-#define IA_L2PMRESX_BASE 0x410
-#define IA_L2PMXEVFILTER_BASE 0x423
-#define IA_L2PMXEVCNTR_BASE 0x421
-
-/* event format is -e rsRCCG See get_event_desc() */
-
-#define EVENT_REG_MASK 0xf000
-#define EVENT_GROUPSEL_MASK 0x000f
-#define EVENT_GROUPCODE_MASK 0x0ff0
-#define EVENT_REG_SHIFT 12
-#define EVENT_GROUPCODE_SHIFT 4
-
-#define RESRX_VALUE_EN 0x80000000
-
-static struct platform_device *l2_pmu_device;
-
-struct hw_krait_l2_pmu {
- struct perf_event *events[MAX_KRAIT_L2_CTRS];
- unsigned long active_mask[BITS_TO_LONGS(MAX_KRAIT_L2_CTRS)];
- raw_spinlock_t lock;
-};
-
-struct hw_krait_l2_pmu hw_krait_l2_pmu;
-
-struct event_desc {
- int event_groupsel;
- int event_reg;
- int event_group_code;
-};
-
-void get_event_desc(u64 config, struct event_desc *evdesc)
-{
- /* L2PMEVCNTRX */
- evdesc->event_reg = (config & EVENT_REG_MASK) >> EVENT_REG_SHIFT;
- /* Group code (row ) */
- evdesc->event_group_code =
- (config & EVENT_GROUPCODE_MASK) >> EVENT_GROUPCODE_SHIFT;
- /* Group sel (col) */
- evdesc->event_groupsel = (config & EVENT_GROUPSEL_MASK);
-
- pr_debug("%s: reg: %x, group_code: %x, groupsel: %x\n", __func__,
- evdesc->event_reg, evdesc->event_group_code,
- evdesc->event_groupsel);
-}
-
-static void set_evcntcr(int ctr)
-{
- u32 evtcr_reg = (ctr * 16) + IA_L2PMXEVCNTCR_BASE;
-
- set_l2_indirect_reg(evtcr_reg, 0x0);
-}
-
-static void set_evtyper(int event_groupsel, int event_reg, int ctr)
-{
- u32 evtype_reg = (ctr * 16) + IA_L2PMXEVTYPER_BASE;
- u32 evtype_val = event_groupsel + (4 * event_reg);
-
- set_l2_indirect_reg(evtype_reg, evtype_val);
-}
-
-static void set_evres(int event_groupsel, int event_reg, int event_group_code)
-{
- u32 group_reg = event_reg + IA_L2PMRESX_BASE;
- u32 group_val =
- RESRX_VALUE_EN | (event_group_code << (8 * event_groupsel));
- u32 resr_val;
- u32 group_byte = 0xff;
- u32 group_mask = ~(group_byte << (8 * event_groupsel));
-
- resr_val = get_l2_indirect_reg(group_reg);
- resr_val &= group_mask;
- resr_val |= group_val;
-
- set_l2_indirect_reg(group_reg, resr_val);
-}
-
-static void set_evfilter_task_mode(int ctr)
-{
- u32 filter_reg = (ctr * 16) + IA_L2PMXEVFILTER_BASE;
- u32 filter_val = 0x000f0030 | 1 << smp_processor_id();
-
- set_l2_indirect_reg(filter_reg, filter_val);
-}
-
-static void set_evfilter_sys_mode(int ctr)
-{
- u32 filter_reg = (ctr * 16) + IA_L2PMXEVFILTER_BASE;
- u32 filter_val = 0x000f003f;
-
- set_l2_indirect_reg(filter_reg, filter_val);
-}
-
-static void enable_intenset(u32 idx)
-{
- if (idx == L2CYCLE_CTR_EVENT_IDX)
- set_l2_indirect_reg(L2PMINTENSET, 1 << L2CYCLE_CTR_BIT);
- else
- set_l2_indirect_reg(L2PMINTENSET, 1 << idx);
-}
-
-static void disable_intenclr(u32 idx)
-{
- if (idx == L2CYCLE_CTR_EVENT_IDX)
- set_l2_indirect_reg(L2PMINTENCLR, 1 << L2CYCLE_CTR_BIT);
- else
- set_l2_indirect_reg(L2PMINTENCLR, 1 << idx);
-}
-
-static void enable_counter(u32 idx)
-{
- if (idx == L2CYCLE_CTR_EVENT_IDX)
- set_l2_indirect_reg(L2PMCNTENSET, 1 << L2CYCLE_CTR_BIT);
- else
- set_l2_indirect_reg(L2PMCNTENSET, 1 << idx);
-}
-
-static void disable_counter(u32 idx)
-{
- if (idx == L2CYCLE_CTR_EVENT_IDX)
- set_l2_indirect_reg(L2PMCNTENCLR, 1 << L2CYCLE_CTR_BIT);
- else
- set_l2_indirect_reg(L2PMCNTENCLR, 1 << idx);
-}
-
-static u64 read_counter(u32 idx)
-{
- u32 val;
- u32 counter_reg = (idx * 16) + IA_L2PMXEVCNTR_BASE;
-
- if (idx == L2CYCLE_CTR_EVENT_IDX)
- val = get_l2_indirect_reg(L2PMCCNTR);
- else
- val = get_l2_indirect_reg(counter_reg);
-
- return val;
-}
-
-static void write_counter(u32 idx, u32 val)
-{
- u32 counter_reg = (idx * 16) + IA_L2PMXEVCNTR_BASE;
-
- if (idx == L2CYCLE_CTR_EVENT_IDX)
- set_l2_indirect_reg(L2PMCCNTR, val);
- else
- set_l2_indirect_reg(counter_reg, val);
-}
-
-static int
-pmu_event_set_period(struct perf_event *event,
- struct hw_perf_event *hwc, int idx)
-{
- s64 left = local64_read(&hwc->period_left);
- s64 period = hwc->sample_period;
- int ret = 0;
-
- if (unlikely(left <= -period)) {
- left = period;
- local64_set(&hwc->period_left, left);
- hwc->last_period = period;
- ret = 1;
- }
-
- if (unlikely(left <= 0)) {
- left += period;
- local64_set(&hwc->period_left, left);
- hwc->last_period = period;
- ret = 1;
- }
-
- if (left > (s64) MAX_L2_PERIOD)
- left = MAX_L2_PERIOD;
-
- local64_set(&hwc->prev_count, (u64)-left);
-
- write_counter(idx, (u64) (-left) & 0xffffffff);
-
- perf_event_update_userpage(event);
-
- return ret;
-}
-
-static u64
-pmu_event_update(struct perf_event *event, struct hw_perf_event *hwc, int idx,
- int overflow)
-{
- u64 prev_raw_count, new_raw_count;
- u64 delta;
-
-again:
- prev_raw_count = local64_read(&hwc->prev_count);
- new_raw_count = read_counter(idx);
-
- if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
- new_raw_count) != prev_raw_count)
- goto again;
-
- new_raw_count &= MAX_L2_PERIOD;
- prev_raw_count &= MAX_L2_PERIOD;
-
- if (overflow)
- delta = MAX_L2_PERIOD - prev_raw_count + new_raw_count;
- else
- delta = new_raw_count - prev_raw_count;
-
- local64_add(delta, &event->count);
- local64_sub(delta, &hwc->period_left);
-
- pr_debug("%s: new: %lld, prev: %lld, event: %ld count: %lld\n",
- __func__, new_raw_count, prev_raw_count,
- hwc->config_base, local64_read(&event->count));
-
- return new_raw_count;
-}
-
-static void krait_l2_read(struct perf_event *event)
-{
- struct hw_perf_event *hwc = &event->hw;
-
- pmu_event_update(event, hwc, hwc->idx, 0);
-}
-
-static void krait_l2_stop_counter(struct perf_event *event, int flags)
-{
- struct hw_perf_event *hwc = &event->hw;
- int idx = hwc->idx;
-
- if (!(hwc->state & PERF_HES_STOPPED)) {
- disable_intenclr(idx);
- disable_counter(idx);
-
- pmu_event_update(event, hwc, idx, 0);
- hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
- }
-
- pr_debug("%s: event: %ld ctr: %d stopped\n", __func__, hwc->config_base,
- idx);
-}
-
-static void krait_l2_start_counter(struct perf_event *event, int flags)
-{
- struct hw_perf_event *hwc = &event->hw;
- int idx = hwc->idx;
- struct event_desc evdesc;
-
- if (flags & PERF_EF_RELOAD)
- WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
-
- hwc->state = 0;
-
- pmu_event_set_period(event, hwc, idx);
-
- if (hwc->config_base == L2CYCLE_CTR_RAW_CODE)
- goto out;
-
- set_evcntcr(idx);
-
- memset(&evdesc, 0, sizeof(evdesc));
-
- get_event_desc(hwc->config_base, &evdesc);
-
- set_evtyper(evdesc.event_groupsel, evdesc.event_reg, idx);
-
- set_evres(evdesc.event_groupsel, evdesc.event_reg,
- evdesc.event_group_code);
-
- if (event->cpu < 0)
- set_evfilter_task_mode(idx);
- else
- set_evfilter_sys_mode(idx);
-
-out:
- enable_intenset(idx);
- enable_counter(idx);
-
- pr_debug
- ("%s: ctr: %d group: %ld group_code: %lld started from cpu:%d\n",
- __func__, idx, hwc->config_base, hwc->config, smp_processor_id());
-}
-
-static void krait_l2_del_event(struct perf_event *event, int flags)
-{
- struct hw_perf_event *hwc = &event->hw;
- int idx = hwc->idx;
- unsigned long iflags;
-
- raw_spin_lock_irqsave(&hw_krait_l2_pmu.lock, iflags);
-
- clear_bit(idx, (long unsigned int *)(&hw_krait_l2_pmu.active_mask));
-
- krait_l2_stop_counter(event, PERF_EF_UPDATE);
- hw_krait_l2_pmu.events[idx] = NULL;
- hwc->idx = -1;
-
- raw_spin_unlock_irqrestore(&hw_krait_l2_pmu.lock, iflags);
-
- pr_debug("%s: event: %ld deleted\n", __func__, hwc->config_base);
-
- perf_event_update_userpage(event);
-}
-
-static int krait_l2_add_event(struct perf_event *event, int flags)
-{
- int ctr = 0;
- struct hw_perf_event *hwc = &event->hw;
- unsigned long iflags;
- int err = 0;
-
- perf_pmu_disable(event->pmu);
-
- raw_spin_lock_irqsave(&hw_krait_l2_pmu.lock, iflags);
-
- /* Cycle counter has a resrvd index */
- if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) {
- if (hw_krait_l2_pmu.events[L2CYCLE_CTR_EVENT_IDX]) {
- pr_err("%s: Stale cycle ctr event ptr !\n", __func__);
- err = -EINVAL;
- goto out;
- }
- hwc->idx = L2CYCLE_CTR_EVENT_IDX;
- hw_krait_l2_pmu.events[L2CYCLE_CTR_EVENT_IDX] = event;
- set_bit(L2CYCLE_CTR_EVENT_IDX,
- (long unsigned int *)&hw_krait_l2_pmu.active_mask);
- goto skip_ctr_loop;
- }
-
- for (ctr = 0; ctr < MAX_KRAIT_L2_CTRS - 1; ctr++) {
- if (!hw_krait_l2_pmu.events[ctr]) {
- hwc->idx = ctr;
- hw_krait_l2_pmu.events[ctr] = event;
- set_bit(ctr,
- (long unsigned int *)
- &hw_krait_l2_pmu.active_mask);
- break;
- }
- }
-
- if (hwc->idx < 0) {
- err = -ENOSPC;
- pr_err("%s: No space for event: %llx!!\n", __func__,
- event->attr.config);
- goto out;
- }
-
-skip_ctr_loop:
-
- disable_counter(hwc->idx);
-
- hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
-
- if (flags & PERF_EF_START)
- krait_l2_start_counter(event, PERF_EF_RELOAD);
-
- perf_event_update_userpage(event);
-
- pr_debug("%s: event: %ld, ctr: %d added from cpu:%d\n",
- __func__, hwc->config_base, hwc->idx, smp_processor_id());
-out:
- raw_spin_unlock_irqrestore(&hw_krait_l2_pmu.lock, iflags);
-
- /* Resume the PMU even if this event could not be added */
- perf_pmu_enable(event->pmu);
-
- return err;
-}
-
-static void krait_l2_pmu_enable(struct pmu *pmu)
-{
- isb();
- set_l2_indirect_reg(L2PMCR, L2PMCR_GLOBAL_ENABLE);
-}
-
-static void krait_l2_pmu_disable(struct pmu *pmu)
-{
- set_l2_indirect_reg(L2PMCR, L2PMCR_GLOBAL_DISABLE);
- isb();
-}
-
-u32 get_reset_pmovsr(void)
-{
- int val;
-
- val = get_l2_indirect_reg(L2PMOVSR);
- /* reset it */
- val &= 0xffffffff;
- set_l2_indirect_reg(L2PMOVSR, val);
-
- return val;
-}
-
-static irqreturn_t krait_l2_handle_irq(int irq_num, void *dev)
-{
- unsigned long pmovsr;
- struct perf_sample_data data;
- struct pt_regs *regs;
- struct perf_event *event;
- struct hw_perf_event *hwc;
- int bitp;
- int idx = 0;
-
- pmovsr = get_reset_pmovsr();
-
- if (!(pmovsr & 0xffffffff))
- return IRQ_NONE;
-
- regs = get_irq_regs();
-
- perf_sample_data_init(&data, 0);
-
- raw_spin_lock(&hw_krait_l2_pmu.lock);
-
- while (pmovsr) {
- bitp = __ffs(pmovsr);
-
- if (bitp == L2CYCLE_CTR_BIT)
- idx = L2CYCLE_CTR_EVENT_IDX;
- else
- idx = bitp;
-
- event = hw_krait_l2_pmu.events[idx];
-
- if (!event)
- goto next;
-
- if (!test_bit(idx, hw_krait_l2_pmu.active_mask))
- goto next;
-
- hwc = &event->hw;
- pmu_event_update(event, hwc, idx, 1);
- data.period = event->hw.last_period;
-
- if (!pmu_event_set_period(event, hwc, idx))
- goto next;
-
- if (perf_event_overflow(event, 0, &data, regs))
- disable_counter(hwc->idx);
-next:
- pmovsr &= (pmovsr - 1);
- }
-
- raw_spin_unlock(&hw_krait_l2_pmu.lock);
-
- irq_work_run();
-
- return IRQ_HANDLED;
-}
-
-static atomic_t active_l2_events = ATOMIC_INIT(0);
-static DEFINE_MUTEX(krait_pmu_reserve_mutex);
-
-static int pmu_reserve_hardware(void)
-{
- int i, err = -ENODEV, irq;
-
- l2_pmu_device = reserve_pmu(ARM_PMU_DEVICE_L2);
-
- if (IS_ERR(l2_pmu_device)) {
- pr_warning("unable to reserve pmu\n");
- return PTR_ERR(l2_pmu_device);
- }
-
- if (l2_pmu_device->num_resources < 1) {
- pr_err("no irqs for PMUs defined\n");
- return -ENODEV;
- }
-
- if (strncmp(l2_pmu_device->name, "l2-arm-pmu", 6)) {
- pr_err("Incorrect pdev reserved !\n");
- return -EINVAL;
- }
-
- for (i = 0; i < l2_pmu_device->num_resources; ++i) {
- irq = platform_get_irq(l2_pmu_device, i);
- if (irq < 0)
- continue;
-
- err = request_irq(irq, krait_l2_handle_irq,
- IRQF_DISABLED | IRQF_NOBALANCING,
- "krait-l2-pmu", NULL);
- if (err) {
- pr_warning("unable to request IRQ%d for Krait L2 perf "
- "counters\n", irq);
- break;
- }
-
- irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq));
- }
-
- if (err) {
- for (i = i - 1; i >= 0; --i) {
- irq = platform_get_irq(l2_pmu_device, i);
- if (irq >= 0)
- free_irq(irq, NULL);
- }
- release_pmu(l2_pmu_device);
- l2_pmu_device = NULL;
- }
-
- return err;
-}
-
-static void pmu_release_hardware(void)
-{
- int i, irq;
-
- for (i = l2_pmu_device->num_resources - 1; i >= 0; --i) {
- irq = platform_get_irq(l2_pmu_device, i);
- if (irq >= 0)
- free_irq(irq, NULL);
- }
-
- krait_l2_pmu_disable(NULL);
-
- release_pmu(l2_pmu_device);
- l2_pmu_device = NULL;
-}
-
-static void pmu_perf_event_destroy(struct perf_event *event)
-{
- if (atomic_dec_and_mutex_lock
- (&active_l2_events, &krait_pmu_reserve_mutex)) {
- pmu_release_hardware();
- mutex_unlock(&krait_pmu_reserve_mutex);
- }
-}
-
-static int krait_l2_event_init(struct perf_event *event)
-{
- int err = 0;
- struct hw_perf_event *hwc = &event->hw;
- int status = 0;
-
- switch (event->attr.type) {
- case PERF_TYPE_SHARED:
- break;
-
- default:
- return -ENOENT;
- }
-
- hwc->idx = -1;
-
- event->destroy = pmu_perf_event_destroy;
-
- if (!atomic_inc_not_zero(&active_l2_events)) {
- /* 0 active events */
- mutex_lock(&krait_pmu_reserve_mutex);
- err = pmu_reserve_hardware();
- mutex_unlock(&krait_pmu_reserve_mutex);
- if (!err)
- atomic_inc(&active_l2_events);
- else
- return err;
- }
-
- hwc->config = 0;
- hwc->event_base = 0;
-
- /* Check if we came via perf default syms */
- if (event->attr.config == PERF_COUNT_HW_L2_CYCLES)
- hwc->config_base = L2CYCLE_CTR_RAW_CODE;
- else
- hwc->config_base = event->attr.config;
-
- /* Only one CPU can control the cycle counter */
- if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) {
- /* Check if its already running */
- status = get_l2_indirect_reg(L2PMCCNTSR);
- if (status == 0x2) {
- err = -ENOSPC;
- goto out;
- }
- }
-
- if (!hwc->sample_period) {
- hwc->sample_period = MAX_L2_PERIOD;
- hwc->last_period = hwc->sample_period;
- local64_set(&hwc->period_left, hwc->sample_period);
- }
-
- pr_debug("%s: event: %lld init'd\n", __func__, event->attr.config);
-
-out:
- if (err < 0)
- pmu_perf_event_destroy(event);
-
- return err;
-}
-
-static struct pmu krait_l2_pmu = {
- .pmu_enable = krait_l2_pmu_enable,
- .pmu_disable = krait_l2_pmu_disable,
- .event_init = krait_l2_event_init,
- .add = krait_l2_add_event,
- .del = krait_l2_del_event,
- .start = krait_l2_start_counter,
- .stop = krait_l2_stop_counter,
- .read = krait_l2_read,
-};
-
-static const struct arm_pmu *__init krait_l2_pmu_init(void)
-{
- /* Register our own PMU here */
- perf_pmu_register(&krait_l2_pmu, "Krait L2", PERF_TYPE_SHARED);
-
- memset(&hw_krait_l2_pmu, 0, sizeof(hw_krait_l2_pmu));
-
- /* Reset all ctrs */
- set_l2_indirect_reg(L2PMCR, L2PMCR_RESET_ALL);
-
- /* Avoid spurious interrupt if any */
- get_reset_pmovsr();
-
- raw_spin_lock_init(&hw_krait_l2_pmu.lock);
-
- /* Don't return an arm_pmu here */
- return NULL;
-}
-#else
-
-static const struct arm_pmu *__init krait_l2_pmu_init(void)
-{
- return NULL;
-}
-#endif
diff --git a/arch/arm/kernel/perf_event_msm_l2.c b/arch/arm/kernel/perf_event_msm_l2.c
deleted file mode 100644
index 26753d8..0000000
--- a/arch/arm/kernel/perf_event_msm_l2.c
+++ /dev/null
@@ -1,1013 +0,0 @@
-/*
- * Copyright (c) 2011, 2012 Code Aurora Forum. 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.
- */
-#ifdef CONFIG_ARCH_MSM8X60
-
-#include <linux/irq.h>
-
-#define MAX_BB_L2_PERIOD ((1ULL << 32) - 1)
-#define MAX_BB_L2_CTRS 5
-#define BB_L2CYCLE_CTR_BIT 31
-#define BB_L2CYCLE_CTR_EVENT_IDX 4
-#define BB_L2CYCLE_CTR_RAW_CODE 0xfe
-#define SCORPIONL2_PMNC_E (1 << 0) /* Enable all counters */
-#define SCORPION_L2_EVT_PREFIX 3
-#define SCORPION_MAX_L2_REG 4
-
-/*
- * Lock to protect r/m/w sequences to the L2 PMU.
- */
-DEFINE_RAW_SPINLOCK(bb_l2_pmu_lock);
-
-static struct platform_device *bb_l2_pmu_device;
-
-struct hw_bb_l2_pmu {
- struct perf_event *events[MAX_BB_L2_CTRS];
- unsigned long active_mask[BITS_TO_LONGS(MAX_BB_L2_CTRS)];
- raw_spinlock_t lock;
-};
-
-struct hw_bb_l2_pmu hw_bb_l2_pmu;
-
-struct bb_l2_scorp_evt {
- u32 evt_type;
- u32 val;
- u8 grp;
- u32 evt_type_act;
-};
-
-enum scorpion_perf_types {
- SCORPIONL2_TOTAL_BANK_REQ = 0x90,
- SCORPIONL2_DSIDE_READ = 0x91,
- SCORPIONL2_DSIDE_WRITE = 0x92,
- SCORPIONL2_ISIDE_READ = 0x93,
- SCORPIONL2_L2CACHE_ISIDE_READ = 0x94,
- SCORPIONL2_L2CACHE_BANK_REQ = 0x95,
- SCORPIONL2_L2CACHE_DSIDE_READ = 0x96,
- SCORPIONL2_L2CACHE_DSIDE_WRITE = 0x97,
- SCORPIONL2_L2NOCACHE_DSIDE_WRITE = 0x98,
- SCORPIONL2_L2NOCACHE_ISIDE_READ = 0x99,
- SCORPIONL2_L2NOCACHE_TOTAL_REQ = 0x9a,
- SCORPIONL2_L2NOCACHE_DSIDE_READ = 0x9b,
- SCORPIONL2_DSIDE_READ_NOL1 = 0x9c,
- SCORPIONL2_L2CACHE_WRITETHROUGH = 0x9d,
- SCORPIONL2_BARRIERS = 0x9e,
- SCORPIONL2_HARDWARE_TABLE_WALKS = 0x9f,
- SCORPIONL2_MVA_POC = 0xa0,
- SCORPIONL2_L2CACHE_HW_TABLE_WALKS = 0xa1,
- SCORPIONL2_SETWAY_CACHE_OPS = 0xa2,
- SCORPIONL2_DSIDE_WRITE_HITS = 0xa3,
- SCORPIONL2_ISIDE_READ_HITS = 0xa4,
- SCORPIONL2_CACHE_DSIDE_READ_NOL1 = 0xa5,
- SCORPIONL2_TOTAL_CACHE_HITS = 0xa6,
- SCORPIONL2_CACHE_MATCH_MISS = 0xa7,
- SCORPIONL2_DREAD_HIT_L1_DATA = 0xa8,
- SCORPIONL2_L2LINE_LOCKED = 0xa9,
- SCORPIONL2_HW_TABLE_WALK_HIT = 0xaa,
- SCORPIONL2_CACHE_MVA_POC = 0xab,
- SCORPIONL2_L2ALLOC_DWRITE_MISS = 0xac,
- SCORPIONL2_CORRECTED_TAG_ARRAY = 0xad,
- SCORPIONL2_CORRECTED_DATA_ARRAY = 0xae,
- SCORPIONL2_CORRECTED_REPLACEMENT_ARRAY = 0xaf,
- SCORPIONL2_PMBUS_MPAAF = 0xb0,
- SCORPIONL2_PMBUS_MPWDAF = 0xb1,
- SCORPIONL2_PMBUS_MPBRT = 0xb2,
- SCORPIONL2_CPU0_GRANT = 0xb3,
- SCORPIONL2_CPU1_GRANT = 0xb4,
- SCORPIONL2_CPU0_NOGRANT = 0xb5,
- SCORPIONL2_CPU1_NOGRANT = 0xb6,
- SCORPIONL2_CPU0_LOSING_ARB = 0xb7,
- SCORPIONL2_CPU1_LOSING_ARB = 0xb8,
- SCORPIONL2_SLAVEPORT_NOGRANT = 0xb9,
- SCORPIONL2_SLAVEPORT_BPQ_FULL = 0xba,
- SCORPIONL2_SLAVEPORT_LOSING_ARB = 0xbb,
- SCORPIONL2_SLAVEPORT_GRANT = 0xbc,
- SCORPIONL2_SLAVEPORT_GRANTLOCK = 0xbd,
- SCORPIONL2_L2EM_STREX_PASS = 0xbe,
- SCORPIONL2_L2EM_STREX_FAIL = 0xbf,
- SCORPIONL2_LDREX_RESERVE_L2EM = 0xc0,
- SCORPIONL2_SLAVEPORT_LDREX = 0xc1,
- SCORPIONL2_CPU0_L2EM_CLEARED = 0xc2,
- SCORPIONL2_CPU1_L2EM_CLEARED = 0xc3,
- SCORPIONL2_SLAVEPORT_L2EM_CLEARED = 0xc4,
- SCORPIONL2_CPU0_CLAMPED = 0xc5,
- SCORPIONL2_CPU1_CLAMPED = 0xc6,
- SCORPIONL2_CPU0_WAIT = 0xc7,
- SCORPIONL2_CPU1_WAIT = 0xc8,
- SCORPIONL2_CPU0_NONAMBAS_WAIT = 0xc9,
- SCORPIONL2_CPU1_NONAMBAS_WAIT = 0xca,
- SCORPIONL2_CPU0_DSB_WAIT = 0xcb,
- SCORPIONL2_CPU1_DSB_WAIT = 0xcc,
- SCORPIONL2_AXI_READ = 0xcd,
- SCORPIONL2_AXI_WRITE = 0xce,
-
- SCORPIONL2_1BEAT_WRITE = 0xcf,
- SCORPIONL2_2BEAT_WRITE = 0xd0,
- SCORPIONL2_4BEAT_WRITE = 0xd1,
- SCORPIONL2_8BEAT_WRITE = 0xd2,
- SCORPIONL2_12BEAT_WRITE = 0xd3,
- SCORPIONL2_16BEAT_WRITE = 0xd4,
- SCORPIONL2_1BEAT_DSIDE_READ = 0xd5,
- SCORPIONL2_2BEAT_DSIDE_READ = 0xd6,
- SCORPIONL2_4BEAT_DSIDE_READ = 0xd7,
- SCORPIONL2_8BEAT_DSIDE_READ = 0xd8,
- SCORPIONL2_CSYS_READ_1BEAT = 0xd9,
- SCORPIONL2_CSYS_READ_2BEAT = 0xda,
- SCORPIONL2_CSYS_READ_4BEAT = 0xdb,
- SCORPIONL2_CSYS_READ_8BEAT = 0xdc,
- SCORPIONL2_4BEAT_IFETCH_READ = 0xdd,
- SCORPIONL2_8BEAT_IFETCH_READ = 0xde,
- SCORPIONL2_CSYS_WRITE_1BEAT = 0xdf,
- SCORPIONL2_CSYS_WRITE_2BEAT = 0xe0,
- SCORPIONL2_AXI_READ_DATA_BEAT = 0xe1,
- SCORPIONL2_AXI_WRITE_EVT1 = 0xe2,
- SCORPIONL2_AXI_WRITE_EVT2 = 0xe3,
- SCORPIONL2_LDREX_REQ = 0xe4,
- SCORPIONL2_STREX_PASS = 0xe5,
- SCORPIONL2_STREX_FAIL = 0xe6,
- SCORPIONL2_CPREAD = 0xe7,
- SCORPIONL2_CPWRITE = 0xe8,
- SCORPIONL2_BARRIER_REQ = 0xe9,
- SCORPIONL2_AXI_READ_SLVPORT = 0xea,
- SCORPIONL2_AXI_WRITE_SLVPORT = 0xeb,
- SCORPIONL2_AXI_READ_SLVPORT_DATABEAT = 0xec,
- SCORPIONL2_AXI_WRITE_SLVPORT_DATABEAT = 0xed,
- SCORPIONL2_SNOOPKILL_PREFILTER = 0xee,
- SCORPIONL2_SNOOPKILL_FILTEROUT = 0xef,
- SCORPIONL2_SNOOPED_IC = 0xf0,
- SCORPIONL2_SNOOPED_BP = 0xf1,
- SCORPIONL2_SNOOPED_BARRIERS = 0xf2,
- SCORPIONL2_SNOOPED_TLB = 0xf3,
- BB_L2_MAX_EVT,
-};
-
-static const struct bb_l2_scorp_evt sc_evt[] = {
- {SCORPIONL2_TOTAL_BANK_REQ, 0x80000001, 0, 0x00},
- {SCORPIONL2_DSIDE_READ, 0x80000100, 0, 0x01},
- {SCORPIONL2_DSIDE_WRITE, 0x80010000, 0, 0x02},
- {SCORPIONL2_ISIDE_READ, 0x81000000, 0, 0x03},
- {SCORPIONL2_L2CACHE_ISIDE_READ, 0x80000002, 0, 0x00},
- {SCORPIONL2_L2CACHE_BANK_REQ, 0x80000200, 0, 0x01},
- {SCORPIONL2_L2CACHE_DSIDE_READ, 0x80020000, 0, 0x02},
- {SCORPIONL2_L2CACHE_DSIDE_WRITE, 0x82000000, 0, 0x03},
- {SCORPIONL2_L2NOCACHE_DSIDE_WRITE, 0x80000003, 0, 0x00},
- {SCORPIONL2_L2NOCACHE_ISIDE_READ, 0x80000300, 0, 0x01},
- {SCORPIONL2_L2NOCACHE_TOTAL_REQ, 0x80030000, 0, 0x02},
- {SCORPIONL2_L2NOCACHE_DSIDE_READ, 0x83000000, 0, 0x03},
- {SCORPIONL2_DSIDE_READ_NOL1, 0x80000004, 0, 0x00},
- {SCORPIONL2_L2CACHE_WRITETHROUGH, 0x80000400, 0, 0x01},
- {SCORPIONL2_BARRIERS, 0x84000000, 0, 0x03},
- {SCORPIONL2_HARDWARE_TABLE_WALKS, 0x80000005, 0, 0x00},
- {SCORPIONL2_MVA_POC, 0x80000500, 0, 0x01},
- {SCORPIONL2_L2CACHE_HW_TABLE_WALKS, 0x80050000, 0, 0x02},
- {SCORPIONL2_SETWAY_CACHE_OPS, 0x85000000, 0, 0x03},
- {SCORPIONL2_DSIDE_WRITE_HITS, 0x80000006, 0, 0x00},
- {SCORPIONL2_ISIDE_READ_HITS, 0x80000600, 0, 0x01},
- {SCORPIONL2_CACHE_DSIDE_READ_NOL1, 0x80060000, 0, 0x02},
- {SCORPIONL2_TOTAL_CACHE_HITS, 0x86000000, 0, 0x03},
- {SCORPIONL2_CACHE_MATCH_MISS, 0x80000007, 0, 0x00},
- {SCORPIONL2_DREAD_HIT_L1_DATA, 0x87000000, 0, 0x03},
- {SCORPIONL2_L2LINE_LOCKED, 0x80000008, 0, 0x00},
- {SCORPIONL2_HW_TABLE_WALK_HIT, 0x80000800, 0, 0x01},
- {SCORPIONL2_CACHE_MVA_POC, 0x80080000, 0, 0x02},
- {SCORPIONL2_L2ALLOC_DWRITE_MISS, 0x88000000, 0, 0x03},
- {SCORPIONL2_CORRECTED_TAG_ARRAY, 0x80001A00, 0, 0x01},
- {SCORPIONL2_CORRECTED_DATA_ARRAY, 0x801A0000, 0, 0x02},
- {SCORPIONL2_CORRECTED_REPLACEMENT_ARRAY, 0x9A000000, 0, 0x03},
- {SCORPIONL2_PMBUS_MPAAF, 0x80001C00, 0, 0x01},
- {SCORPIONL2_PMBUS_MPWDAF, 0x801C0000, 0, 0x02},
- {SCORPIONL2_PMBUS_MPBRT, 0x9C000000, 0, 0x03},
-
- {SCORPIONL2_CPU0_GRANT, 0x80000001, 1, 0x04},
- {SCORPIONL2_CPU1_GRANT, 0x80000100, 1, 0x05},
- {SCORPIONL2_CPU0_NOGRANT, 0x80020000, 1, 0x06},
- {SCORPIONL2_CPU1_NOGRANT, 0x82000000, 1, 0x07},
- {SCORPIONL2_CPU0_LOSING_ARB, 0x80040000, 1, 0x06},
- {SCORPIONL2_CPU1_LOSING_ARB, 0x84000000, 1, 0x07},
- {SCORPIONL2_SLAVEPORT_NOGRANT, 0x80000007, 1, 0x04},
- {SCORPIONL2_SLAVEPORT_BPQ_FULL, 0x80000700, 1, 0x05},
- {SCORPIONL2_SLAVEPORT_LOSING_ARB, 0x80070000, 1, 0x06},
- {SCORPIONL2_SLAVEPORT_GRANT, 0x87000000, 1, 0x07},
- {SCORPIONL2_SLAVEPORT_GRANTLOCK, 0x80000008, 1, 0x04},
- {SCORPIONL2_L2EM_STREX_PASS, 0x80000009, 1, 0x04},
- {SCORPIONL2_L2EM_STREX_FAIL, 0x80000900, 1, 0x05},
- {SCORPIONL2_LDREX_RESERVE_L2EM, 0x80090000, 1, 0x06},
- {SCORPIONL2_SLAVEPORT_LDREX, 0x89000000, 1, 0x07},
- {SCORPIONL2_CPU0_L2EM_CLEARED, 0x800A0000, 1, 0x06},
- {SCORPIONL2_CPU1_L2EM_CLEARED, 0x8A000000, 1, 0x07},
- {SCORPIONL2_SLAVEPORT_L2EM_CLEARED, 0x80000B00, 1, 0x05},
- {SCORPIONL2_CPU0_CLAMPED, 0x8000000E, 1, 0x04},
- {SCORPIONL2_CPU1_CLAMPED, 0x80000E00, 1, 0x05},
- {SCORPIONL2_CPU0_WAIT, 0x800F0000, 1, 0x06},
- {SCORPIONL2_CPU1_WAIT, 0x8F000000, 1, 0x07},
- {SCORPIONL2_CPU0_NONAMBAS_WAIT, 0x80000010, 1, 0x04},
- {SCORPIONL2_CPU1_NONAMBAS_WAIT, 0x80001000, 1, 0x05},
- {SCORPIONL2_CPU0_DSB_WAIT, 0x80000014, 1, 0x04},
- {SCORPIONL2_CPU1_DSB_WAIT, 0x80001400, 1, 0x05},
-
- {SCORPIONL2_AXI_READ, 0x80000001, 2, 0x08},
- {SCORPIONL2_AXI_WRITE, 0x80000100, 2, 0x09},
- {SCORPIONL2_1BEAT_WRITE, 0x80010000, 2, 0x0a},
- {SCORPIONL2_2BEAT_WRITE, 0x80010000, 2, 0x0b},
- {SCORPIONL2_4BEAT_WRITE, 0x80000002, 2, 0x08},
- {SCORPIONL2_8BEAT_WRITE, 0x80000200, 2, 0x09},
- {SCORPIONL2_12BEAT_WRITE, 0x80020000, 2, 0x0a},
- {SCORPIONL2_16BEAT_WRITE, 0x82000000, 2, 0x0b},
- {SCORPIONL2_1BEAT_DSIDE_READ, 0x80000003, 2, 0x08},
- {SCORPIONL2_2BEAT_DSIDE_READ, 0x80000300, 2, 0x09},
- {SCORPIONL2_4BEAT_DSIDE_READ, 0x80030000, 2, 0x0a},
- {SCORPIONL2_8BEAT_DSIDE_READ, 0x83000000, 2, 0x0b},
- {SCORPIONL2_CSYS_READ_1BEAT, 0x80000004, 2, 0x08},
- {SCORPIONL2_CSYS_READ_2BEAT, 0x80000400, 2, 0x09},
- {SCORPIONL2_CSYS_READ_4BEAT, 0x80040000, 2, 0x0a},
- {SCORPIONL2_CSYS_READ_8BEAT, 0x84000000, 2, 0x0b},
- {SCORPIONL2_4BEAT_IFETCH_READ, 0x80000005, 2, 0x08},
- {SCORPIONL2_8BEAT_IFETCH_READ, 0x80000500, 2, 0x09},
- {SCORPIONL2_CSYS_WRITE_1BEAT, 0x80050000, 2, 0x0a},
- {SCORPIONL2_CSYS_WRITE_2BEAT, 0x85000000, 2, 0x0b},
- {SCORPIONL2_AXI_READ_DATA_BEAT, 0x80000600, 2, 0x09},
- {SCORPIONL2_AXI_WRITE_EVT1, 0x80060000, 2, 0x0a},
- {SCORPIONL2_AXI_WRITE_EVT2, 0x86000000, 2, 0x0b},
- {SCORPIONL2_LDREX_REQ, 0x80000007, 2, 0x08},
- {SCORPIONL2_STREX_PASS, 0x80000700, 2, 0x09},
- {SCORPIONL2_STREX_FAIL, 0x80070000, 2, 0x0a},
- {SCORPIONL2_CPREAD, 0x80000008, 2, 0x08},
- {SCORPIONL2_CPWRITE, 0x80000800, 2, 0x09},
- {SCORPIONL2_BARRIER_REQ, 0x88000000, 2, 0x0b},
-
- {SCORPIONL2_AXI_READ_SLVPORT, 0x80000001, 3, 0x0c},
- {SCORPIONL2_AXI_WRITE_SLVPORT, 0x80000100, 3, 0x0d},
- {SCORPIONL2_AXI_READ_SLVPORT_DATABEAT, 0x80010000, 3, 0x0e},
- {SCORPIONL2_AXI_WRITE_SLVPORT_DATABEAT, 0x81000000, 3, 0x0f},
-
- {SCORPIONL2_SNOOPKILL_PREFILTER, 0x80000001, 4, 0x10},
- {SCORPIONL2_SNOOPKILL_FILTEROUT, 0x80000100, 4, 0x11},
- {SCORPIONL2_SNOOPED_IC, 0x80000002, 4, 0x10},
- {SCORPIONL2_SNOOPED_BP, 0x80000200, 4, 0x11},
- {SCORPIONL2_SNOOPED_BARRIERS, 0x80020000, 4, 0x12},
- {SCORPIONL2_SNOOPED_TLB, 0x82000000, 4, 0x13},
-};
-
-static u32 bb_l2_read_l2pm0(void)
-{
- u32 val;
- asm volatile ("mrc p15, 3, %0, c15, c7, 0" : "=r" (val));
- return val;
-}
-
-static void bb_l2_write_l2pm0(u32 val)
-{
- asm volatile ("mcr p15, 3, %0, c15, c7, 0" : : "r" (val));
-}
-
-static u32 bb_l2_read_l2pm1(void)
-{
- u32 val;
- asm volatile ("mrc p15, 3, %0, c15, c7, 1" : "=r" (val));
- return val;
-}
-
-static void bb_l2_write_l2pm1(u32 val)
-{
- asm volatile ("mcr p15, 3, %0, c15, c7, 1" : : "r" (val));
-}
-
-static u32 bb_l2_read_l2pm2(void)
-{
- u32 val;
- asm volatile ("mrc p15, 3, %0, c15, c7, 2" : "=r" (val));
- return val;
-}
-
-static void bb_l2_write_l2pm2(u32 val)
-{
- asm volatile ("mcr p15, 3, %0, c15, c7, 2" : : "r" (val));
-}
-
-static u32 bb_l2_read_l2pm3(void)
-{
- u32 val;
- asm volatile ("mrc p15, 3, %0, c15, c7, 3" : "=r" (val));
- return val;
-}
-
-static void bb_l2_write_l2pm3(u32 val)
-{
- asm volatile ("mcr p15, 3, %0, c15, c7, 3" : : "r" (val));
-}
-
-static u32 bb_l2_read_l2pm4(void)
-{
- u32 val;
- asm volatile ("mrc p15, 3, %0, c15, c7, 4" : "=r" (val));
- return val;
-}
-
-static void bb_l2_write_l2pm4(u32 val)
-{
- asm volatile ("mcr p15, 3, %0, c15, c7, 4" : : "r" (val));
-}
-
-struct bb_scorpion_access_funcs {
- u32(*read) (void);
- void (*write) (u32);
- void (*pre) (void);
- void (*post) (void);
-};
-
-struct bb_scorpion_access_funcs bb_l2_func[] = {
- {bb_l2_read_l2pm0, bb_l2_write_l2pm0, NULL, NULL},
- {bb_l2_read_l2pm1, bb_l2_write_l2pm1, NULL, NULL},
- {bb_l2_read_l2pm2, bb_l2_write_l2pm2, NULL, NULL},
- {bb_l2_read_l2pm3, bb_l2_write_l2pm3, NULL, NULL},
- {bb_l2_read_l2pm4, bb_l2_write_l2pm4, NULL, NULL},
-};
-
-#define COLMN0MASK 0x000000ff
-#define COLMN1MASK 0x0000ff00
-#define COLMN2MASK 0x00ff0000
-
-static u32 bb_l2_get_columnmask(u32 setval)
-{
- if (setval & COLMN0MASK)
- return 0xffffff00;
- else if (setval & COLMN1MASK)
- return 0xffff00ff;
- else if (setval & COLMN2MASK)
- return 0xff00ffff;
- else
- return 0x80ffffff;
-}
-
-static void bb_l2_evt_setup(u32 gr, u32 setval)
-{
- u32 val;
- if (bb_l2_func[gr].pre)
- bb_l2_func[gr].pre();
- val = bb_l2_get_columnmask(setval) & bb_l2_func[gr].read();
- val = val | setval;
- bb_l2_func[gr].write(val);
- if (bb_l2_func[gr].post)
- bb_l2_func[gr].post();
-}
-
-#define BB_L2_EVT_START_IDX 0x90
-#define BB_L2_INV_EVTYPE 0
-
-static unsigned int get_bb_l2_evtinfo(unsigned int evt_type,
- struct bb_l2_scorp_evt *evtinfo)
-{
- u32 idx;
- u8 prefix;
- u8 reg;
- u8 code;
- u8 group;
-
- prefix = (evt_type & 0xF0000) >> 16;
- if (prefix == SCORPION_L2_EVT_PREFIX) {
- reg = (evt_type & 0x0F000) >> 12;
- code = (evt_type & 0x00FF0) >> 4;
- group = evt_type & 0x0000F;
-
- if ((group > 3) || (reg > SCORPION_MAX_L2_REG))
- return BB_L2_INV_EVTYPE;
-
- evtinfo->val = 0x80000000 | (code << (group * 8));
- evtinfo->grp = reg;
- evtinfo->evt_type_act = group | (reg << 2);
- return evtinfo->evt_type_act;
- }
-
- if (evt_type < BB_L2_EVT_START_IDX || evt_type >= BB_L2_MAX_EVT)
- return BB_L2_INV_EVTYPE;
- idx = evt_type - BB_L2_EVT_START_IDX;
- if (sc_evt[idx].evt_type == evt_type) {
- evtinfo->val = sc_evt[idx].val;
- evtinfo->grp = sc_evt[idx].grp;
- evtinfo->evt_type_act = sc_evt[idx].evt_type_act;
- return sc_evt[idx].evt_type_act;
- }
- return BB_L2_INV_EVTYPE;
-}
-
-static inline void bb_l2_pmnc_write(unsigned long val)
-{
- val &= 0xff;
- asm volatile ("mcr p15, 3, %0, c15, c4, 0" : : "r" (val));
-}
-
-static inline unsigned long bb_l2_pmnc_read(void)
-{
- u32 val;
- asm volatile ("mrc p15, 3, %0, c15, c4, 0" : "=r" (val));
- return val;
-}
-
-static void bb_l2_set_evcntcr(void)
-{
- u32 val = 0x0;
- asm volatile ("mcr p15, 3, %0, c15, c6, 4" : : "r" (val));
-}
-
-static inline void bb_l2_set_evtyper(int ctr, int val)
-{
- /* select ctr */
- asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (ctr));
-
- /* write into EVTYPER */
- asm volatile ("mcr p15, 3, %0, c15, c6, 7" : : "r" (val));
-}
-
-static void bb_l2_set_evfilter_task_mode(void)
-{
- u32 filter_val = 0x000f0030 | 1 << smp_processor_id();
-
- asm volatile ("mcr p15, 3, %0, c15, c6, 3" : : "r" (filter_val));
-}
-
-static void bb_l2_set_evfilter_sys_mode(void)
-{
- u32 filter_val = 0x000f003f;
-
- asm volatile ("mcr p15, 3, %0, c15, c6, 3" : : "r" (filter_val));
-}
-
-static void bb_l2_enable_intenset(u32 idx)
-{
- if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
- asm volatile ("mcr p15, 3, %0, c15, c5, 1" : : "r"
- (1 << BB_L2CYCLE_CTR_BIT));
- } else {
- asm volatile ("mcr p15, 3, %0, c15, c5, 1" : : "r" (1 << idx));
- }
-}
-
-static void bb_l2_disable_intenclr(u32 idx)
-{
- if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
- asm volatile ("mcr p15, 3, %0, c15, c5, 0" : : "r"
- (1 << BB_L2CYCLE_CTR_BIT));
- } else {
- asm volatile ("mcr p15, 3, %0, c15, c5, 0" : : "r" (1 << idx));
- }
-}
-
-static void bb_l2_enable_counter(u32 idx)
-{
- if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
- asm volatile ("mcr p15, 3, %0, c15, c4, 3" : : "r"
- (1 << BB_L2CYCLE_CTR_BIT));
- } else {
- asm volatile ("mcr p15, 3, %0, c15, c4, 3" : : "r" (1 << idx));
- }
-}
-
-static void bb_l2_disable_counter(u32 idx)
-{
- if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
- asm volatile ("mcr p15, 3, %0, c15, c4, 2" : : "r"
- (1 << BB_L2CYCLE_CTR_BIT));
-
- } else {
- asm volatile ("mcr p15, 3, %0, c15, c4, 2" : : "r" (1 << idx));
- }
-}
-
-static u64 bb_l2_read_counter(u32 idx)
-{
- u32 val;
- unsigned long flags;
-
- if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
- asm volatile ("mrc p15, 3, %0, c15, c4, 5" : "=r" (val));
- } else {
- raw_spin_lock_irqsave(&bb_l2_pmu_lock, flags);
- asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (idx));
-
- /* read val from counter */
- asm volatile ("mrc p15, 3, %0, c15, c6, 5" : "=r" (val));
- raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, flags);
- }
-
- return val;
-}
-
-static void bb_l2_write_counter(u32 idx, u32 val)
-{
- unsigned long flags;
-
- if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
- asm volatile ("mcr p15, 3, %0, c15, c4, 5" : : "r" (val));
- } else {
- raw_spin_lock_irqsave(&bb_l2_pmu_lock, flags);
- /* select counter */
- asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (idx));
-
- /* write val into counter */
- asm volatile ("mcr p15, 3, %0, c15, c6, 5" : : "r" (val));
- raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, flags);
- }
-}
-
-static int
-bb_pmu_event_set_period(struct perf_event *event,
- struct hw_perf_event *hwc, int idx)
-{
- s64 left = local64_read(&hwc->period_left);
- s64 period = hwc->sample_period;
- int ret = 0;
-
- if (unlikely(left <= -period)) {
- left = period;
- local64_set(&hwc->period_left, left);
- hwc->last_period = period;
- ret = 1;
- }
-
- if (unlikely(left <= 0)) {
- left += period;
- local64_set(&hwc->period_left, left);
- hwc->last_period = period;
- ret = 1;
- }
-
- if (left > (s64) MAX_BB_L2_PERIOD)
- left = MAX_BB_L2_PERIOD;
-
- local64_set(&hwc->prev_count, (u64)-left);
-
- bb_l2_write_counter(idx, (u64) (-left) & 0xffffffff);
-
- perf_event_update_userpage(event);
-
- return ret;
-}
-
-static u64
-bb_pmu_event_update(struct perf_event *event, struct hw_perf_event *hwc,
- int idx, int overflow)
-{
- u64 prev_raw_count, new_raw_count;
- u64 delta;
-
-again:
- prev_raw_count = local64_read(&hwc->prev_count);
- new_raw_count = bb_l2_read_counter(idx);
-
- if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
- new_raw_count) != prev_raw_count)
- goto again;
-
- new_raw_count &= MAX_BB_L2_PERIOD;
- prev_raw_count &= MAX_BB_L2_PERIOD;
-
- if (overflow) {
- delta = MAX_BB_L2_PERIOD - prev_raw_count + new_raw_count;
- pr_err("%s: delta: %lld\n", __func__, delta);
- } else
- delta = new_raw_count - prev_raw_count;
-
- local64_add(delta, &event->count);
- local64_sub(delta, &hwc->period_left);
-
- pr_debug("%s: new: %lld, prev: %lld, event: %ld count: %lld\n",
- __func__, new_raw_count, prev_raw_count,
- hwc->config_base, local64_read(&event->count));
-
- return new_raw_count;
-}
-
-static void bb_l2_read(struct perf_event *event)
-{
- struct hw_perf_event *hwc = &event->hw;
-
- bb_pmu_event_update(event, hwc, hwc->idx, 0);
-}
-
-static void bb_l2_stop_counter(struct perf_event *event, int flags)
-{
- struct hw_perf_event *hwc = &event->hw;
- int idx = hwc->idx;
-
- if (!(hwc->state & PERF_HES_STOPPED)) {
- bb_l2_disable_intenclr(idx);
- bb_l2_disable_counter(idx);
-
- bb_pmu_event_update(event, hwc, idx, 0);
- hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
- }
-
- pr_debug("%s: event: %ld ctr: %d stopped\n", __func__, hwc->config_base,
- idx);
-}
-
-static void bb_l2_start_counter(struct perf_event *event, int flags)
-{
- struct hw_perf_event *hwc = &event->hw;
- int idx = hwc->idx;
- struct bb_l2_scorp_evt evtinfo;
- int evtype = hwc->config_base;
- int ev_typer;
- unsigned long iflags;
- int cpu_id = smp_processor_id();
-
- if (flags & PERF_EF_RELOAD)
- WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
-
- hwc->state = 0;
-
- bb_pmu_event_set_period(event, hwc, idx);
-
- if (hwc->config_base == BB_L2CYCLE_CTR_RAW_CODE)
- goto out;
-
- memset(&evtinfo, 0, sizeof(evtinfo));
-
- ev_typer = get_bb_l2_evtinfo(evtype, &evtinfo);
-
- raw_spin_lock_irqsave(&bb_l2_pmu_lock, iflags);
-
- bb_l2_set_evtyper(idx, ev_typer);
-
- bb_l2_set_evcntcr();
-
- if (event->cpu < 0)
- bb_l2_set_evfilter_task_mode();
- else
- bb_l2_set_evfilter_sys_mode();
-
- bb_l2_evt_setup(evtinfo.grp, evtinfo.val);
-
- raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, iflags);
-
-out:
-
- bb_l2_enable_intenset(idx);
-
- bb_l2_enable_counter(idx);
-
- pr_debug("%s: idx: %d, event: %d, val: %x, cpu: %d\n",
- __func__, idx, evtype, evtinfo.val, cpu_id);
-}
-
-static void bb_l2_del_event(struct perf_event *event, int flags)
-{
- struct hw_perf_event *hwc = &event->hw;
- int idx = hwc->idx;
- unsigned long iflags;
-
- raw_spin_lock_irqsave(&hw_bb_l2_pmu.lock, iflags);
-
- clear_bit(idx, (long unsigned int *)(&hw_bb_l2_pmu.active_mask));
-
- bb_l2_stop_counter(event, PERF_EF_UPDATE);
- hw_bb_l2_pmu.events[idx] = NULL;
- hwc->idx = -1;
-
- raw_spin_unlock_irqrestore(&hw_bb_l2_pmu.lock, iflags);
-
- pr_debug("%s: event: %ld deleted\n", __func__, hwc->config_base);
-
- perf_event_update_userpage(event);
-}
-
-static int bb_l2_add_event(struct perf_event *event, int flags)
-{
- int ctr = 0;
- struct hw_perf_event *hwc = &event->hw;
- unsigned long iflags;
- int err = 0;
-
- perf_pmu_disable(event->pmu);
-
- raw_spin_lock_irqsave(&hw_bb_l2_pmu.lock, iflags);
-
- /* Cycle counter has a resrvd index */
- if (hwc->config_base == BB_L2CYCLE_CTR_RAW_CODE) {
- if (hw_bb_l2_pmu.events[BB_L2CYCLE_CTR_EVENT_IDX]) {
- pr_err("%s: Stale cycle ctr event ptr !\n", __func__);
- err = -EINVAL;
- goto out;
- }
- hwc->idx = BB_L2CYCLE_CTR_EVENT_IDX;
- hw_bb_l2_pmu.events[BB_L2CYCLE_CTR_EVENT_IDX] = event;
- set_bit(BB_L2CYCLE_CTR_EVENT_IDX,
- (long unsigned int *)&hw_bb_l2_pmu.active_mask);
- goto skip_ctr_loop;
- }
-
- for (ctr = 0; ctr < MAX_BB_L2_CTRS - 1; ctr++) {
- if (!hw_bb_l2_pmu.events[ctr]) {
- hwc->idx = ctr;
- hw_bb_l2_pmu.events[ctr] = event;
- set_bit(ctr, (long unsigned int *)
- &hw_bb_l2_pmu.active_mask);
- break;
- }
- }
-
- if (hwc->idx < 0) {
- err = -ENOSPC;
- pr_err("%s: No space for event: %llx!!\n", __func__,
- event->attr.config);
- goto out;
- }
-
-skip_ctr_loop:
-
- bb_l2_disable_counter(hwc->idx);
-
- hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
-
- if (flags & PERF_EF_START)
- bb_l2_start_counter(event, PERF_EF_RELOAD);
-
- perf_event_update_userpage(event);
-
- pr_debug("%s: event: %ld, ctr: %d added from cpu:%d\n",
- __func__, hwc->config_base, hwc->idx, smp_processor_id());
-out:
- raw_spin_unlock_irqrestore(&hw_bb_l2_pmu.lock, iflags);
-
- /* Resume the PMU even if this event could not be added */
- perf_pmu_enable(event->pmu);
-
- return err;
-}
-
-static void bb_l2_pmu_enable(struct pmu *pmu)
-{
- unsigned long flags;
- isb();
- raw_spin_lock_irqsave(&bb_l2_pmu_lock, flags);
- /* Enable all counters */
- bb_l2_pmnc_write(bb_l2_pmnc_read() | SCORPIONL2_PMNC_E);
- raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, flags);
-}
-
-static void bb_l2_pmu_disable(struct pmu *pmu)
-{
- unsigned long flags;
- raw_spin_lock_irqsave(&bb_l2_pmu_lock, flags);
- /* Disable all counters */
- bb_l2_pmnc_write(bb_l2_pmnc_read() & ~SCORPIONL2_PMNC_E);
- raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, flags);
- isb();
-}
-
-static inline u32 bb_l2_get_reset_pmovsr(void)
-{
- u32 val;
-
- /* Read */
- asm volatile ("mrc p15, 3, %0, c15, c4, 1" : "=r" (val));
-
- /* Write to clear flags */
- val &= 0xffffffff;
- asm volatile ("mcr p15, 3, %0, c15, c4, 1" : : "r" (val));
-
- return val;
-}
-
-static irqreturn_t bb_l2_handle_irq(int irq_num, void *dev)
-{
- unsigned long pmovsr;
- struct perf_sample_data data;
- struct pt_regs *regs;
- struct perf_event *event;
- struct hw_perf_event *hwc;
- int bitp;
- int idx = 0;
-
- pmovsr = bb_l2_get_reset_pmovsr();
-
- if (!(pmovsr & 0xffffffff))
- return IRQ_NONE;
-
- regs = get_irq_regs();
-
- perf_sample_data_init(&data, 0);
-
- raw_spin_lock(&hw_bb_l2_pmu.lock);
-
- while (pmovsr) {
- bitp = __ffs(pmovsr);
-
- if (bitp == BB_L2CYCLE_CTR_BIT)
- idx = BB_L2CYCLE_CTR_EVENT_IDX;
- else
- idx = bitp;
-
- event = hw_bb_l2_pmu.events[idx];
-
- if (!event)
- goto next;
-
- if (!test_bit(idx, hw_bb_l2_pmu.active_mask))
- goto next;
-
- hwc = &event->hw;
- bb_pmu_event_update(event, hwc, idx, 1);
- data.period = event->hw.last_period;
-
- if (!bb_pmu_event_set_period(event, hwc, idx))
- goto next;
-
- if (perf_event_overflow(event, 0, &data, regs))
- bb_l2_disable_counter(hwc->idx);
-next:
- pmovsr &= (pmovsr - 1);
- }
-
- raw_spin_unlock(&hw_bb_l2_pmu.lock);
-
- irq_work_run();
-
- return IRQ_HANDLED;
-}
-
-static atomic_t active_bb_l2_events = ATOMIC_INIT(0);
-static DEFINE_MUTEX(bb_pmu_reserve_mutex);
-
-static int bb_pmu_reserve_hardware(void)
-{
- int i, err = -ENODEV, irq;
-
- bb_l2_pmu_device = reserve_pmu(ARM_PMU_DEVICE_L2);
-
- if (IS_ERR(bb_l2_pmu_device)) {
- pr_warning("unable to reserve pmu\n");
- return PTR_ERR(bb_l2_pmu_device);
- }
-
- if (bb_l2_pmu_device->num_resources < 1) {
- pr_err("no irqs for PMUs defined\n");
- return -ENODEV;
- }
-
- if (strncmp(bb_l2_pmu_device->name, "l2-arm-pmu", 6)) {
- pr_err("Incorrect pdev reserved !\n");
- return -EINVAL;
- }
-
- for (i = 0; i < bb_l2_pmu_device->num_resources; ++i) {
- irq = platform_get_irq(bb_l2_pmu_device, i);
- if (irq < 0)
- continue;
-
- err = request_irq(irq, bb_l2_handle_irq,
- IRQF_DISABLED | IRQF_NOBALANCING,
- "bb-l2-pmu", NULL);
- if (err) {
- pr_warning("unable to request IRQ%d for Krait L2 perf "
- "counters\n", irq);
- break;
- }
-
- irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq));
- }
-
- if (err) {
- for (i = i - 1; i >= 0; --i) {
- irq = platform_get_irq(bb_l2_pmu_device, i);
- if (irq >= 0)
- free_irq(irq, NULL);
- }
- release_pmu(bb_l2_pmu_device);
- bb_l2_pmu_device = NULL;
- }
-
- return err;
-}
-
-static void bb_pmu_release_hardware(void)
-{
- int i, irq;
-
- for (i = bb_l2_pmu_device->num_resources - 1; i >= 0; --i) {
- irq = platform_get_irq(bb_l2_pmu_device, i);
- if (irq >= 0)
- free_irq(irq, NULL);
- }
-
- bb_l2_pmu_disable(NULL);
-
- release_pmu(bb_l2_pmu_device);
- bb_l2_pmu_device = NULL;
-}
-
-static void bb_pmu_perf_event_destroy(struct perf_event *event)
-{
- if (atomic_dec_and_mutex_lock
- (&active_bb_l2_events, &bb_pmu_reserve_mutex)) {
- bb_pmu_release_hardware();
- mutex_unlock(&bb_pmu_reserve_mutex);
- }
-}
-
-static int bb_l2_event_init(struct perf_event *event)
-{
- int err = 0;
- struct hw_perf_event *hwc = &event->hw;
- int status = 0;
-
- switch (event->attr.type) {
- case PERF_TYPE_SHARED:
- break;
-
- default:
- return -ENOENT;
- }
-
- hwc->idx = -1;
-
- event->destroy = bb_pmu_perf_event_destroy;
-
- if (!atomic_inc_not_zero(&active_bb_l2_events)) {
- /* 0 active events */
- mutex_lock(&bb_pmu_reserve_mutex);
- err = bb_pmu_reserve_hardware();
- mutex_unlock(&bb_pmu_reserve_mutex);
- if (!err)
- atomic_inc(&active_bb_l2_events);
- else
- return err;
- }
-
- hwc->config = 0;
- hwc->event_base = 0;
-
- /* Check if we came via perf default syms */
- if (event->attr.config == PERF_COUNT_HW_L2_CYCLES)
- hwc->config_base = BB_L2CYCLE_CTR_RAW_CODE;
- else
- hwc->config_base = event->attr.config;
-
- /* Only one CPU can control the cycle counter */
- if (hwc->config_base == BB_L2CYCLE_CTR_RAW_CODE) {
- /* Check if its already running */
- asm volatile ("mrc p15, 3, %0, c15, c4, 6" : "=r" (status));
- if (status == 0x2) {
- err = -ENOSPC;
- goto out;
- }
- }
-
- if (!hwc->sample_period) {
- hwc->sample_period = MAX_BB_L2_PERIOD;
- hwc->last_period = hwc->sample_period;
- local64_set(&hwc->period_left, hwc->sample_period);
- }
-
- pr_debug("%s: event: %lld init'd\n", __func__, event->attr.config);
-
-out:
- if (err < 0)
- bb_pmu_perf_event_destroy(event);
-
- return err;
-}
-
-static struct pmu bb_l2_pmu = {
- .pmu_enable = bb_l2_pmu_enable,
- .pmu_disable = bb_l2_pmu_disable,
- .event_init = bb_l2_event_init,
- .add = bb_l2_add_event,
- .del = bb_l2_del_event,
- .start = bb_l2_start_counter,
- .stop = bb_l2_stop_counter,
- .read = bb_l2_read,
-};
-
-static const struct arm_pmu *__init scorpionmp_l2_pmu_init(void)
-{
- /* Register our own PMU here */
- perf_pmu_register(&bb_l2_pmu, "BB L2", PERF_TYPE_SHARED);
-
- memset(&hw_bb_l2_pmu, 0, sizeof(hw_bb_l2_pmu));
-
- /* Avoid spurious interrupts at startup */
- bb_l2_get_reset_pmovsr();
-
- raw_spin_lock_init(&hw_bb_l2_pmu.lock);
-
- /* Don't return an arm_pmu here */
- return NULL;
-}
-#else
-
-static const struct arm_pmu *__init scorpionmp_l2_pmu_init(void)
-{
- return NULL;
-}
-
-#endif
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 3554b56..24c57ae 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -242,6 +242,9 @@
select SPARSE_IRQ
select MSM_RPM_SMD
select REGULATOR
+ select MSM_QDSP6_APR
+ select MSM_QDSP6V2_CODECS
+ select MSM_AUDIO_QDSP6V2 if SND_SOC
config ARCH_FSM9XXX
bool "FSM9XXX"
@@ -2085,6 +2088,17 @@
help
Enables support for Qualcomm Debug Subsystem.
+config MSM_QDSS_STM_DEFAULT_ENABLE
+ bool "Turn on QDSS STM Tracing by Default"
+ depends on MSM_QDSS
+ help
+ Turns on QDSS STM tracing (hardware assisted software
+ instrumentation based tracing) by default. Otherwise, tracing is
+ disabled by default but can be enabled via sysfs.
+
+ For production builds, you should probably say 'N' here to avoid
+ potential power, performance and memory penalty.
+
config MSM_QDSS_ETM_DEFAULT_ENABLE
bool "Turn on QDSS ETM Tracing by Default"
depends on MSM_QDSS
@@ -2173,6 +2187,15 @@
used by audio driver to configure QDSP6's
ASM, ADM and AFE.
+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.
+
config MSM_AUDIO_QDSP6
bool "QDSP6 HW Audio support"
select SND_SOC_MSM_QDSP6_INTF
@@ -2181,6 +2204,16 @@
Enable HW audio support in QDSP6.
QDSP6 can support HW encoder & decoder and audio processing
+config MSM_AUDIO_QDSP6V2
+ bool "QDSP6V2 HW Audio support"
+ select SND_SOC_MSM_QDSP6V2_INTF
+ help
+ Enable HW audio support in QDSP6V2.
+ QDSP6V2 can support HW encoder & decoder and
+ audio processing. It will enable support for
+ AAC, AMRNB, AMRWB, EVRC, MP3, QCELP among
+ others.
+
config MSM_ULTRASOUND
bool "MSM ultrasound support"
depends on MSM_AUDIO_QDSP6
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index eb9dc0a..c3b13ec 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -25,7 +25,8 @@
obj-y += acpuclock.o
obj-$(CONFIG_ARCH_MSM7X27) += acpuclock-7627.o clock-pll.o
obj-$(CONFIG_ARCH_MSM_SCORPION) += pmu.o
-obj-$(CONFIG_ARCH_MSM_KRAIT) += msm-krait-l2-accessors.o pmu.o
+obj-$(CONFIG_ARCH_MSM_SCORPIONMP) += perf_event_msm_l2.o
+obj-$(CONFIG_ARCH_MSM_KRAIT) += msm-krait-l2-accessors.o pmu.o perf_event_msm_krait_l2.o
obj-$(CONFIG_ARCH_MSM7X27A) += pmu.o
ifndef CONFIG_MSM_SMP
@@ -54,7 +55,7 @@
msm-etm-objs := etm.o
obj-$(CONFIG_MSM_ETM) += msm-etm.o
-obj-$(CONFIG_MSM_QDSS) += qdss.o qdss-etb.o qdss-tpiu.o qdss-funnel.o qdss-etm.o
+obj-$(CONFIG_MSM_QDSS) += qdss.o qdss-etb.o qdss-tpiu.o qdss-funnel.o qdss-stm.o qdss-etm.o
quiet_cmd_mkrpcsym = MKCAP $@
cmd_mkrpcsym = $(PERL) $(srctree)/$(src)/mkrpcsym.pl $< $@
@@ -155,6 +156,7 @@
obj-$(CONFIG_MSM_QDSP6) += qdsp6/
obj-$(CONFIG_MSM8X60_AUDIO) += qdsp6v2/
obj-$(CONFIG_MSM_AUDIO_QDSP6) += qdsp6v2/
+obj-$(CONFIG_MSM_AUDIO_QDSP6V2) += qdsp6v2/
obj-$(CONFIG_MSM_HW3D) += hw3d.o
obj-$(CONFIG_PM) += pm-boot.o
obj-$(CONFIG_MSM_PM8X60) += pm-8x60.o pm-data.o
diff --git a/arch/arm/mach-msm/acpuclock-7627.c b/arch/arm/mach-msm/acpuclock-7627.c
index 7c2c556..f9ff226 100644
--- a/arch/arm/mach-msm/acpuclock-7627.c
+++ b/arch/arm/mach-msm/acpuclock-7627.c
@@ -18,6 +18,7 @@
#include <linux/version.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/string.h>
@@ -27,6 +28,8 @@
#include <linux/mutex.h>
#include <linux/io.h>
#include <linux/sort.h>
+#include <linux/platform_device.h>
+
#include <mach/board.h>
#include <mach/msm_iomap.h>
#include <mach/socinfo.h>
@@ -402,7 +405,7 @@
#ifdef CONFIG_CPU_FREQ_MSM
static struct cpufreq_frequency_table freq_table[NR_CPUS][20];
-static void __init cpufreq_table_init(void)
+static void __devinit cpufreq_table_init(void)
{
int cpu;
for_each_possible_cpu(cpu) {
@@ -693,7 +696,7 @@
return rc;
}
-static void __init acpuclk_hw_init(void)
+static void __devinit acpuclk_hw_init(void)
{
struct clkctl_acpu_speed *speed;
uint32_t div, sel, reg_clksel;
@@ -775,7 +778,7 @@
* Clock driver initialization
*---------------------------------------------------------------------------*/
#define MHZ 1000000
-static void __init select_freq_plan(void)
+static void __devinit select_freq_plan(void)
{
unsigned long pll_mhz[ACPU_PLL_END];
struct pll_freq_tbl_map *t;
@@ -835,7 +838,7 @@
* Hardware requires the CPU to be dropped to less than MAX_WAIT_FOR_IRQ_KHZ
* before entering a wait for irq low-power mode. Find a suitable rate.
*/
-static unsigned long __init find_wait_for_irq_khz(void)
+static unsigned long __devinit find_wait_for_irq_khz(void)
{
unsigned long found_khz = 0;
int i;
@@ -847,7 +850,7 @@
return found_khz;
}
-static void __init lpj_init(void)
+static void __devinit lpj_init(void)
{
int i = 0, cpu;
const struct clkctl_acpu_speed *base_clk = drv_state.current_speed;
@@ -868,7 +871,7 @@
}
}
-static void __init precompute_stepping(void)
+static void __devinit precompute_stepping(void)
{
int i, step_idx;
@@ -909,7 +912,7 @@
}
}
-static void __init print_acpu_freq_tbl(void)
+static void __devinit print_acpu_freq_tbl(void)
{
struct clkctl_acpu_speed *t;
short down_idx[ACPU_PLL_END];
@@ -947,15 +950,17 @@
.switch_time_us = 50,
};
-static int __init acpuclk_7627_init(struct acpuclk_soc_data *soc_data)
+static int __devinit acpuclk_7627_probe(struct platform_device *pdev)
{
+ const struct acpuclk_pdata *pdata = pdev->dev.platform_data;
+
pr_info("%s()\n", __func__);
drv_state.ebi1_clk = clk_get(NULL, "ebi1_acpu_clk");
BUG_ON(IS_ERR(drv_state.ebi1_clk));
mutex_init(&drv_state.lock);
- drv_state.max_speed_delta_khz = soc_data->max_speed_delta_khz;
+ drv_state.max_speed_delta_khz = pdata->max_speed_delta_khz;
select_freq_plan();
acpuclk_7627_data.wait_for_irq_khz = find_wait_for_irq_khz();
precompute_stepping();
@@ -970,23 +975,16 @@
return 0;
}
-struct acpuclk_soc_data acpuclk_7x27_soc_data __initdata = {
- .max_speed_delta_khz = 400000,
- .init = acpuclk_7627_init,
+static struct platform_driver acpuclk_7627_driver = {
+ .probe = acpuclk_7627_probe,
+ .driver = {
+ .name = "acpuclk-7627",
+ .owner = THIS_MODULE,
+ },
};
-struct acpuclk_soc_data acpuclk_7x27a_soc_data __initdata = {
- .max_speed_delta_khz = 400000,
- .init = acpuclk_7627_init,
-};
-
-struct acpuclk_soc_data acpuclk_7x27aa_soc_data __initdata = {
- .max_speed_delta_khz = 504000,
- .init = acpuclk_7627_init,
-};
-
-struct acpuclk_soc_data acpuclk_8625_soc_data __initdata = {
- /* TODO: Need to update speed delta from H/w Team */
- .max_speed_delta_khz = 604800,
- .init = acpuclk_7627_init,
-};
+static int __init acpuclk_7627_init(void)
+{
+ return platform_driver_register(&acpuclk_7627_driver);
+}
+postcore_initcall(acpuclk_7627_init);
diff --git a/arch/arm/mach-msm/acpuclock-7x30.c b/arch/arm/mach-msm/acpuclock-7x30.c
index 29b0065..b49613e 100644
--- a/arch/arm/mach-msm/acpuclock-7x30.c
+++ b/arch/arm/mach-msm/acpuclock-7x30.c
@@ -16,6 +16,7 @@
#include <linux/version.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/string.h>
@@ -25,6 +26,7 @@
#include <linux/mutex.h>
#include <linux/io.h>
#include <linux/sort.h>
+#include <linux/platform_device.h>
#include <mach/board.h>
#include <mach/msm_iomap.h>
#include <asm/mach-types.h>
@@ -315,7 +317,7 @@
* Clock driver initialization
*---------------------------------------------------------------------------*/
-static void __init acpuclk_hw_init(void)
+static void __devinit acpuclk_hw_init(void)
{
struct clkctl_acpu_speed *s;
uint32_t div, sel, src_num;
@@ -393,7 +395,7 @@
}
/* Initalize the lpj field in the acpu_freq_tbl. */
-static void __init lpj_init(void)
+static void __devinit lpj_init(void)
{
int i;
const struct clkctl_acpu_speed *base_clk = drv_state.current_speed;
@@ -431,7 +433,7 @@
* Truncate the frequency table at the current PLL2 rate and determine the
* backup PLL to use when scaling PLL2.
*/
-void __init pll2_fixup(void)
+void __devinit pll2_fixup(void)
{
struct clkctl_acpu_speed *speed = acpu_freq_tbl;
u8 pll2_l = readl_relaxed(PLL2_L_VAL_ADDR) & 0xFF;
@@ -453,7 +455,7 @@
#define RPM_BYPASS_MASK (1 << 3)
#define PMIC_MODE_MASK (1 << 4)
-static void __init populate_plls(void)
+static void __devinit populate_plls(void)
{
acpuclk_sources[PLL_1] = clk_get_sys("acpu", "pll1_clk");
BUG_ON(IS_ERR(acpuclk_sources[PLL_1]));
@@ -479,7 +481,7 @@
.switch_time_us = 50,
};
-static int __init acpuclk_7x30_init(struct acpuclk_soc_data *soc_data)
+static int __devinit acpuclk_7x30_probe(struct platform_device *pdev)
{
pr_info("%s()\n", __func__);
@@ -494,6 +496,16 @@
return 0;
}
-struct acpuclk_soc_data acpuclk_7x30_soc_data __initdata = {
- .init = acpuclk_7x30_init,
+static struct platform_driver acpuclk_7x30_driver = {
+ .probe = acpuclk_7x30_probe,
+ .driver = {
+ .name = "acpuclk-7x30",
+ .owner = THIS_MODULE,
+ },
};
+
+static int __init acpuclk_7x30_init(void)
+{
+ return platform_driver_register(&acpuclk_7x30_driver);
+}
+postcore_initcall(acpuclk_7x30_init);
diff --git a/arch/arm/mach-msm/acpuclock-8960.c b/arch/arm/mach-msm/acpuclock-8960.c
index a58eb6e..d29fee6 100644
--- a/arch/arm/mach-msm/acpuclock-8960.c
+++ b/arch/arm/mach-msm/acpuclock-8960.c
@@ -13,6 +13,7 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/delay.h>
@@ -22,6 +23,7 @@
#include <linux/cpufreq.h>
#include <linux/cpu.h>
#include <linux/regulator/consumer.h>
+#include <linux/platform_device.h>
#include <asm/mach-types.h>
#include <asm/cpu.h>
@@ -660,7 +662,7 @@
{ 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(15), 1175000 },
{ 0, { 1404000, HFPLL, 1, 0, 0x34 }, L2(15), 1187500 },
{ 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(15), 1187500 },
- { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(15), 1212500 },
+ { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(15), 1200000 },
{ 0, { 0 } }
};
@@ -687,7 +689,7 @@
{ 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(15), 1125000 },
{ 0, { 1404000, HFPLL, 1, 0, 0x34 }, L2(15), 1137500 },
{ 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(15), 1137500 },
- { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(15), 1175000 },
+ { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(15), 1150000 },
{ 0, { 0 } }
};
@@ -715,27 +717,80 @@
};
/* TODO: Update core voltages when data is available. */
-static struct acpu_level acpu_freq_tbl_8930[] = {
- { 0, { STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 925000 },
- { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 925000 },
- { 1, { 432000, HFPLL, 2, 0, 0x20 }, L2(6), 937500 },
- { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(6), 962500 },
- { 1, { 540000, HFPLL, 2, 0, 0x28 }, L2(6), 987500 },
+static struct acpu_level acpu_freq_tbl_8930_slow[] = {
+ { 0, { STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 950000 },
+ { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 950000 },
+ { 1, { 432000, HFPLL, 2, 0, 0x20 }, L2(6), 975000 },
+ { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(6), 975000 },
+ { 1, { 540000, HFPLL, 2, 0, 0x28 }, L2(6), 1000000 },
{ 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(6), 1000000 },
{ 1, { 648000, HFPLL, 1, 0, 0x18 }, L2(6), 1025000 },
- { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(6), 1037500 },
- { 1, { 756000, HFPLL, 1, 0, 0x1C }, L2(11), 1062500 },
- { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(11), 1087500 },
+ { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(6), 1025000 },
+ { 1, { 756000, HFPLL, 1, 0, 0x1C }, L2(11), 1075000 },
+ { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(11), 1075000 },
{ 1, { 864000, HFPLL, 1, 0, 0x20 }, L2(11), 1100000 },
- { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(11), 1125000 },
- { 1, { 972000, HFPLL, 1, 0, 0x24 }, L2(16), 1137500 },
- { 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(16), 1162500 },
- { 1, { 1080000, HFPLL, 1, 0, 0x28 }, L2(16), 1187500 },
- { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(16), 1200000 },
- { 1, { 1188000, HFPLL, 1, 0, 0x2C }, L2(16), 1225000 },
+ { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(11), 1100000 },
+ { 1, { 972000, HFPLL, 1, 0, 0x24 }, L2(11), 1125000 },
+ { 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(11), 1125000 },
+ { 1, { 1080000, HFPLL, 1, 0, 0x28 }, L2(16), 1175000 },
+ { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(16), 1175000 },
+ { 1, { 1188000, HFPLL, 1, 0, 0x2C }, L2(16), 1200000 },
+ { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(16), 1200000 },
+ { 1, { 1296000, HFPLL, 1, 0, 0x30 }, L2(16), 1225000 },
+ { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(16), 1225000 },
+ { 1, { 1404000, HFPLL, 1, 0, 0x34 }, L2(16), 1237500 },
{ 0, { 0 } }
};
+static struct acpu_level acpu_freq_tbl_8930_nom[] = {
+ { 0, { STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 925000 },
+ { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 925000 },
+ { 1, { 432000, HFPLL, 2, 0, 0x20 }, L2(6), 950000 },
+ { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(6), 950000 },
+ { 1, { 540000, HFPLL, 2, 0, 0x28 }, L2(6), 975000 },
+ { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(6), 975000 },
+ { 1, { 648000, HFPLL, 1, 0, 0x18 }, L2(6), 1000000 },
+ { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(6), 1000000 },
+ { 1, { 756000, HFPLL, 1, 0, 0x1C }, L2(11), 1050000 },
+ { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(11), 1050000 },
+ { 1, { 864000, HFPLL, 1, 0, 0x20 }, L2(11), 1075000 },
+ { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(11), 1075000 },
+ { 1, { 972000, HFPLL, 1, 0, 0x24 }, L2(11), 1100000 },
+ { 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(11), 1100000 },
+ { 1, { 1080000, HFPLL, 1, 0, 0x28 }, L2(16), 1150000 },
+ { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(16), 1150000 },
+ { 1, { 1188000, HFPLL, 1, 0, 0x2C }, L2(16), 1175000 },
+ { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(16), 1175000 },
+ { 1, { 1296000, HFPLL, 1, 0, 0x30 }, L2(16), 1200000 },
+ { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(16), 1200000 },
+ { 1, { 1404000, HFPLL, 1, 0, 0x34 }, L2(16), 1212500 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level acpu_freq_tbl_8930_fast[] = {
+ { 0, { STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 900000 },
+ { 1, { 432000, HFPLL, 2, 0, 0x20 }, L2(6), 900000 },
+ { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(6), 900000 },
+ { 1, { 540000, HFPLL, 2, 0, 0x28 }, L2(6), 925000 },
+ { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(6), 925000 },
+ { 1, { 648000, HFPLL, 1, 0, 0x18 }, L2(6), 950000 },
+ { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(6), 950000 },
+ { 1, { 756000, HFPLL, 1, 0, 0x1C }, L2(11), 1000000 },
+ { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(11), 1000000 },
+ { 1, { 864000, HFPLL, 1, 0, 0x20 }, L2(11), 1025000 },
+ { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(11), 1025000 },
+ { 1, { 972000, HFPLL, 1, 0, 0x24 }, L2(11), 1050000 },
+ { 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(11), 1050000 },
+ { 1, { 1080000, HFPLL, 1, 0, 0x28 }, L2(16), 1100000 },
+ { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(16), 1100000 },
+ { 1, { 1188000, HFPLL, 1, 0, 0x2C }, L2(16), 1125000 },
+ { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(16), 1125000 },
+ { 1, { 1296000, HFPLL, 1, 0, 0x30 }, L2(16), 1150000 },
+ { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(16), 1150000 },
+ { 1, { 1404000, HFPLL, 1, 0, 0x34 }, L2(16), 1162500 },
+ { 0, { 0 } }
+};
/* TODO: Update vdd_dig, vdd_mem and bw when data is available. */
#undef L2
#define L2(x) (&l2_freq_tbl_8627[(x)])
@@ -793,6 +848,12 @@
[PVS_FASTER] = acpu_freq_tbl_8064_fast,
};
+static struct acpu_level *acpu_freq_tbl_8930_pvs[NUM_PVS] __initdata = {
+ [PVS_SLOW] = acpu_freq_tbl_8930_slow,
+ [PVS_NOM] = acpu_freq_tbl_8930_nom,
+ [PVS_FAST] = acpu_freq_tbl_8930_fast,
+};
+
static unsigned long acpuclk_8960_get_rate(int cpu)
{
return scalable[cpu].current_speed->khz;
@@ -1560,8 +1621,10 @@
l2_freq_tbl = l2_freq_tbl_8627;
l2_freq_tbl_size = ARRAY_SIZE(l2_freq_tbl_8627);
} else if (cpu_is_msm8930()) {
+ enum pvs pvs_id = get_pvs();
+
scalable = scalable_8930;
- acpu_freq_tbl = acpu_freq_tbl_8930;
+ acpu_freq_tbl = acpu_freq_tbl_8930_pvs[pvs_id];
l2_freq_tbl = l2_freq_tbl_8930;
l2_freq_tbl_size = ARRAY_SIZE(l2_freq_tbl_8930);
} else {
@@ -1588,7 +1651,7 @@
.wait_for_irq_khz = STBY_KHZ,
};
-static int __init acpuclk_8960_init(struct acpuclk_soc_data *soc_data)
+static int __init acpuclk_8960_probe(struct platform_device *pdev)
{
struct acpu_level *max_acpu_level = select_freq_plan();
@@ -1606,14 +1669,15 @@
return 0;
}
-struct acpuclk_soc_data acpuclk_8960_soc_data __initdata = {
- .init = acpuclk_8960_init,
+static struct platform_driver acpuclk_8960_driver = {
+ .driver = {
+ .name = "acpuclk-8960",
+ .owner = THIS_MODULE,
+ },
};
-struct acpuclk_soc_data acpuclk_8930_soc_data __initdata = {
- .init = acpuclk_8960_init,
-};
-
-struct acpuclk_soc_data acpuclk_8064_soc_data __initdata = {
- .init = acpuclk_8960_init,
-};
+static int __init acpuclk_8960_init(void)
+{
+ return platform_driver_probe(&acpuclk_8960_driver, acpuclk_8960_probe);
+}
+device_initcall(acpuclk_8960_init);
diff --git a/arch/arm/mach-msm/acpuclock-8x50.c b/arch/arm/mach-msm/acpuclock-8x50.c
index cde5a14..996f883 100644
--- a/arch/arm/mach-msm/acpuclock-8x50.c
+++ b/arch/arm/mach-msm/acpuclock-8x50.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-2012, Code Aurora Forum. 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
@@ -12,6 +12,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/delay.h>
@@ -20,6 +21,7 @@
#include <linux/cpufreq.h>
#include <linux/clk.h>
#include <linux/mfd/tps65023.h>
+#include <linux/platform_device.h>
#include <mach/board.h>
#include <mach/msm_iomap.h>
@@ -130,7 +132,7 @@
#ifdef CONFIG_CPU_FREQ_MSM
static struct cpufreq_frequency_table freq_table[20];
-static void __init cpufreq_table_init(void)
+static void __devinit cpufreq_table_init(void)
{
unsigned int i;
unsigned int freq_cnt = 0;
@@ -504,7 +506,7 @@
return rc;
}
-static void __init acpuclk_hw_init(void)
+static void __devinit acpuclk_hw_init(void)
{
struct clkctl_acpu_speed *speed;
uint32_t div, sel, regval;
@@ -582,7 +584,7 @@
#define PLL0_M_VAL_ADDR (MSM_CLK_CTL_BASE + 0x308)
-static void __init acpu_freq_tbl_fixup(void)
+static void __devinit acpu_freq_tbl_fixup(void)
{
void __iomem *ct_csr_base;
uint32_t tcsr_spare2, pll0_m_val;
@@ -645,7 +647,7 @@
}
/* Initalize the lpj field in the acpu_freq_tbl. */
-static void __init lpj_init(void)
+static void __devinit lpj_init(void)
{
int i;
const struct clkctl_acpu_speed *base_clk = drv_state.current_speed;
@@ -657,7 +659,7 @@
}
#ifdef CONFIG_MSM_CPU_AVS
-static int __init acpu_avs_init(int (*set_vdd) (int), int khz)
+static int __devinit acpu_avs_init(int (*set_vdd) (int), int khz)
{
int i;
int freq_count = 0;
@@ -704,7 +706,7 @@
.switch_time_us = 20,
};
-static int __init acpuclk_8x50_init(struct acpuclk_soc_data *soc_data)
+static int __devinit acpuclk_8x50_probe(struct platform_device *pdev)
{
mutex_init(&drv_state.lock);
drv_state.acpu_set_vdd = qsd8x50_tps65023_set_dcdc1;
@@ -736,6 +738,16 @@
return 0;
}
-struct acpuclk_soc_data acpuclk_8x50_soc_data __initdata = {
- .init = acpuclk_8x50_init,
+static struct platform_driver acpuclk_8x50_driver = {
+ .probe = acpuclk_8x50_probe,
+ .driver = {
+ .name = "acpuclk-8x50",
+ .owner = THIS_MODULE,
+ },
};
+
+static int __init acpuclk_8x50_init(void)
+{
+ return platform_driver_register(&acpuclk_8x50_driver);
+}
+postcore_initcall(acpuclk_8x50_init);
diff --git a/arch/arm/mach-msm/acpuclock-8x60.c b/arch/arm/mach-msm/acpuclock-8x60.c
index 48efa18..ef34b3c 100644
--- a/arch/arm/mach-msm/acpuclock-8x60.c
+++ b/arch/arm/mach-msm/acpuclock-8x60.c
@@ -11,6 +11,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/delay.h>
@@ -20,6 +21,7 @@
#include <linux/cpufreq.h>
#include <linux/cpu.h>
#include <linux/regulator/consumer.h>
+#include <linux/platform_device.h>
#include <asm/cpu.h>
@@ -734,7 +736,7 @@
}
/* AVS needs SAW_VCTL to be intitialized correctly, before enable,
- * and is not initialized at acpuclk_init().
+ * and is not initialized during probe.
*/
if (reason == SETRATE_CPUFREQ)
AVS_DISABLE(cpu);
@@ -1062,7 +1064,7 @@
.wait_for_irq_khz = MAX_AXI,
};
-static int __init acpuclk_8x60_init(struct acpuclk_soc_data *soc_data)
+static int __init acpuclk_8x60_probe(struct platform_device *pdev)
{
struct clkctl_acpu_speed *max_freq;
int cpu;
@@ -1091,6 +1093,15 @@
return 0;
}
-struct acpuclk_soc_data acpuclk_8x60_soc_data __initdata = {
- .init = acpuclk_8x60_init,
+static struct platform_driver acpuclk_8x60_driver = {
+ .driver = {
+ .name = "acpuclk-8x60",
+ .owner = THIS_MODULE,
+ },
};
+
+static int __init acpuclk_8x60_init(void)
+{
+ return platform_driver_probe(&acpuclk_8x60_driver, acpuclk_8x60_probe);
+}
+device_initcall(acpuclk_8x60_init);
diff --git a/arch/arm/mach-msm/acpuclock-9615.c b/arch/arm/mach-msm/acpuclock-9615.c
index 8882f41..db7bab32 100644
--- a/arch/arm/mach-msm/acpuclock-9615.c
+++ b/arch/arm/mach-msm/acpuclock-9615.c
@@ -14,6 +14,7 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/delay.h>
@@ -22,6 +23,7 @@
#include <linux/errno.h>
#include <linux/cpufreq.h>
#include <linux/clk.h>
+#include <linux/platform_device.h>
#include <asm/cpu.h>
@@ -39,7 +41,6 @@
#define REG_CLKDIV_1 (MSM_APCS_GLB_BASE + 0x14)
#define REG_CLKOUTSEL (MSM_APCS_GLB_BASE + 0x18)
-#define MAX_VDD_CPU 1150000
#define MAX_VDD_MEM 1150000
enum clk_src {
@@ -111,12 +112,12 @@
static uint32_t bus_perf_client;
static struct clkctl_acpu_speed acpu_freq_tbl[] = {
- { 0, 19200, SRC_CXO, 0, 0, 950000, 1050000, 0 },
- { 1, 138000, SRC_PLL0, 6, 1, 950000, 1050000, 2 },
- { 1, 276000, SRC_PLL0, 6, 0, 1050000, 1050000, 2 },
- { 1, 384000, SRC_PLL8, 3, 0, 1150000, 1150000, 4 },
+ { 0, 19200, SRC_CXO, 0, 0, RPM_VREG_CORNER_LOW, 1050000, 0 },
+ { 1, 138000, SRC_PLL0, 6, 1, RPM_VREG_CORNER_LOW, 1050000, 2 },
+ { 1, 276000, SRC_PLL0, 6, 0, RPM_VREG_CORNER_NOMINAL, 1050000, 2 },
+ { 1, 384000, SRC_PLL8, 3, 0, RPM_VREG_CORNER_HIGH, 1150000, 4 },
/* The row below may be changed at runtime depending on hw rev. */
- { 1, 440000, SRC_PLL9, 2, 0, 1150000, 1150000, 4 },
+ { 1, 440000, SRC_PLL9, 2, 0, RPM_VREG_CORNER_HIGH, 1150000, 4 },
{ 0 }
};
@@ -171,8 +172,8 @@
return rc;
}
- rc = rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_S1, RPM_VREG_VOTER1,
- vdd_cpu, MAX_VDD_CPU, 0);
+ rc = rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_VDD_DIG_CORNER,
+ RPM_VREG_VOTER1, vdd_cpu, RPM_VREG_CORNER_HIGH, 0);
if (rc)
pr_err("vdd_cpu increase failed (%d)\n", rc);
@@ -185,8 +186,9 @@
int ret;
/* Update CPU voltage. */
- ret = rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_S1, RPM_VREG_VOTER1,
- vdd_cpu, MAX_VDD_CPU, 0);
+ ret = rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_VDD_DIG_CORNER,
+ RPM_VREG_VOTER1, vdd_cpu, RPM_VREG_CORNER_HIGH, 0);
+
if (ret) {
pr_err("vdd_cpu decrease failed (%d)\n", ret);
return;
@@ -306,7 +308,7 @@
.wait_for_irq_khz = 19200,
};
-static int __init acpuclk_9615_init(struct acpuclk_soc_data *soc_data)
+static int __init acpuclk_9615_probe(struct platform_device *pdev)
{
unsigned long max_cpu_khz = 0;
int i;
@@ -351,6 +353,15 @@
return 0;
}
-struct acpuclk_soc_data acpuclk_9615_soc_data __initdata = {
- .init = acpuclk_9615_init,
+static struct platform_driver acpuclk_9615_driver = {
+ .driver = {
+ .name = "acpuclk-9615",
+ .owner = THIS_MODULE,
+ },
};
+
+static int __init acpuclk_9615_init(void)
+{
+ return platform_driver_probe(&acpuclk_9615_driver, acpuclk_9615_probe);
+}
+device_initcall(acpuclk_9615_init);
diff --git a/arch/arm/mach-msm/acpuclock-fsm9xxx.c b/arch/arm/mach-msm/acpuclock-fsm9xxx.c
index 3cdc58d..af1c0eb 100644
--- a/arch/arm/mach-msm/acpuclock-fsm9xxx.c
+++ b/arch/arm/mach-msm/acpuclock-fsm9xxx.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. 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,8 +11,10 @@
*
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/platform_device.h>
#include <mach/board.h>
#include "acpuclock.h"
@@ -40,13 +42,22 @@
.get_rate = acpuclk_9xxx_get_rate,
};
-static int __init acpuclk_9xxx_init(struct acpuclk_soc_data *soc_data)
+static int __init acpuclk_9xxx_probe(struct platform_device *pdev)
{
acpuclk_register(&acpuclk_9xxx_data);
pr_info("ACPU running at %lu KHz\n", acpuclk_get_rate(0));
return 0;
}
-struct acpuclk_soc_data acpuclk_9xxx_soc_data __initdata = {
- .init = acpuclk_9xxx_init,
+static struct platform_driver acpuclk_9xxx_driver = {
+ .driver = {
+ .name = "acpuclk-9xxx",
+ .owner = THIS_MODULE,
+ },
};
+
+static int __init acpuclk_9xxx_init(void)
+{
+ return platform_driver_probe(&acpuclk_9xxx_driver, acpuclk_9xxx_probe);
+}
+device_initcall(acpuclk_9xxx_init);
diff --git a/arch/arm/mach-msm/acpuclock.c b/arch/arm/mach-msm/acpuclock.c
index 91071c4..be056e6 100644
--- a/arch/arm/mach-msm/acpuclock.c
+++ b/arch/arm/mach-msm/acpuclock.c
@@ -53,24 +53,7 @@
return rate;
}
-void __init acpuclk_register(struct acpuclk_data *data)
+void __devinit acpuclk_register(struct acpuclk_data *data)
{
acpuclk_data = data;
}
-
-int __init acpuclk_init(struct acpuclk_soc_data *soc_data)
-{
- int rc;
-
- if (!soc_data->init)
- return -EINVAL;
-
- rc = soc_data->init(soc_data);
- if (rc)
- return rc;
-
- if (!acpuclk_data)
- return -ENODEV;
-
- return 0;
-}
diff --git a/arch/arm/mach-msm/acpuclock.h b/arch/arm/mach-msm/acpuclock.h
index c5f0ee3..e73a2af 100644
--- a/arch/arm/mach-msm/acpuclock.h
+++ b/arch/arm/mach-msm/acpuclock.h
@@ -31,12 +31,11 @@
};
/**
- * struct acpuclk_soc_data - SoC data for acpuclk_init()
+ * struct acpuclk_pdata - Platform data for acpuclk
*/
-struct acpuclk_soc_data {
+struct acpuclk_pdata {
unsigned long max_speed_delta_khz;
unsigned int max_axi_khz;
- int (*init)(struct acpuclk_soc_data *);
};
/**
@@ -91,25 +90,4 @@
*/
void acpuclk_register(struct acpuclk_data *data);
-/**
- * acpuclk_init() - acpuclock driver initialization function
- *
- * Return 0 for success.
- */
-int acpuclk_init(struct acpuclk_soc_data *);
-
-/* SoC-specific acpuclock initialization functions. */
-extern struct acpuclk_soc_data acpuclk_7x27_soc_data;
-extern struct acpuclk_soc_data acpuclk_7x27a_soc_data;
-extern struct acpuclk_soc_data acpuclk_7x27aa_soc_data;
-extern struct acpuclk_soc_data acpuclk_7x30_soc_data;
-extern struct acpuclk_soc_data acpuclk_8x50_soc_data;
-extern struct acpuclk_soc_data acpuclk_8x60_soc_data;
-extern struct acpuclk_soc_data acpuclk_8960_soc_data;
-extern struct acpuclk_soc_data acpuclk_9xxx_soc_data;
-extern struct acpuclk_soc_data acpuclk_9615_soc_data;
-extern struct acpuclk_soc_data acpuclk_8930_soc_data;
-extern struct acpuclk_soc_data acpuclk_8064_soc_data;
-extern struct acpuclk_soc_data acpuclk_8625_soc_data;
-
#endif
diff --git a/arch/arm/mach-msm/board-8064-camera.c b/arch/arm/mach-msm/board-8064-camera.c
index 2d1f787..c37491d 100644
--- a/arch/arm/mach-msm/board-8064-camera.c
+++ b/arch/arm/mach-msm/board-8064-camera.c
@@ -334,17 +334,11 @@
static struct msm_camera_device_platform_data msm_camera_csi_device_data[] = {
{
.csid_core = 0,
- .is_csiphy = 1,
- .is_csid = 1,
- .is_ispif = 1,
.is_vpe = 1,
.cam_bus_scale_table = &cam_bus_client_pdata,
},
{
.csid_core = 1,
- .is_csiphy = 1,
- .is_csid = 1,
- .is_ispif = 1,
.is_vpe = 1,
.cam_bus_scale_table = &cam_bus_client_pdata,
},
diff --git a/arch/arm/mach-msm/board-8064-pmic.c b/arch/arm/mach-msm/board-8064-pmic.c
index bef5cdc..fc886ed 100644
--- a/arch/arm/mach-msm/board-8064-pmic.c
+++ b/arch/arm/mach-msm/board-8064-pmic.c
@@ -364,6 +364,7 @@
static struct pm8xxx_ccadc_platform_data
apq8064_pm8xxx_ccadc_pdata = {
.r_sense = 10,
+ .calib_delay_ms = 600000,
};
static struct pm8921_bms_platform_data
@@ -372,7 +373,6 @@
.r_sense = 10,
.i_test = 2500,
.v_failure = 3000,
- .calib_delay_ms = 600000,
.max_voltage_uv = MAX_VOLTAGE_MV * 1000,
};
diff --git a/arch/arm/mach-msm/board-8064-regulator.c b/arch/arm/mach-msm/board-8064-regulator.c
index f7d5403..622b213 100644
--- a/arch/arm/mach-msm/board-8064-regulator.c
+++ b/arch/arm/mach-msm/board-8064-regulator.c
@@ -560,7 +560,7 @@
RPM_SMPS(S2, 0, 1, 0, 1300000, 1300000, NULL, 0, 1p60, NONE, NONE),
RPM_SMPS(S3, 0, 1, 1, 500000, 1150000, NULL, 100000, 4p80, NONE, NONE),
RPM_SMPS(S4, 1, 1, 0, 1800000, 1800000, NULL, 100000, 1p60, AUTO, AUTO),
- RPM_SMPS(S7, 0, 1, 0, 1300000, 1300000, NULL, 100000, 3p20, NONE, NONE),
+ RPM_SMPS(S7, 0, 0, 0, 1300000, 1300000, NULL, 100000, 3p20, NONE, NONE),
RPM_SMPS(S8, 0, 1, 0, 2200000, 2200000, NULL, 0, 1p60, NONE, NONE),
/* ID a_on pd ss min_uV max_uV supply sys_uA init_ip */
@@ -586,7 +586,7 @@
RPM_LDO(L23, 0, 1, 0, 1800000, 1800000, NULL, 0, 0),
RPM_LDO(L24, 0, 1, 1, 750000, 1150000, "8921_s1", 10000, 10000),
RPM_LDO(L25, 1, 1, 0, 1250000, 1250000, "8921_s1", 10000, 10000),
- RPM_LDO(L27, 0, 1, 0, 1100000, 1100000, "8921_s7", 0, 0),
+ RPM_LDO(L27, 0, 0, 0, 1100000, 1100000, "8921_s7", 0, 0),
RPM_LDO(L28, 0, 1, 0, 1050000, 1050000, "8921_s7", 0, 0),
RPM_LDO(L29, 0, 1, 0, 2000000, 2000000, NULL, 0, 0),
diff --git a/arch/arm/mach-msm/board-8064-storage.c b/arch/arm/mach-msm/board-8064-storage.c
index fe4beab..a53f771 100644
--- a/arch/arm/mach-msm/board-8064-storage.c
+++ b/arch/arm/mach-msm/board-8064-storage.c
@@ -291,7 +291,7 @@
.pin_data = &mmc_slot_pin_data[SDCC3],
.vreg_data = &mmc_slot_vreg_data[SDCC3],
.wpswitch_gpio = PM8921_GPIO_PM_TO_SYS(17),
- .wpswitch_polarity = 1,
+ .is_wpswitch_active_low = true,
.status_gpio = 26,
.status_irq = MSM_GPIO_TO_INT(26),
.irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
@@ -354,7 +354,7 @@
if (apq8064_sdc3_pdata) {
if (!machine_is_apq8064_cdp()) {
apq8064_sdc3_pdata->wpswitch_gpio = 0;
- apq8064_sdc3_pdata->wpswitch_polarity = 0;
+ apq8064_sdc3_pdata->is_wpswitch_active_low = false;
}
if (machine_is_mpq8064_cdp() || machine_is_mpq8064_hrd() ||
machine_is_mpq8064_dtv()) {
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index 5f1a57d..e4958f5 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -29,6 +29,7 @@
#include <linux/ion.h>
#include <linux/memory.h>
#include <linux/memblock.h>
+#include <linux/msm_thermal.h>
#include <linux/i2c/atmel_mxt_ts.h>
#include <linux/cyttsp-qc.h>
#include <linux/i2c/isa1200.h>
@@ -74,7 +75,6 @@
#include "msm_watchdog.h"
#include "board-8064.h"
-#include "acpuclock.h"
#include "spm.h"
#include <mach/mpm.h>
#include "rpm_resources.h"
@@ -1717,6 +1717,14 @@
.id = -1,
};
+static struct msm_thermal_data msm_thermal_pdata = {
+ .sensor_id = 7,
+ .poll_ms = 1000,
+ .limit_temp = 60,
+ .temp_hysteresis = 10,
+ .limit_freq = 918000,
+};
+
#define MSM_SHARED_RAM_PHYS 0x80000000
static void __init apq8064_map_io(void)
{
@@ -2110,6 +2118,7 @@
};
static struct platform_device *common_devices[] __initdata = {
+ &msm8960_device_acpuclk,
&apq8064_device_dmov,
&apq8064_device_qup_spi_gsbi5,
&apq8064_device_ext_5v_vreg,
@@ -2210,6 +2219,8 @@
&apq8064_cpu_idle_device,
&apq8064_msm_gov_device,
&apq8064_device_cache_erp,
+ &msm8960_device_ebi1_ch0_erp,
+ &msm8960_device_ebi1_ch1_erp,
&epm_adc_device,
&apq8064_qdss_device,
&msm_etb_device,
@@ -2862,6 +2873,7 @@
static void __init apq8064_common_init(void)
{
msm_tsens_early_init(&apq_tsens_pdata);
+ msm_thermal_init(&msm_thermal_pdata);
if (socinfo_init() < 0)
pr_err("socinfo_init() failed!\n");
BUG_ON(msm_rpm_init(&apq8064_rpm_data));
@@ -2909,7 +2921,6 @@
ARRAY_SIZE(apq8064_slim_devices));
apq8064_init_dsps();
msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data));
- acpuclk_init(&acpuclk_8064_soc_data);
msm_spm_l2_init(msm_spm_l2_data);
BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
msm_pm_init_sleep_status_data(&msm_pm_slp_sts_data);
diff --git a/arch/arm/mach-msm/board-8930-camera.c b/arch/arm/mach-msm/board-8930-camera.c
index e7f0e68..6ee315c 100644
--- a/arch/arm/mach-msm/board-8930-camera.c
+++ b/arch/arm/mach-msm/board-8930-camera.c
@@ -342,17 +342,11 @@
static struct msm_camera_device_platform_data msm_camera_csi_device_data[] = {
{
.csid_core = 0,
- .is_csiphy = 1,
- .is_csid = 1,
- .is_ispif = 1,
.is_vpe = 1,
.cam_bus_scale_table = &cam_bus_client_pdata,
},
{
.csid_core = 1,
- .is_csiphy = 1,
- .is_csid = 1,
- .is_ispif = 1,
.is_vpe = 1,
.cam_bus_scale_table = &cam_bus_client_pdata,
},
diff --git a/arch/arm/mach-msm/board-8930-pmic.c b/arch/arm/mach-msm/board-8930-pmic.c
index 188cf39..e6a13b1 100644
--- a/arch/arm/mach-msm/board-8930-pmic.c
+++ b/arch/arm/mach-msm/board-8930-pmic.c
@@ -328,6 +328,7 @@
static struct pm8xxx_ccadc_platform_data pm8xxx_ccadc_pdata = {
.r_sense = 10,
+ .calib_delay_ms = 600000,
};
static struct pm8xxx_misc_platform_data pm8xxx_misc_pdata = {
@@ -343,7 +344,6 @@
.r_sense = 10,
.i_test = 2500,
.v_failure = 3000,
- .calib_delay_ms = 600000,
.max_voltage_uv = MAX_VOLTAGE_MV * 1000,
};
diff --git a/arch/arm/mach-msm/board-8930-storage.c b/arch/arm/mach-msm/board-8930-storage.c
index 65da578..739b1c7 100644
--- a/arch/arm/mach-msm/board-8930-storage.c
+++ b/arch/arm/mach-msm/board-8930-storage.c
@@ -220,7 +220,7 @@
#define MSM_MPM_PIN_SDC3_DAT1 21
static unsigned int sdc1_sup_clk_rates[] = {
- 400000, 24000000, 48000000,
+ 400000, 24000000, 48000000, 96000000
};
#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT
@@ -259,7 +259,7 @@
.wpswitch_gpio = PM8921_GPIO_PM_TO_SYS(16),
#else
.wpswitch_gpio = 66,
- .wpswitch_polarity = 1,
+ .is_wpswitch_active_low = true,
#endif
#endif
.vreg_data = &mmc_slot_vreg_data[SDCC3],
@@ -288,6 +288,16 @@
void __init msm8930_init_mmc(void)
{
#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT
+ /*
+ * When eMMC runs in DDR mode on CDP platform, we have
+ * seen instability due to DATA CRC errors. These errors are
+ * attributed to long physical path between MSM and eMMC on CDP.
+ * So let's not enable the DDR mode on CDP platform but let other
+ * platforms take advantage of eMMC DDR mode.
+ */
+ if (!machine_is_msm8930_cdp())
+ msm8960_sdc1_data.uhs_caps |= (MMC_CAP_1_8V_DDR |
+ MMC_CAP_UHS_DDR50);
/* SDC1 : eMMC card connected */
msm_add_sdcc(1, &msm8960_sdc1_data);
#endif
@@ -295,7 +305,7 @@
/* SDC3: External card slot */
if (!machine_is_msm8930_cdp()) {
msm8960_sdc3_data.wpswitch_gpio = 0;
- msm8960_sdc3_data.wpswitch_polarity = 0;
+ msm8960_sdc3_data.is_wpswitch_active_low = false;
}
msm_add_sdcc(3, &msm8960_sdc3_data);
#endif
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index 8df37f7..ab9fe5e 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -42,6 +42,7 @@
#include <linux/gpio_keys.h>
#include <linux/memory.h>
#include <linux/memblock.h>
+#include <linux/msm_thermal.h>
#include <linux/slimbus/slimbus.h>
#include <linux/mfd/wcd9xxx/core.h>
@@ -86,7 +87,6 @@
#include <mach/cpuidle.h>
#include "rpm_resources.h"
#include <mach/mpm.h>
-#include "acpuclock.h"
#include "smd_private.h"
#include "pm-boot.h"
#include "msm_watchdog.h"
@@ -1018,6 +1018,55 @@
#define QCE_SHARE_CE_RESOURCE 1
#define QCE_CE_SHARED 0
+/* Begin Bus scaling definitions */
+static struct msm_bus_vectors crypto_hw_init_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_ADM_PORT0,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+ {
+ .src = MSM_BUS_MASTER_ADM_PORT1,
+ .dst = MSM_BUS_SLAVE_GSBI1_UART,
+ .ab = 0,
+ .ib = 0,
+ },
+};
+
+static struct msm_bus_vectors crypto_hw_active_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_ADM_PORT0,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 70000000UL,
+ .ib = 70000000UL,
+ },
+ {
+ .src = MSM_BUS_MASTER_ADM_PORT1,
+ .dst = MSM_BUS_SLAVE_GSBI1_UART,
+ .ab = 2480000000UL,
+ .ib = 2480000000UL,
+ },
+};
+
+static struct msm_bus_paths crypto_hw_bus_scale_usecases[] = {
+ {
+ ARRAY_SIZE(crypto_hw_init_vectors),
+ crypto_hw_init_vectors,
+ },
+ {
+ ARRAY_SIZE(crypto_hw_active_vectors),
+ crypto_hw_active_vectors,
+ },
+};
+
+static struct msm_bus_scale_pdata crypto_hw_bus_scale_pdata = {
+ crypto_hw_bus_scale_usecases,
+ ARRAY_SIZE(crypto_hw_bus_scale_usecases),
+ .name = "cryptohw",
+};
+/* End Bus Scaling Definitions*/
+
static struct resource qcrypto_resources[] = {
[0] = {
.start = QCE_0_BASE,
@@ -1080,6 +1129,7 @@
.shared_ce_resource = QCE_SHARE_CE_RESOURCE,
.hw_key_support = QCE_HW_KEY_SUPPORT,
.sha_hmac = QCE_SHA_HMAC_SUPPORT,
+ .bus_scale_table = &crypto_hw_bus_scale_pdata,
};
static struct platform_device qcrypto_device = {
@@ -1102,6 +1152,7 @@
.shared_ce_resource = QCE_SHARE_CE_RESOURCE,
.hw_key_support = QCE_HW_KEY_SUPPORT,
.sha_hmac = QCE_SHA_HMAC_SUPPORT,
+ .bus_scale_table = &crypto_hw_bus_scale_pdata,
};
static struct platform_device qcedev_device = {
@@ -1364,6 +1415,14 @@
};
#endif
+static int hsusb_phy_init_seq[] = {
+ 0x44, 0x80, /* set VBUS valid threshold
+ and disconnect valid threshold */
+ 0x38, 0x81, /* update DC voltage level */
+ 0x24, 0x82, /* set preemphasis and rise/fall time */
+ 0x13, 0x83, /* set source impedance adjusment */
+ -1};
+
static struct msm_otg_platform_data msm_otg_pdata = {
.mode = USB_OTG,
.otg_control = OTG_PMIC_CONTROL,
@@ -1923,6 +1982,14 @@
.id = -1,
};
+static struct msm_thermal_data msm_thermal_pdata = {
+ .sensor_id = 9,
+ .poll_ms = 1000,
+ .limit_temp = 60,
+ .temp_hysteresis = 10,
+ .limit_freq = 918000,
+};
+
#ifdef CONFIG_MSM_FAKE_BATTERY
static struct platform_device fish_battery_device = {
.name = "fish_battery",
@@ -1984,6 +2051,7 @@
};
static struct platform_device *common_devices[] __initdata = {
+ &msm8960_device_acpuclk,
&msm8960_device_dmov,
&msm_device_smd,
&msm8960_device_uart_gsbi5,
@@ -2333,6 +2401,7 @@
pr_err("meminfo_init() failed!\n");
msm_tsens_early_init(&msm_tsens_pdata);
+ msm_thermal_init(&msm_thermal_pdata);
BUG_ON(msm_rpm_init(&msm8930_rpm_data));
BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data));
@@ -2341,6 +2410,7 @@
pr_err("Failed to initialize XO votes\n");
platform_device_register(&msm8930_device_rpm_regulator);
msm_clock_init(&msm8930_clock_init_data);
+ msm_otg_pdata.phy_init_seq = hsusb_phy_init_seq;
msm8960_device_otg.dev.platform_data = &msm_otg_pdata;
android_usb_pdata.swfi_latency =
msm_rpmrs_levels[0].latency_us;
@@ -2382,7 +2452,6 @@
msm8930_init_cam();
#endif
msm8930_init_mmc();
- acpuclk_init(&acpuclk_8930_soc_data);
mxt_init_vkeys_8930();
register_i2c_devices();
msm8930_init_fb();
diff --git a/arch/arm/mach-msm/board-8960-camera.c b/arch/arm/mach-msm/board-8960-camera.c
index 9c25b78..b6c03a4 100644
--- a/arch/arm/mach-msm/board-8960-camera.c
+++ b/arch/arm/mach-msm/board-8960-camera.c
@@ -399,25 +399,16 @@
static struct msm_camera_device_platform_data msm_camera_csi_device_data[] = {
{
.csid_core = 0,
- .is_csiphy = 1,
- .is_csid = 1,
- .is_ispif = 1,
.is_vpe = 1,
.cam_bus_scale_table = &cam_bus_client_pdata,
},
{
.csid_core = 1,
- .is_csiphy = 1,
- .is_csid = 1,
- .is_ispif = 1,
.is_vpe = 1,
.cam_bus_scale_table = &cam_bus_client_pdata,
},
{
.csid_core = 2,
- .is_csiphy = 1,
- .is_csid = 1,
- .is_ispif = 1,
.is_vpe = 1,
.cam_bus_scale_table = &cam_bus_client_pdata,
},
diff --git a/arch/arm/mach-msm/board-8960-pmic.c b/arch/arm/mach-msm/board-8960-pmic.c
index 977dbb2..19564e9 100644
--- a/arch/arm/mach-msm/board-8960-pmic.c
+++ b/arch/arm/mach-msm/board-8960-pmic.c
@@ -442,9 +442,8 @@
.r_sense = 10,
.i_test = 2500,
.v_failure = 3000,
- .calib_delay_ms = 600000,
.max_voltage_uv = MAX_VOLTAGE_MV * 1000,
- .rconn_mohm = 30,
+ .rconn_mohm = 18,
};
#define PM8921_LC_LED_MAX_CURRENT 4 /* I = 4mA */
@@ -567,6 +566,7 @@
static struct pm8xxx_ccadc_platform_data pm8xxx_ccadc_pdata = {
.r_sense = 10,
+ .calib_delay_ms = 600000,
};
/**
diff --git a/arch/arm/mach-msm/board-8960-storage.c b/arch/arm/mach-msm/board-8960-storage.c
index e674e91..4b09f82 100644
--- a/arch/arm/mach-msm/board-8960-storage.c
+++ b/arch/arm/mach-msm/board-8960-storage.c
@@ -271,7 +271,7 @@
#define MSM_MPM_PIN_SDC3_DAT1 21
static unsigned int sdc1_sup_clk_rates[] = {
- 400000, 24000000, 48000000
+ 400000, 24000000, 48000000, 96000000
};
#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT
@@ -361,6 +361,16 @@
void __init msm8960_init_mmc(void)
{
#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT
+ /*
+ * When eMMC runs in DDR mode on CDP platform, we have
+ * seen instability due to DATA CRC errors. These errors are
+ * attributed to long physical path between MSM and eMMC on CDP.
+ * So let's not enable the DDR mode on CDP platform but let other
+ * platforms take advantage of eMMC DDR mode.
+ */
+ if (!machine_is_msm8960_cdp())
+ msm8960_sdc1_data.uhs_caps |= (MMC_CAP_1_8V_DDR |
+ MMC_CAP_UHS_DDR50);
/* SDC1 : eMMC card connected */
msm_add_sdcc(1, &msm8960_sdc1_data);
#endif
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 9c096b7..628a324 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -42,6 +42,7 @@
#include <linux/i2c/isa1200.h>
#include <linux/memory.h>
#include <linux/memblock.h>
+#include <linux/msm_thermal.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -97,7 +98,6 @@
#include <mach/cpuidle.h>
#include "rpm_resources.h"
#include <mach/mpm.h>
-#include "acpuclock.h"
#include "smd_private.h"
#include "pm-boot.h"
#include "msm_watchdog.h"
@@ -2428,6 +2428,14 @@
.id = -1,
};
+static struct msm_thermal_data msm_thermal_pdata = {
+ .sensor_id = 0,
+ .poll_ms = 1000,
+ .limit_temp = 60,
+ .temp_hysteresis = 10,
+ .limit_freq = 918000,
+};
+
#ifdef CONFIG_MSM_FAKE_BATTERY
static struct platform_device fish_battery_device = {
.name = "fish_battery",
@@ -2508,6 +2516,7 @@
#endif
static struct platform_device *common_devices[] __initdata = {
+ &msm8960_device_acpuclk,
&msm8960_device_dmov,
&msm_device_smd,
&msm_device_uart_dm6,
@@ -2587,6 +2596,8 @@
&msm8960_cpu_idle_device,
&msm8960_msm_gov_device,
&msm8960_device_cache_erp,
+ &msm8960_device_ebi1_ch0_erp,
+ &msm8960_device_ebi1_ch1_erp,
&msm8960_cache_dump_device,
&msm8960_iommu_domain_device,
&msm_tsens_device,
@@ -3042,6 +3053,7 @@
wdog_pdata->bark_time = 15000;
msm_tsens_early_init(&msm_tsens_pdata);
+ msm_thermal_init(&msm_thermal_pdata);
BUG_ON(msm_rpm_init(&msm8960_rpm_data));
BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data));
regulator_suppress_info_printing();
@@ -3058,7 +3070,6 @@
platform_add_devices(common_devices, ARRAY_SIZE(common_devices));
msm8960_pm8921_gpio_mpp_init();
platform_add_devices(sim_devices, ARRAY_SIZE(sim_devices));
- acpuclk_init(&acpuclk_8960_soc_data);
msm8960_device_qup_spi_gsbi1.dev.platform_data =
&msm8960_qup_spi_gsbi1_pdata;
@@ -3075,6 +3086,7 @@
static void __init msm8960_rumi3_init(void)
{
msm_tsens_early_init(&msm_tsens_pdata);
+ msm_thermal_init(&msm_thermal_pdata);
BUG_ON(msm_rpm_init(&msm8960_rpm_data));
BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data));
regulator_suppress_info_printing();
@@ -3107,6 +3119,7 @@
pr_err("meminfo_init() failed!\n");
msm_tsens_early_init(&msm_tsens_pdata);
+ msm_thermal_init(&msm_thermal_pdata);
BUG_ON(msm_rpm_init(&msm8960_rpm_data));
BUG_ON(msm_rpmrs_levels_init(&msm_rpmrs_data));
@@ -3171,7 +3184,6 @@
msm8960_init_cam();
#endif
msm8960_init_mmc();
- acpuclk_init(&acpuclk_8960_soc_data);
if (machine_is_msm8960_liquid())
mxt_init_hw_liquid();
register_i2c_devices();
diff --git a/arch/arm/mach-msm/board-9615-regulator.c b/arch/arm/mach-msm/board-9615-regulator.c
index 1122ed9..2561def 100644
--- a/arch/arm/mach-msm/board-9615-regulator.c
+++ b/arch/arm/mach-msm/board-9615-regulator.c
@@ -69,9 +69,6 @@
};
VREG_CONSUMERS(S1) = {
REGULATOR_SUPPLY("8018_s1", NULL),
- REGULATOR_SUPPLY("HSUSB_VDDCX", "msm_otg"),
- REGULATOR_SUPPLY("HSIC_VDDCX", "msm_hsic_peripheral"),
- REGULATOR_SUPPLY("HSIC_VDDCX", "msm_hsic_host"),
};
VREG_CONSUMERS(S2) = {
REGULATOR_SUPPLY("8018_s2", NULL),
@@ -117,6 +114,11 @@
REGULATOR_SUPPLY("ext_2p95v", NULL),
REGULATOR_SUPPLY("sdc_vdd", "msm_sdcc.1"),
};
+VREG_CONSUMERS(VDD_DIG_CORNER) = {
+ REGULATOR_SUPPLY("hsusb_vdd_dig", "msm_otg"),
+ REGULATOR_SUPPLY("hsic_vdd_dig", "msm_hsic_peripheral"),
+ REGULATOR_SUPPLY("hsic_vdd_dig", "msm_hsic_host"),
+};
#define PM8XXX_VREG_INIT(_id, _name, _min_uV, _max_uV, _modes, _ops, \
_apply_uV, _pull_down, _always_on, _supply_regulator, \
@@ -262,6 +264,16 @@
RPM_VREG_STATE_OFF, _sleep_selectable, _always_on, \
_supply_regulator, 0)
+#define RPM_CORNER(_id, _always_on, _sleep_selectable, _min_uV, _max_uV, \
+ _supply_regulator) \
+ RPM_INIT(_id, _min_uV, _max_uV, 0, REGULATOR_CHANGE_VOLTAGE \
+ | REGULATOR_CHANGE_STATUS, 0, _max_uV, 0, 0, 0, \
+ RPM_VREG_PIN_CTRL_NONE, NONE, RPM_VREG_PIN_FN_9615_NONE, \
+ RPM_VREG_FORCE_MODE_9615_NONE, \
+ RPM_VREG_FORCE_MODE_9615_NONE, RPM_VREG_POWER_MODE_9615_PWM, \
+ RPM_VREG_STATE_OFF, _sleep_selectable, _always_on, \
+ _supply_regulator, 0)
+
/* Pin control initialization */
#define RPM_PC_INIT(_id, _always_on, _pin_fn, _pin_ctrl, _supply_regulator) \
{ \
@@ -331,6 +343,10 @@
/* ID a_on pd ss supply */
RPM_VS(LVS1, 0, 1, 0, "8018_s3"),
+
+ /* ID a_on ss min_corner max_corner supply */
+ RPM_CORNER(VDD_DIG_CORNER, 0, 1, RPM_VREG_CORNER_NONE,
+ RPM_VREG_CORNER_HIGH, NULL),
};
int msm_pm8018_regulator_pdata_len __devinitdata =
@@ -342,5 +358,5 @@
.num_regulators = ARRAY_SIZE(msm_rpm_regulator_init_data),
.version = RPM_VREG_VERSION_9615,
.vreg_id_vdd_mem = RPM_VREG_ID_PM8018_L9,
- .vreg_id_vdd_dig = RPM_VREG_ID_PM8018_S1,
+ .vreg_id_vdd_dig = RPM_VREG_ID_PM8018_VDD_DIG_CORNER,
};
diff --git a/arch/arm/mach-msm/board-9615.c b/arch/arm/mach-msm/board-9615.c
index dc376b5..568de46 100644
--- a/arch/arm/mach-msm/board-9615.c
+++ b/arch/arm/mach-msm/board-9615.c
@@ -50,7 +50,6 @@
#include "devices.h"
#include "board-9615.h"
#include "pm.h"
-#include "acpuclock.h"
#include "pm-boot.h"
#include <mach/gpiomux.h>
@@ -759,10 +758,11 @@
.vbus_power = msm_hsusb_vbus_power,
.disable_reset_on_disconnect = true,
.enable_lpm_on_dev_suspend = true,
+ .core_clk_always_on_workaround = true,
};
static struct msm_hsic_peripheral_platform_data msm_hsic_peripheral_pdata = {
- .keep_core_clk_on_suspend_workaround = true,
+ .core_clk_always_on_workaround = true,
};
#define PID_MAGIC_ID 0x71432909
@@ -850,6 +850,7 @@
};
static struct platform_device *common_devices[] = {
+ &msm9615_device_acpuclk,
&msm9615_device_dmov,
&msm_device_smd,
#ifdef CONFIG_LTC4088_CHARGER
@@ -979,7 +980,6 @@
msm_device_usb_bam.dev.platform_data = &msm_usb_bam_pdata;
platform_add_devices(common_devices, ARRAY_SIZE(common_devices));
msm9615_pm8xxx_gpio_mpp_init();
- acpuclk_init(&acpuclk_9615_soc_data);
/* Ensure ar6000pm device is registered before MMC/SDC */
msm9615_init_ar6000pm();
diff --git a/arch/arm/mach-msm/board-copper.c b/arch/arm/mach-msm/board-copper.c
index 614cfe2..94a73c6 100644
--- a/arch/arm/mach-msm/board-copper.c
+++ b/arch/arm/mach-msm/board-copper.c
@@ -46,6 +46,7 @@
#include "clock.h"
#include "devices.h"
#include "spm.h"
+#include "modem_notifier.h"
#define MSM_KERNEL_EBI1_MEM_SIZE 0x280000
#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY
@@ -442,6 +443,12 @@
CLK_DUMMY("core_clk", NULL, "f9966000.i2c", 0),
CLK_DUMMY("iface_clk", NULL, "f9966000.i2c", 0),
CLK_DUMMY("core_clk", NULL, "fe12f000.slim", OFF),
+ CLK_DUMMY("core_clk", "mdp.0", NULL, 0),
+ CLK_DUMMY("core_clk_src", "mdp.0", NULL, 0),
+ CLK_DUMMY("lut_clk", "mdp.0", NULL, 0),
+ CLK_DUMMY("vsync_clk", "mdp.0", NULL, 0),
+ CLK_DUMMY("iface_clk", "mdp.0", NULL, 0),
+ CLK_DUMMY("bus_clk", "mdp.0", NULL, 0),
};
struct clock_init_data msm_dummy_clock_init_data __initdata = {
@@ -457,6 +464,7 @@
*/
void __init msm_copper_add_drivers(void)
{
+ msm_init_modem_notifier_list();
msm_smd_init();
msm_rpm_driver_init();
rpm_regulator_smd_driver_init();
@@ -509,6 +517,7 @@
"msm_rng", NULL),
OF_DEV_AUXDATA("qcom,qseecom", 0xFE806000, \
"qseecom", NULL),
+ OF_DEV_AUXDATA("qcom,mdss_mdp", 0xFD900000, "mdp.0", NULL),
{}
};
diff --git a/arch/arm/mach-msm/board-fsm9xxx.c b/arch/arm/mach-msm/board-fsm9xxx.c
index 6ad3cef..b071353 100644
--- a/arch/arm/mach-msm/board-fsm9xxx.c
+++ b/arch/arm/mach-msm/board-fsm9xxx.c
@@ -38,7 +38,6 @@
#include <mach/socinfo.h>
#include "devices.h"
#include "timer.h"
-#include "acpuclock.h"
#include "pm.h"
#include "spm.h"
#include <linux/regulator/consumer.h>
@@ -804,11 +803,17 @@
},
};
+static struct platform_device fsm9xxx_device_acpuclk = {
+ .name = "acpuclk-9xxx",
+ .id = -1,
+};
+
/*
* Devices
*/
static struct platform_device *devices[] __initdata = {
+ &fsm9xxx_device_acpuclk,
&msm_device_smd,
&msm_device_dmov,
&msm_device_nand,
@@ -873,8 +878,6 @@
static void __init fsm9xxx_init(void)
{
- acpuclk_init(&acpuclk_9xxx_soc_data);
-
regulator_has_full_constraints();
#if defined(CONFIG_I2C_SSBI) || defined(CONFIG_MSM_SSBI)
diff --git a/arch/arm/mach-msm/board-msm7627a-camera.c b/arch/arm/mach-msm/board-msm7627a-camera.c
index 2873fc0..3ab5ba0 100644
--- a/arch/arm/mach-msm/board-msm7627a-camera.c
+++ b/arch/arm/mach-msm/board-msm7627a-camera.c
@@ -120,19 +120,32 @@
{"usb2", REG_LDO, 1800000, 1800000, 0},
};
+static struct camera_vreg_t ov5647_gpio_vreg[] = {
+ {"cam_ov5647_avdd", REG_GPIO, 0, 0, 0},
+ {"cam_ov5647_vdd", REG_GPIO, 0, 0, 0},
+};
+
+static struct camera_vreg_t ov8825_gpio_vreg[] = {
+ {"cam_ov8825_avdd", REG_GPIO, 0, 0, 0},
+ {"cam_ov8825_vdd", REG_GPIO, 0, 0, 0},
+};
+
+static struct camera_vreg_t ov7692_gpio_vreg[] = {
+ {"cam_ov7692_avdd", REG_GPIO, 0, 0, 0},
+ {"cam_ov7692_vdd", REG_GPIO, 0, 0, 0},
+};
+
static struct msm_camera_sensor_info msm_camera_sensor_s5k4e1_data;
struct msm_camera_device_platform_data msm_camera_device_data_csi1[] = {
{
.csid_core = 1,
- .is_csic = 1,
.ioclk = {
.vfe_clk_rate = 192000000,
},
},
{
.csid_core = 1,
- .is_csic = 1,
.ioclk = {
.vfe_clk_rate = 266667000,
},
@@ -142,14 +155,12 @@
struct msm_camera_device_platform_data msm_camera_device_data_csi0[] = {
{
.csid_core = 0,
- .is_csic = 1,
.ioclk = {
.vfe_clk_rate = 192000000,
},
},
{
.csid_core = 0,
- .is_csic = 1,
.ioclk = {
.vfe_clk_rate = 266667000,
},
@@ -184,7 +195,6 @@
static struct msm_camera_sensor_info msm_camera_sensor_s5k4e1_data = {
.sensor_name = "s5k4e1",
.sensor_reset_enable = 1,
- .pmic_gpio_enable = 0,
.pdata = &msm_camera_device_data_csi1[0],
.flash_data = &flash_s5k4e1,
.sensor_platform_info = &sensor_board_info_s5k4e1,
@@ -210,8 +220,6 @@
static struct msm_camera_sensor_info msm_camera_sensor_ov7692_data = {
.sensor_name = "ov7692",
.sensor_reset_enable = 0,
- .pmic_gpio_enable = 1,
- .sensor_lcd_gpio_onoff = lcd_camera_power_onoff,
.sensor_reset = GPIO_SKU1_CAM_VGA_RESET_N,
.sensor_pwd = GPIO_SKU1_CAM_VGA_SHDN,
.pdata = &msm_camera_device_data_csi0[0],
@@ -254,8 +262,6 @@
static struct msm_camera_sensor_info msm_camera_sensor_ov5647_data = {
.sensor_name = "ov5647",
.sensor_reset_enable = 1,
- .pmic_gpio_enable = 1,
- .sensor_lcd_gpio_onoff = lcd_camera_power_onoff,
.sensor_reset = GPIO_SKU3_CAM_5MP_CAMIF_RESET,
.sensor_pwd = GPIO_SKU3_CAM_5MP_SHDN_N,
.pdata = &msm_camera_device_data_csi1[0],
@@ -315,7 +321,6 @@
static struct msm_camera_sensor_info msm_camera_sensor_mt9e013_data = {
.sensor_name = "mt9e013",
.sensor_reset_enable = 1,
- .pmic_gpio_enable = 0,
.pdata = &msm_camera_device_data_csi1[1],
.flash_data = &flash_mt9e013,
.sensor_platform_info = &sensor_board_info_mt9e013,
@@ -341,7 +346,6 @@
static struct msm_camera_sensor_info msm_camera_sensor_ov9726_data = {
.sensor_name = "ov9726",
.sensor_reset_enable = 0,
- .pmic_gpio_enable = 0,
.pdata = &msm_camera_device_data_csi0[0],
.flash_data = &flash_ov9726,
.sensor_platform_info = &sensor_board_info_ov9726,
@@ -375,6 +379,21 @@
sensor_board_info_ov8825.num_vreg = 0;
}
+ if (machine_is_msm8625_evb()
+ || machine_is_msm8625_evt()) {
+ sensor_board_info_ov7692.cam_vreg =
+ ov7692_gpio_vreg;
+ sensor_board_info_ov7692.num_vreg =
+ ARRAY_SIZE(ov7692_gpio_vreg);
+ sensor_board_info_ov5647.cam_vreg =
+ ov5647_gpio_vreg;
+ sensor_board_info_ov5647.num_vreg =
+ ARRAY_SIZE(ov5647_gpio_vreg);
+ sensor_board_info_ov8825.cam_vreg =
+ ov8825_gpio_vreg;
+ sensor_board_info_ov8825.num_vreg =
+ ARRAY_SIZE(ov8825_gpio_vreg);
+ }
platform_device_register(&msm_camera_server);
if (machine_is_msm8625_surf() || machine_is_msm8625_evb()
|| machine_is_msm8625_evt()
@@ -1026,6 +1045,7 @@
ARRAY_SIZE(cam_exp_i2c_info));
}
+#ifndef CONFIG_MSM_CAMERA_V4L2
#define LCD_CAMERA_LDO_2V8 35 /* SKU1&SKU3 2.8V LDO */
#define SKU3_LCD_CAMERA_LDO_1V8 40 /* SKU3 1.8V LDO */
#define SKU7_LCD_CAMERA_LDO_1V8 58 /* SKU7 1.8V LDO */
@@ -1124,6 +1144,7 @@
return rc;
}
EXPORT_SYMBOL(lcd_camera_power_onoff);
+#endif
void __init msm7627a_camera_init(void)
{
@@ -1144,7 +1165,6 @@
GPIO_SKU7_CAM_5MP_SHDN_N;
msm_camera_sensor_ov5647_data.sensor_reset =
GPIO_SKU7_CAM_5MP_CAMIF_RESET;
-
}
/* LCD and camera power (VREG & LDO) init */
@@ -1152,8 +1172,9 @@
|| machine_is_msm8625_evt()
|| machine_is_msm7627a_qrd3()
|| machine_is_msm8625_qrd7()) {
-
+#ifndef CONFIG_MSM_CAMERA_V4L2
lcd_camera_power_init();
+#endif
evb_camera_gpio_cfg();
}
diff --git a/arch/arm/mach-msm/board-msm7x27.c b/arch/arm/mach-msm/board-msm7x27.c
index d42458f..a7fed3e 100644
--- a/arch/arm/mach-msm/board-msm7x27.c
+++ b/arch/arm/mach-msm/board-msm7x27.c
@@ -65,7 +65,6 @@
#include "board-msm7627-regulator.h"
#include "devices.h"
#include "clock.h"
-#include "acpuclock.h"
#include "msm-keypad-devices.h"
#include "pm.h"
#include "pm-boot.h"
@@ -1775,7 +1774,7 @@
}
}
#endif
- acpuclk_init(&acpuclk_7x27_soc_data);
+ platform_device_register(&msm7x27_device_acpuclk);
usb_mpp_init();
diff --git a/arch/arm/mach-msm/board-msm7x27a.c b/arch/arm/mach-msm/board-msm7x27a.c
index 1521a6c..dc473e6 100644
--- a/arch/arm/mach-msm/board-msm7x27a.c
+++ b/arch/arm/mach-msm/board-msm7x27a.c
@@ -162,7 +162,7 @@
#define MSM_PMEM_MDP_SIZE 0x2300000
#define MSM7x25A_MSM_PMEM_MDP_SIZE 0x1500000
-#define MSM_PMEM_ADSP_SIZE 0x1100000
+#define MSM_PMEM_ADSP_SIZE 0x1200000
#define MSM7x25A_MSM_PMEM_ADSP_SIZE 0xB91000
#endif
diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c
index 7ac0c9a..2834f24 100644
--- a/arch/arm/mach-msm/board-msm7x30.c
+++ b/arch/arm/mach-msm/board-msm7x30.c
@@ -105,7 +105,7 @@
*/
#define MSM_V4L2_VIDEO_OVERLAY_BUF_SIZE 2764800
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL
+#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL
#define MSM_FB_EXT_BUF_SIZE (1280 * 720 * 2 * 1) /* 2 bpp x 1 page */
#else
#define MSM_FB_EXT_BUF_SIZE 0
@@ -880,7 +880,6 @@
static struct msm_camera_device_platform_data msm_camera_csi_device_data[] = {
{
.csid_core = 0,
- .is_csic = 1,
.is_vpe = 1,
.ioclk = {
.vfe_clk_rate = 153600000,
@@ -6957,7 +6956,7 @@
msm7x30_init_uart2();
#endif
msm_spm_init(&msm_spm_data, 1);
- acpuclk_init(&acpuclk_7x30_soc_data);
+ platform_device_register(&msm7x30_device_acpuclk);
if (machine_is_msm7x30_surf() || machine_is_msm7x30_fluid())
msm7x30_cfg_smsc911x();
diff --git a/arch/arm/mach-msm/board-msm8x60-camera.c b/arch/arm/mach-msm/board-msm8x60-camera.c
index 32d5530..cd95630 100644
--- a/arch/arm/mach-msm/board-msm8x60-camera.c
+++ b/arch/arm/mach-msm/board-msm8x60-camera.c
@@ -361,7 +361,6 @@
static struct msm_camera_device_platform_data msm_camera_csi_device_data[] = {
{
.csid_core = 0,
- .is_csic = 1,
.is_vpe = 1,
.cam_bus_scale_table = &cam_bus_client_pdata,
.ioclk = {
@@ -370,7 +369,6 @@
},
{
.csid_core = 1,
- .is_csic = 1,
.is_vpe = 1,
.cam_bus_scale_table = &cam_bus_client_pdata,
.ioclk = {
diff --git a/arch/arm/mach-msm/board-msm8x60.c b/arch/arm/mach-msm/board-msm8x60.c
index 54c8fbd..098ad6e 100644
--- a/arch/arm/mach-msm/board-msm8x60.c
+++ b/arch/arm/mach-msm/board-msm8x60.c
@@ -100,7 +100,6 @@
#include "peripheral-loader.h"
#include <linux/platform_data/qcom_crypto_device.h>
#include "rpm_resources.h"
-#include "acpuclock.h"
#include "pm-boot.h"
#include "board-storage-common-a.h"
@@ -5135,6 +5134,7 @@
};
static struct platform_device *surf_devices[] __initdata = {
+ &msm8x60_device_acpuclk,
&msm_device_smd,
&msm_device_uart_dm12,
&msm_pil_q6v3,
@@ -10339,9 +10339,6 @@
*/
msm8x60_init_buses();
platform_add_devices(early_devices, ARRAY_SIZE(early_devices));
- /* CPU frequency control is not supported on simulated targets. */
- if (!machine_is_msm8x60_rumi3() && !machine_is_msm8x60_sim())
- acpuclk_init(&acpuclk_8x60_soc_data);
/*
* Enable EBI2 only for boards which make use of it. Leave
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index d8d8934..9c80c8b 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -130,7 +130,7 @@
#ifdef CONFIG_ARCH_MSM7X27A
#define MSM_PMEM_MDP_SIZE 0x2300000
-#define MSM_PMEM_ADSP_SIZE 0x1100000
+#define MSM_PMEM_ADSP_SIZE 0x1200000
#endif
static struct android_usb_platform_data android_usb_pdata = {
diff --git a/arch/arm/mach-msm/board-qsd8x50.c b/arch/arm/mach-msm/board-qsd8x50.c
index 6a39316..4df4266 100644
--- a/arch/arm/mach-msm/board-qsd8x50.c
+++ b/arch/arm/mach-msm/board-qsd8x50.c
@@ -2409,7 +2409,7 @@
{
msm_clock_init(&qds8x50_clock_init_data);
qsd8x50_cfg_smc91x();
- acpuclk_init(&acpuclk_8x50_soc_data);
+ platform_device_register(&msm8x50_device_acpuclk);
msm_hsusb_pdata.swfi_latency =
msm_pm_data
diff --git a/arch/arm/mach-msm/clock-9615.c b/arch/arm/mach-msm/clock-9615.c
index 834deb6..a2e0bc9 100644
--- a/arch/arm/mach-msm/clock-9615.c
+++ b/arch/arm/mach-msm/clock-9615.c
@@ -187,15 +187,15 @@
static int set_vdd_dig(struct clk_vdd_class *vdd_class, int level)
{
- static const int vdd_uv[] = {
- [VDD_DIG_NONE] = 0,
- [VDD_DIG_LOW] = 945000,
- [VDD_DIG_NOMINAL] = 1050000,
- [VDD_DIG_HIGH] = 1150000
+ static const int vdd_corner[] = {
+ [VDD_DIG_NONE] = RPM_VREG_CORNER_NONE,
+ [VDD_DIG_LOW] = RPM_VREG_CORNER_LOW,
+ [VDD_DIG_NOMINAL] = RPM_VREG_CORNER_NOMINAL,
+ [VDD_DIG_HIGH] = RPM_VREG_CORNER_HIGH,
};
- return rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_S1, RPM_VREG_VOTER3,
- vdd_uv[level], vdd_uv[VDD_DIG_HIGH], 1);
+ return rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_VDD_DIG_CORNER,
+ RPM_VREG_VOTER3, vdd_corner[level], RPM_VREG_CORNER_HIGH, 1);
}
static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig);
diff --git a/arch/arm/mach-msm/clock-copper.c b/arch/arm/mach-msm/clock-copper.c
index 6ddf1a0..87a8998 100644
--- a/arch/arm/mach-msm/clock-copper.c
+++ b/arch/arm/mach-msm/clock-copper.c
@@ -746,6 +746,11 @@
static DEFINE_CLK_VOTER(ocmemgx_msmbus_clk, &ocmemgx_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(ocmemgx_msmbus_a_clk, &ocmemgx_a_clk.c, LONG_MAX);
+static DEFINE_CLK_VOTER(pnoc_sdcc1_clk, &pnoc_clk.c, 0);
+static DEFINE_CLK_VOTER(pnoc_sdcc2_clk, &pnoc_clk.c, 0);
+static DEFINE_CLK_VOTER(pnoc_sdcc3_clk, &pnoc_clk.c, 0);
+static DEFINE_CLK_VOTER(pnoc_sdcc4_clk, &pnoc_clk.c, 0);
+
static struct clk_freq_tbl ftbl_gcc_usb30_master_clk[] = {
F(125000000, gpll0, 1, 5, 24),
F_END
@@ -3716,7 +3721,8 @@
static struct branch_clk mmss_mmssnoc_axi_clk = {
.cbcr_reg = MMSS_MMSSNOC_AXI_CBCR,
.parent = &axi_clk_src.c,
- .has_sibling = 1,
+ /* The bus driver needs set_rate to go through to the parent */
+ .has_sibling = 0,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mmss_mmssnoc_axi_clk",
@@ -3998,7 +4004,6 @@
static struct branch_clk audio_core_lpaif_codec_spkr_ebit_clk = {
.cbcr_reg = AUDIO_CORE_LPAIF_CODEC_SPKR_EBIT_CBCR,
- .parent = &audio_core_lpaif_codec_spkr_clk_src.c,
.has_sibling = 1,
.base = &virt_bases[LPASS_BASE],
.c = {
@@ -4012,7 +4017,7 @@
.cbcr_reg = AUDIO_CORE_LPAIF_CODEC_SPKR_IBIT_CBCR,
.parent = &audio_core_lpaif_codec_spkr_clk_src.c,
.has_sibling = 1,
- .max_div = 16,
+ .max_div = 15,
.base = &virt_bases[LPASS_BASE],
.c = {
.dbg_name = "audio_core_lpaif_codec_spkr_clk_src",
@@ -4035,7 +4040,6 @@
static struct branch_clk audio_core_lpaif_pri_ebit_clk = {
.cbcr_reg = AUDIO_CORE_LPAIF_PRI_EBIT_CBCR,
- .parent = &audio_core_lpaif_pri_clk_src.c,
.has_sibling = 1,
.base = &virt_bases[LPASS_BASE],
.c = {
@@ -4049,7 +4053,7 @@
.cbcr_reg = AUDIO_CORE_LPAIF_PRI_IBIT_CBCR,
.parent = &audio_core_lpaif_pri_clk_src.c,
.has_sibling = 1,
- .max_div = 16,
+ .max_div = 15,
.base = &virt_bases[LPASS_BASE],
.c = {
.dbg_name = "audio_core_lpaif_pri_ibit_clk",
@@ -4072,7 +4076,6 @@
static struct branch_clk audio_core_lpaif_sec_ebit_clk = {
.cbcr_reg = AUDIO_CORE_LPAIF_SEC_EBIT_CBCR,
- .parent = &audio_core_lpaif_sec_clk_src.c,
.has_sibling = 1,
.base = &virt_bases[LPASS_BASE],
.c = {
@@ -4086,7 +4089,7 @@
.cbcr_reg = AUDIO_CORE_LPAIF_SEC_IBIT_CBCR,
.parent = &audio_core_lpaif_sec_clk_src.c,
.has_sibling = 1,
- .max_div = 16,
+ .max_div = 15,
.base = &virt_bases[LPASS_BASE],
.c = {
.dbg_name = "audio_core_lpaif_sec_ibit_clk",
@@ -4109,7 +4112,6 @@
static struct branch_clk audio_core_lpaif_ter_ebit_clk = {
.cbcr_reg = AUDIO_CORE_LPAIF_TER_EBIT_CBCR,
- .parent = &audio_core_lpaif_ter_clk_src.c,
.has_sibling = 1,
.base = &virt_bases[LPASS_BASE],
.c = {
@@ -4123,7 +4125,7 @@
.cbcr_reg = AUDIO_CORE_LPAIF_TER_IBIT_CBCR,
.parent = &audio_core_lpaif_ter_clk_src.c,
.has_sibling = 1,
- .max_div = 16,
+ .max_div = 15,
.base = &virt_bases[LPASS_BASE],
.c = {
.dbg_name = "audio_core_lpaif_ter_ibit_clk",
@@ -4146,7 +4148,6 @@
static struct branch_clk audio_core_lpaif_quad_ebit_clk = {
.cbcr_reg = AUDIO_CORE_LPAIF_QUAD_EBIT_CBCR,
- .parent = &audio_core_lpaif_quad_clk_src.c,
.has_sibling = 1,
.base = &virt_bases[LPASS_BASE],
.c = {
@@ -4160,7 +4161,7 @@
.cbcr_reg = AUDIO_CORE_LPAIF_QUAD_IBIT_CBCR,
.parent = &audio_core_lpaif_quad_clk_src.c,
.has_sibling = 1,
- .max_div = 16,
+ .max_div = 15,
.base = &virt_bases[LPASS_BASE],
.c = {
.dbg_name = "audio_core_lpaif_quad_ibit_clk",
@@ -4171,7 +4172,6 @@
static struct branch_clk audio_core_lpaif_pcm0_ebit_clk = {
.cbcr_reg = AUDIO_CORE_LPAIF_PCM0_EBIT_CBCR,
- .parent = &audio_core_lpaif_pcm0_clk_src.c,
.has_sibling = 1,
.base = &virt_bases[LPASS_BASE],
.c = {
@@ -4185,7 +4185,6 @@
.cbcr_reg = AUDIO_CORE_LPAIF_PCM0_IBIT_CBCR,
.parent = &audio_core_lpaif_pcm0_clk_src.c,
.has_sibling = 1,
- .max_div = 16,
.base = &virt_bases[LPASS_BASE],
.c = {
.dbg_name = "audio_core_lpaif_pcm0_ibit_clk",
@@ -4210,7 +4209,6 @@
.cbcr_reg = AUDIO_CORE_LPAIF_PCM1_IBIT_CBCR,
.parent = &audio_core_lpaif_pcm1_clk_src.c,
.has_sibling = 1,
- .max_div = 16,
.base = &virt_bases[LPASS_BASE],
.c = {
.dbg_name = "audio_core_lpaif_pcm1_ibit_clk",
@@ -4675,12 +4673,16 @@
CLK_LOOKUP("iface_clk", gcc_sdcc1_ahb_clk.c, "msm_sdcc.1"),
CLK_LOOKUP("core_clk", gcc_sdcc1_apps_clk.c, "msm_sdcc.1"),
+ CLK_LOOKUP("bus_clk", pnoc_sdcc1_clk.c, "msm_sdcc.1"),
CLK_LOOKUP("iface_clk", gcc_sdcc2_ahb_clk.c, "msm_sdcc.2"),
CLK_LOOKUP("core_clk", gcc_sdcc2_apps_clk.c, "msm_sdcc.2"),
+ CLK_LOOKUP("bus_clk", pnoc_sdcc2_clk.c, "msm_sdcc.2"),
CLK_LOOKUP("iface_clk", gcc_sdcc3_ahb_clk.c, "msm_sdcc.3"),
CLK_LOOKUP("core_clk", gcc_sdcc3_apps_clk.c, "msm_sdcc.3"),
+ CLK_LOOKUP("bus_clk", pnoc_sdcc3_clk.c, "msm_sdcc.3"),
CLK_LOOKUP("iface_clk", gcc_sdcc4_ahb_clk.c, "msm_sdcc.4"),
CLK_LOOKUP("core_clk", gcc_sdcc4_apps_clk.c, "msm_sdcc.4"),
+ CLK_LOOKUP("bus_clk", pnoc_sdcc4_clk.c, "msm_sdcc.4"),
CLK_LOOKUP("iface_clk", gcc_tsif_ahb_clk.c, ""),
CLK_LOOKUP("ref_clk", gcc_tsif_ref_clk.c, ""),
@@ -4704,10 +4706,10 @@
CLK_LOOKUP("core_clk", mdss_esc1_clk.c, ""),
CLK_LOOKUP("iface_clk", mdss_hdmi_ahb_clk.c, ""),
CLK_LOOKUP("core_clk", mdss_hdmi_clk.c, ""),
- CLK_LOOKUP("core_clk", mdss_mdp_clk.c, ""),
- CLK_LOOKUP("core_clk", mdss_mdp_lut_clk.c, ""),
- CLK_LOOKUP("core_clk", mdp_clk_src.c, ""),
- CLK_LOOKUP("core_clk", mdss_vsync_clk.c, ""),
+ CLK_LOOKUP("core_clk", mdss_mdp_clk.c, "mdp.0"),
+ CLK_LOOKUP("lut_clk", mdss_mdp_lut_clk.c, "mdp.0"),
+ CLK_LOOKUP("core_clk_src", mdp_clk_src.c, "mdp.0"),
+ CLK_LOOKUP("vsync_clk", mdss_vsync_clk.c, "mdp.0"),
CLK_LOOKUP("iface_clk", camss_cci_cci_ahb_clk.c, ""),
CLK_LOOKUP("core_clk", camss_cci_cci_clk.c, ""),
CLK_LOOKUP("iface_clk", camss_csi0_ahb_clk.c, ""),
@@ -4766,9 +4768,10 @@
CLK_LOOKUP("iface_clk", camss_vfe_vfe_ahb_clk.c, ""),
CLK_LOOKUP("bus_clk", camss_vfe_vfe_axi_clk.c, ""),
CLK_LOOKUP("bus_clk", camss_vfe_vfe_ocmemnoc_clk.c, ""),
+ CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "mdp.0"),
CLK_LOOKUP("iface_clk", mdss_ahb_clk.c, "fd928000.qcom,iommu"),
CLK_LOOKUP("core_clk", mdss_axi_clk.c, "fd928000.qcom,iommu"),
- CLK_LOOKUP("bus_clk", mdss_axi_clk.c, ""),
+ CLK_LOOKUP("bus_clk", mdss_axi_clk.c, "mdp.0"),
CLK_LOOKUP("core_clk", oxili_gfx3d_clk.c, ""),
CLK_LOOKUP("iface_clk", oxilicx_ahb_clk.c, ""),
CLK_LOOKUP("bus_clk", oxilicx_axi_clk.c, ""),
@@ -4845,8 +4848,8 @@
CLK_LOOKUP("ocmem_a_clk", ocmemgx_msmbus_a_clk.c, "msm_bus"),
CLK_LOOKUP("bus_clk", ocmemnoc_clk.c, "msm_ocmem_noc"),
CLK_LOOKUP("bus_a_clk", ocmemnoc_clk.c, "msm_ocmem_noc"),
- CLK_LOOKUP("bus_clk", axi_clk_src.c, "msm_mmss_noc"),
- CLK_LOOKUP("bus_a_clk", axi_clk_src.c, "msm_mmss_noc"),
+ CLK_LOOKUP("bus_clk", mmss_mmssnoc_axi_clk.c, "msm_mmss_noc"),
+ CLK_LOOKUP("bus_a_clk", mmss_mmssnoc_axi_clk.c, "msm_mmss_noc"),
};
static struct pll_config_regs gpll0_regs __initdata = {
diff --git a/arch/arm/mach-msm/clock-local2.c b/arch/arm/mach-msm/clock-local2.c
index 467d5d1..9fe9591 100644
--- a/arch/arm/mach-msm/clock-local2.c
+++ b/arch/arm/mach-msm/clock-local2.c
@@ -408,6 +408,19 @@
return -EPERM;
}
+static long branch_clk_round_rate(struct clk *c, unsigned long rate)
+{
+ struct branch_clk *branch = to_branch_clk(c);
+
+ if (branch->max_div)
+ return rate <= (branch->max_div) ? rate : -EPERM;
+
+ if (!branch->has_sibling)
+ return clk_round_rate(branch->parent, rate);
+
+ return -EPERM;
+}
+
static unsigned long branch_clk_get_rate(struct clk *c)
{
struct branch_clk *branch = to_branch_clk(c);
@@ -580,6 +593,7 @@
.set_rate = branch_clk_set_rate,
.get_rate = branch_clk_get_rate,
.list_rate = branch_clk_list_rate,
+ .round_rate = branch_clk_round_rate,
.reset = branch_clk_reset,
.get_parent = branch_clk_get_parent,
.handoff = branch_clk_handoff,
diff --git a/arch/arm/mach-msm/clock-rpm.c b/arch/arm/mach-msm/clock-rpm.c
index e0707fc..2ec40ce 100644
--- a/arch/arm/mach-msm/clock-rpm.c
+++ b/arch/arm/mach-msm/clock-rpm.c
@@ -237,7 +237,7 @@
if (r->rpmrs_data->get_rate_fn)
return r->rpmrs_data->get_rate_fn(r);
else
- return 0;
+ return clk->rate;
}
static int rpm_clk_is_enabled(struct clk *clk)
@@ -272,9 +272,11 @@
if (rc < 0)
return HANDOFF_DISABLED_CLK;
- r->last_set_khz = iv.value;
- r->last_set_sleep_khz = iv.value;
- clk->rate = iv.value * 1000;
+ if (!r->branch) {
+ r->last_set_khz = iv.value;
+ r->last_set_sleep_khz = iv.value;
+ clk->rate = iv.value * 1000;
+ }
return HANDOFF_ENABLED_CLK;
}
diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c
index 63534a4..0fa1e2d 100644
--- a/arch/arm/mach-msm/cpufreq.c
+++ b/arch/arm/mach-msm/cpufreq.c
@@ -19,6 +19,7 @@
#include <linux/earlysuspend.h>
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/cpufreq.h>
#include <linux/workqueue.h>
#include <linux/completion.h>
@@ -27,6 +28,7 @@
#include <linux/sched.h>
#include <linux/suspend.h>
#include <mach/socinfo.h>
+#include <mach/cpufreq.h>
#include "acpuclock.h"
@@ -50,10 +52,33 @@
static DEFINE_PER_CPU(struct cpufreq_suspend_t, cpufreq_suspend);
+struct cpu_freq {
+ uint32_t max;
+ uint32_t min;
+ uint32_t allowed_max;
+ uint32_t allowed_min;
+ uint32_t limits_init;
+};
+
+static DEFINE_PER_CPU(struct cpu_freq, cpu_freq_info);
+
static int set_cpu_freq(struct cpufreq_policy *policy, unsigned int new_freq)
{
int ret = 0;
struct cpufreq_freqs freqs;
+ struct cpu_freq *limit = &per_cpu(cpu_freq_info, policy->cpu);
+
+ if (limit->limits_init) {
+ if (new_freq > limit->allowed_max) {
+ new_freq = limit->allowed_max;
+ pr_debug("max: limiting freq to %d\n", new_freq);
+ }
+
+ if (new_freq < limit->allowed_min) {
+ new_freq = limit->allowed_min;
+ pr_debug("min: limiting freq to %d\n", new_freq);
+ }
+ }
freqs.old = policy->cur;
freqs.new = new_freq;
@@ -158,6 +183,72 @@
return 0;
}
+static unsigned int msm_cpufreq_get_freq(unsigned int cpu)
+{
+ return acpuclk_get_rate(cpu);
+}
+
+static inline int msm_cpufreq_limits_init(void)
+{
+ int cpu = 0;
+ int i = 0;
+ struct cpufreq_frequency_table *table = NULL;
+ uint32_t min = (uint32_t) -1;
+ uint32_t max = 0;
+ struct cpu_freq *limit = NULL;
+
+ for_each_possible_cpu(cpu) {
+ limit = &per_cpu(cpu_freq_info, cpu);
+ table = cpufreq_frequency_get_table(cpu);
+ if (table == NULL) {
+ pr_err("%s: error reading cpufreq table for cpu %d\n",
+ __func__, cpu);
+ continue;
+ }
+ for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
+ if (table[i].frequency > max)
+ max = table[i].frequency;
+ if (table[i].frequency < min)
+ min = table[i].frequency;
+ }
+ limit->allowed_min = min;
+ limit->allowed_max = max;
+ limit->min = min;
+ limit->max = max;
+ limit->limits_init = 1;
+ }
+
+ return 0;
+}
+
+int msm_cpufreq_set_freq_limits(uint32_t cpu, uint32_t min, uint32_t max)
+{
+ struct cpu_freq *limit = &per_cpu(cpu_freq_info, cpu);
+
+ if (!limit->limits_init)
+ msm_cpufreq_limits_init();
+
+ if ((min != MSM_CPUFREQ_NO_LIMIT) &&
+ min >= limit->min && min <= limit->max)
+ limit->allowed_min = min;
+ else
+ limit->allowed_min = limit->min;
+
+
+ if ((max != MSM_CPUFREQ_NO_LIMIT) &&
+ max <= limit->max && max >= limit->min)
+ limit->allowed_max = max;
+ else
+ limit->allowed_max = limit->max;
+
+ pr_debug("%s: Limiting cpu %d min = %d, max = %d\n",
+ __func__, cpu,
+ limit->allowed_min, limit->allowed_max);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_cpufreq_set_freq_limits);
+
static int __cpuinit msm_cpufreq_init(struct cpufreq_policy *policy)
{
int cur_freq;
@@ -274,6 +365,7 @@
.init = msm_cpufreq_init,
.verify = msm_cpufreq_verify,
.target = msm_cpufreq_target,
+ .get = msm_cpufreq_get_freq,
.name = "msm",
.attr = msm_freq_attr,
};
@@ -300,4 +392,3 @@
}
late_initcall(msm_cpufreq_register);
-
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index 09e30c1..f6ce848 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -1061,6 +1061,59 @@
},
};
+static struct msm_bus_vectors vidc_venc_1080p_turbo_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VIDEO_ENC,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 222298112,
+ .ib = 3522000000U,
+ },
+ {
+ .src = MSM_BUS_MASTER_VIDEO_DEC,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 330301440,
+ .ib = 3522000000U,
+ },
+ {
+ .src = MSM_BUS_MASTER_AMPSS_M0,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 2500000,
+ .ib = 700000000,
+ },
+ {
+ .src = MSM_BUS_MASTER_AMPSS_M0,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 2500000,
+ .ib = 10000000,
+ },
+};
+static struct msm_bus_vectors vidc_vdec_1080p_turbo_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_VIDEO_ENC,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 222298112,
+ .ib = 3522000000U,
+ },
+ {
+ .src = MSM_BUS_MASTER_VIDEO_DEC,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 330301440,
+ .ib = 3522000000U,
+ },
+ {
+ .src = MSM_BUS_MASTER_AMPSS_M0,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 2500000,
+ .ib = 700000000,
+ },
+ {
+ .src = MSM_BUS_MASTER_AMPSS_M0,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 2500000,
+ .ib = 10000000,
+ },
+};
+
static struct msm_bus_paths vidc_bus_client_config[] = {
{
ARRAY_SIZE(vidc_init_vectors),
@@ -1090,6 +1143,14 @@
ARRAY_SIZE(vidc_vdec_1080p_vectors),
vidc_vdec_1080p_vectors,
},
+ {
+ ARRAY_SIZE(vidc_venc_1080p_turbo_vectors),
+ vidc_venc_1080p_turbo_vectors,
+ },
+ {
+ ARRAY_SIZE(vidc_vdec_1080p_turbo_vectors),
+ vidc_vdec_1080p_turbo_vectors,
+ },
};
static struct msm_bus_scale_pdata vidc_bus_client_data = {
@@ -2525,15 +2586,15 @@
.name = "jpegd_dst",
.domain = CAMERA_DOMAIN,
},
- /* Rotator */
+ /* Rotator src*/
{
.name = "rot_src",
- .domain = ROTATOR_DOMAIN,
+ .domain = ROTATOR_SRC_DOMAIN,
},
- /* Rotator */
+ /* Rotator dst */
{
.name = "rot_dst",
- .domain = ROTATOR_DOMAIN,
+ .domain = ROTATOR_DST_DOMAIN,
},
/* Video */
{
@@ -2589,18 +2650,36 @@
},
};
-static struct mem_pool apq8064_display_pools[] = {
+static struct mem_pool apq8064_display_read_pools[] = {
[GEN_POOL] =
- /* One address space for display */
+ /* One address space for display reads */
{
.paddr = SZ_128K,
.size = SZ_2G - SZ_128K,
},
};
-static struct mem_pool apq8064_rotator_pools[] = {
+static struct mem_pool apq8064_display_write_pools[] = {
[GEN_POOL] =
- /* One address space for rotator */
+ /* One address space for display writes */
+ {
+ .paddr = SZ_128K,
+ .size = SZ_2G - SZ_128K,
+ },
+};
+
+static struct mem_pool apq8064_rotator_src_pools[] = {
+ [GEN_POOL] =
+ /* One address space for rotator src */
+ {
+ .paddr = SZ_128K,
+ .size = SZ_2G - SZ_128K,
+ },
+};
+
+static struct mem_pool apq8064_rotator_dst_pools[] = {
+ [GEN_POOL] =
+ /* One address space for rotator dst */
{
.paddr = SZ_128K,
.size = SZ_2G - SZ_128K,
@@ -2616,13 +2695,21 @@
.iova_pools = apq8064_camera_pools,
.npools = ARRAY_SIZE(apq8064_camera_pools),
},
- [DISPLAY_DOMAIN] = {
- .iova_pools = apq8064_display_pools,
- .npools = ARRAY_SIZE(apq8064_display_pools),
+ [DISPLAY_READ_DOMAIN] = {
+ .iova_pools = apq8064_display_read_pools,
+ .npools = ARRAY_SIZE(apq8064_display_read_pools),
},
- [ROTATOR_DOMAIN] = {
- .iova_pools = apq8064_rotator_pools,
- .npools = ARRAY_SIZE(apq8064_rotator_pools),
+ [DISPLAY_WRITE_DOMAIN] = {
+ .iova_pools = apq8064_display_write_pools,
+ .npools = ARRAY_SIZE(apq8064_display_write_pools),
+ },
+ [ROTATOR_SRC_DOMAIN] = {
+ .iova_pools = apq8064_rotator_src_pools,
+ .npools = ARRAY_SIZE(apq8064_rotator_src_pools),
+ },
+ [ROTATOR_DST_DOMAIN] = {
+ .iova_pools = apq8064_rotator_dst_pools,
+ .npools = ARRAY_SIZE(apq8064_rotator_dst_pools),
},
};
diff --git a/arch/arm/mach-msm/devices-8930.c b/arch/arm/mach-msm/devices-8930.c
index c480bba..03685da 100644
--- a/arch/arm/mach-msm/devices-8930.c
+++ b/arch/arm/mach-msm/devices-8930.c
@@ -765,12 +765,12 @@
/* Rotator */
{
.name = "rot_src",
- .domain = ROTATOR_DOMAIN,
+ .domain = ROTATOR_SRC_DOMAIN,
},
/* Rotator */
{
.name = "rot_dst",
- .domain = ROTATOR_DOMAIN,
+ .domain = ROTATOR_DST_DOMAIN,
},
/* Video */
{
@@ -826,18 +826,36 @@
},
};
-static struct mem_pool msm8930_display_pools[] = {
+static struct mem_pool msm8930_display_read_pools[] = {
[GEN_POOL] =
- /* One address space for display */
+ /* One address space for display reads */
{
.paddr = SZ_128K,
.size = SZ_2G - SZ_128K,
},
};
-static struct mem_pool msm8930_rotator_pools[] = {
+static struct mem_pool msm8930_display_write_pools[] = {
[GEN_POOL] =
- /* One address space for rotator */
+ /* One address space for display writes */
+ {
+ .paddr = SZ_128K,
+ .size = SZ_2G - SZ_128K,
+ },
+};
+
+static struct mem_pool msm8930_rotator_src_pools[] = {
+ [GEN_POOL] =
+ /* One address space for rotator src */
+ {
+ .paddr = SZ_128K,
+ .size = SZ_2G - SZ_128K,
+ },
+};
+
+static struct mem_pool msm8930_rotator_dst_pools[] = {
+ [GEN_POOL] =
+ /* One address space for rotator dst */
{
.paddr = SZ_128K,
.size = SZ_2G - SZ_128K,
@@ -853,13 +871,21 @@
.iova_pools = msm8930_camera_pools,
.npools = ARRAY_SIZE(msm8930_camera_pools),
},
- [DISPLAY_DOMAIN] = {
- .iova_pools = msm8930_display_pools,
- .npools = ARRAY_SIZE(msm8930_display_pools),
+ [DISPLAY_READ_DOMAIN] = {
+ .iova_pools = msm8930_display_read_pools,
+ .npools = ARRAY_SIZE(msm8930_display_read_pools),
},
- [ROTATOR_DOMAIN] = {
- .iova_pools = msm8930_rotator_pools,
- .npools = ARRAY_SIZE(msm8930_rotator_pools),
+ [DISPLAY_WRITE_DOMAIN] = {
+ .iova_pools = msm8930_display_write_pools,
+ .npools = ARRAY_SIZE(msm8930_display_write_pools),
+ },
+ [ROTATOR_SRC_DOMAIN] = {
+ .iova_pools = msm8930_rotator_src_pools,
+ .npools = ARRAY_SIZE(msm8930_rotator_src_pools),
+ },
+ [ROTATOR_DST_DOMAIN] = {
+ .iova_pools = msm8930_rotator_dst_pools,
+ .npools = ARRAY_SIZE(msm8930_rotator_dst_pools),
},
};
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index db55d14..0d417bd 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -202,6 +202,11 @@
},
};
+struct platform_device msm8960_device_acpuclk = {
+ .name = "acpuclk-8960",
+ .id = -1,
+};
+
#define SHARED_IMEM_TZ_BASE 0x2a03f720
static struct resource tzlog_resources[] = {
{
@@ -3488,6 +3493,44 @@
#endif
+static struct resource msm_ebi1_ch0_erp_resources[] = {
+ {
+ .start = HSDDRX_EBI1CH0_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = 0x00A40000,
+ .end = 0x00A40000 + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device msm8960_device_ebi1_ch0_erp = {
+ .name = "msm_ebi_erp",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(msm_ebi1_ch0_erp_resources),
+ .resource = msm_ebi1_ch0_erp_resources,
+};
+
+static struct resource msm_ebi1_ch1_erp_resources[] = {
+ {
+ .start = HSDDRX_EBI1CH1_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = 0x00D40000,
+ .end = 0x00D40000 + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device msm8960_device_ebi1_ch1_erp = {
+ .name = "msm_ebi_erp",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(msm_ebi1_ch1_erp_resources),
+ .resource = msm_ebi1_ch1_erp_resources,
+};
+
static int msm8960_LPM_latency = 1000; /* >100 usec for WFI */
struct platform_device msm8960_cpu_idle_device = {
@@ -3596,15 +3639,15 @@
.name = "jpegd_dst",
.domain = CAMERA_DOMAIN,
},
- /* Rotator */
+ /* Rotator src*/
{
.name = "rot_src",
- .domain = ROTATOR_DOMAIN,
+ .domain = ROTATOR_SRC_DOMAIN,
},
- /* Rotator */
+ /* Rotator dst */
{
.name = "rot_dst",
- .domain = ROTATOR_DOMAIN,
+ .domain = ROTATOR_DST_DOMAIN,
},
/* Video */
{
@@ -3660,18 +3703,36 @@
},
};
-static struct mem_pool msm8960_display_pools[] = {
+static struct mem_pool msm8960_display_read_pools[] = {
[GEN_POOL] =
- /* One address space for display */
+ /* One address space for display reads */
{
.paddr = SZ_128K,
.size = SZ_2G - SZ_128K,
},
};
-static struct mem_pool msm8960_rotator_pools[] = {
+static struct mem_pool msm8960_display_write_pools[] = {
[GEN_POOL] =
- /* One address space for rotator */
+ /* One address space for display writes */
+ {
+ .paddr = SZ_128K,
+ .size = SZ_2G - SZ_128K,
+ },
+};
+
+static struct mem_pool msm8960_rotator_src_pools[] = {
+ [GEN_POOL] =
+ /* One address space for rotator src */
+ {
+ .paddr = SZ_128K,
+ .size = SZ_2G - SZ_128K,
+ },
+};
+
+static struct mem_pool msm8960_rotator_dst_pools[] = {
+ [GEN_POOL] =
+ /* One address space for rotator dst */
{
.paddr = SZ_128K,
.size = SZ_2G - SZ_128K,
@@ -3687,13 +3748,21 @@
.iova_pools = msm8960_camera_pools,
.npools = ARRAY_SIZE(msm8960_camera_pools),
},
- [DISPLAY_DOMAIN] = {
- .iova_pools = msm8960_display_pools,
- .npools = ARRAY_SIZE(msm8960_display_pools),
+ [DISPLAY_READ_DOMAIN] = {
+ .iova_pools = msm8960_display_read_pools,
+ .npools = ARRAY_SIZE(msm8960_display_read_pools),
},
- [ROTATOR_DOMAIN] = {
- .iova_pools = msm8960_rotator_pools,
- .npools = ARRAY_SIZE(msm8960_rotator_pools),
+ [DISPLAY_WRITE_DOMAIN] = {
+ .iova_pools = msm8960_display_write_pools,
+ .npools = ARRAY_SIZE(msm8960_display_write_pools),
+ },
+ [ROTATOR_SRC_DOMAIN] = {
+ .iova_pools = msm8960_rotator_src_pools,
+ .npools = ARRAY_SIZE(msm8960_rotator_src_pools),
+ },
+ [ROTATOR_DST_DOMAIN] = {
+ .iova_pools = msm8960_rotator_dst_pools,
+ .npools = ARRAY_SIZE(msm8960_rotator_dst_pools),
},
};
diff --git a/arch/arm/mach-msm/devices-9615.c b/arch/arm/mach-msm/devices-9615.c
index 4677a1c..06d8653 100644
--- a/arch/arm/mach-msm/devices-9615.c
+++ b/arch/arm/mach-msm/devices-9615.c
@@ -107,6 +107,11 @@
},
};
+struct platform_device msm9615_device_acpuclk = {
+ .name = "acpuclk-9615",
+ .id = -1,
+};
+
#define MSM_USB_BAM_BASE 0x12502000
#define MSM_USB_BAM_SIZE SZ_16K
#define MSM_HSIC_BAM_BASE 0x12542000
@@ -1035,6 +1040,7 @@
MSM_RPM_MAP(9615, CXO_BUFFERS, CXO_BUFFERS, 1),
MSM_RPM_MAP(9615, USB_OTG_SWITCH, USB_OTG_SWITCH, 1),
MSM_RPM_MAP(9615, HDMI_SWITCH, HDMI_SWITCH, 1),
+ MSM_RPM_MAP(9615, VOLTAGE_CORNER, VOLTAGE_CORNER, 1),
},
.target_status = {
MSM_RPM_STATUS_ID_MAP(9615, VERSION_MAJOR),
@@ -1100,6 +1106,7 @@
MSM_RPM_STATUS_ID_MAP(9615, CXO_BUFFERS),
MSM_RPM_STATUS_ID_MAP(9615, USB_OTG_SWITCH),
MSM_RPM_STATUS_ID_MAP(9615, HDMI_SWITCH),
+ MSM_RPM_STATUS_ID_MAP(9615, VOLTAGE_CORNER),
},
.target_ctrl_id = {
MSM_RPM_CTRL_MAP(9615, VERSION_MAJOR),
@@ -1280,17 +1287,17 @@
[MSM_RPMRS_VDD_MEM_MAX] = 1150000,
},
.vdd_dig_levels = {
- [MSM_RPMRS_VDD_DIG_RET_LOW] = 500000,
- [MSM_RPMRS_VDD_DIG_RET_HIGH] = 750000,
- [MSM_RPMRS_VDD_DIG_ACTIVE] = 950000,
- [MSM_RPMRS_VDD_DIG_MAX] = 1150000,
+ [MSM_RPMRS_VDD_DIG_RET_LOW] = 0,
+ [MSM_RPMRS_VDD_DIG_RET_HIGH] = 0,
+ [MSM_RPMRS_VDD_DIG_ACTIVE] = 1,
+ [MSM_RPMRS_VDD_DIG_MAX] = 3,
},
.vdd_mask = 0x7FFFFF,
.rpmrs_target_id = {
[MSM_RPMRS_ID_PXO_CLK] = MSM_RPM_ID_CXO_CLK,
[MSM_RPMRS_ID_L2_CACHE_CTL] = MSM_RPM_ID_LAST,
- [MSM_RPMRS_ID_VDD_DIG_0] = MSM_RPM_ID_PM8018_S1_0,
- [MSM_RPMRS_ID_VDD_DIG_1] = MSM_RPM_ID_PM8018_S1_1,
+ [MSM_RPMRS_ID_VDD_DIG_0] = MSM_RPM_ID_VOLTAGE_CORNER,
+ [MSM_RPMRS_ID_VDD_DIG_1] = MSM_RPM_ID_LAST,
[MSM_RPMRS_ID_VDD_MEM_0] = MSM_RPM_ID_PM8018_L9_0,
[MSM_RPMRS_ID_VDD_MEM_1] = MSM_RPM_ID_PM8018_L9_1,
[MSM_RPMRS_ID_RPM_CTL] = MSM_RPM_ID_RPM_CTL,
diff --git a/arch/arm/mach-msm/devices-msm7x27.c b/arch/arm/mach-msm/devices-msm7x27.c
index ffd10fa..4619cca 100644
--- a/arch/arm/mach-msm/devices-msm7x27.c
+++ b/arch/arm/mach-msm/devices-msm7x27.c
@@ -27,6 +27,7 @@
#include "devices.h"
#include "footswitch.h"
+#include "acpuclock.h"
#include <asm/mach/flash.h>
@@ -431,6 +432,16 @@
msm_pm_set_irq_extns(&msm7x27_pm_irq_calls);
}
+static struct acpuclk_pdata msm7x27_acpuclk_pdata = {
+ .max_speed_delta_khz = 400000,
+};
+
+struct platform_device msm7x27_device_acpuclk = {
+ .name = "acpuclk-7627",
+ .id = -1,
+ .dev.platform_data = &msm7x27_acpuclk_pdata,
+};
+
#define MSM_SDC1_BASE 0xA0400000
#define MSM_SDC2_BASE 0xA0500000
#define MSM_SDC3_BASE 0xA0600000
diff --git a/arch/arm/mach-msm/devices-msm7x27a.c b/arch/arm/mach-msm/devices-msm7x27a.c
index adc9169c..b3454cd2 100644
--- a/arch/arm/mach-msm/devices-msm7x27a.c
+++ b/arch/arm/mach-msm/devices-msm7x27a.c
@@ -211,6 +211,37 @@
},
};
+static struct acpuclk_pdata msm7x27a_acpuclk_pdata = {
+ .max_speed_delta_khz = 400000,
+};
+
+struct platform_device msm7x27a_device_acpuclk = {
+ .name = "acpuclk-7627",
+ .id = -1,
+ .dev.platform_data = &msm7x27a_acpuclk_pdata,
+};
+
+static struct acpuclk_pdata msm7x27aa_acpuclk_pdata = {
+ .max_speed_delta_khz = 504000,
+};
+
+struct platform_device msm7x27aa_device_acpuclk = {
+ .name = "acpuclk-7627",
+ .id = -1,
+ .dev.platform_data = &msm7x27aa_acpuclk_pdata,
+};
+
+static struct acpuclk_pdata msm8625_acpuclk_pdata = {
+ /* TODO: Need to update speed delta from H/w Team */
+ .max_speed_delta_khz = 604800,
+};
+
+struct platform_device msm8625_device_acpuclk = {
+ .name = "acpuclk-7627",
+ .id = -1,
+ .dev.platform_data = &msm8625_acpuclk_pdata,
+};
+
struct platform_device msm_device_smd = {
.name = "msm_smd",
.id = -1,
@@ -1623,16 +1654,15 @@
msm_clock_init(&msm7x27a_clock_init_data);
if (cpu_is_msm7x27aa() || cpu_is_msm7x25ab())
- acpuclk_init(&acpuclk_7x27aa_soc_data);
+ platform_device_register(&msm7x27aa_device_acpuclk);
else if (cpu_is_msm8625()) {
if (msm8625_cpu_id() == MSM8625)
- acpuclk_init(&acpuclk_7x27aa_soc_data);
+ platform_device_register(&msm7x27aa_device_acpuclk);
else if (msm8625_cpu_id() == MSM8625A)
- acpuclk_init(&acpuclk_8625_soc_data);
- } else {
- acpuclk_init(&acpuclk_7x27a_soc_data);
- }
-
+ platform_device_register(&msm8625_device_acpuclk);
+ } else {
+ platform_device_register(&msm7x27a_device_acpuclk);
+ }
return 0;
}
diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c
index de70429..ff747e27 100644
--- a/arch/arm/mach-msm/devices-msm7x30.c
+++ b/arch/arm/mach-msm/devices-msm7x30.c
@@ -42,6 +42,11 @@
#include "pm.h"
#include "irq.h"
+struct platform_device msm7x30_device_acpuclk = {
+ .name = "acpuclk-7x30",
+ .id = -1,
+};
+
/* EBI THERMAL DRIVER */
static struct resource msm_ebi0_thermal_resources[] = {
{
diff --git a/arch/arm/mach-msm/devices-msm8x60.c b/arch/arm/mach-msm/devices-msm8x60.c
index 37844c1..d8bf054 100644
--- a/arch/arm/mach-msm/devices-msm8x60.c
+++ b/arch/arm/mach-msm/devices-msm8x60.c
@@ -163,6 +163,11 @@
},
};
+struct platform_device msm8x60_device_acpuclk = {
+ .name = "acpuclk-8x60",
+ .id = -1,
+};
+
#ifdef CONFIG_MSM_DSPS
#define GSBI12_DEV (&msm_dsps_device.dev)
#else
diff --git a/arch/arm/mach-msm/devices-qsd8x50.c b/arch/arm/mach-msm/devices-qsd8x50.c
index ee8a2cf..2ecc852 100644
--- a/arch/arm/mach-msm/devices-qsd8x50.c
+++ b/arch/arm/mach-msm/devices-qsd8x50.c
@@ -34,6 +34,11 @@
#include <mach/rpc_hsusb.h>
#include "pm.h"
+struct platform_device msm8x50_device_acpuclk = {
+ .name = "acpuclk-8x50",
+ .id = -1,
+};
+
static struct resource resources_uart1[] = {
{
.start = INT_UART1,
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index 2b2fcc7..ea47727 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -77,6 +77,8 @@
extern struct platform_device msm8960_device_vfe;
extern struct platform_device msm8960_device_vpe;
extern struct platform_device msm8960_device_cache_erp;
+extern struct platform_device msm8960_device_ebi1_ch0_erp;
+extern struct platform_device msm8960_device_ebi1_ch1_erp;
extern struct platform_device apq8064_device_uart_gsbi1;
extern struct platform_device apq8064_device_uart_gsbi3;
@@ -410,3 +412,13 @@
extern struct platform_device mdm_sglte_device;
extern struct platform_device apq_device_tz_log;
+
+extern struct platform_device msm7x27_device_acpuclk;
+extern struct platform_device msm7x27a_device_acpuclk;
+extern struct platform_device msm7x27aa_device_acpuclk;
+extern struct platform_device msm7x30_device_acpuclk;
+extern struct platform_device msm8625_device_acpuclk;
+extern struct platform_device msm8x50_device_acpuclk;
+extern struct platform_device msm8x60_device_acpuclk;
+extern struct platform_device msm8960_device_acpuclk;
+extern struct platform_device msm9615_device_acpuclk;
diff --git a/arch/arm/mach-msm/hsic_sysmon.c b/arch/arm/mach-msm/hsic_sysmon.c
index 07a9dbb..153e1b4 100644
--- a/arch/arm/mach-msm/hsic_sysmon.c
+++ b/arch/arm/mach-msm/hsic_sysmon.c
@@ -314,6 +314,8 @@
static inline void hsic_sysmon_debugfs_cleanup(void) { }
#endif
+static void hsic_sysmon_pdev_release(struct device *dev) { }
+
static int
hsic_sysmon_probe(struct usb_interface *ifc, const struct usb_device_id *id)
{
@@ -371,6 +373,7 @@
hs->pdev.name = "sys_mon";
hs->pdev.id = SYSMON_SS_EXT_MODEM;
+ hs->pdev.dev.release = hsic_sysmon_pdev_release;
platform_device_register(&hs->pdev);
pr_debug("complete");
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index 243b488..460eefc 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -61,10 +61,6 @@
struct msm_camera_io_ext ioext;
struct msm_camera_io_clk ioclk;
uint8_t csid_core;
- uint8_t is_csiphy;
- uint8_t is_csic;
- uint8_t is_csid;
- uint8_t is_ispif;
uint8_t is_vpe;
struct msm_bus_scale_pdata *cam_bus_scale_table;
};
@@ -187,6 +183,7 @@
enum camera_vreg_type {
REG_LDO,
REG_VS,
+ REG_GPIO,
};
struct camera_vreg_t {
@@ -294,7 +291,6 @@
enum msm_sensor_type sensor_type;
struct msm_actuator_info *actuator_info;
int pmic_gpio_enable;
- int (*sensor_lcd_gpio_onoff)(int on);
struct msm_eeprom_info *eeprom_info;
};
diff --git a/arch/arm/mach-msm/include/mach/camera.h b/arch/arm/mach-msm/include/mach/camera.h
index 87e7de1..6d2c25a 100644
--- a/arch/arm/mach-msm/include/mach/camera.h
+++ b/arch/arm/mach-msm/include/mach/camera.h
@@ -94,6 +94,7 @@
VFE_MSG_V2X_CAPTURE,
VFE_MSG_OUTPUT_PRIMARY,
VFE_MSG_OUTPUT_SECONDARY,
+ VFE_MSG_OUTPUT_TERTIARY1,
};
enum vpe_resp_msg {
@@ -114,52 +115,6 @@
STEREO_RAW_SNAP_STARTED,
};
-enum msm_ispif_intftype {
- PIX0,
- RDI0,
- PIX1,
- RDI1,
- PIX2,
- RDI2,
-};
-
-enum msm_ispif_vc {
- VC0,
- VC1,
- VC2,
- VC3,
-};
-
-enum msm_ispif_cid {
- CID0,
- CID1,
- CID2,
- CID3,
- CID4,
- CID5,
- CID6,
- CID7,
- CID8,
- CID9,
- CID10,
- CID11,
- CID12,
- CID13,
- CID14,
- CID15,
-};
-
-struct msm_ispif_params {
- uint8_t intftype;
- uint16_t cid_mask;
- uint8_t csid;
-};
-
-struct msm_ispif_params_list {
- uint32_t len;
- struct msm_ispif_params params[3];
-};
-
struct msm_vpe_phy_info {
uint32_t sbuf_phy;
uint32_t planar0_off;
@@ -172,12 +127,6 @@
uint32_t frame_id;
};
-struct msm_camera_csid_vc_cfg {
- uint8_t cid;
- uint8_t dt;
- uint8_t decode_format;
-};
-
struct msm_camera_csid_lut_params {
uint8_t num_cid;
struct msm_camera_csid_vc_cfg *vc_cfg;
@@ -209,18 +158,6 @@
#define VFE31_OUTPUT_MODE_P_ALL_CHNLS (0x1 << 5)
#endif
-#define CSI_EMBED_DATA 0x12
-#define CSI_RESERVED_DATA_0 0x13
-#define CSI_YUV422_8 0x1E
-#define CSI_RAW8 0x2A
-#define CSI_RAW10 0x2B
-#define CSI_RAW12 0x2C
-
-#define CSI_DECODE_6BIT 0
-#define CSI_DECODE_8BIT 1
-#define CSI_DECODE_10BIT 2
-#define CSI_DECODE_DPCM_10_8_10 5
-
struct msm_vfe_phy_info {
uint32_t sbuf_phy;
uint32_t planar0_off;
diff --git a/arch/arm/mach-msm/include/mach/cpufreq.h b/arch/arm/mach-msm/include/mach/cpufreq.h
new file mode 100644
index 0000000..8c2be11
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/cpufreq.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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_MACH_CPUFREQ_H
+#define __ARCH_ARM_MACH_MSM_MACH_CPUFREQ_H
+
+#define MSM_CPUFREQ_NO_LIMIT 0xFFFFFFFF
+
+#ifdef CONFIG_CPU_FREQ_MSM
+
+/**
+ * msm_cpufreq_set_freq_limit() - Set max/min freq limits on cpu
+ *
+ * @cpu: The cpu core for which the limits apply
+ * @max: The max frequency allowed
+ * @min: The min frequency allowed
+ *
+ * If the @max or @min is set to MSM_CPUFREQ_NO_LIMIT, the limit
+ * will default to the CPUFreq limit.
+ *
+ * returns 0 on success, errno on failure
+ */
+extern int msm_cpufreq_set_freq_limits(
+ uint32_t cpu, uint32_t min, uint32_t max);
+#else
+static inline int msm_cpufreq_set_freq_limits(
+ uint32_t cpu, uint32_t min, uint32_t max)
+{
+ return -ENOSYS;
+}
+#endif
+
+#endif /* __ARCH_ARM_MACH_MSM_MACH_CPUFREQ_H */
diff --git a/arch/arm/mach-msm/include/mach/iommu_domains.h b/arch/arm/mach-msm/include/mach/iommu_domains.h
index 1a3a022..1d538f2 100644
--- a/arch/arm/mach-msm/include/mach/iommu_domains.h
+++ b/arch/arm/mach-msm/include/mach/iommu_domains.h
@@ -18,8 +18,10 @@
enum {
VIDEO_DOMAIN,
CAMERA_DOMAIN,
- DISPLAY_DOMAIN,
- ROTATOR_DOMAIN,
+ DISPLAY_READ_DOMAIN,
+ DISPLAY_WRITE_DOMAIN,
+ ROTATOR_SRC_DOMAIN,
+ ROTATOR_DST_DOMAIN,
MAX_DOMAINS
};
diff --git a/arch/arm/mach-msm/include/mach/msm_bus_board.h b/arch/arm/mach-msm/include/mach/msm_bus_board.h
index 956d44e..0c556b5 100644
--- a/arch/arm/mach-msm/include/mach/msm_bus_board.h
+++ b/arch/arm/mach-msm/include/mach/msm_bus_board.h
@@ -39,6 +39,8 @@
const struct msm_bus_board_algorithm *board_algo;
int hw_sel;
void *hw_data;
+ uint32_t qos_freq;
+ bool virt;
};
enum msm_bus_bw_tier_type {
diff --git a/arch/arm/mach-msm/include/mach/msm_smsm.h b/arch/arm/mach-msm/include/mach/msm_smsm.h
index fbb8502..e19f39d 100644
--- a/arch/arm/mach-msm/include/mach/msm_smsm.h
+++ b/arch/arm/mach-msm/include/mach/msm_smsm.h
@@ -95,7 +95,7 @@
#define SMSM_WLAN_TX_RINGS_EMPTY 0x00000200
#define SMSM_WLAN_TX_ENABLE 0x00000400
-#define SMSM_ERR_SRV_READY 0x00008000
+#define SMSM_SUBSYS2AP_STATUS 0x00008000
#ifdef CONFIG_MSM_SMD
void *smem_alloc(unsigned id, unsigned size);
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
index a55dee6c..65cf647 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/audio_acdb.h
@@ -14,8 +14,11 @@
#define _AUDIO_ACDB_H
#include <linux/msm_audio_acdb.h>
+#ifdef CONFIG_ARCH_MSMCOPPER
+#include <sound/q6adm-v2.h>
+#else
#include <sound/q6adm.h>
-
+#endif
enum {
RX_CAL,
TX_CAL,
diff --git a/arch/arm/mach-msm/include/mach/rpm-9615.h b/arch/arm/mach-msm/include/mach/rpm-9615.h
index 6ae7bae..4ca5eea 100644
--- a/arch/arm/mach-msm/include/mach/rpm-9615.h
+++ b/arch/arm/mach-msm/include/mach/rpm-9615.h
@@ -72,8 +72,9 @@
MSM_RPM_9615_SEL_CXO_BUFFERS = 81,
MSM_RPM_9615_SEL_USB_OTG_SWITCH = 82,
MSM_RPM_9615_SEL_HDMI_SWITCH = 83,
+ MSM_RPM_9615_SEL_VOLTAGE_CORNER = 87,
- MSM_RPM_9615_SEL_LAST = MSM_RPM_9615_SEL_HDMI_SWITCH,
+ MSM_RPM_9615_SEL_LAST = MSM_RPM_9615_SEL_VOLTAGE_CORNER,
};
/* RPM resource (4 byte) word ID enum */
@@ -162,8 +163,9 @@
MSM_RPM_9615_ID_CXO_BUFFERS = 105,
MSM_RPM_9615_ID_USB_OTG_SWITCH = 106,
MSM_RPM_9615_ID_HDMI_SWITCH = 107,
+ MSM_RPM_9615_ID_VOLTAGE_CORNER = 109,
- MSM_RPM_9615_ID_LAST = MSM_RPM_9615_ID_HDMI_SWITCH,
+ MSM_RPM_9615_ID_LAST = MSM_RPM_9615_ID_VOLTAGE_CORNER,
};
/* RPM status ID enum */
@@ -231,8 +233,9 @@
MSM_RPM_9615_STATUS_ID_CXO_BUFFERS = 60,
MSM_RPM_9615_STATUS_ID_USB_OTG_SWITCH = 61,
MSM_RPM_9615_STATUS_ID_HDMI_SWITCH = 62,
+ MSM_RPM_9615_STATUS_ID_VOLTAGE_CORNER = 64,
- MSM_RPM_9615_STATUS_ID_LAST = MSM_RPM_9615_STATUS_ID_HDMI_SWITCH,
+ MSM_RPM_9615_STATUS_ID_LAST = MSM_RPM_9615_STATUS_ID_VOLTAGE_CORNER,
};
#endif /* __ARCH_ARM_MACH_MSM_RPM_9615_H */
diff --git a/arch/arm/mach-msm/include/mach/rpm-regulator-9615.h b/arch/arm/mach-msm/include/mach/rpm-regulator-9615.h
index f5fa8ca..6a7fae5 100644
--- a/arch/arm/mach-msm/include/mach/rpm-regulator-9615.h
+++ b/arch/arm/mach-msm/include/mach/rpm-regulator-9615.h
@@ -107,7 +107,8 @@
RPM_VREG_ID_PM8018_S4,
RPM_VREG_ID_PM8018_S5,
RPM_VREG_ID_PM8018_LVS1,
- RPM_VREG_ID_PM8018_MAX_REAL = RPM_VREG_ID_PM8018_LVS1,
+ RPM_VREG_ID_PM8018_VDD_DIG_CORNER,
+ RPM_VREG_ID_PM8018_MAX_REAL = RPM_VREG_ID_PM8018_VDD_DIG_CORNER,
/* The following are IDs for regulator devices to enable pin control. */
RPM_VREG_ID_PM8018_L2_PC,
diff --git a/arch/arm/mach-msm/include/mach/stm.h b/arch/arm/mach-msm/include/mach/stm.h
new file mode 100644
index 0000000..20c4963
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/stm.h
@@ -0,0 +1,60 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 __MACH_STM_H
+#define __MACH_STM_H
+
+enum {
+ OST_ENTITY_NONE = 0x0,
+ OST_ENTITY_FTRACE_EVENTS = 0x1,
+ OST_ENTITY_TRACE_PRINTK = 0x2,
+ OST_ENTITY_TRACE_MARKER = 0x4,
+ OST_ENTITY_DEV_NODE = 0x8,
+ OST_ENTITY_ALL = 0xF,
+};
+
+enum {
+ STM_OPTION_NONE = 0x0,
+ STM_OPTION_TIMESTAMPED = 0x08,
+ STM_OPTION_GUARANTEED = 0x80,
+};
+
+#define stm_log_inv(entity_id, proto_id, data, size) \
+ stm_trace(STM_OPTION_NONE, entity_id, proto_id, data, size)
+
+#define stm_log_inv_ts(entity_id, proto_id, data, size) \
+ stm_trace(STM_OPTION_TIMESTAMPED, entity_id, proto_id, \
+ data, size)
+
+#define stm_log_gtd(entity_id, proto_id, data, size) \
+ stm_trace(STM_OPTION_GUARANTEED, entity_id, proto_id, \
+ data, size)
+
+#define stm_log_gtd_ts(entity_id, proto_id, data, size) \
+ stm_trace(STM_OPTION_GUARANTEED | STM_OPTION_TIMESTAMPED, \
+ entity_id, proto_id, data, size)
+
+#define stm_log(entity_id, data, size) \
+ stm_log_inv_ts(entity_id, 0, data, size)
+
+#ifdef CONFIG_MSM_QDSS
+extern int stm_trace(uint32_t options, uint8_t entity_id, uint8_t proto_id,
+ const void *data, uint32_t size);
+#else
+static inline int stm_trace(uint32_t options, uint8_t entity_id,
+ uint8_t proto_id, const void *data, uint32_t size)
+{
+ return 0;
+}
+#endif
+
+#endif
diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c
index c15e8b1..c0bff63 100644
--- a/arch/arm/mach-msm/ipc_router.c
+++ b/arch/arm/mach-msm/ipc_router.c
@@ -472,19 +472,19 @@
return port_ptr;
}
+/*
+ * Should be called with local_ports_lock locked
+ */
static struct msm_ipc_port *msm_ipc_router_lookup_local_port(uint32_t port_id)
{
int key = (port_id & (LP_HASH_SIZE - 1));
struct msm_ipc_port *port_ptr;
- mutex_lock(&local_ports_lock);
list_for_each_entry(port_ptr, &local_ports[key], list) {
if (port_ptr->this_port.port_id == port_id) {
- mutex_unlock(&local_ports_lock);
return port_ptr;
}
}
- mutex_unlock(&local_ports_lock);
return NULL;
}
@@ -1432,16 +1432,19 @@
resume_tx_node_id = hdr->dst_node_id;
resume_tx_port_id = hdr->dst_port_id;
+ rport_ptr = msm_ipc_router_lookup_remote_port(hdr->src_node_id,
+ hdr->src_port_id);
+
+ mutex_lock(&local_ports_lock);
port_ptr = msm_ipc_router_lookup_local_port(hdr->dst_port_id);
if (!port_ptr) {
pr_err("%s: No local port id %08x\n", __func__,
hdr->dst_port_id);
+ mutex_unlock(&local_ports_lock);
release_pkt(pkt);
goto process_done;
}
- rport_ptr = msm_ipc_router_lookup_remote_port(hdr->src_node_id,
- hdr->src_port_id);
if (!rport_ptr) {
rport_ptr = msm_ipc_router_create_remote_port(
hdr->src_node_id,
@@ -1449,6 +1452,7 @@
if (!rport_ptr) {
pr_err("%s: Remote port %08x:%08x creation failed\n",
__func__, hdr->src_node_id, hdr->src_port_id);
+ mutex_unlock(&local_ports_lock);
goto process_done;
}
}
@@ -1459,7 +1463,9 @@
list_add_tail(&pkt->list, &port_ptr->port_rx_q);
wake_up(&port_ptr->port_rx_wait_q);
mutex_unlock(&port_ptr->port_rx_q_lock);
+ mutex_unlock(&local_ports_lock);
} else {
+ mutex_lock(&port_ptr->port_rx_q_lock);
src_addr = kmalloc(sizeof(struct msm_ipc_port_addr),
GFP_KERNEL);
if (src_addr) {
@@ -1467,8 +1473,10 @@
src_addr->port_id = hdr->src_port_id;
}
skb_pull(head_skb, IPC_ROUTER_HDR_SIZE);
+ mutex_unlock(&local_ports_lock);
port_ptr->notify(MSM_IPC_ROUTER_READ_CB, pkt->pkt_fragment_q,
src_addr, port_ptr->priv);
+ mutex_unlock(&port_ptr->port_rx_q_lock);
pkt->pkt_fragment_q = NULL;
src_addr = NULL;
release_pkt(pkt);
@@ -1628,9 +1636,11 @@
hdr->dst_port_id = port_id;
pkt->length += IPC_ROUTER_HDR_SIZE;
+ mutex_lock(&local_ports_lock);
port_ptr = msm_ipc_router_lookup_local_port(port_id);
if (!port_ptr) {
pr_err("%s: Local port %d not present\n", __func__, port_id);
+ mutex_unlock(&local_ports_lock);
release_pkt(pkt);
return -ENODEV;
}
@@ -1640,6 +1650,7 @@
list_add_tail(&pkt->list, &port_ptr->port_rx_q);
wake_up(&port_ptr->port_rx_wait_q);
mutex_unlock(&port_ptr->port_rx_q_lock);
+ mutex_unlock(&local_ports_lock);
return pkt->length;
}
@@ -1932,6 +1943,10 @@
return -EINVAL;
if (port_ptr->type == SERVER_PORT || port_ptr->type == CLIENT_PORT) {
+ mutex_lock(&local_ports_lock);
+ list_del(&port_ptr->list);
+ mutex_unlock(&local_ports_lock);
+
if (port_ptr->type == SERVER_PORT) {
msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
msg.srv.service = port_ptr->port_name.service;
@@ -1950,6 +1965,10 @@
}
broadcast_ctl_msg(&msg);
broadcast_ctl_msg_locally(&msg);
+ } else if (port_ptr->type == CONTROL_PORT) {
+ mutex_lock(&control_ports_lock);
+ list_del(&port_ptr->list);
+ mutex_unlock(&control_ports_lock);
}
mutex_lock(&port_ptr->port_rx_q_lock);
@@ -1969,17 +1988,6 @@
msm_ipc_router_destroy_server(server,
port_ptr->this_port.node_id,
port_ptr->this_port.port_id);
- mutex_lock(&local_ports_lock);
- list_del(&port_ptr->list);
- mutex_unlock(&local_ports_lock);
- } else if (port_ptr->type == CLIENT_PORT) {
- mutex_lock(&local_ports_lock);
- list_del(&port_ptr->list);
- mutex_unlock(&local_ports_lock);
- } else if (port_ptr->type == CONTROL_PORT) {
- mutex_lock(&control_ports_lock);
- list_del(&port_ptr->list);
- mutex_unlock(&control_ports_lock);
}
wake_lock_destroy(&port_ptr->port_rx_wake_lock);
diff --git a/arch/arm/mach-msm/mdm_common.c b/arch/arm/mach-msm/mdm_common.c
index ffff782..74bf25d 100644
--- a/arch/arm/mach-msm/mdm_common.c
+++ b/arch/arm/mach-msm/mdm_common.c
@@ -45,6 +45,7 @@
#define MDM_MODEM_DELTA 100
#define MDM_BOOT_TIMEOUT 60000L
#define MDM_RDUMP_TIMEOUT 60000L
+#define MDM2AP_STATUS_TIMEOUT_MS 60000L
static int mdm_debug_on;
static struct workqueue_struct *mdm_queue;
@@ -90,6 +91,22 @@
static DECLARE_WORK(sfr_reason_work, mdm_restart_reason_fn);
+static void mdm2ap_status_check(struct work_struct *work)
+{
+ /*
+ * If the mdm modem did not pull the MDM2AP_STATUS gpio
+ * high then call subsystem_restart.
+ */
+ if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) {
+ pr_err("%s: MDM2AP_STATUS gpio did not go high\n",
+ __func__);
+ mdm_drv->mdm_ready = 0;
+ subsystem_restart(EXTERNAL_MODEM);
+ }
+}
+
+static DECLARE_DELAYED_WORK(mdm2ap_status_check_work, mdm2ap_status_check);
+
long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
@@ -131,6 +148,14 @@
complete(&mdm_boot);
else
first_boot = 0;
+
+ /* Start a timer to check that the mdm2ap_status gpio
+ * goes high.
+ */
+
+ if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
+ schedule_delayed_work(&mdm2ap_status_check_work,
+ msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS));
break;
case RAM_DUMP_DONE:
pr_debug("%s: mdm done collecting RAM dumps\n", __func__);
@@ -256,6 +281,7 @@
mdm_drv->mdm_ready = 0;
subsystem_restart(EXTERNAL_MODEM);
} else if (value == 1) {
+ cancel_delayed_work(&mdm2ap_status_check_work);
pr_info("%s: status = 1: mdm is now ready\n", __func__);
queue_work(mdm_queue, &mdm_status_work);
}
diff --git a/arch/arm/mach-msm/msm_bus/Makefile b/arch/arm/mach-msm/msm_bus/Makefile
index 061998c..766856c 100644
--- a/arch/arm/mach-msm/msm_bus/Makefile
+++ b/arch/arm/mach-msm/msm_bus/Makefile
@@ -1,7 +1,8 @@
#
# Makefile for msm-bus driver specific files
#
-obj-y += msm_bus_core.o msm_bus_fabric.o msm_bus_config.o msm_bus_arb.o msm_bus_rpm.o
+obj-y += msm_bus_core.o msm_bus_fabric.o msm_bus_config.o msm_bus_arb.o
+obj-y += msm_bus_rpm.o msm_bus_bimc.o msm_bus_noc.o
obj-$(CONFIG_ARCH_MSM8X60) += msm_bus_board_8660.o
obj-$(CONFIG_ARCH_MSM8960) += msm_bus_board_8960.o
obj-$(CONFIG_ARCH_MSM9615) += msm_bus_board_9615.o
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
new file mode 100644
index 0000000..823f14d
--- /dev/null
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.c
@@ -0,0 +1,1990 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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) "AXI: BIMC: %s(): " fmt, __func__
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <mach/msm_bus_board.h>
+#include "msm_bus_core.h"
+#include "msm_bus_bimc.h"
+
+enum msm_bus_bimc_slave_block {
+ SLAVE_BLOCK_RESERVED = 0,
+ SLAVE_BLOCK_SLAVE_WAY,
+ SLAVE_BLOCK_XPU,
+ SLAVE_BLOCK_ARBITER,
+ SLAVE_BLOCK_SCMO,
+};
+
+
+/* Misc module */
+
+#define MISC_REG_BASE(b) ((b) + 0x00000000)
+
+#define MISC_HW_VERSION_ADDR(b) (MISC_REG_BASE(b) + 0x0000000)
+enum bimc_misc_hw_version {
+ MISC_HW_VERSION_RMSK = 0xffffff,
+ MISC_HW_VERSION_MAJOR_BMSK = 0xff0000,
+ MISC_HW_VERSION_MAJOR_SHFT = 0x10,
+ MISC_HW_VERSION_MINOR_BMSK = 0xff00,
+ MISC_HW_VERSION_MINOR_SHFT = 0x8,
+ MISC_HW_VERSION_STEP_BMSK = 0xff,
+ MISC_HW_VERSION_STEP_SHFT = 0x0,
+};
+
+#define MISC_CORE_INFO_ADDR(b) (MISC_REG_BASE(b) + 0x00000004)
+enum bimc_misc_core_info {
+ MISC_CORE_INFO_RMSK = 0xffffffff,
+ MISC_CORE_INFO_CORE_INFO_BMSK = 0xffffffff,
+ MISC_CORE_INFO_CORE_INFO_SHFT = 0x0,
+};
+
+#define MISC_HW_INFO_ADDR(b) (MISC_REG_BASE(b) + 0x00000008)
+enum bimc_misc_hw_info {
+ MISC_HW_INFO_RMSK = 0xffffffff,
+ MISC_HW_INFO_HW_MAJOR_BMSK = 0xff000000,
+ MISC_HW_INFO_HW_MAJOR_SHFT = 0x18,
+ MISC_HW_INFO_HW_BRANCH_BMSK = 0xff0000,
+ MISC_HW_INFO_HW_BRANCH_SHFT = 0x10,
+ MISC_HW_INFO_HW_MINOR_BMSK = 0xff00,
+ MISC_HW_INFO_HW_MINOR_SHFT = 0x8,
+ MISC_HW_INFO_HW_ECO_BMSK = 0xff,
+ MISC_HW_INFO_HW_ECO_SHFT = 0x0,
+};
+
+#define MISC_CORE_CLK_SEL_ADDR(b) \
+ (MISC_REG_BASE(b) + 0x00000020)
+enum bimc_misc_core_clk_sel {
+ MISC_CORE_CLK_SEL_RMSK = 0x3,
+ MISC_CORE_CLK_SEL_CLK_2X_MODE_BMSK = 0x2,
+ MISC_CORE_CLK_SEL_CLK_2X_MODE_SHFT = 0x1,
+ MISC_CORE_CLK_SEL_CLK_2X_MODE_CTRL_EN_BMSK = 0x1,
+ MISC_CORE_CLK_SEL_CLK_2X_MODE_CTRL_EN_SHFT = 0x0,
+};
+
+#define MISC_CLK_PERIOD_ADDR(b) \
+ (MISC_REG_BASE(b) + 0x00000024)
+enum bimc_misc_bimc_clk_preiod {
+ MISC_CLK_PERIOD_RMSK = 0xff,
+ MISC_CLK_PERIOD_CORE_CLK_PERIOD_BMSK = 0xff,
+ MISC_CLK_PERIOD_CORE_CLK_PERIOD_SHFT = 0x0,
+};
+
+#define MISC_SCMOn_LOCAL_CGC_THRESH_ADDR(b, n) \
+ (MISC_REG_BASE(b) + 0x00000100 + 0x4 * (n))
+enum bimc_misc_scm0n_local_cgc_thresh {
+ MISC_SCMOn_LOCAL_CGC_THRESH_RMSK = 0x800000ff,
+ MISC_SCMOn_LOCAL_CGC_THRESH_MAXn = 1,
+ MISC_SCMOn_LOCAL_CGC_THRESH_EN_BMSK = 0x80000000,
+ MISC_SCMOn_LOCAL_CGC_THRESH_EN_SHFT = 0x1f,
+ MISC_SCMOn_LOCAL_CGC_THRESH_THRESH_VAL_BMSK = 0xff,
+ MISC_SCMOn_LOCAL_CGC_THRESH_THRESH_VAL_SHFT = 0x0,
+};
+
+#define MISC_DPEn_LOCAL_CGC_THRESH_ADDR(b, n) \
+ (MISC_REG_BASE(b) + 0x00000140 + 0x4 * (n))
+enum bimc_misc_dpen_local_cgc_thresh {
+ MISC_DPEn_LOCAL_CGC_THRESH_RMSK = 0x800000ff,
+ MISC_DPEn_LOCAL_CGC_THRESH_MAXn = 1,
+ MISC_DPEn_LOCAL_CGC_THRESH_EN_BMSK = 0x80000000,
+ MISC_DPEn_LOCAL_CGC_THRESH_EN_SHFT = 0x1f,
+ MISC_DPEn_LOCAL_CGC_THRESH_THRESH_VAL_BMSK = 0xff,
+ MISC_DPEn_LOCAL_CGC_THRESH_THRESH_VAL_SHFT = 0x0,
+};
+
+#define MISC_MPORT_LOCAL_CGC_THRESH_ADDR(b) \
+ (MISC_REG_BASE(b) + 0x00000180)
+enum bimc_misc_mport_local_cgc_thresh {
+ MISC_MPORT_LOCAL_CGC_THRESH_RMSK = 0x800000ff,
+ MISC_MPORT_LOCAL_CGC_THRESH_EN_BMSK = 0x80000000,
+ MISC_MPORT_LOCAL_CGC_THRESH_EN_SHFT = 0x1f,
+ MISC_MPORT_LOCAL_CGC_THRESH_THRESH_VAL_BMSK = 0xff,
+ MISC_MPORT_LOCAL_CGC_THRESH_THRESH_VAL_SHFT = 0x0,
+};
+
+#define MISC_SWAY_LOCAL_CGC_THRESH_ADDR(b) \
+ (MISC_REG_BASE(b) + 0x000001c0)
+enum bimc_misc_sway_local_cgc_thresh {
+ MISC_SWAY_LOCAL_CGC_THRESH_RMSK = 0x800000ff,
+ MISC_SWAY_LOCAL_CGC_THRESH_EN_BMSK = 0x80000000,
+ MISC_SWAY_LOCAL_CGC_THRESH_EN_SHFT = 0x1f,
+ MISC_SWAY_LOCAL_CGC_THRESH_THRESH_VAL_BMSK = 0xff,
+ MISC_SWAY_LOCAL_CGC_THRESH_THRESH_VAL_SHFT = 0x0,
+};
+
+#define MISC_SCMOn_CGC_THRESH_ADDR(b, n) \
+ (MISC_REG_BASE(b) + 0x00000200 + 0x4 * (n))
+enum bimc_misc_scmon_cgc_thresh {
+ MISC_SCMOn_CGC_THRESH_RMSK = 0x800000ff,
+ MISC_SCMOn_CGC_THRESH_MAXn = 1,
+ MISC_SCMOn_CGC_THRESH_EN_BMSK = 0x80000000,
+ MISC_SCMOn_CGC_THRESH_EN_SHFT = 0x1f,
+ MISC_SCMOn_CGC_THRESH_THRESH_VAL_BMSK = 0xff,
+ MISC_SCMOn_CGC_THRESH_THRESH_VAL_SHFT = 0x0,
+};
+
+#define MISC_SCMOn_DYN_CLK_CTRL_ADDR(b, n) \
+ (MISC_REG_BASE(b) + 0x00000240 + 0x4 * (n))
+enum bimc_misc_scmon_dyn_clk_ctrl {
+ MISC_SCMOn_DYN_CLK_CTRL_RMSK = 0x3,
+ MISC_SCMOn_DYN_CLK_CTRL_MAXn = 1,
+ MISC_SCMOn_DYN_CLK_CTRL_CGC_EN_BMSK = 0x2,
+ MISC_SCMOn_DYN_CLK_CTRL_CGC_EN_SHFT = 0x1,
+ MISC_SCMOn_DYN_CLK_CTRL_CLK_EN_BMSK = 0x1,
+ MISC_SCMOn_DYN_CLK_CTRL_CLK_EN_SHFT = 0x0,
+};
+
+#define MISC_DPEn_CGC_THRESH_ADDR(b, n) \
+ (MISC_REG_BASE(b) + 0x00000280 + 0x4 * (n))
+enum bimc_misc_dpen_cgc_thresh {
+ MISC_DPEn_CGC_THRESH_RMSK = 0x800000ff,
+ MISC_DPEn_CGC_THRESH_MAXn = 1,
+ MISC_DPEn_CGC_THRESH_EN_BMSK = 0x80000000,
+ MISC_DPEn_CGC_THRESH_EN_SHFT = 0x1f,
+ MISC_DPEn_CGC_THRESH_THRESH_VAL_BMSK = 0xff,
+ MISC_DPEn_CGC_THRESH_THRESH_VAL_SHFT = 0x0,
+};
+
+#define MISC_DPEn_DYN_CLK_CTRL_ADDR(b, n) \
+ (MISC_REG_BASE(b) + 0x000002c0 + 0x4 * (n))
+enum bimc_misc_dpen_dyn_clk_ctrl {
+ MISC_DPEn_DYN_CLK_CTRL_RMSK = 0x3,
+ MISC_DPEn_DYN_CLK_CTRL_MAXn = 1,
+ MISC_DPEn_DYN_CLK_CTRL_CGC_EN_BMSK = 0x2,
+ MISC_DPEn_DYN_CLK_CTRL_CGC_EN_SHFT = 0x1,
+ MISC_DPEn_DYN_CLK_CTRL_CLK_EN_BMSK = 0x1,
+ MISC_DPEn_DYN_CLK_CTRL_CLK_EN_SHFT = 0x0,
+};
+
+/* BIMC Global registers */
+
+#define GLOBAL_1_REG_BASE(b) ((b) + 0x00001000)
+
+#define GLOBAL_1_COMPONENT_INFO_ADDR(b) \
+ (GLOBAL1_REG_BASE(b) + 0x00000000)
+enum bimc_global_1_component_info {
+ GLOBAL_1_COMPONENT_INFO_RMSK = 0xffff,
+ GLOBAL_1_COMPONENT_INFO_SUB_TYPE_BMSK = 0xff00,
+ GLOBAL_1_COMPONENT_INFO_SUB_TYPE_SHFT = 0x8,
+ GLOBAL_1_COMPONENT_INFO_TYPE_BMSK = 0xff,
+ GLOBAL_1_COMPONENT_INFO_TYPE_SHFT = 0x0,
+};
+
+#define VERSION_INFO_ADDR(b) \
+ (GLOBAL1_REG_BASE(b) + 0x00000010)
+enum bimc_version_info {
+ VERSION_INFO_RMSK = 0xffff,
+ VERSION_INFO_MAJOR_BMSK = 0xf0000000,
+ VERSION_INFO_MAJOR_SHFT = 0x1c,
+ VERSION_INFO_BRANCH_BMSK = 0xfff0000,
+ VERSION_INFO_BRANCH_SHFT = 0x10,
+ VERSION_INFO_MINOR_BMSK = 0xff00,
+ VERSION_INFO_MINOR_SHFT = 0x8,
+ VERSION_INFO_ECO_BMSK = 0xff,
+ VERSION_INFO_ECO_SHFT = 0x0,
+};
+
+#define HW_VERSION_ADDR(b) \
+ (GLOBAL1_REG_BASE(b) + 0x00000014)
+enum bimc_hw_version {
+ HW_VERSION_RMSK = 0xffffffff,
+ HW_VERSION_MAJOR_BMSK = 0xf0000000,
+ HW_VERSION_MAJOR_SHFT = 0x1c,
+ HW_VERSION_MINOR_BMSK = 0xfff0000,
+ HW_VERSION_MINOR_SHFT = 0x10,
+ HW_VERSION_STEP_BMSK = 0xffff,
+ HW_VERSION_STEP_SHFT = 0x0,
+};
+
+#define BRIC_CONFIG_INFO_0_ADDR(b) \
+ (GLOBAL1_REG_BASE(b) + 0x00000020)
+enum bimc_bric_config_info_0 {
+ BRIC_CONFIG_INFO_0_RMSK = 0xffffffff,
+ BRIC_CONFIG_INFO_0_ADDR_WIDTH_BMSK = 0xff000000,
+ BRIC_CONFIG_INFO_0_ADDR_WIDTH_SHFT = 0x18,
+ BRIC_CONFIG_INFO_0_BUSID_BMSK = 0xff0000,
+ BRIC_CONFIG_INFO_0_BUSID_SHFT = 0x10,
+ BRIC_CONFIG_INFO_0_NUM_SWAYS_BMSK = 0xff00,
+ BRIC_CONFIG_INFO_0_NUM_SWAYS_SHFT = 0x8,
+ BRIC_CONFIG_INFO_0_NUM_MPORTS_BMSK = 0xff,
+ BRIC_CONFIG_INFO_0_NUM_MPORTS_SHFT = 0x0,
+};
+
+#define BRIC_CONFIG_INFO_1_ADDR(b) \
+ (GLOBAL1_REG_BASE(b) + 0x00000030)
+enum bimc_bric_config_info_1 {
+ BRIC_CONFIG_INFO_1_RMSK = 0xffffffff,
+ BRIC_CONFIG_INFO_1_DATA_WIDTH_BMSK = 0xffff0000,
+ BRIC_CONFIG_INFO_1_DATA_WIDTH_SHFT = 0x10,
+ BRIC_CONFIG_INFO_1_TID_WIDTH_BMSK = 0xff00,
+ BRIC_CONFIG_INFO_1_TID_WIDTH_SHFT = 0x8,
+ BRIC_CONFIG_INFO_1_MID_WIDTH_BMSK = 0xff,
+ BRIC_CONFIG_INFO_1_MID_WIDTH_SHFT = 0x0,
+};
+
+#define MP_INT_STATUS_ADDR(b) \
+ (GLOBAL1_REG_BASE(b) + 0x00000100)
+enum bimc_mp_int_status {
+ MP_INT_STATUS_RMSK = 0x7f,
+ MP_INT_STATUS_MASTERPORT_BMSK = 0x7f,
+ MP_INT_STATUS_MASTERPORT_SHFT = 0x0,
+};
+
+#define MP_INT_CLR_ADDR(b) \
+ (GLOBAL1_REG_BASE(b) + 0x00000108)
+enum bimc_mp_int_clr {
+ MP_INT_CLR_RMSK = 0x7f,
+ MP_INT_CLR_MASTERPORT_BMSK = 0x7f,
+ MP_INT_CLR_MASTERPORT_SHFT = 0x0,
+};
+
+#define MP_INT_EN_ADDR(b) \
+ (GLOBAL1_REG_BASE(b) + 0x0000010c)
+enum bimc_mp_int_en {
+ MP_INT_EN_RMSK = 0x7f,
+ MP_INT_EN_MASTERPORT_BMSK = 0x7f,
+ MP_INT_EN_MASTERPORT_SHFT = 0x0,
+};
+
+#define SW_INT_STATUS_ADDR(b) \
+ (GLOBAL1_REG_BASE(b) + 0x00000110)
+enum bimc_sw_int_status {
+ SW_INT_STATUS_RMSK = 0x1f,
+ SW_INT_STATUS_MASTERPORT_BMSK = 0x1f,
+ SW_INT_STATUS_MASTERPORT_SHFT = 0x0,
+};
+
+#define SW_INT_CLR_ADDR(b) \
+ (GLOBAL1_REG_BASE(b) + 0x00000118)
+enum bimc_sw_int_clr {
+ SW_INT_CLR_RMSK = 0x1f,
+ SW_INT_CLR_MASTERPORT_BMSK = 0x1f,
+ SW_INT_CLR_MASTERPORT_SHFT = 0x0,
+};
+
+#define SW_INT_EN_ADDR(b) \
+ (GLOBAL1_REG_BASE(b) + 0x0000011c)
+enum bimc_sw_int_en {
+ SW_INT_EN_RMSK = 0x1f,
+ SW_INT_EN_MASTERPORT_BMSK = 0x1f,
+ SW_INT_EN_MASTERPORT_SHFT = 0x0,
+};
+
+#define REF_TIMER_CONFIG_ADDR(b) \
+ (GLOBAL1_REG_BASE(b) + 0x00000200)
+enum bimc_ref_timer_config {
+ REF_TIMER_CONFIG_RMSK = 0xffff,
+ REF_TIMER_CONFIG_MASTERPORT_BMSK = 0xffff,
+ REF_TIMER_CONFIG_MASTERPORT_SHFT = 0x0,
+};
+
+#define DEBUG_SEL_ADDR(b) \
+ (GLOBAL1_REG_BASE(b) + 0x00000210)
+enum bimc_debug_sel {
+ DEBUG_SEL_RMSK = 0xffffffff,
+ DEBUG_SEL_COMPONENT_BMSK = 0xff000000,
+ DEBUG_SEL_COMPONENT_SHFT = 0x18,
+ DEBUG_SEL_INSTANCE_BMSK = 0xff0000,
+ DEBUG_SEL_INSTANCE_SHFT = 0x10,
+ DEBUG_SEL_SEL_BMSK = 0xffff,
+ DEBUG_SEL_SEL_SHFT = 0x0,
+};
+
+/* BIMC Global 2 */
+
+#define GLOBAL2_REG_BASE(b) ((b) + 0x00002000)
+
+#define GLOBAL2_COMPONENT_INFO_ADDR(b) \
+ (GLOBAL2_REG_BASE(b) + 0x00000000)
+enum bimc_global2_component_info {
+ GLOBAL2_COMPONENT_INFO_RMSK = 0xffff,
+ GLOBAL2_COMPONENT_INFO_SUB_TYPE_BMSK = 0xff00,
+ GLOBAL2_COMPONENT_INFO_SUB_TYPE_SHFT = 0x8,
+ GLOBAL2_COMPONENT_INFO_TYPE_BMSK = 0xff,
+ GLOBAL2_COMPONENT_INFO_TYPE_SHFT = 0x0,
+};
+
+#define DEFAULT_SEGMENT_CONFIG_ADDR(b) \
+ (GLOBAL1_REG_BASE(b) + 0x000001f0)
+enum bimc_default_segment_config {
+ DEFAULT_SEGMENT_CONFIG_RMSK = 0xf00f,
+ DEFAULT_SEGMENT_CONFIG_REDIRECT_BMSK = 0xf0000,
+ DEFAULT_SEGMENT_CONFIG_REDIRECT_SHFT = 0x10,
+ DEFAULT_SEGMENT_CONFIG_DEFAULT_BMSK = 0xf,
+ DEFAULT_SEGMENT_CONFIG_DEFAULT_SHFT = 0x0,
+};
+
+#define SEGMENTn_ADDR_BASEm_LOWER_ADDR(b, n, m) \
+ (GLOBAL2_REG_BASE(b) + 0x00000200 + 0x80 * (n) + 0x10 * (m))
+enum bimc_segmentn_addr_basem_lower {
+ SEGMENTn_ADDR_BASEm_LOWER_RMSK = 0xfff0040f,
+ SEGMENTn_ADDR_BASEm_LOWER_MAXn = 4,
+ SEGMENTn_ADDR_BASEm_LOWER_BASE_31_20_BMSK = 0xfff00000,
+ SEGMENTn_ADDR_BASEm_LOWER_BASE_31_20_SHFT = 0x14,
+ SEGMENTn_ADDR_BASEm_LOWER_BASE_10_BMSK = 0x400,
+ SEGMENTn_ADDR_BASEm_LOWER_BASE_10_SHFT = 0xa,
+ SEGMENTn_ADDR_BASEm_LOWER_EN_BMSK = 0x1,
+ SEGMENTn_ADDR_BASEm_LOWER_EN_SHFT = 0x0,
+};
+
+#define SEGMENTn_ADDR_BASEm_UPPER_ADDR(b, n, m) \
+ (GLOBAL2_REG_BASE(b) + 0x00000204 + 0x80 * (n) + 0x10 * (m))
+enum bimc_segmentn_addr_basem_upper {
+ SEGMENTn_ADDR_BASEm_UPPER_RMSK = 0xf,
+ SEGMENTn_ADDR_BASEm_UPPER_MAXn = 4,
+};
+
+#define SEGMENTn_ADDR_MASKm_LOWER_ADDR(b, n, m) \
+ (GLOBAL2_REG_BASE(b) + 0x00000208 + 0x80 * (n) + 0x10 * (m))
+enum bimc_segmentn_addr_maskm_lower {
+ SEGMENTn_ADDR_MASKm_LOWER_RMSK = 0xfff00400,
+ SEGMENTn_ADDR_MASKm_LOWER_MAXn = 4,
+ SEGMENTn_ADDR_MASKm_LOWER_MASK_31_20_BMSK = 0xfff00000,
+ SEGMENTn_ADDR_MASKm_LOWER_MASK_31_20_SHFT = 0x14,
+ SEGMENTn_ADDR_MASKm_LOWER_MASK_10_BMSK = 0x400,
+ SEGMENTn_ADDR_MASKm_LOWER_MASK_10_SHFT = 0xa,
+};
+
+#define SEGMENTn_ADDR_MASKm_UPPER_ADDR(b, n, m) \
+ (GLOBAL2_REG_BASE(b) + 0x0000020c + 0x80 * (n) + 0x10 * (m))
+enum bimc_segmentn_addr_maskm_upper {
+ SEGMENTn_ADDR_MASKm_UPPER_RMSK = 0xf,
+ SEGMENTn_ADDR_MASKm_UPPER_MAXn = 4,
+};
+
+/* M_Generic */
+
+#define M_REG_BASE(b) ((b) + 0x00008000)
+
+#define M_COMPONENT_INFO_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000000)
+enum bimc_m_component_info {
+ M_COMPONENT_INFO_RMSK = 0xffffff,
+ M_COMPONENT_INFO_INSTANCE_BMSK = 0xff0000,
+ M_COMPONENT_INFO_INSTANCE_SHFT = 0x10,
+ M_COMPONENT_INFO_SUB_TYPE_BMSK = 0xff00,
+ M_COMPONENT_INFO_SUB_TYPE_SHFT = 0x8,
+ M_COMPONENT_INFO_TYPE_BMSK = 0xff,
+ M_COMPONENT_INFO_TYPE_SHFT = 0x0,
+};
+
+#define M_CONFIG_INFO_0_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000020)
+enum bimc_m_config_info_0 {
+ M_CONFIG_INFO_0_RMSK = 0xff00ffff,
+ M_CONFIG_INFO_0_SYNC_MODE_BMSK = 0xff000000,
+ M_CONFIG_INFO_0_SYNC_MODE_SHFT = 0x18,
+ M_CONFIG_INFO_0_CONNECTION_TYPE_BMSK = 0xff00,
+ M_CONFIG_INFO_0_CONNECTION_TYPE_SHFT = 0x8,
+ M_CONFIG_INFO_0_FUNC_BMSK = 0xff,
+ M_CONFIG_INFO_0_FUNC_SHFT = 0x0,
+};
+
+#define M_CONFIG_INFO_1_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000030)
+enum bimc_m_config_info_1 {
+ M_CONFIG_INFO_1_RMSK = 0xffffffff,
+ M_CONFIG_INFO_1_SWAY_CONNECTIVITY_BMSK = 0xffffffff,
+ M_CONFIG_INFO_1_SWAY_CONNECTIVITY_SHFT = 0x0,
+};
+
+#define M_CONFIG_INFO_2_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000040)
+enum bimc_m_config_info_2 {
+ M_CONFIG_INFO_2_RMSK = 0xffffffff,
+ M_CONFIG_INFO_2_M_DATA_WIDTH_BMSK = 0xffff0000,
+ M_CONFIG_INFO_2_M_DATA_WIDTH_SHFT = 0x10,
+ M_CONFIG_INFO_2_M_TID_WIDTH_BMSK = 0xff00,
+ M_CONFIG_INFO_2_M_TID_WIDTH_SHFT = 0x8,
+ M_CONFIG_INFO_2_M_MID_WIDTH_BMSK = 0xff,
+ M_CONFIG_INFO_2_M_MID_WIDTH_SHFT = 0x0,
+};
+
+#define M_CONFIG_INFO_3_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000050)
+enum bimc_m_config_info_3 {
+ M_CONFIG_INFO_3_RMSK = 0xffffffff,
+ M_CONFIG_INFO_3_RCH_DEPTH_BMSK = 0xff000000,
+ M_CONFIG_INFO_3_RCH_DEPTH_SHFT = 0x18,
+ M_CONFIG_INFO_3_BCH_DEPTH_BMSK = 0xff0000,
+ M_CONFIG_INFO_3_BCH_DEPTH_SHFT = 0x10,
+ M_CONFIG_INFO_3_WCH_DEPTH_BMSK = 0xff00,
+ M_CONFIG_INFO_3_WCH_DEPTH_SHFT = 0x8,
+ M_CONFIG_INFO_3_ACH_DEPTH_BMSK = 0xff,
+ M_CONFIG_INFO_3_ACH_DEPTH_SHFT = 0x0,
+};
+
+#define M_CONFIG_INFO_4_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000060)
+enum bimc_m_config_info_4 {
+ M_CONFIG_INFO_4_RMSK = 0xffff,
+ M_CONFIG_INFO_4_REORDER_BUF_DEPTH_BMSK = 0xff00,
+ M_CONFIG_INFO_4_REORDER_BUF_DEPTH_SHFT = 0x8,
+ M_CONFIG_INFO_4_REORDER_TABLE_DEPTH_BMSK = 0xff,
+ M_CONFIG_INFO_4_REORDER_TABLE_DEPTH_SHFT = 0x0,
+};
+
+#define M_CONFIG_INFO_5_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000070)
+enum bimc_m_config_info_5 {
+ M_CONFIG_INFO_5_RMSK = 0x111,
+ M_CONFIG_INFO_5_MP2ARB_PIPELINE_EN_BMSK = 0x100,
+ M_CONFIG_INFO_5_MP2ARB_PIPELINE_EN_SHFT = 0x8,
+ M_CONFIG_INFO_5_MPBUF_PIPELINE_EN_BMSK = 0x10,
+ M_CONFIG_INFO_5_MPBUF_PIPELINE_EN_SHFT = 0x4,
+ M_CONFIG_INFO_5_M2MP_PIPELINE_EN_BMSK = 0x1,
+ M_CONFIG_INFO_5_M2MP_PIPELINE_EN_SHFT = 0x0,
+};
+
+#define M_INT_STATUS_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000100)
+enum bimc_m_int_status {
+ M_INT_STATUS_RMSK = 0x3,
+};
+
+#define M_INT_CLR_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000108)
+enum bimc_m_int_clr {
+ M_INT_CLR_RMSK = 0x3,
+};
+
+#define M_INT_EN_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x0000010c)
+enum bimc_m_int_en {
+ M_INT_EN_RMSK = 0x3,
+};
+
+#define M_CLK_CTRL_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000200)
+enum bimc_m_clk_ctrl {
+ M_CLK_CTRL_RMSK = 0x3,
+ M_CLK_CTRL_MAS_CLK_GATING_EN_BMSK = 0x2,
+ M_CLK_CTRL_MAS_CLK_GATING_EN_SHFT = 0x1,
+ M_CLK_CTRL_CORE_CLK_GATING_EN_BMSK = 0x1,
+ M_CLK_CTRL_CORE_CLK_GATING_EN_SHFT = 0x0,
+};
+
+#define M_MODE_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000210)
+enum bimc_m_mode {
+ M_MODE_RMSK = 0xf0000001,
+ M_MODE_WR_GATHER_BEATS_BMSK = 0xf0000000,
+ M_MODE_WR_GATHER_BEATS_SHFT = 0x1c,
+ M_MODE_ORDERING_MODEL_BMSK = 0x1,
+ M_MODE_ORDERING_MODEL_SHFT = 0x0,
+};
+
+#define M_PRIOLVL_OVERRIDE_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000220)
+enum bimc_m_priolvl_override {
+ M_PRIOLVL_OVERRIDE_RMSK = 0x301,
+ M_PRIOLVL_OVERRIDE_BMSK = 0x300,
+ M_PRIOLVL_OVERRIDE_SHFT = 0x8,
+ M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_BMSK = 0x1,
+ M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_SHFT = 0x0,
+};
+
+#define M_RD_CMD_OVERRIDE_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000230)
+enum bimc_m_read_command_override {
+ M_RD_CMD_OVERRIDE_RMSK = 0x37f3f,
+ M_RD_CMD_OVERRIDE_AREQPRIO_BMSK = 0x300000,
+ M_RD_CMD_OVERRIDE_AREQPRIO_SHFT = 0x18,
+ M_RD_CMD_OVERRIDE_AMEMTYPE_BMSK = 0x70000,
+ M_RD_CMD_OVERRIDE_AMEMTYPE_SHFT = 0x10,
+ M_RD_CMD_OVERRIDE_ATRANSIENT_BMSK = 0x1000,
+ M_RD_CMD_OVERRIDE_ATRANSIENT_SHFT = 0xc,
+ M_RD_CMD_OVERRIDE_ASHARED_BMSK = 0x800,
+ M_RD_CMD_OVERRIDE_ASHARED_SHFT = 0xb,
+ M_RD_CMD_OVERRIDE_AREDIRECT_BMSK = 0x400,
+ M_RD_CMD_OVERRIDE_AREDIRECT_SHFT = 0xa,
+ M_RD_CMD_OVERRIDE_AOOO_BMSK = 0x200,
+ M_RD_CMD_OVERRIDE_AOOO_SHFT = 0x9,
+ M_RD_CMD_OVERRIDE_AINNERSHARED_BMSK = 0x100,
+ M_RD_CMD_OVERRIDE_AINNERSHARED_SHFT = 0x8,
+ M_RD_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK = 0x40,
+ M_RD_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT = 0x6,
+ M_RD_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_BMSK = 0x20,
+ M_RD_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_SHFT = 0x5,
+ M_RD_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_BMSK = 0x10,
+ M_RD_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_SHFT = 0x4,
+ M_RD_CMD_OVERRIDE_OVERRIDE_ASHARED_BMSK = 0x8,
+ M_RD_CMD_OVERRIDE_OVERRIDE_ASHARED_SHFT = 0x3,
+ M_RD_CMD_OVERRIDE_OVERRIDE_AREDIRECT_BMSK = 0x4,
+ M_RD_CMD_OVERRIDE_OVERRIDE_AREDIRECT_SHFT = 0x2,
+ M_RD_CMD_OVERRIDE_OVERRIDE_AOOO_BMSK = 0x2,
+ M_RD_CMD_OVERRIDE_OVERRIDE_AOOO_SHFT = 0x1,
+ M_RD_CMD_OVERRIDE_OVERRIDE_AINNERSHARED_BMSK = 0x1,
+ M_RD_CMD_OVERRIDE_OVERRIDE_AINNERSHARED_SHFT = 0x0,
+};
+
+#define M_WR_CMD_OVERRIDE_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000240)
+enum bimc_m_write_command_override {
+ M_WR_CMD_OVERRIDE_RMSK = 0x37f3f,
+ M_WR_CMD_OVERRIDE_AREQPRIO_BMSK = 0x30000,
+ M_WR_CMD_OVERRIDE_AREQPRIO_SHFT = 0x10,
+ M_WR_CMD_OVERRIDE_AMEMTYPE_BMSK = 0x7000,
+ M_WR_CMD_OVERRIDE_AMEMTYPE_SHFT = 0xc,
+ M_WR_CMD_OVERRIDE_ASHARED_BMSK = 0x800,
+ M_WR_CMD_OVERRIDE_ASHARED_SHFT = 0xb,
+ M_WR_CMD_OVERRIDE_AREDIRECT_BMSK = 0x400,
+ M_WR_CMD_OVERRIDE_AREDIRECT_SHFT = 0xa,
+ M_WR_CMD_OVERRIDE_AOOO_BMSK = 0x200,
+ M_WR_CMD_OVERRIDE_AOOO_SHFT = 0x9,
+ M_WR_CMD_OVERRIDE_AINNERSHARED_BMSK = 0x100,
+ M_WR_CMD_OVERRIDE_AINNERSHARED_SHFT = 0x8,
+ M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK = 0x20,
+ M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT = 0x5,
+ M_WR_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_BMSK = 0x10,
+ M_WR_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_SHFT = 0x4,
+ M_WR_CMD_OVERRIDE_OVERRIDE_ASHARED_BMSK = 0x8,
+ M_WR_CMD_OVERRIDE_OVERRIDE_ASHARED_SHFT = 0x3,
+ M_WR_CMD_OVERRIDE_OVERRIDE_AREDIRECT_BMSK = 0x4,
+ M_WR_CMD_OVERRIDE_OVERRIDE_AREDIRECT_SHFT = 0x2,
+ M_WR_CMD_OVERRIDE_OVERRIDE_AOOO_BMSK = 0x2,
+ M_WR_CMD_OVERRIDE_OVERRIDE_AOOO_SHFT = 0x1,
+ M_WR_CMD_OVERRIDE_OVERRIDE_AINNERSHARED_BMSK = 0x1,
+ M_WR_CMD_OVERRIDE_OVERRIDE_AINNERSHARED_SHFT = 0x0,
+};
+
+#define M_BKE_EN_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000300)
+enum bimc_m_bke_en {
+ M_BKE_EN_RMSK = 0x1,
+ M_BKE_EN_EN_BMSK = 0x1,
+ M_BKE_EN_EN_SHFT = 0x0,
+};
+
+/* Grant Period registers */
+#define M_BKE_GP_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000304)
+enum bimc_m_bke_grant_period {
+ M_BKE_GP_RMSK = 0x3ff,
+ M_BKE_GP_GP_BMSK = 0x3ff,
+ M_BKE_GP_GP_SHFT = 0x0,
+};
+
+/* Grant count registers */
+#define M_BKE_GC_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000308)
+enum bimc_m_bke_grant_count {
+ M_BKE_GC_RMSK = 0xffff,
+ M_BKE_GC_GC_BMSK = 0xffff,
+ M_BKE_GC_GC_SHFT = 0x0,
+};
+
+/* Threshold High Registers */
+#define M_BKE_THH_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000320)
+enum bimc_m_bke_thresh_high {
+ M_BKE_THH_RMSK = 0xffff,
+ M_BKE_THH_THRESH_BMSK = 0xffff,
+ M_BKE_THH_THRESH_SHFT = 0x0,
+};
+
+/* Threshold Medium Registers */
+#define M_BKE_THM_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000324)
+enum bimc_m_bke_thresh_medium {
+ M_BKE_THM_RMSK = 0xffff,
+ M_BKE_THM_THRESH_BMSK = 0xffff,
+ M_BKE_THM_THRESH_SHFT = 0x0,
+};
+
+/* Threshold Low Registers */
+#define M_BKE_THL_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000328)
+enum bimc_m_bke_thresh_low {
+ M_BKE_THL_RMSK = 0xffff,
+ M_BKE_THL_THRESH_BMSK = 0xffff,
+ M_BKE_THL_THRESH_SHFT = 0x0,
+};
+
+#define M_BKE_HEALTH_0_CONFIG_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000340)
+enum bimc_m_bke_health_0 {
+ M_BKE_HEALTH_0_CONFIG_RMSK = 0x80000303,
+ M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_BMSK = 0x80000000,
+ M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_SHFT = 0x1f,
+ M_BKE_HEALTH_0_CONFIG_AREQPRIO_BMSK = 0x300,
+ M_BKE_HEALTH_0_CONFIG_AREQPRIO_SHFT = 0x8,
+ M_BKE_HEALTH_0_CONFIG_PRIOLVL_BMSK = 0x3,
+ M_BKE_HEALTH_0_CONFIG_PRIOLVL_SHFT = 0x0,
+};
+
+#define M_BKE_HEALTH_1_CONFIG_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000344)
+enum bimc_m_bke_health_1 {
+ M_BKE_HEALTH_1_CONFIG_RMSK = 0x80000303,
+ M_BKE_HEALTH_1_CONFIG_LIMIT_CMDS_BMSK = 0x80000000,
+ M_BKE_HEALTH_1_CONFIG_LIMIT_CMDS_SHFT = 0x1f,
+ M_BKE_HEALTH_1_CONFIG_AREQPRIO_BMSK = 0x300,
+ M_BKE_HEALTH_1_CONFIG_AREQPRIO_SHFT = 0x8,
+ M_BKE_HEALTH_1_CONFIG_PRIOLVL_BMSK = 0x3,
+ M_BKE_HEALTH_1_CONFIG_PRIOLVL_SHFT = 0x0,
+};
+
+#define M_BKE_HEALTH_2_CONFIG_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000348)
+enum bimc_m_bke_health_2 {
+ M_BKE_HEALTH_2_CONFIG_RMSK = 0x80000303,
+ M_BKE_HEALTH_2_CONFIG_LIMIT_CMDS_BMSK = 0x80000000,
+ M_BKE_HEALTH_2_CONFIG_LIMIT_CMDS_SHFT = 0x1f,
+ M_BKE_HEALTH_2_CONFIG_AREQPRIO_BMSK = 0x300,
+ M_BKE_HEALTH_2_CONFIG_AREQPRIO_SHFT = 0x8,
+ M_BKE_HEALTH_2_CONFIG_PRIOLVL_BMSK = 0x3,
+ M_BKE_HEALTH_2_CONFIG_PRIOLVL_SHFT = 0x0,
+};
+
+#define M_BKE_HEALTH_3_CONFIG_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x0000034c)
+enum bimc_m_bke_health_3 {
+ M_BKE_HEALTH_3_CONFIG_RMSK = 0x303,
+ M_BKE_HEALTH_3_CONFIG_AREQPRIO_BMSK = 0x300,
+ M_BKE_HEALTH_3_CONFIG_AREQPRIO_SHFT = 0x8,
+ M_BKE_HEALTH_3_CONFIG_PRIOLVL_BMSK = 0x3,
+ M_BKE_HEALTH_3_CONFIG_PRIOLVL_SHFT = 0x0,
+};
+
+#define M_BUF_STATUS_ADDR(b, n) \
+ (M_REG_BASE(b) + (0x4000 * (n)) + 0x00000400)
+enum bimc_m_buf_status {
+ M_BUF_STATUS_RMSK = 0xf03f030,
+ M_BUF_STATUS_RCH_DATA_WR_FULL_BMSK = 0x8000000,
+ M_BUF_STATUS_RCH_DATA_WR_FULL_SHFT = 0x1b,
+ M_BUF_STATUS_RCH_DATA_WR_EMPTY_BMSK = 0x4000000,
+ M_BUF_STATUS_RCH_DATA_WR_EMPTY_SHFT = 0x1a,
+ M_BUF_STATUS_RCH_CTRL_WR_FULL_BMSK = 0x2000000,
+ M_BUF_STATUS_RCH_CTRL_WR_FULL_SHFT = 0x19,
+ M_BUF_STATUS_RCH_CTRL_WR_EMPTY_BMSK = 0x1000000,
+ M_BUF_STATUS_RCH_CTRL_WR_EMPTY_SHFT = 0x18,
+ M_BUF_STATUS_BCH_WR_FULL_BMSK = 0x20000,
+ M_BUF_STATUS_BCH_WR_FULL_SHFT = 0x11,
+ M_BUF_STATUS_BCH_WR_EMPTY_BMSK = 0x10000,
+ M_BUF_STATUS_BCH_WR_EMPTY_SHFT = 0x10,
+ M_BUF_STATUS_WCH_DATA_RD_FULL_BMSK = 0x8000,
+ M_BUF_STATUS_WCH_DATA_RD_FULL_SHFT = 0xf,
+ M_BUF_STATUS_WCH_DATA_RD_EMPTY_BMSK = 0x4000,
+ M_BUF_STATUS_WCH_DATA_RD_EMPTY_SHFT = 0xe,
+ M_BUF_STATUS_WCH_CTRL_RD_FULL_BMSK = 0x2000,
+ M_BUF_STATUS_WCH_CTRL_RD_FULL_SHFT = 0xd,
+ M_BUF_STATUS_WCH_CTRL_RD_EMPTY_BMSK = 0x1000,
+ M_BUF_STATUS_WCH_CTRL_RD_EMPTY_SHFT = 0xc,
+ M_BUF_STATUS_ACH_RD_FULL_BMSK = 0x20,
+ M_BUF_STATUS_ACH_RD_FULL_SHFT = 0x5,
+ M_BUF_STATUS_ACH_RD_EMPTY_BMSK = 0x10,
+ M_BUF_STATUS_ACH_RD_EMPTY_SHFT = 0x4,
+};
+/*BIMC Generic */
+
+#define S_REG_BASE(b) ((b) + 0x00048000)
+
+#define S_COMPONENT_INFO_ADDR(b, n) \
+ (S_REG_BASE(b) + (0x8000 * (n)) + 0x00000000)
+enum bimc_s_component_info {
+ S_COMPONENT_INFO_RMSK = 0xffffff,
+ S_COMPONENT_INFO_INSTANCE_BMSK = 0xff0000,
+ S_COMPONENT_INFO_INSTANCE_SHFT = 0x10,
+ S_COMPONENT_INFO_SUB_TYPE_BMSK = 0xff00,
+ S_COMPONENT_INFO_SUB_TYPE_SHFT = 0x8,
+ S_COMPONENT_INFO_TYPE_BMSK = 0xff,
+ S_COMPONENT_INFO_TYPE_SHFT = 0x0,
+};
+
+#define S_HW_INFO_ADDR(b, n) \
+ (S_REG_BASE(b) + (0x80000 * (n)) + 0x00000010)
+enum bimc_s_hw_info {
+ S_HW_INFO_RMSK = 0xffffffff,
+ S_HW_INFO_MAJOR_BMSK = 0xff000000,
+ S_HW_INFO_MAJOR_SHFT = 0x18,
+ S_HW_INFO_BRANCH_BMSK = 0xff0000,
+ S_HW_INFO_BRANCH_SHFT = 0x10,
+ S_HW_INFO_MINOR_BMSK = 0xff00,
+ S_HW_INFO_MINOR_SHFT = 0x8,
+ S_HW_INFO_ECO_BMSK = 0xff,
+ S_HW_INFO_ECO_SHFT = 0x0,
+};
+
+
+/* S_SCMO_GENERIC */
+
+#define S_SCMO_REG_BASE(b) ((b) + 0x00048000)
+
+#define S_SCMO_CONFIG_INFO_0_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000020)
+enum bimc_s_scmo_config_info_0 {
+ S_SCMO_CONFIG_INFO_0_RMSK = 0xffffffff,
+ S_SCMO_CONFIG_INFO_0_DATA_WIDTH_BMSK = 0xffff0000,
+ S_SCMO_CONFIG_INFO_0_DATA_WIDTH_SHFT = 0x10,
+ S_SCMO_CONFIG_INFO_0_TID_WIDTH_BMSK = 0xff00,
+ S_SCMO_CONFIG_INFO_0_TID_WIDTH_SHFT = 0x8,
+ S_SCMO_CONFIG_INFO_0_MID_WIDTH_BMSK = 0xff,
+ S_SCMO_CONFIG_INFO_0_MID_WIDTH_SHFT = 0x0,
+};
+
+#define S_SCMO_CONFIG_INFO_1_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000030)
+enum bimc_s_scmo_config_info_1 {
+ S_SCMO_CONFIG_INFO_1_RMSK = 0xffffffff,
+ S_SCMO_CONFIG_INFO_1_MPORT_CONNECTIVITY_BMSK = 0xffffffff,
+ S_SCMO_CONFIG_INFO_1_MPORT_CONNECTIVITY_SHFT = 0x0,
+};
+
+#define S_SCMO_CONFIG_INFO_2_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000040)
+enum bimc_s_scmo_config_info_2 {
+ S_SCMO_CONFIG_INFO_2_RMSK = 0xff00ff,
+ S_SCMO_CONFIG_INFO_2_NUM_GLOBAL_MONS_BMSK = 0xff0000,
+ S_SCMO_CONFIG_INFO_2_NUM_GLOBAL_MONS_SHFT = 0x10,
+ S_SCMO_CONFIG_INFO_2_VMID_WIDTH_BMSK = 0xff,
+ S_SCMO_CONFIG_INFO_2_VMID_WIDTH_SHFT = 0x0,
+};
+
+#define S_SCMO_CONFIG_INFO_3_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000050)
+enum bimc_s_scmo_config_info_3 {
+ S_SCMO_CONFIG_INFO_3_RMSK = 0xffffffff,
+ S_SCMO_CONFIG_INFO_3_RCH0_CTRL_DEPTH_BMSK = 0xff000000,
+ S_SCMO_CONFIG_INFO_3_RCH0_CTRL_DEPTH_SHFT = 0x18,
+ S_SCMO_CONFIG_INFO_3_RCH0_DEPTH_BMSK = 0xff0000,
+ S_SCMO_CONFIG_INFO_3_RCH0_DEPTH_SHFT = 0x10,
+ S_SCMO_CONFIG_INFO_3_BCH_DEPTH_BMSK = 0xff00,
+ S_SCMO_CONFIG_INFO_3_BCH_DEPTH_SHFT = 0x8,
+ S_SCMO_CONFIG_INFO_3_WCH_DEPTH_BMSK = 0xff,
+ S_SCMO_CONFIG_INFO_3_WCH_DEPTH_SHFT = 0x0,
+};
+
+#define S_SCMO_CONFIG_INFO_4_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000060)
+enum bimc_s_scmo_config_info_4 {
+ S_SCMO_CONFIG_INFO_4_RMSK = 0xffff,
+ S_SCMO_CONFIG_INFO_4_RCH1_CTRL_DEPTH_BMSK = 0xff00,
+ S_SCMO_CONFIG_INFO_4_RCH1_CTRL_DEPTH_SHFT = 0x8,
+ S_SCMO_CONFIG_INFO_4_RCH1_DEPTH_BMSK = 0xff,
+ S_SCMO_CONFIG_INFO_4_RCH1_DEPTH_SHFT = 0x0,
+};
+
+#define S_SCMO_CONFIG_INFO_5_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000070)
+enum bimc_s_scmo_config_info_5 {
+ S_SCMO_CONFIG_INFO_5_RMSK = 0xffff,
+ S_SCMO_CONFIG_INFO_5_DPE_CQ_DEPTH_BMSK = 0xff00,
+ S_SCMO_CONFIG_INFO_5_DPE_CQ_DEPTH_SHFT = 0x8,
+ S_SCMO_CONFIG_INFO_5_DDR_BUS_WIDTH_BMSK = 0xff,
+ S_SCMO_CONFIG_INFO_5_DDR_BUS_WIDTH_SHFT = 0x0,
+};
+
+#define S_SCMO_CONFIG_INFO_6_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000080)
+enum bimc_s_scmo_config_info_6 {
+ S_SCMO_CONFIG_INFO_6_RMSK = 0x1111,
+ S_SCMO_CONFIG_INFO_6_WBUFC_PIPE_BMSK = 0x1000,
+ S_SCMO_CONFIG_INFO_6_WBUFC_PIPE_SHFT = 0xc,
+ S_SCMO_CONFIG_INFO_6_RDOPT_PIPE_BMSK = 0x100,
+ S_SCMO_CONFIG_INFO_6_RDOPT_PIPE_SHFT = 0x8,
+ S_SCMO_CONFIG_INFO_6_ACHAN_INTF_PIPE_BMSK = 0x10,
+ S_SCMO_CONFIG_INFO_6_ACHAN_INTF_PIPE_SHFT = 0x4,
+ S_SCMO_CONFIG_INFO_6_ADDR_DECODE_HT_BMSK = 0x1,
+ S_SCMO_CONFIG_INFO_6_ADDR_DECODE_HT_SHFT = 0x0,
+};
+
+#define S_SCMO_INT_STATUS_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000100)
+enum bimc_s_scmo_int_status {
+ S_SCMO_INT_STATUS_RMSK = 0x1,
+ S_SCMO_INT_STATUS_ERR_OCCURED_BMSK = 0x1,
+ S_SCMO_INT_STATUS_ERR_OCCURED_SHFT = 0x0,
+};
+
+#define S_SCMO_INT_CLR_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000108)
+enum bimc_s_scmo_int_clr {
+ S_SCMO_INT_CLR_RMSK = 0x1,
+ S_SCMO_INT_CLR_IRQ_CLR_BMSK = 0x1,
+ S_SCMO_INT_CLR_IRQ_CLR_SHFT = 0x0,
+};
+
+#define S_SCMO_INT_EN_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x0000010c)
+enum bimc_s_scmo_int_en {
+ S_SCMO_INT_EN_RMSK = 0x1,
+ S_SCMO_INT_EN_IRQ_EN_BMSK = 0x1,
+ S_SCMO_INT_EN_IRQ_EN_SHFT = 0x0,
+};
+
+#define S_SCMO_ESYN_ADDR_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000120)
+enum bimc_s_scmo_esyn_addr {
+ S_SCMO_ESYN_ADDR_RMSK = 0xffffffff,
+ S_SCMO_ESYN_ADDR_ESYN_ADDR_ERR_ADDR_BMSK = 0xffffffff,
+ S_SCMO_ESYN_ADDR_ESYN_ADDR_ERR_ADDR_SHFT = 0x0,
+};
+
+#define S_SCMO_ESYN_APACKET_0_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000128)
+enum bimc_s_scmo_esyn_apacket_0 {
+ S_SCMO_ESYN_APACKET_0_RMSK = 0xff1fffff,
+ S_SCMO_ESYN_APACKET_0_ERR_ATID_BMSK = 0xff000000,
+ S_SCMO_ESYN_APACKET_0_ERR_ATID_SHFT = 0x18,
+ S_SCMO_ESYN_APACKET_0_ERR_AVMID_BMSK = 0x1f0000,
+ S_SCMO_ESYN_APACKET_0_ERR_AVMID_SHFT = 0x10,
+ S_SCMO_ESYN_APACKET_0_ERR_AMID_BMSK = 0xffff,
+ S_SCMO_ESYN_APACKET_0_ERR_AMID_SHFT = 0x0,
+};
+
+#define S_SCMO_ESYN_APACKET_1_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x0000012c)
+enum bimc_s_scmo_esyn_apacket_1 {
+ S_SCMO_ESYN_APACKET_1_RMSK = 0x10ff117,
+ S_SCMO_ESYN_APACKET_1_ERR_CODE_BMSK = 0x1000000,
+ S_SCMO_ESYN_APACKET_1_ERR_CODE_SHFT = 0x18,
+ S_SCMO_ESYN_APACKET_1_ERR_ALEN_BMSK = 0xf0000,
+ S_SCMO_ESYN_APACKET_1_ERR_ALEN_SHFT = 0x10,
+ S_SCMO_ESYN_APACKET_1_ERR_ASIZE_BMSK = 0xe000,
+ S_SCMO_ESYN_APACKET_1_ERR_ASIZE_SHFT = 0xd,
+ S_SCMO_ESYN_APACKET_1_ERR_ABURST_BMSK = 0x1000,
+ S_SCMO_ESYN_APACKET_1_ERR_ABURST_SHFT = 0xc,
+ S_SCMO_ESYN_APACKET_1_ERR_AEXCLUSIVE_BMSK = 0x100,
+ S_SCMO_ESYN_APACKET_1_ERR_AEXCLUSIVE_SHFT = 0x8,
+ S_SCMO_ESYN_APACKET_1_ERR_APRONTS_BMSK = 0x10,
+ S_SCMO_ESYN_APACKET_1_ERR_APRONTS_SHFT = 0x4,
+ S_SCMO_ESYN_APACKET_1_ERR_AOOORD_BMSK = 0x4,
+ S_SCMO_ESYN_APACKET_1_ERR_AOOORD_SHFT = 0x2,
+ S_SCMO_ESYN_APACKET_1_ERR_AOOOWR_BMSK = 0x2,
+ S_SCMO_ESYN_APACKET_1_ERR_AOOOWR_SHFT = 0x1,
+ S_SCMO_ESYN_APACKET_1_ERR_AWRITE_BMSK = 0x1,
+ S_SCMO_ESYN_APACKET_1_ERR_AWRITE_SHFT = 0x0,
+};
+
+#define S_SCMO_CLK_CTRL_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000200)
+enum bimc_s_scmo_clk_ctrl {
+ S_SCMO_CLK_CTRL_RMSK = 0xffff1111,
+ S_SCMO_CLK_CTRL_PEN_CMD_CG_EN_BMSK = 0x10000,
+ S_SCMO_CLK_CTRL_PEN_CMD_CG_EN_SHFT = 0x10,
+ S_SCMO_CLK_CTRL_RCH_CG_EN_BMSK = 0x1000,
+ S_SCMO_CLK_CTRL_RCH_CG_EN_SHFT = 0xc,
+ S_SCMO_CLK_CTRL_FLUSH_CG_EN_BMSK = 0x100,
+ S_SCMO_CLK_CTRL_FLUSH_CG_EN_SHFT = 0x8,
+ S_SCMO_CLK_CTRL_WCH_CG_EN_BMSK = 0x10,
+ S_SCMO_CLK_CTRL_WCH_CG_EN_SHFT = 0x4,
+ S_SCMO_CLK_CTRL_ACH_CG_EN_BMSK = 0x1,
+ S_SCMO_CLK_CTRL_ACH_CG_EN_SHFT = 0x0,
+};
+
+#define S_SCMO_SLV_INTERLEAVE_CFG_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000400)
+enum bimc_s_scmo_slv_interleave_cfg {
+ S_SCMO_SLV_INTERLEAVE_CFG_RMSK = 0xff,
+ S_SCMO_SLV_INTERLEAVE_CFG_INTERLEAVE_CS1_BMSK = 0x10,
+ S_SCMO_SLV_INTERLEAVE_CFG_INTERLEAVE_CS1_SHFT = 0x4,
+ S_SCMO_SLV_INTERLEAVE_CFG_INTERLEAVE_CS0_BMSK = 0x1,
+ S_SCMO_SLV_INTERLEAVE_CFG_INTERLEAVE_CS0_SHFT = 0x0,
+};
+
+#define S_SCMO_ADDR_BASE_CSn_ADDR(b, n, o) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000410 + 0x4 * (o))
+enum bimc_s_scmo_addr_base_csn {
+ S_SCMO_ADDR_BASE_CSn_RMSK = 0xffff,
+ S_SCMO_ADDR_BASE_CSn_MAXn = 1,
+ S_SCMO_ADDR_BASE_CSn_ADDR_BASE_BMSK = 0xfc,
+ S_SCMO_ADDR_BASE_CSn_ADDR_BASE_SHFT = 0x2,
+};
+
+#define S_SCMO_ADDR_MAP_CSn_ADDR(b, n, o) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000420 + 0x4 * (o))
+enum bimc_s_scmo_addr_map_csn {
+ S_SCMO_ADDR_MAP_CSn_RMSK = 0xffff,
+ S_SCMO_ADDR_MAP_CSn_MAXn = 1,
+ S_SCMO_ADDR_MAP_CSn_RANK_EN_BMSK = 0x8000,
+ S_SCMO_ADDR_MAP_CSn_RANK_EN_SHFT = 0xf,
+ S_SCMO_ADDR_MAP_CSn_ADDR_MODE_BMSK = 0x1000,
+ S_SCMO_ADDR_MAP_CSn_ADDR_MODE_SHFT = 0xc,
+ S_SCMO_ADDR_MAP_CSn_BANK_SIZE_BMSK = 0x100,
+ S_SCMO_ADDR_MAP_CSn_BANK_SIZE_SHFT = 0x8,
+ S_SCMO_ADDR_MAP_CSn_ROW_SIZE_BMSK = 0x30,
+ S_SCMO_ADDR_MAP_CSn_ROW_SIZE_SHFT = 0x4,
+ S_SCMO_ADDR_MAP_CSn_COL_SIZE_BMSK = 0x3,
+ S_SCMO_ADDR_MAP_CSn_COL_SIZE_SHFT = 0x0,
+};
+
+#define S_SCMO_ADDR_MASK_CSn_ADDR(b, n, o) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000430 + 0x4 * (0))
+enum bimc_s_scmo_addr_mask_csn {
+ S_SCMO_ADDR_MASK_CSn_RMSK = 0xffff,
+ S_SCMO_ADDR_MASK_CSn_MAXn = 1,
+ S_SCMO_ADDR_MASK_CSn_ADDR_MASK_BMSK = 0xfc,
+ S_SCMO_ADDR_MASK_CSn_ADDR_MASK_SHFT = 0x2,
+};
+
+#define S_SCMO_SLV_STATUS_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000450)
+enum bimc_s_scmo_slv_status {
+ S_SCMO_SLV_STATUS_RMSK = 0xff3,
+ S_SCMO_SLV_STATUS_GLOBAL_MONS_IN_USE_BMSK = 0xff0,
+ S_SCMO_SLV_STATUS_GLOBAL_MONS_IN_USE_SHFT = 0x4,
+ S_SCMO_SLV_STATUS_SLAVE_IDLE_BMSK = 0x3,
+ S_SCMO_SLV_STATUS_SLAVE_IDLE_SHFT = 0x0,
+};
+
+#define S_SCMO_CMD_BUF_CFG_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000500)
+enum bimc_s_scmo_cmd_buf_cfg {
+ S_SCMO_CMD_BUF_CFG_RMSK = 0xf1f,
+ S_SCMO_CMD_BUF_CFG_CMD_ORDERING_BMSK = 0x300,
+ S_SCMO_CMD_BUF_CFG_CMD_ORDERING_SHFT = 0x8,
+ S_SCMO_CMD_BUF_CFG_HP_CMD_AREQPRIO_MAP_BMSK = 0x10,
+ S_SCMO_CMD_BUF_CFG_HP_CMD_AREQPRIO_MAP_SHFT = 0x4,
+ S_SCMO_CMD_BUF_CFG_HP_CMD_Q_DEPTH_BMSK = 0x7,
+ S_SCMO_CMD_BUF_CFG_HP_CMD_Q_DEPTH_SHFT = 0x0,
+};
+
+#define S_SCM_CMD_BUF_STATUS_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000520)
+enum bimc_s_scm_cmd_buf_status {
+ S_SCMO_CMD_BUF_STATUS_RMSK = 0x77,
+ S_SCMO_CMD_BUF_STATUS_HP_CMD_BUF_ENTRIES_IN_USE_BMSK = 0x70,
+ S_SCMO_CMD_BUF_STATUS_HP_CMD_BUF_ENTRIES_IN_USE_SHFT = 0x4,
+ S_SCMO_CMD_BUF_STATUS_LP_CMD_BUF_ENTRIES_IN_USE_BMSK = 0x7,
+ S_SCMO_CMD_BUF_STATUS_LP_CMD_BUF_ENTRIES_IN_USE_SHFT = 0x0,
+};
+
+#define S_SCMO_RCH_SEL_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000540)
+enum bimc_s_scmo_rch_sel {
+ S_SCMO_RCH_SEL_RMSK = 0xffffffff,
+ S_SCMO_CMD_BUF_STATUS_RCH_PORTS_BMSK = 0xffffffff,
+ S_SCMO_CMD_BUF_STATUS_RCH_PORTS_SHFT = 0x0,
+};
+
+#define S_SCMO_RCH_BKPR_CFG_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000544)
+enum bimc_s_scmo_rch_bkpr_cfg {
+ S_SCMO_RCH_BKPR_CFG_RMSK = 0xffffffff,
+ S_SCMO_RCH_BKPR_CFG_RCH1_FIFO_BKPR_HI_TH_BMSK = 0x3f000000,
+ S_SCMO_RCH_BKPR_CFG_RCH1_FIFO_BKPR_HI_TH_SHFT = 0x18,
+ S_SCMO_RCH_BKPR_CFG_RCH1_FIFO_BKPR_LO_TH_BMSK = 0x3f0000,
+ S_SCMO_RCH_BKPR_CFG_RCH1_FIFO_BKPR_LO_TH_SHFT = 0x10,
+ S_SCMO_RCH_BKPR_CFG_RCH0_FIFO_BKPR_HI_TH_BMSK = 0x3f00,
+ S_SCMO_RCH_BKPR_CFG_RCH0_FIFO_BKPR_HI_TH_SHFT = 0x8,
+ S_SCMO_RCH_BKPR_CFG_RCH0_FIFO_BKPR_LO_TH_BMSK = 0x3f,
+ S_SCMO_RCH_BKPR_CFG_RCH0_FIFO_BKPR_LO_TH_SHFT = 0x0,
+};
+
+#define S_SCMO_RCH_STATUS_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000560)
+enum bimc_s_scmo_rch_status {
+ S_SCMO_RCH_STATUS_RMSK = 0x33333,
+ S_SCMO_RCH_STATUS_PRQ_FIFO_FULL_BMSK = 0x20000,
+ S_SCMO_RCH_STATUS_PRQ_FIFO_FULL_SHFT = 0x11,
+ S_SCMO_RCH_STATUS_PRQ_FIFO_EMPTY_BMSK = 0x10000,
+ S_SCMO_RCH_STATUS_PRQ_FIFO_EMPTY_SHFT = 0x10,
+ S_SCMO_RCH_STATUS_RCH1_QUAL_FIFO_FULL_BMSK = 0x2000,
+ S_SCMO_RCH_STATUS_RCH1_QUAL_FIFO_FULL_SHFT = 0xd,
+ S_SCMO_RCH_STATUS_RCH1_QUAL_FIFO_EMPTY_BMSK = 0x1000,
+ S_SCMO_RCH_STATUS_RCH1_QUAL_FIFO_EMPTY_SHFT = 0xc,
+ S_SCMO_RCH_STATUS_RCH1_DATA_FIFO_FULL_BMSK = 0x200,
+ S_SCMO_RCH_STATUS_RCH1_DATA_FIFO_FULL_SHFT = 0x9,
+ S_SCMO_RCH_STATUS_RCH1_DATA_FIFO_EMPTY_BMSK = 0x100,
+ S_SCMO_RCH_STATUS_RCH1_DATA_FIFO_EMPTY_SHFT = 0x8,
+ S_SCMO_RCH_STATUS_RCH0_QUAL_FIFO_FULL_BMSK = 0x20,
+ S_SCMO_RCH_STATUS_RCH0_QUAL_FIFO_FULL_SHFT = 0x5,
+ S_SCMO_RCH_STATUS_RCH0_QUAL_FIFO_EMPTY_BMSK = 0x10,
+ S_SCMO_RCH_STATUS_RCH0_QUAL_FIFO_EMPTY_SHFT = 0x4,
+ S_SCMO_RCH_STATUS_RCH0_DATA_FIFO_FULL_BMSK = 0x2,
+ S_SCMO_RCH_STATUS_RCH0_DATA_FIFO_FULL_SHFT = 0x1,
+ S_SCMO_RCH_STATUS_RCH0_DATA_FIFO_EMPTY_BMSK = 0x1,
+ S_SCMO_RCH_STATUS_RCH0_DATA_FIFO_EMPTY_SHFT = 0x0,
+};
+
+#define S_SCMO_WCH_BUF_CFG_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000580)
+enum bimc_s_scmo_wch_buf_cfg {
+ S_SCMO_WCH_BUF_CFG_RMSK = 0xff,
+ S_SCMO_WCH_BUF_CFG_WRITE_BLOCK_READ_BMSK = 0x10,
+ S_SCMO_WCH_BUF_CFG_WRITE_BLOCK_READ_SHFT = 0x4,
+ S_SCMO_WCH_BUF_CFG_COALESCE_EN_BMSK = 0x1,
+ S_SCMO_WCH_BUF_CFG_COALESCE_EN_SHFT = 0x0,
+};
+
+#define S_SCMO_WCH_STATUS_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x000005a0)
+enum bimc_s_scmo_wch_status {
+ S_SCMO_WCH_STATUS_RMSK = 0x333,
+ S_SCMO_WCH_STATUS_BRESP_FIFO_FULL_BMSK = 0x200,
+ S_SCMO_WCH_STATUS_BRESP_FIFO_FULL_SHFT = 0x9,
+ S_SCMO_WCH_STATUS_BRESP_FIFO_EMPTY_BMSK = 0x100,
+ S_SCMO_WCH_STATUS_BRESP_FIFO_EMPTY_SHFT = 0x8,
+ S_SCMO_WCH_STATUS_WDATA_FIFO_FULL_BMSK = 0x20,
+ S_SCMO_WCH_STATUS_WDATA_FIFO_FULL_SHFT = 0x5,
+ S_SCMO_WCH_STATUS_WDATA_FIFO_EMPTY_BMSK = 0x10,
+ S_SCMO_WCH_STATUS_WDATA_FIFO_EMPTY_SHFT = 0x4,
+ S_SCMO_WCH_STATUS_WBUF_FULL_BMSK = 0x2,
+ S_SCMO_WCH_STATUS_WBUF_FULL_SHFT = 0x1,
+ S_SCMO_WCH_STATUS_WBUF_EMPTY_BMSK = 0x1,
+ S_SCMO_WCH_STATUS_WBUF_EMPTY_SHFT = 0x0,
+};
+
+#define S_SCMO_FLUSH_CFG_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x000005c0)
+enum bimc_s_scmo_flush_cfg {
+ S_SCMO_FLUSH_CFG_RMSK = 0xffffffff,
+ S_SCMO_FLUSH_CFG_FLUSH_IN_ORDER_BMSK = 0x10000000,
+ S_SCMO_FLUSH_CFG_FLUSH_IN_ORDER_SHFT = 0x1c,
+ S_SCMO_FLUSH_CFG_FLUSH_IDLE_DELAY_BMSK = 0x3ff0000,
+ S_SCMO_FLUSH_CFG_FLUSH_IDLE_DELAY_SHFT = 0x10,
+ S_SCMO_FLUSH_CFG_FLUSH_UPPER_LIMIT_BMSK = 0xf00,
+ S_SCMO_FLUSH_CFG_FLUSH_UPPER_LIMIT_SHFT = 0x8,
+ S_SCMO_FLUSH_CFG_FLUSH_LOWER_LIMIT_BMSK = 0xf,
+ S_SCMO_FLUSH_CFG_FLUSH_LOWER_LIMIT_SHFT = 0x0,
+};
+
+#define S_SCMO_FLUSH_CMD_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x000005c4)
+enum bimc_s_scmo_flush_cmd {
+ S_SCMO_FLUSH_CMD_RMSK = 0xf,
+ S_SCMO_FLUSH_CMD_FLUSH_ALL_BUF_BMSK = 0x3,
+ S_SCMO_FLUSH_CMD_FLUSH_ALL_BUF_SHFT = 0x0,
+};
+
+#define S_SCMO_CMD_OPT_CFG0_ADDR(b, n) \
+ (S_SCM0_REG_BASE(b) + (0x8000 * (n)) + 0x00000700)
+enum bimc_s_scmo_cmd_opt_cfg0 {
+ S_SCMO_CMD_OPT_CFG0_RMSK = 0xffffff,
+ S_SCMO_CMD_OPT_CFG0_IGNORE_BANK_UNAVL_BMSK = 0x100000,
+ S_SCMO_CMD_OPT_CFG0_IGNORE_BANK_UNAVL_SHFT = 0x14,
+ S_SCMO_CMD_OPT_CFG0_MASK_CMDOUT_PRI_BMSK = 0x10000,
+ S_SCMO_CMD_OPT_CFG0_MASK_CMDOUT_PRI_SHFT = 0x10,
+ S_SCMO_CMD_OPT_CFG0_DPE_CMD_REORDERING_BMSK = 0x1000,
+ S_SCMO_CMD_OPT_CFG0_DPE_CMD_REORDERING_SHFT = 0xc,
+ S_SCMO_CMD_OPT_CFG0_WR_OPT_EN_BMSK = 0x100,
+ S_SCMO_CMD_OPT_CFG0_WR_OPT_EN_SHFT = 0x8,
+ S_SCMO_CMD_OPT_CFG0_RD_OPT_EN_BMSK = 0x10,
+ S_SCMO_CMD_OPT_CFG0_RD_OPT_EN_SHFT = 0x4,
+ S_SCMO_CMD_OPT_CFG0_PAGE_MGMT_POLICY_BMSK = 0x1,
+ S_SCMO_CMD_OPT_CFG0_PAGE_MGMT_POLICY_SHFT = 0x0,
+};
+
+#define S_SCMO_CMD_OPT_CFG1_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000704)
+enum bimc_s_scmo_cmd_opt_cfg1 {
+ S_SCMO_CMD_OPT_CFG1_RMSK = 0xffffffff,
+ S_SCMO_CMD_OPT_CFG1_HSTP_CMD_TIMEOUT_BMSK = 0x1f000000,
+ S_SCMO_CMD_OPT_CFG1_HSTP_CMD_TIMEOUT_SHFT = 0x18,
+ S_SCMO_CMD_OPT_CFG1_HP_CMD_TIMEOUT_BMSK = 0x1f0000,
+ S_SCMO_CMD_OPT_CFG1_HP_CMD_TIMEOUT_SHFT = 0x10,
+ S_SCMO_CMD_OPT_CFG1_MP_CMD_TIMEOUT_BMSK = 0x1f00,
+ S_SCMO_CMD_OPT_CFG1_MP_CMD_TIMEOUT_SHFT = 0x8,
+ S_SCMO_CMD_OPT_CFG1_LP_CMD_TIMEOUT_BMSK = 0x1f,
+ S_SCMO_CMD_OPT_CFG1_LP_CMD_TIMEOUT_SHFT = 0x0,
+};
+
+#define S_SCMO_CMD_OPT_CFG2_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000708)
+enum bimc_s_scmo_cmd_opt_cfg2 {
+ S_SCMO_CMD_OPT_CFG2_RMSK = 0xff,
+ S_SCMO_CMD_OPT_CFG2_RWOPT_CMD_TIMEOUT_BMSK = 0xf,
+ S_SCMO_CMD_OPT_CFG2_RWOPT_CMD_TIMEOUT_SHFT = 0x0,
+};
+
+#define S_SCMO_CMD_OPT_CFG3_ADDR(b, n) \
+ (S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x0000070c)
+enum bimc_s_scmo_cmd_opt_cfg3 {
+ S_SCMO_CMD_OPT_CFG3_RMSK = 0xff,
+ S_SCMO_CMD_OPT_CFG3_FLUSH_CMD_TIMEOUT_BMSK = 0xf,
+ S_SCMO_CMD_OPT_CFG3_FLUSH_CMD_TIMEOUT_SHFT = 0x0,
+};
+
+/* S_SWAY_GENERIC */
+#define S_SWAY_REG_BASE(b) ((b) + 0x00048000)
+
+#define S_SWAY_CONFIG_INFO_0_ADDR(b, n) \
+ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000020)
+enum bimc_s_sway_config_info_0 {
+ S_SWAY_CONFIG_INFO_0_RMSK = 0xff0000ff,
+ S_SWAY_CONFIG_INFO_0_SYNC_MODE_BMSK = 0xff000000,
+ S_SWAY_CONFIG_INFO_0_SYNC_MODE_SHFT = 0x18,
+ S_SWAY_CONFIG_INFO_0_FUNC_BMSK = 0xff,
+ S_SWAY_CONFIG_INFO_0_FUNC_SHFT = 0x0,
+};
+
+#define S_SWAY_CONFIG_INFO_1_ADDR(b, n) \
+ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000030)
+enum bimc_s_sway_config_info_1 {
+ S_SWAY_CONFIG_INFO_1_RMSK = 0xffffffff,
+ S_SWAY_CONFIG_INFO_1_MPORT_CONNECTIVITY_BMSK = 0xffffffff,
+ S_SWAY_CONFIG_INFO_1_MPORT_CONNECTIVITY_SHFT = 0x0,
+};
+
+#define S_SWAY_CONFIG_INFO_2_ADDR(b, n) \
+ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000040)
+enum bimc_s_sway_config_info_2 {
+ S_SWAY_CONFIG_INFO_2_RMSK = 0xffff0000,
+ S_SWAY_CONFIG_INFO_2_MPORT_CONNECTIVITY_BMSK = 0xffff0000,
+ S_SWAY_CONFIG_INFO_2_MPORT_CONNECTIVITY_SHFT = 0x10,
+};
+
+#define S_SWAY_CONFIG_INFO_3_ADDR(b, n) \
+ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000050)
+enum bimc_s_sway_config_info_3 {
+ S_SWAY_CONFIG_INFO_3_RMSK = 0xffffffff,
+ S_SWAY_CONFIG_INFO_3_RCH0_DEPTH_BMSK = 0xff000000,
+ S_SWAY_CONFIG_INFO_3_RCH0_DEPTH_SHFT = 0x18,
+ S_SWAY_CONFIG_INFO_3_BCH_DEPTH_BMSK = 0xff0000,
+ S_SWAY_CONFIG_INFO_3_BCH_DEPTH_SHFT = 0x10,
+ S_SWAY_CONFIG_INFO_3_WCH_DEPTH_BMSK = 0xff,
+ S_SWAY_CONFIG_INFO_3_WCH_DEPTH_SHFT = 0x0,
+};
+
+#define S_SWAY_CONFIG_INFO_4_ADDR(b, n) \
+ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000060)
+enum bimc_s_sway_config_info_4 {
+ S_SWAY_CONFIG_INFO_4_RMSK = 0x800000ff,
+ S_SWAY_CONFIG_INFO_4_DUAL_RCH_EN_BMSK = 0x80000000,
+ S_SWAY_CONFIG_INFO_4_DUAL_RCH_EN_SHFT = 0x1f,
+ S_SWAY_CONFIG_INFO_4_RCH1_DEPTH_BMSK = 0xff,
+ S_SWAY_CONFIG_INFO_4_RCH1_DEPTH_SHFT = 0x0,
+};
+
+#define S_SWAY_CONFIG_INFO_5_ADDR(b, n) \
+ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000070)
+enum bimc_s_sway_config_info_5 {
+ S_SWAY_CONFIG_INFO_5_RMSK = 0x800000ff,
+ S_SWAY_CONFIG_INFO_5_QCH_EN_BMSK = 0x80000000,
+ S_SWAY_CONFIG_INFO_5_QCH_EN_SHFT = 0x1f,
+ S_SWAY_CONFIG_INFO_5_QCH_DEPTH_BMSK = 0xff,
+ S_SWAY_CONFIG_INFO_5_QCH_DEPTH_SHFT = 0x0,
+};
+
+#define S_SWAY_CONFIG_INFO_6_ADDR(b, n) \
+ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000080)
+enum bimc_s_sway_config_info_6 {
+ S_SWAY_CONFIG_INFO_6_RMSK = 0x1,
+ S_SWAY_CONFIG_INFO_6_S2SW_PIPELINE_EN_BMSK = 0x1,
+ S_SWAY_CONFIG_INFO_6_S2SW_PIPELINE_EN_SHFT = 0x0,
+};
+
+#define S_SWAY_INT_STATUS_ADDR(b, n) \
+ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000100)
+enum bimc_s_sway_int_status {
+ S_SWAY_INT_STATUS_RMSK = 0x3,
+ S_SWAY_INT_STATUS_RFU_BMSK = 0x3,
+ S_SWAY_INT_STATUS_RFU_SHFT = 0x0,
+};
+
+#define S_SWAY_INT_CLR_ADDR(b, n) \
+ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000108)
+enum bimc_s_sway_int_clr {
+ S_SWAY_INT_CLR_RMSK = 0x3,
+ S_SWAY_INT_CLR_RFU_BMSK = 0x3,
+ S_SWAY_INT_CLR_RFU_SHFT = 0x0,
+};
+
+
+#define S_SWAY_INT_EN_ADDR(b, n) \
+ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x0000010c)
+enum bimc_s_sway_int_en {
+ S_SWAY_INT_EN_RMSK = 0x3,
+ S_SWAY_INT_EN_RFU_BMSK = 0x3,
+ S_SWAY_INT_EN_RFU_SHFT = 0x0,
+};
+
+#define S_SWAY_CLK_CTRL_ADDR(b, n) \
+ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000200)
+enum bimc_s_sway_clk_ctrl {
+ S_SWAY_CLK_CTRL_RMSK = 0x3,
+ S_SWAY_CLK_CTRL_SLAVE_CLK_GATING_EN_BMSK = 0x2,
+ S_SWAY_CLK_CTRL_SLAVE_CLK_GATING_EN_SHFT = 0x1,
+ S_SWAY_CLK_CTRL_CORE_CLK_GATING_EN_BMSK = 0x1,
+ S_SWAY_CLK_CTRL_CORE_CLK_GATING_EN_SHFT = 0x0,
+};
+
+#define S_SWAY_RCH_SEL_ADDR(b, n) \
+ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000210)
+enum bimc_s_sway_rch_sel {
+ S_SWAY_RCH_SEL_RMSK = 0x7f,
+ S_SWAY_RCH_SEL_UNUSED_BMSK = 0x7f,
+ S_SWAY_RCH_SEL_UNUSED_SHFT = 0x0,
+};
+
+
+#define S_SWAY_MAX_OUTSTANDING_REQS_ADDR(b, n) \
+ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000220)
+enum bimc_s_sway_max_outstanding_reqs {
+ S_SWAY_MAX_OUTSTANDING_REQS_RMSK = 0xffff,
+ S_SWAY_MAX_OUTSTANDING_REQS_WRITE_BMSK = 0xff00,
+ S_SWAY_MAX_OUTSTANDING_REQS_WRITE_SHFT = 0x8,
+ S_SWAY_MAX_OUTSTANDING_REQS_READ_BMSK = 0xff,
+ S_SWAY_MAX_OUTSTANDING_REQS_READ_SHFT = 0x0,
+};
+
+
+#define S_SWAY_BUF_STATUS_0_ADDR(b, n) \
+ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000400)
+enum bimc_s_sway_buf_status_0 {
+ S_SWAY_BUF_STATUS_0_RMSK = 0xf0300f03,
+ S_SWAY_BUF_STATUS_0_RCH0_DATA_RD_FULL_BMSK = 0x80000000,
+ S_SWAY_BUF_STATUS_0_RCH0_DATA_RD_FULL_SHFT = 0x1f,
+ S_SWAY_BUF_STATUS_0_RCH0_DATA_RD_EMPTY_BMSK = 0x40000000,
+ S_SWAY_BUF_STATUS_0_RCH0_DATA_RD_EMPTY_SHFT = 0x1e,
+ S_SWAY_BUF_STATUS_0_RCH0_CTRL_RD_FULL_BMSK = 0x20000000,
+ S_SWAY_BUF_STATUS_0_RCH0_CTRL_RD_FULL_SHFT = 0x1d,
+ S_SWAY_BUF_STATUS_0_RCH0_CTRL_RD_EMPTY_BMSK = 0x10000000,
+ S_SWAY_BUF_STATUS_0_RCH0_CTRL_RD_EMPTY_SHFT = 0x1c,
+ S_SWAY_BUF_STATUS_0_BCH_RD_FULL_BMSK = 0x200000,
+ S_SWAY_BUF_STATUS_0_BCH_RD_FULL_SHFT = 0x15,
+ S_SWAY_BUF_STATUS_0_BCH_RD_EMPTY_BMSK = 0x100000,
+ S_SWAY_BUF_STATUS_0_BCH_RD_EMPTY_SHFT = 0x14,
+ S_SWAY_BUF_STATUS_0_WCH_DATA_WR_FULL_BMSK = 0x800,
+ S_SWAY_BUF_STATUS_0_WCH_DATA_WR_FULL_SHFT = 0xb,
+ S_SWAY_BUF_STATUS_0_WCH_DATA_WR_EMPTY_BMSK = 0x400,
+ S_SWAY_BUF_STATUS_0_WCH_DATA_WR_EMPTY_SHFT = 0xa,
+ S_SWAY_BUF_STATUS_0_WCH_CTRL_WR_FULL_BMSK = 0x200,
+ S_SWAY_BUF_STATUS_0_WCH_CTRL_WR_FULL_SHFT = 0x9,
+ S_SWAY_BUF_STATUS_0_WCH_CTRL_WR_EMPTY_BMSK = 0x100,
+ S_SWAY_BUF_STATUS_0_WCH_CTRL_WR_EMPTY_SHFT = 0x8,
+ S_SWAY_BUF_STATUS_0_ACH_WR_FULL_BMSK = 0x2,
+ S_SWAY_BUF_STATUS_0_ACH_WR_FULL_SHFT = 0x1,
+ S_SWAY_BUF_STATUS_0_ACH_WR_EMPTY_BMSK = 0x1,
+ S_SWAY_BUF_STATUS_0_ACH_WR_EMPTY_SHFT = 0x0,
+};
+
+#define S_SWAY_BUF_STATUS_1_ADDR(b, n) \
+ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000410)
+enum bimc_s_sway_buf_status_1 {
+ S_SWAY_BUF_STATUS_1_RMSK = 0xf0,
+ S_SWAY_BUF_STATUS_1_RCH1_DATA_RD_FULL_BMSK = 0x80,
+ S_SWAY_BUF_STATUS_1_RCH1_DATA_RD_FULL_SHFT = 0x7,
+ S_SWAY_BUF_STATUS_1_RCH1_DATA_RD_EMPTY_BMSK = 0x40,
+ S_SWAY_BUF_STATUS_1_RCH1_DATA_RD_EMPTY_SHFT = 0x6,
+ S_SWAY_BUF_STATUS_1_RCH1_CTRL_RD_FULL_BMSK = 0x20,
+ S_SWAY_BUF_STATUS_1_RCH1_CTRL_RD_FULL_SHFT = 0x5,
+ S_SWAY_BUF_STATUS_1_RCH1_CTRL_RD_EMPTY_BMSK = 0x10,
+ S_SWAY_BUF_STATUS_1_RCH1_CTRL_RD_EMPTY_SHFT = 0x4,
+};
+
+#define S_SWAY_BUF_STATUS_2_ADDR(b, n) \
+ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000420)
+enum bimc_s_sway_buf_status_2 {
+ S_SWAY_BUF_STATUS_2_RMSK = 0x30,
+ S_SWAY_BUF_STATUS_2_QCH_RD_FULL_BMSK = 0x20,
+ S_SWAY_BUF_STATUS_2_QCH_RD_FULL_SHFT = 0x5,
+ S_SWAY_BUF_STATUS_2_QCH_RD_EMPTY_BMSK = 0x10,
+ S_SWAY_BUF_STATUS_2_QCH_RD_EMPTY_SHFT = 0x4,
+};
+
+/* S_ARB_GENERIC */
+
+#define S_ARB_REG_BASE(b) ((b) + 0x00049000)
+
+#define S_ARB_COMPONENT_INFO_ADDR(b, n) \
+ (S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000000)
+enum bimc_s_arb_component_info {
+ S_ARB_COMPONENT_INFO_RMSK = 0xffffff,
+ S_ARB_COMPONENT_INFO_INSTANCE_BMSK = 0xff0000,
+ S_ARB_COMPONENT_INFO_INSTANCE_SHFT = 0x10,
+ S_ARB_COMPONENT_INFO_SUB_TYPE_BMSK = 0xff00,
+ S_ARB_COMPONENT_INFO_SUB_TYPE_SHFT = 0x8,
+ S_ARB_COMPONENT_INFO_TYPE_BMSK = 0xff,
+ S_ARB_COMPONENT_INFO_TYPE_SHFT = 0x0,
+};
+
+#define S_ARB_CONFIG_INFO_0_ADDR(b, n) \
+ (S_ARB_REG_BASE(b) + (0x8000 * (n)) + 0x00000020)
+enum bimc_s_arb_config_info_0 {
+ S_ARB_CONFIG_INFO_0_RMSK = 0x800000ff,
+ S_ARB_CONFIG_INFO_0_ARB2SW_PIPELINE_EN_BMSK = 0x80000000,
+ S_ARB_CONFIG_INFO_0_ARB2SW_PIPELINE_EN_SHFT = 0x1f,
+ S_ARB_CONFIG_INFO_0_FUNC_BMSK = 0xff,
+ S_ARB_CONFIG_INFO_0_FUNC_SHFT = 0x0,
+};
+
+#define S_ARB_CONFIG_INFO_1_ADDR(b, n) \
+ (S_ARB_REG_BASE(b) + (0x8000 * (n)) + 0x00000030)
+enum bimc_s_arb_config_info_1 {
+ S_ARB_CONFIG_INFO_1_RMSK = 0xffffffff,
+ S_ARB_CONFIG_INFO_1_MPORT_CONNECTIVITY_BMSK = 0xffffffff,
+ S_ARB_CONFIG_INFO_1_MPORT_CONNECTIVITY_SHFT = 0x0,
+};
+
+#define S_ARB_CLK_CTRL_ADDR(b) \
+ (S_ARB_REG_BASE(b) + (0x8000 * (n)) + 0x00000200)
+enum bimc_s_arb_clk_ctrl {
+ S_ARB_CLK_CTRL_RMSK = 0x1,
+ S_ARB_CLK_CTRL_SLAVE_CLK_GATING_EN_BMSK = 0x2,
+ S_ARB_CLK_CTRL_SLAVE_CLK_GATING_EN_SHFT = 0x1,
+ S_ARB_CLK_CTRL_CORE_CLK_GATING_EN_BMSK = 0x1,
+ S_ARB_CLK_CTRL_CORE_CLK_GATING_EN_SHFT = 0x0,
+ S_ARB_CLK_CTRL_CLK_GATING_EN_BMSK = 0x1,
+ S_ARB_CLK_CTRL_CLK_GATING_EN_SHFT = 0x0,
+};
+
+#define S_ARB_MODE_ADDR(b, n) \
+ (S_ARB_REG_BASE(b) + (0x8000 * (n)) + 0x00000210)
+enum bimc_s_arb_mode {
+ S_ARB_MODE_RMSK = 0xf0000001,
+ S_ARB_MODE_WR_GRANTS_AHEAD_BMSK = 0xf0000000,
+ S_ARB_MODE_WR_GRANTS_AHEAD_SHFT = 0x1c,
+ S_ARB_MODE_PRIO_RR_EN_BMSK = 0x1,
+ S_ARB_MODE_PRIO_RR_EN_SHFT = 0x0,
+};
+
+#define BKE_HEALTH_MASK \
+ (M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_BMSK |\
+ M_BKE_HEALTH_0_CONFIG_AREQPRIO_BMSK |\
+ M_BKE_HEALTH_0_CONFIG_PRIOLVL_BMSK)
+
+#define BKE_HEALTH_VAL(limit, areq, plvl) \
+ ((((limit) << M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_SHFT) & \
+ M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_BMSK) | \
+ (((areq) << M_BKE_HEALTH_0_CONFIG_AREQPRIO_SHFT) & \
+ M_BKE_HEALTH_0_CONFIG_AREQPRIO_BMSK) | \
+ (((plvl) << M_BKE_HEALTH_0_CONFIG_PRIOLVL_SHFT) & \
+ M_BKE_HEALTH_0_CONFIG_PRIOLVL_BMSK))
+
+#define MAX_GRANT_PERIOD \
+ (M_BKE_GP_GP_BMSK >> \
+ M_BKE_GP_GP_SHFT)
+
+#define MAX_GC \
+ (M_BKE_GC_GC_BMSK >> \
+ M_BKE_GC_GC_SHFT)
+
+static int bimc_div(uint64_t *a, uint32_t b)
+{
+ if ((*a > 0) && (*a < b))
+ return 1;
+ else
+ return do_div(*a, b);
+}
+
+#define ENABLE(val) ((val) == 1 ? 1 : 0)
+void msm_bus_bimc_set_mas_clk_gate(struct msm_bus_bimc_info *binfo,
+ uint32_t mas_index, struct msm_bus_bimc_clk_gate *bgate)
+{
+ uint32_t val, mask, reg_val;
+ void __iomem *addr;
+
+ reg_val = readl_relaxed(M_CLK_CTRL_ADDR(binfo->base,
+ mas_index)) & M_CLK_CTRL_RMSK;
+ addr = M_CLK_CTRL_ADDR(binfo->base, mas_index);
+ mask = (M_CLK_CTRL_MAS_CLK_GATING_EN_BMSK |
+ M_CLK_CTRL_CORE_CLK_GATING_EN_BMSK);
+ val = (bgate->core_clk_gate_en <<
+ M_CLK_CTRL_MAS_CLK_GATING_EN_SHFT) |
+ bgate->port_clk_gate_en;
+ writel_relaxed(((reg_val & (~mask)) | (val & mask)), addr);
+ /* Ensure clock gating enable mask is set before exiting */
+ wmb();
+}
+
+void msm_bus_bimc_arb_en(struct msm_bus_bimc_info *binfo,
+ uint32_t slv_index, bool en)
+{
+ uint32_t reg_val, reg_mask_val, enable, val;
+
+ reg_mask_val = (readl_relaxed(S_ARB_CONFIG_INFO_0_ADDR(binfo->
+ base, slv_index)) & S_ARB_CONFIG_INFO_0_FUNC_BMSK)
+ >> S_ARB_CONFIG_INFO_0_FUNC_SHFT;
+ enable = ENABLE(en);
+ val = enable << S_ARB_MODE_PRIO_RR_EN_SHFT;
+ if (reg_mask_val == BIMC_ARB_MODE_PRIORITY_RR) {
+ reg_val = readl_relaxed(S_ARB_CONFIG_INFO_0_ADDR(binfo->
+ base, slv_index)) & S_ARB_MODE_RMSK;
+ writel_relaxed(((reg_val & (~(S_ARB_MODE_PRIO_RR_EN_BMSK))) |
+ (val & S_ARB_MODE_PRIO_RR_EN_BMSK)),
+ S_ARB_MODE_ADDR(binfo->base, slv_index));
+ /* Ensure arbitration mode is set before returning */
+ wmb();
+ }
+}
+
+static void set_qos_mode(void __iomem *baddr, uint32_t index, uint32_t val0,
+ uint32_t val1, uint32_t val2)
+{
+ uint32_t reg_val, val;
+
+ reg_val = readl_relaxed(M_PRIOLVL_OVERRIDE_ADDR(baddr,
+ index)) & M_PRIOLVL_OVERRIDE_RMSK;
+ val = val0 << M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_SHFT;
+ writel_relaxed(((reg_val & ~(M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_BMSK))
+ | (val & M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_BMSK)),
+ M_PRIOLVL_OVERRIDE_ADDR(baddr, index));
+ reg_val = readl_relaxed(M_RD_CMD_OVERRIDE_ADDR(baddr, index)) &
+ M_RD_CMD_OVERRIDE_RMSK;
+ val = val1 << M_RD_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT;
+ writel_relaxed(((reg_val & ~(M_RD_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK
+ )) | (val & M_RD_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK)),
+ M_RD_CMD_OVERRIDE_ADDR(baddr, index));
+ reg_val = readl_relaxed(M_WR_CMD_OVERRIDE_ADDR(baddr, index)) &
+ M_WR_CMD_OVERRIDE_RMSK;
+ val = val2 << M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT;
+ writel_relaxed(((reg_val & ~(M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK
+ )) | (val & M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK)),
+ M_WR_CMD_OVERRIDE_ADDR(baddr, index));
+ /* Ensure the priority register writes go through */
+ wmb();
+}
+
+static void msm_bus_bimc_set_qos_mode(struct msm_bus_bimc_info *binfo,
+ uint32_t mas_index, uint8_t qmode_sel)
+{
+ uint32_t reg_val, val;
+
+ switch (qmode_sel) {
+ case BIMC_QOS_MODE_FIXED:
+ reg_val = readl_relaxed(M_BKE_EN_ADDR(binfo->base,
+ mas_index)) & M_BKE_EN_RMSK;
+ writel_relaxed((reg_val & (~M_BKE_EN_EN_BMSK)),
+ M_BKE_EN_ADDR(binfo->base, mas_index));
+ /* Ensure that the book-keeping register writes
+ * go through before setting QoS mode.
+ * QoS mode registers might write beyond 1K
+ * boundary in future
+ */
+ wmb();
+ set_qos_mode(binfo->base, mas_index, 1, 1, 1);
+ break;
+
+ case BIMC_QOS_MODE_BYPASS:
+ reg_val = readl_relaxed(M_BKE_EN_ADDR(binfo->base,
+ mas_index)) & M_BKE_EN_RMSK;
+ writel_relaxed((reg_val & (~M_BKE_EN_EN_BMSK)),
+ M_BKE_EN_ADDR(binfo->base, mas_index));
+ /* Ensure that the book-keeping register writes
+ * go through before setting QoS mode.
+ * QoS mode registers might write beyond 1K
+ * boundary in future
+ */
+ wmb();
+ set_qos_mode(binfo->base, mas_index, 0, 0, 0);
+ break;
+
+ case BIMC_QOS_MODE_REGULATOR:
+ case BIMC_QOS_MODE_LIMITER:
+ set_qos_mode(binfo->base, mas_index, 0, 0, 0);
+ reg_val = readl_relaxed(M_BKE_EN_ADDR(binfo->base,
+ mas_index)) & M_BKE_EN_RMSK;
+ val = 1 << M_BKE_EN_EN_SHFT;
+ /* Ensure that the book-keeping register writes
+ * go through before setting QoS mode.
+ * QoS mode registers might write beyond 1K
+ * boundary in future
+ */
+ wmb();
+ writel_relaxed(((reg_val & (~M_BKE_EN_EN_BMSK)) | (val &
+ M_BKE_EN_EN_BMSK)), M_BKE_EN_ADDR(binfo->base,
+ mas_index));
+ break;
+ default:
+ break;
+ }
+}
+
+static void set_qos_prio_rl(void __iomem *addr, uint32_t rmsk,
+ uint8_t index, struct msm_bus_bimc_qos_mode *qmode)
+{
+ uint32_t reg_val, val0, val;
+
+ /* Note, addr is already passed with right mas_index */
+ reg_val = readl_relaxed(addr) & rmsk;
+ val0 = BKE_HEALTH_VAL(qmode->rl.qhealth[index].limit_commands,
+ qmode->rl.qhealth[index].areq_prio,
+ qmode->rl.qhealth[index].prio_level);
+ val = (reg_val & (~(BKE_HEALTH_MASK) | (val0 & BKE_HEALTH_MASK)));
+ writel_relaxed(val, addr);
+ /* Ensure that priority for regulator/limiter modes are
+ * set before returning
+ */
+ wmb();
+
+}
+
+static void msm_bus_bimc_set_qos_prio(struct msm_bus_bimc_info *binfo,
+ uint32_t mas_index, uint8_t qmode_sel,
+ struct msm_bus_bimc_qos_mode *qmode)
+{
+ uint32_t reg_val, val;
+
+ switch (qmode_sel) {
+ case BIMC_QOS_MODE_FIXED:
+ reg_val = readl_relaxed(M_PRIOLVL_OVERRIDE_ADDR(binfo->
+ base, mas_index)) & M_PRIOLVL_OVERRIDE_RMSK;
+ val = qmode->fixed.prio_level <<
+ M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_SHFT;
+ writel_relaxed(((reg_val &
+ ~(M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_BMSK)) | (val
+ & M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_BMSK)),
+ M_PRIOLVL_OVERRIDE_ADDR(binfo->base, mas_index));
+
+ reg_val = readl_relaxed(M_RD_CMD_OVERRIDE_ADDR(binfo->
+ base, mas_index)) & M_RD_CMD_OVERRIDE_RMSK;
+ val = qmode->fixed.areq_prio_rd <<
+ M_RD_CMD_OVERRIDE_AREQPRIO_SHFT;
+ writel_relaxed(((reg_val & ~(M_RD_CMD_OVERRIDE_AREQPRIO_BMSK))
+ | (val & M_RD_CMD_OVERRIDE_AREQPRIO_BMSK)),
+ M_RD_CMD_OVERRIDE_ADDR(binfo->base, mas_index));
+
+ reg_val = readl_relaxed(M_WR_CMD_OVERRIDE_ADDR(binfo->
+ base, mas_index)) & M_WR_CMD_OVERRIDE_RMSK;
+ val = qmode->fixed.areq_prio_wr <<
+ M_WR_CMD_OVERRIDE_AREQPRIO_SHFT;
+ writel_relaxed(((reg_val & ~(M_WR_CMD_OVERRIDE_AREQPRIO_BMSK))
+ | (val & M_WR_CMD_OVERRIDE_AREQPRIO_BMSK)),
+ M_WR_CMD_OVERRIDE_ADDR(binfo->base, mas_index));
+ /* Ensure that fixed mode register writes go through
+ * before returning
+ */
+ wmb();
+ break;
+
+ case BIMC_QOS_MODE_REGULATOR:
+ case BIMC_QOS_MODE_LIMITER:
+ set_qos_prio_rl(M_BKE_HEALTH_3_CONFIG_ADDR(binfo->base,
+ mas_index), M_BKE_HEALTH_3_CONFIG_RMSK, 3, qmode);
+ set_qos_prio_rl(M_BKE_HEALTH_2_CONFIG_ADDR(binfo->base,
+ mas_index), M_BKE_HEALTH_2_CONFIG_RMSK, 2, qmode);
+ set_qos_prio_rl(M_BKE_HEALTH_1_CONFIG_ADDR(binfo->base,
+ mas_index), M_BKE_HEALTH_1_CONFIG_RMSK, 1, qmode);
+ set_qos_prio_rl(M_BKE_HEALTH_1_CONFIG_ADDR(binfo->base,
+ mas_index), M_BKE_HEALTH_0_CONFIG_RMSK, 0 , qmode);
+ break;
+ case BIMC_QOS_MODE_BYPASS:
+ default:
+ break;
+ }
+}
+
+static void set_qos_bw_regs(void __iomem *baddr, uint32_t mas_index,
+ long int th, long int tm, long int tl, uint32_t gp,
+ uint32_t gc, bool bke_en)
+{
+ uint32_t reg_val, val;
+
+ /* Disable BKE before writing to registers as per spec */
+ reg_val = readl_relaxed(M_BKE_EN_ADDR(baddr, mas_index)) &
+ M_BKE_EN_RMSK;
+ writel_relaxed((reg_val & ~(M_BKE_EN_EN_BMSK)),
+ M_BKE_EN_ADDR(baddr, mas_index));
+
+ /* Write values of registers calculated */
+ reg_val = readl_relaxed(M_BKE_GP_ADDR(baddr, mas_index))
+ & M_BKE_GP_RMSK;
+ val = gp << M_BKE_GP_GP_SHFT;
+ writel_relaxed(((reg_val & ~(M_BKE_GP_GP_BMSK)) | (val &
+ M_BKE_GP_GP_BMSK)), M_BKE_GP_ADDR(baddr, mas_index));
+
+ reg_val = readl_relaxed(M_BKE_GC_ADDR(baddr, mas_index)) &
+ M_BKE_GC_RMSK;
+ val = gc << M_BKE_GC_GC_SHFT;
+ writel_relaxed(((reg_val & ~(M_BKE_GC_GC_BMSK)) | (val &
+ M_BKE_GC_GC_BMSK)), M_BKE_GC_ADDR(baddr, mas_index));
+
+ reg_val = readl_relaxed(M_BKE_THH_ADDR(baddr, mas_index)) &
+ M_BKE_THH_RMSK;
+ val = th << M_BKE_THH_THRESH_SHFT;
+ writel_relaxed(((reg_val & ~(M_BKE_THH_THRESH_BMSK)) | (val &
+ M_BKE_THH_THRESH_BMSK)), M_BKE_THH_ADDR(baddr, mas_index));
+
+ reg_val = readl_relaxed(M_BKE_THM_ADDR(baddr, mas_index)) &
+ M_BKE_THM_RMSK;
+ val = tm << M_BKE_THM_THRESH_SHFT;
+ writel_relaxed(((reg_val & ~(M_BKE_THM_THRESH_BMSK)) | (val &
+ M_BKE_THM_THRESH_BMSK)), M_BKE_THM_ADDR(baddr, mas_index));
+
+ reg_val = readl_relaxed(M_BKE_THL_ADDR(baddr, mas_index)) &
+ M_BKE_THL_RMSK;
+ val = tl << M_BKE_THL_THRESH_SHFT;
+ writel_relaxed(((reg_val & ~(M_BKE_THL_THRESH_BMSK)) |
+ (val & M_BKE_THL_THRESH_BMSK)), M_BKE_THL_ADDR(baddr,
+ mas_index));
+
+ /* Set BKE enable to the value it was */
+ reg_val = readl_relaxed(M_BKE_EN_ADDR(baddr, mas_index)) &
+ M_BKE_EN_RMSK;
+ val = bke_en << M_BKE_EN_EN_SHFT;
+ writel_relaxed(((reg_val & ~(M_BKE_EN_EN_BMSK)) | (val &
+ M_BKE_EN_EN_BMSK)), M_BKE_EN_ADDR(baddr, mas_index));
+ /* Ensure that all bandwidth register writes have completed
+ * before returning
+ */
+ wmb();
+}
+
+static void msm_bus_bimc_set_qos_bw(struct msm_bus_bimc_info *binfo,
+ uint32_t mas_index, struct msm_bus_bimc_qos_bw *qbw)
+{
+ uint32_t bke_en;
+
+ /* Validate QOS Frequency */
+ if (binfo->qos_freq == 0) {
+ MSM_BUS_DBG("Zero frequency\n");
+ return;
+ }
+
+ /* Get enable bit for BKE before programming the period */
+ bke_en = (readl_relaxed(M_BKE_EN_ADDR(binfo->base, mas_index)) &
+ M_BKE_EN_EN_BMSK) >> M_BKE_EN_EN_SHFT;
+
+ /* Only calculate if there's a requested bandwidth and window */
+ if (qbw->bw && qbw->ws) {
+ uint64_t th, tm, tl;
+ uint32_t gp, gc, data_width;
+ uint64_t gp_nominal, gp_required, gp_calc, data, temp;
+ uint64_t win = qbw->ws * binfo->qos_freq;
+ temp = win;
+ /*
+ * Calculate nominal grant period defined by requested
+ * window size.
+ * Ceil this value to max grant period.
+ */
+ bimc_div(&temp, 1000000);
+ gp_nominal = min_t(uint64_t, MAX_GRANT_PERIOD, temp);
+ /*
+ * Calculate max window size, defined by bw request.
+ * Units: (KHz, MB/s)
+ */
+ data_width = (readl_relaxed(M_CONFIG_INFO_2_ADDR(
+ binfo->base, mas_index)) &
+ M_CONFIG_INFO_2_M_DATA_WIDTH_BMSK) >>
+ M_CONFIG_INFO_2_M_DATA_WIDTH_SHFT;
+
+ /* If unspecified, use data-width 8 by default */
+ if (!data_width)
+ data_width = 8;
+
+ gp_calc = MAX_GC * data_width * binfo->qos_freq * 1000;
+ gp_required = gp_calc;
+ bimc_div(&gp_required, qbw->bw);
+
+ /* User min of two grant periods */
+ gp = min_t(uint64_t, gp_nominal, gp_required);
+
+ /* Calculate bandwith in grants and ceil. */
+ temp = qbw->bw * gp;
+ data = data_width * binfo->qos_freq * 1000;
+ bimc_div(&temp, data);
+ gc = min_t(uint64_t, MAX_GC, temp);
+
+ /* Calculate thresholds */
+ th = qbw->bw - qbw->thh;
+ tm = qbw->bw - qbw->thm;
+ tl = qbw->bw - qbw->thl;
+
+ th = th * gp;
+ bimc_div(&th, data);
+ tm = tm * gp;
+ bimc_div(&tm, data);
+ tl = tl * gp;
+ bimc_div(&tl, data);
+
+ MSM_BUS_DBG("BIMC: BW: mas_index: %d, th: %llu tm: %llu\n",
+ mas_index, th, tm);
+ MSM_BUS_DBG("BIMC: tl: %llu gp:%u gc: %u bke_en: %u\n",
+ tl, gp, gc, bke_en);
+ set_qos_bw_regs(binfo->base, mas_index, th, tm, tl, gp,
+ gc, bke_en);
+ } else
+ /* Clear bandwidth registers */
+ set_qos_bw_regs(binfo->base, mas_index, 0, 0, 0, 0, 0,
+ bke_en);
+}
+
+static int msm_bus_bimc_allocate_commit_data(struct msm_bus_fabric_registration
+ *fab_pdata, void **cdata, int ctx)
+{
+ struct msm_bus_bimc_commit **cd = (struct msm_bus_bimc_commit **)cdata;
+ struct msm_bus_bimc_info *binfo =
+ (struct msm_bus_bimc_info *)fab_pdata->hw_data;
+
+ MSM_BUS_DBG("Allocating BIMC commit data\n");
+ *cd = kzalloc(sizeof(struct msm_bus_bimc_commit), GFP_KERNEL);
+ if (!*cd) {
+ MSM_BUS_DBG("Couldn't alloc mem for cdata\n");
+ return -ENOMEM;
+ }
+
+ (*cd)->mas = binfo->cdata[ctx].mas;
+ (*cd)->slv = binfo->cdata[ctx].slv;
+
+ return 0;
+}
+
+static void *msm_bus_bimc_allocate_bimc_data(struct platform_device *pdev,
+ struct msm_bus_fabric_registration *fab_pdata)
+{
+ struct resource *bimc_mem;
+ struct resource *bimc_io;
+ struct msm_bus_bimc_info *binfo;
+ int i;
+
+ MSM_BUS_DBG("Allocating BIMC data\n");
+ binfo = kzalloc(sizeof(struct msm_bus_bimc_info), GFP_KERNEL);
+ if (!binfo) {
+ WARN(!binfo, "Couldn't alloc mem for bimc_info\n");
+ return NULL;
+ }
+
+ binfo->qos_freq = fab_pdata->qos_freq;
+
+ binfo->params.nmasters = fab_pdata->nmasters;
+ binfo->params.nslaves = fab_pdata->nslaves;
+ binfo->params.bus_id = fab_pdata->id;
+
+ for (i = 0; i < NUM_CTX; i++) {
+ binfo->cdata[i].mas = kzalloc(sizeof(struct
+ msm_bus_node_hw_info) * fab_pdata->nmasters * 2,
+ GFP_KERNEL);
+ if (!binfo->cdata[i].mas) {
+ MSM_BUS_ERR("Couldn't alloc mem for bimc master hw\n");
+ kfree(binfo);
+ return NULL;
+ }
+
+ binfo->cdata[i].slv = kzalloc(sizeof(struct
+ msm_bus_node_hw_info) * fab_pdata->nslaves * 2,
+ GFP_KERNEL);
+ if (!binfo->cdata[i].slv) {
+ MSM_BUS_DBG("Couldn't alloc mem for bimc slave hw\n");
+ kfree(binfo->cdata[i].mas);
+ kfree(binfo);
+ return NULL;
+ }
+ }
+
+ bimc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!bimc_mem && !fab_pdata->virt) {
+ MSM_BUS_ERR("Cannot get BIMC Base address\n");
+ kfree(binfo);
+ return NULL;
+ }
+
+ bimc_io = request_mem_region(bimc_mem->start,
+ resource_size(bimc_mem), pdev->name);
+ if (!bimc_io) {
+ MSM_BUS_ERR("BIMC memory unavailable\n");
+ kfree(binfo);
+ return NULL;
+ }
+
+ binfo->base = ioremap(bimc_mem->start, resource_size(bimc_mem));
+ if (!binfo->base) {
+ MSM_BUS_ERR("IOremap failed for BIMC!\n");
+ release_mem_region(bimc_mem->start, resource_size(bimc_mem));
+ kfree(binfo);
+ return NULL;
+ }
+
+ fab_pdata->hw_data = (void *)binfo;
+ return (void *)binfo;
+}
+
+static void free_commit_data(void *cdata)
+{
+ struct msm_bus_bimc_commit *cd = (struct msm_bus_bimc_commit *)cdata;
+
+ kfree(cd->mas);
+ kfree(cd->slv);
+ kfree(cd);
+}
+
+static void msm_bus_bimc_update_bw(struct msm_bus_inode_info *hop,
+ struct msm_bus_inode_info *info,
+ struct msm_bus_fabric_registration *fab_pdata,
+ void *sel_cdata, int *master_tiers,
+ long int add_bw)
+{
+ struct msm_bus_bimc_info *binfo;
+ struct msm_bus_bimc_qos_bw qbw;
+ int i;
+ long int bw;
+ int ports = info->node_info->num_mports;
+ struct msm_bus_bimc_commit *sel_cd =
+ (struct msm_bus_bimc_commit *)sel_cdata;
+
+ MSM_BUS_DBG("BIMC: Update bw for ID %d, with IID: %d: %ld\n",
+ info->node_info->id, info->node_info->priv_id, add_bw);
+
+ binfo = (struct msm_bus_bimc_info *)fab_pdata->hw_data;
+ if (!info->node_info->qport) {
+ MSM_BUS_DBG("No qos ports to update!\n");
+ return;
+ }
+
+ if (info->node_info->num_mports == 0) {
+ MSM_BUS_DBG("BIMC: Skip Master BW\n");
+ goto skip_mas_bw;
+ }
+
+ bw = INTERLEAVED_BW(fab_pdata, add_bw, ports);
+ ports = INTERLEAVED_VAL(fab_pdata, ports);
+
+ for (i = 0; i < ports; i++) {
+ MSM_BUS_DBG("qport: %d\n", info->node_info->qport[i]);
+ sel_cd->mas[info->node_info->masterp[i]].bw += bw;
+ sel_cd->mas[info->node_info->masterp[i]].hw_id =
+ info->node_info->mas_hw_id;
+ qbw.bw = sel_cd->mas[info->node_info->masterp[i]].bw;
+ qbw.ws = info->node_info->ws;
+ /* Threshold low = 90% of bw */
+ qbw.thl = (90 * bw) / 100;
+ /* Threshold medium = bw */
+ qbw.thm = bw;
+ /* Threshold high = 10% more than bw */
+ qbw.thh = (110 * bw) / 100;
+ /* Check if info is a shared master.
+ * If it is, mark it dirty
+ * If it isn't, then set QOS Bandwidth
+ **/
+ MSM_BUS_DBG("BIMC: Update mas_bw for ID: %d -> %ld\n",
+ info->node_info->priv_id,
+ sel_cd->mas[info->node_info->masterp[i]].bw);
+ if (info->node_info->hw_sel == MSM_BUS_RPM)
+ sel_cd->mas[info->node_info->masterp[i]].dirty = 1;
+ else
+ msm_bus_bimc_set_qos_bw(binfo,
+ info->node_info->qport[i], &qbw);
+ }
+
+skip_mas_bw:
+ ports = hop->node_info->num_sports;
+ MSM_BUS_DBG("BIMC: ID: %d, Sports: %d\n", hop->node_info->priv_id,
+ ports);
+ if (ports)
+ bw = INTERLEAVED_BW(fab_pdata, add_bw, ports);
+ else
+ return;
+
+ for (i = 0; i < ports; i++) {
+ sel_cd->slv[hop->node_info->slavep[i]].bw += bw;
+ sel_cd->slv[hop->node_info->slavep[i]].hw_id =
+ hop->node_info->slv_hw_id;
+ MSM_BUS_DBG("BIMC: Update slave_bw: ID: %d -> %ld\n",
+ hop->node_info->priv_id,
+ sel_cd->slv[hop->node_info->slavep[i]].bw);
+ MSM_BUS_DBG("BIMC: Update slave_bw: index: %d\n",
+ hop->node_info->slavep[i]);
+ /* Check if hop is a shared slave.
+ * If it is, mark it dirty
+ * If it isn't, then nothing to be done as the
+ * slaves are in bypass mode.
+ **/
+ if (hop->node_info->hw_sel == MSM_BUS_RPM) {
+ MSM_BUS_DBG("Slave dirty: %d, slavep: %d\n",
+ hop->node_info->priv_id,
+ hop->node_info->slavep[i]);
+ sel_cd->slv[hop->node_info->slavep[i]].dirty = 1;
+ }
+ }
+}
+
+static int msm_bus_bimc_commit(struct msm_bus_fabric_registration
+ *fab_pdata, void *hw_data, void **cdata)
+{
+ MSM_BUS_DBG("\nReached BIMC Commit\n");
+ return 0;
+}
+
+static int msm_bus_bimc_mas_init(struct msm_bus_bimc_info *binfo,
+ struct msm_bus_inode_info *info)
+{
+ int i;
+ struct msm_bus_bimc_qos_mode *qmode;
+ qmode = kzalloc(sizeof(struct msm_bus_bimc_qos_mode),
+ GFP_KERNEL);
+ if (!qmode) {
+ MSM_BUS_WARN("Couldn't alloc prio data for node: %d\n",
+ info->node_info->id);
+ return -ENOMEM;
+ }
+
+ switch (info->node_info->mode) {
+ case BIMC_QOS_MODE_FIXED:
+ qmode->fixed.prio_level = info->node_info->prio_lvl;
+ qmode->fixed.areq_prio_rd = info->node_info->prio_rd;
+ qmode->fixed.areq_prio_wr = info->node_info->prio_wr;
+ break;
+ default:
+ break;
+ }
+
+ info->hw_data = (void *)qmode;
+ if (!info->node_info->qport) {
+ MSM_BUS_DBG("No QoS Ports to init\n");
+ return 0;
+ }
+
+ for (i = 0; i < info->node_info->num_mports; i++) {
+ /* If in bypass mode, update priority */
+ if (info->node_info->mode != BIMC_QOS_MODE_BYPASS)
+ msm_bus_bimc_set_qos_prio(binfo, info->node_info->
+ qport[i], info->node_info->mode, qmode);
+
+ /* If in fixed mode, update bandwidth */
+ if (info->node_info->mode != BIMC_QOS_MODE_FIXED) {
+ struct msm_bus_bimc_qos_bw qbw;
+ qbw.ws = info->node_info->ws;
+ msm_bus_bimc_set_qos_bw(binfo,
+ info->node_info->qport[i], &qbw);
+ }
+
+ /* set mode */
+ msm_bus_bimc_set_qos_mode(binfo, info->node_info->qport[i],
+ info->node_info->mode);
+ }
+
+ return 0;
+}
+
+static void msm_bus_bimc_node_init(void *hw_data,
+ struct msm_bus_inode_info *info)
+{
+ struct msm_bus_bimc_info *binfo =
+ (struct msm_bus_bimc_info *)hw_data;
+
+ if (!IS_SLAVE(info->node_info->priv_id))
+ msm_bus_bimc_mas_init(binfo, info);
+}
+
+static int msm_bus_bimc_port_halt(uint32_t haltid, uint8_t mport)
+{
+ return 0;
+}
+
+static int msm_bus_bimc_port_unhalt(uint32_t haltid, uint8_t mport)
+{
+ return 0;
+}
+
+
+int msm_bus_bimc_hw_init(struct msm_bus_fabric_registration *pdata,
+ struct msm_bus_hw_algorithm *hw_algo)
+{
+ /* Set interleaving to true by default */
+ MSM_BUS_DBG("\nInitializing BIMC...\n");
+ pdata->il_flag = true;
+ hw_algo->allocate_commit_data = msm_bus_bimc_allocate_commit_data;
+ hw_algo->allocate_hw_data = msm_bus_bimc_allocate_bimc_data;
+ hw_algo->node_init = msm_bus_bimc_node_init;
+ hw_algo->free_commit_data = free_commit_data;
+ hw_algo->update_bw = msm_bus_bimc_update_bw;
+ hw_algo->commit = msm_bus_bimc_commit;
+ hw_algo->port_halt = msm_bus_bimc_port_halt;
+ hw_algo->port_unhalt = msm_bus_bimc_port_unhalt;
+ /* BIMC slaves are shared. Slave registers are set through RPM */
+ if (!pdata->ahb)
+ pdata->rpm_enabled = 1;
+ return 0;
+}
+
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_bimc.h b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.h
new file mode 100644
index 0000000..249e8bb
--- /dev/null
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_bimc.h
@@ -0,0 +1,125 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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_BUS_BIMC_H
+#define _ARCH_ARM_MACH_MSM_BUS_BIMC_H
+
+struct msm_bus_bimc_params {
+ uint32_t bus_id;
+ uint32_t addr_width;
+ uint32_t data_width;
+ uint32_t nmasters;
+ uint32_t nslaves;
+};
+
+struct msm_bus_bimc_commit {
+ struct msm_bus_node_hw_info *mas;
+ struct msm_bus_node_hw_info *slv;
+};
+
+struct msm_bus_bimc_info {
+ void __iomem *base;
+ uint32_t base_addr;
+ uint32_t qos_freq;
+ struct msm_bus_bimc_params params;
+ struct msm_bus_bimc_commit cdata[NUM_CTX];
+};
+
+struct msm_bus_bimc_node {
+ uint32_t conn_mask;
+ uint32_t data_width;
+ uint8_t slv_arb_mode;
+};
+
+enum msm_bus_bimc_arb_mode {
+ BIMC_ARB_MODE_RR = 0,
+ BIMC_ARB_MODE_PRIORITY_RR,
+ BIMC_ARB_MODE_TIERED_RR,
+};
+
+
+enum msm_bus_bimc_interleave {
+ BIMC_INTERLEAVE_NONE = 0,
+ BIMC_INTERLEAVE_ODD,
+ BIMC_INTERLEAVE_EVEN,
+};
+
+struct msm_bus_bimc_slave_seg {
+ bool enable;
+ uint64_t start_addr;
+ uint64_t seg_size;
+ uint8_t interleave;
+};
+
+enum msm_bus_bimc_qos_mode_type {
+ BIMC_QOS_MODE_FIXED = 0,
+ BIMC_QOS_MODE_LIMITER,
+ BIMC_QOS_MODE_BYPASS,
+ BIMC_QOS_MODE_REGULATOR,
+};
+
+struct msm_bus_bimc_qos_health {
+ bool limit_commands;
+ uint32_t areq_prio;
+ uint32_t prio_level;
+};
+
+struct msm_bus_bimc_mode_fixed {
+ uint32_t prio_level;
+ uint32_t areq_prio_rd;
+ uint32_t areq_prio_wr;
+};
+
+struct msm_bus_bimc_mode_rl {
+ uint8_t qhealthnum;
+ struct msm_bus_bimc_qos_health qhealth[4];
+};
+
+struct msm_bus_bimc_qos_mode {
+ uint8_t mode;
+ struct msm_bus_bimc_mode_fixed fixed;
+ struct msm_bus_bimc_mode_rl rl;
+};
+
+struct msm_bus_bimc_qos_bw {
+ uint64_t bw; /* bw is in Bytes/sec */
+ uint32_t ws; /* Window size in nano seconds*/
+ uint64_t thh; /* Threshold high, bytes per second */
+ uint64_t thm; /* Threshold medium, bytes per second */
+ uint64_t thl; /* Threshold low, bytes per second */
+};
+
+struct msm_bus_bimc_clk_gate {
+ bool core_clk_gate_en;
+ bool arb_clk_gate_en; /* For arbiter */
+ bool port_clk_gate_en; /* For regs on BIMC core clock */
+};
+
+void msm_bus_bimc_set_slave_seg(struct msm_bus_bimc_info *binfo,
+ uint32_t slv_index, uint32_t seg_index,
+ struct msm_bus_bimc_slave_seg *bsseg);
+void msm_bus_bimc_set_slave_clk_gate(struct msm_bus_bimc_info *binfo,
+ uint32_t slv_index, struct msm_bus_bimc_clk_gate *bgate);
+void msm_bus_bimc_set_mas_clk_gate(struct msm_bus_bimc_info *binfo,
+ uint32_t mas_index, struct msm_bus_bimc_clk_gate *bgate);
+void msm_bus_bimc_arb_en(struct msm_bus_bimc_info *binfo,
+ uint32_t slv_index, bool en);
+void msm_bus_bimc_get_params(struct msm_bus_bimc_info *binfo,
+ struct msm_bus_bimc_params *params);
+void msm_bus_bimc_get_mas_params(struct msm_bus_bimc_info *binfo,
+ uint32_t mas_index, struct msm_bus_bimc_node *mparams);
+void msm_bus_bimc_get_slv_params(struct msm_bus_bimc_info *binfo,
+ uint32_t slv_index, struct msm_bus_bimc_node *sparams);
+bool msm_bus_bimc_get_arb_en(struct msm_bus_bimc_info *binfo,
+ uint32_t slv_index);
+
+#endif /*_ARCH_ARM_MACH_MSM_BUS_BIMC_H*/
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_core.h b/arch/arm/mach-msm/msm_bus/msm_bus_core.h
index 341fda8..a9d6e4f 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_core.h
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_core.h
@@ -46,13 +46,22 @@
MSM_BUS_DBG_OP = 1,
};
+enum msm_bus_hw_sel {
+ MSM_BUS_RPM = 0,
+ MSM_BUS_NOC,
+ MSM_BUS_BIMC,
+};
+
extern struct bus_type msm_bus_type;
struct msm_bus_node_info {
unsigned int id;
unsigned int priv_id;
+ unsigned int mas_hw_id;
+ unsigned int slv_hw_id;
int gateway;
int *masterp;
+ int *qport;
int num_mports;
int *slavep;
int num_sports;
@@ -65,6 +74,12 @@
unsigned int buswidth;
unsigned int ws;
unsigned int mode;
+ unsigned int perm_mode;
+ unsigned int prio_lvl;
+ unsigned int prio_rd;
+ unsigned int prio_wr;
+ unsigned int prio1;
+ unsigned int prio0;
};
struct path_node {
@@ -105,6 +120,12 @@
void *hw_data;
};
+struct msm_bus_node_hw_info {
+ bool dirty;
+ unsigned int hw_id;
+ unsigned long bw;
+};
+
struct msm_bus_hw_algorithm {
int (*allocate_commit_data)(struct msm_bus_fabric_registration
*fab_pdata, void **cdata, int ctx);
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
index 8c015d1..3671916 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
@@ -594,12 +594,22 @@
struct msm_bus_hw_algorithm *hw_algo)
{
int ret = 0;
- ret = msm_bus_rpm_hw_init(pdata, hw_algo);
- if (ret) {
- MSM_BUS_ERR("RPM initialization failed\n");
- ret = -EINVAL;
- }
+ switch (pdata->hw_sel) {
+ case MSM_BUS_NOC:
+ msm_bus_noc_hw_init(pdata, hw_algo);
+ break;
+ case MSM_BUS_BIMC:
+ msm_bus_bimc_hw_init(pdata, hw_algo);
+ break;
+ default:
+ ret = msm_bus_rpm_hw_init(pdata, hw_algo);
+ if (ret) {
+ MSM_BUS_ERR("RPM initialization failed\n");
+ ret = -EINVAL;
+ }
+ break;
+ }
return ret;
}
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_noc.c b/arch/arm/mach-msm/msm_bus/msm_bus_noc.c
new file mode 100644
index 0000000..5179d2a
--- /dev/null
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_noc.c
@@ -0,0 +1,613 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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) "AXI: NOC: %s(): " fmt, __func__
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <mach/msm_bus_board.h>
+#include "msm_bus_core.h"
+#include "msm_bus_noc.h"
+
+/* NOC_QOS generic */
+#define __CLZ(x) ((8 * sizeof(uint32_t)) - 1 - __fls(x))
+#define SAT_SCALE 16 /* 16 bytes minimum for saturation */
+#define BW_SCALE 256 /* 1/256 byte per cycle unit */
+#define MAX_BW_FIELD (NOC_QOS_BWn_BW_BMSK >> NOC_QOS_BWn_BW_SHFT)
+#define MAX_SAT_FIELD (NOC_QOS_SATn_SAT_BMSK >> NOC_QOS_SATn_SAT_SHFT)
+
+#define NOC_QOS_REG_BASE(b) ((b) + 0x00003000)
+
+#define NOC_QOS_ID_COREIDn_ADDR(b, n) (NOC_QOS_REG_BASE(b) + 0x80 * (n))
+enum noc_qos_id_coreidn {
+ NOC_QOS_ID_COREIDn_RMSK = 0xffffffff,
+ NOC_QOS_ID_COREIDn_MAXn = 32,
+ NOC_QOS_ID_COREIDn_CORECHSUM_BMSK = 0xffffff00,
+ NOC_QOS_ID_COREIDn_CORECHSUM_SHFT = 0x8,
+ NOC_QOS_ID_COREIDn_CORETYPEID_BMSK = 0xff,
+ NOC_QOS_ID_COREIDn_CORETYPEID_SHFT = 0x0,
+};
+
+#define NOC_QOS_ID_REVISIONIDn_ADDR(b, n) \
+ (NOC_QOS_REG_BASE(b) + 0x4 + 0x80 * (n))
+enum noc_qos_id_revisionidn {
+ NOC_QOS_ID_REVISIONIDn_RMSK = 0xffffffff,
+ NOC_QOS_ID_REVISIONIDn_MAXn = 32,
+ NOC_QOS_ID_REVISIONIDn_FLEXNOCID_BMSK = 0xffffff00,
+ NOC_QOS_ID_REVISIONIDn_FLEXNOCID_SHFT = 0x8,
+ NOC_QOS_ID_REVISIONIDn_USERID_BMSK = 0xff,
+ NOC_QOS_ID_REVISIONIDn_USERID_SHFT = 0x0,
+};
+
+#define NOC_QOS_PRIORITYn_ADDR(b, n) \
+ (NOC_QOS_REG_BASE(b) + 0x8 + 0x80 * (n))
+enum noc_qos_id_priorityn {
+ NOC_QOS_PRIORITYn_RMSK = 0x0000000f,
+ NOC_QOS_PRIORITYn_MAXn = 32,
+ NOC_QOS_PRIORITYn_P1_BMSK = 0xc,
+ NOC_QOS_PRIORITYn_P1_SHFT = 0x2,
+ NOC_QOS_PRIORITYn_P0_BMSK = 0x3,
+ NOC_QOS_PRIORITYn_P0_SHFT = 0x0,
+};
+
+#define NOC_QOS_MODEn_ADDR(b, n) \
+ (NOC_QOS_REG_BASE(b) + 0xC + 0x80 * (n))
+enum noc_qos_id_moden_rmsk {
+ NOC_QOS_MODEn_RMSK = 0x00000003,
+ NOC_QOS_MODEn_MAXn = 32,
+ NOC_QOS_MODEn_MODE_BMSK = 0x3,
+ NOC_QOS_MODEn_MODE_SHFT = 0x0,
+};
+
+#define NOC_QOS_BWn_ADDR(b, n) \
+ (NOC_QOS_REG_BASE(b) + 0x10 + 0x80 * (n))
+enum noc_qos_id_bwn {
+ NOC_QOS_BWn_RMSK = 0x0000ffff,
+ NOC_QOS_BWn_MAXn = 32,
+ NOC_QOS_BWn_BW_BMSK = 0xffff,
+ NOC_QOS_BWn_BW_SHFT = 0x0,
+};
+
+/* QOS Saturation registers */
+#define NOC_QOS_SATn_ADDR(b, n) \
+ (NOC_QOS_REG_BASE(b) + 0x14 + 0x80 * (n))
+enum noc_qos_id_saturationn {
+ NOC_QOS_SATn_RMSK = 0x000003ff,
+ NOC_QOS_SATn_MAXn = 32,
+ NOC_QOS_SATn_SAT_BMSK = 0x3ff,
+ NOC_QOS_SATn_SAT_SHFT = 0x0,
+};
+
+static int noc_div(uint64_t *a, uint32_t b)
+{
+ if ((*a > 0) && (*a < b))
+ return 1;
+ else
+ return do_div(*a, b);
+}
+
+/**
+ * Calculates bw hardware is using from register values
+ * bw returned is in bytes/sec
+ */
+static uint64_t noc_bw(uint32_t bw_field, uint32_t qos_freq)
+{
+ uint64_t res;
+ uint32_t rem, scale;
+
+ res = 2 * qos_freq * bw_field;
+ scale = BW_SCALE * 1000;
+ rem = noc_div(&res, scale);
+ MSM_BUS_DBG("NOC: Calculated bw: %llu\n", res * 1000000ULL);
+ return res * 1000000ULL;
+}
+
+static uint32_t noc_bw_ceil(long int bw_field, uint32_t qos_freq)
+{
+ uint64_t bw_temp = 2 * qos_freq * bw_field;
+ uint32_t scale = 1000 * BW_SCALE;
+ noc_div(&bw_temp, scale);
+ return bw_temp * 1000000;
+}
+#define MAX_BW(timebase) noc_bw_ceil(MAX_BW_FIELD, (timebase))
+
+/**
+ * Calculates ws hardware is using from register values
+ * ws returned is in nanoseconds
+ */
+static uint32_t noc_ws(uint64_t bw, uint32_t sat, uint32_t qos_freq)
+{
+ if (bw && qos_freq) {
+ uint32_t bwf = bw * qos_freq;
+ uint64_t scale = 1000000000000LL * BW_SCALE *
+ SAT_SCALE * sat;
+ noc_div(&scale, bwf);
+ MSM_BUS_DBG("NOC: Calculated ws: %llu\n", scale);
+ return scale;
+ }
+
+ return 0;
+}
+#define MAX_WS(bw, timebase) noc_ws((bw), MAX_SAT_FIELD, (timebase))
+
+/* Calculate bandwidth field value for requested bandwidth */
+static uint32_t noc_bw_field(uint64_t bw, uint32_t qos_freq)
+{
+ uint32_t bw_field = 0;
+
+ if (bw) {
+ uint32_t rem;
+ uint64_t bw_capped = min_t(uint64_t, bw, MAX_BW(qos_freq));
+ uint64_t bwc = bw_capped * BW_SCALE;
+ uint64_t qf = 2 * qos_freq * 1000;
+
+ rem = noc_div(&bwc, qf);
+ bw_field = (uint32_t)min_t(uint64_t, bwc, MAX_BW_FIELD);
+ }
+
+ MSM_BUS_DBG("NOC: bw_field: %u\n", bw_field);
+ return bw_field;
+}
+
+static uint32_t noc_sat_field(uint64_t bw, uint32_t ws, uint32_t qos_freq)
+{
+ uint32_t sat_field = 0, win;
+
+ if (bw) {
+ /* Limit to max bw and scale bw to 100 KB increments */
+ uint64_t tbw, tscale;
+ uint64_t bw_scaled = min_t(uint64_t, bw, MAX_BW(qos_freq));
+ uint32_t rem = noc_div(&bw_scaled, 100000);
+
+ /**
+ * Calculate saturation from windows size.
+ * WS must be at least one arb period.
+ * Saturation must not exceed max field size
+ *
+ * Bandwidth is in 100KB increments
+ * Window size is in ns
+ * qos_freq is in KHz
+ */
+ win = max(ws, 1000000 / qos_freq);
+ tbw = bw_scaled * win * qos_freq;
+ tscale = 10000000ULL * BW_SCALE * SAT_SCALE;
+ rem = noc_div(&tbw, tscale);
+ sat_field = (uint32_t)min_t(uint64_t, tbw, MAX_SAT_FIELD);
+ }
+
+ MSM_BUS_DBG("NOC: sat_field: %d\n", sat_field);
+ return sat_field;
+}
+
+static void noc_set_qos_mode(struct msm_bus_noc_info *ninfo, uint32_t mport,
+ uint8_t mode)
+{
+ if (mode < NOC_QOS_MODE_MAX &&
+ ((1 << mode) & ninfo->mas_modes[mport])) {
+ uint32_t reg_val;
+
+ reg_val = readl_relaxed(NOC_QOS_MODEn_ADDR(ninfo->base,
+ mport)) & NOC_QOS_MODEn_RMSK;
+ writel_relaxed(((reg_val & (~(NOC_QOS_MODEn_MODE_BMSK))) |
+ (mode & NOC_QOS_MODEn_MODE_BMSK)),
+ NOC_QOS_MODEn_ADDR(ninfo->base, mport));
+ }
+ /* Ensure qos mode is set before exiting */
+ wmb();
+}
+
+static void noc_set_qos_priority(struct msm_bus_noc_info *ninfo, uint32_t mport,
+ struct msm_bus_noc_qos_priority *priority)
+{
+ uint32_t reg_val, val;
+
+ reg_val = readl_relaxed(NOC_QOS_PRIORITYn_ADDR(ninfo->base, mport))
+ & NOC_QOS_PRIORITYn_RMSK;
+ val = priority->p1 << NOC_QOS_PRIORITYn_P1_SHFT;
+ writel_relaxed(((reg_val & (~(NOC_QOS_PRIORITYn_P1_BMSK))) |
+ (val & NOC_QOS_PRIORITYn_P1_BMSK)),
+ NOC_QOS_PRIORITYn_ADDR(ninfo->base, mport));
+
+ reg_val = readl_relaxed(NOC_QOS_PRIORITYn_ADDR(ninfo->base, mport))
+ & NOC_QOS_PRIORITYn_RMSK;
+ writel_relaxed(((reg_val & (~(NOC_QOS_PRIORITYn_P0_BMSK))) |
+ (priority->p0 & NOC_QOS_PRIORITYn_P0_BMSK)),
+ NOC_QOS_PRIORITYn_ADDR(ninfo->base, mport));
+ /* Ensure qos priority is set before exiting */
+ wmb();
+}
+
+static void msm_bus_noc_set_qos_bw(struct msm_bus_noc_info *ninfo,
+ uint32_t mport, uint8_t perm_mode, struct msm_bus_noc_qos_bw *qbw)
+{
+ uint32_t reg_val, val, mode;
+
+ if (!ninfo->qos_freq) {
+ MSM_BUS_DBG("Zero QoS Freq\n");
+ return;
+ }
+
+
+ /* If Limiter or Regulator modes are not supported, bw not available*/
+ if (perm_mode & (NOC_QOS_PERM_MODE_LIMITER |
+ NOC_QOS_PERM_MODE_REGULATOR)) {
+ uint32_t bw_val = noc_bw_field(qbw->bw, ninfo->qos_freq);
+ uint32_t sat_val = noc_sat_field(qbw->bw, qbw->ws,
+ ninfo->qos_freq);
+
+ MSM_BUS_DBG("NOC: BW: perm_mode: %d bw_val: %d, sat_val: %d\n",
+ perm_mode, bw_val, sat_val);
+ /*
+ * If in Limiter/Regulator mode, first go to fixed mode.
+ * Clear QoS accumulator
+ **/
+ mode = readl_relaxed(NOC_QOS_MODEn_ADDR(ninfo->base,
+ mport)) & NOC_QOS_MODEn_MODE_BMSK;
+ if (mode == NOC_QOS_MODE_REGULATOR || mode ==
+ NOC_QOS_MODE_LIMITER) {
+ reg_val = readl_relaxed(NOC_QOS_MODEn_ADDR(ninfo->
+ base, mport));
+ val = NOC_QOS_MODE_FIXED;
+ writel_relaxed((reg_val & (~(NOC_QOS_MODEn_MODE_BMSK)))
+ | (val & NOC_QOS_MODEn_MODE_BMSK),
+ NOC_QOS_MODEn_ADDR(ninfo->base, mport));
+ }
+
+ reg_val = readl_relaxed(NOC_QOS_BWn_ADDR(ninfo->base, mport));
+ val = bw_val << NOC_QOS_BWn_BW_SHFT;
+ writel_relaxed(((reg_val & (~(NOC_QOS_BWn_BW_BMSK))) |
+ (val & NOC_QOS_BWn_BW_BMSK)),
+ NOC_QOS_BWn_ADDR(ninfo->base, mport));
+
+ MSM_BUS_DBG("NOC: BW: Wrote value: 0x%x\n", ((reg_val &
+ (~NOC_QOS_BWn_BW_BMSK)) | (val &
+ NOC_QOS_BWn_BW_BMSK)));
+
+ reg_val = readl_relaxed(NOC_QOS_SATn_ADDR(ninfo->base,
+ mport));
+ val = sat_val << NOC_QOS_SATn_SAT_SHFT;
+ writel_relaxed(((reg_val & (~(NOC_QOS_SATn_SAT_BMSK))) |
+ (val & NOC_QOS_SATn_SAT_BMSK)),
+ NOC_QOS_SATn_ADDR(ninfo->base, mport));
+
+ MSM_BUS_DBG("NOC: SAT: Wrote value: 0x%x\n", ((reg_val &
+ (~NOC_QOS_SATn_SAT_BMSK)) | (val &
+ NOC_QOS_SATn_SAT_BMSK)));
+
+ /* Set mode back to what it was initially */
+ reg_val = readl_relaxed(NOC_QOS_MODEn_ADDR(ninfo->base,
+ mport));
+ writel_relaxed((reg_val & (~(NOC_QOS_MODEn_MODE_BMSK)))
+ | (mode & NOC_QOS_MODEn_MODE_BMSK),
+ NOC_QOS_MODEn_ADDR(ninfo->base, mport));
+ /* Ensure that all writes for bandwidth registers have
+ * completed before returning
+ */
+ wmb();
+ }
+}
+
+uint8_t msm_bus_noc_get_qos_mode(struct msm_bus_noc_info *ninfo,
+ uint32_t mport)
+{
+ if (NOC_QOS_MODES_ALL_PERM == ninfo->mas_modes[mport])
+ return readl_relaxed(NOC_QOS_MODEn_ADDR(ninfo->base,
+ mport)) & NOC_QOS_MODEn_MODE_BMSK;
+ else
+ return 31 - __CLZ(ninfo->mas_modes[mport] &
+ NOC_QOS_MODES_ALL_PERM);
+}
+
+void msm_bus_noc_get_qos_priority(struct msm_bus_noc_info *ninfo,
+ uint32_t mport, struct msm_bus_noc_qos_priority *priority)
+{
+ priority->p1 = (readl_relaxed(NOC_QOS_PRIORITYn_ADDR(ninfo->base,
+ mport)) & NOC_QOS_PRIORITYn_P1_BMSK) >>
+ NOC_QOS_PRIORITYn_P1_SHFT;
+
+ priority->p0 = (readl_relaxed(NOC_QOS_PRIORITYn_ADDR(ninfo->base,
+ mport)) & NOC_QOS_PRIORITYn_P0_BMSK) >>
+ NOC_QOS_PRIORITYn_P0_SHFT;
+}
+
+void msm_bus_noc_get_qos_bw(struct msm_bus_noc_info *ninfo,
+ uint32_t mport, struct msm_bus_noc_qos_bw *qbw)
+{
+ if (ninfo->mas_modes[mport] & (NOC_QOS_PERM_MODE_LIMITER |
+ NOC_QOS_PERM_MODE_REGULATOR)) {
+ uint32_t bw_val = readl_relaxed(NOC_QOS_BWn_ADDR(ninfo->
+ base, mport)) & NOC_QOS_BWn_BW_BMSK;
+ uint32_t sat = readl_relaxed(NOC_QOS_SATn_ADDR(ninfo->
+ base, mport)) & NOC_QOS_SATn_SAT_BMSK;
+
+ qbw->bw = noc_bw(bw_val, ninfo->qos_freq);
+ qbw->ws = noc_ws(qbw->bw, sat, ninfo->qos_freq);
+ } else {
+ qbw->bw = 0;
+ qbw->ws = 0;
+ }
+}
+
+static int msm_bus_noc_mas_init(struct msm_bus_noc_info *ninfo,
+ struct msm_bus_inode_info *info)
+{
+ int i;
+ struct msm_bus_noc_qos_priority *prio;
+ prio = kzalloc(sizeof(struct msm_bus_noc_qos_priority),
+ GFP_KERNEL);
+ if (!prio) {
+ MSM_BUS_WARN("Couldn't alloc prio data for node: %d\n",
+ info->node_info->id);
+ return -ENOMEM;
+ }
+
+ prio->read_prio = info->node_info->prio_rd;
+ prio->write_prio = info->node_info->prio_wr;
+ prio->p1 = info->node_info->prio1;
+ prio->p0 = info->node_info->prio0;
+ info->hw_data = (void *)prio;
+
+ if (!info->node_info->qport) {
+ MSM_BUS_DBG("No QoS Ports to init\n");
+ return 0;
+ }
+
+ for (i = 0; i < info->node_info->num_mports; i++) {
+ if (info->node_info->mode != NOC_QOS_MODE_BYPASS)
+ noc_set_qos_priority(ninfo, info->node_info->qport[i],
+ prio);
+
+ if (info->node_info->mode != NOC_QOS_MODE_FIXED) {
+ struct msm_bus_noc_qos_bw qbw;
+ qbw.ws = info->node_info->ws;
+ qbw.bw = 0;
+ msm_bus_noc_set_qos_bw(ninfo, info->node_info->qport[i],
+ info->node_info->perm_mode, &qbw);
+ }
+
+ noc_set_qos_mode(ninfo, info->node_info->qport[i], info->
+ node_info->mode);
+ }
+
+ return 0;
+}
+
+static void msm_bus_noc_node_init(void *hw_data,
+ struct msm_bus_inode_info *info)
+{
+ struct msm_bus_noc_info *ninfo =
+ (struct msm_bus_noc_info *)hw_data;
+
+ if (!IS_SLAVE(info->node_info->priv_id))
+ msm_bus_noc_mas_init(ninfo, info);
+}
+
+static int msm_bus_noc_allocate_commit_data(struct msm_bus_fabric_registration
+ *fab_pdata, void **cdata, int ctx)
+{
+ struct msm_bus_noc_commit **cd = (struct msm_bus_noc_commit **)cdata;
+ struct msm_bus_noc_info *ninfo =
+ (struct msm_bus_noc_info *)fab_pdata->hw_data;
+
+ *cd = kzalloc(sizeof(struct msm_bus_noc_commit), GFP_KERNEL);
+ if (!*cd) {
+ MSM_BUS_DBG("Couldn't alloc mem for cdata\n");
+ return -ENOMEM;
+ }
+
+ (*cd)->mas = ninfo->cdata[ctx].mas;
+ (*cd)->slv = ninfo->cdata[ctx].slv;
+
+ return 0;
+}
+
+static void *msm_bus_noc_allocate_noc_data(struct platform_device *pdev,
+ struct msm_bus_fabric_registration *fab_pdata)
+{
+ struct resource *noc_mem;
+ struct resource *noc_io;
+ struct msm_bus_noc_info *ninfo;
+ int i;
+
+ ninfo = kzalloc(sizeof(struct msm_bus_noc_info), GFP_KERNEL);
+ if (!ninfo) {
+ MSM_BUS_DBG("Couldn't alloc mem for noc info\n");
+ return NULL;
+ }
+
+ ninfo->nmasters = fab_pdata->nmasters;
+ ninfo->nqos_masters = fab_pdata->nmasters;
+ ninfo->nslaves = fab_pdata->nslaves;
+ ninfo->qos_freq = fab_pdata->qos_freq;
+ ninfo->mas_modes = kzalloc(sizeof(uint32_t) * fab_pdata->nmasters,
+ GFP_KERNEL);
+ if (!ninfo->mas_modes) {
+ MSM_BUS_DBG("Couldn't alloc mem for noc master-modes\n");
+ return NULL;
+ }
+
+ for (i = 0; i < NUM_CTX; i++) {
+ ninfo->cdata[i].mas = kzalloc(sizeof(struct
+ msm_bus_node_hw_info) * fab_pdata->nmasters * 2,
+ GFP_KERNEL);
+ if (!ninfo->cdata[i].mas) {
+ MSM_BUS_DBG("Couldn't alloc mem for noc master-bw\n");
+ kfree(ninfo->mas_modes);
+ kfree(ninfo);
+ return NULL;
+ }
+
+ ninfo->cdata[i].slv = kzalloc(sizeof(struct
+ msm_bus_node_hw_info) * fab_pdata->nslaves * 2,
+ GFP_KERNEL);
+ if (!ninfo->cdata[i].slv) {
+ MSM_BUS_DBG("Couldn't alloc mem for noc master-bw\n");
+ kfree(ninfo->cdata[i].mas);
+ goto err;
+ }
+ }
+
+ /* If it's a virtual fabric, don't get memory info */
+ if (fab_pdata->virt)
+ goto skip_mem;
+
+ noc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!noc_mem && !fab_pdata->virt) {
+ MSM_BUS_ERR("Cannot get NoC Base address\n");
+ goto err;
+ }
+
+ noc_io = request_mem_region(noc_mem->start,
+ resource_size(noc_mem), pdev->name);
+ if (!noc_io) {
+ MSM_BUS_ERR("NoC memory unavailable\n");
+ goto err;
+ }
+
+ ninfo->base = ioremap(noc_mem->start, resource_size(noc_mem));
+ if (!ninfo->base) {
+ MSM_BUS_ERR("IOremap failed for NoC!\n");
+ release_mem_region(noc_mem->start, resource_size(noc_mem));
+ goto err;
+ }
+
+skip_mem:
+ fab_pdata->hw_data = (void *)ninfo;
+ return (void *)ninfo;
+
+err:
+ kfree(ninfo->mas_modes);
+ kfree(ninfo);
+ return NULL;
+}
+
+static void free_commit_data(void *cdata)
+{
+ struct msm_bus_noc_commit *cd = (struct msm_bus_noc_commit *)cdata;
+
+ kfree(cd->mas);
+ kfree(cd->slv);
+ kfree(cd);
+}
+
+static void msm_bus_noc_update_bw(struct msm_bus_inode_info *hop,
+ struct msm_bus_inode_info *info,
+ struct msm_bus_fabric_registration *fab_pdata,
+ void *sel_cdata, int *master_tiers,
+ long int add_bw)
+{
+ struct msm_bus_noc_info *ninfo;
+ struct msm_bus_noc_qos_bw qos_bw;
+ int i, ports;
+ long int bw;
+ struct msm_bus_noc_commit *sel_cd =
+ (struct msm_bus_noc_commit *)sel_cdata;
+
+ ninfo = (struct msm_bus_noc_info *)fab_pdata->hw_data;
+ if (!ninfo->qos_freq) {
+ MSM_BUS_DBG("NOC: No qos frequency to update bw\n");
+ return;
+ }
+
+ if (!info->node_info->qport) {
+ MSM_BUS_DBG("NOC: No QoS Ports to update bw\n");
+ return;
+ }
+
+ ports = info->node_info->num_mports;
+ qos_bw.ws = info->node_info->ws;
+
+ bw = INTERLEAVED_BW(fab_pdata, add_bw, ports);
+
+ MSM_BUS_DBG("NOC: Update bw for: %d: %ld\n",
+ info->node_info->priv_id, add_bw);
+ for (i = 0; i < ports; i++) {
+ sel_cd->mas[info->node_info->masterp[i]].bw += bw;
+ sel_cd->mas[info->node_info->masterp[i]].hw_id =
+ info->node_info->mas_hw_id;
+ qos_bw.bw = sel_cd->mas[info->node_info->masterp[i]].bw;
+ MSM_BUS_DBG("NOC: Update mas_bw for ID: %d, BW: %ld, QoS: %u\n",
+ info->node_info->priv_id,
+ sel_cd->mas[info->node_info->masterp[i]].bw,
+ qos_bw.ws);
+ /* Check if info is a shared master.
+ * If it is, mark it dirty
+ * If it isn't, then set QOS Bandwidth
+ **/
+ if (info->node_info->hw_sel == MSM_BUS_RPM)
+ sel_cd->mas[info->node_info->masterp[i]].dirty = 1;
+ else
+ msm_bus_noc_set_qos_bw(ninfo,
+ info->node_info->qport[i],
+ info->node_info->perm_mode, &qos_bw);
+ }
+
+ ports = hop->node_info->num_sports;
+ if (ports == 0) {
+ MSM_BUS_ERR("\nDIVIDE BY 0, hop: %d\n",
+ hop->node_info->priv_id);
+ return;
+ }
+ bw = INTERLEAVED_BW(fab_pdata, add_bw, ports);
+ for (i = 0; i < ports; i++) {
+ sel_cd->slv[hop->node_info->slavep[i]].bw += bw;
+ sel_cd->slv[hop->node_info->slavep[i]].hw_id =
+ hop->node_info->slv_hw_id;
+ MSM_BUS_DBG("NOC: Update slave_bw for ID: %d -> %ld\n",
+ hop->node_info->priv_id,
+ sel_cd->slv[hop->node_info->slavep[i]].bw);
+ MSM_BUS_DBG("NOC: Update slave_bw for hw_id: %d, index: %d\n",
+ hop->node_info->slv_hw_id, hop->node_info->slavep[i]);
+ /* Check if hop is a shared slave.
+ * If it is, mark it dirty
+ * If it isn't, then nothing to be done as the
+ * slaves are in bypass mode.
+ **/
+ if (hop->node_info->hw_sel == MSM_BUS_RPM)
+ sel_cd->slv[hop->node_info->slavep[i]].dirty = 1;
+ }
+}
+
+static int msm_bus_noc_commit(struct msm_bus_fabric_registration
+ *fab_pdata, void *hw_data, void **cdata)
+{
+ MSM_BUS_DBG("\nReached NOC Commit\n");
+ return 0;
+}
+
+static int msm_bus_noc_port_halt(uint32_t haltid, uint8_t mport)
+{
+ return 0;
+}
+
+static int msm_bus_noc_port_unhalt(uint32_t haltid, uint8_t mport)
+{
+ return 0;
+}
+
+int msm_bus_noc_hw_init(struct msm_bus_fabric_registration *pdata,
+ struct msm_bus_hw_algorithm *hw_algo)
+{
+ /* Set interleaving to true by default */
+ pdata->il_flag = true;
+ hw_algo->allocate_commit_data = msm_bus_noc_allocate_commit_data;
+ hw_algo->allocate_hw_data = msm_bus_noc_allocate_noc_data;
+ hw_algo->node_init = msm_bus_noc_node_init;
+ hw_algo->free_commit_data = free_commit_data;
+ hw_algo->update_bw = msm_bus_noc_update_bw;
+ hw_algo->commit = msm_bus_noc_commit;
+ hw_algo->port_halt = msm_bus_noc_port_halt;
+ hw_algo->port_unhalt = msm_bus_noc_port_unhalt;
+
+ return 0;
+}
+
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_noc.h b/arch/arm/mach-msm/msm_bus/msm_bus_noc.h
new file mode 100644
index 0000000..407d3ec
--- /dev/null
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_noc.h
@@ -0,0 +1,73 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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_BUS_BIMC_H
+#define _ARCH_ARM_MACH_MSM_BUS_BIMC_H
+
+enum msm_bus_noc_qos_mode_type {
+ NOC_QOS_MODE_FIXED = 0,
+ NOC_QOS_MODE_LIMITER,
+ NOC_QOS_MODE_BYPASS,
+ NOC_QOS_MODE_REGULATOR,
+ NOC_QOS_MODE_MAX,
+};
+
+enum msm_bus_noc_qos_mode_perm {
+ NOC_QOS_PERM_MODE_FIXED = (1 << NOC_QOS_MODE_FIXED),
+ NOC_QOS_PERM_MODE_LIMITER = (1 << NOC_QOS_MODE_LIMITER),
+ NOC_QOS_PERM_MODE_BYPASS = (1 << NOC_QOS_MODE_BYPASS),
+ NOC_QOS_PERM_MODE_REGULATOR = (1 << NOC_QOS_MODE_REGULATOR),
+};
+
+#define NOC_QOS_MODES_ALL_PERM (NOC_QOS_PERM_MODE_FIXED | \
+ NOC_QOS_PERM_MODE_LIMITER | NOC_QOS_PERM_MODE_BYPASS | \
+ NOC_QOS_PERM_MODE_REGULATOR)
+
+struct msm_bus_noc_commit {
+ struct msm_bus_node_hw_info *mas;
+ struct msm_bus_node_hw_info *slv;
+};
+
+struct msm_bus_noc_info {
+ void __iomem *base;
+ uint32_t base_addr;
+ uint32_t nmasters;
+ uint32_t nqos_masters;
+ uint32_t nslaves;
+ uint32_t qos_freq; /* QOS Clock in KHz */
+ uint32_t *mas_modes;
+ struct msm_bus_noc_commit cdata[NUM_CTX];
+};
+
+struct msm_bus_noc_qos_priority {
+ uint32_t high_prio;
+ uint32_t low_prio;
+ uint32_t read_prio;
+ uint32_t write_prio;
+ uint32_t p1;
+ uint32_t p0;
+};
+
+struct msm_bus_noc_qos_bw {
+ uint64_t bw; /* Bandwidth in bytes per second */
+ uint32_t ws; /* Window size in nano seconds */
+};
+
+void msm_bus_noc_init(struct msm_bus_noc_info *ninfo);
+uint8_t msm_bus_noc_get_qos_mode(struct msm_bus_noc_info *ninfo,
+ uint32_t mport);
+void msm_bus_noc_get_qos_priority(struct msm_bus_noc_info *ninfo,
+ uint32_t mport, struct msm_bus_noc_qos_priority *qprio);
+void msm_bus_noc_get_qos_bw(struct msm_bus_noc_info *ninfo,
+ uint32_t mport, struct msm_bus_noc_qos_bw *qbw);
+
+#endif /*_ARCH_ARM_MACH_MSM_BUS_NOC_H */
diff --git a/arch/arm/mach-msm/ocmem.c b/arch/arm/mach-msm/ocmem.c
index ae1d6f2..43c7fc8 100644
--- a/arch/arm/mach-msm/ocmem.c
+++ b/arch/arm/mach-msm/ocmem.c
@@ -23,6 +23,15 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+/* This code is to temporarily work around the default state of OCMEM
+ regions in Virtio. These registers will be read from DT in a subsequent
+ patch which initializes the regions to appropriate default state.
+*/
+
+#define OCMEM_REGION_CTL_BASE 0xFDD0003C
+#define OCMEM_REGION_CTL_SIZE 0xC
+#define REGION_ENABLE 0x00003333
+
struct ocmem_partition {
const char *name;
int id;
@@ -271,6 +280,7 @@
static int __devinit msm_ocmem_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ void *ocmem_region_vbase = NULL;
if (!pdev->dev.of_node->child) {
dev_info(dev, "Missing Configuration in Device Tree\n");
@@ -297,6 +307,15 @@
if (ocmem_sched_init())
return -EBUSY;
+
+ ocmem_region_vbase = devm_ioremap_nocache(dev, OCMEM_REGION_CTL_BASE,
+ OCMEM_REGION_CTL_SIZE);
+ if (!ocmem_region_vbase)
+ return -EBUSY;
+ /* Enable all the 3 regions until we have support for power features */
+ writel_relaxed(REGION_ENABLE, ocmem_region_vbase);
+ writel_relaxed(REGION_ENABLE, ocmem_region_vbase + 4);
+ writel_relaxed(REGION_ENABLE, ocmem_region_vbase + 8);
dev_info(dev, "initialized successfully\n");
return 0;
}
diff --git a/arch/arm/mach-msm/perf_event_msm_krait_l2.c b/arch/arm/mach-msm/perf_event_msm_krait_l2.c
new file mode 100644
index 0000000..d82f4dd
--- /dev/null
+++ b/arch/arm/mach-msm/perf_event_msm_krait_l2.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2011, 2012 Code Aurora Forum. 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/irq.h>
+#include <asm/pmu.h>
+#include <linux/platform_device.h>
+
+#include <mach/msm-krait-l2-accessors.h>
+
+#define MAX_L2_PERIOD ((1ULL << 32) - 1)
+#define MAX_KRAIT_L2_CTRS 5
+
+#define L2PMCCNTR 0x409
+#define L2PMCCNTCR 0x408
+#define L2PMCCNTSR 0x40A
+#define L2CYCLE_CTR_BIT 31
+#define L2CYCLE_CTR_EVENT_IDX 4
+#define L2CYCLE_CTR_RAW_CODE 0xfe
+
+#define L2PMOVSR 0x406
+
+#define L2PMCR 0x400
+#define L2PMCR_RESET_ALL 0x6
+#define L2PMCR_GLOBAL_ENABLE 0x1
+#define L2PMCR_GLOBAL_DISABLE 0x0
+
+#define L2PMCNTENSET 0x403
+#define L2PMCNTENCLR 0x402
+
+#define L2PMINTENSET 0x405
+#define L2PMINTENCLR 0x404
+
+#define IA_L2PMXEVCNTCR_BASE 0x420
+#define IA_L2PMXEVTYPER_BASE 0x424
+#define IA_L2PMRESX_BASE 0x410
+#define IA_L2PMXEVFILTER_BASE 0x423
+#define IA_L2PMXEVCNTR_BASE 0x421
+
+/* event format is -e rsRCCG See get_event_desc() */
+
+#define EVENT_REG_MASK 0xf000
+#define EVENT_GROUPSEL_MASK 0x000f
+#define EVENT_GROUPCODE_MASK 0x0ff0
+#define EVENT_REG_SHIFT 12
+#define EVENT_GROUPCODE_SHIFT 4
+
+#define RESRX_VALUE_EN 0x80000000
+
+static u32 l2_orig_filter_prefix = 0x000f0030;
+
+static u32 pmu_type;
+
+static struct arm_pmu krait_l2_pmu;
+
+static struct perf_event *l2_events[MAX_KRAIT_L2_CTRS];
+static unsigned long l2_used_mask[BITS_TO_LONGS(MAX_KRAIT_L2_CTRS)];
+
+static struct pmu_hw_events krait_l2_pmu_hw_events = {
+ .events = l2_events,
+ .used_mask = l2_used_mask,
+ .pmu_lock = __RAW_SPIN_LOCK_UNLOCKED(krait_l2_pmu_hw_events.pmu_lock),
+};
+
+struct event_desc {
+ int event_groupsel;
+ int event_reg;
+ int event_group_code;
+};
+
+static struct pmu_hw_events *krait_l2_get_hw_events(void)
+{
+ return &krait_l2_pmu_hw_events;
+}
+
+void get_event_desc(u64 config, struct event_desc *evdesc)
+{
+ /* L2PMEVCNTRX */
+ evdesc->event_reg = (config & EVENT_REG_MASK) >> EVENT_REG_SHIFT;
+ /* Group code (row ) */
+ evdesc->event_group_code =
+ (config & EVENT_GROUPCODE_MASK) >> EVENT_GROUPCODE_SHIFT;
+ /* Group sel (col) */
+ evdesc->event_groupsel = (config & EVENT_GROUPSEL_MASK);
+
+ pr_debug("%s: reg: %x, group_code: %x, groupsel: %x\n", __func__,
+ evdesc->event_reg, evdesc->event_group_code,
+ evdesc->event_groupsel);
+}
+
+static void set_evcntcr(int ctr)
+{
+ u32 evtcr_reg = (ctr * 16) + IA_L2PMXEVCNTCR_BASE;
+
+ set_l2_indirect_reg(evtcr_reg, 0x0);
+}
+
+static void set_evtyper(int event_groupsel, int event_reg, int ctr)
+{
+ u32 evtype_reg = (ctr * 16) + IA_L2PMXEVTYPER_BASE;
+ u32 evtype_val = event_groupsel + (4 * event_reg);
+
+ set_l2_indirect_reg(evtype_reg, evtype_val);
+}
+
+static void set_evres(int event_groupsel, int event_reg, int event_group_code)
+{
+ u32 group_reg = event_reg + IA_L2PMRESX_BASE;
+ u32 group_val =
+ RESRX_VALUE_EN | (event_group_code << (8 * event_groupsel));
+ u32 resr_val;
+ u32 group_byte = 0xff;
+ u32 group_mask = ~(group_byte << (8 * event_groupsel));
+
+ resr_val = get_l2_indirect_reg(group_reg);
+ resr_val &= group_mask;
+ resr_val |= group_val;
+
+ set_l2_indirect_reg(group_reg, resr_val);
+}
+
+static void set_evfilter_task_mode(int ctr)
+{
+ u32 filter_reg = (ctr * 16) + IA_L2PMXEVFILTER_BASE;
+ u32 filter_val = l2_orig_filter_prefix | 1 << smp_processor_id();
+
+ set_l2_indirect_reg(filter_reg, filter_val);
+}
+
+static void set_evfilter_sys_mode(int ctr)
+{
+ u32 filter_reg = (ctr * 16) + IA_L2PMXEVFILTER_BASE;
+ u32 filter_val = l2_orig_filter_prefix | 0xf;
+
+ set_l2_indirect_reg(filter_reg, filter_val);
+}
+
+static void enable_intenset(u32 idx)
+{
+ if (idx == L2CYCLE_CTR_EVENT_IDX)
+ set_l2_indirect_reg(L2PMINTENSET, 1 << L2CYCLE_CTR_BIT);
+ else
+ set_l2_indirect_reg(L2PMINTENSET, 1 << idx);
+}
+
+static void disable_intenclr(u32 idx)
+{
+ if (idx == L2CYCLE_CTR_EVENT_IDX)
+ set_l2_indirect_reg(L2PMINTENCLR, 1 << L2CYCLE_CTR_BIT);
+ else
+ set_l2_indirect_reg(L2PMINTENCLR, 1 << idx);
+}
+
+static void enable_counter(u32 idx)
+{
+ if (idx == L2CYCLE_CTR_EVENT_IDX)
+ set_l2_indirect_reg(L2PMCNTENSET, 1 << L2CYCLE_CTR_BIT);
+ else
+ set_l2_indirect_reg(L2PMCNTENSET, 1 << idx);
+}
+
+static void disable_counter(u32 idx)
+{
+ if (idx == L2CYCLE_CTR_EVENT_IDX)
+ set_l2_indirect_reg(L2PMCNTENCLR, 1 << L2CYCLE_CTR_BIT);
+ else
+ set_l2_indirect_reg(L2PMCNTENCLR, 1 << idx);
+}
+
+static u32 krait_l2_read_counter(int idx)
+{
+ u32 val;
+ u32 counter_reg = (idx * 16) + IA_L2PMXEVCNTR_BASE;
+
+ if (idx == L2CYCLE_CTR_EVENT_IDX)
+ val = get_l2_indirect_reg(L2PMCCNTR);
+ else
+ val = get_l2_indirect_reg(counter_reg);
+
+ return val;
+}
+
+static void krait_l2_write_counter(int idx, u32 val)
+{
+ u32 counter_reg = (idx * 16) + IA_L2PMXEVCNTR_BASE;
+
+ if (idx == L2CYCLE_CTR_EVENT_IDX)
+ set_l2_indirect_reg(L2PMCCNTR, val);
+ else
+ set_l2_indirect_reg(counter_reg, val);
+}
+
+static void krait_l2_stop_counter(struct hw_perf_event *hwc, int idx)
+{
+ disable_intenclr(idx);
+ disable_counter(idx);
+
+ pr_debug("%s: event: %ld ctr: %d stopped\n", __func__,
+ hwc->config_base, idx);
+}
+
+static void krait_l2_enable(struct hw_perf_event *hwc, int idx, int cpu)
+{
+ struct event_desc evdesc;
+ unsigned long iflags;
+
+ raw_spin_lock_irqsave(&krait_l2_pmu_hw_events.pmu_lock, iflags);
+
+ if (hwc->config_base == L2CYCLE_CTR_RAW_CODE)
+ goto out;
+
+ set_evcntcr(idx);
+
+ memset(&evdesc, 0, sizeof(evdesc));
+
+ get_event_desc(hwc->config_base, &evdesc);
+
+ set_evtyper(evdesc.event_groupsel, evdesc.event_reg, idx);
+
+ set_evres(evdesc.event_groupsel, evdesc.event_reg,
+ evdesc.event_group_code);
+
+ if (cpu < 0)
+ set_evfilter_task_mode(idx);
+ else
+ set_evfilter_sys_mode(idx);
+
+out:
+ enable_intenset(idx);
+ enable_counter(idx);
+
+ raw_spin_unlock_irqrestore(&krait_l2_pmu_hw_events.pmu_lock, iflags);
+
+ pr_debug("%s: ctr: %d group: %ld group_code: %lld started from cpu:%d\n",
+ __func__, idx, hwc->config_base, hwc->config, smp_processor_id());
+}
+
+static void krait_l2_disable(struct hw_perf_event *hwc, int idx)
+{
+ unsigned long iflags;
+
+ raw_spin_lock_irqsave(&krait_l2_pmu_hw_events.pmu_lock, iflags);
+
+ krait_l2_stop_counter(hwc, idx);
+
+ raw_spin_unlock_irqrestore(&krait_l2_pmu_hw_events.pmu_lock, iflags);
+
+ pr_debug("%s: event: %ld deleted\n", __func__, hwc->config_base);
+
+}
+
+static int krait_l2_get_event_idx(struct pmu_hw_events *cpuc,
+ struct hw_perf_event *hwc)
+{
+ int ctr = 0;
+
+ if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) {
+ if (!test_and_set_bit(L2CYCLE_CTR_EVENT_IDX, cpuc->used_mask))
+ return L2CYCLE_CTR_EVENT_IDX;
+ }
+
+ for (ctr = 0; ctr < MAX_KRAIT_L2_CTRS - 1; ctr++) {
+ if (!test_and_set_bit(ctr, cpuc->used_mask))
+ return ctr;
+ }
+
+ return -EAGAIN;
+}
+
+static void krait_l2_start(void)
+{
+ isb();
+ set_l2_indirect_reg(L2PMCR, L2PMCR_GLOBAL_ENABLE);
+}
+
+static void krait_l2_stop(void)
+{
+ set_l2_indirect_reg(L2PMCR, L2PMCR_GLOBAL_DISABLE);
+ isb();
+}
+
+u32 get_reset_pmovsr(void)
+{
+ int val;
+
+ val = get_l2_indirect_reg(L2PMOVSR);
+ /* reset it */
+ val &= 0xffffffff;
+ set_l2_indirect_reg(L2PMOVSR, val);
+
+ return val;
+}
+
+static irqreturn_t krait_l2_handle_irq(int irq_num, void *dev)
+{
+ unsigned long pmovsr;
+ struct perf_sample_data data;
+ struct pt_regs *regs;
+ struct perf_event *event;
+ struct hw_perf_event *hwc;
+ int bitp;
+ int idx = 0;
+
+ pmovsr = get_reset_pmovsr();
+
+ if (!(pmovsr & 0xffffffff))
+ return IRQ_NONE;
+
+ regs = get_irq_regs();
+
+ perf_sample_data_init(&data, 0);
+
+ while (pmovsr) {
+ bitp = __ffs(pmovsr);
+
+ if (bitp == L2CYCLE_CTR_BIT)
+ idx = L2CYCLE_CTR_EVENT_IDX;
+ else
+ idx = bitp;
+
+ event = krait_l2_pmu_hw_events.events[idx];
+
+ if (!event)
+ goto next;
+
+ if (!test_bit(idx, krait_l2_pmu_hw_events.used_mask))
+ goto next;
+
+ hwc = &event->hw;
+
+ armpmu_event_update(event, hwc, idx);
+
+ data.period = event->hw.last_period;
+
+ if (!armpmu_event_set_period(event, hwc, idx))
+ goto next;
+
+ if (perf_event_overflow(event, &data, regs))
+ disable_counter(hwc->idx);
+next:
+ pmovsr &= (pmovsr - 1);
+ }
+
+ irq_work_run();
+
+ return IRQ_HANDLED;
+}
+
+static int krait_l2_map_event(struct perf_event *event)
+{
+ if (pmu_type > 0 && pmu_type == event->attr.type)
+ return event->attr.config & 0xfffff;
+ else
+ return -ENOENT;
+}
+
+static int
+krait_l2_pmu_generic_request_irq(int irq, irq_handler_t *handle_irq)
+{
+ return request_irq(irq, *handle_irq,
+ IRQF_DISABLED | IRQF_NOBALANCING,
+ "krait-l2-armpmu", NULL);
+}
+
+static void
+krait_l2_pmu_generic_free_irq(int irq)
+{
+ if (irq >= 0)
+ free_irq(irq, NULL);
+}
+
+static struct arm_pmu krait_l2_pmu = {
+ .id = ARM_PERF_PMU_ID_KRAIT_L2,
+ .type = ARM_PMU_DEVICE_L2CC,
+ .name = "Krait L2CC PMU",
+ .start = krait_l2_start,
+ .stop = krait_l2_stop,
+ .handle_irq = krait_l2_handle_irq,
+ .request_pmu_irq = krait_l2_pmu_generic_request_irq,
+ .free_pmu_irq = krait_l2_pmu_generic_free_irq,
+ .enable = krait_l2_enable,
+ .disable = krait_l2_disable,
+ .get_event_idx = krait_l2_get_event_idx,
+ .read_counter = krait_l2_read_counter,
+ .write_counter = krait_l2_write_counter,
+ .map_event = krait_l2_map_event,
+ .max_period = (1LLU << 32) - 1,
+ .get_hw_events = krait_l2_get_hw_events,
+ .num_events = MAX_KRAIT_L2_CTRS,
+};
+
+static int __devinit krait_l2_pmu_device_probe(struct platform_device *pdev)
+{
+ krait_l2_pmu.plat_device = pdev;
+
+ if (!armpmu_register(&krait_l2_pmu, "krait-l2", -1))
+ pmu_type = krait_l2_pmu.pmu.type;
+
+ return 0;
+}
+
+static struct platform_driver krait_l2_pmu_driver = {
+ .driver = {
+ .name = "l2-arm-pmu",
+ },
+ .probe = krait_l2_pmu_device_probe,
+};
+
+static int __init register_krait_l2_pmu_driver(void)
+{
+ /* Reset all ctrs */
+ set_l2_indirect_reg(L2PMCR, L2PMCR_RESET_ALL);
+
+ /* Avoid spurious interrupt if any */
+ get_reset_pmovsr();
+
+ return platform_driver_register(&krait_l2_pmu_driver);
+}
+device_initcall(register_krait_l2_pmu_driver);
diff --git a/arch/arm/mach-msm/perf_event_msm_l2.c b/arch/arm/mach-msm/perf_event_msm_l2.c
new file mode 100644
index 0000000..3310d92
--- /dev/null
+++ b/arch/arm/mach-msm/perf_event_msm_l2.c
@@ -0,0 +1,760 @@
+/*
+ * Copyright (c) 2011, 2012 Code Aurora Forum. 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/irq.h>
+#include <asm/pmu.h>
+#include <linux/platform_device.h>
+
+
+#define MAX_SCORPION_L2_CTRS 5
+#define SCORPION_L2CYCLE_CTR_BIT 31
+#define SCORPION_L2CYCLE_CTR_EVENT_IDX 4
+#define SCORPION_L2CYCLE_CTR_RAW_CODE 0xfe
+#define SCORPIONL2_PMNC_E (1 << 0) /* Enable all counters */
+#define SCORPION_L2_EVT_PREFIX 3
+#define SCORPION_MAX_L2_REG 4
+
+static u32 pmu_type;
+
+static struct arm_pmu scorpion_l2_pmu;
+
+static struct perf_event *l2_events[MAX_SCORPION_L2_CTRS];
+static unsigned long l2_used_mask[BITS_TO_LONGS(MAX_SCORPION_L2_CTRS)];
+
+static struct pmu_hw_events scorpion_l2_pmu_hw_events = {
+ .events = l2_events,
+ .used_mask = l2_used_mask,
+ .pmu_lock =
+ __RAW_SPIN_LOCK_UNLOCKED(scorpion_l2_pmu_hw_events.pmu_lock),
+};
+
+struct scorpion_l2_scorp_evt {
+ u32 evt_type;
+ u32 val;
+ u8 grp;
+ u32 evt_type_act;
+};
+
+enum scorpion_perf_types {
+ SCORPIONL2_TOTAL_BANK_REQ = 0x90,
+ SCORPIONL2_DSIDE_READ = 0x91,
+ SCORPIONL2_DSIDE_WRITE = 0x92,
+ SCORPIONL2_ISIDE_READ = 0x93,
+ SCORPIONL2_L2CACHE_ISIDE_READ = 0x94,
+ SCORPIONL2_L2CACHE_BANK_REQ = 0x95,
+ SCORPIONL2_L2CACHE_DSIDE_READ = 0x96,
+ SCORPIONL2_L2CACHE_DSIDE_WRITE = 0x97,
+ SCORPIONL2_L2NOCACHE_DSIDE_WRITE = 0x98,
+ SCORPIONL2_L2NOCACHE_ISIDE_READ = 0x99,
+ SCORPIONL2_L2NOCACHE_TOTAL_REQ = 0x9a,
+ SCORPIONL2_L2NOCACHE_DSIDE_READ = 0x9b,
+ SCORPIONL2_DSIDE_READ_NOL1 = 0x9c,
+ SCORPIONL2_L2CACHE_WRITETHROUGH = 0x9d,
+ SCORPIONL2_BARRIERS = 0x9e,
+ SCORPIONL2_HARDWARE_TABLE_WALKS = 0x9f,
+ SCORPIONL2_MVA_POC = 0xa0,
+ SCORPIONL2_L2CACHE_HW_TABLE_WALKS = 0xa1,
+ SCORPIONL2_SETWAY_CACHE_OPS = 0xa2,
+ SCORPIONL2_DSIDE_WRITE_HITS = 0xa3,
+ SCORPIONL2_ISIDE_READ_HITS = 0xa4,
+ SCORPIONL2_CACHE_DSIDE_READ_NOL1 = 0xa5,
+ SCORPIONL2_TOTAL_CACHE_HITS = 0xa6,
+ SCORPIONL2_CACHE_MATCH_MISS = 0xa7,
+ SCORPIONL2_DREAD_HIT_L1_DATA = 0xa8,
+ SCORPIONL2_L2LINE_LOCKED = 0xa9,
+ SCORPIONL2_HW_TABLE_WALK_HIT = 0xaa,
+ SCORPIONL2_CACHE_MVA_POC = 0xab,
+ SCORPIONL2_L2ALLOC_DWRITE_MISS = 0xac,
+ SCORPIONL2_CORRECTED_TAG_ARRAY = 0xad,
+ SCORPIONL2_CORRECTED_DATA_ARRAY = 0xae,
+ SCORPIONL2_CORRECTED_REPLACEMENT_ARRAY = 0xaf,
+ SCORPIONL2_PMBUS_MPAAF = 0xb0,
+ SCORPIONL2_PMBUS_MPWDAF = 0xb1,
+ SCORPIONL2_PMBUS_MPBRT = 0xb2,
+ SCORPIONL2_CPU0_GRANT = 0xb3,
+ SCORPIONL2_CPU1_GRANT = 0xb4,
+ SCORPIONL2_CPU0_NOGRANT = 0xb5,
+ SCORPIONL2_CPU1_NOGRANT = 0xb6,
+ SCORPIONL2_CPU0_LOSING_ARB = 0xb7,
+ SCORPIONL2_CPU1_LOSING_ARB = 0xb8,
+ SCORPIONL2_SLAVEPORT_NOGRANT = 0xb9,
+ SCORPIONL2_SLAVEPORT_BPQ_FULL = 0xba,
+ SCORPIONL2_SLAVEPORT_LOSING_ARB = 0xbb,
+ SCORPIONL2_SLAVEPORT_GRANT = 0xbc,
+ SCORPIONL2_SLAVEPORT_GRANTLOCK = 0xbd,
+ SCORPIONL2_L2EM_STREX_PASS = 0xbe,
+ SCORPIONL2_L2EM_STREX_FAIL = 0xbf,
+ SCORPIONL2_LDREX_RESERVE_L2EM = 0xc0,
+ SCORPIONL2_SLAVEPORT_LDREX = 0xc1,
+ SCORPIONL2_CPU0_L2EM_CLEARED = 0xc2,
+ SCORPIONL2_CPU1_L2EM_CLEARED = 0xc3,
+ SCORPIONL2_SLAVEPORT_L2EM_CLEARED = 0xc4,
+ SCORPIONL2_CPU0_CLAMPED = 0xc5,
+ SCORPIONL2_CPU1_CLAMPED = 0xc6,
+ SCORPIONL2_CPU0_WAIT = 0xc7,
+ SCORPIONL2_CPU1_WAIT = 0xc8,
+ SCORPIONL2_CPU0_NONAMBAS_WAIT = 0xc9,
+ SCORPIONL2_CPU1_NONAMBAS_WAIT = 0xca,
+ SCORPIONL2_CPU0_DSB_WAIT = 0xcb,
+ SCORPIONL2_CPU1_DSB_WAIT = 0xcc,
+ SCORPIONL2_AXI_READ = 0xcd,
+ SCORPIONL2_AXI_WRITE = 0xce,
+
+ SCORPIONL2_1BEAT_WRITE = 0xcf,
+ SCORPIONL2_2BEAT_WRITE = 0xd0,
+ SCORPIONL2_4BEAT_WRITE = 0xd1,
+ SCORPIONL2_8BEAT_WRITE = 0xd2,
+ SCORPIONL2_12BEAT_WRITE = 0xd3,
+ SCORPIONL2_16BEAT_WRITE = 0xd4,
+ SCORPIONL2_1BEAT_DSIDE_READ = 0xd5,
+ SCORPIONL2_2BEAT_DSIDE_READ = 0xd6,
+ SCORPIONL2_4BEAT_DSIDE_READ = 0xd7,
+ SCORPIONL2_8BEAT_DSIDE_READ = 0xd8,
+ SCORPIONL2_CSYS_READ_1BEAT = 0xd9,
+ SCORPIONL2_CSYS_READ_2BEAT = 0xda,
+ SCORPIONL2_CSYS_READ_4BEAT = 0xdb,
+ SCORPIONL2_CSYS_READ_8BEAT = 0xdc,
+ SCORPIONL2_4BEAT_IFETCH_READ = 0xdd,
+ SCORPIONL2_8BEAT_IFETCH_READ = 0xde,
+ SCORPIONL2_CSYS_WRITE_1BEAT = 0xdf,
+ SCORPIONL2_CSYS_WRITE_2BEAT = 0xe0,
+ SCORPIONL2_AXI_READ_DATA_BEAT = 0xe1,
+ SCORPIONL2_AXI_WRITE_EVT1 = 0xe2,
+ SCORPIONL2_AXI_WRITE_EVT2 = 0xe3,
+ SCORPIONL2_LDREX_REQ = 0xe4,
+ SCORPIONL2_STREX_PASS = 0xe5,
+ SCORPIONL2_STREX_FAIL = 0xe6,
+ SCORPIONL2_CPREAD = 0xe7,
+ SCORPIONL2_CPWRITE = 0xe8,
+ SCORPIONL2_BARRIER_REQ = 0xe9,
+ SCORPIONL2_AXI_READ_SLVPORT = 0xea,
+ SCORPIONL2_AXI_WRITE_SLVPORT = 0xeb,
+ SCORPIONL2_AXI_READ_SLVPORT_DATABEAT = 0xec,
+ SCORPIONL2_AXI_WRITE_SLVPORT_DATABEAT = 0xed,
+ SCORPIONL2_SNOOPKILL_PREFILTER = 0xee,
+ SCORPIONL2_SNOOPKILL_FILTEROUT = 0xef,
+ SCORPIONL2_SNOOPED_IC = 0xf0,
+ SCORPIONL2_SNOOPED_BP = 0xf1,
+ SCORPIONL2_SNOOPED_BARRIERS = 0xf2,
+ SCORPIONL2_SNOOPED_TLB = 0xf3,
+ SCORPION_L2_MAX_EVT,
+};
+
+static const struct scorpion_l2_scorp_evt sc_evt[] = {
+ {SCORPIONL2_TOTAL_BANK_REQ, 0x80000001, 0, 0x00},
+ {SCORPIONL2_DSIDE_READ, 0x80000100, 0, 0x01},
+ {SCORPIONL2_DSIDE_WRITE, 0x80010000, 0, 0x02},
+ {SCORPIONL2_ISIDE_READ, 0x81000000, 0, 0x03},
+ {SCORPIONL2_L2CACHE_ISIDE_READ, 0x80000002, 0, 0x00},
+ {SCORPIONL2_L2CACHE_BANK_REQ, 0x80000200, 0, 0x01},
+ {SCORPIONL2_L2CACHE_DSIDE_READ, 0x80020000, 0, 0x02},
+ {SCORPIONL2_L2CACHE_DSIDE_WRITE, 0x82000000, 0, 0x03},
+ {SCORPIONL2_L2NOCACHE_DSIDE_WRITE, 0x80000003, 0, 0x00},
+ {SCORPIONL2_L2NOCACHE_ISIDE_READ, 0x80000300, 0, 0x01},
+ {SCORPIONL2_L2NOCACHE_TOTAL_REQ, 0x80030000, 0, 0x02},
+ {SCORPIONL2_L2NOCACHE_DSIDE_READ, 0x83000000, 0, 0x03},
+ {SCORPIONL2_DSIDE_READ_NOL1, 0x80000004, 0, 0x00},
+ {SCORPIONL2_L2CACHE_WRITETHROUGH, 0x80000400, 0, 0x01},
+ {SCORPIONL2_BARRIERS, 0x84000000, 0, 0x03},
+ {SCORPIONL2_HARDWARE_TABLE_WALKS, 0x80000005, 0, 0x00},
+ {SCORPIONL2_MVA_POC, 0x80000500, 0, 0x01},
+ {SCORPIONL2_L2CACHE_HW_TABLE_WALKS, 0x80050000, 0, 0x02},
+ {SCORPIONL2_SETWAY_CACHE_OPS, 0x85000000, 0, 0x03},
+ {SCORPIONL2_DSIDE_WRITE_HITS, 0x80000006, 0, 0x00},
+ {SCORPIONL2_ISIDE_READ_HITS, 0x80000600, 0, 0x01},
+ {SCORPIONL2_CACHE_DSIDE_READ_NOL1, 0x80060000, 0, 0x02},
+ {SCORPIONL2_TOTAL_CACHE_HITS, 0x86000000, 0, 0x03},
+ {SCORPIONL2_CACHE_MATCH_MISS, 0x80000007, 0, 0x00},
+ {SCORPIONL2_DREAD_HIT_L1_DATA, 0x87000000, 0, 0x03},
+ {SCORPIONL2_L2LINE_LOCKED, 0x80000008, 0, 0x00},
+ {SCORPIONL2_HW_TABLE_WALK_HIT, 0x80000800, 0, 0x01},
+ {SCORPIONL2_CACHE_MVA_POC, 0x80080000, 0, 0x02},
+ {SCORPIONL2_L2ALLOC_DWRITE_MISS, 0x88000000, 0, 0x03},
+ {SCORPIONL2_CORRECTED_TAG_ARRAY, 0x80001A00, 0, 0x01},
+ {SCORPIONL2_CORRECTED_DATA_ARRAY, 0x801A0000, 0, 0x02},
+ {SCORPIONL2_CORRECTED_REPLACEMENT_ARRAY, 0x9A000000, 0, 0x03},
+ {SCORPIONL2_PMBUS_MPAAF, 0x80001C00, 0, 0x01},
+ {SCORPIONL2_PMBUS_MPWDAF, 0x801C0000, 0, 0x02},
+ {SCORPIONL2_PMBUS_MPBRT, 0x9C000000, 0, 0x03},
+
+ {SCORPIONL2_CPU0_GRANT, 0x80000001, 1, 0x04},
+ {SCORPIONL2_CPU1_GRANT, 0x80000100, 1, 0x05},
+ {SCORPIONL2_CPU0_NOGRANT, 0x80020000, 1, 0x06},
+ {SCORPIONL2_CPU1_NOGRANT, 0x82000000, 1, 0x07},
+ {SCORPIONL2_CPU0_LOSING_ARB, 0x80040000, 1, 0x06},
+ {SCORPIONL2_CPU1_LOSING_ARB, 0x84000000, 1, 0x07},
+ {SCORPIONL2_SLAVEPORT_NOGRANT, 0x80000007, 1, 0x04},
+ {SCORPIONL2_SLAVEPORT_BPQ_FULL, 0x80000700, 1, 0x05},
+ {SCORPIONL2_SLAVEPORT_LOSING_ARB, 0x80070000, 1, 0x06},
+ {SCORPIONL2_SLAVEPORT_GRANT, 0x87000000, 1, 0x07},
+ {SCORPIONL2_SLAVEPORT_GRANTLOCK, 0x80000008, 1, 0x04},
+ {SCORPIONL2_L2EM_STREX_PASS, 0x80000009, 1, 0x04},
+ {SCORPIONL2_L2EM_STREX_FAIL, 0x80000900, 1, 0x05},
+ {SCORPIONL2_LDREX_RESERVE_L2EM, 0x80090000, 1, 0x06},
+ {SCORPIONL2_SLAVEPORT_LDREX, 0x89000000, 1, 0x07},
+ {SCORPIONL2_CPU0_L2EM_CLEARED, 0x800A0000, 1, 0x06},
+ {SCORPIONL2_CPU1_L2EM_CLEARED, 0x8A000000, 1, 0x07},
+ {SCORPIONL2_SLAVEPORT_L2EM_CLEARED, 0x80000B00, 1, 0x05},
+ {SCORPIONL2_CPU0_CLAMPED, 0x8000000E, 1, 0x04},
+ {SCORPIONL2_CPU1_CLAMPED, 0x80000E00, 1, 0x05},
+ {SCORPIONL2_CPU0_WAIT, 0x800F0000, 1, 0x06},
+ {SCORPIONL2_CPU1_WAIT, 0x8F000000, 1, 0x07},
+ {SCORPIONL2_CPU0_NONAMBAS_WAIT, 0x80000010, 1, 0x04},
+ {SCORPIONL2_CPU1_NONAMBAS_WAIT, 0x80001000, 1, 0x05},
+ {SCORPIONL2_CPU0_DSB_WAIT, 0x80000014, 1, 0x04},
+ {SCORPIONL2_CPU1_DSB_WAIT, 0x80001400, 1, 0x05},
+
+ {SCORPIONL2_AXI_READ, 0x80000001, 2, 0x08},
+ {SCORPIONL2_AXI_WRITE, 0x80000100, 2, 0x09},
+ {SCORPIONL2_1BEAT_WRITE, 0x80010000, 2, 0x0a},
+ {SCORPIONL2_2BEAT_WRITE, 0x80010000, 2, 0x0b},
+ {SCORPIONL2_4BEAT_WRITE, 0x80000002, 2, 0x08},
+ {SCORPIONL2_8BEAT_WRITE, 0x80000200, 2, 0x09},
+ {SCORPIONL2_12BEAT_WRITE, 0x80020000, 2, 0x0a},
+ {SCORPIONL2_16BEAT_WRITE, 0x82000000, 2, 0x0b},
+ {SCORPIONL2_1BEAT_DSIDE_READ, 0x80000003, 2, 0x08},
+ {SCORPIONL2_2BEAT_DSIDE_READ, 0x80000300, 2, 0x09},
+ {SCORPIONL2_4BEAT_DSIDE_READ, 0x80030000, 2, 0x0a},
+ {SCORPIONL2_8BEAT_DSIDE_READ, 0x83000000, 2, 0x0b},
+ {SCORPIONL2_CSYS_READ_1BEAT, 0x80000004, 2, 0x08},
+ {SCORPIONL2_CSYS_READ_2BEAT, 0x80000400, 2, 0x09},
+ {SCORPIONL2_CSYS_READ_4BEAT, 0x80040000, 2, 0x0a},
+ {SCORPIONL2_CSYS_READ_8BEAT, 0x84000000, 2, 0x0b},
+ {SCORPIONL2_4BEAT_IFETCH_READ, 0x80000005, 2, 0x08},
+ {SCORPIONL2_8BEAT_IFETCH_READ, 0x80000500, 2, 0x09},
+ {SCORPIONL2_CSYS_WRITE_1BEAT, 0x80050000, 2, 0x0a},
+ {SCORPIONL2_CSYS_WRITE_2BEAT, 0x85000000, 2, 0x0b},
+ {SCORPIONL2_AXI_READ_DATA_BEAT, 0x80000600, 2, 0x09},
+ {SCORPIONL2_AXI_WRITE_EVT1, 0x80060000, 2, 0x0a},
+ {SCORPIONL2_AXI_WRITE_EVT2, 0x86000000, 2, 0x0b},
+ {SCORPIONL2_LDREX_REQ, 0x80000007, 2, 0x08},
+ {SCORPIONL2_STREX_PASS, 0x80000700, 2, 0x09},
+ {SCORPIONL2_STREX_FAIL, 0x80070000, 2, 0x0a},
+ {SCORPIONL2_CPREAD, 0x80000008, 2, 0x08},
+ {SCORPIONL2_CPWRITE, 0x80000800, 2, 0x09},
+ {SCORPIONL2_BARRIER_REQ, 0x88000000, 2, 0x0b},
+
+ {SCORPIONL2_AXI_READ_SLVPORT, 0x80000001, 3, 0x0c},
+ {SCORPIONL2_AXI_WRITE_SLVPORT, 0x80000100, 3, 0x0d},
+ {SCORPIONL2_AXI_READ_SLVPORT_DATABEAT, 0x80010000, 3, 0x0e},
+ {SCORPIONL2_AXI_WRITE_SLVPORT_DATABEAT, 0x81000000, 3, 0x0f},
+
+ {SCORPIONL2_SNOOPKILL_PREFILTER, 0x80000001, 4, 0x10},
+ {SCORPIONL2_SNOOPKILL_FILTEROUT, 0x80000100, 4, 0x11},
+ {SCORPIONL2_SNOOPED_IC, 0x80000002, 4, 0x10},
+ {SCORPIONL2_SNOOPED_BP, 0x80000200, 4, 0x11},
+ {SCORPIONL2_SNOOPED_BARRIERS, 0x80020000, 4, 0x12},
+ {SCORPIONL2_SNOOPED_TLB, 0x82000000, 4, 0x13},
+};
+
+static struct pmu_hw_events *scorpion_l2_get_hw_events(void)
+{
+ return &scorpion_l2_pmu_hw_events;
+}
+static u32 scorpion_l2_read_l2pm0(void)
+{
+ u32 val;
+ asm volatile ("mrc p15, 3, %0, c15, c7, 0" : "=r" (val));
+ return val;
+}
+
+static void scorpion_l2_write_l2pm0(u32 val)
+{
+ asm volatile ("mcr p15, 3, %0, c15, c7, 0" : : "r" (val));
+}
+
+static u32 scorpion_l2_read_l2pm1(void)
+{
+ u32 val;
+ asm volatile ("mrc p15, 3, %0, c15, c7, 1" : "=r" (val));
+ return val;
+}
+
+static void scorpion_l2_write_l2pm1(u32 val)
+{
+ asm volatile ("mcr p15, 3, %0, c15, c7, 1" : : "r" (val));
+}
+
+static u32 scorpion_l2_read_l2pm2(void)
+{
+ u32 val;
+ asm volatile ("mrc p15, 3, %0, c15, c7, 2" : "=r" (val));
+ return val;
+}
+
+static void scorpion_l2_write_l2pm2(u32 val)
+{
+ asm volatile ("mcr p15, 3, %0, c15, c7, 2" : : "r" (val));
+}
+
+static u32 scorpion_l2_read_l2pm3(void)
+{
+ u32 val;
+ asm volatile ("mrc p15, 3, %0, c15, c7, 3" : "=r" (val));
+ return val;
+}
+
+static void scorpion_l2_write_l2pm3(u32 val)
+{
+ asm volatile ("mcr p15, 3, %0, c15, c7, 3" : : "r" (val));
+}
+
+static u32 scorpion_l2_read_l2pm4(void)
+{
+ u32 val;
+ asm volatile ("mrc p15, 3, %0, c15, c7, 4" : "=r" (val));
+ return val;
+}
+
+static void scorpion_l2_write_l2pm4(u32 val)
+{
+ asm volatile ("mcr p15, 3, %0, c15, c7, 4" : : "r" (val));
+}
+
+struct scorpion_scorpion_access_funcs {
+ u32(*read) (void);
+ void (*write) (u32);
+ void (*pre) (void);
+ void (*post) (void);
+};
+
+struct scorpion_scorpion_access_funcs scorpion_l2_func[] = {
+ {scorpion_l2_read_l2pm0, scorpion_l2_write_l2pm0, NULL, NULL},
+ {scorpion_l2_read_l2pm1, scorpion_l2_write_l2pm1, NULL, NULL},
+ {scorpion_l2_read_l2pm2, scorpion_l2_write_l2pm2, NULL, NULL},
+ {scorpion_l2_read_l2pm3, scorpion_l2_write_l2pm3, NULL, NULL},
+ {scorpion_l2_read_l2pm4, scorpion_l2_write_l2pm4, NULL, NULL},
+};
+
+#define COLMN0MASK 0x000000ff
+#define COLMN1MASK 0x0000ff00
+#define COLMN2MASK 0x00ff0000
+
+static u32 scorpion_l2_get_columnmask(u32 setval)
+{
+ if (setval & COLMN0MASK)
+ return 0xffffff00;
+ else if (setval & COLMN1MASK)
+ return 0xffff00ff;
+ else if (setval & COLMN2MASK)
+ return 0xff00ffff;
+ else
+ return 0x80ffffff;
+}
+
+static void scorpion_l2_evt_setup(u32 gr, u32 setval)
+{
+ u32 val;
+ if (scorpion_l2_func[gr].pre)
+ scorpion_l2_func[gr].pre();
+ val = scorpion_l2_get_columnmask(setval) & scorpion_l2_func[gr].read();
+ val = val | setval;
+ scorpion_l2_func[gr].write(val);
+ if (scorpion_l2_func[gr].post)
+ scorpion_l2_func[gr].post();
+}
+
+#define SCORPION_L2_EVT_START_IDX 0x90
+#define SCORPION_L2_INV_EVTYPE 0
+
+static unsigned int get_scorpion_l2_evtinfo(unsigned int evt_type,
+ struct scorpion_l2_scorp_evt *evtinfo)
+{
+ u32 idx;
+ u8 prefix;
+ u8 reg;
+ u8 code;
+ u8 group;
+
+ prefix = (evt_type & 0xF0000) >> 16;
+ if (prefix == SCORPION_L2_EVT_PREFIX) {
+ reg = (evt_type & 0x0F000) >> 12;
+ code = (evt_type & 0x00FF0) >> 4;
+ group = evt_type & 0x0000F;
+
+ if ((group > 3) || (reg > SCORPION_MAX_L2_REG))
+ return SCORPION_L2_INV_EVTYPE;
+
+ evtinfo->val = 0x80000000 | (code << (group * 8));
+ evtinfo->grp = reg;
+ evtinfo->evt_type_act = group | (reg << 2);
+ return evtinfo->evt_type_act;
+ }
+
+ if (evt_type < SCORPION_L2_EVT_START_IDX
+ || evt_type >= SCORPION_L2_MAX_EVT)
+ return SCORPION_L2_INV_EVTYPE;
+
+ idx = evt_type - SCORPION_L2_EVT_START_IDX;
+
+ if (sc_evt[idx].evt_type == evt_type) {
+ evtinfo->val = sc_evt[idx].val;
+ evtinfo->grp = sc_evt[idx].grp;
+ evtinfo->evt_type_act = sc_evt[idx].evt_type_act;
+ return sc_evt[idx].evt_type_act;
+ }
+ return SCORPION_L2_INV_EVTYPE;
+}
+
+static inline void scorpion_l2_pmnc_write(unsigned long val)
+{
+ val &= 0xff;
+ asm volatile ("mcr p15, 3, %0, c15, c4, 0" : : "r" (val));
+}
+
+static inline unsigned long scorpion_l2_pmnc_read(void)
+{
+ u32 val;
+ asm volatile ("mrc p15, 3, %0, c15, c4, 0" : "=r" (val));
+ return val;
+}
+
+static void scorpion_l2_set_evcntcr(void)
+{
+ u32 val = 0x0;
+ asm volatile ("mcr p15, 3, %0, c15, c6, 4" : : "r" (val));
+}
+
+static inline void scorpion_l2_set_evtyper(int ctr, int val)
+{
+ /* select ctr */
+ asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (ctr));
+
+ /* write into EVTYPER */
+ asm volatile ("mcr p15, 3, %0, c15, c6, 7" : : "r" (val));
+}
+
+static void scorpion_l2_set_evfilter_task_mode(void)
+{
+ u32 filter_val = 0x000f0030 | 1 << smp_processor_id();
+
+ asm volatile ("mcr p15, 3, %0, c15, c6, 3" : : "r" (filter_val));
+}
+
+static void scorpion_l2_set_evfilter_sys_mode(void)
+{
+ u32 filter_val = 0x000f003f;
+
+ asm volatile ("mcr p15, 3, %0, c15, c6, 3" : : "r" (filter_val));
+}
+
+static void scorpion_l2_enable_intenset(u32 idx)
+{
+ if (idx == SCORPION_L2CYCLE_CTR_EVENT_IDX) {
+ asm volatile ("mcr p15, 3, %0, c15, c5, 1" : : "r"
+ (1 << SCORPION_L2CYCLE_CTR_BIT));
+ } else {
+ asm volatile ("mcr p15, 3, %0, c15, c5, 1" : : "r" (1 << idx));
+ }
+}
+
+static void scorpion_l2_disable_intenclr(u32 idx)
+{
+ if (idx == SCORPION_L2CYCLE_CTR_EVENT_IDX) {
+ asm volatile ("mcr p15, 3, %0, c15, c5, 0" : : "r"
+ (1 << SCORPION_L2CYCLE_CTR_BIT));
+ } else {
+ asm volatile ("mcr p15, 3, %0, c15, c5, 0" : : "r" (1 << idx));
+ }
+}
+
+static void scorpion_l2_enable_counter(u32 idx)
+{
+ if (idx == SCORPION_L2CYCLE_CTR_EVENT_IDX) {
+ asm volatile ("mcr p15, 3, %0, c15, c4, 3" : : "r"
+ (1 << SCORPION_L2CYCLE_CTR_BIT));
+ } else {
+ asm volatile ("mcr p15, 3, %0, c15, c4, 3" : : "r" (1 << idx));
+ }
+}
+
+static void scorpion_l2_disable_counter(u32 idx)
+{
+ if (idx == SCORPION_L2CYCLE_CTR_EVENT_IDX) {
+ asm volatile ("mcr p15, 3, %0, c15, c4, 2" : : "r"
+ (1 << SCORPION_L2CYCLE_CTR_BIT));
+ } else {
+ asm volatile ("mcr p15, 3, %0, c15, c4, 2" : : "r" (1 << idx));
+ }
+}
+
+static u32 scorpion_l2_read_counter(int idx)
+{
+ u32 val;
+ unsigned long iflags;
+
+ if (idx == SCORPION_L2CYCLE_CTR_EVENT_IDX) {
+ asm volatile ("mrc p15, 3, %0, c15, c4, 5" : "=r" (val));
+ } else {
+ raw_spin_lock_irqsave(&scorpion_l2_pmu_hw_events.pmu_lock,
+ iflags);
+ asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (idx));
+
+ /* read val from counter */
+ asm volatile ("mrc p15, 3, %0, c15, c6, 5" : "=r" (val));
+ raw_spin_unlock_irqrestore(&scorpion_l2_pmu_hw_events.pmu_lock,
+ iflags);
+ }
+
+ return val;
+}
+
+static void scorpion_l2_write_counter(int idx, u32 val)
+{
+ unsigned long iflags;
+
+ if (idx == SCORPION_L2CYCLE_CTR_EVENT_IDX) {
+ asm volatile ("mcr p15, 3, %0, c15, c4, 5" : : "r" (val));
+ } else {
+ raw_spin_lock_irqsave(&scorpion_l2_pmu_hw_events.pmu_lock,
+ iflags);
+
+ /* select counter */
+ asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (idx));
+
+ /* write val into counter */
+ asm volatile ("mcr p15, 3, %0, c15, c6, 5" : : "r" (val));
+ raw_spin_unlock_irqrestore(&scorpion_l2_pmu_hw_events.pmu_lock,
+ iflags);
+ }
+}
+
+static void scorpion_l2_stop_counter(struct hw_perf_event *hwc, int idx)
+{
+ scorpion_l2_disable_intenclr(idx);
+ scorpion_l2_disable_counter(idx);
+ pr_debug("%s: event: %ld ctr: %d stopped\n", __func__,
+ hwc->config_base, idx);
+}
+
+static void scorpion_l2_enable(struct hw_perf_event *hwc, int idx, int cpu)
+{
+ struct scorpion_l2_scorp_evt evtinfo;
+ int evtype = hwc->config_base;
+ int ev_typer;
+ unsigned long iflags;
+
+ raw_spin_lock_irqsave(&scorpion_l2_pmu_hw_events.pmu_lock, iflags);
+
+ if (hwc->config_base == SCORPION_L2CYCLE_CTR_RAW_CODE)
+ goto out;
+
+ memset(&evtinfo, 0, sizeof(evtinfo));
+
+ ev_typer = get_scorpion_l2_evtinfo(evtype, &evtinfo);
+
+ scorpion_l2_set_evtyper(idx, ev_typer);
+
+ scorpion_l2_set_evcntcr();
+
+ if (cpu < 0)
+ scorpion_l2_set_evfilter_task_mode();
+ else
+ scorpion_l2_set_evfilter_sys_mode();
+
+ scorpion_l2_evt_setup(evtinfo.grp, evtinfo.val);
+
+out:
+
+ scorpion_l2_enable_intenset(idx);
+
+ scorpion_l2_enable_counter(idx);
+
+ raw_spin_unlock_irqrestore(&scorpion_l2_pmu_hw_events.pmu_lock, iflags);
+
+ pr_debug("%s: ctr: %d group: %ld group_code: %lld started from cpu:%d\n",
+ __func__, idx, hwc->config_base, hwc->config, smp_processor_id());
+}
+
+static void scorpion_l2_disable(struct hw_perf_event *hwc, int idx)
+{
+ unsigned long iflags;
+
+ raw_spin_lock_irqsave(&scorpion_l2_pmu_hw_events.pmu_lock, iflags);
+
+ scorpion_l2_stop_counter(hwc, idx);
+
+ raw_spin_unlock_irqrestore(&scorpion_l2_pmu_hw_events.pmu_lock, iflags);
+
+ pr_debug("%s: event: %ld deleted\n", __func__, hwc->config_base);
+}
+
+static int scorpion_l2_get_event_idx(struct pmu_hw_events *cpuc,
+ struct hw_perf_event *hwc)
+{
+ int ctr = 0;
+
+ if (hwc->config_base == SCORPION_L2CYCLE_CTR_RAW_CODE) {
+ if (!test_and_set_bit(SCORPION_L2CYCLE_CTR_EVENT_IDX,
+ cpuc->used_mask))
+ return SCORPION_L2CYCLE_CTR_EVENT_IDX;
+ }
+
+ for (ctr = 0; ctr < MAX_SCORPION_L2_CTRS - 1; ctr++) {
+ if (!test_and_set_bit(ctr, cpuc->used_mask))
+ return ctr;
+ }
+
+ return -EAGAIN;
+}
+
+static void scorpion_l2_start(void)
+{
+ isb();
+ /* Enable all counters */
+ scorpion_l2_pmnc_write(scorpion_l2_pmnc_read() | SCORPIONL2_PMNC_E);
+}
+
+static void scorpion_l2_stop(void)
+{
+ /* Disable all counters */
+ scorpion_l2_pmnc_write(scorpion_l2_pmnc_read() & ~SCORPIONL2_PMNC_E);
+ isb();
+}
+
+static inline u32 scorpion_l2_get_reset_pmovsr(void)
+{
+ u32 val;
+
+ /* Read */
+ asm volatile ("mrc p15, 3, %0, c15, c4, 1" : "=r" (val));
+
+ /* Write to clear flags */
+ val &= 0xffffffff;
+ asm volatile ("mcr p15, 3, %0, c15, c4, 1" : : "r" (val));
+
+ return val;
+}
+
+static irqreturn_t scorpion_l2_handle_irq(int irq_num, void *dev)
+{
+ unsigned long pmovsr;
+ struct perf_sample_data data;
+ struct pt_regs *regs;
+ struct perf_event *event;
+ struct hw_perf_event *hwc;
+ int bitp;
+ int idx = 0;
+
+ pmovsr = scorpion_l2_get_reset_pmovsr();
+
+ if (!(pmovsr & 0xffffffff))
+ return IRQ_NONE;
+
+ regs = get_irq_regs();
+
+ perf_sample_data_init(&data, 0);
+
+ while (pmovsr) {
+ bitp = __ffs(pmovsr);
+
+ if (bitp == SCORPION_L2CYCLE_CTR_BIT)
+ idx = SCORPION_L2CYCLE_CTR_EVENT_IDX;
+ else
+ idx = bitp;
+
+ event = scorpion_l2_pmu_hw_events.events[idx];
+
+ if (!event)
+ goto next;
+
+ if (!test_bit(idx, scorpion_l2_pmu_hw_events.used_mask))
+ goto next;
+
+ hwc = &event->hw;
+
+ armpmu_event_update(event, hwc, idx);
+
+ data.period = event->hw.last_period;
+
+ if (!armpmu_event_set_period(event, hwc, idx))
+ goto next;
+
+ if (perf_event_overflow(event, &data, regs))
+ scorpion_l2_disable_counter(hwc->idx);
+next:
+ pmovsr &= (pmovsr - 1);
+ }
+
+ irq_work_run();
+
+ return IRQ_HANDLED;
+}
+
+static int scorpion_l2_map_event(struct perf_event *event)
+{
+ if (pmu_type > 0 && pmu_type == event->attr.type)
+ return event->attr.config & 0xfffff;
+ else
+ return -ENOENT;
+}
+
+static int
+scorpion_l2_pmu_generic_request_irq(int irq, irq_handler_t *handle_irq)
+{
+ return request_irq(irq, *handle_irq,
+ IRQF_DISABLED | IRQF_NOBALANCING,
+ "scorpion-l2-armpmu", NULL);
+}
+
+static void
+scorpion_l2_pmu_generic_free_irq(int irq)
+{
+ if (irq >= 0)
+ free_irq(irq, NULL);
+}
+
+static struct arm_pmu scorpion_l2_pmu = {
+ .id = ARM_PERF_PMU_ID_SCORPIONMP_L2,
+ .type = ARM_PMU_DEVICE_L2CC,
+ .name = "Scorpion L2CC PMU",
+ .start = scorpion_l2_start,
+ .stop = scorpion_l2_stop,
+ .handle_irq = scorpion_l2_handle_irq,
+ .request_pmu_irq = scorpion_l2_pmu_generic_request_irq,
+ .free_pmu_irq = scorpion_l2_pmu_generic_free_irq,
+ .enable = scorpion_l2_enable,
+ .disable = scorpion_l2_disable,
+ .read_counter = scorpion_l2_read_counter,
+ .get_event_idx = scorpion_l2_get_event_idx,
+ .write_counter = scorpion_l2_write_counter,
+ .map_event = scorpion_l2_map_event,
+ .max_period = (1LLU << 32) - 1,
+ .get_hw_events = scorpion_l2_get_hw_events,
+ .num_events = MAX_SCORPION_L2_CTRS,
+};
+
+static int __devinit scorpion_l2_pmu_device_probe(struct platform_device *pdev)
+{
+ scorpion_l2_pmu.plat_device = pdev;
+
+ if (!armpmu_register(&scorpion_l2_pmu, "scorpion-l2", -1))
+ pmu_type = scorpion_l2_pmu.pmu.type;
+
+ return 0;
+}
+
+static struct platform_driver scorpion_l2_pmu_driver = {
+ .driver = {
+ .name = "l2-arm-pmu",
+ },
+ .probe = scorpion_l2_pmu_device_probe,
+};
+
+static int __init register_scorpion_l2_pmu_driver(void)
+{
+ /* Avoid spurious interrupt if any */
+ scorpion_l2_get_reset_pmovsr();
+
+ return platform_driver_register(&scorpion_l2_pmu_driver);
+}
+device_initcall(register_scorpion_l2_pmu_driver);
diff --git a/arch/arm/mach-msm/platsmp-8625.c b/arch/arm/mach-msm/platsmp-8625.c
index 915047a..b31d94b 100644
--- a/arch/arm/mach-msm/platsmp-8625.c
+++ b/arch/arm/mach-msm/platsmp-8625.c
@@ -86,7 +86,7 @@
c->irq_mask(d);
local_irq_disable();
/* Clear the IRQ from the ENABLE_SET */
- gic_clear_spi_pending(irq);
+ gic_clear_irq_pending(irq);
local_irq_enable();
}
diff --git a/arch/arm/mach-msm/pm-stats.c b/arch/arm/mach-msm/pm-stats.c
index 936820a..675febb 100644
--- a/arch/arm/mach-msm/pm-stats.c
+++ b/arch/arm/mach-msm/pm-stats.c
@@ -188,18 +188,19 @@
int ret;
unsigned long flags;
unsigned int cpu;
+ size_t len = strnlen(MSM_PM_STATS_RESET, sizeof(MSM_PM_STATS_RESET));
if (count < sizeof(MSM_PM_STATS_RESET)) {
ret = -EINVAL;
goto write_proc_failed;
}
- if (copy_from_user(buf, buffer, sizeof(MSM_PM_STATS_RESET))) {
+ if (copy_from_user(buf, buffer, len)) {
ret = -EFAULT;
goto write_proc_failed;
}
- if (memcmp(buf, MSM_PM_STATS_RESET, sizeof(MSM_PM_STATS_RESET))) {
+ if (strncmp(buf, MSM_PM_STATS_RESET, len)) {
ret = -EINVAL;
goto write_proc_failed;
}
diff --git a/arch/arm/mach-msm/pmu.c b/arch/arm/mach-msm/pmu.c
index 1f82468..5e339da 100644
--- a/arch/arm/mach-msm/pmu.c
+++ b/arch/arm/mach-msm/pmu.c
@@ -33,7 +33,7 @@
static struct platform_device l2_pmu_device = {
.name = "l2-arm-pmu",
- .id = ARM_PMU_DEVICE_L2,
+ .id = ARM_PMU_DEVICE_L2CC,
.resource = l2_pmu_resource,
.num_resources = ARRAY_SIZE(l2_pmu_resource),
};
diff --git a/arch/arm/mach-msm/qdsp6v2/Makefile b/arch/arm/mach-msm/qdsp6v2/Makefile
index cee8f04..2ea1bc9 100644
--- a/arch/arm/mach-msm/qdsp6v2/Makefile
+++ b/arch/arm/mach-msm/qdsp6v2/Makefile
@@ -1,4 +1,3 @@
-obj-y += rtac.o
ifdef CONFIG_ARCH_MSM8X60
obj-y += audio_dev_ctl.o
obj-y += board-msm8x60-audio.o
@@ -13,10 +12,15 @@
endif
obj-$(CONFIG_MSM_QDSP6_APR) += apr.o apr_tal.o q6core.o dsp_debug.o
obj-y += audio_acdb.o
-ifndef CONFIG_ARCH_MSM9615
-obj-y += aac_in.o qcelp_in.o evrc_in.o amrnb_in.o audio_utils.o
-obj-y += audio_wma.o audio_wmapro.o audio_aac.o audio_multi_aac.o audio_utils_aio.o
-obj-$(CONFIG_MSM_QDSP6_CODECS) += q6audio_v1.o q6audio_v1_aio.o
-obj-$(CONFIG_MSM_ULTRASOUND) += ultrasound/
-obj-y += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_evrc.o audio_qcelp.o amrwb_in.o
+ifdef CONFIG_ARCH_MSM9615
+obj-y += rtac.o
endif
+obj-$(CONFIG_MSM_QDSP6_CODECS) += aac_in.o qcelp_in.o evrc_in.o amrnb_in.o audio_utils.o
+obj-$(CONFIG_MSM_QDSP6_CODECS) += audio_wma.o audio_wmapro.o audio_aac.o audio_multi_aac.o audio_utils_aio.o
+obj-$(CONFIG_MSM_QDSP6_CODECS) += rtac.o q6audio_v1.o q6audio_v1_aio.o
+obj-$(CONFIG_MSM_QDSP6_CODECS) += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_evrc.o audio_qcelp.o amrwb_in.o
+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) += rtac_v2.o q6audio_v2.o q6audio_v2_aio.o
+obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_evrc.o audio_qcelp.o amrwb_in.o
+obj-$(CONFIG_MSM_ULTRASOUND) += ultrasound/
diff --git a/arch/arm/mach-msm/qdsp6v2/q6audio_common.h b/arch/arm/mach-msm/qdsp6v2/q6audio_common.h
index e108de5..fc20847 100644
--- a/arch/arm/mach-msm/qdsp6v2/q6audio_common.h
+++ b/arch/arm/mach-msm/qdsp6v2/q6audio_common.h
@@ -15,8 +15,13 @@
#ifndef __Q6_AUDIO_COMMON_H__
#define __Q6_AUDIO_COMMON_H__
+#ifdef CONFIG_ARCH_MSMCOPPER
+#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);
diff --git a/arch/arm/mach-msm/qdsp6v2/q6audio_v2.c b/arch/arm/mach-msm/qdsp6v2/q6audio_v2.c
new file mode 100644
index 0000000..0db1ef4
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6v2/q6audio_v2.c
@@ -0,0 +1,90 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 <asm/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;
+ 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/arch/arm/mach-msm/qdsp6v2/q6audio_v2_aio.c b/arch/arm/mach-msm/qdsp6v2/q6audio_v2_aio.c
new file mode 100644
index 0000000..aab7b19
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6v2/q6audio_v2_aio.c
@@ -0,0 +1,112 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 <asm/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:
+ 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;
+ default:
+ break;
+ }
+}
diff --git a/arch/arm/mach-msm/qdsp6v2/rtac.c b/arch/arm/mach-msm/qdsp6v2/rtac.c
index 4ce9b030..8808375 100644
--- a/arch/arm/mach-msm/qdsp6v2/rtac.c
+++ b/arch/arm/mach-msm/qdsp6v2/rtac.c
@@ -22,8 +22,8 @@
#include <asm/atomic.h>
#include <mach/qdsp6v2/audio_acdb.h>
#include <mach/qdsp6v2/rtac.h>
-#include <sound/q6asm.h>
-#include <sound/q6adm.h>
+#include "q6audio_common.h"
+#include <sound/q6afe.h>
#ifndef CONFIG_RTAC
@@ -400,10 +400,8 @@
memcpy(rtac_adm_buffer, &payload_size, sizeof(u32));
if (payload_size != 0) {
if (payload_size > rtac_adm_user_buf_size) {
- pr_err("%s: Buffer set not big enough for "
- "returned data, buf size = %d, "
- "ret data = %d\n", __func__,
- rtac_adm_user_buf_size, payload_size);
+ pr_err("%s: Buffer set not big enough for returned data, buf size = %d,ret data = %d\n",
+ __func__, rtac_adm_user_buf_size, payload_size);
goto done;
}
memcpy(rtac_adm_buffer + sizeof(u32), payload, payload_size);
@@ -414,20 +412,20 @@
u32 send_adm_apr(void *buf, u32 opcode)
{
- s32 result;
- u32 count = 0;
- u32 bytes_returned = 0;
- u32 port_index = 0;
- u32 copp_id;
- u32 payload_size;
- struct apr_hdr adm_params;
+ s32 result;
+ u32 count = 0;
+ u32 bytes_returned = 0;
+ u32 port_index = 0;
+ u32 copp_id;
+ u32 payload_size;
+ struct apr_hdr adm_params;
pr_debug("%s\n", __func__);
if (copy_from_user(&count, (void *)buf, sizeof(count))) {
- pr_err("%s: Copy to user failed! buf = 0x%x\n",
- __func__, (unsigned int)buf);
- result = -EFAULT;
- goto done;
+ pr_err("%s: Copy to user failed! buf = 0x%x\n",
+ __func__, (unsigned int)buf);
+ result = -EFAULT;
+ goto done;
}
if (count <= 0) {
@@ -443,9 +441,8 @@
if (payload_size > MAX_PAYLOAD_SIZE) {
-
- pr_err("%s: Invalid payload size = %d\n",
- __func__, payload_size);
+ pr_err("%s: Invalid payload size = %d\n",
+ __func__, payload_size);
goto done;
}
@@ -521,9 +518,9 @@
if (rtac_adm_payload_size != 0) {
if (copy_to_user(buf, rtac_adm_buffer,
- rtac_adm_payload_size + sizeof(u32))) {
- pr_err("%s: Could not copy buffer to user,"
- "size = %d\n", __func__, payload_size);
+ rtac_adm_payload_size + sizeof(u32))) {
+ pr_err("%s: Could not copy buffer to user, size = %d\n",
+ __func__, payload_size);
goto done;
}
}
@@ -573,10 +570,8 @@
memcpy(rtac_asm_buffer, &payload_size, sizeof(u32));
if (payload_size) {
if (payload_size > rtac_asm_user_buf_size) {
- pr_err("%s: Buffer set not big enough for "
- "returned data, buf size = %d, "
- "ret data = %d\n", __func__,
- rtac_asm_user_buf_size, payload_size);
+ pr_err("%s: Buffer set not big enough for returned data, buf size = %d, ret data = %d\n",
+ __func__, rtac_asm_user_buf_size, payload_size);
goto done;
}
memcpy(rtac_asm_buffer + sizeof(u32), payload, payload_size);
@@ -587,19 +582,19 @@
u32 send_rtac_asm_apr(void *buf, u32 opcode)
{
- s32 result;
- u32 count = 0;
- u32 bytes_returned = 0;
- u32 session_id = 0;
- u32 payload_size;
- struct apr_hdr asm_params;
+ s32 result;
+ u32 count = 0;
+ u32 bytes_returned = 0;
+ u32 session_id = 0;
+ u32 payload_size;
+ struct apr_hdr asm_params;
pr_debug("%s\n", __func__);
if (copy_from_user(&count, (void *)buf, sizeof(count))) {
- pr_err("%s: Copy to user failed! buf = 0x%x\n",
- __func__, (unsigned int)buf);
- result = -EFAULT;
- goto done;
+ pr_err("%s: Copy to user failed! buf = 0x%x\n",
+ __func__, (unsigned int)buf);
+ result = -EFAULT;
+ goto done;
}
if (count <= 0) {
@@ -614,9 +609,8 @@
}
if (payload_size > MAX_PAYLOAD_SIZE) {
-
- pr_err("%s: Invalid payload size = %d\n",
- __func__, payload_size);
+ pr_err("%s: Invalid payload size = %d\n",
+ __func__, payload_size);
goto done;
}
@@ -643,7 +637,7 @@
/* Copy buffer to in-band payload */
if (copy_from_user(rtac_asm_buffer + sizeof(asm_params),
- buf + 3 * sizeof(u32), payload_size)) {
+ buf + 3 * sizeof(u32), payload_size)) {
pr_err("%s: Could not copy payload from user buffer\n",
__func__);
goto err;
@@ -691,9 +685,9 @@
if (rtac_asm_payload_size != 0) {
if (copy_to_user(buf, rtac_asm_buffer,
- rtac_asm_payload_size + sizeof(u32))) {
- pr_err("%s: Could not copy buffer to user,"
- "size = %d\n", __func__, payload_size);
+ rtac_asm_payload_size + sizeof(u32))) {
+ pr_err("%s: Could not copy buffer to user,size = %d\n",
+ __func__, payload_size);
goto done;
}
}
@@ -715,7 +709,6 @@
void rtac_set_voice_handle(u32 mode, void *handle)
{
pr_debug("%s\n", __func__);
-
mutex_lock(&rtac_voice_apr_mutex);
rtac_voice_apr_data[mode].apr_handle = handle;
mutex_unlock(&rtac_voice_apr_mutex);
@@ -724,7 +717,7 @@
bool rtac_make_voice_callback(u32 mode, uint32_t *payload, u32 payload_size)
{
if ((atomic_read(&rtac_voice_apr_data[mode].cmd_state) != 1) ||
- (mode >= RTAC_VOICE_MODES))
+ (mode >= RTAC_VOICE_MODES))
return false;
pr_debug("%s\n", __func__);
@@ -743,10 +736,8 @@
memcpy(rtac_voice_buffer, &payload_size, sizeof(u32));
if (payload_size) {
if (payload_size > rtac_voice_user_buf_size) {
- pr_err("%s: Buffer set not big enough for "
- "returned data, buf size = %d, "
- "ret data = %d\n", __func__,
- rtac_voice_user_buf_size, payload_size);
+ pr_err("%s: Buffer set not big enough for returned data, buf size = %d, ret data = %d\n",
+ __func__, rtac_voice_user_buf_size, payload_size);
goto done;
}
memcpy(rtac_voice_buffer + sizeof(u32), payload, payload_size);
@@ -757,19 +748,19 @@
u32 send_voice_apr(u32 mode, void *buf, u32 opcode)
{
- s32 result;
- u32 count = 0;
- u32 bytes_returned = 0;
- u32 payload_size;
- u16 dest_port;
- struct apr_hdr voice_params;
+ s32 result;
+ u32 count = 0;
+ u32 bytes_returned = 0;
+ u32 payload_size;
+ u16 dest_port;
+ struct apr_hdr voice_params;
pr_debug("%s\n", __func__);
if (copy_from_user(&count, (void *)buf, sizeof(count))) {
- pr_err("%s: Copy to user failed! buf = 0x%x\n",
- __func__, (unsigned int)buf);
- result = -EFAULT;
- goto done;
+ pr_err("%s: Copy to user failed! buf = 0x%x\n",
+ __func__, (unsigned int)buf);
+ result = -EFAULT;
+ goto done;
}
if (count <= 0) {
@@ -785,7 +776,7 @@
if (payload_size > MAX_PAYLOAD_SIZE) {
pr_err("%s: Invalid payload size = %d\n",
- __func__, payload_size);
+ __func__, payload_size);
goto done;
}
@@ -812,7 +803,7 @@
/* Copy buffer to in-band payload */
if (copy_from_user(rtac_voice_buffer + sizeof(voice_params),
- buf + 3 * sizeof(u32), payload_size)) {
+ buf + 3 * sizeof(u32), payload_size)) {
pr_err("%s: Could not copy payload from user buffer\n",
__func__);
goto err;
@@ -860,8 +851,8 @@
if (rtac_voice_payload_size != 0) {
if (copy_to_user(buf, rtac_voice_buffer,
rtac_voice_payload_size + sizeof(u32))) {
- pr_err("%s: Could not copy buffer to user,"
- "size = %d\n", __func__, payload_size);
+ pr_err("%s: Could not copy buffer to user,size = %d\n",
+ __func__, payload_size);
goto done;
}
}
@@ -972,7 +963,7 @@
mutex_init(&rtac_adm_mutex);
mutex_init(&rtac_adm_apr_mutex);
- rtac_adm_buffer = kmalloc(RTAC_BUF_SIZE, GFP_KERNEL);
+ rtac_adm_buffer = kzalloc(RTAC_BUF_SIZE, GFP_KERNEL);
if (rtac_adm_buffer == NULL) {
pr_err("%s: Could not allocate payload of size = %d\n",
__func__, RTAC_BUF_SIZE);
@@ -987,10 +978,11 @@
}
mutex_init(&rtac_asm_apr_mutex);
- rtac_asm_buffer = kmalloc(RTAC_BUF_SIZE, GFP_KERNEL);
+ rtac_asm_buffer = kzalloc(RTAC_BUF_SIZE, GFP_KERNEL);
if (rtac_asm_buffer == NULL) {
pr_err("%s: Could not allocate payload of size = %d\n",
__func__, RTAC_BUF_SIZE);
+ kzfree(rtac_adm_buffer);
goto nomem;
}
@@ -1004,10 +996,12 @@
mutex_init(&rtac_voice_mutex);
mutex_init(&rtac_voice_apr_mutex);
- rtac_voice_buffer = kmalloc(RTAC_BUF_SIZE, GFP_KERNEL);
+ rtac_voice_buffer = kzalloc(RTAC_BUF_SIZE, GFP_KERNEL);
if (rtac_voice_buffer == NULL) {
pr_err("%s: Could not allocate payload of size = %d\n",
__func__, RTAC_BUF_SIZE);
+ kzfree(rtac_adm_buffer);
+ kzfree(rtac_asm_buffer);
goto nomem;
}
diff --git a/arch/arm/mach-msm/qdsp6v2/rtac_v2.c b/arch/arm/mach-msm/qdsp6v2/rtac_v2.c
new file mode 100644
index 0000000..2d0607c
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6v2/rtac_v2.c
@@ -0,0 +1,1019 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/msm_audio_acdb.h>
+#include <asm/atomic.h>
+#include <mach/qdsp6v2/audio_acdb.h>
+#include <mach/qdsp6v2/rtac.h>
+#include "q6audio_common.h"
+#include <sound/q6afe-v2.h>
+
+#ifndef CONFIG_RTAC
+
+void rtac_add_adm_device(u32 port_id, u32 copp_id, u32 path_id, u32 popp_id) {}
+void rtac_remove_adm_device(u32 port_id) {}
+void rtac_remove_popp_from_adm_devices(u32 popp_id) {}
+void rtac_set_adm_handle(void *handle) {}
+bool rtac_make_adm_callback(uint32_t *payload, u32 payload_size)
+ {return false; }
+void rtac_set_asm_handle(u32 session_id, void *handle) {}
+bool rtac_make_asm_callback(u32 session_id, uint32_t *payload,
+ u32 payload_size) {return false; }
+void rtac_add_voice(u32 cvs_handle, u32 cvp_handle, u32 rx_afe_port,
+ u32 tx_afe_port, u32 session_id) {}
+void rtac_remove_voice(u32 cvs_handle) {}
+void rtac_set_voice_handle(u32 mode, void *handle) {}
+bool rtac_make_voice_callback(u32 mode, uint32_t *payload,
+ u32 payload_size) {return false; }
+
+#else
+
+#define VOICE_CMD_SET_PARAM 0x00011006
+#define VOICE_CMD_GET_PARAM 0x00011007
+#define VOICE_EVT_GET_PARAM_ACK 0x00011008
+
+/* Max size of payload (buf size - apr header) */
+#define MAX_PAYLOAD_SIZE 4076
+#define RTAC_MAX_ACTIVE_DEVICES 4
+#define RTAC_MAX_ACTIVE_VOICE_COMBOS 2
+#define RTAC_MAX_ACTIVE_POPP 8
+#define RTAC_BUF_SIZE 4096
+
+#define TIMEOUT_MS 1000
+
+/* APR data */
+struct rtac_apr_data {
+ void *apr_handle;
+ atomic_t cmd_state;
+ wait_queue_head_t cmd_wait;
+};
+
+static struct rtac_apr_data rtac_adm_apr_data;
+static struct rtac_apr_data rtac_asm_apr_data[SESSION_MAX+1];
+static struct rtac_apr_data rtac_voice_apr_data[RTAC_VOICE_MODES];
+
+
+/* ADM info & APR */
+struct rtac_adm_data {
+ uint32_t topology_id;
+ uint32_t afe_port;
+ uint32_t copp;
+ uint32_t num_of_popp;
+ uint32_t popp[RTAC_MAX_ACTIVE_POPP];
+};
+
+struct rtac_adm {
+ uint32_t num_of_dev;
+ struct rtac_adm_data device[RTAC_MAX_ACTIVE_DEVICES];
+};
+static struct rtac_adm rtac_adm_data;
+static u32 rtac_adm_payload_size;
+static u32 rtac_adm_user_buf_size;
+static u8 *rtac_adm_buffer;
+
+
+/* ASM APR */
+static u32 rtac_asm_payload_size;
+static u32 rtac_asm_user_buf_size;
+static u8 *rtac_asm_buffer;
+
+
+/* Voice info & APR */
+struct rtac_voice_data {
+ uint32_t tx_topology_id;
+ uint32_t rx_topology_id;
+ uint32_t tx_afe_port;
+ uint32_t rx_afe_port;
+ uint16_t cvs_handle;
+ uint16_t cvp_handle;
+};
+
+struct rtac_voice {
+ uint32_t num_of_voice_combos;
+ struct rtac_voice_data voice[RTAC_MAX_ACTIVE_VOICE_COMBOS];
+};
+
+static struct rtac_voice rtac_voice_data;
+static u32 rtac_voice_payload_size;
+static u32 rtac_voice_user_buf_size;
+static u8 *rtac_voice_buffer;
+static u32 voice_session_id[RTAC_MAX_ACTIVE_VOICE_COMBOS];
+
+
+struct mutex rtac_adm_mutex;
+struct mutex rtac_adm_apr_mutex;
+struct mutex rtac_asm_apr_mutex;
+struct mutex rtac_voice_mutex;
+struct mutex rtac_voice_apr_mutex;
+
+static int rtac_open(struct inode *inode, struct file *f)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static int rtac_release(struct inode *inode, struct file *f)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+/* ADM Info */
+void add_popp(u32 dev_idx, u32 port_id, u32 popp_id)
+{
+ u32 i = 0;
+
+ for (; i < rtac_adm_data.device[dev_idx].num_of_popp; i++)
+ if (rtac_adm_data.device[dev_idx].popp[i] == popp_id)
+ goto done;
+
+ if (rtac_adm_data.device[dev_idx].num_of_popp ==
+ RTAC_MAX_ACTIVE_POPP) {
+ pr_err("%s, Max POPP!\n", __func__);
+ goto done;
+ }
+ rtac_adm_data.device[dev_idx].popp[
+ rtac_adm_data.device[dev_idx].num_of_popp++] = popp_id;
+done:
+ return;
+}
+
+void rtac_add_adm_device(u32 port_id, u32 copp_id, u32 path_id, u32 popp_id)
+{
+ u32 i = 0;
+ pr_debug("%s: port_id = %d, popp_id = %d\n", __func__, port_id,
+ popp_id);
+
+ mutex_lock(&rtac_adm_mutex);
+ if (rtac_adm_data.num_of_dev == RTAC_MAX_ACTIVE_DEVICES) {
+ pr_err("%s, Can't add anymore RTAC devices!\n", __func__);
+ goto done;
+ }
+
+ /* Check if device already added */
+ if (rtac_adm_data.num_of_dev != 0) {
+ for (; i < rtac_adm_data.num_of_dev; i++) {
+ if (rtac_adm_data.device[i].afe_port == port_id) {
+ add_popp(i, port_id, popp_id);
+ goto done;
+ }
+ if (rtac_adm_data.device[i].num_of_popp ==
+ RTAC_MAX_ACTIVE_POPP) {
+ pr_err("%s, Max POPP!\n", __func__);
+ goto done;
+ }
+ }
+ }
+
+ /* Add device */
+ rtac_adm_data.num_of_dev++;
+
+ if (path_id == ADM_PATH_PLAYBACK)
+ rtac_adm_data.device[i].topology_id =
+ get_adm_rx_topology();
+ else
+ rtac_adm_data.device[i].topology_id =
+ get_adm_tx_topology();
+ rtac_adm_data.device[i].afe_port = port_id;
+ rtac_adm_data.device[i].copp = copp_id;
+ rtac_adm_data.device[i].popp[
+ rtac_adm_data.device[i].num_of_popp++] = popp_id;
+done:
+ mutex_unlock(&rtac_adm_mutex);
+ return;
+}
+
+static void shift_adm_devices(u32 dev_idx)
+{
+ for (; dev_idx < rtac_adm_data.num_of_dev; dev_idx++) {
+ memcpy(&rtac_adm_data.device[dev_idx],
+ &rtac_adm_data.device[dev_idx + 1],
+ sizeof(rtac_adm_data.device[dev_idx]));
+ memset(&rtac_adm_data.device[dev_idx + 1], 0,
+ sizeof(rtac_adm_data.device[dev_idx]));
+ }
+}
+
+static void shift_popp(u32 copp_idx, u32 popp_idx)
+{
+ for (; popp_idx < rtac_adm_data.device[copp_idx].num_of_popp;
+ popp_idx++) {
+ memcpy(&rtac_adm_data.device[copp_idx].popp[popp_idx],
+ &rtac_adm_data.device[copp_idx].popp[popp_idx + 1],
+ sizeof(uint32_t));
+ memset(&rtac_adm_data.device[copp_idx].popp[popp_idx + 1], 0,
+ sizeof(uint32_t));
+ }
+}
+
+void rtac_remove_adm_device(u32 port_id)
+{
+ s32 i;
+ pr_debug("%s: port_id = %d\n", __func__, port_id);
+
+ mutex_lock(&rtac_adm_mutex);
+ /* look for device */
+ for (i = 0; i < rtac_adm_data.num_of_dev; i++) {
+ if (rtac_adm_data.device[i].afe_port == port_id) {
+ memset(&rtac_adm_data.device[i], 0,
+ sizeof(rtac_adm_data.device[i]));
+ rtac_adm_data.num_of_dev--;
+
+ if (rtac_adm_data.num_of_dev >= 1) {
+ shift_adm_devices(i);
+ break;
+ }
+ }
+ }
+
+ mutex_unlock(&rtac_adm_mutex);
+ return;
+}
+
+void rtac_remove_popp_from_adm_devices(u32 popp_id)
+{
+ s32 i, j;
+ pr_debug("%s: popp_id = %d\n", __func__, popp_id);
+
+ mutex_lock(&rtac_adm_mutex);
+
+ for (i = 0; i < rtac_adm_data.num_of_dev; i++) {
+ for (j = 0; j < rtac_adm_data.device[i].num_of_popp; j++) {
+ if (rtac_adm_data.device[i].popp[j] == popp_id) {
+ rtac_adm_data.device[i].popp[j] = 0;
+ rtac_adm_data.device[i].num_of_popp--;
+ shift_popp(i, j);
+ }
+ }
+ }
+
+ mutex_unlock(&rtac_adm_mutex);
+}
+
+/* Voice Info */
+static void set_rtac_voice_data(int idx, u32 cvs_handle, u32 cvp_handle,
+ u32 rx_afe_port, u32 tx_afe_port,
+ u32 session_id)
+{
+ rtac_voice_data.voice[idx].tx_topology_id = get_voice_tx_topology();
+ rtac_voice_data.voice[idx].rx_topology_id = get_voice_rx_topology();
+ rtac_voice_data.voice[idx].tx_afe_port = tx_afe_port;
+ rtac_voice_data.voice[idx].rx_afe_port = rx_afe_port;
+ rtac_voice_data.voice[idx].cvs_handle = cvs_handle;
+ rtac_voice_data.voice[idx].cvp_handle = cvp_handle;
+
+ /* Store session ID for voice RTAC */
+ voice_session_id[idx] = session_id;
+}
+
+void rtac_add_voice(u32 cvs_handle, u32 cvp_handle, u32 rx_afe_port,
+ u32 tx_afe_port, u32 session_id)
+{
+ u32 i = 0;
+ pr_debug("%s\n", __func__);
+ mutex_lock(&rtac_voice_mutex);
+
+ if (rtac_voice_data.num_of_voice_combos ==
+ RTAC_MAX_ACTIVE_VOICE_COMBOS) {
+ pr_err("%s, Can't add anymore RTAC devices!\n", __func__);
+ goto done;
+ }
+
+ /* Check if device already added */
+ if (rtac_voice_data.num_of_voice_combos != 0) {
+ for (; i < rtac_voice_data.num_of_voice_combos; i++) {
+ if (rtac_voice_data.voice[i].cvs_handle ==
+ cvs_handle) {
+ set_rtac_voice_data(i, cvs_handle, cvp_handle,
+ rx_afe_port, tx_afe_port,
+ session_id);
+ goto done;
+ }
+ }
+ }
+
+ /* Add device */
+ rtac_voice_data.num_of_voice_combos++;
+ set_rtac_voice_data(i, cvs_handle, cvp_handle,
+ rx_afe_port, tx_afe_port,
+ session_id);
+done:
+ mutex_unlock(&rtac_voice_mutex);
+ return;
+}
+
+static void shift_voice_devices(u32 idx)
+{
+ for (; idx < rtac_voice_data.num_of_voice_combos - 1; idx++) {
+ memcpy(&rtac_voice_data.voice[idx],
+ &rtac_voice_data.voice[idx + 1],
+ sizeof(rtac_voice_data.voice[idx]));
+ voice_session_id[idx] = voice_session_id[idx + 1];
+ }
+}
+
+void rtac_remove_voice(u32 cvs_handle)
+{
+ u32 i = 0;
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&rtac_voice_mutex);
+ /* look for device */
+ for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) {
+ if (rtac_voice_data.voice[i].cvs_handle == cvs_handle) {
+ shift_voice_devices(i);
+ rtac_voice_data.num_of_voice_combos--;
+ memset(&rtac_voice_data.voice[
+ rtac_voice_data.num_of_voice_combos], 0,
+ sizeof(rtac_voice_data.voice
+ [rtac_voice_data.num_of_voice_combos]));
+ voice_session_id[rtac_voice_data.num_of_voice_combos]
+ = 0;
+ break;
+ }
+ }
+ mutex_unlock(&rtac_voice_mutex);
+ return;
+}
+
+static int get_voice_index(u32 cvs_handle)
+{
+ u32 i;
+
+ for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) {
+ if (rtac_voice_data.voice[i].cvs_handle == cvs_handle)
+ return i;
+ }
+
+ pr_err("%s: No voice index for CVS handle %d found returning 0\n",
+ __func__, cvs_handle);
+ return 0;
+}
+
+
+/* ADM APR */
+void rtac_set_adm_handle(void *handle)
+{
+ pr_debug("%s: handle = %d\n", __func__, (unsigned int)handle);
+
+ mutex_lock(&rtac_adm_apr_mutex);
+ rtac_adm_apr_data.apr_handle = handle;
+ mutex_unlock(&rtac_adm_apr_mutex);
+}
+
+bool rtac_make_adm_callback(uint32_t *payload, u32 payload_size)
+{
+ pr_debug("%s:cmd_state = %d\n", __func__,
+ atomic_read(&rtac_adm_apr_data.cmd_state));
+ if (atomic_read(&rtac_adm_apr_data.cmd_state) != 1)
+ return false;
+
+ /* Offset data for in-band payload */
+ rtac_copy_adm_payload_to_user(payload, payload_size);
+ atomic_set(&rtac_adm_apr_data.cmd_state, 0);
+ wake_up(&rtac_adm_apr_data.cmd_wait);
+ return true;
+}
+
+void rtac_copy_adm_payload_to_user(void *payload, u32 payload_size)
+{
+ pr_debug("%s\n", __func__);
+ rtac_adm_payload_size = payload_size;
+
+ memcpy(rtac_adm_buffer, &payload_size, sizeof(u32));
+ if (payload_size != 0) {
+ if (payload_size > rtac_adm_user_buf_size) {
+ pr_err("%s: Buffer set not big enough for returned data, buf size = %d, ret data = %d\n",
+ __func__, rtac_adm_user_buf_size, payload_size);
+ goto done;
+ }
+ memcpy(rtac_adm_buffer + sizeof(u32), payload, payload_size);
+ }
+done:
+ return;
+}
+
+u32 send_adm_apr(void *buf, u32 opcode)
+{
+ s32 result;
+ u32 count = 0;
+ u32 bytes_returned = 0;
+ u32 port_index = 0;
+ u32 copp_id;
+ u32 payload_size;
+ struct apr_hdr adm_params;
+ pr_debug("%s\n", __func__);
+
+ if (copy_from_user(&count, (void *)buf, sizeof(count))) {
+ pr_err("%s: Copy to user failed! buf = 0x%x\n",
+ __func__, (unsigned int)buf);
+ result = -EFAULT;
+ goto done;
+ }
+
+ if (count <= 0) {
+ pr_err("%s: Invalid buffer size = %d\n", __func__, count);
+ goto done;
+ }
+
+ if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) {
+ pr_err("%s: Could not copy payload size from user buffer\n",
+ __func__);
+ goto done;
+ }
+
+
+ if (payload_size > MAX_PAYLOAD_SIZE) {
+ pr_err("%s: Invalid payload size = %d\n",
+ __func__, payload_size);
+ goto done;
+ }
+
+ if (copy_from_user(&copp_id, buf + 2 * sizeof(u32), sizeof(u32))) {
+ pr_err("%s: Could not copy port id from user buffer\n",
+ __func__);
+ goto done;
+ }
+
+ for (port_index = 0; port_index < AFE_MAX_PORTS; port_index++) {
+ if (adm_get_copp_id(port_index) == copp_id)
+ break;
+ }
+ if (port_index >= AFE_MAX_PORTS) {
+ pr_err("%s: Could not find port index for copp = %d\n",
+ __func__, copp_id);
+ goto done;
+ }
+
+ mutex_lock(&rtac_adm_apr_mutex);
+ if (rtac_adm_apr_data.apr_handle == NULL) {
+ pr_err("%s: APR not initialized\n", __func__);
+ goto err;
+ }
+
+ /* Set globals for copy of returned payload */
+ rtac_adm_user_buf_size = count;
+ /* Copy buffer to in-band payload */
+ if (copy_from_user(rtac_adm_buffer + sizeof(adm_params),
+ buf + 3 * sizeof(u32), payload_size)) {
+ pr_err("%s: Could not copy payload from user buffer\n",
+ __func__);
+ goto err;
+ }
+
+ /* Pack header */
+ adm_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ adm_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ payload_size);
+ adm_params.src_svc = APR_SVC_ADM;
+ adm_params.src_domain = APR_DOMAIN_APPS;
+ adm_params.src_port = copp_id;
+ adm_params.dest_svc = APR_SVC_ADM;
+ adm_params.dest_domain = APR_DOMAIN_ADSP;
+ adm_params.dest_port = copp_id;
+ adm_params.token = copp_id;
+ adm_params.opcode = opcode;
+
+ memcpy(rtac_adm_buffer, &adm_params, sizeof(adm_params));
+ atomic_set(&rtac_adm_apr_data.cmd_state, 1);
+
+ pr_debug("%s: Sending RTAC command size = %d\n",
+ __func__, adm_params.pkt_size);
+
+ result = apr_send_pkt(rtac_adm_apr_data.apr_handle,
+ (uint32_t *)rtac_adm_buffer);
+ if (result < 0) {
+ pr_err("%s: Set params failed port = %d, copp = %d\n",
+ __func__, port_index, copp_id);
+ goto err;
+ }
+ /* Wait for the callback */
+ result = wait_event_timeout(rtac_adm_apr_data.cmd_wait,
+ (atomic_read(&rtac_adm_apr_data.cmd_state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ mutex_unlock(&rtac_adm_apr_mutex);
+ if (!result) {
+ pr_err("%s: Set params timed out port = %d, copp = %d\n",
+ __func__, port_index, copp_id);
+ goto done;
+ }
+
+ if (rtac_adm_payload_size != 0) {
+ if (copy_to_user(buf, rtac_adm_buffer,
+ rtac_adm_payload_size + sizeof(u32))) {
+ pr_err("%s: Could not copy buffer to user,size = %d\n",
+ __func__, payload_size);
+ goto done;
+ }
+ }
+
+ /* Return data written for SET & data read for GET */
+ if (opcode == ADM_CMD_GET_PP_PARAMS_V5)
+ bytes_returned = rtac_adm_payload_size;
+ else
+ bytes_returned = payload_size;
+done:
+ return bytes_returned;
+err:
+ mutex_unlock(&rtac_adm_apr_mutex);
+ return bytes_returned;
+}
+
+
+/* ASM APR */
+void rtac_set_asm_handle(u32 session_id, void *handle)
+{
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&rtac_asm_apr_mutex);
+ rtac_asm_apr_data[session_id].apr_handle = handle;
+ mutex_unlock(&rtac_asm_apr_mutex);
+}
+
+bool rtac_make_asm_callback(u32 session_id, uint32_t *payload,
+ u32 payload_size)
+{
+ if (atomic_read(&rtac_asm_apr_data[session_id].cmd_state) != 1)
+ return false;
+
+ pr_debug("%s\n", __func__);
+ /* Offset data for in-band payload */
+ rtac_copy_asm_payload_to_user(payload, payload_size);
+ atomic_set(&rtac_asm_apr_data[session_id].cmd_state, 0);
+ wake_up(&rtac_asm_apr_data[session_id].cmd_wait);
+ return true;
+}
+
+void rtac_copy_asm_payload_to_user(void *payload, u32 payload_size)
+{
+ pr_debug("%s\n", __func__);
+ rtac_asm_payload_size = payload_size;
+
+ memcpy(rtac_asm_buffer, &payload_size, sizeof(u32));
+ if (payload_size) {
+ if (payload_size > rtac_asm_user_buf_size) {
+ pr_err("%s: Buffer set not big enough for returned data, buf size = %d, ret data = %d\n",
+ __func__, rtac_asm_user_buf_size, payload_size);
+ goto done;
+ }
+ memcpy(rtac_asm_buffer + sizeof(u32), payload, payload_size);
+ }
+done:
+ return;
+}
+
+u32 send_rtac_asm_apr(void *buf, u32 opcode)
+{
+ s32 result;
+ u32 count = 0;
+ u32 bytes_returned = 0;
+ u32 session_id = 0;
+ u32 payload_size;
+ struct apr_hdr asm_params;
+ pr_debug("%s\n", __func__);
+
+ if (copy_from_user(&count, (void *)buf, sizeof(count))) {
+ pr_err("%s: Copy to user failed! buf = 0x%x\n",
+ __func__, (unsigned int)buf);
+ result = -EFAULT;
+ goto done;
+ }
+
+ if (count <= 0) {
+ pr_err("%s: Invalid buffer size = %d\n", __func__, count);
+ goto done;
+ }
+
+ if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) {
+ pr_err("%s: Could not copy payload size from user buffer\n",
+ __func__);
+ goto done;
+ }
+
+ if (payload_size > MAX_PAYLOAD_SIZE) {
+ pr_err("%s: Invalid payload size = %d\n",
+ __func__, payload_size);
+ goto done;
+ }
+
+ if (copy_from_user(&session_id, buf + 2 * sizeof(u32), sizeof(u32))) {
+ pr_err("%s: Could not copy session id from user buffer\n",
+ __func__);
+ goto done;
+ }
+ if (session_id > (SESSION_MAX + 1)) {
+ pr_err("%s: Invalid Session = %d\n", __func__, session_id);
+ goto done;
+ }
+
+ mutex_lock(&rtac_asm_apr_mutex);
+ if (session_id < SESSION_MAX+1) {
+ if (rtac_asm_apr_data[session_id].apr_handle == NULL) {
+ pr_err("%s: APR not initialized\n", __func__);
+ goto err;
+ }
+ }
+
+ /* Set globals for copy of returned payload */
+ rtac_asm_user_buf_size = count;
+
+ /* Copy buffer to in-band payload */
+ if (copy_from_user(rtac_asm_buffer + sizeof(asm_params),
+ buf + 3 * sizeof(u32), payload_size)) {
+ pr_err("%s: Could not copy payload from user buffer\n",
+ __func__);
+ goto err;
+ }
+
+ /* Pack header */
+ asm_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ asm_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ payload_size);
+ asm_params.src_svc = q6asm_get_apr_service_id(session_id);
+ asm_params.src_domain = APR_DOMAIN_APPS;
+ asm_params.src_port = (session_id << 8) | 0x0001;
+ asm_params.dest_svc = APR_SVC_ASM;
+ asm_params.dest_domain = APR_DOMAIN_ADSP;
+ asm_params.dest_port = (session_id << 8) | 0x0001;
+ asm_params.token = session_id;
+ asm_params.opcode = opcode;
+
+ memcpy(rtac_asm_buffer, &asm_params, sizeof(asm_params));
+ if (session_id < SESSION_MAX+1)
+ atomic_set(&rtac_asm_apr_data[session_id].cmd_state, 1);
+
+ pr_debug("%s: Sending RTAC command size = %d, session_id=%d\n",
+ __func__, asm_params.pkt_size, session_id);
+
+ result = apr_send_pkt(rtac_asm_apr_data[session_id].apr_handle,
+ (uint32_t *)rtac_asm_buffer);
+ if (result < 0) {
+ pr_err("%s: Set params failed session = %d\n",
+ __func__, session_id);
+ goto err;
+ }
+
+ /* Wait for the callback */
+ result = wait_event_timeout(rtac_asm_apr_data[session_id].cmd_wait,
+ (atomic_read(&rtac_asm_apr_data[session_id].cmd_state) == 0),
+ 5 * HZ);
+ mutex_unlock(&rtac_asm_apr_mutex);
+ if (!result) {
+ pr_err("%s: Set params timed out session = %d\n",
+ __func__, session_id);
+ goto done;
+ }
+
+ if (rtac_asm_payload_size != 0) {
+ if (copy_to_user(buf, rtac_asm_buffer,
+ rtac_asm_payload_size + sizeof(u32))) {
+ pr_err("%s: Could not copy buffer to user,size = %d\n",
+ __func__, payload_size);
+ goto done;
+ }
+ }
+
+ /* Return data written for SET & data read for GET */
+ if (opcode == ASM_STREAM_CMD_GET_PP_PARAMS_V2)
+ bytes_returned = rtac_asm_payload_size;
+ else
+ bytes_returned = payload_size;
+done:
+ return bytes_returned;
+err:
+ mutex_unlock(&rtac_asm_apr_mutex);
+ return bytes_returned;
+}
+
+
+/* Voice APR */
+void rtac_set_voice_handle(u32 mode, void *handle)
+{
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&rtac_voice_apr_mutex);
+ rtac_voice_apr_data[mode].apr_handle = handle;
+ mutex_unlock(&rtac_voice_apr_mutex);
+}
+
+bool rtac_make_voice_callback(u32 mode, uint32_t *payload, u32 payload_size)
+{
+ if ((atomic_read(&rtac_voice_apr_data[mode].cmd_state) != 1) ||
+ (mode >= RTAC_VOICE_MODES))
+ return false;
+
+ pr_debug("%s\n", __func__);
+ /* Offset data for in-band payload */
+ rtac_copy_voice_payload_to_user(payload, payload_size);
+ atomic_set(&rtac_voice_apr_data[mode].cmd_state, 0);
+ wake_up(&rtac_voice_apr_data[mode].cmd_wait);
+ return true;
+}
+
+void rtac_copy_voice_payload_to_user(void *payload, u32 payload_size)
+{
+ pr_debug("%s\n", __func__);
+ rtac_voice_payload_size = payload_size;
+
+ memcpy(rtac_voice_buffer, &payload_size, sizeof(u32));
+ if (payload_size) {
+ if (payload_size > rtac_voice_user_buf_size) {
+ pr_err("%s: Buffer set not big enough for returned data, buf size = %d, ret data = %d\n",
+ __func__, rtac_voice_user_buf_size, payload_size);
+ goto done;
+ }
+ memcpy(rtac_voice_buffer + sizeof(u32), payload, payload_size);
+ }
+done:
+ return;
+}
+
+u32 send_voice_apr(u32 mode, void *buf, u32 opcode)
+{
+ s32 result;
+ u32 count = 0;
+ u32 bytes_returned = 0;
+ u32 payload_size;
+ u16 dest_port;
+ struct apr_hdr voice_params;
+ pr_debug("%s\n", __func__);
+
+ if (copy_from_user(&count, (void *)buf, sizeof(count))) {
+ pr_err("%s: Copy to user failed! buf = 0x%x\n",
+ __func__, (unsigned int)buf);
+ result = -EFAULT;
+ goto done;
+ }
+
+ if (count <= 0) {
+ pr_err("%s: Invalid buffer size = %d\n", __func__, count);
+ goto done;
+ }
+
+ if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) {
+ pr_err("%s: Could not copy payload size from user buffer\n",
+ __func__);
+ goto done;
+ }
+
+ if (payload_size > MAX_PAYLOAD_SIZE) {
+ pr_err("%s: Invalid payload size = %d\n",
+ __func__, payload_size);
+ goto done;
+ }
+
+ if (copy_from_user(&dest_port, buf + 2 * sizeof(u32), sizeof(u32))) {
+ pr_err("%s: Could not copy port id from user buffer\n",
+ __func__);
+ goto done;
+ }
+
+ if ((mode != RTAC_CVP) && (mode != RTAC_CVS)) {
+ pr_err("%s: Invalid Mode for APR, mode = %d\n",
+ __func__, mode);
+ goto done;
+ }
+
+ mutex_lock(&rtac_voice_apr_mutex);
+ if (rtac_voice_apr_data[mode].apr_handle == NULL) {
+ pr_err("%s: APR not initialized\n", __func__);
+ goto err;
+ }
+
+ /* Set globals for copy of returned payload */
+ rtac_voice_user_buf_size = count;
+
+ /* Copy buffer to in-band payload */
+ if (copy_from_user(rtac_voice_buffer + sizeof(voice_params),
+ buf + 3 * sizeof(u32), payload_size)) {
+ pr_err("%s: Could not copy payload from user buffer\n",
+ __func__);
+ goto err;
+ }
+
+ /* Pack header */
+ voice_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ voice_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ payload_size);
+ voice_params.src_svc = 0;
+ voice_params.src_domain = APR_DOMAIN_APPS;
+ voice_params.src_port = voice_session_id[
+ get_voice_index(dest_port)];
+ voice_params.dest_svc = 0;
+ voice_params.dest_domain = APR_DOMAIN_MODEM;
+ voice_params.dest_port = dest_port;
+ voice_params.token = 0;
+ voice_params.opcode = opcode;
+
+ memcpy(rtac_voice_buffer, &voice_params, sizeof(voice_params));
+ atomic_set(&rtac_voice_apr_data[mode].cmd_state, 1);
+
+ pr_debug("%s: Sending RTAC command size = %d, opcode = %x\n",
+ __func__, voice_params.pkt_size, opcode);
+
+ result = apr_send_pkt(rtac_voice_apr_data[mode].apr_handle,
+ (uint32_t *)rtac_voice_buffer);
+ if (result < 0) {
+ pr_err("%s: apr_send_pkt failed opcode = %x\n",
+ __func__, opcode);
+ goto err;
+ }
+ /* Wait for the callback */
+ result = wait_event_timeout(rtac_voice_apr_data[mode].cmd_wait,
+ (atomic_read(&rtac_voice_apr_data[mode].cmd_state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ mutex_unlock(&rtac_voice_apr_mutex);
+ if (!result) {
+ pr_err("%s: apr_send_pkt timed out opcode = %x\n",
+ __func__, opcode);
+ goto done;
+ }
+
+ if (rtac_voice_payload_size != 0) {
+ if (copy_to_user(buf, rtac_voice_buffer,
+ rtac_voice_payload_size + sizeof(u32))) {
+ pr_err("%s: Could not copy buffer to user, size = %d\n",
+ __func__, payload_size);
+ goto done;
+ }
+ }
+
+ /* Return data written for SET & data read for GET */
+ if (opcode == VOICE_CMD_GET_PARAM)
+ bytes_returned = rtac_voice_payload_size;
+ else
+ bytes_returned = payload_size;
+done:
+ return bytes_returned;
+err:
+ mutex_unlock(&rtac_voice_apr_mutex);
+ return bytes_returned;
+}
+
+
+
+static long rtac_ioctl(struct file *f,
+ unsigned int cmd, unsigned long arg)
+{
+ s32 result = 0;
+ pr_debug("%s\n", __func__);
+
+ if (arg == 0) {
+ pr_err("%s: No data sent to driver!\n", __func__);
+ result = -EFAULT;
+ goto done;
+ }
+
+ switch (cmd) {
+ case AUDIO_GET_RTAC_ADM_INFO:
+ if (copy_to_user((void *)arg, &rtac_adm_data,
+ sizeof(rtac_adm_data)))
+ pr_err("%s: Could not copy to userspace!\n", __func__);
+ else
+ result = sizeof(rtac_adm_data);
+ break;
+ case AUDIO_GET_RTAC_VOICE_INFO:
+ if (copy_to_user((void *)arg, &rtac_voice_data,
+ sizeof(rtac_voice_data)))
+ pr_err("%s: Could not copy to userspace!\n", __func__);
+ else
+ result = sizeof(rtac_voice_data);
+ break;
+ case AUDIO_GET_RTAC_ADM_CAL:
+ result = send_adm_apr((void *)arg, ADM_CMD_GET_PP_PARAMS_V5);
+ break;
+ case AUDIO_SET_RTAC_ADM_CAL:
+ result = send_adm_apr((void *)arg, ADM_CMD_SET_PP_PARAMS_V5);
+ break;
+ case AUDIO_GET_RTAC_ASM_CAL:
+ result = send_rtac_asm_apr((void *)arg,
+ ASM_STREAM_CMD_GET_PP_PARAMS_V2);
+ break;
+ case AUDIO_SET_RTAC_ASM_CAL:
+ result = send_rtac_asm_apr((void *)arg,
+ ASM_STREAM_CMD_SET_PP_PARAMS_V2);
+ break;
+ case AUDIO_GET_RTAC_CVS_CAL:
+ result = send_voice_apr(RTAC_CVS, (void *)arg,
+ VOICE_CMD_GET_PARAM);
+ break;
+ case AUDIO_SET_RTAC_CVS_CAL:
+ result = send_voice_apr(RTAC_CVS, (void *)arg,
+ VOICE_CMD_SET_PARAM);
+ break;
+ case AUDIO_GET_RTAC_CVP_CAL:
+ result = send_voice_apr(RTAC_CVP, (void *)arg,
+ VOICE_CMD_GET_PARAM);
+ break;
+ case AUDIO_SET_RTAC_CVP_CAL:
+ result = send_voice_apr(RTAC_CVP, (void *)arg,
+ VOICE_CMD_SET_PARAM);
+ break;
+ default:
+ pr_err("%s: Invalid IOCTL, command = %d!\n",
+ __func__, cmd);
+ }
+done:
+ return result;
+}
+
+
+static const struct file_operations rtac_fops = {
+ .owner = THIS_MODULE,
+ .open = rtac_open,
+ .release = rtac_release,
+ .unlocked_ioctl = rtac_ioctl,
+};
+
+struct miscdevice rtac_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_rtac",
+ .fops = &rtac_fops,
+};
+
+static int __init rtac_init(void)
+{
+ int i = 0;
+ pr_debug("%s\n", __func__);
+
+ /* ADM */
+ memset(&rtac_adm_data, 0, sizeof(rtac_adm_data));
+ rtac_adm_apr_data.apr_handle = NULL;
+ atomic_set(&rtac_adm_apr_data.cmd_state, 0);
+ init_waitqueue_head(&rtac_adm_apr_data.cmd_wait);
+ mutex_init(&rtac_adm_mutex);
+ mutex_init(&rtac_adm_apr_mutex);
+
+ rtac_adm_buffer = kzalloc(RTAC_BUF_SIZE, GFP_KERNEL);
+ if (rtac_adm_buffer == NULL) {
+ pr_err("%s: Could not allocate payload of size = %d\n",
+ __func__, RTAC_BUF_SIZE);
+ goto nomem;
+ }
+
+ /* ASM */
+ for (i = 0; i < SESSION_MAX+1; i++) {
+ rtac_asm_apr_data[i].apr_handle = NULL;
+ atomic_set(&rtac_asm_apr_data[i].cmd_state, 0);
+ init_waitqueue_head(&rtac_asm_apr_data[i].cmd_wait);
+ }
+ mutex_init(&rtac_asm_apr_mutex);
+
+ rtac_asm_buffer = kzalloc(RTAC_BUF_SIZE, GFP_KERNEL);
+ if (rtac_asm_buffer == NULL) {
+ pr_err("%s: Could not allocate payload of size = %d\n",
+ __func__, RTAC_BUF_SIZE);
+ kzfree(rtac_adm_buffer);
+ goto nomem;
+ }
+
+ /* Voice */
+ memset(&rtac_voice_data, 0, sizeof(rtac_voice_data));
+ for (i = 0; i < RTAC_VOICE_MODES; i++) {
+ rtac_voice_apr_data[i].apr_handle = NULL;
+ atomic_set(&rtac_voice_apr_data[i].cmd_state, 0);
+ init_waitqueue_head(&rtac_voice_apr_data[i].cmd_wait);
+ }
+ mutex_init(&rtac_voice_mutex);
+ mutex_init(&rtac_voice_apr_mutex);
+
+ rtac_voice_buffer = kzalloc(RTAC_BUF_SIZE, GFP_KERNEL);
+ if (rtac_voice_buffer == NULL) {
+ pr_err("%s: Could not allocate payload of size = %d\n",
+ __func__, RTAC_BUF_SIZE);
+ kzfree(rtac_adm_buffer);
+ kzfree(rtac_asm_buffer);
+ goto nomem;
+ }
+
+ return misc_register(&rtac_misc);
+nomem:
+ return -ENOMEM;
+}
+
+module_init(rtac_init);
+
+MODULE_DESCRIPTION("MSM 8x60 Real-Time Audio Calibration driver");
+MODULE_LICENSE("GPL v2");
+
+#endif
diff --git a/arch/arm/mach-msm/qdss-stm.c b/arch/arm/mach-msm/qdss-stm.c
new file mode 100644
index 0000000..9ce6318
--- /dev/null
+++ b/arch/arm/mach-msm/qdss-stm.c
@@ -0,0 +1,595 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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/init.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include <mach/stm.h>
+
+#include "qdss-priv.h"
+
+#define stm_writel(stm, val, off) \
+ __raw_writel((val), stm.base + off)
+#define stm_readl(stm, val, off) \
+ __raw_readl(stm.base + off)
+
+#define NR_STM_CHANNEL (32)
+#define BYTES_PER_CHANNEL (256)
+
+enum {
+ STM_PKT_TYPE_DATA = 0x98,
+ STM_PKT_TYPE_FLAG = 0xE8,
+ STM_PKT_TYPE_TRIG = 0xF8,
+};
+
+enum {
+ STM_OPTION_MARKED = 0x10,
+};
+
+#define STM_TRACE_BUF_SIZE (1024)
+
+#define OST_START_TOKEN (0x30)
+#define OST_VERSION (0x1)
+
+#define stm_channel_addr(ch) \
+ (stm.chs.base + (ch * BYTES_PER_CHANNEL))
+#define stm_channel_off(type, opts) (type & ~opts)
+
+#define STM_LOCK() \
+do { \
+ mb(); \
+ stm_writel(stm, 0x0, CS_LAR); \
+} while (0)
+#define STM_UNLOCK() \
+do { \
+ stm_writel(stm, CS_UNLOCK_MAGIC, CS_LAR); \
+ mb(); \
+} while (0)
+
+#define STMSPER (0xE00)
+#define STMSPTER (0xE20)
+#define STMTCSR (0xE80)
+#define STMSYNCR (0xE90)
+
+#ifdef CONFIG_MSM_QDSS_STM_DEFAULT_ENABLE
+static int stm_boot_enable = 1;
+#else
+static int stm_boot_enable;
+#endif
+
+module_param_named(
+ stm_boot_enable, stm_boot_enable, int, S_IRUGO
+);
+
+static int stm_boot_nr_channel;
+
+module_param_named(
+ stm_boot_nr_channel, stm_boot_nr_channel, int, S_IRUGO
+);
+
+struct channel_space {
+ void __iomem *base;
+ unsigned long *bitmap;
+};
+
+struct stm_ctx {
+ void __iomem *base;
+ bool enabled;
+ struct qdss_source *src;
+ struct device *dev;
+ struct kobject *kobj;
+ uint32_t entity;
+ struct channel_space chs;
+};
+
+static struct stm_ctx stm = {
+ .entity = OST_ENTITY_ALL,
+};
+
+
+static void __stm_enable(void)
+{
+ STM_UNLOCK();
+
+ stm_writel(stm, 0x80, STMSYNCR);
+ stm_writel(stm, 0xFFFFFFFF, STMSPTER);
+ stm_writel(stm, 0xFFFFFFFF, STMSPER);
+ stm_writel(stm, 0x30003, STMTCSR);
+
+ STM_LOCK();
+}
+
+static int stm_enable(void)
+{
+ int ret;
+
+ if (stm.enabled) {
+ dev_err(stm.dev, "STM tracing already enabled\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = qdss_enable(stm.src);
+ if (ret)
+ goto err;
+
+ __stm_enable();
+
+ stm.enabled = true;
+
+ dev_info(stm.dev, "STM tracing enabled\n");
+ return 0;
+
+err:
+ return ret;
+}
+
+static void __stm_disable(void)
+{
+ STM_UNLOCK();
+
+ stm_writel(stm, 0x30000, STMTCSR);
+ stm_writel(stm, 0x0, STMSPER);
+ stm_writel(stm, 0x0, STMSPTER);
+
+ STM_LOCK();
+}
+
+static int stm_disable(void)
+{
+ int ret;
+
+ if (!stm.enabled) {
+ dev_err(stm.dev, "STM tracing already disabled\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ __stm_disable();
+
+ qdss_disable(stm.src);
+
+ stm.enabled = false;
+
+ dev_info(stm.dev, "STM tracing disabled\n");
+ return 0;
+
+err:
+ return ret;
+}
+
+static uint32_t stm_channel_alloc(uint32_t off)
+{
+ uint32_t ch;
+
+ do {
+ ch = find_next_zero_bit(stm.chs.bitmap, NR_STM_CHANNEL, off);
+ } while ((ch < NR_STM_CHANNEL) && test_and_set_bit(ch, stm.chs.bitmap));
+
+ return ch;
+}
+
+static void stm_channel_free(uint32_t ch)
+{
+ clear_bit(ch, stm.chs.bitmap);
+}
+
+static int stm_send(void *addr, const void *data, uint32_t size)
+{
+ uint64_t prepad = 0;
+ uint64_t postpad = 0;
+ char *pad;
+ uint8_t off, endoff;
+ uint32_t len = size;
+
+ /* only 64bit writes are supported, we rely on the compiler to
+ * generate STRD instruction for the casted 64bit assignments
+ */
+
+ off = (unsigned long)data & 0x7;
+
+ if (off) {
+ endoff = 8 - off;
+ pad = (char *)&prepad;
+ pad += off;
+
+ while (endoff && size) {
+ *pad++ = *(char *)data++;
+ endoff--;
+ size--;
+ }
+ *(volatile uint64_t __force *)addr = prepad;
+ }
+
+ /* now we are 64bit aligned */
+ while (size >= 8) {
+ *(volatile uint64_t __force *)addr = *(uint64_t *)data;
+ data += 8;
+ size -= 8;
+ }
+
+ if (size) {
+ pad = (char *)&postpad;
+
+ while (size) {
+ *pad++ = *(char *)data++;
+ size--;
+ }
+ *(volatile uint64_t __force *)addr = postpad;
+ }
+
+ return roundup(len + off, 8);
+}
+
+static int stm_trace_ost_header(unsigned long ch_addr, uint32_t options,
+ uint8_t entity_id, uint8_t proto_id,
+ const void *payload_data, uint32_t payload_size)
+{
+ void *addr;
+ uint8_t prepad_size;
+ uint64_t header;
+ char *hdr;
+
+ hdr = (char *)&header;
+
+ hdr[0] = OST_START_TOKEN;
+ hdr[1] = OST_VERSION;
+ hdr[2] = entity_id;
+ hdr[3] = proto_id;
+ prepad_size = (unsigned long)payload_data & 0x7;
+ *(uint32_t *)(hdr + 4) = (prepad_size << 24) | payload_size;
+
+ /* for 64bit writes, header is expected to be of the D32M, D32M */
+ options |= STM_OPTION_MARKED;
+ options &= ~STM_OPTION_TIMESTAMPED;
+ addr = (void *)(ch_addr | stm_channel_off(STM_PKT_TYPE_DATA, options));
+
+ return stm_send(addr, &header, sizeof(header));
+}
+
+static int stm_trace_data(unsigned long ch_addr, uint32_t options,
+ const void *data, uint32_t size)
+{
+ void *addr;
+
+ options &= ~STM_OPTION_TIMESTAMPED;
+ addr = (void *)(ch_addr | stm_channel_off(STM_PKT_TYPE_DATA, options));
+
+ return stm_send(addr, data, size);
+}
+
+static int stm_trace_ost_tail(unsigned long ch_addr, uint32_t options)
+{
+ void *addr;
+ uint64_t tail = 0x0;
+
+ addr = (void *)(ch_addr | stm_channel_off(STM_PKT_TYPE_FLAG, options));
+
+ return stm_send(addr, &tail, sizeof(tail));
+}
+
+static inline int __stm_trace(uint32_t options, uint8_t entity_id,
+ uint8_t proto_id, const void *data, uint32_t size)
+{
+ int len = 0;
+ uint32_t ch;
+ unsigned long ch_addr;
+
+ /* allocate channel and get the channel address */
+ ch = stm_channel_alloc(0);
+ ch_addr = (unsigned long)stm_channel_addr(ch);
+
+ /* send the ost header */
+ len += stm_trace_ost_header(ch_addr, options, entity_id, proto_id, data,
+ size);
+
+ /* send the payload data */
+ len += stm_trace_data(ch_addr, options, data, size);
+
+ /* send the ost tail */
+ len += stm_trace_ost_tail(ch_addr, options);
+
+ /* we are done, free the channel */
+ stm_channel_free(ch);
+
+ return len;
+}
+
+/**
+ * stm_trace - trace the binary or string data through STM
+ * @options: tracing options - guaranteed, timestamped, etc
+ * @entity_id: entity representing the trace data
+ * @proto_id: protocol id to distinguish between different binary formats
+ * @data: pointer to binary or string data buffer
+ * @size: size of data to send
+ *
+ * Packetizes the data as the payload to an OST packet and sends it over STM
+ *
+ * CONTEXT:
+ * Can be called from any context.
+ *
+ * RETURNS:
+ * number of bytes transfered over STM
+ */
+int stm_trace(uint32_t options, uint8_t entity_id, uint8_t proto_id,
+ const void *data, uint32_t size)
+{
+ /* we don't support sizes more than 24bits (0 to 23) */
+ if (!(stm.enabled && (stm.entity & entity_id) &&
+ (size < 0x1000000)))
+ return 0;
+
+ return __stm_trace(options, entity_id, proto_id, data, size);
+}
+EXPORT_SYMBOL(stm_trace);
+
+static ssize_t stm_write(struct file *file, const char __user *data,
+ size_t size, loff_t *ppos)
+{
+ char *buf;
+
+ if (!stm.enabled)
+ return -EINVAL;
+
+ if (!(stm.entity & OST_ENTITY_DEV_NODE))
+ return size;
+
+ if (size > STM_TRACE_BUF_SIZE)
+ size = STM_TRACE_BUF_SIZE;
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (copy_from_user(buf, data, size)) {
+ kfree(buf);
+ dev_dbg(stm.dev, "%s: copy_from_user failed\n", __func__);
+ return -EFAULT;
+ }
+
+ __stm_trace(STM_OPTION_TIMESTAMPED, OST_ENTITY_DEV_NODE, 0, buf, size);
+
+ kfree(buf);
+
+ return size;
+}
+
+static const struct file_operations stm_fops = {
+ .owner = THIS_MODULE,
+ .write = stm_write,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice stm_misc = {
+ .name = "msm_stm",
+ .minor = MISC_DYNAMIC_MINOR,
+ .fops = &stm_fops,
+};
+
+#define STM_ATTR(__name) \
+static struct kobj_attribute __name##_attr = \
+ __ATTR(__name, S_IRUGO | S_IWUSR, __name##_show, __name##_store)
+
+static ssize_t enabled_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ int ret = 0;
+ unsigned long val;
+
+ if (sscanf(buf, "%lx", &val) != 1)
+ return -EINVAL;
+
+ if (val)
+ ret = stm_enable();
+ else
+ ret = stm_disable();
+
+ if (ret)
+ return ret;
+ return n;
+}
+static ssize_t enabled_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ unsigned long val = stm.enabled;
+ return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+STM_ATTR(enabled);
+
+static ssize_t entity_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned long val;
+
+ if (sscanf(buf, "%lx", &val) != 1)
+ return -EINVAL;
+
+ stm.entity = val;
+ return n;
+}
+static ssize_t entity_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ unsigned long val = stm.entity;
+ return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+STM_ATTR(entity);
+
+static int __devinit stm_sysfs_init(void)
+{
+ int ret;
+
+ stm.kobj = kobject_create_and_add("stm", qdss_get_modulekobj());
+ if (!stm.kobj) {
+ dev_err(stm.dev, "failed to create STM sysfs kobject\n");
+ ret = -ENOMEM;
+ goto err_create;
+ }
+
+ ret = sysfs_create_file(stm.kobj, &enabled_attr.attr);
+ if (ret) {
+ dev_err(stm.dev, "failed to create STM sysfs enabled attr\n");
+ goto err_file;
+ }
+
+ if (sysfs_create_file(stm.kobj, &entity_attr.attr))
+ dev_err(stm.dev, "failed to create STM sysfs entity attr\n");
+
+ return 0;
+err_file:
+ kobject_put(stm.kobj);
+err_create:
+ return ret;
+}
+
+static void __devexit stm_sysfs_exit(void)
+{
+ sysfs_remove_file(stm.kobj, &entity_attr.attr);
+ sysfs_remove_file(stm.kobj, &enabled_attr.attr);
+ kobject_put(stm.kobj);
+}
+
+static int __devinit stm_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *res;
+ size_t res_size, bitmap_size;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -EINVAL;
+ goto err_res0;
+ }
+
+ stm.base = ioremap_nocache(res->start, resource_size(res));
+ if (!stm.base) {
+ ret = -EINVAL;
+ goto err_ioremap0;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ ret = -EINVAL;
+ goto err_res1;
+ }
+
+ if (stm_boot_nr_channel) {
+ res_size = min((resource_size_t)(stm_boot_nr_channel *
+ BYTES_PER_CHANNEL), resource_size(res));
+ bitmap_size = stm_boot_nr_channel * sizeof(long);
+ } else {
+ res_size = min((resource_size_t)(NR_STM_CHANNEL *
+ BYTES_PER_CHANNEL), resource_size(res));
+ bitmap_size = NR_STM_CHANNEL * sizeof(long);
+ }
+
+ stm.chs.bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+ if (!stm.chs.bitmap) {
+ ret = -ENOMEM;
+ goto err_bitmap;
+ }
+
+ stm.chs.base = ioremap_nocache(res->start, res_size);
+ if (!stm.chs.base) {
+ ret = -EINVAL;
+ goto err_ioremap1;
+ }
+
+ stm.dev = &pdev->dev;
+
+ ret = misc_register(&stm_misc);
+ if (ret)
+ goto err_misc;
+
+ stm.src = qdss_get("msm_stm");
+ if (IS_ERR(stm.src)) {
+ ret = PTR_ERR(stm.src);
+ goto err_qdssget;
+ }
+
+ ret = stm_sysfs_init();
+ if (ret)
+ goto err_sysfs;
+
+ if (stm_boot_enable)
+ stm_enable();
+
+ dev_info(stm.dev, "STM initialized\n");
+ return 0;
+
+err_sysfs:
+ qdss_put(stm.src);
+err_qdssget:
+ misc_deregister(&stm_misc);
+err_misc:
+ iounmap(stm.chs.base);
+err_ioremap1:
+ kfree(stm.chs.bitmap);
+err_bitmap:
+err_res1:
+ iounmap(stm.base);
+err_ioremap0:
+err_res0:
+ dev_err(stm.dev, "STM init failed\n");
+ return ret;
+}
+
+static int __devexit stm_remove(struct platform_device *pdev)
+{
+ if (stm.enabled)
+ stm_disable();
+ stm_sysfs_exit();
+ qdss_put(stm.src);
+ misc_deregister(&stm_misc);
+ iounmap(stm.chs.base);
+ kfree(stm.chs.bitmap);
+ iounmap(stm.base);
+
+ return 0;
+}
+
+static struct platform_driver stm_driver = {
+ .probe = stm_probe,
+ .remove = __devexit_p(stm_remove),
+ .driver = {
+ .name = "msm_stm",
+ },
+};
+
+static int __init stm_init(void)
+{
+ return platform_driver_register(&stm_driver);
+}
+module_init(stm_init);
+
+static void __exit stm_exit(void)
+{
+ platform_driver_unregister(&stm_driver);
+}
+module_exit(stm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CoreSight System Trace Macrocell driver");
diff --git a/arch/arm/mach-msm/rpm-regulator-9615.c b/arch/arm/mach-msm/rpm-regulator-9615.c
index 23c0ee3..82ac3d8 100644
--- a/arch/arm/mach-msm/rpm-regulator-9615.c
+++ b/arch/arm/mach-msm/rpm-regulator-9615.c
@@ -50,6 +50,11 @@
.hpm = REQUEST_MEMBER(0, 0x00000C00, 10),
};
+static struct rpm_vreg_parts corner_parts = {
+ .request_len = 1,
+ .uV = REQUEST_MEMBER(0, 0x00000003, 0),
+};
+
/* Physically available PMIC regulator voltage setpoint ranges */
static struct vreg_range pldo_ranges[] = {
VOLTAGE_RANGE( 750000, 1487500, 12500),
@@ -72,16 +77,22 @@
VOLTAGE_RANGE(1500000, 3075000, 25000),
};
+static struct vreg_range corner_ranges[] = {
+ VOLTAGE_RANGE(RPM_VREG_CORNER_NONE, RPM_VREG_CORNER_HIGH, 1),
+};
+
static struct vreg_set_points pldo_set_points = SET_POINTS(pldo_ranges);
static struct vreg_set_points nldo_set_points = SET_POINTS(nldo_ranges);
static struct vreg_set_points nldo1200_set_points = SET_POINTS(nldo1200_ranges);
static struct vreg_set_points smps_set_points = SET_POINTS(smps_ranges);
+static struct vreg_set_points corner_set_points = SET_POINTS(corner_ranges);
static struct vreg_set_points *all_set_points[] = {
&pldo_set_points,
&nldo_set_points,
&nldo1200_set_points,
&smps_set_points,
+ &corner_set_points,
};
#define LDO(_id, _name, _name_pc, _ranges, _hpm_min_load) \
@@ -127,6 +138,19 @@
.rdesc_pc.name = _name_pc, \
}
+#define CORNER(_id, _rpm_id, _name, _ranges) \
+ [RPM_VREG_ID_PM8018_##_id] = { \
+ .req = { \
+ [0] = { .id = MSM_RPM_ID_##_rpm_id, }, \
+ [1] = { .id = -1, }, \
+ }, \
+ .type = RPM_REGULATOR_TYPE_CORNER, \
+ .set_points = &_ranges##_set_points, \
+ .part = &corner_parts, \
+ .id = RPM_VREG_ID_PM8018_##_id, \
+ .rdesc.name = _name, \
+ }
+
static struct vreg vregs[] = {
LDO(L2, "8018_l2", "8018_l2_pc", pldo, LDO_50),
LDO(L3, "8018_l3", "8018_l3_pc", pldo, LDO_50),
@@ -149,6 +173,8 @@
SMPS(S5, "8018_s5", "8018_s5_pc", smps, SMPS_1500),
LVS(LVS1, "8018_lvs1", "8018_lvs1_pc"),
+
+ CORNER(VDD_DIG_CORNER, VOLTAGE_CORNER, "vdd_dig_corner", corner),
};
static const char *pin_control_label[] = {
diff --git a/arch/arm/mach-msm/rpm.c b/arch/arm/mach-msm/rpm.c
index 44e50dd9..5ab484e2 100644
--- a/arch/arm/mach-msm/rpm.c
+++ b/arch/arm/mach-msm/rpm.c
@@ -275,13 +275,13 @@
int rc;
do {
- while (!gic_is_spi_pending(msm_rpm_data.irq_ack) &&
+ while (!gic_is_irq_pending(msm_rpm_data.irq_ack) &&
msm_rpm_request) {
if (allow_async_completion)
spin_unlock(&msm_rpm_irq_lock);
- if (gic_is_spi_pending(msm_rpm_data.irq_err))
+ if (gic_is_irq_pending(msm_rpm_data.irq_err))
msm_rpm_err_fatal();
- gic_clear_spi_pending(msm_rpm_data.irq_err);
+ gic_clear_irq_pending(msm_rpm_data.irq_err);
udelay(1);
if (allow_async_completion)
spin_lock(&msm_rpm_irq_lock);
@@ -291,7 +291,7 @@
break;
rc = msm_rpm_process_ack_interrupt();
- gic_clear_spi_pending(msm_rpm_data.irq_ack);
+ gic_clear_irq_pending(msm_rpm_data.irq_ack);
} while (rc);
}
diff --git a/arch/arm/mach-msm/subsystem_map.c b/arch/arm/mach-msm/subsystem_map.c
index fcb8517..5f5a02b43 100644
--- a/arch/arm/mach-msm/subsystem_map.c
+++ b/arch/arm/mach-msm/subsystem_map.c
@@ -38,8 +38,10 @@
VIDEO_DOMAIN,
VIDEO_DOMAIN,
CAMERA_DOMAIN,
- DISPLAY_DOMAIN,
- ROTATOR_DOMAIN,
+ DISPLAY_READ_DOMAIN,
+ DISPLAY_WRITE_DOMAIN,
+ ROTATOR_SRC_DOMAIN,
+ ROTATOR_DST_DOMAIN,
0xFFFFFFFF
};
diff --git a/arch/arm/mach-msm/wcnss-ssr-8960.c b/arch/arm/mach-msm/wcnss-ssr-8960.c
index 266c8b4..793ef7f 100644
--- a/arch/arm/mach-msm/wcnss-ssr-8960.c
+++ b/arch/arm/mach-msm/wcnss-ssr-8960.c
@@ -30,11 +30,7 @@
#define MODULE_NAME "wcnss_8960"
#define MAX_BUF_SIZE 0x51
-static void riva_smsm_cb_fn(struct work_struct *);
-static DECLARE_WORK(riva_smsm_cb_work, riva_smsm_cb_fn);
-static void riva_fatal_fn(struct work_struct *);
-static DECLARE_WORK(riva_fatal_work, riva_fatal_fn);
static struct delayed_work cancel_vote_work;
static void *riva_ramdump_dev;
@@ -42,14 +38,6 @@
static int ss_restart_inprogress;
static int enable_riva_ssr;
-static void riva_smsm_cb_fn(struct work_struct *work)
-{
- if (!enable_riva_ssr)
- panic(MODULE_NAME ": SMSM reset request received from Riva");
- else
- subsystem_restart("riva");
-}
-
static void smsm_state_cb_hdlr(void *data, uint32_t old_state,
uint32_t new_state)
{
@@ -58,60 +46,62 @@
unsigned smem_reset_size;
unsigned size;
+ riva_crash = true;
+
+ pr_err("%s: smsm state changed\n", MODULE_NAME);
+
if (!(new_state & SMSM_RESET))
return;
- riva_crash = true;
- pr_err("%s: smsm state changed to smsm reset\n", MODULE_NAME);
-
- smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_WCNSS0,
- &smem_reset_size);
-
- if (!smem_reset_reason || !smem_reset_size) {
- pr_err("%s: wcnss subsystem failure reason: %s\n", __func__,
- "(unknown, smem_get_entry failed)");
- } else if (!smem_reset_reason[0]) {
- pr_err("%s: wcnss subsystem failure reason: %s\n", __func__,
- "(unknown, init string found)");
- } else {
- size = smem_reset_size < MAX_BUF_SIZE ? smem_reset_size :
- (MAX_BUF_SIZE - 1);
- memcpy(buffer, smem_reset_reason, size);
- buffer[size] = '\0';
- pr_err("%s: wcnss subsystem failure reason: %s\n", __func__,
- buffer);
- memset(smem_reset_reason, 0, smem_reset_size);
- wmb();
- }
-
if (ss_restart_inprogress) {
pr_err("%s: Ignoring smsm reset req, restart in progress\n",
MODULE_NAME);
return;
}
- ss_restart_inprogress = true;
- schedule_work(&riva_smsm_cb_work);
-}
-static void riva_fatal_fn(struct work_struct *work)
-{
if (!enable_riva_ssr)
- panic(MODULE_NAME ": Watchdog bite received from Riva");
- else
- subsystem_restart("riva");
+ panic(MODULE_NAME ": SMSM reset request received from Riva");
+
+ smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_WCNSS0,
+ &smem_reset_size);
+
+ if (!smem_reset_reason || !smem_reset_size) {
+ pr_err("%s: wcnss subsystem failure reason: %s\n",
+ __func__, "(unknown, smem_get_entry failed)");
+ } else if (!smem_reset_reason[0]) {
+ pr_err("%s: wcnss subsystem failure reason: %s\n",
+ __func__, "(unknown, init string found)");
+ } else {
+ size = smem_reset_size < MAX_BUF_SIZE ? smem_reset_size :
+ (MAX_BUF_SIZE - 1);
+ memcpy(buffer, smem_reset_reason, size);
+ buffer[size] = '\0';
+ pr_err("%s: wcnss subsystem failure reason: %s\n",
+ __func__, buffer);
+ memset(smem_reset_reason, 0, smem_reset_size);
+ wmb();
+ }
+
+ ss_restart_inprogress = true;
+ subsystem_restart("riva");
}
static irqreturn_t riva_wdog_bite_irq_hdlr(int irq, void *dev_id)
{
- int ret;
+ riva_crash = true;
if (ss_restart_inprogress) {
pr_err("%s: Ignoring riva bite irq, restart in progress\n",
MODULE_NAME);
return IRQ_HANDLED;
}
+
+ if (!enable_riva_ssr)
+ panic(MODULE_NAME ": Watchdog bite received from Riva");
+
ss_restart_inprogress = true;
- ret = schedule_work(&riva_fatal_work);
+ subsystem_restart("riva");
+
return IRQ_HANDLED;
}
@@ -229,8 +219,8 @@
ret = smsm_state_cb_register(SMSM_WCNSS_STATE, SMSM_RESET,
smsm_state_cb_hdlr, 0);
if (ret < 0) {
- pr_err("%s: Unable to register smsm callback for Riva Reset!"
- " (%d)\n", MODULE_NAME, ret);
+ pr_err("%s: Unable to register smsm callback for Riva Reset! %d\n",
+ MODULE_NAME, ret);
goto out;
}
ret = request_irq(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ,
@@ -238,8 +228,8 @@
"riva_wdog", NULL);
if (ret < 0) {
- pr_err("%s: Unable to register for Riva bite interrupt"
- " (%d)\n", MODULE_NAME, ret);
+ pr_err("%s: Unable to register for Riva bite interrupt (%d)\n",
+ MODULE_NAME, ret);
goto out;
}
ret = riva_restart_init();
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
index 4387287..e62af21 100644
--- a/arch/arm/vfp/vfpmodule.c
+++ b/arch/arm/vfp/vfpmodule.c
@@ -20,6 +20,7 @@
#include <linux/init.h>
#include <linux/uaccess.h>
#include <linux/user.h>
+#include <linux/proc_fs.h>
#include <asm/cp15.h>
#include <asm/cputype.h>
@@ -87,6 +88,11 @@
}
/*
+ * Used for reporting emulation statistics via /proc
+ */
+static atomic64_t vfp_bounce_count = ATOMIC64_INIT(0);
+
+/*
* Per-thread VFP initialization.
*/
static void vfp_thread_flush(struct thread_info *thread)
@@ -337,6 +343,7 @@
u32 fpscr, orig_fpscr, fpsid, exceptions;
pr_debug("VFP: bounce: trigger %08x fpexc %08x\n", trigger, fpexc);
+ atomic64_inc(&vfp_bounce_count);
/*
* At this point, FPEXC can have the following configuration:
@@ -648,6 +655,26 @@
return NOTIFY_OK;
}
+#ifdef CONFIG_PROC_FS
+static int proc_read_status(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ char *p = page;
+ int len;
+
+ p += snprintf(p, PAGE_SIZE, "%llu\n", atomic64_read(&vfp_bounce_count));
+
+ len = (p - page) - off;
+ if (len < 0)
+ len = 0;
+
+ *eof = (len <= count) ? 1 : 0;
+ *start = page + off;
+
+ return len;
+}
+#endif
+
/*
* VFP support code initialisation.
*/
@@ -655,7 +682,9 @@
{
unsigned int vfpsid;
unsigned int cpu_arch = cpu_architecture();
-
+#ifdef CONFIG_PROC_FS
+ static struct proc_dir_entry *procfs_entry;
+#endif
if (cpu_arch >= CPU_ARCH_ARMv6)
on_each_cpu(vfp_enable, NULL, 1);
@@ -724,6 +753,16 @@
elf_hwcap |= HWCAP_VFPv4;
}
}
+
+#ifdef CONFIG_PROC_FS
+ procfs_entry = create_proc_entry("cpu/vfp_bounce", S_IRUGO, NULL);
+
+ if (procfs_entry)
+ procfs_entry->read_proc = proc_read_status;
+ else
+ pr_err("Failed to create procfs node for VFP bounce reporting\n");
+#endif
+
return 0;
}
diff --git a/drivers/bluetooth/hci_ibs.c b/drivers/bluetooth/hci_ibs.c
index 2a6f3f8..6845020 100644
--- a/drivers/bluetooth/hci_ibs.c
+++ b/drivers/bluetooth/hci_ibs.c
@@ -5,7 +5,7 @@
* protocol extension to H4.
*
* Copyright (C) 2007 Texas Instruments, Inc.
- * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2010, 2012 Code Aurora Forum. All rights reserved.
*
* Acknowledgements:
* This file is based on hci_ll.c, which was...
@@ -37,7 +37,9 @@
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
+#include <linux/ftrace.h>
#include <linux/poll.h>
+#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/tty.h>
@@ -92,6 +94,15 @@
HCI_IBS_RX_VOTE_CLOCK_OFF,
};
+/* HCI_IBS state for the WorkQueue */
+enum hci_ibs_wq_state_e {
+ HCI_IBS_WQ_INIT_STATE = 0,
+ HCI_IBS_WQ_TX_VOTE_OFF,
+ HCI_IBS_WQ_RX_VOTE_OFF,
+ HCI_IBS_WQ_AWAKE_RX,
+ HCI_IBS_WQ_AWAKE_DEVICE,
+};
+
static unsigned long wake_retrans = 1;
static unsigned long tx_idle_delay = (HZ * 2);
@@ -112,6 +123,11 @@
unsigned long rx_vote; /* clock must be on for RX */
struct timer_list tx_idle_timer;
struct timer_list wake_retrans_timer;
+ struct workqueue_struct *workqueue;
+ struct work_struct ws_ibs;
+ unsigned long ibs_wq_state;
+ void *ibs_hu; /* keeps the hci_uart pointer for reference */
+
/* debug */
unsigned long ibs_sent_wacks;
unsigned long ibs_sent_slps;
@@ -242,6 +258,56 @@
return err;
}
+static void ibs_wq(struct work_struct *work)
+{
+ unsigned long flags = 0;
+ struct ibs_struct *ibs = container_of(work, struct ibs_struct,
+ ws_ibs);
+ struct hci_uart *hu = (struct hci_uart *)ibs->ibs_hu;
+
+ BT_DBG("hu %p, ibs_wq state: %lu\n", hu, ibs->ibs_wq_state);
+
+ /* lock hci_ibs state */
+ spin_lock_irqsave(&ibs->hci_ibs_lock, flags);
+
+ switch (ibs->ibs_wq_state) {
+ case HCI_IBS_WQ_AWAKE_DEVICE:
+ /* Vote for serial clock */
+ ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_ON, hu);
+
+ /* send wake indication to device */
+ if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0)
+ BT_ERR("cannot send WAKE to device");
+
+ ibs->ibs_sent_wakes++; /* debug */
+
+ /* start retransmit timer */
+ mod_timer(&ibs->wake_retrans_timer, jiffies + wake_retrans);
+ break;
+ case HCI_IBS_WQ_AWAKE_RX:
+ ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_ON, hu);
+ ibs->rx_ibs_state = HCI_IBS_RX_AWAKE;
+
+ if (send_hci_ibs_cmd(HCI_IBS_WAKE_ACK, hu) < 0)
+ BT_ERR("cannot acknowledge device wake up");
+
+ ibs->ibs_sent_wacks++; /* debug */
+ /* actually send the packets */
+ hci_uart_tx_wakeup(hu);
+ break;
+ case HCI_IBS_WQ_RX_VOTE_OFF:
+ ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_OFF, hu);
+ break;
+ case HCI_IBS_WQ_TX_VOTE_OFF:
+ ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
+ break;
+ default:
+ BT_DBG("Invalid state in ibs workqueue");
+ break;
+ }
+ spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
+}
+
static void hci_ibs_tx_idle_timeout(unsigned long arg)
{
struct hci_uart *hu = (struct hci_uart *) arg;
@@ -282,7 +348,9 @@
spin_lock_irqsave_nested(&ibs->hci_ibs_lock,
flags, SINGLE_DEPTH_NESTING);
- ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
+ /* vote off tx clock */
+ ibs->ibs_wq_state = HCI_IBS_WQ_TX_VOTE_OFF;
+ queue_work(ibs->workqueue, &ibs->ws_ibs);
out:
spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
}
@@ -336,6 +404,16 @@
skb_queue_head_init(&ibs->txq);
skb_queue_head_init(&ibs->tx_wait_q);
spin_lock_init(&ibs->hci_ibs_lock);
+ ibs->workqueue = create_singlethread_workqueue("ibs_wq");
+ if (!ibs->workqueue) {
+ BT_ERR("IBS Workqueue not initialized properly");
+ kfree(ibs);
+ return -ENOMEM;
+ }
+
+ INIT_WORK(&ibs->ws_ibs, ibs_wq);
+ ibs->ibs_hu = (void *)hu;
+ ibs->ibs_wq_state = HCI_IBS_WQ_INIT_STATE;
/* Assume we start with both sides asleep -- extra wakes OK */
ibs->tx_ibs_state = HCI_IBS_TX_ASLEEP;
@@ -432,6 +510,8 @@
skb_queue_purge(&ibs->txq);
del_timer(&ibs->tx_idle_timer);
del_timer(&ibs->wake_retrans_timer);
+ destroy_workqueue(ibs->workqueue);
+ ibs->ibs_hu = NULL;
kfree_skb(ibs->rx_skb);
@@ -463,9 +543,11 @@
/* Make sure clock is on - we may have turned clock off since
* receiving the wake up indicator
*/
- ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_ON, hu);
- ibs->rx_ibs_state = HCI_IBS_RX_AWAKE;
- /* deliberate fall-through */
+ /* awake rx clock */
+ ibs->ibs_wq_state = HCI_IBS_WQ_AWAKE_RX;
+ queue_work(ibs->workqueue, &ibs->ws_ibs);
+ spin_unlock_irqrestore(&ibs->hci_ibs_lock, flags);
+ return;
case HCI_IBS_RX_AWAKE:
/* Always acknowledge device wake up,
* sending IBS message doesn't count as TX ON.
@@ -510,7 +592,9 @@
case HCI_IBS_RX_AWAKE:
/* update state */
ibs->rx_ibs_state = HCI_IBS_RX_ASLEEP;
- ibs_msm_serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_OFF, hu);
+ /* vote off rx clock under workqueue */
+ ibs->ibs_wq_state = HCI_IBS_WQ_RX_VOTE_OFF;
+ queue_work(ibs->workqueue, &ibs->ws_ibs);
break;
case HCI_IBS_RX_ASLEEP:
/* deliberate fall-through */
@@ -595,20 +679,12 @@
case HCI_IBS_TX_ASLEEP:
BT_DBG("device asleep, waking up and queueing packet");
- ibs_msm_serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_ON, hu);
/* save packet for later */
skb_queue_tail(&ibs->tx_wait_q, skb);
- /* awake device */
- if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0) {
- BT_ERR("cannot send WAKE to device");
- break;
- }
- ibs->ibs_sent_wakes++; /* debug */
-
- /* start retransmit timer */
- mod_timer(&ibs->wake_retrans_timer, jiffies + wake_retrans);
-
ibs->tx_ibs_state = HCI_IBS_TX_WAKING;
+ /* schedule a work queue to wake up device */
+ ibs->ibs_wq_state = HCI_IBS_WQ_AWAKE_DEVICE;
+ queue_work(ibs->workqueue, &ibs->ws_ibs);
break;
case HCI_IBS_TX_WAKING:
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 5cbf888..2860055 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -16,6 +16,7 @@
#include <linux/diagchar.h>
#include <linux/sched.h>
#include <linux/err.h>
+#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
@@ -115,6 +116,23 @@
queue_work(driver->diag_wq, &(driver->diag_read_smd_dci_work));
}
+void diag_dci_notify_client(int peripheral_mask)
+{
+ int i, stat;
+
+ /* Notify the DCI process that the peripheral DCI Channel is up */
+ for (i = 0; i < MAX_DCI_CLIENT; i++) {
+ if (driver->dci_notify_tbl[i].list & peripheral_mask) {
+ pr_debug("diag: sending signal now\n");
+ stat = send_sig(driver->dci_notify_tbl[i].signal_type,
+ driver->dci_notify_tbl[i].client, 0);
+ if (stat)
+ pr_err("diag: Err send sig stat: %d\n", stat);
+ break;
+ }
+ } /* end of loop for all DCI clients */
+}
+
static int diag_dci_probe(struct platform_device *pdev)
{
int err = 0;
@@ -125,6 +143,8 @@
if (err)
pr_err("diag: cannot open DCI port, Id = %d, err ="
" %d\n", pdev->id, err);
+ else
+ diag_dci_notify_client(DIAG_CON_MPSS);
}
return err;
}
@@ -302,6 +322,12 @@
if (driver->dci_tbl == NULL)
goto err;
}
+ if (driver->dci_notify_tbl == NULL) {
+ driver->dci_notify_tbl = kzalloc(MAX_DCI_CLIENT *
+ sizeof(struct dci_notification_tbl), GFP_KERNEL);
+ if (driver->dci_notify_tbl == NULL)
+ goto err;
+ }
if (driver->apps_dci_buf == NULL) {
driver->apps_dci_buf = kzalloc(APPS_BUF_SIZE, GFP_KERNEL);
if (driver->apps_dci_buf == NULL)
@@ -316,6 +342,7 @@
err:
pr_err("diag: Could not initialize diag DCI buffers");
kfree(driver->dci_tbl);
+ kfree(driver->dci_notify_tbl);
kfree(driver->apps_dci_buf);
kfree(driver->buf_in_dci);
kfree(driver->write_ptr_dci);
@@ -328,6 +355,7 @@
driver->ch_dci = 0;
platform_driver_unregister(&msm_diag_dci_driver);
kfree(driver->dci_tbl);
+ kfree(driver->dci_notify_tbl);
kfree(driver->apps_dci_buf);
kfree(driver->buf_in_dci);
kfree(driver->write_ptr_dci);
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index cc6e0cf..c0b82df 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -22,6 +22,12 @@
int tag;
};
+struct dci_notification_tbl {
+ struct task_struct *client;
+ uint16_t list; /* bit mask */
+ int signal_type;
+};
+
#define DIAG_CON_APSS (0x0001) /* Bit mask for APSS */
#define DIAG_CON_MPSS (0x0002) /* Bit mask for MPSS */
#define DIAG_CON_LPASS (0x0004) /* Bit mask for LPASS */
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 7e7b514..6a7b931 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -18,6 +18,7 @@
#include <linux/mempool.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
+#include <linux/sched.h>
#include <mach/msm_smd.h>
#include <asm/atomic.h>
#include <asm/mach-types.h>
@@ -139,6 +140,7 @@
int use_device_tree;
/* DCI related variables */
struct diag_dci_tbl *dci_tbl;
+ struct dci_notification_tbl *dci_notify_tbl;
int dci_tag;
int dci_client_id;
struct mutex dci_mutex;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index d6a6e66..547f42f 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -343,6 +343,7 @@
int success = -1;
void *temp_buf;
uint16_t support_list = 0;
+ struct dci_notification_tbl *notify_params;
if (iocmd == DIAG_IOCTL_COMMAND_REG) {
struct bindpkt_params_per_process *pkt_params =
@@ -413,13 +414,23 @@
} else if (iocmd == DIAG_IOCTL_DCI_REG) {
if (driver->dci_state == DIAG_DCI_NO_REG)
return DIAG_DCI_NO_REG;
- /* use the 'list' later on to notify user space */
if (driver->num_dci_client >= MAX_DCI_CLIENT)
return DIAG_DCI_NO_REG;
+ notify_params = (struct dci_notification_tbl *) ioarg;
mutex_lock(&driver->dci_mutex);
driver->num_dci_client++;
pr_debug("diag: id = %d\n", driver->dci_client_id);
driver->dci_client_id++;
+ for (i = 0; i < MAX_DCI_CLIENT; i++) {
+ if (driver->dci_notify_tbl[i].client == NULL) {
+ driver->dci_notify_tbl[i].client = current;
+ driver->dci_notify_tbl[i].list =
+ notify_params->list;
+ driver->dci_notify_tbl[i].signal_type =
+ notify_params->signal_type;
+ break;
+ }
+ }
mutex_unlock(&driver->dci_mutex);
return driver->dci_client_id;
} else if (iocmd == DIAG_IOCTL_DCI_DEINIT) {
@@ -433,6 +444,12 @@
success = i;
}
}
+ for (i = 0; i < MAX_DCI_CLIENT; i++) {
+ if (driver->dci_notify_tbl[i].client == current) {
+ driver->dci_notify_tbl[i].client = NULL;
+ break;
+ }
+ }
/* if any registrations were deleted successfully OR a valid
client_id was sent in DEINIT call , then its DCI client */
if (success >= 0 || ioarg)
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 83d65b1..69aa411 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -1016,7 +1016,7 @@
ENCODE_RSP_AND_SEND(8+rt_mask_size-1);
return 0;
}
- ptr += MAX_SSID_PER_RANGE*4;
+ rt_mask_ptr += MAX_SSID_PER_RANGE*4;
}
} else
buf = temp;
diff --git a/drivers/char/msm_rotator.c b/drivers/char/msm_rotator.c
index 6cd1806..9c8f7ee 100644
--- a/drivers/char/msm_rotator.c
+++ b/drivers/char/msm_rotator.c
@@ -170,9 +170,9 @@
CLK_SUSPEND,
};
-int msm_rotator_iommu_map_buf(int mem_id, unsigned char src,
+int msm_rotator_iommu_map_buf(int mem_id, int domain,
unsigned long *start, unsigned long *len,
- struct ion_handle **pihdl)
+ struct ion_handle **pihdl, unsigned int secure)
{
if (!msm_rotator_dev->client)
return -EINVAL;
@@ -185,11 +185,20 @@
pr_debug("%s(): ion_hdl %p, ion_buf %p\n", __func__, *pihdl,
ion_share(msm_rotator_dev->client, *pihdl));
- if (ion_map_iommu(msm_rotator_dev->client,
- *pihdl, ROTATOR_DOMAIN, GEN_POOL,
- SZ_4K, 0, start, len, 0, ION_IOMMU_UNMAP_DELAYED)) {
- pr_err("ion_map_iommu() failed\n");
- return -EINVAL;
+ if (secure) {
+ if (ion_phys(msm_rotator_dev->client,
+ *pihdl, start, (unsigned *)len)) {
+ pr_err("%s:%d: ion_phys map failed\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+ } else {
+ if (ion_map_iommu(msm_rotator_dev->client,
+ *pihdl, domain, GEN_POOL,
+ SZ_4K, 0, start, len, 0, ION_IOMMU_UNMAP_DELAYED)) {
+ pr_err("ion_map_iommu() failed\n");
+ return -EINVAL;
+ }
}
pr_debug("%s(): mem_id %d, start 0x%lx, len 0x%lx\n",
@@ -806,9 +815,9 @@
return 0;
}
-static int get_img(struct msmfb_data *fbd, unsigned char src,
+static int get_img(struct msmfb_data *fbd, int domain,
unsigned long *start, unsigned long *len, struct file **p_file,
- int *p_need, struct ion_handle **p_ihdl)
+ int *p_need, struct ion_handle **p_ihdl, unsigned int secure)
{
int ret = 0;
#ifdef CONFIG_FB
@@ -850,8 +859,8 @@
#endif
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
- return msm_rotator_iommu_map_buf(fbd->memory_id, src, start,
- len, p_ihdl);
+ return msm_rotator_iommu_map_buf(fbd->memory_id, domain, start,
+ len, p_ihdl, secure);
#endif
#ifdef CONFIG_ANDROID_PMEM
if (!get_pmem_file(fbd->memory_id, start, &vstart, len, p_file))
@@ -862,17 +871,20 @@
}
-static void put_img(struct file *p_file, struct ion_handle *p_ihdl)
+static void put_img(struct file *p_file, struct ion_handle *p_ihdl,
+ int domain, unsigned int secure)
{
#ifdef CONFIG_ANDROID_PMEM
if (p_file != NULL)
put_pmem_file(p_file);
#endif
+
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
if (!IS_ERR_OR_NULL(p_ihdl)) {
pr_debug("%s(): p_ihdl %p\n", __func__, p_ihdl);
- ion_unmap_iommu(msm_rotator_dev->client,
- p_ihdl, ROTATOR_DOMAIN, GEN_POOL);
+ if (!secure)
+ ion_unmap_iommu(msm_rotator_dev->client,
+ p_ihdl, domain, GEN_POOL);
ion_free(msm_rotator_dev->client, p_ihdl);
}
@@ -939,18 +951,18 @@
goto do_rotate_unlock_mutex;
}
- rc = get_img(&info.src, 1, (unsigned long *)&in_paddr,
+ rc = get_img(&info.src, ROTATOR_SRC_DOMAIN, (unsigned long *)&in_paddr,
(unsigned long *)&src_len, &srcp0_file, &ps0_need,
- &srcp0_ihdl);
+ &srcp0_ihdl, 0);
if (rc) {
pr_err("%s: in get_img() failed id=0x%08x\n",
DRIVER_NAME, info.src.memory_id);
goto do_rotate_unlock_mutex;
}
- rc = get_img(&info.dst, 0, (unsigned long *)&out_paddr,
+ rc = get_img(&info.dst, ROTATOR_DST_DOMAIN, (unsigned long *)&out_paddr,
(unsigned long *)&dst_len, &dstp0_file, &p_need,
- &dstp0_ihdl);
+ &dstp0_ihdl, img_info->secure);
if (rc) {
pr_err("%s: out get_img() failed id=0x%08x\n",
DRIVER_NAME, info.dst.memory_id);
@@ -978,20 +990,20 @@
goto do_rotate_unlock_mutex;
}
- rc = get_img(&info.src_chroma, 1,
+ rc = get_img(&info.src_chroma, ROTATOR_SRC_DOMAIN,
(unsigned long *)&in_chroma_paddr,
(unsigned long *)&src_len, &srcp1_file, &p_need,
- &srcp1_ihdl);
+ &srcp1_ihdl, 0);
if (rc) {
pr_err("%s: in chroma get_img() failed id=0x%08x\n",
DRIVER_NAME, info.src_chroma.memory_id);
goto do_rotate_unlock_mutex;
}
- rc = get_img(&info.dst_chroma, 0,
+ rc = get_img(&info.dst_chroma, ROTATOR_DST_DOMAIN,
(unsigned long *)&out_chroma_paddr,
(unsigned long *)&dst_len, &dstp1_file, &p_need,
- &dstp1_ihdl);
+ &dstp1_ihdl, img_info->secure);
if (rc) {
pr_err("%s: out chroma get_img() failed id=0x%08x\n",
DRIVER_NAME, info.dst_chroma.memory_id);
@@ -1162,15 +1174,17 @@
#endif
schedule_delayed_work(&msm_rotator_dev->rot_clk_work, HZ);
do_rotate_unlock_mutex:
- put_img(dstp1_file, dstp1_ihdl);
- put_img(srcp1_file, srcp1_ihdl);
- put_img(dstp0_file, dstp0_ihdl);
+ put_img(dstp1_file, dstp1_ihdl, ROTATOR_DST_DOMAIN,
+ msm_rotator_dev->img_info[s]->secure);
+ put_img(srcp1_file, srcp1_ihdl, ROTATOR_SRC_DOMAIN, 0);
+ put_img(dstp0_file, dstp0_ihdl, ROTATOR_DST_DOMAIN,
+ msm_rotator_dev->img_info[s]->secure);
/* only source may use frame buffer */
if (info.src.flags & MDP_MEMORY_ID_TYPE_FB)
fput_light(srcp0_file, ps0_need);
else
- put_img(srcp0_file, srcp0_ihdl);
+ put_img(srcp0_file, srcp0_ihdl, ROTATOR_SRC_DOMAIN, 0);
mutex_unlock(&msm_rotator_dev->rotator_lock);
dev_dbg(msm_rotator_dev->device, "%s() returning rc = %d\n",
__func__, rc);
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index fdff32e..826ba9a 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -122,7 +122,7 @@
}
EXPORT_SYMBOL(clkdev_add);
-void __init clkdev_add_table(struct clk_lookup *cl, size_t num)
+void clkdev_add_table(struct clk_lookup *cl, size_t num)
{
mutex_lock(&clocks_mutex);
while (num--) {
diff --git a/drivers/gpio/gpio-msm-v2.c b/drivers/gpio/gpio-msm-v2.c
index fb43562..bac9f8a 100644
--- a/drivers/gpio/gpio-msm-v2.c
+++ b/drivers/gpio/gpio-msm-v2.c
@@ -12,6 +12,7 @@
*/
#include <linux/bitmap.h>
#include <linux/bitops.h>
+#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/io.h>
@@ -147,14 +148,12 @@
void __msm_gpio_set_intr_cfg_enable(unsigned gpio, unsigned val)
{
if (val) {
- set_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE,
- GPIO_INTR_CFG(gpio));
+ set_gpio_bits(INTR_ENABLE, GPIO_INTR_CFG(gpio));
__raw_writel(TARGET_PROC_SCORPION, GPIO_INTR_CFG_SU(gpio));
} else {
__raw_writel(TARGET_PROC_NONE, GPIO_INTR_CFG_SU(gpio));
- clr_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE,
- GPIO_INTR_CFG(gpio));
+ clr_gpio_bits(INTR_ENABLE, GPIO_INTR_CFG(gpio));
}
}
@@ -173,7 +172,23 @@
else
cfg &= ~INTR_POL_CTL_HI;
+ /* 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 |= INTR_RAW_STATUS_EN;
__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
+ */
+ udelay(5);
+
+ /* Clear the interrupt status to clear out any spurious
+ * irq as a result of the above operation
+ */
+ __msm_gpio_set_intr_status(gpio);
+
}
void __gpio_tlmm_config(unsigned config)
diff --git a/drivers/gpio/gpio-msm-v3.c b/drivers/gpio/gpio-msm-v3.c
index 49ad517e..6086de3 100644
--- a/drivers/gpio/gpio-msm-v3.c
+++ b/drivers/gpio/gpio-msm-v3.c
@@ -12,6 +12,7 @@
*/
#include <linux/bitmap.h>
#include <linux/bitops.h>
+#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/io.h>
@@ -155,10 +156,9 @@
cfg = __raw_readl(GPIO_INTR_CFG(gpio));
if (val) {
cfg &= ~(INTR_TARGET_PROC_NONE | INTR_DIR_CONN_EN);
- cfg |= INTR_RAW_STATUS_EN | INTR_ENABLE | INTR_TARGET_PROC_APPS;
+ cfg |= INTR_ENABLE | INTR_TARGET_PROC_APPS;
} else {
- cfg &= ~(INTR_TARGET_PROC_APPS | INTR_RAW_STATUS_EN |
- INTR_ENABLE);
+ cfg &= ~(INTR_TARGET_PROC_APPS | INTR_ENABLE);
cfg |= INTR_TARGET_PROC_NONE;
}
__raw_writel(cfg, GPIO_INTR_CFG(gpio));
@@ -184,7 +184,22 @@
else
cfg &= ~INTR_POL_CTL_HI;
+ /* 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 |= INTR_RAW_STATUS_EN;
__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
+ */
+ udelay(5);
+
+ /* Clear the interrupt status to clear out any spurious
+ * irq as a result of the above operation
+ */
+ __msm_gpio_set_intr_status(gpio);
}
void __gpio_tlmm_config(unsigned config)
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c
index 7e84aa7..5c7ab3a 100644
--- a/drivers/gpu/ion/ion.c
+++ b/drivers/gpu/ion/ion.c
@@ -1898,7 +1898,8 @@
mutex_unlock(&dev->lock);
}
-int ion_secure_heap(struct ion_device *dev, int heap_id)
+int ion_secure_heap(struct ion_device *dev, int heap_id, int version,
+ void *data)
{
struct rb_node *n;
int ret_val = 0;
@@ -1915,7 +1916,7 @@
if (ION_HEAP(heap->id) != heap_id)
continue;
if (heap->ops->secure_heap)
- ret_val = heap->ops->secure_heap(heap);
+ ret_val = heap->ops->secure_heap(heap, version, data);
else
ret_val = -EINVAL;
break;
@@ -1924,7 +1925,8 @@
return ret_val;
}
-int ion_unsecure_heap(struct ion_device *dev, int heap_id)
+int ion_unsecure_heap(struct ion_device *dev, int heap_id, int version,
+ void *data)
{
struct rb_node *n;
int ret_val = 0;
@@ -1941,7 +1943,7 @@
if (ION_HEAP(heap->id) != heap_id)
continue;
if (heap->ops->secure_heap)
- ret_val = heap->ops->unsecure_heap(heap);
+ ret_val = heap->ops->unsecure_heap(heap, version, data);
else
ret_val = -EINVAL;
break;
diff --git a/drivers/gpu/ion/ion_cp_heap.c b/drivers/gpu/ion/ion_cp_heap.c
index a857988a..c5e9caf 100644
--- a/drivers/gpu/ion/ion_cp_heap.c
+++ b/drivers/gpu/ion/ion_cp_heap.c
@@ -40,6 +40,7 @@
#include <asm/mach/map.h>
#include <asm/cacheflush.h>
+#include "msm/ion_cp_common.h"
/**
* struct ion_cp_heap - container for the heap and shared heap data
@@ -97,6 +98,7 @@
int iommu_map_all;
int iommu_2x_map_domain;
unsigned int has_outer_cache;
+ atomic_t protect_cnt;
};
enum {
@@ -105,10 +107,12 @@
};
static int ion_cp_protect_mem(unsigned int phy_base, unsigned int size,
- unsigned int permission_type);
+ unsigned int permission_type, int version,
+ void *data);
static int ion_cp_unprotect_mem(unsigned int phy_base, unsigned int size,
- unsigned int permission_type);
+ unsigned int permission_type, int version,
+ void *data);
/**
* Get the total number of kernel mappings.
@@ -125,13 +129,13 @@
* the correct FMEM state if this heap is a reusable heap.
* Must be called with heap->lock locked.
*/
-static int ion_cp_protect(struct ion_heap *heap)
+static int ion_cp_protect(struct ion_heap *heap, int version, void *data)
{
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
int ret_value = 0;
- if (cp_heap->heap_protected == HEAP_NOT_PROTECTED) {
+ if (atomic_inc_return(&cp_heap->protect_cnt) == 1) {
/* Make sure we are in C state when the heap is protected. */
if (cp_heap->reusable && !cp_heap->allocated_bytes) {
ret_value = fmem_set_state(FMEM_C_STATE);
@@ -140,7 +144,8 @@
}
ret_value = ion_cp_protect_mem(cp_heap->secure_base,
- cp_heap->secure_size, cp_heap->permission_type);
+ cp_heap->secure_size, cp_heap->permission_type,
+ version, data);
if (ret_value) {
pr_err("Failed to protect memory for heap %s - "
"error code: %d\n", heap->name, ret_value);
@@ -150,6 +155,7 @@
pr_err("%s: unable to transition heap to T-state\n",
__func__);
}
+ atomic_dec(&cp_heap->protect_cnt);
} else {
cp_heap->heap_protected = HEAP_PROTECTED;
pr_debug("Protected heap %s @ 0x%lx\n",
@@ -157,6 +163,9 @@
}
}
out:
+ pr_debug("%s: protect count is %d\n", __func__,
+ atomic_read(&cp_heap->protect_cnt));
+ BUG_ON(atomic_read(&cp_heap->protect_cnt) < 0);
return ret_value;
}
@@ -165,15 +174,15 @@
* the correct FMEM state if this heap is a reusable heap.
* Must be called with heap->lock locked.
*/
-static void ion_cp_unprotect(struct ion_heap *heap)
+static void ion_cp_unprotect(struct ion_heap *heap, int version, void *data)
{
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
- if (cp_heap->heap_protected == HEAP_PROTECTED) {
+ if (atomic_dec_and_test(&cp_heap->protect_cnt)) {
int error_code = ion_cp_unprotect_mem(
cp_heap->secure_base, cp_heap->secure_size,
- cp_heap->permission_type);
+ cp_heap->permission_type, version, data);
if (error_code) {
pr_err("Failed to un-protect memory for heap %s - "
"error code: %d\n", heap->name, error_code);
@@ -189,6 +198,9 @@
}
}
}
+ pr_debug("%s: protect count is %d\n", __func__,
+ atomic_read(&cp_heap->protect_cnt));
+ BUG_ON(atomic_read(&cp_heap->protect_cnt) < 0);
}
ion_phys_addr_t ion_cp_allocate(struct ion_heap *heap,
@@ -641,14 +653,14 @@
return 0;
}
-int ion_cp_secure_heap(struct ion_heap *heap)
+int ion_cp_secure_heap(struct ion_heap *heap, int version, void *data)
{
int ret_value;
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
mutex_lock(&cp_heap->lock);
if (cp_heap->umap_count == 0 && cp_heap->kmap_cached_count == 0) {
- ret_value = ion_cp_protect(heap);
+ ret_value = ion_cp_protect(heap, version, data);
} else {
pr_err("ION cannot secure heap with outstanding mappings: "
"User space: %lu, kernel space (cached): %lu\n",
@@ -660,13 +672,13 @@
return ret_value;
}
-int ion_cp_unsecure_heap(struct ion_heap *heap)
+int ion_cp_unsecure_heap(struct ion_heap *heap, int version, void *data)
{
int ret_value = 0;
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
mutex_lock(&cp_heap->lock);
- ion_cp_unprotect(heap);
+ ion_cp_unprotect(heap, version, data);
mutex_unlock(&cp_heap->lock);
return ret_value;
}
@@ -925,6 +937,7 @@
cp_heap->secure_base = cp_heap->base;
cp_heap->secure_size = heap_data->size;
cp_heap->has_outer_cache = heap_data->has_outer_cache;
+ atomic_set(&cp_heap->protect_cnt, 0);
if (heap_data->extra_data) {
struct ion_cp_heap_pdata *extra_data =
heap_data->extra_data;
@@ -991,8 +1004,7 @@
unsigned char lock;
} __attribute__ ((__packed__));
-
-static int ion_cp_protect_mem(unsigned int phy_base, unsigned int size,
+static int ion_cp_protect_mem_v1(unsigned int phy_base, unsigned int size,
unsigned int permission_type)
{
struct cp_lock_msg cmd;
@@ -1005,7 +1017,7 @@
&cmd, sizeof(cmd), NULL, 0);
}
-static int ion_cp_unprotect_mem(unsigned int phy_base, unsigned int size,
+static int ion_cp_unprotect_mem_v1(unsigned int phy_base, unsigned int size,
unsigned int permission_type)
{
struct cp_lock_msg cmd;
@@ -1017,3 +1029,70 @@
return scm_call(SCM_SVC_CP, SCM_CP_LOCK_CMD_ID,
&cmd, sizeof(cmd), NULL, 0);
}
+
+#define V2_CHUNK_SIZE SZ_1M
+
+static int ion_cp_change_mem_v2(unsigned int phy_base, unsigned int size,
+ void *data, int lock)
+{
+ enum cp_mem_usage usage = (enum cp_mem_usage) data;
+ unsigned long *chunk_list;
+ int nchunks;
+ int ret;
+ int i;
+
+ if (usage < 0 || usage >= MAX_USAGE)
+ return -EINVAL;
+
+ if (!IS_ALIGNED(size, V2_CHUNK_SIZE)) {
+ pr_err("%s: heap size is not aligned to %x\n",
+ __func__, V2_CHUNK_SIZE);
+ return -EINVAL;
+ }
+
+ nchunks = size / V2_CHUNK_SIZE;
+
+ chunk_list = allocate_contiguous_ebi(sizeof(unsigned long)*nchunks,
+ SZ_4K, 0);
+ if (!chunk_list)
+ return -ENOMEM;
+
+ for (i = 0; i < nchunks; i++)
+ chunk_list[i] = phy_base + i * V2_CHUNK_SIZE;
+
+ ret = ion_cp_change_chunks_state(memory_pool_node_paddr(chunk_list),
+ nchunks, V2_CHUNK_SIZE, usage, lock);
+
+ free_contiguous_memory(chunk_list);
+ return ret;
+}
+
+static int ion_cp_protect_mem(unsigned int phy_base, unsigned int size,
+ unsigned int permission_type, int version,
+ void *data)
+{
+ switch (version) {
+ case ION_CP_V1:
+ return ion_cp_protect_mem_v1(phy_base, size, permission_type);
+ case ION_CP_V2:
+ return ion_cp_change_mem_v2(phy_base, size, data,
+ SCM_CP_PROTECT);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ion_cp_unprotect_mem(unsigned int phy_base, unsigned int size,
+ unsigned int permission_type, int version,
+ void *data)
+{
+ switch (version) {
+ case ION_CP_V1:
+ return ion_cp_unprotect_mem_v1(phy_base, size, permission_type);
+ case ION_CP_V2:
+ return ion_cp_change_mem_v2(phy_base, size, data,
+ SCM_CP_UNPROTECT);
+ default:
+ return -EINVAL;
+ }
+}
diff --git a/drivers/gpu/ion/ion_iommu_heap.c b/drivers/gpu/ion/ion_iommu_heap.c
index 621144b..9ea6f2b 100644
--- a/drivers/gpu/ion/ion_iommu_heap.c
+++ b/drivers/gpu/ion/ion_iommu_heap.c
@@ -196,7 +196,7 @@
data->mapped_size, align,
&data->iova_addr);
- if (!data->iova_addr)
+ if (ret)
goto out;
domain = msm_get_iommu_domain(domain_num);
diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h
index 6d636ee..6940e2f 100644
--- a/drivers/gpu/ion/ion_priv.h
+++ b/drivers/gpu/ion/ion_priv.h
@@ -158,8 +158,8 @@
void (*unmap_iommu)(struct ion_iommu_map *data);
int (*print_debug)(struct ion_heap *heap, struct seq_file *s,
const struct rb_root *mem_map);
- int (*secure_heap)(struct ion_heap *heap);
- int (*unsecure_heap)(struct ion_heap *heap);
+ int (*secure_heap)(struct ion_heap *heap, int version, void *data);
+ int (*unsecure_heap)(struct ion_heap *heap, int version, void *data);
};
/**
diff --git a/drivers/gpu/ion/msm/Makefile b/drivers/gpu/ion/msm/Makefile
index bedd8d2..1893405 100644
--- a/drivers/gpu/ion/msm/Makefile
+++ b/drivers/gpu/ion/msm/Makefile
@@ -1 +1 @@
-obj-y += msm_ion.o
+obj-y += msm_ion.o ion_cp_common.o
diff --git a/drivers/gpu/ion/msm/ion_cp_common.c b/drivers/gpu/ion/msm/ion_cp_common.c
new file mode 100644
index 0000000..b274ba2
--- /dev/null
+++ b/drivers/gpu/ion/msm/ion_cp_common.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 Google, Inc
+ * Copyright (c) 2011-2012, Code Aurora Forum. 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 <mach/scm.h>
+
+#include "ion_cp_common.h"
+
+#define MEM_PROTECT_LOCK_ID 0x05
+
+struct cp2_mem_chunks {
+ unsigned int *chunk_list;
+ unsigned int chunk_list_size;
+ unsigned int chunk_size;
+} __attribute__ ((__packed__));
+
+struct cp2_lock_req {
+ struct cp2_mem_chunks chunks;
+ unsigned int mem_usage;
+ unsigned int lock;
+} __attribute__ ((__packed__));
+
+int ion_cp_change_chunks_state(unsigned long chunks, unsigned int nchunks,
+ unsigned int chunk_size,
+ enum cp_mem_usage usage,
+ int lock)
+{
+ struct cp2_lock_req request;
+
+ request.mem_usage = usage;
+ request.lock = lock;
+
+ request.chunks.chunk_list = (unsigned int *)chunks;
+ request.chunks.chunk_list_size = nchunks;
+ request.chunks.chunk_size = chunk_size;
+
+ return scm_call(SCM_SVC_CP, MEM_PROTECT_LOCK_ID,
+ &request, sizeof(request), NULL, 0);
+
+}
+
diff --git a/drivers/gpu/ion/msm/ion_cp_common.h b/drivers/gpu/ion/msm/ion_cp_common.h
new file mode 100644
index 0000000..69dd19e
--- /dev/null
+++ b/drivers/gpu/ion/msm/ion_cp_common.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2012, Code Aurora Forum. 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 ION_CP_COMMON_H
+#define ION_CP_COMMON_H
+
+#include <asm-generic/errno-base.h>
+#include <linux/ion.h>
+
+#define ION_CP_V1 1
+#define ION_CP_V2 2
+
+#if defined(CONFIG_ION_MSM)
+/*
+ * ion_cp2_protect_mem - secures memory via trustzone
+ *
+ * @chunks - physical address of the array containing the chunks to
+ * be locked down
+ * @nchunks - number of entries in the array
+ * @chunk_size - size of each memory chunk
+ * @usage - usage hint
+ * @lock - 1 for lock, 0 for unlock
+ *
+ * return value is the result of the scm call
+ */
+int ion_cp_change_chunks_state(unsigned long chunks, unsigned int nchunks,
+ unsigned int chunk_size, enum cp_mem_usage usage,
+ int lock);
+
+#else
+static inline int ion_cp_change_chunks_state(unsigned long chunks,
+ unsigned int nchunks, unsigned int chunk_size,
+ enum cp_mem_usage usage, int lock)
+{
+ return -ENODEV;
+}
+#endif
+
+#endif
diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
index f6a4cf4..eec3fe0 100644
--- a/drivers/gpu/ion/msm/msm_ion.c
+++ b/drivers/gpu/ion/msm/msm_ion.c
@@ -21,6 +21,7 @@
#include <mach/ion.h>
#include <mach/msm_memtypes.h>
#include "../ion_priv.h"
+#include "ion_cp_common.h"
static struct ion_device *idev;
static int num_heaps;
@@ -35,16 +36,28 @@
int msm_ion_secure_heap(int heap_id)
{
- return ion_secure_heap(idev, heap_id);
+ return ion_secure_heap(idev, heap_id, ION_CP_V1, NULL);
}
EXPORT_SYMBOL(msm_ion_secure_heap);
int msm_ion_unsecure_heap(int heap_id)
{
- return ion_unsecure_heap(idev, heap_id);
+ return ion_unsecure_heap(idev, heap_id, ION_CP_V1, NULL);
}
EXPORT_SYMBOL(msm_ion_unsecure_heap);
+int msm_ion_secure_heap_2_0(int heap_id, enum cp_mem_usage usage)
+{
+ return ion_secure_heap(idev, heap_id, ION_CP_V2, (void *)usage);
+}
+EXPORT_SYMBOL(msm_ion_secure_heap_2_0);
+
+int msm_ion_unsecure_heap_2_0(int heap_id, enum cp_mem_usage usage)
+{
+ return ion_unsecure_heap(idev, heap_id, ION_CP_V2, (void *)usage);
+}
+EXPORT_SYMBOL(msm_ion_unsecure_heap_2_0);
+
int msm_ion_do_cache_op(struct ion_client *client, struct ion_handle *handle,
void *vaddr, unsigned long len, unsigned int cmd)
{
diff --git a/drivers/gpu/msm/a3xx_reg.h b/drivers/gpu/msm/a3xx_reg.h
index 0a71982..bd58b4e 100644
--- a/drivers/gpu/msm/a3xx_reg.h
+++ b/drivers/gpu/msm/a3xx_reg.h
@@ -46,6 +46,7 @@
#define A3XX_RBBM_HW_VERSION 0x000
#define A3XX_RBBM_HW_RELEASE 0x001
#define A3XX_RBBM_HW_CONFIGURATION 0x002
+#define A3XX_RBBM_CLOCK_CTL 0x010
#define A3XX_RBBM_SP_HYST_CNT 0x012
#define A3XX_RBBM_SW_RESET_CMD 0x018
#define A3XX_RBBM_AHB_CTL0 0x020
@@ -507,4 +508,7 @@
#define RBBM_BLOCK_ID_MARB_2 0x2a
#define RBBM_BLOCK_ID_MARB_3 0x2b
+/* RBBM_CLOCK_CTL default value */
+#define A3XX_RBBM_CLOCK_CTL_DEFAULT 0x00000000
+
#endif
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 615c7ad4..f5cb888 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -267,7 +267,7 @@
KGSL_IOMMU_CONTEXT_USER))
goto done;
- if (adreno_is_a225(adreno_dev))
+ if (cpu_is_msm8960())
cmds += adreno_add_change_mh_phys_limit_cmds(cmds, 0xFFFFF000,
device->mmu.setstate_memory.gpuaddr +
KGSL_IOMMU_SETSTATE_NOP_OFFSET);
@@ -362,7 +362,7 @@
}
}
- if (adreno_is_a225(adreno_dev))
+ if (cpu_is_msm8960())
cmds += adreno_add_change_mh_phys_limit_cmds(cmds,
reg_map_desc[num_iommu_units - 1]->gpuaddr - PAGE_SIZE,
device->mmu.setstate_memory.gpuaddr +
@@ -1354,7 +1354,7 @@
* get an interrupt */
cmds[0] = cp_type3_packet(CP_NOP, 1);
cmds[1] = 0;
- adreno_ringbuffer_issuecmds_intr(device, context,
+ adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE,
&cmds[0], 2);
}
}
@@ -1592,10 +1592,17 @@
adreno_dev->gpudev->irq_control(adreno_dev, state);
}
-static unsigned int adreno_gpuid(struct kgsl_device *device)
+static unsigned int adreno_gpuid(struct kgsl_device *device,
+ unsigned int *chipid)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ /* Some applications need to know the chip ID too, so pass
+ * that as a parameter */
+
+ if (chipid != NULL)
+ *chipid = adreno_dev->chip_id;
+
/* Standard KGSL gpuid format:
* top word is 0x0002 for 2D or 0x0003 for 3D
* Bottom word is core specific identifer
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index df5f0f9..feaa36f 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -27,8 +27,8 @@
/* Flags to control command packet settings */
#define KGSL_CMD_FLAGS_NONE 0x00000000
#define KGSL_CMD_FLAGS_PMODE 0x00000001
-#define KGSL_CMD_FLAGS_NOT_KERNEL_CMD 0x00000002
-#define KGSL_CMD_FLAGS_DUMMY_INTR_CMD 0x00000004
+#define KGSL_CMD_FLAGS_NO_TS_CMP 0x00000002
+#define KGSL_CMD_FLAGS_NOT_KERNEL_CMD 0x00000004
/* Command identifiers */
#define KGSL_CONTEXT_TO_MEM_IDENTIFIER 0x2EADBEEF
diff --git a/drivers/gpu/msm/adreno_a2xx.c b/drivers/gpu/msm/adreno_a2xx.c
index c3c266e..d846d3d 100644
--- a/drivers/gpu/msm/adreno_a2xx.c
+++ b/drivers/gpu/msm/adreno_a2xx.c
@@ -1450,43 +1450,55 @@
return ret;
}
-static void a2xx_drawctxt_draw_workaround(struct adreno_device *adreno_dev)
+static void a2xx_drawctxt_workaround(struct adreno_device *adreno_dev)
{
struct kgsl_device *device = &adreno_dev->dev;
unsigned int cmd[11];
unsigned int *cmds = &cmd[0];
- adreno_dev->gpudev->ctx_switches_since_last_draw++;
- /* If there have been > than ADRENO_NUM_CTX_SWITCH_ALLOWED_BEFORE_DRAW
- * calls to context switches w/o gmem being saved then we need to
- * execute this workaround */
- if (adreno_dev->gpudev->ctx_switches_since_last_draw >
- ADRENO_NUM_CTX_SWITCH_ALLOWED_BEFORE_DRAW)
- adreno_dev->gpudev->ctx_switches_since_last_draw = 0;
- else
- return;
- /*
- * Issue an empty draw call to avoid possible hangs due to
- * repeated idles without intervening draw calls.
- * On adreno 225 the PC block has a cache that is only
- * flushed on draw calls and repeated idles can make it
- * overflow. The gmem save path contains draw calls so
- * this workaround isn't needed there.
- */
- *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
- *cmds++ = (0x4 << 16) | (REG_PA_SU_SC_MODE_CNTL - 0x2000);
- *cmds++ = 0;
- *cmds++ = cp_type3_packet(CP_DRAW_INDX, 5);
- *cmds++ = 0;
- *cmds++ = 1<<14;
- *cmds++ = 0;
- *cmds++ = device->mmu.setstate_memory.gpuaddr;
- *cmds++ = 0;
- *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
- *cmds++ = 0x00000000;
+ if (adreno_is_a225(adreno_dev)) {
+ adreno_dev->gpudev->ctx_switches_since_last_draw++;
+ /* If there have been > than
+ * ADRENO_NUM_CTX_SWITCH_ALLOWED_BEFORE_DRAW calls to context
+ * switches w/o gmem being saved then we need to execute
+ * this workaround */
+ if (adreno_dev->gpudev->ctx_switches_since_last_draw >
+ ADRENO_NUM_CTX_SWITCH_ALLOWED_BEFORE_DRAW)
+ adreno_dev->gpudev->ctx_switches_since_last_draw = 0;
+ else
+ return;
+ /*
+ * Issue an empty draw call to avoid possible hangs due to
+ * repeated idles without intervening draw calls.
+ * On adreno 225 the PC block has a cache that is only
+ * flushed on draw calls and repeated idles can make it
+ * overflow. The gmem save path contains draw calls so
+ * this workaround isn't needed there.
+ */
+ *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+ *cmds++ = (0x4 << 16) | (REG_PA_SU_SC_MODE_CNTL - 0x2000);
+ *cmds++ = 0;
+ *cmds++ = cp_type3_packet(CP_DRAW_INDX, 5);
+ *cmds++ = 0;
+ *cmds++ = 1<<14;
+ *cmds++ = 0;
+ *cmds++ = device->mmu.setstate_memory.gpuaddr;
+ *cmds++ = 0;
+ *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+ *cmds++ = 0x00000000;
+ } else {
+ /* On Adreno 20x/220, if the events for shader space reuse
+ * gets dropped, the CP block would wait indefinitely.
+ * Sending CP_SET_SHADER_BASES packet unblocks the CP from
+ * this wait.
+ */
+ *cmds++ = cp_type3_packet(CP_SET_SHADER_BASES, 1);
+ *cmds++ = adreno_encode_istore_size(adreno_dev)
+ | adreno_dev->pix_shader_start;
+ }
adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE,
- &cmd[0], 11);
+ &cmd[0], cmds - cmd);
}
static void a2xx_drawctxt_save(struct adreno_device *adreno_dev,
@@ -1540,8 +1552,8 @@
adreno_dev->gpudev->ctx_switches_since_last_draw = 0;
context->flags |= CTXT_FLAGS_GMEM_RESTORE;
- } else if (adreno_is_a225(adreno_dev))
- a2xx_drawctxt_draw_workaround(adreno_dev);
+ } else if (adreno_is_a2xx(adreno_dev))
+ a2xx_drawctxt_workaround(adreno_dev);
}
static void a2xx_drawctxt_restore(struct adreno_device *adreno_dev,
@@ -1999,7 +2011,7 @@
.ctxt_create = a2xx_drawctxt_create,
.ctxt_save = a2xx_drawctxt_save,
.ctxt_restore = a2xx_drawctxt_restore,
- .ctxt_draw_workaround = a2xx_drawctxt_draw_workaround,
+ .ctxt_draw_workaround = a2xx_drawctxt_workaround,
.irq_handler = a2xx_irq_handler,
.irq_control = a2xx_irq_control,
.snapshot = a2xx_snapshot,
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index e258072..58a0963 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -433,6 +433,9 @@
unsigned int *cmds = tmp_ctx.cmd;
unsigned int *start = cmds;
+ *cmds++ = cp_type0_packet(A3XX_RBBM_CLOCK_CTL, 1);
+ *cmds++ = A3XX_RBBM_CLOCK_CTL_DEFAULT;
+
*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
*cmds++ = CP_REG(A3XX_RB_MODE_CONTROL);
@@ -1162,6 +1165,9 @@
unsigned int *cmds = tmp_ctx.cmd;
unsigned int *start = cmds;
+ *cmds++ = cp_type0_packet(A3XX_RBBM_CLOCK_CTL, 1);
+ *cmds++ = A3XX_RBBM_CLOCK_CTL_DEFAULT;
+
*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5);
*cmds++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG);
/* HLSQ_CONTROL_0_REG */
diff --git a/drivers/gpu/msm/adreno_a3xx_snapshot.c b/drivers/gpu/msm/adreno_a3xx_snapshot.c
index c8c7c441..60aab64 100644
--- a/drivers/gpu/msm/adreno_a3xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a3xx_snapshot.c
@@ -19,6 +19,29 @@
#define DEBUG_SECTION_SZ(_dwords) (((_dwords) * sizeof(unsigned int)) \
+ sizeof(struct kgsl_snapshot_debug))
+#define SHADER_MEMORY_SIZE 0x4000
+
+static int a3xx_snapshot_shader_memory(struct kgsl_device *device,
+ void *snapshot, int remain, void *priv)
+{
+ struct kgsl_snapshot_debug *header = snapshot;
+ unsigned int *data = snapshot + sizeof(*header);
+ int i;
+
+ if (remain < DEBUG_SECTION_SZ(SHADER_MEMORY_SIZE)) {
+ SNAPSHOT_ERR_NOMEM(device, "SHADER MEMORY");
+ return 0;
+ }
+
+ header->type = SNAPSHOT_DEBUG_SHADER_MEMORY;
+ header->size = SHADER_MEMORY_SIZE;
+
+ for (i = 0; i < SHADER_MEMORY_SIZE; i++)
+ adreno_regread(device, 0x4000 + i, &data[i]);
+
+ return DEBUG_SECTION_SZ(SHADER_MEMORY_SIZE);
+}
+
#define VPC_MEMORY_BANKS 4
#define VPC_MEMORY_SIZE 512
@@ -272,6 +295,12 @@
KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
a3xx_snapshot_cp_meq, NULL);
+ /* Shader working/shadow memory */
+ snapshot = kgsl_snapshot_add_section(device,
+ KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+ a3xx_snapshot_shader_memory, NULL);
+
+
/* CP PFP and PM4 */
/* Reading these will hang the GPU if it isn't already hung */
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index 76122ae..267fd45 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -147,7 +147,6 @@
{
struct adreno_context *drawctxt;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
int ret;
drawctxt = kzalloc(sizeof(struct adreno_context), GFP_KERNEL);
@@ -158,7 +157,6 @@
drawctxt->pagetable = pagetable;
drawctxt->bin_base_offset = 0;
drawctxt->id = context->id;
- rb->timestamp[context->id] = 0;
if (flags & KGSL_CONTEXT_PREAMBLE)
drawctxt->flags |= CTXT_FLAGS_PREAMBLE;
@@ -176,12 +174,6 @@
kgsl_sharedmem_writel(&device->memstore,
KGSL_MEMSTORE_OFFSET(drawctxt->id, ref_wait_ts),
KGSL_INIT_REFTIMESTAMP);
- kgsl_sharedmem_writel(&device->memstore,
- KGSL_MEMSTORE_OFFSET(drawctxt->id, ts_cmp_enable), 0);
- kgsl_sharedmem_writel(&device->memstore,
- KGSL_MEMSTORE_OFFSET(drawctxt->id, soptimestamp), 0);
- kgsl_sharedmem_writel(&device->memstore,
- KGSL_MEMSTORE_OFFSET(drawctxt->id, eoptimestamp), 0);
context->devctxt = drawctxt;
return 0;
@@ -207,7 +199,7 @@
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct adreno_context *drawctxt;
- if (context == NULL)
+ if (context == NULL || context->devctxt == NULL)
return;
drawctxt = context->devctxt;
diff --git a/drivers/gpu/msm/adreno_pm4types.h b/drivers/gpu/msm/adreno_pm4types.h
index fb44b25..016862b 100644
--- a/drivers/gpu/msm/adreno_pm4types.h
+++ b/drivers/gpu/msm/adreno_pm4types.h
@@ -203,7 +203,14 @@
#define type0_pkt_size(pkt) ((((pkt) >> 16) & 0x3FFF) + 1)
#define type0_pkt_offset(pkt) ((pkt) & 0x7FFF)
-#define pkt_is_type3(pkt) (((pkt) & 0xC0000000) == CP_TYPE3_PKT)
+/*
+ * Check both for the type3 opcode and make sure that the reserved bits [1:7]
+ * and 15 are 0
+ */
+
+#define pkt_is_type3(pkt) \
+ ((((pkt) & 0xC0000000) == CP_TYPE3_PKT) && \
+ (((pkt) & 0x80FE) == 0))
#define cp_type3_opcode(pkt) (((pkt) >> 8) & 0xFF)
#define type3_pkt_size(pkt) ((((pkt) >> 16) & 0x3FFF) + 1)
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index 8bc933a..3d46221 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -453,7 +453,7 @@
* error checking if needed
*/
total_sizedwords += flags & KGSL_CMD_FLAGS_PMODE ? 4 : 0;
- total_sizedwords += context ? 7 : 0;
+ total_sizedwords += !(flags & KGSL_CMD_FLAGS_NO_TS_CMP) ? 7 : 0;
total_sizedwords += !(flags & KGSL_CMD_FLAGS_NOT_KERNEL_CMD) ? 2 : 0;
if (adreno_is_a3xx(adreno_dev))
@@ -498,10 +498,9 @@
/* always increment the global timestamp. once. */
rb->timestamp[KGSL_MEMSTORE_GLOBAL]++;
-
- if (context && !(flags & KGSL_CMD_FLAGS_DUMMY_INTR_CMD)) {
+ if (context) {
if (context_id == KGSL_MEMSTORE_GLOBAL)
- rb->timestamp[context->id] =
+ rb->timestamp[context_id] =
rb->timestamp[KGSL_MEMSTORE_GLOBAL];
else
rb->timestamp[context_id]++;
@@ -531,7 +530,7 @@
GSL_RB_WRITE(ringcmds, rcmd_gpu,
cp_type3_packet(CP_MEM_WRITE, 2));
GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
- KGSL_MEMSTORE_OFFSET(context_id, soptimestamp)));
+ KGSL_MEMSTORE_OFFSET(context->id, soptimestamp)));
GSL_RB_WRITE(ringcmds, rcmd_gpu, timestamp);
/* end-of-pipeline timestamp */
@@ -539,14 +538,14 @@
cp_type3_packet(CP_EVENT_WRITE, 3));
GSL_RB_WRITE(ringcmds, rcmd_gpu, CACHE_FLUSH_TS);
GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
- KGSL_MEMSTORE_OFFSET(context_id, eoptimestamp)));
+ KGSL_MEMSTORE_OFFSET(context->id, eoptimestamp)));
GSL_RB_WRITE(ringcmds, rcmd_gpu, timestamp);
GSL_RB_WRITE(ringcmds, rcmd_gpu,
cp_type3_packet(CP_MEM_WRITE, 2));
GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
- KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
- eoptimestamp)));
+ KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+ eoptimestamp)));
GSL_RB_WRITE(ringcmds, rcmd_gpu,
rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
} else {
@@ -554,11 +553,13 @@
cp_type3_packet(CP_EVENT_WRITE, 3));
GSL_RB_WRITE(ringcmds, rcmd_gpu, CACHE_FLUSH_TS);
GSL_RB_WRITE(ringcmds, rcmd_gpu, (gpuaddr +
- KGSL_MEMSTORE_OFFSET(context_id, eoptimestamp)));
- GSL_RB_WRITE(ringcmds, rcmd_gpu, rb->timestamp[context_id]);
+ KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+ eoptimestamp)));
+ GSL_RB_WRITE(ringcmds, rcmd_gpu,
+ rb->timestamp[KGSL_MEMSTORE_GLOBAL]);
}
- if (context) {
+ if (!(flags & KGSL_CMD_FLAGS_NO_TS_CMP)) {
/* Conditional execution based on memory values */
GSL_RB_WRITE(ringcmds, rcmd_gpu,
cp_type3_packet(CP_COND_EXEC, 4));
@@ -591,30 +592,6 @@
}
void
-adreno_ringbuffer_issuecmds_intr(struct kgsl_device *device,
- struct kgsl_context *k_ctxt,
- unsigned int *cmds,
- int sizedwords)
-{
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
- struct adreno_context *a_ctxt = NULL;
-
- if (!k_ctxt)
- return;
-
- a_ctxt = k_ctxt->devctxt;
-
- if (k_ctxt->id == KGSL_CONTEXT_INVALID ||
- a_ctxt == NULL ||
- device->state & KGSL_STATE_HUNG)
- return;
-
- adreno_ringbuffer_addcmds(rb, a_ctxt, KGSL_CMD_FLAGS_DUMMY_INTR_CMD,
- cmds, sizedwords);
-}
-
-void
adreno_ringbuffer_issuecmds(struct kgsl_device *device,
unsigned int flags,
unsigned int *cmds,
diff --git a/drivers/gpu/msm/adreno_ringbuffer.h b/drivers/gpu/msm/adreno_ringbuffer.h
index 6ad85ad..ae2e4c7 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.h
+++ b/drivers/gpu/msm/adreno_ringbuffer.h
@@ -108,11 +108,6 @@
unsigned int *cmdaddr,
int sizedwords);
-void adreno_ringbuffer_issuecmds_intr(struct kgsl_device *device,
- struct kgsl_context *k_ctxt,
- unsigned int *cmdaddr,
- int sizedwords);
-
void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb);
void kgsl_cp_intrcallback(struct kgsl_device *device);
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
index 5572695..08a01b0 100644
--- a/drivers/gpu/msm/adreno_snapshot.c
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -161,6 +161,22 @@
static unsigned int sp_vs_pvt_mem_addr;
static unsigned int sp_fs_pvt_mem_addr;
+/*
+ * Each load state block has two possible types. Each type has a different
+ * number of dwords per unit. Use this handy lookup table to make sure
+ * we dump the right amount of data from the indirect buffer
+ */
+
+static int load_state_unit_sizes[7][2] = {
+ { 2, 4 },
+ { 0, 1 },
+ { 2, 4 },
+ { 0, 1 },
+ { 8, 2 },
+ { 8, 2 },
+ { 8, 2 },
+};
+
static void ib_parse_load_state(struct kgsl_device *device, unsigned int *pkt,
unsigned int ptbase)
{
@@ -170,11 +186,9 @@
* The object here is to find indirect shaders i.e - shaders loaded from
* GPU memory instead of directly in the command. These should be added
* to the list of memory objects to dump. So look at the load state
- * call and see if 1) the shader block is a shader (block = 4, 5 or 6)
- * 2) that the block is indirect (source = 4). If these all match then
- * add the memory address to the list. The size of the object will
- * differ depending on the type. Type 0 (instructions) are 8 dwords per
- * unit and type 1 (constants) are 2 dwords per unit.
+ * if the block is indirect (source = 4). If so then add the memory
+ * address to the list. The size of the object differs depending on the
+ * type per the load_state_unit_sizes array above.
*/
if (type3_pkt_size(pkt[0]) < 2)
@@ -192,9 +206,13 @@
source = (pkt[1] >> 16) & 0x07;
type = pkt[2] & 0x03;
- if ((block == 4 || block == 5 || block == 6) && source == 4) {
- int unitsize = (type == 0) ? 8 : 2;
- int ret;
+ if (source == 4) {
+ int unitsize, ret;
+
+ if (type == 0)
+ unitsize = load_state_unit_sizes[block][0];
+ else
+ unitsize = load_state_unit_sizes[block][1];
/* Freeze the GPU buffer containing the shader */
@@ -528,7 +546,6 @@
unsigned int ptbase, rptr, *rbptr, ibbase;
int index, size, i;
int parse_ibs = 0, ib_parse_start;
- int skip_pktsize = 1;
/* Get the physical address of the MMU pagetable */
ptbase = kgsl_mmu_get_current_ptbase(&device->mmu);
@@ -541,13 +558,14 @@
/*
* Figure out the window of ringbuffer data to dump. First we need to
- * find where the last processed IB ws submitted
+ * find where the last processed IB ws submitted. Start walking back
+ * from the rptr
*/
index = rptr;
rbptr = rb->buffer_desc.hostptr;
- while (index != rb->wptr) {
+ do {
index--;
if (index < 0) {
@@ -563,7 +581,7 @@
if (adreno_cmd_is_ib(rbptr[index]) &&
rbptr[index + 1] == ibbase)
break;
- }
+ } while (index != rb->wptr);
/*
* index points at the last submitted IB. We can only trust that the
@@ -636,18 +654,15 @@
* try to adust for that by modifying the rptr to match a
* packet boundary. Unfortunately for us, it is hard to tell
* which dwords are legitimate type0 header and which are just
- * random data so just walk over type0 packets until we get
- * to the first type3, and from that point on start checking the
- * size of the packet and adjusting accordingly
+ * random data so only do the adjustments for type3 packets
*/
- if (skip_pktsize && pkt_is_type3(rbptr[index]))
- skip_pktsize = 0;
-
- if (skip_pktsize == 0) {
- unsigned int pktsize = type3_pkt_size(rbptr[index]);
+ if (pkt_is_type3(rbptr[index])) {
+ unsigned int pktsize =
+ type3_pkt_size(rbptr[index]);
if (index + pktsize > rptr)
- rptr = (index + pktsize) % rb->sizedwords;
+ rptr = (index + pktsize) %
+ rb->sizedwords;
}
/*
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 81de64f..5883f089 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -2598,9 +2598,17 @@
kgsl_drm_exit();
kgsl_cffdump_destroy();
kgsl_core_debugfs_close();
- kgsl_sharedmem_uninit_sysfs();
- device_unregister(&kgsl_driver.virtdev);
+ /*
+ * We call kgsl_sharedmem_uninit_sysfs() and device_unregister()
+ * only if kgsl_driver.virtdev has been populated.
+ * We check at least one member of kgsl_driver.virtdev to
+ * see if it is not NULL (and thus, has been populated).
+ */
+ if (kgsl_driver.virtdev.class) {
+ kgsl_sharedmem_uninit_sysfs();
+ device_unregister(&kgsl_driver.virtdev);
+ }
if (kgsl_driver.class) {
class_destroy(kgsl_driver.class);
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 64acff8..932c995 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -90,7 +90,7 @@
void (*power_stats)(struct kgsl_device *device,
struct kgsl_power_stats *stats);
void (*irqctrl)(struct kgsl_device *device, int state);
- unsigned int (*gpuid)(struct kgsl_device *device);
+ unsigned int (*gpuid)(struct kgsl_device *device, unsigned int *chipid);
void * (*snapshot)(struct kgsl_device *device, void *snapshot,
int *remain, int hang);
irqreturn_t (*irq_handler)(struct kgsl_device *device);
@@ -287,9 +287,10 @@
return device->ftbl->idle(device, timeout);
}
-static inline unsigned int kgsl_gpuid(struct kgsl_device *device)
+static inline unsigned int kgsl_gpuid(struct kgsl_device *device,
+ unsigned int *chipid)
{
- return device->ftbl->gpuid(device);
+ return device->ftbl->gpuid(device, chipid);
}
static inline unsigned int kgsl_readtimestamp(struct kgsl_device *device,
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index b3f2d1e..d20cf7e0 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -17,6 +17,7 @@
#include <linux/slab.h>
#include <linux/iommu.h>
#include <linux/msm_kgsl.h>
+#include <mach/socinfo.h>
#include "kgsl.h"
#include "kgsl_device.h"
@@ -268,14 +269,17 @@
struct kgsl_iommu *iommu = mmu->priv;
int i, j;
- BUG_ON(mmu->hwpagetable == NULL);
- BUG_ON(mmu->hwpagetable->priv == NULL);
-
- iommu_pt = mmu->hwpagetable->priv;
-
for (i = 0; i < iommu->unit_count; i++) {
struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
+ iommu_pt = mmu->defaultpagetable->priv;
for (j = 0; j < iommu_unit->dev_count; j++) {
+ /*
+ * If there is a 2nd default pagetable then priv domain
+ * is attached with this pagetable
+ */
+ if (mmu->priv_bank_table &&
+ (KGSL_IOMMU_CONTEXT_PRIV == j))
+ iommu_pt = mmu->priv_bank_table->priv;
if (iommu_unit->dev[j].attached) {
iommu_detach_device(iommu_pt->domain,
iommu_unit->dev[j].dev);
@@ -307,18 +311,21 @@
struct kgsl_iommu *iommu = mmu->priv;
int i, j, ret = 0;
- BUG_ON(mmu->hwpagetable == NULL);
- BUG_ON(mmu->hwpagetable->priv == NULL);
-
- iommu_pt = mmu->hwpagetable->priv;
-
/*
* Loop through all the iommu devcies under all iommu units and
* attach the domain
*/
for (i = 0; i < iommu->unit_count; i++) {
struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
+ iommu_pt = mmu->defaultpagetable->priv;
for (j = 0; j < iommu_unit->dev_count; j++) {
+ /*
+ * If there is a 2nd default pagetable then priv domain
+ * is attached to this pagetable
+ */
+ if (mmu->priv_bank_table &&
+ (KGSL_IOMMU_CONTEXT_PRIV == j))
+ iommu_pt = mmu->priv_bank_table->priv;
if (!iommu_unit->dev[j].attached) {
ret = iommu_attach_device(iommu_pt->domain,
iommu_unit->dev[j].dev);
@@ -614,17 +621,32 @@
int i = 0;
struct kgsl_iommu *iommu = mmu->priv;
struct kgsl_iommu_pt *iommu_pt;
+ struct kgsl_pagetable *pagetable = NULL;
+ /* If chip is not 8960 then we use the 2nd context bank for pagetable
+ * switching on the 3D side for which a separate table is allocated */
+ if (!cpu_is_msm8960()) {
+ mmu->priv_bank_table =
+ kgsl_mmu_getpagetable(KGSL_MMU_PRIV_BANK_TABLE_NAME);
+ if (mmu->priv_bank_table == NULL) {
+ status = -ENOMEM;
+ goto err;
+ }
+ iommu_pt = mmu->priv_bank_table->priv;
+ iommu_pt->asid = 1;
+ }
mmu->defaultpagetable = kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT);
/* Return error if the default pagetable doesn't exist */
if (mmu->defaultpagetable == NULL) {
status = -ENOMEM;
goto err;
}
+ pagetable = mmu->priv_bank_table ? mmu->priv_bank_table :
+ mmu->defaultpagetable;
/* Map the IOMMU regsiters to only defaultpagetable */
for (i = 0; i < iommu->unit_count; i++) {
iommu->iommu_units[i].reg_map.priv |= KGSL_MEMFLAGS_GLOBAL;
- status = kgsl_mmu_map(mmu->defaultpagetable,
+ status = kgsl_mmu_map(pagetable,
&(iommu->iommu_units[i].reg_map),
GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
if (status) {
@@ -644,10 +666,14 @@
return status;
err:
for (i--; i >= 0; i--) {
- kgsl_mmu_unmap(mmu->defaultpagetable,
+ kgsl_mmu_unmap(pagetable,
&(iommu->iommu_units[i].reg_map));
iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMFLAGS_GLOBAL;
}
+ if (mmu->priv_bank_table) {
+ kgsl_mmu_putpagetable(mmu->priv_bank_table);
+ mmu->priv_bank_table = NULL;
+ }
if (mmu->defaultpagetable) {
kgsl_mmu_putpagetable(mmu->defaultpagetable);
mmu->defaultpagetable = NULL;
@@ -669,9 +695,9 @@
if (status)
return -ENOMEM;
}
- /* We use the GPU MMU to control access to IOMMU registers on a225,
- * hence we still keep the MMU active on a225 */
- if (adreno_is_a225(ADRENO_DEVICE(mmu->device))) {
+ /* We use the GPU MMU to control access to IOMMU registers on 8960 with
+ * a225, hence we still keep the MMU active on 8960 */
+ if (cpu_is_msm8960()) {
struct kgsl_mh *mh = &(mmu->device->mh);
kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000001);
kgsl_regwrite(mmu->device, MH_MMU_MPU_END,
@@ -707,6 +733,12 @@
*/
for (i = 0; i < iommu->unit_count; i++) {
struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
+ /* Make sure that the ASID of the priv bank is set to 1.
+ * When we a different pagetable for the priv bank then the
+ * iommu driver sets the ASID to 0 instead of 1 */
+ KGSL_IOMMU_SET_IOMMU_REG(iommu->iommu_units[i].reg_map.hostptr,
+ KGSL_IOMMU_CONTEXT_PRIV,
+ CONTEXTIDR, 1);
for (j = 0; j < iommu_unit->dev_count; j++)
iommu_unit->dev[j].pt_lsb = KGSL_IOMMMU_PT_LSB(
KGSL_IOMMU_GET_IOMMU_REG(
@@ -816,14 +848,19 @@
struct kgsl_iommu *iommu = mmu->priv;
int i;
for (i = 0; i < iommu->unit_count; i++) {
+ struct kgsl_pagetable *pagetable = (mmu->priv_bank_table ?
+ mmu->priv_bank_table : mmu->defaultpagetable);
if (iommu->iommu_units[i].reg_map.gpuaddr)
- kgsl_mmu_unmap(mmu->defaultpagetable,
+ kgsl_mmu_unmap(pagetable,
&(iommu->iommu_units[i].reg_map));
if (iommu->iommu_units[i].reg_map.hostptr)
iounmap(iommu->iommu_units[i].reg_map.hostptr);
kgsl_sg_free(iommu->iommu_units[i].reg_map.sg,
iommu->iommu_units[i].reg_map.sglen);
}
+
+ if (mmu->priv_bank_table)
+ kgsl_mmu_putpagetable(mmu->priv_bank_table);
if (mmu->defaultpagetable)
kgsl_mmu_putpagetable(mmu->defaultpagetable);
kfree(iommu->asids);
@@ -837,6 +874,10 @@
{
unsigned int pt_base;
struct kgsl_iommu *iommu = mmu->priv;
+ /* We cannot enable or disable the clocks in interrupt context, this
+ function is called from interrupt context if there is an axi error */
+ if (in_interrupt())
+ return 0;
/* Return the current pt base by reading IOMMU pt_base register */
kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
pt_base = readl_relaxed(iommu->iommu_units[0].reg_map.hostptr +
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index 5216b34..dfaadba 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -39,7 +39,8 @@
/* For IOMMU only unmap the global structures to global pt */
if ((KGSL_MMU_TYPE_NONE != kgsl_mmu_type) &&
(KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type) &&
- (KGSL_MMU_GLOBAL_PT != pt->name))
+ (KGSL_MMU_GLOBAL_PT != pt->name) &&
+ (KGSL_MMU_PRIV_BANK_TABLE_NAME != pt->name))
return 0;
for (i = 0; i < KGSL_DEVICE_MAX; i++) {
struct kgsl_device *device = kgsl_driver.devp[i];
@@ -58,7 +59,8 @@
/* For IOMMU only map the global structures to global pt */
if ((KGSL_MMU_TYPE_NONE != kgsl_mmu_type) &&
(KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type) &&
- (KGSL_MMU_GLOBAL_PT != pt->name))
+ (KGSL_MMU_GLOBAL_PT != pt->name) &&
+ (KGSL_MMU_PRIV_BANK_TABLE_NAME != pt->name))
return 0;
for (i = 0; i < KGSL_DEVICE_MAX; i++) {
struct kgsl_device *device = kgsl_driver.devp[i];
@@ -453,9 +455,9 @@
* just once from this pool of the defaultpagetable
*/
if ((KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype()) &&
- (KGSL_MMU_GLOBAL_PT == name)) {
- pagetable->kgsl_pool = gen_pool_create(KGSL_MMU_ALIGN_SHIFT,
- -1);
+ ((KGSL_MMU_GLOBAL_PT == name) ||
+ (KGSL_MMU_PRIV_BANK_TABLE_NAME == name))) {
+ pagetable->kgsl_pool = gen_pool_create(PAGE_SHIFT, -1);
if (pagetable->kgsl_pool == NULL) {
KGSL_CORE_ERR("gen_pool_create(%d) failed\n",
KGSL_MMU_ALIGN_SHIFT);
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index 2db327b..4c0c015 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -29,6 +29,7 @@
as an identifier */
#define KGSL_MMU_GLOBAL_PT 0
+#define KGSL_MMU_PRIV_BANK_TABLE_NAME 0xFFFFFFFF
struct kgsl_device;
@@ -165,6 +166,8 @@
struct kgsl_memdesc setstate_memory;
/* current page table object being used by device mmu */
struct kgsl_pagetable *defaultpagetable;
+ /* pagetable object used for priv bank of IOMMU */
+ struct kgsl_pagetable *priv_bank_table;
struct kgsl_pagetable *hwpagetable;
const struct kgsl_mmu_ops *mmu_ops;
void *priv;
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index 8935b29..824d806 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -35,6 +35,56 @@
struct list_head node;
};
+struct snapshot_obj_itr {
+ void *buf; /* Buffer pointer to write to */
+ int pos; /* Current position in the sequence */
+ loff_t offset; /* file offset to start writing from */
+ size_t remain; /* Bytes remaining in buffer */
+ size_t write; /* Bytes written so far */
+};
+
+static void obj_itr_init(struct snapshot_obj_itr *itr, void *buf,
+ loff_t offset, size_t remain)
+{
+ itr->buf = buf;
+ itr->offset = offset;
+ itr->remain = remain;
+ itr->pos = 0;
+ itr->write = 0;
+}
+
+static int obj_itr_out(struct snapshot_obj_itr *itr, void *src, int size)
+{
+ if (itr->remain == 0)
+ return 0;
+
+ if ((itr->pos + size) <= itr->offset)
+ goto done;
+
+ /* Handle the case that offset is in the middle of the buffer */
+
+ if (itr->offset > itr->pos) {
+ src += (itr->offset - itr->pos);
+ size -= (itr->offset - itr->pos);
+
+ /* Advance pos to the offset start */
+ itr->pos = itr->offset;
+ }
+
+ if (size > itr->remain)
+ size = itr->remain;
+
+ memcpy(itr->buf, src, size);
+
+ itr->buf += size;
+ itr->write += size;
+ itr->remain -= size;
+
+done:
+ itr->pos += size;
+ return size;
+}
+
/* idr_for_each function to count the number of contexts */
static int snapshot_context_count(int id, void *ptr, void *data)
@@ -182,76 +232,46 @@
(sizeof(struct kgsl_snapshot_section_header) + \
sizeof(struct kgsl_snapshot_gpu_object))
-#define GPU_OBJ_SECTION_SIZE(_o) \
- (GPU_OBJ_HEADER_SZ + ((_o)->size))
-
static int kgsl_snapshot_dump_object(struct kgsl_device *device,
- struct kgsl_snapshot_object *obj, void *buf,
- unsigned int off, unsigned int count)
+ struct kgsl_snapshot_object *obj, struct snapshot_obj_itr *itr)
{
- unsigned char headers[GPU_OBJ_HEADER_SZ];
- struct kgsl_snapshot_section_header *sect =
- (struct kgsl_snapshot_section_header *) headers;
- struct kgsl_snapshot_gpu_object *header =
- (struct kgsl_snapshot_gpu_object *) (headers + sizeof(*sect));
- int ret = 0;
+ struct kgsl_snapshot_section_header sect;
+ struct kgsl_snapshot_gpu_object header;
+ int ret;
- /* Construct a local copy of the headers */
+ sect.magic = SNAPSHOT_SECTION_MAGIC;
+ sect.id = KGSL_SNAPSHOT_SECTION_GPU_OBJECT;
- sect->magic = SNAPSHOT_SECTION_MAGIC;
- sect->id = KGSL_SNAPSHOT_SECTION_GPU_OBJECT;
- sect->size = GPU_OBJ_SECTION_SIZE(obj);
+ /*
+ * Header size is in dwords, object size is in bytes -
+ * round up if the object size isn't dword aligned
+ */
- header->type = obj->type;
+ sect.size = GPU_OBJ_HEADER_SZ + ALIGN(obj->size, 4);
- /* Header size is in dwords, object size is in bytes */
- header->size = obj->size >> 2;
- header->gpuaddr = obj->gpuaddr;
- header->ptbase = obj->ptbase;
+ ret = obj_itr_out(itr, §, sizeof(sect));
+ if (ret == 0)
+ return 0;
- /* Copy out any part of the header block that is needed */
+ header.size = ALIGN(obj->size, 4) >> 2;
+ header.gpuaddr = obj->gpuaddr;
+ header.ptbase = obj->ptbase;
+ header.type = obj->type;
- if (off < GPU_OBJ_HEADER_SZ) {
- int size = count < GPU_OBJ_HEADER_SZ - off ?
- count : GPU_OBJ_HEADER_SZ - off;
+ ret = obj_itr_out(itr, &header, sizeof(header));
+ if (ret == 0)
+ return 0;
- memcpy(buf, headers + off, size);
+ ret = obj_itr_out(itr, obj->entry->memdesc.hostptr + obj->offset,
+ obj->size);
+ if (ret == 0)
+ return 0;
- count -= size;
- ret += size;
- }
+ /* Pad the end to a dword boundary if we need to */
- /* Now copy whatever part of the data is needed */
-
- if (off < (GPU_OBJ_HEADER_SZ + obj->size)) {
- int offset;
- int size = count < obj->size ? count : obj->size;
-
- /*
- * If the desired gpuaddr isn't at the beginning of the region,
- * then offset the source pointer
- */
-
- offset = obj->offset;
-
- /*
- * Then adjust it to account for the offset for the output
- * buffer.
- */
-
- if (off > GPU_OBJ_HEADER_SZ) {
- int loff = (off - GPU_OBJ_HEADER_SZ);
-
- /* Adjust the size so we don't walk off the end */
-
- if ((loff + size) > obj->size)
- size = obj->size - loff;
-
- offset += loff;
- }
-
- memcpy(buf + ret, obj->entry->memdesc.hostptr + offset, size);
- ret += size;
+ if (obj->size % 4) {
+ unsigned int dummy = 0;
+ ret = obj_itr_out(itr, &dummy, obj->size % 4);
}
return ret;
@@ -488,7 +508,7 @@
header->magic = SNAPSHOT_MAGIC;
- header->gpuid = kgsl_gpuid(device);
+ header->gpuid = kgsl_gpuid(device, &header->chipid);
/* Get a pointer to the first section (right after the header) */
snapshot = ((void *) device->snapshot) + sizeof(*header);
@@ -539,7 +559,9 @@
{
struct kgsl_device *device = kobj_to_device(kobj);
struct kgsl_snapshot_object *obj, *tmp;
- unsigned int size, src, dst = 0;
+ struct kgsl_snapshot_section_header head;
+ struct snapshot_obj_itr itr;
+ int ret;
if (device == NULL)
return 0;
@@ -551,80 +573,46 @@
/* Get the mutex to keep things from changing while we are dumping */
mutex_lock(&device->mutex);
- if (off < device->snapshot_size) {
- size = count < (device->snapshot_size - off) ?
- count : device->snapshot_size - off;
+ obj_itr_init(&itr, buf, off, count);
- memcpy(buf, device->snapshot + off, size);
+ ret = obj_itr_out(&itr, device->snapshot, device->snapshot_size);
- count -= size;
- dst += size;
- }
-
- if (count == 0)
+ if (ret == 0)
goto done;
- src = device->snapshot_size;
+ list_for_each_entry(obj, &device->snapshot_obj_list, node)
+ kgsl_snapshot_dump_object(device, obj, &itr);
- list_for_each_entry(obj, &device->snapshot_obj_list, node) {
+ {
+ head.magic = SNAPSHOT_SECTION_MAGIC;
+ head.id = KGSL_SNAPSHOT_SECTION_END;
+ head.size = sizeof(head);
- int objsize = GPU_OBJ_SECTION_SIZE(obj);
- int offset;
-
- /* If the offset is beyond this object, then move on */
-
- if (off >= (src + objsize)) {
- src += objsize;
- continue;
- }
-
- /* Adjust the offset to be relative to the object */
- offset = (off >= src) ? (off - src) : 0;
-
- size = kgsl_snapshot_dump_object(device, obj, buf + dst,
- offset, count);
-
- count -= size;
- dst += size;
-
- if (count == 0)
- goto done;
-
- /* Move on to the next object - update src accordingly */
- src += objsize;
+ obj_itr_out(&itr, &head, sizeof(head));
}
- /* Add the end section */
+ /*
+ * Make sure everything has been written out before destroying things.
+ * The best way to confirm this is to go all the way through without
+ * writing any bytes - so only release if we get this far and
+ * itr->write is 0
+ */
- if (off < (src + sizeof(struct kgsl_snapshot_section_header))) {
- if (count >= sizeof(struct kgsl_snapshot_section_header)) {
- struct kgsl_snapshot_section_header *head =
- (void *) (buf + dst);
+ if (itr.write == 0) {
+ list_for_each_entry_safe(obj, tmp, &device->snapshot_obj_list,
+ node)
+ kgsl_snapshot_put_object(device, obj);
- head->magic = SNAPSHOT_SECTION_MAGIC;
- head->id = KGSL_SNAPSHOT_SECTION_END;
- head->size = sizeof(*head);
+ if (device->snapshot_frozen)
+ KGSL_DRV_ERR(device, "Snapshot objects released\n");
- dst += sizeof(*head);
- } else {
- goto done;
- }
+ device->snapshot_frozen = 0;
}
- /* Release the buffers and unfreeze the snapshot */
-
- list_for_each_entry_safe(obj, tmp, &device->snapshot_obj_list, node)
- kgsl_snapshot_put_object(device, obj);
-
- if (device->snapshot_frozen)
- KGSL_DRV_ERR(device, "Snapshot objects released\n");
-
- device->snapshot_frozen = 0;
-
done:
mutex_unlock(&device->mutex);
- return dst;
+ return itr.write;
}
/* Show the timestamp of the last collected snapshot */
diff --git a/drivers/gpu/msm/kgsl_snapshot.h b/drivers/gpu/msm/kgsl_snapshot.h
index 304f4bb..d54afcf 100644
--- a/drivers/gpu/msm/kgsl_snapshot.h
+++ b/drivers/gpu/msm/kgsl_snapshot.h
@@ -18,7 +18,8 @@
/* Snapshot header */
-#define SNAPSHOT_MAGIC 0x504D0001
+/* High word is static, low word is snapshot version ID */
+#define SNAPSHOT_MAGIC 0x504D0002
/* GPU ID scheme:
* [16:31] - core identifer (0x0002 for 2D or 0x0003 for 3D)
@@ -28,6 +29,8 @@
struct kgsl_snapshot_header {
__u32 magic; /* Magic identifier */
__u32 gpuid; /* GPU ID - see above */
+ /* Added in snapshot version 2 */
+ __u32 chipid; /* Chip ID from the GPU */
} __packed;
/* Section header */
@@ -140,6 +143,7 @@
#define SNAPSHOT_DEBUG_CP_PM4_RAM 8
#define SNAPSHOT_DEBUG_CP_PFP_RAM 9
#define SNAPSHOT_DEBUG_CP_ROQ 10
+#define SNAPSHOT_DEBUG_SHADER_MEMORY 11
struct kgsl_snapshot_debug {
int type; /* Type identifier for the attached tata */
diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c
index f4bbf69..bc2685c 100644
--- a/drivers/gpu/msm/z180.c
+++ b/drivers/gpu/msm/z180.c
@@ -896,8 +896,11 @@
}
}
-static unsigned int z180_gpuid(struct kgsl_device *device)
+static unsigned int z180_gpuid(struct kgsl_device *device, unsigned int *chipid)
{
+ if (chipid != NULL)
+ *chipid = 0;
+
/* Standard KGSL gpuid format:
* top word is 0x0002 for 2D or 0x0003 for 3D
* Bottom word is core specific identifer
diff --git a/drivers/i2c/busses/i2c-msm.c b/drivers/i2c/busses/i2c-msm.c
index b753fd0..4b48a69 100644
--- a/drivers/i2c/busses/i2c-msm.c
+++ b/drivers/i2c/busses/i2c-msm.c
@@ -636,7 +636,7 @@
spin_lock_init(&dev->lock);
platform_set_drvdata(pdev, dev);
- clk_enable(clk);
+ clk_prepare_enable(clk);
if (pdata->rmutex) {
struct remote_mutex_id rmid;
@@ -697,7 +697,8 @@
/* Config GPIOs for primary and secondary lines */
pdata->msm_i2c_config_gpio(dev->adap_pri.nr, 1);
pdata->msm_i2c_config_gpio(dev->adap_aux.nr, 1);
- clk_disable(dev->clk);
+ clk_disable_unprepare(dev->clk);
+ clk_prepare(dev->clk);
setup_timer(&dev->pwr_timer, msm_i2c_pwr_timer, (unsigned long) dev);
return 0;
@@ -706,7 +707,7 @@
i2c_del_adapter(&dev->adap_pri);
i2c_del_adapter(&dev->adap_aux);
err_i2c_add_adapter_failed:
- clk_disable(clk);
+ clk_disable_unprepare(clk);
iounmap(dev->base);
err_ioremap_failed:
kfree(dev);
@@ -736,6 +737,7 @@
free_irq(dev->irq, dev);
i2c_del_adapter(&dev->adap_pri);
i2c_del_adapter(&dev->adap_aux);
+ clk_unprepare(dev->clk);
clk_put(dev->clk);
iounmap(dev->base);
kfree(dev);
@@ -759,6 +761,7 @@
del_timer_sync(&dev->pwr_timer);
if (dev->clk_state != 0)
msm_i2c_pwr_mgmt(dev, 0);
+ clk_unprepare(dev->clk);
}
return 0;
@@ -767,6 +770,7 @@
static int msm_i2c_resume(struct platform_device *pdev)
{
struct msm_i2c_dev *dev = platform_get_drvdata(pdev);
+ clk_prepare(dev->clk);
dev->suspended = 0;
return 0;
}
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index daf21b8..78fc3ec 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -1769,7 +1769,7 @@
kref_put(&send_ioctx->kref, srpt_put_send_ioctx_kref);
goto send_sense;
}
- ret = transport_generic_allocate_tasks(cmd, srp_cmd->cdb);
+ ret = target_setup_cmd_from_cdb(cmd, srp_cmd->cdb);
if (ret < 0) {
kref_put(&send_ioctx->kref, srpt_put_send_ioctx_kref);
if (cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT) {
diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c
index 5403c57..8b6e172 100644
--- a/drivers/input/misc/mpu3050.c
+++ b/drivers/input/misc/mpu3050.c
@@ -40,6 +40,8 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
+#include <linux/input/mpu3050.h>
+#include <linux/regulator/consumer.h>
#define MPU3050_CHIP_ID 0x69
@@ -112,8 +114,81 @@
struct i2c_client *client;
struct device *dev;
struct input_dev *idev;
+ struct mpu3050_gyro_platform_data *platform_data;
+ struct delayed_work input_work;
+ u32 use_poll;
};
+struct sensor_regulator {
+ struct regulator *vreg;
+ const char *name;
+ u32 min_uV;
+ u32 max_uV;
+};
+
+struct sensor_regulator mpu_vreg[] = {
+ {NULL, "vdd", 2100000, 3600000},
+ {NULL, "vlogic", 1800000, 1800000},
+};
+
+static int mpu3050_config_regulator(struct i2c_client *client, bool on)
+{
+ int rc = 0, i;
+ int num_reg = sizeof(mpu_vreg) / sizeof(struct sensor_regulator);
+
+ if (on) {
+ for (i = 0; i < num_reg; i++) {
+ mpu_vreg[i].vreg = regulator_get(&client->dev,
+ mpu_vreg[i].name);
+ if (IS_ERR(mpu_vreg[i].vreg)) {
+ rc = PTR_ERR(mpu_vreg[i].vreg);
+ pr_err("%s:regulator get failed rc=%d\n",
+ __func__, rc);
+ goto error_vdd;
+ }
+
+ if (regulator_count_voltages(mpu_vreg[i].vreg) > 0) {
+ rc = regulator_set_voltage(mpu_vreg[i].vreg,
+ mpu_vreg[i].min_uV, mpu_vreg[i].max_uV);
+ if (rc) {
+ pr_err("%s:set_voltage failed rc=%d\n",
+ __func__, rc);
+ regulator_put(mpu_vreg[i].vreg);
+ goto error_vdd;
+ }
+ }
+
+ rc = regulator_enable(mpu_vreg[i].vreg);
+ if (rc) {
+ pr_err("%s: regulator_enable failed rc =%d\n",
+ __func__,
+ rc);
+
+ if (regulator_count_voltages(
+ mpu_vreg[i].vreg) > 0) {
+ regulator_set_voltage(mpu_vreg[i].vreg,
+ 0, mpu_vreg[i].max_uV);
+ }
+ regulator_put(mpu_vreg[i].vreg);
+ goto error_vdd;
+ }
+ }
+ return rc;
+ } else {
+ i = num_reg;
+ }
+error_vdd:
+ while (--i >= 0) {
+ if (regulator_count_voltages(mpu_vreg[i].vreg) > 0) {
+ regulator_set_voltage(mpu_vreg[i].vreg, 0,
+ mpu_vreg[i].max_uV);
+ }
+ regulator_disable(mpu_vreg[i].vreg);
+ regulator_put(mpu_vreg[i].vreg);
+ }
+ return rc;
+}
+
/**
* mpu3050_xyz_read_reg - read the axes values
* @buffer: provide register addr and get register
@@ -179,11 +254,21 @@
{
u8 value;
+ if (val) {
+ mpu3050_config_regulator(client, 1);
+ udelay(10);
+ }
+
value = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM);
value = (value & ~MPU3050_PWR_MGM_MASK) |
(((val << MPU3050_PWR_MGM_POS) & MPU3050_PWR_MGM_MASK) ^
MPU3050_PWR_MGM_MASK);
i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM, value);
+
+ if (!val) {
+ udelay(10);
+ mpu3050_config_regulator(client, 0);
+ }
}
/**
@@ -210,6 +295,9 @@
pm_runtime_put(sensor->dev);
return error;
}
+ if (sensor->use_poll)
+ schedule_delayed_work(&sensor->input_work,
+ msecs_to_jiffies(MPU3050_DEFAULT_POLL_INTERVAL));
return 0;
}
@@ -225,6 +313,9 @@
{
struct mpu3050_sensor *sensor = input_get_drvdata(input);
+ if (sensor->use_poll)
+ cancel_delayed_work_sync(&sensor->input_work);
+
pm_runtime_put(sensor->dev);
}
@@ -252,6 +343,33 @@
}
/**
+ * mpu3050_input_work_fn - polling work
+ * @work: the work struct
+ *
+ * Called by the work queue; read sensor data and generate an input
+ * event
+ */
+static void mpu3050_input_work_fn(struct work_struct *work)
+{
+ struct mpu3050_sensor *sensor;
+ struct axis_data axis;
+
+ sensor = container_of((struct delayed_work *)work,
+ struct mpu3050_sensor, input_work);
+
+ mpu3050_read_xyz(sensor->client, &axis);
+
+ input_report_abs(sensor->idev, ABS_X, axis.x);
+ input_report_abs(sensor->idev, ABS_Y, axis.y);
+ input_report_abs(sensor->idev, ABS_Z, axis.z);
+ input_sync(sensor->idev);
+
+ if (sensor->use_poll)
+ schedule_delayed_work(&sensor->input_work,
+ msecs_to_jiffies(MPU3050_DEFAULT_POLL_INTERVAL));
+}
+
+/**
* mpu3050_hw_init - initialize hardware
* @sensor: the sensor
*
@@ -325,6 +443,7 @@
sensor->client = client;
sensor->dev = &client->dev;
sensor->idev = idev;
+ sensor->platform_data = client->dev.platform_data;
mpu3050_set_power_mode(client, 1);
msleep(10);
@@ -365,14 +484,22 @@
if (error)
goto err_pm_set_suspended;
- error = request_threaded_irq(client->irq,
+ if (client->irq == 0) {
+ INIT_DELAYED_WORK(&sensor->input_work, mpu3050_input_work_fn);
+ sensor->use_poll = 1;
+ } else {
+ sensor->use_poll = 0;
+
+ error = request_threaded_irq(client->irq,
NULL, mpu3050_interrupt_thread,
IRQF_TRIGGER_RISING,
"mpu3050", sensor);
- if (error) {
- dev_err(&client->dev,
- "can't get IRQ %d, error %d\n", client->irq, error);
- goto err_pm_set_suspended;
+ if (error) {
+ dev_err(&client->dev,
+ "can't get IRQ %d, error %d\n",
+ client->irq, error);
+ goto err_pm_set_suspended;
+ }
}
error = input_register_device(idev);
@@ -387,7 +514,8 @@
return 0;
err_free_irq:
- free_irq(client->irq, sensor);
+ if (client->irq > 0)
+ free_irq(client->irq, sensor);
err_pm_set_suspended:
pm_runtime_set_suspended(&client->dev);
err_free_mem:
diff --git a/drivers/input/touchscreen/atmel_maxtouch.c b/drivers/input/touchscreen/atmel_maxtouch.c
index 52f1f4a..9bdeb51 100644
--- a/drivers/input/touchscreen/atmel_maxtouch.c
+++ b/drivers/input/touchscreen/atmel_maxtouch.c
@@ -2034,6 +2034,7 @@
mxt_debug(DEBUG_INFO, "maXTouch driver setting abs parameters\n");
set_bit(BTN_TOUCH, input->keybit);
+ set_bit(INPUT_PROP_DIRECT, input->propbit);
/* Single touch */
input_set_abs_params(input, ABS_X, mxt->min_x_val,
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 09ff05d..23317d6 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -2296,6 +2296,7 @@
__set_bit(EV_ABS, input_dev->evbit);
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(BTN_TOUCH, input_dev->keybit);
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
/* For single touch */
input_set_abs_params(input_dev, ABS_X,
diff --git a/drivers/input/touchscreen/cyttsp-i2c-qc.c b/drivers/input/touchscreen/cyttsp-i2c-qc.c
index e82dd13..5af4534 100644
--- a/drivers/input/touchscreen/cyttsp-i2c-qc.c
+++ b/drivers/input/touchscreen/cyttsp-i2c-qc.c
@@ -2510,6 +2510,8 @@
set_bit(EV_ABS, input_device->evbit);
set_bit(BTN_TOUCH, input_device->keybit);
set_bit(BTN_2, input_device->keybit);
+ set_bit(INPUT_PROP_DIRECT, input_device->propbit);
+
if (ts->platform_data->use_gestures)
set_bit(BTN_3, input_device->keybit);
diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c
index c9905a4a..ffeb8fe7 100644
--- a/drivers/input/touchscreen/ft5x06_ts.c
+++ b/drivers/input/touchscreen/ft5x06_ts.c
@@ -445,6 +445,7 @@
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(EV_ABS, input_dev->evbit);
__set_bit(BTN_TOUCH, input_dev->keybit);
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
pdata->x_max, 0, 0);
diff --git a/drivers/media/radio/radio-tavarua.c b/drivers/media/radio/radio-tavarua.c
index c15609f..971cf10 100644
--- a/drivers/media/radio/radio-tavarua.c
+++ b/drivers/media/radio/radio-tavarua.c
@@ -120,6 +120,8 @@
/*PS repeatcount for PS Tx */
int ps_repeatcount;
int enable_optimized_srch_alg;
+ unsigned char spur_table_size;
+ struct fm_spur_data spur_data;
};
/**************************************************************************
@@ -147,6 +149,10 @@
enum radio_state_t state);
static int tavarua_request_irq(struct tavarua_device *radio);
static void start_pending_xfr(struct tavarua_device *radio);
+static int update_spur_table(struct tavarua_device *radio);
+static int xfr_rdwr_data(struct tavarua_device *radio, int op, int size,
+ unsigned long offset, unsigned char *buf);
+
/* work function */
static void read_int_stat(struct work_struct *work);
@@ -1025,6 +1031,35 @@
FMDBG("write PHY_TXGAIN is successful");
complete(&radio->sync_req_done);
break;
+ case (XFR_POKE_COMPLETE | LSH_DATA(ONE_BYTE, 1)):
+ case (XFR_POKE_COMPLETE | LSH_DATA(TWO_BYTE, 1)):
+ case (XFR_POKE_COMPLETE | LSH_DATA(THREE_BYTE, 1)):
+ case (XFR_POKE_COMPLETE | LSH_DATA(FOUR_BYTE, 1)):
+ case (XFR_POKE_COMPLETE | LSH_DATA(FIVE_BYTE, 1)):
+ case (XFR_POKE_COMPLETE | LSH_DATA(SIX_BYTE, 1)):
+ case (XFR_POKE_COMPLETE | LSH_DATA(SEVEN_BYTE, 1)):
+ case (XFR_POKE_COMPLETE | LSH_DATA(EIGHT_BYTE, 1)):
+ case (XFR_POKE_COMPLETE | LSH_DATA(NINE_BYTE, 1)):
+ case (XFR_POKE_COMPLETE | LSH_DATA(TEN_BYTE, 1)):
+ case (XFR_POKE_COMPLETE | LSH_DATA(ELEVEN_BYTE, 1)):
+ case (XFR_POKE_COMPLETE | LSH_DATA(TWELVE_BYTE, 1)):
+ case (XFR_POKE_COMPLETE | LSH_DATA(THIRTEEN_BYTE, 1)):
+ case (XFR_PEEK_COMPLETE | LSH_DATA(ONE_BYTE, 1)):
+ case (XFR_PEEK_COMPLETE | LSH_DATA(TWO_BYTE, 1)):
+ case (XFR_PEEK_COMPLETE | LSH_DATA(THREE_BYTE, 1)):
+ case (XFR_PEEK_COMPLETE | LSH_DATA(FOUR_BYTE, 1)):
+ case (XFR_PEEK_COMPLETE | LSH_DATA(FIVE_BYTE, 1)):
+ case (XFR_PEEK_COMPLETE | LSH_DATA(SIX_BYTE, 1)):
+ case (XFR_PEEK_COMPLETE | LSH_DATA(SEVEN_BYTE, 1)):
+ case (XFR_PEEK_COMPLETE | LSH_DATA(EIGHT_BYTE, 1)):
+ case (XFR_PEEK_COMPLETE | LSH_DATA(NINE_BYTE, 1)):
+ case (XFR_PEEK_COMPLETE | LSH_DATA(TEN_BYTE, 1)):
+ case (XFR_PEEK_COMPLETE | LSH_DATA(ELEVEN_BYTE, 1)):
+ case (XFR_PEEK_COMPLETE | LSH_DATA(TWELVE_BYTE, 1)):
+ case (XFR_PEEK_COMPLETE | LSH_DATA(THIRTEEN_BYTE, 1)):
+ FMDBG("XFR interrupt for PEEK/POKE complete\n");
+ complete(&radio->sync_req_done);
+ break;
default:
FMDERR("UNKNOWN XFR = %d\n", xfr_status);
}
@@ -2158,6 +2193,7 @@
wait_for_completion(&radio->shutdown_done);
radio->handle_irq = 1;
radio->lp_mode = 1;
+ radio->spur_table_size = 0;
atomic_inc(&radio->users);
radio->marimba->mod_id = SLAVE_ID_BAHAMA;
flush_workqueue(radio->wqueue);
@@ -2481,6 +2517,135 @@
return retval;
}
+
+static int update_spur_table(struct tavarua_device *radio)
+{
+ unsigned char xfr_buf[XFR_REG_NUM];
+ unsigned char size = 0, tbl_size = 0;
+ int index = 0, offset = 0, addr = 0x0, val = 0;
+ int retval = 0, temp = 0, cnt = 0, j = 0;
+
+ memset(xfr_buf, 0x0, XFR_REG_NUM);
+
+ /* Read the SPUR Table Size */
+ retval = xfr_rdwr_data(radio, XFR_READ, 1, SPUR_TABLE_ADDR, &tbl_size);
+ if (retval < 0) {
+ FMDERR("%s: Failed to read SPUR table size\n", __func__);
+ return retval;
+ }
+
+ /* Calculate the new SPUR Register address */
+ val = addr = (SPUR_TABLE_START_ADDR + (tbl_size * 3));
+
+ /* Save the SPUR Table length configured by user*/
+ temp = radio->spur_table_size;
+
+ /* COnfigure the new spur table length */
+ size = (radio->spur_table_size + tbl_size);
+ retval = xfr_rdwr_data(radio, XFR_WRITE, 1, SPUR_TABLE_ADDR, &size);
+ if (retval < 0) {
+ FMDERR("%s: Failed to configure SPUR table size\n", __func__);
+ return retval;
+ }
+
+ /* Program the spur table entries */
+ for (cnt = 0; cnt < (temp / 4); cnt++) {
+ offset = 0;
+ for (j = 0; j < 4; j++) {
+ xfr_buf[offset++] = GET_FREQ(COMPUTE_SPUR(
+ radio->spur_data.freq[index]), 1);
+ xfr_buf[offset++] = GET_FREQ(COMPUTE_SPUR(
+ radio->spur_data.freq[index]), 0);
+ xfr_buf[offset++] =
+ radio->spur_data.rmssi[index];
+ index++;
+ }
+ retval = xfr_rdwr_data(radio, XFR_WRITE, (SPUR_DATA_SIZE * 4),
+ addr, xfr_buf);
+ if (retval < 0) {
+ FMDERR("%s: Failed to program SPUR frequencies\n",
+ __func__);
+ return retval;
+ }
+ addr += (SPUR_DATA_SIZE * 4);
+ }
+
+ /* Program the additional SPUR Frequencies */
+ temp = radio->spur_table_size;
+ temp = (temp % 4);
+ if (temp > 0) {
+ offset = 0;
+ for (j = 0; j < temp; j++) {
+ xfr_buf[offset++] = GET_FREQ(COMPUTE_SPUR(
+ radio->spur_data.freq[index]), 1);
+ xfr_buf[offset++] = GET_FREQ(COMPUTE_SPUR(
+ radio->spur_data.freq[index]), 0);
+ xfr_buf[offset++] =
+ radio->spur_data.rmssi[index];
+ index++;
+ }
+ size = (temp * SPUR_DATA_SIZE);
+ retval = xfr_rdwr_data(radio, XFR_WRITE, size, addr, xfr_buf);
+ if (retval < 0) {
+ FMDERR("%s: Failed to program SPUR frequencies\n",
+ __func__);
+ return retval;
+ }
+ }
+
+ return retval;
+}
+
+static int xfr_rdwr_data(struct tavarua_device *radio, int op, int size,
+ unsigned long offset, unsigned char *buf) {
+
+ unsigned char xfr_buf[XFR_REG_NUM];
+ int retval = 0, temp = 0;
+
+ memset(xfr_buf, 0x0, XFR_REG_NUM);
+ temp = size;
+
+ xfr_buf[XFR_MODE_OFFSET] = (size << 1);
+ xfr_buf[XFR_ADDR_MSB_OFFSET] = GET_FREQ(offset, 1);
+ xfr_buf[XFR_ADDR_LSB_OFFSET] = GET_FREQ(offset, 0);
+
+ if (op == XFR_READ) {
+ xfr_buf[XFR_MODE_OFFSET] |= (XFR_PEEK_MODE);
+ size = 3;
+ } else if (op == XFR_WRITE) {
+ xfr_buf[XFR_MODE_OFFSET] |= (XFR_POKE_MODE);
+ memcpy(&xfr_buf[XFR_DATA_OFFSET], buf, size);
+ size += 3;
+ }
+
+ retval = tavarua_write_registers(radio, XFRCTRL, xfr_buf, size);
+ if (retval < 0) {
+ FMDERR("%s: Failed to performXFR operation\n", __func__);
+ return retval;
+ }
+
+ size = temp;
+
+ /*Wait for the XFR interrupt */
+ init_completion(&radio->sync_req_done);
+ if (!wait_for_completion_timeout(&radio->sync_req_done,
+ msecs_to_jiffies(WAIT_TIMEOUT))) {
+ FMDERR("Timeout: No XFR interrupt");
+ }
+
+ if (op == XFR_READ) {
+ retval = tavarua_read_registers(radio, XFRDAT0, size);
+ if (retval < 0) {
+ FMDERR("%s: Failed to read the XFR data\n", __func__);
+ return retval;
+ }
+ if (buf != NULL)
+ memcpy(buf, &radio->registers[XFRDAT0], size);
+ }
+
+ return retval;
+}
+
static int peek_MPX_DCC(struct tavarua_device *radio)
{
int retval = 0;
@@ -3001,6 +3166,7 @@
}
/* check if off */
else if ((ctrl->value == FM_OFF) && radio->registers[RDCTRL]) {
+ radio->spur_table_size = 0;
FMDBG("%s: turning off...\n", __func__);
tavarua_write_register(radio, RDCTRL, ctrl->value);
/* flush the event and work queues */
@@ -3299,8 +3465,20 @@
case V4L2_CID_PRIVATE_TAVARUA_DO_CALIBRATION:
case V4L2_CID_PRIVATE_SINR_THRESHOLD:
case V4L2_CID_PRIVATE_SINR_SAMPLES:
+ case V4L2_CID_PRIVATE_SPUR_SELECTION:
retval = 0;
break;
+ case V4L2_CID_PRIVATE_SPUR_FREQ:
+ radio->spur_data.freq[radio->spur_table_size] =
+ ctrl->value;
+ break;
+ case V4L2_CID_PRIVATE_SPUR_FREQ_RMSSI:
+ radio->spur_data.rmssi[radio->spur_table_size++] =
+ ctrl->value;
+ break;
+ case V4L2_CID_PRIVATE_UPDATE_SPUR_TABLE:
+ retval = update_spur_table(radio);
+ break;
default:
retval = -EINVAL;
}
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index 3416c91..d7dc67d 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -28,6 +28,7 @@
struct rc_dev *rcdev;
unsigned int gpio_nr;
bool active_low;
+ int can_sleep;
};
static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id)
@@ -37,7 +38,10 @@
int rc = 0;
enum raw_event_type type = IR_SPACE;
- gval = gpio_get_value_cansleep(gpio_dev->gpio_nr);
+ if (gpio_dev->can_sleep)
+ gval = gpio_get_value_cansleep(gpio_dev->gpio_nr);
+ else
+ gval = gpio_get_value(gpio_dev->gpio_nr);
if (gval < 0)
goto err_get_value;
@@ -96,6 +100,9 @@
rc = gpio_request(pdata->gpio_nr, "gpio-ir-recv");
if (rc < 0)
goto err_gpio_request;
+
+ gpio_dev->can_sleep = gpio_cansleep(pdata->gpio_nr);
+
rc = gpio_direction_input(pdata->gpio_nr);
if (rc < 0)
goto err_gpio_direction_input;
diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c
index 3c9431a..a3fe1c8 100644
--- a/drivers/media/rc/ir-nec-decoder.c
+++ b/drivers/media/rc/ir-nec-decoder.c
@@ -149,6 +149,10 @@
break;
data->state = STATE_TRAILER_SPACE;
+
+ if (data->is_nec_x)
+ goto rc_data;
+
return 0;
case STATE_TRAILER_SPACE:
@@ -157,7 +161,7 @@
if (!geq_margin(ev.duration, NEC_TRAILER_SPACE, NEC_UNIT / 2))
break;
-
+rc_data:
address = bitrev8((data->bits >> 24) & 0xff);
not_address = bitrev8((data->bits >> 16) & 0xff);
command = bitrev8((data->bits >> 8) & 0xff);
diff --git a/drivers/media/rc/keymaps/rc-samsung-necx.c b/drivers/media/rc/keymaps/rc-samsung-necx.c
index 6cf837f..1a3d6be 100644
--- a/drivers/media/rc/keymaps/rc-samsung-necx.c
+++ b/drivers/media/rc/keymaps/rc-samsung-necx.c
@@ -32,7 +32,7 @@
{ 0x70707, KEY_VOLUMEUP},
{ 0x7070b, KEY_VOLUMEDOWN},
{ 0x70760, KEY_UP},
- { 0x70768, KEY_ENTER}, /* ok */
+ { 0x70768, KEY_ENTER},
{ 0x70761, KEY_DOWN},
{ 0x70765, KEY_LEFT},
{ 0x70762, KEY_RIGHT},
@@ -44,15 +44,18 @@
{ 0x70748, KEY_FORWARD},
{ 0x7074a, KEY_PAUSE},
{ 0x70703, KEY_SLEEP},
- { 0x7076c, KEY_A}, /* search */
- { 0x70714, KEY_B}, /* camera */
- { 0x70715, KEY_C},
- { 0x70716, KEY_D},
+ { 0x7076c, KEY_RED},
+ { 0x70714, KEY_GREEN},
+ { 0x70715, KEY_YELLOW},
+ { 0x70716, KEY_BLUE},
{ 0x70758, KEY_BACK},
{ 0x7071a, KEY_MENU},
{ 0x7076b, KEY_LIST},
- { 0x70701, KEY_SCREENLOCK},
- { 0x7071f, KEY_HOME},
+ { 0x70701, KEY_TV2},
+ { 0x7071f, KEY_INFO},
+ { 0x7071b, KEY_TV},
+ { 0x7078b, KEY_AUX},
+ { 0x7078c, KEY_MEDIA},
};
diff --git a/drivers/media/rc/keymaps/rc-ue-rf4ce.c b/drivers/media/rc/keymaps/rc-ue-rf4ce.c
index 71c5505..ea982a8 100644
--- a/drivers/media/rc/keymaps/rc-ue-rf4ce.c
+++ b/drivers/media/rc/keymaps/rc-ue-rf4ce.c
@@ -25,7 +25,7 @@
{ 0x0d, KEY_EXIT },
{ 0x72, KEY_TV },
{ 0x73, KEY_VIDEO },
- { 0x74, KEY_COMPOSE },
+ { 0x74, KEY_PC },
{ 0x71, KEY_AUX },
{ 0x45, KEY_STOP },
{ 0x0b, KEY_LIST },
@@ -43,18 +43,20 @@
{ 0x30, KEY_CHANNELUP },
{ 0x31, KEY_CHANNELDOWN },
- { 0x20, KEY_NUMERIC_0 },
- { 0x21, KEY_NUMERIC_1 },
- { 0x22, KEY_NUMERIC_2 },
- { 0x23, KEY_NUMERIC_3 },
- { 0x24, KEY_NUMERIC_4 },
- { 0x25, KEY_NUMERIC_5 },
- { 0x26, KEY_NUMERIC_6 },
- { 0x27, KEY_NUMERIC_7 },
- { 0x28, KEY_NUMERIC_8 },
- { 0x29, KEY_NUMERIC_9 },
- { 0x34, KEY_INSERT },
+ { 0x20, KEY_0 },
+ { 0x21, KEY_1 },
+ { 0x22, KEY_2 },
+ { 0x23, KEY_3 },
+ { 0x24, KEY_4 },
+ { 0x25, KEY_5 },
+ { 0x26, KEY_6 },
+ { 0x27, KEY_7 },
+ { 0x28, KEY_8 },
+ { 0x29, KEY_9 },
+ { 0x34, KEY_TV2 },
{ 0x2b, KEY_ENTER },
+ { 0x35, KEY_INFO },
+ { 0x09, KEY_MENU },
};
static struct rc_map_list ue_rf4ce_map = {
diff --git a/drivers/media/video/msm/Kconfig b/drivers/media/video/msm/Kconfig
index 964218f..5ffc133 100644
--- a/drivers/media/video/msm/Kconfig
+++ b/drivers/media/video/msm/Kconfig
@@ -261,6 +261,16 @@
---help---
Enable support for Video Pre-processing Engine
+config MSM_CAM_IRQ_ROUTER
+ bool "Enable MSM CAM IRQ Router"
+ depends on MSM_CAMERA
+ ---help---
+ Enable IRQ Router for Camera. Depending on the
+ configuration, this module can handle the
+ interrupts from multiple camera hardware
+ cores and composite them into a single
+ interrupt to the MSM.
+
config QUP_EXCLUSIVE_TO_CAMERA
bool "QUP exclusive to camera"
depends on MSM_CAMERA
diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile
index 9ed6cca..431da2e 100644
--- a/drivers/media/video/msm/Makefile
+++ b/drivers/media/video/msm/Makefile
@@ -14,6 +14,7 @@
obj-$(CONFIG_MSM_CAMERA) += msm_isp.o msm.o msm_mem.o msm_mctl.o msm_mctl_buf.o msm_mctl_pp.o
obj-$(CONFIG_MSM_CAMERA) += server/ eeprom/ sensors/ actuators/ csi/
obj-$(CONFIG_MSM_CAMERA) += msm_gesture.o
+ obj-$(CONFIG_MSM_CAM_IRQ_ROUTER) += msm_camirq_router.o
else
obj-$(CONFIG_MSM_CAMERA) += msm_camera.o
endif
diff --git a/drivers/media/video/msm/csi/Makefile b/drivers/media/video/msm/csi/Makefile
index e5d5966..f7cb408 100644
--- a/drivers/media/video/msm/csi/Makefile
+++ b/drivers/media/video/msm/csi/Makefile
@@ -1,6 +1,7 @@
GCC_VERSION := $(shell $(CONFIG_SHELL) $(PWD)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
EXTRA_CFLAGS += -Idrivers/media/video/msm
-obj-$(CONFIG_ARCH_MSM8960) += msm_csiphy.o msm_csid.o msm_ispif.o
-obj-$(CONFIG_ARCH_MSM7X27A) += msm_csic.o
-obj-$(CONFIG_ARCH_MSM8X60) += msm_csic.o
-obj-$(CONFIG_ARCH_MSM7X30) += msm_csic.o
+obj-$(CONFIG_ARCH_MSM8960) += msm_csi2_register.o msm_csiphy.o msm_csid.o msm_ispif.o
+obj-$(CONFIG_ARCH_MSM7X27A) += msm_csic_register.o msm_csic.o
+obj-$(CONFIG_ARCH_MSM8X60) += msm_csic_register.o msm_csic.o
+obj-$(CONFIG_ARCH_MSM7X30) += msm_csic_register.o msm_csic.o
+
diff --git a/drivers/media/video/msm/csi/msm_csi2_register.c b/drivers/media/video/msm/csi/msm_csi2_register.c
new file mode 100644
index 0000000..7b85ded
--- /dev/null
+++ b/drivers/media/video/msm/csi/msm_csi2_register.c
@@ -0,0 +1,67 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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/device.h>
+#include "msm.h"
+#include "msm_csi_register.h"
+
+int msm_csi_register_subdevs(struct msm_cam_media_controller *p_mctl,
+ int core_index,
+ int (*msm_mctl_subdev_match_core)(struct device *, void *))
+{
+ int rc = -ENODEV;
+ struct device_driver *driver;
+ struct device *dev;
+
+ /* register csiphy subdev */
+ driver = driver_find(MSM_CSIPHY_DRV_NAME, &platform_bus_type);
+ if (!driver)
+ goto out;
+
+ dev = driver_find_device(driver, NULL, (void *)core_index,
+ msm_mctl_subdev_match_core);
+ if (!dev)
+ goto out;
+
+ p_mctl->csiphy_sdev = dev_get_drvdata(dev);
+
+ /* register csid subdev */
+ driver = driver_find(MSM_CSID_DRV_NAME, &platform_bus_type);
+ if (!driver)
+ goto out;
+
+ dev = driver_find_device(driver, NULL, (void *)core_index,
+ msm_mctl_subdev_match_core);
+ if (!dev)
+ goto out;
+
+ p_mctl->csid_sdev = dev_get_drvdata(dev);
+
+ /* register ispif subdev */
+ driver = driver_find(MSM_ISPIF_DRV_NAME, &platform_bus_type);
+ if (!driver)
+ goto out;
+
+ dev = driver_find_device(driver, NULL, 0,
+ msm_mctl_subdev_match_core);
+ if (!dev)
+ goto out;
+
+ p_mctl->ispif_sdev = dev_get_drvdata(dev);
+ rc = 0;
+ return rc;
+out:
+ p_mctl->ispif_sdev = NULL;
+ return rc;
+}
+
diff --git a/drivers/media/video/msm/csi/msm_csi_register.h b/drivers/media/video/msm/csi/msm_csi_register.h
new file mode 100644
index 0000000..578b609
--- /dev/null
+++ b/drivers/media/video/msm/csi/msm_csi_register.h
@@ -0,0 +1,16 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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.
+ *
+ */
+
+int msm_csi_register_subdevs(struct msm_cam_media_controller *p_mctl,
+ int core_index,
+ int (*msm_mctl_subdev_match_core)(struct device *, void *));
diff --git a/drivers/media/video/msm/csi/msm_csic.c b/drivers/media/video/msm/csi/msm_csic.c
index a217f9d..dbb4f32 100644
--- a/drivers/media/video/msm/csi/msm_csic.c
+++ b/drivers/media/video/msm/csi/msm_csic.c
@@ -388,6 +388,8 @@
{
struct csic_device *new_csic_dev;
int rc = 0;
+ struct msm_cam_subdev_info sd_info;
+
CDBG("%s: device id = %d\n", __func__, pdev->id);
new_csic_dev = kzalloc(sizeof(struct csic_device), GFP_KERNEL);
if (!new_csic_dev) {
@@ -455,8 +457,11 @@
iounmap(new_csic_dev->base);
new_csic_dev->base = NULL;
+ sd_info.sdev_type = CSIC_DEV;
+ sd_info.sd_index = pdev->id;
+ sd_info.irq_num = new_csic_dev->irq->start;
msm_cam_register_subdev_node(
- &new_csic_dev->subdev, CSIC_DEV, pdev->id);
+ &new_csic_dev->subdev, &sd_info);
return 0;
diff --git a/drivers/media/video/msm/csi/msm_csic_register.c b/drivers/media/video/msm/csi/msm_csic_register.c
new file mode 100644
index 0000000..7ccaff2
--- /dev/null
+++ b/drivers/media/video/msm/csi/msm_csic_register.c
@@ -0,0 +1,45 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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/device.h>
+#include "msm.h"
+#include "msm_csi_register.h"
+
+int msm_csi_register_subdevs(struct msm_cam_media_controller *p_mctl,
+ int core_index,
+ int (*msm_mctl_subdev_match_core)(struct device *, void *))
+{
+ int rc = -ENODEV;
+ struct device_driver *driver;
+ struct device *dev;
+
+ driver = driver_find(MSM_CSIC_DRV_NAME, &platform_bus_type);
+ if (!driver)
+ goto out;
+
+ dev = driver_find_device(driver, NULL, (void *)core_index,
+ msm_mctl_subdev_match_core);
+ if (!dev)
+ goto out;
+
+ p_mctl->csic_sdev = dev_get_drvdata(dev);
+
+ rc = 0;
+ p_mctl->ispif_sdev = NULL;
+ return rc;
+
+out:
+ p_mctl->ispif_sdev = NULL;
+ return rc;
+}
+
diff --git a/drivers/media/video/msm/csi/msm_csid.c b/drivers/media/video/msm/csi/msm_csid.c
index bb2a1d48..1ab4e66 100644
--- a/drivers/media/video/msm/csi/msm_csid.c
+++ b/drivers/media/video/msm/csi/msm_csid.c
@@ -298,6 +298,8 @@
static int __devinit csid_probe(struct platform_device *pdev)
{
struct csid_device *new_csid_dev;
+ struct msm_cam_subdev_info sd_info;
+
int rc = 0;
CDBG("%s: device id = %d\n", __func__, pdev->id);
new_csid_dev = kzalloc(sizeof(struct csid_device), GFP_KERNEL);
@@ -338,7 +340,10 @@
}
new_csid_dev->pdev = pdev;
- msm_cam_register_subdev_node(&new_csid_dev->subdev, CSID_DEV, pdev->id);
+ sd_info.sdev_type = CSID_DEV;
+ sd_info.sd_index = pdev->id;
+ sd_info.irq_num = new_csid_dev->irq->start;
+ msm_cam_register_subdev_node(&new_csid_dev->subdev, &sd_info);
return 0;
csid_no_resource:
diff --git a/drivers/media/video/msm/csi/msm_csiphy.c b/drivers/media/video/msm/csi/msm_csiphy.c
index e25660b..4693a8a 100644
--- a/drivers/media/video/msm/csi/msm_csiphy.c
+++ b/drivers/media/video/msm/csi/msm_csiphy.c
@@ -282,6 +282,8 @@
{
struct csiphy_device *new_csiphy_dev;
int rc = 0;
+ struct msm_cam_subdev_info sd_info;
+
CDBG("%s: device id = %d\n", __func__, pdev->id);
new_csiphy_dev = kzalloc(sizeof(struct csiphy_device), GFP_KERNEL);
if (!new_csiphy_dev) {
@@ -333,8 +335,11 @@
disable_irq(new_csiphy_dev->irq->start);
new_csiphy_dev->pdev = pdev;
+ sd_info.sdev_type = CSIPHY_DEV;
+ sd_info.sd_index = pdev->id;
+ sd_info.irq_num = new_csiphy_dev->irq->start;
msm_cam_register_subdev_node(
- &new_csiphy_dev->subdev, CSIPHY_DEV, pdev->id);
+ &new_csiphy_dev->subdev, &sd_info);
return 0;
csiphy_no_resource:
diff --git a/drivers/media/video/msm/csi/msm_ispif.c b/drivers/media/video/msm/csi/msm_ispif.c
index 23982dd..0f16bbf 100644
--- a/drivers/media/video/msm/csi/msm_ispif.c
+++ b/drivers/media/video/msm/csi/msm_ispif.c
@@ -571,16 +571,51 @@
}
}
+static long msm_ispif_cmd(struct v4l2_subdev *sd, void *arg)
+{
+ long rc = 0;
+ struct ispif_cfg_data cdata;
+
+ if (copy_from_user(&cdata, (void *)arg, sizeof(struct ispif_cfg_data)))
+ return -EFAULT;
+ CDBG("%s cfgtype = %d\n", __func__, cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case ISPIF_INIT:
+ CDBG("%s csid_version = %x\n", __func__,
+ cdata.cfg.csid_version);
+ rc = msm_ispif_init(&cdata.cfg.csid_version);
+ break;
+ case ISPIF_SET_CFG:
+ CDBG("%s len = %d, intftype = %d,.cid_mask = %d, csid = %d\n",
+ __func__,
+ cdata.cfg.ispif_params.len,
+ cdata.cfg.ispif_params.params[0].intftype,
+ cdata.cfg.ispif_params.params[0].cid_mask,
+ cdata.cfg.ispif_params.params[0].csid);
+ rc = msm_ispif_config(&cdata.cfg.ispif_params);
+ break;
+
+ case ISPIF_SET_ON_FRAME_BOUNDARY:
+ case ISPIF_SET_OFF_FRAME_BOUNDARY:
+ case ISPIF_SET_OFF_IMMEDIATELY:
+ rc = msm_ispif_subdev_video_s_stream(sd, cdata.cfg.cmd);
+ break;
+ case ISPIF_RELEASE:
+ msm_ispif_release(sd);
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
static long msm_ispif_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd,
void *arg)
{
switch (cmd) {
case VIDIOC_MSM_ISPIF_CFG:
- return msm_ispif_config((struct msm_ispif_params_list *)arg);
- case VIDIOC_MSM_ISPIF_INIT:
- return msm_ispif_init((uint32_t *)arg);
- case VIDIOC_MSM_ISPIF_RELEASE:
- msm_ispif_release(sd);
+ return msm_ispif_cmd(sd, arg);
default:
return -ENOIOCTLCMD;
}
@@ -605,6 +640,8 @@
static int __devinit ispif_probe(struct platform_device *pdev)
{
int rc = 0;
+ struct msm_cam_subdev_info sd_info;
+
CDBG("%s\n", __func__);
ispif = kzalloc(sizeof(struct ispif_device), GFP_KERNEL);
if (!ispif) {
@@ -652,7 +689,10 @@
}
ispif->pdev = pdev;
- msm_cam_register_subdev_node(&ispif->subdev, ISPIF_DEV, pdev->id);
+ sd_info.sdev_type = ISPIF_DEV;
+ sd_info.sd_index = pdev->id;
+ sd_info.irq_num = ispif->irq->start;
+ msm_cam_register_subdev_node(&ispif->subdev, &sd_info);
return 0;
ispif_no_mem:
diff --git a/drivers/media/video/msm/csi/msm_ispif.h b/drivers/media/video/msm/csi/msm_ispif.h
index 8f1dd12..7b301ba 100644
--- a/drivers/media/video/msm/csi/msm_ispif.h
+++ b/drivers/media/video/msm/csi/msm_ispif.h
@@ -43,25 +43,7 @@
};
#define VIDIOC_MSM_ISPIF_CFG \
- _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct msm_ispif_params)
-
-#define VIDIOC_MSM_ISPIF_INIT \
- _IO('V', BASE_VIDIOC_PRIVATE + 2)
-
-#define VIDIOC_MSM_ISPIF_RELEASE \
- _IOWR('V', BASE_VIDIOC_PRIVATE + 3, struct v4l2_subdev*)
-
-#define ISPIF_STREAM(intf, action) (((intf)<<ISPIF_S_STREAM_SHIFT)+(action))
-#define ISPIF_ON_FRAME_BOUNDARY (0x01 << 0)
-#define ISPIF_OFF_FRAME_BOUNDARY (0x01 << 1)
-#define ISPIF_OFF_IMMEDIATELY (0x01 << 2)
-#define ISPIF_S_STREAM_SHIFT 4
-
-
-#define PIX_0 (0x01 << 0)
-#define RDI_0 (0x01 << 1)
-#define PIX_1 (0x01 << 2)
-#define RDI_1 (0x01 << 3)
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 18, struct ispif_cfg_data*)
void msm_ispif_vfe_get_cid(uint8_t intftype, char *cids, int *num);
diff --git a/drivers/media/video/msm/io/msm_io_vfe31.c b/drivers/media/video/msm/io/msm_io_vfe31.c
index 0c0c450..8c733a0 100644
--- a/drivers/media/video/msm/io/msm_io_vfe31.c
+++ b/drivers/media/video/msm/io/msm_io_vfe31.c
@@ -247,7 +247,7 @@
}
if (!IS_ERR(clk))
- clk_enable(clk);
+ clk_prepare_enable(clk);
else
rc = -1;
return rc;
@@ -303,10 +303,11 @@
}
if (!IS_ERR(clk)) {
- clk_disable(clk);
+ clk_disable_unprepare(clk);
clk_put(clk);
- } else
+ } else {
rc = -1;
+ }
return rc;
}
diff --git a/drivers/media/video/msm/msm.c b/drivers/media/video/msm/msm.c
index c3c09d4..d34b8e1 100644
--- a/drivers/media/video/msm/msm.c
+++ b/drivers/media/video/msm/msm.c
@@ -1277,6 +1277,7 @@
struct msm_camera_sensor_info *sdata;
struct msm_cam_v4l2_device *pcam;
struct msm_sensor_ctrl_t *s_ctrl;
+ struct msm_cam_subdev_info sd_info;
D("%s for %s\n", __func__, sensor_sd->name);
@@ -1321,8 +1322,11 @@
}
msm_server_update_sensor_info(pcam, sdata);
+ sd_info.sdev_type = SENSOR_DEV;
+ sd_info.sd_index = vnode_count;
+ sd_info.irq_num = 0;
/* register the subdevice, must be done for callbacks */
- rc = msm_cam_register_subdev_node(sensor_sd, SENSOR_DEV, vnode_count);
+ rc = msm_cam_register_subdev_node(sensor_sd, &sd_info);
if (rc < 0) {
D("%s sensor sub device register failed\n",
__func__);
diff --git a/drivers/media/video/msm/msm.h b/drivers/media/video/msm/msm.h
index 42330b4..d0322d1 100644
--- a/drivers/media/video/msm/msm.h
+++ b/drivers/media/video/msm/msm.h
@@ -58,14 +58,16 @@
#define MSM_GEMINI_DRV_NAME "msm_gemini"
#define MSM_MERCURY_DRV_NAME "msm_mercury"
#define MSM_I2C_MUX_DRV_NAME "msm_cam_i2c_mux"
+#define MSM_IRQ_ROUTER_DRV_NAME "msm_cam_irq_router"
#define MAX_NUM_CSIPHY_DEV 3
-#define MAX_NUM_CSID_DEV 3
+#define MAX_NUM_CSID_DEV 4
#define MAX_NUM_CSIC_DEV 3
#define MAX_NUM_ISPIF_DEV 1
#define MAX_NUM_VFE_DEV 2
#define MAX_NUM_AXI_DEV 2
#define MAX_NUM_VPE_DEV 1
+#define MAX_NUM_JPEG_DEV 3
enum msm_cam_subdev_type {
CSIPHY_DEV,
@@ -79,6 +81,7 @@
ACTUATOR_DEV,
EEPROM_DEV,
GESTURE_DEV,
+ IRQ_ROUTER_DEV,
};
/* msm queue management APIs*/
@@ -129,6 +132,7 @@
struct msm_free_buf {
uint8_t num_planes;
+ int32_t image_mode;
uint32_t ch_paddr[VIDEO_MAX_PLANES];
uint32_t vb;
};
@@ -146,13 +150,11 @@
/* message id for v4l2_subdev_notify*/
enum msm_camera_v4l2_subdev_notify {
- NOTIFY_CID_CHANGE, /* arg = msm_camera_csid_params */
NOTIFY_ISP_MSG_EVT, /* arg = enum ISP_MESSAGE_ID */
NOTIFY_VFE_MSG_OUT, /* arg = struct isp_msg_output */
NOTIFY_VFE_MSG_STATS, /* arg = struct isp_msg_stats */
NOTIFY_VFE_MSG_COMP_STATS, /* arg = struct msm_stats_buf */
NOTIFY_VFE_BUF_EVT, /* arg = struct msm_vfe_resp */
- NOTIFY_ISPIF_STREAM, /* arg = enable parameter for s_stream */
NOTIFY_PCLK_CHANGE, /* arg = pclk */
NOTIFY_CSIPHY_CFG, /* arg = msm_camera_csiphy_params */
NOTIFY_CSID_CFG, /* arg = msm_camera_csid_params */
@@ -406,6 +408,15 @@
struct msm_mem_map_info mem_map;
};
+struct msm_cam_subdev_info {
+ uint8_t sdev_type;
+ /* Subdev index. For eg: CSIPHY0, CSIPHY1 etc */
+ uint8_t sd_index;
+ /* This device/subdev's interrupt number, assigned
+ * from the hardware document. */
+ uint8_t irq_num;
+};
+
/* 2 for camera, 1 for gesture */
#define MAX_NUM_ACTIVE_CAMERA 3
@@ -422,6 +433,68 @@
uint32_t handle;
};
+struct msm_cam_server_irqmap_entry {
+ int irq_num;
+ int irq_idx;
+ uint8_t cam_hw_idx;
+ uint8_t is_composite;
+};
+
+struct intr_table_entry {
+ /* irq_num as understood by msm.
+ * Unique for every camera hw core & target. Use a mapping function
+ * to map this irq number to its equivalent index in camera side. */
+ int irq_num;
+ /* Camera hw core idx, in case of non-composite IRQs*/
+ uint8_t cam_hw_idx;
+ /* Camera hw core mask, in case of composite IRQs. */
+ uint32_t cam_hw_mask;
+ /* Each interrupt is mapped to an index, which is used
+ * to add/delete entries into the lookup table. Both the information
+ * are needed in the lookup table to avoid another subdev call into
+ * the IRQ Router subdev to get the irq_idx in the interrupt context */
+ int irq_idx;
+ /* Is this irq composite? */
+ uint8_t is_composite;
+ /* IRQ Trigger type: TRIGGER_RAISING, TRIGGER_HIGH, etc. */
+ uint32_t irq_trigger_type;
+ /* If IRQ Router hw is present,
+ * this field holds the number of camera hw core
+ * which are bundled together in the above
+ * interrupt. > 1 in case of composite irqs.
+ * If IRQ Router hw is not present, this field should be set to 1. */
+ int num_hwcore;
+ /* Pointers to the subdevs composited in this
+ * irq. If not composite, the 0th index stores the subdev to which
+ * this irq needs to be dispatched to. */
+ struct v4l2_subdev *subdev_list[CAMERA_SS_IRQ_MAX];
+ /* Device requesting the irq. */
+ const char *dev_name;
+ /* subdev private data, if any */
+ void *data;
+};
+
+struct irqmgr_intr_lkup_table {
+ /* Individual(hw) interrupt lookup table:
+ * This table is populated during initialization and doesnt
+ * change, unless the IRQ Router has been configured
+ * for composite IRQs. If the IRQ Router has been configured
+ * for composite IRQs, the is_composite field of that IRQ will
+ * be set to 1(default 0). And when there is an interrupt on
+ * that line, the composite interrupt lookup table is used
+ * for handling the interrupt. */
+ struct intr_table_entry ind_intr_tbl[CAMERA_SS_IRQ_MAX];
+
+ /* Composite interrupt lookup table:
+ * This table can be dynamically modified based on the usecase.
+ * If the usecase requires two or more HW core IRQs to be bundled
+ * into a single composite IRQ, then this table is populated
+ * accordingly. Also when this is done, the composite field
+ * in the intr_lookup_table has to be updated to reflect that
+ * the irq 'irq_num' will now be triggered in composite mode. */
+ struct intr_table_entry comp_intr_tbl[CAMERA_SS_IRQ_MAX];
+};
+
/* abstract camera server device for all sensor successfully probed*/
struct msm_cam_server_dev {
@@ -467,9 +540,18 @@
struct v4l2_subdev *axi_device[MAX_NUM_AXI_DEV];
struct v4l2_subdev *vpe_device[MAX_NUM_VPE_DEV];
struct v4l2_subdev *gesture_device;
-};
+ struct v4l2_subdev *irqr_device;
-/* camera server related functions */
+ spinlock_t intr_table_lock;
+ struct irqmgr_intr_lkup_table irq_lkup_table;
+ /* Stores the pointer to the subdev when the individual
+ * subdevices register themselves with the server. This
+ * will be used while dispatching composite irqs. The
+ * cam_hw_idx will serve as the index into this array to
+ * dispatch the irq to the corresponding subdev. */
+ struct v4l2_subdev *subdev_table[MSM_CAM_HW_MAX];
+ struct msm_cam_server_irqmap_entry hw_irqmap[CAMERA_SS_IRQ_MAX];
+};
/* ISP related functions */
void msm_isp_vfe_dev_init(struct v4l2_subdev *vd);
@@ -584,7 +666,7 @@
void __user *arg);
void msm_release_ion_client(struct kref *ref);
int msm_cam_register_subdev_node(struct v4l2_subdev *sd,
- enum msm_cam_subdev_type sdev_type, uint8_t index);
+ struct msm_cam_subdev_info *sd_info);
int msm_server_open_client(int *p_qidx);
int msm_server_send_ctrl(struct msm_ctrl_cmd *out, int ctrl_id);
int msm_server_close_client(int idx);
diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c
index 624fe9b..9f32bfee 100644
--- a/drivers/media/video/msm/msm_camera.c
+++ b/drivers/media/video/msm/msm_camera.c
@@ -36,6 +36,7 @@
#include <linux/hrtimer.h>
#include <linux/ion.h>
+#include <mach/cpuidle.h>
DEFINE_MUTEX(ctrl_cmd_lock);
#define CAMERA_STOP_VIDEO 58
@@ -3085,7 +3086,7 @@
msm_queue_drain(&sync->pict_q, list_pict);
msm_queue_drain(&sync->event_q, list_config);
- wake_unlock(&sync->wake_lock);
+ pm_qos_update_request(&sync->idle_pm_qos, PM_QOS_DEFAULT_VALUE);
sync->apps_id = NULL;
sync->core_powered_on = 0;
}
@@ -3745,7 +3746,8 @@
sync->apps_id = apps_id;
if (!sync->core_powered_on && !is_controlnode) {
- wake_lock(&sync->wake_lock);
+ pm_qos_update_request(&sync->idle_pm_qos,
+ msm_cpuidle_get_deep_idle_latency());
msm_camvfe_fn_init(&sync->vfefn, sync);
if (sync->vfefn.vfe_init) {
@@ -3959,11 +3961,12 @@
msm_queue_init(&sync->pict_q, "pict");
msm_queue_init(&sync->vpe_q, "vpe");
- wake_lock_init(&sync->wake_lock, WAKE_LOCK_SUSPEND, "msm_camera");
+ pm_qos_add_request(&sync->idle_pm_qos, PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
rc = msm_camio_probe_on(pdev);
if (rc < 0) {
- wake_lock_destroy(&sync->wake_lock);
+ pm_qos_remove_request(&sync->idle_pm_qos);
return rc;
}
rc = sensor_probe(sync->sdata, &sctrl);
@@ -3976,7 +3979,7 @@
pr_err("%s: failed to initialize %s\n",
__func__,
sync->sdata->sensor_name);
- wake_lock_destroy(&sync->wake_lock);
+ pm_qos_remove_request(&sync->idle_pm_qos);
return rc;
}
@@ -3995,7 +3998,7 @@
static int msm_sync_destroy(struct msm_sync *sync)
{
- wake_lock_destroy(&sync->wake_lock);
+ pm_qos_remove_request(&sync->idle_pm_qos);
return 0;
}
diff --git a/drivers/media/video/msm/msm_camirq_router.c b/drivers/media/video/msm/msm_camirq_router.c
new file mode 100644
index 0000000..52dd175
--- /dev/null
+++ b/drivers/media/video/msm/msm_camirq_router.c
@@ -0,0 +1,266 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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/interrupt.h>
+#include <mach/irqs.h>
+#include <media/msm_isp.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include "msm.h"
+#include "server/msm_cam_server.h"
+#include "msm_camirq_router.h"
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define D(fmt, args...) pr_debug("msm: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+static void msm_irqrouter_update_irqmap_entry(
+ struct msm_cam_server_irqmap_entry *entry,
+ int is_composite, int irq_idx, int cam_hw_idx)
+{
+ int rc = 0;
+ entry->irq_idx = irq_idx;
+ entry->is_composite = is_composite;
+ entry->cam_hw_idx = cam_hw_idx;
+ rc = msm_cam_server_update_irqmap(entry);
+ if (rc < 0)
+ pr_err("%s Error updating irq %d information ",
+ __func__, irq_idx);
+}
+
+static void msm_irqrouter_send_default_irqmap(
+ struct irqrouter_ctrl_type *irqrouter_ctrl)
+{
+ struct msm_cam_server_irqmap_entry *irqmap =
+ &irqrouter_ctrl->def_hw_irqmap[0];
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_0],
+ 0, CAMERA_SS_IRQ_0, MSM_CAM_HW_MICRO);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_1],
+ 0, CAMERA_SS_IRQ_1, MSM_CAM_HW_CCI);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_2],
+ 0, CAMERA_SS_IRQ_2, MSM_CAM_HW_CSI0);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_3],
+ 0, CAMERA_SS_IRQ_3, MSM_CAM_HW_CSI1);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_4],
+ 0, CAMERA_SS_IRQ_4, MSM_CAM_HW_CSI2);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_5],
+ 0, CAMERA_SS_IRQ_5, MSM_CAM_HW_CSI3);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_6],
+ 0, CAMERA_SS_IRQ_6, MSM_CAM_HW_ISPIF);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_7],
+ 0, CAMERA_SS_IRQ_7, MSM_CAM_HW_CPP);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_8],
+ 0, CAMERA_SS_IRQ_8, MSM_CAM_HW_VFE0);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_9],
+ 0, CAMERA_SS_IRQ_9, MSM_CAM_HW_VFE1);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_10],
+ 0, CAMERA_SS_IRQ_10, MSM_CAM_HW_JPEG0);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_11],
+ 0, CAMERA_SS_IRQ_11, MSM_CAM_HW_JPEG1);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_12],
+ 0, CAMERA_SS_IRQ_12, MSM_CAM_HW_JPEG2);
+}
+
+static int msm_irqrouter_open(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct irqrouter_ctrl_type *irqrouter_ctrl = v4l2_get_subdevdata(sd);
+ /* Only one object of IRQ Router allowed. */
+ if (atomic_read(&irqrouter_ctrl->active) != 0) {
+ pr_err("%s IRQ router is already opened\n", __func__);
+ return -EINVAL;
+ }
+
+ D("%s E ", __func__);
+ atomic_inc(&irqrouter_ctrl->active);
+
+ return 0;
+}
+
+static int msm_irqrouter_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct irqrouter_ctrl_type *irqrouter_ctrl = v4l2_get_subdevdata(sd);
+ if (atomic_read(&irqrouter_ctrl->active) == 0) {
+ pr_err("%s IRQ router is already closed\n", __func__);
+ return -EINVAL;
+ }
+ D("%s E ", __func__);
+ atomic_dec(&irqrouter_ctrl->active);
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops msm_irqrouter_internal_ops = {
+ .open = msm_irqrouter_open,
+ .close = msm_irqrouter_close,
+};
+
+long msm_irqrouter_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct irqrouter_ctrl_type *irqrouter_ctrl = v4l2_get_subdevdata(sd);
+ struct msm_camera_irq_cfg *irq_cfg;
+ struct intr_table_entry irq_req;
+ int rc = 0;
+
+ /* Handle all IRQ Router Subdev IOCTLs here.
+ * Userspace sends the composite irq configuration.
+ * IRQ Router subdev then configures the registers to group
+ * together individual core hw irqs into a composite IRQ
+ * to the MSM IRQ controller. It also registers them with
+ * the irq manager in the camera server. */
+ switch (cmd) {
+ case MSM_IRQROUTER_CFG_COMPIRQ:
+ COPY_FROM_USER(rc, &irq_cfg, (void __user *)arg,
+ sizeof(struct msm_camera_irq_cfg));
+ if (rc) {
+ ERR_COPY_FROM_USER();
+ break;
+ }
+
+ if (!irq_cfg ||
+ (irq_cfg->irq_idx < CAMERA_SS_IRQ_0) ||
+ (irq_cfg->irq_idx >= CAMERA_SS_IRQ_MAX)) {
+ pr_err("%s Invalid input", __func__);
+ return -EINVAL;
+ } else {
+ irq_req.cam_hw_mask = irq_cfg->cam_hw_mask;
+ irq_req.irq_idx = irq_cfg->irq_idx;
+ irq_req.irq_num =
+ irqrouter_ctrl->def_hw_irqmap[irq_cfg->irq_idx].irq_num;
+ irq_req.is_composite = 1;
+ irq_req.irq_trigger_type = IRQF_TRIGGER_RISING;
+ irq_req.num_hwcore = irq_cfg->num_hwcore;
+ irq_req.data = NULL;
+ rc = msm_cam_server_request_irq(&irq_req);
+ if (rc < 0) {
+ pr_err("%s Error requesting comp irq %d ",
+ __func__, irq_req.irq_idx);
+ return rc;
+ }
+ irqrouter_ctrl->def_hw_irqmap
+ [irq_cfg->irq_idx].is_composite = 1;
+ }
+ break;
+ default:
+ pr_err("%s Invalid cmd %d ", __func__, cmd);
+ break;
+ }
+
+ return rc;
+}
+
+static const struct v4l2_subdev_core_ops msm_irqrouter_subdev_core_ops = {
+ .ioctl = msm_irqrouter_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_irqrouter_subdev_ops = {
+ .core = &msm_irqrouter_subdev_core_ops,
+};
+
+static int __devinit irqrouter_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct irqrouter_ctrl_type *irqrouter_ctrl;
+ struct msm_cam_subdev_info sd_info;
+
+ D("%s: device id = %d\n", __func__, pdev->id);
+
+ irqrouter_ctrl = kzalloc(sizeof(struct irqrouter_ctrl_type),
+ GFP_KERNEL);
+ if (!irqrouter_ctrl) {
+ pr_err("%s: not enough memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ v4l2_subdev_init(&irqrouter_ctrl->subdev, &msm_irqrouter_subdev_ops);
+ irqrouter_ctrl->subdev.internal_ops = &msm_irqrouter_internal_ops;
+ irqrouter_ctrl->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(irqrouter_ctrl->subdev.name,
+ sizeof(irqrouter_ctrl->subdev.name), "msm_irqrouter");
+ v4l2_set_subdevdata(&irqrouter_ctrl->subdev, irqrouter_ctrl);
+ irqrouter_ctrl->pdev = pdev;
+
+ msm_irqrouter_send_default_irqmap(irqrouter_ctrl);
+
+ media_entity_init(&irqrouter_ctrl->subdev.entity, 0, NULL, 0);
+ irqrouter_ctrl->subdev.entity.type = MEDIA_ENT_T_DEVNODE_V4L;
+ irqrouter_ctrl->subdev.entity.group_id = IRQ_ROUTER_DEV;
+ irqrouter_ctrl->subdev.entity.name = pdev->name;
+
+ sd_info.sdev_type = IRQ_ROUTER_DEV;
+ sd_info.sd_index = 0;
+ sd_info.irq_num = 0;
+ /* Now register this subdev with the camera server. */
+ rc = msm_cam_register_subdev_node(&irqrouter_ctrl->subdev, &sd_info);
+ if (rc < 0) {
+ pr_err("%s Error registering irqr subdev %d", __func__, rc);
+ goto error;
+ }
+ irqrouter_ctrl->subdev.entity.revision =
+ irqrouter_ctrl->subdev.devnode->num;
+ atomic_set(&irqrouter_ctrl->active, 0);
+
+ platform_set_drvdata(pdev, &irqrouter_ctrl->subdev);
+
+ return rc;
+error:
+ kfree(irqrouter_ctrl);
+ return rc;
+}
+
+static int __exit irqrouter_exit(struct platform_device *pdev)
+{
+ kfree(irqrouter_ctrl);
+ return 0;
+}
+
+static struct platform_driver msm_irqrouter_driver = {
+ .probe = irqrouter_probe,
+ .remove = irqrouter_exit,
+ .driver = {
+ .name = MSM_IRQ_ROUTER_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_irqrouter_init_module(void)
+{
+ return platform_driver_register(&msm_irqrouter_driver);
+}
+
+static void __exit msm_irqrouter_exit_module(void)
+{
+ platform_driver_unregister(&msm_irqrouter_driver);
+}
+
+module_init(msm_irqrouter_init_module);
+module_exit(msm_irqrouter_exit_module);
+MODULE_DESCRIPTION("msm camera irq router");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/msm_camirq_router.h b/drivers/media/video/msm/msm_camirq_router.h
new file mode 100644
index 0000000..2c9cb73
--- /dev/null
+++ b/drivers/media/video/msm/msm_camirq_router.h
@@ -0,0 +1,206 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 __MSM_CAM_IRQROUTER_H__
+#define __MSM_CAM_IRQROUTER_H__
+
+#include <linux/bitops.h>
+
+/* Camera SS common registers defines - Start */
+/* These registers are not directly related to
+ * IRQ Router, but are common to the Camera SS.
+ * IRQ Router registers dont have a unique base address
+ * in the memory mapped address space. It is offset from
+ * the Camera SS base address. So keep the common Camera
+ * SS registers also in the IRQ Router subdev for now. */
+
+/* READ ONLY: Camera Subsystem HW version */
+#define CAMSS_HW_VERSION 0x00000000
+
+/* Bits 4:0 of this register can be used to select a desired
+ * camera core test bus to drive the Camera_SS test bus output */
+#define CAMSS_TESTBUS_SEL 0x00000004
+
+/* Bits 4:0 of this register is used to allow either Microcontroller
+ * or the CCI drive the corresponding GPIO output.
+ * For eg: Setting bit 0 of this register allows Microcontroller to
+ * drive GPIO #0. Clearing the bit allows CCI to drive GPIO #0. */
+#define CAMSS_GPIO_MUX_SEL 0x00000008
+
+/* Bit 0 of this register is used to set the default AHB master
+ * for the AHB Arbiter. 0 - AHB Master 0, 1 - AHB Master 1*/
+#define CAMSS_AHB_ARB_CTL 0x0000000C
+
+/* READ ONLY */
+#define CAMSS_XPU2_STATUS 0x00000010
+
+/* Select the appropriate CSI clock for CSID Pixel pipes */
+#define CAMSS_CSI_PIX_CLK_MUX_SEL 0x00000020
+#define CAMSS_CSI_PIX_CLK_CGC_EN 0x00000024
+
+/* Select the appropriate CSI clock for CSID RDI pipes */
+#define CAMSS_CSI_RDI_CLK_MUX_SEL 0x00000028
+#define CAMSS_CSI_RDI_CLK_CGC_EN 0x0000002C
+
+/* Select the appropriate CSI clock for CSI Phy0 */
+#define CAMSS_CSI_PHY_0_CLK_MUX_SEL 0x00000030
+#define CAMSS_CSI_PHY_0_CLK_CGC_EN 0x00000034
+
+/* Select the appropriate CSI clock for CSI Phy1 */
+#define CAMSS_CSI_PHY_1_CLK_MUX_SEL 0x00000038
+#define CAMSS_CSI_PHY_1_CLK_CGC_EN 0x0000003C
+
+/* Select the appropriate CSI clock for CSI Phy2 */
+#define CAMSS_CSI_PHY_2_CLK_MUX_SEL 0x00000040
+#define CAMSS_CSI_PHY_2_CLK_CGC_EN 0x00000044
+/* Camera SS common registers defines - End */
+
+/* IRQ Router registers defines - Start */
+/* This register is used to reset the composite
+ * IRQ outputs of the Camera_SS IRQ Router */
+#define CAMSS_IRQ_COMPOSITE_RESET_CTRL 0x00000060
+
+/* By default, this 'allows' the interrupts from
+ * Micro to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_0 0x00000064
+
+/* By default, this 'allows' the interrupts from
+ * CCI to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_1 0x00000068
+
+/* By default, this 'allows' the interrupts from
+ * CSI_0 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_2 0x0000006C
+
+/* By default, this 'allows' the interrupts from
+ * CSI_1 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_3 0x00000070
+
+/* By default, this 'allows' the interrupts from
+ * CSI_2 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_4 0x00000074
+
+/* By default, this 'allows' the interrupts from
+ * CSI_3 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_5 0x00000078
+
+/* By default, this 'allows' the interrupts from
+ * ISPIF to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_6 0x0000007C
+
+/* By default, this 'allows' the interrupts from
+ * CPP to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_7 0x00000080
+
+/* By default, this 'allows' the interrupts from
+ * VFE_0 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_8 0x00000084
+
+/* By default, this 'allows' the interrupts from
+ * VFE_1 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_9 0x00000088
+
+/* By default, this 'allows' the interrupts from
+ * JPEG_0 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_10 0x0000008C
+
+/* By default, this 'allows' the interrupts from
+ * JPEG_1 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_11 0x00000090
+
+/* By default, this 'allows' the interrupts from
+ * JPEG_2 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_12 0x00000094
+
+/* The following IRQ_COMPOSITE_MICRO_MASK registers
+ * allow the interrupts from the individual hw
+ * cores to be composited into an IRQ for Micro. */
+#define CAMSS_IRQ_COMPOSITE_MICRO_MASK_0 0x000000A4
+#define CAMSS_IRQ_COMPOSITE_MICRO_MASK_1 0x000000A8
+#define CAMSS_IRQ_COMPOSITE_MICRO_MASK_2 0x000000AC
+#define CAMSS_IRQ_COMPOSITE_MICRO_MASK_3 0x000000B0
+#define CAMSS_IRQ_COMPOSITE_MICRO_MASK_4 0x000000B4
+#define CAMSS_IRQ_COMPOSITE_MICRO_MASK_5 0x000000B8
+/* IRQ Router register defines - End */
+
+/* Writing this mask will reset all the composite
+ * IRQs of the Camera_SS IRQ Router */
+#define CAMSS_IRQ_COMPOSITE_RESET_MASK 0x003F1FFF
+
+/* Use this to enable Micro IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_MICRO_IRQ_IN_COMPOSITE BIT(0)
+/* Use this to enable CCI IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_CCI_IRQ_IN_COMPOSITE BIT(1)
+/* Use this to enable CSI0 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_CSI0_IRQ_IN_COMPOSITE BIT(2)
+/* Use this to enable CSI1 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_CSI1_IRQ_IN_COMPOSITE BIT(3)
+/* Use this to enable CSI2 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_CSI2_IRQ_IN_COMPOSITE BIT(4)
+/* Use this to enable CSI3 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_CSI3_IRQ_IN_COMPOSITE BIT(5)
+/* Use this to enable ISPIF IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_ISPIF_IRQ_IN_COMPOSITE BIT(6)
+/* Use this to enable CPP IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_CPP_IRQ_IN_COMPOSITE BIT(7)
+/* Use this to enable VFE0 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_VFE0_IRQ_IN_COMPOSITE BIT(8)
+/* Use this to enable VFE1 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_VFE1_IRQ_IN_COMPOSITE BIT(9)
+/* Use this to enable JPEG0 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_JPEG0_IRQ_IN_COMPOSITE BIT(10)
+/* Use this to enable JPEG1 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_JPEG1_IRQ_IN_COMPOSITE BIT(11)
+/* Use this to enable JPEG2 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_JPEG2_IRQ_IN_COMPOSITE BIT(12)
+
+struct irqrouter_ctrl_type {
+ /* v4l2 subdev */
+ struct v4l2_subdev subdev;
+ struct platform_device *pdev;
+
+ void __iomem *irqr_dev_base;
+
+ struct resource *irqr_dev_mem;
+ struct resource *irqr_dev_io;
+ atomic_t active;
+ struct msm_cam_server_irqmap_entry def_hw_irqmap[CAMERA_SS_IRQ_MAX];
+};
+
+#endif /* __MSM_CAM_IRQROUTER_H__ */
diff --git a/drivers/media/video/msm/msm_gesture.c b/drivers/media/video/msm/msm_gesture.c
index 6b81d15..5777cb5 100644
--- a/drivers/media/video/msm/msm_gesture.c
+++ b/drivers/media/video/msm/msm_gesture.c
@@ -455,6 +455,8 @@
struct msm_gesture_ctrl *p_gesture_ctrl = &g_gesture_ctrl;
struct v4l2_subdev *gesture_subdev =
kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ struct msm_cam_subdev_info sd_info;
+
D("%s\n", __func__);
if (!gesture_subdev) {
pr_err("%s: no enough memory\n", __func__);
@@ -475,7 +477,10 @@
/* events */
gesture_subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS;
- msm_cam_register_subdev_node(gesture_subdev, GESTURE_DEV, 0);
+ sd_info.sdev_type = GESTURE_DEV;
+ sd_info.sd_index = 0;
+ sd_info.irq_num = 0;
+ msm_cam_register_subdev_node(gesture_subdev, &sd_info);
gesture_subdev->entity.revision = gesture_subdev->devnode->num;
diff --git a/drivers/media/video/msm/msm_isp.c b/drivers/media/video/msm/msm_isp.c
index fab0095..9b42f9b 100644
--- a/drivers/media/video/msm/msm_isp.c
+++ b/drivers/media/video/msm/msm_isp.c
@@ -136,6 +136,15 @@
image_mode = -1;
break;
}
+ } else if (vfe_msg == VFE_MSG_OUTPUT_TERTIARY1) {
+ switch (pmctl->vfe_output_mode) {
+ case VFE_OUTPUTS_RDI0:
+ image_mode = MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW;
+ break;
+ default:
+ image_mode = -1;
+ break;
+ }
} else
image_mode = -1;
@@ -146,7 +155,7 @@
static int msm_isp_notify_VFE_BUF_EVT(struct v4l2_subdev *sd, void *arg)
{
- int rc = -EINVAL, image_mode;
+ int rc = -EINVAL;
struct msm_vfe_resp *vdata = (struct msm_vfe_resp *)arg;
struct msm_free_buf free_buf, temp_free_buf;
struct msm_camvfe_params vfe_params;
@@ -154,16 +163,22 @@
struct msm_cam_media_controller *pmctl =
(struct msm_cam_media_controller *)v4l2_get_subdev_hostdata(sd);
struct msm_cam_v4l2_device *pcam = pmctl->pcam_ptr;
-
- int vfe_id = vdata->evt_msg.msg_id;
+ struct msm_frame_info *frame_info =
+ (struct msm_frame_info *)vdata->evt_msg.data;
+ uint32_t vfe_id, image_mode;
if (!pcam) {
pr_debug("%s pcam is null. return\n", __func__);
msm_isp_sync_free(vdata);
return rc;
}
- /* Convert the vfe msg to the image mode */
- image_mode = msm_isp_vfe_msg_to_img_mode(pmctl, vfe_id);
- BUG_ON(image_mode < 0);
+ if (frame_info) {
+ vfe_id = frame_info->path;
+ image_mode = frame_info->image_mode;
+ } else {
+ vfe_id = vdata->evt_msg.msg_id;
+ image_mode = msm_isp_vfe_msg_to_img_mode(pmctl, vfe_id);
+ }
+
switch (vdata->type) {
case VFE_MSG_V32_START:
case VFE_MSG_V32_START_RECORDING:
@@ -302,45 +317,52 @@
}
case NOTIFY_VFE_MSG_OUT: {
uint8_t msgid;
+ int32_t image_mode = -1;
struct isp_msg_output *isp_output =
(struct isp_msg_output *)arg;
- switch (isp_output->output_id) {
- case MSG_ID_OUTPUT_P:
- msgid = VFE_MSG_OUTPUT_P;
- break;
- case MSG_ID_OUTPUT_V:
- msgid = VFE_MSG_OUTPUT_V;
- break;
- case MSG_ID_OUTPUT_T:
- msgid = VFE_MSG_OUTPUT_T;
- break;
- case MSG_ID_OUTPUT_S:
- msgid = VFE_MSG_OUTPUT_S;
- break;
- case MSG_ID_OUTPUT_PRIMARY:
- msgid = VFE_MSG_OUTPUT_PRIMARY;
- break;
- case MSG_ID_OUTPUT_SECONDARY:
- msgid = VFE_MSG_OUTPUT_SECONDARY;
- break;
- default:
- pr_err("%s: Invalid VFE output id: %d\n",
- __func__, isp_output->output_id);
- rc = -EINVAL;
- break;
+ if (isp_output->buf.image_mode < 0) {
+ switch (isp_output->output_id) {
+ case MSG_ID_OUTPUT_P:
+ msgid = VFE_MSG_OUTPUT_P;
+ break;
+ case MSG_ID_OUTPUT_V:
+ msgid = VFE_MSG_OUTPUT_V;
+ break;
+ case MSG_ID_OUTPUT_T:
+ msgid = VFE_MSG_OUTPUT_T;
+ break;
+ case MSG_ID_OUTPUT_S:
+ msgid = VFE_MSG_OUTPUT_S;
+ break;
+ case MSG_ID_OUTPUT_PRIMARY:
+ msgid = VFE_MSG_OUTPUT_PRIMARY;
+ break;
+ case MSG_ID_OUTPUT_SECONDARY:
+ msgid = VFE_MSG_OUTPUT_SECONDARY;
+ break;
+ case MSG_ID_OUTPUT_TERTIARY1:
+ msgid = VFE_MSG_OUTPUT_TERTIARY1;
+ break;
+ default:
+ pr_err("%s: Invalid VFE output id: %d\n",
+ __func__, isp_output->output_id);
+ rc = -EINVAL;
+ break;
+ }
+ if (!rc)
+ image_mode =
+ msm_isp_vfe_msg_to_img_mode(pmctl, msgid);
+ } else {
+ image_mode = isp_output->buf.image_mode;
}
-
- if (!rc) {
- isp_event->isp_data.isp_msg.msg_id =
- isp_output->output_id;
- isp_event->isp_data.isp_msg.frame_id =
- isp_output->frameCounter;
- buf = isp_output->buf;
- msgid = msm_isp_vfe_msg_to_img_mode(pmctl, msgid);
- BUG_ON(msgid < 0);
- msm_mctl_buf_done(pmctl, msgid,
- &buf, isp_output->frameCounter);
- }
+ isp_event->isp_data.isp_msg.msg_id =
+ isp_output->output_id;
+ isp_event->isp_data.isp_msg.frame_id =
+ isp_output->frameCounter;
+ buf = isp_output->buf;
+ BUG_ON(image_mode < 0);
+ msm_mctl_buf_done(pmctl, image_mode,
+ &buf, isp_output->frameCounter);
}
break;
case NOTIFY_VFE_MSG_COMP_STATS: {
@@ -663,6 +685,7 @@
case CMD_AXI_CFG_PRIM_ALL_CHNLS|CMD_AXI_CFG_SEC:
case CMD_AXI_START:
case CMD_AXI_STOP:
+ case CMD_AXI_CFG_TERT1:
/* Dont need to pass buffer information.
* subdev will get the buffer from media
* controller free queue.
diff --git a/drivers/media/video/msm/msm_mctl.c b/drivers/media/video/msm/msm_mctl.c
index ed6c493..cdfad3b 100644
--- a/drivers/media/video/msm/msm_mctl.c
+++ b/drivers/media/video/msm/msm_mctl.c
@@ -39,6 +39,7 @@
#include "msm_vpe.h"
#include "msm_vfe32.h"
#include "msm_camera_eeprom.h"
+#include "msm_csi_register.h"
#ifdef CONFIG_MSM_CAMERA_DEBUG
#define D(fmt, args...) pr_debug("msm_mctl: " fmt, ##args)
@@ -47,7 +48,6 @@
#endif
#define MSM_V4L2_SWFI_LATENCY 3
-
/* VFE required buffer number for streaming */
static struct msm_isp_color_fmt msm_isp_formats[] = {
{
@@ -162,6 +162,8 @@
info.mount_angle = sdata->sensor_platform_info->mount_angle;
info.actuator_enabled = sdata->actuator_info ? 1 : 0;
info.strobe_flash_enabled = sdata->strobe_flash_data ? 1 : 0;
+ info.ispif_supported = mctl->ispif_sdev ? 1 : 0;
+
/* copy back to user space */
if (copy_to_user((void *)arg,
&info,
@@ -400,6 +402,10 @@
else
rc = p_mctl->isp_sdev->isp_config(p_mctl, cmd, arg);
break;
+ case MSM_CAM_IOCTL_ISPIF_IO_CFG:
+ rc = v4l2_subdev_call(p_mctl->ispif_sdev,
+ core, ioctl, VIDIOC_MSM_ISPIF_CFG, argp);
+ break;
default:
/* ISP config*/
D("%s:%d: go to default. Calling msm_isp_config\n",
@@ -435,62 +441,12 @@
(struct msm_camera_sensor_info *) s_ctrl->sensordata;
struct msm_camera_device_platform_data *pdata = sinfo->pdata;
- if (pdata->is_csiphy) {
- /* register csiphy subdev */
- driver = driver_find(MSM_CSIPHY_DRV_NAME, &platform_bus_type);
- if (!driver)
- goto out;
-
- dev = driver_find_device(driver, NULL, (void *)core_index,
+ rc = msm_csi_register_subdevs(p_mctl, core_index,
msm_mctl_subdev_match_core);
- if (!dev)
+
+ if (rc < 0)
goto out;
- p_mctl->csiphy_sdev = dev_get_drvdata(dev);
- }
-
- if (pdata->is_csic) {
- /* register csic subdev */
- driver = driver_find(MSM_CSIC_DRV_NAME, &platform_bus_type);
- if (!driver)
- goto out;
-
- dev = driver_find_device(driver, NULL, (void *)core_index,
- msm_mctl_subdev_match_core);
- if (!dev)
- goto out;
-
- p_mctl->csic_sdev = dev_get_drvdata(dev);
- }
-
- if (pdata->is_csid) {
- /* register csid subdev */
- driver = driver_find(MSM_CSID_DRV_NAME, &platform_bus_type);
- if (!driver)
- goto out;
-
- dev = driver_find_device(driver, NULL, (void *)core_index,
- msm_mctl_subdev_match_core);
- if (!dev)
- goto out;
-
- p_mctl->csid_sdev = dev_get_drvdata(dev);
- }
-
- if (pdata->is_ispif) {
- /* register ispif subdev */
- driver = driver_find(MSM_ISPIF_DRV_NAME, &platform_bus_type);
- if (!driver)
- goto out;
-
- dev = driver_find_device(driver, NULL, 0,
- msm_mctl_subdev_match_core);
- if (!dev)
- goto out;
-
- p_mctl->ispif_sdev = dev_get_drvdata(dev);
- }
-
/* register vfe subdev */
driver = driver_find(MSM_VFE_DRV_NAME, &platform_bus_type);
if (!driver)
@@ -568,6 +524,7 @@
mutex_lock(&p_mctl->lock);
/* open sub devices - once only*/
if (!p_mctl->opencnt) {
+ struct msm_sensor_csi_info csi_info;
uint32_t csid_version;
wake_lock(&p_mctl->wake_lock);
@@ -594,7 +551,7 @@
goto act_power_up_failed;
}
- if (camdev->is_csiphy) {
+ if (p_mctl->csiphy_sdev) {
rc = v4l2_subdev_call(p_mctl->csiphy_sdev, core, ioctl,
VIDIOC_MSM_CSIPHY_INIT, NULL);
if (rc < 0) {
@@ -604,7 +561,7 @@
}
}
- if (camdev->is_csid) {
+ if (p_mctl->csid_sdev) {
rc = v4l2_subdev_call(p_mctl->csid_sdev, core, ioctl,
VIDIOC_MSM_CSID_INIT, &csid_version);
if (rc < 0) {
@@ -612,9 +569,10 @@
__func__, rc);
goto csid_init_failed;
}
+ csi_info.is_csic = 0;
}
- if (camdev->is_csic) {
+ if (p_mctl->csic_sdev) {
rc = v4l2_subdev_call(p_mctl->csic_sdev, core, ioctl,
VIDIOC_MSM_CSIC_INIT, &csid_version);
if (rc < 0) {
@@ -622,6 +580,16 @@
__func__, rc);
goto csic_init_failed;
}
+ csi_info.is_csic = 1;
+ }
+
+ csi_info.csid_version = csid_version;
+ rc = v4l2_subdev_call(p_mctl->sensor_sdev, core, ioctl,
+ VIDIOC_MSM_SENSOR_CSID_INFO, &csi_info);
+ if (rc < 0) {
+ pr_err("%s: sensor csi version failed %d\n",
+ __func__, rc);
+ goto msm_csi_version;
}
/* ISP first*/
@@ -655,15 +623,6 @@
}
}
- if (camdev->is_ispif) {
- rc = v4l2_subdev_call(p_mctl->ispif_sdev, core, ioctl,
- VIDIOC_MSM_ISPIF_INIT, &csid_version);
- if (rc < 0) {
- pr_err("%s: ispif initialization failed %d\n",
- __func__, rc);
- goto ispif_init_failed;
- }
- }
pm_qos_add_request(&p_mctl->pm_qos_req_list,
PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
@@ -679,11 +638,6 @@
return rc;
-ispif_init_failed:
- if (camdev->is_vpe)
- if (v4l2_subdev_call(p_mctl->vpe_sdev, core, ioctl,
- VIDIOC_MSM_VPE_RELEASE, NULL) < 0)
- pr_err("%s: vpe release failed %d\n", __func__, rc);
vpe_init_failed:
if (p_mctl->axi_sdev)
if (v4l2_subdev_call(p_mctl->axi_sdev, core, ioctl,
@@ -692,18 +646,19 @@
axi_init_failed:
if (p_mctl->isp_sdev && p_mctl->isp_sdev->isp_release)
p_mctl->isp_sdev->isp_release(p_mctl, p_mctl->isp_sdev->sd);
+msm_csi_version:
isp_open_failed:
- if (camdev->is_csic)
+ if (p_mctl->csic_sdev)
if (v4l2_subdev_call(p_mctl->csic_sdev, core, ioctl,
VIDIOC_MSM_CSIC_RELEASE, NULL) < 0)
pr_err("%s: csic release failed %d\n", __func__, rc);
csic_init_failed:
- if (camdev->is_csid)
+ if (p_mctl->csid_sdev)
if (v4l2_subdev_call(p_mctl->csid_sdev, core, ioctl,
VIDIOC_MSM_CSID_RELEASE, NULL) < 0)
pr_err("%s: csid release failed %d\n", __func__, rc);
csid_init_failed:
- if (camdev->is_csiphy)
+ if (p_mctl->csiphy_sdev)
if (v4l2_subdev_call(p_mctl->csiphy_sdev, core, ioctl,
VIDIOC_MSM_CSIPHY_RELEASE, NULL) < 0)
pr_err("%s: csiphy release failed %d\n", __func__, rc);
@@ -733,12 +688,7 @@
v4l2_subdev_call(p_mctl->sensor_sdev, core, ioctl,
VIDIOC_MSM_SENSOR_RELEASE, NULL);
- if (camdev->is_ispif) {
- v4l2_subdev_call(p_mctl->ispif_sdev, core, ioctl,
- VIDIOC_MSM_ISPIF_RELEASE, NULL);
- }
-
- if (camdev->is_csic) {
+ if (p_mctl->csic_sdev) {
v4l2_subdev_call(p_mctl->csic_sdev, core, ioctl,
VIDIOC_MSM_CSIC_RELEASE, NULL);
}
@@ -757,12 +707,12 @@
p_mctl->isp_sdev->isp_release(p_mctl,
p_mctl->isp_sdev->sd);
- if (camdev->is_csid) {
+ if (p_mctl->csid_sdev) {
v4l2_subdev_call(p_mctl->csid_sdev, core, ioctl,
VIDIOC_MSM_CSID_RELEASE, NULL);
}
- if (camdev->is_csiphy) {
+ if (p_mctl->csiphy_sdev) {
v4l2_subdev_call(p_mctl->csiphy_sdev, core, ioctl,
VIDIOC_MSM_CSIPHY_RELEASE, NULL);
}
diff --git a/drivers/media/video/msm/msm_vfe31_v4l2.c b/drivers/media/video/msm/msm_vfe31_v4l2.c
index 7a2670c8..885cd90 100644
--- a/drivers/media/video/msm/msm_vfe31_v4l2.c
+++ b/drivers/media/video/msm/msm_vfe31_v4l2.c
@@ -420,15 +420,18 @@
vfe31_ctrl->vfebase + VFE_GLOBAL_RESET);
}
-static void vfe31_subdev_notify(int id, int path)
+static void vfe31_subdev_notify(int id, int path, int image_mode)
{
struct msm_vfe_resp rp;
+ struct msm_frame_info frame_info;
unsigned long flags;
spin_lock_irqsave(&vfe31_ctrl->sd_notify_lock, flags);
memset(&rp, 0, sizeof(struct msm_vfe_resp));
CDBG("vfe31_subdev_notify : msgId = %d\n", id);
rp.evt_msg.type = MSM_CAMERA_MSG;
- rp.evt_msg.msg_id = path;
+ frame_info.image_mode = image_mode;
+ frame_info.path = path;
+ rp.evt_msg.data = &frame_info;
rp.type = id;
v4l2_subdev_notify(&vfe31_ctrl->subdev, NOTIFY_VFE_BUF_EVT, &rp);
spin_unlock_irqrestore(&vfe31_ctrl->sd_notify_lock, flags);
@@ -442,10 +445,12 @@
ch_info = axi_cfg + V31_AXI_CFG_LEN;
vfe31_ctrl->outpath.out0.ch0 = 0x0000FFFF & *ch_info;
vfe31_ctrl->outpath.out0.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
- vfe31_ctrl->outpath.out0.ch2 = 0x0000FFFF & *ch_info++;
+ vfe31_ctrl->outpath.out0.ch2 = 0x0000FFFF & *ch_info;
+ vfe31_ctrl->outpath.out0.image_mode = 0x0000FFFF & (*ch_info++ >> 16);
vfe31_ctrl->outpath.out1.ch0 = 0x0000FFFF & *ch_info;
vfe31_ctrl->outpath.out1.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
- vfe31_ctrl->outpath.out1.ch2 = 0x0000FFFF & *ch_info++;
+ vfe31_ctrl->outpath.out1.ch2 = 0x0000FFFF & *ch_info;
+ vfe31_ctrl->outpath.out1.image_mode = 0x0000FFFF & (*ch_info++ >> 16);
vfe31_ctrl->outpath.out2.ch0 = 0x0000FFFF & *ch_info;
vfe31_ctrl->outpath.out2.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
vfe31_ctrl->outpath.out2.ch2 = 0x0000FFFF & *ch_info++;
@@ -1168,7 +1173,14 @@
{
struct vfe31_output_ch *outch = NULL;
struct msm_free_buf *b = NULL;
- vfe31_subdev_notify(id, path);
+ uint32_t image_mode = 0;
+
+ if (path == VFE_MSG_OUTPUT_PRIMARY)
+ image_mode = vfe31_ctrl->outpath.out0.image_mode;
+ else
+ image_mode = vfe31_ctrl->outpath.out1.image_mode;
+
+ vfe31_subdev_notify(id, path, image_mode);
outch = vfe31_get_ch(path);
if (outch->free_buf.ch_paddr[0])
b = &outch->free_buf;
@@ -1178,7 +1190,14 @@
{
struct vfe31_output_ch *outch = NULL;
int rc = 0;
- vfe31_subdev_notify(id, path);
+ uint32_t image_mode = 0;
+
+ if (path == VFE_MSG_OUTPUT_PRIMARY)
+ image_mode = vfe31_ctrl->outpath.out0.image_mode;
+ else
+ image_mode = vfe31_ctrl->outpath.out1.image_mode;
+
+ vfe31_subdev_notify(id, path, image_mode);
outch = vfe31_get_ch(path);
if (outch->ping.ch_paddr[0] && outch->pong.ch_paddr[0]) {
/* Configure Preview Ping Pong */
@@ -2595,11 +2614,13 @@
}
}
static void vfe_send_outmsg(struct v4l2_subdev *sd, uint8_t msgid,
- uint32_t ch0_paddr, uint32_t ch1_paddr, uint32_t ch2_paddr)
+ uint32_t ch0_paddr, uint32_t ch1_paddr,
+ uint32_t ch2_paddr, uint32_t image_mode)
{
struct isp_msg_output msg;
msg.output_id = msgid;
+ msg.buf.image_mode = image_mode;
msg.buf.ch_paddr[0] = ch0_paddr;
msg.buf.ch_paddr[1] = ch1_paddr;
msg.buf.ch_paddr[2] = ch2_paddr;
@@ -2681,7 +2702,8 @@
vfe_send_outmsg(&vfe31_ctrl->subdev,
MSG_ID_OUTPUT_PRIMARY, ch0_paddr,
- ch1_paddr, ch2_paddr);
+ ch1_paddr, ch2_paddr,
+ vfe31_ctrl->outpath.out0.image_mode);
if (vfe31_ctrl->liveshot_state == VFE_STATE_STOPPED)
vfe31_ctrl->liveshot_state = VFE_STATE_IDLE;
@@ -2753,7 +2775,8 @@
vfe_send_outmsg(&vfe31_ctrl->subdev,
MSG_ID_OUTPUT_SECONDARY, ch0_paddr,
- ch1_paddr, ch2_paddr);
+ ch1_paddr, ch2_paddr,
+ vfe31_ctrl->outpath.out1.image_mode);
} else {
vfe31_ctrl->outpath.out1.frame_drop_cnt++;
CDBG("path_irq_1 - no free buffer!\n");
@@ -3821,7 +3844,10 @@
static int __devinit vfe31_probe(struct platform_device *pdev)
{
int rc = 0;
+ struct msm_cam_subdev_info sd_info;
+
CDBG("%s: device id = %d\n", __func__, pdev->id);
+
vfe31_ctrl = kzalloc(sizeof(struct vfe31_ctrl_type), GFP_KERNEL);
if (!vfe31_ctrl) {
pr_err("%s: no enough memory\n", __func__);
@@ -3893,7 +3919,10 @@
disable_irq(vfe31_ctrl->vfeirq->start);
vfe31_ctrl->pdev = pdev;
- msm_cam_register_subdev_node(&vfe31_ctrl->subdev, VFE_DEV, 0);
+ sd_info.sdev_type = VFE_DEV;
+ sd_info.sd_index = 0;
+ sd_info.irq_num = vfe31_ctrl->vfeirq->start;
+ msm_cam_register_subdev_node(&vfe31_ctrl->subdev, &sd_info);
return 0;
vfe31_no_resource:
diff --git a/drivers/media/video/msm/msm_vfe31_v4l2.h b/drivers/media/video/msm/msm_vfe31_v4l2.h
index e94f286..739d157 100644
--- a/drivers/media/video/msm/msm_vfe31_v4l2.h
+++ b/drivers/media/video/msm/msm_vfe31_v4l2.h
@@ -695,7 +695,7 @@
struct vfe31_output_ch {
struct list_head free_buf_queue;
spinlock_t free_buf_lock;
- uint16_t output_fmt;
+ uint16_t image_mode;
int8_t ch0;
int8_t ch1;
int8_t ch2;
diff --git a/drivers/media/video/msm/msm_vfe32.c b/drivers/media/video/msm/msm_vfe32.c
index 3611656..acff492 100644
--- a/drivers/media/video/msm/msm_vfe32.c
+++ b/drivers/media/video/msm/msm_vfe32.c
@@ -25,6 +25,7 @@
#include <media/msm_isp.h>
#include "msm.h"
+#include "msm_cam_server.h"
#include "msm_vfe32.h"
atomic_t irq_cnt;
@@ -389,16 +390,19 @@
vfe32_ctrl->share_ctrl->vfebase + VFE_CAMIF_COMMAND);
}
-static void vfe32_subdev_notify(int id, int path,
+static void vfe32_subdev_notify(int id, int path, int image_mode,
struct v4l2_subdev *sd, struct vfe_share_ctrl_t *share_ctrl)
{
struct msm_vfe_resp rp;
+ struct msm_frame_info frame_info;
unsigned long flags = 0;
spin_lock_irqsave(&share_ctrl->sd_notify_lock, flags);
CDBG("vfe32_subdev_notify : msgId = %d\n", id);
memset(&rp, 0, sizeof(struct msm_vfe_resp));
rp.evt_msg.type = MSM_CAMERA_MSG;
- rp.evt_msg.msg_id = path;
+ frame_info.image_mode = image_mode;
+ frame_info.path = path;
+ rp.evt_msg.data = &frame_info;
rp.type = id;
v4l2_subdev_notify(sd, NOTIFY_VFE_BUF_EVT, &rp);
spin_unlock_irqrestore(&share_ctrl->sd_notify_lock, flags);
@@ -415,17 +419,28 @@
axi_ctrl->share_ctrl->outpath.out0.ch0 = 0x0000FFFF & *ch_info;
axi_ctrl->share_ctrl->outpath.out0.ch1 =
0x0000FFFF & (*ch_info++ >> 16);
- axi_ctrl->share_ctrl->outpath.out0.ch2 = 0x0000FFFF & *ch_info++;
+ axi_ctrl->share_ctrl->outpath.out0.ch2 = 0x0000FFFF & *ch_info;
+ axi_ctrl->share_ctrl->outpath.out0.image_mode =
+ 0x0000FFFF & (*ch_info++ >> 16);
axi_ctrl->share_ctrl->outpath.out1.ch0 = 0x0000FFFF & *ch_info;
axi_ctrl->share_ctrl->outpath.out1.ch1 =
0x0000FFFF & (*ch_info++ >> 16);
- axi_ctrl->share_ctrl->outpath.out1.ch2 = 0x0000FFFF & *ch_info++;
+ axi_ctrl->share_ctrl->outpath.out1.ch2 = 0x0000FFFF & *ch_info;
+ axi_ctrl->share_ctrl->outpath.out1.image_mode =
+ 0x0000FFFF & (*ch_info++ >> 16);
axi_ctrl->share_ctrl->outpath.out2.ch0 = 0x0000FFFF & *ch_info;
axi_ctrl->share_ctrl->outpath.out2.ch1 =
0x0000FFFF & (*ch_info++ >> 16);
axi_ctrl->share_ctrl->outpath.out2.ch2 = 0x0000FFFF & *ch_info++;
+ axi_ctrl->share_ctrl->outpath.out2.image_mode =
+ 0x0000FFFF & (*ch_info++ >> 16);
+
switch (mode) {
+ case OUTPUT_TERT1:
+ axi_ctrl->share_ctrl->outpath.output_mode =
+ VFE32_OUTPUT_MODE_TERTIARY1;
+ break;
case OUTPUT_PRIM:
axi_ctrl->share_ctrl->outpath.output_mode =
VFE32_OUTPUT_MODE_PRIMARY;
@@ -706,7 +721,7 @@
static void vfe32_start_common(struct vfe32_ctrl_type *vfe32_ctrl)
{
- uint32_t irq_mask = 0x00E00021;
+ uint32_t irq_mask = 0x00E00021, irq_mask1;
vfe32_ctrl->start_ack_pending = TRUE;
CDBG("VFE opertaion mode = 0x%x, output mode = 0x%x\n",
vfe32_ctrl->share_ctrl->operation_mode,
@@ -715,19 +730,31 @@
irq_mask |= VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK;
else
irq_mask |= 0x000FE000;
-
+ irq_mask |=
+ msm_camera_io_r(vfe32_ctrl->share_ctrl->vfebase +
+ VFE_IRQ_MASK_0);
msm_camera_io_w(irq_mask,
vfe32_ctrl->share_ctrl->vfebase + VFE_IRQ_MASK_0);
msm_camera_io_w(VFE_IMASK_WHILE_STOPPING_1,
vfe32_ctrl->share_ctrl->vfebase + VFE_IRQ_MASK_1);
+ if (vfe32_ctrl->share_ctrl->operation_mode == VFE_OUTPUTS_RDI0) {
+ irq_mask1 =
+ msm_camera_io_r(vfe32_ctrl->share_ctrl->vfebase +
+ VFE_IRQ_MASK_1);
+ irq_mask1 |= VFE_IRQ_STATUS1_RDI0_REG_UPDATE_MASK;
+ msm_camera_io_w(irq_mask1, vfe32_ctrl->share_ctrl->vfebase +
+ VFE_IRQ_MASK_1);
+ msm_camera_io_w_mb(2, vfe32_ctrl->share_ctrl->vfebase +
+ VFE_REG_UPDATE_CMD);
+ } else {
+ msm_camera_io_w_mb(1, vfe32_ctrl->share_ctrl->vfebase +
+ VFE_REG_UPDATE_CMD);
+ msm_camera_io_w_mb(1, vfe32_ctrl->share_ctrl->vfebase +
+ VFE_CAMIF_COMMAND);
+ }
/* Ensure the write order while writing
to the command register using the barrier */
- msm_camera_io_w_mb(1,
- vfe32_ctrl->share_ctrl->vfebase + VFE_REG_UPDATE_CMD);
- msm_camera_io_w_mb(1,
- vfe32_ctrl->share_ctrl->vfebase + VFE_CAMIF_COMMAND);
-
atomic_set(&vfe32_ctrl->share_ctrl->vstate, 1);
}
@@ -981,7 +1008,8 @@
struct msm_cam_media_controller *pmctl,
struct vfe32_ctrl_type *vfe32_ctrl)
{
- uint32_t irq_comp_mask = 0;
+ uint32_t irq_comp_mask = 0, irq_mask = 0;
+
irq_comp_mask =
msm_camera_io_r(vfe32_ctrl->share_ctrl->vfebase +
VFE_IRQ_COMP_MASK);
@@ -1010,6 +1038,16 @@
0x1 << (vfe32_ctrl->share_ctrl->outpath.out1.ch1 + 8) |
0x1 << (vfe32_ctrl->share_ctrl->outpath.out1.ch2 + 8));
}
+ if (vfe32_ctrl->share_ctrl->outpath.output_mode &
+ VFE32_OUTPUT_MODE_TERTIARY1) {
+ irq_mask = msm_camera_io_r(vfe32_ctrl->share_ctrl->vfebase +
+ VFE_IRQ_MASK_0);
+ irq_mask |= (0x1 << (vfe32_ctrl->share_ctrl->outpath.out2.ch0 +
+ VFE_WM_OFFSET));
+ msm_camera_io_w(irq_mask, vfe32_ctrl->share_ctrl->vfebase +
+ VFE_IRQ_MASK_0);
+ }
+
msm_camera_io_w(irq_comp_mask,
vfe32_ctrl->share_ctrl->vfebase + VFE_IRQ_COMP_MASK);
@@ -1227,6 +1265,8 @@
ch = &share_ctrl->outpath.out0;
else if (path == VFE_MSG_OUTPUT_SECONDARY)
ch = &share_ctrl->outpath.out1;
+ else if (path == VFE_MSG_OUTPUT_TERTIARY1)
+ ch = &share_ctrl->outpath.out2;
else
pr_err("%s: Invalid path %d\n", __func__,
path);
@@ -1239,7 +1279,16 @@
{
struct vfe32_output_ch *outch = NULL;
struct msm_free_buf *b = NULL;
- vfe32_subdev_notify(id, path,
+ uint32_t image_mode = 0;
+
+ if (path == VFE_MSG_OUTPUT_PRIMARY)
+ image_mode = axi_ctrl->share_ctrl->outpath.out0.image_mode;
+ else if (path == VFE_MSG_OUTPUT_SECONDARY)
+ image_mode = axi_ctrl->share_ctrl->outpath.out1.image_mode;
+ else if (path == VFE_MSG_OUTPUT_TERTIARY1)
+ image_mode = axi_ctrl->share_ctrl->outpath.out2.image_mode;
+
+ vfe32_subdev_notify(id, path, image_mode,
&axi_ctrl->subdev, axi_ctrl->share_ctrl);
outch = vfe32_get_ch(path, axi_ctrl->share_ctrl);
if (outch->free_buf.ch_paddr[0])
@@ -1251,7 +1300,15 @@
{
struct vfe32_output_ch *outch = NULL;
int rc = 0;
- vfe32_subdev_notify(id, path,
+ uint32_t image_mode = 0;
+ if (path == VFE_MSG_OUTPUT_PRIMARY)
+ image_mode = vfe32_ctrl->share_ctrl->outpath.out0.image_mode;
+ else if (path == VFE_MSG_OUTPUT_SECONDARY)
+ image_mode = vfe32_ctrl->share_ctrl->outpath.out1.image_mode;
+ else if (path == VFE_MSG_OUTPUT_TERTIARY1)
+ image_mode = vfe32_ctrl->share_ctrl->outpath.out2.image_mode;
+
+ vfe32_subdev_notify(id, path, image_mode,
&vfe32_ctrl->subdev, vfe32_ctrl->share_ctrl);
outch = vfe32_get_ch(path, vfe32_ctrl->share_ctrl);
if (outch->ping.ch_paddr[0] && outch->pong.ch_paddr[0]) {
@@ -1265,8 +1322,9 @@
vfe32_ctrl->share_ctrl->vfebase, outch->ch0,
outch->pong.ch_paddr[0]);
- if (vfe32_ctrl->share_ctrl->operation_mode !=
- VFE_OUTPUTS_RAW) {
+ if ((vfe32_ctrl->share_ctrl->operation_mode !=
+ VFE_OUTPUTS_RAW) &&
+ (path != VFE_MSG_OUTPUT_TERTIARY1)) {
vfe32_put_ch_ping_addr(
vfe32_ctrl->share_ctrl->vfebase, outch->ch1,
outch->ping.ch_paddr[1]);
@@ -1356,6 +1414,11 @@
rc = vfe32_configure_pingpong_buffers(
VFE_MSG_V32_START, VFE_MSG_OUTPUT_PRIMARY,
vfe32_ctrl);
+ else if (vfe32_ctrl->share_ctrl->operation_mode ==
+ VFE_OUTPUTS_RDI0)
+ rc = vfe32_configure_pingpong_buffers(
+ VFE_MSG_V32_START, VFE_MSG_OUTPUT_TERTIARY1,
+ vfe32_ctrl);
else
/* Configure secondary channel */
rc = vfe32_configure_pingpong_buffers(
@@ -2823,11 +2886,14 @@
CDBG("stop video triggered .\n");
}
+ spin_lock_irqsave(&vfe32_ctrl->start_ack_lock, flags);
if (vfe32_ctrl->start_ack_pending == TRUE) {
+ vfe32_ctrl->start_ack_pending = FALSE;
+ spin_unlock_irqrestore(&vfe32_ctrl->start_ack_lock, flags);
vfe32_send_isp_msg(&vfe32_ctrl->subdev,
vfe32_ctrl->share_ctrl->vfeFrameId, MSG_ID_START_ACK);
- vfe32_ctrl->start_ack_pending = FALSE;
} else {
+ spin_unlock_irqrestore(&vfe32_ctrl->start_ack_lock, flags);
if (vfe32_ctrl->recording_state ==
VFE_STATE_STOP_REQUESTED) {
vfe32_ctrl->recording_state = VFE_STATE_STOPPED;
@@ -2956,6 +3022,23 @@
} /* if snapshot mode. */
}
+static void vfe32_process_rdi0_reg_update_irq(
+ struct vfe32_ctrl_type *vfe32_ctrl)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&vfe32_ctrl->start_ack_lock, flags);
+ if (vfe32_ctrl->start_ack_pending == TRUE) {
+ vfe32_ctrl->start_ack_pending = FALSE;
+ spin_unlock_irqrestore(
+ &vfe32_ctrl->start_ack_lock, flags);
+ vfe32_send_isp_msg(&vfe32_ctrl->subdev,
+ vfe32_ctrl->share_ctrl->vfeFrameId, MSG_ID_START_ACK);
+ } else {
+ spin_unlock_irqrestore(
+ &vfe32_ctrl->start_ack_lock, flags);
+ }
+}
+
static void vfe32_set_default_reg_values(
struct vfe32_ctrl_type *vfe32_ctrl)
{
@@ -3163,11 +3246,13 @@
static void vfe_send_outmsg(
struct axi_ctrl_t *axi_ctrl, uint8_t msgid,
- uint32_t ch0_paddr, uint32_t ch1_paddr, uint32_t ch2_paddr)
+ uint32_t ch0_paddr, uint32_t ch1_paddr,
+ uint32_t ch2_paddr, uint32_t image_mode)
{
struct isp_msg_output msg;
msg.output_id = msgid;
+ msg.buf.image_mode = image_mode;
msg.buf.ch_paddr[0] = ch0_paddr;
msg.buf.ch_paddr[1] = ch1_paddr;
msg.buf.ch_paddr[2] = ch2_paddr;
@@ -3268,7 +3353,8 @@
vfe_send_outmsg(axi_ctrl,
MSG_ID_OUTPUT_PRIMARY, ch0_paddr,
- ch1_paddr, ch2_paddr);
+ ch1_paddr, ch2_paddr,
+ axi_ctrl->share_ctrl->outpath.out0.image_mode);
if (axi_ctrl->share_ctrl->liveshot_state == VFE_STATE_STOPPED)
axi_ctrl->share_ctrl->liveshot_state = VFE_STATE_IDLE;
@@ -3348,13 +3434,57 @@
vfe_send_outmsg(axi_ctrl,
MSG_ID_OUTPUT_SECONDARY, ch0_paddr,
- ch1_paddr, ch2_paddr);
+ ch1_paddr, ch2_paddr,
+ axi_ctrl->share_ctrl->outpath.out1.image_mode);
+
} else {
axi_ctrl->share_ctrl->outpath.out1.frame_drop_cnt++;
CDBG("path_irq_1 - no free buffer!\n");
}
}
+static void vfe32_process_output_path_irq_rdi0(
+ struct axi_ctrl_t *axi_ctrl)
+{
+ uint32_t ping_pong;
+ uint32_t ch0_paddr = 0;
+ /* this must be rdi image output. */
+ struct msm_free_buf *free_buf = NULL;
+ /*RDI0*/
+ if (axi_ctrl->share_ctrl->operation_mode == VFE_OUTPUTS_RDI0) {
+ free_buf = vfe32_check_free_buffer(VFE_MSG_OUTPUT_IRQ,
+ VFE_MSG_OUTPUT_TERTIARY1, axi_ctrl);
+ if (free_buf) {
+ ping_pong = msm_camera_io_r(axi_ctrl->
+ share_ctrl->vfebase +
+ VFE_BUS_PING_PONG_STATUS);
+
+ /* Y only channel */
+ ch0_paddr = vfe32_get_ch_addr(ping_pong,
+ axi_ctrl->share_ctrl->vfebase,
+ axi_ctrl->share_ctrl->outpath.out2.ch0);
+
+ pr_debug("%s ch0 = 0x%x\n",
+ __func__, ch0_paddr);
+
+ /* Y channel */
+ vfe32_put_ch_addr(ping_pong,
+ axi_ctrl->share_ctrl->vfebase,
+ axi_ctrl->share_ctrl->outpath.out2.ch0,
+ free_buf->ch_paddr[0]);
+
+ vfe_send_outmsg(axi_ctrl,
+ MSG_ID_OUTPUT_TERTIARY1, ch0_paddr,
+ 0, 0,
+ axi_ctrl->share_ctrl->outpath.out2.image_mode);
+
+ } else {
+ axi_ctrl->share_ctrl->outpath.out2.frame_drop_cnt++;
+ pr_err("path_irq_2 irq - no free buffer for rdi0!\n");
+ }
+ }
+}
+
static uint32_t vfe32_process_stats_irq_common(
struct vfe32_ctrl_type *vfe32_ctrl,
uint32_t statsNum, uint32_t newAddr)
@@ -3730,6 +3860,10 @@
CDBG("irq regUpdateIrq\n");
vfe32_process_reg_update_irq(vfe32_ctrl);
break;
+ case VFE_IRQ_STATUS1_RDI0_REG_UPDATE:
+ CDBG("irq rdi0 regUpdateIrq\n");
+ vfe32_process_rdi0_reg_update_irq(vfe32_ctrl);
+ break;
case VFE_IMASK_WHILE_STOPPING_1:
CDBG("irq resetAckIrq\n");
vfe32_process_reset_irq(vfe32_ctrl);
@@ -3819,6 +3953,12 @@
(void *)VFE_IRQ_STATUS0_REG_UPDATE_MASK);
if (qcmd->vfeInterruptStatus1 &
+ VFE_IRQ_STATUS1_RDI0_REG_UPDATE_MASK)
+ v4l2_subdev_notify(&axi_ctrl->subdev,
+ NOTIFY_VFE_IRQ,
+ (void *)VFE_IRQ_STATUS1_RDI0_REG_UPDATE);
+
+ if (qcmd->vfeInterruptStatus1 &
VFE_IMASK_WHILE_STOPPING_1)
v4l2_subdev_notify(&axi_ctrl->subdev,
NOTIFY_VFE_IRQ,
@@ -3954,6 +4094,17 @@
return IRQ_HANDLED;
}
+int msm_axi_subdev_isr_routine(struct v4l2_subdev *sd,
+ u32 status, bool *handled)
+{
+ struct axi_ctrl_t *axi_ctrl = v4l2_get_subdevdata(sd);
+ irqreturn_t ret;
+ pr_info("%s E ", __func__);
+ ret = vfe32_parse_irq(axi_ctrl->vfeirq->start, axi_ctrl);
+ *handled = TRUE;
+ return 0;
+}
+
static long msm_vfe_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int subdev_cmd, void *arg)
{
@@ -4247,6 +4398,7 @@
spin_lock_init(&vfe32_ctrl->state_lock);
spin_lock_init(&vfe32_ctrl->io_lock);
spin_lock_init(&vfe32_ctrl->update_ack_lock);
+ spin_lock_init(&vfe32_ctrl->start_ack_lock);
spin_lock_init(&vfe32_ctrl->aec_ack_lock);
spin_lock_init(&vfe32_ctrl->awb_ack_lock);
@@ -4329,6 +4481,11 @@
share_ctrl->outpath.out0.ch2]);
}
break;
+ case VFE_OUTPUTS_RDI0:
+ msm_camera_io_w(1, axi_ctrl->share_ctrl->vfebase +
+ vfe32_AXI_WM_CFG[axi_ctrl->
+ share_ctrl->outpath.out2.ch0]);
+ break;
default:
if (axi_ctrl->share_ctrl->outpath.output_mode &
VFE32_OUTPUT_MODE_SECONDARY) {
@@ -4430,8 +4587,8 @@
}
vfe32_config_axi(axi_ctrl, OUTPUT_PRIM, axio);
kfree(axio);
- }
break;
+ }
case CMD_AXI_CFG_PRIM_ALL_CHNLS: {
uint32_t *axio = NULL;
axio = kmalloc(vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length,
@@ -4449,8 +4606,8 @@
}
vfe32_config_axi(axi_ctrl, OUTPUT_PRIM_ALL_CHNLS, axio);
kfree(axio);
- }
break;
+ }
case CMD_AXI_CFG_PRIM|CMD_AXI_CFG_SEC: {
uint32_t *axio = NULL;
axio = kmalloc(vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length,
@@ -4468,8 +4625,8 @@
}
vfe32_config_axi(axi_ctrl, OUTPUT_PRIM|OUTPUT_SEC, axio);
kfree(axio);
- }
break;
+ }
case CMD_AXI_CFG_PRIM|CMD_AXI_CFG_SEC_ALL_CHNLS: {
uint32_t *axio = NULL;
axio = kmalloc(vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length,
@@ -4488,8 +4645,8 @@
vfe32_config_axi(axi_ctrl,
OUTPUT_PRIM|OUTPUT_SEC_ALL_CHNLS, axio);
kfree(axio);
- }
break;
+ }
case CMD_AXI_CFG_PRIM_ALL_CHNLS|CMD_AXI_CFG_SEC: {
uint32_t *axio = NULL;
axio = kmalloc(vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length,
@@ -4508,8 +4665,28 @@
vfe32_config_axi(axi_ctrl,
OUTPUT_PRIM_ALL_CHNLS|OUTPUT_SEC, axio);
kfree(axio);
- }
break;
+ }
+ case CMD_AXI_CFG_TERT1: {
+ uint32_t *axio = NULL;
+ axio = kmalloc(vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length,
+ GFP_ATOMIC);
+ if (!axio) {
+ rc = -ENOMEM;
+ break;
+ }
+
+ if (copy_from_user(axio, (void __user *)(vfecmd.value),
+ vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length)) {
+ kfree(axio);
+ rc = -EFAULT;
+ break;
+ }
+ vfe32_config_axi(axi_ctrl,
+ OUTPUT_TERT1, axio);
+ kfree(axio);
+ break;
+ }
case CMD_AXI_CFG_PRIM_ALL_CHNLS|CMD_AXI_CFG_SEC_ALL_CHNLS:
pr_err("%s Invalid/Unsupported AXI configuration %x",
__func__, cfgcmd.cmd_type);
@@ -4548,6 +4725,13 @@
CDBG("Image composite done 1 irq occured.\n");
vfe32_process_output_path_irq_1(axi_ctrl);
}
+
+ if (axi_ctrl->share_ctrl->outpath.output_mode &
+ VFE32_OUTPUT_MODE_TERTIARY1)
+ if (irqstatus & (0x1 << (axi_ctrl->share_ctrl->outpath.out2.ch0
+ + VFE_WM_OFFSET)))
+ vfe32_process_output_path_irq_rdi0(axi_ctrl);
+
/* in snapshot mode if done then send
snapshot done message */
if (axi_ctrl->share_ctrl->operation_mode ==
@@ -4653,6 +4837,7 @@
static const struct v4l2_subdev_core_ops msm_axi_subdev_core_ops = {
.ioctl = msm_axi_subdev_ioctl,
+ .interrupt_service_routine = msm_axi_subdev_isr_routine,
};
static const struct v4l2_subdev_video_ops msm_axi_subdev_video_ops = {
@@ -4672,6 +4857,9 @@
struct axi_ctrl_t *axi_ctrl;
struct vfe32_ctrl_type *vfe32_ctrl;
struct vfe_share_ctrl_t *share_ctrl;
+ struct intr_table_entry irq_req;
+ struct msm_cam_subdev_info sd_info;
+
CDBG("%s: device id = %d\n", __func__, pdev->id);
share_ctrl = kzalloc(sizeof(struct vfe_share_ctrl_t), GFP_KERNEL);
@@ -4707,7 +4895,11 @@
sizeof(axi_ctrl->subdev.name), "axi");
v4l2_set_subdevdata(&axi_ctrl->subdev, axi_ctrl);
axi_ctrl->pdev = pdev;
- msm_cam_register_subdev_node(&axi_ctrl->subdev, AXI_DEV, 0);
+
+ sd_info.sdev_type = AXI_DEV;
+ sd_info.sd_index = 0;
+ sd_info.irq_num = 0;
+ msm_cam_register_subdev_node(&axi_ctrl->subdev, &sd_info);
v4l2_subdev_init(&vfe32_ctrl->subdev, &msm_vfe_subdev_ops);
vfe32_ctrl->subdev.internal_ops = &msm_vfe_internal_ops;
@@ -4740,23 +4932,49 @@
goto vfe32_no_resource;
}
- rc = request_irq(axi_ctrl->vfeirq->start, vfe32_parse_irq,
- IRQF_TRIGGER_RISING, "vfe", axi_ctrl);
- if (rc < 0) {
- release_mem_region(axi_ctrl->vfemem->start,
- resource_size(axi_ctrl->vfemem));
- pr_err("%s: irq request fail\n", __func__);
- rc = -EBUSY;
+ /* Request for this device irq from the camera server. If the
+ * IRQ Router is present on this target, the interrupt will be
+ * handled by the camera server and the interrupt service
+ * routine called. If the request_irq call returns ENXIO, then
+ * the IRQ Router hardware is not present on this target. We
+ * have to request for the irq ourselves and register the
+ * appropriate interrupt handler. */
+ irq_req.cam_hw_idx = MSM_CAM_HW_VFE0;
+ irq_req.dev_name = "vfe";
+ irq_req.irq_idx = CAMERA_SS_IRQ_8;
+ irq_req.irq_num = axi_ctrl->vfeirq->start;
+ irq_req.is_composite = 0;
+ irq_req.irq_trigger_type = IRQF_TRIGGER_RISING;
+ irq_req.num_hwcore = 1;
+ irq_req.subdev_list[0] = &axi_ctrl->subdev;
+ irq_req.data = (void *)axi_ctrl;
+ rc = msm_cam_server_request_irq(&irq_req);
+ if (rc == -ENXIO) {
+ /* IRQ Router hardware is not present on this hardware.
+ * Request for the IRQ and register the interrupt handler. */
+ rc = request_irq(axi_ctrl->vfeirq->start, vfe32_parse_irq,
+ IRQF_TRIGGER_RISING, "vfe", axi_ctrl);
+ if (rc < 0) {
+ release_mem_region(axi_ctrl->vfemem->start,
+ resource_size(axi_ctrl->vfemem));
+ pr_err("%s: irq request fail\n", __func__);
+ rc = -EBUSY;
+ goto vfe32_no_resource;
+ }
+ disable_irq(axi_ctrl->vfeirq->start);
+ } else if (rc < 0) {
+ pr_err("%s Error registering irq ", __func__);
goto vfe32_no_resource;
}
- disable_irq(axi_ctrl->vfeirq->start);
-
tasklet_init(&axi_ctrl->vfe32_tasklet,
axi32_do_tasklet, (unsigned long)axi_ctrl);
vfe32_ctrl->pdev = pdev;
- msm_cam_register_subdev_node(&vfe32_ctrl->subdev, VFE_DEV, 0);
+ sd_info.sdev_type = VFE_DEV;
+ sd_info.sd_index = 0;
+ sd_info.irq_num = axi_ctrl->vfeirq->start;
+ msm_cam_register_subdev_node(&vfe32_ctrl->subdev, &sd_info);
return 0;
vfe32_no_resource:
diff --git a/drivers/media/video/msm/msm_vfe32.h b/drivers/media/video/msm/msm_vfe32.h
index 086600a..1746f3f 100644
--- a/drivers/media/video/msm/msm_vfe32.h
+++ b/drivers/media/video/msm/msm_vfe32.h
@@ -87,6 +87,9 @@
* the luma samples. JPEG 4:2:2 */
#define VFE_CHROMA_UPSAMPLE_INTERPOLATED 0
+/* wm bit offset for IRQ MASK and IRQ STATUS register */
+#define VFE_WM_OFFSET 6
+
/* constants for irq registers */
#define VFE_DISABLE_ALL_IRQS 0
/* bit =1 is to clear the corresponding bit in VFE_IRQ_STATUS. */
@@ -115,6 +118,17 @@
#define VFE_IRQ_STATUS0_ASYNC_TIMER2 0x40000000 /* bit 30 */
#define VFE_IRQ_STATUS0_ASYNC_TIMER3 0x80000000 /* bit 32 */
+#define VFE_IRQ_STATUS1_RDI0_REG_UPDATE_MASK 0x4000000 /*bit 26*/
+#define VFE_IRQ_STATUS1_RDI1_REG_UPDATE_MASK 0x8000000 /*bit 27*/
+
+/*TODOs the irq status passed from axi to vfe irq handler does not account
+* for 2 irq status registers. So below macro is added to differentiate between
+* same bit set on both irq status registers. This wil be fixed later by passing
+*entire payload to vfe irq handler and parsing there instead of passing just the
+*status bit*/
+#define VFE_IRQ_STATUS1_RDI0_REG_UPDATE 0x84000000 /*bit 26*/
+#define VFE_IRQ_STATUS1_RDI1_REG_UPDATE 0x88000000 /*bit 27*/
+
/* imask for while waiting for stop ack, driver has already
* requested stop, waiting for reset irq, and async timer irq.
* For irq_status_0, bit 28-32 are for async timer. For
@@ -741,7 +755,7 @@
struct vfe32_output_ch {
struct list_head free_buf_queue;
spinlock_t free_buf_lock;
- uint16_t output_fmt;
+ uint16_t image_mode;
int8_t ch0;
int8_t ch1;
int8_t ch2;
@@ -788,7 +802,7 @@
struct vfe32_output_ch out0; /* preview and thumbnail */
struct vfe32_output_ch out1; /* snapshot */
- struct vfe32_output_ch out2; /* video */
+ struct vfe32_output_ch out2; /* rdi0 */
};
struct vfe32_frame_extra {
@@ -893,6 +907,7 @@
#define VFE32_OUTPUT_MODE_PRIMARY_ALL_CHNLS BIT(7)
#define VFE32_OUTPUT_MODE_SECONDARY BIT(8)
#define VFE32_OUTPUT_MODE_SECONDARY_ALL_CHNLS BIT(9)
+#define VFE32_OUTPUT_MODE_TERTIARY1 BIT(10)
struct vfe_stats_control {
uint8_t ackPending;
@@ -946,6 +961,7 @@
uint32_t vfeImaskCompositePacked;
spinlock_t update_ack_lock;
+ spinlock_t start_ack_lock;
spinlock_t state_lock;
spinlock_t io_lock;
diff --git a/drivers/media/video/msm/msm_vfe7x27a_v4l2.c b/drivers/media/video/msm/msm_vfe7x27a_v4l2.c
index 135ad20..398621f 100644
--- a/drivers/media/video/msm/msm_vfe7x27a_v4l2.c
+++ b/drivers/media/video/msm/msm_vfe7x27a_v4l2.c
@@ -370,6 +370,7 @@
struct isp_msg_output msg;
msg.output_id = msgid;
+ msg.buf.image_mode = -1;
msg.buf.ch_paddr[0] = ch0_paddr;
msg.buf.ch_paddr[1] = ch1_paddr;
msg.frameCounter = vfe2x_ctrl->vfeFrameId;
@@ -859,6 +860,7 @@
CDBG("vfe2x_subdev_notify : msgId = %d\n", id);
rp.evt_msg.type = MSM_CAMERA_MSG;
rp.evt_msg.msg_id = path;
+ rp.evt_msg.data = NULL;
rp.type = id;
v4l2_subdev_notify(&vfe2x_ctrl->subdev, NOTIFY_VFE_BUF_EVT, &rp);
spin_unlock_irqrestore(&vfe2x_ctrl->sd_notify_lock, flags);
@@ -1264,6 +1266,16 @@
vfestopped = 1;
spin_lock_irqsave(&vfe2x_ctrl->table_lock,
flags);
+ if (op_mode & SNAPSHOT_MASK_MODE) {
+ vfe2x_ctrl->stop_pending = 0;
+ vfe2x_send_isp_msg(vfe2x_ctrl,
+ msgs_map[MSG_STOP_ACK].
+ isp_id);
+ spin_unlock_irqrestore(
+ &vfe2x_ctrl->table_lock,
+ flags);
+ return 0;
+ }
if ((!list_empty(&vfe2x_ctrl->table_q)) ||
vfe2x_ctrl->tableack_pending) {
CDBG("stop pending\n");
@@ -1783,6 +1795,8 @@
static int __devinit vfe2x_probe(struct platform_device *pdev)
{
+ struct msm_cam_subdev_info sd_info;
+
CDBG("%s: device id = %d\n", __func__, pdev->id);
vfe2x_ctrl = kzalloc(sizeof(struct vfe2x_ctrl_type), GFP_KERNEL);
if (!vfe2x_ctrl) {
@@ -1799,7 +1813,10 @@
platform_set_drvdata(pdev, &vfe2x_ctrl->subdev);
vfe2x_ctrl->pdev = pdev;
- msm_cam_register_subdev_node(&vfe2x_ctrl->subdev, VFE_DEV, 0);
+ sd_info.sdev_type = VFE_DEV;
+ sd_info.sd_index = 0;
+ sd_info.irq_num = 0;
+ msm_cam_register_subdev_node(&vfe2x_ctrl->subdev, &sd_info);
return 0;
}
diff --git a/drivers/media/video/msm/msm_vpe.c b/drivers/media/video/msm/msm_vpe.c
index 54e9582..fb22cf9c 100644
--- a/drivers/media/video/msm/msm_vpe.c
+++ b/drivers/media/video/msm/msm_vpe.c
@@ -970,6 +970,8 @@
static int __devinit msm_vpe_probe(struct platform_device *pdev)
{
int rc = 0;
+ struct msm_cam_subdev_info sd_info;
+
D("%s: device id = %d\n", __func__, pdev->id);
vpe_ctrl = kzalloc(sizeof(struct vpe_ctrl_type), GFP_KERNEL);
if (!vpe_ctrl) {
@@ -1028,7 +1030,10 @@
atomic_set(&vpe_ctrl->active, 0);
vpe_ctrl->pdev = pdev;
- msm_cam_register_subdev_node(&vpe_ctrl->subdev, VPE_DEV, pdev->id);
+ sd_info.sdev_type = VPE_DEV;
+ sd_info.sd_index = pdev->id;
+ sd_info.irq_num = vpe_ctrl->vpeirq->start;
+ msm_cam_register_subdev_node(&vpe_ctrl->subdev, &sd_info);
vpe_ctrl->subdev.entity.revision = vpe_ctrl->subdev.devnode->num;
msm_queue_init(&vpe_ctrl->eventData_q, "ackevents");
diff --git a/drivers/media/video/msm/msm_vpe1.c b/drivers/media/video/msm/msm_vpe1.c
index 4f97c43..df3630a 100644
--- a/drivers/media/video/msm/msm_vpe1.c
+++ b/drivers/media/video/msm/msm_vpe1.c
@@ -16,6 +16,7 @@
#include <mach/irqs.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include "msm_vpe1.h"
#include <linux/pm_qos.h>
#include <linux/clk.h>
diff --git a/drivers/media/video/msm/mt9d112.c b/drivers/media/video/msm/mt9d112.c
index a7b5156..4dd0285 100644
--- a/drivers/media/video/msm/mt9d112.c
+++ b/drivers/media/video/msm/mt9d112.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. 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
@@ -17,6 +17,7 @@
#include <linux/i2c.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
+#include <linux/module.h>
#include <media/msm_camera.h>
#include <mach/gpio.h>
#include "mt9d112.h"
diff --git a/drivers/media/video/msm/mt9p012.h b/drivers/media/video/msm/mt9p012.h
index 0579813..3df98b7 100644
--- a/drivers/media/video/msm/mt9p012.h
+++ b/drivers/media/video/msm/mt9p012.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-2012, Code Aurora Forum. 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
@@ -15,6 +15,7 @@
#define MT9T012_H
#include <linux/types.h>
+#include <mach/board.h>
extern struct mt9p012_reg mt9p012_regs; /* from mt9p012_reg.c */
diff --git a/drivers/media/video/msm/mt9p012_km.h b/drivers/media/video/msm/mt9p012_km.h
index aefabd4..0feb331 100644
--- a/drivers/media/video/msm/mt9p012_km.h
+++ b/drivers/media/video/msm/mt9p012_km.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-2012, Code Aurora Forum. 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
@@ -15,6 +15,7 @@
#define MT9P012_KM_H
#include <linux/types.h>
+#include <mach/board.h>
extern struct mt9p012_km_reg mt9p012_km_regs; /* from mt9p012_km_reg.c */
diff --git a/drivers/media/video/msm/mt9t013.c b/drivers/media/video/msm/mt9t013.c
index e1f6167..b4b5bdd 100644
--- a/drivers/media/video/msm/mt9t013.c
+++ b/drivers/media/video/msm/mt9t013.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-2012, Code Aurora Forum. 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,6 +18,7 @@
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <media/msm_camera.h>
#include <mach/gpio.h>
#include <mach/camera.h>
diff --git a/drivers/media/video/msm/mt9t013.h b/drivers/media/video/msm/mt9t013.h
index f6b7c28..6afcb2d 100644
--- a/drivers/media/video/msm/mt9t013.h
+++ b/drivers/media/video/msm/mt9t013.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-2012, Code Aurora Forum. 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
@@ -15,6 +15,7 @@
#define MT9T013_H
#include <linux/types.h>
+#include <mach/board.h>
extern struct mt9t013_reg mt9t013_regs; /* from mt9t013_reg.c */
diff --git a/drivers/media/video/msm/sensors/imx074_v4l2.c b/drivers/media/video/msm/sensors/imx074_v4l2.c
index 7e41418a..3d23337 100644
--- a/drivers/media/video/msm/sensors/imx074_v4l2.c
+++ b/drivers/media/video/msm/sensors/imx074_v4l2.c
@@ -277,6 +277,7 @@
.sensor_power_up = msm_sensor_power_up,
.sensor_power_down = msm_sensor_power_down,
.sensor_adjust_frame_lines = msm_sensor_adjust_frame_lines,
+ .sensor_get_csi_params = msm_sensor_get_csi_params,
};
static struct msm_sensor_reg_t imx074_regs = {
diff --git a/drivers/media/video/msm/sensors/imx091.c b/drivers/media/video/msm/sensors/imx091.c
index 70c3f6e..49442e9 100644
--- a/drivers/media/video/msm/sensors/imx091.c
+++ b/drivers/media/video/msm/sensors/imx091.c
@@ -304,6 +304,7 @@
.sensor_power_up = msm_sensor_power_up,
.sensor_power_down = msm_sensor_power_down,
.sensor_adjust_frame_lines = msm_sensor_adjust_frame_lines,
+ .sensor_get_csi_params = msm_sensor_get_csi_params,
};
static struct msm_sensor_reg_t imx091_regs = {
diff --git a/drivers/media/video/msm/sensors/msm_sensor.c b/drivers/media/video/msm/sensors/msm_sensor.c
index 76981b0..8ab3963 100644
--- a/drivers/media/video/msm/sensors/msm_sensor.c
+++ b/drivers/media/video/msm/sensors/msm_sensor.c
@@ -246,9 +246,6 @@
{
int32_t rc = 0;
- v4l2_subdev_notify(&s_ctrl->sensor_v4l2_subdev,
- NOTIFY_ISPIF_STREAM, (void *)ISPIF_STREAM(
- PIX_0, ISPIF_OFF_IMMEDIATELY));
s_ctrl->func_tbl->sensor_stop_stream(s_ctrl);
msleep(30);
if (update_type == MSM_SENSOR_REG_INIT) {
@@ -268,8 +265,6 @@
v4l2_subdev_notify(&s_ctrl->sensor_v4l2_subdev,
NOTIFY_CSID_CFG,
&s_ctrl->curr_csi_params->csid_params);
- v4l2_subdev_notify(&s_ctrl->sensor_v4l2_subdev,
- NOTIFY_CID_CHANGE, NULL);
mb();
v4l2_subdev_notify(&s_ctrl->sensor_v4l2_subdev,
NOTIFY_CSIPHY_CFG,
@@ -281,9 +276,6 @@
v4l2_subdev_notify(&s_ctrl->sensor_v4l2_subdev,
NOTIFY_PCLK_CHANGE, &s_ctrl->msm_sensor_reg->
output_settings[res].op_pixel_clk);
- v4l2_subdev_notify(&s_ctrl->sensor_v4l2_subdev,
- NOTIFY_ISPIF_STREAM, (void *)ISPIF_STREAM(
- PIX_0, ISPIF_ON_FRAME_BOUNDARY));
s_ctrl->func_tbl->sensor_start_stream(s_ctrl);
msleep(30);
}
@@ -303,7 +295,7 @@
s_ctrl->msm_sensor_reg->
output_settings[res].line_length_pclk;
- if (s_ctrl->sensordata->pdata->is_csic ||
+ if (s_ctrl->is_csic ||
!s_ctrl->sensordata->csi_if)
rc = s_ctrl->func_tbl->sensor_csi_setting(s_ctrl,
MSM_SENSOR_UPDATE_PERIODIC, res);
@@ -330,7 +322,7 @@
s_ctrl->curr_res = MSM_SENSOR_INVALID_RES;
s_ctrl->cam_mode = mode;
- if (s_ctrl->sensordata->pdata->is_csic ||
+ if (s_ctrl->is_csic ||
!s_ctrl->sensordata->csi_if)
rc = s_ctrl->func_tbl->sensor_csi_setting(s_ctrl,
MSM_SENSOR_REG_INIT, 0);
@@ -383,11 +375,32 @@
return s_ctrl->func_tbl->sensor_config(s_ctrl, argp);
case VIDIOC_MSM_SENSOR_RELEASE:
return msm_sensor_release(s_ctrl);
+ case VIDIOC_MSM_SENSOR_CSID_INFO: {
+ struct msm_sensor_csi_info *csi_info =
+ (struct msm_sensor_csi_info *)arg;
+ s_ctrl->csid_version = csi_info->csid_version;
+ s_ctrl->is_csic = csi_info->is_csic;
+ return 0;
+ }
default:
return -ENOIOCTLCMD;
}
}
+int32_t msm_sensor_get_csi_params(struct msm_sensor_ctrl_t *s_ctrl,
+ struct csi_lane_params_t *sensor_output_info)
+{
+ sensor_output_info->csi_lane_assign = s_ctrl->sensordata->
+ sensor_platform_info->csi_lane_params->csi_lane_assign;
+ sensor_output_info->csi_lane_mask = s_ctrl->sensordata->
+ sensor_platform_info->csi_lane_params->csi_lane_mask;
+ sensor_output_info->csi_if = s_ctrl->sensordata->csi_if;
+ sensor_output_info->csid_core = s_ctrl->sensordata->
+ pdata[0].csid_core;
+ sensor_output_info->csid_version = s_ctrl->csid_version;
+ return 0;
+}
+
int32_t msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp)
{
struct sensor_cfg_data cdata;
@@ -489,6 +502,37 @@
rc = -EFAULT;
break;
+ case CFG_START_STREAM:
+ if (s_ctrl->func_tbl->sensor_start_stream == NULL) {
+ rc = -EFAULT;
+ break;
+ }
+ s_ctrl->func_tbl->sensor_start_stream(s_ctrl);
+ break;
+
+ case CFG_STOP_STREAM:
+ if (s_ctrl->func_tbl->sensor_stop_stream == NULL) {
+ rc = -EFAULT;
+ break;
+ }
+ s_ctrl->func_tbl->sensor_stop_stream(s_ctrl);
+ break;
+
+ case CFG_GET_CSI_PARAMS:
+ if (s_ctrl->func_tbl->sensor_get_csi_params == NULL) {
+ rc = -EFAULT;
+ break;
+ }
+ rc = s_ctrl->func_tbl->sensor_get_csi_params(
+ s_ctrl,
+ &cdata.cfg.csi_lane_params);
+
+ if (copy_to_user((void *)argp,
+ &cdata,
+ sizeof(struct sensor_cfg_data)))
+ rc = -EFAULT;
+ break;
+
default:
rc = -EFAULT;
break;
diff --git a/drivers/media/video/msm/sensors/msm_sensor.h b/drivers/media/video/msm/sensors/msm_sensor.h
index 7ade827..b1e584d 100644
--- a/drivers/media/video/msm/sensors/msm_sensor.h
+++ b/drivers/media/video/msm/sensors/msm_sensor.h
@@ -137,6 +137,13 @@
int32_t (*sensor_match_id)(struct msm_sensor_ctrl_t *s_ctrl);
int (*sensor_adjust_frame_lines)
(struct msm_sensor_ctrl_t *s_ctrl, uint16_t res);
+ int32_t (*sensor_get_csi_params)(struct msm_sensor_ctrl_t *,
+ struct csi_lane_params_t *);
+};
+
+struct msm_sensor_csi_info {
+ uint32_t csid_version;
+ uint8_t is_csic;
};
struct msm_sensor_ctrl_t {
@@ -152,6 +159,8 @@
struct msm_sensor_reg_t *msm_sensor_reg;
struct msm_sensor_v4l2_ctrl_info_t *msm_sensor_v4l2_ctrl_info;
uint16_t num_v4l2_ctrl;
+ uint32_t csid_version;
+ uint8_t is_csic;
uint16_t curr_line_length_pclk;
uint16_t curr_frame_length_lines;
@@ -241,11 +250,10 @@
long msm_sensor_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg);
-struct msm_sensor_ctrl_t *get_sctrl(struct v4l2_subdev *sd);
+int32_t msm_sensor_get_csi_params(struct msm_sensor_ctrl_t *s_ctrl,
+ struct csi_lane_params_t *sensor_output_info);
-#if defined(CONFIG_OV5647)
- extern int lcd_camera_power_onoff(int on);
-#endif
+struct msm_sensor_ctrl_t *get_sctrl(struct v4l2_subdev *sd);
#define VIDIOC_MSM_SENSOR_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE + 10, void __user *)
@@ -253,4 +261,7 @@
#define VIDIOC_MSM_SENSOR_RELEASE \
_IO('V', BASE_VIDIOC_PRIVATE + 11)
+#define VIDIOC_MSM_SENSOR_CSID_INFO\
+ _IOWR('V', BASE_VIDIOC_PRIVATE + 12, struct msm_sensor_csi_info *)
+
#endif
diff --git a/drivers/media/video/msm/sensors/mt9e013_v4l2.c b/drivers/media/video/msm/sensors/mt9e013_v4l2.c
index e6e2d52..69a5498 100644
--- a/drivers/media/video/msm/sensors/mt9e013_v4l2.c
+++ b/drivers/media/video/msm/sensors/mt9e013_v4l2.c
@@ -472,6 +472,7 @@
.sensor_config = msm_sensor_config,
.sensor_power_up = msm_sensor_power_up,
.sensor_power_down = msm_sensor_power_down,
+ .sensor_get_csi_params = msm_sensor_get_csi_params,
};
static struct msm_sensor_reg_t mt9e013_regs = {
diff --git a/drivers/media/video/msm/sensors/mt9m114_v4l2.c b/drivers/media/video/msm/sensors/mt9m114_v4l2.c
index 2184806..806bcc2 100644
--- a/drivers/media/video/msm/sensors/mt9m114_v4l2.c
+++ b/drivers/media/video/msm/sensors/mt9m114_v4l2.c
@@ -1268,6 +1268,7 @@
.sensor_config = msm_sensor_config,
.sensor_power_up = msm_sensor_power_up,
.sensor_power_down = msm_sensor_power_down,
+ .sensor_get_csi_params = msm_sensor_get_csi_params,
};
static struct msm_sensor_reg_t mt9m114_regs = {
diff --git a/drivers/media/video/msm/sensors/ov2720.c b/drivers/media/video/msm/sensors/ov2720.c
index 40867fb..03f1af1 100644
--- a/drivers/media/video/msm/sensors/ov2720.c
+++ b/drivers/media/video/msm/sensors/ov2720.c
@@ -784,6 +784,7 @@
.sensor_power_up = msm_sensor_power_up,
.sensor_power_down = msm_sensor_power_down,
.sensor_adjust_frame_lines = msm_sensor_adjust_frame_lines,
+ .sensor_get_csi_params = msm_sensor_get_csi_params,
};
static struct msm_sensor_reg_t ov2720_regs = {
diff --git a/drivers/media/video/msm/sensors/ov5647_v4l2.c b/drivers/media/video/msm/sensors/ov5647_v4l2.c
index 9dfbf8d..d192563 100644
--- a/drivers/media/video/msm/sensors/ov5647_v4l2.c
+++ b/drivers/media/video/msm/sensors/ov5647_v4l2.c
@@ -643,8 +643,6 @@
}
s_ctrl = client->dev.platform_data;
- if (s_ctrl->sensordata->pmic_gpio_enable)
- lcd_camera_power_onoff(0);
return rc;
}
@@ -716,11 +714,6 @@
gpio_direction_output(info->sensor_pwd, 1);
gpio_direction_output(info->sensor_reset, 0);
usleep_range(10000, 11000);
- if (info->pmic_gpio_enable) {
- info->pmic_gpio_enable = 0;
- lcd_camera_power_onoff(1);
- }
- usleep_range(10000, 11000);
rc = msm_sensor_power_up(s_ctrl);
if (rc < 0) {
CDBG("%s: msm_sensor_power_up failed\n", __func__);
@@ -815,6 +808,7 @@
.sensor_config = msm_sensor_config,
.sensor_power_up = ov5647_sensor_power_up,
.sensor_power_down = ov5647_sensor_power_down,
+ .sensor_get_csi_params = msm_sensor_get_csi_params,
};
static struct msm_sensor_reg_t ov5647_regs = {
diff --git a/drivers/media/video/msm/sensors/ov7692_v4l2.c b/drivers/media/video/msm/sensors/ov7692_v4l2.c
index 8c94e3b..6fc1da1 100644
--- a/drivers/media/video/msm/sensors/ov7692_v4l2.c
+++ b/drivers/media/video/msm/sensors/ov7692_v4l2.c
@@ -585,7 +585,8 @@
static struct msm_camera_i2c_reg_conf ov7692_wb_oem[][4] = {
{{-1, -1, -1}, {-1, -1, -1}, {-1, -1, -1},
{-1, -1, -1},},/*WHITEBALNACE OFF*/
- {{0x13, 0xf7}, {0x15, 0x00},}, /*WHITEBALNACE AUTO*/
+ {{0x13, 0xf7}, {0x15, 0x00}, {-1, -1, -1},
+ {-1, -1, -1},}, /*WHITEBALNACE AUTO*/
{{0x13, 0xf5}, {0x01, 0x56}, {0x02, 0x50},
{0x15, 0x00},}, /*WHITEBALNACE CUSTOM*/
{{0x13, 0xf5}, {0x01, 0x66}, {0x02, 0x40},
@@ -861,44 +862,6 @@
.video = &ov7692_subdev_video_ops,
};
-int32_t ov7692_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl)
-{
- int32_t rc = 0;
- struct msm_camera_sensor_info *info = NULL;
-
- info = s_ctrl->sensordata;
- if (info->pmic_gpio_enable) {
- info->sensor_lcd_gpio_onoff(1);
- usleep_range(5000, 5100);
- }
-
- rc = msm_sensor_power_up(s_ctrl);
- if (rc < 0) {
- CDBG("%s: msm_sensor_power_up failed\n", __func__);
- return rc;
- }
-
- return rc;
-}
-
-int32_t ov7692_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl)
-{
- int32_t rc = 0;
- struct msm_camera_sensor_info *info = NULL;
-
- rc = msm_sensor_power_down(s_ctrl);
- if (rc < 0)
- CDBG("%s: msm_sensor_power_down failed\n", __func__);
-
- info = s_ctrl->sensordata;
- if (info->pmic_gpio_enable) {
- info->pmic_gpio_enable = 0;
- info->sensor_lcd_gpio_onoff(0);
- usleep_range(5000, 5100);
- }
- return rc;
-}
-
static struct msm_sensor_fn_t ov7692_func_tbl = {
.sensor_start_stream = msm_sensor_start_stream,
.sensor_stop_stream = msm_sensor_stop_stream,
@@ -907,8 +870,9 @@
.sensor_mode_init = msm_sensor_mode_init,
.sensor_get_output_info = msm_sensor_get_output_info,
.sensor_config = msm_sensor_config,
- .sensor_power_up = ov7692_sensor_power_up,
- .sensor_power_down = ov7692_sensor_power_down,
+ .sensor_power_up = msm_sensor_power_up,
+ .sensor_power_down = msm_sensor_power_down,
+ .sensor_get_csi_params = msm_sensor_get_csi_params,
};
static struct msm_sensor_reg_t ov7692_regs = {
diff --git a/drivers/media/video/msm/sensors/ov8825_v4l2.c b/drivers/media/video/msm/sensors/ov8825_v4l2.c
index bb846e9..9f09208 100644
--- a/drivers/media/video/msm/sensors/ov8825_v4l2.c
+++ b/drivers/media/video/msm/sensors/ov8825_v4l2.c
@@ -808,33 +808,12 @@
{ }
};
-int32_t ov8825_sensor_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- int32_t rc = 0;
- struct msm_sensor_ctrl_t *s_ctrl;
-
- CDBG("\n in ov8825_sensor_i2c_probe\n");
- rc = msm_sensor_i2c_probe(client, id);
- if (client->dev.platform_data == NULL) {
- pr_err("%s: NULL sensor data\n", __func__);
- return -EFAULT;
- }
- s_ctrl = client->dev.platform_data;
- if (s_ctrl->sensordata->pmic_gpio_enable)
- lcd_camera_power_onoff(0);
- return rc;
-}
-
int32_t ov8825_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl)
{
int32_t rc = 0;
struct msm_camera_sensor_info *info = NULL;
+
info = s_ctrl->sensordata;
- if (info->pmic_gpio_enable) {
- info->pmic_gpio_enable = 0;
- lcd_camera_power_onoff(1);
- }
gpio_direction_output(info->sensor_pwd, 0);
gpio_direction_output(info->sensor_reset, 0);
usleep_range(10000, 11000);
@@ -853,7 +832,7 @@
static struct i2c_driver ov8825_i2c_driver = {
.id_table = ov8825_i2c_id,
- .probe = ov8825_sensor_i2c_probe,
+ .probe = msm_sensor_i2c_probe,
.driver = {
.name = SENSOR_NAME,
},
diff --git a/drivers/media/video/msm/sensors/ov9726_v4l2.c b/drivers/media/video/msm/sensors/ov9726_v4l2.c
index 61c693e..27a8b38 100644
--- a/drivers/media/video/msm/sensors/ov9726_v4l2.c
+++ b/drivers/media/video/msm/sensors/ov9726_v4l2.c
@@ -236,6 +236,7 @@
.sensor_config = msm_sensor_config,
.sensor_power_up = msm_sensor_power_up,
.sensor_power_down = msm_sensor_power_down,
+ .sensor_get_csi_params = msm_sensor_get_csi_params,
};
static struct msm_sensor_reg_t ov9726_regs = {
diff --git a/drivers/media/video/msm/sensors/s5k3l1yx.c b/drivers/media/video/msm/sensors/s5k3l1yx.c
index 1d7da0d..d7aeb74 100644
--- a/drivers/media/video/msm/sensors/s5k3l1yx.c
+++ b/drivers/media/video/msm/sensors/s5k3l1yx.c
@@ -653,6 +653,7 @@
.sensor_power_up = msm_sensor_power_up,
.sensor_power_down = msm_sensor_power_down,
.sensor_adjust_frame_lines = msm_sensor_adjust_frame_lines,
+ .sensor_get_csi_params = msm_sensor_get_csi_params,
};
static struct msm_sensor_reg_t s5k3l1yx_regs = {
diff --git a/drivers/media/video/msm/sensors/s5k4e1_v4l2.c b/drivers/media/video/msm/sensors/s5k4e1_v4l2.c
index 2d25824..e90390e2 100644
--- a/drivers/media/video/msm/sensors/s5k4e1_v4l2.c
+++ b/drivers/media/video/msm/sensors/s5k4e1_v4l2.c
@@ -487,6 +487,7 @@
.sensor_config = msm_sensor_config,
.sensor_power_up = msm_sensor_power_up,
.sensor_power_down = msm_sensor_power_down,
+ .sensor_get_csi_params = msm_sensor_get_csi_params,
};
static struct msm_sensor_reg_t s5k4e1_regs = {
diff --git a/drivers/media/video/msm/server/msm_cam_server.c b/drivers/media/video/msm/server/msm_cam_server.c
index cb59737d..dfa7fbe 100644
--- a/drivers/media/video/msm/server/msm_cam_server.c
+++ b/drivers/media/video/msm/server/msm_cam_server.c
@@ -1365,9 +1365,7 @@
struct msm_camera_device_platform_data *camdev;
uint8_t csid_core = 0;
- if (notification == NOTIFY_CID_CHANGE ||
- notification == NOTIFY_ISPIF_STREAM ||
- notification == NOTIFY_PCLK_CHANGE ||
+ if (notification == NOTIFY_PCLK_CHANGE ||
notification == NOTIFY_CSIPHY_CFG ||
notification == NOTIFY_CSID_CFG ||
notification == NOTIFY_CSIC_CFG) {
@@ -1378,30 +1376,6 @@
}
switch (notification) {
- case NOTIFY_CID_CHANGE:
- /* reconfig the ISPIF*/
- if (g_server_dev.ispif_device) {
- struct msm_ispif_params_list ispif_params;
- ispif_params.len = 1;
- ispif_params.params[0].intftype = PIX0;
- ispif_params.params[0].cid_mask = 0x0001;
- ispif_params.params[0].csid = csid_core;
-
- rc = v4l2_subdev_call(
- g_server_dev.ispif_device, core, ioctl,
- VIDIOC_MSM_ISPIF_CFG, &ispif_params);
- if (rc < 0)
- return;
- }
- break;
- case NOTIFY_ISPIF_STREAM:
- /* call ISPIF stream on/off */
- rc = v4l2_subdev_call(g_server_dev.ispif_device, video,
- s_stream, (int)arg);
- if (rc < 0)
- return;
-
- break;
case NOTIFY_ISP_MSG_EVT:
case NOTIFY_VFE_MSG_OUT:
case NOTIFY_VFE_MSG_STATS:
@@ -1461,20 +1435,379 @@
}
return;
-}
-
-void msm_cam_release_subdev_node(struct video_device *vdev)
-{
- struct v4l2_subdev *sd = video_get_drvdata(vdev);
- sd->devnode = NULL;
- kfree(vdev);
+}
+
+void msm_cam_release_subdev_node(struct video_device *vdev)
+{
+ struct v4l2_subdev *sd = video_get_drvdata(vdev);
+ sd->devnode = NULL;
+ kfree(vdev);
+}
+
+/* Helper function to get the irq_idx corresponding
+ * to the irq_num. */
+int get_irq_idx_from_irq_num(int irq_num)
+{
+ int i;
+ for (i = 0; i < CAMERA_SS_IRQ_MAX; i++)
+ if (irq_num == g_server_dev.hw_irqmap[i].irq_num)
+ return g_server_dev.hw_irqmap[i].irq_idx;
+
+ return -EINVAL;
+}
+
+static irqreturn_t msm_camera_server_parse_irq(int irq_num, void *data)
+{
+ unsigned long flags;
+ int irq_idx, i, rc;
+ u32 status = 0;
+ struct intr_table_entry *ind_irq_tbl;
+ struct intr_table_entry *comp_irq_tbl;
+ bool subdev_handled = 0;
+
+ irq_idx = get_irq_idx_from_irq_num(irq_num);
+ if (irq_idx < 0) {
+ pr_err("server_parse_irq: no clients for irq #%d. returning ",
+ irq_num);
+ return IRQ_HANDLED;
+ }
+
+ spin_lock_irqsave(&g_server_dev.intr_table_lock, flags);
+ ind_irq_tbl = &g_server_dev.irq_lkup_table.ind_intr_tbl[0];
+ comp_irq_tbl = &g_server_dev.irq_lkup_table.comp_intr_tbl[0];
+ if (ind_irq_tbl[irq_idx].is_composite) {
+ for (i = 0; i < comp_irq_tbl[irq_idx].num_hwcore; i++) {
+ if (comp_irq_tbl[irq_idx].subdev_list[i]) {
+ rc = v4l2_subdev_call(
+ comp_irq_tbl[irq_idx].subdev_list[i],
+ core, interrupt_service_routine,
+ status, &subdev_handled);
+ if ((rc < 0) || !subdev_handled) {
+ pr_err("server_parse_irq:Error\n"
+ "handling irq %d rc = %d",
+ irq_num, rc);
+ /* Dispatch the irq to the remaining
+ * subdevs in the list. */
+ continue;
+ }
+ }
+ }
+ } else {
+ rc = v4l2_subdev_call(ind_irq_tbl[irq_idx].subdev_list[0],
+ core, interrupt_service_routine,
+ status, &subdev_handled);
+ if ((rc < 0) || !subdev_handled) {
+ pr_err("server_parse_irq: Error handling irq %d rc = %d",
+ irq_num, rc);
+ spin_unlock_irqrestore(&g_server_dev.intr_table_lock,
+ flags);
+ return IRQ_HANDLED;
+ }
+ }
+ spin_unlock_irqrestore(&g_server_dev.intr_table_lock, flags);
+ return IRQ_HANDLED;
+}
+
+/* Helper function to get the irq_idx corresponding
+ * to the camera hwcore. This function should _only_
+ * be invoked when the IRQ Router is configured
+ * non-composite mode. */
+int get_irq_idx_from_camhw_idx(int cam_hw_idx)
+{
+ int i;
+ for (i = 0; i < MSM_CAM_HW_MAX; i++)
+ if (cam_hw_idx == g_server_dev.hw_irqmap[i].cam_hw_idx)
+ return g_server_dev.hw_irqmap[i].irq_idx;
+
+ return -EINVAL;
+}
+
+static inline void update_compirq_subdev_info(
+ struct intr_table_entry *irq_entry,
+ uint32_t cam_hw_mask, uint8_t cam_hw_id,
+ int *num_hwcore)
+{
+ if (cam_hw_mask & (0x1 << cam_hw_id)) {
+ /* If the mask has been set for this cam hwcore
+ * update the subdev ptr......*/
+ irq_entry->subdev_list[cam_hw_id] =
+ g_server_dev.subdev_table[cam_hw_id];
+ (*num_hwcore)++;
+ } else {
+ /*....else, just clear it, so that the irq will
+ * not be dispatched to this hw. */
+ irq_entry->subdev_list[cam_hw_id] = NULL;
+ }
+}
+
+static int msm_server_update_composite_irq_info(
+ struct intr_table_entry *irq_req)
+{
+ int num_hwcore = 0, rc = 0;
+ struct intr_table_entry *comp_irq_tbl =
+ &g_server_dev.irq_lkup_table.comp_intr_tbl[0];
+
+ comp_irq_tbl[irq_req->irq_idx].is_composite = 1;
+ comp_irq_tbl[irq_req->irq_idx].irq_trigger_type =
+ irq_req->irq_trigger_type;
+ comp_irq_tbl[irq_req->irq_idx].num_hwcore = irq_req->num_hwcore;
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_MICRO, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_CCI, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_CSI0, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_CSI1, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_CSI2, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_CSI3, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_ISPIF, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_CPP, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_VFE0, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_VFE1, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_JPEG0, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_JPEG1, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_JPEG2, &num_hwcore);
+
+ if (num_hwcore != irq_req->num_hwcore) {
+ pr_warn("%s Mismatch!! requested cam hwcores: %d, Mask set %d",
+ __func__, irq_req->num_hwcore, num_hwcore);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+int msm_cam_server_request_irq(void *arg)
+{
+ unsigned long flags;
+ int rc = 0;
+ struct intr_table_entry *irq_req = (struct intr_table_entry *)arg;
+ struct intr_table_entry *ind_irq_tbl =
+ &g_server_dev.irq_lkup_table.ind_intr_tbl[0];
+ struct intr_table_entry *comp_irq_tbl =
+ &g_server_dev.irq_lkup_table.comp_intr_tbl[0];
+
+ if (!irq_req || !irq_req->irq_num || !irq_req->num_hwcore) {
+ pr_err("%s Invalid input ", __func__);
+ return -EINVAL;
+ }
+
+ if (!g_server_dev.irqr_device) {
+ /* This either means, the current target does not
+ * have a IRQ Router hw or the IRQ Router device is
+ * not probed yet. The latter should not happen.
+ * In any case, just return back without updating
+ * the interrupt lookup table. */
+ pr_info("%s IRQ Router hw is not present. ", __func__);
+ return -ENXIO;
+ }
+
+ if (irq_req->is_composite) {
+ if (irq_req->irq_idx >= CAMERA_SS_IRQ_0 &&
+ irq_req->irq_idx < CAMERA_SS_IRQ_MAX) {
+ spin_lock_irqsave(&g_server_dev.intr_table_lock, flags);
+ /* Update the composite irq information into
+ * the composite irq lookup table.... */
+ if (msm_server_update_composite_irq_info(irq_req)) {
+ pr_err("%s Invalid configuration", __func__);
+ spin_unlock_irqrestore(
+ &g_server_dev.intr_table_lock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&g_server_dev.intr_table_lock,
+ flags);
+ /*...and then update the corresponding entry
+ * in the individual irq lookup table to indicate
+ * that this IRQ is a composite irq and needs to be
+ * sent to multiple subdevs. */
+ ind_irq_tbl[irq_req->irq_idx].is_composite = 1;
+ rc = request_irq(comp_irq_tbl[irq_req->irq_idx].irq_num,
+ msm_camera_server_parse_irq,
+ irq_req->irq_trigger_type,
+ ind_irq_tbl[irq_req->irq_idx].dev_name,
+ ind_irq_tbl[irq_req->irq_idx].data);
+ if (rc < 0) {
+ pr_err("%s: request_irq failed for %s\n",
+ __func__, irq_req->dev_name);
+ return -EBUSY;
+ }
+ } else {
+ pr_err("%s Invalid irq_idx %d ",
+ __func__, irq_req->irq_idx);
+ return -EINVAL;
+ }
+ } else {
+ if (irq_req->cam_hw_idx >= MSM_CAM_HW_MICRO &&
+ irq_req->cam_hw_idx < MSM_CAM_HW_MAX) {
+ /* Update the irq information into
+ * the individual irq lookup table.... */
+ irq_req->irq_idx =
+ get_irq_idx_from_camhw_idx(irq_req->cam_hw_idx);
+ if (irq_req->cam_hw_idx < 0) {
+ pr_err("%s Invalid hw index %d ", __func__,
+ irq_req->cam_hw_idx);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&g_server_dev.intr_table_lock, flags);
+ /* Make sure the composite irq is not configured for
+ * this IRQ already. */
+ BUG_ON(ind_irq_tbl[irq_req->irq_idx].is_composite);
+
+ ind_irq_tbl[irq_req->irq_idx] = *irq_req;
+ /* irq_num is stored inside the server's hw_irqmap
+ * during the device subdevice registration. */
+ ind_irq_tbl[irq_req->irq_idx].irq_num =
+ g_server_dev.hw_irqmap[irq_req->irq_idx].irq_num;
+
+ /*...and clear the corresponding entry in the
+ * compsoite irq lookup table to indicate that this
+ * IRQ will only be dispatched to single subdev. */
+ memset(&comp_irq_tbl[irq_req->irq_idx], 0,
+ sizeof(struct intr_table_entry));
+ D("%s Saving Entry %d %d %d %p",
+ __func__,
+ ind_irq_tbl[irq_req->cam_hw_idx].irq_num,
+ ind_irq_tbl[irq_req->cam_hw_idx].cam_hw_idx,
+ ind_irq_tbl[irq_req->cam_hw_idx].is_composite,
+ ind_irq_tbl[irq_req->cam_hw_idx].subdev_list[0]);
+
+ spin_unlock_irqrestore(&g_server_dev.intr_table_lock,
+ flags);
+
+ rc = request_irq(ind_irq_tbl[irq_req->irq_idx].irq_num,
+ msm_camera_server_parse_irq,
+ irq_req->irq_trigger_type,
+ ind_irq_tbl[irq_req->irq_idx].dev_name,
+ ind_irq_tbl[irq_req->irq_idx].data);
+ if (rc < 0) {
+ pr_err("%s: request_irq failed for %s\n",
+ __func__, irq_req->dev_name);
+ return -EBUSY;
+ }
+ } else {
+ pr_err("%s Invalid hw index %d ", __func__,
+ irq_req->cam_hw_idx);
+ return -EINVAL;
+ }
+ }
+ D("%s Successfully requested for IRQ for device %s ", __func__,
+ irq_req->dev_name);
+ return rc;
+}
+
+int msm_cam_server_update_irqmap(
+ struct msm_cam_server_irqmap_entry *irqmap_entry)
+{
+ if (!irqmap_entry || (irqmap_entry->irq_idx < CAMERA_SS_IRQ_0 ||
+ irqmap_entry->irq_idx >= CAMERA_SS_IRQ_MAX)) {
+ pr_err("%s Invalid irqmap entry ", __func__);
+ return -EINVAL;
+ }
+ g_server_dev.hw_irqmap[irqmap_entry->irq_idx] = *irqmap_entry;
+ return 0;
+}
+
+static int msm_cam_server_register_subdev(struct v4l2_device *v4l2_dev,
+ struct v4l2_subdev *sd)
+{
+ int rc = 0;
+ struct video_device *vdev;
+
+ if (v4l2_dev == NULL || sd == NULL || !sd->name[0]) {
+ pr_err("%s Invalid input ", __func__);
+ return -EINVAL;
+ }
+
+ rc = v4l2_device_register_subdev(v4l2_dev, sd);
+ if (rc < 0) {
+ pr_err("%s v4l2 subdev register failed for %s ret = %d",
+ __func__, sd->name, rc);
+ return rc;
+ }
+
+ /* Register a device node for every subdev marked with the
+ * V4L2_SUBDEV_FL_HAS_DEVNODE flag.
+ */
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
+ return rc;
+
+ vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+ if (!vdev) {
+ pr_err("%s Not enough memory ", __func__);
+ rc = -ENOMEM;
+ goto clean_up;
+ }
+
+ video_set_drvdata(vdev, sd);
+ strlcpy(vdev->name, sd->name, sizeof(vdev->name));
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->fops = &v4l2_subdev_fops;
+ vdev->release = msm_cam_release_subdev_node;
+ rc = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
+ sd->owner);
+ if (rc < 0) {
+ pr_err("%s Error registering video device %s", __func__,
+ sd->name);
+ kfree(vdev);
+ goto clean_up;
+ }
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ sd->entity.info.v4l.major = VIDEO_MAJOR;
+ sd->entity.info.v4l.minor = vdev->minor;
+#endif
+ sd->devnode = vdev;
+ return 0;
+
+clean_up:
+ if (sd->devnode)
+ video_unregister_device(sd->devnode);
+ return rc;
+}
+
+static int msm_cam_server_fill_sdev_irqnum(int cam_hw_idx,
+ int irq_num)
+{
+ int rc = 0, irq_idx;
+ irq_idx = get_irq_idx_from_camhw_idx(cam_hw_idx);
+ if (irq_idx < 0) {
+ pr_err("%s Invalid cam_hw_idx %d ", __func__, cam_hw_idx);
+ rc = -EINVAL;
+ } else {
+ g_server_dev.hw_irqmap[irq_idx].irq_num = irq_num;
+ }
+ return rc;
}
int msm_cam_register_subdev_node(struct v4l2_subdev *sd,
- enum msm_cam_subdev_type sdev_type, uint8_t index)
+ struct msm_cam_subdev_info *sd_info)
{
- struct video_device *vdev;
- int err = 0;
+ int err = 0, cam_hw_idx;
+ uint8_t sdev_type, index;
+
+ sdev_type = sd_info->sdev_type;
+ index = sd_info->sd_index;
switch (sdev_type) {
case CSIPHY_DEV:
@@ -1492,7 +1825,13 @@
err = -EINVAL;
break;
}
+ cam_hw_idx = MSM_CAM_HW_CSI0 + index;
g_server_dev.csid_device[index] = sd;
+ if (g_server_dev.irqr_device) {
+ g_server_dev.subdev_table[cam_hw_idx] = sd;
+ err = msm_cam_server_fill_sdev_irqnum(cam_hw_idx,
+ sd_info->irq_num);
+ }
break;
case CSIC_DEV:
@@ -1506,6 +1845,11 @@
case ISPIF_DEV:
g_server_dev.ispif_device = sd;
+ if (g_server_dev.irqr_device) {
+ g_server_dev.subdev_table[cam_hw_idx] = sd;
+ err = msm_cam_server_fill_sdev_irqnum(MSM_CAM_HW_ISPIF,
+ sd_info->irq_num);
+ }
break;
case VFE_DEV:
@@ -1514,7 +1858,13 @@
err = -EINVAL;
break;
}
+ cam_hw_idx = MSM_CAM_HW_VFE0 + index;
g_server_dev.vfe_device[index] = sd;
+ if (g_server_dev.irqr_device) {
+ g_server_dev.subdev_table[cam_hw_idx] = sd;
+ err = msm_cam_server_fill_sdev_irqnum(cam_hw_idx,
+ sd_info->irq_num);
+ }
break;
case VPE_DEV:
@@ -1538,6 +1888,9 @@
case GESTURE_DEV:
g_server_dev.gesture_device = sd;
break;
+ case IRQ_ROUTER_DEV:
+ g_server_dev.irqr_device = sd;
+ break;
default:
break;
}
@@ -1545,49 +1898,11 @@
if (err < 0)
return err;
- err = v4l2_device_register_subdev(&g_server_dev.v4l2_dev, sd);
- if (err < 0) {
- pr_err("%s v4l2 subdev register failed for %d ret = %d",
- __func__, sdev_type, err);
- return err;
- }
-
- /* Register a device node for every subdev marked with the
- * V4L2_SUBDEV_FL_HAS_DEVNODE flag.
- */
- if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
- return err;
-
- vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
- if (!vdev) {
- err = -ENOMEM;
- goto clean_up;
- }
-
- video_set_drvdata(vdev, sd);
- strlcpy(vdev->name, sd->name, sizeof(vdev->name));
- vdev->v4l2_dev = &g_server_dev.v4l2_dev;
- vdev->fops = &v4l2_subdev_fops;
- vdev->release = msm_cam_release_subdev_node;
- err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
- sd->owner);
- if (err < 0) {
- kfree(vdev);
- goto clean_up;
- }
-#if defined(CONFIG_MEDIA_CONTROLLER)
- sd->entity.info.v4l.major = VIDEO_MAJOR;
- sd->entity.info.v4l.minor = vdev->minor;
-#endif
- sd->devnode = vdev;
- return 0;
-
-clean_up:
- if (sd->devnode)
- video_unregister_device(sd->devnode);
- return err;
+ err = msm_cam_server_register_subdev(&g_server_dev.v4l2_dev, sd);
+ return err;
}
+
static int msm_setup_server_dev(struct platform_device *pdev)
{
int rc = -ENODEV, i;
@@ -1630,6 +1945,9 @@
mutex_init(&g_server_dev.server_lock);
mutex_init(&g_server_dev.server_queue_lock);
+ spin_lock_init(&g_server_dev.intr_table_lock);
+ memset(&g_server_dev.irq_lkup_table, 0,
+ sizeof(struct irqmgr_intr_lkup_table));
g_server_dev.pcam_active = NULL;
g_server_dev.camera_info.num_cameras = 0;
atomic_set(&g_server_dev.number_pcam_active, 0);
@@ -1786,16 +2104,20 @@
struct v4l2_event v4l2_evt;
struct msm_isp_event_ctrl *isp_event;
- isp_event = kzalloc(sizeof(struct msm_isp_event_ctrl), GFP_KERNEL);
- if (!isp_event) {
- pr_err("%s Insufficient memory. return", __func__);
- return -ENOMEM;
- }
+ void *ctrlcmd_data;
+
event_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
if (!event_qcmd) {
pr_err("%s Insufficient memory. return", __func__);
- kfree(isp_event);
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto event_qcmd_alloc_fail;
+ }
+
+ isp_event = kzalloc(sizeof(struct msm_isp_event_ctrl), GFP_KERNEL);
+ if (!isp_event) {
+ pr_err("%s Insufficient memory. return", __func__);
+ rc = -ENOMEM;
+ goto isp_event_alloc_fail;
}
D("%s\n", __func__);
@@ -1808,12 +2130,23 @@
server_dev->server_queue[out->queue_idx].evt_id =
server_dev->server_evt_id;
v4l2_evt.type = V4L2_EVENT_PRIVATE_START + ctrl_id;
+ v4l2_evt.id = 0;
v4l2_evt.u.data[0] = out->queue_idx;
/* setup event object to transfer the command; */
isp_event->resptype = MSM_CAM_RESP_V4L2;
isp_event->isp_data.ctrl = *out;
isp_event->isp_data.ctrl.evt_id = server_dev->server_evt_id;
+ if (out->value != NULL && out->length != 0) {
+ ctrlcmd_data = kzalloc(out->length, GFP_KERNEL);
+ if (!ctrlcmd_data) {
+ rc = -ENOMEM;
+ goto ctrlcmd_alloc_fail;
+ }
+ memcpy(ctrlcmd_data, out->value, out->length);
+ isp_event->isp_data.ctrl.value = ctrlcmd_data;
+ }
+
atomic_set(&event_qcmd->on_heap, 1);
event_qcmd->command = isp_event;
@@ -1837,7 +2170,8 @@
if (!rc)
rc = -ETIMEDOUT;
if (rc < 0) {
- kfree(isp_event);
+ if (++server_dev->server_evt_id == 0)
+ server_dev->server_evt_id++;
pr_err("%s: wait_event error %d\n", __func__, rc);
return rc;
}
@@ -1858,7 +2192,6 @@
kfree(ctrlcmd);
free_qcmd(rcmd);
- kfree(isp_event);
D("%s: rc %d\n", __func__, rc);
/* rc is the time elapsed. */
if (rc >= 0) {
@@ -1871,6 +2204,13 @@
rc = -EINVAL;
}
return rc;
+
+ctrlcmd_alloc_fail:
+ kfree(isp_event);
+isp_event_alloc_fail:
+ kfree(event_qcmd);
+event_qcmd_alloc_fail:
+ return rc;
}
int msm_server_close_client(int idx)
diff --git a/drivers/media/video/msm/server/msm_cam_server.h b/drivers/media/video/msm/server/msm_cam_server.h
index 677feaa..8a02d32 100644
--- a/drivers/media/video/msm/server/msm_cam_server.h
+++ b/drivers/media/video/msm/server/msm_cam_server.h
@@ -61,5 +61,7 @@
struct v4l2_event_subscription *sub);
int msm_server_get_crop(struct msm_cam_v4l2_device *pcam,
int idx, struct v4l2_crop *crop);
-
+int msm_cam_server_request_irq(void *arg);
+int msm_cam_server_update_irqmap(
+ struct msm_cam_server_irqmap_entry *entry);
#endif /* _MSM_CAM_SERVER_H */
diff --git a/drivers/media/video/msm/sn12m0pz.c b/drivers/media/video/msm/sn12m0pz.c
index 2eabb3c..c39e97f 100644
--- a/drivers/media/video/msm/sn12m0pz.c
+++ b/drivers/media/video/msm/sn12m0pz.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -17,6 +17,7 @@
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <media/msm_camera.h>
#include <mach/gpio.h>
#include <mach/camera.h>
diff --git a/drivers/media/video/msm/vx6953.c b/drivers/media/video/msm/vx6953.c
index 3b8f14c..f933a76 100644
--- a/drivers/media/video/msm/vx6953.c
+++ b/drivers/media/video/msm/vx6953.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -16,6 +16,7 @@
#include <linux/i2c.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
+#include <linux/module.h>
#include <media/msm_camera.h>
#include <mach/gpio.h>
#include <mach/camera.h>
diff --git a/drivers/media/video/msm/wfd/wfd-ioctl.c b/drivers/media/video/msm/wfd/wfd-ioctl.c
index c198815..68a8a7d 100644
--- a/drivers/media/video/msm/wfd/wfd-ioctl.c
+++ b/drivers/media/video/msm/wfd/wfd-ioctl.c
@@ -288,7 +288,7 @@
mdp_mregion->ion_handle = enc_mregion->ion_handle;
rc = ion_map_iommu(wfd_dev->ion_client, mdp_mregion->ion_handle,
- DISPLAY_DOMAIN, GEN_POOL, SZ_4K,
+ DISPLAY_WRITE_DOMAIN, GEN_POOL, SZ_4K,
0, (unsigned long *)&mdp_mregion->paddr,
(unsigned long *)&mdp_mregion->size, 0, 0);
if (rc) {
@@ -363,7 +363,7 @@
if (mpair->mdp->paddr)
ion_unmap_iommu(wfd_dev->ion_client,
mpair->mdp->ion_handle,
- DISPLAY_DOMAIN, GEN_POOL);
+ DISPLAY_WRITE_DOMAIN, GEN_POOL);
if (mpair->enc->paddr)
ion_unmap_iommu(wfd_dev->ion_client,
diff --git a/drivers/media/video/msm_vidc/msm_smem.c b/drivers/media/video/msm_vidc/msm_smem.c
index 25b5c5c..bdace3c 100644
--- a/drivers/media/video/msm_vidc/msm_smem.c
+++ b/drivers/media/video/msm_vidc/msm_smem.c
@@ -69,6 +69,8 @@
struct ion_handle *hndl;
size_t len;
int rc = 0;
+ if (size == 0)
+ goto skip_mem_alloc;
flags = flags | ION_HEAP(ION_CP_MM_HEAP_ID);
hndl = ion_alloc(client->clnt, size, align, flags);
if (IS_ERR_OR_NULL(hndl)) {
@@ -96,6 +98,7 @@
fail_map:
ion_free(client->clnt, hndl);
fail_shared_mem_alloc:
+skip_mem_alloc:
return rc;
}
diff --git a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
index 1c646dd..6cd9e6b 100644
--- a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
@@ -28,6 +28,7 @@
#include "msm_smem.h"
#define BASE_DEVICE_NUMBER 32
+#define MAX_EVENTS 30
struct msm_vidc_drv *vidc_driver;
@@ -215,21 +216,20 @@
int rc;
struct buffer_info *bi;
struct v4l2_buffer buffer_info;
+ struct v4l2_plane plane;
v4l2_inst = get_v4l2_inst(file, NULL);
if (b->count == 0) {
list_for_each_safe(ptr, next, &v4l2_inst->registered_bufs) {
bi = list_entry(ptr, struct buffer_info, list);
if (bi->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
buffer_info.type = bi->type;
- buffer_info.m.planes[0].reserved[0] =
- bi->fd;
- buffer_info.m.planes[0].reserved[1] =
- bi->buff_off;
- buffer_info.m.planes[0].length = bi->size;
- buffer_info.m.planes[0].m.userptr =
- bi->uvaddr;
+ plane.reserved[0] = bi->fd;
+ plane.reserved[1] = bi->buff_off;
+ plane.length = bi->size;
+ plane.m.userptr = bi->uvaddr;
+ buffer_info.m.planes = &plane;
buffer_info.length = 1;
- pr_err("Releasing buffer: %d, %d, %d\n",
+ pr_info("Releasing 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);
@@ -359,9 +359,7 @@
struct v4l2_event_subscription *sub)
{
int rc = 0;
- if (sub->type == V4L2_EVENT_ALL)
- sub->type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
- rc = v4l2_event_subscribe(fh, sub, 0);
+ rc = v4l2_event_subscribe(fh, sub, MAX_EVENTS);
return rc;
}
@@ -445,7 +443,7 @@
INIT_LIST_HEAD(&core->instances);
mutex_init(&core->sync_lock);
spin_lock_init(&core->lock);
- core->base_addr = 0x34f00000;
+ core->base_addr = 0x14f00000;
core->state = VIDC_CORE_UNINIT;
for (i = SYS_MSG_INDEX(SYS_MSG_START);
i <= SYS_MSG_INDEX(SYS_MSG_END); i++) {
diff --git a/drivers/media/video/msm_vidc/msm_venc.c b/drivers/media/video/msm_vidc/msm_venc.c
index ed99d35..ec93628 100644
--- a/drivers/media/video/msm_vidc/msm_venc.c
+++ b/drivers/media/video/msm_vidc/msm_venc.c
@@ -348,7 +348,7 @@
{
.id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
.name = "H.264 Loop Filter Mode",
- .type = V4L2_CTRL_TYPE_INTEGER,
+ .type = V4L2_CTRL_TYPE_MENU,
.minimum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
.maximum = L_MODE,
.default_value = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
@@ -375,7 +375,9 @@
static u32 get_frame_size_compressed(int plane, u32 height, u32 width)
{
- return ((height + 31) & (~31)) * ((width + 31) & (~31)) * 3/2;
+ int sz = ((height + 31) & (~31)) * ((width + 31) & (~31)) * 3/2;
+ sz = (sz + 4095) & (~4095);
+ return sz;
}
static struct hal_quantization
diff --git a/drivers/media/video/msm_vidc/msm_vidc.c b/drivers/media/video/msm_vidc/msm_vidc.c
index a11f817..11fbcf4 100644
--- a/drivers/media/video/msm_vidc/msm_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_vidc.c
@@ -186,7 +186,7 @@
void *vidc_get_userptr(void *alloc_ctx, unsigned long vaddr,
unsigned long size, int write)
{
- return NULL;
+ return (void *)0xdeadbeef;
}
void vidc_put_userptr(void *buf_priv)
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index 52f1dca..9b617aa 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -183,21 +183,9 @@
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
- struct video_device *vdev;
- struct v4l2_event dqevent;
- struct msm_vidc_core *core;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
signal_session_msg_receipt(cmd, inst);
- core = inst->core;
- if (inst->session_type == MSM_VIDC_ENCODER)
- vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
- else
- vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
- dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
- dqevent.u.data[0] = (uint8_t)MSM_VIDC_OPEN_DONE;
- v4l2_event_queue(vdev, &dqevent);
- return;
} else {
pr_err("Failed to get valid response for session init\n");
}
@@ -207,24 +195,17 @@
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
- struct video_device *vdev;
struct v4l2_event dqevent;
struct msm_vidc_cb_event *event_notify;
- struct msm_vidc_core *core;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
- core = inst->core;
- if (inst->session_type == MSM_VIDC_ENCODER)
- vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
- else
- vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
- dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
- dqevent.u.data[0] = (uint8_t)MSM_VIDC_DECODER_EVENT_CHANGE;
+ dqevent.type = V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED;
+ dqevent.id = 0;
event_notify = (struct msm_vidc_cb_event *) response->data;
inst->reconfig_height = event_notify->height;
inst->reconfig_width = event_notify->width;
inst->in_reconfig = true;
- v4l2_event_queue(vdev, &dqevent);
+ v4l2_event_queue_fh(&inst->event_handler, &dqevent);
return;
} else {
pr_err("Failed to get valid response for event_change\n");
@@ -262,20 +243,9 @@
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
- struct video_device *vdev;
- struct v4l2_event dqevent;
- struct msm_vidc_core *core;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
signal_session_msg_receipt(cmd, inst);
- core = inst->core;
- if (inst->session_type == MSM_VIDC_ENCODER)
- vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
- else
- vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
- dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
- dqevent.u.data[0] = (uint8_t)MSM_VIDC_START_DONE;
- v4l2_event_queue(vdev, &dqevent);
} else {
pr_err("Failed to get valid response for start\n");
}
@@ -285,20 +255,9 @@
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
- struct video_device *vdev;
- struct v4l2_event dqevent;
- struct msm_vidc_core *core;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
signal_session_msg_receipt(cmd, inst);
- core = inst->core;
- if (inst->session_type == MSM_VIDC_ENCODER)
- vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
- else
- vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
- dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
- dqevent.u.data[0] = (uint8_t)MSM_VIDC_STOP_DONE;
- v4l2_event_queue(vdev, &dqevent);
} else {
pr_err("Failed to get valid response for stop\n");
}
@@ -320,19 +279,12 @@
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
- struct video_device *vdev;
struct v4l2_event dqevent;
- struct msm_vidc_core *core;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
- core = inst->core;
- if (inst->session_type == MSM_VIDC_ENCODER)
- vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
- else
- vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
- dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
- dqevent.u.data[0] = (uint8_t)MSM_VIDC_DECODER_FLUSH_DONE;
- v4l2_event_queue(vdev, &dqevent);
+ dqevent.type = V4L2_EVENT_MSM_VIDC_FLUSH_DONE;
+ dqevent.id = 0;
+ v4l2_event_queue_fh(&inst->event_handler, &dqevent);
} else {
pr_err("Failed to get valid response for flush\n");
}
@@ -343,20 +295,13 @@
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
- struct video_device *vdev;
struct v4l2_event dqevent;
- struct msm_vidc_core *core;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
signal_session_msg_receipt(cmd, inst);
- core = inst->core;
- if (inst->session_type == MSM_VIDC_ENCODER)
- vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
- else
- vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
- dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
- dqevent.u.data[0] = (uint8_t)MSM_VIDC_CLOSE_DONE;
- v4l2_event_queue(vdev, &dqevent);
+ dqevent.type = V4L2_EVENT_MSM_VIDC_CLOSE_DONE;
+ dqevent.id = 0;
+ v4l2_event_queue_fh(&inst->event_handler, &dqevent);
} else {
pr_err("Failed to get valid response for session close\n");
}
diff --git a/drivers/media/video/msm_vidc/msm_vidc_internal.h b/drivers/media/video/msm_vidc/msm_vidc_internal.h
index fb1ab58..fb8fbc4 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_internal.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_internal.h
@@ -75,11 +75,6 @@
MSM_VIDC_CORE_UNINIT,
};
-enum vidc_resposes_id {
- MSM_VIDC_DECODER_FLUSH_DONE = 0x11,
- MSM_VIDC_DECODER_EVENT_CHANGE,
-};
-
struct buf_info {
struct list_head list;
struct vb2_buffer *buf;
diff --git a/drivers/media/video/msm_vidc/vidc_hal.c b/drivers/media/video/msm_vidc/vidc_hal.c
index 1f33c2c..13a319d9 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.c
+++ b/drivers/media/video/msm_vidc/vidc_hal.c
@@ -84,8 +84,8 @@
{
struct hfi_cmd_session_release_buffer_packet *pkt =
(struct hfi_cmd_session_release_buffer_packet *)packet;
- if ((pkt->buffer_type == HAL_BUFFER_OUTPUT) ||
- (pkt->buffer_type == HAL_BUFFER_OUTPUT2)) {
+ if ((pkt->buffer_type == HFI_BUFFER_OUTPUT) ||
+ (pkt->buffer_type == HFI_BUFFER_OUTPUT2)) {
struct hfi_buffer_info *buff;
buff = (struct hfi_buffer_info *) pkt->rg_buffer_info;
buff->buffer_addr -= HFI_VIRTIO_FW_BIAS;
@@ -824,8 +824,7 @@
}
HAL_MSG_INFO("IN func: %s, with property id: %d", __func__, ptype);
- pkt->size = sizeof(struct hfi_cmd_session_set_property_packet)
- - sizeof(u32);
+ pkt->size = sizeof(struct hfi_cmd_session_set_property_packet);
pkt->packet_type = HFI_CMD_SESSION_SET_PROPERTY;
pkt->session_id = (u32) session;
pkt->num_properties = 1;
diff --git a/drivers/media/video/msm_vidc/vidc_hal.h b/drivers/media/video/msm_vidc/vidc_hal.h
index 166ed0d..15441f405 100644
--- a/drivers/media/video/msm_vidc/vidc_hal.h
+++ b/drivers/media/video/msm_vidc/vidc_hal.h
@@ -1153,7 +1153,7 @@
enum HFI_COMMAND packet_type;
u32 session_id;
u32 num_properties;
- u32 rg_property_data[1];
+ u32 rg_property_data[0];
};
struct hfi_cmd_session_get_property_packet {
diff --git a/drivers/mfd/pm8038-core.c b/drivers/mfd/pm8038-core.c
index 4271a2a..8fef786 100644
--- a/drivers/mfd/pm8038-core.c
+++ b/drivers/mfd/pm8038-core.c
@@ -36,6 +36,9 @@
#define REG_SPK_BASE 0x253
#define REG_SPK_REGISTERS 3
+#define REG_TEMP_ALARM_CTRL 0x01B
+#define REG_TEMP_ALARM_PWM 0x09B
+
#define PM8038_VERSION_MASK 0xFFF0
#define PM8038_VERSION_VALUE 0x09F0
#define PM8038_REVISION_MASK 0x000F
@@ -300,6 +303,30 @@
.pdata_size = sizeof("pm8038-dbg"),
};
+static const struct resource thermal_alarm_cell_resources[] __devinitconst = {
+ SINGLE_IRQ_RESOURCE("pm8038_tempstat_irq", PM8038_TEMPSTAT_IRQ),
+ SINGLE_IRQ_RESOURCE("pm8038_overtemp_irq", PM8038_OVERTEMP_IRQ),
+};
+
+static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
+ .adc_channel = CHANNEL_DIE_TEMP,
+ .adc_type = PM8XXX_TM_ADC_PM8XXX_ADC,
+ .reg_addr_temp_alarm_ctrl = REG_TEMP_ALARM_CTRL,
+ .reg_addr_temp_alarm_pwm = REG_TEMP_ALARM_PWM,
+ .tm_name = "pm8038_tz",
+ .irq_name_temp_stat = "pm8038_tempstat_irq",
+ .irq_name_over_temp = "pm8038_overtemp_irq",
+};
+
+static struct mfd_cell thermal_alarm_cell __devinitdata = {
+ .name = PM8XXX_TM_DEV_NAME,
+ .id = -1,
+ .resources = thermal_alarm_cell_resources,
+ .num_resources = ARRAY_SIZE(thermal_alarm_cell_resources),
+ .platform_data = &thermal_alarm_cdata,
+ .pdata_size = sizeof(struct pm8xxx_tm_core_data),
+};
+
static struct pm8xxx_vreg regulator_data[] = {
/* name pc_name ctrl test hpm_min */
NLDO1200("8038_l1", 0x0AE, 0x0AF, LDO_1200),
@@ -606,6 +633,14 @@
goto bail;
}
}
+
+ ret = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add thermal alarm subdevice ret=%d\n", ret);
+ goto bail;
+ }
+
return 0;
bail:
if (pmic->irq_chip) {
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index bbb2509..021dcf1 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -31,7 +31,8 @@
#define WCD9XXX_SLIM_RW_MAX_TRIES 3
#define MAX_WCD9XXX_DEVICE 4
-#define WCD9XXX_I2C_MODE 0x03
+#define TABLA_I2C_MODE 0x03
+#define SITAR_I2C_MODE 0x01
struct wcd9xxx_i2c {
struct i2c_client *client;
@@ -351,15 +352,15 @@
* care of now only tabla.
*/
pr_debug("%s : Read codec version using I2C\n", __func__);
- if (TABLA_IS_1_X(wcd9xxx->version)) {
+ if (!strncmp(wcd9xxx_modules[0].client->name, "sitar", 5)) {
+ wcd9xxx_dev = sitar_devs;
+ wcd9xxx_dev_size = ARRAY_SIZE(sitar_devs);
+ } else if (TABLA_IS_1_X(wcd9xxx->version)) {
wcd9xxx_dev = tabla1x_devs;
wcd9xxx_dev_size = ARRAY_SIZE(tabla1x_devs);
} else if (TABLA_IS_2_0(wcd9xxx->version)) {
wcd9xxx_dev = tabla_devs;
wcd9xxx_dev_size = ARRAY_SIZE(tabla_devs);
- } else {
- wcd9xxx_dev = sitar_devs;
- wcd9xxx_dev_size = ARRAY_SIZE(sitar_devs);
}
}
@@ -712,8 +713,10 @@
struct wcd9xxx_pdata *pdata = client->dev.platform_data;
int val = 0;
int ret = 0;
+ int i2c_mode = 0;
static int device_id;
+ pr_info("%s\n", __func__);
if (wcd9xxx_intf == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
pr_info("tabla card is already detected in slimbus mode\n");
return -ENODEV;
@@ -765,10 +768,13 @@
/*read the tabla status before initializing the device type*/
ret = wcd9xxx_read(wcd9xxx, WCD9XXX_A_CHIP_STATUS, 1, &val, 0);
- if ((ret < 0) || (val != WCD9XXX_I2C_MODE)) {
- pr_err("failed to read the wcd9xxx status\n");
- goto err_device_init;
- }
+ if (!strncmp(wcd9xxx_modules[0].client->name, "sitar", 5))
+ i2c_mode = SITAR_I2C_MODE;
+ else if (!strncmp(wcd9xxx_modules[0].client->name, "tabla", 5))
+ i2c_mode = TABLA_I2C_MODE;
+
+ if ((ret < 0) || (val != i2c_mode))
+ pr_err("failed to read the wcd9xxx status ret = %d\n", ret);
ret = wcd9xxx_device_init(wcd9xxx, wcd9xxx->irq);
if (ret) {
@@ -1107,16 +1113,25 @@
.suspend = wcd9xxx_slim_suspend,
};
-#define TABLA_I2C_TOP_LEVEL 0
-#define TABLA_I2C_ANALOG 1
-#define TABLA_I2C_DIGITAL_1 2
-#define TABLA_I2C_DIGITAL_2 3
+#define WCD9XXX_I2C_TOP_LEVEL 0
+#define WCD9XXX_I2C_ANALOG 1
+#define WCD9XXX_I2C_DIGITAL_1 2
+#define WCD9XXX_I2C_DIGITAL_2 3
static struct i2c_device_id tabla_id_table[] = {
- {"tabla top level", TABLA_I2C_TOP_LEVEL},
- {"tabla analog", TABLA_I2C_TOP_LEVEL},
- {"tabla digital1", TABLA_I2C_TOP_LEVEL},
- {"tabla digital2", TABLA_I2C_TOP_LEVEL},
+ {"tabla top level", WCD9XXX_I2C_TOP_LEVEL},
+ {"tabla analog", WCD9XXX_I2C_ANALOG},
+ {"tabla digital1", WCD9XXX_I2C_DIGITAL_1},
+ {"tabla digital2", WCD9XXX_I2C_DIGITAL_2},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, tabla_id_table);
+
+static struct i2c_device_id sitar_id_table[] = {
+ {"sitar top level", WCD9XXX_I2C_TOP_LEVEL},
+ {"sitar analog", WCD9XXX_I2C_ANALOG},
+ {"sitar digital1", WCD9XXX_I2C_DIGITAL_1},
+ {"sitar digital2", WCD9XXX_I2C_DIGITAL_2},
{}
};
MODULE_DEVICE_TABLE(i2c, tabla_id_table);
@@ -1133,9 +1148,21 @@
.suspend = wcd9xxx_i2c_suspend,
};
+static struct i2c_driver sitar_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "sitar-i2c-core",
+ },
+ .id_table = sitar_id_table,
+ .probe = wcd9xxx_i2c_probe,
+ .remove = __devexit_p(wcd9xxx_i2c_remove),
+ .resume = wcd9xxx_i2c_resume,
+ .suspend = wcd9xxx_i2c_suspend,
+};
+
static int __init wcd9xxx_init(void)
{
- int ret1, ret2, ret3, ret4, ret5;
+ int ret1, ret2, ret3, ret4, ret5, ret6;
ret1 = slim_driver_register(&tabla_slim_driver);
if (ret1 != 0)
@@ -1157,7 +1184,11 @@
if (ret5 != 0)
pr_err("Failed to register sitar SB driver: %d\n", ret5);
- return (ret1 && ret2 && ret3 && ret4 && ret5) ? -1 : 0;
+ ret6 = i2c_add_driver(&sitar_i2c_driver);
+ if (ret6 != 0)
+ pr_err("failed to add the I2C driver\n");
+
+ return (ret1 && ret2 && ret3 && ret4 && ret5 && ret6) ? -1 : 0;
}
module_init(wcd9xxx_init);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index a21b39d..b5ffe94 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1455,7 +1455,15 @@
err = mmc_poweroff_notify(host, MMC_PW_OFF_NOTIFY_SHORT);
} else {
if (mmc_card_can_sleep(host))
- err = mmc_card_sleep(host);
+ /*
+ * If sleep command has error it doesn't mean host
+ * cannot suspend, but a deeper low power state
+ * transition for the card has failed. Ignore
+ * sleep errors so that the suspend is not aborted.
+ * In error case, mmc_resume() takes care of
+ * complete intialization of the card.
+ */
+ mmc_card_sleep(host);
else if (!mmc_host_is_spi(host))
mmc_deselect_cards(host);
}
@@ -1512,7 +1520,7 @@
if (card && card->ext_csd.rev >= 3) {
err = mmc_card_sleepawake(host, 1);
if (err < 0)
- pr_debug("%s: Error %d while putting card into sleep",
+ pr_warn("%s: Error %d while putting card into sleep",
mmc_hostname(host), err);
}
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 5c6eb22..edf4400 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -3062,7 +3062,7 @@
msleep(300);
status = gpio_get_value_cansleep(
host->plat->wpswitch_gpio);
- status ^= !host->plat->wpswitch_polarity;
+ status ^= !host->plat->is_wpswitch_active_low;
}
gpio_free(host->plat->wpswitch_gpio);
}
@@ -4940,9 +4940,10 @@
mmc->f_min = msmsdcc_get_min_sup_clk_rate(host);
mmc->f_max = msmsdcc_get_max_sup_clk_rate(host);
mmc->ocr_avail = plat->ocr_mask;
+ mmc->clkgate_delay = MSM_MMC_CLK_GATE_DELAY;
+
mmc->pm_caps |= MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ;
mmc->caps |= plat->mmc_bus_width;
-
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE;
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index 2222337..5531f06 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -212,10 +212,10 @@
#define NR_SG 128
#define MSM_MMC_IDLE_TIMEOUT 5000 /* msecs */
+#define MSM_MMC_CLK_GATE_DELAY 200 /* msecs */
/* Set the request timeout to 10secs */
#define MSM_MMC_REQ_TIMEOUT 10000 /* msecs */
-#define MSM_MMC_DISABLE_TIMEOUT 200 /* msecs */
/*
* Controller HW limitations
diff --git a/drivers/net/usb/rmnet_usb_ctrl.c b/drivers/net/usb/rmnet_usb_ctrl.c
index 23ba900..c2085c9 100644
--- a/drivers/net/usb/rmnet_usb_ctrl.c
+++ b/drivers/net/usb/rmnet_usb_ctrl.c
@@ -174,6 +174,8 @@
goto resubmit_int_urb;
}
+ usb_mark_last_busy(udev);
+
if (!dev->resp_available) {
dev->resp_available = true;
wake_up(&dev->open_wait_queue);
@@ -261,6 +263,7 @@
resubmit_int_urb:
/*re-submit int urb to check response available*/
+ usb_mark_last_busy(udev);
status = usb_submit_urb(dev->inturb, GFP_ATOMIC);
if (status)
dev_err(dev->devicep, "%s: Error re-submitting Int URB %d\n",
@@ -796,6 +799,7 @@
dev->intbuf, wMaxPacketSize,
notification_available_cb, dev, interval);
+ usb_mark_last_busy(udev);
return rmnet_usb_ctrl_start_rx(dev);
}
diff --git a/drivers/net/usb/rmnet_usb_data.c b/drivers/net/usb/rmnet_usb_data.c
index ee9737a..9e1e252 100644
--- a/drivers/net/usb/rmnet_usb_data.c
+++ b/drivers/net/usb/rmnet_usb_data.c
@@ -505,6 +505,7 @@
{
struct usbnet *unet;
struct driver_info *info;
+ struct usb_device *udev;
unsigned int iface_num;
static int first_rmnet_iface_num = -EINVAL;
int status = 0;
@@ -555,8 +556,17 @@
if (status)
dev_dbg(&iface->dev, "mode debugfs file is not available\n");
+ udev = unet->udev;
+
/* allow modem to wake up suspended system */
- device_set_wakeup_enable(&unet->udev->dev, 1);
+ device_set_wakeup_enable(&udev->dev, 1);
+
+ /* set default autosuspend timeout for modem and roothub */
+ if (udev->parent && !udev->parent->parent) {
+ pm_runtime_set_autosuspend_delay(&udev->dev, 1000);
+ pm_runtime_set_autosuspend_delay(&udev->parent->dev, 200);
+ }
+
out:
return status;
}
diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig
index 23efb00..15e7f3f 100644
--- a/drivers/platform/msm/Kconfig
+++ b/drivers/platform/msm/Kconfig
@@ -13,7 +13,8 @@
config SPS
bool "SPS support"
depends on (HAS_IOMEM && (ARCH_MSM8960 || ARCH_MSM8X60 \
- || ARCH_APQ8064 || ARCH_MSM9615 || ARCH_MSMCOPPER))
+ || ARCH_APQ8064 || ARCH_MSM9615 \
+ || ARCH_MSM9625 || ARCH_MSMCOPPER))
select GENERIC_ALLOCATOR
default n
help
diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c
index fbaea09..1329f6c 100644
--- a/drivers/platform/msm/sps/sps.c
+++ b/drivers/platform/msm/sps/sps.c
@@ -79,6 +79,8 @@
*/
static struct sps_drv *sps;
+u32 d_type;
+
static void sps_device_de_init(void);
#ifdef CONFIG_DEBUG_FS
@@ -1443,11 +1445,6 @@
{
SPS_DBG("sps:%s.", __func__);
- if (h == NULL) {
- SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
- return SPS_ERROR;
- }
-
if (sps == NULL)
return -ENODEV;
@@ -1461,6 +1458,11 @@
return SPS_ERROR;
}
+ if (h == NULL)
+ SPS_DBG("sps:allocate pipe memory before setup pipe");
+ else
+ SPS_DBG("sps:allocate pipe memory for pipe %d", h->pipe_index);
+
mem_buffer->phys_base = sps_mem_alloc_io(mem_buffer->size);
if (mem_buffer->phys_base == SPS_ADDR_INVALID) {
SPS_ERR("sps:invalid address of allocated memory");
@@ -1481,16 +1483,16 @@
{
SPS_DBG("sps:%s.", __func__);
- if (h == NULL) {
- SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
- return SPS_ERROR;
- }
-
if (mem_buffer == NULL || mem_buffer->phys_base == SPS_ADDR_INVALID) {
SPS_ERR("sps:invalid memory to free");
return SPS_ERROR;
}
+ if (h == NULL)
+ SPS_DBG("sps:free pipe memory.");
+ else
+ SPS_DBG("sps:free pipe memory for pipe %d.", h->pipe_index);
+
sps_mem_free_io(mem_buffer->phys_base, mem_buffer->size);
return 0;
@@ -1928,13 +1930,20 @@
if (of_property_read_u32((&pdev->dev)->of_node,
"qcom,bam-dma-res-pipes",
- &sps->bamdma_restricted_pipes)) {
- SPS_ERR("sps:Fail to get restricted bamdma pipes.\n");
- return -EINVAL;
- } else
+ &sps->bamdma_restricted_pipes))
+ SPS_DBG("sps:No restricted bamdma pipes on this target.\n");
+ else
SPS_DBG("sps:bamdma_restricted_pipes=0x%x.",
sps->bamdma_restricted_pipes);
+ if (of_property_read_u32((&pdev->dev)->of_node,
+ "qcom,device-type",
+ &d_type)) {
+ d_type = 1;
+ SPS_DBG("sps:default device type.\n");
+ } else
+ SPS_DBG("sps:device type is %d.", d_type);
+
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (resource) {
sps->bamdma_bam_phys_base = resource->start;
@@ -1959,6 +1968,16 @@
return -ENODEV;
}
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (resource) {
+ sps->pipemem_phys_base = resource->start;
+ sps->pipemem_size = resource_size(resource);
+ SPS_DBG("sps:pipemem.base=0x%x,size=0x%x.",
+ sps->pipemem_phys_base,
+ sps->pipemem_size);
+ } else
+ SPS_DBG("sps:No pipe memory on this target.\n");
+
resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (resource) {
sps->bamdma_irq = resource->start;
@@ -1985,6 +2004,7 @@
} else
SPS_DBG("sps:get data from device tree.");
} else {
+ d_type = 0;
if (get_platform_data(pdev)) {
SPS_ERR("sps:Fail to get platform data.");
return -ENODEV;
@@ -2008,17 +2028,18 @@
goto device_create_err;
}
- sps->dfab_clk = clk_get(sps->dev, "dfab_clk");
- if (IS_ERR(sps->dfab_clk)) {
- SPS_ERR("sps:fail to get dfab_clk.");
- goto clk_err;
- } else {
- ret = clk_set_rate(sps->dfab_clk, 64000000);
- if (ret) {
- SPS_ERR("sps:failed to set dfab_clk rate. "
- "ret=%d", ret);
- clk_put(sps->dfab_clk);
+ if (!d_type) {
+ sps->dfab_clk = clk_get(sps->dev, "dfab_clk");
+ if (IS_ERR(sps->dfab_clk)) {
+ SPS_ERR("sps:fail to get dfab_clk.");
goto clk_err;
+ } else {
+ ret = clk_set_rate(sps->dfab_clk, 64000000);
+ if (ret) {
+ SPS_ERR("sps:failed to set dfab_clk rate.");
+ clk_put(sps->dfab_clk);
+ goto clk_err;
+ }
}
}
@@ -2047,22 +2068,26 @@
}
}
- ret = clk_prepare_enable(sps->dfab_clk);
- if (ret) {
- SPS_ERR("sps:failed to enable dfab_clk. ret=%d", ret);
- goto clk_err;
+ if (!d_type) {
+ ret = clk_prepare_enable(sps->dfab_clk);
+ if (ret) {
+ SPS_ERR("sps:failed to enable dfab_clk. ret=%d", ret);
+ goto clk_err;
+ }
}
#endif
ret = sps_device_init();
if (ret) {
SPS_ERR("sps:sps_device_init err.");
#ifdef CONFIG_SPS_SUPPORT_BAMDMA
- clk_disable_unprepare(sps->dfab_clk);
+ if (!d_type)
+ clk_disable_unprepare(sps->dfab_clk);
#endif
goto sps_device_init_err;
}
#ifdef CONFIG_SPS_SUPPORT_BAMDMA
- clk_disable_unprepare(sps->dfab_clk);
+ if (!d_type)
+ clk_disable_unprepare(sps->dfab_clk);
#endif
sps->is_ready = true;
@@ -2089,7 +2114,8 @@
class_destroy(sps->dev_class);
sps_device_de_init();
- clk_put(sps->dfab_clk);
+ if (!d_type)
+ clk_put(sps->dfab_clk);
clk_put(sps->pmem_clk);
clk_put(sps->bamdma_clk);
diff --git a/drivers/platform/msm/sps/sps_mem.c b/drivers/platform/msm/sps/sps_mem.c
index 1b19b12..fa22f1c 100644
--- a/drivers/platform/msm/sps/sps_mem.c
+++ b/drivers/platform/msm/sps/sps_mem.c
@@ -104,31 +104,30 @@
*/
int sps_mem_init(u32 pipemem_phys_base, u32 pipemem_size)
{
-#ifndef CONFIG_SPS_SUPPORT_NDP_BAM
int res;
-#endif
+
/* 2^8=128. The desc-fifo and data-fifo minimal allocation. */
int min_alloc_order = 8;
-#ifndef CONFIG_SPS_SUPPORT_NDP_BAM
- iomem_phys = pipemem_phys_base;
- iomem_size = pipemem_size;
+ if ((d_type == 0) || (d_type == 2)) {
+ iomem_phys = pipemem_phys_base;
+ iomem_size = pipemem_size;
- if (iomem_phys == 0) {
- SPS_ERR("sps:Invalid Pipe-Mem address");
- return SPS_ERROR;
- } else {
- iomem_virt = ioremap(iomem_phys, iomem_size);
- if (!iomem_virt) {
- SPS_ERR("sps:Failed to IO map pipe memory.\n");
- return -ENOMEM;
+ if (iomem_phys == 0) {
+ SPS_ERR("sps:Invalid Pipe-Mem address");
+ return SPS_ERROR;
+ } else {
+ iomem_virt = ioremap(iomem_phys, iomem_size);
+ if (!iomem_virt) {
+ SPS_ERR("sps:Failed to IO map pipe memory.\n");
+ return -ENOMEM;
+ }
}
- }
- iomem_offset = 0;
- SPS_DBG("sps:sps_mem_init.iomem_phys=0x%x,iomem_virt=0x%x.",
- iomem_phys, (u32) iomem_virt);
-#endif
+ iomem_offset = 0;
+ SPS_DBG("sps:sps_mem_init.iomem_phys=0x%x,iomem_virt=0x%x.",
+ iomem_phys, (u32) iomem_virt);
+ }
pool = gen_pool_create(min_alloc_order, nid);
@@ -137,11 +136,11 @@
return -ENOMEM;
}
-#ifndef CONFIG_SPS_SUPPORT_NDP_BAM
- res = gen_pool_add(pool, (u32) iomem_virt, iomem_size, nid);
- if (res)
- return res;
-#endif
+ if ((d_type == 0) || (d_type == 2)) {
+ res = gen_pool_add(pool, (u32) iomem_virt, iomem_size, nid);
+ if (res)
+ return res;
+ }
return 0;
}
diff --git a/drivers/platform/msm/sps/spsi.h b/drivers/platform/msm/sps/spsi.h
index e8ab832..5a141ca 100644
--- a/drivers/platform/msm/sps/spsi.h
+++ b/drivers/platform/msm/sps/spsi.h
@@ -40,6 +40,8 @@
/* "Clear" value for the connection parameter struct */
#define SPSRM_CLEAR 0xcccccccc
+extern u32 d_type;
+
#ifdef CONFIG_DEBUG_FS
extern u8 debugfs_record_enabled;
extern u8 logging_option;
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index b0439bc..73c042d5 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -100,8 +100,6 @@
struct sf_lut *rbatt_sf_lut;
int delta_rbatt_mohm;
struct work_struct calib_hkadc_work;
- struct delayed_work calib_ccadc_work;
- unsigned int calib_delay_ms;
unsigned int revision;
unsigned int xoadc_v0625_usb_present;
unsigned int xoadc_v0625_usb_absent;
@@ -1604,18 +1602,6 @@
schedule_work(&the_chip->calib_hkadc_work);
}
-static void calibrate_ccadc_work(struct work_struct *work)
-{
- struct pm8921_bms_chip *chip = container_of(work,
- struct pm8921_bms_chip, calib_ccadc_work.work);
-
- pm8xxx_calib_ccadc();
- calib_hkadc(chip);
- schedule_delayed_work(&chip->calib_ccadc_work,
- round_jiffies_relative(msecs_to_jiffies
- (chip->calib_delay_ms)));
-}
-
int pm8921_bms_get_vsense_avg(int *result)
{
int rc = -EINVAL;
@@ -2601,7 +2587,6 @@
chip->r_sense = pdata->r_sense;
chip->i_test = pdata->i_test;
chip->v_failure = pdata->v_failure;
- chip->calib_delay_ms = pdata->calib_delay_ms;
chip->max_voltage_uv = pdata->max_voltage_uv;
chip->batt_type = pdata->battery_type;
chip->rconn_mohm = pdata->rconn_mohm;
@@ -2655,11 +2640,6 @@
}
check_initial_ocv(chip);
- INIT_DELAYED_WORK(&chip->calib_ccadc_work, calibrate_ccadc_work);
- /* begin calibration only on chips > 2.0 */
- if (chip->revision >= PM8XXX_REVISION_8921_2p0)
- schedule_delayed_work(&chip->calib_ccadc_work, 0);
-
/* initial hkadc calibration */
schedule_work(&chip->calib_hkadc_work);
/* enable the vbatt reading interrupts for scheduling hkadc calib */
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index c983389..a1561f0 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -3308,6 +3308,9 @@
int rc;
int vdd_safe;
+ /* forcing 19p2mhz before accessing any charger registers */
+ pm8921_chg_force_19p2mhz_clk(chip);
+
rc = pm_chg_masked_write(chip, SYS_CONFIG_2,
BOOT_DONE_BIT, BOOT_DONE_BIT);
if (rc) {
@@ -3501,8 +3504,6 @@
/* Disable EOC FSM processing */
pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0x91);
- pm8921_chg_force_19p2mhz_clk(chip);
-
rc = pm_chg_masked_write(chip, CHG_CNTRL, VREF_BATT_THERM_FORCE_ON,
VREF_BATT_THERM_FORCE_ON);
if (rc)
diff --git a/drivers/power/pm8xxx-ccadc.c b/drivers/power/pm8xxx-ccadc.c
index ce72a5b..ef31575 100644
--- a/drivers/power/pm8xxx-ccadc.c
+++ b/drivers/power/pm8xxx-ccadc.c
@@ -70,8 +70,10 @@
u16 ccadc_offset;
int ccadc_gain_uv;
unsigned int revision;
+ unsigned int calib_delay_ms;
int eoc_irq;
int r_sense;
+ struct delayed_work calib_ccadc_work;
};
static struct pm8xxx_ccadc_chip *the_chip;
@@ -334,6 +336,11 @@
u16 result;
int i, rc;
+ if (!the_chip) {
+ pr_err("chip not initialized\n");
+ return;
+ }
+
rc = pm8xxx_readb(the_chip->dev->parent,
ADC_ARB_SECP_CNTRL, &sec_cntrl);
if (rc < 0) {
@@ -473,6 +480,17 @@
}
EXPORT_SYMBOL(pm8xxx_calib_ccadc);
+static void calibrate_ccadc_work(struct work_struct *work)
+{
+ struct pm8xxx_ccadc_chip *chip = container_of(work,
+ struct pm8xxx_ccadc_chip, calib_ccadc_work.work);
+
+ pm8xxx_calib_ccadc();
+ schedule_delayed_work(&chip->calib_ccadc_work,
+ round_jiffies_relative(msecs_to_jiffies
+ (chip->calib_delay_ms)));
+}
+
static irqreturn_t pm8921_bms_ccadc_eoc_handler(int irq, void *data)
{
u8 data_msb, data_lsb;
@@ -671,6 +689,7 @@
chip->revision = pm8xxx_get_revision(chip->dev->parent);
chip->eoc_irq = res->start;
chip->r_sense = pdata->r_sense;
+ chip->calib_delay_ms = pdata->calib_delay_ms;
calib_ccadc_read_offset_and_gain(chip,
&chip->ccadc_gain_uv,
@@ -682,6 +701,10 @@
pr_err("failed to request %d irq rc= %d\n", chip->eoc_irq, rc);
goto free_chip;
}
+
+ INIT_DELAYED_WORK(&chip->calib_ccadc_work, calibrate_ccadc_work);
+ schedule_delayed_work(&chip->calib_ccadc_work, 0);
+
disable_irq_nosync(chip->eoc_irq);
platform_set_drvdata(pdev, chip);
diff --git a/drivers/power/smb349.c b/drivers/power/smb349.c
index 2784cc3c..ffc92d5 100644
--- a/drivers/power/smb349.c
+++ b/drivers/power/smb349.c
@@ -244,7 +244,7 @@
{
int addr = (int)data;
int ret;
- u8 temp;
+ u8 temp = 0;
ret = smb349_read_reg(the_smb349_chg->client, addr, &temp);
if (ret) {
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index 8b1d5e6..e569132 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -1016,13 +1016,8 @@
send_check_condition = 1;
goto attach_cmd;
}
- /*
- * The Initiator Node has access to the LUN (the addressing method
- * is handled inside of iscsit_get_lun_for_cmd()). Now it's time to
- * allocate 1->N transport tasks (depending on sector count and
- * maximum request size the physical HBA(s) can handle.
- */
- transport_ret = transport_generic_allocate_tasks(&cmd->se_cmd, hdr->cdb);
+
+ transport_ret = target_setup_cmd_from_cdb(&cmd->se_cmd, hdr->cdb);
if (transport_ret == -ENOMEM) {
return iscsit_add_reject_from_cmd(
ISCSI_REASON_BOOKMARK_NO_RESOURCES,
diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c
index a9b4eee..38dfac2 100644
--- a/drivers/target/loopback/tcm_loop.c
+++ b/drivers/target/loopback/tcm_loop.c
@@ -213,7 +213,7 @@
* associated read buffers, go ahead and do that here for type
* SCF_SCSI_CONTROL_SG_IO_CDB. Also note that this is currently
* guaranteed to be a single SGL for SCF_SCSI_CONTROL_SG_IO_CDB
- * by target core in transport_generic_allocate_tasks() ->
+ * by target core in target_setup_cmd_from_cdb() ->
* transport_generic_cmd_sequencer().
*/
if (se_cmd->se_cmd_flags & SCF_SCSI_CONTROL_SG_IO_CDB &&
@@ -227,7 +227,7 @@
}
}
- ret = transport_generic_allocate_tasks(se_cmd, sc->cmnd);
+ ret = target_setup_cmd_from_cdb(se_cmd, sc->cmnd);
if (ret == -ENOMEM) {
transport_send_check_condition_and_sense(se_cmd,
TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0);
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 443704f..843ad54 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -1550,11 +1550,11 @@
return 0;
}
-/* transport_generic_allocate_tasks():
+/* target_setup_cmd_from_cdb():
*
* Called from fabric RX Thread.
*/
-int transport_generic_allocate_tasks(
+int target_setup_cmd_from_cdb(
struct se_cmd *cmd,
unsigned char *cdb)
{
@@ -1620,7 +1620,7 @@
spin_unlock(&cmd->se_lun->lun_sep_lock);
return 0;
}
-EXPORT_SYMBOL(transport_generic_allocate_tasks);
+EXPORT_SYMBOL(target_setup_cmd_from_cdb);
/*
* Used by fabric module frontends to queue tasks directly.
@@ -1701,6 +1701,8 @@
*/
transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess,
data_length, data_dir, task_attr, sense);
+ if (flags & TARGET_SCF_UNKNOWN_SIZE)
+ se_cmd->unknown_data_length = 1;
/*
* Obtain struct se_cmd->cmd_kref reference and add new cmd to
* se_sess->sess_cmd_list. A second kref_get here is necessary
@@ -1726,7 +1728,7 @@
* Sanitize CDBs via transport_generic_cmd_sequencer() and
* allocate the necessary tasks to complete the received CDB+data
*/
- rc = transport_generic_allocate_tasks(se_cmd, cdb);
+ rc = target_setup_cmd_from_cdb(se_cmd, cdb);
if (rc != 0) {
transport_generic_request_failure(se_cmd);
return;
@@ -2581,7 +2583,7 @@
* Generic Command Sequencer that should work for most DAS transport
* drivers.
*
- * Called from transport_generic_allocate_tasks() in the $FABRIC_MOD
+ * Called from target_setup_cmd_from_cdb() in the $FABRIC_MOD
* RX Thread.
*
* FIXME: Need to support other SCSI OPCODES where as well.
@@ -3142,6 +3144,9 @@
goto out_unsupported_cdb;
}
+ if (cmd->unknown_data_length)
+ cmd->data_length = size;
+
if (size != cmd->data_length) {
pr_warn("TARGET_CORE[%s]: Expected Transfer Length:"
" %u does not match SCSI CDB Length: %u for SAM Opcode:"
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index fbe0dd7..d99a02a 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -66,6 +66,7 @@
config THERMAL_MONITOR
bool "Monitor thermal state and limit CPU Frequency"
depends on THERMAL_TSENS8960
+ depends on CPU_FREQ_MSM
default n
help
This enables thermal monitoring capability in the kernel in the
diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c
index e0d8d14..a8d3720 100644
--- a/drivers/thermal/msm_thermal.c
+++ b/drivers/thermal/msm_thermal.c
@@ -14,60 +14,51 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/cpufreq.h>
#include <linux/mutex.h>
#include <linux/msm_tsens.h>
#include <linux/workqueue.h>
#include <linux/cpu.h>
-
-#define DEF_TEMP_SENSOR 0
-#define DEF_THERMAL_CHECK_MS 1000
-#define DEF_ALLOWED_MAX_HIGH 60
-#define DEF_ALLOWED_MAX_FREQ 918000
+#include <linux/cpufreq.h>
+#include <linux/msm_tsens.h>
+#include <linux/msm_thermal.h>
+#include <mach/cpufreq.h>
static int enabled;
-static int allowed_max_high = DEF_ALLOWED_MAX_HIGH;
-static int allowed_max_low = (DEF_ALLOWED_MAX_HIGH - 10);
-static int allowed_max_freq = DEF_ALLOWED_MAX_FREQ;
-static int check_interval_ms = DEF_THERMAL_CHECK_MS;
-
-module_param(allowed_max_high, int, 0);
-module_param(allowed_max_freq, int, 0);
-module_param(check_interval_ms, int, 0);
-
+static struct msm_thermal_data msm_thermal_info;
+static uint32_t limited_max_freq = MSM_CPUFREQ_NO_LIMIT;
static struct delayed_work check_temp_work;
-static int update_cpu_max_freq(struct cpufreq_policy *cpu_policy,
- int cpu, int max_freq)
+static int update_cpu_max_freq(int cpu, uint32_t max_freq)
{
int ret = 0;
- if (!cpu_policy)
- return -EINVAL;
-
- cpufreq_verify_within_limits(cpu_policy,
- cpu_policy->min, max_freq);
- cpu_policy->user_policy.max = max_freq;
+ ret = msm_cpufreq_set_freq_limits(cpu, MSM_CPUFREQ_NO_LIMIT, max_freq);
+ if (ret)
+ return ret;
ret = cpufreq_update_policy(cpu);
- if (!ret)
- pr_info("msm_thermal: Limiting core%d max frequency to %d\n",
- cpu, max_freq);
+ if (ret)
+ return ret;
+
+ limited_max_freq = max_freq;
+ if (max_freq != MSM_CPUFREQ_NO_LIMIT)
+ pr_info("msm_thermal: Limiting cpu%d max frequency to %d\n",
+ cpu, max_freq);
+ else
+ pr_info("msm_thermal: Max frequency reset for cpu%d\n", cpu);
return ret;
}
static void check_temp(struct work_struct *work)
{
- struct cpufreq_policy *cpu_policy = NULL;
struct tsens_device tsens_dev;
unsigned long temp = 0;
- unsigned int max_freq = 0;
- int update_policy = 0;
+ uint32_t max_freq = limited_max_freq;
int cpu = 0;
int ret = 0;
- tsens_dev.sensor_num = DEF_TEMP_SENSOR;
+ tsens_dev.sensor_num = msm_thermal_info.sensor_id;
ret = tsens_get_temp(&tsens_dev, &temp);
if (ret) {
pr_debug("msm_thermal: Unable to read TSENS sensor %d\n",
@@ -75,61 +66,42 @@
goto reschedule;
}
+ if (temp >= msm_thermal_info.limit_temp)
+ max_freq = msm_thermal_info.limit_freq;
+ else if (temp <
+ msm_thermal_info.limit_temp - msm_thermal_info.temp_hysteresis)
+ max_freq = MSM_CPUFREQ_NO_LIMIT;
+
+ if (max_freq == limited_max_freq)
+ goto reschedule;
+
+ /* Update new limits */
for_each_possible_cpu(cpu) {
- update_policy = 0;
- cpu_policy = cpufreq_cpu_get(cpu);
- if (!cpu_policy) {
- pr_debug("msm_thermal: NULL policy on cpu %d\n", cpu);
- continue;
- }
- if (temp >= allowed_max_high) {
- if (cpu_policy->max > allowed_max_freq) {
- update_policy = 1;
- max_freq = allowed_max_freq;
- } else {
- pr_debug("msm_thermal: policy max for cpu %d "
- "already < allowed_max_freq\n", cpu);
- }
- } else if (temp < allowed_max_low) {
- if (cpu_policy->max < cpu_policy->cpuinfo.max_freq) {
- max_freq = cpu_policy->cpuinfo.max_freq;
- update_policy = 1;
- } else {
- pr_debug("msm_thermal: policy max for cpu %d "
- "already at max allowed\n", cpu);
- }
- }
-
- if (update_policy)
- update_cpu_max_freq(cpu_policy, cpu, max_freq);
-
- cpufreq_cpu_put(cpu_policy);
+ ret = update_cpu_max_freq(cpu, max_freq);
+ if (ret)
+ pr_debug("Unable to limit cpu%d max freq to %d\n",
+ cpu, max_freq);
}
reschedule:
if (enabled)
schedule_delayed_work(&check_temp_work,
- msecs_to_jiffies(check_interval_ms));
+ msecs_to_jiffies(msm_thermal_info.poll_ms));
}
static void disable_msm_thermal(void)
{
int cpu = 0;
- struct cpufreq_policy *cpu_policy = NULL;
/* make sure check_temp is no longer running */
cancel_delayed_work(&check_temp_work);
flush_scheduled_work();
+ if (limited_max_freq == MSM_CPUFREQ_NO_LIMIT)
+ return;
+
for_each_possible_cpu(cpu) {
- cpu_policy = cpufreq_cpu_get(cpu);
- if (cpu_policy) {
- if (cpu_policy->max < cpu_policy->cpuinfo.max_freq)
- update_cpu_max_freq(cpu_policy, cpu,
- cpu_policy->
- cpuinfo.max_freq);
- cpufreq_cpu_put(cpu_policy);
- }
+ update_cpu_max_freq(cpu, MSM_CPUFREQ_NO_LIMIT);
}
}
@@ -156,16 +128,17 @@
module_param_cb(enabled, &module_ops, &enabled, 0644);
MODULE_PARM_DESC(enabled, "enforce thermal limit on cpu");
-static int __init msm_thermal_init(void)
+int __init msm_thermal_init(struct msm_thermal_data *pdata)
{
int ret = 0;
+ BUG_ON(!pdata);
+ BUG_ON(pdata->sensor_id >= TSENS_MAX_SENSORS);
+ memcpy(&msm_thermal_info, pdata, sizeof(struct msm_thermal_data));
+
enabled = 1;
INIT_DELAYED_WORK(&check_temp_work, check_temp);
-
schedule_delayed_work(&check_temp_work, 0);
return ret;
}
-fs_initcall(msm_thermal_init);
-
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 21b9669..083ed19 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -95,6 +95,8 @@
struct msm_hs_tx {
unsigned int tx_ready_int_en; /* ok to dma more tx */
unsigned int dma_in_flight; /* tx dma in progress */
+ enum flush_reason flush;
+ wait_queue_head_t wait;
struct msm_dmov_cmd xfer;
dmov_box *command_ptr;
u32 *command_ptr_ptr;
@@ -895,7 +897,7 @@
dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr,
sizeof(u32), DMA_TO_DEVICE);
-
+ msm_uport->tx.flush = FLUSH_NONE;
msm_dmov_enqueue_cmd(msm_uport->dma_tx_channel, &tx->xfer);
}
@@ -1115,9 +1117,13 @@
{
struct msm_hs_port *msm_uport;
- WARN_ON(result != 0x80000002); /* DMA did not finish properly */
-
msm_uport = container_of(cmd_ptr, struct msm_hs_port, tx.xfer);
+ if (msm_uport->tx.flush == FLUSH_STOP)
+ /* DMA FLUSH unsuccesfful */
+ WARN_ON(!(result & DMOV_RSLT_FLUSH));
+ else
+ /* DMA did not finish properly */
+ WARN_ON(!(result & DMOV_RSLT_DONE));
tasklet_schedule(&msm_uport->tx.tlet);
}
@@ -1129,6 +1135,12 @@
tlet_ptr, struct msm_hs_port, tx.tlet);
spin_lock_irqsave(&(msm_uport->uport.lock), flags);
+ if (msm_uport->tx.flush == FLUSH_STOP) {
+ msm_uport->tx.flush = FLUSH_SHUTDOWN;
+ wake_up(&msm_uport->tx.wait);
+ spin_unlock_irqrestore(&(msm_uport->uport.lock), flags);
+ return;
+ }
msm_uport->imr_reg |= UARTDM_ISR_TX_READY_BMSK;
msm_hs_write(&(msm_uport->uport), UARTDM_IMR_ADDR, msm_uport->imr_reg);
@@ -1770,6 +1782,7 @@
tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr);
init_waitqueue_head(&rx->wait);
+ init_waitqueue_head(&tx->wait);
wake_lock_init(&rx->wake_lock, WAKE_LOCK_SUSPEND, "msm_serial_hs_rx");
wake_lock_init(&msm_uport->dma_wake_lock, WAKE_LOCK_SUSPEND,
"msm_serial_hs_dma");
@@ -2043,19 +2056,41 @@
*/
static void msm_hs_shutdown(struct uart_port *uport)
{
+ int ret;
+ unsigned int data;
+ unsigned long flags;
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
- BUG_ON(msm_uport->rx.flush < FLUSH_STOP);
+ if (msm_uport->tx.dma_in_flight) {
+ spin_lock_irqsave(&uport->lock, flags);
+ /* disable UART TX interface to DM */
+ data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
+ data &= ~UARTDM_TX_DM_EN_BMSK;
+ msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
+ /* turn OFF UART Transmitter */
+ msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_DISABLE_BMSK);
+ /* reset UART TX */
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX);
+ /* reset UART TX Error */
+ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX_ERROR);
+ msm_uport->tx.flush = FLUSH_STOP;
+ spin_unlock_irqrestore(&uport->lock, flags);
+ /* discard flush */
+ msm_dmov_flush(msm_uport->dma_tx_channel, 0);
+ ret = wait_event_timeout(msm_uport->tx.wait,
+ msm_uport->tx.flush == FLUSH_SHUTDOWN, 100);
+ if (!ret)
+ pr_err("%s():HSUART TX Stalls.\n", __func__);
+ }
tasklet_kill(&msm_uport->tx.tlet);
+ BUG_ON(msm_uport->rx.flush < FLUSH_STOP);
wait_event(msm_uport->rx.wait, msm_uport->rx.flush == FLUSH_SHUTDOWN);
tasklet_kill(&msm_uport->rx.tlet);
cancel_delayed_work_sync(&msm_uport->rx.flip_insert_work);
-
flush_workqueue(msm_uport->hsuart_wq);
pm_runtime_disable(uport->dev);
pm_runtime_set_suspended(uport->dev);
- mutex_lock(&msm_uport->clk_mutex);
/* Disable the transmitter */
msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_DISABLE_BMSK);
/* Disable the receiver */
@@ -2068,7 +2103,6 @@
* Hence mb() requires here.
*/
mb();
-
if (msm_uport->clk_state != MSM_HS_CLK_OFF) {
/* to balance clk_state */
clk_disable_unprepare(msm_uport->clk);
@@ -2076,8 +2110,8 @@
clk_disable_unprepare(msm_uport->pclk);
wake_unlock(&msm_uport->dma_wake_lock);
}
- msm_uport->clk_state = MSM_HS_CLK_PORT_OFF;
+ msm_uport->clk_state = MSM_HS_CLK_PORT_OFF;
dma_unmap_single(uport->dev, msm_uport->tx.dma_base,
UART_XMIT_SIZE, DMA_TO_DEVICE);
@@ -2088,7 +2122,6 @@
free_irq(uport->irq, msm_uport);
if (use_low_power_wakeup(msm_uport))
free_irq(msm_uport->wakeup.irq, msm_uport);
- mutex_unlock(&msm_uport->clk_mutex);
mutex_destroy(&msm_uport->clk_mutex);
}
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 742beef..882eb97 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -1,6 +1,6 @@
config USB_DWC3
tristate "DesignWare USB3 DRD Core Support"
- depends on (USB && USB_GADGET)
+ depends on (USB || USB_GADGET)
select USB_OTG_UTILS
select USB_GADGET_DUALSPEED
select USB_XHCI_PLATFORM
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 3227508..0b46082 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -1,11 +1,13 @@
ccflags-$(CONFIG_USB_DWC3_DEBUG) := -DDEBUG
ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG
+ccflags-y += -Idrivers/usb/host
obj-$(CONFIG_USB_DWC3) += dwc3.o
dwc3-y := core.o
dwc3-y += host.o
dwc3-y += gadget.o ep0.o
+dwc3-y += dwc3_otg.o
ifneq ($(CONFIG_DEBUG_FS),)
dwc3-y += debugfs.o
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 99b58d84..1fbfdd8 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -372,6 +372,32 @@
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ /*
+ * Currently, the default and the recommended value for GUSB3PIPECTL
+ * [21:19] in the RTL is 3'b100 or 32 consecutive errors. Based on
+ * analysis and experiments in the lab, it is found that there is a
+ * relatively low probability of getting 32 consecutive word errors
+ * in the presence of random recovered noise (during electrical idle).
+ * This can delay the entry to a low power state such that for
+ * applications where the link stays in a non-U0 state for a short
+ * duration (< 1 microsecond), the local PHY does not enter the low
+ * power state prior to receiving a potential LFPS wakeup. This causes
+ * the PHY CDR (Clock and Data Recovery) operation to be unstable for
+ * some Synopsys PHYs.
+ *
+ * The proposal now is to change the default and the recommended value
+ * for GUSB3PIPECTL[21:19] in the RTL from 3'b100 to a minimum of
+ * 3'b001. Perform the same in software for controllers prior to 2.30a
+ * revision.
+ */
+
+ if (dwc->revision < DWC3_REVISION_230A) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+ reg &= ~DWC3_GUSB3PIPECTL_DELAY_P1P2P3;
+ reg |= 1 << __ffs(DWC3_GUSB3PIPECTL_DELAY_P1P2P3);
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+ }
+
ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
if (ret) {
dev_err(dwc->dev, "failed to allocate event buffers\n");
@@ -410,7 +436,6 @@
struct device *dev = &pdev->dev;
int ret = -ENOMEM;
- int irq;
void __iomem *regs;
void *mem;
@@ -425,16 +450,30 @@
dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
dwc->mem = mem;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
- dev_err(dev, "missing resource\n");
+ dev_err(dev, "missing IRQ\n");
return -ENODEV;
}
+ dwc->xhci_resources[1] = *res;
- dwc->res = res;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "missing memory resource\n");
+ return -ENODEV;
+ }
+ dwc->xhci_resources[0] = *res;
+ dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
+ DWC3_XHCI_REGS_END;
- res = devm_request_mem_region(dev, res->start, resource_size(res),
+ /*
+ * Request memory region but exclude xHCI regs,
+ * since it will be requested by the xhci-plat driver.
+ */
+ res = devm_request_mem_region(dev, res->start + DWC3_GLOBALS_REGS_START,
+ resource_size(res) - DWC3_GLOBALS_REGS_START,
dev_name(dev));
+
if (!res) {
dev_err(dev, "can't request mem region\n");
return -ENOMEM;
@@ -446,19 +485,12 @@
return -ENOMEM;
}
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "missing IRQ\n");
- return -ENODEV;
- }
-
spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc);
dwc->regs = regs;
dwc->regs_size = resource_size(res);
dwc->dev = dev;
- dwc->irq = irq;
if (!strncmp("super", maximum_speed, 5))
dwc->maximum_speed = DWC3_DCFG_SUPERSPEED;
@@ -505,15 +537,24 @@
break;
case DWC3_MODE_DRD:
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
+ ret = dwc3_otg_init(dwc);
+ if (ret) {
+ dev_err(dev, "failed to initialize otg\n");
+ goto err1;
+ }
+
ret = dwc3_host_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize host\n");
+ dwc3_otg_exit(dwc);
goto err1;
}
ret = dwc3_gadget_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize gadget\n");
+ dwc3_host_exit(dwc);
+ dwc3_otg_exit(dwc);
goto err1;
}
break;
@@ -542,8 +583,9 @@
dwc3_host_exit(dwc);
break;
case DWC3_MODE_DRD:
- dwc3_host_exit(dwc);
dwc3_gadget_exit(dwc);
+ dwc3_host_exit(dwc);
+ dwc3_otg_exit(dwc);
break;
default:
/* do nothing */
@@ -576,8 +618,9 @@
dwc3_host_exit(dwc);
break;
case DWC3_MODE_DRD:
- dwc3_host_exit(dwc);
dwc3_gadget_exit(dwc);
+ dwc3_host_exit(dwc);
+ dwc3_otg_exit(dwc);
break;
default:
/* do nothing */
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 6c7945b..98adff7 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -50,10 +50,13 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include "dwc3_otg.h"
+
/* Global constants */
#define DWC3_ENDPOINTS_NUM 32
+#define DWC3_XHCI_RESOURCES_NUM 2
-#define DWC3_EVENT_BUFFERS_SIZE PAGE_SIZE
+#define DWC3_EVENT_BUFFERS_SIZE (2 * PAGE_SIZE)
#define DWC3_EVENT_TYPE_MASK 0xfe
#define DWC3_EVENT_TYPE_DEV 0
@@ -70,11 +73,22 @@
#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9
#define DWC3_DEVICE_EVENT_CMD_CMPL 10
#define DWC3_DEVICE_EVENT_OVERFLOW 11
+#define DWC3_DEVICE_EVENT_VENDOR_DEV_TEST_LMP 12
#define DWC3_GEVNTCOUNT_MASK 0xfffc
#define DWC3_GSNPSID_MASK 0xffff0000
#define DWC3_GSNPSREV_MASK 0xffff
+/* DWC3 registers memory space boundries */
+#define DWC3_XHCI_REGS_START 0x0
+#define DWC3_XHCI_REGS_END 0x7fff
+#define DWC3_GLOBALS_REGS_START 0xc100
+#define DWC3_GLOBALS_REGS_END 0xc6ff
+#define DWC3_DEVICE_REGS_START 0xc700
+#define DWC3_DEVICE_REGS_END 0xcbff
+#define DWC3_OTG_REGS_START 0xcc00
+#define DWC3_OTG_REGS_END 0xccff
+
/* Global Registers */
#define DWC3_GSBUSCFG0 0xc100
#define DWC3_GSBUSCFG1 0xc104
@@ -139,8 +153,9 @@
/* OTG Registers */
#define DWC3_OCFG 0xcc00
#define DWC3_OCTL 0xcc04
-#define DWC3_OEVTEN 0xcc08
-#define DWC3_OSTS 0xcc0C
+#define DWC3_OEVT 0xcc08
+#define DWC3_OEVTEN 0xcc0c
+#define DWC3_OSTS 0xcc10
/* Bit fields */
@@ -172,6 +187,7 @@
/* Global USB3 PIPE Control Register */
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
+#define DWC3_GUSB3PIPECTL_DELAY_P1P2P3 (7 << 19)
/* Global TX Fifo Size Register */
#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
@@ -182,6 +198,9 @@
#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0
#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1
+/* Global HWPARAMS6 Register */
+#define DWC3_GHWPARAMS6_SRP_SUPPORT (1 << 10)
+
/* Device Configuration Register */
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
@@ -300,6 +319,37 @@
#define DWC3_DEPCMD_TYPE_BULK 2
#define DWC3_DEPCMD_TYPE_INTR 3
+/* OTG Events Register */
+#define DWC3_OEVT_DEVICEMODE (1 << 31)
+#define DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT (1 << 24)
+#define DWC3_OEVTEN_OTGADEVBHOSTENDEVNT (1 << 20)
+#define DWC3_OEVTEN_OTGADEVHOSTEVNT (1 << 19)
+#define DWC3_OEVTEN_OTGADEVHNPCHNGEVNT (1 << 18)
+#define DWC3_OEVTEN_OTGADEVSRPDETEVNT (1 << 17)
+#define DWC3_OEVTEN_OTGADEVSESSENDDETEVNT (1 << 16)
+#define DWC3_OEVTEN_OTGBDEVBHOSTENDEVNT (1 << 11)
+#define DWC3_OEVTEN_OTGBDEVHNPCHNGEVNT (1 << 10)
+#define DWC3_OEVTEN_OTGBDEVSESSVLDDETEVNT (1 << 9)
+#define DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT (1 << 8)
+
+/* OTG OSTS register */
+#define DWC3_OTG_OSTS_OTGSTATE_SHIFT (8)
+#define DWC3_OTG_OSTS_OTGSTATE (0xF << DWC3_OTG_OSTS_OTGSTATE_SHIFT)
+#define DWC3_OTG_OSTS_PERIPHERALSTATE (1 << 4)
+#define DWC3_OTG_OSTS_XHCIPRTPOWER (1 << 3)
+#define DWC3_OTG_OSTS_BSESVALID (1 << 2)
+#define DWC3_OTG_OSTS_VBUSVALID (1 << 1)
+#define DWC3_OTG_OSTS_CONIDSTS (1 << 0)
+
+/* OTG OSTS register */
+#define DWC3_OTG_OCTL_PERIMODE (1 << 6)
+#define DWC3_OTG_OCTL_PRTPWRCTL (1 << 5)
+#define DWC3_OTG_OCTL_HNPREQ (1 << 4)
+#define DWC3_OTG_OCTL_SESREQ (1 << 3)
+#define DWC3_OTG_OCTL_TERMSELDLPULSE (1 << 2)
+#define DWC3_OTG_OCTL_DEVSETHNPEN (1 << 1)
+#define DWC3_OTG_OCTL_HSTSETHNPEN (1 << 0)
+
/* Structures */
struct dwc3_trb;
@@ -582,8 +632,9 @@
spinlock_t lock;
struct device *dev;
+ struct dwc3_otg *dotg;
struct platform_device *xhci;
- struct resource *res;
+ struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM];
struct dwc3_event_buffer **ev_buffs;
struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
@@ -594,8 +645,6 @@
void __iomem *regs;
size_t regs_size;
- int irq;
-
u32 num_event_buffers;
u32 u1u2;
u32 maximum_speed;
@@ -609,6 +658,7 @@
#define DWC3_REVISION_185A 0x5533185a
#define DWC3_REVISION_188A 0x5533188a
#define DWC3_REVISION_190A 0x5533190a
+#define DWC3_REVISION_230A 0x5533230a
unsigned is_selfpowered:1;
unsigned three_stage_setup:1;
@@ -633,6 +683,12 @@
u8 test_mode;
u8 test_mode_nr;
+
+ /* Indicate if the gadget was powered by the otg driver */
+ bool vbus_active;
+
+ /* Indicate if software connect was issued by the usb_gadget_driver */
+ bool softconnect;
};
/* -------------------------------------------------------------------------- */
@@ -772,6 +828,9 @@
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
+int dwc3_otg_init(struct dwc3 *dwc);
+void dwc3_otg_exit(struct dwc3 *dwc);
+
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 5a79cae..d216f17 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -17,6 +17,7 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/ioport.h>
+#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -30,6 +31,7 @@
#include <mach/rpm-regulator.h>
+#include "dwc3_otg.h"
#include "core.h"
#include "gadget.h"
@@ -87,6 +89,16 @@
#define DBM_TRB_DATA_SRC 0x40000000
#define DBM_TRB_DMA 0x20000000
#define DBM_TRB_EP_NUM(ep) (ep<<24)
+/**
+ * USB QSCRATCH Hardware registers
+ *
+ */
+#define QSCRATCH_REG_OFFSET (0x000F8800)
+#define CHARGING_DET_CTRL_REG (QSCRATCH_REG_OFFSET + 0x18)
+#define CHARGING_DET_OUTPUT_REG (QSCRATCH_REG_OFFSET + 0x1C)
+#define ALT_INTERRUPT_EN_REG (QSCRATCH_REG_OFFSET + 0x20)
+#define HS_PHY_IRQ_STAT_REG (QSCRATCH_REG_OFFSET + 0x24)
+
struct dwc3_msm_req_complete {
struct list_head list_item;
@@ -104,6 +116,7 @@
u8 ep_num_mapping[DBM_MAX_EPS];
const struct usb_ep_ops *original_ep_ops[DWC3_ENDPOINTS_NUM];
struct list_head req_complete_list;
+ struct clk *core_clk;
struct regulator *hsusb_3p3;
struct regulator *hsusb_1p8;
struct regulator *hsusb_vddcx;
@@ -111,6 +124,11 @@
struct regulator *ssusb_vddcx;
enum usb_vdd_type ss_vdd_type;
enum usb_vdd_type hs_vdd_type;
+ struct dwc3_charger charger;
+ struct usb_phy *otg_xceiv;
+ struct delayed_work chg_work;
+ enum usb_chg_state chg_state;
+ u8 dcd_retries;
};
#define USB_HSPHY_3P3_VOL_MIN 3050000 /* uV */
@@ -150,6 +168,7 @@
};
static struct dwc3_msm *context;
+static u64 dwc3_msm_dma_mask = DMA_BIT_MASK(64);
/**
*
@@ -221,6 +240,34 @@
}
/**
+ * Write register and read back masked value to confirm it is written
+ *
+ * @base - DWC3 base virtual address.
+ * @offset - register offset.
+ * @mask - register bitmask specifying what should be updated
+ * @val - value to write.
+ *
+ */
+static inline void dwc3_msm_write_readback(void *base, u32 offset,
+ const u32 mask, u32 val)
+{
+ u32 write_val, tmp = ioread32(base + offset);
+
+ tmp &= ~mask; /* retain other bits */
+ write_val = tmp | val;
+
+ iowrite32(write_val, base + offset);
+
+ /* Read back to see if val was written */
+ tmp = ioread32(base + offset);
+ tmp &= mask; /* clear other bits */
+
+ if (tmp != val)
+ dev_err(context->dev, "%s: write: %x to QSCRATCH: %x FAILED\n",
+ __func__, val, offset);
+}
+
+/**
* Return DBM EP number which is not already configured.
*
*/
@@ -996,6 +1043,184 @@
return rc < 0 ? rc : 0;
}
+static void dwc3_chg_enable_secondary_det(struct dwc3_msm *mdwc)
+{
+ u32 chg_ctrl;
+
+ /* Turn off VDP_SRC */
+ dwc3_msm_write_reg(mdwc->base, CHARGING_DET_CTRL_REG, 0x0);
+ msleep(20);
+
+ /* Before proceeding make sure VDP_SRC is OFF */
+ chg_ctrl = dwc3_msm_read_reg(mdwc->base, CHARGING_DET_CTRL_REG);
+ if (chg_ctrl & 0x3F)
+ dev_err(mdwc->dev, "%s Unable to reset chg_det block: %x\n",
+ __func__, chg_ctrl);
+ /*
+ * Configure DM as current source, DP as current sink
+ * and enable battery charging comparators.
+ */
+ dwc3_msm_write_readback(mdwc->base, CHARGING_DET_CTRL_REG, 0x3F, 0x34);
+}
+
+static bool dwc3_chg_det_check_output(struct dwc3_msm *mdwc)
+{
+ u32 chg_det;
+ bool ret = false;
+
+ chg_det = dwc3_msm_read_reg(mdwc->base, CHARGING_DET_OUTPUT_REG);
+ ret = chg_det & 1;
+
+ return ret;
+}
+
+static void dwc3_chg_enable_primary_det(struct dwc3_msm *mdwc)
+{
+ /*
+ * Configure DP as current source, DM as current sink
+ * and enable battery charging comparators.
+ */
+ dwc3_msm_write_readback(mdwc->base, CHARGING_DET_CTRL_REG, 0x3F, 0x30);
+}
+
+static inline bool dwc3_chg_check_dcd(struct dwc3_msm *mdwc)
+{
+ u32 chg_state;
+ bool ret = false;
+
+ chg_state = dwc3_msm_read_reg(mdwc->base, CHARGING_DET_OUTPUT_REG);
+ ret = chg_state & 2;
+
+ return ret;
+}
+
+static inline void dwc3_chg_disable_dcd(struct dwc3_msm *mdwc)
+{
+ dwc3_msm_write_readback(mdwc->base, CHARGING_DET_CTRL_REG, 0x3F, 0x0);
+}
+
+static inline void dwc3_chg_enable_dcd(struct dwc3_msm *mdwc)
+{
+ /* Data contact detection enable, DCDENB */
+ dwc3_msm_write_readback(mdwc->base, CHARGING_DET_CTRL_REG, 0x3F, 0x2);
+}
+
+static void dwc3_chg_block_reset(struct dwc3_msm *mdwc)
+{
+ u32 chg_ctrl;
+
+ /* Clear charger detecting control bits */
+ dwc3_msm_write_reg(mdwc->base, CHARGING_DET_CTRL_REG, 0x0);
+
+ /* Clear alt interrupt latch and enable bits */
+ dwc3_msm_write_reg(mdwc->base, HS_PHY_IRQ_STAT_REG, 0xFFF);
+ dwc3_msm_write_reg(mdwc->base, ALT_INTERRUPT_EN_REG, 0x0);
+
+ udelay(100);
+
+ /* Before proceeding make sure charger block is RESET */
+ chg_ctrl = dwc3_msm_read_reg(mdwc->base, CHARGING_DET_CTRL_REG);
+ if (chg_ctrl & 0x3F)
+ dev_err(mdwc->dev, "%s Unable to reset chg_det block: %x\n",
+ __func__, chg_ctrl);
+}
+
+static const char *chg_to_string(enum dwc3_chg_type chg_type)
+{
+ switch (chg_type) {
+ case USB_SDP_CHARGER: return "USB_SDP_CHARGER";
+ case USB_DCP_CHARGER: return "USB_DCP_CHARGER";
+ case USB_CDP_CHARGER: return "USB_CDP_CHARGER";
+ default: return "INVALID_CHARGER";
+ }
+}
+
+#define DWC3_CHG_DCD_POLL_TIME (100 * HZ/1000) /* 100 msec */
+#define DWC3_CHG_DCD_MAX_RETRIES 6 /* Tdcd_tmout = 6 * 100 msec */
+#define DWC3_CHG_PRIMARY_DET_TIME (50 * HZ/1000) /* TVDPSRC_ON */
+#define DWC3_CHG_SECONDARY_DET_TIME (50 * HZ/1000) /* TVDMSRC_ON */
+
+static void dwc3_chg_detect_work(struct work_struct *w)
+{
+ struct dwc3_msm *mdwc = container_of(w, struct dwc3_msm, chg_work.work);
+ bool is_dcd = false, tmout, vout;
+ unsigned long delay;
+
+ dev_dbg(mdwc->dev, "chg detection work\n");
+ switch (mdwc->chg_state) {
+ case USB_CHG_STATE_UNDEFINED:
+ dwc3_chg_block_reset(mdwc);
+ dwc3_chg_enable_dcd(mdwc);
+ mdwc->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
+ mdwc->dcd_retries = 0;
+ delay = DWC3_CHG_DCD_POLL_TIME;
+ break;
+ case USB_CHG_STATE_WAIT_FOR_DCD:
+ is_dcd = dwc3_chg_check_dcd(mdwc);
+ tmout = ++mdwc->dcd_retries == DWC3_CHG_DCD_MAX_RETRIES;
+ if (is_dcd || tmout) {
+ dwc3_chg_disable_dcd(mdwc);
+ dwc3_chg_enable_primary_det(mdwc);
+ delay = DWC3_CHG_PRIMARY_DET_TIME;
+ mdwc->chg_state = USB_CHG_STATE_DCD_DONE;
+ } else {
+ delay = DWC3_CHG_DCD_POLL_TIME;
+ }
+ break;
+ case USB_CHG_STATE_DCD_DONE:
+ vout = dwc3_chg_det_check_output(mdwc);
+ if (vout) {
+ dwc3_chg_enable_secondary_det(mdwc);
+ delay = DWC3_CHG_SECONDARY_DET_TIME;
+ mdwc->chg_state = USB_CHG_STATE_PRIMARY_DONE;
+ } else {
+ mdwc->charger.chg_type = USB_SDP_CHARGER;
+ mdwc->chg_state = USB_CHG_STATE_DETECTED;
+ delay = 0;
+ }
+ break;
+ case USB_CHG_STATE_PRIMARY_DONE:
+ vout = dwc3_chg_det_check_output(mdwc);
+ if (vout)
+ mdwc->charger.chg_type = USB_DCP_CHARGER;
+ else
+ mdwc->charger.chg_type = USB_CDP_CHARGER;
+ mdwc->chg_state = USB_CHG_STATE_SECONDARY_DONE;
+ /* fall through */
+ case USB_CHG_STATE_SECONDARY_DONE:
+ mdwc->chg_state = USB_CHG_STATE_DETECTED;
+ /* fall through */
+ case USB_CHG_STATE_DETECTED:
+ dwc3_chg_block_reset(mdwc);
+ dev_dbg(mdwc->dev, "chg_type = %s\n",
+ chg_to_string(mdwc->charger.chg_type));
+ mdwc->charger.notify_detection_complete(mdwc->otg_xceiv->otg,
+ &mdwc->charger);
+ return;
+ default:
+ return;
+ }
+
+ queue_delayed_work(system_nrt_wq, &mdwc->chg_work, delay);
+}
+
+static void dwc3_start_chg_det(struct dwc3_charger *charger, bool start)
+{
+ struct dwc3_msm *mdwc = context;
+
+ if (start == false) {
+ cancel_delayed_work_sync(&mdwc->chg_work);
+ mdwc->chg_state = USB_CHG_STATE_UNDEFINED;
+ charger->chg_type = DWC3_INVALID_CHARGER;
+ return;
+ }
+
+ mdwc->chg_state = USB_CHG_STATE_UNDEFINED;
+ charger->chg_type = DWC3_INVALID_CHARGER;
+ queue_delayed_work(system_nrt_wq, &mdwc->chg_work, 0);
+}
+
+
static int __devinit dwc3_msm_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@@ -1015,6 +1240,19 @@
msm->dev = &pdev->dev;
INIT_LIST_HEAD(&msm->req_complete_list);
+ INIT_DELAYED_WORK(&msm->chg_work, dwc3_chg_detect_work);
+
+ /*
+ * DWC3 Core requires its CORE CLK (aka master / bus clk) to
+ * run at 125Mhz in SSUSB mode and >60MHZ for HSUSB mode.
+ */
+ msm->core_clk = devm_clk_get(&pdev->dev, "core_clk");
+ if (IS_ERR(msm->core_clk)) {
+ dev_err(&pdev->dev, "failed to get core_clk\n");
+ return PTR_ERR(msm->core_clk);
+ }
+ clk_set_rate(msm->core_clk, 125000000);
+ clk_prepare_enable(msm->core_clk);
/* SS PHY */
msm->ss_vdd_type = VDDCX_CORNER;
@@ -1024,7 +1262,8 @@
"SSUSB_VDDCX");
if (IS_ERR(msm->ssusb_vddcx)) {
dev_err(&pdev->dev, "unable to get ssusb vddcx\n");
- return PTR_ERR(msm->ssusb_vddcx);
+ ret = PTR_ERR(msm->ssusb_vddcx);
+ goto disable_core_clk;
}
msm->ss_vdd_type = VDDCX;
dev_dbg(&pdev->dev, "ss_vdd_type: VDDCX\n");
@@ -1033,7 +1272,7 @@
ret = dwc3_ssusb_config_vddcx(1);
if (ret) {
dev_err(&pdev->dev, "ssusb vddcx configuration failed\n");
- return ret;
+ goto disable_core_clk;
}
ret = regulator_enable(context->ssusb_vddcx);
@@ -1115,10 +1354,9 @@
goto disable_hs_ldo;
}
- dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask);
-
dwc3->dev.parent = &pdev->dev;
- dwc3->dev.dma_mask = pdev->dev.dma_mask;
+ dwc3->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+ dwc3->dev.dma_mask = &dwc3_msm_dma_mask;
dwc3->dev.dma_parms = pdev->dev.dma_parms;
msm->resource_size = resource_size(res);
msm->dwc3 = dwc3;
@@ -1155,8 +1393,24 @@
/* Reset the DBM */
dwc3_msm_dbm_soft_reset();
+ msm->otg_xceiv = usb_get_transceiver();
+ if (msm->otg_xceiv) {
+ msm->charger.start_detection = dwc3_start_chg_det;
+ ret = dwc3_set_charger(msm->otg_xceiv->otg, &msm->charger);
+ if (ret || !msm->charger.notify_detection_complete) {
+ dev_err(&pdev->dev, "failed to register charger: %d\n",
+ ret);
+ goto put_xcvr;
+ }
+ } else {
+ dev_err(&pdev->dev, "%s: No OTG transceiver found\n", __func__);
+ }
+
return 0;
+put_xcvr:
+ usb_put_transceiver(msm->otg_xceiv);
+ platform_device_del(dwc3);
put_pdev:
platform_device_put(dwc3);
disable_hs_ldo:
@@ -1175,6 +1429,8 @@
regulator_disable(context->ssusb_vddcx);
unconfig_ss_vddcx:
dwc3_ssusb_config_vddcx(0);
+disable_core_clk:
+ clk_disable_unprepare(msm->core_clk);
return ret;
}
@@ -1183,6 +1439,10 @@
{
struct dwc3_msm *msm = platform_get_drvdata(pdev);
+ if (msm->otg_xceiv) {
+ dwc3_start_chg_det(&msm->charger, false);
+ usb_put_transceiver(msm->otg_xceiv);
+ }
platform_device_unregister(msm->dwc3);
dwc3_hsusb_ldo_enable(0);
@@ -1193,6 +1453,7 @@
dwc3_ssusb_ldo_init(0);
regulator_disable(msm->ssusb_vddcx);
dwc3_ssusb_config_vddcx(0);
+ clk_disable_unprepare(msm->core_clk);
return 0;
}
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index d7d9c0e..a5c77ad 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -49,7 +49,6 @@
#include <linux/of.h>
#include "core.h"
-#include "io.h"
/*
* All these registers belong to OMAP's Wrapper around the
@@ -143,6 +142,17 @@
u32 dma_status:1;
};
+static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset)
+{
+ return readl_relaxed(base + offset);
+}
+
+static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
+{
+ writel_relaxed(value, base + offset);
+}
+
+
static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
{
struct dwc3_omap *omap = _omap;
@@ -150,7 +160,7 @@
spin_lock(&omap->lock);
- reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_1);
+ reg = dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_1);
if (reg & USBOTGSS_IRQ1_DMADISABLECLR) {
dev_dbg(omap->dev, "DMA Disable was Cleared\n");
@@ -184,10 +194,10 @@
if (reg & USBOTGSS_IRQ1_IDPULLUP_FALL)
dev_dbg(omap->dev, "IDPULLUP Fall\n");
- dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_1, reg);
+ dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_1, reg);
- reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_0);
- dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_0, reg);
+ reg = dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_0);
+ dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_0, reg);
spin_unlock(&omap->lock);
@@ -270,7 +280,7 @@
omap->base = base;
omap->dwc3 = dwc3;
- reg = dwc3_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
+ reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
utmi_mode = of_get_property(node, "utmi-mode", &size);
if (utmi_mode && size == sizeof(*utmi_mode)) {
@@ -293,10 +303,10 @@
}
}
- dwc3_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg);
+ dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg);
/* check the DMA Status */
- reg = dwc3_readl(omap->base, USBOTGSS_SYSCONFIG);
+ reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);
/* Set No-Idle and No-Standby */
@@ -306,7 +316,7 @@
reg |= (USBOTGSS_SYSCONFIG_STANDBYMODE(USBOTGSS_STANDBYMODE_NO_STANDBY)
| USBOTGSS_SYSCONFIG_IDLEMODE(USBOTGSS_IDLEMODE_NO_IDLE));
- dwc3_writel(omap->base, USBOTGSS_SYSCONFIG, reg);
+ dwc3_omap_writel(omap->base, USBOTGSS_SYSCONFIG, reg);
ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
"dwc3-omap", omap);
@@ -318,7 +328,7 @@
/* enable all IRQs */
reg = USBOTGSS_IRQO_COREIRQ_ST;
- dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg);
+ dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg);
reg = (USBOTGSS_IRQ1_OEVT |
USBOTGSS_IRQ1_DRVVBUS_RISE |
@@ -330,7 +340,7 @@
USBOTGSS_IRQ1_DISCHRGVBUS_FALL |
USBOTGSS_IRQ1_IDPULLUP_FALL);
- dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
+ dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
ret = platform_device_add_resources(dwc3, pdev->resource,
pdev->num_resources);
diff --git a/drivers/usb/dwc3/dwc3_otg.c b/drivers/usb/dwc3/dwc3_otg.c
new file mode 100644
index 0000000..5df030a
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3_otg.c
@@ -0,0 +1,664 @@
+/**
+ * dwc3_otg.c - DesignWare USB3 DRD Controller OTG
+ *
+ * Copyright (c) 2012, Code Aurora Forum. 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/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/platform_device.h>
+
+#include "core.h"
+#include "dwc3_otg.h"
+#include "io.h"
+#include "xhci.h"
+
+
+/**
+ * dwc3_otg_set_host_regs - reset dwc3 otg registers to host operation.
+ *
+ * This function sets the OTG registers to work in A-Device host mode.
+ * This function should be called just before entering to A-Device mode.
+ *
+ * @w: Pointer to the dwc3 otg workqueue.
+ */
+static void dwc3_otg_set_host_regs(struct dwc3_otg *dotg)
+{
+ u32 octl;
+
+ /* Set OCTL[6](PeriMode) to 0 (host) */
+ octl = dwc3_readl(dotg->regs, DWC3_OCTL);
+ octl &= ~DWC3_OTG_OCTL_PERIMODE;
+ dwc3_writel(dotg->regs, DWC3_OCTL, octl);
+
+ /*
+ * TODO: add more OTG registers writes for HOST mode here,
+ * see figure 12-10 A-device flow in dwc3 Synopsis spec
+ */
+}
+
+/**
+ * dwc3_otg_set_peripheral_regs - reset dwc3 otg registers to peripheral operation.
+ *
+ * This function sets the OTG registers to work in B-Device peripheral mode.
+ * This function should be called just before entering to B-Device mode.
+ *
+ * @w: Pointer to the dwc3 otg workqueue.
+ */
+static void dwc3_otg_set_peripheral_regs(struct dwc3_otg *dotg)
+{
+ u32 octl;
+
+ /* Set OCTL[6](PeriMode) to 1 (peripheral) */
+ octl = dwc3_readl(dotg->regs, DWC3_OCTL);
+ octl |= DWC3_OTG_OCTL_PERIMODE;
+ dwc3_writel(dotg->regs, DWC3_OCTL, octl);
+
+ /*
+ * TODO: add more OTG registers writes for PERIPHERAL mode here,
+ * see figure 12-19 B-device flow in dwc3 Synopsis spec
+ */
+}
+
+/**
+ * dwc3_otg_start_host - helper function for starting/stoping the host controller driver.
+ *
+ * @otg: Pointer to the otg_transceiver structure.
+ * @on: start / stop the host controller driver.
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+static int dwc3_otg_start_host(struct usb_otg *otg, int on)
+{
+ struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
+ struct usb_hcd *hcd;
+ struct xhci_hcd *xhci;
+ int ret = 0;
+
+ if (!otg->host)
+ return -EINVAL;
+
+ hcd = bus_to_hcd(otg->host);
+ xhci = hcd_to_xhci(hcd);
+ if (on) {
+ dev_dbg(otg->phy->dev, "%s: turn on host %s\n",
+ __func__, otg->host->bus_name);
+ dwc3_otg_set_host_regs(dotg);
+
+ /*
+ * This should be revisited for more testing post-silicon.
+ * In worst case we may need to disconnect the root hub
+ * before stopping the controller so that it does not
+ * interfere with runtime pm/system pm.
+ * We can also consider registering and unregistering xhci
+ * platform device. It is almost similar to add_hcd and
+ * remove_hcd, But we may not use standard set_host method
+ * anymore.
+ */
+ ret = hcd->driver->start(hcd);
+ if (ret) {
+ dev_err(otg->phy->dev,
+ "%s: failed to start primary hcd, ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = xhci->shared_hcd->driver->start(xhci->shared_hcd);
+ if (ret) {
+ dev_err(otg->phy->dev,
+ "%s: failed to start secondary hcd, ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+ } else {
+ dev_dbg(otg->phy->dev, "%s: turn off host %s\n",
+ __func__, otg->host->bus_name);
+ hcd->driver->stop(hcd);
+ }
+
+ return 0;
+}
+
+/**
+ * dwc3_otg_set_host - bind/unbind the host controller driver.
+ *
+ * @otg: Pointer to the otg_transceiver structure.
+ * @host: Pointer to the usb_bus structure.
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+static int dwc3_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+ struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
+
+ if (host) {
+ dev_dbg(otg->phy->dev, "%s: set host %s\n",
+ __func__, host->bus_name);
+ otg->host = host;
+
+ /*
+ * Only after both peripheral and host are set then check
+ * OTG sm. This prevents unnecessary activation of the sm
+ * in case the ID is high.
+ */
+ if (otg->gadget)
+ schedule_work(&dotg->sm_work);
+ } else {
+ if (otg->phy->state == OTG_STATE_A_HOST) {
+ dwc3_otg_start_host(otg, 0);
+ otg->host = NULL;
+ otg->phy->state = OTG_STATE_UNDEFINED;
+ schedule_work(&dotg->sm_work);
+ } else {
+ otg->host = NULL;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * dwc3_otg_start_peripheral - bind/unbind the peripheral controller.
+ *
+ * @otg: Pointer to the otg_transceiver structure.
+ * @gadget: pointer to the usb_gadget structure.
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+static int dwc3_otg_start_peripheral(struct usb_otg *otg, int on)
+{
+ struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
+
+ if (!otg->gadget)
+ return -EINVAL;
+
+ if (on) {
+ dev_dbg(otg->phy->dev, "%s: turn on gadget %s\n",
+ __func__, otg->gadget->name);
+ dwc3_otg_set_peripheral_regs(dotg);
+ usb_gadget_vbus_connect(otg->gadget);
+ } else {
+ dev_dbg(otg->phy->dev, "%s: turn off gadget %s\n",
+ __func__, otg->gadget->name);
+ usb_gadget_vbus_disconnect(otg->gadget);
+ }
+
+ return 0;
+}
+
+/**
+ * dwc3_otg_set_peripheral - bind/unbind the peripheral controller driver.
+ *
+ * @otg: Pointer to the otg_transceiver structure.
+ * @gadget: pointer to the usb_gadget structure.
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+static int dwc3_otg_set_peripheral(struct usb_otg *otg,
+ struct usb_gadget *gadget)
+{
+ struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
+
+ if (gadget) {
+ dev_dbg(otg->phy->dev, "%s: set gadget %s\n",
+ __func__, gadget->name);
+ otg->gadget = gadget;
+
+ /*
+ * Only after both peripheral and host are set then check
+ * OTG sm. This prevents unnecessary activation of the sm
+ * in case the ID is grounded.
+ */
+ if (otg->host)
+ schedule_work(&dotg->sm_work);
+ } else {
+ if (otg->phy->state == OTG_STATE_B_PERIPHERAL) {
+ dwc3_otg_start_peripheral(otg, 0);
+ otg->gadget = NULL;
+ otg->phy->state = OTG_STATE_UNDEFINED;
+ schedule_work(&dotg->sm_work);
+ } else {
+ otg->gadget = NULL;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * dwc3_ext_chg_det_done - callback to handle charger detection completion
+ * @otg: Pointer to the otg transceiver structure
+ * @charger: Pointer to the external charger structure
+ *
+ * Returns 0 on success
+ */
+static void dwc3_ext_chg_det_done(struct usb_otg *otg, struct dwc3_charger *chg)
+{
+ struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
+
+ /*
+ * Ignore chg_detection notification if BSV has gone off by this time.
+ * STOP chg_det as part of !BSV handling would reset the chg_det flags
+ */
+ if (test_bit(B_SESS_VLD, &dotg->inputs))
+ schedule_work(&dotg->sm_work);
+}
+
+/**
+ * dwc3_set_charger - bind/unbind external charger driver
+ * @otg: Pointer to the otg transceiver structure
+ * @charger: Pointer to the external charger structure
+ *
+ * Returns 0 on success
+ */
+int dwc3_set_charger(struct usb_otg *otg, struct dwc3_charger *charger)
+{
+ struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
+
+ dotg->charger = charger;
+ if (charger)
+ charger->notify_detection_complete = dwc3_ext_chg_det_done;
+
+ return 0;
+}
+
+/* IRQs which OTG driver is interested in handling */
+#define DWC3_OEVT_MASK (DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT | \
+ DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT)
+
+/**
+ * dwc3_otg_interrupt - interrupt handler for dwc3 otg events.
+ * @_dotg: Pointer to out controller context structure
+ *
+ * Returns IRQ_HANDLED on success otherwise IRQ_NONE.
+ */
+static irqreturn_t dwc3_otg_interrupt(int irq, void *_dotg)
+{
+ struct dwc3_otg *dotg = (struct dwc3_otg *)_dotg;
+ u32 osts, oevt_reg;
+ int ret = IRQ_NONE;
+ int handled_irqs = 0;
+
+ oevt_reg = dwc3_readl(dotg->regs, DWC3_OEVT);
+
+ if (!(oevt_reg & DWC3_OEVT_MASK))
+ return IRQ_NONE;
+
+ osts = dwc3_readl(dotg->regs, DWC3_OSTS);
+
+ if ((oevt_reg & DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT) ||
+ (oevt_reg & DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT)) {
+ /*
+ * ID sts has changed, set inputs later, in the workqueue
+ * function, switch from A to B or from B to A.
+ */
+
+ if (osts & DWC3_OTG_OSTS_CONIDSTS)
+ set_bit(ID, &dotg->inputs);
+ else
+ clear_bit(ID, &dotg->inputs);
+
+ if (osts & DWC3_OTG_OSTS_BSESVALID)
+ set_bit(B_SESS_VLD, &dotg->inputs);
+ else
+ clear_bit(B_SESS_VLD, &dotg->inputs);
+
+ schedule_work(&dotg->sm_work);
+
+ handled_irqs |= (oevt_reg & DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT) ?
+ DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT : 0;
+ handled_irqs |= (oevt_reg & DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT) ?
+ DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT : 0;
+
+ ret = IRQ_HANDLED;
+
+ /* Clear the interrupts we handled */
+ dwc3_writel(dotg->regs, DWC3_OEVT, handled_irqs);
+ }
+
+ return ret;
+}
+
+/**
+ * dwc3_otg_init_sm - initialize OTG statemachine input
+ * @dotg: Pointer to the dwc3_otg structure
+ *
+ */
+void dwc3_otg_init_sm(struct dwc3_otg *dotg)
+{
+ u32 osts = dwc3_readl(dotg->regs, DWC3_OSTS);
+ struct usb_phy *phy = dotg->otg.phy;
+
+ /*
+ * TODO: If using external notifications then wait here till initial
+ * state is reported
+ */
+
+ dev_dbg(phy->dev, "Initialize OTG inputs, osts: 0x%x\n", osts);
+
+ if (osts & DWC3_OTG_OSTS_CONIDSTS)
+ set_bit(ID, &dotg->inputs);
+ else
+ clear_bit(ID, &dotg->inputs);
+
+ if (osts & DWC3_OTG_OSTS_BSESVALID)
+ set_bit(B_SESS_VLD, &dotg->inputs);
+ else
+ clear_bit(B_SESS_VLD, &dotg->inputs);
+}
+
+/**
+ * dwc3_otg_sm_work - workqueue function.
+ *
+ * @w: Pointer to the dwc3 otg workqueue
+ *
+ * NOTE: After any change in phy->state,
+ * we must reschdule the state machine.
+ */
+static void dwc3_otg_sm_work(struct work_struct *w)
+{
+ struct dwc3_otg *dotg = container_of(w, struct dwc3_otg, sm_work);
+ struct usb_phy *phy = dotg->otg.phy;
+ struct dwc3_charger *charger = dotg->charger;
+ bool work = 0;
+
+ dev_dbg(phy->dev, "%s state\n", otg_state_string(phy->state));
+
+ /* Check OTG state */
+ switch (phy->state) {
+ case OTG_STATE_UNDEFINED:
+ dwc3_otg_init_sm(dotg);
+ /* Switch to A or B-Device according to ID / BSV */
+ if (!test_bit(ID, &dotg->inputs) && phy->otg->host) {
+ dev_dbg(phy->dev, "!id\n");
+ phy->state = OTG_STATE_A_IDLE;
+ work = 1;
+ } else if (test_bit(B_SESS_VLD, &dotg->inputs)) {
+ dev_dbg(phy->dev, "b_sess_vld\n");
+ phy->state = OTG_STATE_B_IDLE;
+ work = 1;
+ } else {
+ phy->state = OTG_STATE_B_IDLE;
+ /* TODO: Enter low power state */
+ }
+ break;
+
+ case OTG_STATE_B_IDLE:
+ if (!test_bit(ID, &dotg->inputs) && phy->otg->host) {
+ dev_dbg(phy->dev, "!id\n");
+ phy->state = OTG_STATE_A_IDLE;
+ work = 1;
+ if (charger) {
+ if (charger->chg_type == DWC3_INVALID_CHARGER)
+ charger->start_detection(dotg->charger,
+ false);
+ else
+ charger->chg_type =
+ DWC3_INVALID_CHARGER;
+ }
+ } else if (test_bit(B_SESS_VLD, &dotg->inputs)) {
+ dev_dbg(phy->dev, "b_sess_vld\n");
+ if (charger) {
+ /* Has charger been detected? If no detect it */
+ switch (charger->chg_type) {
+ case DWC3_DCP_CHARGER:
+ /* TODO: initiate LPM */
+ break;
+ case DWC3_CDP_CHARGER:
+ dwc3_otg_start_peripheral(&dotg->otg,
+ 1);
+ phy->state = OTG_STATE_B_PERIPHERAL;
+ work = 1;
+ break;
+ case DWC3_SDP_CHARGER:
+ dwc3_otg_start_peripheral(&dotg->otg,
+ 1);
+ phy->state = OTG_STATE_B_PERIPHERAL;
+ work = 1;
+ break;
+ default:
+ dev_dbg(phy->dev, "chg_det started\n");
+ charger->start_detection(charger, true);
+ break;
+ }
+ } else {
+ /* no charger registered, start peripheral */
+ if (dwc3_otg_start_peripheral(&dotg->otg, 1)) {
+ /*
+ * Probably set_peripheral not called
+ * yet. We will re-try as soon as it
+ * will be called
+ */
+ dev_err(phy->dev,
+ "unable to start B-device\n");
+ phy->state = OTG_STATE_UNDEFINED;
+ return;
+ }
+ }
+ } else {
+ if (charger) {
+ if (charger->chg_type == DWC3_INVALID_CHARGER)
+ charger->start_detection(dotg->charger,
+ false);
+ else
+ charger->chg_type =
+ DWC3_INVALID_CHARGER;
+ }
+ /* TODO: Enter low power state */
+ }
+ break;
+
+ case OTG_STATE_B_PERIPHERAL:
+ if (!test_bit(B_SESS_VLD, &dotg->inputs) ||
+ !test_bit(ID, &dotg->inputs)) {
+ dev_dbg(phy->dev, "!id || !bsv\n");
+ dwc3_otg_start_peripheral(&dotg->otg, 0);
+ phy->state = OTG_STATE_B_IDLE;
+ if (charger)
+ charger->chg_type = DWC3_INVALID_CHARGER;
+ work = 1;
+ }
+ break;
+
+ case OTG_STATE_A_IDLE:
+ /* Switch to A-Device*/
+ if (test_bit(ID, &dotg->inputs)) {
+ dev_dbg(phy->dev, "id\n");
+ phy->state = OTG_STATE_B_IDLE;
+ work = 1;
+ } else {
+ if (dwc3_otg_start_host(&dotg->otg, 1)) {
+ /*
+ * Probably set_host was not called yet.
+ * We will re-try as soon as it will be called
+ */
+ dev_dbg(phy->dev,
+ "unable to start A-device\n");
+ phy->state = OTG_STATE_UNDEFINED;
+ return;
+ }
+ phy->state = OTG_STATE_A_HOST;
+ }
+ break;
+
+ case OTG_STATE_A_HOST:
+ if (test_bit(ID, &dotg->inputs)) {
+ dev_dbg(phy->dev, "id\n");
+ dwc3_otg_start_host(&dotg->otg, 0);
+ phy->state = OTG_STATE_B_IDLE;
+ work = 1;
+ }
+ break;
+
+ default:
+ dev_err(phy->dev, "%s: invalid otg-state\n", __func__);
+
+ }
+
+ if (work)
+ schedule_work(&dotg->sm_work);
+}
+
+
+/**
+ * dwc3_otg_reset - reset dwc3 otg registers.
+ *
+ * @w: Pointer to the dwc3 otg workqueue
+ */
+static void dwc3_otg_reset(struct dwc3_otg *dotg)
+{
+ /*
+ * OCFG[2] - OTG-Version = 1
+ * OCFG[1] - HNPCap = 0
+ * OCFG[0] - SRPCap = 0
+ */
+ dwc3_writel(dotg->regs, DWC3_OCFG, 0x4);
+
+ /*
+ * OCTL[6] - PeriMode = 1
+ * OCTL[5] - PrtPwrCtl = 0
+ * OCTL[4] - HNPReq = 0
+ * OCTL[3] - SesReq = 0
+ * OCTL[2] - TermSelDLPulse = 0
+ * OCTL[1] - DevSetHNPEn = 0
+ * OCTL[0] - HstSetHNPEn = 0
+ */
+ dwc3_writel(dotg->regs, DWC3_OCTL, 0x40);
+
+ /* Clear all otg events (interrupts) indications */
+ dwc3_writel(dotg->regs, DWC3_OEVT, 0xFFFF);
+
+ /* Enable ID/BSV StsChngEn event*/
+ dwc3_writel(dotg->regs, DWC3_OEVTEN,
+ DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT |
+ DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT);
+}
+
+/**
+ * dwc3_otg_init - Initializes otg related registers
+ * @dwc: Pointer to out controller context structure
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+int dwc3_otg_init(struct dwc3 *dwc)
+{
+ u32 reg;
+ int ret = 0;
+ struct dwc3_otg *dotg;
+
+ dev_dbg(dwc->dev, "dwc3_otg_init\n");
+
+ /*
+ * GHWPARAMS6[10] bit is SRPSupport.
+ * This bit also reflects DWC_USB3_EN_OTG
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6);
+ if (!(reg & DWC3_GHWPARAMS6_SRP_SUPPORT)) {
+ /*
+ * No OTG support in the HW core.
+ * We return 0 to indicate no error, since this is acceptable
+ * situation, just continue probe the dwc3 driver without otg.
+ */
+ dev_dbg(dwc->dev, "dwc3_otg address space is not supported\n");
+ return 0;
+ }
+
+ /* Allocate and init otg instance */
+ dotg = kzalloc(sizeof(struct dwc3_otg), GFP_KERNEL);
+ if (!dotg) {
+ dev_err(dwc->dev, "unable to allocate dwc3_otg\n");
+ return -ENOMEM;
+ }
+
+ dotg->irq = platform_get_irq(to_platform_device(dwc->dev), 0);
+ if (dotg->irq < 0) {
+ dev_err(dwc->dev, "%s: missing IRQ\n", __func__);
+ ret = -ENODEV;
+ goto err1;
+ }
+
+ dotg->regs = dwc->regs;
+
+ dotg->otg.set_peripheral = dwc3_otg_set_peripheral;
+ dotg->otg.set_host = dwc3_otg_set_host;
+
+ /* This reference is used by dwc3 modules for checking otg existance */
+ dwc->dotg = dotg;
+
+ dotg->otg.phy = kzalloc(sizeof(struct usb_phy), GFP_KERNEL);
+ if (!dotg->otg.phy) {
+ dev_err(dwc->dev, "unable to allocate dwc3_otg.phy\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ dotg->otg.phy->otg = &dotg->otg;
+ dotg->otg.phy->dev = dwc->dev;
+
+ ret = usb_set_transceiver(dotg->otg.phy);
+ if (ret) {
+ dev_err(dotg->otg.phy->dev,
+ "%s: failed to set transceiver, already exists\n",
+ __func__);
+ goto err2;
+ }
+
+ dwc3_otg_reset(dotg);
+
+ dotg->otg.phy->state = OTG_STATE_UNDEFINED;
+
+ INIT_WORK(&dotg->sm_work, dwc3_otg_sm_work);
+
+ ret = request_irq(dotg->irq, dwc3_otg_interrupt, IRQF_SHARED,
+ "dwc3_otg", dotg);
+ if (ret) {
+ dev_err(dotg->otg.phy->dev, "failed to request irq #%d --> %d\n",
+ dotg->irq, ret);
+ goto err3;
+ }
+
+ return 0;
+
+err3:
+ cancel_work_sync(&dotg->sm_work);
+ usb_set_transceiver(NULL);
+err2:
+ kfree(dotg->otg.phy);
+err1:
+ dwc->dotg = NULL;
+ kfree(dotg);
+
+ return ret;
+}
+
+/**
+ * dwc3_otg_exit
+ * @dwc: Pointer to out controller context structure
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+void dwc3_otg_exit(struct dwc3 *dwc)
+{
+ struct dwc3_otg *dotg = dwc->dotg;
+
+ /* dotg is null when GHWPARAMS6[10]=SRPSupport=0, see dwc3_otg_init */
+ if (dotg) {
+ if (dotg->charger)
+ dotg->charger->start_detection(dotg->charger, false);
+ cancel_work_sync(&dotg->sm_work);
+ usb_set_transceiver(NULL);
+ free_irq(dotg->irq, dotg);
+ kfree(dotg->otg.phy);
+ kfree(dotg);
+ dwc->dotg = NULL;
+ }
+}
diff --git a/drivers/usb/dwc3/dwc3_otg.h b/drivers/usb/dwc3/dwc3_otg.h
new file mode 100644
index 0000000..0d8b61b
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3_otg.h
@@ -0,0 +1,76 @@
+/**
+ * dwc3_otg.h - DesignWare USB3 DRD Controller OTG
+ *
+ * Copyright (c) 2012, Code Aurora Forum. 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_USB_DWC3_OTG_H
+#define __LINUX_USB_DWC3_OTG_H
+
+#include <linux/workqueue.h>
+
+#include <linux/usb/otg.h>
+
+struct dwc3_charger;
+
+/**
+ * struct dwc3_otg: OTG driver data. Shared by HCD and DCD.
+ * @otg: USB OTG Transceiver structure.
+ * @irq: IRQ number assigned for HSUSB controller.
+ * @regs: ioremapped register base address.
+ * @sm_work: OTG state machine work.
+ * @charger: DWC3 external charger detector
+ * @inputs: OTG state machine inputs
+ */
+struct dwc3_otg {
+ struct usb_otg otg;
+ int irq;
+ void __iomem *regs;
+ struct work_struct sm_work;
+ struct dwc3_charger *charger;
+#define ID 0
+#define B_SESS_VLD 1
+ unsigned long inputs;
+};
+
+/**
+ * USB charger types
+ *
+ * DWC3_INVALID_CHARGER Invalid USB charger.
+ * DWC3_SDP_CHARGER Standard downstream port. Refers to a downstream port
+ * on USB compliant host/hub.
+ * DWC3_DCP_CHARGER Dedicated charger port (AC charger/ Wall charger).
+ * DWC3_CDP_CHARGER Charging downstream port. Enumeration can happen and
+ * IDEV_CHG_MAX can be drawn irrespective of USB state.
+ */
+enum dwc3_chg_type {
+ DWC3_INVALID_CHARGER = 0,
+ DWC3_SDP_CHARGER,
+ DWC3_DCP_CHARGER,
+ DWC3_CDP_CHARGER,
+};
+
+struct dwc3_charger {
+ enum dwc3_chg_type chg_type;
+
+ /* start/stop charger detection, provided by external charger module */
+ void (*start_detection)(struct dwc3_charger *charger, bool start);
+
+ /* to notify OTG about charger detection completion, provided by OTG */
+ void (*notify_detection_complete)(struct usb_otg *otg,
+ struct dwc3_charger *charger);
+};
+
+/* for external charger driver */
+extern int dwc3_set_charger(struct usb_otg *otg, struct dwc3_charger *charger);
+
+#endif /* __LINUX_USB_DWC3_OTG_H */
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index a988c43..060144f 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -49,6 +49,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
#include "core.h"
#include "gadget.h"
@@ -1326,7 +1327,59 @@
is_on = !!is_on;
spin_lock_irqsave(&dwc->lock, flags);
+
+ dwc->softconnect = is_on;
+
+ if ((dwc->dotg && !dwc->vbus_active) ||
+ !dwc->gadget_driver) {
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ /*
+ * Need to wait for vbus_session(on) from otg driver or to
+ * the udc_start.
+ */
+ return 0;
+ }
+
dwc3_gadget_run_stop(dwc, is_on);
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return 0;
+}
+
+static int dwc3_gadget_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+ struct dwc3 *dwc = gadget_to_dwc(_gadget);
+ unsigned long flags;
+
+ if (!dwc->dotg)
+ return -EPERM;
+
+ is_active = !!is_active;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ /* Mark that the vbus was powered */
+ dwc->vbus_active = is_active;
+
+ /*
+ * Check if upper level usb_gadget_driver was already registerd with
+ * this udc controller driver (if dwc3_gadget_start was called)
+ */
+ if (dwc->gadget_driver && dwc->softconnect) {
+ if (dwc->vbus_active) {
+ /*
+ * Both vbus was activated by otg and pullup was
+ * signaled by the gadget driver.
+ */
+ dwc3_gadget_run_stop(dwc, 1);
+ } else {
+ dwc3_gadget_run_stop(dwc, 0);
+ }
+ }
+
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
@@ -1417,6 +1470,7 @@
.get_frame = dwc3_gadget_get_frame,
.wakeup = dwc3_gadget_wakeup,
.set_selfpowered = dwc3_gadget_set_selfpowered,
+ .vbus_session = dwc3_gadget_vbus_session,
.pullup = dwc3_gadget_pullup,
.udc_start = dwc3_gadget_start,
.udc_stop = dwc3_gadget_stop,
@@ -2153,6 +2207,33 @@
break;
case DWC3_DEVICE_EVENT_OVERFLOW:
dev_vdbg(dwc->dev, "Overflow\n");
+ /*
+ * Controllers prior to 2.30a revision has a bug where
+ * Overflow Event may overwrite an unacknowledged event
+ * in the event buffer. The severity of the issue depends
+ * on the overwritten event type. Add a warning message
+ * saying that an event is overwritten.
+ *
+ * TODO: In future we may need to see if we can re-enumerate
+ * with host.
+ */
+ if (dwc->revision < DWC3_REVISION_230A)
+ dev_warn(dwc->dev, "Unacknowledged event overwritten\n");
+ break;
+ case DWC3_DEVICE_EVENT_VENDOR_DEV_TEST_LMP:
+ /*
+ * Controllers prior to 2.30a revision has a bug, due to which
+ * a vendor device test LMP event can not be filtered. But
+ * this event is not handled in the current code. This is a
+ * special event and 8 bytes of data will follow the event.
+ * Handling this event is tricky when event buffer is almost
+ * full. Moreover this event will not occur in normal scenario
+ * and can only happen with special hosts in testing scenarios.
+ * Add a warning message to indicate that this event is received
+ * which means that event buffer might have corrupted.
+ */
+ if (dwc->revision < DWC3_REVISION_230A)
+ dev_warn(dwc->dev, "Vendor Device Test LMP Received\n");
break;
default:
dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
@@ -2314,8 +2395,7 @@
}
/* Enable all but Start and End of Frame IRQs */
- reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
- DWC3_DEVTEN_EVNTOVERFLOWEN |
+ reg = (DWC3_DEVTEN_EVNTOVERFLOWEN |
DWC3_DEVTEN_CMDCMPLTEN |
DWC3_DEVTEN_ERRTICERREN |
DWC3_DEVTEN_WKUPEVTEN |
@@ -2338,6 +2418,15 @@
goto err7;
}
+ if (dwc->dotg) {
+ /* dwc3 otg driver is active (DRD mode + SRPSupport=1) */
+ ret = otg_set_peripheral(&dwc->dotg->otg, &dwc->gadget);
+ if (ret) {
+ dev_err(dwc->dev, "failed to set peripheral to otg\n");
+ goto err7;
+ }
+ }
+
return 0;
err7:
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index b108d18..099708b 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -38,20 +38,13 @@
#include <linux/platform_device.h>
#include "core.h"
-
-static struct resource generic_resources[] = {
- {
- .flags = IORESOURCE_IRQ,
- },
- {
- .flags = IORESOURCE_MEM,
- },
-};
+#include "xhci.h"
int dwc3_host_init(struct dwc3 *dwc)
{
struct platform_device *xhci;
int ret;
+ struct xhci_plat_data pdata;
xhci = platform_device_alloc("xhci-hcd", -1);
if (!xhci) {
@@ -67,15 +60,19 @@
xhci->dev.dma_parms = dwc->dev->dma_parms;
dwc->xhci = xhci;
+ pdata.vendor = ((dwc->revision & DWC3_GSNPSID_MASK) >>
+ __ffs(DWC3_GSNPSID_MASK) & DWC3_GSNPSREV_MASK);
+ pdata.revision = dwc->revision & DWC3_GSNPSREV_MASK;
- /* setup resources */
- generic_resources[0].start = dwc->irq;
+ ret = platform_device_add_data(xhci, (const void *) &pdata,
+ sizeof(struct xhci_plat_data));
+ if (ret) {
+ dev_err(dwc->dev, "couldn't add pdata to xHCI device\n");
+ goto err1;
+ }
- generic_resources[1].start = dwc->res->start;
- generic_resources[1].end = dwc->res->start + 0x7fff;
-
- ret = platform_device_add_resources(xhci, generic_resources,
- ARRAY_SIZE(generic_resources));
+ ret = platform_device_add_resources(xhci, dwc->xhci_resources,
+ DWC3_XHCI_RESOURCES_NUM);
if (ret) {
dev_err(dwc->dev, "couldn't add resources to xHCI device\n");
goto err1;
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index 071d561..90de7a4 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -41,14 +41,26 @@
#include <linux/io.h>
+#include "core.h"
+
static inline u32 dwc3_readl(void __iomem *base, u32 offset)
{
- return readl(base + offset);
+ /*
+ * We requested the mem region starting from the Globals address
+ * space, see dwc3_probe in core.c.
+ * However, the offsets are given starting from xHCI address space.
+ */
+ return readl_relaxed(base + (offset - DWC3_GLOBALS_REGS_START));
}
static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
{
- writel(value, base + offset);
+ /*
+ * We requested the mem region starting from the Globals address
+ * space, see dwc3_probe in core.c.
+ * However, the offsets are given starting from xHCI address space.
+ */
+ writel_relaxed(value, base + (offset - DWC3_GLOBALS_REGS_START));
}
#endif /* __DRIVERS_USB_DWC3_IO_H */
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 95f11c1..87b307c 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -854,6 +854,16 @@
Say "y" to link the driver statically, or "m" to build
a dynamically linked module called "g_mass_storage".
+config USB_GADGET_TARGET
+ tristate "USB Gadget Target Fabric Module"
+ depends on TARGET_CORE
+ help
+ This fabric is an USB gadget. Two USB protocols are supported that is
+ BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is
+ advertised on alternative interface 0 (primary) and UAS is on
+ alternative interface 1. Both protocols can work on USB2.0 and USB3.0.
+ UAS utilizes the USB 3.0 feature called streams support.
+
config USB_G_SERIAL
tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
help
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index c646c9f..b8f51490 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -54,6 +54,7 @@
g_webcam-y := webcam.o
g_ncm-y := ncm.o
g_acm_ms-y := acm_ms.o
+g_tcm_usb_gadget-y := tcm_usb_gadget.o
g_android-y := android.o
obj-$(CONFIG_USB_ZERO) += g_zero.o
@@ -74,4 +75,5 @@
obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
obj-$(CONFIG_USB_G_NCM) += g_ncm.o
obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o
+obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o
obj-$(CONFIG_USB_G_ANDROID) += g_android.o
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 90a3b1e2..b5a7291 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -1438,7 +1438,6 @@
sscanf(buff, "%d", &enabled);
if (enabled && !dev->enabled) {
- cdev->next_string_id = 0;
/*
* Update values in composite driver's copy of
* device descriptor.
@@ -1546,7 +1545,8 @@
{ \
if (size >= sizeof(buffer)) \
return -EINVAL; \
- strlcpy(buffer, strim((char *) buf), sizeof(buffer)); \
+ strlcpy(buffer, buf, sizeof(buffer)); \
+ strim(buffer); \
return size; \
} \
static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store);
diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c
index 1cbaa8e..e3c1216 100644
--- a/drivers/usb/gadget/ci13xxx_msm.c
+++ b/drivers/usb/gadget/ci13xxx_msm.c
@@ -56,7 +56,7 @@
if (_udc_ctxt.wake_irq && _udc_ctxt.wake_irq_state) {
disable_irq_wake(_udc_ctxt.wake_irq);
- disable_irq(_udc_ctxt.wake_irq);
+ disable_irq_nosync(_udc_ctxt.wake_irq);
_udc_ctxt.wake_irq_state = false;
}
}
@@ -134,7 +134,7 @@
dev_dbg(&pdev->dev, "_udc_ctxt.gpio_irq = %d and irq = %d\n",
_udc_ctxt.wake_gpio, wake_irq);
ret = request_irq(wake_irq, ci13xxx_msm_resume_irq,
- IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "usb resume", NULL);
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT, "usb resume", NULL);
if (ret < 0) {
dev_err(&pdev->dev, "could not register USB_RESUME IRQ.\n");
goto gpio_free;
diff --git a/drivers/usb/gadget/ci13xxx_msm_hsic.c b/drivers/usb/gadget/ci13xxx_msm_hsic.c
index 39d4720..f353b070 100644
--- a/drivers/usb/gadget/ci13xxx_msm_hsic.c
+++ b/drivers/usb/gadget/ci13xxx_msm_hsic.c
@@ -30,16 +30,16 @@
#include <mach/clk.h>
#include <mach/msm_iomap.h>
#include <mach/msm_xo.h>
+#include <mach/rpm-regulator.h>
#include "ci13xxx_udc.c"
#define MSM_USB_BASE (mhsic->regs)
#define ULPI_IO_TIMEOUT_USEC (10 * 1000)
-#define USB_PHY_VDD_DIG_VOL_SUSP_MIN 500000 /* uV */
+#define USB_PHY_VDD_DIG_VOL_NONE 0 /*uV */
#define USB_PHY_VDD_DIG_VOL_MIN 1045000 /* uV */
#define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */
-#define USB_PHY_VDD_DIG_LOAD 49360 /* uA */
#define LINK_RESET_TIMEOUT_USEC (250 * 1000)
#define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000)
#define PHY_RESUME_TIMEOUT_USEC (100 * 1000)
@@ -66,38 +66,56 @@
struct workqueue_struct *wq;
struct work_struct suspend_w;
struct msm_hsic_peripheral_platform_data *pdata;
+ enum usb_vdd_type vdd_type;
+};
+
+static const int vdd_val[VDD_TYPE_MAX][VDD_VAL_MAX] = {
+ { /* VDD_CX CORNER Voting */
+ [VDD_NONE] = RPM_VREG_CORNER_NONE,
+ [VDD_MIN] = RPM_VREG_CORNER_NOMINAL,
+ [VDD_MAX] = RPM_VREG_CORNER_HIGH,
+ },
+ { /* VDD_CX Voltage Voting */
+ [VDD_NONE] = USB_PHY_VDD_DIG_VOL_NONE,
+ [VDD_MIN] = USB_PHY_VDD_DIG_VOL_MIN,
+ [VDD_MAX] = USB_PHY_VDD_DIG_VOL_MAX,
+ },
};
static int msm_hsic_init_vddcx(struct msm_hsic_per *mhsic, int init)
{
int ret = 0;
+ int none_vol, min_vol, max_vol;
+
+ if (!mhsic->hsic_vddcx) {
+ mhsic->vdd_type = VDDCX_CORNER;
+ mhsic->hsic_vddcx = devm_regulator_get(mhsic->dev,
+ "hsic_vdd_dig");
+ if (IS_ERR(mhsic->hsic_vddcx)) {
+ mhsic->hsic_vddcx = devm_regulator_get(mhsic->dev,
+ "HSIC_VDDCX");
+ if (IS_ERR(mhsic->hsic_vddcx)) {
+ dev_err(mhsic->dev, "unable to get hsic vddcx\n");
+ return PTR_ERR(mhsic->hsic_vddcx);
+ }
+ mhsic->vdd_type = VDDCX;
+ }
+ }
+
+ none_vol = vdd_val[mhsic->vdd_type][VDD_NONE];
+ min_vol = vdd_val[mhsic->vdd_type][VDD_MIN];
+ max_vol = vdd_val[mhsic->vdd_type][VDD_MAX];
if (!init)
goto disable_reg;
- mhsic->hsic_vddcx = regulator_get(mhsic->dev, "HSIC_VDDCX");
- if (IS_ERR(mhsic->hsic_vddcx)) {
- dev_err(mhsic->dev, "unable to get hsic vddcx\n");
- return PTR_ERR(mhsic->hsic_vddcx);
- }
-
- ret = regulator_set_voltage(mhsic->hsic_vddcx,
- USB_PHY_VDD_DIG_VOL_MIN,
- USB_PHY_VDD_DIG_VOL_MAX);
+ ret = regulator_set_voltage(mhsic->hsic_vddcx, min_vol, max_vol);
if (ret) {
dev_err(mhsic->dev, "unable to set the voltage"
"for hsic vddcx\n");
goto reg_set_voltage_err;
}
- ret = regulator_set_optimum_mode(mhsic->hsic_vddcx,
- USB_PHY_VDD_DIG_LOAD);
- if (ret < 0) {
- pr_err("%s: Unable to set optimum mode of the regulator:"
- "VDDCX\n", __func__);
- goto reg_optimum_mode_err;
- }
-
ret = regulator_enable(mhsic->hsic_vddcx);
if (ret) {
dev_err(mhsic->dev, "unable to enable hsic vddcx\n");
@@ -109,12 +127,8 @@
disable_reg:
regulator_disable(mhsic->hsic_vddcx);
reg_enable_err:
- regulator_set_optimum_mode(mhsic->hsic_vddcx, 0);
-reg_optimum_mode_err:
- regulator_set_voltage(mhsic->hsic_vddcx, 0,
- USB_PHY_VDD_DIG_VOL_MIN);
+ regulator_set_voltage(mhsic->hsic_vddcx, none_vol, max_vol);
reg_set_voltage_err:
- regulator_put(mhsic->hsic_vddcx);
return ret;
@@ -323,6 +337,7 @@
{
int cnt = 0, ret;
u32 val;
+ int none_vol, max_vol;
if (atomic_read(&mhsic->in_lpm)) {
dev_dbg(mhsic->dev, "%s called while in lpm\n", __func__);
@@ -366,7 +381,7 @@
*/
mb();
- if (!mhsic->pdata->keep_core_clk_on_suspend_workaround) {
+ if (!mhsic->pdata->core_clk_always_on_workaround) {
clk_disable(mhsic->iface_clk);
clk_disable(mhsic->core_clk);
}
@@ -378,11 +393,12 @@
dev_err(mhsic->dev, "%s failed to devote for TCXO %d\n"
, __func__, ret);
- ret = regulator_set_voltage(mhsic->hsic_vddcx,
- USB_PHY_VDD_DIG_VOL_SUSP_MIN,
- USB_PHY_VDD_DIG_VOL_MAX);
+ none_vol = vdd_val[mhsic->vdd_type][VDD_NONE];
+ max_vol = vdd_val[mhsic->vdd_type][VDD_MAX];
+
+ ret = regulator_set_voltage(mhsic->hsic_vddcx, none_vol, max_vol);
if (ret < 0)
- dev_err(mhsic->dev, "unable to set vddcx voltage: min:0.5v max:1.32v\n");
+ dev_err(mhsic->dev, "unable to set vddcx voltage for VDD MIN\n");
if (device_may_wakeup(mhsic->dev))
enable_irq_wake(mhsic->irq);
@@ -400,6 +416,7 @@
{
int cnt = 0, ret;
unsigned temp;
+ int min_vol, max_vol;
if (!atomic_read(&mhsic->in_lpm)) {
dev_dbg(mhsic->dev, "%s called while not in lpm\n", __func__);
@@ -407,19 +424,21 @@
}
wake_lock(&mhsic->wlock);
- ret = regulator_set_voltage(mhsic->hsic_vddcx,
- USB_PHY_VDD_DIG_VOL_MIN,
- USB_PHY_VDD_DIG_VOL_MAX);
+
+ min_vol = vdd_val[mhsic->vdd_type][VDD_MIN];
+ max_vol = vdd_val[mhsic->vdd_type][VDD_MAX];
+
+ ret = regulator_set_voltage(mhsic->hsic_vddcx, min_vol, max_vol);
if (ret < 0)
dev_err(mhsic->dev,
- "unable to set vddcx voltage: min:1.045v max:1.32v\n");
+ "unable to set nominal vddcx voltage (no VDD MIN)\n");
ret = msm_xo_mode_vote(mhsic->xo_handle, MSM_XO_MODE_ON);
if (ret)
dev_err(mhsic->dev, "%s failed to vote for TCXO %d\n",
__func__, ret);
- if (!mhsic->pdata->keep_core_clk_on_suspend_workaround) {
+ if (!mhsic->pdata->core_clk_always_on_workaround) {
clk_enable(mhsic->iface_clk);
clk_enable(mhsic->core_clk);
}
diff --git a/drivers/usb/gadget/f_mbim.c b/drivers/usb/gadget/f_mbim.c
index 41a1777..6883b78 100644
--- a/drivers/usb/gadget/f_mbim.c
+++ b/drivers/usb/gadget/f_mbim.c
@@ -795,6 +795,7 @@
spin_unlock(&mbim->lock);
mbim_clear_queues(mbim);
mbim_reset_function_queue(mbim);
+ spin_lock(&mbim->lock);
break;
default:
pr_err("Unknown event %02x --> %d\n",
@@ -1450,8 +1451,6 @@
mbim->cdev = c->cdev;
- spin_lock_init(&mbim->lock);
-
mbim_reset_values(mbim);
mbim->function.name = "usb_mbim";
@@ -1615,6 +1614,7 @@
pr_debug("Exit(%d)", count);
return count;
+
}
static int mbim_open(struct inode *ip, struct file *fp)
diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c
new file mode 100644
index 0000000..c46439c
--- /dev/null
+++ b/drivers/usb/gadget/tcm_usb_gadget.c
@@ -0,0 +1,2480 @@
+/* Target based USB-Gadget
+ *
+ * UAS protocol handling, target callbacks, configfs handling,
+ * BBB (USB Mass Storage Class Bulk-Only (BBB) and Transport protocol handling.
+ *
+ * Author: Sebastian Andrzej Siewior <bigeasy at linutronix dot de>
+ * License: GPLv2 as published by FSF.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/storage.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+#include <target/configfs_macros.h>
+#include <asm/unaligned.h>
+
+#include "usbstring.c"
+#include "epautoconf.c"
+#include "config.c"
+#include "composite.c"
+
+#include "tcm_usb_gadget.h"
+
+static struct target_fabric_configfs *usbg_fabric_configfs;
+
+static inline struct f_uas *to_f_uas(struct usb_function *f)
+{
+ return container_of(f, struct f_uas, function);
+}
+
+static void usbg_cmd_release(struct kref *);
+
+static inline void usbg_cleanup_cmd(struct usbg_cmd *cmd)
+{
+ kref_put(&cmd->ref, usbg_cmd_release);
+}
+
+/* Start bot.c code */
+
+static int bot_enqueue_cmd_cbw(struct f_uas *fu)
+{
+ int ret;
+
+ if (fu->flags & USBG_BOT_CMD_PEND)
+ return 0;
+
+ ret = usb_ep_queue(fu->ep_out, fu->cmd.req, GFP_ATOMIC);
+ if (!ret)
+ fu->flags |= USBG_BOT_CMD_PEND;
+ return ret;
+}
+
+static void bot_status_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usbg_cmd *cmd = req->context;
+ struct f_uas *fu = cmd->fu;
+
+ usbg_cleanup_cmd(cmd);
+ if (req->status < 0) {
+ pr_err("ERR %s(%d)\n", __func__, __LINE__);
+ return;
+ }
+
+ /* CSW completed, wait for next CBW */
+ bot_enqueue_cmd_cbw(fu);
+}
+
+static void bot_enqueue_sense_code(struct f_uas *fu, struct usbg_cmd *cmd)
+{
+ struct bulk_cs_wrap *csw = &fu->bot_status.csw;
+ int ret;
+ u8 *sense;
+ unsigned int csw_stat;
+
+ csw_stat = cmd->csw_code;
+
+ /*
+ * We can't send SENSE as a response. So we take ASC & ASCQ from our
+ * sense buffer and queue it and hope the host sends a REQUEST_SENSE
+ * command where it learns why we failed.
+ */
+ sense = cmd->sense_iu.sense;
+
+ csw->Tag = cmd->bot_tag;
+ csw->Status = csw_stat;
+ fu->bot_status.req->context = cmd;
+ ret = usb_ep_queue(fu->ep_in, fu->bot_status.req, GFP_ATOMIC);
+ if (ret)
+ pr_err("%s(%d) ERR: %d\n", __func__, __LINE__, ret);
+}
+
+static void bot_err_compl(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usbg_cmd *cmd = req->context;
+ struct f_uas *fu = cmd->fu;
+
+ if (req->status < 0)
+ pr_err("ERR %s(%d)\n", __func__, __LINE__);
+
+ if (cmd->data_len) {
+ if (cmd->data_len > ep->maxpacket) {
+ req->length = ep->maxpacket;
+ cmd->data_len -= ep->maxpacket;
+ } else {
+ req->length = cmd->data_len;
+ cmd->data_len = 0;
+ }
+
+ usb_ep_queue(ep, req, GFP_ATOMIC);
+ return ;
+ }
+ bot_enqueue_sense_code(fu, cmd);
+}
+
+static void bot_send_bad_status(struct usbg_cmd *cmd)
+{
+ struct f_uas *fu = cmd->fu;
+ struct bulk_cs_wrap *csw = &fu->bot_status.csw;
+ struct usb_request *req;
+ struct usb_ep *ep;
+
+ csw->Residue = cpu_to_le32(cmd->data_len);
+
+ if (cmd->data_len) {
+ if (cmd->is_read) {
+ ep = fu->ep_in;
+ req = fu->bot_req_in;
+ } else {
+ ep = fu->ep_out;
+ req = fu->bot_req_out;
+ }
+
+ if (cmd->data_len > fu->ep_in->maxpacket) {
+ req->length = ep->maxpacket;
+ cmd->data_len -= ep->maxpacket;
+ } else {
+ req->length = cmd->data_len;
+ cmd->data_len = 0;
+ }
+ req->complete = bot_err_compl;
+ req->context = cmd;
+ req->buf = fu->cmd.buf;
+ usb_ep_queue(ep, req, GFP_KERNEL);
+ } else {
+ bot_enqueue_sense_code(fu, cmd);
+ }
+}
+
+static int bot_send_status(struct usbg_cmd *cmd, bool moved_data)
+{
+ struct f_uas *fu = cmd->fu;
+ struct bulk_cs_wrap *csw = &fu->bot_status.csw;
+ int ret;
+
+ if (cmd->se_cmd.scsi_status == SAM_STAT_GOOD) {
+ if (!moved_data && cmd->data_len) {
+ /*
+ * the host wants to move data, we don't. Fill / empty
+ * the pipe and then send the csw with reside set.
+ */
+ cmd->csw_code = US_BULK_STAT_OK;
+ bot_send_bad_status(cmd);
+ return 0;
+ }
+
+ csw->Tag = cmd->bot_tag;
+ csw->Residue = cpu_to_le32(0);
+ csw->Status = US_BULK_STAT_OK;
+ fu->bot_status.req->context = cmd;
+
+ ret = usb_ep_queue(fu->ep_in, fu->bot_status.req, GFP_KERNEL);
+ if (ret)
+ pr_err("%s(%d) ERR: %d\n", __func__, __LINE__, ret);
+ } else {
+ cmd->csw_code = US_BULK_STAT_FAIL;
+ bot_send_bad_status(cmd);
+ }
+ return 0;
+}
+
+/*
+ * Called after command (no data transfer) or after the write (to device)
+ * operation is completed
+ */
+static int bot_send_status_response(struct usbg_cmd *cmd)
+{
+ bool moved_data = false;
+
+ if (!cmd->is_read)
+ moved_data = true;
+ return bot_send_status(cmd, moved_data);
+}
+
+/* Read request completed, now we have to send the CSW */
+static void bot_read_compl(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usbg_cmd *cmd = req->context;
+
+ if (req->status < 0)
+ pr_err("ERR %s(%d)\n", __func__, __LINE__);
+
+ bot_send_status(cmd, true);
+}
+
+static int bot_send_read_response(struct usbg_cmd *cmd)
+{
+ struct f_uas *fu = cmd->fu;
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct usb_gadget *gadget = fuas_to_gadget(fu);
+ int ret;
+
+ if (!cmd->data_len) {
+ cmd->csw_code = US_BULK_STAT_PHASE;
+ bot_send_bad_status(cmd);
+ return 0;
+ }
+
+ if (!gadget->sg_supported) {
+ cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC);
+ if (!cmd->data_buf)
+ return -ENOMEM;
+
+ sg_copy_to_buffer(se_cmd->t_data_sg,
+ se_cmd->t_data_nents,
+ cmd->data_buf,
+ se_cmd->data_length);
+
+ fu->bot_req_in->buf = cmd->data_buf;
+ } else {
+ fu->bot_req_in->buf = NULL;
+ fu->bot_req_in->num_sgs = se_cmd->t_data_nents;
+ fu->bot_req_in->sg = se_cmd->t_data_sg;
+ }
+
+ fu->bot_req_in->complete = bot_read_compl;
+ fu->bot_req_in->length = se_cmd->data_length;
+ fu->bot_req_in->context = cmd;
+ ret = usb_ep_queue(fu->ep_in, fu->bot_req_in, GFP_ATOMIC);
+ if (ret)
+ pr_err("%s(%d)\n", __func__, __LINE__);
+ return 0;
+}
+
+static void usbg_data_write_cmpl(struct usb_ep *, struct usb_request *);
+static int usbg_prepare_w_request(struct usbg_cmd *, struct usb_request *);
+
+static int bot_send_write_request(struct usbg_cmd *cmd)
+{
+ struct f_uas *fu = cmd->fu;
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct usb_gadget *gadget = fuas_to_gadget(fu);
+ int ret;
+
+ init_completion(&cmd->write_complete);
+ cmd->fu = fu;
+
+ if (!cmd->data_len) {
+ cmd->csw_code = US_BULK_STAT_PHASE;
+ return -EINVAL;
+ }
+
+ if (!gadget->sg_supported) {
+ cmd->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL);
+ if (!cmd->data_buf)
+ return -ENOMEM;
+
+ fu->bot_req_out->buf = cmd->data_buf;
+ } else {
+ fu->bot_req_out->buf = NULL;
+ fu->bot_req_out->num_sgs = se_cmd->t_data_nents;
+ fu->bot_req_out->sg = se_cmd->t_data_sg;
+ }
+
+ fu->bot_req_out->complete = usbg_data_write_cmpl;
+ fu->bot_req_out->length = se_cmd->data_length;
+ fu->bot_req_out->context = cmd;
+
+ ret = usbg_prepare_w_request(cmd, fu->bot_req_out);
+ if (ret)
+ goto cleanup;
+ ret = usb_ep_queue(fu->ep_out, fu->bot_req_out, GFP_KERNEL);
+ if (ret)
+ pr_err("%s(%d)\n", __func__, __LINE__);
+
+ wait_for_completion(&cmd->write_complete);
+ transport_generic_process_write(se_cmd);
+cleanup:
+ return ret;
+}
+
+static int bot_submit_command(struct f_uas *, void *, unsigned int);
+
+static void bot_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_uas *fu = req->context;
+ int ret;
+
+ fu->flags &= ~USBG_BOT_CMD_PEND;
+
+ if (req->status < 0)
+ return;
+
+ ret = bot_submit_command(fu, req->buf, req->actual);
+ if (ret)
+ pr_err("%s(%d): %d\n", __func__, __LINE__, ret);
+}
+
+static int bot_prepare_reqs(struct f_uas *fu)
+{
+ int ret;
+
+ fu->bot_req_in = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL);
+ if (!fu->bot_req_in)
+ goto err;
+
+ fu->bot_req_out = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL);
+ if (!fu->bot_req_out)
+ goto err_out;
+
+ fu->cmd.req = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL);
+ if (!fu->cmd.req)
+ goto err_cmd;
+
+ fu->bot_status.req = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL);
+ if (!fu->bot_status.req)
+ goto err_sts;
+
+ fu->bot_status.req->buf = &fu->bot_status.csw;
+ fu->bot_status.req->length = US_BULK_CS_WRAP_LEN;
+ fu->bot_status.req->complete = bot_status_complete;
+ fu->bot_status.csw.Signature = cpu_to_le32(US_BULK_CS_SIGN);
+
+ fu->cmd.buf = kmalloc(fu->ep_out->maxpacket, GFP_KERNEL);
+ if (!fu->cmd.buf)
+ goto err_buf;
+
+ fu->cmd.req->complete = bot_cmd_complete;
+ fu->cmd.req->buf = fu->cmd.buf;
+ fu->cmd.req->length = fu->ep_out->maxpacket;
+ fu->cmd.req->context = fu;
+
+ ret = bot_enqueue_cmd_cbw(fu);
+ if (ret)
+ goto err_queue;
+ return 0;
+err_queue:
+ kfree(fu->cmd.buf);
+ fu->cmd.buf = NULL;
+err_buf:
+ usb_ep_free_request(fu->ep_in, fu->bot_status.req);
+err_sts:
+ usb_ep_free_request(fu->ep_out, fu->cmd.req);
+ fu->cmd.req = NULL;
+err_cmd:
+ usb_ep_free_request(fu->ep_out, fu->bot_req_out);
+ fu->bot_req_out = NULL;
+err_out:
+ usb_ep_free_request(fu->ep_in, fu->bot_req_in);
+ fu->bot_req_in = NULL;
+err:
+ pr_err("BOT: endpoint setup failed\n");
+ return -ENOMEM;
+}
+
+void bot_cleanup_old_alt(struct f_uas *fu)
+{
+ if (!(fu->flags & USBG_ENABLED))
+ return;
+
+ usb_ep_disable(fu->ep_in);
+ usb_ep_disable(fu->ep_out);
+
+ if (!fu->bot_req_in)
+ return;
+
+ usb_ep_free_request(fu->ep_in, fu->bot_req_in);
+ usb_ep_free_request(fu->ep_out, fu->bot_req_out);
+ usb_ep_free_request(fu->ep_out, fu->cmd.req);
+ usb_ep_free_request(fu->ep_out, fu->bot_status.req);
+
+ kfree(fu->cmd.buf);
+
+ fu->bot_req_in = NULL;
+ fu->bot_req_out = NULL;
+ fu->cmd.req = NULL;
+ fu->bot_status.req = NULL;
+ fu->cmd.buf = NULL;
+}
+
+static void bot_set_alt(struct f_uas *fu)
+{
+ struct usb_function *f = &fu->function;
+ struct usb_gadget *gadget = f->config->cdev->gadget;
+ int ret;
+
+ fu->flags = USBG_IS_BOT;
+
+ config_ep_by_speed(gadget, f, fu->ep_in);
+ ret = usb_ep_enable(fu->ep_in);
+ if (ret)
+ goto err_b_in;
+
+ config_ep_by_speed(gadget, f, fu->ep_out);
+ ret = usb_ep_enable(fu->ep_out);
+ if (ret)
+ goto err_b_out;
+
+ ret = bot_prepare_reqs(fu);
+ if (ret)
+ goto err_wq;
+ fu->flags |= USBG_ENABLED;
+ pr_info("Using the BOT protocol\n");
+ return;
+err_wq:
+ usb_ep_disable(fu->ep_out);
+err_b_out:
+ usb_ep_disable(fu->ep_in);
+err_b_in:
+ fu->flags = USBG_IS_BOT;
+}
+
+static int usbg_bot_setup(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct f_uas *fu = to_f_uas(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+ int luns;
+ u8 *ret_lun;
+
+ switch (ctrl->bRequest) {
+ case US_BULK_GET_MAX_LUN:
+ if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE))
+ return -ENOTSUPP;
+
+ if (w_length < 1)
+ return -EINVAL;
+ if (w_value != 0)
+ return -EINVAL;
+ luns = atomic_read(&fu->tpg->tpg_port_count);
+ if (!luns) {
+ pr_err("No LUNs configured?\n");
+ return -EINVAL;
+ }
+ /*
+ * If 4 LUNs are present we return 3 i.e. LUN 0..3 can be
+ * accessed. The upper limit is 0xf
+ */
+ luns--;
+ if (luns > 0xf) {
+ pr_info_once("Limiting the number of luns to 16\n");
+ luns = 0xf;
+ }
+ ret_lun = cdev->req->buf;
+ *ret_lun = luns;
+ cdev->req->length = 1;
+ return usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC);
+ break;
+
+ case US_BULK_RESET_REQUEST:
+ /* XXX maybe we should remove previous requests for IN + OUT */
+ bot_enqueue_cmd_cbw(fu);
+ return 0;
+ break;
+ };
+ return -ENOTSUPP;
+}
+
+/* Start uas.c code */
+
+static void uasp_cleanup_one_stream(struct f_uas *fu, struct uas_stream *stream)
+{
+ /* We have either all three allocated or none */
+ if (!stream->req_in)
+ return;
+
+ usb_ep_free_request(fu->ep_in, stream->req_in);
+ usb_ep_free_request(fu->ep_out, stream->req_out);
+ usb_ep_free_request(fu->ep_status, stream->req_status);
+
+ stream->req_in = NULL;
+ stream->req_out = NULL;
+ stream->req_status = NULL;
+}
+
+static void uasp_free_cmdreq(struct f_uas *fu)
+{
+ usb_ep_free_request(fu->ep_cmd, fu->cmd.req);
+ kfree(fu->cmd.buf);
+ fu->cmd.req = NULL;
+ fu->cmd.buf = NULL;
+}
+
+static void uasp_cleanup_old_alt(struct f_uas *fu)
+{
+ int i;
+
+ if (!(fu->flags & USBG_ENABLED))
+ return;
+
+ usb_ep_disable(fu->ep_in);
+ usb_ep_disable(fu->ep_out);
+ usb_ep_disable(fu->ep_status);
+ usb_ep_disable(fu->ep_cmd);
+
+ for (i = 0; i < UASP_SS_EP_COMP_NUM_STREAMS; i++)
+ uasp_cleanup_one_stream(fu, &fu->stream[i]);
+ uasp_free_cmdreq(fu);
+}
+
+static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req);
+
+static int uasp_prepare_r_request(struct usbg_cmd *cmd)
+{
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct f_uas *fu = cmd->fu;
+ struct usb_gadget *gadget = fuas_to_gadget(fu);
+ struct uas_stream *stream = cmd->stream;
+
+ if (!gadget->sg_supported) {
+ cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC);
+ if (!cmd->data_buf)
+ return -ENOMEM;
+
+ sg_copy_to_buffer(se_cmd->t_data_sg,
+ se_cmd->t_data_nents,
+ cmd->data_buf,
+ se_cmd->data_length);
+
+ stream->req_in->buf = cmd->data_buf;
+ } else {
+ stream->req_in->buf = NULL;
+ stream->req_in->num_sgs = se_cmd->t_data_nents;
+ stream->req_in->sg = se_cmd->t_data_sg;
+ }
+
+ stream->req_in->complete = uasp_status_data_cmpl;
+ stream->req_in->length = se_cmd->data_length;
+ stream->req_in->context = cmd;
+
+ cmd->state = UASP_SEND_STATUS;
+ return 0;
+}
+
+static void uasp_prepare_status(struct usbg_cmd *cmd)
+{
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct sense_iu *iu = &cmd->sense_iu;
+ struct uas_stream *stream = cmd->stream;
+
+ cmd->state = UASP_QUEUE_COMMAND;
+ iu->iu_id = IU_ID_STATUS;
+ iu->tag = cpu_to_be16(cmd->tag);
+
+ /*
+ * iu->status_qual = cpu_to_be16(STATUS QUALIFIER SAM-4. Where R U?);
+ */
+ iu->len = cpu_to_be16(se_cmd->scsi_sense_length);
+ iu->status = se_cmd->scsi_status;
+ stream->req_status->context = cmd;
+ stream->req_status->length = se_cmd->scsi_sense_length + 16;
+ stream->req_status->buf = iu;
+ stream->req_status->complete = uasp_status_data_cmpl;
+}
+
+static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usbg_cmd *cmd = req->context;
+ struct uas_stream *stream = cmd->stream;
+ struct f_uas *fu = cmd->fu;
+ int ret;
+
+ if (req->status < 0)
+ goto cleanup;
+
+ switch (cmd->state) {
+ case UASP_SEND_DATA:
+ ret = uasp_prepare_r_request(cmd);
+ if (ret)
+ goto cleanup;
+ ret = usb_ep_queue(fu->ep_in, stream->req_in, GFP_ATOMIC);
+ if (ret)
+ pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
+ break;
+
+ case UASP_RECEIVE_DATA:
+ ret = usbg_prepare_w_request(cmd, stream->req_out);
+ if (ret)
+ goto cleanup;
+ ret = usb_ep_queue(fu->ep_out, stream->req_out, GFP_ATOMIC);
+ if (ret)
+ pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
+ break;
+
+ case UASP_SEND_STATUS:
+ uasp_prepare_status(cmd);
+ ret = usb_ep_queue(fu->ep_status, stream->req_status,
+ GFP_ATOMIC);
+ if (ret)
+ pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
+ break;
+
+ case UASP_QUEUE_COMMAND:
+ usbg_cleanup_cmd(cmd);
+ usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
+ break;
+
+ default:
+ BUG();
+ };
+ return;
+
+cleanup:
+ usbg_cleanup_cmd(cmd);
+}
+
+static int uasp_send_status_response(struct usbg_cmd *cmd)
+{
+ struct f_uas *fu = cmd->fu;
+ struct uas_stream *stream = cmd->stream;
+ struct sense_iu *iu = &cmd->sense_iu;
+
+ iu->tag = cpu_to_be16(cmd->tag);
+ stream->req_status->complete = uasp_status_data_cmpl;
+ stream->req_status->context = cmd;
+ cmd->fu = fu;
+ uasp_prepare_status(cmd);
+ return usb_ep_queue(fu->ep_status, stream->req_status, GFP_ATOMIC);
+}
+
+static int uasp_send_read_response(struct usbg_cmd *cmd)
+{
+ struct f_uas *fu = cmd->fu;
+ struct uas_stream *stream = cmd->stream;
+ struct sense_iu *iu = &cmd->sense_iu;
+ int ret;
+
+ cmd->fu = fu;
+
+ iu->tag = cpu_to_be16(cmd->tag);
+ if (fu->flags & USBG_USE_STREAMS) {
+
+ ret = uasp_prepare_r_request(cmd);
+ if (ret)
+ goto out;
+ ret = usb_ep_queue(fu->ep_in, stream->req_in, GFP_ATOMIC);
+ if (ret) {
+ pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
+ kfree(cmd->data_buf);
+ cmd->data_buf = NULL;
+ }
+
+ } else {
+
+ iu->iu_id = IU_ID_READ_READY;
+ iu->tag = cpu_to_be16(cmd->tag);
+
+ stream->req_status->complete = uasp_status_data_cmpl;
+ stream->req_status->context = cmd;
+
+ cmd->state = UASP_SEND_DATA;
+ stream->req_status->buf = iu;
+ stream->req_status->length = sizeof(struct iu);
+
+ ret = usb_ep_queue(fu->ep_status, stream->req_status,
+ GFP_ATOMIC);
+ if (ret)
+ pr_err("%s(%d) => %d\n", __func__, __LINE__, ret);
+ }
+out:
+ return ret;
+}
+
+static int uasp_send_write_request(struct usbg_cmd *cmd)
+{
+ struct f_uas *fu = cmd->fu;
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct uas_stream *stream = cmd->stream;
+ struct sense_iu *iu = &cmd->sense_iu;
+ int ret;
+
+ init_completion(&cmd->write_complete);
+ cmd->fu = fu;
+
+ iu->tag = cpu_to_be16(cmd->tag);
+
+ if (fu->flags & USBG_USE_STREAMS) {
+
+ ret = usbg_prepare_w_request(cmd, stream->req_out);
+ if (ret)
+ goto cleanup;
+ ret = usb_ep_queue(fu->ep_out, stream->req_out, GFP_ATOMIC);
+ if (ret)
+ pr_err("%s(%d)\n", __func__, __LINE__);
+
+ } else {
+
+ iu->iu_id = IU_ID_WRITE_READY;
+ iu->tag = cpu_to_be16(cmd->tag);
+
+ stream->req_status->complete = uasp_status_data_cmpl;
+ stream->req_status->context = cmd;
+
+ cmd->state = UASP_RECEIVE_DATA;
+ stream->req_status->buf = iu;
+ stream->req_status->length = sizeof(struct iu);
+
+ ret = usb_ep_queue(fu->ep_status, stream->req_status,
+ GFP_ATOMIC);
+ if (ret)
+ pr_err("%s(%d)\n", __func__, __LINE__);
+ }
+
+ wait_for_completion(&cmd->write_complete);
+ transport_generic_process_write(se_cmd);
+cleanup:
+ return ret;
+}
+
+static int usbg_submit_command(struct f_uas *, void *, unsigned int);
+
+static void uasp_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_uas *fu = req->context;
+ int ret;
+
+ if (req->status < 0)
+ return;
+
+ ret = usbg_submit_command(fu, req->buf, req->actual);
+ /*
+ * Once we tune for performance enqueue the command req here again so
+ * we can receive a second command while we processing this one. Pay
+ * attention to properly sync STAUS endpoint with DATA IN + OUT so you
+ * don't break HS.
+ */
+ if (!ret)
+ return;
+ usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
+}
+
+static int uasp_alloc_stream_res(struct f_uas *fu, struct uas_stream *stream)
+{
+ stream->req_in = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL);
+ if (!stream->req_in)
+ goto out;
+
+ stream->req_out = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL);
+ if (!stream->req_out)
+ goto err_out;
+
+ stream->req_status = usb_ep_alloc_request(fu->ep_status, GFP_KERNEL);
+ if (!stream->req_status)
+ goto err_sts;
+
+ return 0;
+err_sts:
+ usb_ep_free_request(fu->ep_status, stream->req_status);
+ stream->req_status = NULL;
+err_out:
+ usb_ep_free_request(fu->ep_out, stream->req_out);
+ stream->req_out = NULL;
+out:
+ return -ENOMEM;
+}
+
+static int uasp_alloc_cmd(struct f_uas *fu)
+{
+ fu->cmd.req = usb_ep_alloc_request(fu->ep_cmd, GFP_KERNEL);
+ if (!fu->cmd.req)
+ goto err;
+
+ fu->cmd.buf = kmalloc(fu->ep_cmd->maxpacket, GFP_KERNEL);
+ if (!fu->cmd.buf)
+ goto err_buf;
+
+ fu->cmd.req->complete = uasp_cmd_complete;
+ fu->cmd.req->buf = fu->cmd.buf;
+ fu->cmd.req->length = fu->ep_cmd->maxpacket;
+ fu->cmd.req->context = fu;
+ return 0;
+
+err_buf:
+ usb_ep_free_request(fu->ep_cmd, fu->cmd.req);
+err:
+ return -ENOMEM;
+}
+
+static void uasp_setup_stream_res(struct f_uas *fu, int max_streams)
+{
+ int i;
+
+ for (i = 0; i < max_streams; i++) {
+ struct uas_stream *s = &fu->stream[i];
+
+ s->req_in->stream_id = i + 1;
+ s->req_out->stream_id = i + 1;
+ s->req_status->stream_id = i + 1;
+ }
+}
+
+static int uasp_prepare_reqs(struct f_uas *fu)
+{
+ int ret;
+ int i;
+ int max_streams;
+
+ if (fu->flags & USBG_USE_STREAMS)
+ max_streams = UASP_SS_EP_COMP_NUM_STREAMS;
+ else
+ max_streams = 1;
+
+ for (i = 0; i < max_streams; i++) {
+ ret = uasp_alloc_stream_res(fu, &fu->stream[i]);
+ if (ret)
+ goto err_cleanup;
+ }
+
+ ret = uasp_alloc_cmd(fu);
+ if (ret)
+ goto err_free_stream;
+ uasp_setup_stream_res(fu, max_streams);
+
+ ret = usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
+ if (ret)
+ goto err_free_stream;
+
+ return 0;
+
+err_free_stream:
+ uasp_free_cmdreq(fu);
+
+err_cleanup:
+ if (i) {
+ do {
+ uasp_cleanup_one_stream(fu, &fu->stream[i - 1]);
+ i--;
+ } while (i);
+ }
+ pr_err("UASP: endpoint setup failed\n");
+ return ret;
+}
+
+static void uasp_set_alt(struct f_uas *fu)
+{
+ struct usb_function *f = &fu->function;
+ struct usb_gadget *gadget = f->config->cdev->gadget;
+ int ret;
+
+ fu->flags = USBG_IS_UAS;
+
+ if (gadget->speed == USB_SPEED_SUPER)
+ fu->flags |= USBG_USE_STREAMS;
+
+ config_ep_by_speed(gadget, f, fu->ep_in);
+ ret = usb_ep_enable(fu->ep_in);
+ if (ret)
+ goto err_b_in;
+
+ config_ep_by_speed(gadget, f, fu->ep_out);
+ ret = usb_ep_enable(fu->ep_out);
+ if (ret)
+ goto err_b_out;
+
+ config_ep_by_speed(gadget, f, fu->ep_cmd);
+ ret = usb_ep_enable(fu->ep_cmd);
+ if (ret)
+ goto err_cmd;
+ config_ep_by_speed(gadget, f, fu->ep_status);
+ ret = usb_ep_enable(fu->ep_status);
+ if (ret)
+ goto err_status;
+
+ ret = uasp_prepare_reqs(fu);
+ if (ret)
+ goto err_wq;
+ fu->flags |= USBG_ENABLED;
+
+ pr_info("Using the UAS protocol\n");
+ return;
+err_wq:
+ usb_ep_disable(fu->ep_status);
+err_status:
+ usb_ep_disable(fu->ep_cmd);
+err_cmd:
+ usb_ep_disable(fu->ep_out);
+err_b_out:
+ usb_ep_disable(fu->ep_in);
+err_b_in:
+ fu->flags = 0;
+}
+
+static int get_cmd_dir(const unsigned char *cdb)
+{
+ int ret;
+
+ switch (cdb[0]) {
+ case READ_6:
+ case READ_10:
+ case READ_12:
+ case READ_16:
+ case INQUIRY:
+ case MODE_SENSE:
+ case MODE_SENSE_10:
+ case SERVICE_ACTION_IN:
+ case MAINTENANCE_IN:
+ case PERSISTENT_RESERVE_IN:
+ case SECURITY_PROTOCOL_IN:
+ case ACCESS_CONTROL_IN:
+ case REPORT_LUNS:
+ case READ_BLOCK_LIMITS:
+ case READ_POSITION:
+ case READ_CAPACITY:
+ case READ_TOC:
+ case READ_FORMAT_CAPACITIES:
+ case REQUEST_SENSE:
+ ret = DMA_FROM_DEVICE;
+ break;
+
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12:
+ case WRITE_16:
+ case MODE_SELECT:
+ case MODE_SELECT_10:
+ case WRITE_VERIFY:
+ case WRITE_VERIFY_12:
+ case PERSISTENT_RESERVE_OUT:
+ case MAINTENANCE_OUT:
+ case SECURITY_PROTOCOL_OUT:
+ case ACCESS_CONTROL_OUT:
+ ret = DMA_TO_DEVICE;
+ break;
+ case ALLOW_MEDIUM_REMOVAL:
+ case TEST_UNIT_READY:
+ case SYNCHRONIZE_CACHE:
+ case START_STOP:
+ case ERASE:
+ case REZERO_UNIT:
+ case SEEK_10:
+ case SPACE:
+ case VERIFY:
+ case WRITE_FILEMARKS:
+ ret = DMA_NONE;
+ break;
+ default:
+ pr_warn("target: Unknown data direction for SCSI Opcode "
+ "0x%02x\n", cdb[0]);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static void usbg_data_write_cmpl(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usbg_cmd *cmd = req->context;
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+
+ if (req->status < 0) {
+ pr_err("%s() state %d transfer failed\n", __func__, cmd->state);
+ goto cleanup;
+ }
+
+ if (req->num_sgs == 0) {
+ sg_copy_from_buffer(se_cmd->t_data_sg,
+ se_cmd->t_data_nents,
+ cmd->data_buf,
+ se_cmd->data_length);
+ }
+
+ complete(&cmd->write_complete);
+ return;
+
+cleanup:
+ usbg_cleanup_cmd(cmd);
+}
+
+static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req)
+{
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct f_uas *fu = cmd->fu;
+ struct usb_gadget *gadget = fuas_to_gadget(fu);
+
+ if (!gadget->sg_supported) {
+ cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC);
+ if (!cmd->data_buf)
+ return -ENOMEM;
+
+ req->buf = cmd->data_buf;
+ } else {
+ req->buf = NULL;
+ req->num_sgs = se_cmd->t_data_nents;
+ req->sg = se_cmd->t_data_sg;
+ }
+
+ req->complete = usbg_data_write_cmpl;
+ req->length = se_cmd->data_length;
+ req->context = cmd;
+ return 0;
+}
+
+static int usbg_send_status_response(struct se_cmd *se_cmd)
+{
+ struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
+ se_cmd);
+ struct f_uas *fu = cmd->fu;
+
+ if (fu->flags & USBG_IS_BOT)
+ return bot_send_status_response(cmd);
+ else
+ return uasp_send_status_response(cmd);
+}
+
+static int usbg_send_write_request(struct se_cmd *se_cmd)
+{
+ struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
+ se_cmd);
+ struct f_uas *fu = cmd->fu;
+
+ if (fu->flags & USBG_IS_BOT)
+ return bot_send_write_request(cmd);
+ else
+ return uasp_send_write_request(cmd);
+}
+
+static int usbg_send_read_response(struct se_cmd *se_cmd)
+{
+ struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
+ se_cmd);
+ struct f_uas *fu = cmd->fu;
+
+ if (fu->flags & USBG_IS_BOT)
+ return bot_send_read_response(cmd);
+ else
+ return uasp_send_read_response(cmd);
+}
+
+static void usbg_cmd_work(struct work_struct *work)
+{
+ struct usbg_cmd *cmd = container_of(work, struct usbg_cmd, work);
+ struct se_cmd *se_cmd;
+ struct tcm_usbg_nexus *tv_nexus;
+ struct usbg_tpg *tpg;
+ int dir;
+
+ se_cmd = &cmd->se_cmd;
+ tpg = cmd->fu->tpg;
+ tv_nexus = tpg->tpg_nexus;
+ dir = get_cmd_dir(cmd->cmd_buf);
+ if (dir < 0) {
+ transport_init_se_cmd(se_cmd,
+ tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo,
+ tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE,
+ cmd->prio_attr, cmd->sense_iu.sense);
+
+ transport_send_check_condition_and_sense(se_cmd,
+ TCM_UNSUPPORTED_SCSI_OPCODE, 1);
+ usbg_cleanup_cmd(cmd);
+ return;
+ }
+
+ target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess,
+ cmd->cmd_buf, cmd->sense_iu.sense, cmd->unpacked_lun,
+ 0, cmd->prio_attr, dir, TARGET_SCF_UNKNOWN_SIZE);
+}
+
+static int usbg_submit_command(struct f_uas *fu,
+ void *cmdbuf, unsigned int len)
+{
+ struct command_iu *cmd_iu = cmdbuf;
+ struct usbg_cmd *cmd;
+ struct usbg_tpg *tpg;
+ struct se_cmd *se_cmd;
+ struct tcm_usbg_nexus *tv_nexus;
+ u32 cmd_len;
+ int ret;
+
+ if (cmd_iu->iu_id != IU_ID_COMMAND) {
+ pr_err("Unsupported type %d\n", cmd_iu->iu_id);
+ return -EINVAL;
+ }
+
+ cmd = kzalloc(sizeof *cmd, GFP_ATOMIC);
+ if (!cmd)
+ return -ENOMEM;
+
+ cmd->fu = fu;
+
+ /* XXX until I figure out why I can't free in on complete */
+ kref_init(&cmd->ref);
+ kref_get(&cmd->ref);
+
+ tpg = fu->tpg;
+ cmd_len = (cmd_iu->len & ~0x3) + 16;
+ if (cmd_len > USBG_MAX_CMD)
+ goto err;
+
+ memcpy(cmd->cmd_buf, cmd_iu->cdb, cmd_len);
+
+ cmd->tag = be16_to_cpup(&cmd_iu->tag);
+ if (fu->flags & USBG_USE_STREAMS) {
+ if (cmd->tag > UASP_SS_EP_COMP_NUM_STREAMS)
+ goto err;
+ if (!cmd->tag)
+ cmd->stream = &fu->stream[0];
+ else
+ cmd->stream = &fu->stream[cmd->tag - 1];
+ } else {
+ cmd->stream = &fu->stream[0];
+ }
+
+ tv_nexus = tpg->tpg_nexus;
+ if (!tv_nexus) {
+ pr_err("Missing nexus, ignoring command\n");
+ goto err;
+ }
+
+ switch (cmd_iu->prio_attr & 0x7) {
+ case UAS_HEAD_TAG:
+ cmd->prio_attr = MSG_HEAD_TAG;
+ break;
+ case UAS_ORDERED_TAG:
+ cmd->prio_attr = MSG_ORDERED_TAG;
+ break;
+ case UAS_ACA:
+ cmd->prio_attr = MSG_ACA_TAG;
+ break;
+ default:
+ pr_debug_once("Unsupported prio_attr: %02x.\n",
+ cmd_iu->prio_attr);
+ case UAS_SIMPLE_TAG:
+ cmd->prio_attr = MSG_SIMPLE_TAG;
+ break;
+ }
+
+ se_cmd = &cmd->se_cmd;
+ cmd->unpacked_lun = scsilun_to_int(&cmd_iu->lun);
+
+ INIT_WORK(&cmd->work, usbg_cmd_work);
+ ret = queue_work(tpg->workqueue, &cmd->work);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+err:
+ kfree(cmd);
+ return -EINVAL;
+}
+
+static void bot_cmd_work(struct work_struct *work)
+{
+ struct usbg_cmd *cmd = container_of(work, struct usbg_cmd, work);
+ struct se_cmd *se_cmd;
+ struct tcm_usbg_nexus *tv_nexus;
+ struct usbg_tpg *tpg;
+ int dir;
+
+ se_cmd = &cmd->se_cmd;
+ tpg = cmd->fu->tpg;
+ tv_nexus = tpg->tpg_nexus;
+ dir = get_cmd_dir(cmd->cmd_buf);
+ if (dir < 0) {
+ transport_init_se_cmd(se_cmd,
+ tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo,
+ tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE,
+ cmd->prio_attr, cmd->sense_iu.sense);
+
+ transport_send_check_condition_and_sense(se_cmd,
+ TCM_UNSUPPORTED_SCSI_OPCODE, 1);
+ usbg_cleanup_cmd(cmd);
+ return;
+ }
+
+ target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess,
+ cmd->cmd_buf, cmd->sense_iu.sense, cmd->unpacked_lun,
+ cmd->data_len, cmd->prio_attr, dir, 0);
+}
+
+static int bot_submit_command(struct f_uas *fu,
+ void *cmdbuf, unsigned int len)
+{
+ struct bulk_cb_wrap *cbw = cmdbuf;
+ struct usbg_cmd *cmd;
+ struct usbg_tpg *tpg;
+ struct se_cmd *se_cmd;
+ struct tcm_usbg_nexus *tv_nexus;
+ u32 cmd_len;
+ int ret;
+
+ if (cbw->Signature != cpu_to_le32(US_BULK_CB_SIGN)) {
+ pr_err("Wrong signature on CBW\n");
+ return -EINVAL;
+ }
+ if (len != 31) {
+ pr_err("Wrong length for CBW\n");
+ return -EINVAL;
+ }
+
+ cmd_len = cbw->Length;
+ if (cmd_len < 1 || cmd_len > 16)
+ return -EINVAL;
+
+ cmd = kzalloc(sizeof *cmd, GFP_ATOMIC);
+ if (!cmd)
+ return -ENOMEM;
+
+ cmd->fu = fu;
+
+ /* XXX until I figure out why I can't free in on complete */
+ kref_init(&cmd->ref);
+ kref_get(&cmd->ref);
+
+ tpg = fu->tpg;
+
+ memcpy(cmd->cmd_buf, cbw->CDB, cmd_len);
+
+ cmd->bot_tag = cbw->Tag;
+
+ tv_nexus = tpg->tpg_nexus;
+ if (!tv_nexus) {
+ pr_err("Missing nexus, ignoring command\n");
+ goto err;
+ }
+
+ cmd->prio_attr = MSG_SIMPLE_TAG;
+ se_cmd = &cmd->se_cmd;
+ cmd->unpacked_lun = cbw->Lun;
+ cmd->is_read = cbw->Flags & US_BULK_FLAG_IN ? 1 : 0;
+ cmd->data_len = le32_to_cpu(cbw->DataTransferLength);
+
+ INIT_WORK(&cmd->work, bot_cmd_work);
+ ret = queue_work(tpg->workqueue, &cmd->work);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+err:
+ kfree(cmd);
+ return -EINVAL;
+}
+
+/* Start fabric.c code */
+
+static int usbg_check_true(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static int usbg_check_false(struct se_portal_group *se_tpg)
+{
+ return 0;
+}
+
+static char *usbg_get_fabric_name(void)
+{
+ return "usb_gadget";
+}
+
+static u8 usbg_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg,
+ struct usbg_tpg, se_tpg);
+ struct usbg_tport *tport = tpg->tport;
+ u8 proto_id;
+
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ default:
+ proto_id = sas_get_fabric_proto_ident(se_tpg);
+ break;
+ }
+
+ return proto_id;
+}
+
+static char *usbg_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg,
+ struct usbg_tpg, se_tpg);
+ struct usbg_tport *tport = tpg->tport;
+
+ return &tport->tport_name[0];
+}
+
+static u16 usbg_get_tag(struct se_portal_group *se_tpg)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg,
+ struct usbg_tpg, se_tpg);
+ return tpg->tport_tpgt;
+}
+
+static u32 usbg_get_default_depth(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static u32 usbg_get_pr_transport_id(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code,
+ unsigned char *buf)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg,
+ struct usbg_tpg, se_tpg);
+ struct usbg_tport *tport = tpg->tport;
+ int ret = 0;
+
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ default:
+ ret = sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+ break;
+ }
+
+ return ret;
+}
+
+static u32 usbg_get_pr_transport_id_len(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg,
+ struct usbg_tpg, se_tpg);
+ struct usbg_tport *tport = tpg->tport;
+ int ret = 0;
+
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ default:
+ ret = sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+ break;
+ }
+
+ return ret;
+}
+
+static char *usbg_parse_pr_out_transport_id(
+ struct se_portal_group *se_tpg,
+ const char *buf,
+ u32 *out_tid_len,
+ char **port_nexus_ptr)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg,
+ struct usbg_tpg, se_tpg);
+ struct usbg_tport *tport = tpg->tport;
+ char *tid = NULL;
+
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ default:
+ tid = sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+ }
+
+ return tid;
+}
+
+static struct se_node_acl *usbg_alloc_fabric_acl(struct se_portal_group *se_tpg)
+{
+ struct usbg_nacl *nacl;
+
+ nacl = kzalloc(sizeof(struct usbg_nacl), GFP_KERNEL);
+ if (!nacl) {
+ printk(KERN_ERR "Unable to alocate struct usbg_nacl\n");
+ return NULL;
+ }
+
+ return &nacl->se_node_acl;
+}
+
+static void usbg_release_fabric_acl(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl)
+{
+ struct usbg_nacl *nacl = container_of(se_nacl,
+ struct usbg_nacl, se_node_acl);
+ kfree(nacl);
+}
+
+static u32 usbg_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static int usbg_new_cmd(struct se_cmd *se_cmd)
+{
+ struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
+ se_cmd);
+ int ret;
+
+ ret = target_setup_cmd_from_cdb(se_cmd, cmd->cmd_buf);
+ if (ret)
+ return ret;
+
+ return transport_generic_map_mem_to_cmd(se_cmd, NULL, 0, NULL, 0);
+}
+
+static void usbg_cmd_release(struct kref *ref)
+{
+ struct usbg_cmd *cmd = container_of(ref, struct usbg_cmd,
+ ref);
+
+ transport_generic_free_cmd(&cmd->se_cmd, 0);
+}
+
+static void usbg_release_cmd(struct se_cmd *se_cmd)
+{
+ struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
+ se_cmd);
+ kfree(cmd->data_buf);
+ kfree(cmd);
+ return;
+}
+
+static int usbg_shutdown_session(struct se_session *se_sess)
+{
+ return 0;
+}
+
+static void usbg_close_session(struct se_session *se_sess)
+{
+ return;
+}
+
+static u32 usbg_sess_get_index(struct se_session *se_sess)
+{
+ return 0;
+}
+
+/*
+ * XXX Error recovery: return != 0 if we expect writes. Dunno when that could be
+ */
+static int usbg_write_pending_status(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static void usbg_set_default_node_attrs(struct se_node_acl *nacl)
+{
+ return;
+}
+
+static u32 usbg_get_task_tag(struct se_cmd *se_cmd)
+{
+ struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
+ se_cmd);
+ struct f_uas *fu = cmd->fu;
+
+ if (fu->flags & USBG_IS_BOT)
+ return le32_to_cpu(cmd->bot_tag);
+ else
+ return cmd->tag;
+}
+
+static int usbg_get_cmd_state(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static int usbg_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static u16 usbg_set_fabric_sense_len(struct se_cmd *se_cmd, u32 sense_length)
+{
+ return 0;
+}
+
+static u16 usbg_get_fabric_sense_len(void)
+{
+ return 0;
+}
+
+static const char *usbg_check_wwn(const char *name)
+{
+ const char *n;
+ unsigned int len;
+
+ n = strstr(name, "naa.");
+ if (!n)
+ return NULL;
+ n += 4;
+ len = strlen(n);
+ if (len == 0 || len > USBG_NAMELEN - 1)
+ return NULL;
+ return n;
+}
+
+static struct se_node_acl *usbg_make_nodeacl(
+ struct se_portal_group *se_tpg,
+ struct config_group *group,
+ const char *name)
+{
+ struct se_node_acl *se_nacl, *se_nacl_new;
+ struct usbg_nacl *nacl;
+ u64 wwpn = 0;
+ u32 nexus_depth;
+ const char *wnn_name;
+
+ wnn_name = usbg_check_wwn(name);
+ if (!wnn_name)
+ return ERR_PTR(-EINVAL);
+ se_nacl_new = usbg_alloc_fabric_acl(se_tpg);
+ if (!(se_nacl_new))
+ return ERR_PTR(-ENOMEM);
+
+ nexus_depth = 1;
+ /*
+ * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
+ * when converting a NodeACL from demo mode -> explict
+ */
+ se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,
+ name, nexus_depth);
+ if (IS_ERR(se_nacl)) {
+ usbg_release_fabric_acl(se_tpg, se_nacl_new);
+ return se_nacl;
+ }
+ /*
+ * Locate our struct usbg_nacl and set the FC Nport WWPN
+ */
+ nacl = container_of(se_nacl, struct usbg_nacl, se_node_acl);
+ nacl->iport_wwpn = wwpn;
+ snprintf(nacl->iport_name, sizeof(nacl->iport_name), "%s", name);
+ return se_nacl;
+}
+
+static void usbg_drop_nodeacl(struct se_node_acl *se_acl)
+{
+ struct usbg_nacl *nacl = container_of(se_acl,
+ struct usbg_nacl, se_node_acl);
+ core_tpg_del_initiator_node_acl(se_acl->se_tpg, se_acl, 1);
+ kfree(nacl);
+}
+
+struct usbg_tpg *the_only_tpg_I_currently_have;
+
+static struct se_portal_group *usbg_make_tpg(
+ struct se_wwn *wwn,
+ struct config_group *group,
+ const char *name)
+{
+ struct usbg_tport *tport = container_of(wwn, struct usbg_tport,
+ tport_wwn);
+ struct usbg_tpg *tpg;
+ unsigned long tpgt;
+ int ret;
+
+ if (strstr(name, "tpgt_") != name)
+ return ERR_PTR(-EINVAL);
+ if (kstrtoul(name + 5, 0, &tpgt) || tpgt > UINT_MAX)
+ return ERR_PTR(-EINVAL);
+ if (the_only_tpg_I_currently_have) {
+ pr_err("Until the gadget framework can't handle multiple\n");
+ pr_err("gadgets, you can't do this here.\n");
+ return ERR_PTR(-EBUSY);
+ }
+
+ tpg = kzalloc(sizeof(struct usbg_tpg), GFP_KERNEL);
+ if (!tpg) {
+ printk(KERN_ERR "Unable to allocate struct usbg_tpg");
+ return ERR_PTR(-ENOMEM);
+ }
+ mutex_init(&tpg->tpg_mutex);
+ atomic_set(&tpg->tpg_port_count, 0);
+ tpg->workqueue = alloc_workqueue("tcm_usb_gadget", 0, 1);
+ if (!tpg->workqueue) {
+ kfree(tpg);
+ return NULL;
+ }
+
+ tpg->tport = tport;
+ tpg->tport_tpgt = tpgt;
+
+ ret = core_tpg_register(&usbg_fabric_configfs->tf_ops, wwn,
+ &tpg->se_tpg, tpg,
+ TRANSPORT_TPG_TYPE_NORMAL);
+ if (ret < 0) {
+ destroy_workqueue(tpg->workqueue);
+ kfree(tpg);
+ return NULL;
+ }
+ the_only_tpg_I_currently_have = tpg;
+ return &tpg->se_tpg;
+}
+
+static void usbg_drop_tpg(struct se_portal_group *se_tpg)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg,
+ struct usbg_tpg, se_tpg);
+
+ core_tpg_deregister(se_tpg);
+ destroy_workqueue(tpg->workqueue);
+ kfree(tpg);
+ the_only_tpg_I_currently_have = NULL;
+}
+
+static struct se_wwn *usbg_make_tport(
+ struct target_fabric_configfs *tf,
+ struct config_group *group,
+ const char *name)
+{
+ struct usbg_tport *tport;
+ const char *wnn_name;
+ u64 wwpn = 0;
+
+ wnn_name = usbg_check_wwn(name);
+ if (!wnn_name)
+ return ERR_PTR(-EINVAL);
+
+ tport = kzalloc(sizeof(struct usbg_tport), GFP_KERNEL);
+ if (!(tport)) {
+ printk(KERN_ERR "Unable to allocate struct usbg_tport");
+ return ERR_PTR(-ENOMEM);
+ }
+ tport->tport_wwpn = wwpn;
+ snprintf(tport->tport_name, sizeof(tport->tport_name), wnn_name);
+ return &tport->tport_wwn;
+}
+
+static void usbg_drop_tport(struct se_wwn *wwn)
+{
+ struct usbg_tport *tport = container_of(wwn,
+ struct usbg_tport, tport_wwn);
+ kfree(tport);
+}
+
+/*
+ * If somebody feels like dropping the version property, go ahead.
+ */
+static ssize_t usbg_wwn_show_attr_version(
+ struct target_fabric_configfs *tf,
+ char *page)
+{
+ return sprintf(page, "usb-gadget fabric module\n");
+}
+TF_WWN_ATTR_RO(usbg, version);
+
+static struct configfs_attribute *usbg_wwn_attrs[] = {
+ &usbg_wwn_version.attr,
+ NULL,
+};
+
+static ssize_t tcm_usbg_tpg_show_enable(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", tpg->gadget_connect);
+}
+
+static int usbg_attach(struct usbg_tpg *);
+static void usbg_detach(struct usbg_tpg *);
+
+static ssize_t tcm_usbg_tpg_store_enable(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+ unsigned long op;
+ ssize_t ret;
+
+ ret = kstrtoul(page, 0, &op);
+ if (ret < 0)
+ return -EINVAL;
+ if (op > 1)
+ return -EINVAL;
+
+ if (op && tpg->gadget_connect)
+ goto out;
+ if (!op && !tpg->gadget_connect)
+ goto out;
+
+ if (op) {
+ ret = usbg_attach(tpg);
+ if (ret)
+ goto out;
+ } else {
+ usbg_detach(tpg);
+ }
+ tpg->gadget_connect = op;
+out:
+ return count;
+}
+TF_TPG_BASE_ATTR(tcm_usbg, enable, S_IRUGO | S_IWUSR);
+
+static ssize_t tcm_usbg_tpg_show_nexus(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+ struct tcm_usbg_nexus *tv_nexus;
+ ssize_t ret;
+
+ mutex_lock(&tpg->tpg_mutex);
+ tv_nexus = tpg->tpg_nexus;
+ if (!tv_nexus) {
+ ret = -ENODEV;
+ goto out;
+ }
+ ret = snprintf(page, PAGE_SIZE, "%s\n",
+ tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
+out:
+ mutex_unlock(&tpg->tpg_mutex);
+ return ret;
+}
+
+static int tcm_usbg_make_nexus(struct usbg_tpg *tpg, char *name)
+{
+ struct se_portal_group *se_tpg;
+ struct tcm_usbg_nexus *tv_nexus;
+ int ret;
+
+ mutex_lock(&tpg->tpg_mutex);
+ if (tpg->tpg_nexus) {
+ ret = -EEXIST;
+ pr_debug("tpg->tpg_nexus already exists\n");
+ goto err_unlock;
+ }
+ se_tpg = &tpg->se_tpg;
+
+ ret = -ENOMEM;
+ tv_nexus = kzalloc(sizeof(*tv_nexus), GFP_KERNEL);
+ if (!tv_nexus) {
+ pr_err("Unable to allocate struct tcm_vhost_nexus\n");
+ goto err_unlock;
+ }
+ tv_nexus->tvn_se_sess = transport_init_session();
+ if (IS_ERR(tv_nexus->tvn_se_sess))
+ goto err_free;
+
+ /*
+ * Since we are running in 'demo mode' this call with generate a
+ * struct se_node_acl for the tcm_vhost struct se_portal_group with
+ * the SCSI Initiator port name of the passed configfs group 'name'.
+ */
+ tv_nexus->tvn_se_sess->se_node_acl = core_tpg_check_initiator_node_acl(
+ se_tpg, name);
+ if (!tv_nexus->tvn_se_sess->se_node_acl) {
+ pr_debug("core_tpg_check_initiator_node_acl() failed"
+ " for %s\n", name);
+ goto err_session;
+ }
+ /*
+ * Now register the TCM vHost virtual I_T Nexus as active with the
+ * call to __transport_register_session()
+ */
+ __transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl,
+ tv_nexus->tvn_se_sess, tv_nexus);
+ tpg->tpg_nexus = tv_nexus;
+ mutex_unlock(&tpg->tpg_mutex);
+ return 0;
+
+err_session:
+ transport_free_session(tv_nexus->tvn_se_sess);
+err_free:
+ kfree(tv_nexus);
+err_unlock:
+ mutex_unlock(&tpg->tpg_mutex);
+ return ret;
+}
+
+static int tcm_usbg_drop_nexus(struct usbg_tpg *tpg)
+{
+ struct se_session *se_sess;
+ struct tcm_usbg_nexus *tv_nexus;
+ int ret = -ENODEV;
+
+ mutex_lock(&tpg->tpg_mutex);
+ tv_nexus = tpg->tpg_nexus;
+ if (!tv_nexus)
+ goto out;
+
+ se_sess = tv_nexus->tvn_se_sess;
+ if (!se_sess)
+ goto out;
+
+ if (atomic_read(&tpg->tpg_port_count)) {
+ ret = -EPERM;
+ pr_err("Unable to remove Host I_T Nexus with"
+ " active TPG port count: %d\n",
+ atomic_read(&tpg->tpg_port_count));
+ goto out;
+ }
+
+ pr_debug("Removing I_T Nexus to Initiator Port: %s\n",
+ tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
+ /*
+ * Release the SCSI I_T Nexus to the emulated vHost Target Port
+ */
+ transport_deregister_session(tv_nexus->tvn_se_sess);
+ tpg->tpg_nexus = NULL;
+
+ kfree(tv_nexus);
+out:
+ mutex_unlock(&tpg->tpg_mutex);
+ return 0;
+}
+
+static ssize_t tcm_usbg_tpg_store_nexus(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+ unsigned char i_port[USBG_NAMELEN], *ptr;
+ int ret;
+
+ if (!strncmp(page, "NULL", 4)) {
+ ret = tcm_usbg_drop_nexus(tpg);
+ return (!ret) ? count : ret;
+ }
+ if (strlen(page) > USBG_NAMELEN) {
+ pr_err("Emulated NAA Sas Address: %s, exceeds"
+ " max: %d\n", page, USBG_NAMELEN);
+ return -EINVAL;
+ }
+ snprintf(i_port, USBG_NAMELEN, "%s", page);
+
+ ptr = strstr(i_port, "naa.");
+ if (!ptr) {
+ pr_err("Missing 'naa.' prefix\n");
+ return -EINVAL;
+ }
+
+ if (i_port[strlen(i_port) - 1] == '\n')
+ i_port[strlen(i_port) - 1] = '\0';
+
+ ret = tcm_usbg_make_nexus(tpg, &i_port[4]);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+TF_TPG_BASE_ATTR(tcm_usbg, nexus, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *usbg_base_attrs[] = {
+ &tcm_usbg_tpg_enable.attr,
+ &tcm_usbg_tpg_nexus.attr,
+ NULL,
+};
+
+static int usbg_port_link(struct se_portal_group *se_tpg, struct se_lun *lun)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+
+ atomic_inc(&tpg->tpg_port_count);
+ smp_mb__after_atomic_inc();
+ return 0;
+}
+
+static void usbg_port_unlink(struct se_portal_group *se_tpg,
+ struct se_lun *se_lun)
+{
+ struct usbg_tpg *tpg = container_of(se_tpg, struct usbg_tpg, se_tpg);
+
+ atomic_dec(&tpg->tpg_port_count);
+ smp_mb__after_atomic_dec();
+}
+
+static int usbg_check_stop_free(struct se_cmd *se_cmd)
+{
+ struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd,
+ se_cmd);
+
+ kref_put(&cmd->ref, usbg_cmd_release);
+ return 1;
+}
+
+static struct target_core_fabric_ops usbg_ops = {
+ .get_fabric_name = usbg_get_fabric_name,
+ .get_fabric_proto_ident = usbg_get_fabric_proto_ident,
+ .tpg_get_wwn = usbg_get_fabric_wwn,
+ .tpg_get_tag = usbg_get_tag,
+ .tpg_get_default_depth = usbg_get_default_depth,
+ .tpg_get_pr_transport_id = usbg_get_pr_transport_id,
+ .tpg_get_pr_transport_id_len = usbg_get_pr_transport_id_len,
+ .tpg_parse_pr_out_transport_id = usbg_parse_pr_out_transport_id,
+ .tpg_check_demo_mode = usbg_check_true,
+ .tpg_check_demo_mode_cache = usbg_check_false,
+ .tpg_check_demo_mode_write_protect = usbg_check_false,
+ .tpg_check_prod_mode_write_protect = usbg_check_false,
+ .tpg_alloc_fabric_acl = usbg_alloc_fabric_acl,
+ .tpg_release_fabric_acl = usbg_release_fabric_acl,
+ .tpg_get_inst_index = usbg_tpg_get_inst_index,
+ .new_cmd_map = usbg_new_cmd,
+ .release_cmd = usbg_release_cmd,
+ .shutdown_session = usbg_shutdown_session,
+ .close_session = usbg_close_session,
+ .sess_get_index = usbg_sess_get_index,
+ .sess_get_initiator_sid = NULL,
+ .write_pending = usbg_send_write_request,
+ .write_pending_status = usbg_write_pending_status,
+ .set_default_node_attributes = usbg_set_default_node_attrs,
+ .get_task_tag = usbg_get_task_tag,
+ .get_cmd_state = usbg_get_cmd_state,
+ .queue_data_in = usbg_send_read_response,
+ .queue_status = usbg_send_status_response,
+ .queue_tm_rsp = usbg_queue_tm_rsp,
+ .get_fabric_sense_len = usbg_get_fabric_sense_len,
+ .set_fabric_sense_len = usbg_set_fabric_sense_len,
+ .check_stop_free = usbg_check_stop_free,
+
+ .fabric_make_wwn = usbg_make_tport,
+ .fabric_drop_wwn = usbg_drop_tport,
+ .fabric_make_tpg = usbg_make_tpg,
+ .fabric_drop_tpg = usbg_drop_tpg,
+ .fabric_post_link = usbg_port_link,
+ .fabric_pre_unlink = usbg_port_unlink,
+ .fabric_make_np = NULL,
+ .fabric_drop_np = NULL,
+ .fabric_make_nodeacl = usbg_make_nodeacl,
+ .fabric_drop_nodeacl = usbg_drop_nodeacl,
+};
+
+static int usbg_register_configfs(void)
+{
+ struct target_fabric_configfs *fabric;
+ int ret;
+
+ fabric = target_fabric_configfs_init(THIS_MODULE, "usb_gadget");
+ if (IS_ERR(fabric)) {
+ printk(KERN_ERR "target_fabric_configfs_init() failed\n");
+ return PTR_ERR(fabric);
+ }
+
+ fabric->tf_ops = usbg_ops;
+ TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = usbg_wwn_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = usbg_base_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+ ret = target_fabric_configfs_register(fabric);
+ if (ret < 0) {
+ printk(KERN_ERR "target_fabric_configfs_register() failed"
+ " for usb-gadget\n");
+ return ret;
+ }
+ usbg_fabric_configfs = fabric;
+ return 0;
+};
+
+static void usbg_deregister_configfs(void)
+{
+ if (!(usbg_fabric_configfs))
+ return;
+
+ target_fabric_configfs_deregister(usbg_fabric_configfs);
+ usbg_fabric_configfs = NULL;
+};
+
+/* Start gadget.c code */
+
+static struct usb_interface_descriptor bot_intf_desc = {
+ .bLength = sizeof(bot_intf_desc),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bAlternateSetting = USB_G_ALT_INT_BBB,
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = USB_SC_SCSI,
+ .bInterfaceProtocol = USB_PR_BULK,
+ .iInterface = USB_G_STR_INT_UAS,
+};
+
+static struct usb_interface_descriptor uasp_intf_desc = {
+ .bLength = sizeof(uasp_intf_desc),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bNumEndpoints = 4,
+ .bAlternateSetting = USB_G_ALT_INT_UAS,
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = USB_SC_SCSI,
+ .bInterfaceProtocol = USB_PR_UAS,
+ .iInterface = USB_G_STR_INT_BBB,
+};
+
+static struct usb_endpoint_descriptor uasp_bi_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor uasp_fs_bi_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_pipe_usage_descriptor uasp_bi_pipe_desc = {
+ .bLength = sizeof(uasp_bi_pipe_desc),
+ .bDescriptorType = USB_DT_PIPE_USAGE,
+ .bPipeID = DATA_IN_PIPE_ID,
+};
+
+static struct usb_endpoint_descriptor uasp_ss_bi_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor uasp_bi_ep_comp_desc = {
+ .bLength = sizeof(uasp_bi_ep_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 0,
+ .bmAttributes = UASP_SS_EP_COMP_LOG_STREAMS,
+ .wBytesPerInterval = 0,
+};
+
+static struct usb_ss_ep_comp_descriptor bot_bi_ep_comp_desc = {
+ .bLength = sizeof(bot_bi_ep_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 0,
+};
+
+static struct usb_endpoint_descriptor uasp_bo_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor uasp_fs_bo_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_pipe_usage_descriptor uasp_bo_pipe_desc = {
+ .bLength = sizeof(uasp_bo_pipe_desc),
+ .bDescriptorType = USB_DT_PIPE_USAGE,
+ .bPipeID = DATA_OUT_PIPE_ID,
+};
+
+static struct usb_endpoint_descriptor uasp_ss_bo_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(0x400),
+};
+
+static struct usb_ss_ep_comp_descriptor uasp_bo_ep_comp_desc = {
+ .bLength = sizeof(uasp_bo_ep_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bmAttributes = UASP_SS_EP_COMP_LOG_STREAMS,
+};
+
+static struct usb_ss_ep_comp_descriptor bot_bo_ep_comp_desc = {
+ .bLength = sizeof(bot_bo_ep_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_endpoint_descriptor uasp_status_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor uasp_fs_status_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_pipe_usage_descriptor uasp_status_pipe_desc = {
+ .bLength = sizeof(uasp_status_pipe_desc),
+ .bDescriptorType = USB_DT_PIPE_USAGE,
+ .bPipeID = STATUS_PIPE_ID,
+};
+
+static struct usb_endpoint_descriptor uasp_ss_status_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor uasp_status_in_ep_comp_desc = {
+ .bLength = sizeof(uasp_status_in_ep_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bmAttributes = UASP_SS_EP_COMP_LOG_STREAMS,
+};
+
+static struct usb_endpoint_descriptor uasp_cmd_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor uasp_fs_cmd_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_pipe_usage_descriptor uasp_cmd_pipe_desc = {
+ .bLength = sizeof(uasp_cmd_pipe_desc),
+ .bDescriptorType = USB_DT_PIPE_USAGE,
+ .bPipeID = CMD_PIPE_ID,
+};
+
+static struct usb_endpoint_descriptor uasp_ss_cmd_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor uasp_cmd_comp_desc = {
+ .bLength = sizeof(uasp_cmd_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *uasp_fs_function_desc[] = {
+ (struct usb_descriptor_header *) &bot_intf_desc,
+ (struct usb_descriptor_header *) &uasp_fs_bi_desc,
+ (struct usb_descriptor_header *) &uasp_fs_bo_desc,
+
+ (struct usb_descriptor_header *) &uasp_intf_desc,
+ (struct usb_descriptor_header *) &uasp_fs_bi_desc,
+ (struct usb_descriptor_header *) &uasp_bi_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_fs_bo_desc,
+ (struct usb_descriptor_header *) &uasp_bo_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_fs_status_desc,
+ (struct usb_descriptor_header *) &uasp_status_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_fs_cmd_desc,
+ (struct usb_descriptor_header *) &uasp_cmd_pipe_desc,
+};
+
+static struct usb_descriptor_header *uasp_hs_function_desc[] = {
+ (struct usb_descriptor_header *) &bot_intf_desc,
+ (struct usb_descriptor_header *) &uasp_bi_desc,
+ (struct usb_descriptor_header *) &uasp_bo_desc,
+
+ (struct usb_descriptor_header *) &uasp_intf_desc,
+ (struct usb_descriptor_header *) &uasp_bi_desc,
+ (struct usb_descriptor_header *) &uasp_bi_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_bo_desc,
+ (struct usb_descriptor_header *) &uasp_bo_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_status_desc,
+ (struct usb_descriptor_header *) &uasp_status_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_cmd_desc,
+ (struct usb_descriptor_header *) &uasp_cmd_pipe_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *uasp_ss_function_desc[] = {
+ (struct usb_descriptor_header *) &bot_intf_desc,
+ (struct usb_descriptor_header *) &uasp_ss_bi_desc,
+ (struct usb_descriptor_header *) &bot_bi_ep_comp_desc,
+ (struct usb_descriptor_header *) &uasp_ss_bo_desc,
+ (struct usb_descriptor_header *) &bot_bo_ep_comp_desc,
+
+ (struct usb_descriptor_header *) &uasp_intf_desc,
+ (struct usb_descriptor_header *) &uasp_ss_bi_desc,
+ (struct usb_descriptor_header *) &uasp_bi_ep_comp_desc,
+ (struct usb_descriptor_header *) &uasp_bi_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_ss_bo_desc,
+ (struct usb_descriptor_header *) &uasp_bo_ep_comp_desc,
+ (struct usb_descriptor_header *) &uasp_bo_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_ss_status_desc,
+ (struct usb_descriptor_header *) &uasp_status_in_ep_comp_desc,
+ (struct usb_descriptor_header *) &uasp_status_pipe_desc,
+ (struct usb_descriptor_header *) &uasp_ss_cmd_desc,
+ (struct usb_descriptor_header *) &uasp_cmd_comp_desc,
+ (struct usb_descriptor_header *) &uasp_cmd_pipe_desc,
+ NULL,
+};
+
+#define UAS_VENDOR_ID 0x0525 /* NetChip */
+#define UAS_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
+
+static struct usb_device_descriptor usbg_device_desc = {
+ .bLength = sizeof(usbg_device_desc),
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+ .idVendor = cpu_to_le16(UAS_VENDOR_ID),
+ .idProduct = cpu_to_le16(UAS_PRODUCT_ID),
+ .iManufacturer = USB_G_STR_MANUFACTOR,
+ .iProduct = USB_G_STR_PRODUCT,
+ .iSerialNumber = USB_G_STR_SERIAL,
+
+ .bNumConfigurations = 1,
+};
+
+static struct usb_string usbg_us_strings[] = {
+ { USB_G_STR_MANUFACTOR, "Target Manufactor"},
+ { USB_G_STR_PRODUCT, "Target Product"},
+ { USB_G_STR_SERIAL, "000000000001"},
+ { USB_G_STR_CONFIG, "default config"},
+ { USB_G_STR_INT_UAS, "USB Attached SCSI"},
+ { USB_G_STR_INT_BBB, "Bulk Only Transport"},
+ { },
+};
+
+static struct usb_gadget_strings usbg_stringtab = {
+ .language = 0x0409,
+ .strings = usbg_us_strings,
+};
+
+static struct usb_gadget_strings *usbg_strings[] = {
+ &usbg_stringtab,
+ NULL,
+};
+
+static int guas_unbind(struct usb_composite_dev *cdev)
+{
+ return 0;
+}
+
+static struct usb_configuration usbg_config_driver = {
+ .label = "Linux Target",
+ .bConfigurationValue = 1,
+ .iConfiguration = USB_G_STR_CONFIG,
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+static void give_back_ep(struct usb_ep **pep)
+{
+ struct usb_ep *ep = *pep;
+ if (!ep)
+ return;
+ ep->driver_data = NULL;
+}
+
+static int usbg_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct f_uas *fu = to_f_uas(f);
+ struct usb_gadget *gadget = c->cdev->gadget;
+ struct usb_ep *ep;
+ int iface;
+
+ iface = usb_interface_id(c, f);
+ if (iface < 0)
+ return iface;
+
+ bot_intf_desc.bInterfaceNumber = iface;
+ uasp_intf_desc.bInterfaceNumber = iface;
+ fu->iface = iface;
+ ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bi_desc,
+ &uasp_bi_ep_comp_desc);
+ if (!ep)
+ goto ep_fail;
+
+ ep->driver_data = fu;
+ fu->ep_in = ep;
+
+ ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bo_desc,
+ &uasp_bo_ep_comp_desc);
+ if (!ep)
+ goto ep_fail;
+ ep->driver_data = fu;
+ fu->ep_out = ep;
+
+ ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_status_desc,
+ &uasp_status_in_ep_comp_desc);
+ if (!ep)
+ goto ep_fail;
+ ep->driver_data = fu;
+ fu->ep_status = ep;
+
+ ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_cmd_desc,
+ &uasp_cmd_comp_desc);
+ if (!ep)
+ goto ep_fail;
+ ep->driver_data = fu;
+ fu->ep_cmd = ep;
+
+ /* Assume endpoint addresses are the same for both speeds */
+ uasp_bi_desc.bEndpointAddress = uasp_ss_bi_desc.bEndpointAddress;
+ uasp_bo_desc.bEndpointAddress = uasp_ss_bo_desc.bEndpointAddress;
+ uasp_status_desc.bEndpointAddress =
+ uasp_ss_status_desc.bEndpointAddress;
+ uasp_cmd_desc.bEndpointAddress = uasp_ss_cmd_desc.bEndpointAddress;
+
+ uasp_fs_bi_desc.bEndpointAddress = uasp_ss_bi_desc.bEndpointAddress;
+ uasp_fs_bo_desc.bEndpointAddress = uasp_ss_bo_desc.bEndpointAddress;
+ uasp_fs_status_desc.bEndpointAddress =
+ uasp_ss_status_desc.bEndpointAddress;
+ uasp_fs_cmd_desc.bEndpointAddress = uasp_ss_cmd_desc.bEndpointAddress;
+
+ return 0;
+ep_fail:
+ pr_err("Can't claim all required eps\n");
+
+ give_back_ep(&fu->ep_in);
+ give_back_ep(&fu->ep_out);
+ give_back_ep(&fu->ep_status);
+ give_back_ep(&fu->ep_cmd);
+ return -ENOTSUPP;
+}
+
+static void usbg_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct f_uas *fu = to_f_uas(f);
+
+ kfree(fu);
+}
+
+struct guas_setup_wq {
+ struct work_struct work;
+ struct f_uas *fu;
+ unsigned int alt;
+};
+
+static void usbg_delayed_set_alt(struct work_struct *wq)
+{
+ struct guas_setup_wq *work = container_of(wq, struct guas_setup_wq,
+ work);
+ struct f_uas *fu = work->fu;
+ int alt = work->alt;
+
+ kfree(work);
+
+ if (fu->flags & USBG_IS_BOT)
+ bot_cleanup_old_alt(fu);
+ if (fu->flags & USBG_IS_UAS)
+ uasp_cleanup_old_alt(fu);
+
+ if (alt == USB_G_ALT_INT_BBB)
+ bot_set_alt(fu);
+ else if (alt == USB_G_ALT_INT_UAS)
+ uasp_set_alt(fu);
+ usb_composite_setup_continue(fu->function.config->cdev);
+}
+
+static int usbg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct f_uas *fu = to_f_uas(f);
+
+ if ((alt == USB_G_ALT_INT_BBB) || (alt == USB_G_ALT_INT_UAS)) {
+ struct guas_setup_wq *work;
+
+ work = kmalloc(sizeof(*work), GFP_ATOMIC);
+ if (!work)
+ return -ENOMEM;
+ INIT_WORK(&work->work, usbg_delayed_set_alt);
+ work->fu = fu;
+ work->alt = alt;
+ schedule_work(&work->work);
+ return USB_GADGET_DELAYED_STATUS;
+ }
+ return -EOPNOTSUPP;
+}
+
+static void usbg_disable(struct usb_function *f)
+{
+ struct f_uas *fu = to_f_uas(f);
+
+ if (fu->flags & USBG_IS_UAS)
+ uasp_cleanup_old_alt(fu);
+ else if (fu->flags & USBG_IS_BOT)
+ bot_cleanup_old_alt(fu);
+ fu->flags = 0;
+}
+
+static int usbg_setup(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct f_uas *fu = to_f_uas(f);
+
+ if (!(fu->flags & USBG_IS_BOT))
+ return -EOPNOTSUPP;
+
+ return usbg_bot_setup(f, ctrl);
+}
+
+static int usbg_cfg_bind(struct usb_configuration *c)
+{
+ struct f_uas *fu;
+ int ret;
+
+ fu = kzalloc(sizeof(*fu), GFP_KERNEL);
+ if (!fu)
+ return -ENOMEM;
+ fu->function.name = "Target Function";
+ fu->function.descriptors = uasp_fs_function_desc;
+ fu->function.hs_descriptors = uasp_hs_function_desc;
+ fu->function.ss_descriptors = uasp_ss_function_desc;
+ fu->function.bind = usbg_bind;
+ fu->function.unbind = usbg_unbind;
+ fu->function.set_alt = usbg_set_alt;
+ fu->function.setup = usbg_setup;
+ fu->function.disable = usbg_disable;
+ fu->tpg = the_only_tpg_I_currently_have;
+
+ ret = usb_add_function(c, &fu->function);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ kfree(fu);
+ return ret;
+}
+
+static int usb_target_bind(struct usb_composite_dev *cdev)
+{
+ int ret;
+
+ ret = usb_add_config(cdev, &usbg_config_driver,
+ usbg_cfg_bind);
+ return 0;
+}
+
+static struct usb_composite_driver usbg_driver = {
+ .name = "g_target",
+ .dev = &usbg_device_desc,
+ .strings = usbg_strings,
+ .max_speed = USB_SPEED_SUPER,
+ .unbind = guas_unbind,
+};
+
+static int usbg_attach(struct usbg_tpg *tpg)
+{
+ return usb_composite_probe(&usbg_driver, usb_target_bind);
+}
+
+static void usbg_detach(struct usbg_tpg *tpg)
+{
+ usb_composite_unregister(&usbg_driver);
+}
+
+static int __init usb_target_gadget_init(void)
+{
+ int ret;
+
+ ret = usbg_register_configfs();
+ return ret;
+}
+module_init(usb_target_gadget_init);
+
+static void __exit usb_target_gadget_exit(void)
+{
+ usbg_deregister_configfs();
+}
+module_exit(usb_target_gadget_exit);
+
+MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>");
+MODULE_DESCRIPTION("usb-gadget fabric");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/tcm_usb_gadget.h b/drivers/usb/gadget/tcm_usb_gadget.h
new file mode 100644
index 0000000..bb18999
--- /dev/null
+++ b/drivers/usb/gadget/tcm_usb_gadget.h
@@ -0,0 +1,146 @@
+#ifndef __TARGET_USB_GADGET_H__
+#define __TARGET_USB_GADGET_H__
+
+#include <linux/kref.h>
+/* #include <linux/usb/uas.h> */
+#include <linux/usb/composite.h>
+#include <linux/usb/uas.h>
+#include <linux/usb/storage.h>
+#include <scsi/scsi.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#define USBG_NAMELEN 32
+
+#define fuas_to_gadget(f) (f->function.config->cdev->gadget)
+#define UASP_SS_EP_COMP_LOG_STREAMS 4
+#define UASP_SS_EP_COMP_NUM_STREAMS (1 << UASP_SS_EP_COMP_LOG_STREAMS)
+
+#define USB_G_STR_MANUFACTOR 1
+#define USB_G_STR_PRODUCT 2
+#define USB_G_STR_SERIAL 3
+#define USB_G_STR_CONFIG 4
+#define USB_G_STR_INT_UAS 5
+#define USB_G_STR_INT_BBB 6
+
+#define USB_G_ALT_INT_BBB 0
+#define USB_G_ALT_INT_UAS 1
+
+struct usbg_nacl {
+ /* Binary World Wide unique Port Name for SAS Initiator port */
+ u64 iport_wwpn;
+ /* ASCII formatted WWPN for Sas Initiator port */
+ char iport_name[USBG_NAMELEN];
+ /* Returned by usbg_make_nodeacl() */
+ struct se_node_acl se_node_acl;
+};
+
+struct tcm_usbg_nexus {
+ struct se_session *tvn_se_sess;
+};
+
+struct usbg_tpg {
+ struct mutex tpg_mutex;
+ /* SAS port target portal group tag for TCM */
+ u16 tport_tpgt;
+ /* Pointer back to usbg_tport */
+ struct usbg_tport *tport;
+ struct workqueue_struct *workqueue;
+ /* Returned by usbg_make_tpg() */
+ struct se_portal_group se_tpg;
+ u32 gadget_connect;
+ struct tcm_usbg_nexus *tpg_nexus;
+ atomic_t tpg_port_count;
+};
+
+struct usbg_tport {
+ /* SCSI protocol the tport is providing */
+ u8 tport_proto_id;
+ /* Binary World Wide unique Port Name for SAS Target port */
+ u64 tport_wwpn;
+ /* ASCII formatted WWPN for SAS Target port */
+ char tport_name[USBG_NAMELEN];
+ /* Returned by usbg_make_tport() */
+ struct se_wwn tport_wwn;
+};
+
+enum uas_state {
+ UASP_SEND_DATA,
+ UASP_RECEIVE_DATA,
+ UASP_SEND_STATUS,
+ UASP_QUEUE_COMMAND,
+};
+
+#define USBG_MAX_CMD 64
+struct usbg_cmd {
+ /* common */
+ u8 cmd_buf[USBG_MAX_CMD];
+ u32 data_len;
+ struct work_struct work;
+ int unpacked_lun;
+ struct se_cmd se_cmd;
+ void *data_buf; /* used if no sg support available */
+ struct f_uas *fu;
+ struct completion write_complete;
+ struct kref ref;
+
+ /* UAS only */
+ u16 tag;
+ u16 prio_attr;
+ struct sense_iu sense_iu;
+ enum uas_state state;
+ struct uas_stream *stream;
+
+ /* BOT only */
+ __le32 bot_tag;
+ unsigned int csw_code;
+ unsigned is_read:1;
+
+};
+
+struct uas_stream {
+ struct usb_request *req_in;
+ struct usb_request *req_out;
+ struct usb_request *req_status;
+};
+
+struct usbg_cdb {
+ struct usb_request *req;
+ void *buf;
+};
+
+struct bot_status {
+ struct usb_request *req;
+ struct bulk_cs_wrap csw;
+};
+
+struct f_uas {
+ struct usbg_tpg *tpg;
+ struct usb_function function;
+ u16 iface;
+
+ u32 flags;
+#define USBG_ENABLED (1 << 0)
+#define USBG_IS_UAS (1 << 1)
+#define USBG_USE_STREAMS (1 << 2)
+#define USBG_IS_BOT (1 << 3)
+#define USBG_BOT_CMD_PEND (1 << 4)
+
+ struct usbg_cdb cmd;
+ struct usb_ep *ep_in;
+ struct usb_ep *ep_out;
+
+ /* UAS */
+ struct usb_ep *ep_status;
+ struct usb_ep *ep_cmd;
+ struct uas_stream stream[UASP_SS_EP_COMP_NUM_STREAMS];
+
+ /* BOT */
+ struct bot_status bot_status;
+ struct usb_request *bot_req_in;
+ struct usb_request *bot_req_out;
+};
+
+extern struct usbg_tpg *the_only_tpg_I_currently_have;
+
+#endif
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 9e3301f..62955c2 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -690,6 +690,14 @@
usb_ep_disable(link->in_ep);
usb_ep_disable(link->out_ep);
if (netif_carrier_ok(net)) {
+ if (config_ep_by_speed(dev->gadget, &link->func,
+ link->in_ep) ||
+ config_ep_by_speed(dev->gadget, &link->func,
+ link->out_ep)) {
+ link->in_ep->desc = NULL;
+ link->out_ep->desc = NULL;
+ return -EINVAL;
+ }
DBG(dev, "host still using in/out endpoints\n");
usb_ep_enable(link->in_ep);
usb_ep_enable(link->out_ep);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 5cc70e0..caf86ca 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -313,7 +313,7 @@
if (!ehci->susp_sof_bug)
ehci_halt(ehci); /* turn off now-idle HC */
- hcd->state = HC_STATE_SUSPENDED;
+ ehci->rh_state = EHCI_RH_SUSPENDED;
if (ehci->reclaim)
end_unlink_async(ehci);
@@ -562,6 +562,10 @@
unsigned long flags;
u32 ppcd = 0;
+ /* if !USB_SUSPEND, root hub timers won't get shut down ... */
+ if (ehci->rh_state != EHCI_RH_RUNNING)
+ return 0;
+
/* init status to no-changes */
buf [0] = 0;
ports = HCS_N_PORTS (ehci->hcs_params);
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index 5ed16cc..a6b7dee 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -30,16 +30,19 @@
#include <linux/wakelock.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
-#include <mach/msm_bus.h>
#include <linux/usb/msm_hsusb_hw.h>
#include <linux/usb/msm_hsusb.h>
#include <linux/gpio.h>
+#include <linux/spinlock.h>
+
+#include <mach/msm_bus.h>
#include <mach/clk.h>
#include <mach/msm_iomap.h>
#include <mach/msm_xo.h>
#include <linux/spinlock.h>
#include <linux/cpu.h>
+#include <mach/rpm-regulator.h>
#define MSM_USB_BASE (hcd->regs)
@@ -62,6 +65,7 @@
atomic_t pm_usage_cnt;
uint32_t bus_perf_client;
uint32_t wakeup_int_cnt;
+ enum usb_vdd_type vdd_type;
};
static bool debug_bus_voting_enabled = true;
@@ -254,43 +258,59 @@
#define ULPI_IO_TIMEOUT_USEC (10 * 1000)
-#define USB_PHY_VDD_DIG_VOL_SUSP_MIN 500000 /* uV */
+#define USB_PHY_VDD_DIG_VOL_NONE 0 /*uV */
#define USB_PHY_VDD_DIG_VOL_MIN 1000000 /* uV */
#define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */
-#define USB_PHY_VDD_DIG_LOAD 49360 /* uA */
#define HSIC_DBG1_REG 0x38
+static const int vdd_val[VDD_TYPE_MAX][VDD_VAL_MAX] = {
+ { /* VDD_CX CORNER Voting */
+ [VDD_NONE] = RPM_VREG_CORNER_NONE,
+ [VDD_MIN] = RPM_VREG_CORNER_NOMINAL,
+ [VDD_MAX] = RPM_VREG_CORNER_HIGH,
+ },
+ { /* VDD_CX Voltage Voting */
+ [VDD_NONE] = USB_PHY_VDD_DIG_VOL_NONE,
+ [VDD_MIN] = USB_PHY_VDD_DIG_VOL_MIN,
+ [VDD_MAX] = USB_PHY_VDD_DIG_VOL_MAX,
+ },
+};
+
static int msm_hsic_init_vddcx(struct msm_hsic_hcd *mehci, int init)
{
int ret = 0;
+ int none_vol, min_vol, max_vol;
+
+ if (!mehci->hsic_vddcx) {
+ mehci->vdd_type = VDDCX_CORNER;
+ mehci->hsic_vddcx = devm_regulator_get(mehci->dev,
+ "hsic_vdd_dig");
+ if (IS_ERR(mehci->hsic_vddcx)) {
+ mehci->hsic_vddcx = devm_regulator_get(mehci->dev,
+ "HSIC_VDDCX");
+ if (IS_ERR(mehci->hsic_vddcx)) {
+ dev_err(mehci->dev, "unable to get hsic vddcx\n");
+ return PTR_ERR(mehci->hsic_vddcx);
+ }
+ mehci->vdd_type = VDDCX;
+ }
+ }
+
+ none_vol = vdd_val[mehci->vdd_type][VDD_NONE];
+ min_vol = vdd_val[mehci->vdd_type][VDD_MIN];
+ max_vol = vdd_val[mehci->vdd_type][VDD_MAX];
if (!init)
goto disable_reg;
- mehci->hsic_vddcx = devm_regulator_get(mehci->dev, "HSIC_VDDCX");
- if (IS_ERR(mehci->hsic_vddcx)) {
- dev_err(mehci->dev, "unable to get hsic vddcx\n");
- return PTR_ERR(mehci->hsic_vddcx);
- }
-
- ret = regulator_set_voltage(mehci->hsic_vddcx,
- USB_PHY_VDD_DIG_VOL_MIN,
- USB_PHY_VDD_DIG_VOL_MAX);
+ ret = regulator_set_voltage(mehci->hsic_vddcx, min_vol, max_vol);
if (ret) {
dev_err(mehci->dev, "unable to set the voltage"
"for hsic vddcx\n");
return ret;
}
- ret = regulator_set_optimum_mode(mehci->hsic_vddcx,
- USB_PHY_VDD_DIG_LOAD);
- if (ret < 0) {
- pr_err("%s: Unable to set optimum mode of the regulator:"
- "VDDCX\n", __func__);
- goto reg_optimum_mode_err;
- }
-
ret = regulator_enable(mehci->hsic_vddcx);
if (ret) {
dev_err(mehci->dev, "unable to enable hsic vddcx\n");
@@ -302,10 +322,8 @@
disable_reg:
regulator_disable(mehci->hsic_vddcx);
reg_enable_err:
- regulator_set_optimum_mode(mehci->hsic_vddcx, 0);
-reg_optimum_mode_err:
- regulator_set_voltage(mehci->hsic_vddcx, 0,
- USB_PHY_VDD_DIG_VOL_MIN);
+ regulator_set_voltage(mehci->hsic_vddcx, none_vol, max_vol);
+
return ret;
}
@@ -528,6 +546,7 @@
struct usb_hcd *hcd = hsic_to_hcd(mehci);
int cnt = 0, ret;
u32 val;
+ int none_vol, max_vol;
if (atomic_read(&mehci->in_lpm)) {
dev_dbg(mehci->dev, "%s called in lpm\n", __func__);
@@ -593,11 +612,12 @@
clk_disable_unprepare(mehci->cal_clk);
clk_disable_unprepare(mehci->ahb_clk);
- ret = regulator_set_voltage(mehci->hsic_vddcx,
- USB_PHY_VDD_DIG_VOL_SUSP_MIN,
- USB_PHY_VDD_DIG_VOL_MAX);
+ none_vol = vdd_val[mehci->vdd_type][VDD_NONE];
+ max_vol = vdd_val[mehci->vdd_type][VDD_MAX];
+
+ ret = regulator_set_voltage(mehci->hsic_vddcx, none_vol, max_vol);
if (ret < 0)
- dev_err(mehci->dev, "unable to set vddcx voltage: min:0.5v max:1.3v\n");
+ dev_err(mehci->dev, "unable to set vddcx voltage for VDD MIN\n");
if (mehci->bus_perf_client && debug_bus_voting_enabled) {
ret = msm_bus_scale_client_update_request(
@@ -626,6 +646,7 @@
struct usb_hcd *hcd = hsic_to_hcd(mehci);
int cnt = 0, ret;
unsigned temp;
+ int min_vol, max_vol;
if (!atomic_read(&mehci->in_lpm)) {
dev_dbg(mehci->dev, "%s called in !in_lpm\n", __func__);
@@ -648,11 +669,12 @@
"bus bandwidth %d\n", __func__, ret);
}
- ret = regulator_set_voltage(mehci->hsic_vddcx,
- USB_PHY_VDD_DIG_VOL_MIN,
- USB_PHY_VDD_DIG_VOL_MAX);
+ min_vol = vdd_val[mehci->vdd_type][VDD_MIN];
+ max_vol = vdd_val[mehci->vdd_type][VDD_MAX];
+
+ ret = regulator_set_voltage(mehci->hsic_vddcx, min_vol, max_vol);
if (ret < 0)
- dev_err(mehci->dev, "unable to set vddcx voltage: min:1v max:1.3v\n");
+ dev_err(mehci->dev, "unable to set nominal vddcx voltage (no VDD MIN)\n");
clk_prepare_enable(mehci->core_clk);
clk_prepare_enable(mehci->phy_clk);
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 673ad12..78ece8d 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -343,6 +343,8 @@
/* Write 1 to disable the port */
xhci_writel(xhci, port_status | PORT_PE, addr);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
port_status = xhci_readl(xhci, addr);
xhci_dbg(xhci, "disable port, actual port %d status = 0x%x\n",
wIndex, port_status);
@@ -389,6 +391,8 @@
}
/* Change bits are all write 1 to clear */
xhci_writel(xhci, port_status | status, addr);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
port_status = xhci_readl(xhci, addr);
xhci_dbg(xhci, "clear port %s change, actual port %d status = 0x%x\n",
port_change_bit, wIndex, port_status);
@@ -420,6 +424,8 @@
temp &= ~PORT_PLS_MASK;
temp |= PORT_LINK_STROBE | link_state;
xhci_writel(xhci, temp, port_array[port_id]);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
}
void xhci_set_remote_wake_mask(struct xhci_hcd *xhci,
@@ -446,6 +452,8 @@
temp &= ~PORT_WKOC_E;
xhci_writel(xhci, temp, port_array[port_id]);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
}
/* Test and clear port RWC bit */
@@ -459,6 +467,8 @@
temp = xhci_port_state_to_neutral(temp);
temp |= port_bit;
xhci_writel(xhci, temp, port_array[port_id]);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
}
}
@@ -721,6 +731,8 @@
*/
xhci_writel(xhci, temp | PORT_POWER,
port_array[wIndex]);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
temp = xhci_readl(xhci, port_array[wIndex]);
xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", wIndex, temp);
@@ -728,6 +740,8 @@
case USB_PORT_FEAT_RESET:
temp = (temp | PORT_RESET);
xhci_writel(xhci, temp, port_array[wIndex]);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
temp = xhci_readl(xhci, port_array[wIndex]);
xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp);
@@ -743,6 +757,8 @@
case USB_PORT_FEAT_BH_PORT_RESET:
temp |= PORT_WR;
xhci_writel(xhci, temp, port_array[wIndex]);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
temp = xhci_readl(xhci, port_array[wIndex]);
break;
@@ -936,8 +952,11 @@
t2 &= ~PORT_WAKE_BITS;
t1 = xhci_port_state_to_neutral(t1);
- if (t1 != t2)
+ if (t1 != t2) {
xhci_writel(xhci, t2, port_array[port_index]);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
+ }
if (hcd->speed != HCD_USB3) {
/* enable remote wake up for USB 2.0 */
@@ -951,6 +970,8 @@
tmp = xhci_readl(xhci, addr);
tmp |= PORT_RWE;
xhci_writel(xhci, tmp, addr);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
}
}
hcd->state = HC_STATE_SUSPENDED;
@@ -1028,8 +1049,11 @@
xhci, port_index + 1);
if (slot_id)
xhci_ring_device(xhci, slot_id);
- } else
+ } else {
xhci_writel(xhci, temp, port_array[port_index]);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
+ }
if (hcd->speed != HCD_USB3) {
/* disable remote wake up for USB 2.0 */
@@ -1043,6 +1067,8 @@
tmp = xhci_readl(xhci, addr);
tmp &= ~PORT_RWE;
xhci_writel(xhci, tmp, addr);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
}
}
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 689bc18..8467dc0 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -14,17 +14,30 @@
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/usb/otg.h>
#include "xhci.h"
+#define SYNOPSIS_DWC3_VENDOR 0x5533
+
+static struct usb_phy *phy;
+
static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
{
+ struct xhci_plat_data *pdata = dev->platform_data;
+
/*
* As of now platform drivers don't provide MSI support so we ensure
* here that the generic code does not try to make a pci_dev from our
* dev struct in order to setup MSI
*/
xhci->quirks |= XHCI_BROKEN_MSI;
+
+ if (!pdata)
+ return;
+ else if (pdata->vendor == SYNOPSIS_DWC3_VENDOR &&
+ pdata->revision < 0x230A)
+ xhci->quirks |= XHCI_PORTSC_DELAY;
}
/* called during probe() after chip reset completes */
@@ -149,6 +162,19 @@
if (ret)
goto put_usb3_hcd;
+ phy = usb_get_transceiver();
+ if (phy && phy->otg) {
+ dev_dbg(&pdev->dev, "%s otg support available\n", __func__);
+ hcd->driver->stop(hcd);
+ ret = otg_set_host(phy->otg, &hcd->self);
+ if (ret) {
+ dev_err(&pdev->dev, "%s otg_set_host failed\n",
+ __func__);
+ usb_put_transceiver(phy);
+ goto put_usb3_hcd;
+ }
+ }
+
return 0;
put_usb3_hcd:
@@ -182,6 +208,11 @@
usb_put_hcd(hcd);
kfree(xhci);
+ if (phy && phy->otg) {
+ otg_set_host(phy->otg, NULL);
+ usb_put_transceiver(phy);
+ }
+
return 0;
}
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 36641a7..2c26998 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -318,16 +318,6 @@
return;
}
-static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
-{
- int i;
-
- if (xhci->msix_entries) {
- for (i = 0; i < xhci->msix_count; i++)
- synchronize_irq(xhci->msix_entries[i].vector);
- }
-}
-
static int xhci_try_enable_msi(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
@@ -383,11 +373,7 @@
{
}
-static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
-{
-}
-
-#endif
+#endif /* CONFIG_PCI */
/*
* Initialize memory for HCD and xHC (one-time init).
@@ -513,6 +499,13 @@
xhci_dbg(xhci, "xhci_run\n");
+ xhci_dbg(xhci, "Calling HCD init\n");
+ /* Initialize HCD and host controller data structures. */
+ ret = xhci_init(hcd);
+ if (ret)
+ return ret;
+ xhci_dbg(xhci, "Called HCD init\n");
+
ret = xhci_try_enable_msi(hcd);
if (ret)
return ret;
@@ -661,6 +654,23 @@
}
#ifdef CONFIG_PM
+
+#ifdef CONFIG_PCI
+static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
+{
+ int i;
+
+ if (xhci->msix_entries) {
+ for (i = 0; i < xhci->msix_count; i++)
+ synchronize_irq(xhci->msix_entries[i].vector);
+ }
+}
+#else
+static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
+{
+}
+#endif /* CONFIG_PCI */
+
static void xhci_save_registers(struct xhci_hcd *xhci)
{
xhci->s3.command = xhci_readl(xhci, &xhci->op_regs->command);
@@ -3712,6 +3722,8 @@
hird = xhci_calculate_hird_besl(xhci, udev);
temp = PORT_L1DS(udev->slot_id) | PORT_HIRD(hird);
xhci_writel(xhci, temp, pm_addr);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
/* Set port link state to U2(L1) */
addr = port_array[port_num];
@@ -3789,6 +3801,7 @@
unsigned int port_num;
unsigned long flags;
int hird;
+ bool delay;
if (hcd->speed == HCD_USB3 || !xhci->hw_lpm_support ||
!udev->lpm_capable)
@@ -3801,6 +3814,9 @@
if (udev->usb2_hw_lpm_capable != 1)
return -EPERM;
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ delay = true;
+
spin_lock_irqsave(&xhci->lock, flags);
port_array = xhci->usb2_ports;
@@ -3817,12 +3833,18 @@
temp &= ~PORT_HIRD_MASK;
temp |= PORT_HIRD(hird) | PORT_RWE;
xhci_writel(xhci, temp, pm_addr);
+ if (delay)
+ ndelay(100);
temp = xhci_readl(xhci, pm_addr);
temp |= PORT_HLE;
xhci_writel(xhci, temp, pm_addr);
+ if (delay)
+ ndelay(100);
} else {
temp &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK);
xhci_writel(xhci, temp, pm_addr);
+ if (delay)
+ ndelay(100);
}
spin_unlock_irqrestore(&xhci->lock, flags);
@@ -4043,12 +4065,6 @@
dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32));
}
- xhci_dbg(xhci, "Calling HCD init\n");
- /* Initialize HCD and host controller data structures. */
- retval = xhci_init(hcd);
- if (retval)
- goto error;
- xhci_dbg(xhci, "Called HCD init\n");
return 0;
error:
kfree(xhci);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 3d69c4b..127b0e9 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1479,6 +1479,21 @@
#define XHCI_RESET_ON_RESUME (1 << 7)
#define XHCI_SW_BW_CHECKING (1 << 8)
#define XHCI_AMD_0x96_HOST (1 << 9)
+/*
+ * In Synopsis DWC3 controller, PORTSC register access involves multiple clock
+ * domains. When the software does a PORTSC write, handshakes are needed
+ * across these clock domains. This results in long access times, especially
+ * for USB 2.0 ports. In order to solve this issue, when the PORTSC write
+ * operations happen on the system bus, the command is latched and system bus
+ * is released immediately. However, the real PORTSC write access will take
+ * some time internally to complete. If the software quickly does a read to the
+ * PORTSC, some fields (port status change related fields like OCC, etc.) may
+ * not have correct value due to the current way of handling these bits.
+ *
+ * The workaround is to give some delay (5 mac2_clk -> UTMI clock = 60 MHz ->
+ * (16.66 ns x 5 = 84ns) ~100ns after writing to the PORTSC register.
+ */
+#define XHCI_PORTSC_DELAY (1 << 10)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
@@ -1667,6 +1682,11 @@
static inline void xhci_unregister_pci(void) {}
#endif
+struct xhci_plat_data {
+ unsigned vendor;
+ unsigned revision;
+};
+
#if defined(CONFIG_USB_XHCI_PLATFORM) \
|| defined(CONFIG_USB_XHCI_PLATFORM_MODULE)
int xhci_register_plat(void);
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 61fbac0..dedad53 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -87,13 +87,6 @@
#endif
}
-enum usb_vdd_value {
- VDD_NONE = 0,
- VDD_MIN,
- VDD_MAX,
- VDD_VAL_MAX,
-};
-
static const int vdd_val[VDD_TYPE_MAX][VDD_VAL_MAX] = {
{ /* VDD_CX CORNER Voting */
[VDD_NONE] = RPM_VREG_CORNER_NONE,
@@ -834,8 +827,10 @@
/* Ensure that above operation is completed before turning off clocks */
mb();
- clk_disable_unprepare(motg->pclk);
- clk_disable_unprepare(motg->core_clk);
+ if (!motg->pdata->core_clk_always_on_workaround) {
+ clk_disable_unprepare(motg->pclk);
+ clk_disable_unprepare(motg->core_clk);
+ }
/* usb phy no more require TCXO clock, hence vote for TCXO disable */
if (!host_bus_suspend) {
@@ -898,9 +893,10 @@
motg->lpm_flags &= ~XO_SHUTDOWN;
}
- clk_prepare_enable(motg->core_clk);
-
- clk_prepare_enable(motg->pclk);
+ if (!motg->pdata->core_clk_always_on_workaround) {
+ clk_prepare_enable(motg->core_clk);
+ clk_prepare_enable(motg->pclk);
+ }
if (motg->lpm_flags & PHY_PWR_COLLAPSED) {
msm_hsusb_ldo_enable(motg, 1);
diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig
index 7777154..7e078ab 100644
--- a/drivers/video/msm/Kconfig
+++ b/drivers/video/msm/Kconfig
@@ -84,6 +84,16 @@
Support for MSM MDP HW revision 4.0
Say Y here if this is msm7x30 variant platform.
+config FB_MSM_MDSS
+ bool "MDSS HW"
+ ---help---
+ The Mobile Display Sub System (MDSS) driver supports devices which
+ contain MDSS hardware block.
+
+ The MDSS driver implements frame buffer interface to provide access to
+ the display hardware and provide a way for users to display graphics
+ on connected display panels.
+
config FB_MSM_MDP_NONE
bool "MDP HW None"
---help---
@@ -936,4 +946,8 @@
default n
---help---
Support for EBI2 panel auto detect
+
+if FB_MSM_MDSS
+ source "drivers/video/msm/mdss/Kconfig"
+endif
endif
diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile
index e4a0948..a0f9e02 100644
--- a/drivers/video/msm/Makefile
+++ b/drivers/video/msm/Makefile
@@ -1,10 +1,12 @@
+ifeq ($(CONFIG_FB_MSM_MDSS),y)
+obj-y += mdss/
+else
obj-y := msm_fb.o
obj-$(CONFIG_FB_MSM_LOGO) += logo.o
obj-$(CONFIG_FB_BACKLIGHT) += msm_fb_bl.o
ifeq ($(CONFIG_FB_MSM_MDP_HW),y)
-
# MDP
obj-y += mdp.o
@@ -182,15 +184,15 @@
obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += mdp4_wfd_writeback_panel.o
obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += mdp4_wfd_writeback.o
obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += mdp4_overlay_writeback.o
-
-obj-$(CONFIG_MSM_VIDC_1080P) += vidc/
-obj-$(CONFIG_MSM_VIDC_720P) += vidc/
else
obj-$(CONFIG_FB_MSM_EBI2) += ebi2_host.o
obj-$(CONFIG_FB_MSM_EBI2) += ebi2_lcd.o
obj-y += msm_fb_panel.o
obj-$(CONFIG_FB_MSM_EBI2_EPSON_S1D_QVGA_PANEL) += ebi2_epson_s1d_qvga.o
endif
+endif
+obj-$(CONFIG_MSM_VIDC_1080P) += vidc/
+obj-$(CONFIG_MSM_VIDC_720P) += vidc/
clean:
rm *.o .*cmd
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index e76d8b2..cad6e02 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -88,6 +88,7 @@
struct workqueue_struct *mdp_vsync_wq; /*mdp vsync wq */
struct workqueue_struct *mdp_hist_wq; /*mdp histogram wq */
+bool mdp_pp_initialized = FALSE;
static struct workqueue_struct *mdp_pipe_ctrl_wq; /* mdp mdp pipe ctrl wq */
static struct delayed_work mdp_pipe_ctrl_worker;
@@ -219,10 +220,28 @@
mutex_unlock(&mdp_hist_lut_list_mutex);
}
-static int mdp_hist_lut_init(void)
+static int mdp_hist_lut_destroy(void)
{
struct mdp_hist_lut_mgmt *temp;
struct list_head *pos, *q;
+
+ mutex_lock(&mdp_hist_lut_list_mutex);
+ list_for_each_safe(pos, q, &mdp_hist_lut_list) {
+ temp = list_entry(pos, struct mdp_hist_lut_mgmt, list);
+ list_del(pos);
+ kfree(temp);
+ }
+ mutex_unlock(&mdp_hist_lut_list_mutex);
+ return 0;
+}
+
+static int mdp_hist_lut_init(void)
+{
+ struct mdp_hist_lut_mgmt *temp;
+
+ if (mdp_pp_initialized)
+ return -EEXIST;
+
INIT_LIST_HEAD(&mdp_hist_lut_list);
if (mdp_rev >= MDP_REV_30) {
@@ -253,13 +272,7 @@
return 0;
exit_list:
- mutex_lock(&mdp_hist_lut_list_mutex);
- list_for_each_safe(pos, q, &mdp_hist_lut_list) {
- temp = list_entry(pos, struct mdp_hist_lut_mgmt, list);
- list_del(pos);
- kfree(temp);
- }
- mutex_unlock(&mdp_hist_lut_list_mutex);
+ mdp_hist_lut_destroy();
exit:
pr_err("Failed initializing histogram LUT memory\n");
return -ENOMEM;
@@ -682,10 +695,30 @@
kfree(mgmt->c0);
}
+static int mdp_histogram_destroy(void)
+{
+ struct mdp_hist_mgmt *temp;
+ int i;
+
+ for (i = 0; i < MDP_HIST_MGMT_MAX; i++) {
+ temp = mdp_hist_mgmt_array[i];
+ if (!temp)
+ continue;
+ mdp_hist_del_mgmt(temp);
+ kfree(temp);
+ mdp_hist_mgmt_array[i] = NULL;
+ }
+ return 0;
+}
+
static int mdp_histogram_init(void)
{
struct mdp_hist_mgmt *temp;
int i, ret;
+
+ if (mdp_pp_initialized)
+ return -EEXIST;
+
mdp_hist_wq = alloc_workqueue("mdp_hist_wq",
WQ_NON_REENTRANT | WQ_UNBOUND, 0);
@@ -731,14 +764,7 @@
return 0;
exit_list:
- for (i = 0; i < MDP_HIST_MGMT_MAX; i++) {
- temp = mdp_hist_mgmt_array[i];
- if (!temp)
- continue;
- mdp_hist_del_mgmt(temp);
- kfree(temp);
- mdp_hist_mgmt_array[i] = NULL;
- }
+ mdp_histogram_destroy();
exit:
return -ENOMEM;
}
@@ -888,6 +914,7 @@
goto error;
}
+ mutex_lock(&mgmt->mdp_do_hist_mutex);
mutex_lock(&mgmt->mdp_hist_mutex);
if (mgmt->mdp_is_hist_start == TRUE) {
pr_err("%s histogram already started\n", __func__);
@@ -907,6 +934,7 @@
error_lock:
mutex_unlock(&mgmt->mdp_hist_mutex);
+ mutex_unlock(&mgmt->mdp_do_hist_mutex);
error:
return ret;
}
@@ -923,6 +951,7 @@
goto error;
}
+ mutex_lock(&mgmt->mdp_do_hist_mutex);
mutex_lock(&mgmt->mdp_hist_mutex);
if (mgmt->mdp_is_hist_start == FALSE) {
pr_err("%s histogram already stopped\n", __func__);
@@ -943,10 +972,12 @@
mutex_unlock(&mgmt->mdp_hist_mutex);
cancel_work_sync(&mgmt->mdp_histogram_worker);
+ mutex_unlock(&mgmt->mdp_do_hist_mutex);
return ret;
error_lock:
mutex_unlock(&mgmt->mdp_hist_mutex);
+ mutex_unlock(&mgmt->mdp_do_hist_mutex);
error:
return ret;
}
@@ -1152,11 +1183,13 @@
return ret;
}
+#define MDP_HISTOGRAM_TIMEOUT_MS 84 /*5 Frames*/
static int mdp_do_histogram(struct fb_info *info,
struct mdp_histogram_data *hist)
{
struct mdp_hist_mgmt *mgmt = NULL;
int ret = 0;
+ unsigned long timeout = (MDP_HISTOGRAM_TIMEOUT_MS * HZ) / 1000;
ret = mdp_histogram_block2mgmt(hist->block, &mgmt);
if (ret) {
@@ -1201,9 +1234,17 @@
mgmt->hist = hist;
mutex_unlock(&mgmt->mdp_hist_mutex);
- if (wait_for_completion_killable(&mgmt->mdp_hist_comp)) {
- pr_err("%s(): histogram bin collection killed", __func__);
- ret = -EINVAL;
+ ret = wait_for_completion_killable_timeout(&mgmt->mdp_hist_comp,
+ timeout);
+ if (ret <= 0) {
+ if (!ret) {
+ mgmt->hist = NULL;
+ ret = -ETIMEDOUT;
+ pr_debug("%s: bin collection timedout", __func__);
+ } else {
+ mgmt->hist = NULL;
+ pr_debug("%s: bin collection interrupted", __func__);
+ }
goto error;
}
@@ -2256,6 +2297,7 @@
/* initialize Post Processing data*/
mdp_hist_lut_init();
mdp_histogram_init();
+ mdp_pp_initialized = TRUE;
/* add panel data */
if (platform_device_add_data
@@ -2657,6 +2699,12 @@
{
if (footswitch != NULL)
regulator_put(footswitch);
+
+ /*free post processing memory*/
+ mdp_histogram_destroy();
+ mdp_hist_lut_destroy();
+ mdp_pp_initialized = FALSE;
+
iounmap(msm_mdp_base);
pm_runtime_disable(&pdev->dev);
#ifdef CONFIG_MSM_BUS_SCALING
diff --git a/drivers/video/msm/mdp.h b/drivers/video/msm/mdp.h
index e60b24e..b232e0a 100644
--- a/drivers/video/msm/mdp.h
+++ b/drivers/video/msm/mdp.h
@@ -74,7 +74,8 @@
struct mdp_buf_type {
struct ion_handle *ihdl;
- u32 phys_addr;
+ u32 write_addr;
+ u32 read_addr;
u32 size;
};
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index 1557eed..59404d0 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -337,7 +337,8 @@
uint32 element1; /* 0 = C0, 1 = C1, 2 = C2, 3 = C3 */
uint32 element0; /* 0 = C0, 1 = C1, 2 = C2, 3 = C3 */
struct completion comp;
- ulong blt_addr; /* blt mode addr */
+ ulong ov_blt_addr; /* blt mode addr */
+ ulong dma_blt_addr; /* blt mode addr */
ulong blt_base;
ulong blt_offset;
uint32 blt_cnt;
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index 2a15506..287b564 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -126,7 +126,7 @@
pr_debug("mixer %u, pipe %u, plane %u\n", pipe->mixer_num,
pipe->pipe_ndx, plane);
if (ion_map_iommu(display_iclient, *srcp_ihdl,
- DISPLAY_DOMAIN, GEN_POOL, SZ_4K, 0, start,
+ DISPLAY_READ_DOMAIN, GEN_POOL, SZ_4K, 0, start,
len, 0, ION_IOMMU_UNMAP_DELAYED)) {
ion_free(display_iclient, *srcp_ihdl);
pr_err("ion_map_iommu() failed\n");
@@ -140,7 +140,7 @@
if (iom_pipe_info->prev_ihdl[plane]) {
ion_unmap_iommu(display_iclient,
iom_pipe_info->prev_ihdl[plane],
- DISPLAY_DOMAIN, GEN_POOL);
+ DISPLAY_READ_DOMAIN, GEN_POOL);
ion_free(display_iclient,
iom_pipe_info->prev_ihdl[plane]);
pr_debug("Previous: mixer %u, pipe %u, plane %u, "
@@ -175,7 +175,7 @@
iom_pipe_info->prev_ihdl[i]);
ion_unmap_iommu(display_iclient,
iom_pipe_info->prev_ihdl[i],
- DISPLAY_DOMAIN, GEN_POOL);
+ DISPLAY_READ_DOMAIN, GEN_POOL);
ion_free(display_iclient,
iom_pipe_info->prev_ihdl[i]);
iom_pipe_info->prev_ihdl[i] = NULL;
@@ -191,7 +191,7 @@
iom_pipe_info->ihdl[i]);
ion_unmap_iommu(display_iclient,
iom_pipe_info->ihdl[i],
- DISPLAY_DOMAIN, GEN_POOL);
+ DISPLAY_READ_DOMAIN, GEN_POOL);
ion_free(display_iclient,
iom_pipe_info->ihdl[i]);
iom_pipe_info->ihdl[i] = NULL;
@@ -346,7 +346,7 @@
MDP_OUTP(MDP_BASE + 0xb0004,
(pipe->src_height << 16 | pipe->src_width));
- if (pipe->blt_addr) {
+ if (pipe->dma_blt_addr) {
uint32 off, bpp;
#ifdef BLT_RGB565
bpp = 2; /* overlay ouput is RGB565 */
@@ -356,7 +356,7 @@
off = 0;
if (pipe->ov_cnt & 0x01)
off = pipe->src_height * pipe->src_width * bpp;
- MDP_OUTP(MDP_BASE + 0xb0008, pipe->blt_addr + off);
+ MDP_OUTP(MDP_BASE + 0xb0008, pipe->dma_blt_addr + off);
/* RGB888, output of overlay blending */
MDP_OUTP(MDP_BASE + 0xb000c, pipe->src_width * bpp);
} else {
@@ -427,7 +427,7 @@
/* dma_p source */
MDP_OUTP(MDP_BASE + 0x90004,
(pipe->src_height << 16 | pipe->src_width));
- if (pipe->blt_addr) {
+ if (pipe->dma_blt_addr) {
#ifdef BLT_RGB565
bpp = 2; /* overlay ouput is RGB565 */
#else
@@ -436,7 +436,7 @@
off = 0;
if (pipe->dmap_cnt & 0x01)
off = pipe->src_height * pipe->src_width * bpp;
- MDP_OUTP(MDP_BASE + 0x90008, pipe->blt_addr + off);
+ MDP_OUTP(MDP_BASE + 0x90008, pipe->dma_blt_addr + off);
/* RGB888, output of overlay blending */
MDP_OUTP(MDP_BASE + 0x9000c, pipe->src_width * bpp);
} else {
@@ -1321,7 +1321,7 @@
/*
* BLT support both primary and external external
*/
- if (pipe->blt_addr) {
+ if (pipe->ov_blt_addr) {
int off, bpp;
#ifdef BLT_RGB565
bpp = 2; /* overlay ouput is RGB565 */
@@ -1338,10 +1338,10 @@
if (pipe->ov_cnt & 0x01)
off = pipe->src_height * pipe->src_width * bpp;
- outpdw(overlay_base + 0x000c, pipe->blt_addr + off);
+ outpdw(overlay_base + 0x000c, pipe->ov_blt_addr + off);
/* overlay ouput is RGB888 */
outpdw(overlay_base + 0x0010, pipe->src_width * bpp);
- outpdw(overlay_base + 0x001c, pipe->blt_addr + off);
+ outpdw(overlay_base + 0x001c, pipe->ov_blt_addr + off);
/* MDDI - BLT + on demand */
outpdw(overlay_base + 0x0004, 0x08);
@@ -1361,19 +1361,19 @@
pipe->src_width * bpp;
outpdw(overlay_base + 0x000c,
- pipe->blt_addr + off);
+ pipe->ov_blt_addr + off);
/* overlay ouput is RGB888 */
outpdw(overlay_base + 0x0010,
((pipe->src_width << 16) |
pipe->src_width));
outpdw(overlay_base + 0x001c,
- pipe->blt_addr + off);
+ pipe->ov_blt_addr + off);
off = pipe->src_height * pipe->src_width;
/* align chroma to 2k address */
off = (off + 2047) & ~2047;
/* UV plane adress */
outpdw(overlay_base + 0x0020,
- pipe->blt_addr + off);
+ pipe->ov_blt_addr + off);
/* MDDI - BLT + on demand */
outpdw(overlay_base + 0x0004, 0x08);
/* pseudo planar + writeback */
@@ -3180,25 +3180,25 @@
char *name;
int domain;
} msm_iommu_ctx_names[] = {
- /* Display */
+ /* Display read*/
{
.name = "mdp_port0_cb0",
- .domain = DISPLAY_DOMAIN,
+ .domain = DISPLAY_READ_DOMAIN,
},
- /* Display */
+ /* Display read*/
{
.name = "mdp_port0_cb1",
- .domain = DISPLAY_DOMAIN,
+ .domain = DISPLAY_WRITE_DOMAIN,
},
- /* Display */
+ /* Display write */
{
.name = "mdp_port1_cb0",
- .domain = DISPLAY_DOMAIN,
+ .domain = DISPLAY_READ_DOMAIN,
},
- /* Display */
+ /* Display write */
{
.name = "mdp_port1_cb1",
- .domain = DISPLAY_DOMAIN,
+ .domain = DISPLAY_WRITE_DOMAIN,
},
};
diff --git a/drivers/video/msm/mdp4_overlay_dsi_cmd.c b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
index 7ba4e75..0d8fea7 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_cmd.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
@@ -162,7 +162,8 @@
dsi_pipe = pipe; /* keep it */
mdp4_init_writeback_buf(mfd, MDP4_MIXER0);
- pipe->blt_addr = 0;
+ pipe->ov_blt_addr = 0;
+ pipe->dma_blt_addr = 0;
} else {
pipe = dsi_pipe;
@@ -321,24 +322,25 @@
{
unsigned long flag;
- pr_debug("%s: blt_end=%d blt_addr=%x pid=%d\n",
- __func__, dsi_pipe->blt_end, (int)dsi_pipe->blt_addr, current->pid);
+ pr_debug("%s: blt_end=%d ov_blt_addr=%x pid=%d\n",
+ __func__, dsi_pipe->blt_end, (int)dsi_pipe->ov_blt_addr, current->pid);
mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
- if (mfd->ov0_wb_buf->phys_addr == 0) {
+ if (mfd->ov0_wb_buf->write_addr == 0) {
pr_info("%s: no blt_base assigned\n", __func__);
return -EBUSY;
}
- if (dsi_pipe->blt_addr == 0) {
+ if (dsi_pipe->ov_blt_addr == 0) {
mdp4_dsi_cmd_dma_busy_wait(mfd);
spin_lock_irqsave(&mdp_spin_lock, flag);
dsi_pipe->blt_end = 0;
dsi_pipe->blt_cnt = 0;
dsi_pipe->ov_cnt = 0;
dsi_pipe->dmap_cnt = 0;
- dsi_pipe->blt_addr = mfd->ov0_wb_buf->phys_addr;
+ dsi_pipe->ov_blt_addr = mfd->ov0_wb_buf->write_addr;
+ dsi_pipe->dma_blt_addr = mfd->ov0_wb_buf->read_addr;
mdp4_stat.blt_dsi_cmd++;
spin_unlock_irqrestore(&mdp_spin_lock, flag);
return 0;
@@ -352,10 +354,10 @@
unsigned long flag;
- pr_debug("%s: blt_end=%d blt_addr=%x\n",
- __func__, dsi_pipe->blt_end, (int)dsi_pipe->blt_addr);
+ pr_debug("%s: blt_end=%d ov_blt_addr=%x\n",
+ __func__, dsi_pipe->blt_end, (int)dsi_pipe->ov_blt_addr);
- if ((dsi_pipe->blt_end == 0) && dsi_pipe->blt_addr) {
+ if ((dsi_pipe->blt_end == 0) && dsi_pipe->ov_blt_addr) {
spin_lock_irqsave(&mdp_spin_lock, flag);
dsi_pipe->blt_end = 1; /* mark as end */
spin_unlock_irqrestore(&mdp_spin_lock, flag);
@@ -393,7 +395,7 @@
char *overlay_base;
- if (pipe->blt_addr == 0)
+ if (pipe->ov_blt_addr == 0)
return;
@@ -405,7 +407,7 @@
off = 0;
if (pipe->dmap_cnt & 0x01)
off = pipe->src_height * pipe->src_width * bpp;
- addr = pipe->blt_addr + off;
+ addr = pipe->dma_blt_addr + off;
/* dmap */
MDP_OUTP(MDP_BASE + 0x90008, addr);
@@ -413,7 +415,7 @@
off = 0;
if (pipe->ov_cnt & 0x01)
off = pipe->src_height * pipe->src_width * bpp;
- addr2 = pipe->blt_addr + off;
+ addr2 = pipe->ov_blt_addr + off;
/* overlay 0 */
overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
outpdw(overlay_base + 0x000c, addr2);
@@ -441,7 +443,8 @@
spin_unlock(&mdp_spin_lock);
if (dsi_pipe->blt_end) {
dsi_pipe->blt_end = 0;
- dsi_pipe->blt_addr = 0;
+ dsi_pipe->dma_blt_addr = 0;
+ dsi_pipe->ov_blt_addr = 0;
pr_debug("%s: END, ov_cnt=%d dmap_cnt=%d\n",
__func__, dsi_pipe->ov_cnt, dsi_pipe->dmap_cnt);
mdp_intr_mask &= ~INTR_DMA_P_DONE;
@@ -479,7 +482,7 @@
{
int diff;
- if (dsi_pipe->blt_addr == 0) {
+ if (dsi_pipe->ov_blt_addr == 0) {
mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
spin_lock(&mdp_spin_lock);
dma->busy = FALSE;
@@ -539,7 +542,7 @@
mipi_dsi_mdp_busy_wait(dsi_mfd);
mdp4_overlay_update_dsi_cmd(dsi_mfd);
- if (dsi_pipe->blt_addr)
+ if (dsi_pipe->ov_blt_addr)
mdp4_dsi_blt_dmap_busy_wait(dsi_mfd);
mdp4_dsi_cmd_overlay_kickoff(dsi_mfd, dsi_pipe);
}
@@ -622,17 +625,17 @@
* to be called before kickoff.
* vice versa for blt disabled.
*/
- if (dsi_pipe->blt_addr && dsi_pipe->blt_cnt == 0)
+ if (dsi_pipe->ov_blt_addr && dsi_pipe->blt_cnt == 0)
mdp4_overlay_update_dsi_cmd(mfd); /* first time */
- else if (dsi_pipe->blt_addr == 0 && dsi_pipe->blt_cnt) {
+ else if (dsi_pipe->ov_blt_addr == 0 && dsi_pipe->blt_cnt) {
mdp4_overlay_update_dsi_cmd(mfd); /* last time */
dsi_pipe->blt_cnt = 0;
}
- pr_debug("%s: blt_addr=%d blt_cnt=%d\n",
- __func__, (int)dsi_pipe->blt_addr, dsi_pipe->blt_cnt);
+ pr_debug("%s: ov_blt_addr=%d blt_cnt=%d\n",
+ __func__, (int)dsi_pipe->ov_blt_addr, dsi_pipe->blt_cnt);
- if (dsi_pipe->blt_addr)
+ if (dsi_pipe->ov_blt_addr)
mdp4_dsi_blt_dmap_busy_wait(dsi_mfd);
mdp4_dsi_cmd_overlay_kickoff(mfd, pipe);
@@ -658,7 +661,7 @@
mipi_dsi_mdp_busy_wait(mfd);
- if (dsi_pipe->blt_addr == 0)
+ if (dsi_pipe->ov_blt_addr == 0)
mipi_dsi_cmd_mdp_start();
mdp4_overlay_dsi_state_set(ST_DSI_PLAYING);
@@ -666,7 +669,7 @@
spin_lock_irqsave(&mdp_spin_lock, flag);
mdp_enable_irq(MDP_OVERLAY0_TERM);
mfd->dma->busy = TRUE;
- if (dsi_pipe->blt_addr)
+ if (dsi_pipe->ov_blt_addr)
mfd->dma->dmap_busy = TRUE;
/* start OVERLAY pipe */
spin_unlock_irqrestore(&mdp_spin_lock, flag);
@@ -700,7 +703,7 @@
if (mfd && mfd->panel_power_on) {
mdp4_dsi_cmd_dma_busy_wait(mfd);
- if (dsi_pipe && dsi_pipe->blt_addr)
+ if (dsi_pipe && dsi_pipe->ov_blt_addr)
mdp4_dsi_blt_dmap_busy_wait(mfd);
mdp4_overlay_update_dsi_cmd(mfd);
diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c
index 05c6fe8..478a8ce 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_video.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_video.c
@@ -152,7 +152,8 @@
init_completion(&dsi_video_comp);
mdp4_init_writeback_buf(mfd, MDP4_MIXER0);
- pipe->blt_addr = 0;
+ pipe->ov_blt_addr = 0;
+ pipe->dma_blt_addr = 0;
} else {
pipe = dsi_pipe;
@@ -415,7 +416,7 @@
char *overlay_base;
- if (pipe->blt_addr == 0)
+ if (pipe->ov_blt_addr == 0)
return;
@@ -427,7 +428,7 @@
off = 0;
if (pipe->ov_cnt & 0x01)
off = pipe->src_height * pipe->src_width * bpp;
- addr = pipe->blt_addr + off;
+ addr = pipe->ov_blt_addr + off;
/* overlay 0 */
overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
@@ -440,7 +441,7 @@
uint32 off, addr;
int bpp;
- if (pipe->blt_addr == 0)
+ if (pipe->ov_blt_addr == 0)
return;
@@ -452,7 +453,7 @@
off = 0;
if (pipe->dmap_cnt & 0x01)
off = pipe->src_height * pipe->src_width * bpp;
- addr = pipe->blt_addr + off;
+ addr = pipe->dma_blt_addr + off;
/* dmap */
MDP_OUTP(MDP_BASE + 0x90008, addr);
@@ -529,7 +530,7 @@
if (pipe->flags & MDP_OV_PLAY_NOWAIT)
return;
- if (dsi_pipe->blt_addr) {
+ if (dsi_pipe->ov_blt_addr) {
mdp4_overlay_dsi_video_dma_busy_wait(mfd);
mdp4_dsi_video_blt_ov_update(dsi_pipe);
@@ -572,7 +573,7 @@
mdp4_overlayproc_cfg(dsi_pipe);
mdp4_overlay_dmap_xy(dsi_pipe);
mdp_is_in_isr = FALSE;
- if (dsi_pipe->blt_addr) {
+ if (dsi_pipe->ov_blt_addr) {
mdp4_dsi_video_blt_ov_update(dsi_pipe);
dsi_pipe->ov_cnt++;
outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE);
@@ -595,7 +596,7 @@
{
spin_lock(&mdp_spin_lock);
dma->busy = FALSE;
- if (dsi_pipe->blt_addr == 0) {
+ if (dsi_pipe->ov_blt_addr == 0) {
spin_unlock(&mdp_spin_lock);
return;
}
@@ -618,21 +619,23 @@
mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
- if (mfd->ov0_wb_buf->phys_addr == 0) {
+ if (mfd->ov0_wb_buf->write_addr == 0) {
pr_info("%s: no blt_base assigned\n", __func__);
return;
}
spin_lock_irqsave(&mdp_spin_lock, flag);
- if (enable && dsi_pipe->blt_addr == 0) {
- dsi_pipe->blt_addr = mfd->ov0_wb_buf->phys_addr;
+ if (enable && dsi_pipe->ov_blt_addr == 0) {
+ dsi_pipe->ov_blt_addr = mfd->ov0_wb_buf->write_addr;
+ dsi_pipe->dma_blt_addr = mfd->ov0_wb_buf->read_addr;
dsi_pipe->blt_cnt = 0;
dsi_pipe->ov_cnt = 0;
dsi_pipe->dmap_cnt = 0;
mdp4_stat.blt_dsi_video++;
change++;
- } else if (enable == 0 && dsi_pipe->blt_addr) {
- dsi_pipe->blt_addr = 0;
+ } else if (enable == 0 && dsi_pipe->ov_blt_addr) {
+ dsi_pipe->ov_blt_addr = 0;
+ dsi_pipe->dma_blt_addr = 0;
change++;
}
@@ -641,8 +644,8 @@
return;
}
- pr_debug("%s: enable=%d blt_addr=%x\n", __func__,
- enable, (int)dsi_pipe->blt_addr);
+ pr_debug("%s: enable=%d ov_blt_addr=%x\n", __func__,
+ enable, (int)dsi_pipe->ov_blt_addr);
blt_cfg_changed = 1;
spin_unlock_irqrestore(&mdp_spin_lock, flag);
diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c
index fe31f93..e41f9e8 100644
--- a/drivers/video/msm/mdp4_overlay_dtv.c
+++ b/drivers/video/msm/mdp4_overlay_dtv.c
@@ -239,15 +239,8 @@
mdp_footswitch_ctrl(TRUE);
mdp4_overlay_panel_mode(MDP4_MIXER1, MDP4_PANEL_DTV);
-
- /* Allocate dtv_pipe at dtv_on*/
- if (dtv_pipe == NULL) {
- if (mdp4_overlay_dtv_set(mfd, NULL)) {
- pr_warn("%s: dtv_pipe is NULL, dtv_set failed\n",
- __func__);
- return -EINVAL;
- }
- }
+ if (dtv_pipe != NULL)
+ ret = mdp4_dtv_start(mfd);
ret = panel_next_on(pdev);
if (ret != 0)
@@ -383,7 +376,8 @@
return -ENODEV;
mdp4_init_writeback_buf(mfd, MDP4_MIXER1);
- dtv_pipe->blt_addr = 0;
+ dtv_pipe->ov_blt_addr = 0;
+ dtv_pipe->dma_blt_addr = 0;
return mdp4_dtv_start(mfd);
}
@@ -414,7 +408,7 @@
int bpp;
char *overlay_base;
- if (pipe->blt_addr == 0)
+ if (pipe->ov_blt_addr == 0)
return;
#ifdef BLT_RGB565
bpp = 2; /* overlay ouput is RGB565 */
@@ -424,7 +418,7 @@
off = (pipe->ov_cnt & 0x01) ?
pipe->src_height * pipe->src_width * bpp : 0;
- addr = pipe->blt_addr + off;
+ addr = pipe->ov_blt_addr + off;
pr_debug("%s overlay addr 0x%x\n", __func__, addr);
/* overlay 1 */
overlay_base = MDP_BASE + MDP4_OVERLAYPROC1_BASE;/* 0x18000 */
@@ -437,7 +431,7 @@
uint32 off, addr;
int bpp;
- if (pipe->blt_addr == 0)
+ if (pipe->ov_blt_addr == 0)
return;
#ifdef BLT_RGB565
@@ -447,7 +441,7 @@
#endif
off = (pipe->dmae_cnt & 0x01) ?
pipe->src_height * pipe->src_width * bpp : 0;
- addr = pipe->blt_addr + off;
+ addr = pipe->dma_blt_addr + off;
MDP_OUTP(MDP_BASE + 0xb0008, addr);
}
@@ -470,7 +464,7 @@
return;
}
- if (dtv_pipe->blt_addr) {
+ if (dtv_pipe->ov_blt_addr) {
mdp4_dtv_blt_ov_update(dtv_pipe);
dtv_pipe->ov_cnt++;
mdp4_overlay_dtv_ov_kick_start();
@@ -530,7 +524,7 @@
msecs_to_jiffies(VSYNC_PERIOD*2));
mdp_disable_irq(MDP_OVERLAY1_TERM);
- if (dtv_pipe->blt_addr)
+ if (dtv_pipe->ov_blt_addr)
mdp4_overlay_dtv_wait4dmae(mfd);
}
@@ -588,7 +582,7 @@
{
if (!dtv_pipe)
return;
- if (dtv_pipe->blt_addr) {
+ if (dtv_pipe->ov_blt_addr) {
mdp4_dtv_blt_dmae_update(dtv_pipe);
dtv_pipe->dmae_cnt++;
}
@@ -649,7 +643,7 @@
unsigned long flag;
int change = 0;
- if (!mfd->ov1_wb_buf->phys_addr) {
+ if (!mfd->ov1_wb_buf->write_addr) {
pr_debug("%s: no writeback buf assigned\n", __func__);
return;
}
@@ -661,16 +655,18 @@
}
spin_lock_irqsave(&mdp_spin_lock, flag);
- if (enable && dtv_pipe->blt_addr == 0) {
- dtv_pipe->blt_addr = mfd->ov1_wb_buf->phys_addr;
+ if (enable && dtv_pipe->ov_blt_addr == 0) {
+ dtv_pipe->ov_blt_addr = mfd->ov1_wb_buf->write_addr;
+ dtv_pipe->dma_blt_addr = mfd->ov1_wb_buf->read_addr;
change++;
dtv_pipe->ov_cnt = 0;
dtv_pipe->dmae_cnt = 0;
- } else if (enable == 0 && dtv_pipe->blt_addr) {
- dtv_pipe->blt_addr = 0;
+ } else if (enable == 0 && dtv_pipe->ov_blt_addr) {
+ dtv_pipe->ov_blt_addr = 0;
+ dtv_pipe->dma_blt_addr = 0;
change++;
}
- pr_debug("%s: blt_addr=%x\n", __func__, (int)dtv_pipe->blt_addr);
+ pr_debug("%s: ov_blt_addr=%x\n", __func__, (int)dtv_pipe->ov_blt_addr);
spin_unlock_irqrestore(&mdp_spin_lock, flag);
if (!change)
@@ -708,6 +704,14 @@
return;
}
mutex_lock(&mfd->dma->ov_mutex);
+ if (dtv_pipe == NULL) {
+ if (mdp4_overlay_dtv_set(mfd, NULL)) {
+ pr_warn("%s: dtv_pipe == NULL\n", __func__);
+ mutex_unlock(&mfd->dma->ov_mutex);
+ return;
+ }
+ }
+
pipe = dtv_pipe;
if (hdmi_prim_display && (pipe->pipe_used == 0 ||
diff --git a/drivers/video/msm/mdp4_overlay_lcdc.c b/drivers/video/msm/mdp4_overlay_lcdc.c
index fd6d365..1c3bf3f 100644
--- a/drivers/video/msm/mdp4_overlay_lcdc.c
+++ b/drivers/video/msm/mdp4_overlay_lcdc.c
@@ -133,8 +133,8 @@
init_completion(&lcdc_comp);
mdp4_init_writeback_buf(mfd, MDP4_MIXER0);
- pipe->blt_addr = 0;
-
+ pipe->ov_blt_addr = 0;
+ pipe->dma_blt_addr = 0;
} else {
pipe = lcdc_pipe;
}
@@ -322,7 +322,7 @@
char *overlay_base;
- if (pipe->blt_addr == 0)
+ if (pipe->ov_blt_addr == 0)
return;
@@ -334,7 +334,7 @@
off = 0;
if (pipe->ov_cnt & 0x01)
off = pipe->src_height * pipe->src_width * bpp;
- addr = pipe->blt_addr + off;
+ addr = pipe->ov_blt_addr + off;
/* overlay 0 */
overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
@@ -347,7 +347,7 @@
uint32 off, addr;
int bpp;
- if (pipe->blt_addr == 0)
+ if (pipe->ov_blt_addr == 0)
return;
@@ -359,7 +359,7 @@
off = 0;
if (pipe->dmap_cnt & 0x01)
off = pipe->src_height * pipe->src_width * bpp;
- addr = pipe->blt_addr + off;
+ addr = pipe->dma_blt_addr + off;
/* dmap */
MDP_OUTP(MDP_BASE + 0x90008, addr);
@@ -436,7 +436,7 @@
if (pipe->flags & MDP_OV_PLAY_NOWAIT)
return;
- if (lcdc_pipe->blt_addr) {
+ if (lcdc_pipe->ov_blt_addr) {
mdp4_overlay_lcdc_dma_busy_wait(mfd);
mdp4_lcdc_blt_ov_update(lcdc_pipe);
@@ -483,7 +483,7 @@
{
spin_lock(&mdp_spin_lock);
dma->busy = FALSE;
- if (lcdc_pipe->blt_addr == 0) {
+ if (lcdc_pipe->ov_blt_addr == 0) {
spin_unlock(&mdp_spin_lock);
return;
}
@@ -498,7 +498,7 @@
{
unsigned long flag;
- if (lcdc_pipe->blt_addr) {
+ if (lcdc_pipe->ov_blt_addr) {
mdp4_overlay_lcdc_dma_busy_wait(mfd);
mdp4_lcdc_blt_ov_update(lcdc_pipe);
@@ -528,24 +528,26 @@
mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
- if (!mfd->ov0_wb_buf->phys_addr) {
+ if (!mfd->ov0_wb_buf->write_addr) {
pr_debug("%s: no blt_base assigned\n", __func__);
return;
}
spin_lock_irqsave(&mdp_spin_lock, flag);
- if (enable && lcdc_pipe->blt_addr == 0) {
- lcdc_pipe->blt_addr = mfd->ov0_wb_buf->phys_addr;
+ if (enable && lcdc_pipe->ov_blt_addr == 0) {
+ lcdc_pipe->ov_blt_addr = mfd->ov0_wb_buf->write_addr;
+ lcdc_pipe->dma_blt_addr = mfd->ov0_wb_buf->read_addr;
change++;
lcdc_pipe->blt_cnt = 0;
lcdc_pipe->ov_cnt = 0;
lcdc_pipe->dmap_cnt = 0;
mdp4_stat.blt_lcdc++;
- } else if (enable == 0 && lcdc_pipe->blt_addr) {
- lcdc_pipe->blt_addr = 0;
+ } else if (enable == 0 && lcdc_pipe->ov_blt_addr) {
+ lcdc_pipe->ov_blt_addr = 0;
+ lcdc_pipe->dma_blt_addr = 0;
change++;
}
- pr_info("%s: blt_addr=%x\n", __func__, (int)lcdc_pipe->blt_addr);
+ pr_info("%s: ov_blt_addr=%x\n", __func__, (int)lcdc_pipe->ov_blt_addr);
spin_unlock_irqrestore(&mdp_spin_lock, flag);
if (!change)
@@ -559,7 +561,7 @@
mdp4_overlayproc_cfg(lcdc_pipe);
mdp4_overlay_dmap_xy(lcdc_pipe);
- if (lcdc_pipe->blt_addr) {
+ if (lcdc_pipe->ov_blt_addr) {
mdp4_overlay_lcdc_prefill(mfd);
mdp4_overlay_lcdc_prefill(mfd);
}
diff --git a/drivers/video/msm/mdp4_overlay_mddi.c b/drivers/video/msm/mdp4_overlay_mddi.c
index 82864918..c4e6793 100644
--- a/drivers/video/msm/mdp4_overlay_mddi.c
+++ b/drivers/video/msm/mdp4_overlay_mddi.c
@@ -163,7 +163,8 @@
MDP_OUTP(MDP_BASE + 0x00098, 0x01);
mdp4_init_writeback_buf(mfd, MDP4_MIXER0);
- pipe->blt_addr = 0;
+ pipe->ov_blt_addr = 0;
+ pipe->dma_blt_addr = 0;
} else {
pipe = mddi_pipe;
}
@@ -254,23 +255,25 @@
unsigned long flag;
pr_debug("%s: blt_end=%d blt_addr=%x pid=%d\n",
- __func__, mddi_pipe->blt_end, (int)mddi_pipe->blt_addr, current->pid);
+ __func__, mddi_pipe->blt_end,
+ (int)mddi_pipe->ov_blt_addr, current->pid);
mdp4_allocate_writeback_buf(mfd, MDP4_MIXER0);
- if (mfd->ov0_wb_buf->phys_addr == 0) {
+ if (mfd->ov0_wb_buf->write_addr == 0) {
pr_info("%s: no blt_base assigned\n", __func__);
return -EBUSY;
}
- if (mddi_pipe->blt_addr == 0) {
+ if (mddi_pipe->ov_blt_addr == 0) {
mdp4_mddi_dma_busy_wait(mfd);
spin_lock_irqsave(&mdp_spin_lock, flag);
mddi_pipe->blt_end = 0;
mddi_pipe->blt_cnt = 0;
mddi_pipe->ov_cnt = 0;
mddi_pipe->dmap_cnt = 0;
- mddi_pipe->blt_addr = mfd->ov0_wb_buf->phys_addr;
+ mddi_pipe->ov_blt_addr = mfd->ov0_wb_buf->write_addr;
+ mddi_pipe->dma_blt_addr = mfd->ov0_wb_buf->write_addr;
mdp4_stat.blt_mddi++;
spin_unlock_irqrestore(&mdp_spin_lock, flag);
return 0;
@@ -284,9 +287,9 @@
unsigned long flag;
pr_debug("%s: blt_end=%d blt_addr=%x\n",
- __func__, mddi_pipe->blt_end, (int)mddi_pipe->blt_addr);
+ __func__, mddi_pipe->blt_end, (int)mddi_pipe->ov_blt_addr);
- if ((mddi_pipe->blt_end == 0) && mddi_pipe->blt_addr) {
+ if ((mddi_pipe->blt_end == 0) && mddi_pipe->ov_blt_addr) {
spin_lock_irqsave(&mdp_spin_lock, flag);
mddi_pipe->blt_end = 1; /* mark as end */
spin_unlock_irqrestore(&mdp_spin_lock, flag);
@@ -323,7 +326,7 @@
int bpp;
char *overlay_base;
- if (pipe->blt_addr == 0)
+ if (pipe->ov_blt_addr == 0)
return;
@@ -336,7 +339,7 @@
if (pipe->dmap_cnt & 0x01)
off = pipe->src_height * pipe->src_width * bpp;
- addr = pipe->blt_addr + off;
+ addr = pipe->ov_blt_addr + off;
/* dmap */
MDP_OUTP(MDP_BASE + 0x90008, addr);
@@ -344,7 +347,7 @@
off = 0;
if (pipe->ov_cnt & 0x01)
off = pipe->src_height * pipe->src_width * bpp;
- addr2 = pipe->blt_addr + off;
+ addr2 = pipe->ov_blt_addr + off;
/* overlay 0 */
overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
outpdw(overlay_base + 0x000c, addr2);
@@ -371,7 +374,8 @@
if (mddi_pipe->blt_end) {
mddi_pipe->blt_end = 0;
- mddi_pipe->blt_addr = 0;
+ mddi_pipe->ov_blt_addr = 0;
+ mddi_pipe->dma_blt_addr = 0;
pr_debug("%s: END, ov_cnt=%d dmap_cnt=%d\n", __func__,
mddi_pipe->ov_cnt, mddi_pipe->dmap_cnt);
mdp_intr_mask &= ~INTR_DMA_P_DONE;
@@ -406,7 +410,7 @@
{
int diff;
- if (mddi_pipe->blt_addr == 0) {
+ if (mddi_pipe->ov_blt_addr == 0) {
mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
spin_lock(&mdp_spin_lock);
dma->busy = FALSE;
@@ -473,7 +477,7 @@
mdp4_mddi_dma_busy_wait(mddi_mfd);
mdp4_overlay_update_lcd(mddi_mfd);
- if (mddi_pipe->blt_addr)
+ if (mddi_pipe->ov_blt_addr)
mdp4_mddi_blt_dmap_busy_wait(mddi_mfd);
mdp4_mddi_overlay_kickoff(mddi_mfd, mddi_pipe);
mddi_mfd->dma_update_flag = 1;
@@ -539,17 +543,17 @@
* to be called before kickoff.
* vice versa for blt disabled.
*/
- if (mddi_pipe->blt_addr && mddi_pipe->blt_cnt == 0)
+ if (mddi_pipe->ov_blt_addr && mddi_pipe->blt_cnt == 0)
mdp4_overlay_update_lcd(mfd); /* first time */
- else if (mddi_pipe->blt_addr == 0 && mddi_pipe->blt_cnt) {
+ else if (mddi_pipe->ov_blt_addr == 0 && mddi_pipe->blt_cnt) {
mdp4_overlay_update_lcd(mfd); /* last time */
mddi_pipe->blt_cnt = 0;
}
pr_debug("%s: blt_addr=%d blt_cnt=%d\n",
- __func__, (int)mddi_pipe->blt_addr, mddi_pipe->blt_cnt);
+ __func__, (int)mddi_pipe->ov_blt_addr, mddi_pipe->blt_cnt);
- if (mddi_pipe->blt_addr)
+ if (mddi_pipe->ov_blt_addr)
mdp4_mddi_blt_dmap_busy_wait(mddi_mfd);
mdp4_mddi_overlay_kickoff(mfd, pipe);
}
@@ -572,7 +576,7 @@
mdp_enable_irq(MDP_OVERLAY0_TERM);
spin_lock_irqsave(&mdp_spin_lock, flag);
mfd->dma->busy = TRUE;
- if (mddi_pipe->blt_addr)
+ if (mddi_pipe->ov_blt_addr)
mfd->dma->dmap_busy = TRUE;
spin_unlock_irqrestore(&mdp_spin_lock, flag);
/* start OVERLAY pipe */
@@ -657,7 +661,7 @@
mdp_enable_irq(MDP_DMA_S_TERM);
- if (mddi_pipe->blt_addr == 0)
+ if (mddi_pipe->ov_blt_addr == 0)
mfd->dma->busy = TRUE;
mfd->ibuf_flushed = TRUE;
@@ -688,7 +692,7 @@
if (mfd && mfd->panel_power_on) {
mdp4_mddi_dma_busy_wait(mfd);
- if (mddi_pipe && mddi_pipe->blt_addr)
+ if (mddi_pipe && mddi_pipe->ov_blt_addr)
mdp4_mddi_blt_dmap_busy_wait(mfd);
mdp4_overlay_update_lcd(mfd);
diff --git a/drivers/video/msm/mdp4_overlay_writeback.c b/drivers/video/msm/mdp4_overlay_writeback.c
index f1a2ada..0174309 100644
--- a/drivers/video/msm/mdp4_overlay_writeback.c
+++ b/drivers/video/msm/mdp4_overlay_writeback.c
@@ -272,11 +272,11 @@
}
mutex_unlock(&mfd->writeback_mutex);
- writeback_pipe->blt_addr = (ulong) (node ? node->addr : NULL);
+ writeback_pipe->ov_blt_addr = (ulong) (node ? node->addr : NULL);
- if (!writeback_pipe->blt_addr) {
+ if (!writeback_pipe->ov_blt_addr) {
pr_err("%s: no writeback buffer 0x%x, %p\n", __func__,
- (unsigned int)writeback_pipe->blt_addr, node);
+ (unsigned int)writeback_pipe->ov_blt_addr, node);
mutex_unlock(&mfd->unregister_mutex);
return;
}
@@ -324,13 +324,13 @@
}
mutex_unlock(&mfd->writeback_mutex);
- writeback_pipe->blt_addr = (ulong) (node ? node->addr : NULL);
+ writeback_pipe->ov_blt_addr = (ulong) (node ? node->addr : NULL);
mutex_lock(&mfd->dma->ov_mutex);
pr_debug("%s in writeback\n", __func__);
- if (writeback_pipe && !writeback_pipe->blt_addr) {
+ if (writeback_pipe && !writeback_pipe->ov_blt_addr) {
pr_err("%s: no writeback buffer 0x%x\n", __func__,
- (unsigned int)writeback_pipe->blt_addr);
+ (unsigned int)writeback_pipe->ov_blt_addr);
ret = mdp4_overlay_writeback_update(mfd);
if (ret)
pr_err("%s: update failed writeback pipe NULL\n",
@@ -351,7 +351,7 @@
}
pr_debug("%s: in writeback pan display 0x%x\n", __func__,
- (unsigned int)writeback_pipe->blt_addr);
+ (unsigned int)writeback_pipe->ov_blt_addr);
mdp4_writeback_kickoff_ui(mfd, writeback_pipe);
mdp4_iommu_unmap(writeback_pipe);
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index f192b12..208e3ce 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -2559,13 +2559,14 @@
buf = mfd->ov1_wb_buf;
buf->ihdl = NULL;
- buf->phys_addr = 0;
+ buf->write_addr = 0;
+ buf->read_addr = 0;
}
u32 mdp4_allocate_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num)
{
struct mdp_buf_type *buf;
- ion_phys_addr_t addr;
+ ion_phys_addr_t addr, read_addr = 0;
size_t buffer_size;
unsigned long len;
@@ -2574,7 +2575,7 @@
else
buf = mfd->ov1_wb_buf;
- if (buf->phys_addr || !IS_ERR_OR_NULL(buf->ihdl))
+ if (buf->write_addr || !IS_ERR_OR_NULL(buf->ihdl))
return 0;
if (!buf->size) {
@@ -2591,10 +2592,25 @@
buf->ihdl = ion_alloc(mfd->iclient, buffer_size, SZ_4K,
mfd->mem_hid);
if (!IS_ERR_OR_NULL(buf->ihdl)) {
+ if (mfd->mem_hid & ION_SECURE) {
+ if (ion_phys(mfd->iclient, buf->ihdl,
+ &addr, (unsigned *)&len)) {
+ pr_err("%s:%d: ion_phys map failed\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+ } else {
+ if (ion_map_iommu(mfd->iclient, buf->ihdl,
+ DISPLAY_WRITE_DOMAIN, GEN_POOL, SZ_4K,
+ 0, &addr, &len, 0, 0)) {
+ pr_err("ion_map_iommu() write failed\n");
+ return -ENOMEM;
+ }
+ }
if (ion_map_iommu(mfd->iclient, buf->ihdl,
- DISPLAY_DOMAIN, GEN_POOL, SZ_4K, 0, &addr,
- &len, 0, 0)) {
- pr_err("ion_map_iommu() failed\n");
+ DISPLAY_READ_DOMAIN, GEN_POOL, SZ_4K,
+ 0, &read_addr, &len, 0, 0)) {
+ pr_err("ion_map_iommu() read failed\n");
return -ENOMEM;
}
} else {
@@ -2609,7 +2625,13 @@
if (addr) {
pr_info("allocating %d bytes at %x for mdp writeback\n",
buffer_size, (u32) addr);
- buf->phys_addr = addr;
+ buf->write_addr = addr;
+
+ if (read_addr)
+ buf->read_addr = read_addr;
+ else
+ buf->read_addr = buf->write_addr;
+
return 0;
} else {
pr_err("%s cannot allocate memory for mdp writeback!\n",
@@ -2629,21 +2651,25 @@
if (!IS_ERR_OR_NULL(mfd->iclient)) {
if (!IS_ERR_OR_NULL(buf->ihdl)) {
+ if (!(mfd->mem_hid & ION_SECURE))
+ ion_unmap_iommu(mfd->iclient, buf->ihdl,
+ DISPLAY_WRITE_DOMAIN, GEN_POOL);
ion_unmap_iommu(mfd->iclient, buf->ihdl,
- DISPLAY_DOMAIN, GEN_POOL);
+ DISPLAY_READ_DOMAIN, GEN_POOL);
ion_free(mfd->iclient, buf->ihdl);
pr_debug("%s:%d free writeback imem\n", __func__,
__LINE__);
buf->ihdl = NULL;
}
} else {
- if (buf->phys_addr) {
- free_contiguous_memory_by_paddr(buf->phys_addr);
+ if (buf->write_addr) {
+ free_contiguous_memory_by_paddr(buf->write_addr);
pr_debug("%s:%d free writeback pmem\n", __func__,
__LINE__);
}
}
- buf->phys_addr = 0;
+ buf->write_addr = 0;
+ buf->read_addr = 0;
}
static int mdp4_update_pcc_regs(uint32_t offset,
diff --git a/drivers/video/msm/mdss/Kconfig b/drivers/video/msm/mdss/Kconfig
new file mode 100644
index 0000000..30351a3
--- /dev/null
+++ b/drivers/video/msm/mdss/Kconfig
@@ -0,0 +1,5 @@
+config FB_MSM_MDSS_WRITEBACK
+ bool "MDSS Writeback Panel"
+ ---help---
+ The MDSS Writeback Panel provides support for routing the output of
+ MDSS frame buffer driver and MDP processing to memory.
diff --git a/drivers/video/msm/mdss/Makefile b/drivers/video/msm/mdss/Makefile
new file mode 100644
index 0000000..780e0c6
--- /dev/null
+++ b/drivers/video/msm/mdss/Makefile
@@ -0,0 +1,9 @@
+mdss-mdp-objs := mdss_mdp.o mdss_mdp_ctl.o mdss_mdp_pipe.o mdss_mdp_util.o
+mdss-mdp-objs += mdss_mdp_pp.o
+mdss-mdp-objs += mdss_mdp_intf_video.o
+mdss-mdp-objs += mdss_mdp_intf_writeback.o
+mdss-mdp-objs += mdss_mdp_rotator.o
+mdss-mdp-objs += mdss_mdp_overlay.o
+obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o
+obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o
+obj-$(CONFIG_FB_MSM_MDSS_WRITEBACK) += mdss_wb.o
diff --git a/drivers/video/msm/mdss/mdss.h b/drivers/video/msm/mdss/mdss.h
new file mode 100644
index 0000000..a58c3e6
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss.h
@@ -0,0 +1,90 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 MDSS_H
+#define MDSS_H
+
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#define MDSS_REG_WRITE(addr, val) writel_relaxed(val, mdss_reg_base + addr)
+#define MDSS_REG_READ(addr) readl_relaxed(mdss_reg_base + addr)
+
+extern unsigned char *mdss_reg_base;
+
+enum mdss_mdp_clk_type {
+ MDSS_CLK_AHB,
+ MDSS_CLK_AXI,
+ MDSS_CLK_MDP_SRC,
+ MDSS_CLK_MDP_CORE,
+ MDSS_CLK_MDP_LUT,
+ MDSS_CLK_MDP_VSYNC,
+ MDSS_MAX_CLK
+};
+
+struct mdss_res_type {
+ u32 rev;
+ u32 mdp_rev;
+ struct clk *mdp_clk[MDSS_MAX_CLK];
+ struct regulator *fs;
+
+ struct workqueue_struct *clk_ctrl_wq;
+ struct delayed_work clk_ctrl_worker;
+
+ u32 irq;
+ u32 irq_mask;
+ u32 irq_ena;
+ u32 irq_buzy;
+
+ u32 mdp_irq_mask;
+
+ u32 clk_ena;
+ u32 suspend;
+ u32 timeout;
+
+ u32 fs_ena;
+ u32 vsync_ena;
+
+ u32 intf;
+ u32 eintf_ena;
+ u32 prim_ptype;
+ u32 res_init;
+ u32 pdev_lcnt;
+ u32 bus_hdl;
+
+ u32 smp_mb_cnt;
+ u32 smp_mb_size;
+ u32 *pipe_type_map;
+ u32 *mixer_type_map;
+};
+extern struct mdss_res_type *mdss_res;
+
+enum mdss_hw_index {
+ MDSS_HW_MDP,
+ MDSS_HW_DSI0,
+ MDSS_HW_DSI1,
+ MDSS_HW_HDMI,
+ MDSS_HW_EDP,
+ MDSS_MAX_HW_BLK
+};
+
+struct mdss_hw {
+ u32 hw_ndx;
+ irqreturn_t (*irq_handler)(int irq, void *ptr);
+};
+
+void mdss_enable_irq(struct mdss_hw *hw);
+void mdss_disable_irq(struct mdss_hw *hw);
+void mdss_disable_irq_nosync(struct mdss_hw *hw);
+#endif /* MDSS_H */
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
new file mode 100644
index 0000000..0fedb6c
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -0,0 +1,1231 @@
+/*
+ * Core MDSS framebuffer driver.
+ *
+ * Copyright (C) 2007 Google Incorporated
+ * Copyright (c) 2008-2012, Code Aurora Forum. 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/android_pmem.h>
+#include <linux/bootmem.h>
+#include <linux/console.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/memory.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/msm_mdp.h>
+#include <linux/proc_fs.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+#include <linux/vmalloc.h>
+
+#include <mach/board.h>
+
+#include "mdss_fb.h"
+#include "mdss_mdp.h"
+
+#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER
+#define MDSS_FB_NUM 3
+#else
+#define MDSS_FB_NUM 2
+#endif
+
+#define MAX_FBI_LIST 32
+static struct fb_info *fbi_list[MAX_FBI_LIST];
+static int fbi_list_index;
+
+static u32 mdss_fb_pseudo_palette[16] = {
+ 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+};
+
+static int mdss_fb_register(struct msm_fb_data_type *mfd);
+static int mdss_fb_open(struct fb_info *info, int user);
+static int mdss_fb_release(struct fb_info *info, int user);
+static int mdss_fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info);
+static int mdss_fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info);
+static int mdss_fb_set_par(struct fb_info *info);
+static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info,
+ int op_enable);
+static int mdss_fb_suspend_sub(struct msm_fb_data_type *mfd);
+static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg);
+static int mdss_fb_mmap(struct fb_info *info, struct vm_area_struct *vma);
+
+#define MAX_BACKLIGHT_BRIGHTNESS 255
+static int lcd_backlight_registered;
+
+static void mdss_fb_set_bl_brightness(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct msm_fb_data_type *mfd = dev_get_drvdata(led_cdev->dev->parent);
+ int bl_lvl;
+
+ if (value > MAX_BACKLIGHT_BRIGHTNESS)
+ value = MAX_BACKLIGHT_BRIGHTNESS;
+
+ /* This maps android backlight level 0 to 255 into
+ driver backlight level 0 to bl_max with rounding */
+ bl_lvl = (2 * value * mfd->panel_info.bl_max + MAX_BACKLIGHT_BRIGHTNESS)
+ /(2 * MAX_BACKLIGHT_BRIGHTNESS);
+
+ if (!bl_lvl && value)
+ bl_lvl = 1;
+
+ mdss_fb_set_backlight(mfd, bl_lvl);
+}
+
+static struct led_classdev backlight_led = {
+ .name = "lcd-backlight",
+ .brightness = MAX_BACKLIGHT_BRIGHTNESS,
+ .brightness_set = mdss_fb_set_bl_brightness,
+};
+
+static ssize_t mdss_fb_get_type(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret = 0;
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
+
+ switch (mfd->panel_info.type) {
+ case NO_PANEL:
+ ret = snprintf(buf, PAGE_SIZE, "no panel\n");
+ break;
+ case HDMI_PANEL:
+ ret = snprintf(buf, PAGE_SIZE, "hdmi panel\n");
+ break;
+ case LVDS_PANEL:
+ ret = snprintf(buf, PAGE_SIZE, "lvds panel\n");
+ break;
+ case DTV_PANEL:
+ ret = snprintf(buf, PAGE_SIZE, "dtv panel\n");
+ break;
+ case MIPI_VIDEO_PANEL:
+ ret = snprintf(buf, PAGE_SIZE, "mipi dsi video panel\n");
+ break;
+ case MIPI_CMD_PANEL:
+ ret = snprintf(buf, PAGE_SIZE, "mipi dsi cmd panel\n");
+ break;
+ case WRITEBACK_PANEL:
+ ret = snprintf(buf, PAGE_SIZE, "writeback panel\n");
+ break;
+ default:
+ ret = snprintf(buf, PAGE_SIZE, "unknown panel\n");
+ break;
+ }
+
+ return ret;
+}
+
+static DEVICE_ATTR(mdss_fb_type, S_IRUGO, mdss_fb_get_type, NULL);
+static struct attribute *mdss_fb_attrs[] = {
+ &dev_attr_mdss_fb_type.attr,
+ NULL,
+};
+
+static struct attribute_group mdss_fb_attr_group = {
+ .attrs = mdss_fb_attrs,
+};
+
+static int mdss_fb_create_sysfs(struct msm_fb_data_type *mfd)
+{
+ int rc;
+
+ rc = sysfs_create_group(&mfd->fbi->dev->kobj, &mdss_fb_attr_group);
+ if (rc)
+ pr_err("sysfs group creation failed, rc=%d\n", rc);
+ return rc;
+}
+
+static void mdss_fb_remove_sysfs(struct msm_fb_data_type *mfd)
+{
+ sysfs_remove_group(&mfd->fbi->dev->kobj, &mdss_fb_attr_group);
+}
+
+static int mdss_fb_probe(struct platform_device *pdev)
+{
+ struct msm_fb_data_type *mfd = NULL;
+ struct mdss_panel_data *pdata;
+ struct fb_info *fbi;
+ int rc;
+
+ if (fbi_list_index >= MAX_FBI_LIST)
+ return -ENOMEM;
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata)
+ return -ENODEV;
+
+ /*
+ * alloc framebuffer info + par data
+ */
+ fbi = framebuffer_alloc(sizeof(struct msm_fb_data_type), &pdev->dev);
+ if (fbi == NULL) {
+ pr_err("can't allocate framebuffer info data!\n");
+ return -ENOMEM;
+ }
+
+ mfd = (struct msm_fb_data_type *)fbi->par;
+ mfd->key = MFD_KEY;
+ mfd->fbi = fbi;
+ mfd->panel_info = pdata->panel_info;
+ mfd->panel.type = pdata->panel_info.type;
+ mfd->panel.id = mfd->index;
+ mfd->fb_page = MDSS_FB_NUM;
+ mfd->index = fbi_list_index;
+ mfd->mdp_fb_page_protection = MDP_FB_PAGE_PROTECTION_WRITECOMBINE;
+ mfd->panel_info.frame_count = 0;
+ mfd->bl_level = 0;
+ mfd->fb_imgType = MDP_RGBA_8888;
+ mfd->iclient = msm_ion_client_create(-1, pdev->name);
+ if (IS_ERR(mfd->iclient))
+ mfd->iclient = NULL;
+
+ mfd->pdev = pdev;
+
+ mutex_init(&mfd->lock);
+
+ fbi_list[fbi_list_index++] = fbi;
+
+ platform_set_drvdata(pdev, mfd);
+
+ rc = mdss_fb_register(mfd);
+ if (rc)
+ return rc;
+
+ rc = pm_runtime_set_active(mfd->fbi->dev);
+ if (rc < 0)
+ pr_err("pm_runtime: fail to set active.\n");
+ pm_runtime_enable(mfd->fbi->dev);
+
+ /* android supports only one lcd-backlight/lcd for now */
+ if (!lcd_backlight_registered) {
+ if (led_classdev_register(&pdev->dev, &backlight_led))
+ pr_err("led_classdev_register failed\n");
+ else
+ lcd_backlight_registered = 1;
+ }
+
+ mdss_fb_create_sysfs(mfd);
+
+ return 0;
+}
+
+static int mdss_fb_remove(struct platform_device *pdev)
+{
+ struct msm_fb_data_type *mfd;
+
+ mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+ mdss_fb_remove_sysfs(mfd);
+
+ pm_runtime_disable(mfd->fbi->dev);
+
+ if (!mfd)
+ return -ENODEV;
+
+ if (mfd->key != MFD_KEY)
+ return -EINVAL;
+
+ if (mdss_fb_suspend_sub(mfd))
+ pr_err("msm_fb_remove: can't stop the device %d\n",
+ mfd->index);
+
+ /* remove /dev/fb* */
+ unregister_framebuffer(mfd->fbi);
+
+ if (lcd_backlight_registered) {
+ lcd_backlight_registered = 0;
+ led_classdev_unregister(&backlight_led);
+ }
+
+ return 0;
+}
+
+static int mdss_fb_suspend_sub(struct msm_fb_data_type *mfd)
+{
+ int ret = 0;
+
+ if ((!mfd) || (mfd->key != MFD_KEY))
+ return 0;
+
+ /*
+ * suspend this channel
+ */
+ mfd->suspend.op_enable = mfd->op_enable;
+ mfd->suspend.panel_power_on = mfd->panel_power_on;
+
+ if (mfd->op_enable) {
+ ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, mfd->fbi,
+ mfd->suspend.op_enable);
+ if (ret) {
+ pr_warn("can't turn off display!\n");
+ return ret;
+ }
+ mfd->op_enable = false;
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_PM)
+static int mdss_fb_resume_sub(struct msm_fb_data_type *mfd)
+{
+ int ret = 0;
+
+ if ((!mfd) || (mfd->key != MFD_KEY))
+ return 0;
+
+ /* resume state var recover */
+ mfd->op_enable = mfd->suspend.op_enable;
+
+ if (mfd->suspend.panel_power_on) {
+ ret = mdss_fb_blank_sub(FB_BLANK_UNBLANK, mfd->fbi,
+ mfd->op_enable);
+ if (ret)
+ pr_warn("can't turn on display!\n");
+ }
+
+ return ret;
+}
+
+static int mdss_fb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct msm_fb_data_type *mfd;
+ int ret = 0;
+
+ pr_debug("mdss_fb_suspend\n");
+
+ mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+ if ((!mfd) || (mfd->key != MFD_KEY))
+ return 0;
+
+ console_lock();
+ fb_set_suspend(mfd->fbi, FBINFO_STATE_SUSPENDED);
+
+ ret = mdss_fb_suspend_sub(mfd);
+ if (ret != 0) {
+ pr_err("failed to suspend! %d\n", ret);
+ fb_set_suspend(mfd->fbi, FBINFO_STATE_RUNNING);
+ } else {
+ pdev->dev.power.power_state = state;
+ }
+
+ console_unlock();
+ return ret;
+}
+
+static int mdss_fb_resume(struct platform_device *pdev)
+{
+ /* This resume function is called when interrupt is enabled.
+ */
+ int ret = 0;
+ struct msm_fb_data_type *mfd;
+
+ pr_debug("mdss_fb_resume\n");
+
+ mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+ if ((!mfd) || (mfd->key != MFD_KEY))
+ return 0;
+
+ console_lock();
+ ret = mdss_fb_resume_sub(mfd);
+ pdev->dev.power.power_state = PMSG_ON;
+ fb_set_suspend(mfd->fbi, FBINFO_STATE_RUNNING);
+ console_unlock();
+
+ return ret;
+}
+#else
+#define mdss_fb_suspend NULL
+#define mdss_fb_resume NULL
+#endif
+
+#if defined(CONFIG_PM) && defined(CONFIG_SUSPEND)
+static int mdss_fb_ext_suspend(struct device *dev)
+{
+ struct msm_fb_data_type *mfd = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if ((!mfd) || (mfd->key != MFD_KEY))
+ return 0;
+
+ if (mfd->panel_info.type == HDMI_PANEL ||
+ mfd->panel_info.type == DTV_PANEL)
+ ret = mdss_fb_suspend_sub(mfd);
+
+ return ret;
+}
+
+static int mdss_fb_ext_resume(struct device *dev)
+{
+ struct msm_fb_data_type *mfd = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if ((!mfd) || (mfd->key != MFD_KEY))
+ return 0;
+
+ if (mfd->panel_info.type == HDMI_PANEL ||
+ mfd->panel_info.type == DTV_PANEL)
+ ret = mdss_fb_resume_sub(mfd);
+
+ return ret;
+}
+#else
+#define mdss_fb_ext_suspend NULL
+#define mdss_fb_ext_resume NULL
+#endif
+
+static const struct dev_pm_ops mdss_fb_dev_pm_ops = {
+ .suspend = mdss_fb_ext_suspend,
+ .resume = mdss_fb_ext_resume,
+};
+
+static struct platform_driver mdss_fb_driver = {
+ .probe = mdss_fb_probe,
+ .remove = mdss_fb_remove,
+ .suspend = mdss_fb_suspend,
+ .resume = mdss_fb_resume,
+ .shutdown = NULL,
+ .driver = {
+ .name = "mdss_fb",
+ .pm = &mdss_fb_dev_pm_ops,
+ },
+};
+
+static int unset_bl_level, bl_updated;
+static int bl_level_old;
+
+void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl)
+{
+ struct mdss_panel_data *pdata;
+
+ if (!mfd->panel_power_on || !bl_updated) {
+ unset_bl_level = bkl_lvl;
+ return;
+ } else {
+ unset_bl_level = 0;
+ }
+
+ pdata = dev_get_platdata(&mfd->pdev->dev);
+
+ if ((pdata) && (pdata->set_backlight)) {
+ mutex_lock(&mfd->lock);
+ if (bl_level_old == bkl_lvl) {
+ mutex_unlock(&mfd->lock);
+ return;
+ }
+ mfd->bl_level = bkl_lvl;
+ pdata->set_backlight(mfd->bl_level);
+ bl_level_old = mfd->bl_level;
+ mutex_unlock(&mfd->lock);
+ }
+}
+
+void mdss_fb_update_backlight(struct msm_fb_data_type *mfd)
+{
+ struct mdss_panel_data *pdata;
+
+ if (unset_bl_level && !bl_updated) {
+ pdata = dev_get_platdata(&mfd->pdev->dev);
+ if ((pdata) && (pdata->set_backlight)) {
+ mutex_lock(&mfd->lock);
+ mfd->bl_level = unset_bl_level;
+ pdata->set_backlight(mfd->bl_level);
+ bl_level_old = unset_bl_level;
+ mutex_unlock(&mfd->lock);
+ bl_updated = 1;
+ }
+ }
+}
+
+static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info,
+ int op_enable)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ int ret = 0;
+
+ if (!op_enable)
+ return -EPERM;
+
+ switch (blank_mode) {
+ case FB_BLANK_UNBLANK:
+ if (!mfd->panel_power_on) {
+ msleep(20);
+ ret = mfd->on_fnc(mfd);
+ if (ret == 0)
+ mfd->panel_power_on = true;
+ }
+ break;
+
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ case FB_BLANK_POWERDOWN:
+ default:
+ if (mfd->panel_power_on) {
+ int curr_pwr_state;
+
+ mfd->op_enable = false;
+ curr_pwr_state = mfd->panel_power_on;
+ mfd->panel_power_on = false;
+ bl_updated = 0;
+
+ msleep(20);
+ ret = mfd->off_fnc(mfd);
+ if (ret)
+ mfd->panel_power_on = curr_pwr_state;
+
+ mfd->op_enable = true;
+ }
+ break;
+ }
+
+ return ret;
+}
+
+static int mdss_fb_blank(int blank_mode, struct fb_info *info)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ return mdss_fb_blank_sub(blank_mode, info, mfd->op_enable);
+}
+
+/*
+ * Custom Framebuffer mmap() function for MSM driver.
+ * Differs from standard mmap() function by allowing for customized
+ * page-protection.
+ */
+static int mdss_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ /* Get frame buffer memory range. */
+ unsigned long start = info->fix.smem_start;
+ u32 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
+ unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
+ if (off >= len) {
+ /* memory mapped io */
+ off -= len;
+ if (info->var.accel_flags) {
+ mutex_unlock(&info->lock);
+ return -EINVAL;
+ }
+ start = info->fix.mmio_start;
+ len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
+ }
+
+ /* Set VM flags. */
+ start &= PAGE_MASK;
+ if ((vma->vm_end - vma->vm_start + off) > len)
+ return -EINVAL;
+ off += start;
+ vma->vm_pgoff = off >> PAGE_SHIFT;
+ /* This is an IO map - tell maydump to skip this VMA */
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+
+ /* Set VM page protection */
+ if (mfd->mdp_fb_page_protection == MDP_FB_PAGE_PROTECTION_WRITECOMBINE)
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ else if (mfd->mdp_fb_page_protection ==
+ MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE)
+ vma->vm_page_prot = pgprot_writethroughcache(vma->vm_page_prot);
+ else if (mfd->mdp_fb_page_protection ==
+ MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE)
+ vma->vm_page_prot = pgprot_writebackcache(vma->vm_page_prot);
+ else if (mfd->mdp_fb_page_protection ==
+ MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE)
+ vma->vm_page_prot = pgprot_writebackwacache(vma->vm_page_prot);
+ else
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* Remap the frame buffer I/O range */
+ if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ return 0;
+}
+
+static struct fb_ops mdss_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = mdss_fb_open,
+ .fb_release = mdss_fb_release,
+ .fb_check_var = mdss_fb_check_var, /* vinfo check */
+ .fb_set_par = mdss_fb_set_par, /* set the video mode */
+ .fb_blank = mdss_fb_blank, /* blank display */
+ .fb_pan_display = mdss_fb_pan_display, /* pan display */
+ .fb_ioctl = mdss_fb_ioctl, /* perform fb specific ioctl */
+ .fb_mmap = mdss_fb_mmap,
+};
+
+static u32 mdss_fb_line_length(u32 fb_index, u32 xres, int bpp)
+{
+ /* The adreno GPU hardware requires that the pitch be aligned to
+ 32 pixels for color buffers, so for the cases where the GPU
+ is writing directly to fb0, the framebuffer pitch
+ also needs to be 32 pixel aligned */
+
+ if (fb_index == 0)
+ return ALIGN(xres, 32) * bpp;
+ else
+ return xres * bpp;
+}
+
+static int mdss_fb_alloc_fbmem(struct msm_fb_data_type *mfd)
+{
+ void *virt = NULL;
+ unsigned long phys = 0;
+ size_t size;
+
+ size = PAGE_ALIGN(mfd->fbi->fix.line_length * mfd->panel_info.yres);
+ size *= mfd->fb_page;
+
+ if (mfd->index == 0) {
+ virt = dma_alloc_coherent(NULL, size, (dma_addr_t *) &phys,
+ GFP_KERNEL);
+ if (!virt) {
+ pr_err("unable to alloc fb memory size=%u\n", size);
+ return -ENOMEM;
+ }
+
+ pr_info("allocating %u bytes at %p (%lx phys) for fb %d\n",
+ size, virt, phys, mfd->index);
+ } else {
+ pr_debug("no memory allocated for fb%d\n", mfd->index);
+ size = 0;
+ }
+
+ mfd->fbi->screen_base = virt;
+ mfd->fbi->fix.smem_start = phys;
+ mfd->fbi->fix.smem_len = size;
+
+ return 0;
+}
+
+static int mdss_fb_register(struct msm_fb_data_type *mfd)
+{
+ int ret = -ENODEV;
+ int bpp;
+ struct mdss_panel_info *panel_info = &mfd->panel_info;
+ struct fb_info *fbi = mfd->fbi;
+ struct fb_fix_screeninfo *fix;
+ struct fb_var_screeninfo *var;
+ int *id;
+
+ /*
+ * fb info initialization
+ */
+ fix = &fbi->fix;
+ var = &fbi->var;
+
+ fix->type_aux = 0; /* if type == FB_TYPE_INTERLEAVED_PLANES */
+ fix->visual = FB_VISUAL_TRUECOLOR; /* True Color */
+ fix->ywrapstep = 0; /* No support */
+ fix->mmio_start = 0; /* No MMIO Address */
+ fix->mmio_len = 0; /* No MMIO Address */
+ fix->accel = FB_ACCEL_NONE;/* FB_ACCEL_MSM needes to be added in fb.h */
+
+ var->xoffset = 0, /* Offset from virtual to visible */
+ var->yoffset = 0, /* resolution */
+ var->grayscale = 0, /* No graylevels */
+ var->nonstd = 0, /* standard pixel format */
+ var->activate = FB_ACTIVATE_VBL, /* activate it at vsync */
+ var->height = -1, /* height of picture in mm */
+ var->width = -1, /* width of picture in mm */
+ var->accel_flags = 0, /* acceleration flags */
+ var->sync = 0, /* see FB_SYNC_* */
+ var->rotate = 0, /* angle we rotate counter clockwise */
+ mfd->op_enable = false;
+
+ switch (mfd->fb_imgType) {
+ case MDP_RGB_565:
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+ var->vmode = FB_VMODE_NONINTERLACED;
+ var->blue.offset = 0;
+ var->green.offset = 5;
+ var->red.offset = 11;
+ var->blue.length = 5;
+ var->green.length = 6;
+ var->red.length = 5;
+ var->blue.msb_right = 0;
+ var->green.msb_right = 0;
+ var->red.msb_right = 0;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ bpp = 2;
+ break;
+
+ case MDP_RGB_888:
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+ var->vmode = FB_VMODE_NONINTERLACED;
+ var->blue.offset = 0;
+ var->green.offset = 8;
+ var->red.offset = 16;
+ var->blue.length = 8;
+ var->green.length = 8;
+ var->red.length = 8;
+ var->blue.msb_right = 0;
+ var->green.msb_right = 0;
+ var->red.msb_right = 0;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ bpp = 3;
+ break;
+
+ case MDP_ARGB_8888:
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+ var->vmode = FB_VMODE_NONINTERLACED;
+ var->blue.offset = 0;
+ var->green.offset = 8;
+ var->red.offset = 16;
+ var->blue.length = 8;
+ var->green.length = 8;
+ var->red.length = 8;
+ var->blue.msb_right = 0;
+ var->green.msb_right = 0;
+ var->red.msb_right = 0;
+ var->transp.offset = 24;
+ var->transp.length = 8;
+ bpp = 4;
+ break;
+
+ case MDP_RGBA_8888:
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+ var->vmode = FB_VMODE_NONINTERLACED;
+ var->blue.offset = 8;
+ var->green.offset = 16;
+ var->red.offset = 24;
+ var->blue.length = 8;
+ var->green.length = 8;
+ var->red.length = 8;
+ var->blue.msb_right = 0;
+ var->green.msb_right = 0;
+ var->red.msb_right = 0;
+ var->transp.offset = 0;
+ var->transp.length = 8;
+ bpp = 4;
+ break;
+
+ case MDP_YCRYCB_H2V1:
+ fix->type = FB_TYPE_INTERLEAVED_PLANES;
+ fix->xpanstep = 2;
+ fix->ypanstep = 1;
+ var->vmode = FB_VMODE_NONINTERLACED;
+
+ /* how about R/G/B offset? */
+ var->blue.offset = 0;
+ var->green.offset = 5;
+ var->red.offset = 11;
+ var->blue.length = 5;
+ var->green.length = 6;
+ var->red.length = 5;
+ var->blue.msb_right = 0;
+ var->green.msb_right = 0;
+ var->red.msb_right = 0;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ bpp = 2;
+ break;
+
+ default:
+ pr_err("msm_fb_init: fb %d unkown image type!\n",
+ mfd->index);
+ return ret;
+ }
+
+ fix->type = panel_info->is_3d_panel;
+ fix->line_length = mdss_fb_line_length(mfd->index, panel_info->xres,
+ bpp);
+ mfd->var_xres = panel_info->xres;
+ mfd->var_yres = panel_info->yres;
+
+ var->pixclock = mfd->panel_info.clk_rate;
+ mfd->var_pixclock = var->pixclock;
+
+ var->xres = panel_info->xres;
+ var->yres = panel_info->yres;
+ var->xres_virtual = panel_info->xres;
+ var->yres_virtual = panel_info->yres * mfd->fb_page;
+ var->bits_per_pixel = bpp * 8; /* FrameBuffer color depth */
+
+ /* id field for fb app */
+
+ id = (int *)&mfd->panel;
+
+ snprintf(fix->id, sizeof(fix->id), "mdssfb_%x", (u32) *id);
+
+ fbi->fbops = &mdss_fb_ops;
+ fbi->flags = FBINFO_FLAG_DEFAULT;
+ fbi->pseudo_palette = mdss_fb_pseudo_palette;
+
+ mfd->ref_cnt = 0;
+ mfd->panel_power_on = false;
+
+ if (mdss_fb_alloc_fbmem(mfd)) {
+ pr_err("unable to allocate framebuffer memory\n");
+ return -ENOMEM;
+ }
+
+ mfd->op_enable = true;
+
+ /* cursor memory allocation */
+ if (mfd->cursor_update) {
+ mfd->cursor_buf = dma_alloc_coherent(NULL, MDSS_MDP_CURSOR_SIZE,
+ (dma_addr_t *) &mfd->cursor_buf_phys,
+ GFP_KERNEL);
+ if (!mfd->cursor_buf)
+ mfd->cursor_update = 0;
+ }
+
+ if (mfd->lut_update) {
+ ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
+ if (ret)
+ pr_err("fb_alloc_cmap() failed!\n");
+ }
+
+ if (register_framebuffer(fbi) < 0) {
+ if (mfd->lut_update)
+ fb_dealloc_cmap(&fbi->cmap);
+
+ if (mfd->cursor_buf)
+ dma_free_coherent(NULL, MDSS_MDP_CURSOR_SIZE,
+ mfd->cursor_buf,
+ (dma_addr_t) mfd->cursor_buf_phys);
+
+ mfd->op_enable = false;
+ return -EPERM;
+ }
+
+ pr_info("FrameBuffer[%d] %dx%d size=%d registered successfully!\n",
+ mfd->index, fbi->var.xres, fbi->var.yres,
+ fbi->fix.smem_len);
+
+ ret = 0;
+
+ return ret;
+}
+
+static int mdss_fb_open(struct fb_info *info, int user)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ int result;
+
+ result = pm_runtime_get_sync(info->dev);
+
+ if (result < 0)
+ pr_err("pm_runtime: fail to wake up\n");
+
+
+ if (!mfd->ref_cnt) {
+ result = mdss_fb_blank_sub(FB_BLANK_UNBLANK, info,
+ mfd->op_enable);
+ if (result) {
+ pr_err("mdss_fb_open: can't turn on display!\n");
+ return result;
+ }
+ }
+
+ mfd->ref_cnt++;
+ return 0;
+}
+
+static int mdss_fb_release(struct fb_info *info, int user)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ int ret = 0;
+
+ if (!mfd->ref_cnt) {
+ pr_info("try to close unopened fb %d!\n", mfd->index);
+ return -EINVAL;
+ }
+
+ mfd->ref_cnt--;
+
+ if (!mfd->ref_cnt) {
+ ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, info,
+ mfd->op_enable);
+ if (ret) {
+ pr_err("can't turn off display!\n");
+ return ret;
+ }
+ }
+
+ pm_runtime_put(info->dev);
+ return ret;
+}
+
+static int mdss_fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
+ if ((!mfd->op_enable) || (!mfd->panel_power_on))
+ return -EPERM;
+
+ if (var->xoffset > (info->var.xres_virtual - info->var.xres))
+ return -EINVAL;
+
+ if (var->yoffset > (info->var.yres_virtual - info->var.yres))
+ return -EINVAL;
+
+ if (info->fix.xpanstep)
+ info->var.xoffset =
+ (var->xoffset / info->fix.xpanstep) * info->fix.xpanstep;
+
+ if (info->fix.ypanstep)
+ info->var.yoffset =
+ (var->yoffset / info->fix.ypanstep) * info->fix.ypanstep;
+
+ if (mfd->dma_fnc)
+ mfd->dma_fnc(mfd);
+ else
+ pr_warn("dma function not set for panel type=%d\n",
+ mfd->panel.type);
+
+ mdss_fb_update_backlight(mfd);
+
+ ++mfd->panel_info.frame_count;
+ return 0;
+}
+
+static int mdss_fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ u32 len;
+
+ if (var->rotate != FB_ROTATE_UR)
+ return -EINVAL;
+ if (var->grayscale != info->var.grayscale)
+ return -EINVAL;
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ if ((var->green.offset != 5) ||
+ !((var->blue.offset == 11)
+ || (var->blue.offset == 0)) ||
+ !((var->red.offset == 11)
+ || (var->red.offset == 0)) ||
+ (var->blue.length != 5) ||
+ (var->green.length != 6) ||
+ (var->red.length != 5) ||
+ (var->blue.msb_right != 0) ||
+ (var->green.msb_right != 0) ||
+ (var->red.msb_right != 0) ||
+ (var->transp.offset != 0) ||
+ (var->transp.length != 0))
+ return -EINVAL;
+ break;
+
+ case 24:
+ if ((var->blue.offset != 0) ||
+ (var->green.offset != 8) ||
+ (var->red.offset != 16) ||
+ (var->blue.length != 8) ||
+ (var->green.length != 8) ||
+ (var->red.length != 8) ||
+ (var->blue.msb_right != 0) ||
+ (var->green.msb_right != 0) ||
+ (var->red.msb_right != 0) ||
+ !(((var->transp.offset == 0) &&
+ (var->transp.length == 0)) ||
+ ((var->transp.offset == 24) &&
+ (var->transp.length == 8))))
+ return -EINVAL;
+ break;
+
+ case 32:
+ /* Figure out if the user meant RGBA or ARGB
+ and verify the position of the RGB components */
+
+ if (var->transp.offset == 24) {
+ if ((var->blue.offset != 0) ||
+ (var->green.offset != 8) ||
+ (var->red.offset != 16))
+ return -EINVAL;
+ } else if (var->transp.offset == 0) {
+ if ((var->blue.offset != 8) ||
+ (var->green.offset != 16) ||
+ (var->red.offset != 24))
+ return -EINVAL;
+ } else
+ return -EINVAL;
+
+ /* Check the common values for both RGBA and ARGB */
+
+ if ((var->blue.length != 8) ||
+ (var->green.length != 8) ||
+ (var->red.length != 8) ||
+ (var->transp.length != 8) ||
+ (var->blue.msb_right != 0) ||
+ (var->green.msb_right != 0) ||
+ (var->red.msb_right != 0))
+ return -EINVAL;
+
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if ((var->xres_virtual <= 0) || (var->yres_virtual <= 0))
+ return -EINVAL;
+
+ len = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8);
+ if (len > info->fix.smem_len)
+ return -EINVAL;
+
+ if ((var->xres == 0) || (var->yres == 0))
+ return -EINVAL;
+
+ if ((var->xres > mfd->panel_info.xres) ||
+ (var->yres > mfd->panel_info.yres))
+ return -EINVAL;
+
+ if (var->xoffset > (var->xres_virtual - var->xres))
+ return -EINVAL;
+
+ if (var->yoffset > (var->yres_virtual - var->yres))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int mdss_fb_set_par(struct fb_info *info)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ struct fb_var_screeninfo *var = &info->var;
+ int old_imgType;
+ int blank = 0;
+
+ old_imgType = mfd->fb_imgType;
+ switch (var->bits_per_pixel) {
+ case 16:
+ if (var->red.offset == 0)
+ mfd->fb_imgType = MDP_BGR_565;
+ else
+ mfd->fb_imgType = MDP_RGB_565;
+ break;
+
+ case 24:
+ if ((var->transp.offset == 0) && (var->transp.length == 0))
+ mfd->fb_imgType = MDP_RGB_888;
+ else if ((var->transp.offset == 24) &&
+ (var->transp.length == 8)) {
+ mfd->fb_imgType = MDP_ARGB_8888;
+ info->var.bits_per_pixel = 32;
+ }
+ break;
+
+ case 32:
+ if (var->transp.offset == 24)
+ mfd->fb_imgType = MDP_ARGB_8888;
+ else
+ mfd->fb_imgType = MDP_RGBA_8888;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if ((mfd->var_pixclock != var->pixclock) ||
+ (mfd->hw_refresh && ((mfd->fb_imgType != old_imgType) ||
+ (mfd->var_pixclock != var->pixclock) ||
+ (mfd->var_xres != var->xres) ||
+ (mfd->var_yres != var->yres)))) {
+ mfd->var_xres = var->xres;
+ mfd->var_yres = var->yres;
+ mfd->var_pixclock = var->pixclock;
+ blank = 1;
+ }
+ mfd->fbi->fix.line_length = mdss_fb_line_length(mfd->index, var->xres,
+ var->bits_per_pixel / 8);
+
+ if (blank) {
+ mdss_fb_blank_sub(FB_BLANK_POWERDOWN, info, mfd->op_enable);
+ mdss_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable);
+ }
+
+ return 0;
+}
+
+static int mdss_fb_cursor(struct fb_info *info, void __user *p)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ struct fb_cursor cursor;
+ int ret;
+
+ if (!mfd->cursor_update)
+ return -ENODEV;
+
+ ret = copy_from_user(&cursor, p, sizeof(cursor));
+ if (ret)
+ return ret;
+
+ return mfd->cursor_update(info, &cursor);
+}
+
+static int mdss_fb_set_lut(struct fb_info *info, void __user *p)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ struct fb_cmap cmap;
+ int ret;
+
+ if (!mfd->lut_update)
+ return -ENODEV;
+
+ ret = copy_from_user(&cmap, p, sizeof(cmap));
+ if (ret)
+ return ret;
+
+ mfd->lut_update(info, &cmap);
+ return 0;
+}
+
+static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ void __user *argp = (void __user *)arg;
+ struct mdp_page_protection fb_page_protection;
+ int ret = -ENOSYS;
+
+ switch (cmd) {
+ case MSMFB_CURSOR:
+ ret = mdss_fb_cursor(info, argp);
+ break;
+
+ case MSMFB_SET_LUT:
+ ret = mdss_fb_set_lut(info, argp);
+ break;
+
+ case MSMFB_GET_PAGE_PROTECTION:
+ fb_page_protection.page_protection =
+ mfd->mdp_fb_page_protection;
+ ret = copy_to_user(argp, &fb_page_protection,
+ sizeof(fb_page_protection));
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ if (mfd->ioctl_handler)
+ ret = mfd->ioctl_handler(mfd, cmd, argp);
+ break;
+ }
+
+ if (ret == -ENOSYS)
+ pr_err("unsupported ioctl (%x)\n", cmd);
+
+ return ret;
+}
+
+int mdss_register_panel(struct mdss_panel_data *pdata)
+{
+ struct platform_device *mdss_fb_dev = NULL;
+ struct msm_fb_data_type *mfd;
+ int rc;
+
+ if (!mdss_res) {
+ pr_err("mdss mdp resources not initialized yet\n");
+ return -ENODEV;
+ }
+
+ mdss_fb_dev = platform_device_alloc("mdss_fb", pdata->panel_info.pdest);
+ if (!mdss_fb_dev) {
+ pr_err("unable to allocate mdss_fb device\n");
+ return -ENOMEM;
+ }
+
+ mdss_fb_dev->dev.platform_data = pdata;
+
+ rc = platform_device_add(mdss_fb_dev);
+ if (rc) {
+ platform_device_put(mdss_fb_dev);
+ pr_err("unable to probe mdss_fb device (%d)\n", rc);
+ return rc;
+ }
+
+ mfd = platform_get_drvdata(mdss_fb_dev);
+ if (!mfd)
+ return -ENODEV;
+ if (mfd->key != MFD_KEY)
+ return -EINVAL;
+
+ mfd->on_fnc = mdss_mdp_ctl_on;
+ mfd->off_fnc = mdss_mdp_ctl_off;
+
+ rc = mdss_mdp_overlay_init(mfd);
+ if (rc)
+ pr_err("unable to init overlay\n");
+
+ return rc;
+}
+EXPORT_SYMBOL(mdss_register_panel);
+
+int mdss_fb_get_phys_info(unsigned long *start, unsigned long *len, int fb_num)
+{
+ struct fb_info *info;
+
+ if (fb_num > MAX_FBI_LIST)
+ return -EINVAL;
+
+ info = fbi_list[fb_num];
+ if (!info)
+ return -ENOENT;
+
+ *start = info->fix.smem_start;
+ *len = info->fix.smem_len;
+ return 0;
+}
+EXPORT_SYMBOL(mdss_fb_get_phys_info);
+
+int __init mdss_fb_init(void)
+{
+ int rc = -ENODEV;
+
+ if (platform_driver_register(&mdss_fb_driver))
+ return rc;
+
+ return 0;
+}
+
+module_init(mdss_fb_init);
diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h
new file mode 100644
index 0000000..a3f0dbe
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_fb.h
@@ -0,0 +1,98 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. 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 MDSS_FB_H
+#define MDSS_FB_H
+
+#include <linux/ion.h>
+#include <linux/list.h>
+#include <linux/msm_mdp.h>
+#include <linux/types.h>
+
+#include "mdss_mdp.h"
+#include "mdss_panel.h"
+
+#define MSM_FB_DEFAULT_PAGE_SIZE 2
+#define MFD_KEY 0x11161126
+#define MSM_FB_MAX_DEV_LIST 32
+
+#define MSM_FB_ENABLE_DBGFS
+
+#ifndef MAX
+#define MAX(x, y) (((x) > (y)) ? (x) : (y))
+#endif
+
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+struct disp_info_type_suspend {
+ int op_enable;
+ int panel_power_on;
+};
+
+struct msm_fb_data_type {
+ u32 key;
+ u32 index;
+ u32 ref_cnt;
+ u32 fb_page;
+
+ struct panel_id panel;
+ struct mdss_panel_info panel_info;
+
+ u32 dest;
+ struct fb_info *fbi;
+
+ int op_enable;
+ u32 fb_imgType;
+
+ int hw_refresh;
+
+ int overlay_play_enable;
+
+ int panel_power_on;
+ struct disp_info_type_suspend suspend;
+
+ int (*on_fnc) (struct msm_fb_data_type *mfd);
+ int (*off_fnc) (struct msm_fb_data_type *mfd);
+ int (*kickoff_fnc) (struct mdss_mdp_ctl *ctl);
+ int (*ioctl_handler) (struct msm_fb_data_type *mfd, u32 cmd, void *arg);
+ void (*dma_fnc) (struct msm_fb_data_type *mfd);
+ int (*cursor_update) (struct fb_info *info,
+ struct fb_cursor *cursor);
+ int (*lut_update) (struct fb_info *info,
+ struct fb_cmap *cmap);
+ int (*do_histogram) (struct fb_info *info,
+ struct mdp_histogram *hist);
+ void *cursor_buf;
+ void *cursor_buf_phys;
+
+ u32 bl_level;
+ struct mutex lock;
+
+ struct platform_device *pdev;
+
+ u32 var_xres;
+ u32 var_yres;
+ u32 var_pixclock;
+
+ u32 mdp_fb_page_protection;
+ struct ion_client *iclient;
+
+ struct mdss_mdp_ctl *ctl;
+};
+
+int mdss_fb_get_phys_info(unsigned long *start, unsigned long *len, int fb_num);
+void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl);
+void mdss_fb_update_backlight(struct msm_fb_data_type *mfd);
+#endif /* MDSS_FB_H */
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
new file mode 100644
index 0000000..41e0c18
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -0,0 +1,822 @@
+/*
+ * MDSS MDP Interface (used by framebuffer core)
+ *
+ * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (C) 2007 Google Incorporated
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/hrtimer.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/memory_alloc.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/spinlock.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+
+#include <mach/board.h>
+#include <mach/clk.h>
+#include <mach/hardware.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+
+#include "mdss.h"
+#include "mdss_fb.h"
+#include "mdss_mdp.h"
+
+unsigned char *mdss_reg_base;
+
+struct mdss_res_type *mdss_res;
+static struct msm_panel_common_pdata *mdp_pdata;
+
+static DEFINE_SPINLOCK(mdp_lock);
+static DEFINE_MUTEX(mdp_clk_lock);
+static DEFINE_MUTEX(mdp_suspend_mutex);
+
+u32 mdss_mdp_pipe_type_map[MDSS_MDP_MAX_SSPP] = {
+ MDSS_MDP_PIPE_TYPE_VIG,
+ MDSS_MDP_PIPE_TYPE_VIG,
+ MDSS_MDP_PIPE_TYPE_VIG,
+ MDSS_MDP_PIPE_TYPE_RGB,
+ MDSS_MDP_PIPE_TYPE_RGB,
+ MDSS_MDP_PIPE_TYPE_RGB,
+ MDSS_MDP_PIPE_TYPE_DMA,
+ MDSS_MDP_PIPE_TYPE_DMA,
+};
+
+u32 mdss_mdp_mixer_type_map[MDSS_MDP_MAX_LAYERMIXER] = {
+ MDSS_MDP_MIXER_TYPE_INTF,
+ MDSS_MDP_MIXER_TYPE_INTF,
+ MDSS_MDP_MIXER_TYPE_INTF,
+ MDSS_MDP_MIXER_TYPE_WRITEBACK,
+ MDSS_MDP_MIXER_TYPE_WRITEBACK,
+};
+
+#define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val) \
+ { \
+ .src = MSM_BUS_MASTER_MDP_PORT0, \
+ .dst = MSM_BUS_SLAVE_EBI_CH0, \
+ .ab = (ab_val), \
+ .ib = (ib_val), \
+ }
+
+#define MDP_BUS_VECTOR_ENTRY_NDX(n) \
+ MDP_BUS_VECTOR_ENTRY((n) * 100000000, (n) * 200000000)
+
+static struct msm_bus_vectors mdp_bus_vectors[] = {
+ MDP_BUS_VECTOR_ENTRY_NDX(0),
+ MDP_BUS_VECTOR_ENTRY_NDX(1),
+ MDP_BUS_VECTOR_ENTRY_NDX(2),
+ MDP_BUS_VECTOR_ENTRY_NDX(3),
+ MDP_BUS_VECTOR_ENTRY_NDX(4),
+ MDP_BUS_VECTOR_ENTRY_NDX(5),
+ MDP_BUS_VECTOR_ENTRY_NDX(6),
+ MDP_BUS_VECTOR_ENTRY_NDX(7),
+ MDP_BUS_VECTOR_ENTRY_NDX(8),
+ MDP_BUS_VECTOR_ENTRY_NDX(9),
+ MDP_BUS_VECTOR_ENTRY_NDX(10),
+ MDP_BUS_VECTOR_ENTRY(200000000, 200000000)
+};
+static struct msm_bus_paths mdp_bus_usecases[ARRAY_SIZE(mdp_bus_vectors)];
+static struct msm_bus_scale_pdata mdp_bus_scale_table = {
+ .usecase = mdp_bus_usecases,
+ .num_usecases = ARRAY_SIZE(mdp_bus_usecases),
+ .name = "mdss_mdp",
+};
+
+struct mdss_hw mdss_mdp_hw = {
+ .hw_ndx = MDSS_HW_MDP,
+ .irq_handler = mdss_mdp_isr,
+};
+
+static DEFINE_SPINLOCK(mdss_lock);
+struct mdss_hw *mdss_irq_handlers[MDSS_MAX_HW_BLK];
+
+static inline int mdss_irq_dispatch(u32 hw_ndx, int irq, void *ptr)
+{
+ struct mdss_hw *hw;
+
+ spin_lock(&mdss_lock);
+ hw = mdss_irq_handlers[hw_ndx];
+ spin_unlock(&mdss_lock);
+ if (hw)
+ return hw->irq_handler(irq, ptr);
+
+ return -ENODEV;
+}
+
+static irqreturn_t mdss_irq_handler(int irq, void *ptr)
+{
+ u32 intr = MDSS_MDP_REG_READ(MDSS_REG_HW_INTR_STATUS);
+
+ mdss_res->irq_buzy = true;
+
+ if (intr & MDSS_INTR_MDP)
+ mdss_irq_dispatch(MDSS_HW_MDP, irq, ptr);
+
+ if (intr & MDSS_INTR_DSI0)
+ mdss_irq_dispatch(MDSS_HW_DSI0, irq, ptr);
+
+ if (intr & MDSS_INTR_DSI1)
+ mdss_irq_dispatch(MDSS_HW_DSI1, irq, ptr);
+
+ if (intr & MDSS_INTR_EDP)
+ mdss_irq_dispatch(MDSS_HW_EDP, irq, ptr);
+
+ if (intr & MDSS_INTR_HDMI)
+ mdss_irq_dispatch(MDSS_HW_HDMI, irq, ptr);
+
+ mdss_res->irq_buzy = false;
+
+ return IRQ_HANDLED;
+}
+
+
+void mdss_enable_irq(struct mdss_hw *hw)
+{
+ unsigned long irq_flags;
+ u32 ndx_bit;
+
+ if (hw->hw_ndx >= MDSS_MAX_HW_BLK)
+ return;
+
+ ndx_bit = BIT(hw->hw_ndx);
+
+ pr_debug("Enable HW=%d irq ena=%d mask=%x\n", hw->hw_ndx,
+ mdss_res->irq_ena, mdss_res->irq_mask);
+
+ spin_lock_irqsave(&mdss_lock, irq_flags);
+ if (mdss_res->irq_mask & ndx_bit) {
+ pr_debug("MDSS HW ndx=%d is already set, mask=%x\n",
+ hw->hw_ndx, mdss_res->irq_mask);
+ } else {
+ mdss_irq_handlers[hw->hw_ndx] = hw;
+ mdss_res->irq_mask |= ndx_bit;
+ if (!mdss_res->irq_ena) {
+ mdss_res->irq_ena = true;
+ enable_irq(mdss_res->irq);
+ }
+ }
+ spin_unlock_irqrestore(&mdss_lock, irq_flags);
+}
+EXPORT_SYMBOL(mdss_enable_irq);
+
+void mdss_disable_irq(struct mdss_hw *hw)
+{
+ unsigned long irq_flags;
+ u32 ndx_bit;
+
+ if (hw->hw_ndx >= MDSS_MAX_HW_BLK)
+ return;
+
+ ndx_bit = BIT(hw->hw_ndx);
+
+ pr_debug("Disable HW=%d irq ena=%d mask=%x\n", hw->hw_ndx,
+ mdss_res->irq_ena, mdss_res->irq_mask);
+
+ spin_lock_irqsave(&mdss_lock, irq_flags);
+ if (!(mdss_res->irq_mask & ndx_bit)) {
+ pr_warn("MDSS HW ndx=%d is NOT set, mask=%x\n",
+ hw->hw_ndx, mdss_res->mdp_irq_mask);
+ } else {
+ mdss_irq_handlers[hw->hw_ndx] = NULL;
+ mdss_res->irq_mask &= ~ndx_bit;
+ if (mdss_res->irq_mask == 0) {
+ mdss_res->irq_ena = false;
+ disable_irq(mdss_res->irq);
+ }
+ }
+ spin_unlock_irqrestore(&mdss_lock, irq_flags);
+}
+EXPORT_SYMBOL(mdss_disable_irq);
+
+void mdss_disable_irq_nosync(struct mdss_hw *hw)
+{
+ u32 ndx_bit;
+
+ if (hw->hw_ndx >= MDSS_MAX_HW_BLK)
+ return;
+
+ ndx_bit = BIT(hw->hw_ndx);
+
+ pr_debug("Disable HW=%d irq ena=%d mask=%x\n", hw->hw_ndx,
+ mdss_res->irq_ena, mdss_res->irq_mask);
+
+ spin_lock(&mdss_lock);
+ if (!(mdss_res->irq_mask & ndx_bit)) {
+ pr_warn("MDSS HW ndx=%d is NOT set, mask=%x\n",
+ hw->hw_ndx, mdss_res->mdp_irq_mask);
+ } else {
+ mdss_irq_handlers[hw->hw_ndx] = NULL;
+ mdss_res->irq_mask &= ~ndx_bit;
+ if (mdss_res->irq_mask == 0) {
+ mdss_res->irq_ena = false;
+ disable_irq_nosync(mdss_res->irq);
+ }
+ }
+ spin_unlock(&mdss_lock);
+}
+EXPORT_SYMBOL(mdss_disable_irq_nosync);
+
+static int mdss_mdp_bus_scale_register(void)
+{
+ if (!mdss_res->bus_hdl) {
+ struct msm_bus_scale_pdata *bus_pdata = &mdp_bus_scale_table;
+ int i;
+
+ for (i = 0; i < bus_pdata->num_usecases; i++) {
+ mdp_bus_usecases[i].num_paths = 1;
+ mdp_bus_usecases[i].vectors = &mdp_bus_vectors[i];
+ }
+
+ mdss_res->bus_hdl = msm_bus_scale_register_client(bus_pdata);
+ if (!mdss_res->bus_hdl) {
+ pr_err("not able to get bus scale\n");
+ return -ENOMEM;
+ }
+
+ pr_debug("register bus_hdl=%x\n", mdss_res->bus_hdl);
+ }
+ return 0;
+}
+
+static void mdss_mdp_bus_scale_unregister(void)
+{
+ pr_debug("unregister bus_hdl=%x\n", mdss_res->bus_hdl);
+
+ if (mdss_res->bus_hdl)
+ msm_bus_scale_unregister_client(mdss_res->bus_hdl);
+}
+
+int mdss_mdp_bus_scale_set_min_quota(u32 quota)
+{
+ struct msm_bus_scale_pdata *bus_pdata = &mdp_bus_scale_table;
+ struct msm_bus_vectors *vect = NULL;
+ int lvl;
+
+ if (mdss_res->bus_hdl < 1) {
+ pr_err("invalid bus handle %d\n", mdss_res->bus_hdl);
+ return -EINVAL;
+ }
+
+ for (lvl = 0; lvl < bus_pdata->num_usecases; lvl++) {
+ if (bus_pdata->usecase[lvl].num_paths) {
+ vect = &bus_pdata->usecase[lvl].vectors[0];
+ if (vect->ab >= quota) {
+ pr_debug("lvl=%d quota=%u ab=%u\n", lvl, quota,
+ vect->ab);
+ break;
+ }
+ }
+ }
+
+ if (lvl == bus_pdata->num_usecases) {
+ pr_warn("cannot match quota=%u try with max level\n", quota);
+ lvl--;
+ }
+
+ return msm_bus_scale_client_update_request(mdss_res->bus_hdl, lvl);
+}
+
+static inline u32 mdss_mdp_irq_mask(u32 intr_type, u32 intf_num)
+{
+ if (intr_type == MDSS_MDP_IRQ_INTF_UNDER_RUN ||
+ intr_type == MDSS_MDP_IRQ_INTF_VSYNC)
+ intf_num = (intf_num - MDSS_MDP_INTF0) * 2;
+ return 1 << (intr_type + intf_num);
+}
+
+int mdss_mdp_irq_enable(u32 intr_type, u32 intf_num)
+{
+ u32 irq;
+ unsigned long irq_flags;
+ int ret = 0;
+
+ irq = mdss_mdp_irq_mask(intr_type, intf_num);
+
+ spin_lock_irqsave(&mdp_lock, irq_flags);
+ if (mdss_res->mdp_irq_mask & irq) {
+ pr_warn("MDSS MDP IRQ-0x%x is already set, mask=%x\n",
+ irq, mdss_res->mdp_irq_mask);
+ ret = -EBUSY;
+ } else {
+ pr_debug("MDP IRQ mask old=%x new=%x\n",
+ mdss_res->mdp_irq_mask, irq);
+ mdss_res->mdp_irq_mask |= irq;
+ MDSS_MDP_REG_WRITE(MDSS_MDP_REG_INTR_CLEAR, irq);
+ MDSS_MDP_REG_WRITE(MDSS_MDP_REG_INTR_EN,
+ mdss_res->mdp_irq_mask);
+ mdss_enable_irq(&mdss_mdp_hw);
+ }
+ spin_unlock_irqrestore(&mdp_lock, irq_flags);
+
+ return ret;
+}
+
+void mdss_mdp_irq_disable(u32 intr_type, u32 intf_num)
+{
+ u32 irq;
+ unsigned long irq_flags;
+
+ irq = mdss_mdp_irq_mask(intr_type, intf_num);
+
+ spin_lock_irqsave(&mdp_lock, irq_flags);
+ if (!(mdss_res->mdp_irq_mask & irq)) {
+ pr_warn("MDSS MDP IRQ-%x is NOT set, mask=%x\n",
+ irq, mdss_res->mdp_irq_mask);
+ } else {
+ mdss_res->mdp_irq_mask &= ~irq;
+ MDSS_MDP_REG_WRITE(MDSS_MDP_REG_INTR_EN,
+ mdss_res->mdp_irq_mask);
+ mdss_disable_irq(&mdss_mdp_hw);
+ }
+ spin_unlock_irqrestore(&mdp_lock, irq_flags);
+}
+
+void mdss_mdp_irq_disable_nosync(u32 intr_type, u32 intf_num)
+{
+ u32 irq;
+
+ irq = mdss_mdp_irq_mask(intr_type, intf_num);
+
+ spin_lock(&mdp_lock);
+ if (!(mdss_res->mdp_irq_mask & irq)) {
+ pr_warn("MDSS MDP IRQ-%x is NOT set, mask=%x\n",
+ irq, mdss_res->mdp_irq_mask);
+ } else {
+ mdss_res->mdp_irq_mask &= ~irq;
+ MDSS_MDP_REG_WRITE(MDSS_MDP_REG_INTR_EN,
+ mdss_res->mdp_irq_mask);
+ mdss_disable_irq_nosync(&mdss_mdp_hw);
+ }
+ spin_unlock(&mdp_lock);
+}
+
+static inline struct clk *mdss_mdp_get_clk(u32 clk_idx)
+{
+ if (clk_idx < MDSS_MAX_CLK)
+ return mdss_res->mdp_clk[clk_idx];
+ return NULL;
+}
+
+static int mdss_mdp_clk_update(u32 clk_idx, u32 enable)
+{
+ int ret = -ENODEV;
+ struct clk *clk = mdss_mdp_get_clk(clk_idx);
+
+ if (clk) {
+ pr_debug("clk=%d en=%d\n", clk_idx, enable);
+ if (enable) {
+ ret = clk_prepare_enable(clk);
+ } else {
+ clk_disable_unprepare(clk);
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+int mdss_mdp_vsync_clk_enable(int enable)
+{
+ int ret = 0;
+ pr_debug("clk enable=%d\n", enable);
+ mutex_lock(&mdp_clk_lock);
+ if (mdss_res->vsync_ena != enable) {
+ mdss_res->vsync_ena = enable;
+ ret = mdss_mdp_clk_update(MDSS_CLK_MDP_VSYNC, enable);
+ }
+ mutex_unlock(&mdp_clk_lock);
+ return ret;
+}
+
+void mdss_mdp_set_clk_rate(unsigned long min_clk_rate)
+{
+ unsigned long clk_rate;
+ struct clk *clk = mdss_mdp_get_clk(MDSS_CLK_MDP_SRC);
+ if (clk) {
+ mutex_lock(&mdp_clk_lock);
+ clk_rate = clk_round_rate(clk, min_clk_rate);
+ if (IS_ERR_VALUE(clk_rate)) {
+ pr_err("unable to round rate err=%ld\n", clk_rate);
+ } else if (clk_rate != clk_get_rate(clk)) {
+ if (IS_ERR_VALUE(clk_set_rate(clk, clk_rate)))
+ pr_err("clk_set_rate failed\n");
+ else
+ pr_debug("mdp clk rate=%lu\n", clk_rate);
+ }
+ mutex_unlock(&mdp_clk_lock);
+ }
+}
+
+unsigned long mdss_mdp_get_clk_rate(u32 clk_idx)
+{
+ unsigned long clk_rate = 0;
+ struct clk *clk = mdss_mdp_get_clk(clk_idx);
+ mutex_lock(&mdp_clk_lock);
+ if (clk)
+ clk_rate = clk_get_rate(clk);
+ mutex_unlock(&mdp_clk_lock);
+
+ return clk_rate;
+}
+
+static void mdss_mdp_clk_ctrl_update(int enable)
+{
+ if (mdss_res->clk_ena == enable)
+ return;
+
+ pr_debug("MDP CLKS %s\n", (enable ? "Enable" : "Disable"));
+
+ mutex_lock(&mdp_clk_lock);
+ mdss_res->clk_ena = enable;
+ mb();
+
+ mdss_mdp_clk_update(MDSS_CLK_AHB, enable);
+ mdss_mdp_clk_update(MDSS_CLK_AXI, enable);
+
+ mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, enable);
+ mdss_mdp_clk_update(MDSS_CLK_MDP_LUT, enable);
+ if (mdss_res->vsync_ena)
+ mdss_mdp_clk_update(MDSS_CLK_MDP_VSYNC, enable);
+
+ mutex_unlock(&mdp_clk_lock);
+}
+
+static void mdss_mdp_clk_ctrl_workqueue_handler(struct work_struct *work)
+{
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+}
+
+void mdss_mdp_clk_ctrl(int enable, int isr)
+{
+ static atomic_t clk_ref = ATOMIC_INIT(0);
+ static DEFINE_MUTEX(clk_ctrl_lock);
+ int force_off = 0;
+
+ pr_debug("clk enable=%d isr=%d clk_ref=%d\n", enable, isr,
+ atomic_read(&clk_ref));
+ /*
+ * It is assumed that if isr = TRUE then start = OFF
+ * if start = ON when isr = TRUE it could happen that the usercontext
+ * could turn off the clocks while the interrupt is updating the
+ * power to ON
+ */
+ WARN_ON(isr == true && enable);
+
+ if (enable) {
+ atomic_inc(&clk_ref);
+ } else if (!atomic_add_unless(&clk_ref, -1, 0)) {
+ pr_debug("master power-off req\n");
+ force_off = 1;
+ }
+
+ if (isr) {
+ /* if it's power off send workqueue to turn off clocks */
+ if (mdss_res->clk_ena && !atomic_read(&clk_ref))
+ queue_delayed_work(mdss_res->clk_ctrl_wq,
+ &mdss_res->clk_ctrl_worker,
+ mdss_res->timeout);
+ } else {
+ mutex_lock(&clk_ctrl_lock);
+ if (delayed_work_pending(&mdss_res->clk_ctrl_worker))
+ cancel_delayed_work(&mdss_res->clk_ctrl_worker);
+
+ if (atomic_read(&clk_ref)) {
+ mdss_mdp_clk_ctrl_update(true);
+ } else if (mdss_res->clk_ena) {
+ mutex_lock(&mdp_suspend_mutex);
+ if (force_off || mdss_res->suspend) {
+ mdss_mdp_clk_ctrl_update(false);
+ } else {
+ /* send workqueue to turn off mdp power */
+ queue_delayed_work(mdss_res->clk_ctrl_wq,
+ &mdss_res->clk_ctrl_worker,
+ mdss_res->timeout);
+ }
+ mutex_unlock(&mdp_suspend_mutex);
+ }
+ mutex_unlock(&clk_ctrl_lock);
+ }
+}
+
+static inline int mdss_mdp_irq_clk_register(struct platform_device *pdev,
+ char *clk_name, int clk_idx)
+{
+ struct clk *tmp;
+ if (clk_idx >= MDSS_MAX_CLK) {
+ pr_err("invalid clk index %d\n", clk_idx);
+ return -EINVAL;
+ }
+
+
+ tmp = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(tmp)) {
+ pr_err("unable to get clk: %s\n", clk_name);
+ return PTR_ERR(tmp);
+ }
+
+ mdss_res->mdp_clk[clk_idx] = tmp;
+ return 0;
+}
+
+static int mdss_mdp_irq_clk_setup(struct platform_device *pdev)
+{
+ int ret;
+ int i;
+
+ ret = request_irq(mdss_res->irq, mdss_irq_handler, IRQF_DISABLED,
+ "MDSS", 0);
+ if (ret) {
+ pr_err("mdp request_irq() failed!\n");
+ return ret;
+ }
+ disable_irq(mdss_res->irq);
+
+ mdss_res->fs = regulator_get(NULL, "gdsc_mdss");
+ if (IS_ERR_OR_NULL(mdss_res->fs)) {
+ mdss_res->fs = NULL;
+ pr_err("unable to get gdsc_mdss regulator\n");
+ goto error;
+ }
+ regulator_enable(mdss_res->fs);
+
+ if (mdss_mdp_irq_clk_register(pdev, "bus_clk", MDSS_CLK_AXI) ||
+ mdss_mdp_irq_clk_register(pdev, "iface_clk", MDSS_CLK_AHB) ||
+ mdss_mdp_irq_clk_register(pdev, "core_clk_src", MDSS_CLK_MDP_SRC) ||
+ mdss_mdp_irq_clk_register(pdev, "core_clk", MDSS_CLK_MDP_CORE) ||
+ mdss_mdp_irq_clk_register(pdev, "lut_clk", MDSS_CLK_MDP_LUT) ||
+ mdss_mdp_irq_clk_register(pdev, "vsync_clk", MDSS_CLK_MDP_VSYNC))
+ goto error;
+
+ mdss_mdp_set_clk_rate(MDP_CLK_DEFAULT_RATE);
+ pr_debug("mdp clk rate=%ld\n", mdss_mdp_get_clk_rate(MDSS_CLK_MDP_SRC));
+
+ return 0;
+error:
+ for (i = 0; i < MDSS_MAX_CLK; i++) {
+ if (mdss_res->mdp_clk[i])
+ clk_put(mdss_res->mdp_clk[i]);
+ }
+ if (mdss_res->fs)
+ regulator_put(mdss_res->fs);
+ if (mdss_res->irq)
+ free_irq(mdss_res->irq, 0);
+
+ return -EINVAL;
+
+}
+
+static struct msm_panel_common_pdata *mdss_mdp_populate_pdata(
+ struct device *dev)
+{
+ struct msm_panel_common_pdata *pdata;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ dev_err(dev, "could not allocate memory for pdata\n");
+ return pdata;
+}
+
+static u32 mdss_mdp_res_init(struct platform_device *pdev)
+{
+ u32 rc;
+
+ rc = mdss_mdp_irq_clk_setup(pdev);
+ if (rc)
+ return rc;
+
+ mdss_res->clk_ctrl_wq = create_singlethread_workqueue("mdp_clk_wq");
+ INIT_DELAYED_WORK(&mdss_res->clk_ctrl_worker,
+ mdss_mdp_clk_ctrl_workqueue_handler);
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ mdss_res->rev = MDSS_MDP_REG_READ(MDSS_REG_HW_VERSION);
+ mdss_res->mdp_rev = MDSS_MDP_REG_READ(MDSS_MDP_REG_HW_VERSION);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+
+ mdss_res->smp_mb_cnt = MDSS_MDP_SMP_MMB_BLOCKS;
+ mdss_res->smp_mb_size = MDSS_MDP_SMP_MMB_SIZE;
+ mdss_res->pipe_type_map = mdss_mdp_pipe_type_map;
+ mdss_res->mixer_type_map = mdss_mdp_mixer_type_map;
+
+ pr_info("mdss_revision=%x\n", mdss_res->rev);
+ pr_info("mdp_hw_revision=%x\n", mdss_res->mdp_rev);
+
+ mdss_res->res_init = true;
+ mdss_res->timeout = HZ/20;
+ mdss_res->clk_ena = false;
+ mdss_res->irq_mask = MDSS_MDP_DEFAULT_INTR_MASK;
+ mdss_res->suspend = false;
+ mdss_res->prim_ptype = NO_PANEL;
+ mdss_res->irq_ena = false;
+
+ return 0;
+}
+
+static int mdss_mdp_probe(struct platform_device *pdev)
+{
+ struct resource *mdss_mdp_mres;
+ struct resource *mdss_mdp_ires;
+ resource_size_t size;
+ int rc;
+
+ if (!mdss_res) {
+ mdss_res = devm_kzalloc(&pdev->dev, sizeof(*mdss_res),
+ GFP_KERNEL);
+ if (mdss_res == NULL)
+ return -ENOMEM;
+ }
+
+ if (pdev->dev.of_node) {
+ pdev->id = 0;
+ mdp_pdata = mdss_mdp_populate_pdata(&pdev->dev);
+ mdss_mdp_mres = platform_get_resource(pdev,
+ IORESOURCE_MEM, 0);
+ mdss_mdp_ires = platform_get_resource(pdev,
+ IORESOURCE_IRQ, 0);
+ if (!mdss_mdp_mres || !mdss_mdp_ires) {
+ pr_err("unable to get the MDSS resources");
+ rc = -ENOMEM;
+ goto probe_done;
+ }
+ mdss_reg_base = ioremap(mdss_mdp_mres->start,
+ resource_size(mdss_mdp_mres));
+
+ pr_info("MDP HW Base phy_Address=0x%x virt=0x%x\n",
+ (int) mdss_mdp_mres->start,
+ (int) mdss_reg_base);
+
+ mdss_res->irq = mdss_mdp_ires->start;
+ } else if ((pdev->id == 0) && (pdev->num_resources > 0)) {
+ mdp_pdata = pdev->dev.platform_data;
+
+ size = resource_size(&pdev->resource[0]);
+ mdss_reg_base = ioremap(pdev->resource[0].start, size);
+
+ pr_info("MDP HW Base phy_Address=0x%x virt=0x%x\n",
+ (int) pdev->resource[0].start,
+ (int) mdss_reg_base);
+
+ mdss_res->irq = platform_get_irq(pdev, 0);
+ if (mdss_res->irq < 0) {
+ pr_err("can not get mdp irq\n");
+ rc = -ENOMEM;
+ goto probe_done;
+ }
+ }
+
+ if (unlikely(!mdss_reg_base)) {
+ rc = -ENOMEM;
+ goto probe_done;
+ }
+
+ rc = mdss_mdp_res_init(pdev);
+ if (rc) {
+ pr_err("unable to initialize mdss mdp resources\n");
+ goto probe_done;
+ }
+ rc = mdss_mdp_bus_scale_register();
+probe_done:
+ if (IS_ERR_VALUE(rc)) {
+ if (mdss_res) {
+ devm_kfree(&pdev->dev, mdss_res);
+ mdss_res = NULL;
+ }
+ }
+
+ return rc;
+}
+
+void mdss_mdp_footswitch_ctrl(int on)
+{
+ mutex_lock(&mdp_suspend_mutex);
+ if (!mdss_res->suspend || mdss_res->eintf_ena || !mdss_res->fs) {
+ mutex_unlock(&mdp_suspend_mutex);
+ return;
+ }
+
+ if (on && !mdss_res->fs_ena) {
+ pr_debug("Enable MDP FS\n");
+ regulator_enable(mdss_res->fs);
+ mdss_res->fs_ena = true;
+ } else if (!on && mdss_res->fs_ena) {
+ pr_debug("Disable MDP FS\n");
+ regulator_disable(mdss_res->fs);
+ mdss_res->fs_ena = false;
+ }
+ mutex_unlock(&mdp_suspend_mutex);
+}
+
+#ifdef CONFIG_PM
+static void mdss_mdp_suspend_sub(void)
+{
+ cancel_delayed_work(&mdss_res->clk_ctrl_worker);
+
+ flush_workqueue(mdss_res->clk_ctrl_wq);
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+
+ mutex_lock(&mdp_suspend_mutex);
+ mdss_res->suspend = true;
+ mutex_unlock(&mdp_suspend_mutex);
+}
+
+static int mdss_mdp_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ if (pdev->id == 0) {
+ mdss_mdp_suspend_sub();
+ if (mdss_res->clk_ena) {
+ pr_err("MDP suspend failed\n");
+ return -EBUSY;
+ }
+ mdss_mdp_footswitch_ctrl(false);
+ }
+ return 0;
+}
+
+static int mdss_mdp_resume(struct platform_device *pdev)
+{
+ mdss_mdp_footswitch_ctrl(true);
+ mutex_lock(&mdp_suspend_mutex);
+ mdss_res->suspend = false;
+ mutex_unlock(&mdp_suspend_mutex);
+ return 0;
+}
+#else
+#define mdss_mdp_suspend NULL
+#define mdss_mdp_resume NULL
+#endif
+
+static int mdss_mdp_remove(struct platform_device *pdev)
+{
+ if (mdss_res->fs != NULL)
+ regulator_put(mdss_res->fs);
+ iounmap(mdss_reg_base);
+ pm_runtime_disable(&pdev->dev);
+ mdss_mdp_bus_scale_unregister();
+ return 0;
+}
+
+static const struct of_device_id mdss_mdp_dt_match[] = {
+ { .compatible = "qcom,mdss_mdp",},
+};
+MODULE_DEVICE_TABLE(of, mdss_mdp_dt_match);
+
+static struct platform_driver mdss_mdp_driver = {
+ .probe = mdss_mdp_probe,
+ .remove = mdss_mdp_remove,
+ .suspend = mdss_mdp_suspend,
+ .resume = mdss_mdp_resume,
+ .shutdown = NULL,
+ .driver = {
+ /*
+ * Driver name must match the device name added in
+ * platform.c.
+ */
+ .name = "mdp",
+ .of_match_table = mdss_mdp_dt_match,
+ },
+};
+
+static int mdss_mdp_register_driver(void)
+{
+ return platform_driver_register(&mdss_mdp_driver);
+}
+
+static int __init mdss_mdp_driver_init(void)
+{
+ int ret;
+
+ ret = mdss_mdp_register_driver();
+ if (ret) {
+ pr_err("mdp_register_driver() failed!\n");
+ return ret;
+ }
+
+ return 0;
+
+}
+
+module_init(mdss_mdp_driver_init);
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
new file mode 100644
index 0000000..2cdd9f66
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -0,0 +1,316 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 MDSS_MDP_H
+#define MDSS_MDP_H
+
+#include <linux/io.h>
+#include <linux/msm_mdp.h>
+#include <linux/platform_device.h>
+
+#include "mdss.h"
+#include "mdss_mdp_hwio.h"
+
+#define MDSS_MDP_DEFAULT_INTR_MASK 0
+#define MDSS_MDP_CURSOR_WIDTH 64
+#define MDSS_MDP_CURSOR_HEIGHT 64
+#define MDSS_MDP_CURSOR_SIZE (MDSS_MDP_CURSOR_WIDTH*MDSS_MDP_CURSOR_WIDTH*4)
+
+#define MDP_CLK_DEFAULT_RATE 37500000
+#define PHASE_STEP_SHIFT 21
+#define MAX_MIXER_WIDTH 2048
+#define MAX_MIXER_HEIGHT 2048
+#define MAX_IMG_WIDTH 0x3FFF
+#define MAX_IMG_HEIGHT 0x3FFF
+#define MIN_DST_W 10
+#define MIN_DST_H 10
+#define MAX_DST_W MAX_MIXER_WIDTH
+#define MAX_DST_H MAX_MIXER_HEIGHT
+#define MAX_PLANES 4
+#define MAX_DOWNSCALE_RATIO 4
+#define MAX_UPSCALE_RATIO 20
+
+#ifdef MDSS_MDP_DEBUG_REG
+static inline void mdss_mdp_reg_write(u32 addr, u32 val)
+{
+ pr_debug("0x%05X = 0x%08X\n", addr, val);
+ MDSS_REG_WRITE(addr, val);
+}
+#define MDSS_MDP_REG_WRITE(addr, val) mdss_mdp_reg_write((u32)addr, (u32)(val))
+static inline u32 mdss_mdp_reg_read(u32 addr)
+{
+ u32 val;
+ val = MDSS_REG_READ(addr);
+ pr_debug("0x%05X = 0x%08X\n", addr, val);
+ return val;
+}
+#define MDSS_MDP_REG_READ(addr) mdss_mdp_reg_read((u32)(addr))
+#else
+#define MDSS_MDP_REG_WRITE(addr, val) MDSS_REG_WRITE((u32)(addr), (u32)(val))
+#define MDSS_MDP_REG_READ(addr) MDSS_REG_READ((u32)(addr))
+#endif
+
+enum mdss_mdp_block_power_state {
+ MDP_BLOCK_POWER_OFF,
+ MDP_BLOCK_POWER_ON
+};
+
+enum mdss_mdp_mixer_type {
+ MDSS_MDP_MIXER_TYPE_UNUSED,
+ MDSS_MDP_MIXER_TYPE_INTF,
+ MDSS_MDP_MIXER_TYPE_WRITEBACK,
+};
+
+enum mdss_mdp_mixer_mux {
+ MDSS_MDP_MIXER_MUX_DEFAULT,
+ MDSS_MDP_MIXER_MUX_LEFT,
+ MDSS_MDP_MIXER_MUX_RIGHT,
+};
+
+enum mdss_mdp_pipe_type {
+ MDSS_MDP_PIPE_TYPE_UNUSED,
+ MDSS_MDP_PIPE_TYPE_VIG,
+ MDSS_MDP_PIPE_TYPE_RGB,
+ MDSS_MDP_PIPE_TYPE_DMA,
+};
+
+enum mdss_mdp_block_type {
+ MDSS_MDP_BLOCK_UNUSED,
+ MDSS_MDP_BLOCK_SSPP,
+ MDSS_MDP_BLOCK_MIXER,
+ MDSS_MDP_BLOCK_DSPP,
+ MDSS_MDP_BLOCK_WB,
+ MDSS_MDP_BLOCK_MAX
+};
+
+enum mdss_mdp_csc_type {
+ MDSS_MDP_CSC_RGB2RGB,
+ MDSS_MDP_CSC_YUV2RGB,
+ MDSS_MDP_CSC_RGB2YUV,
+ MDSS_MDP_CSC_YUV2YUV,
+ MDSS_MDP_MAX_CSC
+};
+
+struct mdss_mdp_ctl {
+ u32 num;
+ u32 ref_cnt;
+ int power_on;
+
+ u32 intf_num;
+ u32 intf_type;
+
+ u32 opmode;
+ u32 flush_bits;
+
+ u32 play_cnt;
+
+ u16 width;
+ u16 height;
+ u32 dst_format;
+
+ u32 bus_quota;
+
+ struct msm_fb_data_type *mfd;
+ struct mdss_mdp_mixer *mixer_left;
+ struct mdss_mdp_mixer *mixer_right;
+ struct mutex lock;
+
+ int (*start_fnc) (struct mdss_mdp_ctl *ctl);
+ int (*stop_fnc) (struct mdss_mdp_ctl *ctl);
+ int (*prepare_fnc) (struct mdss_mdp_ctl *ctl, void *arg);
+ int (*display_fnc) (struct mdss_mdp_ctl *ctl, void *arg);
+
+ void *priv_data;
+};
+
+struct mdss_mdp_mixer {
+ u32 num;
+ u32 ref_cnt;
+ u8 type;
+ u8 params_changed;
+
+ u16 width;
+ u16 height;
+ u8 cursor_enabled;
+ u8 rotator_mode;
+
+ u32 bus_quota;
+
+ struct mdss_mdp_ctl *ctl;
+ struct mdss_mdp_pipe *stage_pipe[MDSS_MDP_MAX_STAGE];
+};
+
+struct mdss_mdp_img_rect {
+ u16 x;
+ u16 y;
+ u16 w;
+ u16 h;
+};
+
+struct mdss_mdp_format_params {
+ u32 format;
+ u8 is_yuv;
+
+ u8 frame_format;
+ u8 chroma_sample;
+ u8 solid_fill;
+ u8 fetch_planes;
+ u8 unpack_align_msb; /* 0 to LSB, 1 to MSB */
+ u8 unpack_tight; /* 0 for loose, 1 for tight */
+ u8 unpack_count; /* 0 = 1 component, 1 = 2 component ... */
+ u8 bpp;
+ u8 alpha_enable; /* source has alpha */
+
+ /*
+ * number of bits for source component,
+ * 0 = 1 bit, 1 = 2 bits, 2 = 6 bits, 3 = 8 bits
+ */
+ u8 a_bit; /* component 3, alpha */
+ u8 r_bit; /* component 2, R_Cr */
+ u8 b_bit; /* component 1, B_Cb */
+ u8 g_bit; /* component 0, G_lumz */
+
+ /*
+ * unpack pattern
+ * A = C3, R = C2, B = C1, G = C0
+ */
+ u8 element3;
+ u8 element2;
+ u8 element1;
+ u8 element0;
+};
+
+struct mdss_mdp_plane_sizes {
+ u32 num_planes;
+ u32 plane_size[MAX_PLANES];
+ u32 total_size;
+ u32 ystride[MAX_PLANES];
+};
+
+struct mdss_mdp_img_data {
+ u32 addr;
+ u32 len;
+ u32 flags;
+ int p_need;
+ struct file *srcp_file;
+ struct ion_handle *srcp_ihdl;
+ struct ion_client *iclient;
+};
+
+struct mdss_mdp_data {
+ u8 num_planes;
+ u8 bwc_enabled;
+ struct mdss_mdp_img_data p[MAX_PLANES];
+};
+
+struct mdss_mdp_pipe {
+ u32 num;
+ u32 type;
+ u32 ndx;
+ atomic_t ref_cnt;
+ u32 play_cnt;
+
+ u32 flags;
+ u32 bwc_mode;
+
+ u16 img_width;
+ u16 img_height;
+ struct mdss_mdp_img_rect src;
+ struct mdss_mdp_img_rect dst;
+
+ struct mdss_mdp_format_params *src_fmt;
+ struct mdss_mdp_plane_sizes src_planes;
+
+ u32 bus_quota;
+ u8 mixer_stage;
+ u8 is_fg;
+ u8 alpha;
+ u32 transp;
+
+ struct msm_fb_data_type *mfd;
+ struct mdss_mdp_mixer *mixer;
+ struct mutex lock;
+
+ struct mdp_overlay req_data;
+ u32 params_changed;
+
+ unsigned long smp[MAX_PLANES];
+};
+
+static inline void mdss_mdp_ctl_write(struct mdss_mdp_ctl *ctl,
+ u32 reg, u32 val)
+{
+ int offset = MDSS_MDP_REG_CTL_OFFSET(ctl->num);
+ MDSS_MDP_REG_WRITE(offset + reg, val);
+}
+
+static inline u32 mdss_mdp_ctl_read(struct mdss_mdp_ctl *ctl, u32 reg)
+{
+ int offset = MDSS_MDP_REG_CTL_OFFSET(ctl->num);
+ return MDSS_MDP_REG_READ(offset + reg);
+}
+
+irqreturn_t mdss_mdp_isr(int irq, void *ptr);
+int mdss_mdp_irq_enable(u32 intr_type, u32 intf_num);
+void mdss_mdp_irq_disable(u32 intr_type, u32 intf_num);
+void mdss_mdp_irq_disable_nosync(u32 intr_type, u32 intf_num);
+int mdss_mdp_set_intr_callback(u32 intr_type, u32 intf_num,
+ void (*fnc_ptr)(void *), void *arg);
+
+int mdss_mdp_bus_scale_set_min_quota(u32 quota);
+void mdss_mdp_set_clk_rate(unsigned long min_clk_rate);
+unsigned long mdss_mdp_get_clk_rate(u32 clk_idx);
+int mdss_mdp_vsync_clk_enable(int enable);
+void mdss_mdp_clk_ctrl(int enable, int isr);
+void mdss_mdp_footswitch_ctrl(int on);
+
+int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd);
+int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl);
+int mdss_mdp_writeback_start(struct mdss_mdp_ctl *ctl);
+
+int mdss_mdp_ctl_on(struct msm_fb_data_type *mfd);
+int mdss_mdp_ctl_off(struct msm_fb_data_type *mfd);
+
+struct mdss_mdp_mixer *mdss_mdp_wb_mixer_alloc(int rotator);
+int mdss_mdp_wb_mixer_destroy(struct mdss_mdp_mixer *mixer);
+struct mdss_mdp_mixer *mdss_mdp_mixer_get(struct mdss_mdp_ctl *ctl, int mux);
+struct mdss_mdp_pipe *mdss_mdp_mixer_stage_pipe(struct mdss_mdp_ctl *ctl,
+ int mux, int stage);
+int mdss_mdp_mixer_pipe_update(struct mdss_mdp_pipe *pipe, int params_changed);
+int mdss_mdp_mixer_pipe_unstage(struct mdss_mdp_pipe *pipe);
+int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg);
+
+int mdss_mdp_csc_setup(u32 block, u32 blk_idx, u32 tbl_idx, u32 csc_type);
+int mdss_mdp_dspp_setup(struct mdss_mdp_ctl *ctl, struct mdss_mdp_mixer *mixer);
+
+struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_pnum(u32 pnum);
+struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_locked(u32 type);
+struct mdss_mdp_pipe *mdss_mdp_pipe_get_locked(u32 ndx);
+int mdss_mdp_pipe_lock(struct mdss_mdp_pipe *pipe);
+void mdss_mdp_pipe_unlock(struct mdss_mdp_pipe *pipe);
+
+int mdss_mdp_pipe_destroy(struct mdss_mdp_pipe *pipe);
+int mdss_mdp_pipe_release_all(struct msm_fb_data_type *mfd);
+int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe,
+ struct mdss_mdp_data *src_data);
+
+int mdss_mdp_data_check(struct mdss_mdp_data *data,
+ struct mdss_mdp_plane_sizes *ps);
+int mdss_mdp_get_plane_sizes(u32 format, u32 w, u32 h,
+ struct mdss_mdp_plane_sizes *ps);
+struct mdss_mdp_format_params *mdss_mdp_get_format_params(u32 format);
+int mdss_mdp_put_img(struct mdss_mdp_img_data *data);
+int mdss_mdp_get_img(struct ion_client *iclient, struct msmfb_data *img,
+ struct mdss_mdp_img_data *data);
+
+#endif /* MDSS_MDP_H */
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
new file mode 100644
index 0000000..c80527d9
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -0,0 +1,825 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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/errno.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+#include "mdss_fb.h"
+#include "mdss_mdp.h"
+
+enum {
+ MDSS_MDP_BUS_UPDATE_SKIP,
+ MDSS_MDP_BUS_UPDATE_EARLY,
+ MDSS_MDP_BUS_UPDATE_LATE,
+};
+
+static DEFINE_MUTEX(mdss_mdp_ctl_lock);
+static struct mdss_mdp_ctl mdss_mdp_ctl_list[MDSS_MDP_MAX_CTL];
+static struct mdss_mdp_mixer mdss_mdp_mixer_list[MDSS_MDP_MAX_LAYERMIXER];
+
+static int mdss_mdp_ctl_update_clk_rate(void)
+{
+ struct mdss_mdp_ctl *ctl;
+ int cnum;
+ unsigned long clk_rate = MDP_CLK_DEFAULT_RATE;
+
+ mutex_lock(&mdss_mdp_ctl_lock);
+ for (cnum = 0; cnum < MDSS_MDP_MAX_CTL; cnum++) {
+ ctl = &mdss_mdp_ctl_list[cnum];
+ if (ctl->power_on && ctl->mfd) {
+ unsigned long tmp;
+ pr_debug("ctl=%d pclk_rate=%u\n", ctl->num,
+ ctl->mfd->panel_info.clk_rate);
+ tmp = (ctl->mfd->panel_info.clk_rate * 23) / 20;
+ if (tmp > clk_rate)
+ clk_rate = tmp;
+ }
+ }
+ mdss_mdp_set_clk_rate(clk_rate);
+ mutex_unlock(&mdss_mdp_ctl_lock);
+
+ return 0;
+}
+
+static int mdss_mdp_ctl_update_bus_scale(void)
+{
+ struct mdss_mdp_ctl *ctl;
+ int cnum;
+ u32 bus_quota = 0;
+
+ mutex_lock(&mdss_mdp_ctl_lock);
+ for (cnum = 0; cnum < MDSS_MDP_MAX_CTL; cnum++) {
+ ctl = &mdss_mdp_ctl_list[cnum];
+ if (ctl->power_on)
+ bus_quota += ctl->bus_quota;
+ }
+ mdss_mdp_bus_scale_set_min_quota(bus_quota);
+ mutex_unlock(&mdss_mdp_ctl_lock);
+
+ return 0;
+}
+
+static void mdss_mdp_bus_update_pipe_quota(struct mdss_mdp_pipe *pipe)
+{
+ u32 quota;
+
+ quota = pipe->img_width * pipe->img_height * 60 * pipe->src_fmt->bpp;
+ quota *= 5 / 4; /* 1.25 factor */
+
+ pr_debug("pipe=%d quota old=%u new=%u\n", pipe->num,
+ pipe->bus_quota, quota);
+ pipe->bus_quota = quota;
+}
+
+static int mdss_mdp_bus_update_mixer_quota(struct mdss_mdp_mixer *mixer)
+{
+ struct mdss_mdp_pipe *pipe;
+ u32 quota, stage;
+
+ if (!mixer)
+ return 0;
+
+ quota = 0;
+ for (stage = 0; stage < MDSS_MDP_MAX_STAGE; stage++) {
+ pipe = mixer->stage_pipe[stage];
+ if (pipe == NULL)
+ continue;
+
+ quota += pipe->bus_quota;
+ }
+
+ pr_debug("mixer=%d quota old=%u new=%u\n", mixer->num,
+ mixer->bus_quota, quota);
+
+ if (quota != mixer->bus_quota) {
+ mixer->bus_quota = quota;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int mdss_mdp_bus_update_ctl_quota(struct mdss_mdp_ctl *ctl)
+{
+ int ret = MDSS_MDP_BUS_UPDATE_SKIP;
+
+ if (mdss_mdp_bus_update_mixer_quota(ctl->mixer_left) ||
+ mdss_mdp_bus_update_mixer_quota(ctl->mixer_right)) {
+ u32 quota = 0;
+
+ if (ctl->mixer_left)
+ quota += ctl->mixer_left->bus_quota;
+ if (ctl->mixer_right)
+ quota += ctl->mixer_right->bus_quota;
+
+ pr_debug("ctl=%d quota old=%u new=%u\n",
+ ctl->num, ctl->bus_quota, quota);
+
+ if (quota != ctl->bus_quota) {
+ if (quota > ctl->bus_quota)
+ ret = MDSS_MDP_BUS_UPDATE_EARLY;
+ else
+ ret = MDSS_MDP_BUS_UPDATE_LATE;
+
+ ctl->bus_quota = quota;
+ }
+ }
+
+ return ret;
+}
+
+static struct mdss_mdp_ctl *mdss_mdp_ctl_alloc(void)
+{
+ struct mdss_mdp_ctl *ctl = NULL;
+ int cnum;
+
+ mutex_lock(&mdss_mdp_ctl_lock);
+ for (cnum = 0; cnum < MDSS_MDP_MAX_CTL; cnum++) {
+ if (mdss_mdp_ctl_list[cnum].ref_cnt == 0) {
+ ctl = &mdss_mdp_ctl_list[cnum];
+ ctl->num = cnum;
+ ctl->ref_cnt++;
+ mutex_init(&ctl->lock);
+
+ pr_debug("alloc ctl_num=%d\n", ctl->num);
+ break;
+ }
+ }
+ mutex_unlock(&mdss_mdp_ctl_lock);
+
+ return ctl;
+}
+
+static int mdss_mdp_ctl_free(struct mdss_mdp_ctl *ctl)
+{
+ if (!ctl)
+ return -ENODEV;
+
+ pr_debug("free ctl_num=%d ref_cnt=%d\n", ctl->num, ctl->ref_cnt);
+
+ if (!ctl->ref_cnt) {
+ pr_err("called with ref_cnt=0\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&mdss_mdp_ctl_lock);
+ if (--ctl->ref_cnt == 0)
+ memset(ctl, 0, sizeof(*ctl));
+ mutex_unlock(&mdss_mdp_ctl_lock);
+
+ return 0;
+}
+
+static struct mdss_mdp_mixer *mdss_mdp_mixer_alloc(u32 type)
+{
+ struct mdss_mdp_mixer *mixer = NULL;
+ int mnum;
+
+ mutex_lock(&mdss_mdp_ctl_lock);
+ for (mnum = 0; mnum < MDSS_MDP_MAX_LAYERMIXER; mnum++) {
+ if (type == mdss_res->mixer_type_map[mnum] &&
+ mdss_mdp_mixer_list[mnum].ref_cnt == 0) {
+ mixer = &mdss_mdp_mixer_list[mnum];
+ mixer->num = mnum;
+ mixer->ref_cnt++;
+ mixer->params_changed++;
+ mixer->type = type;
+
+ pr_debug("mixer_num=%d\n", mixer->num);
+ break;
+ }
+ }
+ mutex_unlock(&mdss_mdp_ctl_lock);
+
+ return mixer;
+}
+
+static int mdss_mdp_mixer_free(struct mdss_mdp_mixer *mixer)
+{
+ if (!mixer)
+ return -ENODEV;
+
+ pr_debug("free mixer_num=%d ref_cnt=%d\n", mixer->num, mixer->ref_cnt);
+
+ if (!mixer->ref_cnt) {
+ pr_err("called with ref_cnt=0\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&mdss_mdp_ctl_lock);
+ if (--mixer->ref_cnt == 0)
+ memset(mixer, 0, sizeof(*mixer));
+ mutex_unlock(&mdss_mdp_ctl_lock);
+
+ return 0;
+}
+
+struct mdss_mdp_mixer *mdss_mdp_wb_mixer_alloc(int rotator)
+{
+ struct mdss_mdp_ctl *ctl = NULL;
+ struct mdss_mdp_mixer *mixer = NULL;
+
+ ctl = mdss_mdp_ctl_alloc();
+
+ if (!ctl)
+ return NULL;
+
+ mixer = mdss_mdp_mixer_alloc(MDSS_MDP_MIXER_TYPE_WRITEBACK);
+ if (!mixer)
+ goto error;
+
+ mixer->rotator_mode = rotator;
+
+ switch (mixer->num) {
+ case MDSS_MDP_LAYERMIXER3:
+ ctl->opmode = (rotator ? MDSS_MDP_CTL_OP_ROT0_MODE :
+ MDSS_MDP_CTL_OP_WB0_MODE);
+ break;
+ case MDSS_MDP_LAYERMIXER4:
+ ctl->opmode = (rotator ? MDSS_MDP_CTL_OP_ROT1_MODE :
+ MDSS_MDP_CTL_OP_WB1_MODE);
+ break;
+ default:
+ pr_err("invalid layer mixer=%d\n", mixer->num);
+ goto error;
+ }
+
+ ctl->mixer_left = mixer;
+ mixer->ctl = ctl;
+
+ ctl->start_fnc = mdss_mdp_writeback_start;
+
+ if (ctl->start_fnc)
+ ctl->start_fnc(ctl);
+
+ return mixer;
+error:
+ if (mixer)
+ mdss_mdp_mixer_free(mixer);
+ if (ctl)
+ mdss_mdp_ctl_free(ctl);
+
+ return NULL;
+}
+
+int mdss_mdp_wb_mixer_destroy(struct mdss_mdp_mixer *mixer)
+{
+ struct mdss_mdp_ctl *ctl;
+
+ ctl = mixer->ctl;
+
+ pr_debug("destroy ctl=%d mixer=%d\n", ctl->num, mixer->num);
+
+ if (ctl->stop_fnc)
+ ctl->stop_fnc(ctl);
+
+ mdss_mdp_mixer_free(mixer);
+ mdss_mdp_ctl_free(ctl);
+
+ return 0;
+}
+
+static int mdss_mdp_ctl_init(struct msm_fb_data_type *mfd)
+{
+ struct mdss_mdp_ctl *ctl;
+ u32 width, height;
+
+ if (!mfd)
+ return -ENODEV;
+
+ width = mfd->fbi->var.xres;
+ height = mfd->fbi->var.yres;
+
+ if (width > (2 * MAX_MIXER_WIDTH)) {
+ pr_err("unsupported resolution\n");
+ return -EINVAL;
+ }
+
+ ctl = mdss_mdp_ctl_alloc();
+
+ if (!ctl) {
+ pr_err("unable to allocate ctl\n");
+ return -ENOMEM;
+ }
+
+ ctl->mfd = mfd;
+ ctl->width = width;
+ ctl->height = height;
+ ctl->dst_format = mfd->panel_info.out_format;
+
+ ctl->mixer_left = mdss_mdp_mixer_alloc(MDSS_MDP_MIXER_TYPE_INTF);
+ if (!ctl->mixer_left) {
+ pr_err("unable to allocate layer mixer\n");
+ mdss_mdp_ctl_free(ctl);
+ return -ENOMEM;
+ }
+
+ ctl->mixer_left->width = MIN(width, MAX_MIXER_WIDTH);
+ ctl->mixer_left->height = height;
+ ctl->mixer_left->ctl = ctl;
+
+ width -= ctl->mixer_left->width;
+
+ if (width) {
+ ctl->mixer_right =
+ mdss_mdp_mixer_alloc(MDSS_MDP_MIXER_TYPE_INTF);
+ if (!ctl->mixer_right) {
+ pr_err("unable to allocate layer mixer\n");
+ mdss_mdp_mixer_free(ctl->mixer_left);
+ mdss_mdp_ctl_free(ctl);
+ return -ENOMEM;
+ }
+ ctl->mixer_right->width = width;
+ ctl->mixer_right->height = height;
+ ctl->mixer_right->ctl = ctl;
+ }
+
+ switch (mfd->panel_info.type) {
+ case EDP_PANEL:
+ ctl->intf_num = MDSS_MDP_INTF0;
+ ctl->intf_type = MDSS_INTF_EDP;
+ ctl->opmode = MDSS_MDP_CTL_OP_VIDEO_MODE;
+ ctl->start_fnc = mdss_mdp_video_start;
+ break;
+ case MIPI_VIDEO_PANEL:
+ if (mfd->panel_info.pdest == DISPLAY_1)
+ ctl->intf_num = MDSS_MDP_INTF1;
+ else
+ ctl->intf_num = MDSS_MDP_INTF2;
+ ctl->intf_type = MDSS_INTF_DSI;
+ ctl->opmode = MDSS_MDP_CTL_OP_VIDEO_MODE;
+ ctl->start_fnc = mdss_mdp_video_start;
+ break;
+ case DTV_PANEL:
+ ctl->intf_num = MDSS_MDP_INTF3;
+ ctl->intf_type = MDSS_INTF_HDMI;
+ ctl->opmode = MDSS_MDP_CTL_OP_VIDEO_MODE;
+ ctl->start_fnc = mdss_mdp_video_start;
+ break;
+ case WRITEBACK_PANEL:
+ ctl->intf_num = MDSS_MDP_NO_INTF;
+ ctl->opmode = MDSS_MDP_CTL_OP_WFD_MODE;
+ ctl->start_fnc = mdss_mdp_writeback_start;
+ break;
+ default:
+ pr_err("unsupported panel type (%d)\n", mfd->panel_info.type);
+ mdss_mdp_ctl_free(ctl);
+ return -EINVAL;
+
+ }
+
+ ctl->opmode |= (ctl->intf_num << 4);
+
+ if (ctl->mixer_right) {
+ ctl->opmode |= MDSS_MDP_CTL_OP_PACK_3D_ENABLE |
+ MDSS_MDP_CTL_OP_PACK_3D_H_ROW_INT;
+ }
+
+ mfd->ctl = ctl;
+
+ return 0;
+}
+
+static int mdss_mdp_ctl_destroy(struct msm_fb_data_type *mfd)
+{
+ struct mdss_mdp_ctl *ctl;
+ if (!mfd || !mfd->ctl)
+ return -ENODEV;
+
+ ctl = mfd->ctl;
+ mfd->ctl = NULL;
+
+ if (ctl->mixer_left)
+ mdss_mdp_mixer_free(ctl->mixer_left);
+ if (ctl->mixer_right)
+ mdss_mdp_mixer_free(ctl->mixer_right);
+ mdss_mdp_ctl_free(ctl);
+
+ return 0;
+}
+
+int mdss_mdp_ctl_on(struct msm_fb_data_type *mfd)
+{
+ struct mdss_panel_data *pdata;
+ struct mdss_mdp_ctl *ctl;
+ struct mdss_mdp_mixer *mixer;
+ u32 outsize, temp, off;
+ int ret = 0;
+
+ if (!mfd)
+ return -ENODEV;
+
+ if (mfd->key != MFD_KEY)
+ return -EINVAL;
+
+ pdata = dev_get_platdata(&mfd->pdev->dev);
+ if (!pdata) {
+ pr_err("no panel connected\n");
+ return -ENODEV;
+ }
+
+ if (!mfd->ctl) {
+ if (mdss_mdp_ctl_init(mfd)) {
+ pr_err("unable to initialize ctl\n");
+ return -ENODEV;
+ }
+ }
+ ctl = mfd->ctl;
+
+ mutex_lock(&ctl->lock);
+
+ ctl->power_on = true;
+ mdss_mdp_ctl_update_clk_rate();
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ if (ctl->start_fnc)
+ ret = ctl->start_fnc(ctl);
+ else
+ pr_warn("no start function for ctl=%d type=%d\n", ctl->num,
+ mfd->panel_info.type);
+
+ if (ret) {
+ pr_err("unable to start intf\n");
+ goto start_fail;
+ }
+
+ pr_debug("ctl_num=%d\n", ctl->num);
+
+ mixer = ctl->mixer_left;
+ mixer->params_changed++;
+
+ temp = MDSS_MDP_REG_READ(MDSS_MDP_REG_DISP_INTF_SEL);
+ temp |= (ctl->intf_type << ((ctl->intf_num - MDSS_MDP_INTF0) * 8));
+ MDSS_MDP_REG_WRITE(MDSS_MDP_REG_DISP_INTF_SEL, temp);
+
+ outsize = (mixer->height << 16) | mixer->width;
+ off = MDSS_MDP_REG_LM_OFFSET(mixer->num);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OUT_SIZE, outsize);
+
+ if (ctl->mixer_right) {
+ mixer = ctl->mixer_right;
+ mixer->params_changed++;
+ outsize = (mixer->height << 16) | mixer->width;
+ off = MDSS_MDP_REG_LM_OFFSET(mixer->num);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OUT_SIZE, outsize);
+ mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_PACK_3D, 0);
+ }
+
+ ret = pdata->on(pdata);
+
+start_fail:
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+ mutex_unlock(&ctl->lock);
+
+ return ret;
+}
+
+int mdss_mdp_ctl_off(struct msm_fb_data_type *mfd)
+{
+ struct mdss_panel_data *pdata;
+ struct mdss_mdp_ctl *ctl;
+ int ret = 0;
+
+ if (!mfd)
+ return -ENODEV;
+
+ if (mfd->key != MFD_KEY)
+ return -EINVAL;
+
+ if (!mfd->ctl) {
+ pr_err("ctl not initialized\n");
+ return -ENODEV;
+ }
+
+ pdata = dev_get_platdata(&mfd->pdev->dev);
+ if (!pdata) {
+ pr_err("no panel connected\n");
+ return -ENODEV;
+ }
+
+ ctl = mfd->ctl;
+
+ pr_debug("ctl_num=%d\n", mfd->ctl->num);
+
+ mutex_lock(&ctl->lock);
+ ctl->power_on = false;
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ if (ctl->stop_fnc)
+ ret = ctl->stop_fnc(ctl);
+ else
+ pr_warn("no stop func for ctl=%d\n", ctl->num);
+
+ if (ret)
+ pr_warn("error powering off intf ctl=%d\n", ctl->num);
+
+ ret = pdata->off(pdata);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+
+ ctl->play_cnt = 0;
+
+ mdss_mdp_ctl_update_bus_scale();
+ mdss_mdp_ctl_update_clk_rate();
+
+ mutex_unlock(&ctl->lock);
+
+ mdss_mdp_pipe_release_all(mfd);
+
+ if (!mfd->ref_cnt)
+ mdss_mdp_ctl_destroy(mfd);
+
+ return ret;
+}
+
+static int mdss_mdp_mixer_setup(struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_mixer *mixer)
+{
+ struct mdss_mdp_pipe *pipe, *bgpipe = NULL;
+ u32 off, blend_op, blend_stage;
+ u32 mixercfg = 0, blend_color_out = 0, bgalpha = 0;
+ int stage;
+
+ if (!mixer)
+ return -ENODEV;
+
+ pr_debug("setup mixer=%d\n", mixer->num);
+
+ for (stage = MDSS_MDP_STAGE_BASE; stage < MDSS_MDP_MAX_STAGE; stage++) {
+ pipe = mixer->stage_pipe[stage];
+ if (pipe == NULL) {
+ if (stage == MDSS_MDP_STAGE_BASE)
+ mixercfg |= MDSS_MDP_LM_BORDER_COLOR;
+ continue;
+ }
+
+ if (stage != pipe->mixer_stage) {
+ mixer->stage_pipe[stage] = NULL;
+ continue;
+ }
+ mixercfg |= stage << (3 * pipe->num);
+
+ if (stage == MDSS_MDP_STAGE_BASE) {
+ bgpipe = pipe;
+ if (pipe->src_fmt->alpha_enable)
+ bgalpha = 1;
+ continue;
+ }
+
+ blend_stage = stage - MDSS_MDP_STAGE_0;
+ off = MDSS_MDP_REG_LM_OFFSET(mixer->num) +
+ MDSS_MDP_REG_LM_BLEND_OFFSET(blend_stage);
+
+ if (pipe->is_fg) {
+ bgalpha = 0;
+ if (bgpipe) {
+ mixercfg &= ~(0x7 << (3 * bgpipe->num));
+ mixercfg |= MDSS_MDP_LM_BORDER_COLOR;
+ }
+ blend_op = (MDSS_MDP_BLEND_FG_ALPHA_FG_CONST |
+ MDSS_MDP_BLEND_BG_ALPHA_BG_CONST);
+ /* keep fg alpha */
+ blend_color_out |= 1 << (blend_stage + 1);
+
+ pr_debug("pnum=%d stg=%d alpha=IS_FG\n", pipe->num,
+ stage);
+ } else if (pipe->src_fmt->alpha_enable) {
+ bgalpha = 0;
+ blend_op = (MDSS_MDP_BLEND_BG_ALPHA_FG_PIXEL |
+ MDSS_MDP_BLEND_BG_INV_ALPHA);
+ /* keep fg alpha */
+ blend_color_out |= 1 << (blend_stage + 1);
+
+ pr_debug("pnum=%d stg=%d alpha=FG PIXEL\n", pipe->num,
+ stage);
+ } else if (bgalpha) {
+ blend_op = (MDSS_MDP_BLEND_BG_ALPHA_BG_PIXEL |
+ MDSS_MDP_BLEND_FG_ALPHA_BG_PIXEL |
+ MDSS_MDP_BLEND_FG_INV_ALPHA);
+ /* keep bg alpha */
+ pr_debug("pnum=%d stg=%d alpha=BG_PIXEL\n", pipe->num,
+ stage);
+ } else {
+ blend_op = (MDSS_MDP_BLEND_FG_ALPHA_FG_CONST |
+ MDSS_MDP_BLEND_BG_ALPHA_BG_CONST);
+ pr_debug("pnum=%d stg=%d alpha=CONST\n", pipe->num,
+ stage);
+ }
+
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OP_MODE, blend_op);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_BLEND_FG_ALPHA,
+ pipe->alpha);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_BLEND_BG_ALPHA,
+ 0xFF - pipe->alpha);
+ }
+
+ if (mixer->cursor_enabled)
+ mixercfg |= MDSS_MDP_LM_CURSOR_OUT;
+
+ pr_debug("mixer=%d mixer_cfg=%x\n", mixer->num, mixercfg);
+
+ ctl->flush_bits |= BIT(6) << mixer->num; /* LAYER_MIXER */
+
+ off = MDSS_MDP_REG_LM_OFFSET(mixer->num);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OP_MODE, blend_color_out);
+ mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_LAYER(mixer->num), mixercfg);
+
+ return 0;
+}
+
+struct mdss_mdp_mixer *mdss_mdp_mixer_get(struct mdss_mdp_ctl *ctl, int mux)
+{
+ struct mdss_mdp_mixer *mixer = NULL;
+ if (!ctl)
+ return NULL;
+
+ switch (mux) {
+ case MDSS_MDP_MIXER_MUX_DEFAULT:
+ case MDSS_MDP_MIXER_MUX_LEFT:
+ mixer = ctl->mixer_left;
+ break;
+ case MDSS_MDP_MIXER_MUX_RIGHT:
+ mixer = ctl->mixer_right;
+ break;
+ }
+
+ return mixer;
+}
+
+struct mdss_mdp_pipe *mdss_mdp_mixer_stage_pipe(struct mdss_mdp_ctl *ctl,
+ int mux, int stage)
+{
+ struct mdss_mdp_pipe *pipe = NULL;
+ struct mdss_mdp_mixer *mixer;
+ if (!ctl)
+ return NULL;
+
+ if (mutex_lock_interruptible(&ctl->lock))
+ return NULL;
+
+ mixer = mdss_mdp_mixer_get(ctl, mux);
+ if (mixer)
+ pipe = mixer->stage_pipe[stage];
+ mutex_unlock(&ctl->lock);
+
+ return pipe;
+}
+
+int mdss_mdp_mixer_pipe_update(struct mdss_mdp_pipe *pipe, int params_changed)
+{
+ struct mdss_mdp_ctl *ctl;
+ struct mdss_mdp_mixer *mixer;
+
+ if (!pipe)
+ return -EINVAL;
+ mixer = pipe->mixer;
+ if (!mixer)
+ return -EINVAL;
+ ctl = mixer->ctl;
+ if (!ctl)
+ return -EINVAL;
+
+ if (pipe->mixer_stage >= MDSS_MDP_MAX_STAGE) {
+ pr_err("invalid mixer stage\n");
+ return -EINVAL;
+ }
+
+ pr_debug("pnum=%x mixer=%d stage=%d\n", pipe->num, mixer->num,
+ pipe->mixer_stage);
+
+ if (mutex_lock_interruptible(&ctl->lock))
+ return -EINTR;
+
+ if (params_changed) {
+ mixer->params_changed++;
+ mixer->stage_pipe[pipe->mixer_stage] = pipe;
+ mdss_mdp_bus_update_pipe_quota(pipe);
+ }
+
+ if (pipe->type == MDSS_MDP_PIPE_TYPE_DMA)
+ ctl->flush_bits |= BIT(pipe->num) << 5;
+ else /* RGB/VIG pipe */
+ ctl->flush_bits |= BIT(pipe->num);
+
+ mutex_unlock(&ctl->lock);
+
+ return 0;
+}
+
+int mdss_mdp_mixer_pipe_unstage(struct mdss_mdp_pipe *pipe)
+{
+ struct mdss_mdp_ctl *ctl;
+ struct mdss_mdp_mixer *mixer;
+
+ if (!pipe)
+ return -EINVAL;
+ mixer = pipe->mixer;
+ if (!mixer)
+ return -EINVAL;
+ ctl = mixer->ctl;
+ if (!ctl)
+ return -EINVAL;
+
+ pr_debug("unstage pnum=%d stage=%d mixer=%d\n", pipe->num,
+ pipe->mixer_stage, mixer->num);
+
+ if (mutex_lock_interruptible(&ctl->lock))
+ return -EINTR;
+
+ mixer->params_changed++;
+ mixer->stage_pipe[pipe->mixer_stage] = NULL;
+
+ mutex_unlock(&ctl->lock);
+
+ return 0;
+}
+
+static int mdss_mdp_mixer_update(struct mdss_mdp_mixer *mixer)
+{
+ mixer->params_changed = 0;
+
+ if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF)
+ mdss_mdp_dspp_setup(mixer->ctl, mixer);
+
+ /* skip mixer setup for rotator */
+ if (!mixer->rotator_mode)
+ mdss_mdp_mixer_setup(mixer->ctl, mixer);
+
+ return 0;
+}
+
+int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg)
+{
+ int mixer1_changed, mixer2_changed;
+ int ret = 0;
+ int bus_update = MDSS_MDP_BUS_UPDATE_SKIP;
+
+ if (!ctl) {
+ pr_err("display function not set\n");
+ return -ENODEV;
+ }
+
+ pr_debug("commit ctl=%d\n", ctl->num);
+
+ if (mutex_lock_interruptible(&ctl->lock))
+ return -EINTR;
+
+ mixer1_changed = (ctl->mixer_left && ctl->mixer_left->params_changed);
+ mixer2_changed = (ctl->mixer_right && ctl->mixer_right->params_changed);
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ if (mixer1_changed || mixer2_changed) {
+ bus_update = mdss_mdp_bus_update_ctl_quota(ctl);
+
+ if (ctl->prepare_fnc)
+ ret = ctl->prepare_fnc(ctl, arg);
+ if (ret) {
+ pr_err("error preparing display\n");
+ goto done;
+ }
+
+ if (bus_update == MDSS_MDP_BUS_UPDATE_EARLY)
+ mdss_mdp_ctl_update_bus_scale();
+
+ if (mixer1_changed)
+ mdss_mdp_mixer_update(ctl->mixer_left);
+ if (mixer2_changed)
+ mdss_mdp_mixer_update(ctl->mixer_right);
+
+ mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_TOP, ctl->opmode);
+ ctl->flush_bits |= BIT(17); /* CTL */
+ }
+
+ mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, ctl->flush_bits);
+ wmb();
+ ctl->flush_bits = 0;
+
+ if (ctl->display_fnc)
+ ret = ctl->display_fnc(ctl, arg); /* kickoff */
+ if (ret)
+ pr_warn("error displaying frame\n");
+
+ ctl->play_cnt++;
+
+ if (bus_update == MDSS_MDP_BUS_UPDATE_LATE)
+ mdss_mdp_ctl_update_bus_scale();
+
+done:
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+
+ mutex_unlock(&ctl->lock);
+
+ return ret;
+}
diff --git a/drivers/video/msm/mdss/mdss_mdp_formats.h b/drivers/video/msm/mdss/mdss_mdp_formats.h
new file mode 100644
index 0000000..07eefc1
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_mdp_formats.h
@@ -0,0 +1,328 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 MDSS_MDP_FORMATS_H
+#define MDSS_MDP_FORMATS_H
+
+#include <linux/msm_mdp.h>
+
+#include "mdss_mdp.h"
+
+#define C3_ALPHA 3 /* alpha */
+#define C2_R_Cr 2 /* R/Cr */
+#define C1_B_Cb 1 /* B/Cb */
+#define C0_G_Y 0 /* G/luma */
+
+static struct mdss_mdp_format_params mdss_mdp_format_map[MDP_IMGTYPE_LIMIT] = {
+ [MDP_RGB_565] = {
+ .format = MDP_RGB_565,
+ .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED,
+ .a_bit = 0,
+ .r_bit = 1, /* R, 5 bits */
+ .b_bit = 1, /* B, 5 bits */
+ .g_bit = 2, /* G, 6 bits */
+ .alpha_enable = 0,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 2,
+ .element2 = C2_R_Cr,
+ .element1 = C0_G_Y,
+ .element0 = C1_B_Cb,
+ .bpp = 1, /* 2 bpp */
+ },
+ [MDP_BGR_565] = {
+ .format = MDP_BGR_565,
+ .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED,
+ .a_bit = 0,
+ .r_bit = 1, /* R, 5 bits */
+ .b_bit = 1, /* B, 5 bits */
+ .g_bit = 2, /* G, 6 bits */
+ .alpha_enable = 0,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 2,
+ .element2 = C1_B_Cb,
+ .element1 = C0_G_Y,
+ .element0 = C2_R_Cr,
+ .bpp = 1, /* 2 bpp */
+ },
+ [MDP_RGB_888] = {
+ .format = MDP_RGB_888,
+ .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED,
+ .a_bit = 0,
+ .r_bit = 3, /* R, 8 bits */
+ .b_bit = 3, /* B, 8 bits */
+ .g_bit = 3, /* G, 8 bits */
+ .alpha_enable = 0,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 2,
+ .element2 = C1_B_Cb,
+ .element1 = C0_G_Y,
+ .element0 = C2_R_Cr,
+ .bpp = 2, /* 3 bpp */
+ },
+ [MDP_XRGB_8888] = {
+ .format = MDP_XRGB_8888,
+ .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED,
+ .a_bit = 3, /* alpha, 4 bits */
+ .r_bit = 3, /* R, 8 bits */
+ .b_bit = 3, /* B, 8 bits */
+ .g_bit = 3, /* G, 8 bits */
+ .alpha_enable = 0,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 3,
+ .element3 = C1_B_Cb,
+ .element2 = C0_G_Y,
+ .element1 = C2_R_Cr,
+ .element0 = C3_ALPHA,
+ .bpp = 3, /* 4 bpp */
+ },
+ [MDP_ARGB_8888] = {
+ .format = MDP_ARGB_8888,
+ .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED,
+ .a_bit = 3, /* alpha, 4 bits */
+ .r_bit = 3, /* R, 8 bits */
+ .b_bit = 3, /* B, 8 bits */
+ .g_bit = 3, /* G, 8 bits */
+ .alpha_enable = 1,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 3,
+ .element3 = C1_B_Cb,
+ .element2 = C0_G_Y,
+ .element1 = C2_R_Cr,
+ .element0 = C3_ALPHA,
+ .bpp = 3, /* 4 bpp */
+ },
+ [MDP_RGBA_8888] = {
+ .format = MDP_RGBA_8888,
+ .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED,
+ .a_bit = 3, /* alpha, 4 bits */
+ .r_bit = 3, /* R, 8 bits */
+ .b_bit = 3, /* B, 8 bits */
+ .g_bit = 3, /* G, 8 bits */
+ .alpha_enable = 1,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 3,
+ .element3 = C3_ALPHA,
+ .element2 = C1_B_Cb,
+ .element1 = C0_G_Y,
+ .element0 = C2_R_Cr,
+ .bpp = 3, /* 4 bpp */
+ },
+ [MDP_RGBX_8888] = {
+ .format = MDP_RGBX_8888,
+ .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED,
+ .a_bit = 3,
+ .r_bit = 3, /* R, 8 bits */
+ .b_bit = 3, /* B, 8 bits */
+ .g_bit = 3, /* G, 8 bits */
+ .alpha_enable = 0,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 3,
+ .element3 = C3_ALPHA,
+ .element2 = C1_B_Cb,
+ .element1 = C0_G_Y,
+ .element0 = C2_R_Cr,
+ .bpp = 3, /* 4 bpp */
+ },
+ [MDP_BGRA_8888] = {
+ .format = MDP_BGRA_8888,
+ .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED,
+ .a_bit = 3, /* alpha, 4 bits */
+ .r_bit = 3, /* R, 8 bits */
+ .b_bit = 3, /* B, 8 bits */
+ .g_bit = 3, /* G, 8 bits */
+ .alpha_enable = 1,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 3,
+ .element3 = C3_ALPHA,
+ .element2 = C2_R_Cr,
+ .element1 = C0_G_Y,
+ .element0 = C1_B_Cb,
+ .bpp = 3, /* 4 bpp */
+ },
+ [MDP_YCRYCB_H2V1] = {
+ .format = MDP_YCRYCB_H2V1,
+ .is_yuv = 1,
+ .a_bit = 0,
+ .r_bit = 3, /* R, 8 bits */
+ .b_bit = 3, /* B, 8 bits */
+ .g_bit = 3, /* G, 8 bits */
+ .alpha_enable = 0,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 1, /* 2 */
+ .bpp = 1, /* 2 bpp */
+ .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED,
+ .chroma_sample = MDSS_MDP_CHROMA_H2V1,
+ .unpack_count = 3,
+ .element3 = C0_G_Y,
+ .element2 = C2_R_Cr,
+ .element1 = C0_G_Y,
+ .element0 = C1_B_Cb,
+ },
+ [MDP_Y_CRCB_H2V1] = {
+ .format = MDP_Y_CRCB_H2V1,
+ .is_yuv = 1,
+ .a_bit = 0,
+ .r_bit = 3, /* R, 8 bits */
+ .b_bit = 3, /* B, 8 bits */
+ .g_bit = 3, /* G, 8 bits */
+ .alpha_enable = 0,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 1, /* 2 */
+ .bpp = 1, /* 2 bpp */
+ .fetch_planes = MDSS_MDP_PLANE_PSEUDO_PLANAR,
+ .chroma_sample = MDSS_MDP_CHROMA_H2V1,
+ .element1 = C1_B_Cb,
+ .element0 = C2_R_Cr,
+ },
+ [MDP_Y_CBCR_H2V1] = {
+ .format = MDP_Y_CBCR_H2V1,
+ .is_yuv = 1,
+ .a_bit = 0,
+ .r_bit = 3, /* R, 8 bits */
+ .b_bit = 3, /* B, 8 bits */
+ .g_bit = 3, /* G, 8 bits */
+ .alpha_enable = 0,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 1, /* 2 */
+ .bpp = 1, /* 2 bpp */
+ .fetch_planes = MDSS_MDP_PLANE_PSEUDO_PLANAR,
+ .chroma_sample = MDSS_MDP_CHROMA_H2V1,
+ .element1 = C2_R_Cr,
+ .element0 = C1_B_Cb,
+ },
+ [MDP_Y_CRCB_H1V2] = {
+ .format = MDP_Y_CRCB_H1V2,
+ .is_yuv = 1,
+ .a_bit = 0,
+ .r_bit = 3, /* R, 8 bits */
+ .b_bit = 3, /* B, 8 bits */
+ .g_bit = 3, /* G, 8 bits */
+ .alpha_enable = 0,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 1, /* 2 */
+ .bpp = 1, /* 2 bpp */
+ .fetch_planes = MDSS_MDP_PLANE_PSEUDO_PLANAR,
+ .chroma_sample = MDSS_MDP_CHROMA_H1V2,
+ .element1 = C1_B_Cb,
+ .element0 = C2_R_Cr,
+ },
+ [MDP_Y_CBCR_H1V2] = {
+ .format = MDP_Y_CBCR_H1V2,
+ .is_yuv = 1,
+ .a_bit = 0,
+ .r_bit = 3, /* R, 8 bits */
+ .b_bit = 3, /* B, 8 bits */
+ .g_bit = 3, /* G, 8 bits */
+ .alpha_enable = 0,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 1, /* 2 */
+ .bpp = 1, /* 2 bpp */
+ .fetch_planes = MDSS_MDP_PLANE_PSEUDO_PLANAR,
+ .chroma_sample = MDSS_MDP_CHROMA_H1V2,
+ .element1 = C2_R_Cr,
+ .element0 = C1_B_Cb,
+ },
+ [MDP_Y_CRCB_H2V2] = {
+ .format = MDP_Y_CRCB_H2V2,
+ .is_yuv = 1,
+ .a_bit = 0,
+ .r_bit = 3, /* R, 8 bits */
+ .b_bit = 3, /* B, 8 bits */
+ .g_bit = 3, /* G, 8 bits */
+ .alpha_enable = 0,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 1, /* 2 */
+ .bpp = 1, /* 2 bpp */
+ .fetch_planes = MDSS_MDP_PLANE_PSEUDO_PLANAR,
+ .chroma_sample = MDSS_MDP_CHROMA_420,
+ .element1 = C1_B_Cb,
+ .element0 = C2_R_Cr,
+ },
+ [MDP_Y_CBCR_H2V2] = {
+ .format = MDP_Y_CBCR_H2V2,
+ .is_yuv = 1,
+ .a_bit = 0,
+ .r_bit = 3, /* R, 8 bits */
+ .b_bit = 3, /* B, 8 bits */
+ .g_bit = 3, /* G, 8 bits */
+ .alpha_enable = 0,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 1, /* 2 */
+ .bpp = 1, /* 2 bpp */
+ .fetch_planes = MDSS_MDP_PLANE_PSEUDO_PLANAR,
+ .chroma_sample = MDSS_MDP_CHROMA_420,
+ .element1 = C2_R_Cr,
+ .element0 = C1_B_Cb,
+ },
+ [MDP_Y_CR_CB_H2V2] = {
+ .format = MDP_Y_CR_CB_H2V2,
+ .is_yuv = 1,
+ .a_bit = 0,
+ .r_bit = 3, /* R, 8 bits */
+ .b_bit = 3, /* B, 8 bits */
+ .g_bit = 3, /* G, 8 bits */
+ .alpha_enable = 0,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 1, /* 2 */
+ .bpp = 1, /* 2 bpp */
+ .fetch_planes = MDSS_MDP_PLANE_PLANAR,
+ .chroma_sample = MDSS_MDP_CHROMA_420,
+ },
+ [MDP_Y_CB_CR_H2V2] = {
+ .format = MDP_Y_CB_CR_H2V2,
+ .is_yuv = 1,
+ .a_bit = 0,
+ .r_bit = 3, /* R, 8 bits */
+ .b_bit = 3, /* B, 8 bits */
+ .g_bit = 3, /* G, 8 bits */
+ .alpha_enable = 0,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 1, /* 2 */
+ .bpp = 1, /* 2 bpp */
+ .fetch_planes = MDSS_MDP_PLANE_PLANAR,
+ .chroma_sample = MDSS_MDP_CHROMA_420,
+ },
+ [MDP_Y_CR_CB_GH2V2] = {
+ .format = MDP_Y_CR_CB_GH2V2,
+ .is_yuv = 1,
+ .a_bit = 0,
+ .r_bit = 3, /* R, 8 bits */
+ .b_bit = 3, /* B, 8 bits */
+ .g_bit = 3, /* G, 8 bits */
+ .alpha_enable = 0,
+ .unpack_tight = 1,
+ .unpack_align_msb = 0,
+ .unpack_count = 1, /* 2 */
+ .bpp = 1, /* 2 bpp */
+ .fetch_planes = MDSS_MDP_PLANE_PLANAR,
+ .chroma_sample = MDSS_MDP_CHROMA_420,
+ },
+};
+#endif
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h
new file mode 100644
index 0000000..4ca1dce
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h
@@ -0,0 +1,430 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 MDSS_MDP_HWIO_H
+#define MDSS_MDP_HWIO_H
+
+#include <linux/bitops.h>
+
+#define MDSS_REG_HW_VERSION 0x0
+#define MDSS_REG_HW_INTR_STATUS 0x10
+
+#define MDSS_INTR_MDP BIT(0)
+#define MDSS_INTR_DSI0 BIT(4)
+#define MDSS_INTR_DSI1 BIT(5)
+#define MDSS_INTR_HDMI BIT(8)
+#define MDSS_INTR_EDP BIT(12)
+
+#define MDSS_MDP_REG_HW_VERSION 0x00100
+#define MDSS_MDP_REG_DISP_INTF_SEL 0x00104
+#define MDSS_MDP_REG_INTR_EN 0x00110
+#define MDSS_MDP_REG_INTR_STATUS 0x00114
+#define MDSS_MDP_REG_INTR_CLEAR 0x00118
+#define MDSS_MDP_REG_HIST_INTR_EN 0x0011C
+#define MDSS_MDP_REG_HIST_INTR_STATUS 0x00120
+#define MDSS_MDP_REG_HIST_INTR_CLEAR 0x00124
+
+#define MDSS_INTF_DSI 0x1
+#define MDSS_INTF_HDMI 0x3
+#define MDSS_INTF_LCDC 0x5
+#define MDSS_INTF_EDP 0x9
+
+#define MDSS_MDP_INTR_WB_0_DONE BIT(0)
+#define MDSS_MDP_INTR_WB_1_DONE BIT(1)
+#define MDSS_MDP_INTR_WB_2_DONE BIT(4)
+#define MDSS_MDP_INTR_PING_PONG_0_DONE BIT(8)
+#define MDSS_MDP_INTR_PING_PONG_1_DONE BIT(9)
+#define MDSS_MDP_INTR_PING_PONG_2_DONE BIT(10)
+#define MDSS_MDP_INTR_PING_PONG_0_RD_PTR BIT(12)
+#define MDSS_MDP_INTR_PING_PONG_1_RD_PTR BIT(13)
+#define MDSS_MDP_INTR_PING_PONG_2_RD_PTR BIT(14)
+#define MDSS_MDP_INTR_PING_PONG_0_WR_PTR BIT(16)
+#define MDSS_MDP_INTR_PING_PONG_1_WR_PTR BIT(17)
+#define MDSS_MDP_INTR_PING_PONG_2_WR_PTR BIT(18)
+#define MDSS_MDP_INTR_PING_PONG_0_AUTOREFRESH_DONE BIT(20)
+#define MDSS_MDP_INTR_PING_PONG_1_AUTOREFRESH_DONE BIT(21)
+#define MDSS_MDP_INTR_PING_PONG_2_AUTOREFRESH_DONE BIT(22)
+#define MDSS_MDP_INTR_INTF_0_UNDERRUN BIT(24)
+#define MDSS_MDP_INTR_INTF_0_VSYNC BIT(25)
+#define MDSS_MDP_INTR_INTF_1_UNDERRUN BIT(26)
+#define MDSS_MDP_INTR_INTF_1_VSYNC BIT(27)
+#define MDSS_MDP_INTR_INTF_2_UNDERRUN BIT(28)
+#define MDSS_MDP_INTR_INTF_2_VSYNC BIT(29)
+#define MDSS_MDP_INTR_INTF_3_UNDERRUN BIT(30)
+#define MDSS_MDP_INTR_INTF_3_VSYNC BIT(31)
+
+enum mdss_mdp_intr_type {
+ MDSS_MDP_IRQ_WB_ROT_COMP = 0,
+ MDSS_MDP_IRQ_WB_WFD = 4,
+ MDSS_MDP_IRQ_PING_PONG_COMP = 8,
+ MDSS_MDP_IRQ_PING_PONG_RD_PTR = 12,
+ MDSS_MDP_IRQ_PING_PONG_WR_PTR = 16,
+ MDSS_MDP_IRQ_PING_PONG_AUTO_REF = 20,
+ MDSS_MDP_IRQ_INTF_UNDER_RUN = 24,
+ MDSS_MDP_IRQ_INTF_VSYNC = 25,
+};
+
+enum mdss_mdp_ctl_index {
+ MDSS_MDP_CTL0,
+ MDSS_MDP_CTL1,
+ MDSS_MDP_CTL2,
+ MDSS_MDP_CTL3,
+ MDSS_MDP_CTL4,
+ MDSS_MDP_MAX_CTL
+};
+
+#define MDSS_MDP_REG_CTL_OFFSET(ctl) (0x00600 + ((ctl) * 0x100))
+
+#define MDSS_MDP_REG_CTL_LAYER(lm) ((lm) * 0x004)
+#define MDSS_MDP_REG_CTL_TOP 0x014
+#define MDSS_MDP_REG_CTL_FLUSH 0x018
+#define MDSS_MDP_REG_CTL_START 0x01C
+#define MDSS_MDP_REG_CTL_PACK_3D 0x020
+
+#define MDSS_MDP_CTL_OP_VIDEO_MODE (0 << 17)
+#define MDSS_MDP_CTL_OP_CMD_MODE (1 << 17)
+
+#define MDSS_MDP_CTL_OP_ROT0_MODE 0x1
+#define MDSS_MDP_CTL_OP_ROT1_MODE 0x2
+#define MDSS_MDP_CTL_OP_WB0_MODE 0x3
+#define MDSS_MDP_CTL_OP_WB1_MODE 0x4
+#define MDSS_MDP_CTL_OP_WFD_MODE 0x5
+
+#define MDSS_MDP_CTL_OP_PACK_3D_ENABLE BIT(19)
+#define MDSS_MDP_CTL_OP_PACK_3D_FRAME_INT (0 << 20)
+#define MDSS_MDP_CTL_OP_PACK_3D_H_ROW_INT (1 << 20)
+#define MDSS_MDP_CTL_OP_PACK_3D_V_ROW_INT (2 << 20)
+#define MDSS_MDP_CTL_OP_PACK_3D_COL_INT (3 << 20)
+
+enum mdss_mdp_sspp_index {
+ MDSS_MDP_SSPP_VIG0,
+ MDSS_MDP_SSPP_VIG1,
+ MDSS_MDP_SSPP_VIG2,
+ MDSS_MDP_SSPP_RGB0,
+ MDSS_MDP_SSPP_RGB1,
+ MDSS_MDP_SSPP_RGB2,
+ MDSS_MDP_SSPP_DMA0,
+ MDSS_MDP_SSPP_DMA1,
+ MDSS_MDP_MAX_SSPP
+};
+
+enum mdss_mdp_sspp_fetch_type {
+ MDSS_MDP_PLANE_INTERLEAVED,
+ MDSS_MDP_PLANE_PLANAR,
+ MDSS_MDP_PLANE_PSEUDO_PLANAR,
+};
+
+enum mdss_mdp_sspp_chroma_samp_type {
+ MDSS_MDP_CHROMA_RGB,
+ MDSS_MDP_CHROMA_H2V1,
+ MDSS_MDP_CHROMA_H1V2,
+ MDSS_MDP_CHROMA_420
+};
+
+#define MDSS_MDP_REG_SSPP_OFFSET(pipe) (0x01200 + ((pipe) * 0x400))
+
+#define MDSS_MDP_REG_SSPP_SRC_SIZE 0x000
+#define MDSS_MDP_REG_SSPP_SRC_IMG_SIZE 0x004
+#define MDSS_MDP_REG_SSPP_SRC_XY 0x008
+#define MDSS_MDP_REG_SSPP_OUT_SIZE 0x00C
+#define MDSS_MDP_REG_SSPP_OUT_XY 0x010
+#define MDSS_MDP_REG_SSPP_SRC0_ADDR 0x014
+#define MDSS_MDP_REG_SSPP_SRC1_ADDR 0x018
+#define MDSS_MDP_REG_SSPP_SRC2_ADDR 0x01C
+#define MDSS_MDP_REG_SSPP_SRC3_ADDR 0x020
+#define MDSS_MDP_REG_SSPP_SRC_YSTRIDE0 0x024
+#define MDSS_MDP_REG_SSPP_SRC_YSTRIDE1 0x028
+#define MDSS_MDP_REG_SSPP_STILE_FRAME_SIZE 0x02C
+#define MDSS_MDP_REG_SSPP_SRC_FORMAT 0x030
+#define MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN 0x034
+
+#define MDSS_MDP_REG_SSPP_SRC_OP_MODE 0x038
+#define MDSS_MDP_OP_DEINTERLACE BIT(22)
+#define MDSS_MDP_OP_DEINTERLACE_ODD BIT(23)
+#define MDSS_MDP_OP_FLIP_UD BIT(14)
+#define MDSS_MDP_OP_FLIP_LR BIT(13)
+#define MDSS_MDP_OP_BWC_EN BIT(0)
+#define MDSS_MDP_OP_BWC_LOSSLESS (0 << 1)
+#define MDSS_MDP_OP_BWC_Q_HIGH (1 << 1)
+#define MDSS_MDP_OP_BWC_Q_MED (2 << 1)
+
+#define MDSS_MDP_REG_SSPP_SRC_CONSTANT_COLOR 0x03C
+#define MDSS_MDP_REG_SSPP_FETCH_CONFIG 0x048
+#define MDSS_MDP_REG_SSPP_VC1_RANGE 0x04C
+
+#define MDSS_MDP_REG_SSPP_CURRENT_SRC0_ADDR 0x0A4
+#define MDSS_MDP_REG_SSPP_CURRENT_SRC1_ADDR 0x0A8
+#define MDSS_MDP_REG_SSPP_CURRENT_SRC2_ADDR 0x0AC
+#define MDSS_MDP_REG_SSPP_CURRENT_SRC3_ADDR 0x0B0
+#define MDSS_MDP_REG_SSPP_LINE_SKIP_STEP_C03 0x0B4
+#define MDSS_MDP_REG_SSPP_LINE_SKIP_STEP_C12 0x0B8
+
+#define MDSS_MDP_REG_VIG_OP_MODE 0x200
+#define MDSS_MDP_REG_VIG_QSEED2_CONFIG 0x204
+#define MDSS_MDP_REG_VIG_QSEED2_C03_PHASESTEPX 0x210
+#define MDSS_MDP_REG_VIG_QSEED2_C03_PHASESTEPY 0x214
+#define MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPX 0x218
+#define MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPY 0x21C
+#define MDSS_MDP_REG_VIG_QSEED2_C03_INIT_PHASEX 0x220
+#define MDSS_MDP_REG_VIG_QSEED2_C03_INIT_PHASEY 0x224
+#define MDSS_MDP_REG_VIG_QSEED2_C12_INIT_PHASEX 0x228
+#define MDSS_MDP_REG_VIG_QSEED2_C12_INIT_PHASEY 0x22C
+
+#define MDSS_MDP_REG_SCALE_CONFIG 0x204
+#define MDSS_MDP_REG_SCALE_PHASE_STEP_X 0x210
+#define MDSS_MDP_REG_SCALE_PHASE_STEP_Y 0x214
+#define MDSS_MDP_REG_SCALE_INIT_PHASE_X 0x220
+#define MDSS_MDP_REG_SCALE_INIT_PHASE_Y 0x224
+
+#define MDSS_MDP_REG_VIG_CSC_0_BASE 0x280
+#define MDSS_MDP_REG_VIG_CSC_1_BASE 0x320
+
+#define MDSS_MDP_SCALE_FILTER_NEAREST 0x0
+#define MDSS_MDP_SCALE_FILTER_BIL 0x1
+#define MDSS_MDP_SCALE_FILTER_PCMN 0x2
+#define MDSS_MDP_SCALE_FILTER_CA 0x3
+#define MDSS_MDP_SCALEY_EN BIT(1)
+#define MDSS_MDP_SCALEX_EN BIT(0)
+
+#define MDSS_MDP_NUM_REG_MIXERS 3
+#define MDSS_MDP_NUM_WB_MIXERS 2
+
+enum mdss_mdp_mixer_index {
+ MDSS_MDP_LAYERMIXER0,
+ MDSS_MDP_LAYERMIXER1,
+ MDSS_MDP_LAYERMIXER2,
+ MDSS_MDP_LAYERMIXER3,
+ MDSS_MDP_LAYERMIXER4,
+ MDSS_MDP_MAX_LAYERMIXER
+};
+
+enum mdss_mdp_stage_index {
+ MDSS_MDP_STAGE_UNUSED,
+ MDSS_MDP_STAGE_BASE,
+ MDSS_MDP_STAGE_0,
+ MDSS_MDP_STAGE_1,
+ MDSS_MDP_STAGE_2,
+ MDSS_MDP_STAGE_3,
+ MDSS_MDP_MAX_STAGE
+};
+
+enum mdss_mdp_blend_index {
+ MDSS_MDP_BLEND_STAGE0,
+ MDSS_MDP_BLEND_STAGE1,
+ MDSS_MDP_BLEND_STAGE2,
+ MDSS_MDP_BLEND_STAGE3,
+ MDSS_MDP_MAX_BLEND_STAGE,
+};
+
+#define MDSS_MDP_REG_LM_OFFSET(lm) (0x03200 + ((lm) * 0x400))
+
+#define MDSS_MDP_REG_LM_OP_MODE 0x000
+#define MDSS_MDP_REG_LM_OUT_SIZE 0x004
+#define MDSS_MDP_REG_LM_BORDER_COLOR_0 0x008
+#define MDSS_MDP_REG_LM_BORDER_COLOR_1 0x010
+
+#define MDSS_MDP_REG_LM_BLEND_OFFSET(stage) (0x20 + ((stage) * 0x30))
+#define MDSS_MDP_REG_LM_BLEND_OP 0x00
+#define MDSS_MDP_REG_LM_BLEND_FG_ALPHA 0x04
+#define MDSS_MDP_REG_LM_BLEND_BG_ALPHA 0x08
+#define MDSS_MDP_REG_LM_BLEND_FG_TRANSP_LOW0 0x0C
+#define MDSS_MDP_REG_LM_BLEND_FG_TRANSP_LOW1 0x10
+#define MDSS_MDP_REG_LM_BLEND_FG_TRANSP_HIGH0 0x14
+#define MDSS_MDP_REG_LM_BLEND_FG_TRANSP_HIGH1 0x18
+#define MDSS_MDP_REG_LM_BLEND_BG_TRANSP_LOW0 0x1C
+#define MDSS_MDP_REG_LM_BLEND_BG_TRANSP_LOW1 0x20
+#define MDSS_MDP_REG_LM_BLEND_BG_TRANSP_HIGH0 0x24
+#define MDSS_MDP_REG_LM_BLEND_BG_TRANSP_HIGH1 0x28
+
+#define MDSS_MDP_REG_LM_CURSOR_IMG_SIZE 0xE0
+#define MDSS_MDP_REG_LM_CURSOR_SIZE 0xE4
+#define MDSS_MDP_REG_LM_CURSOR_XY 0xE8
+#define MDSS_MDP_REG_LM_CURSOR_STRIDE 0xDC
+#define MDSS_MDP_REG_LM_CURSOR_FORMAT 0xEC
+#define MDSS_MDP_REG_LM_CURSOR_BASE_ADDR 0xF0
+#define MDSS_MDP_REG_LM_CURSOR_START_XY 0xF4
+#define MDSS_MDP_REG_LM_CURSOR_BLEND_CONFIG 0xF8
+#define MDSS_MDP_REG_LM_CURSOR_BLEND_PARAM 0xFC
+#define MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_LOW0 0x100
+#define MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_LOW1 0x104
+#define MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_HIGH0 0x108
+#define MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_HIGH1 0x10C
+
+#define MDSS_MDP_LM_BORDER_COLOR (1 << 24)
+#define MDSS_MDP_LM_CURSOR_OUT (1 << 25)
+#define MDSS_MDP_BLEND_FG_ALPHA_FG_CONST (0 << 0)
+#define MDSS_MDP_BLEND_FG_ALPHA_BG_CONST (1 << 0)
+#define MDSS_MDP_BLEND_FG_ALPHA_FG_PIXEL (2 << 0)
+#define MDSS_MDP_BLEND_FG_ALPHA_BG_PIXEL (3 << 0)
+#define MDSS_MDP_BLEND_FG_INV_ALPHA (1 << 2)
+#define MDSS_MDP_BLEND_FG_MOD_ALPHA (1 << 3)
+#define MDSS_MDP_BLEND_FG_INV_MOD_ALPHA (1 << 4)
+#define MDSS_MDP_BLEND_FG_TRANSP_EN (1 << 5)
+#define MDSS_MDP_BLEND_BG_ALPHA_FG_CONST (0 << 8)
+#define MDSS_MDP_BLEND_BG_ALPHA_BG_CONST (1 << 8)
+#define MDSS_MDP_BLEND_BG_ALPHA_FG_PIXEL (2 << 8)
+#define MDSS_MDP_BLEND_BG_ALPHA_BG_PIXEL (3 << 8)
+#define MDSS_MDP_BLEND_BG_INV_ALPHA (1 << 10)
+#define MDSS_MDP_BLEND_BG_MOD_ALPHA (1 << 11)
+#define MDSS_MDP_BLEND_BG_INV_MOD_ALPHA (1 << 12)
+#define MDSS_MDP_BLEND_BG_TRANSP_EN (1 << 13)
+
+enum mdss_mdp_writeback_index {
+ MDSS_MDP_WRITEBACK0,
+ MDSS_MDP_WRITEBACK1,
+ MDSS_MDP_WRITEBACK2,
+ MDSS_MDP_WRITEBACK3,
+ MDSS_MDP_WRITEBACK4,
+ MDSS_MDP_MAX_WRITEBACK
+};
+
+#define MDSS_MDP_REG_WB_OFFSET(wb) (0x11100 + ((wb) * 0x2000))
+
+#define MDSS_MDP_REG_WB_DST_FORMAT 0x000
+#define MDSS_MDP_REG_WB_DST_OP_MODE 0x004
+#define MDSS_MDP_REG_WB_DST_PACK_PATTERN 0x008
+#define MDSS_MDP_REG_WB_DST0_ADDR 0x00C
+#define MDSS_MDP_REG_WB_DST1_ADDR 0x010
+#define MDSS_MDP_REG_WB_DST2_ADDR 0x014
+#define MDSS_MDP_REG_WB_DST3_ADDR 0x018
+#define MDSS_MDP_REG_WB_DST_YSTRIDE0 0x01C
+#define MDSS_MDP_REG_WB_DST_YSTRIDE1 0x020
+#define MDSS_MDP_REG_WB_DST_YSTRIDE1 0x020
+#define MDSS_MDP_REG_WB_DST_DITHER_BITDEPTH 0x024
+#define MDSS_MDP_REG_WB_DST_MATRIX_ROW0 0x030
+#define MDSS_MDP_REG_WB_DST_MATRIX_ROW1 0x034
+#define MDSS_MDP_REG_WB_DST_MATRIX_ROW2 0x038
+#define MDSS_MDP_REG_WB_DST_MATRIX_ROW3 0x03C
+#define MDSS_MDP_REG_WB_ROTATION_DNSCALER 0x050
+#define MDSS_MDP_REG_WB_N16_INIT_PHASE_X_C03 0x060
+#define MDSS_MDP_REG_WB_N16_INIT_PHASE_X_C12 0x064
+#define MDSS_MDP_REG_WB_N16_INIT_PHASE_Y_C03 0x068
+#define MDSS_MDP_REG_WB_N16_INIT_PHASE_Y_C12 0x06C
+#define MDSS_MDP_REG_WB_OUT_SIZE 0x074
+#define MDSS_MDP_REG_WB_ALPHA_X_VALUE 0x078
+#define MDSS_MDP_REG_WB_CSC_BASE 0x260
+
+enum mdss_mdp_dspp_index {
+ MDSS_MDP_DSPP0,
+ MDSS_MDP_DSPP1,
+ MDSS_MDP_DSPP2,
+ MDSS_MDP_MAX_DSPP
+};
+
+enum mdss_mpd_intf_index {
+ MDSS_MDP_NO_INTF,
+ MDSS_MDP_INTF0,
+ MDSS_MDP_INTF1,
+ MDSS_MDP_INTF2,
+ MDSS_MDP_INTF3,
+ MDSS_MDP_MAX_INTF
+};
+
+#define MDSS_MDP_REG_INTF_OFFSET(intf) (0x20F00 + ((intf) * 0x200))
+
+#define MDSS_MDP_REG_INTF_TIMING_ENGINE_EN 0x000
+#define MDSS_MDP_REG_INTF_CONFIG 0x004
+#define MDSS_MDP_REG_INTF_HSYNC_CTL 0x008
+#define MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0 0x00C
+#define MDSS_MDP_REG_INTF_VSYNC_PERIOD_F1 0x010
+#define MDSS_MDP_REG_INTF_VSYNC_PULSE_WIDTH_F0 0x014
+#define MDSS_MDP_REG_INTF_VSYNC_PULSE_WIDTH_F1 0x018
+#define MDSS_MDP_REG_INTF_DISPLAY_V_START_F0 0x01C
+#define MDSS_MDP_REG_INTF_DISPLAY_V_START_F1 0x020
+#define MDSS_MDP_REG_INTF_DISPLAY_V_END_F0 0x024
+#define MDSS_MDP_REG_INTF_DISPLAY_V_END_F1 0x028
+#define MDSS_MDP_REG_INTF_ACTIVE_V_START_F0 0x02C
+#define MDSS_MDP_REG_INTF_ACTIVE_V_START_F1 0x030
+#define MDSS_MDP_REG_INTF_ACTIVE_V_END_F0 0x034
+#define MDSS_MDP_REG_INTF_ACTIVE_V_END_F1 0x038
+#define MDSS_MDP_REG_INTF_DISPLAY_HCTL 0x03C
+#define MDSS_MDP_REG_INTF_ACTIVE_HCTL 0x040
+#define MDSS_MDP_REG_INTF_BORDER_COLOR 0x044
+#define MDSS_MDP_REG_INTF_UNDERFLOW_COLOR 0x048
+#define MDSS_MDP_REG_INTF_HSYNC_SKEW 0x04C
+#define MDSS_MDP_REG_INTF_POLARITY_CTL 0x050
+#define MDSS_MDP_REG_INTF_TEST_CTL 0x054
+#define MDSS_MDP_REG_INTF_TP_COLOR0 0x058
+#define MDSS_MDP_REG_INTF_TP_COLOR1 0x05C
+
+#define MDSS_MDP_REG_INTF_DEFLICKER_CONFIG 0x0F0
+#define MDSS_MDP_REG_INTF_DEFLICKER_STRNG_COEFF 0x0F4
+#define MDSS_MDP_REG_INTF_DEFLICKER_WEAK_COEFF 0x0F8
+
+#define MDSS_MDP_REG_INTF_DSI_CMD_MODE_TRIGGER_EN 0x084
+#define MDSS_MDP_REG_INTF_PANEL_FORMAT 0x090
+#define MDSS_MDP_REG_INTF_TPG_ENABLE 0x100
+#define MDSS_MDP_REG_INTF_TPG_MAIN_CONTROL 0x104
+#define MDSS_MDP_REG_INTF_TPG_VIDEO_CONFIG 0x108
+#define MDSS_MDP_REG_INTF_TPG_COMPONENT_LIMITS 0x10C
+#define MDSS_MDP_REG_INTF_TPG_RECTANGLE 0x110
+#define MDSS_MDP_REG_INTF_TPG_INITIAL_VALUE 0x114
+#define MDSS_MDP_REG_INTF_TPG_BLK_WHITE_PATTERN_FRAMES 0x118
+#define MDSS_MDP_REG_INTF_TPG_RGB_MAPPING 0x11C
+
+#define MDSS_MDP_REG_INTF_FRAME_LINE_COUNT_EN 0x0A8
+#define MDSS_MDP_REG_INTF_FRAME_COUNT 0x0AC
+#define MDSS_MDP_REG_INTF_LINE_COUNT 0x0B0
+
+enum mdss_mdp_pingpong_index {
+ MDSS_MDP_PINGPONG0,
+ MDSS_MDP_PINGPONG1,
+ MDSS_MDP_PINGPONG2,
+ MDSS_MDP_MAX_PINGPONG
+};
+
+#define MDSS_MDP_REG_PP_OFFSET(pp) (0x21B00 + ((pp) * 0x100))
+
+#define MDSS_MDP_REG_PP_TEAR_CHECK_EN 0x000
+#define MDSS_MDP_REG_PP_SYNC_CONFIG_VSYNC 0x004
+#define MDSS_MDP_REG_PP_SYNC_CONFIG_HEIGHT 0x008
+#define MDSS_MDP_REG_PP_SYNC_WRCOUNT 0x00C
+#define MDSS_MDP_REG_PP_VSYNC_INIT_VAL 0x010
+#define MDSS_MDP_REG_PP_INT_COUNT_VAL 0x014
+#define MDSS_MDP_REG_PP_SYNC_THRESH 0x018
+#define MDSS_MDP_REG_PP_START_POS 0x01C
+#define MDSS_MDP_REG_PP_RD_PTR_IRQ 0x020
+#define MDSS_MDP_REG_PP_WR_PTR_IRQ 0x024
+#define MDSS_MDP_REG_PP_OUT_LINE_COUNT 0x028
+#define MDSS_MDP_REG_PP_LINE_COUNT 0x02C
+#define MDSS_MDP_REG_PP_AUTOREFRESH_CONFIG 0x030
+
+#define MDSS_MDP_REG_SMP_ALLOC_W0 0x00180
+#define MDSS_MDP_REG_SMP_ALLOC_R0 0x00230
+
+#define MDSS_MDP_SMP_MMB_SIZE 4096
+#define MDSS_MDP_SMP_MMB_BLOCKS 22
+
+enum mdss_mdp_smp_client_index {
+ MDSS_MDP_SMP_CLIENT_UNUSED,
+ MDSS_MDP_SMP_CLIENT_VIG0_FETCH_Y,
+ MDSS_MDP_SMP_CLIENT_VIG0_FETCH_CR,
+ MDSS_MDP_SMP_CLIENT_VIG0_FETCH_CB,
+ MDSS_MDP_SMP_CLIENT_VIG1_FETCH_Y,
+ MDSS_MDP_SMP_CLIENT_VIG1_FETCH_CR,
+ MDSS_MDP_SMP_CLIENT_VIG1_FETCH_CB,
+ MDSS_MDP_SMP_CLIENT_VIG2_FETCH_Y,
+ MDSS_MDP_SMP_CLIENT_VIG2_FETCH_CR,
+ MDSS_MDP_SMP_CLIENT_VIG2_FETCH_CB,
+ MDSS_MDP_SMP_CLIENT_DMA0_FETCH_Y,
+ MDSS_MDP_SMP_CLIENT_DMA0_FETCH_CR,
+ MDSS_MDP_SMP_CLIENT_DMA0_FETCH_CB,
+ MDSS_MDP_SMP_CLIENT_DMA1_FETCH_Y,
+ MDSS_MDP_SMP_CLIENT_DMA1_FETCH_CR,
+ MDSS_MDP_SMP_CLIENT_DMA1_FETCH_CB,
+ MDSS_MDP_SMP_CLIENT_RGB0_FETCH,
+ MDSS_MDP_SMP_CLIENT_RGB1_FETCH,
+ MDSS_MDP_SMP_CLIENT_RGB2_FETCH,
+};
+
+#endif /* MDSS_MDP_HWIO_H */
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_video.c b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
new file mode 100644
index 0000000..21ef290
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
@@ -0,0 +1,319 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 "mdss_fb.h"
+#include "mdss_mdp.h"
+
+/* intf timing settings */
+struct intf_timing_params {
+ u32 width;
+ u32 height;
+ u32 xres;
+ u32 yres;
+
+ u32 h_back_porch;
+ u32 h_front_porch;
+ u32 v_back_porch;
+ u32 v_front_porch;
+ u32 hsync_pulse_width;
+ u32 vsync_pulse_width;
+
+ u32 border_clr;
+ u32 underflow_clr;
+ u32 hsync_skew;
+};
+
+#define MAX_SESSIONS 3
+struct mdss_mdp_video_ctx {
+ u32 ctl_num;
+ u32 pp_num;
+ u8 ref_cnt;
+
+ u8 timegen_en;
+ struct completion pp_comp;
+ struct completion vsync_comp;
+};
+
+struct mdss_mdp_video_ctx mdss_mdp_video_ctx_list[MAX_SESSIONS];
+
+static int mdss_mdp_video_timegen_setup(struct mdss_mdp_ctl *ctl,
+ struct intf_timing_params *p)
+{
+ u32 hsync_period, vsync_period;
+ u32 hsync_start_x, hsync_end_x, display_v_start, display_v_end;
+ u32 active_h_start, active_h_end, active_v_start, active_v_end;
+ u32 display_hctl, active_hctl, hsync_ctl, polarity_ctl;
+ int off;
+
+ off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);
+
+ hsync_period = p->hsync_pulse_width + p->h_back_porch +
+ p->width + p->h_front_porch;
+ vsync_period = p->vsync_pulse_width + p->v_back_porch +
+ p->height + p->v_front_porch;
+
+ display_v_start = ((p->vsync_pulse_width + p->v_back_porch) *
+ hsync_period) + p->hsync_skew;
+ display_v_end = ((vsync_period - p->v_front_porch) * hsync_period) +
+ p->hsync_skew - 1;
+
+ if (ctl->intf_type == MDSS_INTF_EDP) {
+ display_v_start += p->hsync_pulse_width + p->h_back_porch;
+ display_v_end -= p->h_front_porch;
+ }
+
+ hsync_start_x = p->h_back_porch + p->hsync_pulse_width;
+ hsync_end_x = hsync_period - p->h_front_porch - 1;
+
+ if (p->width != p->xres) {
+ active_h_start = hsync_start_x;
+ active_h_end = active_h_start + p->xres - 1;
+ } else {
+ active_h_start = 0;
+ active_h_end = 0;
+ }
+
+ if (p->height != p->yres) {
+ active_v_start = display_v_start;
+ active_v_end = active_v_start + (p->yres * hsync_period) - 1;
+ } else {
+ active_v_start = 0;
+ active_v_end = 0;
+ }
+
+
+ if (active_h_end) {
+ active_hctl = (active_h_end << 16) | active_h_start;
+ active_hctl |= BIT(31); /* ACTIVE_H_ENABLE */
+ } else {
+ active_hctl = 0;
+ }
+
+ if (active_v_end)
+ active_v_start |= BIT(31); /* ACTIVE_V_ENABLE */
+
+ hsync_ctl = (hsync_period << 16) | p->hsync_pulse_width;
+ display_hctl = (hsync_end_x << 16) | hsync_start_x;
+ polarity_ctl = (0 << 2) | /* DEN Polarity */
+ (0 << 1) | /* VSYNC Polarity */
+ (0); /* HSYNC Polarity */
+
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_HSYNC_CTL, hsync_ctl);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0,
+ vsync_period * hsync_period);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_VSYNC_PULSE_WIDTH_F0,
+ p->vsync_pulse_width * hsync_period);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_DISPLAY_HCTL,
+ display_hctl);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_DISPLAY_V_START_F0,
+ display_v_start);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_DISPLAY_V_END_F0,
+ display_v_end);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_ACTIVE_HCTL, active_hctl);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_ACTIVE_V_START_F0,
+ active_v_start);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_ACTIVE_V_END_F0,
+ active_v_end);
+
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_BORDER_COLOR,
+ p->border_clr);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_UNDERFLOW_COLOR,
+ p->underflow_clr);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_HSYNC_SKEW,
+ p->hsync_skew);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_POLARITY_CTL,
+ polarity_ctl);
+
+ return 0;
+}
+
+static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_mdp_video_ctx *ctx;
+ int off;
+
+ pr_debug("stop ctl=%d\n", ctl->num);
+
+ ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data;
+ if (!ctx) {
+ pr_err("invalid ctx for ctl=%d\n", ctl->num);
+ return -ENODEV;
+ }
+
+ if (ctx->timegen_en) {
+ off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+ ctx->timegen_en = false;
+ }
+
+ memset(ctx, 0, sizeof(*ctx));
+
+ return 0;
+}
+
+static void mdss_mdp_video_pp_intr_done(void *arg)
+{
+ struct mdss_mdp_video_ctx *ctx;
+
+ ctx = (struct mdss_mdp_video_ctx *) arg;
+ if (!ctx) {
+ pr_err("invalid ctx\n");
+ return;
+ }
+
+ pr_debug("intr mixer=%d\n", ctx->pp_num);
+
+ complete(&ctx->pp_comp);
+}
+
+static void mdss_mdp_video_vsync_intr_done(void *arg)
+{
+ struct mdss_mdp_video_ctx *ctx;
+
+ ctx = (struct mdss_mdp_video_ctx *) arg;
+ if (!ctx) {
+ pr_err("invalid ctx\n");
+ return;
+ }
+
+ pr_debug("intr ctl=%d\n", ctx->ctl_num);
+
+ complete(&ctx->vsync_comp);
+}
+
+static int mdss_mdp_video_prepare(struct mdss_mdp_ctl *ctl, void *arg)
+{
+ struct mdss_mdp_video_ctx *ctx;
+
+ ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data;
+ if (!ctx) {
+ pr_err("invalid ctx\n");
+ return -ENODEV;
+ }
+
+ if (ctx->timegen_en) {
+ u32 intr_type = MDSS_MDP_IRQ_PING_PONG_COMP;
+
+ pr_debug("waiting for ping pong %d done\n", ctx->pp_num);
+ mdss_mdp_set_intr_callback(intr_type, ctx->pp_num,
+ mdss_mdp_video_pp_intr_done, ctx);
+ mdss_mdp_irq_enable(intr_type, ctx->pp_num);
+
+ wait_for_completion_interruptible(&ctx->pp_comp);
+ mdss_mdp_irq_disable(intr_type, ctx->pp_num);
+ }
+
+ return 0;
+}
+
+static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg)
+{
+ struct mdss_mdp_video_ctx *ctx;
+ u32 intr_type = MDSS_MDP_IRQ_INTF_VSYNC;
+
+ pr_debug("kickoff ctl=%d\n", ctl->num);
+
+ ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data;
+ if (!ctx) {
+ pr_err("invalid ctx\n");
+ return -ENODEV;
+ }
+ mdss_mdp_set_intr_callback(intr_type, ctl->intf_num,
+ mdss_mdp_video_vsync_intr_done, ctx);
+ mdss_mdp_irq_enable(intr_type, ctl->intf_num);
+
+ if (!ctx->timegen_en) {
+ int off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);
+
+ pr_debug("enabling timing gen for intf=%d\n", ctl->intf_num);
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 1);
+ ctx->timegen_en = true;
+ wmb();
+ }
+
+ wait_for_completion_interruptible(&ctx->vsync_comp);
+ mdss_mdp_irq_disable(intr_type, ctl->intf_num);
+
+ return 0;
+}
+
+int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl)
+{
+ struct msm_fb_data_type *mfd;
+ struct mdss_panel_info *pinfo;
+ struct mdss_mdp_video_ctx *ctx;
+ struct mdss_mdp_mixer *mixer;
+ struct intf_timing_params itp = {0};
+ struct fb_info *fbi;
+ int i;
+
+ mfd = ctl->mfd;
+ fbi = mfd->fbi;
+ pinfo = &mfd->panel_info;
+ mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_LEFT);
+
+ if (!mixer) {
+ pr_err("mixer not setup correctly\n");
+ return -ENODEV;
+ }
+
+ pr_debug("start ctl=%u\n", ctl->num);
+
+ for (i = 0; i < MAX_SESSIONS; i++) {
+ ctx = &mdss_mdp_video_ctx_list[i];
+ if (ctx->ref_cnt == 0) {
+ ctx->ref_cnt++;
+ break;
+ }
+ }
+ if (i == MAX_SESSIONS) {
+ pr_err("too many sessions\n");
+ return -ENOMEM;
+ }
+ ctl->priv_data = ctx;
+ ctx->ctl_num = ctl->num;
+ ctx->pp_num = mixer->num;
+ init_completion(&ctx->pp_comp);
+ init_completion(&ctx->vsync_comp);
+
+ itp.width = pinfo->xres + pinfo->lcdc.xres_pad;
+ itp.height = pinfo->yres + pinfo->lcdc.yres_pad;
+ itp.border_clr = pinfo->lcdc.border_clr;
+ itp.underflow_clr = pinfo->lcdc.underflow_clr;
+ itp.hsync_skew = pinfo->lcdc.hsync_skew;
+
+ itp.xres = fbi->var.xres;
+ itp.yres = fbi->var.yres;
+ itp.h_back_porch = fbi->var.left_margin;
+ itp.h_front_porch = fbi->var.right_margin;
+ itp.v_back_porch = fbi->var.upper_margin;
+ itp.v_front_porch = fbi->var.lower_margin;
+ itp.hsync_pulse_width = fbi->var.hsync_len;
+ itp.vsync_pulse_width = fbi->var.vsync_len;
+
+ if (mdss_mdp_video_timegen_setup(ctl, &itp)) {
+ pr_err("unable to get timing parameters\n");
+ return -EINVAL;
+ }
+
+ ctl->stop_fnc = mdss_mdp_video_stop;
+ ctl->prepare_fnc = mdss_mdp_video_prepare;
+ ctl->display_fnc = mdss_mdp_video_display;
+
+ return 0;
+}
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
new file mode 100644
index 0000000..c1bc58a
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
@@ -0,0 +1,372 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 "mdss_fb.h"
+#include "mdss_mdp.h"
+#include "mdss_mdp_rotator.h"
+
+#define ROT_BLK_SIZE 128
+
+enum mdss_mdp_writeback_type {
+ MDSS_MDP_WRITEBACK_TYPE_ROTATOR,
+ MDSS_MDP_WRITEBACK_TYPE_LINE,
+ MDSS_MDP_WRITEBACK_TYPE_WFD,
+};
+
+struct mdss_mdp_writeback_ctx {
+ u32 wb_num;
+ u8 ref_cnt;
+ u8 type;
+
+ u32 intr_type;
+ u32 intf_num;
+
+ u32 opmode;
+ u32 format;
+ u16 width;
+ u16 height;
+ u8 rot90;
+
+ struct completion comp;
+ struct mdss_mdp_plane_sizes dst_planes;
+ struct mdss_mdp_data wb_data;
+};
+
+static struct mdss_mdp_writeback_ctx wb_ctx_list[MDSS_MDP_MAX_WRITEBACK] = {
+ {
+ .type = MDSS_MDP_WRITEBACK_TYPE_ROTATOR,
+ .intr_type = MDSS_MDP_IRQ_WB_ROT_COMP,
+ .intf_num = 0,
+ },
+ {
+ .type = MDSS_MDP_WRITEBACK_TYPE_ROTATOR,
+ .intr_type = MDSS_MDP_IRQ_WB_ROT_COMP,
+ .intf_num = 1,
+ },
+ {
+ .type = MDSS_MDP_WRITEBACK_TYPE_LINE,
+ .intr_type = MDSS_MDP_IRQ_WB_ROT_COMP,
+ .intf_num = 0,
+ },
+ {
+ .type = MDSS_MDP_WRITEBACK_TYPE_LINE,
+ .intr_type = MDSS_MDP_IRQ_WB_ROT_COMP,
+ .intf_num = 1,
+ },
+ {
+ .type = MDSS_MDP_WRITEBACK_TYPE_WFD,
+ .intr_type = MDSS_MDP_IRQ_WB_WFD,
+ .intf_num = 0,
+ },
+};
+
+static void *videomemory;
+
+static int mdss_mdp_writeback_addr_setup(struct mdss_mdp_writeback_ctx *ctx,
+ struct mdss_mdp_data *data)
+{
+ int off, ret;
+
+ if (!data)
+ return -EINVAL;
+
+ pr_debug("wb_num=%d addr=0x%x\n", ctx->wb_num, data->p[0].addr);
+
+ ret = mdss_mdp_data_check(data, &ctx->dst_planes);
+ if (ret)
+ return ret;
+
+ off = MDSS_MDP_REG_WB_OFFSET(ctx->wb_num);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_WB_DST0_ADDR, data->p[0].addr);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_WB_DST1_ADDR, data->p[1].addr);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_WB_DST2_ADDR, data->p[2].addr);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_WB_DST3_ADDR, data->p[3].addr);
+
+ return 0;
+}
+
+static int mdss_mdp_writeback_format_setup(struct mdss_mdp_writeback_ctx *ctx)
+{
+ struct mdss_mdp_format_params *fmt;
+ u32 dst_format, pattern, ystride0, ystride1, outsize, chroma_samp;
+ int off, ret;
+
+ pr_debug("wb_num=%d format=%d\n", ctx->wb_num, ctx->format);
+
+ mdss_mdp_get_plane_sizes(ctx->format, ctx->width, ctx->height,
+ &ctx->dst_planes);
+
+ fmt = mdss_mdp_get_format_params(ctx->format);
+ if (!fmt) {
+ pr_err("wb format=%d not supported\n", ctx->format);
+ return ret;
+ }
+
+ chroma_samp = fmt->chroma_sample;
+ if (ctx->rot90) {
+ if (chroma_samp == MDSS_MDP_CHROMA_H2V1)
+ chroma_samp = MDSS_MDP_CHROMA_H1V2;
+ else if (chroma_samp == MDSS_MDP_CHROMA_H1V2)
+ chroma_samp = MDSS_MDP_CHROMA_H2V1;
+ }
+
+ dst_format = (chroma_samp << 23) |
+ (fmt->fetch_planes << 19) |
+ (fmt->unpack_align_msb << 18) |
+ (fmt->unpack_tight << 17) |
+ (fmt->unpack_count << 12) |
+ (fmt->bpp << 9) |
+ (fmt->alpha_enable << 8) |
+ (fmt->a_bit << 6) |
+ (fmt->r_bit << 4) |
+ (fmt->b_bit << 2) |
+ (fmt->g_bit << 0);
+
+ pattern = (fmt->element3 << 24) |
+ (fmt->element2 << 15) |
+ (fmt->element1 << 8) |
+ (fmt->element0 << 0);
+
+ ystride0 = (ctx->dst_planes.ystride[0]) |
+ (ctx->dst_planes.ystride[1] << 16);
+ ystride1 = (ctx->dst_planes.ystride[2]) |
+ (ctx->dst_planes.ystride[3] << 16);
+ outsize = (ctx->height << 16) | ctx->width;
+
+ off = MDSS_MDP_REG_WB_OFFSET(ctx->wb_num);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_WB_DST_FORMAT, dst_format);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_WB_DST_OP_MODE, ctx->opmode);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_WB_DST_PACK_PATTERN, pattern);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_WB_DST_YSTRIDE0, ystride0);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_WB_DST_YSTRIDE1, ystride1);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_WB_OUT_SIZE, outsize);
+
+ return 0;
+}
+
+static int mdss_mdp_writeback_wfd_setup(struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_writeback_ctx *ctx)
+{
+ struct msm_fb_data_type *mfd;
+ struct fb_info *fbi;
+ int ret;
+ u32 plane_size;
+
+ mfd = ctl->mfd;
+ fbi = mfd->fbi;
+
+ pr_debug("setup ctl=%d\n", ctl->num);
+
+ ctx->opmode = 0;
+ ctx->format = ctl->dst_format;
+ ctx->width = fbi->var.xres;
+ ctx->height = fbi->var.yres;
+
+ plane_size = ctx->width * ctx->height * fbi->var.bits_per_pixel / 8;
+
+ videomemory = (void *) fbi->fix.smem_start + fbi->fix.smem_len -
+ plane_size;
+
+ ctx->wb_data.num_planes = 1;
+ ctx->wb_data.p[0].addr = (u32) videomemory;
+ ctx->wb_data.p[0].len = plane_size;
+
+ ret = mdss_mdp_writeback_format_setup(ctx);
+ if (ret) {
+ pr_err("format setup failed\n");
+ return ret;
+ }
+
+ ctl->flush_bits |= BIT(16); /* WB */
+
+ return 0;
+}
+
+static int mdss_mdp_writeback_rot_setup(struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_writeback_ctx *ctx,
+ struct mdss_mdp_rotator_session *rot)
+{
+ pr_debug("rotator wb_num=%d\n", ctx->wb_num);
+
+ ctx->opmode = BIT(6); /* ROT EN */
+ if (ROT_BLK_SIZE == 128)
+ ctx->opmode |= BIT(4); /* block size 128 */
+
+ ctx->opmode |= rot->bwc_mode;
+
+ ctx->width = rot->src_rect.w;
+ ctx->height = rot->src_rect.h;
+
+ ctx->format = rot->format;
+
+ ctx->rot90 = !!(rot->rotations & MDP_ROT_90);
+ if (ctx->rot90) {
+ ctx->opmode |= BIT(5); /* ROT 90 */
+ swap(ctx->width, ctx->height);
+ }
+
+ if (mdss_mdp_writeback_format_setup(ctx))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int mdss_mdp_writeback_stop(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_mdp_writeback_ctx *ctx;
+
+ pr_debug("stop ctl=%d\n", ctl->num);
+
+ ctx = (struct mdss_mdp_writeback_ctx *) ctl->priv_data;
+ if (ctx) {
+ ctl->priv_data = NULL;
+ ctx->ref_cnt--;
+ }
+
+ return 0;
+}
+
+static int mdss_mdp_writeback_prepare(struct mdss_mdp_ctl *ctl, void *arg)
+{
+ struct mdss_mdp_writeback_ctx *ctx;
+ ctx = (struct mdss_mdp_writeback_ctx *) ctl->priv_data;
+ if (!ctx)
+ return -ENODEV;
+
+ if (ctx->type == MDSS_MDP_WRITEBACK_TYPE_ROTATOR) {
+ struct mdss_mdp_rotator_session *rot;
+ rot = (struct mdss_mdp_rotator_session *) arg;
+ if (!rot) {
+ pr_err("unable to retrieve rot session ctl=%d\n",
+ ctl->num);
+ return -ENODEV;
+ }
+ mdss_mdp_writeback_rot_setup(ctl, ctx, rot);
+ }
+
+ return 0;
+}
+
+static void mdss_mdp_writeback_intr_done(void *arg)
+{
+ struct mdss_mdp_writeback_ctx *ctx;
+
+ ctx = (struct mdss_mdp_writeback_ctx *) arg;
+ if (!ctx) {
+ pr_err("invalid ctx\n");
+ return;
+ }
+
+ pr_debug("intr wb_num=%d\n", ctx->wb_num);
+
+ mdss_mdp_irq_disable_nosync(ctx->intr_type, ctx->intf_num);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, true);
+
+ complete_all(&ctx->comp);
+}
+
+static int mdss_mdp_writeback_display(struct mdss_mdp_ctl *ctl, void *arg)
+{
+ struct mdss_mdp_writeback_ctx *ctx;
+ struct mdss_mdp_rotator_session *rot = NULL;
+ struct mdss_mdp_data *wb_data;
+ u32 flush_bits;
+ int ret;
+
+ ctx = (struct mdss_mdp_writeback_ctx *) ctl->priv_data;
+ if (!ctx)
+ return -ENODEV;
+
+ if (ctx->type == MDSS_MDP_WRITEBACK_TYPE_ROTATOR) {
+ rot = (struct mdss_mdp_rotator_session *) arg;
+ if (!rot) {
+ pr_err("unable to retrieve rot session ctl=%d\n",
+ ctl->num);
+ return -ENODEV;
+ }
+ wb_data = rot->dst_data;
+ } else {
+ wb_data = &ctx->wb_data;
+ }
+
+ ret = mdss_mdp_writeback_addr_setup(ctx, wb_data);
+ if (ret) {
+ pr_err("writeback data setup error ctl=%d\n", ctl->num);
+ return ret;
+ }
+
+ flush_bits = BIT(16); /* WB */
+ mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, flush_bits);
+
+ INIT_COMPLETION(ctx->comp);
+ mdss_mdp_set_intr_callback(ctx->intr_type, ctx->intf_num,
+ mdss_mdp_writeback_intr_done, ctx);
+ mdss_mdp_irq_enable(ctx->intr_type, ctx->intf_num);
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_START, 1);
+ wmb();
+
+ if (rot) {
+ pr_debug("rotator kickoff wb_num=%d\n", ctx->wb_num);
+ mutex_lock(&rot->lock);
+ rot->comp = &ctx->comp;
+ rot->busy = 1;
+ mutex_unlock(&rot->lock);
+ } else {
+ pr_debug("writeback kickoff wb_num=%d\n", ctx->wb_num);
+ wait_for_completion_interruptible(&ctx->comp);
+ }
+
+ return 0;
+}
+
+int mdss_mdp_writeback_start(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_mdp_writeback_ctx *ctx;
+ u32 mem_sel;
+ int ret = 0;
+
+ pr_debug("start ctl=%d\n", ctl->num);
+
+ mem_sel = (ctl->opmode & 0xF) - 1;
+ if (mem_sel < MDSS_MDP_MAX_WRITEBACK) {
+ ctx = &wb_ctx_list[mem_sel];
+ if (ctx->ref_cnt) {
+ pr_err("writeback in use %d\n", mem_sel);
+ return -EBUSY;
+ }
+ ctx->ref_cnt++;
+ } else {
+ pr_err("invalid writeback mode %d\n", mem_sel);
+ return -EINVAL;
+ }
+ ctl->priv_data = ctx;
+ ctx->wb_num = ctl->num; /* wb num should match ctl num */
+
+ init_completion(&ctx->comp);
+
+ if (ctx->type == MDSS_MDP_WRITEBACK_TYPE_WFD)
+ ret = mdss_mdp_writeback_wfd_setup(ctl, ctx);
+ else if (ctx->type == MDSS_MDP_WRITEBACK_TYPE_ROTATOR)
+ ctl->prepare_fnc = mdss_mdp_writeback_prepare;
+ else /* line mode not supported */
+ return -ENOSYS;
+
+ ctl->stop_fnc = mdss_mdp_writeback_stop;
+ ctl->display_fnc = mdss_mdp_writeback_display;
+
+ return ret;
+}
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
new file mode 100644
index 0000000..f1b158d
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -0,0 +1,815 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+
+#include "mdss_fb.h"
+#include "mdss_mdp.h"
+#include "mdss_mdp_rotator.h"
+
+#define CHECK_BOUNDS(offset, size, max_size) \
+ (((size) > (max_size)) || ((offset) > ((max_size) - (size))))
+
+static int mdss_mdp_overlay_get(struct msm_fb_data_type *mfd,
+ struct mdp_overlay *req)
+{
+ struct mdss_mdp_pipe *pipe;
+
+ pipe = mdss_mdp_pipe_get_locked(req->id);
+ if (pipe == NULL) {
+ pr_err("invalid pipe ndx=%x\n", req->id);
+ return -ENODEV;
+ }
+
+ *req = pipe->req_data;
+ mdss_mdp_pipe_unlock(pipe);
+
+ return 0;
+}
+
+static int mdss_mdp_overlay_req_check(struct msm_fb_data_type *mfd,
+ struct mdp_overlay *req,
+ struct mdss_mdp_format_params *fmt)
+{
+ u32 xres, yres;
+ u32 dst_w, dst_h;
+
+ xres = mfd->fbi->var.xres;
+ yres = mfd->fbi->var.yres;
+
+ if (req->z_order >= MDSS_MDP_MAX_STAGE) {
+ pr_err("zorder %d out of range\n", req->z_order);
+ return -ERANGE;
+ }
+
+ if (req->src.width > MAX_IMG_WIDTH ||
+ req->src.height > MAX_IMG_HEIGHT ||
+ req->src_rect.w == 0 || req->src_rect.h == 0 ||
+ req->dst_rect.w < MIN_DST_W || req->dst_rect.h < MIN_DST_H ||
+ req->dst_rect.w > MAX_DST_W || req->dst_rect.h > MAX_DST_H ||
+ CHECK_BOUNDS(req->src_rect.x, req->src_rect.w, req->src.width) ||
+ CHECK_BOUNDS(req->src_rect.y, req->src_rect.h, req->src.height) ||
+ CHECK_BOUNDS(req->dst_rect.x, req->dst_rect.w, xres) ||
+ CHECK_BOUNDS(req->dst_rect.y, req->dst_rect.h, yres)) {
+ pr_err("invalid image img_w=%d img_h=%d\n",
+ req->src.width, req->src.height);
+
+ pr_err("\tsrc_rect=%d,%d,%d,%d dst_rect=%d,%d,%d,%d\n",
+ req->src_rect.x, req->src_rect.y,
+ req->src_rect.w, req->src_rect.h,
+ req->dst_rect.x, req->dst_rect.y,
+ req->dst_rect.w, req->dst_rect.h);
+ return -EINVAL;
+ }
+
+ if (req->flags & MDP_ROT_90) {
+ dst_h = req->dst_rect.w;
+ dst_w = req->dst_rect.h;
+ } else {
+ dst_w = req->dst_rect.w;
+ dst_h = req->dst_rect.h;
+ }
+
+ if (!(req->flags & MDSS_MDP_ROT_ONLY)) {
+ if ((req->src_rect.w * MAX_UPSCALE_RATIO) < dst_w) {
+ pr_err("too much upscaling Width %d->%d\n",
+ req->src_rect.w, req->dst_rect.w);
+ return -EINVAL;
+ }
+
+ if ((req->src_rect.h * MAX_UPSCALE_RATIO) < dst_h) {
+ pr_err("too much upscaling. Height %d->%d\n",
+ req->src_rect.h, req->dst_rect.h);
+ return -EINVAL;
+ }
+
+ if (req->src_rect.w > (dst_w * MAX_DOWNSCALE_RATIO)) {
+ pr_err("too much downscaling. Width %d->%d\n",
+ req->src_rect.w, req->dst_rect.w);
+ return -EINVAL;
+ }
+
+ if (req->src_rect.h > (dst_h * MAX_DOWNSCALE_RATIO)) {
+ pr_err("too much downscaling. Height %d->%d\n",
+ req->src_rect.h, req->dst_rect.h);
+ return -EINVAL;
+ }
+ }
+
+ if (fmt->is_yuv) {
+ if ((req->src_rect.x & 0x1) || (req->src_rect.y & 0x1) ||
+ (req->src_rect.w & 0x1) || (req->src_rect.h & 0x1)) {
+ pr_err("invalid odd src resolution\n");
+ return -EINVAL;
+ }
+ if ((req->dst_rect.x & 0x1) || (req->dst_rect.y & 0x1) ||
+ (req->dst_rect.w & 0x1) || (req->dst_rect.h & 0x1)) {
+ pr_err("invalid odd dst resolution\n");
+ return -EINVAL;
+ }
+
+ if (((req->src_rect.w * (MAX_UPSCALE_RATIO / 2)) < dst_w) &&
+ (fmt->chroma_sample == MDSS_MDP_CHROMA_420 ||
+ fmt->chroma_sample == MDSS_MDP_CHROMA_H2V1)) {
+ pr_err("too much YUV upscaling Width %d->%d\n",
+ req->src_rect.w, req->dst_rect.w);
+ return -EINVAL;
+ }
+
+ if (((req->src_rect.h * (MAX_UPSCALE_RATIO / 2)) < dst_h) &&
+ (fmt->chroma_sample == MDSS_MDP_CHROMA_420 ||
+ fmt->chroma_sample == MDSS_MDP_CHROMA_H1V2)) {
+ pr_err("too much YUV upscaling Height %d->%d\n",
+ req->src_rect.h, req->dst_rect.h);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int mdss_mdp_overlay_rotator_setup(struct msm_fb_data_type *mfd,
+ struct mdp_overlay *req)
+{
+ struct mdss_mdp_rotator_session *rot;
+ struct mdss_mdp_format_params *fmt;
+ int ret = 0;
+
+ pr_debug("rot ctl=%u req id=%x\n", mfd->ctl->num, req->id);
+
+ fmt = mdss_mdp_get_format_params(req->src.format);
+ if (!fmt) {
+ pr_err("invalid rot format %d\n", req->src.format);
+ return -EINVAL;
+ }
+
+ ret = mdss_mdp_overlay_req_check(mfd, req, fmt);
+ if (ret)
+ return ret;
+
+ if (req->id == MSMFB_NEW_REQUEST) {
+ rot = mdss_mdp_rotator_session_alloc();
+
+ if (!rot) {
+ pr_err("unable to allocate rotator session\n");
+ return -ENOMEM;
+ }
+ } else if (req->id & MDSS_MDP_ROT_SESSION_MASK) {
+ rot = mdss_mdp_rotator_session_get(req->id);
+
+ if (!rot) {
+ pr_err("rotator session=%x not found\n", req->id);
+ return -ENODEV;
+ }
+ } else {
+ pr_err("invalid rotator session id=%x\n", req->id);
+ return -EINVAL;
+ }
+
+ rot->rotations = req->flags & (MDP_ROT_90 | MDP_FLIP_LR | MDP_FLIP_UD);
+
+ rot->format = fmt->format;
+ rot->img_width = req->src.width;
+ rot->img_height = req->src.height;
+ rot->src_rect.x = req->src_rect.x;
+ rot->src_rect.y = req->src_rect.y;
+ rot->src_rect.w = req->src_rect.w;
+ rot->src_rect.h = req->src_rect.h;
+
+ rot->params_changed++;
+
+ req->id = rot->session_id;
+
+ return ret;
+}
+
+static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
+ struct mdp_overlay *req,
+ struct mdss_mdp_pipe **ppipe)
+{
+ struct mdss_mdp_format_params *fmt;
+ struct mdss_mdp_pipe *pipe;
+ struct mdss_mdp_mixer *mixer = NULL;
+ u32 pipe_type, mixer_mux;
+ int ret;
+
+ if (mfd == NULL || mfd->ctl == NULL)
+ return -ENODEV;
+
+ if (req->flags & MDSS_MDP_RIGHT_MIXER)
+ mixer_mux = MDSS_MDP_MIXER_MUX_RIGHT;
+ else
+ mixer_mux = MDSS_MDP_MIXER_MUX_LEFT;
+
+ pr_debug("pipe ctl=%u req id=%x mux=%d\n", mfd->ctl->num, req->id,
+ mixer_mux);
+
+ if (req->flags & MDP_ROT_90) {
+ pr_err("unsupported inline rotation\n");
+ return -ENOTSUPP;
+ }
+
+ fmt = mdss_mdp_get_format_params(req->src.format);
+ if (!fmt) {
+ pr_err("invalid pipe format %d\n", req->src.format);
+ return -EINVAL;
+ }
+
+ ret = mdss_mdp_overlay_req_check(mfd, req, fmt);
+ if (ret)
+ return ret;
+
+ pipe = mdss_mdp_mixer_stage_pipe(mfd->ctl, mixer_mux, req->z_order);
+ if (pipe && pipe->ndx != req->id) {
+ pr_err("stage %d taken by pnum=%d\n", req->z_order, pipe->num);
+ return -EBUSY;
+ }
+
+
+ if (req->id == MSMFB_NEW_REQUEST) {
+ mixer = mdss_mdp_mixer_get(mfd->ctl, mixer_mux);
+ if (!mixer) {
+ pr_err("unable to get mixer\n");
+ return -ENODEV;
+ }
+
+ if (fmt->is_yuv || (req->flags & MDP_OV_PIPE_SHARE))
+ pipe_type = MDSS_MDP_PIPE_TYPE_VIG;
+ else
+ pipe_type = MDSS_MDP_PIPE_TYPE_RGB;
+
+ pipe = mdss_mdp_pipe_alloc_locked(pipe_type);
+
+ /* VIG pipes can also support RGB format */
+ if (!pipe && pipe_type == MDSS_MDP_PIPE_TYPE_RGB) {
+ pipe_type = MDSS_MDP_PIPE_TYPE_VIG;
+ pipe = mdss_mdp_pipe_alloc_locked(pipe_type);
+ }
+
+ if (pipe == NULL) {
+ pr_err("error allocating pipe\n");
+ return -ENOMEM;
+ }
+
+ pipe->mixer = mixer;
+ pipe->mfd = mfd;
+ } else {
+ pipe = mdss_mdp_pipe_get_locked(req->id);
+ if (pipe == NULL) {
+ pr_err("invalid pipe ndx=%x\n", req->id);
+ return -ENODEV;
+ }
+ }
+
+ pipe->flags = req->flags;
+
+ pipe->img_width = req->src.width & 0x3fff;
+ pipe->img_height = req->src.height & 0x3fff;
+ pipe->src.x = req->src_rect.x;
+ pipe->src.y = req->src_rect.y;
+ pipe->src.w = req->src_rect.w;
+ pipe->src.h = req->src_rect.h;
+ pipe->dst.x = req->dst_rect.x;
+ pipe->dst.y = req->dst_rect.y;
+ pipe->dst.w = req->dst_rect.w;
+ pipe->dst.h = req->dst_rect.h;
+
+ pipe->src_fmt = fmt;
+
+ pipe->mixer_stage = req->z_order;
+ pipe->is_fg = req->is_fg;
+ pipe->alpha = req->alpha;
+ pipe->transp = req->transp_mask;
+
+ pipe->req_data = *req;
+
+ pipe->params_changed++;
+
+ req->id = pipe->ndx;
+
+ *ppipe = pipe;
+
+ mdss_mdp_pipe_unlock(pipe);
+
+ return ret;
+}
+
+static int mdss_mdp_overlay_set(struct msm_fb_data_type *mfd,
+ struct mdp_overlay *req)
+{
+ int ret;
+
+ if (req->flags & MDSS_MDP_ROT_ONLY) {
+ ret = mdss_mdp_overlay_rotator_setup(mfd, req);
+ } else {
+ struct mdss_mdp_pipe *pipe;
+
+ /* userspace zorder start with stage 0 */
+ req->z_order += MDSS_MDP_STAGE_0;
+
+ ret = mdss_mdp_overlay_pipe_setup(mfd, req, &pipe);
+
+ req->z_order -= MDSS_MDP_STAGE_0;
+ }
+
+ return ret;
+}
+
+static int mdss_mdp_overlay_unset(struct msm_fb_data_type *mfd, int ndx)
+{
+ struct mdss_mdp_pipe *pipe;
+ struct mdss_mdp_pipe *cleanup_pipes[MDSS_MDP_MAX_SSPP];
+ int i, ret = 0, clean_cnt = 0;
+ u32 pipe_ndx, unset_ndx = 0;
+
+ if (!mfd || !mfd->ctl)
+ return -ENODEV;
+
+ pr_debug("unset ndx=%x\n", ndx);
+
+ if (ndx & MDSS_MDP_ROT_SESSION_MASK) {
+ struct mdss_mdp_rotator_session *rot;
+ rot = mdss_mdp_rotator_session_get(ndx);
+ if (rot) {
+ mdss_mdp_rotator_finish(rot);
+ } else {
+ pr_warn("unknown session id=%x\n", ndx);
+ ret = -ENODEV;
+ }
+
+ return ret;
+ }
+
+ for (i = 0; unset_ndx != ndx && i < MDSS_MDP_MAX_SSPP; i++) {
+ pipe_ndx = BIT(i);
+ if (pipe_ndx & ndx) {
+ unset_ndx |= pipe_ndx;
+ pipe = mdss_mdp_pipe_get_locked(pipe_ndx);
+ if (pipe) {
+ mdss_mdp_mixer_pipe_unstage(pipe);
+ cleanup_pipes[clean_cnt++] = pipe;
+ } else {
+ pr_warn("unknown pipe ndx=%x\n", pipe_ndx);
+ }
+ }
+ }
+
+ if (clean_cnt) {
+ ret = mfd->kickoff_fnc(mfd->ctl);
+
+ for (i = 0; i < clean_cnt; i++)
+ mdss_mdp_pipe_destroy(cleanup_pipes[i]);
+ }
+
+ return ret;
+}
+
+static int mdss_mdp_overlay_play_wait(struct msm_fb_data_type *mfd,
+ struct msmfb_overlay_data *req)
+{
+ int ret;
+
+ if (!mfd || !mfd->ctl)
+ return -ENODEV;
+
+ ret = mfd->kickoff_fnc(mfd->ctl);
+ if (!ret)
+ pr_err("error displaying\n");
+
+ return ret;
+}
+
+static int mdss_mdp_overlay_rotate(struct msmfb_overlay_data *req,
+ struct mdss_mdp_data *src_data,
+ struct mdss_mdp_data *dst_data)
+{
+ struct mdss_mdp_rotator_session *rot;
+ int ret;
+
+ rot = mdss_mdp_rotator_session_get(req->id);
+ if (!rot) {
+ pr_err("invalid session id=%x\n", req->id);
+ return -ENODEV;
+ }
+
+ ret = mdss_mdp_rotator_queue(rot, src_data, dst_data);
+ if (ret) {
+ pr_err("rotator queue error session id=%x\n", req->id);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mdss_mdp_overlay_queue(struct msmfb_overlay_data *req,
+ struct mdss_mdp_data *src_data)
+{
+ struct mdss_mdp_pipe *pipe;
+ struct mdss_mdp_ctl *ctl;
+ int ret;
+
+ pipe = mdss_mdp_pipe_get_locked(req->id);
+ if (pipe == NULL) {
+ pr_err("pipe ndx=%x doesn't exist\n", req->id);
+ return -ENODEV;
+ }
+
+ pr_debug("ov queue pnum=%d\n", pipe->num);
+
+ ret = mdss_mdp_pipe_queue_data(pipe, src_data);
+ ctl = pipe->mixer->ctl;
+ mdss_mdp_pipe_unlock(pipe);
+
+ if (ret == 0 && !(pipe->flags & MDP_OV_PLAY_NOWAIT))
+ ret = ctl->mfd->kickoff_fnc(ctl);
+
+
+ return ret;
+}
+
+static int mdss_mdp_overlay_play(struct msm_fb_data_type *mfd,
+ struct msmfb_overlay_data *req)
+{
+ struct mdss_mdp_data src_data;
+ int ret = 0;
+
+ if (mfd == NULL)
+ return -ENODEV;
+
+ pr_debug("play req id=%x\n", req->id);
+
+ memset(&src_data, 0, sizeof(src_data));
+ mdss_mdp_get_img(mfd->iclient, &req->data, &src_data.p[0]);
+ if (src_data.p[0].len == 0) {
+ pr_err("src data pmem error\n");
+ return -ENOMEM;
+ }
+ src_data.num_planes = 1;
+
+ if (req->id & MDSS_MDP_ROT_SESSION_MASK) {
+ struct mdss_mdp_data dst_data;
+ memset(&dst_data, 0, sizeof(dst_data));
+
+ mdss_mdp_get_img(mfd->iclient, &req->dst_data, &dst_data.p[0]);
+ if (dst_data.p[0].len == 0) {
+ pr_err("dst data pmem error\n");
+ return -ENOMEM;
+ }
+ dst_data.num_planes = 1;
+
+ ret = mdss_mdp_overlay_rotate(req, &src_data, &dst_data);
+
+ mdss_mdp_put_img(&dst_data.p[0]);
+ } else {
+ ret = mdss_mdp_overlay_queue(req, &src_data);
+ }
+
+ mdss_mdp_put_img(&src_data.p[0]);
+
+ return ret;
+}
+
+static int mdss_mdp_overlay_get_fb_pipe(struct msm_fb_data_type *mfd,
+ struct mdss_mdp_pipe **ppipe,
+ int mixer_mux)
+{
+ struct mdss_mdp_pipe *pipe;
+
+ pipe = mdss_mdp_mixer_stage_pipe(mfd->ctl, mixer_mux,
+ MDSS_MDP_STAGE_BASE);
+ if (pipe == NULL) {
+ struct mdp_overlay req;
+ int ret;
+
+ memset(&req, 0, sizeof(req));
+
+ req.id = MSMFB_NEW_REQUEST;
+ req.src.format = mfd->fb_imgType;
+ req.src.height = mfd->fbi->var.yres;
+ req.src.width = mfd->fbi->var.xres;
+ if (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT) {
+ if (req.src.width <= MAX_MIXER_WIDTH)
+ return -ENODEV;
+
+ req.flags |= MDSS_MDP_RIGHT_MIXER;
+ req.src_rect.x = MAX_MIXER_WIDTH;
+ req.src_rect.w = req.src.width - MAX_MIXER_WIDTH;
+ } else {
+ req.src_rect.x = 0;
+ req.src_rect.w = MIN(req.src.width, MAX_MIXER_WIDTH);
+ }
+
+ req.src_rect.y = 0;
+ req.src_rect.h = req.src.height;
+ req.dst_rect.x = req.src_rect.x;
+ 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);
+
+ ret = mdss_mdp_overlay_pipe_setup(mfd, &req, &pipe);
+ if (ret)
+ return ret;
+
+ pr_debug("ctl=%d pnum=%d\n", mfd->ctl->num, pipe->num);
+ }
+
+ *ppipe = pipe;
+ return 0;
+}
+
+static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd)
+{
+ struct mdss_mdp_data data;
+ struct mdss_mdp_pipe *pipe;
+ struct fb_info *fbi;
+ u32 offset;
+ int bpp, ret;
+
+ if (!mfd)
+ return;
+
+ if (!mfd->ctl || !mfd->panel_power_on)
+ return;
+
+ fbi = mfd->fbi;
+
+ if (fbi->fix.smem_len == 0) {
+ pr_warn("fb memory not allocated\n");
+ return;
+ }
+
+ memset(&data, 0, sizeof(data));
+
+ bpp = fbi->var.bits_per_pixel / 8;
+ offset = fbi->var.xoffset * bpp +
+ fbi->var.yoffset * fbi->fix.line_length;
+
+ data.p[0].addr = fbi->fix.smem_start + offset;
+ data.p[0].len = fbi->fix.smem_len - offset;
+ data.num_planes = 1;
+
+ ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe, MDSS_MDP_MIXER_MUX_LEFT);
+ if (ret) {
+ pr_err("unable to allocate base pipe\n");
+ return;
+ }
+
+ mdss_mdp_pipe_lock(pipe);
+ ret = mdss_mdp_pipe_queue_data(pipe, &data);
+ mdss_mdp_pipe_unlock(pipe);
+ if (ret) {
+ pr_err("unable to queue data\n");
+ return;
+ }
+
+ if (fbi->var.xres > MAX_MIXER_WIDTH) {
+ ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe,
+ MDSS_MDP_MIXER_MUX_RIGHT);
+ if (ret) {
+ pr_err("unable to allocate right base pipe\n");
+ return;
+ }
+ mdss_mdp_pipe_lock(pipe);
+ ret = mdss_mdp_pipe_queue_data(pipe, &data);
+ mdss_mdp_pipe_unlock(pipe);
+ if (ret) {
+ pr_err("unable to queue right data\n");
+ return;
+ }
+ }
+
+ if (fbi->var.activate & FB_ACTIVATE_VBL)
+ mfd->kickoff_fnc(mfd->ctl);
+}
+
+static int mdss_mdp_hw_cursor_update(struct fb_info *info,
+ struct fb_cursor *cursor)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ struct mdss_mdp_mixer *mixer;
+ struct fb_image *img = &cursor->image;
+ int calpha_en, transp_en, blendcfg, alpha;
+ int off, ret = 0;
+
+ mixer = mdss_mdp_mixer_get(mfd->ctl, MDSS_MDP_MIXER_MUX_DEFAULT);
+ off = MDSS_MDP_REG_LM_OFFSET(mixer->num);
+
+ if ((img->width > MDSS_MDP_CURSOR_WIDTH) ||
+ (img->height > MDSS_MDP_CURSOR_HEIGHT) ||
+ (img->depth != 32))
+ return -EINVAL;
+
+ pr_debug("mixer=%d enable=%x set=%x\n", mixer->num, cursor->enable,
+ cursor->set);
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ blendcfg = MDSS_MDP_REG_READ(off + MDSS_MDP_REG_LM_CURSOR_BLEND_CONFIG);
+
+ if (cursor->set & FB_CUR_SETPOS)
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_START_XY,
+ (img->dy << 16) | img->dx);
+
+ if (cursor->set & FB_CUR_SETIMAGE) {
+ ret = copy_from_user(mfd->cursor_buf, img->data,
+ img->width * img->height * 4);
+ if (ret)
+ return ret;
+
+ if (img->bg_color == 0xffffffff)
+ transp_en = 0;
+ else
+ transp_en = 1;
+
+ alpha = (img->fg_color & 0xff000000) >> 24;
+
+ if (alpha)
+ calpha_en = 0x0; /* xrgb */
+ else
+ calpha_en = 0x2; /* argb */
+
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_SIZE,
+ (img->height << 16) | img->width);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_STRIDE,
+ img->width * 4);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_BASE_ADDR,
+ mfd->cursor_buf_phys);
+
+ wmb();
+
+ blendcfg &= ~0x1;
+ blendcfg |= (transp_en << 3) | (calpha_en << 1);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_BLEND_CONFIG,
+ blendcfg);
+ if (calpha_en)
+ MDSS_MDP_REG_WRITE(off +
+ MDSS_MDP_REG_LM_CURSOR_BLEND_PARAM,
+ alpha);
+
+ if (transp_en) {
+ MDSS_MDP_REG_WRITE(off +
+ MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_LOW0,
+ ((img->bg_color & 0xff00) << 8) |
+ (img->bg_color & 0xff));
+ MDSS_MDP_REG_WRITE(off +
+ MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_LOW1,
+ ((img->bg_color & 0xff0000) >> 16));
+ MDSS_MDP_REG_WRITE(off +
+ MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_HIGH0,
+ ((img->bg_color & 0xff00) << 8) |
+ (img->bg_color & 0xff));
+ MDSS_MDP_REG_WRITE(off +
+ MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_HIGH1,
+ ((img->bg_color & 0xff0000) >> 16));
+ }
+ }
+
+ if (!cursor->enable != !(blendcfg & 0x1)) {
+ if (cursor->enable)
+ blendcfg |= 0x1;
+ else
+ blendcfg &= ~0x1;
+
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_BLEND_CONFIG,
+ blendcfg);
+
+ mixer->cursor_enabled = cursor->enable;
+ mixer->params_changed++;
+ }
+
+ mixer->ctl->flush_bits |= BIT(6) << mixer->num;
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+
+ return 0;
+}
+
+static int mdss_mdp_overlay_kickoff(struct mdss_mdp_ctl *ctl)
+{
+ return mdss_mdp_display_commit(ctl, NULL);
+}
+
+static int mdss_mdp_overlay_ioctl_handler(struct msm_fb_data_type *mfd,
+ u32 cmd, void __user *argp)
+{
+ struct mdp_overlay req;
+ int val, ret = -ENOSYS;
+
+ switch (cmd) {
+ case MSMFB_OVERLAY_GET:
+ ret = copy_from_user(&req, argp, sizeof(req));
+ if (!ret) {
+ ret = mdss_mdp_overlay_get(mfd, &req);
+
+ if (!IS_ERR_VALUE(ret))
+ ret = copy_to_user(argp, &req, sizeof(req));
+ }
+
+ if (ret) {
+ pr_err("OVERLAY_GET failed (%d)\n", ret);
+ ret = -EFAULT;
+ }
+ break;
+
+ case MSMFB_OVERLAY_SET:
+ ret = copy_from_user(&req, argp, sizeof(req));
+ if (!ret) {
+ ret = mdss_mdp_overlay_set(mfd, &req);
+
+ if (!IS_ERR_VALUE(ret))
+ ret = copy_to_user(argp, &req, sizeof(req));
+ }
+ if (ret) {
+ pr_err("OVERLAY_SET failed (%d)\n", ret);
+ ret = -EFAULT;
+ }
+ break;
+
+
+ case MSMFB_OVERLAY_UNSET:
+ if (!IS_ERR_VALUE(copy_from_user(&val, argp, sizeof(val))))
+ ret = mdss_mdp_overlay_unset(mfd, val);
+ break;
+
+ case MSMFB_OVERLAY_PLAY_ENABLE:
+ if (!copy_from_user(&val, argp, sizeof(val))) {
+ mfd->overlay_play_enable = val;
+ } else {
+ pr_err("OVERLAY_PLAY_ENABLE failed (%d)\n", ret);
+ ret = -EFAULT;
+ }
+ break;
+
+ case MSMFB_OVERLAY_PLAY:
+ if (mfd->overlay_play_enable) {
+ struct msmfb_overlay_data data;
+
+ ret = copy_from_user(&data, argp, sizeof(data));
+ if (!ret) {
+ ret = mdss_mdp_overlay_play(mfd, &data);
+ if (!IS_ERR_VALUE(ret))
+ mdss_fb_update_backlight(mfd);
+ }
+
+ if (ret) {
+ pr_err("OVERLAY_PLAY failed (%d)\n", ret);
+ ret = -EFAULT;
+ }
+ } else {
+ ret = 0;
+ }
+ break;
+
+ case MSMFB_OVERLAY_PLAY_WAIT:
+ if (mfd->overlay_play_enable) {
+ struct msmfb_overlay_data data;
+
+ ret = copy_from_user(&data, argp, sizeof(data));
+ if (!ret)
+ ret = mdss_mdp_overlay_play_wait(mfd, &data);
+
+ if (ret) {
+ pr_err("OVERLAY_PLAY_WAIT failed (%d)\n", ret);
+ ret = -EFAULT;
+ }
+ } else {
+ ret = 0;
+ }
+ break;
+ }
+
+ return ret;
+}
+
+int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
+{
+ mfd->on_fnc = mdss_mdp_ctl_on;
+ mfd->off_fnc = mdss_mdp_ctl_off;
+ mfd->hw_refresh = true;
+ mfd->lut_update = NULL;
+ mfd->do_histogram = NULL;
+ mfd->overlay_play_enable = true;
+ mfd->cursor_update = mdss_mdp_hw_cursor_update;
+ mfd->dma_fnc = mdss_mdp_overlay_pan_display;
+ mfd->ioctl_handler = mdss_mdp_overlay_ioctl_handler;
+ mfd->kickoff_fnc = mdss_mdp_overlay_kickoff;
+
+ return 0;
+}
diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c
new file mode 100644
index 0000000..52f4324
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c
@@ -0,0 +1,680 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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/bitmap.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+
+#include "mdss_mdp.h"
+
+#define SMP_MB_CNT (mdss_res->smp_mb_cnt)
+
+static DEFINE_MUTEX(mdss_mdp_sspp_lock);
+static DECLARE_BITMAP(mdss_mdp_smp_mmb_pool, MDSS_MDP_SMP_MMB_BLOCKS);
+
+static struct mdss_mdp_pipe mdss_mdp_pipe_list[MDSS_MDP_MAX_SSPP];
+
+static u32 mdss_mdp_smp_mmb_reserve(unsigned long *smp, size_t n)
+{
+ u32 i, mmb;
+
+ /* reserve more blocks if needed, but can't free mmb at this point */
+ for (i = bitmap_weight(smp, SMP_MB_CNT); i < n; i++) {
+ if (bitmap_full(mdss_mdp_smp_mmb_pool, SMP_MB_CNT))
+ break;
+
+ mmb = find_first_zero_bit(mdss_mdp_smp_mmb_pool, SMP_MB_CNT);
+ set_bit(mmb, smp);
+ set_bit(mmb, mdss_mdp_smp_mmb_pool);
+ }
+ return i;
+}
+
+static void mdss_mdp_smp_mmb_set(int client_id, unsigned long *smp)
+{
+ u32 mmb, off, data, s;
+
+ for_each_set_bit(mmb, smp, SMP_MB_CNT) {
+ off = (mmb / 3) * 4;
+ s = (mmb % 3) * 8;
+ data = MDSS_MDP_REG_READ(MDSS_MDP_REG_SMP_ALLOC_W0 + off);
+ data &= ~(0xFF << s);
+ data |= client_id << s;
+ MDSS_MDP_REG_WRITE(MDSS_MDP_REG_SMP_ALLOC_W0 + off, data);
+ MDSS_MDP_REG_WRITE(MDSS_MDP_REG_SMP_ALLOC_R0 + off, data);
+ }
+}
+
+static void mdss_mdp_smp_mmb_free(unsigned long *smp)
+{
+ if (!bitmap_empty(smp, SMP_MB_CNT)) {
+ mdss_mdp_smp_mmb_set(MDSS_MDP_SMP_CLIENT_UNUSED, smp);
+ bitmap_andnot(mdss_mdp_smp_mmb_pool, mdss_mdp_smp_mmb_pool,
+ smp, SMP_MB_CNT);
+ bitmap_zero(smp, SMP_MB_CNT);
+ }
+}
+
+static void mdss_mdp_smp_free(struct mdss_mdp_pipe *pipe)
+{
+ mdss_mdp_smp_mmb_free(&pipe->smp[0]);
+ mdss_mdp_smp_mmb_free(&pipe->smp[1]);
+ mdss_mdp_smp_mmb_free(&pipe->smp[2]);
+}
+
+static int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe)
+{
+ u32 num_blks = 0, reserved = 0;
+ int i;
+
+ if ((pipe->src_planes.num_planes > 1) &&
+ (pipe->type == MDSS_MDP_PIPE_TYPE_RGB))
+ return -EINVAL;
+
+ mutex_lock(&mdss_mdp_sspp_lock);
+ for (i = 0; i < pipe->src_planes.num_planes; i++) {
+ num_blks = DIV_ROUND_UP(2 * pipe->src_planes.ystride[i],
+ mdss_res->smp_mb_size);
+
+ pr_debug("reserving %d mmb for pnum=%d plane=%d\n",
+ num_blks, pipe->num, i);
+ reserved = mdss_mdp_smp_mmb_reserve(&pipe->smp[i], num_blks);
+
+ if (reserved < num_blks)
+ break;
+ }
+
+ if (reserved < num_blks) {
+ pr_err("insufficient MMB blocks\n");
+ mdss_mdp_smp_free(pipe);
+ return -ENOMEM;
+ }
+ mutex_unlock(&mdss_mdp_sspp_lock);
+
+ return 0;
+}
+
+static int mdss_mdp_smp_alloc(struct mdss_mdp_pipe *pipe)
+{
+ u32 client_id;
+ int i;
+
+ switch (pipe->num) {
+ case MDSS_MDP_SSPP_VIG0:
+ client_id = MDSS_MDP_SMP_CLIENT_VIG0_FETCH_Y;
+ break;
+ case MDSS_MDP_SSPP_VIG1:
+ client_id = MDSS_MDP_SMP_CLIENT_VIG1_FETCH_Y;
+ break;
+ case MDSS_MDP_SSPP_VIG2:
+ client_id = MDSS_MDP_SMP_CLIENT_VIG2_FETCH_Y;
+ break;
+ case MDSS_MDP_SSPP_RGB0:
+ client_id = MDSS_MDP_SMP_CLIENT_RGB0_FETCH;
+ break;
+ case MDSS_MDP_SSPP_RGB1:
+ client_id = MDSS_MDP_SMP_CLIENT_RGB1_FETCH;
+ break;
+ case MDSS_MDP_SSPP_RGB2:
+ client_id = MDSS_MDP_SMP_CLIENT_RGB2_FETCH;
+ break;
+ case MDSS_MDP_SSPP_DMA0:
+ client_id = MDSS_MDP_SMP_CLIENT_DMA0_FETCH_Y;
+ break;
+ case MDSS_MDP_SSPP_DMA1:
+ client_id = MDSS_MDP_SMP_CLIENT_DMA1_FETCH_Y;
+ break;
+ default:
+ pr_err("no valid smp client for pnum=%d\n", pipe->num);
+ return -EINVAL;
+ }
+
+ mutex_lock(&mdss_mdp_sspp_lock);
+ for (i = 0; i < pipe->src_planes.num_planes; i++)
+ mdss_mdp_smp_mmb_set(client_id + i, &pipe->smp[i]);
+ mutex_unlock(&mdss_mdp_sspp_lock);
+ return 0;
+}
+
+void mdss_mdp_pipe_unlock(struct mdss_mdp_pipe *pipe)
+{
+ atomic_dec(&pipe->ref_cnt);
+ mutex_unlock(&pipe->lock);
+}
+
+int mdss_mdp_pipe_lock(struct mdss_mdp_pipe *pipe)
+{
+ if (atomic_inc_not_zero(&pipe->ref_cnt)) {
+ if (mutex_lock_interruptible(&pipe->lock)) {
+ atomic_dec(&pipe->ref_cnt);
+ return -EINTR;
+ }
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static struct mdss_mdp_pipe *mdss_mdp_pipe_init(u32 pnum)
+{
+ struct mdss_mdp_pipe *pipe = NULL;
+
+ if (atomic_read(&mdss_mdp_pipe_list[pnum].ref_cnt) == 0) {
+ pipe = &mdss_mdp_pipe_list[pnum];
+ memset(pipe, 0, sizeof(*pipe));
+
+ mutex_init(&pipe->lock);
+ atomic_set(&pipe->ref_cnt, 1);
+
+ if (mdss_mdp_pipe_lock(pipe) == 0) {
+ pipe->num = pnum;
+ pipe->type = mdss_res->pipe_type_map[pnum];
+ pipe->ndx = BIT(pnum);
+
+ pr_debug("ndx=%x pnum=%d\n", pipe->ndx, pipe->num);
+ } else {
+ atomic_set(&pipe->ref_cnt, 0);
+ pipe = NULL;
+ }
+ }
+ return pipe;
+}
+
+struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_pnum(u32 pnum)
+{
+ struct mdss_mdp_pipe *pipe = NULL;
+ mutex_lock(&mdss_mdp_sspp_lock);
+ if (mdss_res->pipe_type_map[pnum] != MDSS_MDP_PIPE_TYPE_UNUSED)
+ pipe = mdss_mdp_pipe_init(pnum);
+ mutex_unlock(&mdss_mdp_sspp_lock);
+ return pipe;
+}
+
+struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_locked(u32 type)
+{
+ struct mdss_mdp_pipe *pipe = NULL;
+ int pnum;
+
+ mutex_lock(&mdss_mdp_sspp_lock);
+ for (pnum = 0; pnum < MDSS_MDP_MAX_SSPP; pnum++) {
+ if (type == mdss_res->pipe_type_map[pnum]) {
+ pipe = mdss_mdp_pipe_init(pnum);
+ if (pipe)
+ break;
+ }
+ }
+ mutex_unlock(&mdss_mdp_sspp_lock);
+
+ return pipe;
+}
+
+struct mdss_mdp_pipe *mdss_mdp_pipe_get_locked(u32 ndx)
+{
+ struct mdss_mdp_pipe *pipe = NULL;
+ int i;
+
+ if (!ndx)
+ return NULL;
+
+ mutex_lock(&mdss_mdp_sspp_lock);
+ for (i = 0; i < MDSS_MDP_MAX_SSPP; i++) {
+ pipe = &mdss_mdp_pipe_list[i];
+ if (ndx == pipe->ndx)
+ break;
+ }
+ mutex_unlock(&mdss_mdp_sspp_lock);
+
+ if (i == MDSS_MDP_MAX_SSPP)
+ return NULL;
+
+ if (mdss_mdp_pipe_lock(pipe))
+ return NULL;
+
+ if (pipe->ndx != ndx) {
+ mdss_mdp_pipe_unlock(pipe);
+ pipe = NULL;
+ }
+
+ return pipe;
+}
+
+
+static void mdss_mdp_pipe_free(struct mdss_mdp_pipe *pipe)
+{
+ mdss_mdp_smp_free(pipe);
+ pipe->ndx = 0;
+ atomic_dec(&pipe->ref_cnt);
+ mdss_mdp_pipe_unlock(pipe);
+}
+
+int mdss_mdp_pipe_destroy(struct mdss_mdp_pipe *pipe)
+{
+ pr_debug("ndx=%x pnum=%d ref_cnt=%d\n", pipe->ndx, pipe->num,
+ atomic_read(&pipe->ref_cnt));
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ mutex_lock(&mdss_mdp_sspp_lock);
+ mdss_mdp_pipe_free(pipe);
+ mutex_unlock(&mdss_mdp_sspp_lock);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+
+ return 0;
+}
+
+int mdss_mdp_pipe_release_all(struct msm_fb_data_type *mfd)
+{
+ struct mdss_mdp_pipe *pipe;
+ int i;
+
+ if (!mfd)
+ return -ENODEV;
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ mutex_lock(&mdss_mdp_sspp_lock);
+ for (i = 0; i < MDSS_MDP_MAX_SSPP; i++) {
+ pipe = &mdss_mdp_pipe_list[i];
+ if (atomic_read(&pipe->ref_cnt) && pipe->mfd == mfd) {
+ pr_debug("release pnum=%d\n", pipe->num);
+ if (mdss_mdp_pipe_lock(pipe) == 0) {
+ mdss_mdp_mixer_pipe_unstage(pipe);
+ mdss_mdp_pipe_free(pipe);
+ } else {
+ pr_err("unable to lock pipe=%d for release",
+ pipe->num);
+ }
+ }
+ }
+ mutex_unlock(&mdss_mdp_sspp_lock);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+
+ return 0;
+}
+
+static inline void mdss_mdp_pipe_write(struct mdss_mdp_pipe *pipe,
+ u32 reg, u32 val)
+{
+ int offset = MDSS_MDP_REG_SSPP_OFFSET(pipe->num);
+ MDSS_MDP_REG_WRITE(offset + reg, val);
+}
+
+static inline u32 mdss_mdp_pipe_read(struct mdss_mdp_pipe *pipe, u32 reg)
+{
+ int offset = MDSS_MDP_REG_SSPP_OFFSET(pipe->num);
+ return MDSS_MDP_REG_READ(offset + reg);
+}
+
+static int mdss_mdp_leading_zero(u32 num)
+{
+ u32 bit = 0x80000000;
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ if (bit & num)
+ return i;
+ bit >>= 1;
+ }
+
+ return i;
+}
+
+static u32 mdss_mdp_scale_phase_step(int f_num, u32 src, u32 dst)
+{
+ u32 val, s;
+ int n;
+
+ n = mdss_mdp_leading_zero(src);
+ if (n > f_num)
+ n = f_num;
+ s = src << n; /* maximum to reduce lose of resolution */
+ val = s / dst;
+ if (n < f_num) {
+ n = f_num - n;
+ val <<= n;
+ val |= ((s % dst) << n) / dst;
+ }
+
+ return val;
+}
+
+static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe)
+{
+ u32 scale_config = 0;
+ u32 phasex_step = 0, phasey_step = 0;
+ u32 chroma_sample;
+
+ if (pipe->type == MDSS_MDP_PIPE_TYPE_DMA) {
+ if (!(pipe->flags & MDP_ROT_90) && (pipe->dst.h != pipe->src.h
+ || pipe->dst.w != pipe->src.w))
+ return -EINVAL; /* no scaling supported on dma pipes */
+ else
+ return 0;
+ }
+
+ chroma_sample = pipe->src_fmt->chroma_sample;
+
+ if ((pipe->src.h != pipe->dst.h) ||
+ (chroma_sample == MDSS_MDP_CHROMA_420) ||
+ (chroma_sample == MDSS_MDP_CHROMA_H1V2)) {
+ pr_debug("scale y - src_h=%d dst_h=%d\n",
+ pipe->src.h, pipe->dst.h);
+
+ if ((pipe->src.h / MAX_DOWNSCALE_RATIO) > pipe->dst.h) {
+ pr_err("too much downscaling height=%d->%d",
+ pipe->src.h, pipe->dst.h);
+ return -EINVAL;
+ }
+
+ scale_config |= MDSS_MDP_SCALEY_EN;
+
+ if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) {
+ u32 chr_dst_h = pipe->dst.h;
+ if ((chroma_sample == MDSS_MDP_CHROMA_420) ||
+ (chroma_sample == MDSS_MDP_CHROMA_H1V2))
+ chr_dst_h *= 2; /* 2x upsample chroma */
+
+ if (pipe->src.h <= pipe->dst.h)
+ scale_config |= /* G/Y, A */
+ (MDSS_MDP_SCALE_FILTER_BIL << 10) |
+ (MDSS_MDP_SCALE_FILTER_NEAREST << 18);
+ else
+ scale_config |= /* G/Y, A */
+ (MDSS_MDP_SCALE_FILTER_PCMN << 10) |
+ (MDSS_MDP_SCALE_FILTER_NEAREST << 18);
+
+ if (pipe->src.h <= chr_dst_h)
+ scale_config |= /* CrCb */
+ (MDSS_MDP_SCALE_FILTER_BIL << 14);
+ else
+ scale_config |= /* CrCb */
+ (MDSS_MDP_SCALE_FILTER_PCMN << 14);
+
+ phasey_step = mdss_mdp_scale_phase_step(
+ PHASE_STEP_SHIFT, pipe->src.h, chr_dst_h);
+
+ mdss_mdp_pipe_write(pipe,
+ MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPY,
+ phasey_step);
+ } else {
+ if (pipe->src.h <= pipe->dst.h)
+ scale_config |= /* RGB, A */
+ (MDSS_MDP_SCALE_FILTER_BIL << 10) |
+ (MDSS_MDP_SCALE_FILTER_NEAREST << 18);
+ else
+ scale_config |= /* RGB, A */
+ (MDSS_MDP_SCALE_FILTER_PCMN << 10) |
+ (MDSS_MDP_SCALE_FILTER_NEAREST << 18);
+ }
+
+ phasey_step = mdss_mdp_scale_phase_step(
+ PHASE_STEP_SHIFT, pipe->src.h, pipe->dst.h);
+ }
+
+ if ((pipe->src.w != pipe->dst.w) ||
+ (chroma_sample == MDSS_MDP_CHROMA_420) ||
+ (chroma_sample == MDSS_MDP_CHROMA_H2V1)) {
+ pr_debug("scale x - src_w=%d dst_w=%d\n",
+ pipe->src.w, pipe->dst.w);
+
+ if ((pipe->src.w / MAX_DOWNSCALE_RATIO) > pipe->dst.w) {
+ pr_err("too much downscaling width=%d->%d",
+ pipe->src.w, pipe->dst.w);
+ return -EINVAL;
+ }
+
+ scale_config |= MDSS_MDP_SCALEX_EN;
+
+ if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) {
+ u32 chr_dst_w = pipe->dst.w;
+
+ if ((chroma_sample == MDSS_MDP_CHROMA_420) ||
+ (chroma_sample == MDSS_MDP_CHROMA_H2V1))
+ chr_dst_w *= 2; /* 2x upsample chroma */
+
+ if (pipe->src.w <= pipe->dst.w)
+ scale_config |= /* G/Y, A */
+ (MDSS_MDP_SCALE_FILTER_BIL << 8) |
+ (MDSS_MDP_SCALE_FILTER_NEAREST << 16);
+ else
+ scale_config |= /* G/Y, A */
+ (MDSS_MDP_SCALE_FILTER_PCMN << 8) |
+ (MDSS_MDP_SCALE_FILTER_NEAREST << 16);
+
+ if (pipe->src.w <= chr_dst_w)
+ scale_config |= /* CrCb */
+ (MDSS_MDP_SCALE_FILTER_BIL << 12);
+ else
+ scale_config |= /* CrCb */
+ (MDSS_MDP_SCALE_FILTER_PCMN << 12);
+
+ phasex_step = mdss_mdp_scale_phase_step(
+ PHASE_STEP_SHIFT, pipe->src.w, chr_dst_w);
+ mdss_mdp_pipe_write(pipe,
+ MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPX,
+ phasex_step);
+ } else {
+ if (pipe->src.w <= pipe->dst.w)
+ scale_config |= /* RGB, A */
+ (MDSS_MDP_SCALE_FILTER_BIL << 8) |
+ (MDSS_MDP_SCALE_FILTER_NEAREST << 16);
+ else
+ scale_config |= /* RGB, A */
+ (MDSS_MDP_SCALE_FILTER_PCMN << 8) |
+ (MDSS_MDP_SCALE_FILTER_NEAREST << 16);
+ }
+
+ phasex_step = mdss_mdp_scale_phase_step(
+ PHASE_STEP_SHIFT, pipe->src.w, pipe->dst.w);
+ }
+
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SCALE_CONFIG, scale_config);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SCALE_PHASE_STEP_X, phasex_step);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SCALE_PHASE_STEP_Y, phasey_step);
+ return 0;
+}
+
+static int mdss_mdp_image_setup(struct mdss_mdp_pipe *pipe)
+{
+ u32 img_size, src_size, src_xy, dst_size, dst_xy, ystride0, ystride1;
+
+ pr_debug("pnum=%d wh=%dx%d src={%d,%d,%d,%d} dst={%d,%d,%d,%d}\n",
+ pipe->num, pipe->img_width, pipe->img_height,
+ pipe->src.x, pipe->src.y, pipe->src.w, pipe->src.h,
+ pipe->dst.x, pipe->dst.y, pipe->dst.w, pipe->dst.h);
+
+ if (mdss_mdp_scale_setup(pipe))
+ return -EINVAL;
+
+ mdss_mdp_get_plane_sizes(pipe->src_fmt->format, pipe->img_width,
+ pipe->img_height, &pipe->src_planes);
+
+ img_size = (pipe->img_height << 16) | pipe->img_width;
+ src_size = (pipe->src.h << 16) | pipe->src.w;
+ src_xy = (pipe->src.y << 16) | pipe->src.x;
+ dst_size = (pipe->dst.h << 16) | pipe->dst.w;
+ dst_xy = (pipe->dst.y << 16) | pipe->dst.x;
+ ystride0 = (pipe->src_planes.ystride[0]) |
+ (pipe->src_planes.ystride[1] << 16);
+ ystride1 = (pipe->src_planes.ystride[2]) |
+ (pipe->src_planes.ystride[3] << 16);
+
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_IMG_SIZE, img_size);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_SIZE, src_size);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_XY, src_xy);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_OUT_SIZE, dst_size);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_OUT_XY, dst_xy);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_YSTRIDE0, ystride0);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_YSTRIDE1, ystride1);
+
+ return 0;
+}
+
+static int mdss_mdp_format_setup(struct mdss_mdp_pipe *pipe)
+{
+ struct mdss_mdp_format_params *fmt;
+ u32 rot90, opmode, chroma_samp;
+
+ fmt = pipe->src_fmt;
+
+ opmode = pipe->bwc_mode;
+ if (pipe->flags & MDP_FLIP_LR)
+ opmode |= MDSS_MDP_OP_FLIP_LR;
+ if (pipe->flags & MDP_FLIP_UD)
+ opmode |= MDSS_MDP_OP_FLIP_UD;
+
+ pr_debug("pnum=%d format=%d opmode=%x\n", pipe->num, fmt->format,
+ opmode);
+
+ rot90 = !!(pipe->flags & MDP_SOURCE_ROTATED_90);
+
+ chroma_samp = fmt->chroma_sample;
+ if (rot90) {
+ if (chroma_samp == MDSS_MDP_CHROMA_H2V1)
+ chroma_samp = MDSS_MDP_CHROMA_H1V2;
+ else if (chroma_samp == MDSS_MDP_CHROMA_H1V2)
+ chroma_samp = MDSS_MDP_CHROMA_H2V1;
+ }
+
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_FORMAT,
+ (chroma_samp << 23) |
+ (fmt->fetch_planes << 19) |
+ (fmt->unpack_align_msb << 18) |
+ (fmt->unpack_tight << 17) |
+ (fmt->unpack_count << 12) |
+ (rot90 << 11) |
+ (fmt->bpp << 9) |
+ (fmt->alpha_enable << 8) |
+ (fmt->a_bit << 6) |
+ (fmt->r_bit << 4) |
+ (fmt->b_bit << 2) |
+ (fmt->g_bit << 0));
+
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN,
+ (fmt->element3 << 24) |
+ (fmt->element2 << 16) |
+ (fmt->element1 << 8) |
+ (fmt->element0 << 0));
+
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_OP_MODE, opmode);
+
+ return 0;
+}
+
+static int mdss_mdp_vig_setup(struct mdss_mdp_pipe *pipe)
+{
+ u32 opmode = 0;
+
+ pr_debug("pnum=%x\n", pipe->num);
+
+ if (pipe->src_fmt->is_yuv)
+ opmode |= (0 << 19) | /* DST_DATA=RGB */
+ (1 << 18) | /* SRC_DATA=YCBCR */
+ (1 << 17); /* CSC_1_EN */
+
+ /* only need to program once */
+ if (pipe->play_cnt == 0) {
+ mdss_mdp_csc_setup(MDSS_MDP_BLOCK_SSPP, pipe->num, 1,
+ MDSS_MDP_CSC_YUV2RGB);
+ }
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_VIG_OP_MODE, opmode);
+
+ return 0;
+}
+
+static int mdss_mdp_src_addr_setup(struct mdss_mdp_pipe *pipe,
+ struct mdss_mdp_data *data)
+{
+ int ret;
+
+ pr_debug("pnum=%d\n", pipe->num);
+
+ if (pipe->type != MDSS_MDP_PIPE_TYPE_DMA)
+ data->bwc_enabled = pipe->bwc_mode;
+
+ ret = mdss_mdp_data_check(data, &pipe->src_planes);
+ if (ret)
+ return ret;
+
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC0_ADDR, data->p[0].addr);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC1_ADDR, data->p[1].addr);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC2_ADDR, data->p[2].addr);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC3_ADDR, data->p[3].addr);
+
+ return 0;
+}
+
+int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe,
+ struct mdss_mdp_data *src_data)
+{
+ int ret = 0;
+ u32 params_changed;
+
+ if (!pipe) {
+ pr_err("pipe not setup properly for queue\n");
+ return -ENODEV;
+ }
+
+ if (!pipe->mixer) {
+ pr_err("pipe mixer not setup properly for queue\n");
+ return -ENODEV;
+ }
+
+ pr_debug("pnum=%x mixer=%d\n", pipe->num, pipe->mixer->num);
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+
+ params_changed = pipe->params_changed;
+ if (params_changed) {
+ pipe->params_changed = 0;
+
+ ret = mdss_mdp_image_setup(pipe);
+ if (ret) {
+ pr_err("image setup error for pnum=%d\n", pipe->num);
+ goto done;
+ }
+
+ ret = mdss_mdp_format_setup(pipe);
+ if (ret) {
+ pr_err("format %d setup error pnum=%d\n",
+ pipe->src_fmt->format, pipe->num);
+ goto done;
+ }
+
+ if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG)
+ mdss_mdp_vig_setup(pipe);
+
+ ret = mdss_mdp_smp_reserve(pipe);
+ if (ret) {
+ pr_err("unable to reserve smp for pnum=%d\n",
+ pipe->num);
+ goto done;
+ }
+
+ mdss_mdp_smp_alloc(pipe);
+ }
+
+ ret = mdss_mdp_src_addr_setup(pipe, src_data);
+ if (ret) {
+ pr_err("addr setup error for pnum=%d\n", pipe->num);
+ goto done;
+ }
+
+ mdss_mdp_mixer_pipe_update(pipe, params_changed);
+
+ pipe->play_cnt++;
+
+done:
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+
+ return ret;
+}
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
new file mode 100644
index 0000000..db840a8
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c
@@ -0,0 +1,175 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 "mdss_mdp.h"
+
+struct mdp_csc_cfg mdp_csc_convert[MDSS_MDP_MAX_CSC] = {
+ [MDSS_MDP_CSC_RGB2RGB] = {
+ 0,
+ {
+ 0x0200, 0x0000, 0x0000,
+ 0x0000, 0x0200, 0x0000,
+ 0x0000, 0x0000, 0x0200,
+ },
+ { 0x0, 0x0, 0x0,},
+ { 0x0, 0x0, 0x0,},
+ { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,},
+ { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,},
+ },
+ [MDSS_MDP_CSC_YUV2RGB] = {
+ 0,
+ {
+ 0x0254, 0x0000, 0x0331,
+ 0x0254, 0xff37, 0xfe60,
+ 0x0254, 0x0409, 0x0000,
+ },
+ { 0xfff0, 0xff80, 0xff80,},
+ { 0x0, 0x0, 0x0,},
+ { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,},
+ { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,},
+ },
+ [MDSS_MDP_CSC_RGB2YUV] = {
+ 0,
+ {
+ 0x0083, 0x0102, 0x0032,
+ 0x1fb5, 0x1f6c, 0x00e1,
+ 0x00e1, 0x1f45, 0x1fdc
+ },
+ { 0x0, 0x0, 0x0,},
+ { 0x0010, 0x0080, 0x0080,},
+ { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,},
+ { 0x0010, 0x00eb, 0x0010, 0x00f0, 0x0010, 0x00f0,},
+ },
+ [MDSS_MDP_CSC_YUV2YUV] = {
+ 0,
+ {
+ 0x0200, 0x0000, 0x0000,
+ 0x0000, 0x0200, 0x0000,
+ 0x0000, 0x0000, 0x0200,
+ },
+ { 0x0, 0x0, 0x0,},
+ { 0x0, 0x0, 0x0,},
+ { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,},
+ { 0x0, 0xff, 0x0, 0xff, 0x0, 0xff,},
+ },
+};
+
+#define CSC_MV_OFF 0x0
+#define CSC_BV_OFF 0x2C
+#define CSC_LV_OFF 0x14
+#define CSC_POST_OFF 0xC
+
+static int mdss_mdp_csc_setup_data(u32 block, u32 blk_idx, u32 tbl_idx,
+ struct mdp_csc_cfg *data)
+{
+ int i, ret = 0;
+ u32 *off, base, val = 0;
+
+ if (data == NULL) {
+ pr_err("no csc matrix specified\n");
+ return -EINVAL;
+ }
+
+ switch (block) {
+ case MDSS_MDP_BLOCK_SSPP:
+ if (blk_idx < MDSS_MDP_SSPP_RGB0) {
+ base = MDSS_MDP_REG_SSPP_OFFSET(blk_idx);
+ if (tbl_idx == 1)
+ base += MDSS_MDP_REG_VIG_CSC_1_BASE;
+ else
+ base += MDSS_MDP_REG_VIG_CSC_0_BASE;
+ } else {
+ ret = -EINVAL;
+ }
+ break;
+ case MDSS_MDP_BLOCK_WB:
+ if (blk_idx < MDSS_MDP_MAX_WRITEBACK) {
+ base = MDSS_MDP_REG_WB_OFFSET(blk_idx) +
+ MDSS_MDP_REG_WB_CSC_BASE;
+ } else {
+ ret = -EINVAL;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (ret != 0) {
+ pr_err("unsupported block id for csc\n");
+ return ret;
+ }
+
+ off = (u32 *) (base + CSC_MV_OFF);
+ for (i = 0; i < 9; i++) {
+ if (i & 0x1) {
+ val |= data->csc_mv[i] << 16;
+ MDSS_MDP_REG_WRITE(off, val);
+ off++;
+ } else {
+ val = data->csc_mv[i];
+ }
+ }
+ MDSS_MDP_REG_WRITE(off, val); /* COEFF_33 */
+
+ off = (u32 *) (base + CSC_BV_OFF);
+ for (i = 0; i < 3; i++) {
+ MDSS_MDP_REG_WRITE(off, data->csc_pre_bv[i]);
+ MDSS_MDP_REG_WRITE((u32 *)(((u32)off) + CSC_POST_OFF),
+ data->csc_post_bv[i]);
+ off++;
+ }
+
+ off = (u32 *) (base + CSC_LV_OFF);
+ for (i = 0; i < 6; i += 2) {
+ val = (data->csc_pre_lv[i] << 8) | data->csc_pre_lv[i+1];
+ MDSS_MDP_REG_WRITE(off, val);
+
+ val = (data->csc_post_lv[i] << 8) | data->csc_post_lv[i+1];
+ MDSS_MDP_REG_WRITE((u32 *)(((u32)off) + CSC_POST_OFF), val);
+ off++;
+ }
+
+ return ret;
+}
+
+int mdss_mdp_csc_setup(u32 block, u32 blk_idx, u32 tbl_idx, u32 csc_type)
+{
+ struct mdp_csc_cfg *data;
+
+ if (csc_type >= MDSS_MDP_MAX_CSC) {
+ pr_err("invalid csc matrix index %d\n", csc_type);
+ return -ERANGE;
+ }
+
+ pr_debug("csc type=%d blk=%d idx=%d tbl=%d\n", csc_type,
+ block, blk_idx, tbl_idx);
+
+ data = &mdp_csc_convert[csc_type];
+ return mdss_mdp_csc_setup_data(block, blk_idx, tbl_idx, data);
+}
+
+int mdss_mdp_dspp_setup(struct mdss_mdp_ctl *ctl, struct mdss_mdp_mixer *mixer)
+{
+ int dspp_num;
+
+ if (!ctl || !mixer)
+ return -EINVAL;
+
+ dspp_num = mixer->num;
+
+ ctl->flush_bits |= BIT(13 + dspp_num); /* DSPP */
+
+ return 0;
+}
diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.c b/drivers/video/msm/mdss/mdss_mdp_rotator.c
new file mode 100644
index 0000000..628b7f5
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.c
@@ -0,0 +1,228 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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/errno.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+
+#include "mdss_mdp.h"
+#include "mdss_mdp_rotator.h"
+
+#define MAX_ROTATOR_SESSIONS 8
+
+static DEFINE_MUTEX(rotator_lock);
+static struct mdss_mdp_rotator_session rotator_session[MAX_ROTATOR_SESSIONS];
+static LIST_HEAD(rotator_queue);
+
+struct mdss_mdp_rotator_session *mdss_mdp_rotator_session_alloc(void)
+{
+ struct mdss_mdp_rotator_session *rot;
+ int i;
+
+ mutex_lock(&rotator_lock);
+ for (i = 0; i < MAX_ROTATOR_SESSIONS; i++) {
+ rot = &rotator_session[i];
+ if (rot->ref_cnt == 0) {
+ rot->ref_cnt++;
+ rot->session_id = i | MDSS_MDP_ROT_SESSION_MASK;
+ mutex_init(&rot->lock);
+ break;
+ }
+ }
+ mutex_unlock(&rotator_lock);
+ if (i == MAX_ROTATOR_SESSIONS) {
+ pr_err("max rotator sessions reached\n");
+ return NULL;
+ }
+
+ return rot;
+}
+
+struct mdss_mdp_rotator_session *mdss_mdp_rotator_session_get(u32 session_id)
+{
+ struct mdss_mdp_rotator_session *rot;
+ u32 ndx;
+
+ ndx = session_id & ~MDSS_MDP_ROT_SESSION_MASK;
+ if (ndx < MAX_ROTATOR_SESSIONS) {
+ rot = &rotator_session[ndx];
+ if (rot->ref_cnt && rot->session_id == session_id)
+ return rot;
+ }
+ return NULL;
+}
+
+static int mdss_mdp_rotator_busy_wait(struct mdss_mdp_rotator_session *rot)
+{
+ mutex_lock(&rot->lock);
+ if (rot->busy) {
+ pr_debug("waiting for rot=%d to complete\n", rot->pipe->num);
+ wait_for_completion_interruptible(rot->comp);
+ rot->busy = 0;
+ }
+ mutex_unlock(&rot->lock);
+
+ return 0;
+}
+
+static struct mdss_mdp_pipe *mdss_mdp_rotator_pipe_alloc(void)
+{
+ struct mdss_mdp_mixer *mixer;
+ struct mdss_mdp_pipe *pipe = NULL;
+ int pnum;
+
+ mixer = mdss_mdp_wb_mixer_alloc(1);
+ if (!mixer)
+ return NULL;
+
+ switch (mixer->num) {
+ case MDSS_MDP_LAYERMIXER3:
+ pnum = MDSS_MDP_SSPP_DMA0;
+ break;
+ case MDSS_MDP_LAYERMIXER4:
+ pnum = MDSS_MDP_SSPP_DMA1;
+ break;
+ default:
+ goto done;
+ }
+
+ pipe = mdss_mdp_pipe_alloc_pnum(pnum);
+
+ if (pipe)
+ pipe->mixer = mixer;
+done:
+ if (!pipe)
+ mdss_mdp_wb_mixer_destroy(mixer);
+
+ return pipe;
+}
+
+static int mdss_mdp_rotator_pipe_dequeue(struct mdss_mdp_rotator_session *rot)
+{
+ if (rot->pipe) {
+ pr_debug("reusing existing session=%d\n", rot->pipe->num);
+ mdss_mdp_rotator_busy_wait(rot);
+ list_move_tail(&rot->head, &rotator_queue);
+ } else {
+ struct mdss_mdp_rotator_session *tmp;
+
+ rot->params_changed++;
+ rot->pipe = mdss_mdp_rotator_pipe_alloc();
+ if (rot->pipe) {
+ pr_debug("use new rotator pipe=%d\n", rot->pipe->num);
+
+ rot->pipe->mixer_stage = MDSS_MDP_STAGE_UNUSED;
+ list_add_tail(&rot->head, &rotator_queue);
+ } else if (!list_empty(&rotator_queue)) {
+ tmp = list_first_entry(&rotator_queue,
+ struct mdss_mdp_rotator_session,
+ head);
+
+ pr_debug("wait for rotator pipe=%d\n", tmp->pipe->num);
+ mdss_mdp_rotator_busy_wait(tmp);
+ rot->pipe = tmp->pipe;
+ tmp->pipe = NULL;
+
+ list_del(&tmp->head);
+ list_add_tail(&rot->head, &rotator_queue);
+ } else {
+ pr_err("no available rotator pipes\n");
+ return -EBUSY;
+ }
+ }
+
+ return 0;
+}
+
+int mdss_mdp_rotator_queue(struct mdss_mdp_rotator_session *rot,
+ struct mdss_mdp_data *src_data,
+ struct mdss_mdp_data *dst_data)
+{
+ struct mdss_mdp_pipe *rot_pipe;
+ struct mdss_mdp_ctl *ctl;
+ int ret;
+
+ if (!rot)
+ return -ENODEV;
+
+ mutex_lock(&rotator_lock);
+ ret = mdss_mdp_rotator_pipe_dequeue(rot);
+ if (ret) {
+ pr_err("unable to acquire rotator\n");
+ goto done;
+ }
+
+ rot_pipe = rot->pipe;
+
+ pr_debug("queue rotator pnum=%d\n", rot_pipe->num);
+
+ ctl = rot_pipe->mixer->ctl;
+
+ if (rot->params_changed) {
+ rot->params_changed = 0;
+ rot_pipe->flags = rot->rotations;
+ rot_pipe->src_fmt = mdss_mdp_get_format_params(rot->format);
+ rot_pipe->img_width = rot->img_width;
+ rot_pipe->img_height = rot->img_height;
+ rot_pipe->src = rot->src_rect;
+ rot_pipe->bwc_mode = rot->bwc_mode;
+ rot_pipe->params_changed++;
+ }
+
+ rot->dst_data = dst_data;
+
+ ret = mdss_mdp_pipe_queue_data(rot->pipe, src_data);
+ if (ret) {
+ pr_err("unable to queue rot data\n");
+ goto done;
+ }
+
+ ret = mdss_mdp_display_commit(ctl, rot);
+
+done:
+ mutex_unlock(&rotator_lock);
+
+ if (!rot->no_wait)
+ mdss_mdp_rotator_busy_wait(rot);
+
+ return ret;
+}
+
+int mdss_mdp_rotator_finish(struct mdss_mdp_rotator_session *rot)
+{
+ struct mdss_mdp_pipe *rot_pipe;
+
+ if (!rot)
+ return -ENODEV;
+
+ pr_debug("finish rot id=%x\n", rot->session_id);
+
+ mutex_lock(&rotator_lock);
+ rot_pipe = rot->pipe;
+ if (rot_pipe) {
+ mdss_mdp_rotator_busy_wait(rot);
+ list_del(&rot->head);
+ }
+ memset(rot, 0, sizeof(*rot));
+ if (rot_pipe) {
+ struct mdss_mdp_mixer *mixer = rot_pipe->mixer;
+ mdss_mdp_pipe_destroy(rot_pipe);
+ mdss_mdp_wb_mixer_destroy(mixer);
+ }
+ mutex_unlock(&rotator_lock);
+
+ return 0;
+}
diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.h b/drivers/video/msm/mdss/mdss_mdp_rotator.h
new file mode 100644
index 0000000..8940c46
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.h
@@ -0,0 +1,55 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 MDSS_MDP_ROTATOR_H
+#define MDSS_MDP_ROTATOR_H
+
+#include <linux/types.h>
+
+#include "mdss_mdp.h"
+
+#define MDSS_MDP_ROT_SESSION_MASK 0x80000000
+
+struct mdss_mdp_rotator_session {
+ u32 session_id;
+ u32 ref_cnt;
+ u32 params_changed;
+
+ u32 format;
+ u32 rotations;
+
+ u16 img_width;
+ u16 img_height;
+ struct mdss_mdp_img_rect src_rect;
+
+ u32 bwc_mode;
+ struct mdss_mdp_pipe *pipe;
+ struct mdss_mdp_data *dst_data;
+
+ struct mutex lock;
+ u8 busy;
+ u8 no_wait;
+ struct completion *comp;
+
+ struct list_head head;
+};
+
+struct mdss_mdp_rotator_session *mdss_mdp_rotator_session_alloc(void);
+struct mdss_mdp_rotator_session *mdss_mdp_rotator_session_get(u32 session_id);
+
+int mdss_mdp_rotator_queue(struct mdss_mdp_rotator_session *rot,
+ struct mdss_mdp_data *src_data,
+ struct mdss_mdp_data *dst_data);
+int mdss_mdp_rotator_finish(struct mdss_mdp_rotator_session *rot);
+int mdss_mdp_rotator_ctl_busy_wait(struct mdss_mdp_ctl *ctl);
+
+#endif /* MDSS_MDP_ROTATOR_H */
diff --git a/drivers/video/msm/mdss/mdss_mdp_util.c b/drivers/video/msm/mdss/mdss_mdp_util.c
new file mode 100644
index 0000000..2e86806
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_mdp_util.c
@@ -0,0 +1,349 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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/android_pmem.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/ion.h>
+#include <linux/msm_kgsl.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include "mdss_fb.h"
+#include "mdss_mdp.h"
+#include "mdss_mdp_formats.h"
+
+enum {
+ MDP_INTR_VSYNC_INTF_0,
+ MDP_INTR_VSYNC_INTF_1,
+ MDP_INTR_VSYNC_INTF_2,
+ MDP_INTR_VSYNC_INTF_3,
+ MDP_INTR_PING_PONG_0,
+ MDP_INTR_PING_PONG_1,
+ MDP_INTR_PING_PONG_2,
+ MDP_INTR_WB_0,
+ MDP_INTR_WB_1,
+ MDP_INTR_WB_2,
+ MDP_INTR_MAX,
+};
+
+struct intr_callback {
+ void (*func)(void *);
+ void *arg;
+};
+
+struct intr_callback mdp_intr_cb[MDP_INTR_MAX];
+static DEFINE_SPINLOCK(mdss_mdp_intr_lock);
+
+static int mdss_mdp_intr2index(u32 intr_type, u32 intf_num)
+{
+ int index = -1;
+ switch (intr_type) {
+ case MDSS_MDP_IRQ_INTF_VSYNC:
+ index = MDP_INTR_VSYNC_INTF_0 + (intf_num - MDSS_MDP_INTF0);
+ break;
+ case MDSS_MDP_IRQ_PING_PONG_COMP:
+ index = MDP_INTR_PING_PONG_0 + intf_num;
+ break;
+ case MDSS_MDP_IRQ_WB_ROT_COMP:
+ index = MDP_INTR_WB_0 + intf_num;
+ break;
+ case MDSS_MDP_IRQ_WB_WFD:
+ index = MDP_INTR_WB_2 + intf_num;
+ break;
+ }
+
+ return index;
+}
+
+int mdss_mdp_set_intr_callback(u32 intr_type, u32 intf_num,
+ void (*fnc_ptr)(void *), void *arg)
+{
+ unsigned long flags;
+ int index, ret;
+
+ index = mdss_mdp_intr2index(intr_type, intf_num);
+ if (index < 0) {
+ pr_warn("invalid intr type=%u intf_num=%u\n",
+ intr_type, intf_num);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&mdss_mdp_intr_lock, flags);
+ if (!mdp_intr_cb[index].func) {
+ mdp_intr_cb[index].func = fnc_ptr;
+ mdp_intr_cb[index].arg = arg;
+ ret = 0;
+ } else {
+ ret = -EBUSY;
+ }
+ spin_unlock_irqrestore(&mdss_mdp_intr_lock, flags);
+
+ return ret;
+}
+
+static inline void mdss_mdp_intr_done(int index)
+{
+ void (*fnc)(void *);
+ void *arg;
+
+ spin_lock(&mdss_mdp_intr_lock);
+ fnc = mdp_intr_cb[index].func;
+ arg = mdp_intr_cb[index].arg;
+ if (fnc != NULL)
+ mdp_intr_cb[index].func = NULL;
+ spin_unlock(&mdss_mdp_intr_lock);
+ if (fnc)
+ fnc(arg);
+}
+
+irqreturn_t mdss_mdp_isr(int irq, void *ptr)
+{
+ u32 isr, mask;
+
+
+ isr = MDSS_MDP_REG_READ(MDSS_MDP_REG_INTR_STATUS);
+
+ pr_debug("isr=%x\n", isr);
+
+ if (isr == 0)
+ goto done;
+
+ mask = MDSS_MDP_REG_READ(MDSS_MDP_REG_INTR_EN);
+ MDSS_MDP_REG_WRITE(MDSS_MDP_REG_INTR_CLEAR, isr);
+
+ isr &= mask;
+ if (isr == 0)
+ goto done;
+
+ if (isr & MDSS_MDP_INTR_PING_PONG_0_DONE)
+ mdss_mdp_intr_done(MDP_INTR_PING_PONG_0);
+
+ if (isr & MDSS_MDP_INTR_PING_PONG_1_DONE)
+ mdss_mdp_intr_done(MDP_INTR_PING_PONG_1);
+
+ if (isr & MDSS_MDP_INTR_PING_PONG_2_DONE)
+ mdss_mdp_intr_done(MDP_INTR_PING_PONG_2);
+
+ if (isr & MDSS_MDP_INTR_INTF_0_VSYNC)
+ mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_0);
+
+ if (isr & MDSS_MDP_INTR_INTF_1_VSYNC)
+ mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_1);
+
+ if (isr & MDSS_MDP_INTR_INTF_2_VSYNC)
+ mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_2);
+
+ if (isr & MDSS_MDP_INTR_INTF_3_VSYNC)
+ mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_3);
+
+ if (isr & MDSS_MDP_INTR_WB_0_DONE)
+ mdss_mdp_intr_done(MDP_INTR_WB_0);
+
+ if (isr & MDSS_MDP_INTR_WB_1_DONE)
+ mdss_mdp_intr_done(MDP_INTR_WB_1);
+
+ if (isr & MDSS_MDP_INTR_WB_2_DONE)
+ mdss_mdp_intr_done(MDP_INTR_WB_2);
+
+done:
+ return IRQ_HANDLED;
+}
+
+struct mdss_mdp_format_params *mdss_mdp_get_format_params(u32 format)
+{
+ struct mdss_mdp_format_params *fmt = NULL;
+ if (format < MDP_IMGTYPE_LIMIT) {
+ fmt = &mdss_mdp_format_map[format];
+ if (fmt->format != format)
+ fmt = NULL;
+ }
+
+ return fmt;
+}
+
+int mdss_mdp_get_plane_sizes(u32 format, u32 w, u32 h,
+ struct mdss_mdp_plane_sizes *ps)
+{
+ struct mdss_mdp_format_params *fmt;
+ int i;
+
+ if (ps == NULL)
+ return -EINVAL;
+
+ if ((w > MAX_IMG_WIDTH) || (h > MAX_IMG_HEIGHT))
+ return -ERANGE;
+
+ fmt = mdss_mdp_get_format_params(format);
+ if (!fmt)
+ return -EINVAL;
+
+ memset(ps, 0, sizeof(struct mdss_mdp_plane_sizes));
+
+ if (fmt->fetch_planes == MDSS_MDP_PLANE_INTERLEAVED) {
+ u32 bpp = fmt->bpp + 1;
+ ps->num_planes = 1;
+ ps->plane_size[0] = w * h * bpp;
+ ps->ystride[0] = w * bpp;
+ } else {
+ u8 hmap[] = { 1, 2, 1, 2 };
+ u8 vmap[] = { 1, 1, 2, 2 };
+ u8 horiz, vert;
+
+ horiz = hmap[fmt->chroma_sample];
+ vert = vmap[fmt->chroma_sample];
+
+ if (format == MDP_Y_CR_CB_GH2V2) {
+ ps->plane_size[0] = ALIGN(w, 16) * h;
+ ps->plane_size[1] = ALIGN(w / horiz, 16) * (h / vert);
+ ps->ystride[0] = ALIGN(w, 16);
+ ps->ystride[1] = ALIGN(w / horiz, 16);
+ } else {
+ ps->plane_size[0] = w * h;
+ ps->plane_size[1] = (w / horiz) * (h / vert);
+ ps->ystride[0] = w;
+ ps->ystride[1] = (w / horiz);
+ }
+
+ if (fmt->fetch_planes == MDSS_MDP_PLANE_PSEUDO_PLANAR) {
+ ps->num_planes = 2;
+ ps->plane_size[1] *= 2;
+ ps->ystride[1] *= 2;
+ } else { /* planar */
+ ps->num_planes = 3;
+ ps->plane_size[2] = ps->plane_size[1];
+ ps->ystride[2] = ps->ystride[1];
+ }
+ }
+
+ for (i = 0; i < ps->num_planes; i++)
+ ps->total_size += ps->plane_size[i];
+
+ return 0;
+}
+
+int mdss_mdp_data_check(struct mdss_mdp_data *data,
+ struct mdss_mdp_plane_sizes *ps)
+{
+ if (!ps)
+ return 0;
+
+ if (!data || data->num_planes == 0)
+ return -ENOMEM;
+
+ if (data->bwc_enabled) {
+ return -EPERM; /* not supported */
+ } else {
+ struct mdss_mdp_img_data *prev, *curr;
+ int i;
+
+ pr_debug("srcp0=%x len=%u frame_size=%u\n", data->p[0].addr,
+ data->p[0].len, ps->total_size);
+
+ for (i = 0; i < ps->num_planes; i++) {
+ curr = &data->p[i];
+ if (i >= data->num_planes) {
+ u32 psize = ps->plane_size[i-1];
+ prev = &data->p[i-1];
+ if (prev->len > psize) {
+ curr->len = prev->len - psize;
+ prev->len = psize;
+ }
+ curr->addr = prev->addr + psize;
+ }
+ if (curr->len < ps->plane_size[i]) {
+ pr_err("insufficient mem=%u p=%d len=%u\n",
+ curr->len, i, ps->plane_size[i]);
+ return -ENOMEM;
+ }
+ pr_debug("plane[%d] addr=%x len=%u\n", i,
+ curr->addr, curr->len);
+ }
+ data->num_planes = ps->num_planes;
+ }
+
+ return 0;
+}
+
+int mdss_mdp_put_img(struct mdss_mdp_img_data *data)
+{
+ /* only source may use frame buffer */
+ if (data->flags & MDP_MEMORY_ID_TYPE_FB) {
+ fput_light(data->srcp_file, data->p_need);
+ return 0;
+ }
+ if (data->srcp_file) {
+ put_pmem_file(data->srcp_file);
+ data->srcp_file = NULL;
+ return 0;
+ }
+ if (!IS_ERR_OR_NULL(data->srcp_ihdl)) {
+ ion_free(data->iclient, data->srcp_ihdl);
+ data->iclient = NULL;
+ data->srcp_ihdl = NULL;
+ return 0;
+ }
+
+ return -ENOMEM;
+}
+
+int mdss_mdp_get_img(struct ion_client *iclient, struct msmfb_data *img,
+ struct mdss_mdp_img_data *data)
+{
+ struct file *file;
+ int ret = -EINVAL;
+ int fb_num;
+ unsigned long *start, *len;
+
+ start = (unsigned long *) &data->addr;
+ len = (unsigned long *) &data->len;
+ data->flags = img->flags;
+ data->p_need = 0;
+
+ if (img->flags & MDP_BLIT_SRC_GEM) {
+ data->srcp_file = NULL;
+ ret = kgsl_gem_obj_addr(img->memory_id, (int) img->priv,
+ start, len);
+ } else if (img->flags & MDP_MEMORY_ID_TYPE_FB) {
+ file = fget_light(img->memory_id, &data->p_need);
+ if (file && FB_MAJOR ==
+ MAJOR(file->f_dentry->d_inode->i_rdev)) {
+ data->srcp_file = file;
+ fb_num = MINOR(file->f_dentry->d_inode->i_rdev);
+ ret = mdss_fb_get_phys_info(start, len, fb_num);
+ }
+ } else if (iclient) {
+ data->iclient = iclient;
+ data->srcp_ihdl = ion_import_fd(iclient, img->memory_id);
+ if (IS_ERR_OR_NULL(data->srcp_ihdl))
+ return PTR_ERR(data->srcp_ihdl);
+ ret = ion_phys(iclient, data->srcp_ihdl,
+ start, (size_t *) len);
+ } else {
+ unsigned long vstart;
+ ret = get_pmem_file(img->memory_id, start, &vstart, len,
+ &data->srcp_file);
+ }
+
+ if (!ret && (img->offset < data->len)) {
+ data->addr += img->offset;
+ data->len -= img->offset;
+ } else {
+ mdss_mdp_put_img(data);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
diff --git a/drivers/video/msm/mdss/mdss_panel.h b/drivers/video/msm/mdss/mdss_panel.h
new file mode 100644
index 0000000..3fd943d
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_panel.h
@@ -0,0 +1,176 @@
+/* Copyright (c) 2008-2012, Code Aurora Forum. 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 MDSS_PANEL_H
+#define MDSS_PANEL_H
+
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+/* panel id type */
+struct panel_id {
+ u16 id;
+ u16 type;
+};
+
+/* panel type list */
+#define NO_PANEL 0xffff /* No Panel */
+#define MDDI_PANEL 1 /* MDDI */
+#define EBI2_PANEL 2 /* EBI2 */
+#define LCDC_PANEL 3 /* internal LCDC type */
+#define EXT_MDDI_PANEL 4 /* Ext.MDDI */
+#define TV_PANEL 5 /* TV */
+#define HDMI_PANEL 6 /* HDMI TV */
+#define DTV_PANEL 7 /* DTV */
+#define MIPI_VIDEO_PANEL 8 /* MIPI */
+#define MIPI_CMD_PANEL 9 /* MIPI */
+#define WRITEBACK_PANEL 10 /* Wifi display */
+#define LVDS_PANEL 11 /* LVDS */
+#define EDP_PANEL 12 /* LVDS */
+
+/* panel class */
+enum {
+ DISPLAY_LCD = 0, /* lcd = ebi2/mddi */
+ DISPLAY_LCDC, /* lcdc */
+ DISPLAY_TV, /* TV Out */
+ DISPLAY_EXT_MDDI, /* External MDDI */
+ DISPLAY_WRITEBACK,
+};
+
+/* panel device locaiton */
+enum {
+ DISPLAY_1 = 0, /* attached as first device */
+ DISPLAY_2, /* attached on second device */
+ DISPLAY_3, /* attached on third writeback device */
+ MAX_PHYS_TARGET_NUM,
+};
+
+/* panel info type */
+struct lcd_panel_info {
+ u32 vsync_enable;
+ u32 refx100;
+ u32 v_back_porch;
+ u32 v_front_porch;
+ u32 v_pulse_width;
+ u32 hw_vsync_mode;
+ u32 vsync_notifier_period;
+ u32 rev;
+};
+
+struct lcdc_panel_info {
+ u32 h_back_porch;
+ u32 h_front_porch;
+ u32 h_pulse_width;
+ u32 v_back_porch;
+ u32 v_front_porch;
+ u32 v_pulse_width;
+ u32 border_clr;
+ u32 underflow_clr;
+ u32 hsync_skew;
+ /* Pad width */
+ u32 xres_pad;
+ /* Pad height */
+ u32 yres_pad;
+};
+
+struct mipi_panel_info {
+ char mode; /* video/cmd */
+ char interleave_mode;
+ char crc_check;
+ char ecc_check;
+ char dst_format; /* shared by video and command */
+ char data_lane0;
+ char data_lane1;
+ char data_lane2;
+ char data_lane3;
+ char dlane_swap; /* data lane swap */
+ char rgb_swap;
+ char b_sel;
+ char g_sel;
+ char r_sel;
+ char rx_eot_ignore;
+ char tx_eot_append;
+ char t_clk_post; /* 0xc0, DSI_CLKOUT_TIMING_CTRL */
+ char t_clk_pre; /* 0xc0, DSI_CLKOUT_TIMING_CTRL */
+ char vc; /* virtual channel */
+ struct mipi_dsi_phy_ctrl *dsi_phy_db;
+ /* video mode */
+ char pulse_mode_hsa_he;
+ char hfp_power_stop;
+ char hbp_power_stop;
+ char hsa_power_stop;
+ char eof_bllp_power_stop;
+ char bllp_power_stop;
+ char traffic_mode;
+ char frame_rate;
+ /* command mode */
+ char interleave_max;
+ char insert_dcs_cmd;
+ char wr_mem_continue;
+ char wr_mem_start;
+ char te_sel;
+ char stream; /* 0 or 1 */
+ char mdp_trigger;
+ char dma_trigger;
+ u32 dsi_pclk_rate;
+ /* The packet-size should not bet changed */
+ char no_max_pkt_size;
+ /* Clock required during LP commands */
+ char force_clk_lane_hs;
+};
+
+enum lvds_mode {
+ LVDS_SINGLE_CHANNEL_MODE,
+ LVDS_DUAL_CHANNEL_MODE,
+};
+
+struct lvds_panel_info {
+ enum lvds_mode channel_mode;
+ /* Channel swap in dual mode */
+ char channel_swap;
+};
+
+struct mdss_panel_info {
+ u32 xres;
+ u32 yres;
+ u32 bpp;
+ u32 type;
+ u32 wait_cycle;
+ u32 pdest;
+ u32 bl_max;
+ u32 bl_min;
+ u32 fb_num;
+ u32 clk_rate;
+ u32 clk_min;
+ u32 clk_max;
+ u32 frame_count;
+ u32 is_3d_panel;
+ u32 out_format;
+
+ struct lcd_panel_info lcd;
+ struct lcdc_panel_info lcdc;
+ struct mipi_panel_info mipi;
+ struct lvds_panel_info lvds;
+};
+
+struct mdss_panel_data {
+ struct mdss_panel_info panel_info;
+ void (*set_backlight) (u32 bl_level);
+
+ /* function entry chain */
+ int (*on) (struct mdss_panel_data *pdata);
+ int (*off) (struct mdss_panel_data *pdata);
+};
+
+int mdss_register_panel(struct mdss_panel_data *pdata);
+#endif /* MDSS_PANEL_H */
diff --git a/drivers/video/msm/mdss/mdss_wb.c b/drivers/video/msm/mdss/mdss_wb.c
new file mode 100644
index 0000000..3be4525
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_wb.c
@@ -0,0 +1,111 @@
+/* Copyright (c) 2011-2012, Code Aurora Forum. 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/ioctl.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/msm_mdp.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/version.h>
+
+#include "mdss_panel.h"
+
+static int mdss_wb_on(struct mdss_panel_data *pdata)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static int mdss_wb_off(struct mdss_panel_data *pdata)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static int mdss_wb_parse_dt(struct platform_device *pdev,
+ struct mdss_panel_data *pdata)
+{
+ struct device_node *np = pdev->dev.of_node;
+ u32 res[2], tmp;
+ int rc;
+
+ rc = of_property_read_u32_array(np, "qcom,mdss_pan_res", res, 2);
+ pdata->panel_info.xres = (!rc ? res[0] : 1280);
+ pdata->panel_info.yres = (!rc ? res[1] : 720);
+
+ rc = of_property_read_u32(np, "qcom,mdss_pan_bpp", &tmp);
+ pdata->panel_info.bpp = (!rc ? tmp : 24);
+
+ return 0;
+}
+
+static int mdss_wb_probe(struct platform_device *pdev)
+{
+ struct mdss_panel_data *pdata = NULL;
+ int rc = 0;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ rc = !mdss_wb_parse_dt(pdev, pdata);
+ if (!rc)
+ return rc;
+
+ pdata->panel_info.type = WRITEBACK_PANEL;
+ pdata->panel_info.clk_rate = 74250000;
+ pdata->panel_info.pdest = DISPLAY_3;
+ pdata->panel_info.out_format = MDP_RGB_888;
+
+ pdata->on = mdss_wb_on;
+ pdata->off = mdss_wb_off;
+ pdev->dev.platform_data = pdata;
+
+ rc = mdss_register_panel(pdata);
+ if (rc) {
+ dev_err(&pdev->dev, "unable to register writeback panel\n");
+ return rc;
+ }
+
+ return rc;
+}
+
+static const struct of_device_id mdss_wb_match[] = {
+ { .compatible = "qcom,mdss_wb", },
+ { { 0 } }
+};
+
+static struct platform_driver mdss_wb_driver = {
+ .probe = mdss_wb_probe,
+ .driver = {
+ .name = "mdss_wb",
+ .of_match_table = mdss_wb_match,
+ },
+};
+
+static int __init mdss_wb_driver_init(void)
+{
+ int rc = 0;
+ rc = platform_driver_register(&mdss_wb_driver);
+ return rc;
+}
+
+module_init(mdss_wb_driver_init);
diff --git a/drivers/video/msm/mipi_tc358764_dsi2lvds.c b/drivers/video/msm/mipi_tc358764_dsi2lvds.c
index f7f353f..2e65c34 100644
--- a/drivers/video/msm/mipi_tc358764_dsi2lvds.c
+++ b/drivers/video/msm/mipi_tc358764_dsi2lvds.c
@@ -574,7 +574,8 @@
mipi_d2l_enable_3d(mfd, false, false);
/* Add I2C driver only after DSI-CLK is running */
- i2c_add_driver(&d2l_i2c_slave_driver);
+ if (d2l_i2c_client == NULL)
+ i2c_add_driver(&d2l_i2c_slave_driver);
pr_info("%s.ret=%d.\n", __func__, ret);
@@ -989,6 +990,8 @@
{
pr_debug("%s.\n", __func__);
+ d2l_i2c_client = NULL;
+
return platform_driver_register(&d2l_driver);
}
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index 81a6e50..18ee3e6 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -1326,7 +1326,7 @@
fbi->fix.smem_start = (unsigned long)fbram_phys;
msm_iommu_map_contig_buffer(fbi->fix.smem_start,
- DISPLAY_DOMAIN,
+ DISPLAY_READ_DOMAIN,
GEN_POOL,
fbi->fix.smem_len,
SZ_4K,
@@ -1334,7 +1334,7 @@
&(mfd->display_iova));
msm_iommu_map_contig_buffer(fbi->fix.smem_start,
- ROTATOR_DOMAIN,
+ ROTATOR_SRC_DOMAIN,
GEN_POOL,
fbi->fix.smem_len,
SZ_4K,
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
index 64d2976..6fd5656 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
@@ -791,7 +791,7 @@
sz_strm = DDL_ALIGN(ddl_get_yuv_buf_size(width, height,
DDL_YUV_BUF_TYPE_LINEAR) + ddl_get_yuv_buf_size(width,
height/2, DDL_YUV_BUF_TYPE_LINEAR), DDL_KILO_BYTE(4));
- sz_mv = DDL_ALIGN(2 * mb_x * mb_y * 8, DDL_KILO_BYTE(2));
+ sz_mv = DDL_ALIGN(2 * mb_x * 8, DDL_KILO_BYTE(2));
if ((codec == VCD_CODEC_MPEG4) ||
(codec == VCD_CODEC_H264)) {
sz_col_zero = DDL_ALIGN(((mb_x * mb_y + 7) / 8) *
@@ -900,7 +900,7 @@
goto fail_enc_free_exit;
}
if (buf_size.sz_pred > 0) {
- enc_bufs->pred.mem_type = DDL_MM_MEM;
+ enc_bufs->pred.mem_type = DDL_FW_MEM;
ptr = ddl_pmem_alloc(&enc_bufs->pred,
buf_size.sz_pred, DDL_KILO_BYTE(2));
if (!ptr)
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_properties.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_properties.c
index 3827bc1..ab4d51c 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_properties.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_properties.c
@@ -294,6 +294,11 @@
}
}
break;
+ case VCD_I_SET_TURBO_CLK:
+ {
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
case VCD_I_BUFFER_FORMAT:
{
struct vcd_property_buffer_format *tile =
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
index 5b22b21..a5171f0 100644
--- a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
@@ -536,6 +536,9 @@
u32 bus_clk_index, client_type = 0;
int rc = 0;
+ if (dev_ctxt->turbo_mode_set)
+ return rc;
+
cctxt_itr = dev_ctxt->cctxt_list_head;
while (cctxt_itr) {
if (cctxt_itr->decoding)
@@ -565,6 +568,9 @@
bus_clk_index = 2;
}
+ if (bus_clk_index == 3)
+ dev_ctxt->turbo_mode_set = 1;
+
bus_clk_index = (bus_clk_index << 1) + (client_type + 1);
VCDRES_MSG_LOW("%s(), bus_clk_index = %d", __func__, bus_clk_index);
VCDRES_MSG_LOW("%s(),context.pcl = %x", __func__, resource_context.pcl);
@@ -584,6 +590,12 @@
__func__, dev_ctxt);
return false;
}
+ if (dev_ctxt->turbo_mode_set &&
+ (req_perf_lvl < RESTRK_1080P_TURBO_PERF_LEVEL)) {
+ VCDRES_MSG_MED("%s(): TURBO MODE!!\n", __func__);
+ return true;
+ }
+
VCDRES_MSG_LOW("%s(), req_perf_lvl = %d", __func__, req_perf_lvl);
if (resource_context.vidc_platform_data->disable_turbo
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h
index d5e656b..01999a4 100644
--- a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h
@@ -75,4 +75,8 @@
#define VCDRES_MSG_ERROR(xx_fmt...) printk(KERN_ERR "\n err: " xx_fmt)
#define VCDRES_MSG_FATAL(xx_fmt...) printk(KERN_ERR "\n<FATAL> " xx_fmt)
+#ifdef CONFIG_MSM_BUS_SCALING
+int res_trk_update_bus_perf_level(struct vcd_dev_ctxt *dev_ctxt,
+ u32 perf_level);
+#endif
#endif
diff --git a/drivers/video/msm/vidc/common/dec/vdec.c b/drivers/video/msm/vidc/common/dec/vdec.c
index 11177b8..3076aa1a 100644
--- a/drivers/video/msm/vidc/common/dec/vdec.c
+++ b/drivers/video/msm/vidc/common/dec/vdec.c
@@ -636,6 +636,26 @@
return true;
}
+static u32 vid_dec_set_turbo_clk(struct video_client_ctx *client_ctx)
+{
+ struct vcd_property_hdr vcd_property_hdr;
+ u32 vcd_status = VCD_ERR_FAIL;
+ u32 dummy = 0;
+
+ if (!client_ctx)
+ return false;
+ vcd_property_hdr.prop_id = VCD_I_SET_TURBO_CLK;
+ vcd_property_hdr.sz = sizeof(struct vcd_property_frame_size);
+
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &dummy);
+
+ if (vcd_status)
+ return false;
+ else
+ return true;
+}
+
static u32 vid_dec_get_frame_resolution(struct video_client_ctx *client_ctx,
struct vdec_picsize *video_resoultion)
{
@@ -1682,6 +1702,11 @@
}
break;
}
+ case VDEC_IOCTL_SET_PERF_CLK:
+ {
+ vid_dec_set_turbo_clk(client_ctx);
+ break;
+ }
case VDEC_IOCTL_FILL_OUTPUT_BUFFER:
{
struct vdec_fillbuffer_cmd fill_buffer_cmd;
diff --git a/drivers/video/msm/vidc/common/vcd/vcd.h b/drivers/video/msm/vidc/common/vcd/vcd.h
index 3e02030..8f44a56 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd.h
+++ b/drivers/video/msm/vidc/common/vcd/vcd.h
@@ -397,4 +397,5 @@
u32 vcd_update_decoder_perf_level(struct vcd_dev_ctxt *dev_ctxt, u32 perf_lvl);
+u32 vcd_set_perf_turbo_level(struct vcd_clnt_ctxt *cctxt);
#endif
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_client_sm.c b/drivers/video/msm/vidc/common/vcd/vcd_client_sm.c
index 5019d31..7c0d9fe 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_client_sm.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_client_sm.c
@@ -541,6 +541,12 @@
}
break;
}
+ case VCD_I_SET_TURBO_CLK:
+ {
+ if (cctxt->sched_clnt_hdl)
+ rc = vcd_set_perf_turbo_level(cctxt);
+ break;
+ }
case VCD_I_INTRA_PERIOD:
{
struct vcd_property_i_period *iperiod =
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_core.h b/drivers/video/msm/vidc/common/vcd/vcd_core.h
index 7ae4f45..5351589 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_core.h
+++ b/drivers/video/msm/vidc/common/vcd/vcd_core.h
@@ -146,7 +146,7 @@
u32 reqd_perf_lvl;
u32 curr_perf_lvl;
u32 set_perf_lvl_pending;
-
+ u32 turbo_mode_set;
};
struct vcd_clnt_status {
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c b/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
index d517028..96e729de 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
@@ -219,6 +219,7 @@
VCD_DEVICE_STATE_INITING,
ev_code);
}
+ dev_ctxt->turbo_mode_set = 0;
return rc;
}
@@ -758,6 +759,7 @@
client = dev_ctxt->cctxt_list_head;
dev_ctxt->cctxt_list_head = cctxt;
cctxt->next = client;
+ dev_ctxt->turbo_mode_set = 0;
*clnt_cctxt = cctxt;
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_power_sm.c b/drivers/video/msm/vidc/common/vcd/vcd_power_sm.c
index beaa872..33b2300 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_power_sm.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_power_sm.c
@@ -15,6 +15,7 @@
#include "vcd_power_sm.h"
#include "vcd_core.h"
#include "vcd.h"
+#include "vcd_res_tracker.h"
u32 vcd_power_event(
struct vcd_dev_ctxt *dev_ctxt,
@@ -297,6 +298,25 @@
return rc;
}
+u32 vcd_set_perf_turbo_level(struct vcd_clnt_ctxt *cctxt)
+{
+ u32 rc = VCD_S_SUCCESS;
+#ifdef CONFIG_MSM_BUS_SCALING
+ struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt;
+ pr_err("\n Setting Turbo mode !!");
+
+ if (res_trk_update_bus_perf_level(dev_ctxt,
+ RESTRK_1080P_TURBO_PERF_LEVEL) < 0) {
+ pr_err("\n %s(): update buf perf level failed\n",
+ __func__);
+ return false;
+ }
+ dev_ctxt->curr_perf_lvl = RESTRK_1080P_TURBO_PERF_LEVEL;
+ vcd_update_decoder_perf_level(dev_ctxt, RESTRK_1080P_TURBO_PERF_LEVEL);
+#endif
+ return rc;
+}
+
u32 vcd_update_decoder_perf_level(struct vcd_dev_ctxt *dev_ctxt, u32 perf_lvl)
{
u32 rc = VCD_S_SUCCESS;
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_sub.c b/drivers/video/msm/vidc/common/vcd/vcd_sub.c
index dcdbf28..f79a147 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_sub.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_sub.c
@@ -3429,7 +3429,7 @@
struct vcd_property_slice_delivery_info slice_delivery_info;
u32 rc = VCD_S_SUCCESS;
prop_hdr.prop_id = VCD_I_SLICE_DELIVERY_MODE;
- prop_hdr.sz = prop_hdr.sz =
+ prop_hdr.sz =
sizeof(struct vcd_property_slice_delivery_info);
rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr,
&slice_delivery_info);
diff --git a/include/linux/input/mpu3050.h b/include/linux/input/mpu3050.h
new file mode 100644
index 0000000..6006abb
--- /dev/null
+++ b/include/linux/input/mpu3050.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012, Code Aurora Forum. 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 __MPU3050_H__
+#define __MPU3050_H__
+
+struct mpu3050_gyro_platform_data {
+ int poll_interval;
+ int min_interval;
+
+ int (*init)(void);
+ void (*exit)(void);
+ int (*power_on)(void);
+ int (*power_off)(void);
+
+ int gpio_int;
+ int gpio_fsync;
+};
+
+#endif /* __MPU3050_H__ */
diff --git a/include/linux/ion.h b/include/linux/ion.h
index d9443ff..fca5517 100644
--- a/include/linux/ion.h
+++ b/include/linux/ion.h
@@ -86,6 +86,14 @@
FIXED_HIGH,
};
+enum cp_mem_usage {
+ VIDEO_BITSTREAM = 0x1,
+ VIDEO_PIXEL = 0x2,
+ VIDEO_NONPIXEL = 0x3,
+ MAX_USAGE = 0x4,
+ UNKNOWN = 0x7FFFFFFF,
+};
+
/**
* Flag to use when allocating to indicate that a heap is secure.
*/
@@ -482,22 +490,28 @@
*
* @client - a client that has allocated from the heap heap_id
* @heap_id - heap id to secure.
+ * @version - version of content protection
+ * @data - extra data needed for protection
*
* Secure a heap
* Returns 0 on success
*/
-int ion_secure_heap(struct ion_device *dev, int heap_id);
+int ion_secure_heap(struct ion_device *dev, int heap_id, int version,
+ void *data);
/**
* ion_unsecure_heap - un-secure a heap
*
* @client - a client that has allocated from the heap heap_id
* @heap_id - heap id to un-secure.
+ * @version - version of content protection
+ * @data - extra data needed for protection
*
* Un-secure a heap
* Returns 0 on success
*/
-int ion_unsecure_heap(struct ion_device *dev, int heap_id);
+int ion_unsecure_heap(struct ion_device *dev, int heap_id, int version,
+ void *data);
/**
* msm_ion_secure_heap - secure a heap. Wrapper around ion_secure_heap.
@@ -520,6 +534,30 @@
int msm_ion_unsecure_heap(int heap_id);
/**
+ * msm_ion_secure_heap_2_0 - secure a heap using 2.0 APIs
+ * Wrapper around ion_secure_heap.
+ *
+ * @heap_id - heap id to secure.
+ * @usage - usage hint to TZ
+ *
+ * Secure a heap
+ * Returns 0 on success
+ */
+int msm_ion_secure_heap_2_0(int heap_id, enum cp_mem_usage usage);
+
+/**
+ * msm_ion_unsecure_heap - unsecure a heap secured with 3.0 APIs.
+ * Wrapper around ion_unsecure_heap.
+ *
+ * @heap_id - heap id to secure.
+ * @usage - usage hint to TZ
+ *
+ * Un-secure a heap
+ * Returns 0 on success
+ */
+int msm_ion_unsecure_heap_2_0(int heap_id, enum cp_mem_usage usage);
+
+/**
* msm_ion_do_cache_op - do cache operations.
*
* @client - pointer to ION client.
@@ -627,13 +665,15 @@
return;
}
-static inline int ion_secure_heap(struct ion_device *dev, int heap_id)
+static inline int ion_secure_heap(struct ion_device *dev, int heap_id,
+ int version, void *data)
{
return -ENODEV;
}
-static inline int ion_unsecure_heap(struct ion_device *dev, int heap_id)
+static inline int ion_unsecure_heap(struct ion_device *dev, int heap_id,
+ int version, void *data)
{
return -ENODEV;
}
@@ -649,6 +689,17 @@
return -ENODEV;
}
+static inline int msm_ion_secure_heap_2_0(int heap_id, enum cp_mem_usage usage)
+{
+ return -ENODEV;
+}
+
+static inline int msm_ion_unsecure_heap_2_0(int heap_id,
+ enum cp_mem_usage usage)
+{
+ return -ENODEV;
+}
+
static inline int msm_ion_do_cache_op(struct ion_client *client,
struct ion_handle *handle, void *vaddr,
unsigned long len, unsigned int cmd)
diff --git a/include/linux/mfd/pm8xxx/ccadc.h b/include/linux/mfd/pm8xxx/ccadc.h
index 23d0fb0..0bd4cc3 100644
--- a/include/linux/mfd/pm8xxx/ccadc.h
+++ b/include/linux/mfd/pm8xxx/ccadc.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. 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
@@ -20,9 +20,11 @@
/**
* struct pm8xxx_ccadc_platform_data -
* @r_sense: sense resistor value in (mOhms)
+ * @calib_delay_ms: how often should the adc calculate gain and offset
*/
struct pm8xxx_ccadc_platform_data {
- int r_sense;
+ int r_sense;
+ unsigned int calib_delay_ms;
};
#define CCADC_READING_RESOLUTION_N_V1 1085069
diff --git a/include/linux/mfd/pm8xxx/pm8038.h b/include/linux/mfd/pm8xxx/pm8038.h
index 90557b9..682abc8 100644
--- a/include/linux/mfd/pm8xxx/pm8038.h
+++ b/include/linux/mfd/pm8xxx/pm8038.h
@@ -33,6 +33,7 @@
#include <linux/leds-pm8xxx.h>
#include <linux/mfd/pm8xxx/ccadc.h>
#include <linux/mfd/pm8xxx/spk.h>
+#include <linux/mfd/pm8xxx/tm.h>
#define PM8038_CORE_DEV_NAME "pm8038-core"
@@ -64,6 +65,9 @@
#define PM8038_RESOUT_IRQ PM8038_IRQ_BLOCK_BIT(6, 4)
+#define PM8038_OVERTEMP_IRQ PM8038_IRQ_BLOCK_BIT(4, 2)
+#define PM8038_TEMPSTAT_IRQ PM8038_IRQ_BLOCK_BIT(6, 7)
+
struct pm8038_platform_data {
int irq_base;
struct pm8xxx_gpio_platform_data *gpio_pdata;
diff --git a/include/linux/mfd/pm8xxx/pm8921-bms.h b/include/linux/mfd/pm8xxx/pm8921-bms.h
index 537e0b5..90c2d99 100644
--- a/include/linux/mfd/pm8xxx/pm8921-bms.h
+++ b/include/linux/mfd/pm8xxx/pm8921-bms.h
@@ -117,7 +117,6 @@
* @i_test: current at which the unusable charger cutoff is to be
* calculated or the peak system current (mA)
* @v_failure: the voltage at which the battery is considered empty(mV)
- * @calib_delay_ms: how often should the adc calculate gain and offset
* @enable_fcc_learning: if set the driver will learn full charge
* capacity of the battery upon end of charge
*/
@@ -127,7 +126,6 @@
unsigned int r_sense;
unsigned int i_test;
unsigned int v_failure;
- unsigned int calib_delay_ms;
unsigned int max_voltage_uv;
unsigned int rconn_mohm;
int enable_fcc_learning;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 71484da..447fbbb 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -253,7 +253,6 @@
mmc_pm_flag_t pm_caps; /* supported pm features */
-#ifdef CONFIG_MMC_CLKGATE
int clk_requests; /* internal reference counter */
unsigned int clk_delay; /* number of MCI clk hold cycles */
bool clk_gated; /* clock gated */
@@ -263,7 +262,6 @@
struct mutex clk_gate_mutex; /* mutex for clock gating */
struct device_attribute clkgate_delay_attr;
unsigned long clkgate_delay;
-#endif
/* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index f757fae..19728fe 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -86,6 +86,8 @@
MDP_YCRYCB_H2V1, /* YCrYCb interleave */
MDP_Y_CRCB_H2V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */
MDP_Y_CBCR_H2V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */
+ MDP_Y_CRCB_H1V2,
+ MDP_Y_CBCR_H1V2,
MDP_RGBA_8888, /* ARGB 888 */
MDP_BGRA_8888, /* ABGR 888 */
MDP_RGBX_8888, /* RGBX 888 */
@@ -98,10 +100,10 @@
MDP_Y_CBCR_H1V1, /* Y and CbCr, pseduo planer w/ Cb is in MSB */
MDP_YCRCB_H1V1, /* YCrCb interleave */
MDP_YCBCR_H1V1, /* YCbCr interleave */
+ MDP_BGR_565, /* BGR 565 planer */
MDP_IMGTYPE_LIMIT,
MDP_RGB_BORDERFILL, /* border fill pipe */
- MDP_BGR_565 = MDP_IMGTYPE2_START, /* BGR 565 planer */
- MDP_FB_FORMAT, /* framebuffer format */
+ MDP_FB_FORMAT = MDP_IMGTYPE2_START, /* framebuffer format */
MDP_IMGTYPE_LIMIT2 /* Non valid image type after this enum */
};
@@ -118,6 +120,9 @@
NUM_HSIC_PARAM,
};
+#define MDSS_MDP_ROT_ONLY 0x80
+#define MDSS_MDP_RIGHT_MIXER 0x100
+
/* mdp_blit_req flag values */
#define MDP_ROT_NOP 0
#define MDP_FLIP_LR 0x1
@@ -245,6 +250,7 @@
uint32_t version_key;
struct msmfb_data plane1_data;
struct msmfb_data plane2_data;
+ struct msmfb_data dst_data;
};
struct msmfb_img {
diff --git a/include/linux/msm_rotator.h b/include/linux/msm_rotator.h
index 0f15a8b..6cfbb35 100644
--- a/include/linux/msm_rotator.h
+++ b/include/linux/msm_rotator.h
@@ -31,6 +31,7 @@
unsigned char rotations;
int enable;
unsigned int downscale_ratio;
+ unsigned int secure;
};
struct msm_rotator_data_info {
diff --git a/include/linux/msm_thermal.h b/include/linux/msm_thermal.h
new file mode 100644
index 0000000..fe9be89
--- /dev/null
+++ b/include/linux/msm_thermal.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2012, Code Aurora Forum. 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 __MSM_THERMAL_H
+#define __MSM_THERMAL_H
+
+struct msm_thermal_data {
+ uint32_t sensor_id;
+ uint32_t poll_ms;
+ uint32_t limit_temp;
+ uint32_t temp_hysteresis;
+ uint32_t limit_freq;
+};
+
+#ifdef CONFIG_THERMAL_MONITOR
+extern int msm_thermal_init(struct msm_thermal_data *pdata);
+#else
+static inline int msm_thermal_init(struct msm_thermal_data *pdata)
+{
+ return -ENOSYS;
+}
+#endif
+
+#endif /*__MSM_THERMAL_H*/
diff --git a/include/linux/msm_vidc_dec.h b/include/linux/msm_vidc_dec.h
index 0c03e13..3d8907a 100644
--- a/include/linux/msm_vidc_dec.h
+++ b/include/linux/msm_vidc_dec.h
@@ -207,6 +207,9 @@
#define VDEC_IOCTL_GET_DISABLE_DMX_SUPPORT \
_IOR(VDEC_IOCTL_MAGIC, 37, struct vdec_ioctl_msg)
+#define VDEC_IOCTL_SET_PERF_CLK \
+ _IOR(VDEC_IOCTL_MAGIC, 38, struct vdec_ioctl_msg)
+
enum vdec_picture {
PICTURE_TYPE_I,
PICTURE_TYPE_P,
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index f8329dd..9da1999 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -24,6 +24,7 @@
#include <linux/usb/otg.h>
#include <linux/wakelock.h>
#include <linux/pm_qos.h>
+#include <linux/hrtimer.h>
/*
* The following are bit fields describing the usb_request.udc_priv word.
@@ -160,6 +161,16 @@
};
/**
+ * Used different VDDCX voltage values
+ */
+enum usb_vdd_value {
+ VDD_NONE = 0,
+ VDD_MIN,
+ VDD_MAX,
+ VDD_VAL_MAX,
+};
+
+/**
* struct msm_otg_platform_data - platform device data
* for msm_otg driver.
* @phy_init_seq: PHY configuration sequence. val, reg pairs
@@ -181,6 +192,8 @@
* @enable_lpm_on_suspend: Enable the USB core to go into Low
* Power Mode, when USB bus is suspended but cable
* is connected.
+ * @core_clk_always_on_workaround: Don't disable core_clk when
+ * USB enters LPM.
* @bus_scale_table: parameters for bus bandwidth requirements
*/
struct msm_otg_platform_data {
@@ -197,6 +210,7 @@
bool disable_reset_on_disconnect;
bool enable_dcd;
bool enable_lpm_on_dev_suspend;
+ bool core_clk_always_on_workaround;
struct msm_bus_scale_pdata *bus_scale_table;
};
@@ -355,8 +369,14 @@
unsigned int dock_connect_irq;
};
+/**
+ * struct msm_hsic_peripheral_platform_data: HSIC peripheral
+ * platform data.
+ * @core_clk_always_on_workaround: Don't disable core_clk when
+ * HSIC enters LPM.
+ */
struct msm_hsic_peripheral_platform_data {
- bool keep_core_clk_on_suspend_workaround;
+ bool core_clk_always_on_workaround;
};
struct usb_bam_pipe_connect {
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index f6f3c1e..63ebdea 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -2295,6 +2295,12 @@
#define V4L2_EVENT_FRAME_SYNC 4
#define V4L2_EVENT_PRIVATE_START 0x08000000
+#define V4L2_EVENT_MSM_VIDC_START (V4L2_EVENT_PRIVATE_START + 0x00001000)
+#define V4L2_EVENT_MSM_VIDC_FLUSH_DONE (V4L2_EVENT_PRIVATE_START + 1)
+#define V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED \
+ (V4L2_EVENT_PRIVATE_START + 2)
+#define V4L2_EVENT_MSM_VIDC_CLOSE_DONE (V4L2_EVENT_PRIVATE_START + 3)
+
/* Payload for V4L2_EVENT_VSYNC */
struct v4l2_event_vsync {
/* Can be V4L2_FIELD_ANY, _NONE, _TOP or _BOTTOM */
diff --git a/include/media/msm/vcd_property.h b/include/media/msm/vcd_property.h
index 37d2343..acd0fa3 100644
--- a/include/media/msm/vcd_property.h
+++ b/include/media/msm/vcd_property.h
@@ -54,6 +54,7 @@
#define VCD_REQ_PERF_LEVEL (VCD_START_BASE + 0x26)
#define VCD_I_SLICE_DELIVERY_MODE (VCD_START_BASE + 0x27)
#define VCD_I_VOP_TIMING_CONSTANT_DELTA (VCD_START_BASE + 0x28)
+#define VCD_I_SET_TURBO_CLK (VCD_START_BASE + 0x29)
#define VCD_START_REQ (VCD_START_BASE + 0x1000)
#define VCD_I_REQ_IFRAME (VCD_START_REQ + 0x1)
diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h
index bcd0afb..3308243 100644
--- a/include/media/msm_camera.h
+++ b/include/media/msm_camera.h
@@ -190,6 +190,9 @@
#define MSM_CAM_IOCTL_EEPROM_IO_CFG \
_IOW(MSM_CAM_IOCTL_MAGIC, 53, struct msm_eeprom_cfg_data *)
+#define MSM_CAM_IOCTL_ISPIF_IO_CFG \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 54, struct ispif_cfg_data *)
+
struct msm_mctl_pp_cmd {
int32_t id;
uint16_t length;
@@ -443,10 +446,12 @@
#define CMD_VFE_BUFFER_RELEASE 51
#define CMD_VFE_PROCESS_IRQ 52
-#define CMD_AXI_CFG_PRIM 0xF1
-#define CMD_AXI_CFG_PRIM_ALL_CHNLS 0xF2
-#define CMD_AXI_CFG_SEC 0xF4
-#define CMD_AXI_CFG_SEC_ALL_CHNLS 0xF8
+#define CMD_AXI_CFG_PRIM 0xc1
+#define CMD_AXI_CFG_PRIM_ALL_CHNLS 0xc2
+#define CMD_AXI_CFG_SEC 0xc4
+#define CMD_AXI_CFG_SEC_ALL_CHNLS 0xc8
+#define CMD_AXI_CFG_TERT1 0xd0
+
#define CMD_AXI_START 0xE1
#define CMD_AXI_STOP 0xE2
@@ -546,10 +551,11 @@
#define OUTPUT_ZSL_ALL_CHNLS 10
#define LAST_AXI_OUTPUT_MODE_ENUM = OUTPUT_ZSL_ALL_CHNLS
-#define OUTPUT_PRIM 0xF1
-#define OUTPUT_PRIM_ALL_CHNLS 0xF2
-#define OUTPUT_SEC 0xF4
-#define OUTPUT_SEC_ALL_CHNLS 0xF8
+#define OUTPUT_PRIM 0xC1
+#define OUTPUT_PRIM_ALL_CHNLS 0xC2
+#define OUTPUT_SEC 0xC4
+#define OUTPUT_SEC_ALL_CHNLS 0xC8
+#define OUTPUT_TERT1 0xD0
#define MSM_FRAME_PREV_1 0
@@ -797,7 +803,10 @@
#define CFG_SET_AUTOFLASH 41
#define CFG_SET_EXPOSURE_COMPENSATION 42
#define CFG_SET_ISO 43
-#define CFG_MAX 44
+#define CFG_START_STREAM 44
+#define CFG_STOP_STREAM 45
+#define CFG_GET_CSI_PARAMS 46
+#define CFG_MAX 47
#define MOVE_NEAR 0
@@ -1127,6 +1136,112 @@
uint16_t index;
};
+struct msm_camera_csid_vc_cfg {
+ uint8_t cid;
+ uint8_t dt;
+ uint8_t decode_format;
+};
+
+struct csi_lane_params_t {
+ uint8_t csi_lane_assign;
+ uint8_t csi_lane_mask;
+ uint8_t csi_if;
+ uint8_t csid_core;
+ uint32_t csid_version;
+};
+
+#define CSI_EMBED_DATA 0x12
+#define CSI_RESERVED_DATA_0 0x13
+#define CSI_YUV422_8 0x1E
+#define CSI_RAW8 0x2A
+#define CSI_RAW10 0x2B
+#define CSI_RAW12 0x2C
+
+#define CSI_DECODE_6BIT 0
+#define CSI_DECODE_8BIT 1
+#define CSI_DECODE_10BIT 2
+#define CSI_DECODE_DPCM_10_8_10 5
+
+#define ISPIF_STREAM(intf, action) (((intf)<<ISPIF_S_STREAM_SHIFT)+(action))
+#define ISPIF_ON_FRAME_BOUNDARY (0x01 << 0)
+#define ISPIF_OFF_FRAME_BOUNDARY (0x01 << 1)
+#define ISPIF_OFF_IMMEDIATELY (0x01 << 2)
+#define ISPIF_S_STREAM_SHIFT 4
+
+
+#define PIX_0 (0x01 << 0)
+#define RDI_0 (0x01 << 1)
+#define PIX_1 (0x01 << 2)
+#define RDI_1 (0x01 << 3)
+#define PIX_2 (0x01 << 4)
+#define RDI_2 (0x01 << 5)
+
+
+enum msm_ispif_intftype {
+ PIX0,
+ RDI0,
+ PIX1,
+ RDI1,
+ PIX2,
+ RDI2,
+ INTF_MAX,
+};
+
+enum msm_ispif_vc {
+ VC0,
+ VC1,
+ VC2,
+ VC3,
+};
+
+enum msm_ispif_cid {
+ CID0,
+ CID1,
+ CID2,
+ CID3,
+ CID4,
+ CID5,
+ CID6,
+ CID7,
+ CID8,
+ CID9,
+ CID10,
+ CID11,
+ CID12,
+ CID13,
+ CID14,
+ CID15,
+};
+
+struct msm_ispif_params {
+ uint8_t intftype;
+ uint16_t cid_mask;
+ uint8_t csid;
+};
+
+struct msm_ispif_params_list {
+ uint32_t len;
+ struct msm_ispif_params params[4];
+};
+
+enum ispif_cfg_type_t {
+ ISPIF_INIT,
+ ISPIF_SET_CFG,
+ ISPIF_SET_ON_FRAME_BOUNDARY,
+ ISPIF_SET_OFF_FRAME_BOUNDARY,
+ ISPIF_SET_OFF_IMMEDIATELY,
+ ISPIF_RELEASE,
+};
+
+struct ispif_cfg_data {
+ enum ispif_cfg_type_t cfgtype;
+ union {
+ uint32_t csid_version;
+ int cmd;
+ struct msm_ispif_params_list ispif_params;
+ } cfg;
+};
+
struct sensor_cfg_data {
int cfgtype;
int mode;
@@ -1153,6 +1268,7 @@
struct sensor_calib_data calib_info;
struct sensor_output_info_t output_info;
struct msm_eeprom_data_t eeprom_data;
+ struct csi_lane_params_t csi_lane_params;
/* QRD */
uint16_t antibanding;
uint8_t contrast;
@@ -1410,6 +1526,7 @@
uint8_t flash_enabled;
uint8_t strobe_flash_enabled;
uint8_t actuator_enabled;
+ uint8_t ispif_supported;
int8_t total_steps;
uint8_t support_3d;
enum flash_type flashtype;
@@ -1480,4 +1597,66 @@
uint32_t len;
};
+enum msm_camss_irq_idx {
+ CAMERA_SS_IRQ_0,
+ CAMERA_SS_IRQ_1,
+ CAMERA_SS_IRQ_2,
+ CAMERA_SS_IRQ_3,
+ CAMERA_SS_IRQ_4,
+ CAMERA_SS_IRQ_5,
+ CAMERA_SS_IRQ_6,
+ CAMERA_SS_IRQ_7,
+ CAMERA_SS_IRQ_8,
+ CAMERA_SS_IRQ_9,
+ CAMERA_SS_IRQ_10,
+ CAMERA_SS_IRQ_11,
+ CAMERA_SS_IRQ_12,
+ CAMERA_SS_IRQ_MAX
+};
+
+enum msm_cam_hw_idx {
+ MSM_CAM_HW_MICRO,
+ MSM_CAM_HW_CCI,
+ MSM_CAM_HW_CSI0,
+ MSM_CAM_HW_CSI1,
+ MSM_CAM_HW_CSI2,
+ MSM_CAM_HW_CSI3,
+ MSM_CAM_HW_ISPIF,
+ MSM_CAM_HW_CPP,
+ MSM_CAM_HW_VFE0,
+ MSM_CAM_HW_VFE1,
+ MSM_CAM_HW_JPEG0,
+ MSM_CAM_HW_JPEG1,
+ MSM_CAM_HW_JPEG2,
+ MSM_CAM_HW_MAX
+};
+
+struct msm_camera_irq_cfg {
+ /* Bit mask of all the camera hardwares that needs to
+ * be composited into a single IRQ to the MSM.
+ * Current usage: (may be updated based on hw changes)
+ * Bits 31:13 - Reserved.
+ * Bits 12:0
+ * 12 - MSM_CAM_HW_JPEG2
+ * 11 - MSM_CAM_HW_JPEG1
+ * 10 - MSM_CAM_HW_JPEG0
+ * 9 - MSM_CAM_HW_VFE1
+ * 8 - MSM_CAM_HW_VFE0
+ * 7 - MSM_CAM_HW_CPP
+ * 6 - MSM_CAM_HW_ISPIF
+ * 5 - MSM_CAM_HW_CSI3
+ * 4 - MSM_CAM_HW_CSI2
+ * 3 - MSM_CAM_HW_CSI1
+ * 2 - MSM_CAM_HW_CSI0
+ * 1 - MSM_CAM_HW_CCI
+ * 0 - MSM_CAM_HW_MICRO
+ */
+ uint32_t cam_hw_mask;
+ uint8_t irq_idx;
+ uint8_t num_hwcore;
+};
+
+#define MSM_IRQROUTER_CFG_COMPIRQ \
+ _IOWR('V', BASE_VIDIOC_PRIVATE, void __user *)
+
#endif /* __LINUX_MSM_CAMERA_H */
diff --git a/include/media/msm_isp.h b/include/media/msm_isp.h
index cb728a0c..93f6c8b 100644
--- a/include/media/msm_isp.h
+++ b/include/media/msm_isp.h
@@ -59,6 +59,8 @@
#define MSG_ID_OUTPUT_PRIMARY 40
#define MSG_ID_OUTPUT_SECONDARY 41
#define MSG_ID_STATS_COMPOSITE 42
+#define MSG_ID_OUTPUT_TERTIARY1 43
+
/* ISP command IDs */
#define VFE_CMD_DUMMY_0 0
@@ -326,6 +328,14 @@
#define VFE_OUTPUTS_RAW BIT(8)
#define VFE_OUTPUTS_JPEG_AND_THUMB BIT(9)
#define VFE_OUTPUTS_THUMB_AND_JPEG BIT(10)
+#define VFE_OUTPUTS_RDI0 BIT(11)
+
+
+
+struct msm_frame_info {
+ uint32_t image_mode;
+ uint32_t path;
+};
#endif /*__MSM_ISP_H__*/
diff --git a/include/media/tavarua.h b/include/media/tavarua.h
index 52194f9..9943287 100644
--- a/include/media/tavarua.h
+++ b/include/media/tavarua.h
@@ -172,6 +172,10 @@
V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD, /* 0x800002E */
V4L2_CID_PRIVATE_SINR_THRESHOLD, /* 0x800002F : IRIS */
V4L2_CID_PRIVATE_SINR_SAMPLES, /* 0x8000030 : IRIS */
+ V4L2_CID_PRIVATE_SPUR_FREQ,
+ V4L2_CID_PRIVATE_SPUR_FREQ_RMSSI,
+ V4L2_CID_PRIVATE_SPUR_SELECTION,
+ V4L2_CID_PRIVATE_UPDATE_SPUR_TABLE,
};
@@ -484,4 +488,47 @@
TAVARUA_REGION_OTHER
};
+enum {
+ ONE_BYTE = 1,
+ TWO_BYTE,
+ THREE_BYTE,
+ FOUR_BYTE,
+ FIVE_BYTE,
+ SIX_BYTE,
+ SEVEN_BYTE,
+ EIGHT_BYTE,
+ NINE_BYTE,
+ TEN_BYTE,
+ ELEVEN_BYTE,
+ TWELVE_BYTE,
+ THIRTEEN_BYTE
+};
+#define XFR_READ (0)
+#define XFR_WRITE (1)
+#define XFR_MODE_OFFSET (0)
+#define XFR_ADDR_MSB_OFFSET (1)
+#define XFR_ADDR_LSB_OFFSET (2)
+#define XFR_DATA_OFFSET (3)
+#define SPUR_DATA_SIZE (3)
+#define MAX_SPUR_FREQ_LIMIT (30)
+#define READ_COMPLETE (0x20)
+#define SPUR_TABLE_ADDR (0x0BB7)
+#define SPUR_TABLE_START_ADDR (SPUR_TABLE_ADDR + 1)
+#define XFR_PEEK_COMPLETE (XFR_PEEK_MODE | READ_COMPLETE)
+#define XFR_POKE_COMPLETE (XFR_POKE_MODE)
+
+#define COMPUTE_SPUR(val) ((((val) - (76000)) / (50)))
+#define GET_FREQ(val, bit) ((bit == 1) ? ((val) >> 8) : ((val) & 0xFF))
+
+struct fm_spur_data {
+ int freq[MAX_SPUR_FREQ_LIMIT];
+ __s8 rmssi[MAX_SPUR_FREQ_LIMIT];
+} __packed;
+
+struct fm_def_data_wr_req {
+ __u8 mode;
+ __u8 length;
+ __u8 data[XFR_REG_NUM];
+} __packed;
+
#endif /* __LINUX_TAVARUA_H */
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index ec8d73e..431dedf 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -1176,6 +1176,7 @@
} __attribute__((packed));
#define ADM_CMD_CONNECT_AFE_PORT 0x00010320
+#define ADM_CMD_DISCONNECT_AFE_PORT 0x00010321
struct adm_cmd_connect_afe_port {
struct apr_hdr hdr;
diff --git a/include/sound/q6adm.h b/include/sound/q6adm.h
index 29fb606..56594d4 100644
--- a/include/sound/q6adm.h
+++ b/include/sound/q6adm.h
@@ -41,6 +41,7 @@
unsigned int *port_id, int copp_id);
int adm_connect_afe_port(int mode, int session_id, int port_id);
+int adm_disconnect_afe_port(int mode, int session_id, int port_id);
#ifdef CONFIG_RTAC
int adm_get_copp_id(int port_id);
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index 4676a02..17fef15 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -241,7 +241,7 @@
struct snd_soc_dai_driver *driver;
/* DAI runtime info */
- unsigned int capture_active:1; /* stream is in use */
+ unsigned int capture_active; /* stream is in use */
unsigned int playback_active:1; /* stream is in use */
unsigned int symmetric_rates:1;
struct snd_pcm_runtime *runtime;
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index aaccc5f..372c60d 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -234,6 +234,7 @@
enum target_sc_flags_table {
TARGET_SCF_BIDI_OP = 0x01,
TARGET_SCF_ACK_KREF = 0x02,
+ TARGET_SCF_UNKNOWN_SIZE = 0x04,
};
/* fabric independent task management function values */
@@ -538,6 +539,7 @@
/* Used to signal cmd->se_tfo->check_release_cmd() usage per cmd */
unsigned check_release:1;
unsigned cmd_wait_set:1;
+ unsigned unknown_data_length:1;
/* See se_cmd_flags_table */
u32 se_cmd_flags;
u32 se_ordered_id;
diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h
index 10c6908..f27f575 100644
--- a/include/target/target_core_fabric.h
+++ b/include/target/target_core_fabric.h
@@ -112,7 +112,7 @@
void transport_init_se_cmd(struct se_cmd *, struct target_core_fabric_ops *,
struct se_session *, u32, int, int, unsigned char *);
int transport_lookup_cmd_lun(struct se_cmd *, u32);
-int transport_generic_allocate_tasks(struct se_cmd *, unsigned char *);
+int target_setup_cmd_from_cdb(struct se_cmd *, unsigned char *);
void target_submit_cmd(struct se_cmd *, struct se_session *, unsigned char *,
unsigned char *, u32, u32, int, int, int);
int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,
diff --git a/sound/soc/codecs/wcd9304.c b/sound/soc/codecs/wcd9304.c
index 94bdd27..be31953 100644
--- a/sound/soc/codecs/wcd9304.c
+++ b/sound/soc/codecs/wcd9304.c
@@ -1930,6 +1930,20 @@
};
+static const struct snd_soc_dapm_route audio_i2s_map[] = {
+ {"RX_I2S_CLK", NULL, "CP"},
+ {"RX_I2S_CLK", NULL, "CDC_CONN"},
+ {"SLIM RX1", NULL, "RX_I2S_CLK"},
+ {"SLIM RX2", NULL, "RX_I2S_CLK"},
+ {"SLIM RX3", NULL, "RX_I2S_CLK"},
+ {"SLIM RX4", NULL, "RX_I2S_CLK"},
+
+ {"SLIM TX1", NULL, "TX_I2S_CLK"},
+ {"SLIM TX2", NULL, "TX_I2S_CLK"},
+ {"SLIM TX3", NULL, "TX_I2S_CLK"},
+ {"SLIM TX4", NULL, "TX_I2S_CLK"},
+};
+
static const struct snd_soc_dapm_route audio_map[] = {
/* Earpiece (RX MIX1) */
{"EAR", NULL, "EAR PA"},
@@ -1944,10 +1958,12 @@
{"LINEOUT2 DAC", NULL, "RX_BIAS"},
{"LINEOUT2", NULL, "LINEOUT2 PA"},
+ {"LINEOUT2 PA", NULL, "CP"},
{"LINEOUT2 PA", NULL, "LINEOUT2 DAC"},
{"LINEOUT2 DAC", NULL, "DAC3 MUX"},
{"LINEOUT1", NULL, "LINEOUT1 PA"},
+ {"LINEOUT2 PA", NULL, "CP"},
{"LINEOUT1 PA", NULL, "LINEOUT1 DAC"},
{"LINEOUT1 DAC", NULL, "DAC2 MUX"},
@@ -2616,9 +2632,9 @@
}
snd_soc_update_bits(codec, SITAR_A_CDC_CLK_TX_I2S_CTL,
0x03, tx_fs_rate);
+ } else {
+ sitar->dai[dai->id - 1].rate = params_rate(params);
}
- } else {
- sitar->dai[dai->id - 1].rate = params_rate(params);
}
/**
@@ -2663,9 +2679,9 @@
}
snd_soc_update_bits(codec, SITAR_A_CDC_CLK_RX_I2S_CTL,
0x03, (rx_fs_rate >> 0x05));
+ } else {
+ sitar->dai[dai->id - 1].rate = params_rate(params);
}
- } else {
- sitar->dai[dai->id - 1].rate = params_rate(params);
}
return 0;
@@ -2712,6 +2728,37 @@
},
};
+static struct snd_soc_dai_driver sitar_i2s_dai[] = {
+ {
+ .name = "sitar_i2s_rx1",
+ .id = AIF1_PB,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .rates = WCD9304_RATES,
+ .formats = SITAR_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &sitar_dai_ops,
+ },
+ {
+ .name = "sitar_i2s_tx1",
+ .id = AIF1_CAP,
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .rates = WCD9304_RATES,
+ .formats = SITAR_FORMATS,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &sitar_dai_ops,
+ },
+};
+
static int sitar_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -4636,11 +4683,16 @@
sitar_1_1_reg_defaults[i].val);
}
+
+static const struct sitar_reg_mask_val sitar_i2c_codec_reg_init_val[] = {
+ {WCD9XXX_A_CHIP_CTL, 0x1, 0x1},
+};
+
static const struct sitar_reg_mask_val sitar_codec_reg_init_val[] = {
/* Initialize current threshold to 350MA
* number of wait and run cycles to 4096
*/
- {SITAR_A_RX_HPH_OCP_CTL, 0xF8, 0x60},
+ {SITAR_A_RX_HPH_OCP_CTL, 0xE0, 0x60},
{SITAR_A_RX_COM_OCP_COUNT, 0xFF, 0xFF},
{SITAR_A_QFUSE_CTL, 0xFF, 0x03},
@@ -4677,6 +4729,15 @@
{SITAR_A_CDC_CLK_MCLK_CTL, 0x01, 0x01},
};
+static void sitar_i2c_codec_init_reg(struct snd_soc_codec *codec)
+{
+ u32 i;
+ for (i = 0; i < ARRAY_SIZE(sitar_i2c_codec_reg_init_val); i++)
+ snd_soc_update_bits(codec, sitar_i2c_codec_reg_init_val[i].reg,
+ sitar_i2c_codec_reg_init_val[i].mask,
+ sitar_i2c_codec_reg_init_val[i].val);
+}
+
static void sitar_codec_init_reg(struct snd_soc_codec *codec)
{
u32 i;
@@ -4732,6 +4793,9 @@
sitar->pdata = dev_get_platdata(codec->dev->parent);
sitar_update_reg_defaults(codec);
sitar_codec_init_reg(codec);
+ sitar->intf_type = wcd9xxx_get_intf_type();
+ if (sitar->intf_type == WCD9XXX_INTERFACE_TYPE_I2C)
+ sitar_i2c_codec_init_reg(codec);
ret = sitar_handle_pdata(sitar);
if (IS_ERR_VALUE(ret)) {
@@ -4743,6 +4807,12 @@
ARRAY_SIZE(sitar_snd_controls));
snd_soc_dapm_new_controls(dapm, sitar_dapm_widgets,
ARRAY_SIZE(sitar_dapm_widgets));
+ if (sitar->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+ snd_soc_dapm_new_controls(dapm, sitar_dapm_i2s_widgets,
+ ARRAY_SIZE(sitar_dapm_i2s_widgets));
+ snd_soc_dapm_add_routes(dapm, audio_i2s_map,
+ ARRAY_SIZE(audio_i2s_map));
+ }
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
sitar_version = snd_soc_read(codec, WCD9XXX_A_CHIP_VERSION);
@@ -4970,8 +5040,12 @@
S_IFREG | S_IRUGO, NULL, (void *) "TRRS", &codec_debug_ops);
#endif
- ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_sitar,
+ if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_sitar,
sitar_dai, ARRAY_SIZE(sitar_dai));
+ else if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_sitar,
+ sitar_i2s_dai, ARRAY_SIZE(sitar_i2s_dai));
return ret;
}
static int __devexit sitar_remove(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 9dcfc1c7..e85e9f5 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -4256,7 +4256,8 @@
continue;
if (!strncmp(w->sname,
tabla_dai[j].playback.stream_name, 13)) {
- --tabla_p->dai[j].ch_act;
+ if (tabla_p->dai[j].ch_act)
+ --tabla_p->dai[j].ch_act;
break;
}
}
@@ -6129,14 +6130,14 @@
int scaled;
struct tabla_mbhc_plug_type_cfg *plug_type_ptr;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- const bool vddio = (tabla->mbhc_data.micb_mv != VDDIO_MICBIAS_MV);
- int num_det = (MBHC_NUM_DCE_PLUG_DETECT + vddio);
+ int num_det = MBHC_NUM_DCE_PLUG_DETECT + 1;
enum tabla_mbhc_plug_type plug_type[num_det];
s16 mb_v[num_det];
s32 mic_mv[num_det];
bool inval;
bool highdelta;
bool ahighv = false, highv;
+ bool gndmicswapped = false;
/* make sure override is on */
WARN_ON(!(snd_soc_read(codec, TABLA_A_CDC_MBHC_B1_CTL) & 0x04));
@@ -6153,11 +6154,10 @@
* 1st: check if voltage is in invalid range
* 2nd - N-2nd: check voltage range and delta
* N-1st: check voltage range, delta with HPHR GND switch
- * Nth: check voltage range with VDDIO switch if micbias V != vddio V*/
+ * Nth: check voltage range with VDDIO switch */
for (i = 0; i < num_det; i++) {
- gndswitch = (i == (num_det - 1 - vddio));
- vddioswitch = (vddio && ((i == num_det - 1) ||
- (i == num_det - 2)));
+ gndswitch = (i == (num_det - 2));
+ vddioswitch = (i == (num_det - 1)) || (i == (num_det - 2));
if (i == 0) {
mb_v[i] = tabla_codec_setup_hs_polling(codec);
mic_mv[i] = tabla_codec_sta_dce_v(codec, 1 , mb_v[i]);
@@ -6183,7 +6183,7 @@
* was done with gndswitch, don't compare with DCE
* with gndswitch */
highdelta = tabla_is_inval_ins_delta(codec, scaled,
- mic_mv[i - !gndswitch - vddioswitch],
+ mic_mv[i - 1],
TABLA_MBHC_FAKE_INS_DELTA_SCALED_MV);
inval = (tabla_is_inval_ins_range(codec, mic_mv[i],
highhph, &highv) ||
@@ -6198,9 +6198,13 @@
* good headset is detected but HPHR GND switch makes
* delta difference */
if (i == (num_det - 2) && highdelta && !ahighv)
- plug_type[0] = PLUG_TYPE_GND_MIC_SWAP;
- else if (i == (num_det - 1) && inval)
- plug_type[0] = PLUG_TYPE_INVALID;
+ gndmicswapped = true;
+ else if (i == (num_det - 1) && inval) {
+ if (gndmicswapped)
+ plug_type[0] = PLUG_TYPE_GND_MIC_SWAP;
+ else
+ plug_type[0] = PLUG_TYPE_INVALID;
+ }
}
pr_debug("%s: DCE #%d, %04x, V %d, scaled V %d, GND %d, "
"VDDIO %d, inval %d\n", __func__,
diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig
index 36cbb23..1125d20 100644
--- a/sound/soc/msm/Kconfig
+++ b/sound/soc/msm/Kconfig
@@ -105,6 +105,16 @@
help
To add support for SoC audio on MSM8960.
+config SND_SOC_MSM_QDSP6V2_INTF
+ bool "SoC Q6 audio driver for MSMCOPPER"
+ depends on MSM_QDSP6_APR
+ help
+ To add support for SoC audio on MSMCOPPER.
+ This will enable all the platform specific
+ interactions towards DSP. It includes asm,
+ adm and afe interfaces on the DSP.
+
+
config SND_SOC_VOICE
bool "SoC Q6 voice driver for MSM8960"
depends on SND_SOC_MSM_QDSP6_INTF
@@ -119,6 +129,15 @@
help
To add support for MSM QDSP6 Soc Audio.
+config SND_SOC_QDSP6V2
+ tristate "SoC ALSA audio driver for QDSP6V2"
+ select SND_SOC_MSM_QDSP6V2_INTF
+ help
+ To add support for MSM QDSP6V2 Soc Audio.
+ This will enable sound soc platform specific
+ audio drivers. This includes q6asm, q6adm,
+ q6afe interfaces to DSP using apr.
+
config SND_SOC_MSM8960
tristate "SoC Machine driver for MSM8960 and APQ8064 boards"
depends on ARCH_MSM8960 || ARCH_APQ8064
@@ -134,6 +153,20 @@
help
To add support for SoC audio on MSM8960 and APQ8064 boards
+config SND_SOC_MSM8974
+ tristate "SoC Machine driver for MSMCOPPER boards"
+ depends on ARCH_MSMCOPPER
+ select SND_SOC_QDSP6V2
+ select SND_SOC_MSM_STUB
+ select SND_SOC_MSM_HOSTLESS_PCM
+ select SND_DYNAMIC_MINORS
+ help
+ To add support for SoC audio on MSMCOPPER.
+ This will enable sound soc drivers which
+ interfaces with DSP, also it will enable
+ the machine drivers and the corresponding
+ DAI-links.
+
config SND_SOC_MDM9615
tristate "SoC Machine driver for MDM9615 boards"
depends on ARCH_MSM9615
diff --git a/sound/soc/msm/apq8064.c b/sound/soc/msm/apq8064.c
index a7fd38b..2da5c6a 100644
--- a/sound/soc/msm/apq8064.c
+++ b/sound/soc/msm/apq8064.c
@@ -18,6 +18,8 @@
#include <linux/gpio.h>
#include <linux/mfd/pm8xxx/pm8921.h>
#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/slimbus/slimbus.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
@@ -1417,6 +1419,19 @@
return 0;
}
+static int msm_slimbus_1_startup(struct snd_pcm_substream *substream)
+{
+ struct slim_controller *slim = slim_busnum_to_ctrl(1);
+
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+
+ if (slim != NULL)
+ pm_runtime_get_sync(slim->dev.parent);
+
+ return 0;
+}
+
static void msm_auxpcm_shutdown(struct snd_pcm_substream *substream)
{
@@ -1433,6 +1448,19 @@
rtd->dai_link->cpu_dai_name, rtd->dai_link->codec_dai_name);
}
+static void msm_slimbus_1_shutdown(struct snd_pcm_substream *substream)
+{
+ struct slim_controller *slim = slim_busnum_to_ctrl(1);
+
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+
+ if (slim != NULL) {
+ pm_runtime_mark_last_busy(slim->dev.parent);
+ pm_runtime_put(slim->dev.parent);
+ }
+}
+
static struct snd_soc_ops msm_be_ops = {
.startup = msm_startup,
.hw_params = msm_hw_params,
@@ -1445,9 +1473,9 @@
};
static struct snd_soc_ops msm_slimbus_1_be_ops = {
- .startup = msm_startup,
+ .startup = msm_slimbus_1_startup,
.hw_params = msm_slimbus_1_hw_params,
- .shutdown = msm_shutdown,
+ .shutdown = msm_slimbus_1_shutdown,
};
static struct snd_soc_ops msm_slimbus_3_be_ops = {
diff --git a/sound/soc/msm/msm-compr-q6.c b/sound/soc/msm/msm-compr-q6.c
index 8061b06..d4045e1 100644
--- a/sound/soc/msm/msm-compr-q6.c
+++ b/sound/soc/msm/msm-compr-q6.c
@@ -103,7 +103,6 @@
break;
} else
atomic_set(&prtd->pending_buffer, 0);
-
if (runtime->status->hw_ptr >= runtime->control->appl_ptr)
break;
buf = prtd->audio_client->port[IN].buf;
@@ -261,7 +260,8 @@
compr->info.codec_param.codec.bit_rate/8;
wma_pro_cfg.block_align = compr->info.codec_param.codec.align;
wma_pro_cfg.valid_bits_per_sample =
- compr->info.codec_param.codec.options.wma.bits_per_sample;
+ compr->info.codec_param.codec\
+ .options.wma.bits_per_sample;
wma_pro_cfg.ch_mask =
compr->info.codec_param.codec.options.wma.channelmask;
wma_pro_cfg.encode_opt =
@@ -297,7 +297,7 @@
SND_AUDIOCODEC_AC3_PASS_THROUGH) {
msm_pcm_routing_reg_psthr_stream(
soc_prtd->dai_link->be_id,
- prtd->session_id, substream->stream);
+ prtd->session_id, substream->stream, 1);
}
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
@@ -307,6 +307,12 @@
break;
case SNDRV_PCM_TRIGGER_STOP:
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ if (compr->info.codec_param.codec.id ==
+ SND_AUDIOCODEC_AC3_PASS_THROUGH) {
+ msm_pcm_routing_reg_psthr_stream(
+ soc_prtd->dai_link->be_id,
+ prtd->session_id, substream->stream, 0);
+ }
atomic_set(&prtd->start, 0);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
@@ -452,9 +458,11 @@
compressed_audio.prtd = NULL;
q6asm_audio_client_buf_free_contiguous(dir,
prtd->audio_client);
-
- msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
- SNDRV_PCM_STREAM_PLAYBACK);
+ if (!(compr->info.codec_param.codec.id ==
+ SND_AUDIOCODEC_AC3_PASS_THROUGH))
+ msm_pcm_routing_dereg_phy_stream(
+ soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
q6asm_audio_client_free(prtd->audio_client);
kfree(prtd);
return 0;
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index 6e190b2..56e83d5 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -426,6 +426,30 @@
.ops = &msm_fe_dai_ops,
.name = "SEC_I2S_RX_HOSTLESS",
},
+ {
+ .playback = {
+ .stream_name = "SGLTE Playback",
+ .aif_name = "SGLTE_DL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "SGLTE Capture",
+ .aif_name = "SGLTE_UL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SGLTE",
+ },
};
static __devinit int msm_fe_dai_dev_probe(struct platform_device *pdev)
diff --git a/sound/soc/msm/msm-pcm-lpa.c b/sound/soc/msm/msm-pcm-lpa.c
index 3cab34f..269b49b 100644
--- a/sound/soc/msm/msm-pcm-lpa.c
+++ b/sound/soc/msm/msm-pcm-lpa.c
@@ -109,15 +109,16 @@
break;
} else
atomic_set(&prtd->pending_buffer, 0);
+
+ buf = prtd->audio_client->port[IN].buf;
if (runtime->status->hw_ptr >= runtime->control->appl_ptr) {
- memset((void *)buf[0].phys +
+ memset((void *)buf[0].data +
(prtd->out_head * prtd->pcm_count),
0, prtd->pcm_count);
}
pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
__func__, prtd->pcm_count);
- buf = prtd->audio_client->port[IN].buf;
param.paddr = (unsigned long)buf[0].phys
+ (prtd->out_head * prtd->pcm_count);
param.len = prtd->pcm_count;
@@ -232,10 +233,12 @@
pr_debug("SNDRV_PCM_TRIGGER_START\n");
q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
atomic_set(&prtd->start, 1);
+ atomic_set(&prtd->stop, 0);
break;
case SNDRV_PCM_TRIGGER_STOP:
pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
atomic_set(&prtd->start, 0);
+ atomic_set(&prtd->stop, 1);
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
break;
break;
@@ -323,6 +326,7 @@
prtd->dsp_cnt = 0;
atomic_set(&prtd->pending_buffer, 1);
+ atomic_set(&prtd->stop, 1);
runtime->private_data = prtd;
lpa_audio.prtd = prtd;
lpa_set_volume(lpa_audio.volume);
@@ -366,7 +370,8 @@
To issue EOS to dsp, we need to be run state otherwise
EOS is not honored.
*/
- if (msm_routing_check_backend_enabled(soc_prtd->dai_link->be_id)) {
+ if (msm_routing_check_backend_enabled(soc_prtd->dai_link->be_id) &&
+ (!atomic_read(&prtd->stop))) {
rc = q6asm_run(prtd->audio_client, 0, 0, 0);
atomic_set(&prtd->pending_buffer, 0);
prtd->cmd_ack = 0;
@@ -386,6 +391,7 @@
q6asm_audio_client_buf_free_contiguous(dir,
prtd->audio_client);
+ atomic_set(&prtd->stop, 1);
pr_debug("%s\n", __func__);
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
SNDRV_PCM_STREAM_PLAYBACK);
diff --git a/sound/soc/msm/msm-pcm-q6.h b/sound/soc/msm/msm-pcm-q6.h
index e5551ea..9e743a7 100644
--- a/sound/soc/msm/msm-pcm-q6.h
+++ b/sound/soc/msm/msm-pcm-q6.h
@@ -71,6 +71,7 @@
int close_ack;
int cmd_ack;
atomic_t start;
+ atomic_t stop;
atomic_t out_count;
atomic_t in_count;
atomic_t out_needed;
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 4e1ce52..cc51a0f6 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -48,6 +48,7 @@
static int fm_switch_enable;
static int fm_pcmrx_switch_enable;
+static int srs_alsa_ctrl_ever_called;
#define INT_RX_VOL_MAX_STEPS 0x2000
#define INT_RX_VOL_GAIN 0x2000
@@ -120,6 +121,12 @@
static void srs_send_params(int port_id, unsigned int techs,
int param_block_idx) {
+
+ /* only send commands to dsp if srs alsa ctrl was used
+ at least one time */
+ if (!srs_alsa_ctrl_ever_called)
+ return;
+
pr_debug("SRS %s: called, port_id = %d, techs flags = %u,"
" paramblockidx %d", __func__, port_id, techs,
param_block_idx);
@@ -237,7 +244,7 @@
}
void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id,
- int stream_type)
+ int stream_type, int enable)
{
int i, session_type, path_type, port_type;
u32 mode = 0;
@@ -267,8 +274,13 @@
(msm_bedais[i].active) &&
(test_bit(fedai_id, &msm_bedais[i].fe_sessions))) {
mode = afe_get_port_type(msm_bedais[i].port_id);
- adm_connect_afe_port(mode, dspst_id,
+ if (enable)
+ adm_connect_afe_port(mode, dspst_id,
msm_bedais[i].port_id);
+ else
+ adm_disconnect_afe_port(mode, dspst_id,
+ msm_bedais[i].port_id);
+
break;
}
}
@@ -328,6 +340,8 @@
payload.copp_ids[payload.num_copps++] =
msm_bedais[i].port_id;
+ srs_port_id = msm_bedais[i].port_id;
+ srs_send_params(srs_port_id, 1, 0);
}
}
if (payload.num_copps)
@@ -436,6 +450,8 @@
msm_pcm_routing_build_matrix(val,
fe_dai_map[val][session_type], path_type);
+ srs_port_id = msm_bedais[reg].port_id;
+ srs_send_params(srs_port_id, 1, 0);
}
} else {
if (test_bit(val, &msm_bedais[reg].fe_sessions) &&
@@ -505,6 +521,8 @@
session_id = voc_get_session_id(VOICE_SESSION_NAME);
else if (val == MSM_FRONTEND_DAI_VOLTE)
session_id = voc_get_session_id(VOLTE_SESSION_NAME);
+ else if (val == MSM_FRONTEND_DAI_SGLTE)
+ session_id = voc_get_session_id(SGLTE_SESSION_NAME);
else
session_id = voc_get_session_id(VOIP_SESSION_NAME);
@@ -811,6 +829,8 @@
unsigned int techs = 0;
unsigned short offset, value, max, index;
+ srs_alsa_ctrl_ever_called = 1;
+
max = sizeof(msm_srs_trumedia_params) >> 1;
index = (unsigned short)((ucontrol->value.integer.value[0] &
SRS_PARAM_INDEX_MASK) >> 31);
@@ -1303,6 +1323,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_PRI_I2S_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new sec_i2s_rx_voice_mixer_controls[] = {
@@ -1315,6 +1338,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SEC_I2S_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = {
@@ -1327,6 +1353,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = {
@@ -1342,6 +1371,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_INT_BT_SCO_RX ,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_INT_BT_SCO_RX ,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new mi2s_rx_voice_mixer_controls[] = {
@@ -1354,6 +1386,9 @@
SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_MI2S_RX,
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new afe_pcm_rx_voice_mixer_controls[] = {
@@ -1369,6 +1404,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_AFE_PCM_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new aux_pcm_rx_voice_mixer_controls[] = {
@@ -1384,6 +1422,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_AUXPCM_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new sec_aux_pcm_rx_voice_mixer_controls[] = {
@@ -1396,6 +1437,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new hdmi_rx_voice_mixer_controls[] = {
@@ -1411,6 +1455,9 @@
SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_HDMI_RX,
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new stub_rx_mixer_controls[] = {
@@ -1476,6 +1523,29 @@
msm_routing_put_voice_mixer),
};
+static const struct snd_kcontrol_new tx_sglte_mixer_controls[] = {
+ SOC_SINGLE_EXT("PRI_TX_SGLTE", MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("MI2S_TX_SGLTE", MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX_SGLTE", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_SGLTE",
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_SGLTE, 1, 0,
+ msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("AFE_PCM_TX_SGLTE", MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("AUX_PCM_TX_SGLTE", MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SEC_AUX_PCM_TX_SGLTE", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
static const struct snd_kcontrol_new tx_voip_mixer_controls[] = {
SOC_SINGLE_EXT("PRI_TX_Voip", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
@@ -1878,6 +1948,8 @@
SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("VoLTE_DL", "VoLTE Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("VoLTE_UL", "VoLTE Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SGLTE_DL", "SGLTE Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SGLTE_UL", "SGLTE Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("VOIP_UL", "VoIP Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("SLIM0_DL_HL", "SLIMBUS0_HOSTLESS Playback",
0, 0, 0, 0),
@@ -2025,6 +2097,9 @@
SND_SOC_DAPM_MIXER("VoLTE_Tx Mixer",
SND_SOC_NOPM, 0, 0, tx_volte_mixer_controls,
ARRAY_SIZE(tx_volte_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SGLTE_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_sglte_mixer_controls,
+ ARRAY_SIZE(tx_sglte_mixer_controls)),
SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
int_bt_sco_rx_mixer_controls, ARRAY_SIZE(int_bt_sco_rx_mixer_controls)),
SND_SOC_DAPM_MIXER("INTERNAL_FM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
@@ -2175,41 +2250,49 @@
{"PRI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"PRI_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"PRI_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"PRI_I2S_RX", NULL, "PRI_RX_Voice Mixer"},
{"SEC_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"SEC_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SEC_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"SEC_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"SEC_I2S_RX", NULL, "SEC_RX_Voice Mixer"},
{"SLIM_0_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"SLIM_0_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SLIM_0_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"SLIMBUS_0_RX", NULL, "SLIM_0_RX_Voice Mixer"},
{"INTERNAL_BT_SCO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"INTERNAL_BT_SCO_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"INTERNAL_BT_SCO_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX_Voice Mixer"},
{"AFE_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"AFE_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"AFE_PCM_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"AFE_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"PCM_RX", NULL, "AFE_PCM_RX_Voice Mixer"},
{"AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"AUX_PCM_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"AUX_PCM_RX", NULL, "AUX_PCM_RX_Voice Mixer"},
{"SEC_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"SEC_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SEC_AUX_PCM_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"SEC_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX_Voice Mixer"},
{"HDMI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"HDMI_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"HDMI_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"HDMI_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"HDMI", NULL, "HDMI_RX_Voice Mixer"},
{"HDMI", NULL, "HDMI_DL_HL"},
@@ -2229,6 +2312,14 @@
{"VoLTE_Tx Mixer", "AUX_PCM_TX_VoLTE", "AUX_PCM_TX"},
{"VoLTE_Tx Mixer", "SEC_AUX_PCM_TX_VoLTE", "SEC_AUX_PCM_TX"},
{"VoLTE_UL", NULL, "VoLTE_Tx Mixer"},
+ {"SGLTE_Tx Mixer", "PRI_TX_SGLTE", "PRI_I2S_TX"},
+ {"SGLTE_Tx Mixer", "MI2S_TX_SGLTE", "MI2S_TX"},
+ {"SGLTE_Tx Mixer", "SLIM_0_TX_SGLTE", "SLIMBUS_0_TX"},
+ {"SGLTE_Tx Mixer", "INTERNAL_BT_SCO_TX_SGLTE", "INT_BT_SCO_TX"},
+ {"SGLTE_Tx Mixer", "AFE_PCM_TX_SGLTE", "PCM_TX"},
+ {"SGLTE_Tx Mixer", "AUX_PCM_TX_SGLTE", "AUX_PCM_TX"},
+ {"SGLTE_Tx Mixer", "SEC_AUX_PCM_TX_SGLTE", "SEC_AUX_PCM_TX"},
+ {"SGLTE_UL", NULL, "SGLTE_Tx Mixer"},
{"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
{"Voip_Tx Mixer", "MI2S_TX_Voip", "MI2S_TX"},
{"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index d1230ad..45dbf40 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -66,6 +66,7 @@
MSM_FRONTEND_DAI_AFE_TX,
MSM_FRONTEND_DAI_VOICE_STUB,
MSM_FRONTEND_DAI_VOLTE,
+ MSM_FRONTEND_DAI_SGLTE,
MSM_FRONTEND_DAI_MAX,
};
@@ -113,7 +114,7 @@
void msm_pcm_routing_reg_phy_stream(int fedai_id, int dspst_id,
int stream_type);
void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id,
- int stream_type);
+ int stream_type, int enable);
void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type);
diff --git a/sound/soc/msm/msm-pcm-voice.c b/sound/soc/msm/msm-pcm-voice.c
index 7bdb4f0..633973e 100644
--- a/sound/soc/msm/msm-pcm-voice.c
+++ b/sound/soc/msm/msm-pcm-voice.c
@@ -59,6 +59,14 @@
return false;
}
+static int is_sglte(struct msm_voice *psglte)
+{
+ if (psglte == &voice_info[SGLTE_SESSION_INDEX])
+ return true;
+ else
+ return false;
+}
+
static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -93,6 +101,10 @@
voice = &voice_info[VOLTE_SESSION_INDEX];
pr_debug("%s: Open VoLTE Substream Id=%s\n",
__func__, substream->pcm->id);
+ } else if (!strncmp("SGLTE", substream->pcm->id, 5)) {
+ voice = &voice_info[SGLTE_SESSION_INDEX];
+ pr_debug("%s: Open SGLTE Substream Id=%s\n",
+ __func__, substream->pcm->id);
} else {
voice = &voice_info[VOICE_SESSION_INDEX];
pr_debug("%s: Open VOICE Substream Id=%s\n",
@@ -162,6 +174,8 @@
pr_debug("end voice call\n");
if (is_volte(prtd))
session_id = voc_get_session_id(VOLTE_SESSION_NAME);
+ else if (is_sglte(prtd))
+ session_id = voc_get_session_id(SGLTE_SESSION_NAME);
else
session_id = voc_get_session_id(VOICE_SESSION_NAME);
voc_end_voice_call(session_id);
@@ -187,6 +201,8 @@
if (prtd->playback_start && prtd->capture_start) {
if (is_volte(prtd))
session_id = voc_get_session_id(VOLTE_SESSION_NAME);
+ else if (is_sglte(prtd))
+ session_id = voc_get_session_id(SGLTE_SESSION_NAME);
else
session_id = voc_get_session_id(VOICE_SESSION_NAME);
voc_start_voice_call(session_id);
@@ -217,6 +233,8 @@
pr_debug("%s: cmd = %d\n", __func__, cmd);
if (is_volte(prtd))
session_id = voc_get_session_id(VOLTE_SESSION_NAME);
+ else if (is_sglte(prtd))
+ session_id = voc_get_session_id(SGLTE_SESSION_NAME);
else
session_id = voc_get_session_id(VOICE_SESSION_NAME);
@@ -290,6 +308,23 @@
return 0;
}
+static int msm_sglte_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_sglte_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int volume = ucontrol->value.integer.value[0];
+ pr_debug("%s: volume: %d\n", __func__, volume);
+ voc_set_rx_vol_index(voc_get_session_id(SGLTE_SESSION_NAME),
+ RX_PATH, volume);
+ return 0;
+}
+
static int msm_voice_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -328,6 +363,25 @@
return 0;
}
+static int msm_sglte_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_sglte_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mute = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: mute=%d\n", __func__, mute);
+
+ voc_set_tx_mute(voc_get_session_id(SGLTE_SESSION_NAME), TX_PATH, mute);
+
+ return 0;
+}
+
static int msm_voice_rx_device_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -368,6 +422,26 @@
return 0;
}
+static int msm_sglte_rx_device_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ voc_get_rx_device_mute(voc_get_session_id(SGLTE_SESSION_NAME));
+ return 0;
+}
+
+static int msm_sglte_rx_device_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mute = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: mute=%d\n", __func__, mute);
+
+ voc_set_rx_device_mute(voc_get_session_id(SGLTE_SESSION_NAME), mute);
+
+ return 0;
+}
+
static const char const *tty_mode[] = {"OFF", "HCO", "VCO", "FULL"};
static const struct soc_enum msm_tty_mode_enum[] = {
SOC_ENUM_SINGLE_EXT(4, tty_mode),
@@ -481,6 +555,13 @@
msm_volte_mute_get, msm_volte_mute_put),
SOC_SINGLE_EXT("VoLTE Rx Volume", SND_SOC_NOPM, 0, 5, 0,
msm_volte_volume_get, msm_volte_volume_put),
+ SOC_SINGLE_EXT("SGLTE Rx Device Mute", SND_SOC_NOPM, 0, 1, 0,
+ msm_sglte_rx_device_mute_get,
+ msm_sglte_rx_device_mute_put),
+ SOC_SINGLE_EXT("SGLTE Tx Mute", SND_SOC_NOPM, 0, 1, 0,
+ msm_sglte_mute_get, msm_sglte_mute_put),
+ SOC_SINGLE_EXT("SGLTE Rx Volume", SND_SOC_NOPM, 0, 5, 0,
+ msm_sglte_volume_get, msm_sglte_volume_put),
};
static struct snd_pcm_ops msm_pcm_ops = {
@@ -543,6 +624,7 @@
memset(&voice_info, 0, sizeof(voice_info));
mutex_init(&voice_info[VOICE_SESSION_INDEX].lock);
mutex_init(&voice_info[VOLTE_SESSION_INDEX].lock);
+ mutex_init(&voice_info[SGLTE_SESSION_INDEX].lock);
return platform_driver_register(&msm_pcm_driver);
}
diff --git a/sound/soc/msm/msm-pcm-voice.h b/sound/soc/msm/msm-pcm-voice.h
index aa00577..41aca89 100644
--- a/sound/soc/msm/msm-pcm-voice.h
+++ b/sound/soc/msm/msm-pcm-voice.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011,2012 Code Aurora Forum. 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
@@ -16,6 +16,7 @@
enum {
VOICE_SESSION_INDEX,
VOLTE_SESSION_INDEX,
+ SGLTE_SESSION_INDEX,
VOICE_SESSION_INDEX_MAX,
};
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index 98cfa6d..2c44b46 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -1321,6 +1321,21 @@
.codec_name = "snd-soc-dummy",
.be_id = MSM_FRONTEND_DAI_VOLTE,
},
+ {
+ .name = "SGLTE",
+ .stream_name = "SGLTE",
+ .cpu_dai_name = "SGLTE",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,/* this dainlink has playback support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_SGLTE,
+ },
/* Backend BT/FM DAI Links */
{
.name = LPASS_BE_INT_BT_SCO_RX,
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index d47910b..4678ea4 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -21,7 +21,6 @@
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <sound/soc-dsp.h>
#include <sound/pcm.h>
#include <sound/jack.h>
#include <asm/mach-types.h>
@@ -123,8 +122,8 @@
}
ret = pm8xxx_gpio_config(bottom_spk_pamp_gpio, ¶m);
if (ret)
- pr_err("%s: Failed to configure Bottom Spk Ampl"
- " gpio %u\n", __func__, bottom_spk_pamp_gpio);
+ pr_err("%s: Failed to configure Bottom Spk Ampl gpio %u\n",
+ __func__, bottom_spk_pamp_gpio);
else {
pr_debug("%s: enable Bottom spkr amp gpio\n", __func__);
gpio_direction_output(bottom_spk_pamp_gpio, 1);
@@ -140,15 +139,15 @@
}
ret = pm8xxx_gpio_config(top_spk_pamp_gpio, ¶m);
if (ret)
- pr_err("%s: Failed to configure Top Spk Ampl"
- " gpio %u\n", __func__, top_spk_pamp_gpio);
+ pr_err("%s: Failed to configure Top Spk Ampl gpio %u\n",
+ __func__, top_spk_pamp_gpio);
else {
pr_debug("%s: enable Top spkr amp gpio\n", __func__);
gpio_direction_output(top_spk_pamp_gpio, 1);
}
} else {
- pr_err("%s: ERROR : Invalid External Speaker Ampl GPIO."
- " gpio = %u\n", __func__, spk_amp_gpio);
+ pr_err("%s: ERROR : Invalid External Speaker Ampl GPIO. gpio = %u\n",
+ __func__, spk_amp_gpio);
return;
}
}
@@ -160,8 +159,8 @@
if ((msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_POS) &&
(msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_NEG)) {
- pr_debug("%s() External Bottom Speaker Ampl already "
- "turned on. spk = 0x%08x\n", __func__, spk);
+ pr_debug("%s() External Bottom Speaker Ampl already turned on. spk = 0x%08x\n",
+ __func__, spk);
return;
}
@@ -171,8 +170,8 @@
(msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_NEG)) {
msm_enable_ext_spk_amp_gpio(bottom_spk_pamp_gpio);
- pr_debug("%s: slepping 4 ms after turning on external "
- " Bottom Speaker Ampl\n", __func__);
+ pr_debug("%s: slepping 4 ms after turning on external Bottom Speaker Ampl\n",
+ __func__);
usleep_range(4000, 4000);
}
@@ -181,8 +180,8 @@
if ((msm_ext_top_spk_pamp & TOP_SPK_AMP_POS) &&
(msm_ext_top_spk_pamp & TOP_SPK_AMP_NEG)) {
- pr_debug("%s() External Top Speaker Ampl already"
- "turned on. spk = 0x%08x\n", __func__, spk);
+ pr_debug("%s() External Top Speaker Ampl already turned on. spk = 0x%08x\n",
+ __func__, spk);
return;
}
@@ -192,8 +191,8 @@
(msm_ext_top_spk_pamp & TOP_SPK_AMP_NEG)) {
msm_enable_ext_spk_amp_gpio(top_spk_pamp_gpio);
- pr_debug("%s: sleeping 4 ms after turning on "
- " external Top Speaker Ampl\n", __func__);
+ pr_debug("%s: sleeping 4 ms after turning on external Top Speaker Ampl\n",
+ __func__);
usleep_range(4000, 4000);
}
} else {
@@ -215,8 +214,8 @@
gpio_free(bottom_spk_pamp_gpio);
msm_ext_bottom_spk_pamp = 0;
- pr_debug("%s: sleeping 4 ms after turning off external Bottom"
- " Speaker Ampl\n", __func__);
+ pr_debug("%s: sleeping 4 ms after turning off external Bottom Speaker Ampl\n",
+ __func__);
usleep_range(4000, 4000);
@@ -229,8 +228,8 @@
gpio_free(top_spk_pamp_gpio);
msm_ext_top_spk_pamp = 0;
- pr_debug("%s: sleeping 4 ms after turning off external Top"
- " Spkaker Ampl\n", __func__);
+ pr_debug("%s: sleeping 4 ms after turning off external Top Spkaker Ampl\n",
+ __func__);
usleep_range(4000, 4000);
} else {
@@ -442,9 +441,9 @@
{"MIC BIAS4 External", NULL, "Digital Mic6"},
};
-static const char *spk_function[] = {"Off", "On"};
-static const char *slim0_rx_ch_text[] = {"One", "Two"};
-static const char *slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
+static const char *const spk_function[] = {"Off", "On"};
+static const char *const slim0_rx_ch_text[] = {"One", "Two"};
+static const char *const slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
static const struct soc_enum msm_enum[] = {
SOC_ENUM_SINGLE_EXT(2, spk_function),
@@ -452,7 +451,7 @@
SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
};
-static const char *btsco_rate_text[] = {"8000", "16000"};
+static const char *const btsco_rate_text[] = {"8000", "16000"};
static const struct soc_enum msm_btsco_enum[] = {
SOC_ENUM_SINGLE_EXT(2, btsco_rate_text),
};
@@ -537,21 +536,6 @@
msm_btsco_rate_get, msm_btsco_rate_put),
};
-static struct snd_soc_dsp_link lpa_fe_media = {
- .playback = true,
- .trigger = {
- SND_SOC_DSP_TRIGGER_POST,
- SND_SOC_DSP_TRIGGER_POST
- },
-};
-static struct snd_soc_dsp_link fe_media = {
- .playback = true,
- .capture = true,
- .trigger = {
- SND_SOC_DSP_TRIGGER_POST,
- SND_SOC_DSP_TRIGGER_POST
- },
-};
static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -654,7 +638,13 @@
.cpu_dai_name = "MultiMedia1",
.platform_name = "msm-pcm-dsp",
.dynamic = 1,
- .dsp_link = &fe_media,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
},
{
@@ -663,7 +653,13 @@
.cpu_dai_name = "MultiMedia3",
.platform_name = "msm-pcm-lpa",
.dynamic = 1,
- .dsp_link = &lpa_fe_media,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
},
@@ -679,6 +675,8 @@
.be_id = MSM_BACKEND_DAI_AUXPCM_RX,
.be_hw_params_fixup = msm_auxpcm_be_params_fixup,
.ops = &msm_auxpcm_be_ops,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
},
{
.name = LPASS_BE_AUXPCM_TX,
diff --git a/sound/soc/msm/qdsp6/q6adm.c b/sound/soc/msm/qdsp6/q6adm.c
index bf6f743..bc57ef3 100644
--- a/sound/soc/msm/qdsp6/q6adm.c
+++ b/sound/soc/msm/qdsp6/q6adm.c
@@ -278,6 +278,7 @@
case ADM_CMD_MEMORY_UNMAP_REGIONS:
case ADM_CMD_MATRIX_MAP_ROUTINGS:
case ADM_CMD_CONNECT_AFE_PORT:
+ case ADM_CMD_DISCONNECT_AFE_PORT:
atomic_set(&this_adm.copp_stat[index], 1);
wake_up(&this_adm.wait);
break;
@@ -523,6 +524,76 @@
return ret;
}
+int adm_disconnect_afe_port(int mode, int session_id, int port_id)
+{
+ struct adm_cmd_connect_afe_port cmd;
+ int ret = 0;
+ int index;
+
+ pr_debug("%s: port %d session id:%d mode:%d\n", __func__,
+ port_id, session_id, mode);
+
+ port_id = afe_convert_virtual_to_portid(port_id);
+
+ if (afe_validate_port(port_id) < 0) {
+ pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
+ return -ENODEV;
+ }
+ if (this_adm.apr == NULL) {
+ this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+ 0xFFFFFFFF, &this_adm);
+ if (this_adm.apr == NULL) {
+ pr_err("%s: Unable to register ADM\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ rtac_set_adm_handle(this_adm.apr);
+ }
+ index = afe_get_port_index(port_id);
+ pr_debug("%s: Port ID %d, index %d\n", __func__, port_id, index);
+
+ cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cmd.hdr.pkt_size = sizeof(cmd);
+ cmd.hdr.src_svc = APR_SVC_ADM;
+ cmd.hdr.src_domain = APR_DOMAIN_APPS;
+ cmd.hdr.src_port = port_id;
+ cmd.hdr.dest_svc = APR_SVC_ADM;
+ cmd.hdr.dest_domain = APR_DOMAIN_ADSP;
+ cmd.hdr.dest_port = port_id;
+ cmd.hdr.token = port_id;
+ cmd.hdr.opcode = ADM_CMD_DISCONNECT_AFE_PORT;
+
+ cmd.mode = mode;
+ cmd.session_id = session_id;
+ cmd.afe_port_id = port_id;
+
+ atomic_set(&this_adm.copp_stat[index], 0);
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&cmd);
+ if (ret < 0) {
+ pr_err("%s:ADM enable for port %d failed\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ /* Wait for the callback with copp id */
+ ret = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[index]),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s ADM connect AFE failed for port %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ atomic_dec(&this_adm.copp_cnt[index]);
+ return 0;
+
+fail_cmd:
+
+ return ret;
+}
+
int adm_open(int port_id, int path, int rate, int channel_mode, int topology)
{
struct adm_copp_open_command open;
diff --git a/sound/soc/msm/qdsp6/q6voice.c b/sound/soc/msm/qdsp6/q6voice.c
index 0c30dc9..f66a01c 100644
--- a/sound/soc/msm/qdsp6/q6voice.c
+++ b/sound/soc/msm/qdsp6/q6voice.c
@@ -34,6 +34,7 @@
#define VOC_PATH_PASSIVE 0
#define VOC_PATH_FULL 1
#define VOC_PATH_VOLTE_PASSIVE 2
+#define VOC_PATH_SGLTE_PASSIVE 3
/* CVP CAL Size: 245760 = 240 * 1024 */
#define CVP_CAL_SIZE 245760
@@ -149,6 +150,9 @@
else if (!strncmp(name, "VoLTE session", 13))
session_id =
common.voice[VOC_PATH_VOLTE_PASSIVE].session_id;
+ else if (!strncmp(name, "SGLTE session", 13))
+ session_id =
+ common.voice[VOC_PATH_SGLTE_PASSIVE].session_id;
else
session_id = common.voice[VOC_PATH_FULL].session_id;
@@ -189,6 +193,11 @@
return (session_id == common.voice[VOC_PATH_VOLTE_PASSIVE].session_id);
}
+static bool is_sglte_session(u16 session_id)
+{
+ return (session_id == common.voice[VOC_PATH_SGLTE_PASSIVE].session_id);
+}
+
static int voice_apr_register(void)
{
pr_debug("%s\n", __func__);
@@ -275,8 +284,10 @@
pr_err("%s: apr_mvm is NULL.\n", __func__);
return -EINVAL;
}
- pr_debug("%s: VoLTE command to MVM\n", __func__);
- if (is_volte_session(v->session_id)) {
+ pr_debug("%s: VoLTE/SGLTE command to MVM\n", __func__);
+ if (is_volte_session(v->session_id) ||
+ is_sglte_session(v->session_id)) {
+
mvm_handle = voice_get_mvm_handle(v);
mvm_voice_ctl_cmd.hdr.hdr_field = APR_HDR_FIELD(
APR_MSG_TYPE_SEQ_CMD,
@@ -350,7 +361,8 @@
if (!mvm_handle) {
if (is_voice_session(v->session_id) ||
- is_volte_session(v->session_id)) {
+ is_volte_session(v->session_id) ||
+ is_sglte_session(v->session_id)) {
mvm_session_cmd.hdr.hdr_field = APR_HDR_FIELD(
APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE),
@@ -369,11 +381,15 @@
if (is_volte_session(v->session_id)) {
strlcpy(mvm_session_cmd.mvm_session.name,
"default volte voice",
+ sizeof(mvm_session_cmd.mvm_session.name) - 1);
+ } else if (is_sglte_session(v->session_id)) {
+ strlcpy(mvm_session_cmd.mvm_session.name,
+ "default modem voice2",
sizeof(mvm_session_cmd.mvm_session.name));
} else {
- strlcpy(mvm_session_cmd.mvm_session.name,
+ strlcpy(mvm_session_cmd.mvm_session.name,
"default modem voice",
- sizeof(mvm_session_cmd.mvm_session.name));
+ sizeof(mvm_session_cmd.mvm_session.name) - 1);
}
v->mvm_state = CMD_STATUS_FAIL;
@@ -432,7 +448,8 @@
/* send cmd to create cvs session */
if (!cvs_handle) {
if (is_voice_session(v->session_id) ||
- is_volte_session(v->session_id)) {
+ is_volte_session(v->session_id) ||
+ is_sglte_session(v->session_id)) {
pr_debug("%s: creating CVS passive session\n",
__func__);
@@ -452,11 +469,15 @@
if (is_volte_session(v->session_id)) {
strlcpy(mvm_session_cmd.mvm_session.name,
"default volte voice",
- sizeof(mvm_session_cmd.mvm_session.name));
- } else {
- strlcpy(cvs_session_cmd.cvs_session.name,
- "default modem voice",
+ sizeof(mvm_session_cmd.mvm_session.name) - 1);
+ } else if (is_sglte_session(v->session_id)) {
+ strlcpy(cvs_session_cmd.cvs_session.name,
+ "default modem voice2",
sizeof(cvs_session_cmd.cvs_session.name));
+ } else {
+ strlcpy(cvs_session_cmd.cvs_session.name,
+ "default modem voice",
+ sizeof(cvs_session_cmd.cvs_session.name) - 1);
}
v->cvs_state = CMD_STATUS_FAIL;
diff --git a/sound/soc/msm/qdsp6/q6voice.h b/sound/soc/msm/qdsp6/q6voice.h
index 88ab0d5..468aba8 100644
--- a/sound/soc/msm/qdsp6/q6voice.h
+++ b/sound/soc/msm/qdsp6/q6voice.h
@@ -16,7 +16,7 @@
#include <linux/ion.h>
#define MAX_VOC_PKT_SIZE 642
-#define SESSION_NAME_LEN 20
+#define SESSION_NAME_LEN 21
#define VOC_REC_UPLINK 0x00
#define VOC_REC_DOWNLINK 0x01
@@ -918,7 +918,7 @@
void *buf;
};
-#define MAX_VOC_SESSIONS 3
+#define MAX_VOC_SESSIONS 4
#define SESSION_ID_BASE 0xFFF0
struct common_data {
@@ -990,6 +990,7 @@
#define VOICE_SESSION_NAME "Voice session"
#define VOIP_SESSION_NAME "VoIP session"
#define VOLTE_SESSION_NAME "VoLTE session"
+#define SGLTE_SESSION_NAME "SGLTE session"
uint16_t voc_get_session_id(char *name);
int voc_start_playback(uint32_t set);
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
index daba79d..7723934 100644
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -139,8 +139,7 @@
case ASM_SESSION_CMD_RUN_V2: {
if (!atomic_read(&prtd->pending_buffer))
break;
- pr_debug("%s:writing %d bytes"
- " of buffer[%d] to dsp\n",
+ pr_debug("%s:writing %d bytes of buffer[%d] to dsp\n",
__func__, prtd->pcm_count, prtd->out_head);
buf = prtd->audio_client->port[IN].buf;
pr_debug("%s:writing buffer[%d] from 0x%08x\n",
@@ -367,8 +366,8 @@
rc = q6asm_set_volume(compressed_audio.prtd->audio_client,
volume);
if (rc < 0) {
- pr_err("%s: Send Volume command failed"
- " rc=%d\n", __func__, rc);
+ pr_err("%s: Send Volume command failed rc=%d\n",
+ __func__, rc);
}
}
compressed_audio.volume = volume;
@@ -489,8 +488,8 @@
runtime->hw.period_bytes_min,
runtime->hw.periods_max);
if (ret < 0) {
- pr_err("Audio Start: Buffer Allocation failed "
- "rc = %d\n", ret);
+ pr_err("Audio Start: Buffer Allocation failed rc = %d\n",
+ ret);
return -ENOMEM;
}
buf = prtd->audio_client->port[dir].buf;
@@ -535,12 +534,9 @@
temp = temp * (runtime->rate/1000);
temp = div_u64(temp, 1000);
tstamp.sampling_rate = runtime->rate;
- tstamp.rendered = (size_t)(temp & 0xFFFFFFFF);
- tstamp.decoded = (size_t)((temp >> 32) & 0xFFFFFFFF);
tstamp.timestamp = timestamp;
- pr_debug("%s: bytes_consumed:lsb = %d, msb = %d,"
- "timestamp = %lld,\n",
- __func__, tstamp.rendered, tstamp.decoded,
+ pr_debug("%s: bytes_consumed:,timestamp = %lld,\n",
+ __func__,
tstamp.timestamp);
if (copy_to_user((void *) arg, &tstamp,
sizeof(struct snd_compr_tstamp)))
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h
index 2183690..9830300 100644
--- a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h
@@ -15,7 +15,7 @@
#define _MSM_COMPR_H
#include <sound/apr_audio-v2.h>
#include <sound/q6asm-v2.h>
-#include <sound/snd_compress_params.h>
+#include <sound/compress_params.h>
#include <sound/compress_offload.h>
#include <sound/compress_driver.h>
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
index ee92753..05ef2ce 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-lpa-v2.c
@@ -27,7 +27,7 @@
#include <asm/dma.h>
#include <linux/dma-mapping.h>
#include <linux/android_pmem.h>
-#include <sound/snd_compress_params.h>
+#include <sound/compress_params.h>
#include <sound/compress_offload.h>
#include <sound/compress_driver.h>
#include <sound/timer.h>
@@ -148,8 +148,7 @@
if (runtime->status->hw_ptr >=
runtime->control->appl_ptr)
break;
- pr_debug("%s:writing %d bytes"
- " of buffer to dsp\n",
+ pr_debug("%s:writing %d bytes of buffer to dsp\n",
__func__, prtd->pcm_count);
buf = prtd->audio_client->port[IN].buf;
param.paddr = (unsigned long)buf[prtd->out_head].phys;
@@ -340,8 +339,8 @@
if (lpa_audio.prtd && lpa_audio.prtd->audio_client) {
rc = q6asm_set_volume(lpa_audio.prtd->audio_client, volume);
if (rc < 0) {
- pr_err("%s: Send Volume command failed"
- " rc=%d\n", __func__, rc);
+ pr_err("%s: Send Volume command failed rc=%d\n",
+ __func__, rc);
}
}
lpa_audio.volume = volume;
@@ -461,8 +460,8 @@
runtime->hw.period_bytes_min,
runtime->hw.periods_max);
if (ret < 0) {
- pr_err("Audio Start: Buffer Allocation failed "
- "rc = %d\n", ret);
+ pr_err("Audio Start: Buffer Allocation failed rc = %d\n",
+ ret);
return -ENOMEM;
}
buf = prtd->audio_client->port[dir].buf;
@@ -509,12 +508,9 @@
temp = temp * (runtime->rate/1000);
temp = div_u64(temp, 1000);
tstamp.sampling_rate = runtime->rate;
- tstamp.rendered = (size_t)(temp & 0xFFFFFFFF);
- tstamp.decoded = (size_t)((temp >> 32) & 0xFFFFFFFF);
tstamp.timestamp = timestamp;
- pr_debug("%s: bytes_consumed:lsb = %d, msb = %d,"
- "timestamp = %lld,\n",
- __func__, tstamp.rendered, tstamp.decoded,
+ pr_debug("%s: bytes_consumed:timestamp = %lld,\n",
+ __func__,
tstamp.timestamp);
if (copy_to_user((void *) arg, &tstamp,
sizeof(struct snd_compr_tstamp)))
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 3a9fbe1..fd3fe6a 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -236,11 +236,24 @@
}
if (codec_dai->driver->ops->startup) {
- ret = codec_dai->driver->ops->startup(substream, codec_dai);
- if (ret < 0) {
- printk(KERN_ERR "asoc: can't open codec %s\n",
- codec_dai->name);
- goto codec_dai_err;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = codec_dai->driver->ops->startup(substream,
+ codec_dai);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: can't open codec %s\n",
+ codec_dai->name);
+ goto codec_dai_err;
+ }
+ } else {
+ if (!codec_dai->capture_active) {
+ ret = codec_dai->driver->ops->startup(substream,
+ codec_dai);
+ if (ret < 0) {
+ printk(KERN_ERR "can't open codec %s\n",
+ codec_dai->name);
+ goto codec_dai_err;
+ }
+ }
}
}
@@ -456,8 +469,15 @@
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_soc_dai_digital_mute(codec_dai, 1);
- if (cpu_dai->driver->ops->shutdown)
- cpu_dai->driver->ops->shutdown(substream, cpu_dai);
+ if (cpu_dai->driver->ops->shutdown) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ codec_dai->driver->ops->shutdown(substream, codec_dai);
+ } else {
+ if (!codec_dai->capture_active)
+ codec_dai->driver->ops->shutdown(substream,
+ codec_dai);
+ }
+ }
if (codec_dai->driver->ops->shutdown)
codec_dai->driver->ops->shutdown(substream, codec_dai);
@@ -485,7 +505,8 @@
}
} else {
/* capture streams can be powered down now */
- snd_soc_dapm_stream_event(rtd,
+ if (!codec_dai->capture_active)
+ snd_soc_dapm_stream_event(rtd,
codec_dai->driver->capture.stream_name,
SND_SOC_DAPM_STREAM_STOP);
}
@@ -557,11 +578,12 @@
snd_soc_dapm_stream_event(rtd,
codec_dai->driver->playback.stream_name,
SND_SOC_DAPM_STREAM_START);
- else
- snd_soc_dapm_stream_event(rtd,
+ else {
+ if (codec_dai->capture_active == 1)
+ snd_soc_dapm_stream_event(rtd,
codec_dai->driver->capture.stream_name,
SND_SOC_DAPM_STREAM_START);
-
+ }
snd_soc_dai_digital_mute(codec_dai, 0);
out:
@@ -594,11 +616,24 @@
}
if (codec_dai->driver->ops->hw_params) {
- ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai);
- if (ret < 0) {
- printk(KERN_ERR "asoc: can't set codec %s hw params\n",
- codec_dai->name);
- goto codec_err;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = codec_dai->driver->ops->hw_params(substream,
+ params, codec_dai);
+ if (ret < 0) {
+ printk(KERN_ERR "not set codec %s hw params\n",
+ codec_dai->name);
+ goto codec_err;
+ }
+ } else {
+ if (codec_dai->capture_active == 1) {
+ ret = codec_dai->driver->ops->hw_params(
+ substream, params, codec_dai);
+ if (ret < 0) {
+ printk(KERN_ERR "fail: %s hw params\n",
+ codec_dai->name);
+ goto codec_err;
+ }
+ }
}
}
@@ -706,9 +741,19 @@
int ret;
if (codec_dai->driver->ops->trigger) {
- ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai);
- if (ret < 0)
- return ret;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = codec_dai->driver->ops->trigger(substream,
+ cmd, codec_dai);
+ if (ret < 0)
+ return ret;
+ } else {
+ if (codec_dai->capture_active == 1) {
+ ret = codec_dai->driver->ops->trigger(
+ substream, cmd, codec_dai);
+ if (ret < 0)
+ return ret;
+ }
+ }
}
if (platform->driver->ops && platform->driver->ops->trigger) {
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 1fcf1bb..28e5548 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -30,6 +30,17 @@
return __value(parse_events_text + 1, 16, PE_RAW);
}
+static int sh_raw(void)
+{
+ return __value(parse_events_text + 2, 16, PE_SH_RAW);
+}
+
+static int fab_raw(void)
+{
+ return __value(parse_events_text + 2, 16, PE_FAB_RAW);
+}
+
+
static int str(int token)
{
parse_events_lval.str = strdup(parse_events_text);
@@ -107,6 +118,8 @@
mem: { return PE_PREFIX_MEM; }
r{num_raw_hex} { return raw(); }
+rs{num_raw_hex} { return sh_raw(); }
+rm{num_raw_hex} { return fab_raw(); }
{num_dec} { return value(10); }
{num_hex} { return value(16); }
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index d9637da..07b292d 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -24,7 +24,7 @@
%}
-%token PE_VALUE PE_VALUE_SYM PE_RAW PE_TERM
+%token PE_VALUE PE_VALUE_SYM PE_RAW PE_SH_RAW PE_FAB_RAW PE_TERM
%token PE_NAME
%token PE_MODIFIER_EVENT PE_MODIFIER_BP
%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
@@ -33,6 +33,8 @@
%type <num> PE_VALUE
%type <num> PE_VALUE_SYM
%type <num> PE_RAW
+%type <num> PE_SH_RAW
+%type <num> PE_FAB_RAW
%type <num> PE_TERM
%type <str> PE_NAME
%type <str> PE_NAME_CACHE_TYPE
@@ -77,7 +79,9 @@
event_legacy_mem |
event_legacy_tracepoint sep_dc |
event_legacy_numeric sep_dc |
- event_legacy_raw sep_dc
+ event_legacy_raw sep_dc |
+ event_legacy_shared_raw sep_dc |
+ event_legacy_fabric_raw sep_dc
event_pmu:
PE_NAME '/' event_config '/'
@@ -149,6 +153,18 @@
ABORT_ON(parse_events_add_numeric(list_event, idx, PERF_TYPE_RAW, $1, NULL));
}
+event_legacy_shared_raw:
+PE_SH_RAW
+{
+ ABORT_ON(parse_events_add_numeric(list_event, idx, 6, $1, NULL));
+}
+
+event_legacy_fabric_raw:
+PE_FAB_RAW
+{
+ ABORT_ON(parse_events_add_numeric(list_event, idx, 7, $1, NULL));
+}
+
event_config:
event_config ',' event_term
{