Merge "input: synaptics_fw_update: fix insufficient bounds checking"
diff --git a/Documentation/devicetree/bindings/fb/mdss-mdp.txt b/Documentation/devicetree/bindings/fb/mdss-mdp.txt
index 191db07..8174d1b 100644
--- a/Documentation/devicetree/bindings/fb/mdss-mdp.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-mdp.txt
@@ -117,6 +117,39 @@
- "edp"
- "hdmi"
+Bus Scaling Data:
+- qcom,msm-bus,name: String property describing MDSS client.
+- qcom,msm-bus,num-cases: This is the the number of Bus Scaling use cases
+ defined in the vectors property. This must be
+ set to <3> for MDSS driver where use-case 0 is
+ used to take off MDSS BW votes from the system.
+ And use-case 1 & 2 are used in ping-pong fashion
+ to generate run-time BW requests.
+- qcom,msm-bus,active-only: A boolean flag indicating if it is active only.
+- qcom,msm-bus,num-paths: This represents the number of paths in each
+ Bus Scaling Usecase. This value depends on
+ how many number of AXI master ports are
+ dedicated to MDSS for particular chipset.
+- qcom,msm-bus,vectors-KBps: * A series of 4 cell properties, with a format
+ of (src, dst, ab, ib) which is defined at
+ Documentation/devicetree/bindings/arm/msm/msm_bus.txt
+ * Current values of src & dst are defined at
+ arch/arm/mach-msm/msm_bus_board.h
+ src values allowed for MDSS are:
+ 22 = MSM_BUS_MASTER_MDP_PORT0
+ 23 = MSM_BUS_MASTER_MDP_PORT1
+ dst values allowed for MDSS are:
+ 512 = MSM_BUS_SLAVE_EBI_CH0
+ ab: Represents aggregated bandwidth.
+ ib: Represents instantaneous bandwidth.
+ * Total number of 4 cell properties will be
+ (number of use-cases * number of paths).
+ * These values will be overridden by the driver
+ based on the run-time requirements. So initial
+ ab and ib values defined here are random and
+ bare no logic except for the use-case 0 where ab
+ and ib values needs to be 0.
+
Optional properties:
- vdd-cx-supply : Phandle for vdd CX regulator device node.
- batfet-supply : Phandle for battery FET regulator device node.
@@ -170,6 +203,30 @@
applied in scenarios where panel interface can
be more tolerant to memory latency such as
command mode panels.
+
+Fudge Factors: Fudge factors are used to boost demand for
+ resources like bus bandswidth, clk rate etc. to
+ overcome system inefficiencies and avoid any
+ glitches. These fudge factors are expressed in
+ terms of numerator and denominator. First value
+ is numerator followed by denominator. They all
+ are optional but highly recommended.
+ Ex:
+ x = value to be fudged
+ a = numerator, default value is 1
+ b = denominator, default value is 1
+ FUDGE(x, a, b) = ((x * a) / b)
+- qcom,mdss-ib-factor: This fudge factor is applied to calculated ib
+ values in default conditions.
+- qcom,mdss-high-ib-factor: This fudge factor is applied to calculated ib
+ values. Use of this factor is determined by the
+ driver and open new use-cases.
+- qcom,mdss-clk-factor: This fudge factor is applied to calculated mdp
+ clk rate in default conditions.
+- qcom,mdss-highest-bank-bit: Property to indicate tile format as opposed to usual
+ linear format. The value tells the GPU highest memory
+ bank bit used.
+
Optional subnodes:
Child nodes representing the frame buffer virtual devices.
@@ -198,6 +255,12 @@
This property is required whenever the continuous splash
screen feature is enabled for the corresponding
framebuffer device.
+- qcom,mdss-fb-splash-logo-enabled: The boolean entry enables the framebuffer
+ driver to display the splash logo image.
+ It is independent of continuous splash
+ screen feature and has no relation with
+ qcom,cont-splash-enabled entry present in
+ panel configuration.
Example:
mdss_mdp: qcom,mdss_mdp@fd900000 {
@@ -213,6 +276,21 @@
qcom,max-bandwidth-low-kbps = <2300000>;
qcom,max-bandwidth-high-kbps = <3000000>;
+ /* Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_mdp";
+ qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,num-paths = <2>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>, <23 512 0 0>,
+ <22 512 0 6400000>, <23 512 0 6400000>,
+ <22 512 0 6400000>, <23 512 0 6400000>;
+
+ /* Fudge factors */
+ qcom,mdss-ab-factor = <2 1>; /* 2 times */
+ qcom,mdss-ib-factor = <3 2>; /* 1.5 times */
+ qcom,mdss-high-ib-factor = <2 1>; /* 2 times */
+ qcom,mdss-clk-factor = <5 4>; /* 1.25 times */
+
qcom,max-clk-rate = <320000000>;
qcom,vbif-settings = <0x0004 0x00000001>,
<0x00D8 0x00000707>;
@@ -260,6 +338,7 @@
compatible = "qcom,mdss-fb";
qcom,mdss-mixer-swap;
qcom,mdss-fb-split = <480 240>
+ qcom,mdss-fb-splash-logo-enabled:
};
};
diff --git a/arch/arm/boot/dts/apq8084-mdss.dtsi b/arch/arm/boot/dts/apq8084-mdss.dtsi
index 0e771ce..2cd75e4 100644
--- a/arch/arm/boot/dts/apq8084-mdss.dtsi
+++ b/arch/arm/boot/dts/apq8084-mdss.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -24,6 +24,21 @@
qcom,max-bandwidth-low-kbps = <6000000>;
qcom,max-bandwidth-high-kbps = <6000000>;
+ /* Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_mdp";
+ qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,num-paths = <2>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>, <23 512 0 0>,
+ <22 512 0 6400000>, <23 512 0 6400000>,
+ <22 512 0 6400000>, <23 512 0 6400000>;
+
+ /* Fudge factors */
+ qcom,mdss-ab-factor = <2 1>; /* 2 times */
+ qcom,mdss-ib-factor = <3 2>; /* 1.5 times */
+ qcom,mdss-high-ib-factor = <2 1>; /* 2 times */
+ qcom,mdss-clk-factor = <105 100>; /* 1.05 times */
+
qcom,max-clk-rate = <320000000>;
qcom,mdss-pipe-vig-off = <0x00001200 0x00001600
diff --git a/arch/arm/boot/dts/msm8226-mdss.dtsi b/arch/arm/boot/dts/msm8226-mdss.dtsi
index 84bb234..55631ee 100644
--- a/arch/arm/boot/dts/msm8226-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8226-mdss.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -21,6 +21,22 @@
qcom,max-bandwidth-low-kbps = <1660000>;
qcom,max-bandwidth-high-kbps = <1660000>;
+
+ /* Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_mdp";
+ qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>,
+ <22 512 0 6400000>,
+ <22 512 0 6400000>;
+
+ /* Fudge factors */
+ qcom,mdss-ab-factor = <2 1>; /* 2 times */
+ qcom,mdss-ib-factor = <3 2>; /* 1.5 times */
+ qcom,mdss-high-ib-factor = <2 1>; /* 2 times */
+ qcom,mdss-clk-factor = <5 4>; /* 1.25 times */
+
qcom,max-clk-rate = <200000000>;
qcom,mdss-pipe-vig-off = <0x00001200>;
qcom,mdss-pipe-rgb-off = <0x00001E00>;
diff --git a/arch/arm/boot/dts/msm8610.dtsi b/arch/arm/boot/dts/msm8610.dtsi
index 6e10deb..61fa6bc 100644
--- a/arch/arm/boot/dts/msm8610.dtsi
+++ b/arch/arm/boot/dts/msm8610.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1071,7 +1071,7 @@
label = "vchg_sns";
reg = <2>;
qcom,decimation = <0>;
- qcom,pre-div-channel-scaling = <2>;
+ qcom,pre-div-channel-scaling = <5>;
qcom,calibration-type = "absolute";
qcom,scale-function = <0>;
qcom,hw-settle-time = <0>;
diff --git a/arch/arm/boot/dts/msm8974-mdss.dtsi b/arch/arm/boot/dts/msm8974-mdss.dtsi
index bf330f3..7e7302e 100644
--- a/arch/arm/boot/dts/msm8974-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8974-mdss.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -21,6 +21,22 @@
qcom,max-bandwidth-low-kbps = <2300000>;
qcom,max-bandwidth-high-kbps = <3000000>;
+
+ /* Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_mdp";
+ qcom,msm-bus,num-cases = <3>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <22 512 0 0>,
+ <22 512 0 6400000>,
+ <22 512 0 6400000>;
+
+ /* Fudge factors */
+ qcom,mdss-ab-factor = <2 1>; /* 2 times */
+ qcom,mdss-ib-factor = <3 2>; /* 1.5 times */
+ qcom,mdss-high-ib-factor = <2 1>; /* 2 times */
+ qcom,mdss-clk-factor = <5 4>; /* 1.25 times */
+
qcom,max-clk-rate = <320000000>;
qcom,mdss-pipe-vig-off = <0x00001200 0x00001600
0x00001A00>;
diff --git a/arch/arm/mach-msm/board-8974-gpiomux.c b/arch/arm/mach-msm/board-8974-gpiomux.c
index 8ab916c..cec1a8f 100755
--- a/arch/arm/mach-msm/board-8974-gpiomux.c
+++ b/arch/arm/mach-msm/board-8974-gpiomux.c
@@ -22,27 +22,28 @@
static struct gpiomux_setting ap2mdm_cfg = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_8MA,
+ .drv = GPIOMUX_DRV_2MA,
.pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_OUT_LOW,
};
static struct gpiomux_setting mdm2ap_status_cfg = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_DOWN,
- .dir = GPIOMUX_IN,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_OUT_LOW,
};
static struct gpiomux_setting mdm2ap_errfatal_cfg = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_DOWN,
- .dir = GPIOMUX_IN,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_OUT_LOW,
};
static struct gpiomux_setting mdm2ap_pblrdy = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_8MA,
+ .drv = GPIOMUX_DRV_2MA,
.pull = GPIOMUX_PULL_NONE,
.dir = GPIOMUX_IN,
};
@@ -50,14 +51,16 @@
static struct gpiomux_setting ap2mdm_soft_reset_cfg = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_8MA,
+ .drv = GPIOMUX_DRV_2MA,
.pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_OUT_LOW,
};
static struct gpiomux_setting ap2mdm_wakeup = {
.func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_DOWN,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ .dir = GPIOMUX_OUT_LOW,
};
static struct msm_gpiomux_config mdm_configs[] __initdata = {
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 12f448f..37c236d 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -282,7 +282,7 @@
{
uint16_t event_id, event_id_packet, length, temp_len;
uint8_t *event_mask_ptr, byte_mask, payload_len, payload_len_field;
- uint8_t timestamp[8], bit_index, timestamp_len;
+ uint8_t timestamp[8] = {0}, bit_index, timestamp_len;
uint8_t event_data[MAX_EVENT_SIZE];
unsigned int byte_index, total_event_len, i;
struct diag_dci_client_tbl *entry;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 0e27693..0c9797e 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -342,6 +342,9 @@
else if (subsys_id == 0x32 && cmd_code_hi >= 0x03 &&
cmd_code_lo <= 0x03)
return 1;
+ else if (subsys_id == 0x57 && cmd_code_hi >= 0x0E &&
+ cmd_code_lo <= 0x0E)
+ return 1;
}
return 0;
}
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index eba60ea..10eaf4e 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -3069,7 +3069,7 @@
return -EINVAL;
ret = adreno_drawctxt_wait(ADRENO_DEVICE(device), context,
- timestamp, msecs_to_jiffies(msecs));
+ timestamp, msecs);
/* If the context got invalidated then return a specific error */
drawctxt = ADRENO_CONTEXT(context);
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index d727423..1b5573f 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -429,7 +429,8 @@
/* Always enable per-context timestamps */
drawctxt->base.flags |= KGSL_CONTEXT_PER_CONTEXT_TS;
-
+ drawctxt->type = (drawctxt->base.flags & KGSL_CONTEXT_TYPE_MASK)
+ >> KGSL_CONTEXT_TYPE_SHIFT;
mutex_init(&drawctxt->mutex);
init_waitqueue_head(&drawctxt->wq);
init_waitqueue_head(&drawctxt->waiting);
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index edc42f9..1743e4a 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1462,7 +1462,7 @@
* @timestamp: Pending timestamp for the event
* @handle: Pointer to a sync fence handle
* @device: Pointer to the KGSL device
- * @lock: Spin lock to protect the sync event list
+ * @refcount: Allow event to be destroyed asynchronously
*/
struct kgsl_cmdbatch_sync_event {
int type;
@@ -1472,10 +1472,37 @@
unsigned int timestamp;
struct kgsl_sync_fence_waiter *handle;
struct kgsl_device *device;
- spinlock_t lock;
+ struct kref refcount;
};
/**
+ * kgsl_cmdbatch_sync_event_destroy() - Destroy a sync event object
+ * @kref: Pointer to the kref structure for this object
+ *
+ * Actually destroy a sync event object. Called from
+ * kgsl_cmdbatch_sync_event_put.
+ */
+static void kgsl_cmdbatch_sync_event_destroy(struct kref *kref)
+{
+ struct kgsl_cmdbatch_sync_event *event = container_of(kref,
+ struct kgsl_cmdbatch_sync_event, refcount);
+
+ kgsl_cmdbatch_put(event->cmdbatch);
+ kfree(event);
+}
+
+/**
+ * kgsl_cmdbatch_sync_event_put() - Decrement the refcount for a
+ * sync event object
+ * @event: Pointer to the sync event object
+ */
+static inline void kgsl_cmdbatch_sync_event_put(
+ struct kgsl_cmdbatch_sync_event *event)
+{
+ kref_put(&event->refcount, kgsl_cmdbatch_sync_event_destroy);
+}
+
+/**
* kgsl_cmdbatch_destroy_object() - Destroy a cmdbatch object
* @kref: Pointer to the kref structure for this object
*
@@ -1500,10 +1527,25 @@
static void kgsl_cmdbatch_sync_expire(struct kgsl_device *device,
struct kgsl_cmdbatch_sync_event *event)
{
+ struct kgsl_cmdbatch_sync_event *e, *tmp;
int sched = 0;
+ int removed = 0;
spin_lock(&event->cmdbatch->lock);
- list_del(&event->node);
+
+ /*
+ * sync events that are contained by a cmdbatch which has been
+ * destroyed may have already been removed from the synclist
+ */
+
+ list_for_each_entry_safe(e, tmp, &event->cmdbatch->synclist, node) {
+ if (e == event) {
+ list_del_init(&event->node);
+ removed = 1;
+ break;
+ }
+ }
+
sched = list_empty(&event->cmdbatch->synclist) ? 1 : 0;
spin_unlock(&event->cmdbatch->lock);
@@ -1514,6 +1556,10 @@
if (sched && device->ftbl->drawctxt_sched)
device->ftbl->drawctxt_sched(device, event->cmdbatch->context);
+
+ /* Put events that have been removed from the synclist */
+ if (removed)
+ kgsl_cmdbatch_sync_event_put(event);
}
@@ -1527,11 +1573,9 @@
struct kgsl_cmdbatch_sync_event *event = priv;
kgsl_cmdbatch_sync_expire(device, event);
-
kgsl_context_put(event->context);
- kgsl_cmdbatch_put(event->cmdbatch);
-
- kfree(event);
+ /* Put events that have signaled */
+ kgsl_cmdbatch_sync_event_put(event);
}
/**
@@ -1539,32 +1583,48 @@
* @cmdbatch: Pointer to the command batch object to destroy
*
* Start the process of destroying a command batch. Cancel any pending events
- * and decrement the refcount.
+ * and decrement the refcount. Asynchronous events can still signal after
+ * kgsl_cmdbatch_destroy has returned.
*/
void kgsl_cmdbatch_destroy(struct kgsl_cmdbatch *cmdbatch)
{
struct kgsl_cmdbatch_sync_event *event, *tmp;
+ LIST_HEAD(cancel_synclist);
+ /*
+ * Empty the synclist before canceling events
+ */
spin_lock(&cmdbatch->lock);
+ list_splice_init(&cmdbatch->synclist, &cancel_synclist);
+ spin_unlock(&cmdbatch->lock);
- /* Delete any pending sync points for this command batch */
- list_for_each_entry_safe(event, tmp, &cmdbatch->synclist, node) {
+ /*
+ * Finish canceling events outside the cmdbatch spinlock and
+ * require the cancel function to return if the event was
+ * successfully canceled meaning that the event is guaranteed
+ * not to signal the callback. This guarantee ensures that
+ * the reference count for the event and cmdbatch is correct.
+ */
+ list_for_each_entry_safe(event, tmp, &cancel_synclist, node) {
if (event->type == KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP) {
- /* Cancel the event if it still exists */
+ /*
+ * Timestamp events are guaranteed to signal
+ * when canceled
+ */
kgsl_cancel_event(cmdbatch->device, event->context,
event->timestamp, kgsl_cmdbatch_sync_func,
event);
} else if (event->type == KGSL_CMD_SYNCPOINT_TYPE_FENCE) {
- if (kgsl_sync_fence_async_cancel(event->handle)) {
- list_del(&event->node);
- kfree(event);
- kgsl_cmdbatch_put(cmdbatch);
- }
+ /* Put events that are successfully canceled */
+ if (kgsl_sync_fence_async_cancel(event->handle))
+ kgsl_cmdbatch_sync_event_put(event);
}
- }
- spin_unlock(&cmdbatch->lock);
+ /* Put events that have been removed from the synclist */
+ list_del_init(&event->node);
+ kgsl_cmdbatch_sync_event_put(event);
+ }
kgsl_cmdbatch_put(cmdbatch);
}
EXPORT_SYMBOL(kgsl_cmdbatch_destroy);
@@ -1577,11 +1637,9 @@
{
struct kgsl_cmdbatch_sync_event *event = priv;
- spin_lock(&event->lock);
kgsl_cmdbatch_sync_expire(event->device, event);
- kgsl_cmdbatch_put(event->cmdbatch);
- spin_unlock(&event->lock);
- kfree(event);
+ /* Put events that have signaled */
+ kgsl_cmdbatch_sync_event_put(event);
}
/* kgsl_cmdbatch_add_sync_fence() - Add a new sync fence syncpoint
@@ -1607,28 +1665,33 @@
event->type = KGSL_CMD_SYNCPOINT_TYPE_FENCE;
event->cmdbatch = cmdbatch;
event->device = device;
- spin_lock_init(&event->lock);
+ event->context = NULL;
+
+ /*
+ * Initial kref is to ensure async callback does not free the
+ * event before this function sets the event handle
+ */
+ kref_init(&event->refcount);
/*
* Add it to the list first to account for the possiblity that the
* callback will happen immediately after the call to
- * kgsl_sync_fence_async_wait
+ * kgsl_sync_fence_async_wait. Decrement the event refcount when
+ * removing from the synclist.
*/
spin_lock(&cmdbatch->lock);
+ kref_get(&event->refcount);
list_add(&event->node, &cmdbatch->synclist);
spin_unlock(&cmdbatch->lock);
/*
- * There is a distinct race condition that can occur if the fence
- * callback is fired before the function has a chance to return. The
- * event struct would be freed before we could write event->handle and
- * hilarity ensued. Protect against this by protecting the call to
- * kgsl_sync_fence_async_wait and the kfree in the callback with a lock.
+ * Increment the reference count for the async callback.
+ * Decrement when the callback is successfully canceled, when
+ * the callback is signaled or if the async wait fails.
*/
- spin_lock(&event->lock);
-
+ kref_get(&event->refcount);
event->handle = kgsl_sync_fence_async_wait(sync->fd,
kgsl_cmdbatch_sync_fence_func, event);
@@ -1636,18 +1699,27 @@
if (IS_ERR_OR_NULL(event->handle)) {
int ret = PTR_ERR(event->handle);
+ /* Failed to add the event to the async callback */
+ kgsl_cmdbatch_sync_event_put(event);
+
+ /* Remove event from the synclist */
spin_lock(&cmdbatch->lock);
list_del(&event->node);
+ kgsl_cmdbatch_sync_event_put(event);
spin_unlock(&cmdbatch->lock);
- kgsl_cmdbatch_put(cmdbatch);
- spin_unlock(&event->lock);
- kfree(event);
+ /* Event no longer needed by this function */
+ kgsl_cmdbatch_sync_event_put(event);
return ret;
}
- spin_unlock(&event->lock);
+ /*
+ * Event was successfully added to the synclist, the async
+ * callback and handle to cancel event has been set.
+ */
+ kgsl_cmdbatch_sync_event_put(event);
+
return 0;
}
@@ -1702,6 +1774,17 @@
event->context = context;
event->timestamp = sync->timestamp;
+ /*
+ * Two krefs are required to support events. The first kref is for
+ * the synclist which holds the event in the cmdbatch. The second
+ * kref is for the callback which can be asynchronous and be called
+ * after kgsl_cmdbatch_destroy. The kref should be put when the event
+ * is removed from the synclist, if the callback is successfully
+ * canceled or when the callback is signaled.
+ */
+ kref_init(&event->refcount);
+ kref_get(&event->refcount);
+
spin_lock(&cmdbatch->lock);
list_add(&event->node, &cmdbatch->synclist);
spin_unlock(&cmdbatch->lock);
diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi4.c b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
index 7152ec8..ca47547 100644
--- a/drivers/input/touchscreen/synaptics_i2c_rmi4.c
+++ b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
@@ -113,16 +113,15 @@
static void synaptics_rmi4_sensor_wake(struct synaptics_rmi4_data *rmi4_data);
-static void synaptics_rmi4_sensor_sleep(struct synaptics_rmi4_data *rmi4_data);
-
static int synaptics_rmi4_check_configuration(struct synaptics_rmi4_data
*rmi4_data);
-#ifdef CONFIG_PM
+
static int synaptics_rmi4_suspend(struct device *dev);
static int synaptics_rmi4_resume(struct device *dev);
+#ifdef CONFIG_PM
static ssize_t synaptics_rmi4_full_pm_cycle_show(struct device *dev,
struct device_attribute *attr, char *buf);
@@ -425,6 +424,31 @@
static bool exp_fn_inited;
static struct mutex exp_fn_list_mutex;
static struct list_head exp_fn_list;
+
+static int synaptics_rmi4_debug_suspend_set(void *_data, u64 val)
+{
+ struct synaptics_rmi4_data *rmi4_data = _data;
+
+ if (val)
+ synaptics_rmi4_suspend(&rmi4_data->input_dev->dev);
+ else
+ synaptics_rmi4_resume(&rmi4_data->input_dev->dev);
+
+ return 0;
+}
+
+static int synaptics_rmi4_debug_suspend_get(void *_data, u64 *val)
+{
+ struct synaptics_rmi4_data *rmi4_data = _data;
+
+ *val = rmi4_data->suspended;
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, synaptics_rmi4_debug_suspend_get,
+ synaptics_rmi4_debug_suspend_set, "%lld\n");
+
#ifdef CONFIG_PM
static ssize_t synaptics_rmi4_full_pm_cycle_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -449,30 +473,6 @@
return count;
}
-static int synaptics_rmi4_debug_suspend_set(void *_data, u64 val)
-{
- struct synaptics_rmi4_data *rmi4_data = _data;
-
- if (val)
- synaptics_rmi4_suspend(&rmi4_data->input_dev->dev);
- else
- synaptics_rmi4_resume(&rmi4_data->input_dev->dev);
-
- return 0;
-}
-
-static ssize_t synaptics_rmi4_debug_suspend_get(void *_data, u64 *val)
-{
- struct synaptics_rmi4_data *rmi4_data = _data;
-
- *val = rmi4_data->suspended;
-
- return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, synaptics_rmi4_debug_suspend_get,
- synaptics_rmi4_debug_suspend_set, "%lld\n");
-
#ifdef CONFIG_FB
static void configure_sleep(struct synaptics_rmi4_data *rmi4_data)
{
@@ -502,6 +502,11 @@
return;
}
#endif
+#else
+static void configure_sleep(struct synaptics_rmi4_data *rmi4_data)
+{
+ return;
+}
#endif
static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev,
@@ -3665,6 +3670,14 @@
{
return 0;
};
+static int synaptics_rmi4_suspend(struct device *dev);
+{
+ return 0;
+}
+static int synaptics_rmi4_resume(struct device *dev);
+{
+ return 0;
+}
#endif
static const struct i2c_device_id synaptics_rmi4_id_table[] = {
diff --git a/drivers/input/touchscreen/synaptics_rmi_dev.c b/drivers/input/touchscreen/synaptics_rmi_dev.c
index 7abd909..8478b24 100644
--- a/drivers/input/touchscreen/synaptics_rmi_dev.c
+++ b/drivers/input/touchscreen/synaptics_rmi_dev.c
@@ -461,7 +461,7 @@
return;
}
-static char *rmi_char_devnode(struct device *dev, mode_t *mode)
+static char *rmi_char_devnode(struct device *dev, umode_t *mode)
{
if (!mode)
return NULL;
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index a40b68a..9208599 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -1613,6 +1613,15 @@
}
+ /* Reset WLED enable register */
+ rc = qpnp_led_masked_write(led, WLED_MOD_CTRL_REG(led->base),
+ WLED_8_BIT_MASK, WLED_BOOST_OFF);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "WLED write ctrl reg failed(%d)\n", rc);
+ return rc;
+ }
+
/* dump wled registers */
qpnp_dump_regs(led, wled_debug_regs, ARRAY_SIZE(wled_debug_regs));
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
index 95673d5..d33d34b 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -404,6 +404,11 @@
uint32_t error_count;
};
+struct msm_vfe_frame_ts {
+ struct timeval buf_time;
+ uint32_t frame_id;
+};
+
struct vfe_device {
struct platform_device *pdev;
struct msm_sd_subdev subdev;
@@ -446,6 +451,7 @@
struct msm_vfe_axi_shared_data axi_data;
struct msm_vfe_stats_shared_data stats_data;
struct msm_vfe_error_info error_info;
+ struct msm_vfe_frame_ts frame_ts;
struct msm_isp_buf_mgr *buf_mgr;
int dump_reg;
int vfe_clk_idx;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
index c0e5a47..0264d6d 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
@@ -235,6 +235,21 @@
return size;
}
+static void msm_isp_get_buffer_ts(struct vfe_device *vfe_dev,
+ struct msm_isp_timestamp *irq_ts, struct msm_isp_timestamp *ts)
+{
+ struct msm_vfe_frame_ts *frame_ts = &vfe_dev->frame_ts;
+ uint32_t frame_count = vfe_dev->error_info.info_dump_frame_count;
+
+ *ts = *irq_ts;
+ if (frame_count == frame_ts->frame_id) {
+ ts->buf_time = frame_ts->buf_time;
+ } else {
+ frame_ts->buf_time = irq_ts->buf_time;
+ frame_ts->frame_id = frame_count;
+ }
+}
+
void msm_isp_axi_reserve_wm(struct msm_vfe_axi_shared_data *axi_data,
struct msm_vfe_axi_stream *stream_info)
{
@@ -1448,6 +1463,7 @@
struct msm_vfe_axi_stream *stream_info;
struct msm_vfe_axi_composite_info *comp_info;
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
+ struct msm_isp_timestamp buf_ts;
comp_mask = vfe_dev->hw_info->vfe_ops.axi_ops.
get_comp_mask(irq_status0, irq_status1);
@@ -1460,6 +1476,8 @@
pingpong_status =
vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(vfe_dev);
+ msm_isp_get_buffer_ts(vfe_dev, ts, &buf_ts);
+
for (i = 0; i < axi_data->hw_info->num_comp_mask; i++) {
comp_info = &axi_data->composite_info[i];
if (comp_mask & (1 << i)) {
@@ -1492,7 +1510,7 @@
}
if (done_buf && !rc)
msm_isp_process_done_buf(vfe_dev,
- stream_info, done_buf, ts);
+ stream_info, done_buf, &buf_ts);
}
}
wm_mask &= ~(comp_info->stream_composite_mask);
@@ -1524,7 +1542,7 @@
}
if (done_buf && !rc)
msm_isp_process_done_buf(vfe_dev,
- stream_info, done_buf, ts);
+ stream_info, done_buf, &buf_ts);
}
}
return;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
index ef8b996..ea16ebd 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c
@@ -411,43 +411,6 @@
return rc;
}
-static int32_t msm_actuator_set_position(
- struct msm_actuator_ctrl_t *a_ctrl,
- struct msm_actuator_set_position_t *set_pos)
-{
- int32_t rc = 0;
- int32_t index;
- uint16_t next_lens_position;
- uint16_t delay;
- uint32_t hw_params = 0;
- struct msm_camera_i2c_reg_setting reg_setting;
- CDBG("%s Enter %d\n", __func__, __LINE__);
- if (set_pos->number_of_steps == 0)
- return rc;
-
- a_ctrl->i2c_tbl_index = 0;
- for (index = 0; index < set_pos->number_of_steps; index++) {
- next_lens_position = set_pos->pos[index];
- delay = set_pos->delay[index];
- a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl,
- next_lens_position, hw_params, delay);
-
- reg_setting.reg_setting = a_ctrl->i2c_reg_tbl;
- reg_setting.size = a_ctrl->i2c_tbl_index;
- reg_setting.data_type = a_ctrl->i2c_data_type;
-
- rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_write_table_w_microdelay(
- &a_ctrl->i2c_client, ®_setting);
- if (rc < 0) {
- pr_err("%s Failed I2C write Line %d\n", __func__, __LINE__);
- return rc;
- }
- a_ctrl->i2c_tbl_index = 0;
- }
- CDBG("%s exit %d\n", __func__, __LINE__);
- return rc;
-}
-
static int32_t msm_actuator_init(struct msm_actuator_ctrl_t *a_ctrl,
struct msm_actuator_set_info_t *set_info) {
struct reg_settings_t *init_settings = NULL;
@@ -602,12 +565,6 @@
pr_err("move focus failed %d\n", rc);
break;
- case CFG_SET_POSITION:
- rc = a_ctrl->func_tbl->actuator_set_position(a_ctrl,
- &cdata->cfg.setpos);
- if (rc < 0)
- pr_err("actuator_set_position failed %d\n", rc);
- break;
default:
break;
}
@@ -962,7 +919,6 @@
.actuator_set_default_focus = msm_actuator_set_default_focus,
.actuator_init_focus = msm_actuator_init_focus,
.actuator_parse_i2c_params = msm_actuator_parse_i2c_params,
- .actuator_set_position = msm_actuator_set_position,
},
};
diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h
index 7ec9a49..809c9cf 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h
@@ -43,8 +43,6 @@
struct damping_params_t *,
int8_t,
int16_t);
- int32_t (*actuator_set_position)(struct msm_actuator_ctrl_t *,
- struct msm_actuator_set_position_t *);
};
struct msm_actuator {
diff --git a/drivers/media/platform/msm/wfd/vsg-subdev.c b/drivers/media/platform/msm/wfd/vsg-subdev.c
index 1f827bb..433468e 100644
--- a/drivers/media/platform/msm/wfd/vsg-subdev.c
+++ b/drivers/media/platform/msm/wfd/vsg-subdev.c
@@ -26,12 +26,6 @@
#define TICKS_PER_TIMEOUT 2
-static void vsg_reset_timer(struct hrtimer *timer, ktime_t time)
-{
- hrtimer_forward_now(timer, time);
- hrtimer_restart(timer);
-}
-
static int vsg_release_input_buffer(struct vsg_context *context,
struct vsg_buf_info *buf)
{
@@ -467,7 +461,7 @@
* otherwise, diff between two consecutive frames might
* be less than max_frame_interval (for just one sample)
*/
- vsg_reset_timer(&context->threshold_timer,
+ hrtimer_forward_now(&context->threshold_timer,
ns_to_ktime(context->max_frame_interval));
}
}
diff --git a/drivers/platform/msm/sps/sps_bam.c b/drivers/platform/msm/sps/sps_bam.c
index bd4328a..ad5e241 100644
--- a/drivers/platform/msm/sps/sps_bam.c
+++ b/drivers/platform/msm/sps/sps_bam.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -169,6 +169,7 @@
list_for_each_entry(pipe, &dev->pipes_q, list) {
/* Check this pipe's bit in the source mask */
if (BAM_PIPE_IS_ASSIGNED(pipe)
+ && (!pipe->disconnecting)
&& (source & pipe->pipe_index_mask)) {
/* This pipe has an interrupt pending */
pipe_handler(dev, pipe);
@@ -585,6 +586,7 @@
pipe->mode = -1;
pipe->num_descs = 0;
pipe->desc_size = 0;
+ pipe->disconnecting = false;
memset(&pipe->sys, 0, sizeof(pipe->sys));
INIT_LIST_HEAD(&pipe->sys.events_q);
}
diff --git a/drivers/platform/msm/sps/sps_bam.h b/drivers/platform/msm/sps/sps_bam.h
index da5dafd..a20156b 100644
--- a/drivers/platform/msm/sps/sps_bam.h
+++ b/drivers/platform/msm/sps/sps_bam.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -185,6 +185,7 @@
/* System mode control */
struct sps_bam_sys_mode sys;
+ bool disconnecting;
};
/* BAM device descriptor */
diff --git a/drivers/platform/msm/sps/sps_rm.c b/drivers/platform/msm/sps/sps_rm.c
index 7d7e1a6..d8c7a4d 100644
--- a/drivers/platform/msm/sps/sps_rm.c
+++ b/drivers/platform/msm/sps/sps_rm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -556,11 +556,8 @@
{
struct sps_connection *map = (void *)pipe->map;
struct sps_connect *cfg = &pipe->connect;
- struct sps_bam *bam = pipe->bam;
- unsigned long flags;
mutex_lock(&sps_rm->lock);
- spin_lock_irqsave(&bam->isr_lock, flags);
/* Free this connection */
if (cfg->mode == SPS_MODE_SRC)
@@ -574,7 +571,6 @@
sps_rm_remove_ref(map);
- spin_unlock_irqrestore(&bam->isr_lock, flags);
mutex_unlock(&sps_rm->lock);
return 0;
@@ -800,8 +796,9 @@
synchronize_irq(bam->props.irq);
spin_lock_irqsave(&bam->isr_lock, flags);
- result = sps_bam_pipe_disconnect(pipe->bam, pipe_index);
+ pipe->disconnecting = true;
spin_unlock_irqrestore(&bam->isr_lock, flags);
+ result = sps_bam_pipe_disconnect(pipe->bam, pipe_index);
if (result) {
SPS_ERR("sps:Failed to disconnect BAM 0x%x pipe %d",
pipe->bam->props.phys_addr,
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index dc728eb..e65c8cf 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -276,12 +276,15 @@
* Returning 0 on the disconnections and
* removals will ensure consistent state of channels,
* ports with the HW
+ * Remote requests to remove channel/port will be
+ * returned from the path where they wait on
+ * acknowledgement from ADSP
*/
if ((txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER) &&
((mc == SLIM_USR_MC_CHAN_CTRL ||
mc == SLIM_USR_MC_DISCONNECT_PORT ||
mc == SLIM_USR_MC_RECONFIG_NOW)))
- return 0;
+ return -EREMOTEIO;
if ((txn->mt == SLIM_MSG_MT_CORE) &&
((mc == SLIM_MSG_MC_DISCONNECT_PORT ||
mc == SLIM_MSG_MC_NEXT_REMOVE_CHANNEL ||
@@ -299,7 +302,7 @@
return -EREMOTEIO;
timeout = wait_for_completion_timeout(&dev->ctrl_up,
HZ);
- if (!timeout)
+ if (!timeout && dev->state == MSM_CTRL_DOWN)
return -ETIMEDOUT;
}
msm_slim_get_ctrl(dev);
@@ -504,10 +507,17 @@
ret = -ETIMEDOUT;
else
ret = txn->ec;
+ } else if (ret == -EREMOTEIO &&
+ (txn->mc == SLIM_USR_MC_CHAN_CTRL ||
+ txn->mc == SLIM_USR_MC_DISCONNECT_PORT)) {
+ /* HW restarting, channel/port removal should succeed */
+ return 0;
}
+
if (ret) {
pr_err("master msg:0x%x,tid:%d ret:%d", txn->mc,
txn->tid, ret);
+ WARN(1, "timeout during xfer and wait");
mutex_lock(&ctrl->m_ctrl);
ctrl->txnt[txn->tid] = NULL;
mutex_unlock(&ctrl->m_ctrl);
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index f6594c8..96fe2a8 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -313,14 +313,12 @@
sbdrv->device_down(sbdev);
return;
}
- if (sbdev->notified)
+ if (sbdev->notified || !sbdrv)
return;
ret = slim_get_logical_addr(sbdev, sbdev->e_addr, 6, &laddr);
if (!ret) {
- if (sbdrv)
- sbdev->notified = true;
- if (sbdrv->device_up)
- sbdrv->device_up(sbdev);
+ sbdev->notified = true;
+ sbdrv->device_up(sbdev);
}
}
diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c
index 61217dc..891eb2f 100644
--- a/drivers/thermal/msm_thermal.c
+++ b/drivers/thermal/msm_thermal.c
@@ -2426,8 +2426,8 @@
key = "qcom,cpu-sensors";
cpu_cnt = of_property_count_strings(node, key);
- if (cpu_cnt != num_possible_cpus()) {
- pr_err("%s: Wrong number of cpu\n", KBUILD_MODNAME);
+ if (cpu_cnt < num_possible_cpus()) {
+ pr_err("%s: Wrong number of cpu sensors\n", KBUILD_MODNAME);
ret = -EINVAL;
goto hotplug_node_fail;
}
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 739696d..21863e8 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -70,16 +70,19 @@
list_for_each_entry_safe(pos, var, &sensor_info_list, sensor_list) {
if (pos->sensor_id == sensor_id)
- break;
+ return pos;
}
- return pos;
+ return NULL;
}
int sensor_get_id(char *name)
{
struct sensor_info *pos, *var;
+ if (!name)
+ return -ENODEV;
+
list_for_each_entry_safe(pos, var, &sensor_info_list, sensor_list) {
if (!strcmp(pos->tz->type, name))
return pos->sensor_id;
diff --git a/drivers/usb/gadget/f_qc_rndis.c b/drivers/usb/gadget/f_qc_rndis.c
index 1af076b..69f4b1c 100644
--- a/drivers/usb/gadget/f_qc_rndis.c
+++ b/drivers/usb/gadget/f_qc_rndis.c
@@ -347,7 +347,7 @@
};
static struct usb_descriptor_header *eth_qc_ss_function[] = {
- (struct usb_descriptor_header *) &rndis_iad_descriptor,
+ (struct usb_descriptor_header *) &rndis_qc_iad_descriptor,
/* control interface matches ACM, not Ethernet */
(struct usb_descriptor_header *) &rndis_qc_control_intf,
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index 8287ad7..47b8e59 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2013, Linux Foundation. All rights reserved.
+/* Copyright (c) 2009-2014, Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -2551,6 +2551,8 @@
bool work = 0, srp_reqd, dcp;
pm_runtime_resume(otg->phy->dev);
+ if (motg->pm_done)
+ pm_runtime_get_sync(otg->phy->dev);
pr_debug("%s work\n", otg_state_string(otg->phy->state));
switch (otg->phy->state) {
case OTG_STATE_UNDEFINED:
@@ -2696,6 +2698,7 @@
*/
pm_runtime_mark_last_busy(otg->phy->dev);
pm_runtime_autosuspend(otg->phy->dev);
+ motg->pm_done = 1;
}
break;
case OTG_STATE_B_SRP_INIT:
@@ -4842,6 +4845,7 @@
dev_dbg(dev, "OTG runtime resume\n");
pm_runtime_get_noresume(dev);
+ motg->pm_done = 0;
return msm_otg_resume(motg);
}
#endif
@@ -4869,6 +4873,7 @@
dev_dbg(dev, "OTG PM resume\n");
+ motg->pm_done = 0;
atomic_set(&motg->pm_suspended, 0);
if (motg->async_int || motg->sm_work_pending) {
pm_runtime_get_noresume(dev);
diff --git a/drivers/video/msm/mdss/mdss.h b/drivers/video/msm/mdss/mdss.h
index 53292f8..bc4ba6c 100644
--- a/drivers/video/msm/mdss/mdss.h
+++ b/drivers/video/msm/mdss/mdss.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -79,6 +79,11 @@
spinlock_t lock;
};
+struct mdss_fudge_factor {
+ u32 numer;
+ u32 denom;
+};
+
struct mdss_data_type {
u32 mdp_rev;
struct clk *mdp_clk[MDSS_MAX_CLK];
@@ -114,8 +119,8 @@
unsigned long min_mdp_clk;
u32 res_init;
- u32 bus_hdl;
+ u32 highest_bank_bit;
u32 smp_mb_cnt;
u32 smp_mb_size;
u32 smp_mb_per_pipe;
@@ -125,6 +130,16 @@
u32 max_bw_low;
u32 max_bw_high;
+ u32 axi_port_cnt;
+ u32 curr_bw_uc_idx;
+ u32 bus_hdl;
+ struct msm_bus_scale_pdata *bus_scale_table;
+
+ struct mdss_fudge_factor ab_factor;
+ struct mdss_fudge_factor ib_factor;
+ struct mdss_fudge_factor high_ib_factor;
+ struct mdss_fudge_factor clk_factor;
+
struct mdss_hw_settings *hw_settings;
struct mdss_mdp_pipe *vig_pipes;
@@ -162,7 +177,6 @@
struct early_suspend early_suspend;
struct mdss_debug_inf debug_inf;
- int current_bus_idx;
bool mixer_switched;
struct mdss_panel_cfg pan_cfg;
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index 5905894..41aaf61 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -2,7 +2,7 @@
* Core MDSS framebuffer driver.
*
* Copyright (C) 2007 Google Incorporated
- * Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2008-2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -268,10 +268,14 @@
return ret;
}
-static void mdss_fb_parse_dt_split(struct msm_fb_data_type *mfd)
+static void mdss_fb_parse_dt(struct msm_fb_data_type *mfd)
{
u32 data[2];
struct platform_device *pdev = mfd->pdev;
+
+ mfd->splash_logo_enabled = of_property_read_bool(pdev->dev.of_node,
+ "qcom,mdss-fb-splash-logo-enabled");
+
if (of_property_read_u32_array(pdev->dev.of_node, "qcom,mdss-fb-split",
data, 2))
return;
@@ -443,19 +447,29 @@
mfd->mdp_sync_pt_data.timeline =
sw_sync_timeline_create(timeline_name);
if (mfd->mdp_sync_pt_data.timeline == NULL) {
- pr_err("%s: cannot create time line", __func__);
+ pr_err("cannot create release fence time line\n");
return -ENOMEM;
}
mfd->mdp_sync_pt_data.notifier.notifier_call =
__mdss_fb_sync_buf_done_callback;
}
- if ((mfd->panel.type == WRITEBACK_PANEL) ||
- (mfd->panel.type == MIPI_CMD_PANEL))
- mfd->mdp_sync_pt_data.threshold = 1;
- else
- mfd->mdp_sync_pt_data.threshold = 2;
- if (mfd->index == 0) {
+ switch (mfd->panel.type) {
+ case WRITEBACK_PANEL:
+ mfd->mdp_sync_pt_data.threshold = 1;
+ mfd->mdp_sync_pt_data.retire_threshold = 0;
+ break;
+ case MIPI_CMD_PANEL:
+ mfd->mdp_sync_pt_data.threshold = 1;
+ mfd->mdp_sync_pt_data.retire_threshold = 1;
+ break;
+ default:
+ mfd->mdp_sync_pt_data.threshold = 2;
+ mfd->mdp_sync_pt_data.retire_threshold = 0;
+ break;
+ }
+
+ if (mfd->splash_logo_enabled) {
mfd->splash_thread = kthread_run(mdss_fb_splash_thread, mfd,
"mdss_fb_splash");
if (IS_ERR(mfd->splash_thread)) {
@@ -1151,7 +1165,7 @@
mfd->panel_power_on = false;
mfd->dcm_state = DCM_UNINIT;
- mdss_fb_parse_dt_split(mfd);
+ mdss_fb_parse_dt(mfd);
if (mdss_fb_alloc_fbmem(mfd)) {
pr_err("unable to allocate framebuffer memory\n");
@@ -2000,42 +2014,37 @@
}
/**
- * mdss_fb_sync_get_rel_fence() - get release fence from sync pt timeline
- * @sync_pt_data: Sync pt structure holding timeline and fence info.
+ * mdss_fb_sync_get_fence() - get fence from timeline
+ * @timeline: Timeline to create the fence on
+ * @fence_name: Name of the fence that will be created for debugging
+ * @val: Timeline value at which the fence will be signaled
*
- * Function returns a release fence on the timeline associated with the
- * sync pt struct given and it's associated information. The release fence
- * created can be used to signal when buffers provided will be released.
+ * Function returns a fence on the timeline given with the name provided.
+ * The fence created will be signaled when the timeline is advanced.
*/
-static struct sync_fence *__mdss_fb_sync_get_rel_fence(
- struct msm_sync_pt_data *sync_pt_data)
+struct sync_fence *mdss_fb_sync_get_fence(struct sw_sync_timeline *timeline,
+ const char *fence_name, int val)
{
- struct sync_pt *rel_sync_pt;
- struct sync_fence *rel_fence;
- int val;
+ struct sync_pt *sync_pt;
+ struct sync_fence *fence;
- val = sync_pt_data->timeline_value + sync_pt_data->threshold +
- atomic_read(&sync_pt_data->commit_cnt);
+ pr_debug("%s: buf sync fence timeline=%d\n", fence_name, val);
- pr_debug("%s: buf sync rel fence timeline=%d\n",
- sync_pt_data->fence_name, val);
-
- rel_sync_pt = sw_sync_pt_create(sync_pt_data->timeline, val);
- if (rel_sync_pt == NULL) {
- pr_err("%s: cannot create sync point\n",
- sync_pt_data->fence_name);
+ sync_pt = sw_sync_pt_create(timeline, val);
+ if (sync_pt == NULL) {
+ pr_err("%s: cannot create sync point\n", fence_name);
return NULL;
}
/* create fence */
- rel_fence = sync_fence_create(sync_pt_data->fence_name, rel_sync_pt);
- if (rel_fence == NULL) {
- sync_pt_free(rel_sync_pt);
- pr_err("%s: cannot create fence\n", sync_pt_data->fence_name);
+ fence = sync_fence_create(fence_name, sync_pt);
+ if (fence == NULL) {
+ sync_pt_free(sync_pt);
+ pr_err("%s: cannot create fence\n", fence_name);
return NULL;
}
- return rel_fence;
+ return fence;
}
static int mdss_fb_handle_buf_sync_ioctl(struct msm_sync_pt_data *sync_pt_data,
@@ -2043,8 +2052,10 @@
{
int i, ret = 0;
int acq_fen_fd[MDP_MAX_FENCE_FD];
- struct sync_fence *fence, *rel_fence;
+ struct sync_fence *fence, *rel_fence, *retire_fence;
int rel_fen_fd;
+ int retire_fen_fd;
+ int val;
if ((buf_sync->acq_fen_fd_cnt > MDP_MAX_FENCE_FD) ||
(sync_pt_data->timeline == NULL))
@@ -2081,7 +2092,12 @@
if (ret)
goto buf_sync_err_1;
- rel_fence = __mdss_fb_sync_get_rel_fence(sync_pt_data);
+ val = sync_pt_data->timeline_value + sync_pt_data->threshold +
+ atomic_read(&sync_pt_data->commit_cnt);
+
+ /* Set release fence */
+ rel_fence = mdss_fb_sync_get_fence(sync_pt_data->timeline,
+ sync_pt_data->fence_name, val);
if (IS_ERR_OR_NULL(rel_fence)) {
pr_err("%s: unable to retrieve release fence\n",
sync_pt_data->fence_name);
@@ -2105,6 +2121,50 @@
pr_err("%s: copy_to_user failed\n", sync_pt_data->fence_name);
goto buf_sync_err_3;
}
+
+ if (!(buf_sync->flags & MDP_BUF_SYNC_FLAG_RETIRE_FENCE))
+ goto skip_retire_fence;
+
+ if (sync_pt_data->get_retire_fence)
+ retire_fence = sync_pt_data->get_retire_fence(sync_pt_data);
+ else
+ retire_fence = NULL;
+
+ if (IS_ERR_OR_NULL(retire_fence)) {
+ val += sync_pt_data->retire_threshold;
+ retire_fence = mdss_fb_sync_get_fence(
+ sync_pt_data->timeline, "mdp-retire", val);
+ }
+
+ if (IS_ERR_OR_NULL(retire_fence)) {
+ pr_err("%s: unable to retrieve retire fence\n",
+ sync_pt_data->fence_name);
+ ret = retire_fence ? PTR_ERR(rel_fence) : -ENOMEM;
+ goto buf_sync_err_3;
+ }
+ retire_fen_fd = get_unused_fd_flags(0);
+
+ if (retire_fen_fd < 0) {
+ pr_err("%s: get_unused_fd_flags failed for retire fence\n",
+ sync_pt_data->fence_name);
+ ret = -EIO;
+ sync_fence_put(retire_fence);
+ goto buf_sync_err_3;
+ }
+
+ sync_fence_install(retire_fence, retire_fen_fd);
+
+ ret = copy_to_user(buf_sync->retire_fen_fd, &retire_fen_fd,
+ sizeof(int));
+ if (ret) {
+ pr_err("%s: copy_to_user failed for retire fence\n",
+ sync_pt_data->fence_name);
+ put_unused_fd(retire_fen_fd);
+ sync_fence_put(retire_fence);
+ goto buf_sync_err_3;
+ }
+
+skip_retire_fence:
mutex_unlock(&sync_pt_data->sync_mutex);
if (buf_sync->flags & MDP_BUF_SYNC_FLAG_WAIT)
@@ -2362,3 +2422,27 @@
}
module_init(mdss_fb_init);
+
+int mdss_fb_suspres_panel(struct device *dev, void *data)
+{
+ struct msm_fb_data_type *mfd;
+ int rc;
+ u32 event;
+
+ if (!data) {
+ pr_err("Device state not defined\n");
+ return -EINVAL;
+ }
+ mfd = dev_get_drvdata(dev);
+ if (!mfd)
+ return 0;
+
+ event = *((bool *) data) ? MDSS_EVENT_RESUME : MDSS_EVENT_SUSPEND;
+
+ rc = mdss_fb_send_panel_event(mfd, event, NULL);
+ if (rc)
+ pr_warn("unable to %s fb%d (%d)\n",
+ event == MDSS_EVENT_RESUME ? "resume" : "suspend",
+ mfd->index, rc);
+ return rc;
+}
diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h
index f9203c0..2e024c9 100644
--- a/drivers/video/msm/mdss/mdss_fb.h
+++ b/drivers/video/msm/mdss/mdss_fb.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -96,13 +96,16 @@
struct sw_sync_timeline *timeline;
int timeline_value;
u32 threshold;
-
+ u32 retire_threshold;
atomic_t commit_cnt;
bool flushed;
bool async_wait_fences;
struct mutex sync_mutex;
struct notifier_block notifier;
+
+ struct sync_fence *(*get_retire_fence)
+ (struct msm_sync_pt_data *sync_pt_data);
};
struct msm_fb_data_type;
@@ -212,6 +215,7 @@
bool shutdown_pending;
struct task_struct *splash_thread;
+ bool splash_logo_enabled;
struct msm_fb_backup_type msm_fb_backup;
struct completion power_set_comp;
@@ -245,6 +249,9 @@
void mdss_fb_update_backlight(struct msm_fb_data_type *mfd);
void mdss_fb_wait_for_fence(struct msm_sync_pt_data *sync_pt_data);
void mdss_fb_signal_timeline(struct msm_sync_pt_data *sync_pt_data);
+struct sync_fence *mdss_fb_sync_get_fence(struct sw_sync_timeline *timeline,
+ const char *fence_name, int val);
int mdss_fb_register_mdp_instance(struct msm_mdp_interface *mdp);
int mdss_fb_dcm(struct msm_fb_data_type *mfd, int req_state);
+int mdss_fb_suspres_panel(struct device *dev, void *data);
#endif /* MDSS_FB_H */
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.c b/drivers/video/msm/mdss/mdss_hdmi_tx.c
index 426855c..357cf89 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.c
@@ -120,6 +120,7 @@
static inline void hdmi_tx_set_audio_switch_node(struct hdmi_tx_ctrl *hdmi_ctrl,
int val, bool force);
static int hdmi_tx_audio_setup(struct hdmi_tx_ctrl *hdmi_ctrl);
+static void hdmi_tx_en_encryption(struct hdmi_tx_ctrl *hdmi_ctrl, u32 on);
struct mdss_hw hdmi_tx_hw = {
.hw_ndx = MDSS_HW_HDMI,
@@ -916,11 +917,11 @@
switch (status) {
case HDCP_STATE_AUTHENTICATED:
if (hdmi_ctrl->hpd_state) {
- /* Clear AV Mute */
- rc = hdmi_tx_config_avmute(hdmi_ctrl, 0);
- if (rc)
- DEV_ERR("%s: Failed to clear av mute. rc=%d\n",
- __func__, rc);
+ if (hdmi_ctrl->pdata.primary)
+ hdmi_tx_en_encryption(hdmi_ctrl, true);
+ else
+ /* Clear AV Mute */
+ rc = hdmi_tx_config_avmute(hdmi_ctrl, 0);
hdmi_tx_set_audio_switch_node(hdmi_ctrl, 1, false);
}
break;
@@ -928,11 +929,11 @@
hdmi_tx_set_audio_switch_node(hdmi_ctrl, 0, false);
if (hdmi_ctrl->hpd_state) {
- /* Set AV Mute */
- rc = hdmi_tx_config_avmute(hdmi_ctrl, 1);
- if (rc)
- DEV_ERR("%s: Failed to set av mute. rc=%d\n",
- __func__, rc);
+ if (hdmi_ctrl->pdata.primary)
+ hdmi_tx_en_encryption(hdmi_ctrl, false);
+ else
+ /* Set AV Mute */
+ rc = hdmi_tx_config_avmute(hdmi_ctrl, 1);
DEV_DBG("%s: Reauthenticating\n", __func__);
rc = hdmi_hdcp_reauthenticate(
@@ -1646,6 +1647,28 @@
DSS_REG_W(io, HDMI_GEN_PKT_CTRL, packet_control);
} /* hdmi_tx_set_spd_infoframe */
+static void hdmi_tx_en_encryption(struct hdmi_tx_ctrl *hdmi_ctrl, u32 on)
+{
+ u32 reg_val;
+ struct dss_io_data *io = NULL;
+
+ if (!hdmi_ctrl->hdcp_feature_on || !hdmi_ctrl->present_hdcp)
+ return;
+
+ io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
+
+ mutex_lock(&hdmi_ctrl->mutex);
+ reg_val = DSS_REG_R_ND(io, HDMI_CTRL);
+
+ if (on)
+ reg_val |= BIT(2);
+ else
+ reg_val &= ~BIT(2);
+ DSS_REG_W(io, HDMI_CTRL, reg_val);
+
+ mutex_unlock(&hdmi_ctrl->mutex);
+} /* hdmi_tx_en_encryption */
+
static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on)
{
struct dss_io_data *io = NULL;
@@ -1662,12 +1685,14 @@
return;
}
+ mutex_lock(&hdmi_ctrl->mutex);
if (power_on) {
/* Enable the block */
reg_val |= BIT(0);
/* HDMI Encryption, if HDCP is enabled */
- if (hdmi_ctrl->hdcp_feature_on && hdmi_ctrl->present_hdcp)
+ if (hdmi_ctrl->hdcp_feature_on &&
+ hdmi_ctrl->present_hdcp && !hdmi_ctrl->pdata.primary)
reg_val |= BIT(2);
/* Set transmission mode to DVI based in EDID info */
@@ -1677,6 +1702,7 @@
}
DSS_REG_W(io, HDMI_CTRL, reg_val);
+ mutex_unlock(&hdmi_ctrl->mutex);
DEV_DBG("HDMI Core: %s, HDMI_CTRL=0x%08x\n",
power_on ? "Enable" : "Disable", reg_val);
@@ -3109,10 +3135,10 @@
case MDSS_EVENT_PANEL_ON:
if (hdmi_ctrl->hdcp_feature_on && hdmi_ctrl->present_hdcp) {
/* Set AV Mute before starting authentication */
- rc = hdmi_tx_config_avmute(hdmi_ctrl, 1);
- if (rc)
- DEV_ERR("%s: Failed to set av mute. rc=%d\n",
- __func__, rc);
+ if (hdmi_ctrl->pdata.primary)
+ hdmi_tx_en_encryption(hdmi_ctrl, false);
+ else
+ rc = hdmi_tx_config_avmute(hdmi_ctrl, 1);
DEV_DBG("%s: Starting HDCP authentication\n", __func__);
rc = hdmi_hdcp_authenticate(
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
index a42aa87..a9f6dbc 100644
--- a/drivers/video/msm/mdss/mdss_mdp.c
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -1,7 +1,7 @@
/*
* MDSS MDP Interface (used by framebuffer core)
*
- * Copyright (c) 2007-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2007-2014, The Linux Foundation. All rights reserved.
* Copyright (C) 2007 Google Incorporated
*
* This software is licensed under the terms of the GNU General Public
@@ -82,33 +82,12 @@
static DEFINE_MUTEX(bus_bw_lock);
static DEFINE_MUTEX(mdp_iommu_lock);
-#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), \
- }
-
-static struct msm_bus_vectors mdp_bus_vectors[] = {
- MDP_BUS_VECTOR_ENTRY(0, 0),
- MDP_BUS_VECTOR_ENTRY(SZ_128M, SZ_256M),
- MDP_BUS_VECTOR_ENTRY(SZ_256M, SZ_512M),
-};
-static struct msm_bus_paths mdp_bus_usecases[ARRAY_SIZE(mdp_bus_vectors)];
-
static struct mdss_panel_intf pan_types[] = {
{"dsi", MDSS_PANEL_INTF_DSI},
{"edp", MDSS_PANEL_INTF_EDP},
{"hdmi", MDSS_PANEL_INTF_HDMI},
};
-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_iommu_map_type mdss_iommu_map[MDSS_IOMMU_MAX_DOMAIN] = {
[MDSS_IOMMU_DOMAIN_UNSECURE] = {
.client_name = "mdp_ns",
@@ -156,6 +135,7 @@
static int mdss_mdp_parse_dt_smp(struct platform_device *pdev);
static int mdss_mdp_parse_dt_misc(struct platform_device *pdev);
static int mdss_mdp_parse_dt_ad_cfg(struct platform_device *pdev);
+static int mdss_mdp_parse_dt_bus_scale(struct platform_device *pdev);
u32 mdss_mdp_fb_stride(u32 fb_index, u32 xres, int bpp)
{
@@ -332,23 +312,17 @@
static int mdss_mdp_bus_scale_register(struct mdss_data_type *mdata)
{
if (!mdata->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];
- }
-
- mdata->bus_hdl = msm_bus_scale_register_client(bus_pdata);
- if (!mdata->bus_hdl) {
- pr_err("not able to get bus scale\n");
- return -ENOMEM;
+ mdata->bus_hdl =
+ msm_bus_scale_register_client(mdata->bus_scale_table);
+ if (IS_ERR_VALUE(mdata->bus_hdl)) {
+ pr_err("bus_client register failed\n");
+ return -EINVAL;
}
pr_debug("register bus_hdl=%x\n", mdata->bus_hdl);
}
- return 0;
+
+ return mdss_mdp_bus_scale_set_quota(AB_QUOTA, IB_QUOTA);
}
static void mdss_mdp_bus_scale_unregister(struct mdss_data_type *mdata)
@@ -361,7 +335,7 @@
int mdss_mdp_bus_scale_set_quota(u64 ab_quota, u64 ib_quota)
{
- int bus_idx;
+ int new_uc_idx;
if (mdss_res->bus_hdl < 1) {
pr_err("invalid bus handle %d\n", mdss_res->bus_hdl);
@@ -369,32 +343,50 @@
}
if ((ab_quota | ib_quota) == 0) {
- bus_idx = 0;
+ new_uc_idx = 0;
} else {
- int num_cases = mdp_bus_scale_table.num_usecases;
+ int i;
struct msm_bus_vectors *vect = NULL;
+ struct msm_bus_scale_pdata *bw_table =
+ mdss_res->bus_scale_table;
+ unsigned long size;
- bus_idx = (mdss_res->current_bus_idx % (num_cases - 1)) + 1;
-
- vect = mdp_bus_scale_table.usecase[mdss_res->current_bus_idx].
- vectors;
-
- /* avoid performing updates for small changes */
- if ((ALIGN(ab_quota, SZ_64M) == ALIGN(vect->ab, SZ_64M)) &&
- (ALIGN(ib_quota, SZ_64M) == ALIGN(vect->ib, SZ_64M))) {
- pr_debug("skip bus scaling, no change in vectors\n");
- return 0;
+ if (!bw_table || !mdss_res->axi_port_cnt) {
+ pr_err("invalid input\n");
+ return -EINVAL;
}
- vect = mdp_bus_scale_table.usecase[bus_idx].vectors;
- vect->ab = ab_quota;
- vect->ib = ib_quota;
+ size = SZ_64M / mdss_res->axi_port_cnt;
- pr_debug("bus scale idx=%d ab=%llu ib=%llu\n", bus_idx,
- vect->ab, vect->ib);
+ ab_quota = div_u64(ab_quota, mdss_res->axi_port_cnt);
+ ib_quota = div_u64(ib_quota, mdss_res->axi_port_cnt);
+
+ new_uc_idx = (mdss_res->curr_bw_uc_idx %
+ (bw_table->num_usecases - 1)) + 1;
+
+ for (i = 0; i < mdss_res->axi_port_cnt; i++) {
+ vect = &bw_table->usecase[mdss_res->curr_bw_uc_idx].
+ vectors[i];
+
+ /* avoid performing updates for small changes */
+ if ((ALIGN(ab_quota, size) == ALIGN(vect->ab, size)) &&
+ (ALIGN(ib_quota, size) == ALIGN(vect->ib, size))) {
+ pr_debug("skip bus scaling, no changes\n");
+ return 0;
+ }
+
+ vect = &bw_table->usecase[new_uc_idx].vectors[i];
+ vect->ab = ab_quota;
+ vect->ib = ib_quota;
+
+ pr_debug("uc_idx=%d path_idx=%d ab=%llu ib=%llu\n",
+ new_uc_idx, i, vect->ab, vect->ib);
+ }
}
- mdss_res->current_bus_idx = bus_idx;
- return msm_bus_scale_client_update_request(mdss_res->bus_hdl, bus_idx);
+ mdss_res->curr_bw_uc_idx = new_uc_idx;
+
+ return msm_bus_scale_client_update_request(mdss_res->bus_hdl,
+ new_uc_idx);
}
static inline u32 mdss_mdp_irq_mask(u32 intr_type, u32 intf_num)
@@ -654,7 +646,7 @@
} else {
pm_runtime_get_sync(&mdata->pdev->dev);
msm_bus_scale_client_update_request(
- mdata->bus_hdl, mdata->current_bus_idx);
+ mdata->bus_hdl, mdata->curr_bw_uc_idx);
if (!mdata->handoff_pending)
mdss_iommu_attach(mdata);
}
@@ -1098,6 +1090,8 @@
SPRINT(" bwc");
if (mdata->has_decimation)
SPRINT(" decimation");
+ if (mdata->highest_bank_bit)
+ SPRINT(" tile_format");
SPRINT("\n");
return cnt;
@@ -1218,7 +1212,6 @@
pr_err("unable to register bus scaling\n");
goto probe_done;
}
- mdss_mdp_bus_scale_set_quota(AB_QUOTA, IB_QUOTA);
rc = mdss_mdp_debug_init(mdata);
if (rc) {
@@ -1558,6 +1551,12 @@
return rc;
}
+ rc = mdss_mdp_parse_dt_bus_scale(pdev);
+ if (rc) {
+ pr_err("Error in device tree : bus scale\n");
+ return rc;
+ }
+
return 0;
}
@@ -2014,6 +2013,24 @@
return rc;
}
+static void mdss_mdp_parse_dt_fudge_factors(struct platform_device *pdev,
+ char *prop_name, struct mdss_fudge_factor *ff)
+{
+ int rc;
+ u32 data[2] = {0, 0};
+
+ ff->numer = 1;
+ ff->denom = 1;
+
+ rc = mdss_mdp_parse_dt_handler(pdev, prop_name, data, 2);
+ if (rc) {
+ pr_debug("err reading %s\n", prop_name);
+ } else {
+ ff->numer = data[0];
+ ff->denom = data[1];
+ }
+}
+
static int mdss_mdp_parse_dt_misc(struct platform_device *pdev)
{
struct mdss_data_type *mdata = platform_get_drvdata(pdev);
@@ -2033,6 +2050,10 @@
"qcom,mdss-has-wfd-blk");
prop = of_find_property(pdev->dev.of_node, "batfet-supply", NULL);
mdata->batfet_required = prop ? true : false;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mdss-highest-bank-bit", &(mdata->highest_bank_bit));
+ if (rc)
+ pr_debug("Could not read optional property: highest bank bit\n");
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,max-bandwidth-low-kbps", &mdata->max_bw_low);
@@ -2044,6 +2065,15 @@
if (rc)
pr_debug("max bandwidth (high) property not specified\n");
+ mdss_mdp_parse_dt_fudge_factors(pdev, "qcom,mdss-ab-factor",
+ &mdata->ab_factor);
+ mdss_mdp_parse_dt_fudge_factors(pdev, "qcom,mdss-ib-factor",
+ &mdata->ib_factor);
+ mdss_mdp_parse_dt_fudge_factors(pdev, "qcom,mdss-high-ib-factor",
+ &mdata->high_ib_factor);
+ mdss_mdp_parse_dt_fudge_factors(pdev, "qcom,mdss-clk-factor",
+ &mdata->clk_factor);
+
return 0;
}
@@ -2087,6 +2117,31 @@
return rc;
}
+static int mdss_mdp_parse_dt_bus_scale(struct platform_device *pdev)
+{
+ int rc;
+ struct mdss_data_type *mdata = platform_get_drvdata(pdev);
+
+ rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-bus,num-paths",
+ &mdata->axi_port_cnt);
+ if (rc) {
+ pr_err("Error. qcom,msm-bus,num-paths prop not found.rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ mdata->bus_scale_table = msm_bus_cl_get_pdata(pdev);
+ if (IS_ERR_OR_NULL(mdata->bus_scale_table)) {
+ rc = PTR_ERR(mdata->bus_scale_table);
+ if (!rc)
+ rc = -EINVAL;
+ pr_err("msm_bus_cl_get_pdata failed. rc=%d\n", rc);
+ mdata->bus_scale_table = NULL;
+ }
+
+ return rc;
+}
+
static int mdss_mdp_parse_dt_handler(struct platform_device *pdev,
char *prop_name, u32 *offsets, int len)
{
@@ -2350,11 +2405,12 @@
static int mdss_mdp_runtime_resume(struct device *dev)
{
struct mdss_data_type *mdata = dev_get_drvdata(dev);
+ bool device_on = true;
if (!mdata)
return -ENODEV;
dev_dbg(dev, "pm_runtime: resuming...\n");
-
+ device_for_each_child(dev, &device_on, mdss_fb_suspres_panel);
mdss_mdp_footswitch_ctrl(mdata, true);
return 0;
@@ -2374,6 +2430,7 @@
static int mdss_mdp_runtime_suspend(struct device *dev)
{
struct mdss_data_type *mdata = dev_get_drvdata(dev);
+ bool device_on = false;
if (!mdata)
return -ENODEV;
dev_dbg(dev, "pm_runtime: suspending...\n");
@@ -2382,6 +2439,7 @@
pr_err("MDP suspend failed\n");
return -EBUSY;
}
+ device_for_each_child(dev, &device_on, mdss_fb_suspres_panel);
mdss_mdp_footswitch_ctrl(mdata, false);
return 0;
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 1c4efb4..4f1da63 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -237,7 +237,7 @@
u8 unpack_count; /* 0 = 1 component, 1 = 2 component ... */
u8 bpp;
u8 alpha_enable; /* source has alpha */
-
+ u8 tile;
u8 bits[MAX_PLANES];
u8 element[MAX_PLANES];
};
@@ -422,6 +422,11 @@
u32 splash_mem_addr;
u32 splash_mem_size;
u32 sd_enabled;
+
+ struct sw_sync_timeline *vsync_timeline;
+ struct mdss_mdp_vsync_handler vsync_retire_handler;
+ struct work_struct retire_work;
+ int retire_cnt;
};
struct mdss_mdp_perf_params {
@@ -520,7 +525,7 @@
int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl);
int mdss_mdp_ctl_intf_event(struct mdss_mdp_ctl *ctl, int event, void *arg);
int mdss_mdp_perf_calc_pipe(struct mdss_mdp_pipe *pipe,
- struct mdss_mdp_perf_params *perf);
+ struct mdss_mdp_perf_params *perf, struct mdss_mdp_img_rect *roi);
int mdss_mdp_ctl_notify(struct mdss_mdp_ctl *ctl, int event);
void mdss_mdp_ctl_notifier_register(struct mdss_mdp_ctl *ctl,
struct notifier_block *notifier);
@@ -632,6 +637,10 @@
void mdss_mdp_intersect_rect(struct mdss_mdp_img_rect *res_rect,
const struct mdss_mdp_img_rect *dst_rect,
const struct mdss_mdp_img_rect *sci_rect);
+void mdss_mdp_crop_rect(struct mdss_mdp_img_rect *src_rect,
+ struct mdss_mdp_img_rect *dst_rect,
+ const struct mdss_mdp_img_rect *sci_rect);
+
int mdss_mdp_wb_kickoff(struct msm_fb_data_type *mfd);
int mdss_mdp_wb_ioctl_handler(struct msm_fb_data_type *mfd, u32 cmd, void *arg);
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index db67773..d96ac45 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -24,14 +24,26 @@
/* truncate at 1k */
#define MDSS_MDP_BUS_FACTOR_SHIFT 10
-/* 1.5 bus fudge factor */
-#define MDSS_MDP_BUS_FUDGE_FACTOR_IB(val) (((val) / 2) * 3)
-#define MDSS_MDP_BUS_FUDGE_FACTOR_HIGH_IB(val) (val << 1)
-#define MDSS_MDP_BUS_FUDGE_FACTOR_AB(val) (val << 1)
#define MDSS_MDP_BUS_FLOOR_BW (1600000000ULL >> MDSS_MDP_BUS_FACTOR_SHIFT)
-/* 1.25 clock fudge factor */
-#define MDSS_MDP_CLK_FUDGE_FACTOR(val) (((val) * 5) / 4)
+static inline u64 fudge_factor(u64 val, u32 numer, u32 denom)
+{
+ u64 result = (val * (u64)numer);
+ do_div(result, denom);
+ return result;
+}
+
+#define AB_FUDGE_FACTOR(val) fudge_factor((val), \
+ (mdss_res->ab_factor.numer), (mdss_res->ab_factor.denom))
+
+#define IB_FUDGE_FACTOR(val) fudge_factor((val), \
+ (mdss_res->ib_factor.numer), (mdss_res->ib_factor.denom))
+
+#define HIGH_IB_FUDGE_FACTOR(val) fudge_factor((val), \
+ (mdss_res->high_ib_factor.numer), (mdss_res->high_ib_factor.denom))
+
+#define CLK_FUDGE_FACTOR(val) fudge_factor((val), \
+ (mdss_res->clk_factor.numer), (mdss_res->clk_factor.denom))
enum {
MDSS_MDP_PERF_UPDATE_SKIP,
@@ -63,6 +75,25 @@
pinfo->clk_rate;
}
+static inline u32 mdss_mdp_clk_fudge_factor(struct mdss_mdp_mixer *mixer,
+ u32 rate)
+{
+ struct mdss_panel_info *pinfo = &mixer->ctl->panel_data->panel_info;
+
+ rate = CLK_FUDGE_FACTOR(rate);
+
+ /*
+ * If the panel is video mode and its back porch period is
+ * small, the workaround of increasing mdp clk is needed to
+ * avoid underrun.
+ */
+ if (mixer->ctl->is_video_mode && pinfo &&
+ (pinfo->lcdc.v_back_porch < MDP_MIN_VBP))
+ rate = CLK_FUDGE_FACTOR(rate);
+
+ return rate;
+}
+
static u32 __mdss_mdp_ctrl_perf_ovrd_helper(struct mdss_mdp_mixer *mixer,
u32 *npipe)
{
@@ -116,11 +147,11 @@
ctl->mixer_right, &npipe);
}
- *ab_quota = MDSS_MDP_BUS_FUDGE_FACTOR_AB(*ab_quota);
+ *ab_quota = AB_FUDGE_FACTOR(*ab_quota);
if (npipe > 1)
- *ib_quota = MDSS_MDP_BUS_FUDGE_FACTOR_HIGH_IB(*ib_quota);
+ *ib_quota = HIGH_IB_FUDGE_FACTOR(*ib_quota);
else
- *ib_quota = MDSS_MDP_BUS_FUDGE_FACTOR_IB(*ib_quota);
+ *ib_quota = IB_FUDGE_FACTOR(*ib_quota);
if (ovrd && (*ib_quota < MDSS_MDP_BUS_FLOOR_BW)) {
*ib_quota = MDSS_MDP_BUS_FLOOR_BW;
@@ -162,7 +193,6 @@
mdss_mdp_bus_scale_set_quota(bus_ab_quota, bus_ib_quota);
}
if (flags & MDSS_MDP_PERF_UPDATE_CLK) {
- clk_rate = MDSS_MDP_CLK_FUDGE_FACTOR(clk_rate);
pr_debug("update clk rate = %lu HZ\n", clk_rate);
mdss_mdp_set_clk_rate(clk_rate);
}
@@ -183,16 +213,20 @@
* (MDP clock requirement) based on frame size and scaling requirements.
*/
int mdss_mdp_perf_calc_pipe(struct mdss_mdp_pipe *pipe,
- struct mdss_mdp_perf_params *perf)
+ struct mdss_mdp_perf_params *perf, struct mdss_mdp_img_rect *roi)
{
struct mdss_mdp_mixer *mixer;
int fps = DEFAULT_FRAME_RATE;
u32 quota, rate, v_total, src_h;
+ struct mdss_mdp_img_rect src, dst;
if (!pipe || !perf || !pipe->mixer)
return -EINVAL;
mixer = pipe->mixer;
+ dst = pipe->dst;
+ src = pipe->src;
+
if (mixer->rotator_mode) {
v_total = pipe->flags & MDP_ROT_90 ? pipe->dst.w : pipe->dst.h;
} else if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
@@ -205,14 +239,17 @@
v_total = mixer->height;
}
+ if (roi)
+ mdss_mdp_crop_rect(&src, &dst, roi);
+
/*
* when doing vertical decimation lines will be skipped, hence there is
* no need to account for these lines in MDP clock or request bus
* bandwidth to fetch them.
*/
- src_h = pipe->src.h >> pipe->vert_deci;
+ src_h = src.h >> pipe->vert_deci;
- quota = fps * pipe->src.w * src_h;
+ quota = fps * src.w * src_h;
if (pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_420)
/*
* with decimation, chroma is not downsampled, this means we
@@ -225,9 +262,9 @@
else
quota *= pipe->src_fmt->bpp;
- rate = pipe->dst.w;
- if (src_h > pipe->dst.h)
- rate = (rate * src_h) / pipe->dst.h;
+ rate = dst.w;
+ if (src_h > dst.h)
+ rate = (rate * src_h) / dst.h;
rate *= v_total * fps;
if (mixer->rotator_mode) {
@@ -235,11 +272,16 @@
quota *= 2; /* bus read + write */
perf->ib_quota = quota;
} else {
- perf->ib_quota = (quota / pipe->dst.h) * v_total;
+ perf->ib_quota = (quota / dst.h) * v_total;
}
+
perf->ab_quota = quota;
+ rate = mdss_mdp_clk_fudge_factor(mixer, rate);
perf->mdp_clk_rate = rate;
+ pr_debug("src(w,h)(%d,%d) dst(w,h)(%d,%d) v_total=%d v_deci=%d fps=%d\n",
+ pipe->src.w, pipe->src.h, pipe->dst.w, pipe->dst.h, v_total,
+ pipe->vert_deci, fps);
pr_debug("mixer=%d pnum=%d clk_rate=%u bus ab=%u ib=%u\n",
mixer->num, pipe->num, rate, perf->ab_quota, perf->ib_quota);
@@ -273,8 +315,7 @@
v_total = mixer->height;
}
*clk_rate = mixer->width * v_total * fps;
- if (pinfo && pinfo->lcdc.v_back_porch < MDP_MIN_VBP)
- *clk_rate = MDSS_MDP_CLK_FUDGE_FACTOR(*clk_rate);
+ *clk_rate = mdss_mdp_clk_fudge_factor(mixer, *clk_rate);
if (!pinfo) {
/* perf for bus writeback */
@@ -290,7 +331,7 @@
if (pipe == NULL)
continue;
- if (mdss_mdp_perf_calc_pipe(pipe, &perf))
+ if (mdss_mdp_perf_calc_pipe(pipe, &perf, &mixer->roi))
continue;
ab_total += perf.ab_quota >> MDSS_MDP_BUS_FACTOR_SHIFT;
diff --git a/drivers/video/msm/mdss/mdss_mdp_formats.h b/drivers/video/msm/mdss/mdss_mdp_formats.h
index a2edf90..dcbff88 100644
--- a/drivers/video/msm/mdss/mdss_mdp_formats.h
+++ b/drivers/video/msm/mdss/mdss_mdp_formats.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -81,6 +81,25 @@
}, \
}
+#define FMT_RGB_8888_TILE(fmt, alpha_en, e0, e1, e2, e3) \
+ { \
+ .format = (fmt), \
+ .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED, \
+ .unpack_tight = 1, \
+ .unpack_align_msb = 0, \
+ .alpha_enable = (alpha_en), \
+ .unpack_count = 4, \
+ .bpp = 4, \
+ .tile = 1, \
+ .element = { (e0), (e1), (e2), (e3) }, \
+ .bits = { \
+ [C3_ALPHA] = COLOR_8BIT, \
+ [C2_R_Cr] = COLOR_8BIT, \
+ [C0_G_Y] = COLOR_8BIT, \
+ [C1_B_Cb] = COLOR_8BIT, \
+ }, \
+ }
+
#define FMT_YUV_COMMON(fmt) \
.format = (fmt), \
.is_yuv = 1, \
@@ -125,6 +144,22 @@
FMT_RGB_8888(MDP_RGBX_8888, 0, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA),
FMT_RGB_8888(MDP_BGRA_8888, 1, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA),
FMT_RGB_8888(MDP_BGRX_8888, 0, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA),
+ FMT_RGB_8888_TILE(MDP_RGBA_8888_TILE, 1, C2_R_Cr, C0_G_Y, C1_B_Cb,
+ C3_ALPHA),
+ FMT_RGB_8888_TILE(MDP_ARGB_8888_TILE, 1, C3_ALPHA, C2_R_Cr, C0_G_Y,
+ C1_B_Cb),
+ FMT_RGB_8888_TILE(MDP_ABGR_8888_TILE, 1, C3_ALPHA, C1_B_Cb, C0_G_Y,
+ C2_R_Cr),
+ FMT_RGB_8888_TILE(MDP_BGRA_8888_TILE, 1, C1_B_Cb, C0_G_Y, C2_R_Cr,
+ C3_ALPHA),
+ FMT_RGB_8888_TILE(MDP_RGBX_8888_TILE, 0, C2_R_Cr, C0_G_Y, C1_B_Cb,
+ C3_ALPHA),
+ FMT_RGB_8888_TILE(MDP_XRGB_8888_TILE, 0, C3_ALPHA, C2_R_Cr, C0_G_Y,
+ C1_B_Cb),
+ FMT_RGB_8888_TILE(MDP_XBGR_8888_TILE, 0, C3_ALPHA, C1_B_Cb, C0_G_Y,
+ C2_R_Cr),
+ FMT_RGB_8888_TILE(MDP_BGRX_8888_TILE, 0, C1_B_Cb, C0_G_Y, C2_R_Cr,
+ C3_ALPHA),
FMT_YUV_PSEUDO(MDP_Y_CRCB_H1V1, MDSS_MDP_CHROMA_RGB, C2_R_Cr, C1_B_Cb),
FMT_YUV_PSEUDO(MDP_Y_CBCR_H1V1, MDSS_MDP_CHROMA_RGB, C1_B_Cb, C2_R_Cr),
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h
index 9948738..bff56d2 100644
--- a/drivers/video/msm/mdss/mdss_mdp_hwio.h
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -25,6 +25,8 @@
#define MDSS_MDP_HW_REV_102 0x10020000
#define MDSS_MDP_HW_REV_103 0x10030000
+#define MDSS_MDP_FETCH_CONFIG_RESET_VALUE 0x00000087
+
#define MDSS_REG_HW_VERSION 0x0
#define MDSS_REG_HW_INTR_STATUS 0x10
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 8ea86d0..9365be8 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -23,6 +23,7 @@
#include <linux/delay.h>
#include <linux/msm_mdp.h>
#include <linux/memblock.h>
+#include <linux/sw_sync.h>
#include <mach/iommu_domains.h>
#include <mach/event_timer.h>
@@ -261,7 +262,7 @@
int rc;
for (;;) {
- rc = mdss_mdp_perf_calc_pipe(pipe, &perf);
+ rc = mdss_mdp_perf_calc_pipe(pipe, &perf, NULL);
if (!rc && (perf.mdp_clk_rate <= mdata->max_mdp_clk_rate))
break;
@@ -598,12 +599,6 @@
!mdp5_data->mdata->has_wfd_blk)
mdss_mdp_smp_release(pipe);
- /*
- * Clear previous SMP reservations and reserve according to the
- * latest configuration
- */
- mdss_mdp_smp_unreserve(pipe);
-
ret = mdss_mdp_smp_reserve(pipe);
if (ret) {
pr_debug("mdss_mdp_smp_reserve failed. ret=%d\n", ret);
@@ -2709,6 +2704,107 @@
return rc;
}
+static void __vsync_retire_handle_vsync(struct mdss_mdp_ctl *ctl, ktime_t t)
+{
+ struct msm_fb_data_type *mfd = ctl->mfd;
+ struct mdss_overlay_private *mdp5_data;
+
+ if (!mfd || !mfd->mdp.private1) {
+ pr_warn("Invalid handle for vsync\n");
+ return;
+ }
+
+ mdp5_data = mfd_to_mdp5_data(mfd);
+ schedule_work(&mdp5_data->retire_work);
+}
+
+static void __vsync_retire_work_handler(struct work_struct *work)
+{
+ struct mdss_overlay_private *mdp5_data =
+ container_of(work, typeof(*mdp5_data), retire_work);
+ struct msm_sync_pt_data *sync_pt_data;
+
+ if (!mdp5_data->ctl || !mdp5_data->ctl->mfd)
+ return;
+
+ if (!mdp5_data->ctl->remove_vsync_handler)
+ return;
+
+ sync_pt_data = &mdp5_data->ctl->mfd->mdp_sync_pt_data;
+ mutex_lock(&sync_pt_data->sync_mutex);
+ if (mdp5_data->retire_cnt > 0) {
+ sw_sync_timeline_inc(mdp5_data->vsync_timeline, 1);
+
+ mdp5_data->retire_cnt--;
+ if (mdp5_data->retire_cnt == 0) {
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ mdp5_data->ctl->remove_vsync_handler(mdp5_data->ctl,
+ &mdp5_data->vsync_retire_handler);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+ }
+ }
+ mutex_unlock(&sync_pt_data->sync_mutex);
+}
+
+static struct sync_fence *
+__vsync_retire_get_fence(struct msm_sync_pt_data *sync_pt_data)
+{
+ struct msm_fb_data_type *mfd;
+ struct mdss_overlay_private *mdp5_data;
+ struct mdss_mdp_ctl *ctl;
+ int rc, value;
+
+ mfd = container_of(sync_pt_data, typeof(*mfd), mdp_sync_pt_data);
+ mdp5_data = mfd_to_mdp5_data(mfd);
+
+ if (!mdp5_data || !mdp5_data->ctl)
+ return ERR_PTR(-ENODEV);
+
+ ctl = mdp5_data->ctl;
+ if (!ctl->add_vsync_handler)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ if (!ctl->power_on) {
+ pr_debug("fb%d vsync pending first update\n", mfd->index);
+ return ERR_PTR(-EPERM);
+ }
+
+ if (mdp5_data->retire_cnt == 0) {
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ rc = ctl->add_vsync_handler(ctl,
+ &mdp5_data->vsync_retire_handler);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+ if (IS_ERR_VALUE(rc))
+ return ERR_PTR(rc);
+ }
+ value = mdp5_data->vsync_timeline->value + 1 + mdp5_data->retire_cnt;
+ mdp5_data->retire_cnt++;
+
+ return mdss_fb_sync_get_fence(mdp5_data->vsync_timeline,
+ "mdp-retire", value);
+}
+
+static int __vsync_retire_setup(struct msm_fb_data_type *mfd)
+{
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ char name[24];
+
+ snprintf(name, sizeof(name), "mdss_fb%d_retire", mfd->index);
+ mdp5_data->vsync_timeline = sw_sync_timeline_create(name);
+ if (mdp5_data->vsync_timeline == NULL) {
+ pr_err("cannot vsync create time line");
+ return -ENOMEM;
+ }
+ mfd->mdp_sync_pt_data.get_retire_fence = __vsync_retire_get_fence;
+
+ mdp5_data->vsync_retire_handler.vsync_handler =
+ __vsync_retire_handle_vsync;
+ mdp5_data->vsync_retire_handler.cmd_post_flush = false;
+ INIT_WORK(&mdp5_data->retire_work, __vsync_retire_work_handler);
+
+ return 0;
+}
+
int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
{
struct device *dev = mfd->fbi->dev;
@@ -2775,6 +2871,12 @@
pr_err("Error dfps sysfs creation ret=%d\n", rc);
goto init_fail;
}
+ } else if (mfd->panel_info->type == MIPI_CMD_PANEL) {
+ rc = __vsync_retire_setup(mfd);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("unable to create vsync timeline\n");
+ goto init_fail;
+ }
}
mfd->mdp_sync_pt_data.async_wait_fences = true;
rc = sysfs_create_link_nowarn(&dev->kobj,
diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c
index 4ade335..1b1f686 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pipe.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -32,6 +32,8 @@
static DEFINE_MUTEX(mdss_mdp_smp_lock);
static int mdss_mdp_pipe_free(struct mdss_mdp_pipe *pipe);
+static int mdss_mdp_smp_mmb_set(int client_id, unsigned long *smp);
+static void mdss_mdp_smp_mmb_free(unsigned long *smp, bool write);
static struct mdss_mdp_pipe *mdss_mdp_pipe_search_by_client_id(
struct mdss_data_type *mdata, int client_id);
@@ -58,8 +60,27 @@
else
n -= fixed_cnt;
- /* reserve more blocks if needed, but can't free mmb at this point */
- for (i = bitmap_weight(smp_map->allocated, SMP_MB_CNT); i < n; i++) {
+ i = bitmap_weight(smp_map->allocated, SMP_MB_CNT);
+
+ /*
+ * SMP programming is not double buffered. Fail the request,
+ * that calls for change in smp configuration (addition/removal
+ * of smp blocks), so that fallback solution happens.
+ */
+ if (i != 0 && n != i) {
+ pr_debug("Can't change mmb config, num_blks: %d alloc: %d\n",
+ n, i);
+ return 0;
+ }
+
+ /*
+ * Clear previous SMP reservations and reserve according to the
+ * latest configuration
+ */
+ mdss_mdp_smp_mmb_free(smp_map->reserved, false);
+
+ /* Reserve mmb blocks*/
+ for (; i < n; i++) {
if (bitmap_full(mdata->mmb_alloc_map, SMP_MB_CNT))
break;
@@ -249,9 +270,21 @@
}
}
- nlines = pipe->bwc_mode ? 1 : 2;
+ if (pipe->src_fmt->tile)
+ nlines = 8;
+ else
+ nlines = pipe->bwc_mode ? 1 : 2;
mutex_lock(&mdss_mdp_smp_lock);
+ for (i = (MAX_PLANES - 1); i >= ps.num_planes; i--) {
+ if (bitmap_weight(pipe->smp_map[i].allocated, SMP_MB_CNT)) {
+ pr_debug("Extra mmb identified for pnum=%d plane=%d\n",
+ pipe->num, i);
+ mutex_unlock(&mdss_mdp_smp_lock);
+ return -EAGAIN;
+ }
+ }
+
for (i = 0; i < ps.num_planes; i++) {
if (rot_mode) {
num_blks = 1;
@@ -768,25 +801,7 @@
return rc;
}
-void mdss_mdp_crop_rect(struct mdss_mdp_img_rect *src_rect,
- struct mdss_mdp_img_rect *dst_rect,
- const struct mdss_mdp_img_rect *sci_rect)
-{
- struct mdss_mdp_img_rect res;
- mdss_mdp_intersect_rect(&res, dst_rect, sci_rect);
- if (res.w && res.h) {
- if ((res.w != dst_rect->w) || (res.h != dst_rect->h)) {
- src_rect->x = src_rect->x + (res.x - dst_rect->x);
- src_rect->y = src_rect->y + (res.y - dst_rect->y);
- src_rect->w = res.w;
- src_rect->h = res.h;
- }
- *dst_rect = (struct mdss_mdp_img_rect)
- {(res.x - sci_rect->x), (res.y - sci_rect->y),
- res.w, res.h};
- }
-}
static int mdss_mdp_image_setup(struct mdss_mdp_pipe *pipe,
struct mdss_mdp_data *data)
@@ -889,6 +904,7 @@
u32 chroma_samp, unpack, src_format;
u32 secure = 0;
u32 opmode;
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
fmt = pipe->src_fmt;
@@ -919,6 +935,9 @@
(fmt->bits[C1_B_Cb] << 2) |
(fmt->bits[C0_G_Y] << 0);
+ if (fmt->tile)
+ src_format |= BIT(30);
+
if (pipe->flags & MDP_ROT_90)
src_format |= BIT(11); /* ROT90 */
@@ -938,6 +957,11 @@
if (pipe->scale.enable_pxl_ext)
opmode |= (1 << 31);
+ if (fmt->tile && mdata->highest_bank_bit) {
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_FETCH_CONFIG,
+ MDSS_MDP_FETCH_CONFIG_RESET_VALUE |
+ mdata->highest_bank_bit << 18);
+ }
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_FORMAT, src_format);
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN, unpack);
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_OP_MODE, opmode);
diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.c b/drivers/video/msm/mdss/mdss_mdp_rotator.c
index 588e217..91e6373 100644
--- a/drivers/video/msm/mdss/mdss_mdp_rotator.c
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.c
@@ -224,12 +224,6 @@
pipe->params_changed++;
rot->params_changed = 0;
- /*
- * Clear previous SMP reservations and reserve according
- * to the latest configuration
- */
- mdss_mdp_smp_unreserve(pipe);
-
ret = mdss_mdp_smp_reserve(pipe);
if (ret) {
pr_err("unable to mdss_mdp_smp_reserve rot data\n");
diff --git a/drivers/video/msm/mdss/mdss_mdp_util.c b/drivers/video/msm/mdss/mdss_mdp_util.c
index 79873b6..69506d4 100644
--- a/drivers/video/msm/mdss/mdss_mdp_util.c
+++ b/drivers/video/msm/mdss/mdss_mdp_util.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -257,6 +257,27 @@
else
*res_rect = (struct mdss_mdp_img_rect){l, t, (r-l), (b-t)};
}
+
+void mdss_mdp_crop_rect(struct mdss_mdp_img_rect *src_rect,
+ struct mdss_mdp_img_rect *dst_rect,
+ const struct mdss_mdp_img_rect *sci_rect)
+{
+ struct mdss_mdp_img_rect res;
+ mdss_mdp_intersect_rect(&res, dst_rect, sci_rect);
+
+ if (res.w && res.h) {
+ if ((res.w != dst_rect->w) || (res.h != dst_rect->h)) {
+ src_rect->x = src_rect->x + (res.x - dst_rect->x);
+ src_rect->y = src_rect->y + (res.y - dst_rect->y);
+ src_rect->w = res.w;
+ src_rect->h = res.h;
+ }
+ *dst_rect = (struct mdss_mdp_img_rect)
+ {(res.x - sci_rect->x), (res.y - sci_rect->y),
+ res.w, res.h};
+ }
+}
+
int mdss_mdp_get_rau_strides(u32 w, u32 h,
struct mdss_mdp_format_params *fmt,
struct mdss_mdp_plane_sizes *ps)
diff --git a/drivers/video/msm/mdss/mdss_mdp_wb.c b/drivers/video/msm/mdss/mdss_mdp_wb.c
index 761b85a..6eb4d6e 100644
--- a/drivers/video/msm/mdss/mdss_mdp_wb.c
+++ b/drivers/video/msm/mdss/mdss_mdp_wb.c
@@ -61,11 +61,14 @@
struct msmfb_data buf_info;
struct mdss_mdp_data buf_data;
int state;
+ bool user_alloc;
};
static DEFINE_MUTEX(mdss_mdp_wb_buf_lock);
static struct mdss_mdp_wb mdss_mdp_wb_info;
+static void mdss_mdp_wb_free_node(struct mdss_mdp_wb_data *node);
+
#ifdef DEBUG_WRITEBACK
/* for debugging: writeback output buffer to allocated memory */
static inline
@@ -285,6 +288,7 @@
struct mdss_mdp_wb_data *node, *temp;
list_for_each_entry_safe(node, temp, &wb->register_queue,
registered_entry) {
+ mdss_mdp_wb_free_node(node);
list_del(&node->registered_entry);
kfree(node);
}
@@ -401,12 +405,24 @@
struct mdss_mdp_img_data *buf;
int ret;
+ if (!list_empty(&wb->register_queue)) {
+ list_for_each_entry(node, &wb->register_queue, registered_entry)
+ if ((node->buf_info.memory_id == data->memory_id) &&
+ (node->buf_info.offset == data->offset)) {
+ pr_debug("found node fd=%x off=%x addr=%x\n",
+ data->memory_id, data->offset,
+ node->buf_data.p[0].addr);
+ return node;
+ }
+ }
+
node = kzalloc(sizeof(struct mdss_mdp_wb_data), GFP_KERNEL);
if (node == NULL) {
pr_err("out of memory\n");
return NULL;
}
+ node->user_alloc = true;
node->buf_data.num_planes = 1;
buf = &node->buf_data.p[0];
if (wb->is_secure)
@@ -434,6 +450,22 @@
return NULL;
}
+static void mdss_mdp_wb_free_node(struct mdss_mdp_wb_data *node)
+{
+ struct mdss_mdp_img_data *buf;
+
+ if (node->user_alloc) {
+ buf = &node->buf_data.p[0];
+ pr_debug("free user node mem_id=%d offset=%u addr=0x%x\n",
+ node->buf_info.memory_id,
+ node->buf_info.offset,
+ buf->addr);
+
+ mdss_mdp_put_img(&node->buf_data.p[0]);
+ node->user_alloc = false;
+ }
+}
+
static int mdss_mdp_wb_queue(struct msm_fb_data_type *mfd,
struct msmfb_data *data, int local)
{
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index b7f9b86..f36ee04 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -984,6 +984,7 @@
#define MDP_MAX_FENCE_FD 32
#define MDP_BUF_SYNC_FLAG_WAIT 1
+#define MDP_BUF_SYNC_FLAG_RETIRE_FENCE 0x10
struct mdp_buf_sync {
uint32_t flags;
@@ -991,6 +992,7 @@
uint32_t session_id;
int *acq_fen_fd;
int *rel_fen_fd;
+ int *retire_fen_fd;
};
struct mdp_async_blit_req_list {
@@ -1000,19 +1002,11 @@
};
#define MDP_DISPLAY_COMMIT_OVERLAY 1
-struct mdp_buf_fence {
- uint32_t flags;
- uint32_t acq_fen_fd_cnt;
- int acq_fen_fd[MDP_MAX_FENCE_FD];
- int rel_fen_fd[MDP_MAX_FENCE_FD];
-};
-
struct mdp_display_commit {
uint32_t flags;
uint32_t wait_for_finish;
struct fb_var_screeninfo var;
- struct mdp_buf_fence buf_fence;
struct mdp_rect roi;
};
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 6a68b72..7be6cd2 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -1380,6 +1380,28 @@
* allowed to be used with the first @NL80211_CMD_SET_STATION command to
* update a TDLS peer STA entry.
*
+ * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information.
+ *
+ * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's
+ * until the channel switch event.
+ * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission
+ * must be blocked on the current channel (before the channel switch
+ * operation).
+ * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
+ * for the time while performing a channel switch.
+ * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter
+ * field in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
+ * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter
+ * field in the probe response (%NL80211_ATTR_PROBE_RESP).
+ *
+ * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
+ * As specified in the &enum nl80211_rxmgmt_flags.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported
+ * supported operating classes.
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1680,6 +1702,19 @@
NL80211_ATTR_PEER_AID,
+ NL80211_ATTR_COALESCE_RULE,
+
+ NL80211_ATTR_CH_SWITCH_COUNT,
+ NL80211_ATTR_CH_SWITCH_BLOCK_TX,
+ NL80211_ATTR_CSA_IES,
+ NL80211_ATTR_CSA_C_OFF_BEACON,
+ NL80211_ATTR_CSA_C_OFF_PRESP,
+
+ NL80211_ATTR_RXMGMT_FLAGS,
+
+ NL80211_ATTR_STA_SUPPORTED_CHANNELS,
+
+ NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 045a58b..862a0cc 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -2,7 +2,7 @@
*
* Copyright (C) 2008 Google, Inc.
* Author: Brian Swetland <swetland@google.com>
- * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2014, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -346,6 +346,7 @@
* @chg_check_timer: The timer used to implement the workaround to detect
* very slow plug in of wall charger.
* @ui_enabled: USB Intterupt is enabled or disabled.
+ * @pm_done: Indicates whether USB is PM resumed
*/
struct msm_otg {
struct usb_phy phy;
@@ -462,6 +463,7 @@
bool ext_chg_active;
struct completion ext_chg_wait;
int ui_enabled;
+ bool pm_done;
};
struct ci13xxx_platform_data {
diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h
index c0df1d7..38d3aab 100644
--- a/include/media/msm_cam_sensor.h
+++ b/include/media/msm_cam_sensor.h
@@ -47,7 +47,6 @@
#define MAX_EEPROM_NAME 32
#define MAX_AF_ITERATIONS 3
-#define MAX_NUMBER_OF_STEPS 47
enum flash_type {
LED_FLASH = 1,
@@ -441,7 +440,6 @@
CFG_GET_ACTUATOR_INFO,
CFG_SET_ACTUATOR_INFO,
CFG_SET_DEFAULT_FOCUS,
- CFG_SET_POSITION,
CFG_MOVE_FOCUS,
};
@@ -540,13 +538,6 @@
ACTUATOR_WEB_CAM_2,
};
-
-struct msm_actuator_set_position_t {
- uint16_t number_of_steps;
- uint16_t pos[MAX_NUMBER_OF_STEPS];
- uint16_t delay[MAX_NUMBER_OF_STEPS];
-};
-
struct msm_actuator_cfg_data {
int cfgtype;
uint8_t is_af_supported;
@@ -554,7 +545,6 @@
struct msm_actuator_move_params_t move;
struct msm_actuator_set_info_t set_info;
struct msm_actuator_get_info_t get_info;
- struct msm_actuator_set_position_t setpos;
enum af_camera_name cam_name;
} cfg;
};
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 7932ba1..77084ed 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -524,6 +524,10 @@
* @capability: station capability
* @ext_capab: extended capabilities of the station
* @ext_capab_len: number of extended capabilities
+ * @supported_channels: supported channels in IEEE 802.11 format
+ * @supported_channels_len: number of supported channels
+ * @supported_oper_classes: supported oper classes in IEEE 802.11 format
+ * @supported_oper_classes_len: number of supported operating classes
*/
struct station_parameters {
u8 *supported_rates;
@@ -542,6 +546,10 @@
u16 capability;
u8 *ext_capab;
u8 ext_capab_len;
+ const u8 *supported_channels;
+ u8 supported_channels_len;
+ const u8 *supported_oper_classes;
+ u8 supported_oper_classes_len;
};
/**
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index 35b86ff..0e44fa8 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -14,14 +14,6 @@
(57240 - 63720 @ 2160), (N/A, 0)
-country AD:
- (2402 - 2482 @ 40), (N/A, 20)
- (5170 - 5250 @ 80), (N/A, 20)
- (5250 - 5330 @ 80), (N/A, 20), DFS
- (5490 - 5710 @ 80), (N/A, 27), DFS
- # 60 gHz band channels 1-4, ref: Etsi En 302 567
- (57240 - 65880 @ 2160), (N/A, 40), NO-OUTDOOR
-
country AE:
(2402 - 2482 @ 40), (N/A, 20)
(5170 - 5250 @ 80), (3, 17)
@@ -126,11 +118,6 @@
(5250 - 5330 @ 20), (N/A, 20), DFS
(5735 - 5835 @ 20), (N/A, 20)
-country BL:
- (2402 - 2482 @ 40), (N/A, 20)
- (5170 - 5250 @ 40), (N/A, 18)
- (5250 - 5330 @ 40), (N/A, 18), DFS
-
country BM:
(2402 - 2472 @ 40), (N/A, 30)
(5150 - 5250 @ 80), (6, 17)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 7db02f3..a251231 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -223,6 +223,8 @@
[NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN },
[NL80211_ATTR_PEER_AID] = { .type = NLA_U16 },
+ [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
+ [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
};
/* policy for the key attributes */
@@ -2774,6 +2776,41 @@
return ERR_PTR(ret);
}
+static int nl80211_parse_sta_channel_info(struct genl_info *info,
+ struct station_parameters *params)
+{
+ if (info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]) {
+ params->supported_channels =
+ nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+ params->supported_channels_len =
+ nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+ /*
+ * Need to include at least one (first channel, number of
+ * channels) tuple for each subband, and must have proper
+ * tuples for the rest of the data as well.
+ */
+ if (params->supported_channels_len < 2)
+ return -EINVAL;
+ if (params->supported_channels_len % 2)
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]) {
+ params->supported_oper_classes =
+ nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+ params->supported_oper_classes_len =
+ nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+ /*
+ * The value of the Length field of the Supported Operating
+ * Classes element is between 2 and 253.
+ */
+ if (params->supported_oper_classes_len < 2 ||
+ params->supported_oper_classes_len > 253)
+ return -EINVAL;
+ }
+ return 0;
+}
+
static struct nla_policy
nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
[NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
@@ -2797,6 +2834,10 @@
params->vht_capa =
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
+ err = nl80211_parse_sta_channel_info(info, params);
+ if (err)
+ return err;
+
/* parse WME attributes if present */
if (!info->attrs[NL80211_ATTR_STA_WME])
return 0;
@@ -3063,6 +3104,10 @@
params.plink_action =
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
+ err = nl80211_parse_sta_channel_info(info, ¶ms);
+ if (err)
+ return err;
+
if (!rdev->ops->add_station)
return -EOPNOTSUPP;
diff --git a/sound/soc/msm/qdsp6v2/audio_ocmem.c b/sound/soc/msm/qdsp6v2/audio_ocmem.c
index 3fe8033..5be880d 100644
--- a/sound/soc/msm/qdsp6v2/audio_ocmem.c
+++ b/sound/soc/msm/qdsp6v2/audio_ocmem.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -79,7 +79,7 @@
#define clear_bit_pos(x, y) (atomic_set(&x, (atomic_read(&x) & (~(1 << y)))))
#define test_bit_pos(x, y) ((atomic_read(&x)) & (1 << y))
-static int enable_ocmem_audio_voice;
+static int enable_ocmem_audio_voice = 1;
module_param(enable_ocmem_audio_voice, int,
S_IRUGO | S_IWUSR | S_IWGRP);
MODULE_PARM_DESC(enable_ocmem_audio_voice, "control OCMEM usage for audio/voice");
diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
index 6394f0b..d2352ff 100644
--- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
@@ -49,8 +49,13 @@
#define PARTIAL_DRAIN_ACK_EARLY_BY_MSEC 150
#define MP3_OUTPUT_FRAME_SZ 1152
#define AAC_OUTPUT_FRAME_SZ 1024
+#define AC3_OUTPUT_FRAME_SZ 1536
+#define EAC3_OUTPUT_FRAME_SZ 1536
#define DSP_NUM_OUTPUT_FRAME_BUFFERED 2
+/* decoder parameter length */
+#define DDP_DEC_MAX_NUM_PARAM 18
+
/* Default values used if user space does not set */
#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024)
#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024)
@@ -76,6 +81,7 @@
uint32_t volume[MSM_FRONTEND_DAI_MAX][2]; /* For both L & R */
struct msm_compr_audio_effects *audio_effects[MSM_FRONTEND_DAI_MAX];
bool use_dsp_gapless_mode;
+ struct msm_compr_dec_params *dec_params[MSM_FRONTEND_DAI_MAX];
};
struct msm_compr_audio {
@@ -105,6 +111,8 @@
uint32_t cmd_ack;
uint32_t cmd_interrupt;
uint32_t drain_ready;
+ uint32_t stream_available;
+ uint32_t next_stream;
struct msm_compr_gapless_state gapless_state;
@@ -120,6 +128,7 @@
wait_queue_head_t drain_wait;
wait_queue_head_t flush_wait;
wait_queue_head_t close_wait;
+ wait_queue_head_t wait_for_stream_avail;
spinlock_t lock;
};
@@ -131,6 +140,10 @@
struct eq_params equalizer;
};
+struct msm_compr_dec_params {
+ struct snd_dec_ddp ddp_params;
+};
+
static int msm_compr_set_volume(struct snd_compr_stream *cstream,
uint32_t volume_l, uint32_t volume_r)
{
@@ -158,6 +171,23 @@
return rc;
}
+static int msm_compr_send_ddp_cfg(struct audio_client *ac,
+ struct snd_dec_ddp *ddp)
+{
+ int i, rc;
+ pr_debug("%s\n", __func__);
+ for (i = 0; i < ddp->params_length; i++) {
+ rc = q6asm_ds1_set_endp_params(ac, ddp->params_id[i],
+ ddp->params_value[i]);
+ if (rc) {
+ pr_err("sending params_id: %d failed\n",
+ ddp->params_id[i]);
+ return rc;
+ }
+ }
+ return 0;
+}
+
static int msm_compr_send_buffer(struct msm_compr_audio *prtd)
{
int buffer_length;
@@ -302,6 +332,8 @@
prtd->gapless_state.stream_opened[stream_id] = 0;
prtd->gapless_state.set_next_stream_id = false;
}
+ if (prtd->gapless_state.gapless_transition)
+ prtd->gapless_state.gapless_transition = 0;
spin_unlock(&prtd->lock);
break;
case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
@@ -346,6 +378,17 @@
break;
case ASM_STREAM_CMD_CLOSE:
pr_debug("ASM_DATA_CMD_CLOSE\n");
+ /*
+ * wakeup wait for stream avail on stream 3
+ * after stream 1 ends.
+ */
+ if (prtd->next_stream) {
+ pr_debug("%s:CLOSE:wakeup wait for stream\n",
+ __func__);
+ prtd->stream_available = 1;
+ wake_up(&prtd->wait_for_stream_avail);
+ prtd->next_stream = 0;
+ }
if (atomic_read(&prtd->close) &&
atomic_read(&prtd->wait_on_close)) {
prtd->cmd_ack = 1;
@@ -541,11 +584,20 @@
kfree(prtd);
return -ENOMEM;
}
+ pdata->dec_params[rtd->dai_link->be_id] =
+ kzalloc(sizeof(struct msm_compr_dec_params), GFP_KERNEL);
+ if (!pdata->dec_params[rtd->dai_link->be_id]) {
+ pr_err("%s: Could not allocate memory for dec params\n",
+ __func__);
+ kfree(prtd);
+ return -ENOMEM;
+ }
prtd->audio_client = q6asm_audio_client_alloc(
(app_cb)compr_event_handler, prtd);
if (!prtd->audio_client) {
pr_err("%s: Could not allocate memory for client\n", __func__);
kfree(pdata->audio_effects[rtd->dai_link->be_id]);
+ kfree(pdata->dec_params[rtd->dai_link->be_id]);
kfree(prtd);
return -ENOMEM;
}
@@ -563,6 +615,7 @@
prtd->last_buffer = 0;
prtd->first_buffer = 1;
prtd->partial_drain_delay = 0;
+ prtd->next_stream = 0;
memset(&prtd->gapless_state, 0, sizeof(struct msm_compr_gapless_state));
/*
* Update the use_dsp_gapless_mode from gapless struture with the value
@@ -586,6 +639,7 @@
init_waitqueue_head(&prtd->drain_wait);
init_waitqueue_head(&prtd->flush_wait);
init_waitqueue_head(&prtd->close_wait);
+ init_waitqueue_head(&prtd->wait_for_stream_avail);
runtime->private_data = prtd;
populate_codec_list(prtd);
@@ -616,19 +670,6 @@
unsigned long flags;
pr_debug("%s\n", __func__);
- pdata->cstream[soc_prtd->dai_link->be_id] = NULL;
- if (cstream->direction == SND_COMPRESS_PLAYBACK) {
- if (atomic_read(&pdata->audio_ocmem_req) > 1)
- atomic_dec(&pdata->audio_ocmem_req);
- else if (atomic_cmpxchg(&pdata->audio_ocmem_req, 1, 0))
- audio_ocmem_process_req(AUDIO, false);
-
- msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
- SNDRV_PCM_STREAM_PLAYBACK);
- }
-
- pr_debug("%s: ocmem_req: %d\n", __func__,
- atomic_read(&pdata->audio_ocmem_req));
if (atomic_read(&prtd->eos)) {
ret = wait_event_timeout(prtd->eos_wait,
@@ -649,16 +690,30 @@
stream_id = ac->stream_id;
if (prtd->gapless_state.stream_opened[stream_id^1]) {
spin_unlock_irqrestore(&prtd->lock, flags);
+ pr_debug(" close stream %d", stream_id^1);
q6asm_stream_cmd(ac, CMD_CLOSE, stream_id^1);
spin_lock_irqsave(&prtd->lock, flags);
}
if (prtd->gapless_state.stream_opened[stream_id]) {
spin_unlock_irqrestore(&prtd->lock, flags);
+ pr_debug("close stream %d", stream_id);
q6asm_stream_cmd(ac, CMD_CLOSE, stream_id);
spin_lock_irqsave(&prtd->lock, flags);
}
spin_unlock_irqrestore(&prtd->lock, flags);
+ pdata->cstream[soc_prtd->dai_link->be_id] = NULL;
+ if (cstream->direction == SND_COMPRESS_PLAYBACK) {
+ if (atomic_read(&pdata->audio_ocmem_req) > 1)
+ atomic_dec(&pdata->audio_ocmem_req);
+ else if (atomic_cmpxchg(&pdata->audio_ocmem_req, 1, 0))
+ audio_ocmem_process_req(AUDIO, false);
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ }
+
+ pr_debug("%s: ocmem_req: %d\n", __func__,
+ atomic_read(&pdata->audio_ocmem_req));
/* client buf alloc was with stream id 0, so free with the same */
ac->stream_id = 0;
q6asm_audio_client_buf_free_contiguous(dir, ac);
@@ -666,6 +721,7 @@
q6asm_audio_client_free(ac);
kfree(pdata->audio_effects[soc_prtd->dai_link->be_id]);
+ kfree(pdata->dec_params[soc_prtd->dai_link->be_id]);
kfree(prtd);
return 0;
@@ -730,11 +786,13 @@
case SND_AUDIOCODEC_AC3: {
prtd->codec = FORMAT_AC3;
+ frame_sz = AC3_OUTPUT_FRAME_SZ;
break;
}
case SND_AUDIOCODEC_EAC3: {
prtd->codec = FORMAT_EAC3;
+ frame_sz = EAC3_OUTPUT_FRAME_SZ;
break;
}
@@ -777,6 +835,45 @@
return rc;
}
+static int msm_compr_wait_for_stream_avail(struct msm_compr_audio *prtd,
+ unsigned long *flags)
+{
+ int rc = 0;
+ pr_debug("next session is already in opened state\n");
+ prtd->next_stream = 1;
+ prtd->cmd_interrupt = 0;
+ spin_unlock_irqrestore(&prtd->lock, *flags);
+ /*
+ * Wait for stream to be available, or the wait to be interrupted by
+ * commands like flush or till a timeout of one second.
+ */
+ rc = wait_event_timeout(prtd->wait_for_stream_avail,
+ prtd->stream_available || prtd->cmd_interrupt, 1 * HZ);
+ pr_err("%s:prtd->stream_available %d, prtd->cmd_interrupt %d rc %d\n",
+ __func__, prtd->stream_available, prtd->cmd_interrupt, rc);
+
+ spin_lock_irqsave(&prtd->lock, *flags);
+ if (rc == 0) {
+ pr_err("%s: wait_for_stream_avail timed out\n",
+ __func__);
+ rc = -ETIMEDOUT;
+ } else if (prtd->cmd_interrupt == 1) {
+ /*
+ * This scenario might not happen as we do not allow
+ * flush in transition state.
+ */
+ pr_debug("%s: wait_for_stream_avail interrupted\n", __func__);
+ prtd->cmd_interrupt = 0;
+ prtd->stream_available = 0;
+ rc = -EINTR;
+ } else {
+ prtd->stream_available = 0;
+ rc = 0;
+ }
+ pr_debug("%s : rc = %d", __func__, rc);
+ return rc;
+}
+
static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
{
struct snd_compr_runtime *runtime = cstream->runtime;
@@ -821,6 +918,13 @@
prtd->gapless_state.gapless_transition);
stream_id = ac->stream_id;
atomic_set(&prtd->start, 0);
+ if (prtd->next_stream) {
+ pr_debug("%s: interrupt next track wait queues\n",
+ __func__);
+ prtd->cmd_interrupt = 1;
+ wake_up(&prtd->wait_for_stream_avail);
+ prtd->next_stream = 0;
+ }
if (atomic_read(&prtd->eos)) {
pr_debug("%s: interrupt eos wait queues", __func__);
prtd->cmd_interrupt = 1;
@@ -1078,12 +1182,43 @@
spin_lock_irqsave(&prtd->lock, flags);
rc = 0;
stream_id = ac->stream_id^1; /*next stream in gapless*/
+ /*
+ * Wait if stream 1 has not completed before honoring next
+ * track for stream 3. Scenario happens if second clip is
+ * small and fills in one buffer so next track will be
+ * called immediately.
+ */
if (prtd->gapless_state.stream_opened[stream_id]) {
- pr_debug("next session is already in opened state\n");
- spin_unlock_irqrestore(&prtd->lock, flags);
- break;
+ if (prtd->gapless_state.gapless_transition) {
+ rc = msm_compr_wait_for_stream_avail(prtd,
+ &flags);
+ } else {
+ /*
+ * If session is already opened break out if
+ * the state is not gapless transition. This
+ * is when seek happens after the last buffer
+ * is sent to the driver. Next track would be
+ * called again after last buffer is sent.
+ */
+ pr_debug("next session is in opened state\n");
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ break;
+ }
}
spin_unlock_irqrestore(&prtd->lock, flags);
+ if (rc < 0) {
+ /*
+ * if return type EINTR then reset to zero. Tiny
+ * compress treats EINTR as error and prevents PARTIAL
+ * DRAIN. EINTR is not an error. wait for stream avail
+ * is interrupted by some other command like FLUSH.
+ */
+ if (rc == -EINTR) {
+ pr_debug("%s: EINTR reset rc to 0\n", __func__);
+ rc = 0;
+ }
+ break;
+ }
rc = q6asm_stream_open_write_v2(prtd->audio_client,
prtd->codec, 16,
stream_id,
@@ -1251,8 +1386,6 @@
* since the available bytes fits fragment_size, copy the data right away
*/
spin_lock_irqsave(&prtd->lock, flags);
- if (prtd->gapless_state.gapless_transition)
- prtd->gapless_state.gapless_transition = 0;
prtd->bytes_received += count;
if (atomic_read(&prtd->start)) {
if (atomic_read(&prtd->xrun)) {
@@ -1474,6 +1607,74 @@
return 0;
}
+static int msm_compr_dec_params_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+ unsigned long fe_id = kcontrol->private_value;
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_platform_get_drvdata(platform);
+ struct msm_compr_dec_params *dec_params = NULL;
+ struct snd_compr_stream *cstream = NULL;
+ struct msm_compr_audio *prtd = NULL;
+ long *values = &(ucontrol->value.integer.value[0]);
+
+ pr_debug("%s\n", __func__);
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received out of bounds fe_id %lu\n",
+ __func__, fe_id);
+ return -EINVAL;
+ }
+
+ cstream = pdata->cstream[fe_id];
+ dec_params = pdata->dec_params[fe_id];
+
+ if (!cstream || !dec_params) {
+ pr_err("%s: stream or dec_params inactive\n", __func__);
+ return -EINVAL;
+ }
+ prtd = cstream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s: cannot set dec_params\n", __func__);
+ return -EINVAL;
+ }
+ switch (prtd->codec) {
+ case FORMAT_MP3:
+ case FORMAT_MPEG4_AAC:
+ pr_debug("%s: no runtime parameters for codec: %d\n", __func__,
+ prtd->codec);
+ break;
+ case FORMAT_AC3:
+ case FORMAT_EAC3: {
+ struct snd_dec_ddp *ddp = &dec_params->ddp_params;
+ int cnt;
+ ddp->params_length = (*values++);
+ if (ddp->params_length > DDP_DEC_MAX_NUM_PARAM) {
+ pr_err("%s: invalid num of params:: %d\n", __func__,
+ ddp->params_length);
+ return -EINVAL;
+ }
+ for (cnt = 0; cnt < ddp->params_length; cnt++) {
+ ddp->params_id[cnt] = *values++;
+ ddp->params_value[cnt] = *values++;
+ }
+ if (msm_compr_send_ddp_cfg(prtd->audio_client, ddp) < 0)
+ pr_err("%s: DDP CMD CFG failed\n", __func__);
+ break;
+ }
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int msm_compr_dec_params_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /* dummy function */
+ return 0;
+}
+
static int msm_compr_probe(struct snd_soc_platform *platform)
{
struct msm_compr_pdata *pdata;
@@ -1493,6 +1694,7 @@
pdata->volume[i][0] = COMPRESSED_LR_VOL_MAX_STEPS;
pdata->volume[i][1] = COMPRESSED_LR_VOL_MAX_STEPS;
pdata->audio_effects[i] = NULL;
+ pdata->dec_params[i] = NULL;
pdata->cstream[i] = NULL;
}
@@ -1526,6 +1728,16 @@
return 0;
}
+static int msm_compr_dec_params_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 128;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xFFFFFFFF;
+ return 0;
+}
+
static int msm_compr_add_volume_control(struct snd_soc_pcm_runtime *rtd)
{
const char *mixer_ctl_name = "Compress Playback";
@@ -1652,6 +1864,57 @@
msm_compr_gapless_put),
};
+static int msm_compr_add_dec_runtime_params_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = "Audio Stream";
+ const char *deviceNo = "NN";
+ const char *suffix = "Dec Params";
+ char *mixer_str = NULL;
+ int ctl_len;
+ struct snd_kcontrol_new fe_dec_params_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_compr_dec_params_info,
+ .get = msm_compr_dec_params_get,
+ .put = msm_compr_dec_params_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ return 0;
+ }
+
+ pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n",
+ __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+ rtd->dai_link->cpu_dai_name, rtd->pcm->device);
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 +
+ strlen(suffix) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_KERNEL);
+
+ if (!mixer_str) {
+ pr_err("failed to allocate mixer ctrl str of len %d", ctl_len);
+ return 0;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d %s", mixer_ctl_name,
+ rtd->pcm->device, suffix);
+
+ fe_dec_params_control[0].name = mixer_str;
+ fe_dec_params_control[0].private_value = rtd->dai_link->be_id;
+ pr_debug("Registering new mixer ctl %s", mixer_str);
+ snd_soc_add_platform_controls(rtd->platform,
+ fe_dec_params_control,
+ ARRAY_SIZE(fe_dec_params_control));
+ kfree(mixer_str);
+ return 0;
+}
+
static int msm_compr_new(struct snd_soc_pcm_runtime *rtd)
{
int rc;
@@ -1663,6 +1926,10 @@
if (rc)
pr_err("%s: Could not add Compr Audio Effects Control\n",
__func__);
+ rc = msm_compr_add_dec_runtime_params_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr Dec runtime params Control\n",
+ __func__);
return 0;
}
diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/sound/soc/msm/qdsp6v2/q6voice.c
index c4395c2..0c7472f 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.c
+++ b/sound/soc/msm/qdsp6v2/q6voice.c
@@ -29,7 +29,7 @@
#include "q6voice.h"
-#define TIMEOUT_MS 200
+#define TIMEOUT_MS 300
#define CMD_STATUS_SUCCESS 0