Merge "msm: camera: enable gc0339 driver for msm8x12 qrd board"
diff --git a/Documentation/devicetree/bindings/coresight/coresight.txt b/Documentation/devicetree/bindings/coresight/coresight.txt
index ce797d3..8d1d46d 100644
--- a/Documentation/devicetree/bindings/coresight/coresight.txt
+++ b/Documentation/devicetree/bindings/coresight/coresight.txt
@@ -226,8 +226,9 @@
compatible = "qcom,coresight-hwevent";
reg = <0xfdf30018 0x80>,
<0xf9011080 0x80>,
- <0xfd4ab160 0x80>;
- reg-names = "mmss-mux", "apcs-mux", "ppss-mux";
+ <0xfd4ab160 0x80>,
+ <0xfc401600 0x80>;
+ reg-names = "mmss-mux", "apcs-mux", "ppss-mux", "gcc-mux";
coresight-id = <29>;
coresight-name = "coresight-hwevent";
diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt
index 4e16ba4..a336287 100644
--- a/Documentation/devicetree/bindings/gpio/gpio.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio.txt
@@ -75,4 +75,40 @@
gpio-controller;
};
+2.1) gpio-controller and pinctrl subsystem
+------------------------------------------
+gpio-controller on a SOC might be tightly coupled with the pinctrl
+subsystem, in the sense that the pins can be used by other functions
+together with optional gpio feature.
+
+While the pin allocation is totally managed by the pin ctrl subsystem,
+gpio (under gpiolib) is still maintained by gpio drivers. It may happen
+that different pin ranges in a SoC is managed by different gpio drivers.
+
+This makes it logical to let gpio drivers announce their pin ranges to
+the pin ctrl subsystem and call 'pinctrl_request_gpio' in order to
+request the corresponding pin before any gpio usage.
+
+For this, the gpio controller can use a pinctrl phandle and pins to
+announce the pinrange to the pin ctrl subsystem. For example,
+
+ qe_pio_e: gpio-controller@1460 {
+ #gpio-cells = <2>;
+ compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
+ reg = <0x1460 0x18>;
+ gpio-controller;
+ gpio-ranges = <&pinctrl1 20 10>, <&pinctrl2 50 20>;
+
+ }
+
+where,
+ &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.
+
+ Next values specify the base pin and number of pins for the range
+ handled by 'qe_pio_e' gpio. In the given example from base pin 20 to
+ pin 29 under pinctrl1 and pin 50 to pin 69 under pinctrl2 is handled
+ by this gpio controller.
+
+The pinctrl node must have "#gpio-range-cells" property to show number of
+arguments to pass with phandle from gpio controllers node.
diff --git a/Documentation/devicetree/bindings/media/video/msm-cci.txt b/Documentation/devicetree/bindings/media/video/msm-cci.txt
index a2503cd..c01193c 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cci.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cci.txt
@@ -37,8 +37,10 @@
MSM sensor node contains properties of camera sensor
Required properties:
-- compatible : should be "qcom" followed by sensor name
+- compatible : should be manufacturer name followed by sensor name
- "qcom,s5k3l1yx"
+ - "shinetech,gc0339"
+ - "shinetech,hi256"
- reg : should contain i2c slave address of the device
- qcom,slave-id : should contain i2c slave address, device id address
and expected id read value
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 8fa9f4a..0ebbe63 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -40,6 +40,7 @@
samsung Samsung Semiconductor
sbs Smart Battery System
schindler Schindler
+shinetech Shine Tech Corporation, Ltd.
sil Silicon Image
simtek
sirf SiRF Technology, Inc.
diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
index 2a596a4..ef4fa7b 100644
--- a/Documentation/driver-model/devres.txt
+++ b/Documentation/driver-model/devres.txt
@@ -276,3 +276,7 @@
devm_regulator_get()
devm_regulator_put()
devm_regulator_bulk_get()
+
+PINCTRL
+ devm_pinctrl_get()
+ devm_pinctrl_put()
diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt
index 620a078..1d5b334 100644
--- a/Documentation/gpio.txt
+++ b/Documentation/gpio.txt
@@ -436,6 +436,48 @@
signaling rate accordingly.
+GPIO controllers and the pinctrl subsystem
+------------------------------------------
+
+A GPIO controller on a SOC might be tightly coupled with the pinctrl
+subsystem, in the sense that the pins can be used by other functions
+together with an optional gpio feature. We have already covered the
+case where e.g. a GPIO controller need to reserve a pin or set the
+direction of a pin by calling any of:
+
+pinctrl_request_gpio()
+pinctrl_free_gpio()
+pinctrl_gpio_direction_input()
+pinctrl_gpio_direction_output()
+
+But how does the pin control subsystem cross-correlate the GPIO
+numbers (which are a global business) to a certain pin on a certain
+pin controller?
+
+This is done by registering "ranges" of pins, which are essentially
+cross-reference tables. These are described in
+Documentation/pinctrl.txt
+
+While the pin allocation is totally managed by the pinctrl subsystem,
+gpio (under gpiolib) is still maintained by gpio drivers. It may happen
+that different pin ranges in a SoC is managed by different gpio drivers.
+
+This makes it logical to let gpio drivers announce their pin ranges to
+the pin ctrl subsystem before it will call 'pinctrl_request_gpio' in order
+to request the corresponding pin to be prepared by the pinctrl subsystem
+before any gpio usage.
+
+For this, the gpio controller can register its pin range with pinctrl
+subsystem. There are two ways of doing it currently: with or without DT.
+
+For with DT support refer to Documentation/devicetree/bindings/gpio/gpio.txt.
+
+For non-DT support, user can call gpiochip_add_pin_range() with appropriate
+parameters to register a range of gpio pins with a pinctrl driver. For this
+exact name string of pinctrl device has to be passed as one of the
+argument to this routine.
+
+
What do these conventions omit?
===============================
One of the biggest things these conventions omit is pin multiplexing, since
diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt
index d97bccf..e454a6a 100644
--- a/Documentation/pinctrl.txt
+++ b/Documentation/pinctrl.txt
@@ -152,11 +152,9 @@
};
-static int foo_list_groups(struct pinctrl_dev *pctldev, unsigned selector)
+static int foo_get_groups_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(foo_groups))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(foo_groups);
}
static const char *foo_get_group_name(struct pinctrl_dev *pctldev,
@@ -175,7 +173,7 @@
}
static struct pinctrl_ops foo_pctrl_ops = {
- .list_groups = foo_list_groups,
+ .get_groups_count = foo_get_groups_count,
.get_group_name = foo_get_group_name,
.get_group_pins = foo_get_group_pins,
};
@@ -186,13 +184,12 @@
.pctlops = &foo_pctrl_ops,
};
-The pin control subsystem will call the .list_groups() function repeatedly
-beginning on 0 until it returns non-zero to determine legal selectors, then
-it will call the other functions to retrieve the name and pins of the group.
-Maintaining the data structure of the groups is up to the driver, this is
-just a simple example - in practice you may need more entries in your group
-structure, for example specific register ranges associated with each group
-and so on.
+The pin control subsystem will call the .get_groups_count() function to
+determine total number of legal selectors, then it will call the other functions
+to retrieve the name and pins of the group. Maintaining the data structure of
+the groups is up to the driver, this is just a simple example - in practice you
+may need more entries in your group structure, for example specific register
+ranges associated with each group and so on.
Pin configuration
@@ -362,6 +359,10 @@
the range ID value, so that the pin controller knows which range it should
deal with.
+Calling pinctrl_add_gpio_range from pinctrl driver is DEPRECATED. Please see
+section 2.1 of Documentation/devicetree/bindings/gpio/gpio.txt on how to bind
+pinctrl and gpio drivers.
+
PINMUX interfaces
=================
@@ -606,11 +607,9 @@
};
-static int foo_list_groups(struct pinctrl_dev *pctldev, unsigned selector)
+static int foo_get_groups_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(foo_groups))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(foo_groups);
}
static const char *foo_get_group_name(struct pinctrl_dev *pctldev,
@@ -629,7 +628,7 @@
}
static struct pinctrl_ops foo_pctrl_ops = {
- .list_groups = foo_list_groups,
+ .get_groups_count = foo_get_groups_count,
.get_group_name = foo_get_group_name,
.get_group_pins = foo_get_group_pins,
};
@@ -663,11 +662,9 @@
},
};
-int foo_list_funcs(struct pinctrl_dev *pctldev, unsigned selector)
+int foo_get_functions_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(foo_functions))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(foo_functions);
}
const char *foo_get_fname(struct pinctrl_dev *pctldev, unsigned selector)
@@ -703,7 +700,7 @@
}
struct pinmux_ops foo_pmxops = {
- .list_functions = foo_list_funcs,
+ .get_functions_count = foo_get_functions_count,
.get_function_name = foo_get_fname,
.get_function_groups = foo_get_groups,
.enable = foo_enable,
@@ -952,13 +949,13 @@
The result of grabbing this mapping from the device with something like
this (see next paragraph):
- p = pinctrl_get(dev);
+ p = devm_pinctrl_get(dev);
s = pinctrl_lookup_state(p, "8bit");
ret = pinctrl_select_state(p, s);
or more simply:
- p = pinctrl_get_select(dev, "8bit");
+ p = devm_pinctrl_get_select(dev, "8bit");
Will be that you activate all the three bottom records in the mapping at
once. Since they share the same name, pin controller device, function and
@@ -970,6 +967,18 @@
Pinmux requests from drivers
============================
+When a device driver is about to probe the device core will automatically
+attempt to issue pinctrl_get_select_default() on these devices.
+This way driver writers do not need to add any of the boilerplate code
+of the type found below. However when doing fine-grained state selection
+and not using the "default" state, you may have to do some device driver
+handling of the pinctrl handles and states.
+
+So if you just want to put the pins for a certain device into the default
+state and be done with it, there is nothing you need to do besides
+providing the proper mapping table. The device core will take care of
+the rest.
+
Generally it is discouraged to let individual drivers get and enable pin
control. So if possible, handle the pin control in platform code or some other
place where you have access to all the affected struct device * pointers. In
@@ -992,7 +1001,7 @@
/* Allocate a state holder named "foo" etc */
struct foo_state *foo = ...;
- foo->p = pinctrl_get(&device);
+ foo->p = devm_pinctrl_get(&device);
if (IS_ERR(foo->p)) {
/* FIXME: clean up "foo" here */
return PTR_ERR(foo->p);
@@ -1000,24 +1009,17 @@
foo->s = pinctrl_lookup_state(foo->p, PINCTRL_STATE_DEFAULT);
if (IS_ERR(foo->s)) {
- pinctrl_put(foo->p);
/* FIXME: clean up "foo" here */
return PTR_ERR(s);
}
ret = pinctrl_select_state(foo->s);
if (ret < 0) {
- pinctrl_put(foo->p);
/* FIXME: clean up "foo" here */
return ret;
}
}
-foo_remove()
-{
- pinctrl_put(state->p);
-}
-
This get/lookup/select/put sequence can just as well be handled by bus drivers
if you don't want each and every driver to handle it and you know the
arrangement on your bus.
@@ -1029,6 +1031,11 @@
kernel memory to hold the pinmux state. All mapping table parsing or similar
slow operations take place within this API.
+- devm_pinctrl_get() is a variant of pinctrl_get() that causes pinctrl_put()
+ to be called automatically on the retrieved pointer when the associated
+ device is removed. It is recommended to use this function over plain
+ pinctrl_get().
+
- pinctrl_lookup_state() is called in process context to obtain a handle to a
specific state for a the client device. This operation may be slow too.
@@ -1041,14 +1048,75 @@
- pinctrl_put() frees all information associated with a pinctrl handle.
+- devm_pinctrl_put() is a variant of pinctrl_put() that may be used to
+ explicitly destroy a pinctrl object returned by devm_pinctrl_get().
+ However, use of this function will be rare, due to the automatic cleanup
+ that will occur even without calling it.
+
+ pinctrl_get() must be paired with a plain pinctrl_put().
+ pinctrl_get() may not be paired with devm_pinctrl_put().
+ devm_pinctrl_get() can optionally be paired with devm_pinctrl_put().
+ devm_pinctrl_get() may not be paired with plain pinctrl_put().
+
Usually the pin control core handled the get/put pair and call out to the
device drivers bookkeeping operations, like checking available functions and
the associated pins, whereas the enable/disable pass on to the pin controller
driver which takes care of activating and/or deactivating the mux setting by
quickly poking some registers.
-The pins are allocated for your device when you issue the pinctrl_get() call,
-after this you should be able to see this in the debugfs listing of all pins.
+The pins are allocated for your device when you issue the devm_pinctrl_get()
+call, after this you should be able to see this in the debugfs listing of all
+pins.
+
+NOTE: the pinctrl system will return -EPROBE_DEFER if it cannot find the
+requested pinctrl handles, for example if the pinctrl driver has not yet
+registered. Thus make sure that the error path in your driver gracefully
+cleans up and is ready to retry the probing later in the startup process.
+
+
+Drivers needing both pin control and GPIOs
+==========================================
+
+Again, it is discouraged to let drivers lookup and select pin control states
+themselves, but again sometimes this is unavoidable.
+
+So say that your driver is fetching its resources like this:
+
+#include <linux/pinctrl/consumer.h>
+#include <linux/gpio.h>
+
+struct pinctrl *pinctrl;
+int gpio;
+
+pinctrl = devm_pinctrl_get_select_default(&dev);
+gpio = devm_gpio_request(&dev, 14, "foo");
+
+Here we first request a certain pin state and then request GPIO 14 to be
+used. If you're using the subsystems orthogonally like this, you should
+nominally always get your pinctrl handle and select the desired pinctrl
+state BEFORE requesting the GPIO. This is a semantic convention to avoid
+situations that can be electrically unpleasant, you will certainly want to
+mux in and bias pins in a certain way before the GPIO subsystems starts to
+deal with them.
+
+The above can be hidden: using the device core, the pinctrl core may be
+setting up the config and muxing for the pins right before the device is
+probing, nevertheless orthogonal to the GPIO subsystem.
+
+But there are also situations where it makes sense for the GPIO subsystem
+to communicate directly with with the pinctrl subsystem, using the latter
+as a back-end. This is when the GPIO driver may call out to the functions
+described in the section "Pin control interaction with the GPIO subsystem"
+above. This only involves per-pin multiplexing, and will be completely
+hidden behind the gpio_*() function namespace. In this case, the driver
+need not interact with the pin control subsystem at all.
+
+If a pin control driver and a GPIO driver is dealing with the same pins
+and the use cases involve multiplexing, you MUST implement the pin controller
+as a back-end for the GPIO driver like this, unless your hardware design
+is such that the GPIO controller can override the pin controller's
+multiplexing state through hardware without the need to interact with the
+pin control system.
System pin control hogging
@@ -1094,13 +1162,13 @@
#include <linux/pinctrl/consumer.h>
-foo_switch()
-{
- struct pinctrl *p;
- struct pinctrl_state *s1, *s2;
+struct pinctrl *p;
+struct pinctrl_state *s1, *s2;
+foo_probe()
+{
/* Setup */
- p = pinctrl_get(&device);
+ p = devm_pinctrl_get(&device);
if (IS_ERR(p))
...
@@ -1111,7 +1179,10 @@
s2 = pinctrl_lookup_state(foo->p, "pos-B");
if (IS_ERR(s2))
...
+}
+foo_switch()
+{
/* Enable on position A */
ret = pinctrl_select_state(s1);
if (ret < 0)
@@ -1125,8 +1196,8 @@
...
...
-
- pinctrl_put(p);
}
-The above has to be done from process context.
+The above has to be done from process context. The reservation of the pins
+will be done when the state is activated, so in effect one specific pin
+can be used by different functions at different times on a running system.
diff --git a/arch/arm/boot/dts/dsi-panel-hx8394a-720p-video.dtsi b/arch/arm/boot/dts/dsi-panel-hx8394a-720p-video.dtsi
new file mode 100644
index 0000000..25c1851
--- /dev/null
+++ b/arch/arm/boot/dts/dsi-panel-hx8394a-720p-video.dtsi
@@ -0,0 +1,107 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ qcom,mdss_dsi_hx8394a_720p_video {
+ compatible = "qcom,mdss-dsi-panel";
+ label = "hx8394a 720p video mode dsi panel";
+ status = "disable";
+ qcom,dsi-ctrl-phandle = <&mdss_dsi0>;
+ qcom,rst-gpio = <&msmgpio 25 0>;
+ qcom,mdss-pan-res = <720 1280>;
+ qcom,mdss-pan-bpp = <24>;
+ qcom,mdss-pan-dest = "display_1";
+ qcom,mdss-pan-porch-values = <59 60 79 10 2 7>;
+ qcom,mdss-pan-underflow-clr = <0xff>;
+ qcom,mdss-pan-bl-ctrl = "bl_ctrl_wled";
+ qcom,mdss-pan-bl-levels = <1 4095>;
+ qcom,mdss-pan-dsi-mode = <0>;
+ qcom,mdss-pan-dsi-h-pulse-mode = <1>;
+ qcom,mdss-pan-dsi-h-power-stop = <0 0 0>;
+ qcom,mdss-pan-dsi-bllp-power-stop = <1 1>;
+ qcom,mdss-pan-dsi-traffic-mode = <2>;
+ qcom,mdss-pan-dsi-dst-format = <3>;
+ qcom,mdss-pan-dsi-vc = <0>;
+ qcom,mdss-pan-dsi-rgb-swap = <0>;
+ qcom,mdss-pan-dsi-data-lanes = <1 1 1 1>; /* 4 lanes */
+ qcom,mdss-pan-dsi-dlane-swap = <0>;
+ qcom,mdss-pan-dsi-t-clk = <0x2d 0x1f>;
+ qcom,mdss-pan-dsi-stream = <0>;
+ qcom,mdss-pan-dsi-mdp-tr = <0x0>;
+ qcom,mdss-pan-dsi-dma-tr = <0x04>;
+ qcom,mdss-pan-dsi-frame-rate = <60>;
+ qcom,panel-phy-regulatorSettings = [07 09 03 00 /* Regualotor settings */
+ 20 00 01];
+ qcom,panel-phy-timingSettings = [8d 24 19 00 34 34
+ 1d 26 2a 03 04 00];
+ qcom,panel-phy-strengthCtrl = [ff 06];
+ qcom,panel-phy-bistCtrl = [00 00 b1 ff /* BIST Ctrl settings */
+ 00 00];
+ qcom,panel-phy-laneConfig = [00 00 00 00 00 00 00 01 97 /* lane0 config */
+ 00 00 00 00 05 00 00 01 97 /* lane1 config */
+ 00 00 00 00 0a 00 00 01 97 /* lane2 config */
+ 00 00 00 00 0f 00 00 01 97 /* lane3 config */
+ 00 c0 00 00 00 00 00 01 bb]; /* Clk ln config */
+ qcom,panel-on-cmds = [39 01 00 00 00 00 04
+ b9 ff 83 94
+ 39 01 00 00 00 00 05
+ c7 00 10 00 10
+ 39 01 00 00 00 00 02
+ bc 07
+ 39 01 00 00 00 00 02
+ ba 13
+ 39 01 00 00 00 00 10
+ b1 01 00 07 83 01
+ 12 0f 32 38 29 29
+ 50 02 00 00
+ 39 01 00 00 00 00 07
+ b2 00 c8 09 05 00
+ 71
+ 39 01 00 00 00 00 02
+ cc 05
+ 05 01 00 00 00 00 02 00 00
+ 39 01 00 00 00 00 35
+ d5 00 00 00 00 0a
+ 00 01 00 00 00 33
+ 00 23 45 67 01 01
+ 23 88 88 88 88 88
+ 88 88 99 99 99 88
+ 88 99 88 54 32 10
+ 76 32 10 88 88 88
+ 88 88 88 88 99 99
+ 99 88 88 88 99
+ 39 01 00 00 00 00 17
+ b4 80 08 32 10 00
+ 32 15 08 32 12 20
+ 33 05 4c 05 37 05
+ 3f 1e 5f 5f 06
+ 39 01 00 00 00 00 02
+ b6 00
+ 39 01 00 00 00 00 23
+ e0 01 05 07 25 35
+ 3f 0b 32 04 09 0e
+ 10 13 10 14 16 1b
+ 01 05 07 25 35 3f
+ 0b 32 04 09 0e 10
+ 13 10 14 16 1b
+ 05 01 00 00 00 00 02 00 00
+ 39 01 00 00 00 00 04
+ bf 06 00 10
+ 05 01 00 00 c8 00 02 11 00
+ 05 01 00 00 32 00 02 29 00];
+
+ qcom,on-cmds-dsi-state = "DSI_LP_MODE";
+ qcom,panel-off-cmds = [05 01 00 00 0a 00 02 28 00
+ 05 01 00 00 96 00 02 10 00];
+ qcom,off-cmds-dsi-state = "DSI_HS_MODE";
+ };
+};
diff --git a/arch/arm/boot/dts/msm8226-pm.dtsi b/arch/arm/boot/dts/msm8226-pm.dtsi
index 703ba4b..4e4b5db 100644
--- a/arch/arm/boot/dts/msm8226-pm.dtsi
+++ b/arch/arm/boot/dts/msm8226-pm.dtsi
@@ -384,4 +384,10 @@
reg-names = "phys_addr_base";
qcom,sleep-stats-version = <2>;
};
+
+ qcom,rpm-rbcpr-stats@fc000000 {
+ compatible = "qcom,rpmrbcpr-stats";
+ reg = <0xfc000000 0x1a0000>;
+ qcom,start-offset = <0x190010>;
+ };
};
diff --git a/arch/arm/boot/dts/msm8226-qrd.dtsi b/arch/arm/boot/dts/msm8226-qrd.dtsi
index 7018c6a..64cbb10 100644
--- a/arch/arm/boot/dts/msm8226-qrd.dtsi
+++ b/arch/arm/boot/dts/msm8226-qrd.dtsi
@@ -323,8 +323,9 @@
&pm8226_chg {
status = "okay";
- qcom,chg-vddmax-mv = <4350>;
- qcom,chg-vddsafe-mv = <4350>;
+ qcom,vddmax-mv = <4350>;
+ qcom,vddsafe-mv = <4380>;
+ qcom,tchg-mins = <240>;
};
&pm8226_gpios {
diff --git a/arch/arm/boot/dts/msm8226-v1.dtsi b/arch/arm/boot/dts/msm8226-v1.dtsi
index 7d3977f..d471bec 100644
--- a/arch/arm/boot/dts/msm8226-v1.dtsi
+++ b/arch/arm/boot/dts/msm8226-v1.dtsi
@@ -17,9 +17,3 @@
*/
/include/ "msm8226.dtsi"
-&soc {
- qcom,acpuclk@f9011050 {
- reg = <0xf9011050 0x8>;
- reg-names = "rcg_base";
- };
-};
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index e22f590..b14a406 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -741,6 +741,8 @@
qcom,acpuclk@f9011050 {
compatible = "qcom,acpuclk-a7";
+ reg = <0xf9011050 0x8>;
+ reg-names = "rcg_base";
a7_cpu-supply = <&apc_vreg_corner>;
};
diff --git a/arch/arm/boot/dts/msm8610-pm.dtsi b/arch/arm/boot/dts/msm8610-pm.dtsi
index faa7a41..0d34868 100644
--- a/arch/arm/boot/dts/msm8610-pm.dtsi
+++ b/arch/arm/boot/dts/msm8610-pm.dtsi
@@ -386,4 +386,10 @@
reg-names = "phys_addr_base";
qcom,sleep-stats-version = <2>;
};
+
+ qcom,rpm-rbcpr-stats@fc000000 {
+ compatible = "qcom,rpmrbcpr-stats";
+ reg = <0xfc000000 0x1a0000>;
+ qcom,start-offset = <0x190010>;
+ };
};
diff --git a/arch/arm/boot/dts/msm8610-qrd-camera-sensor.dtsi b/arch/arm/boot/dts/msm8610-qrd-camera-sensor.dtsi
index e3bd631..2e18ed7 100644
--- a/arch/arm/boot/dts/msm8610-qrd-camera-sensor.dtsi
+++ b/arch/arm/boot/dts/msm8610-qrd-camera-sensor.dtsi
@@ -45,7 +45,42 @@
qcom,sensor-mode = <1>;
qcom,cci-master = <0>;
};
-
-
+ qcom,camera@78 {
+ compatible = "shinetech,gc0339";
+ reg = <0x78>;
+ qcom,slave-id = <0x42 0x00 0xc8>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <180>;
+ qcom,sensor-name = "SKUAA_ST_gc0339";
+ cam_vdig-supply = <&pm8110_l14>;
+ cam_vana-supply = <&pm8110_l19>;
+ cam_vio-supply = <&pm8110_l14>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-type = <0 1 0>;
+ qcom,cam-vreg-min-voltage = <1800000 1800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1800000 1800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 14 0>,
+ <&msmgpio 15 0>,
+ <&msmgpio 8 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET",
+ "CAM_STANDBY";
+ qcom,gpio-set-tbl-num = <1 1>;
+ qcom,gpio-set-tbl-flags = <0 2>;
+ qcom,gpio-set-tbl-delay = <1000 4000>;
+ qcom,csi-lane-assign = <0xe4>;
+ qcom,csi-lane-mask = <0x3>;
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ };
};
diff --git a/arch/arm/boot/dts/msm8610-qrd.dts b/arch/arm/boot/dts/msm8610-qrd.dts
index 90225c0..7b45194 100644
--- a/arch/arm/boot/dts/msm8610-qrd.dts
+++ b/arch/arm/boot/dts/msm8610-qrd.dts
@@ -78,6 +78,15 @@
};
};
+ flashlight {
+ compatible = "qcom,leds-gpio-flash";
+ status = "okay";
+ qcom,flash-en = <&msmgpio 18 0>;
+ qcom,flash-now = <&msmgpio 19 0>;
+ linux,name = "flashlight";
+ linux,default-trigger = "flashlight-trigger";
+ };
+
gpio_keys {
compatible = "gpio-keys";
input-name = "gpio-keys";
diff --git a/arch/arm/boot/dts/msm8974-coresight.dtsi b/arch/arm/boot/dts/msm8974-coresight.dtsi
index 1610f1f..9fee2e5 100644
--- a/arch/arm/boot/dts/msm8974-coresight.dtsi
+++ b/arch/arm/boot/dts/msm8974-coresight.dtsi
@@ -369,8 +369,9 @@
compatible = "qcom,coresight-hwevent";
reg = <0xfdf30018 0x80>,
<0xf9011080 0x80>,
- <0xfd4ab160 0x80>;
- reg-names = "mmss-mux", "apcs-mux", "ppss-mux";
+ <0xfd4ab160 0x80>,
+ <0xfc401600 0x80>;
+ reg-names = "mmss-mux", "apcs-mux", "ppss-mux", "gcc-mux";
coresight-id = <29>;
coresight-name = "coresight-hwevent";
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index a098ce9..068f581 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -864,7 +864,7 @@
vbus_dwc3-supply = <&pm8941_mvs1>;
qcom,dwc-usb3-msm-dbm-eps = <4>;
qcom,vdd-voltage-level = <1 5 7>;
- qcom,dwc-hsphy-init = <0x00D195A4>;
+ qcom,dwc-hsphy-init = <0x00D191A4>;
qcom,msm-bus,name = "usb3";
qcom,msm-bus,num-cases = <2>;
@@ -1635,12 +1635,16 @@
&gdsc_venus {
qcom,clock-names = "core_clk";
qcom,skip-logic-collapse;
+ qcom,retain-periph;
+ qcom,retain-mem;
status = "ok";
};
&gdsc_mdss {
qcom,clock-names = "core_clk", "lut_clk";
+ qcom,skip-logic-collapse;
qcom,retain-periph;
+ qcom,retain-mem;
status = "ok";
};
diff --git a/arch/arm/configs/msm8226-perf_defconfig b/arch/arm/configs/msm8226-perf_defconfig
index 4f0872a..cb6161b 100644
--- a/arch/arm/configs/msm8226-perf_defconfig
+++ b/arch/arm/configs/msm8226-perf_defconfig
@@ -412,3 +412,4 @@
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
diff --git a/arch/arm/configs/msm8226_defconfig b/arch/arm/configs/msm8226_defconfig
index 3705a55..e6ab4a1 100644
--- a/arch/arm/configs/msm8226_defconfig
+++ b/arch/arm/configs/msm8226_defconfig
@@ -448,3 +448,4 @@
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
diff --git a/arch/arm/configs/msm8610-perf_defconfig b/arch/arm/configs/msm8610-perf_defconfig
index 9869fbf..2cfad74 100644
--- a/arch/arm/configs/msm8610-perf_defconfig
+++ b/arch/arm/configs/msm8610-perf_defconfig
@@ -381,3 +381,4 @@
# CONFIG_CRYPTO_HW is not set
CONFIG_CRC_CCITT=y
CONFIG_INPUT_KXTJ9=y
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
diff --git a/arch/arm/configs/msm8610_defconfig b/arch/arm/configs/msm8610_defconfig
index 24c5d9c..cf261d2 100644
--- a/arch/arm/configs/msm8610_defconfig
+++ b/arch/arm/configs/msm8610_defconfig
@@ -352,6 +352,7 @@
CONFIG_MMC_SDHCI_MSM=y
CONFIG_MMC_MSM_SPS_SUPPORT=y
CONFIG_LEDS_QPNP=y
+CONFIG_LEDS_MSM_GPIO_FLASH=y
CONFIG_LEDS_TRIGGERS=y
CONFIG_SWITCH=y
CONFIG_RTC_CLASS=y
@@ -423,3 +424,4 @@
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
CONFIG_INPUT_KXTJ9=y
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
diff --git a/arch/arm/mach-msm/clock-8610.c b/arch/arm/mach-msm/clock-8610.c
index 18a81d0..75d8984 100644
--- a/arch/arm/mach-msm/clock-8610.c
+++ b/arch/arm/mach-msm/clock-8610.c
@@ -3000,9 +3000,11 @@
/* MM sensor clocks */
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6-006f"),
+ CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6-007d"),
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6-006d"),
CLK_LOOKUP("cam_src_clk", mclk1_clk_src.c, "6-0078"),
CLK_LOOKUP("cam_clk", mclk0_clk.c, "6-006f"),
+ CLK_LOOKUP("cam_clk", mclk0_clk.c, "6-007d"),
CLK_LOOKUP("cam_clk", mclk0_clk.c, "6-006d"),
CLK_LOOKUP("cam_clk", mclk1_clk.c, "6-0078"),
diff --git a/arch/arm/mach-msm/clock-mdss-8974.c b/arch/arm/mach-msm/clock-mdss-8974.c
index 1245287..6877b4d9 100644
--- a/arch/arm/mach-msm/clock-mdss-8974.c
+++ b/arch/arm/mach-msm/clock-mdss-8974.c
@@ -906,18 +906,19 @@
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
udelay(1000);
- do {
+ pll_locked = dsi_pll_lock_status();
+ for (i = 0; (i < 4) && !pll_locked; i++) {
+ DSS_REG_W(mdss_dsi_base,
+ DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
+ if (i != 0)
+ DSS_REG_W(mdss_dsi_base,
+ DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x34);
+ udelay(1);
+ DSS_REG_W(mdss_dsi_base,
+ DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
+ udelay(1000);
pll_locked = dsi_pll_lock_status();
- if (!pll_locked) {
- DSS_REG_W(mdss_dsi_base,
- DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
- udelay(1);
- DSS_REG_W(mdss_dsi_base,
- DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
- udelay(1000);
- i++;
- }
- } while ((i < 3) && !pll_locked);
+ }
if (pll_locked)
pr_debug("%s: PLL Locked at attempt #%d\n", __func__, i);
@@ -940,17 +941,17 @@
* PLL to successfully lock
*/
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
- udelay(1);
+ udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
- udelay(1);
+ udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
- udelay(1);
+ udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
- udelay(1);
+ udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
- udelay(1);
+ udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
- udelay(1);
+ udelay(1000);
pll_locked = dsi_pll_lock_status();
pr_debug("%s: PLL status = %s\n", __func__,
@@ -1029,6 +1030,7 @@
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0d);
+ udelay(1);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
udelay(1000);
diff --git a/arch/arm/mach-msm/include/mach/usb_bam.h b/arch/arm/mach-msm/include/mach/usb_bam.h
index a3e993d..b2dd49c 100644
--- a/arch/arm/mach-msm/include/mach/usb_bam.h
+++ b/arch/arm/mach-msm/include/mach/usb_bam.h
@@ -198,14 +198,6 @@
struct usb_bam_connect_ipa_params *ipa_params);
/**
- * Wait for Consumer granted from Resource Manager.
- *
- * @ipa_params - in/out parameters
- *
- */
-void usb_bam_wait_for_cons_granted(
- struct usb_bam_connect_ipa_params *ipa_params);
-/**
* Register a wakeup callback from peer BAM.
*
* @idx - Connection index.
diff --git a/arch/arm/mach-msm/include/mach/usb_bridge.h b/arch/arm/mach-msm/include/mach/usb_bridge.h
index c62cf01..b3b7d71 100644
--- a/arch/arm/mach-msm/include/mach/usb_bridge.h
+++ b/arch/arm/mach-msm/include/mach/usb_bridge.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17,10 +17,8 @@
#include <linux/netdevice.h>
#include <linux/usb.h>
-/* bridge device 0: DUN
- * bridge device 1 : Tethered RMNET
- */
-#define MAX_BRIDGE_DEVICES 2
+#define MAX_BRIDGE_DEVICES 4
+#define BRIDGE_NAME_MAX_LEN 20
struct bridge_ops {
int (*send_pkt)(void *, void *, size_t actual);
@@ -37,11 +35,12 @@
/* context of the gadget port using bridge driver */
void *ctx;
- /* bridge device array index mapped to the gadget port array index.
- * data bridge[ch_id] <-- bridge --> gadget port[ch_id]
- */
+ /*to maps bridge driver instance*/
unsigned int ch_id;
+ /*to match against bridge xport name to get bridge driver instance*/
+ char *name;
+
/* flow control bits */
unsigned long flags;
@@ -101,7 +100,10 @@
int data_bridge_unthrottle_rx(unsigned int);
/* defined in control bridge */
-int ctrl_bridge_probe(struct usb_interface *, struct usb_host_endpoint *, int);
+int ctrl_bridge_init(void);
+void ctrl_bridge_exit(void);
+int ctrl_bridge_probe(struct usb_interface *, struct usb_host_endpoint *,
+ char *, int);
void ctrl_bridge_disconnect(unsigned int);
int ctrl_bridge_resume(unsigned int);
int ctrl_bridge_suspend(unsigned int);
diff --git a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
index a183f0e..634a4dc 100644
--- a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
+++ b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
@@ -89,8 +89,8 @@
USB_GADGET_RMNET,
};
-#define NUM_RMNET_HSIC_PORTS 1
-#define NUM_DUN_HSIC_PORTS 1
+#define NUM_RMNET_HSIC_PORTS 2
+#define NUM_DUN_HSIC_PORTS 2
#define NUM_PORTS (NUM_RMNET_HSIC_PORTS \
+ NUM_DUN_HSIC_PORTS)
@@ -102,9 +102,11 @@
int ghsic_ctrl_connect(void *, int);
void ghsic_ctrl_disconnect(void *, int);
int ghsic_ctrl_setup(unsigned int, enum gadget_type);
+void ghsic_ctrl_set_port_name(const char *, const char *);
int ghsic_data_connect(void *, int);
void ghsic_data_disconnect(void *, int);
int ghsic_data_setup(unsigned int, enum gadget_type);
+void ghsic_data_set_port_name(const char *, const char *);
int ghsuart_ctrl_connect(void *, int);
void ghsuart_ctrl_disconnect(void *, int);
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index cb4528f..7a0d23b 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -22,6 +22,7 @@
obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
obj-$(CONFIG_REGMAP) += regmap/
obj-$(CONFIG_SOC_BUS) += soc.o
+obj-$(CONFIG_PINCTRL) += pinctrl.o
obj-$(CONFIG_SYNC) += sync.o
obj-$(CONFIG_SW_SYNC) += sw_sync.o
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 1b1cbb5..b3a7d6e 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -24,6 +24,7 @@
#include <linux/wait.h>
#include <linux/async.h>
#include <linux/pm_runtime.h>
+#include <linux/pinctrl/devinfo.h>
#include "base.h"
#include "power/power.h"
@@ -257,6 +258,12 @@
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
+
+ /* If using pinctrl, bind pins now before probing */
+ ret = pinctrl_bind_pins(dev);
+ if (ret)
+ goto probe_failed;
+
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
diff --git a/drivers/base/pinctrl.c b/drivers/base/pinctrl.c
new file mode 100644
index 0000000..67a274e
--- /dev/null
+++ b/drivers/base/pinctrl.c
@@ -0,0 +1,69 @@
+/*
+ * Driver core interface to the pinctrl subsystem.
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Written on behalf of Linaro for ST-Ericsson
+ * Based on bits of regulator core, gpio core and clk core
+ *
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/device.h>
+#include <linux/pinctrl/devinfo.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/slab.h>
+
+/**
+ * pinctrl_bind_pins() - called by the device core before probe
+ * @dev: the device that is just about to probe
+ */
+int pinctrl_bind_pins(struct device *dev)
+{
+ int ret;
+
+ dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
+ if (!dev->pins)
+ return -ENOMEM;
+
+ dev->pins->p = devm_pinctrl_get(dev);
+ if (IS_ERR(dev->pins->p)) {
+ dev_dbg(dev, "no pinctrl handle\n");
+ ret = PTR_ERR(dev->pins->p);
+ goto cleanup_alloc;
+ }
+
+ dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
+ PINCTRL_STATE_DEFAULT);
+ if (IS_ERR(dev->pins->default_state)) {
+ dev_dbg(dev, "no default pinctrl state\n");
+ ret = 0;
+ goto cleanup_get;
+ }
+
+ ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state);
+ if (ret) {
+ dev_dbg(dev, "failed to activate default pinctrl state\n");
+ goto cleanup_get;
+ }
+
+ return 0;
+
+ /*
+ * If no pinctrl handle or default state was found for this device,
+ * let's explicitly free the pin container in the device, there is
+ * no point in keeping it around.
+ */
+cleanup_get:
+ devm_pinctrl_put(dev->pins->p);
+cleanup_alloc:
+ devm_kfree(dev, dev->pins);
+ dev->pins = NULL;
+
+ /* Only return deferrals */
+ if (ret != -EPROBE_DEFER)
+ ret = 0;
+
+ return ret;
+}
diff --git a/drivers/coresight/coresight-etm.c b/drivers/coresight/coresight-etm.c
index 5a5c0cf..de86622 100644
--- a/drivers/coresight/coresight-etm.c
+++ b/drivers/coresight/coresight-etm.c
@@ -2154,14 +2154,20 @@
drvdata->cpu = count++;
- get_online_cpus();
etmdrvdata[drvdata->cpu] = drvdata;
+ /*
+ * This is safe wrt CPU_UP_PREPARE and CPU_STARTING hotplug callbacks
+ * on the secondary cores that may enable the clock and perform
+ * etm_os_unlock since they occur before the cpu online mask is updated
+ * for the cpu which is checked by this smp call.
+ */
if (!smp_call_function_single(drvdata->cpu, etm_os_unlock, drvdata, 1))
drvdata->os_unlock = true;
+
/*
- * Use CPU0 to populate read-only configuration data for ETM0. For
- * other ETMs copy it over from ETM0.
+ * OS unlock must have happened on cpu0 so use it to populate read-only
+ * configuration data for ETM0. For other ETMs copy it over from ETM0.
*/
if (drvdata->cpu == 0) {
register_hotcpu_notifier(&etm_cpu_notifier);
@@ -2172,8 +2178,6 @@
etm_copy_arch_data(drvdata);
}
- put_online_cpus();
-
if (etm_arch_supported(drvdata->arch) == false) {
ret = -EINVAL;
goto err1;
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index fda64e5..2fd8bef 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -22,6 +22,7 @@
#include <linux/hrtimer.h>
#include <linux/tick.h>
#include <linux/ktime.h>
+#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/input.h>
#include <linux/workqueue.h>
@@ -103,6 +104,11 @@
* when user is changing the governor or limits.
*/
struct mutex timer_mutex;
+
+ struct task_struct *sync_thread;
+ wait_queue_head_t sync_wq;
+ atomic_t src_sync_cpu;
+ atomic_t sync_enabled;
};
static DEFINE_PER_CPU(struct cpu_dbs_info_s, od_cpu_dbs_info);
@@ -125,14 +131,6 @@
static DEFINE_PER_CPU(struct dbs_work_struct, dbs_refresh_work);
-struct dbs_sync_work_struct {
- struct work_struct work;
- unsigned int src_cpu;
- unsigned int targ_cpu;
-};
-
-static DEFINE_PER_CPU(struct dbs_sync_work_struct, dbs_sync_work);
-
static struct dbs_tuners {
unsigned int sampling_rate;
unsigned int up_threshold;
@@ -1039,12 +1037,11 @@
static int dbs_migration_notify(struct notifier_block *nb,
unsigned long target_cpu, void *arg)
{
- struct dbs_sync_work_struct *sync_work =
- &per_cpu(dbs_sync_work, target_cpu);
- sync_work->src_cpu = (unsigned int)arg;
+ struct cpu_dbs_info_s *target_dbs_info =
+ &per_cpu(od_cpu_dbs_info, target_cpu);
- queue_work_on(target_cpu, dbs_wq,
- &per_cpu(dbs_sync_work, target_cpu).work);
+ atomic_set(&target_dbs_info->src_sync_cpu, (int)arg);
+ wake_up(&target_dbs_info->sync_wq);
return NOTIFY_OK;
}
@@ -1053,73 +1050,97 @@
.notifier_call = dbs_migration_notify,
};
-void dbs_synchronize(struct work_struct *work)
+static int sync_pending(struct cpu_dbs_info_s *this_dbs_info)
{
- struct cpufreq_policy *policy;
- struct cpu_dbs_info_s *this_dbs_info, *src_dbs_info;
- struct dbs_sync_work_struct *dbs_work;
- unsigned int cpu, src_cpu;
+ return atomic_read(&this_dbs_info->src_sync_cpu) >= 0;
+}
+
+static int dbs_sync_thread(void *data)
+{
+ int src_cpu, cpu = (int)data;
unsigned int src_freq, src_max_load;
+ struct cpu_dbs_info_s *this_dbs_info, *src_dbs_info;
+ struct cpufreq_policy *policy;
int delay;
- dbs_work = container_of(work, struct dbs_sync_work_struct, work);
- cpu = dbs_work->targ_cpu;
- src_cpu = dbs_work->src_cpu;
-
- get_online_cpus();
-
- /* Getting source cpu info */
- src_dbs_info = &per_cpu(od_cpu_dbs_info, src_cpu);
- if (src_dbs_info != NULL && src_dbs_info->cur_policy != NULL) {
- src_freq = src_dbs_info->cur_policy->cur;
- src_max_load = src_dbs_info->max_load;
- } else {
- src_freq = dbs_tuners_ins.sync_freq;
- src_max_load = 0;
- }
-
- if (lock_policy_rwsem_write(cpu) < 0)
- goto bail_acq_sema_failed;
-
this_dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
- policy = this_dbs_info->cur_policy;
- if (!policy) {
- /* CPU not using ondemand governor */
- goto bail_incorrect_governor;
- }
- delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
+ while (1) {
+ wait_event(this_dbs_info->sync_wq,
+ sync_pending(this_dbs_info) ||
+ kthread_should_stop());
- if (policy->cur < src_freq) {
+ if (kthread_should_stop())
+ break;
- /* Cancelling the next ondemand sample */
- cancel_delayed_work_sync(&this_dbs_info->work);
+ get_online_cpus();
- /*
- * Arch specific cpufreq driver may fail.
- * Don't update governor frequency upon failure.
+ /* TODO: cur_policy is currently set for all CPUs when
+ * a policy is started but cleared only on the current
+ * CPU when the policy is stopped. If/when this is
+ * resolved, sync_enabled can be removed and
+ * cur_policy can be used instead.
*/
- if (__cpufreq_driver_target(policy, src_freq,
- CPUFREQ_RELATION_L) >= 0) {
- policy->cur = src_freq;
- if (src_max_load > this_dbs_info->max_load) {
- this_dbs_info->max_load = src_max_load;
- this_dbs_info->prev_load = src_max_load;
- }
+ if (!atomic_read(&this_dbs_info->sync_enabled)) {
+ atomic_set(&this_dbs_info->src_sync_cpu, -1);
+ put_online_cpus();
+ continue;
}
- /* Rescheduling the next ondemand sample */
- mutex_lock(&this_dbs_info->timer_mutex);
- schedule_delayed_work_on(cpu, &this_dbs_info->work,
- delay);
- mutex_unlock(&this_dbs_info->timer_mutex);
- }
-bail_incorrect_governor:
- unlock_policy_rwsem_write(cpu);
+ src_cpu = atomic_read(&this_dbs_info->src_sync_cpu);
+ src_dbs_info = &per_cpu(od_cpu_dbs_info, src_cpu);
+ if (src_dbs_info != NULL &&
+ src_dbs_info->cur_policy != NULL) {
+ src_freq = src_dbs_info->cur_policy->cur;
+ src_max_load = src_dbs_info->max_load;
+ } else {
+ src_freq = dbs_tuners_ins.sync_freq;
+ src_max_load = 0;
+ }
+ if (lock_policy_rwsem_write(cpu) < 0)
+ goto bail_acq_sema_failed;
+
+ policy = this_dbs_info->cur_policy;
+ if (!policy) {
+ /* CPU not using ondemand governor */
+ goto bail_incorrect_governor;
+ }
+ delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
+
+
+ if (policy->cur < src_freq) {
+ /* cancel the next ondemand sample */
+ cancel_delayed_work_sync(&this_dbs_info->work);
+
+ /*
+ * Arch specific cpufreq driver may fail.
+ * Don't update governor frequency upon failure.
+ */
+ if (__cpufreq_driver_target(policy, src_freq,
+ CPUFREQ_RELATION_L) >= 0) {
+ policy->cur = src_freq;
+ if (src_max_load > this_dbs_info->max_load) {
+ this_dbs_info->max_load = src_max_load;
+ this_dbs_info->prev_load = src_max_load;
+ }
+ }
+
+ /* reschedule the next ondemand sample */
+ mutex_lock(&this_dbs_info->timer_mutex);
+ queue_delayed_work_on(cpu, dbs_wq,
+ &this_dbs_info->work, delay);
+ mutex_unlock(&this_dbs_info->timer_mutex);
+ }
+
+bail_incorrect_governor:
+ unlock_policy_rwsem_write(cpu);
bail_acq_sema_failed:
- put_online_cpus();
- return;
+ put_online_cpus();
+ atomic_set(&this_dbs_info->src_sync_cpu, -1);
+ }
+
+ return 0;
}
static void dbs_input_event(struct input_handle *handle, unsigned int type,
@@ -1215,6 +1236,9 @@
if (dbs_tuners_ins.ignore_nice)
j_dbs_info->prev_cpu_nice =
kcpustat_cpu(j).cpustat[CPUTIME_NICE];
+ set_cpus_allowed(j_dbs_info->sync_thread,
+ *cpumask_of(j));
+ atomic_set(&j_dbs_info->sync_enabled, 1);
}
this_dbs_info->cpu = cpu;
this_dbs_info->rate_mult = 1;
@@ -1271,6 +1295,13 @@
mutex_lock(&dbs_mutex);
dbs_enable--;
+
+ for_each_cpu(j, policy->cpus) {
+ struct cpu_dbs_info_s *j_dbs_info;
+ j_dbs_info = &per_cpu(od_cpu_dbs_info, j);
+ atomic_set(&j_dbs_info->sync_enabled, 0);
+ }
+
/* If device is being removed, policy is no longer
* valid. */
this_dbs_info->cur_policy = NULL;
@@ -1342,17 +1373,17 @@
&per_cpu(od_cpu_dbs_info, i);
struct dbs_work_struct *dbs_work =
&per_cpu(dbs_refresh_work, i);
- struct dbs_sync_work_struct *dbs_sync =
- &per_cpu(dbs_sync_work, i);
mutex_init(&this_dbs_info->timer_mutex);
INIT_WORK(&dbs_work->work, dbs_refresh_callback);
dbs_work->cpu = i;
- INIT_WORK(&dbs_sync->work, dbs_synchronize);
- dbs_sync->src_cpu = 0;
- dbs_sync->targ_cpu = i;
+ atomic_set(&this_dbs_info->src_sync_cpu, -1);
+ init_waitqueue_head(&this_dbs_info->sync_wq);
+ this_dbs_info->sync_thread = kthread_run(dbs_sync_thread,
+ (void *)i,
+ "dbs_sync/%d", i);
}
return cpufreq_register_governor(&cpufreq_gov_ondemand);
@@ -1367,6 +1398,7 @@
struct cpu_dbs_info_s *this_dbs_info =
&per_cpu(od_cpu_dbs_info, i);
mutex_destroy(&this_dbs_info->timer_mutex);
+ kthread_stop(this_dbs_info->sync_thread);
}
destroy_workqueue(dbs_wq);
}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 5a75510..7db56a3 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1081,6 +1081,10 @@
}
}
+#ifdef CONFIG_PINCTRL
+ INIT_LIST_HEAD(&chip->pin_ranges);
+#endif
+
of_gpiochip_add(chip);
unlock:
@@ -1178,6 +1182,47 @@
}
EXPORT_SYMBOL_GPL(gpiochip_find);
+#ifdef CONFIG_PINCTRL
+int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+ unsigned int pin_base, unsigned int npins)
+{
+ struct gpio_pin_range *pin_range;
+
+ pin_range = devm_kzalloc(chip->dev, sizeof(*pin_range), GFP_KERNEL);
+ if (!pin_range) {
+ pr_err("%s: GPIO chip: failed to allocate pin ranges\n",
+ chip->label);
+ return -ENOMEM;
+ }
+
+ pin_range->range.name = chip->label;
+ pin_range->range.base = chip->base;
+ pin_range->range.pin_base = pin_base;
+ pin_range->range.npins = npins;
+ pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name,
+ &pin_range->range);
+
+ list_add_tail(&pin_range->node, &chip->pin_ranges);
+
+ return 0;
+}
+
+void gpiochip_remove_pin_ranges(struct gpio_chip *chip)
+{
+ struct gpio_pin_range *pin_range, *tmp;
+
+ list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) {
+ list_del(&pin_range->node);
+ pinctrl_remove_gpio_range(pin_range->pctldev,
+ &pin_range->range);
+ }
+}
+#else
+void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+ unsigned int pin_base, unsigned int npins) {}
+void gpiochip_remove_pin_ranges(struct gpio_chip *chip) {}
+#endif
+
/* These "optional" allocation calls help prevent drivers from stomping
* on each other, and help provide better diagnostics in debugfs.
* They're called even less than the "set direction" calls.
diff --git a/drivers/gpu/ion/ion_cma_heap.c b/drivers/gpu/ion/ion_cma_heap.c
index e7f7836..b24b2bd 100644
--- a/drivers/gpu/ion/ion_cma_heap.c
+++ b/drivers/gpu/ion/ion_cma_heap.c
@@ -76,10 +76,10 @@
if (!ION_IS_CACHED(flags))
info->cpu_addr = dma_alloc_writecombine(dev, len,
- &(info->handle), 0);
+ &(info->handle), GFP_KERNEL);
else
info->cpu_addr = dma_alloc_nonconsistent(dev, len,
- &(info->handle), 0);
+ &(info->handle), GFP_KERNEL);
if (!info->cpu_addr) {
dev_err(dev, "Fail to allocate buffer\n");
diff --git a/drivers/gpu/ion/ion_cma_secure_heap.c b/drivers/gpu/ion/ion_cma_secure_heap.c
index b3960b2..415c73e 100644
--- a/drivers/gpu/ion/ion_cma_secure_heap.c
+++ b/drivers/gpu/ion/ion_cma_secure_heap.c
@@ -83,7 +83,8 @@
return ION_CMA_ALLOCATE_FAILED;
}
- info->cpu_addr = dma_alloc_attrs(dev, len, &(info->handle), 0, &attrs);
+ info->cpu_addr = dma_alloc_attrs(dev, len, &(info->handle), GFP_KERNEL,
+ &attrs);
if (!info->cpu_addr) {
dev_err(dev, "Fail to allocate buffer\n");
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 0bacc5e..9113605 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -287,7 +287,7 @@
addr < (p->gpuaddr + p->size)) {
kgsl_get_memory_usage(name, sizeof(name) - 1,
- p->flags),
+ p->flags);
KGSL_LOG_DUMP(iommu_dev->kgsldev,
"---- premature free ----\n");
KGSL_LOG_DUMP(iommu_dev->kgsldev,
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index de7b0e9..9570327 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -389,8 +389,13 @@
rc = qpnp_iadc_read_reg(QPNP_IADC_ATE_GAIN_CALIB_OFFSET,
&iadc->iadc_comp.sys_gain);
- if (rc < 0)
+ if (rc < 0) {
pr_err("full scale read failed with %d\n", rc);
+ return rc;
+ }
+
+ if (iadc->external_rsense)
+ iadc->iadc_comp.ext_rsense = true;
pr_debug("fab id = %u, revision = %u, sys gain = %u, external_rsense = %d\n",
iadc->iadc_comp.id,
diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c
index f54707c..204ad94 100644
--- a/drivers/input/misc/kxtj9.c
+++ b/drivers/input/misc/kxtj9.c
@@ -31,7 +31,9 @@
#include <linux/of_gpio.h>
#endif /* CONFIG_OF */
-#define NAME "kxtj9"
+#define ACCEL_INPUT_DEV_NAME "accelerometer"
+#define DEVICE_NAME "kxtj9"
+
#define G_MAX 8000
/* OUTPUT REGISTERS */
#define XOUT_L 0x06
@@ -421,19 +423,6 @@
tj9->enable = false;
}
-static int kxtj9_input_open(struct input_dev *input)
-{
- struct kxtj9_data *tj9 = input_get_drvdata(input);
-
- return kxtj9_enable(tj9);
-}
-
-static void kxtj9_input_close(struct input_dev *dev)
-{
- struct kxtj9_data *tj9 = input_get_drvdata(dev);
-
- kxtj9_disable(tj9);
-}
static void __devinit kxtj9_init_input_device(struct kxtj9_data *tj9,
struct input_dev *input_dev)
@@ -443,7 +432,7 @@
input_set_abs_params(input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT);
input_set_abs_params(input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT);
- input_dev->name = "kxtj9_accel";
+ input_dev->name = ACCEL_INPUT_DEV_NAME;
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &tj9->client->dev;
}
@@ -461,8 +450,6 @@
tj9->input_dev = input_dev;
- input_dev->open = kxtj9_input_open;
- input_dev->close = kxtj9_input_close;
input_set_drvdata(input_dev, tj9);
kxtj9_init_input_device(tj9, input_dev);
@@ -534,7 +521,7 @@
*/
/* Returns currently selected poll interval (in ms) */
-static ssize_t kxtj9_get_poll(struct device *dev,
+static ssize_t kxtj9_get_poll_delay(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -544,8 +531,9 @@
}
/* Allow users to select a new poll interval (in ms) */
-static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t kxtj9_set_poll_delay(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct kxtj9_data *tj9 = i2c_get_clientdata(client);
@@ -576,11 +564,12 @@
return count;
}
-static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, kxtj9_get_poll, kxtj9_set_poll);
+static DEVICE_ATTR(poll_delay, S_IRUGO|S_IWUSR|S_IWGRP,
+ kxtj9_get_poll_delay, kxtj9_set_poll_delay);
static struct attribute *kxtj9_attributes[] = {
&dev_attr_enable.attr,
- &dev_attr_poll.attr,
+ &dev_attr_poll_delay.attr,
NULL
};
@@ -679,7 +668,8 @@
goto out;
}
- retval = (retval != 0x07 && retval != 0x08) ? -EIO : 0;
+ retval = (retval != 0x05 && retval != 0x07 && retval != 0x08)
+ ? -EIO : 0;
out:
return retval;
@@ -950,7 +940,7 @@
static SIMPLE_DEV_PM_OPS(kxtj9_pm_ops, kxtj9_suspend, kxtj9_resume);
static const struct i2c_device_id kxtj9_id[] = {
- { NAME, 0 },
+ { DEVICE_NAME, 0 },
{ },
};
@@ -964,7 +954,7 @@
static struct i2c_driver kxtj9_driver = {
.driver = {
- .name = NAME,
+ .name = DEVICE_NAME,
.owner = THIS_MODULE,
.of_match_table = kxtj9_match_table,
.pm = &kxtj9_pm_ops,
diff --git a/drivers/input/misc/lis3dh_acc.c b/drivers/input/misc/lis3dh_acc.c
index ea1b079..03fabd0 100644
--- a/drivers/input/misc/lis3dh_acc.c
+++ b/drivers/input/misc/lis3dh_acc.c
@@ -1093,7 +1093,7 @@
static struct device_attribute attributes[] = {
- __ATTR(pollrate_ms, 0664, attr_get_polling_rate,
+ __ATTR(poll_delay, 0664, attr_get_polling_rate,
attr_set_polling_rate),
__ATTR(range, 0664, attr_get_range, attr_set_range),
__ATTR(enable, 0664, attr_get_enable, attr_set_enable),
@@ -1226,7 +1226,7 @@
acc->input_dev->open = lis3dh_acc_input_open;
acc->input_dev->close = lis3dh_acc_input_close;
- acc->input_dev->name = LIS3DH_ACC_DEV_NAME;
+ acc->input_dev->name = ACCEL_INPUT_DEV_NAME;
acc->input_dev->id.bustype = BUS_I2C;
acc->input_dev->dev.parent = &acc->client->dev;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
index 2ff70d3..cb8d821 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
@@ -34,6 +34,7 @@
#define VFE40_8974V2_VERSION 0x1001001A
#define VFE40_8974V3_VERSION 0x1001001B
#define VFE40_8x26_VERSION 0x20000013
+#define VFE40_8x26V2_VERSION 0x20010014
#define VFE40_BURST_LEN 3
#define VFE40_STATS_BURST_LEN 2
@@ -93,8 +94,10 @@
static void msm_vfe40_init_qos_parms(struct vfe_device *vfe_dev)
{
void __iomem *vfebase = vfe_dev->vfe_base;
+
if (vfe_dev->vfe_hw_version == VFE40_8974V1_VERSION ||
- vfe_dev->vfe_hw_version == VFE40_8x26_VERSION) {
+ vfe_dev->vfe_hw_version == VFE40_8x26_VERSION ||
+ vfe_dev->vfe_hw_version == VFE40_8x26V2_VERSION) {
msm_camera_io_w(0xAAAAAAAA, vfebase + VFE40_BUS_BDG_QOS_CFG_0);
msm_camera_io_w(0xAAAAAAAA, vfebase + VFE40_BUS_BDG_QOS_CFG_1);
msm_camera_io_w(0xAAAAAAAA, vfebase + VFE40_BUS_BDG_QOS_CFG_2);
@@ -237,6 +240,7 @@
msm_vfe40_init_vbif_parms_8974_v2(vfe_dev);
break;
case VFE40_8x26_VERSION:
+ case VFE40_8x26V2_VERSION:
msm_vfe40_init_vbif_parms_8x26(vfe_dev);
break;
default:
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
index df72328..f658f13 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
@@ -24,6 +24,9 @@
#include <linux/proc_fs.h>
#include <linux/msm_ion.h>
#include <linux/iommu.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
#include <mach/iommu_domains.h>
#include <mach/iommu.h>
#include <mach/vreg.h>
@@ -36,6 +39,7 @@
#include "msm_cpp.h"
#include "msm_isp_util.h"
#include "msm_camera_io_util.h"
+#include <linux/debugfs.h>
#define MSM_CPP_DRV_NAME "msm_cpp"
@@ -43,6 +47,23 @@
#define CONFIG_MSM_CPP_DBG 0
+#define CPP_CMD_TIMEOUT_MS 300
+
+struct msm_cpp_timer_data_t {
+ struct cpp_device *cpp_dev;
+ struct msm_cpp_frame_info_t *processed_frame;
+};
+
+struct msm_cpp_timer_t {
+ uint8_t used;
+ struct msm_cpp_timer_data_t data;
+ struct timer_list cpp_timer;
+};
+
+struct msm_cpp_timer_t cpp_timers[2];
+static int del_timer_idx;
+static int set_timer_idx;
+
/* dump the frame command before writing to the hardware */
#define MSM_CPP_DUMP_FRM_CMD 0
@@ -109,6 +130,11 @@
{"micro_iface_clk", -1},
};
static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev);
+static void cpp_load_fw(struct cpp_device *cpp_dev, char *fw_name_bin);
+static void cpp_timer_callback(unsigned long data);
+
+uint8_t induce_error;
+static int msm_cpp_enable_debugfs(struct cpp_device *cpp_dev);
static void msm_cpp_write(u32 data, void __iomem *cpp_base)
{
@@ -472,7 +498,6 @@
tx_fifo[i] = msm_camera_io_r(cpp_dev->base +
MSM_CPP_MICRO_FIFO_TX_DATA);
}
-
spin_lock_irqsave(&cpp_dev->tasklet_lock, flags);
queue_cmd = &cpp_dev->tasklet_queue_cmd[cpp_dev->taskletq_idx];
if (queue_cmd->cmd_used) {
@@ -494,6 +519,36 @@
spin_unlock_irqrestore(&cpp_dev->tasklet_lock, flags);
tasklet_schedule(&cpp_dev->cpp_tasklet);
+ } else if (irq_status & 0x7C0) {
+ pr_err("%s: fatal error: 0x%x\n", __func__, irq_status);
+ pr_err("%s: DEBUG_SP: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x40));
+ pr_err("%s: DEBUG_T: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x44));
+ pr_err("%s: DEBUG_N: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x48));
+ pr_err("%s: DEBUG_R: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x4C));
+ pr_err("%s: DEBUG_OPPC: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x50));
+ pr_err("%s: DEBUG_MO: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x54));
+ pr_err("%s: DEBUG_TIMER0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x60));
+ pr_err("%s: DEBUG_TIMER1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x64));
+ pr_err("%s: DEBUG_GPI: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x70));
+ pr_err("%s: DEBUG_GPO: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x74));
+ pr_err("%s: DEBUG_T0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x80));
+ pr_err("%s: DEBUG_R0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x84));
+ pr_err("%s: DEBUG_T1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x88));
+ pr_err("%s: DEBUG_R1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x8C));
}
msm_camera_io_w(irq_status, cpp_dev->base + MSM_CPP_MICRO_IRQGEN_CLR);
return IRQ_HANDLED;
@@ -509,6 +564,7 @@
uint32_t tx_fifo[MSM_CPP_TX_FIFO_LEVEL];
struct cpp_device *cpp_dev = (struct cpp_device *) data;
struct msm_cpp_tasklet_queue_cmd *queue_cmd;
+ struct msm_cpp_timer_t *timer = NULL;
while (atomic_read(&cpp_dev->irq_cnt)) {
spin_lock_irqsave(&cpp_dev->tasklet_lock, flags);
@@ -535,6 +591,25 @@
msg_id = tx_fifo[i+2];
if (msg_id == MSM_CPP_MSG_ID_FRAME_ACK) {
CPP_DBG("Frame done!!\n");
+ /* delete CPP timer */
+ CPP_DBG("delete timer %d.\n",
+ del_timer_idx);
+ timer = &cpp_timers[del_timer_idx];
+ del_timer(&timer->cpp_timer);
+ timer->used = 0;
+ timer->data.processed_frame = NULL;
+ del_timer_idx = 1 - del_timer_idx;
+ msm_cpp_notify_frame_done(cpp_dev);
+ } else if (msg_id ==
+ MSM_CPP_MSG_ID_FRAME_NACK) {
+ pr_err("NACK error from hw!!\n");
+ CPP_DBG("delete timer %d.\n",
+ del_timer_idx);
+ timer = &cpp_timers[del_timer_idx];
+ del_timer(&timer->cpp_timer);
+ timer->used = 0;
+ timer->data.processed_frame = NULL;
+ del_timer_idx = 1 - del_timer_idx;
msm_cpp_notify_frame_done(cpp_dev);
}
i += cmd_len + 2;
@@ -543,48 +618,6 @@
}
}
-static void msm_cpp_boot_hw(struct cpp_device *cpp_dev)
-{
- disable_irq(cpp_dev->irq->start);
-
- msm_camera_io_w(0x1, cpp_dev->base + MSM_CPP_MICRO_CLKEN_CTL);
- msm_camera_io_w(0x1, cpp_dev->base +
- MSM_CPP_MICRO_BOOT_START);
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
-
- /*Trigger MC to jump to start address*/
- msm_cpp_write(MSM_CPP_CMD_EXEC_JUMP, cpp_dev->base);
- msm_cpp_write(MSM_CPP_JUMP_ADDRESS, cpp_dev->base);
-
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
- msm_cpp_poll(cpp_dev->base, 0x1);
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_JUMP_ACK);
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_TRAILER);
-
- /*Get Bootloader Version*/
- msm_cpp_write(MSM_CPP_CMD_GET_BOOTLOADER_VER, cpp_dev->base);
- pr_info("MC Bootloader Version: 0x%x\n",
- msm_cpp_read(cpp_dev->base));
-
- /*Get Firmware Version*/
- msm_cpp_write(MSM_CPP_CMD_GET_FW_VER, cpp_dev->base);
- msm_cpp_write(MSM_CPP_MSG_ID_CMD, cpp_dev->base);
- msm_cpp_write(0x1, cpp_dev->base);
- msm_cpp_write(MSM_CPP_CMD_GET_FW_VER, cpp_dev->base);
- msm_cpp_write(MSM_CPP_MSG_ID_TRAILER, cpp_dev->base);
-
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
- msm_cpp_poll(cpp_dev->base, 0x2);
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_FW_VER);
- pr_info("CPP FW Version: 0x%x\n", msm_cpp_read(cpp_dev->base));
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_TRAILER);
- enable_irq(cpp_dev->irq->start);
- msm_camera_io_w_mb(0x8, cpp_dev->base +
- MSM_CPP_MICRO_IRQGEN_MASK);
- msm_camera_io_w_mb(0xFFFF, cpp_dev->base +
- MSM_CPP_MICRO_IRQGEN_CLR);
-}
-
static int cpp_init_hardware(struct cpp_device *cpp_dev)
{
int rc = 0;
@@ -663,8 +696,15 @@
cpp_dev->taskletq_idx = 0;
atomic_set(&cpp_dev->irq_cnt, 0);
msm_cpp_create_buff_queue(cpp_dev, MSM_CPP_MAX_BUFF_QUEUE);
- if (cpp_dev->is_firmware_loaded == 1)
- msm_cpp_boot_hw(cpp_dev);
+ if (cpp_dev->is_firmware_loaded == 1) {
+ disable_irq(cpp_dev->irq->start);
+ cpp_load_fw(cpp_dev, NULL);
+ enable_irq(cpp_dev->irq->start);
+ msm_camera_io_w_mb(0x7C8, cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_MASK);
+ msm_camera_io_w_mb(0xFFFF, cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_CLR);
+ }
return rc;
req_irq_fail:
iounmap(cpp_dev->cpp_hw_base);
@@ -713,47 +753,44 @@
const struct firmware *fw = NULL;
struct device *dev = &cpp_dev->pdev->dev;
- pr_debug("%s: FW file: %s\n", __func__, fw_name_bin);
- rc = request_firmware(&fw, fw_name_bin, dev);
- if (rc) {
- dev_err(dev, "Failed to locate blob %s from device %p, Error: %d\n",
- fw_name_bin, dev, rc);
- }
-
- CPP_DBG("HW Ver:0x%x\n",
- msm_camera_io_r(cpp_dev->base +
- MSM_CPP_MICRO_HW_VERSION));
-
+ msm_camera_io_w(0x1, cpp_dev->base + MSM_CPP_MICRO_CLKEN_CTL);
msm_camera_io_w(0x1, cpp_dev->base +
- MSM_CPP_MICRO_BOOT_START);
- /*Enable MC clock*/
- msm_camera_io_w(0x1, cpp_dev->base +
- MSM_CPP_MICRO_CLKEN_CTL);
-
+ MSM_CPP_MICRO_BOOT_START);
msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
- /*Start firmware loading*/
- msm_cpp_write(MSM_CPP_CMD_FW_LOAD, cpp_dev->base);
- msm_cpp_write(MSM_CPP_END_ADDRESS, cpp_dev->base);
- msm_cpp_write(MSM_CPP_START_ADDRESS, cpp_dev->base);
- if (NULL != fw)
- ptr_bin = (uint32_t *)fw->data;
+ if (fw_name_bin) {
+ pr_debug("%s: FW file: %s\n", __func__, fw_name_bin);
+ rc = request_firmware(&fw, fw_name_bin, dev);
+ if (rc) {
+ dev_err(dev,
+ "Fail to loc blob %s from dev %p, Error: %d\n",
+ fw_name_bin, dev, rc);
+ }
+ if (NULL != fw)
+ ptr_bin = (uint32_t *)fw->data;
- if (ptr_bin == NULL) {
- pr_err("ptr_bin is NULL\n");
- } else {
- for (i = 0; i < fw->size/4; i++) {
- if (ptr_bin) {
+ msm_camera_io_w(0x1, cpp_dev->base +
+ MSM_CPP_MICRO_BOOT_START);
+ msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
+ msm_camera_io_w(0xFFFFFFFF, cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_CLR);
+
+ /*Start firmware loading*/
+ msm_cpp_write(MSM_CPP_CMD_FW_LOAD, cpp_dev->base);
+ msm_cpp_write(MSM_CPP_END_ADDRESS, cpp_dev->base);
+ msm_cpp_write(MSM_CPP_START_ADDRESS, cpp_dev->base);
+
+ if (ptr_bin) {
+ for (i = 0; i < fw->size/4; i++) {
msm_cpp_write(*ptr_bin, cpp_dev->base);
ptr_bin++;
}
}
+ if (fw)
+ release_firmware(fw);
+ msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_OK);
+ msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
}
- if (fw)
- release_firmware(fw);
-
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_OK);
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
/*Trigger MC to jump to start address*/
msm_cpp_write(MSM_CPP_CMD_EXEC_JUMP, cpp_dev->base);
@@ -852,6 +889,36 @@
cpp_dev->cpp_open_cnt--;
if (cpp_dev->cpp_open_cnt == 0) {
+ pr_err("%s: irq_status: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x4));
+ pr_err("%s: DEBUG_SP: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x40));
+ pr_err("%s: DEBUG_T: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x44));
+ pr_err("%s: DEBUG_N: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x48));
+ pr_err("%s: DEBUG_R: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x4C));
+ pr_err("%s: DEBUG_OPPC: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x50));
+ pr_err("%s: DEBUG_MO: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x54));
+ pr_err("%s: DEBUG_TIMER0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x60));
+ pr_err("%s: DEBUG_TIMER1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x64));
+ pr_err("%s: DEBUG_GPI: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x70));
+ pr_err("%s: DEBUG_GPO: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x74));
+ pr_err("%s: DEBUG_T0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x80));
+ pr_err("%s: DEBUG_R0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x84));
+ pr_err("%s: DEBUG_T1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x88));
+ pr_err("%s: DEBUG_R1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x8C));
msm_camera_io_w(0x0, cpp_dev->base + MSM_CPP_MICRO_CLKEN_CTL);
cpp_deinit_mem(cpp_dev);
iommu_detach_device(cpp_dev->domain, cpp_dev->iommu_ctx);
@@ -969,23 +1036,140 @@
}
#endif
+void msm_cpp_do_timeout_work(struct work_struct *work)
+{
+ int ret;
+ uint32_t i = 0;
+ struct msm_cpp_frame_info_t *this_frame =
+ cpp_timers[del_timer_idx].data.processed_frame;
+ struct msm_cpp_frame_info_t *second_frame = NULL;
+
+ pr_err("cpp_timer_callback called idx:%d. (jiffies=%lu)\n",
+ del_timer_idx, jiffies);
+ pr_err("fatal: cpp_timer expired for identity=0x%x, frame_id=%03d",
+ this_frame->identity, this_frame->frame_id);
+ cpp_timers[del_timer_idx].used = 0;
+ cpp_timers[del_timer_idx].data.processed_frame = NULL;
+ del_timer_idx = 1 - del_timer_idx;
+
+ if (cpp_timers[del_timer_idx].used == 1) {
+ pr_err("deleting cpp_timer %d.\n", del_timer_idx);
+ del_timer(&cpp_timers[del_timer_idx].cpp_timer);
+ cpp_timers[del_timer_idx].used = 0;
+ second_frame = cpp_timers[del_timer_idx].data.processed_frame;
+ cpp_timers[del_timer_idx].data.processed_frame = NULL;
+ del_timer_idx = 1 - del_timer_idx;
+ }
+
+ disable_irq(cpp_timers[del_timer_idx].data.cpp_dev->irq->start);
+ pr_err("Reloading firmware\n");
+ cpp_load_fw(cpp_timers[del_timer_idx].data.cpp_dev, NULL);
+ pr_err("Firmware loading done\n");
+ enable_irq(cpp_timers[del_timer_idx].data.cpp_dev->irq->start);
+ msm_camera_io_w_mb(0x8, cpp_timers[del_timer_idx].data.cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_MASK);
+ msm_camera_io_w_mb(0xFFFF,
+ cpp_timers[del_timer_idx].data.cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_CLR);
+
+ cpp_timers[set_timer_idx].data.processed_frame = this_frame;
+ cpp_timers[set_timer_idx].used = 1;
+ pr_err("ReInstalling cpp_timer %d\n", set_timer_idx);
+ setup_timer(&cpp_timers[set_timer_idx].cpp_timer, cpp_timer_callback,
+ (unsigned long)&cpp_timers[0]);
+ pr_err("Starting timer to fire in %d ms. (jiffies=%lu)\n",
+ CPP_CMD_TIMEOUT_MS, jiffies);
+ ret = mod_timer(&cpp_timers[set_timer_idx].cpp_timer,
+ jiffies + msecs_to_jiffies(CPP_CMD_TIMEOUT_MS));
+ if (ret)
+ pr_err("error in mod_timer\n");
+
+ set_timer_idx = 1 - set_timer_idx;
+ pr_err("Rescheduling for identity=0x%x, frame_id=%03d",
+ this_frame->identity, this_frame->frame_id);
+ msm_cpp_write(0x6, cpp_timers[set_timer_idx].data.cpp_dev->base);
+ msm_cpp_dump_frame_cmd(this_frame->cpp_cmd_msg,
+ this_frame->msg_len);
+ for (i = 0; i < this_frame->msg_len; i++)
+ msm_cpp_write(this_frame->cpp_cmd_msg[i],
+ cpp_timers[set_timer_idx].data.cpp_dev->base);
+
+
+ if (second_frame != NULL) {
+ cpp_timers[set_timer_idx].data.processed_frame = second_frame;
+ cpp_timers[set_timer_idx].used = 1;
+ pr_err("ReInstalling cpp_timer %d\n", set_timer_idx);
+ setup_timer(&cpp_timers[set_timer_idx].cpp_timer,
+ cpp_timer_callback, (unsigned long)&cpp_timers[0]);
+ pr_err("Starting timer to fire in %d ms. (jiffies=%lu)\n",
+ CPP_CMD_TIMEOUT_MS, jiffies);
+ ret = mod_timer(&cpp_timers[set_timer_idx].cpp_timer,
+ jiffies + msecs_to_jiffies(CPP_CMD_TIMEOUT_MS));
+ if (ret)
+ pr_err("error in mod_timer\n");
+
+ set_timer_idx = 1 - set_timer_idx;
+ pr_err("Rescheduling for identity=0x%x, frame_id=%03d",
+ second_frame->identity, second_frame->frame_id);
+ msm_cpp_write(0x6,
+ cpp_timers[set_timer_idx].data.cpp_dev->base);
+ msm_cpp_dump_frame_cmd(second_frame->cpp_cmd_msg,
+ second_frame->msg_len);
+ for (i = 0; i < second_frame->msg_len; i++)
+ msm_cpp_write(second_frame->cpp_cmd_msg[i],
+ cpp_timers[set_timer_idx].data.cpp_dev->base);
+ }
+}
+
+void cpp_timer_callback(unsigned long data)
+{
+ struct msm_cpp_work_t *work =
+ cpp_timers[set_timer_idx].data.cpp_dev->work;
+ queue_work(cpp_timers[set_timer_idx].data.cpp_dev->timer_wq,
+ (struct work_struct *)work);
+}
+
static int msm_cpp_send_frame_to_hardware(struct cpp_device *cpp_dev,
struct msm_queue_cmd *frame_qcmd)
{
uint32_t i;
int32_t rc = -EAGAIN;
+ int ret;
struct msm_cpp_frame_info_t *process_frame;
if (cpp_dev->processing_q.len < MAX_CPP_PROCESSING_FRAME) {
process_frame = frame_qcmd->command;
msm_enqueue(&cpp_dev->processing_q,
&frame_qcmd->list_frame);
+
+ cpp_timers[set_timer_idx].data.processed_frame = process_frame;
+ cpp_timers[set_timer_idx].used = 1;
+ /* install timer for cpp timeout */
+ CPP_DBG("Installing cpp_timer %d\n", set_timer_idx);
+ setup_timer(&cpp_timers[set_timer_idx].cpp_timer,
+ cpp_timer_callback, (unsigned long)&cpp_timers[0]);
+ CPP_DBG("Starting timer to fire in %d ms. (jiffies=%lu)\n",
+ CPP_CMD_TIMEOUT_MS, jiffies);
+ ret = mod_timer(&cpp_timers[set_timer_idx].cpp_timer,
+ jiffies + msecs_to_jiffies(CPP_CMD_TIMEOUT_MS));
+ if (ret)
+ pr_err("error in mod_timer\n");
+
+ set_timer_idx = 1 - set_timer_idx;
+
msm_cpp_write(0x6, cpp_dev->base);
msm_cpp_dump_frame_cmd(process_frame->cpp_cmd_msg,
process_frame->msg_len);
- for (i = 0; i < process_frame->msg_len; i++)
- msm_cpp_write(process_frame->cpp_cmd_msg[i],
- cpp_dev->base);
+ for (i = 0; i < process_frame->msg_len; i++) {
+ if ((induce_error) && (i == 1)) {
+ pr_err("Induce error\n");
+ msm_cpp_write(process_frame->cpp_cmd_msg[i]-1,
+ cpp_dev->base);
+ induce_error--;
+ } else
+ msm_cpp_write(process_frame->cpp_cmd_msg[i],
+ cpp_dev->base);
+ }
do_gettimeofday(&(process_frame->in_time));
rc = 0;
}
@@ -1097,7 +1281,7 @@
&buff_mgr_info);
if (rc < 0) {
rc = -EAGAIN;
- pr_err("error getting buffer rc:%d\n", rc);
+ pr_debug("error getting buffer rc:%d\n", rc);
goto ERROR2;
}
new_frame->output_buffer_info[1].index = buff_mgr_info.index;
@@ -1184,7 +1368,6 @@
struct cpp_device *cpp_dev = v4l2_get_subdevdata(sd);
struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
int rc = 0;
- char *fw_name_bin;
if (ioctl_ptr == NULL) {
pr_err("ioctl_ptr is null\n");
@@ -1206,8 +1389,11 @@
case VIDIOC_MSM_CPP_LOAD_FIRMWARE: {
if (cpp_dev->is_firmware_loaded == 0) {
- fw_name_bin = kzalloc(ioctl_ptr->len+1, GFP_KERNEL);
- if (!fw_name_bin) {
+ kfree(cpp_dev->fw_name_bin);
+ cpp_dev->fw_name_bin = NULL;
+ cpp_dev->fw_name_bin = kzalloc(ioctl_ptr->len+1,
+ GFP_KERNEL);
+ if (!cpp_dev->fw_name_bin) {
pr_err("%s:%d: malloc error\n", __func__,
__LINE__);
mutex_unlock(&cpp_dev->mutex);
@@ -1222,24 +1408,24 @@
pr_err("ioctl_ptr->len is 0\n");
return -EINVAL;
}
- rc = (copy_from_user(fw_name_bin,
+ rc = (copy_from_user(cpp_dev->fw_name_bin,
(void __user *)ioctl_ptr->ioctl_ptr,
ioctl_ptr->len) ? -EFAULT : 0);
if (rc) {
ERR_COPY_FROM_USER();
- kfree(fw_name_bin);
+ kfree(cpp_dev->fw_name_bin);
+ cpp_dev->fw_name_bin = NULL;
mutex_unlock(&cpp_dev->mutex);
return -EINVAL;
}
- *(fw_name_bin+ioctl_ptr->len) = '\0';
+ *(cpp_dev->fw_name_bin+ioctl_ptr->len) = '\0';
if (cpp_dev == NULL) {
pr_err("cpp_dev is null\n");
return -EINVAL;
}
disable_irq(cpp_dev->irq->start);
- cpp_load_fw(cpp_dev, fw_name_bin);
- kfree(fw_name_bin);
+ cpp_load_fw(cpp_dev, cpp_dev->fw_name_bin);
enable_irq(cpp_dev->irq->start);
cpp_dev->is_firmware_loaded = 1;
}
@@ -1587,17 +1773,27 @@
MSM_CPP_MICRO_IRQGEN_MASK);
msm_camera_io_w(0xFFFF, cpp_dev->base +
MSM_CPP_MICRO_IRQGEN_CLR);
-
+ msm_camera_io_w(0x80000000, cpp_dev->base + 0xF0);
cpp_release_hardware(cpp_dev);
cpp_dev->state = CPP_STATE_OFF;
+ msm_cpp_enable_debugfs(cpp_dev);
msm_queue_init(&cpp_dev->eventData_q, "eventdata");
msm_queue_init(&cpp_dev->processing_q, "frame");
INIT_LIST_HEAD(&cpp_dev->tasklet_q);
tasklet_init(&cpp_dev->cpp_tasklet, msm_cpp_do_tasklet,
(unsigned long)cpp_dev);
+ cpp_dev->timer_wq = create_workqueue("msm_cpp_workqueue");
+ cpp_dev->work = kmalloc(sizeof(struct msm_cpp_work_t),
+ GFP_KERNEL);
+ INIT_WORK((struct work_struct *)cpp_dev->work, msm_cpp_do_timeout_work);
cpp_dev->cpp_open_cnt = 0;
cpp_dev->is_firmware_loaded = 0;
+ cpp_timers[0].data.cpp_dev = cpp_dev;
+ cpp_timers[1].data.cpp_dev = cpp_dev;
+ cpp_timers[0].used = 0;
+ cpp_timers[1].used = 0;
+ cpp_dev->fw_name_bin = NULL;
return rc;
ERROR3:
release_mem_region(cpp_dev->mem->start, resource_size(cpp_dev->mem));
@@ -1635,6 +1831,8 @@
release_mem_region(cpp_dev->cpp_hw_mem->start,
resource_size(cpp_dev->cpp_hw_mem));
mutex_destroy(&cpp_dev->mutex);
+ kfree(cpp_dev->work);
+ destroy_workqueue(cpp_dev->timer_wq);
kfree(cpp_dev->cpp_clk);
kfree(cpp_dev);
return 0;
@@ -1660,6 +1858,30 @@
platform_driver_unregister(&cpp_driver);
}
+static int msm_cpp_debugfs_error_s(void *data, u64 val)
+{
+ pr_err("setting error inducement");
+ induce_error = val;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cpp_debugfs_error, NULL,
+ msm_cpp_debugfs_error_s, "%llu\n");
+
+static int msm_cpp_enable_debugfs(struct cpp_device *cpp_dev)
+{
+ struct dentry *debugfs_base;
+ debugfs_base = debugfs_create_dir("msm_cpp", NULL);
+ if (!debugfs_base)
+ return -ENOMEM;
+
+ if (!debugfs_create_file("error", S_IRUGO | S_IWUSR, debugfs_base,
+ (void *)cpp_dev, &cpp_debugfs_error))
+ return -ENOMEM;
+
+ return 0;
+}
+
module_init(msm_cpp_init_module);
module_exit(msm_cpp_exit_module);
MODULE_DESCRIPTION("MSM CPP driver");
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
index 36a5fa5..0a70d37 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
@@ -147,6 +147,11 @@
struct list_head native_buff_head;
};
+struct msm_cpp_work_t {
+ struct work_struct my_work;
+ struct cpp_device *cpp_dev;
+};
+
struct cpp_device {
struct platform_device *pdev;
struct msm_sd_subdev msm_sd;
@@ -165,6 +170,9 @@
struct mutex mutex;
enum cpp_state state;
uint8_t is_firmware_loaded;
+ char *fw_name_bin;
+ struct workqueue_struct *timer_wq;
+ struct msm_cpp_work_t *work;
int domain_num;
struct iommu_domain *domain;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/Makefile b/drivers/media/platform/msm/camera_v2/sensor/Makefile
index 94ed560..40931ef 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/Makefile
+++ b/drivers/media/platform/msm/camera_v2/sensor/Makefile
@@ -10,6 +10,7 @@
obj-$(CONFIG_OV8825) += ov8825.o
obj-$(CONFIG_OV2720) += ov2720.o
obj-$(CONFIG_OV9724) += ov9724.o
+obj-$(CONFIG_HI256) += hi256.o
obj-$(CONFIG_MT9M114) += mt9m114.o
obj-$(CONFIG_SP1628) += sp1628.o
obj-$(CONFIG_GC0339) += gc0339.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
index 676862f..a6c5639 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
@@ -111,9 +111,30 @@
msm_camera_io_w(1 << master, cci_dev->base + CCI_HALT_REQ_ADDR);
rc = wait_for_completion_interruptible_timeout(
&cci_dev->cci_master_info[master].reset_complete, CCI_TIMEOUT);
- if (rc <= 0)
- pr_err("%s: wait_for_completion_interruptible_timeout %d\n",
- __func__, __LINE__);
+ if (rc < 0) {
+ pr_err("%s:%d wait failed\n", __func__, __LINE__);
+ } else if (rc == 0) {
+ pr_err("%s:%d wait timeout\n", __func__, __LINE__);
+
+ /* Set reset pending flag to TRUE */
+ cci_dev->cci_master_info[master].reset_pending = TRUE;
+
+ /* Set proper mask to RESET CMD address based on MASTER */
+ if (master == MASTER_0)
+ msm_camera_io_w(CCI_M0_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ else
+ msm_camera_io_w(CCI_M1_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+
+ /* wait for reset done irq */
+ rc = wait_for_completion_interruptible_timeout(
+ &cci_dev->cci_master_info[master].reset_complete,
+ CCI_TIMEOUT);
+ if (rc <= 0)
+ pr_err("%s:%d wait failed %d\n", __func__, __LINE__,
+ rc);
+ }
return;
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/hi256.c b/drivers/media/platform/msm/camera_v2/sensor/hi256.c
new file mode 100644
index 0000000..4190d7b
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/hi256.c
@@ -0,0 +1,1318 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include "msm_sensor.h"
+#include "msm_cci.h"
+#include "msm_camera_io_util.h"
+#define HI256_SENSOR_NAME "hi256"
+#define PLATFORM_DRIVER_NAME "msm_camera_hi256"
+
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+
+DEFINE_MSM_MUTEX(hi256_mut);
+static struct msm_sensor_ctrl_t hi256_s_ctrl;
+
+static struct msm_sensor_power_setting hi256_power_setting[] = {
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VIO,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VANA,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VDIG,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 10,
+ },
+ {
+ .seq_type = SENSOR_CLK,
+ .seq_val = SENSOR_CAM_MCLK,
+ .config_val = 24000000,
+ .delay = 10,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 20,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 10,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 30,
+ },
+ {
+ .seq_type = SENSOR_I2C_MUX,
+ .seq_val = 0,
+ .config_val = 0,
+ .delay = 0,
+ },
+};
+
+static struct msm_camera_i2c_reg_conf hi256_start_settings[] = {
+ {0x03, 0x00},
+ {0x01, 0xf0},
+};
+
+static struct msm_camera_i2c_reg_conf hi256_stop_settings[] = {
+ {0x03, 0x00},
+ {0x01, 0xf1},
+};
+
+static struct msm_camera_i2c_reg_conf hi256_recommend_settings[] = {
+ {0x01, 0xf1},
+ {0x01, 0xf3},
+ {0x01, 0xf1},
+
+ {0x08, 0x0f},
+ {0x0a, 0x00},
+
+ {0x03, 0x20},
+ {0x10, 0x1c},
+ {0x03, 0x22},
+ {0x10, 0x69},
+
+ {0x03, 0x00},
+ {0x10, 0x90},
+ {0x11, 0x93},
+ {0x12, 0x00},
+ {0x0b, 0xaa},
+ {0x0c, 0xaa},
+ {0x0d, 0xaa},
+ {0x20, 0x00},
+ {0x21, 0x06},
+ {0x22, 0x00},
+ {0x23, 0x05},
+ {0x24, 0x04},
+ {0x25, 0xb0},
+ {0x26, 0x06},
+ {0x27, 0x40},
+ {0x40, 0x01},
+ {0x41, 0x68},
+ {0x42, 0x00},
+ {0x43, 0x12},
+ {0x45, 0x04},
+ {0x46, 0x18},
+ {0x47, 0xd8},
+ {0x80, 0x2e},
+ {0x81, 0x7e},
+ {0x82, 0x90},
+ {0x83, 0x00},
+ {0x84, 0x0c},
+ {0x85, 0x00},
+ {0x90, 0x0c},
+ {0x91, 0x0c},
+ {0x92, 0x78},
+ {0x93, 0x70},
+ {0x94, 0xff},
+ {0x95, 0xff},
+ {0x96, 0xdc},
+ {0x97, 0xfe},
+ {0x98, 0x38},
+ {0xa0, 0x45},
+ {0xa2, 0x45},
+ {0xa4, 0x45},
+ {0xa6, 0x45},
+ {0xa8, 0x45},
+ {0xaa, 0x45},
+ {0xac, 0x45},
+ {0xae, 0x45},
+ {0x99, 0x43},
+ {0x9a, 0x43},
+ {0x9b, 0x43},
+ {0x9c, 0x43},
+ {0x03, 0x02},
+ {0x12, 0x03},
+ {0x13, 0x03},
+ {0x15, 0x00},
+ {0x16, 0x00},
+ {0x17, 0x8C},
+ {0x18, 0x4c},
+ {0x19, 0x00},
+ {0x1a, 0x39},
+ {0x1c, 0x09},
+ {0x1d, 0x40},
+ {0x1e, 0x30},
+ {0x1f, 0x10},
+ {0x20, 0x77},
+ {0x21, 0x6d},
+ {0x22, 0x77},
+ {0x23, 0x30},
+ {0x24, 0x77},
+ {0x27, 0x3c},
+ {0x2b, 0x80},
+ {0x2e, 0x00},
+ {0x2f, 0x00},
+ {0x30, 0x05},
+ {0x50, 0x20},
+ {0x52, 0x01},
+ {0x53, 0xc1},
+ {0x55, 0x1c},
+ {0x56, 0x11},
+ {0x58, 0x22},
+ {0x59, 0x20},
+ {0x5d, 0xa2},
+ {0x5e, 0x5a},
+ {0x60, 0x87},
+ {0x61, 0x99},
+ {0x62, 0x88},
+ {0x63, 0x97},
+ {0x64, 0x88},
+ {0x65, 0x97},
+ {0x67, 0x0c},
+ {0x68, 0x0c},
+ {0x69, 0x0c},
+ {0x72, 0x89},
+ {0x73, 0x96},
+ {0x74, 0x89},
+ {0x75, 0x96},
+ {0x76, 0x89},
+ {0x77, 0x96},
+ {0x7c, 0x85},
+ {0x7d, 0xaf},
+ {0x80, 0x01},
+ {0x81, 0x7f},
+ {0x82, 0x13},
+ {0x83, 0x24},
+ {0x84, 0x7d},
+ {0x85, 0x81},
+ {0x86, 0x7d},
+ {0x87, 0x81},
+ {0x92, 0x48},
+ {0x93, 0x54},
+ {0x94, 0x7d},
+ {0x95, 0x81},
+ {0x96, 0x7d},
+ {0x97, 0x81},
+ {0xa0, 0x02},
+ {0xa1, 0x7b},
+ {0xa2, 0x02},
+ {0xa3, 0x7b},
+ {0xa4, 0x7b},
+ {0xa5, 0x02},
+ {0xa6, 0x7b},
+ {0xa7, 0x02},
+ {0xa8, 0x85},
+ {0xa9, 0x8c},
+ {0xaa, 0x85},
+ {0xab, 0x8c},
+ {0xac, 0x10},
+ {0xad, 0x16},
+ {0xae, 0x10},
+ {0xaf, 0x16},
+ {0xb0, 0x99},
+ {0xb1, 0xa3},
+ {0xb2, 0xa4},
+ {0xb3, 0xae},
+ {0xb4, 0x9b},
+ {0xb5, 0xa2},
+ {0xb6, 0xa6},
+ {0xb7, 0xac},
+ {0xb8, 0x9b},
+ {0xb9, 0x9f},
+ {0xba, 0xa6},
+ {0xbb, 0xaa},
+ {0xbc, 0x9b},
+ {0xbd, 0x9f},
+ {0xbe, 0xa6},
+ {0xbf, 0xaa},
+ {0xc4, 0x2c},
+ {0xc5, 0x43},
+ {0xc6, 0x63},
+ {0xc7, 0x79},
+ {0xc8, 0x2d},
+ {0xc9, 0x42},
+ {0xca, 0x2d},
+ {0xcb, 0x42},
+ {0xcc, 0x64},
+ {0xcd, 0x78},
+ {0xce, 0x64},
+ {0xcf, 0x78},
+ {0xd0, 0x0a},
+ {0xd1, 0x09},
+ {0xd4, 0x0c},
+ {0xd5, 0x0c},
+ {0xd6, 0x78},
+ {0xd7, 0x70},
+ {0xe0, 0xc4},
+ {0xe1, 0xc4},
+ {0xe2, 0xc4},
+ {0xe3, 0xc4},
+ {0xe4, 0x00},
+ {0xe8, 0x80},
+ {0xe9, 0x40},
+ {0xea, 0x7f},
+ {0xf0, 0xc1},
+ {0xf1, 0xc1},
+ {0xf2, 0xc1},
+ {0xf3, 0xc1},
+ {0xf4, 0xc1},
+ {0x03, 0x03},
+ {0x10, 0x10},
+ {0x03, 0x10},
+ {0x10, 0x03},
+ {0x12, 0x30},
+ {0x13, 0x02},
+ {0x20, 0x00},
+ {0x30, 0x00},
+ {0x31, 0x00},
+ {0x32, 0x00},
+ {0x33, 0x00},
+ {0x34, 0x30},
+ {0x35, 0x00},
+ {0x36, 0x00},
+ {0x38, 0x00},
+ {0x3e, 0x58},
+ {0x3f, 0x00},
+ {0x40, 0x80},
+ {0x41, 0x00},
+ {0x48, 0x95},
+ {0x60, 0x67},
+ {0x61, 0x88},
+ {0x62, 0x90},
+ {0x63, 0x50},
+ {0x64, 0x41},
+ {0x66, 0x42},
+ {0x67, 0x20},
+ {0x6a, 0x71},
+ {0x6b, 0x84},
+ {0x6c, 0x72},
+ {0x6d, 0x83},
+ {0x03, 0x11},
+ {0x10, 0x7f},
+ {0x11, 0x40},
+ {0x12, 0x0a},
+ {0x13, 0xbb},
+ {0x26, 0x31},
+ {0x27, 0x34},
+ {0x28, 0x0f},
+ {0x29, 0x10},
+ {0x2b, 0x30},
+ {0x2c, 0x32},
+ {0x30, 0x70},
+ {0x31, 0x10},
+ {0x32, 0x58},
+ {0x33, 0x09},
+ {0x34, 0x06},
+ {0x35, 0x03},
+ {0x36, 0x70},
+ {0x37, 0x18},
+ {0x38, 0x58},
+ {0x39, 0x09},
+ {0x3a, 0x06},
+ {0x3b, 0x03},
+ {0x3c, 0x80},
+ {0x3d, 0x18},
+ {0x3e, 0x80},
+ {0x3f, 0x0c},
+ {0x40, 0x05},
+ {0x41, 0x06},
+ {0x42, 0x80},
+ {0x43, 0x18},
+ {0x44, 0x80},
+ {0x45, 0x0c},
+ {0x46, 0x05},
+ {0x47, 0x06},
+ {0x48, 0x90},
+ {0x49, 0x40},
+ {0x4a, 0x80},
+ {0x4b, 0x13},
+ {0x4c, 0x10},
+ {0x4d, 0x11},
+ {0x4e, 0x80},
+ {0x4f, 0x30},
+ {0x50, 0x80},
+ {0x51, 0x13},
+ {0x52, 0x10},
+ {0x53, 0x13},
+ {0x54, 0x11},
+ {0x55, 0x17},
+ {0x56, 0x20},
+ {0x57, 0x01},
+ {0x58, 0x00},
+ {0x59, 0x00},
+ {0x5a, 0x18},
+ {0x5b, 0x00},
+ {0x5c, 0x00},
+ {0x60, 0x3f},
+ {0x62, 0x60},
+ {0x70, 0x06},
+ {0x03, 0x12},
+ {0x20, 0x00},
+ {0x21, 0x00},
+ {0x25, 0x00},
+ {0x28, 0x00},
+ {0x29, 0x00},
+ {0x2a, 0x00},
+ {0x30, 0x50},
+ {0x31, 0x18},
+ {0x32, 0x32},
+ {0x33, 0x40},
+ {0x34, 0x50},
+ {0x35, 0x70},
+ {0x36, 0xa0},
+ {0x40, 0xa0},
+ {0x41, 0x40},
+ {0x42, 0xa0},
+ {0x43, 0x90},
+ {0x44, 0x90},
+ {0x45, 0x80},
+ {0x46, 0xb0},
+ {0x47, 0x55},
+ {0x48, 0xa0},
+ {0x49, 0x90},
+ {0x4a, 0x90},
+ {0x4b, 0x80},
+ {0x4c, 0xb0},
+ {0x4d, 0x40},
+ {0x4e, 0x90},
+ {0x4f, 0x60},
+ {0x50, 0xa0},
+ {0x51, 0x80},
+ {0x52, 0xb0},
+ {0x53, 0x40},
+ {0x54, 0x90},
+ {0x55, 0x60},
+ {0x56, 0xa0},
+ {0x57, 0x80},
+ {0x58, 0x90},
+ {0x59, 0x40},
+ {0x5a, 0xd0},
+ {0x5b, 0xd0},
+ {0x5c, 0xe0},
+ {0x5d, 0x80},
+ {0x5e, 0x88},
+ {0x5f, 0x40},
+ {0x60, 0xe0},
+ {0x61, 0xe0},
+ {0x62, 0xe0},
+ {0x63, 0x80},
+ {0x70, 0x15},
+ {0x71, 0x01},
+ {0x72, 0x18},
+ {0x73, 0x01},
+ {0x74, 0x25},
+ {0x75, 0x15},
+ {0x80, 0x20},
+ {0x81, 0x40},
+ {0x82, 0x65},
+ {0x85, 0x1a},
+ {0x88, 0x00},
+ {0x89, 0x00},
+ {0x90, 0x5d},
+ {0xD0, 0x0c},
+ {0xD1, 0x80},
+ {0xD2, 0x17},
+ {0xD3, 0x00},
+ {0xD4, 0x00},
+ {0xD5, 0x0f},
+ {0xD6, 0xff},
+ {0xD7, 0xff},
+ {0x3b, 0x06},
+ {0x3c, 0x06},
+ {0xc5, 0x00},
+ {0xc6, 0x00},
+ {0x03, 0x13},
+ {0x10, 0xcb},
+ {0x11, 0x7b},
+ {0x12, 0x07},
+ {0x14, 0x00},
+ {0x20, 0x15},
+ {0x21, 0x13},
+ {0x22, 0x33},
+ {0x23, 0x05},
+ {0x24, 0x09},
+ {0x25, 0x0a},
+ {0x26, 0x18},
+ {0x27, 0x30},
+ {0x29, 0x12},
+ {0x2a, 0x50},
+ {0x2b, 0x02},
+ {0x2c, 0x02},
+ {0x25, 0x06},
+ {0x2d, 0x0c},
+ {0x2e, 0x12},
+ {0x2f, 0x12},
+ {0x50, 0x10},
+ {0x51, 0x14},
+ {0x52, 0x12},
+ {0x53, 0x0c},
+ {0x54, 0x0f},
+ {0x55, 0x0c},
+ {0x56, 0x10},
+ {0x57, 0x13},
+ {0x58, 0x12},
+ {0x59, 0x0c},
+ {0x5a, 0x0f},
+ {0x5b, 0x0c},
+ {0x5c, 0x25},
+ {0x5d, 0x25},
+ {0x5e, 0x25},
+ {0x5f, 0x25},
+ {0x60, 0x25},
+ {0x61, 0x25},
+ {0x62, 0x25},
+ {0x63, 0x25},
+ {0x64, 0x25},
+ {0x65, 0x25},
+ {0x66, 0x25},
+ {0x67, 0x25},
+ {0x68, 0x07},
+ {0x69, 0x07},
+ {0x6a, 0x07},
+ {0x6b, 0x05},
+ {0x6c, 0x05},
+ {0x6d, 0x05},
+ {0x6e, 0x07},
+ {0x6f, 0x07},
+ {0x70, 0x07},
+ {0x71, 0x05},
+ {0x72, 0x05},
+ {0x73, 0x05},
+ {0x80, 0x01},
+ {0x81, 0x1f},
+ {0x82, 0x05},
+ {0x83, 0x31},
+ {0x90, 0x05},
+ {0x91, 0x05},
+ {0x92, 0x33},
+ {0x93, 0x30},
+ {0x94, 0x03},
+ {0x95, 0x14},
+ {0x97, 0x20},
+ {0x99, 0x20},
+ {0xa0, 0x01},
+ {0xa1, 0x02},
+ {0xa2, 0x01},
+ {0xa3, 0x02},
+ {0xa4, 0x05},
+ {0xa5, 0x05},
+ {0xa6, 0x07},
+ {0xa7, 0x08},
+ {0xa8, 0x07},
+ {0xa9, 0x08},
+ {0xaa, 0x07},
+ {0xab, 0x08},
+ {0xb0, 0x22},
+ {0xb1, 0x2a},
+ {0xb2, 0x28},
+ {0xb3, 0x22},
+ {0xb4, 0x2a},
+ {0xb5, 0x28},
+ {0xb6, 0x22},
+ {0xb7, 0x2a},
+ {0xb8, 0x28},
+ {0xb9, 0x22},
+ {0xba, 0x2a},
+ {0xbb, 0x28},
+ {0xbc, 0x25},
+ {0xbd, 0x2a},
+ {0xbe, 0x27},
+ {0xbf, 0x25},
+ {0xc0, 0x2a},
+ {0xc1, 0x27},
+ {0xc2, 0x1e},
+ {0xc3, 0x24},
+ {0xc4, 0x20},
+ {0xc5, 0x1e},
+ {0xc6, 0x24},
+ {0xc7, 0x20},
+ {0xc8, 0x18},
+ {0xc9, 0x20},
+ {0xca, 0x1e},
+ {0xcb, 0x18},
+ {0xcc, 0x20},
+ {0xcd, 0x1e},
+ {0xce, 0x18},
+ {0xcf, 0x20},
+ {0xd0, 0x1e},
+ {0xd1, 0x18},
+ {0xd2, 0x20},
+ {0xd3, 0x1e},
+ {0x03, 0x14},
+ {0x10, 0x11},
+ {0x14, 0x80},
+ {0x15, 0x80},
+ {0x16, 0x80},
+ {0x17, 0x80},
+ {0x18, 0x80},
+ {0x19, 0x80},
+ {0x20, 0x80},
+ {0x21, 0x80},
+ {0x22, 0x80},
+ {0x23, 0x80},
+ {0x24, 0x80},
+ {0x30, 0xc8},
+ {0x31, 0x2b},
+ {0x32, 0x00},
+ {0x33, 0x00},
+ {0x34, 0x90},
+ {0x40, 0x32},
+ {0x50, 0x21},
+ {0x60, 0x19},
+ {0x70, 0x21},
+ {0x03, 0x15},
+ {0x10, 0x0f},
+ {0x14, 0x46},
+ {0x15, 0x36},
+ {0x16, 0x26},
+ {0x17, 0x2f},
+ {0x30, 0x8f},
+ {0x31, 0x59},
+ {0x32, 0x0a},
+ {0x33, 0x15},
+ {0x34, 0x5b},
+ {0x35, 0x06},
+ {0x36, 0x07},
+ {0x37, 0x40},
+ {0x38, 0x87},
+ {0x40, 0x94},
+ {0x41, 0x20},
+ {0x42, 0x89},
+ {0x43, 0x84},
+ {0x44, 0x03},
+ {0x45, 0x01},
+ {0x46, 0x88},
+ {0x47, 0x9c},
+ {0x48, 0x28},
+ {0x50, 0x02},
+ {0x51, 0x82},
+ {0x52, 0x00},
+ {0x53, 0x07},
+ {0x54, 0x11},
+ {0x55, 0x98},
+ {0x56, 0x00},
+ {0x57, 0x0b},
+ {0x58, 0x8b},
+ {0x80, 0x03},
+ {0x85, 0x40},
+ {0x87, 0x02},
+ {0x88, 0x00},
+ {0x89, 0x00},
+ {0x8a, 0x00},
+ {0x03, 0x16},
+ {0x10, 0x31},
+ {0x18, 0x5e},
+ {0x19, 0x5d},
+ {0x1a, 0x0e},
+ {0x1b, 0x01},
+ {0x1c, 0xdc},
+ {0x1d, 0xfe},
+ {0x30, 0x00},
+ {0x31, 0x0a},
+ {0x32, 0x1f},
+ {0x33, 0x33},
+ {0x34, 0x53},
+ {0x35, 0x6c},
+ {0x36, 0x81},
+ {0x37, 0x94},
+ {0x38, 0xa4},
+ {0x39, 0xb3},
+ {0x3a, 0xc0},
+ {0x3b, 0xcb},
+ {0x3c, 0xd5},
+ {0x3d, 0xde},
+ {0x3e, 0xe6},
+ {0x3f, 0xee},
+ {0x40, 0xf5},
+ {0x41, 0xfc},
+ {0x42, 0xff},
+ {0x50, 0x00},
+ {0x51, 0x08},
+ {0x52, 0x1e},
+ {0x53, 0x36},
+ {0x54, 0x5a},
+ {0x55, 0x75},
+ {0x56, 0x8d},
+ {0x57, 0xa1},
+ {0x58, 0xb2},
+ {0x59, 0xbe},
+ {0x5a, 0xc9},
+ {0x5b, 0xd2},
+ {0x5c, 0xdb},
+ {0x5d, 0xe3},
+ {0x5e, 0xeb},
+ {0x5f, 0xf0},
+ {0x60, 0xf5},
+ {0x61, 0xf7},
+ {0x62, 0xf8},
+ {0x70, 0x00},
+ {0x71, 0x08},
+ {0x72, 0x17},
+ {0x73, 0x2f},
+ {0x74, 0x53},
+ {0x75, 0x6c},
+ {0x76, 0x81},
+ {0x77, 0x94},
+ {0x78, 0xa4},
+ {0x79, 0xb3},
+ {0x7a, 0xc0},
+ {0x7b, 0xcb},
+ {0x7c, 0xd5},
+ {0x7d, 0xde},
+ {0x7e, 0xe6},
+ {0x7f, 0xee},
+ {0x80, 0xf4},
+ {0x81, 0xfa},
+ {0x82, 0xff},
+ {0x03, 0x17},
+ {0x10, 0xf7},
+ {0xC4, 0x66},
+ {0xC5, 0x55},
+ {0x03, 0x20},
+ {0x11, 0x1c},
+ {0x18, 0x30},
+ {0x1a, 0x08},
+ {0x20, 0x05},
+ {0x21, 0x30},
+ {0x22, 0x10},
+ {0x23, 0x00},
+ {0x24, 0x00},
+ {0x28, 0xe7},
+ {0x29, 0x0d},
+ {0x2a, 0xf0},
+ {0x2b, 0x34},
+ {0x30, 0x78},
+ {0x2c, 0xc2},
+ {0x2d, 0xff},
+ {0x2e, 0x33},
+ {0x30, 0x78},
+ {0x32, 0x03},
+ {0x33, 0x2e},
+ {0x34, 0x30},
+ {0x35, 0xd4},
+ {0x36, 0xfe},
+ {0x37, 0x32},
+ {0x38, 0x04},
+ {0x39, 0x22},
+ {0x3a, 0xde},
+ {0x3b, 0x22},
+ {0x3c, 0xde},
+ {0x50, 0x45},
+ {0x51, 0x88},
+ {0x56, 0x03},
+ {0x57, 0xf7},
+ {0x58, 0x14},
+ {0x59, 0x88},
+ {0x5a, 0x04},
+ {0x60, 0xaa},
+ {0x61, 0xaa},
+ {0x62, 0xaa},
+ {0x63, 0xaa},
+ {0x64, 0xaa},
+ {0x65, 0xaa},
+ {0x66, 0xab},
+ {0x67, 0xEa},
+ {0x68, 0xab},
+ {0x69, 0xEa},
+ {0x6a, 0xaa},
+ {0x6b, 0xaa},
+ {0x6c, 0xaa},
+ {0x6d, 0xaa},
+ {0x6e, 0xaa},
+ {0x6f, 0xaa},
+ {0x70, 0x76},
+ {0x71, 0x80},
+ {0x76, 0x43},
+ {0x77, 0x04},
+ {0x78, 0x23},
+ {0x79, 0x46},
+ {0x7a, 0x23},
+ {0x7b, 0x22},
+ {0x7d, 0x23},
+ {0x83, 0x01},
+ {0x84, 0x7c},
+ {0x85, 0xdc},
+ {0x86, 0x01},
+ {0x87, 0xf4},
+ {0x88, 0x05},
+ {0x89, 0xf3},
+ {0x8a, 0x70},
+ {0x8B, 0x7e},
+ {0x8C, 0xf4},
+ {0x8D, 0x69},
+ {0x8E, 0x78},
+ {0x9c, 0x17},
+ {0x9d, 0x70},
+ {0x9e, 0x01},
+ {0x9f, 0xf4},
+ {0xb0, 0x18},
+ {0xb1, 0x14},
+ {0xb2, 0x80},
+ {0xb3, 0x18},
+ {0xb4, 0x1a},
+ {0xb5, 0x44},
+ {0xb6, 0x2f},
+ {0xb7, 0x28},
+ {0xb8, 0x25},
+ {0xb9, 0x22},
+ {0xba, 0x21},
+ {0xbb, 0x20},
+ {0xbc, 0x32},
+ {0xbd, 0x30},
+ {0xc0, 0x10},
+ {0xc1, 0x2b},
+ {0xc2, 0x2b},
+ {0xc3, 0x2b},
+ {0xc4, 0x08},
+ {0xc8, 0x40},
+ {0xc9, 0x40},
+ {0x03, 0x22},
+ {0x10, 0xfd},
+ {0x11, 0x2e},
+ {0x19, 0x01},
+ {0x20, 0x10},
+ {0x21, 0x80},
+ {0x24, 0x01},
+ {0x30, 0x80},
+ {0x31, 0x80},
+ {0x38, 0x11},
+ {0x39, 0x34},
+ {0x40, 0xfa},
+ {0x41, 0x44},
+ {0x42, 0x43},
+ {0x43, 0xf6},
+ {0x44, 0x44},
+ {0x45, 0x33},
+ {0x46, 0x00},
+ {0x50, 0xb2},
+ {0x51, 0x81},
+ {0x52, 0x98},
+ {0x80, 0x38},
+ {0x81, 0x20},
+ {0x82, 0x38},
+ {0x83, 0x5e},
+ {0x84, 0x18},
+ {0x85, 0x58},
+ {0x86, 0x20},
+ {0x87, 0x49},
+ {0x88, 0x33},
+ {0x89, 0x37},
+ {0x8a, 0x2a},
+ {0x8b, 0x41},
+ {0x8c, 0x39},
+ {0x8d, 0x34},
+ {0x8e, 0x29},
+ {0x8f, 0x53},
+ {0x90, 0x52},
+ {0x91, 0x51},
+ {0x92, 0x4e},
+ {0x93, 0x46},
+ {0x94, 0x3d},
+ {0x95, 0x34},
+ {0x96, 0x2e},
+ {0x97, 0x29},
+ {0x98, 0x22},
+ {0x99, 0x1c},
+ {0x9a, 0x18},
+ {0x9b, 0x77},
+ {0x9c, 0x77},
+ {0x9d, 0x48},
+ {0x9e, 0x38},
+ {0x9f, 0x30},
+ {0xa0, 0x60},
+ {0xa1, 0x34},
+ {0xa2, 0x6f},
+ {0xa3, 0xff},
+ {0xa4, 0x14},
+ {0xa5, 0x2c},
+ {0xa6, 0xcf},
+ {0xad, 0x40},
+ {0xae, 0x4a},
+ {0xaf, 0x28},
+ {0xb0, 0x26},
+ {0xb1, 0x00},
+ {0xb4, 0xea},
+ {0xb8, 0xa0},
+ {0xb9, 0x00},
+ {0x03, 0x48},
+ {0x70, 0x03},
+ {0x71, 0x78},
+ {0x72, 0x85},
+ {0x73, 0x10},
+ {0x70, 0x85},
+ {0x03, 0x48},
+ {0x03, 0x48},
+ {0x03, 0x48},
+ {0x03, 0x48},
+ {0x70, 0x95},
+ {0x10, 0x1c},
+ {0x11, 0x10},
+ {0x12, 0x00},
+ {0x14, 0x00},
+ {0x16, 0x04},
+ {0x18, 0x80},
+ {0x19, 0x00},
+ {0x1a, 0xa0},
+ {0x1b, 0x0d},
+ {0x1c, 0x01},
+ {0x1d, 0x0a},
+ {0x1e, 0x07},
+ {0x1f, 0x0b},
+ {0x23, 0x01},
+ {0x24, 0x1e},
+ {0x25, 0x00},
+ {0x26, 0x00},
+ {0x27, 0x08},
+ {0x28, 0x00},
+ {0x30, 0x06},
+ {0x31, 0x40},
+ {0x32, 0x13},
+ {0x33, 0x0c},
+ {0x34, 0x04},
+ {0x35, 0x06},
+ {0x36, 0x01},
+ {0x37, 0x06},
+ {0x39, 0x4f},
+ {0x03, 0x20},
+ {0x10, 0x9c},
+ {0x03, 0x22},
+ {0x10, 0xe9},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x0e, 0x03},
+ {0x0e, 0x73},
+ {0x03, 0x00},
+ {0x01, 0xf0},
+};
+
+static struct v4l2_subdev_info hi256_subdev_info[] = {
+ {
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ },
+};
+
+static const struct i2c_device_id hi256_i2c_id[] = {
+ {HI256_SENSOR_NAME, (kernel_ulong_t)&hi256_s_ctrl},
+ { }
+};
+
+static int32_t msm_hi256_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ return msm_sensor_i2c_probe(client, id, &hi256_s_ctrl);
+}
+
+static struct i2c_driver hi256_i2c_driver = {
+ .id_table = hi256_i2c_id,
+ .probe = msm_hi256_i2c_probe,
+ .driver = {
+ .name = HI256_SENSOR_NAME,
+ },
+};
+
+static struct msm_camera_i2c_client hi256_sensor_i2c_client = {
+ .addr_type = MSM_CAMERA_I2C_BYTE_ADDR,
+};
+
+static const struct of_device_id hi256_dt_match[] = {
+ {.compatible = "shinetech,hi256", .data = &hi256_s_ctrl},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, hi256_dt_match);
+
+static struct platform_driver hi256_platform_driver = {
+ .driver = {
+ .name = "shinetech,hi256",
+ .owner = THIS_MODULE,
+ .of_match_table = hi256_dt_match,
+ },
+};
+
+static void hi256_i2c_write_table(struct msm_sensor_ctrl_t *s_ctrl,
+ struct msm_camera_i2c_reg_conf *table,
+ int num)
+{
+ int i = 0;
+ int rc = 0;
+ for (i = 0; i < num; ++i) {
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write(
+ s_ctrl->sensor_i2c_client, table->reg_addr,
+ table->reg_data,
+ MSM_CAMERA_I2C_BYTE_DATA);
+ if (rc < 0) {
+ msleep(100);
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write(
+ s_ctrl->sensor_i2c_client, table->reg_addr,
+ table->reg_data,
+ MSM_CAMERA_I2C_BYTE_DATA);
+ }
+ table++;
+ }
+
+}
+
+static int32_t hi256_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc;
+ const struct of_device_id *match;
+ match = of_match_device(hi256_dt_match, &pdev->dev);
+ rc = msm_sensor_platform_probe(pdev, match->data);
+ return rc;
+}
+
+static int __init hi256_init_module(void)
+{
+ int32_t rc;
+ pr_info("%s:%d\n", __func__, __LINE__);
+ rc = platform_driver_probe(&hi256_platform_driver,
+ hi256_platform_probe);
+ if (!rc)
+ return rc;
+ return i2c_add_driver(&hi256_i2c_driver);
+}
+
+static void __exit hi256_exit_module(void)
+{
+ pr_info("%s:%d\n", __func__, __LINE__);
+ if (hi256_s_ctrl.pdev) {
+ msm_sensor_free_sensor_data(&hi256_s_ctrl);
+ platform_driver_unregister(&hi256_platform_driver);
+ } else
+ i2c_del_driver(&hi256_i2c_driver);
+ return;
+}
+
+static int32_t hi256_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ uint16_t chipid = 0;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_read(
+ s_ctrl->sensor_i2c_client,
+ s_ctrl->sensordata->slave_info->sensor_id_reg_addr,
+ &chipid, MSM_CAMERA_I2C_BYTE_DATA);
+ if (rc < 0) {
+ pr_err("%s: %s: hi256 read id failed\n", __func__,
+ s_ctrl->sensordata->sensor_name);
+ return rc;
+ }
+
+ CDBG("%s: read id: %x expected id %x:\n", __func__, chipid,
+ s_ctrl->sensordata->slave_info->sensor_id);
+ if (chipid != s_ctrl->sensordata->slave_info->sensor_id) {
+ pr_err("msm_sensor_match_id chip id doesnot match\n");
+ return -ENODEV;
+ }
+ return rc;
+}
+
+int32_t hi256_sensor_config(struct msm_sensor_ctrl_t *s_ctrl,
+ void __user *argp)
+{
+ struct sensorb_cfg_data *cdata = (struct sensorb_cfg_data *)argp;
+ long rc = 0;
+ int32_t i = 0;
+ mutex_lock(s_ctrl->msm_sensor_mutex);
+ CDBG("%s:%d %s cfgtype = %d\n", __func__, __LINE__,
+ s_ctrl->sensordata->sensor_name, cdata->cfgtype);
+ switch (cdata->cfgtype) {
+ case CFG_GET_SENSOR_INFO:
+ memcpy(cdata->cfg.sensor_info.sensor_name,
+ s_ctrl->sensordata->sensor_name,
+ sizeof(cdata->cfg.sensor_info.sensor_name));
+ cdata->cfg.sensor_info.session_id =
+ s_ctrl->sensordata->sensor_info->session_id;
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ cdata->cfg.sensor_info.subdev_id[i] =
+ s_ctrl->sensordata->sensor_info->subdev_id[i];
+ CDBG("%s:%d sensor name %s\n", __func__, __LINE__,
+ cdata->cfg.sensor_info.sensor_name);
+ CDBG("%s:%d session id %d\n", __func__, __LINE__,
+ cdata->cfg.sensor_info.session_id);
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ CDBG("%s:%d subdev_id[%d] %d\n", __func__, __LINE__, i,
+ cdata->cfg.sensor_info.subdev_id[i]);
+ break;
+ case CFG_SET_INIT_SETTING:
+ CDBG("init setting");
+ hi256_i2c_write_table(s_ctrl,
+ &hi256_recommend_settings[0],
+ ARRAY_SIZE(hi256_recommend_settings));
+ CDBG("init setting X");
+ break;
+ case CFG_SET_RESOLUTION:
+ break;
+ case CFG_SET_STOP_STREAM:
+ hi256_i2c_write_table(s_ctrl,
+ &hi256_stop_settings[0],
+ ARRAY_SIZE(hi256_stop_settings));
+ break;
+ case CFG_SET_START_STREAM:
+ hi256_i2c_write_table(s_ctrl,
+ &hi256_start_settings[0],
+ ARRAY_SIZE(hi256_start_settings));
+ break;
+ case CFG_GET_SENSOR_INIT_PARAMS:
+ cdata->cfg.sensor_init_params =
+ *s_ctrl->sensordata->sensor_init_params;
+ CDBG("%s:%d init params mode %d pos %d mount %d\n", __func__,
+ __LINE__,
+ cdata->cfg.sensor_init_params.modes_supported,
+ cdata->cfg.sensor_init_params.position,
+ cdata->cfg.sensor_init_params.sensor_mount_angle);
+ break;
+ case CFG_SET_SLAVE_INFO: {
+ struct msm_camera_sensor_slave_info sensor_slave_info;
+ struct msm_sensor_power_setting_array *power_setting_array;
+ int slave_index = 0;
+ if (copy_from_user(&sensor_slave_info,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_sensor_slave_info))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ /* Update sensor slave address */
+ if (sensor_slave_info.slave_addr) {
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ sensor_slave_info.slave_addr >> 1;
+ }
+
+ /* Update sensor address type */
+ s_ctrl->sensor_i2c_client->addr_type =
+ sensor_slave_info.addr_type;
+
+ /* Update power up / down sequence */
+ s_ctrl->power_setting_array =
+ sensor_slave_info.power_setting_array;
+ power_setting_array = &s_ctrl->power_setting_array;
+ power_setting_array->power_setting = kzalloc(
+ power_setting_array->size *
+ sizeof(struct msm_sensor_power_setting), GFP_KERNEL);
+ if (!power_setting_array->power_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(power_setting_array->power_setting,
+ (void *)
+ sensor_slave_info.power_setting_array.power_setting,
+ power_setting_array->size *
+ sizeof(struct msm_sensor_power_setting))) {
+ kfree(power_setting_array->power_setting);
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ s_ctrl->free_power_setting = true;
+ CDBG("%s sensor id %x\n", __func__,
+ sensor_slave_info.slave_addr);
+ CDBG("%s sensor addr type %d\n", __func__,
+ sensor_slave_info.addr_type);
+ CDBG("%s sensor reg %x\n", __func__,
+ sensor_slave_info.sensor_id_info.sensor_id_reg_addr);
+ CDBG("%s sensor id %x\n", __func__,
+ sensor_slave_info.sensor_id_info.sensor_id);
+ for (slave_index = 0; slave_index <
+ power_setting_array->size; slave_index++) {
+ CDBG("%s i %d power setting %d %d %ld %d\n", __func__,
+ slave_index,
+ power_setting_array->power_setting[slave_index].
+ seq_type,
+ power_setting_array->power_setting[slave_index].
+ seq_val,
+ power_setting_array->power_setting[slave_index].
+ config_val,
+ power_setting_array->power_setting[slave_index].
+ delay);
+ }
+ kfree(power_setting_array->power_setting);
+ break;
+ }
+ case CFG_WRITE_I2C_ARRAY: {
+ struct msm_camera_i2c_reg_setting conf_array;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+
+ if (copy_from_user(&conf_array,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.reg_setting = reg_setting;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table(
+ s_ctrl->sensor_i2c_client, &conf_array);
+ kfree(reg_setting);
+ break;
+ }
+ case CFG_WRITE_I2C_SEQ_ARRAY: {
+ struct msm_camera_i2c_seq_reg_setting conf_array;
+ struct msm_camera_i2c_seq_reg_array *reg_setting = NULL;
+
+ if (copy_from_user(&conf_array,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_seq_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_seq_reg_array)),
+ GFP_KERNEL);
+ if (!reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_seq_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.reg_setting = reg_setting;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_seq_table(s_ctrl->sensor_i2c_client,
+ &conf_array);
+ kfree(reg_setting);
+ break;
+ }
+
+ case CFG_POWER_UP:
+ if (s_ctrl->func_tbl->sensor_power_up)
+ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+ else
+ rc = -EFAULT;
+ break;
+
+ case CFG_POWER_DOWN:
+ if (s_ctrl->func_tbl->sensor_power_down)
+ rc = s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+ else
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_STOP_STREAM_SETTING: {
+ struct msm_camera_i2c_reg_setting *stop_setting =
+ &s_ctrl->stop_setting;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+ if (copy_from_user(stop_setting, (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = stop_setting->reg_setting;
+ stop_setting->reg_setting = kzalloc(stop_setting->size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!stop_setting->reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(stop_setting->reg_setting,
+ (void *)reg_setting, stop_setting->size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(stop_setting->reg_setting);
+ stop_setting->reg_setting = NULL;
+ stop_setting->size = 0;
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(s_ctrl->msm_sensor_mutex);
+
+ return rc;
+}
+
+static struct msm_sensor_fn_t hi256_sensor_func_tbl = {
+ .sensor_config = hi256_sensor_config,
+ .sensor_power_up = msm_sensor_power_up,
+ .sensor_power_down = msm_sensor_power_down,
+ .sensor_match_id = hi256_sensor_match_id,
+};
+
+static struct msm_sensor_ctrl_t hi256_s_ctrl = {
+ .sensor_i2c_client = &hi256_sensor_i2c_client,
+ .power_setting_array.power_setting = hi256_power_setting,
+ .power_setting_array.size = ARRAY_SIZE(hi256_power_setting),
+ .msm_sensor_mutex = &hi256_mut,
+ .sensor_v4l2_subdev_info = hi256_subdev_info,
+ .sensor_v4l2_subdev_info_size = ARRAY_SIZE(hi256_subdev_info),
+ .func_tbl = &hi256_sensor_func_tbl,
+};
+
+module_init(hi256_init_module);
+module_exit(hi256_exit_module);
+MODULE_DESCRIPTION("Hi256 2MP YUV sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index bd05180..77f838c 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -1305,6 +1305,25 @@
return rc;
}
+static void msm_vidc_print_running_insts(struct msm_vidc_core *core)
+{
+ struct msm_vidc_inst *temp;
+ dprintk(VIDC_ERR, "Running instances:\n");
+ dprintk(VIDC_ERR, "%4s|%4s|%4s|%4s\n", "type", "w", "h", "fps");
+ list_for_each_entry(temp, &core->instances, list) {
+ mutex_lock(&temp->lock);
+ if (temp->state >= MSM_VIDC_OPEN_DONE &&
+ temp->state < MSM_VIDC_STOP_DONE) {
+ dprintk(VIDC_ERR, "%4d|%4d|%4d|%4d\n",
+ temp->session_type,
+ temp->prop.width,
+ temp->prop.height,
+ temp->prop.fps);
+ }
+ mutex_unlock(&temp->lock);
+ }
+}
+
static int msm_vidc_load_resources(int flipped_state,
struct msm_vidc_inst *inst)
{
@@ -1327,24 +1346,9 @@
num_mbs_per_sec = msm_comm_get_load(inst->core, MSM_VIDC_DECODER);
num_mbs_per_sec += msm_comm_get_load(inst->core, MSM_VIDC_ENCODER);
if (num_mbs_per_sec > inst->core->resources.max_load) {
- struct msm_vidc_inst *temp;
-
dprintk(VIDC_ERR, "HW is overloaded, needed: %d max: %d\n",
num_mbs_per_sec, inst->core->resources.max_load);
- dprintk(VIDC_ERR, "Running instances:\n");
- dprintk(VIDC_ERR, "%4s|%4s|%4s|%4s\n", "type", "w", "h", "fps");
- list_for_each_entry(temp, &inst->core->instances, list) {
- mutex_lock(&temp->lock);
- if (temp->state >= MSM_VIDC_OPEN_DONE &&
- temp->state < MSM_VIDC_STOP_DONE) {
- dprintk(VIDC_ERR, "%4d|%4d|%4d|%4d\n",
- temp->session_type,
- temp->prop.width,
- temp->prop.height,
- temp->prop.fps);
- }
- mutex_unlock(&temp->lock);
- }
+ msm_vidc_print_running_insts(inst->core);
inst->state = MSM_VIDC_CORE_INVALID;
msm_comm_recover_from_session_error(inst);
return -ENOMEM;
@@ -2479,6 +2483,29 @@
return rc;
}
+static int msm_vidc_load_supported(struct msm_vidc_inst *inst)
+{
+ int num_mbs_per_sec = 0;
+
+ if (inst->state == MSM_VIDC_OPEN_DONE) {
+ num_mbs_per_sec = msm_comm_get_load(inst->core,
+ MSM_VIDC_DECODER);
+ num_mbs_per_sec += msm_comm_get_load(inst->core,
+ MSM_VIDC_ENCODER);
+ if (num_mbs_per_sec > inst->core->resources.max_load) {
+ dprintk(VIDC_ERR,
+ "H/w is overloaded. needed: %d max: %d\n",
+ num_mbs_per_sec,
+ inst->core->resources.max_load);
+ mutex_lock(&inst->sync_lock);
+ msm_vidc_print_running_insts(inst->core);
+ mutex_unlock(&inst->sync_lock);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
int msm_vidc_check_session_supported(struct msm_vidc_inst *inst)
{
struct msm_vidc_core_capability *capability;
@@ -2492,7 +2519,8 @@
capability = &inst->capability;
hdev = inst->core->device;
- if (inst->capability.capability_set) {
+ rc = msm_vidc_load_supported(inst);
+ if (!rc && inst->capability.capability_set) {
rc = call_hfi_op(hdev, capability_check,
inst->fmts[OUTPUT_PORT]->fourcc,
inst->prop.width, &capability->width.max,
diff --git a/drivers/net/usb/rmnet_usb_ctrl.h b/drivers/net/usb/rmnet_usb.h
similarity index 61%
rename from drivers/net/usb/rmnet_usb_ctrl.h
rename to drivers/net/usb/rmnet_usb.h
index f0c169f..36002df 100644
--- a/drivers/net/usb/rmnet_usb_ctrl.h
+++ b/drivers/net/usb/rmnet_usb.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -10,8 +10,8 @@
* GNU General Public License for more details.
*/
-#ifndef __RMNET_USB_CTRL_H
-#define __RMNET_USB_CTRL_H
+#ifndef __RMNET_USB_H
+#define __RMNET_USB_H
#include <linux/mutex.h>
#include <linux/usb.h>
@@ -19,7 +19,36 @@
#include <linux/usb/ch9.h>
#include <linux/usb/cdc.h>
-#define CTRL_DEV_MAX_LEN 10
+#define MAX_RMNET_DEVS 4
+#define MAX_RMNET_INSTS_PER_DEV 17
+#define TOTAL_RMNET_DEV_COUNT (MAX_RMNET_DEVS * MAX_RMNET_INSTS_PER_DEV)
+
+#define CTRL_DEV_MAX_LEN 10
+
+#define RMNET_CTRL_DEV_OPEN 0
+#define RMNET_CTRL_DEV_READY 1
+#define RMNET_CTRL_DEV_MUX_EN 2
+
+/*MUX header bit masks*/
+#define MUX_CTRL_MASK 0x1
+#define MUX_PAD_SHIFT 0x2
+
+/*max padding bytes for n byte alignment*/
+#define MAX_PAD_BYTES(n) (n-1)
+
+/*
+ *MUX Header Format
+ *BIT 0 : Mux type 0: Data, 1: control
+ *BIT 1: Reserved
+ *BIT 2-7: Pad bytes
+ *BIT 8-15: Mux ID
+ *BIT 16-31: PACKET_LEN_WITH_PADDING (Bytes)
+ */
+struct mux_hdr {
+ __u8 padding_info;
+ __u8 mux_id;
+ __le16 pkt_len_w_padding;
+} __packed;
struct rmnet_ctrl_dev {
@@ -28,6 +57,10 @@
struct cdev cdev;
struct device *devicep;
+ unsigned ch_id;
+
+ /*to identify the usb device*/
+ unsigned id;
struct usb_interface *intf;
unsigned int int_pipe;
@@ -48,24 +81,17 @@
struct workqueue_struct *wq;
struct work_struct get_encap_work;
- unsigned is_opened;
+ unsigned long status;
- bool is_connected;
+ bool claimed;
+
+ unsigned int mdm_wait_timeout;
/*input control lines (DSR, CTS, CD, RI)*/
unsigned int cbits_tolocal;
-
/*output control lines (DTR, RTS)*/
unsigned int cbits_tomdm;
- /*
- * track first resp available from mdm when it boots up
- * to avoid bigger timeout value used by qmuxd
- */
- bool resp_available;
-
- unsigned int mdm_wait_timeout;
-
/*counters*/
unsigned int snd_encap_cmd_cnt;
unsigned int get_encap_resp_cnt;
@@ -76,15 +102,16 @@
unsigned int zlp_cnt;
};
-extern struct rmnet_ctrl_dev *ctrl_dev[];
+extern struct workqueue_struct *usbnet_wq;
extern int rmnet_usb_ctrl_start_rx(struct rmnet_ctrl_dev *);
extern int rmnet_usb_ctrl_suspend(struct rmnet_ctrl_dev *dev);
-extern int rmnet_usb_ctrl_init(void);
-extern void rmnet_usb_ctrl_exit(void);
+extern int rmnet_usb_ctrl_init(int num_devs, int insts_per_dev);
+extern void rmnet_usb_ctrl_exit(int num_devs, int insts_per_dev);
extern int rmnet_usb_ctrl_probe(struct usb_interface *intf,
- struct usb_host_endpoint *status,
- struct rmnet_ctrl_dev *dev);
+ struct usb_host_endpoint *int_in,
+ unsigned long rmnet_devnum,
+ unsigned long *data);
extern void rmnet_usb_ctrl_disconnect(struct rmnet_ctrl_dev *);
#endif /* __RMNET_USB_H*/
diff --git a/drivers/net/usb/rmnet_usb_ctrl.c b/drivers/net/usb/rmnet_usb_ctrl.c
index 283c6b0..0772592 100644
--- a/drivers/net/usb/rmnet_usb_ctrl.c
+++ b/drivers/net/usb/rmnet_usb_ctrl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,10 +19,11 @@
#include <linux/poll.h>
#include <linux/ratelimit.h>
#include <linux/debugfs.h>
-#include "rmnet_usb_ctrl.h"
+#include "rmnet_usb.h"
-#define DEVICE_NAME "hsicctl"
-#define NUM_CTRL_CHANNELS 4
+static char *rmnet_dev_names[MAX_RMNET_DEVS] = {"hsicctl"};
+module_param_array(rmnet_dev_names, charp, NULL, S_IRUGO | S_IWUSR);
+
#define DEFAULT_READ_URB_LENGTH 0x1000
#define UNLINK_TIMEOUT_MS 500 /*random value*/
@@ -93,13 +94,19 @@
pr_info(x); \
} while (0)
-struct rmnet_ctrl_dev *ctrl_dev[NUM_CTRL_CHANNELS];
-struct class *ctrldev_classp;
-static dev_t ctrldev_num;
+/* passed in rmnet_usb_ctrl_init */
+static int num_devs;
+static int insts_per_dev;
+
+/* dynamically allocated 2-D array of num_devs*insts_per_dev ctrl_devs */
+static struct rmnet_ctrl_dev **ctrl_devs;
+static struct class *ctrldev_classp[MAX_RMNET_DEVS];
+static dev_t ctrldev_num[MAX_RMNET_DEVS];
struct ctrl_pkt {
size_t data_size;
void *data;
+ void *ctxt;
};
struct ctrl_pkt_list_elem {
@@ -109,18 +116,56 @@
static void resp_avail_cb(struct urb *);
-static int is_dev_connected(struct rmnet_ctrl_dev *dev)
+static int rmnet_usb_ctrl_dmux(struct ctrl_pkt_list_elem *clist)
{
- if (dev) {
- mutex_lock(&dev->dev_lock);
- if (!dev->is_connected) {
- mutex_unlock(&dev->dev_lock);
- return 0;
- }
- mutex_unlock(&dev->dev_lock);
- return 1;
+ struct mux_hdr *hdr;
+ size_t pad_len;
+ size_t total_len;
+ unsigned int mux_id;
+
+ hdr = (struct mux_hdr *)clist->cpkt.data;
+ pad_len = hdr->padding_info >> MUX_PAD_SHIFT;
+ if (pad_len > MAX_PAD_BYTES(4)) {
+ pr_err_ratelimited("%s: Invalid pad len %d\n", __func__,
+ pad_len);
+ return -EINVAL;
}
- return 0;
+
+ mux_id = hdr->mux_id;
+ if (!mux_id || mux_id > insts_per_dev) {
+ pr_err_ratelimited("%s: Invalid mux id %d\n", __func__, mux_id);
+ return -EINVAL;
+ }
+
+ total_len = le16_to_cpu(hdr->pkt_len_w_padding);
+ if (!total_len || !(total_len - pad_len)) {
+ pr_err_ratelimited("%s: Invalid pkt length %d\n", __func__,
+ total_len);
+ return -EINVAL;
+ }
+
+ clist->cpkt.data_size = total_len - pad_len;
+
+ return mux_id - 1;
+}
+
+static void rmnet_usb_ctrl_mux(unsigned int id, struct ctrl_pkt *cpkt)
+{
+ struct mux_hdr *hdr;
+ size_t len;
+ size_t pad_len = 0;
+
+ hdr = (struct mux_hdr *)cpkt->data;
+ hdr->mux_id = id + 1;
+ len = cpkt->data_size - sizeof(struct mux_hdr) - MAX_PAD_BYTES(4);
+
+ /*add padding if len is not 4 byte aligned*/
+ pad_len = ALIGN(len, 4) - len;
+
+ hdr->pkt_len_w_padding = cpu_to_le16(len + pad_len);
+ hdr->padding_info = (pad_len << MUX_PAD_SHIFT) | MUX_CTRL_MASK;
+
+ cpkt->data_size = sizeof(struct mux_hdr) + hdr->pkt_len_w_padding;
}
static void get_encap_work(struct work_struct *w)
@@ -130,6 +175,9 @@
container_of(w, struct rmnet_ctrl_dev, get_encap_work);
int status;
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status))
+ return;
+
udev = interface_to_usbdev(dev->intf);
status = usb_autopm_get_interface(dev->intf);
@@ -216,14 +264,17 @@
case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
dev->resp_avail_cnt++;
- usb_mark_last_busy(udev);
- queue_work(dev->wq, &dev->get_encap_work);
-
- if (!dev->resp_available) {
- dev->resp_available = true;
+ /* If MUX is not enabled, wakeup up the open process
+ * upon first notify response available.
+ */
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status)) {
+ set_bit(RMNET_CTRL_DEV_READY, &dev->status);
wake_up(&dev->open_wait_queue);
}
+ usb_mark_last_busy(udev);
+ queue_work(dev->wq, &dev->get_encap_work);
+
return;
default:
dev_err(dev->devicep,
@@ -246,9 +297,9 @@
{
struct usb_device *udev;
struct ctrl_pkt_list_elem *list_elem = NULL;
- struct rmnet_ctrl_dev *dev = urb->context;
+ struct rmnet_ctrl_dev *rx_dev, *dev = urb->context;
void *cpkt;
- int status = 0;
+ int ch_id, status = 0;
size_t cpkt_size = 0;
udev = interface_to_usbdev(dev->intf);
@@ -258,7 +309,6 @@
switch (urb->status) {
case 0:
/*success*/
- dev->get_encap_resp_cnt++;
break;
/*do not resubmit*/
@@ -303,11 +353,27 @@
}
memcpy(list_elem->cpkt.data, cpkt, cpkt_size);
list_elem->cpkt.data_size = cpkt_size;
- spin_lock(&dev->rx_lock);
- list_add_tail(&list_elem->list, &dev->rx_list);
- spin_unlock(&dev->rx_lock);
- wake_up(&dev->read_wait_queue);
+ rx_dev = dev;
+
+ if (test_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status)) {
+ ch_id = rmnet_usb_ctrl_dmux(list_elem);
+ if (ch_id < 0) {
+ kfree(list_elem->cpkt.data);
+ kfree(list_elem);
+ goto resubmit_int_urb;
+ }
+
+ rx_dev = &ctrl_devs[dev->id][ch_id];
+ }
+
+ rx_dev->get_encap_resp_cnt++;
+
+ spin_lock(&rx_dev->rx_lock);
+ list_add_tail(&list_elem->list, &rx_dev->rx_list);
+ spin_unlock(&rx_dev->rx_lock);
+
+ wake_up(&rx_dev->read_wait_queue);
resubmit_int_urb:
/*check if it is already submitted in resume*/
@@ -340,8 +406,6 @@
static int rmnet_usb_ctrl_alloc_rx(struct rmnet_ctrl_dev *dev)
{
- int retval = -ENOMEM;
-
dev->rcvurb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->rcvurb) {
pr_err("%s: Error allocating read urb\n", __func__);
@@ -367,14 +431,14 @@
kfree(dev->rcvbuf);
kfree(dev->in_ctlreq);
- return retval;
+ return -ENOMEM;
}
static int rmnet_usb_ctrl_write_cmd(struct rmnet_ctrl_dev *dev)
{
struct usb_device *udev;
- if (!is_dev_connected(dev))
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status))
return -ENODEV;
udev = interface_to_usbdev(dev->intf);
@@ -389,7 +453,8 @@
static void ctrl_write_callback(struct urb *urb)
{
- struct rmnet_ctrl_dev *dev = urb->context;
+ struct ctrl_pkt *cpkt = urb->context;
+ struct rmnet_ctrl_dev *dev = cpkt->ctxt;
if (urb->status) {
dev->tx_ctrl_err_cnt++;
@@ -400,18 +465,19 @@
kfree(urb->setup_packet);
kfree(urb->transfer_buffer);
usb_free_urb(urb);
+ kfree(cpkt);
usb_autopm_put_interface_async(dev->intf);
}
-static int rmnet_usb_ctrl_write(struct rmnet_ctrl_dev *dev, char *buf,
- size_t size)
+static int rmnet_usb_ctrl_write(struct rmnet_ctrl_dev *dev,
+ struct ctrl_pkt *cpkt, size_t size)
{
int result;
struct urb *sndurb;
struct usb_ctrlrequest *out_ctlreq;
struct usb_device *udev;
- if (!is_dev_connected(dev))
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status))
return -ENETRESET;
udev = interface_to_usbdev(dev->intf);
@@ -435,12 +501,12 @@
out_ctlreq->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
out_ctlreq->wValue = 0;
out_ctlreq->wIndex = dev->intf->cur_altsetting->desc.bInterfaceNumber;
- out_ctlreq->wLength = cpu_to_le16(size);
+ out_ctlreq->wLength = cpu_to_le16(cpkt->data_size);
usb_fill_control_urb(sndurb, udev,
usb_sndctrlpipe(udev, 0),
- (unsigned char *)out_ctlreq, (void *)buf, size,
- ctrl_write_callback, dev);
+ (unsigned char *)out_ctlreq, (void *)cpkt->data,
+ cpkt->data_size, ctrl_write_callback, cpkt);
result = usb_autopm_get_interface(dev->intf);
if (result < 0) {
@@ -483,16 +549,15 @@
if (!dev)
return -ENODEV;
- if (dev->is_opened)
+ if (test_bit(RMNET_CTRL_DEV_OPEN, &dev->status))
goto already_opened;
- /*block open to get first response available from mdm*/
- if (dev->mdm_wait_timeout && !dev->resp_available) {
+ if (dev->mdm_wait_timeout &&
+ !test_bit(RMNET_CTRL_DEV_READY, &dev->status)) {
retval = wait_event_interruptible_timeout(
- dev->open_wait_queue,
- dev->resp_available,
- msecs_to_jiffies(dev->mdm_wait_timeout *
- 1000));
+ dev->open_wait_queue,
+ test_bit(RMNET_CTRL_DEV_READY, &dev->status),
+ msecs_to_jiffies(dev->mdm_wait_timeout * 1000));
if (retval == 0) {
dev_err(dev->devicep, "%s: Timeout opening %s\n",
__func__, dev->name);
@@ -504,15 +569,13 @@
}
}
- if (!dev->resp_available) {
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status)) {
dev_dbg(dev->devicep, "%s: Connection timedout opening %s\n",
__func__, dev->name);
return -ETIMEDOUT;
}
- mutex_lock(&dev->dev_lock);
- dev->is_opened = 1;
- mutex_unlock(&dev->dev_lock);
+ set_bit(RMNET_CTRL_DEV_OPEN, &dev->status);
file->private_data = dev;
@@ -547,9 +610,7 @@
}
spin_unlock_irqrestore(&dev->rx_lock, flag);
- mutex_lock(&dev->dev_lock);
- dev->is_opened = 0;
- mutex_unlock(&dev->dev_lock);
+ clear_bit(RMNET_CTRL_DEV_OPEN, &dev->status);
time = usb_wait_anchor_empty_timeout(&dev->tx_submitted,
UNLINK_TIMEOUT_MS);
@@ -571,7 +632,7 @@
return POLLERR;
poll_wait(file, &dev->read_wait_queue, wait);
- if (!is_dev_connected(dev)) {
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status)) {
dev_dbg(dev->devicep, "%s: Device not connected\n",
__func__);
return POLLERR;
@@ -588,6 +649,7 @@
{
int retval = 0;
int bytes_to_read;
+ unsigned int hdr_len = 0;
struct rmnet_ctrl_dev *dev;
struct ctrl_pkt_list_elem *list_elem = NULL;
unsigned long flags;
@@ -599,7 +661,7 @@
DBG("%s: Read from %s\n", __func__, dev->name);
ctrl_read:
- if (!is_dev_connected(dev)) {
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status)) {
dev_dbg(dev->devicep, "%s: Device not connected\n",
__func__);
return -ENETRESET;
@@ -609,8 +671,8 @@
spin_unlock_irqrestore(&dev->rx_lock, flags);
retval = wait_event_interruptible(dev->read_wait_queue,
- !list_empty(&dev->rx_list) ||
- !is_dev_connected(dev));
+ !list_empty(&dev->rx_list) ||
+ !test_bit(RMNET_CTRL_DEV_READY, &dev->status));
if (retval < 0)
return retval;
@@ -628,7 +690,10 @@
}
spin_unlock_irqrestore(&dev->rx_lock, flags);
- if (copy_to_user(buf, list_elem->cpkt.data, bytes_to_read)) {
+ if (test_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status))
+ hdr_len = sizeof(struct mux_hdr);
+
+ if (copy_to_user(buf, list_elem->cpkt.data + hdr_len, bytes_to_read)) {
dev_err(dev->devicep,
"%s: copy_to_user failed for %s\n",
__func__, dev->name);
@@ -651,7 +716,10 @@
size_t size, loff_t *pos)
{
int status;
+ size_t total_len;
void *wbuf;
+ void *actual_data;
+ struct ctrl_pkt *cpkt;
struct rmnet_ctrl_dev *dev = file->private_data;
if (!dev)
@@ -660,26 +728,46 @@
if (size <= 0)
return -EINVAL;
- if (!is_dev_connected(dev))
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status))
return -ENETRESET;
DBG("%s: Writing %i bytes on %s\n", __func__, size, dev->name);
- wbuf = kmalloc(size , GFP_KERNEL);
+ total_len = size;
+
+ if (test_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status))
+ total_len += sizeof(struct mux_hdr) + MAX_PAD_BYTES(4);
+
+ wbuf = kmalloc(total_len , GFP_KERNEL);
if (!wbuf)
return -ENOMEM;
- status = copy_from_user(wbuf , buf, size);
+ cpkt = kmalloc(sizeof(struct ctrl_pkt), GFP_KERNEL);
+ if (!cpkt) {
+ kfree(wbuf);
+ return -ENOMEM;
+ }
+ actual_data = cpkt->data = wbuf;
+ cpkt->data_size = total_len;
+ cpkt->ctxt = dev;
+
+ if (test_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status)) {
+ actual_data = wbuf + sizeof(struct mux_hdr);
+ rmnet_usb_ctrl_mux(dev->ch_id, cpkt);
+ }
+
+ status = copy_from_user(actual_data, buf, size);
if (status) {
dev_err(dev->devicep,
"%s: Unable to copy data from userspace %d\n",
__func__, status);
kfree(wbuf);
+ kfree(cpkt);
return status;
}
DUMP_BUFFER("Write: ", size, buf);
- status = rmnet_usb_ctrl_write(dev, wbuf, size);
+ status = rmnet_usb_ctrl_write(dev, cpkt, size);
if (status == size)
return size;
@@ -780,51 +868,43 @@
};
int rmnet_usb_ctrl_probe(struct usb_interface *intf,
- struct usb_host_endpoint *int_in, struct rmnet_ctrl_dev *dev)
+ struct usb_host_endpoint *int_in,
+ unsigned long rmnet_devnum,
+ unsigned long *data)
{
+ struct rmnet_ctrl_dev *dev = NULL;
u16 wMaxPacketSize;
struct usb_endpoint_descriptor *ep;
- struct usb_device *udev;
+ struct usb_device *udev = interface_to_usbdev(intf);
int interval;
- int ret = 0;
+ int ret = 0, n;
- udev = interface_to_usbdev(intf);
+ /* Find next available ctrl_dev */
+ for (n = 0; n < insts_per_dev; n++) {
+ dev = &ctrl_devs[rmnet_devnum][n];
+ if (!dev->claimed)
+ break;
+ }
- if (!dev) {
- pr_err("%s: Ctrl device not found\n", __func__);
+ if (!dev || n == insts_per_dev) {
+ pr_err("%s: No available ctrl devices for %lu\n", __func__,
+ rmnet_devnum);
return -ENODEV;
}
+
dev->int_pipe = usb_rcvintpipe(udev,
int_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
- mutex_lock(&dev->dev_lock);
dev->intf = intf;
- /*TBD: for now just update CD status*/
- dev->cbits_tolocal = ACM_CTRL_CD;
+ dev->id = rmnet_devnum;
- /*send DTR high to modem*/
- dev->cbits_tomdm = ACM_CTRL_DTR;
- mutex_unlock(&dev->dev_lock);
-
- dev->resp_available = false;
dev->snd_encap_cmd_cnt = 0;
dev->get_encap_resp_cnt = 0;
dev->resp_avail_cnt = 0;
dev->tx_ctrl_err_cnt = 0;
dev->set_ctrl_line_state_cnt = 0;
- ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
- USB_CDC_REQ_SET_CONTROL_LINE_STATE,
- (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE),
- dev->cbits_tomdm,
- dev->intf->cur_altsetting->desc.bInterfaceNumber,
- NULL, 0, USB_CTRL_SET_TIMEOUT);
- if (ret < 0)
- return ret;
-
- dev->set_ctrl_line_state_cnt++;
-
dev->inturb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->inturb) {
dev_err(dev->devicep, "Error allocating int urb\n");
@@ -861,22 +941,39 @@
usb_mark_last_busy(udev);
ret = rmnet_usb_ctrl_start_rx(dev);
- if (!ret)
- dev->is_connected = true;
+ if (ret) {
+ usb_free_urb(dev->inturb);
+ kfree(dev->intbuf);
+ return ret;
+ }
- return ret;
+ dev->claimed = true;
+
+ /*mux info is passed to data parameter*/
+ if (*data)
+ set_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status);
+
+ *data = (unsigned long)dev;
+
+ /* If MUX is enabled, wakeup the open process here */
+ if (test_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status)) {
+ set_bit(RMNET_CTRL_DEV_READY, &dev->status);
+ wake_up(&dev->open_wait_queue);
+ }
+
+ return 0;
}
void rmnet_usb_ctrl_disconnect(struct rmnet_ctrl_dev *dev)
{
+ dev->claimed = false;
+
+ clear_bit(RMNET_CTRL_DEV_READY, &dev->status);
mutex_lock(&dev->dev_lock);
-
/*TBD: for now just update CD status*/
dev->cbits_tolocal = ~ACM_CTRL_CD;
-
dev->cbits_tomdm = ~ACM_CTRL_DTR;
- dev->is_connected = false;
mutex_unlock(&dev->dev_lock);
wake_up(&dev->read_wait_queue);
@@ -901,50 +998,53 @@
struct rmnet_ctrl_dev *dev;
char *buf;
int ret;
- int i;
+ int i, n;
int temp = 0;
buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
- for (i = 0; i < NUM_CTRL_CHANNELS; i++) {
- dev = ctrl_dev[i];
- if (!dev)
- continue;
-
- temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
- "\n#ctrl_dev: %p Name: %s#\n"
- "snd encap cmd cnt %u\n"
- "resp avail cnt: %u\n"
- "get encap resp cnt: %u\n"
- "set ctrl line state cnt: %u\n"
- "tx_err_cnt: %u\n"
- "cbits_tolocal: %d\n"
- "cbits_tomdm: %d\n"
- "mdm_wait_timeout: %u\n"
- "zlp_cnt: %u\n"
- "get_encap_failure_cnt %u\n"
- "dev opened: %s\n",
- dev, dev->name,
- dev->snd_encap_cmd_cnt,
- dev->resp_avail_cnt,
- dev->get_encap_resp_cnt,
- dev->set_ctrl_line_state_cnt,
- dev->tx_ctrl_err_cnt,
- dev->cbits_tolocal,
- dev->cbits_tomdm,
- dev->mdm_wait_timeout,
- dev->zlp_cnt,
- dev->get_encap_failure_cnt,
- dev->is_opened ? "OPEN" : "CLOSE");
-
+ for (i = 0; i < num_devs; i++) {
+ for (n = 0; n < insts_per_dev; n++) {
+ dev = &ctrl_devs[i][n];
+ temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ "\n#ctrl_dev: %p Name: %s#\n"
+ "snd encap cmd cnt %u\n"
+ "resp avail cnt: %u\n"
+ "get encap resp cnt: %u\n"
+ "set ctrl line state cnt: %u\n"
+ "tx_err_cnt: %u\n"
+ "cbits_tolocal: %d\n"
+ "cbits_tomdm: %d\n"
+ "mdm_wait_timeout: %u\n"
+ "zlp_cnt: %u\n"
+ "get_encap_failure_cnt %u\n"
+ "RMNET_CTRL_DEV_MUX_EN: %d\n"
+ "RMNET_CTRL_DEV_OPEN: %d\n"
+ "RMNET_CTRL_DEV_READY: %d\n",
+ dev, dev->name,
+ dev->snd_encap_cmd_cnt,
+ dev->resp_avail_cnt,
+ dev->get_encap_resp_cnt,
+ dev->set_ctrl_line_state_cnt,
+ dev->tx_ctrl_err_cnt,
+ dev->cbits_tolocal,
+ dev->cbits_tomdm,
+ dev->mdm_wait_timeout,
+ dev->zlp_cnt,
+ dev->get_encap_failure_cnt,
+ test_bit(RMNET_CTRL_DEV_MUX_EN,
+ &dev->status),
+ test_bit(RMNET_CTRL_DEV_OPEN,
+ &dev->status),
+ test_bit(RMNET_CTRL_DEV_READY,
+ &dev->status));
+ }
}
ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
-
kfree(buf);
-
return ret;
}
@@ -952,19 +1052,19 @@
buf, size_t count, loff_t *ppos)
{
struct rmnet_ctrl_dev *dev;
- int i;
+ int i, n;
- for (i = 0; i < NUM_CTRL_CHANNELS; i++) {
- dev = ctrl_dev[i];
- if (!dev)
- continue;
+ for (i = 0; i < num_devs; i++) {
+ for (n = 0; n < insts_per_dev; n++) {
+ dev = &ctrl_devs[i][n];
- dev->snd_encap_cmd_cnt = 0;
- dev->resp_avail_cnt = 0;
- dev->get_encap_resp_cnt = 0;
- dev->set_ctrl_line_state_cnt = 0;
- dev->tx_ctrl_err_cnt = 0;
- dev->zlp_cnt = 0;
+ dev->snd_encap_cmd_cnt = 0;
+ dev->resp_avail_cnt = 0;
+ dev->get_encap_resp_cnt = 0;
+ dev->set_ctrl_line_state_cnt = 0;
+ dev->tx_ctrl_err_cnt = 0;
+ dev->zlp_cnt = 0;
+ }
}
return count;
}
@@ -999,144 +1099,152 @@
static void rmnet_usb_ctrl_debugfs_exit(void) { }
#endif
-int rmnet_usb_ctrl_init(void)
+int rmnet_usb_ctrl_init(int no_rmnet_devs, int no_rmnet_insts_per_dev)
{
struct rmnet_ctrl_dev *dev;
- int n;
+ int i, n;
int status;
- for (n = 0; n < NUM_CTRL_CHANNELS; ++n) {
+ num_devs = no_rmnet_devs;
+ insts_per_dev = no_rmnet_insts_per_dev;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev) {
- status = -ENOMEM;
- goto error0;
- }
- /*for debug purpose*/
- snprintf(dev->name, CTRL_DEV_MAX_LEN, "hsicctl%d", n);
+ ctrl_devs = kzalloc(num_devs * sizeof(*ctrl_devs), GFP_KERNEL);
+ if (!ctrl_devs)
+ return -ENOMEM;
- dev->wq = create_singlethread_workqueue(dev->name);
- if (!dev->wq) {
- pr_err("unable to allocate workqueue");
- kfree(dev);
- goto error0;
- }
+ for (i = 0; i < num_devs; i++) {
+ ctrl_devs[i] = kzalloc(insts_per_dev * sizeof(*ctrl_devs[i]),
+ GFP_KERNEL);
+ if (!ctrl_devs[i])
+ return -ENOMEM;
- mutex_init(&dev->dev_lock);
- spin_lock_init(&dev->rx_lock);
- init_waitqueue_head(&dev->read_wait_queue);
- init_waitqueue_head(&dev->open_wait_queue);
- INIT_LIST_HEAD(&dev->rx_list);
- init_usb_anchor(&dev->tx_submitted);
- init_usb_anchor(&dev->rx_submitted);
- INIT_WORK(&dev->get_encap_work, get_encap_work);
-
- status = rmnet_usb_ctrl_alloc_rx(dev);
- if (status < 0) {
- kfree(dev);
- goto error0;
- }
-
- ctrl_dev[n] = dev;
- }
-
- status = alloc_chrdev_region(&ctrldev_num, 0, NUM_CTRL_CHANNELS,
- DEVICE_NAME);
- if (IS_ERR_VALUE(status)) {
- pr_err("ERROR:%s: alloc_chrdev_region() ret %i.\n",
- __func__, status);
- goto error0;
- }
-
- ctrldev_classp = class_create(THIS_MODULE, DEVICE_NAME);
- if (IS_ERR(ctrldev_classp)) {
- pr_err("ERROR:%s: class_create() ENOMEM\n", __func__);
- status = -ENOMEM;
- goto error1;
- }
- for (n = 0; n < NUM_CTRL_CHANNELS; ++n) {
- cdev_init(&ctrl_dev[n]->cdev, &ctrldev_fops);
- ctrl_dev[n]->cdev.owner = THIS_MODULE;
-
- status = cdev_add(&ctrl_dev[n]->cdev, (ctrldev_num + n), 1);
-
+ status = alloc_chrdev_region(&ctrldev_num[i], 0, insts_per_dev,
+ rmnet_dev_names[i]);
if (IS_ERR_VALUE(status)) {
- pr_err("%s: cdev_add() ret %i\n", __func__, status);
- kfree(ctrl_dev[n]);
- goto error2;
+ pr_err("ERROR:%s: alloc_chrdev_region() ret %i.\n",
+ __func__, status);
+ return status;
}
- ctrl_dev[n]->devicep =
- device_create(ctrldev_classp, NULL,
- (ctrldev_num + n), NULL,
- DEVICE_NAME "%d", n);
+ ctrldev_classp[i] = class_create(THIS_MODULE,
+ rmnet_dev_names[i]);
+ if (IS_ERR(ctrldev_classp[i])) {
+ pr_err("ERROR:%s: class_create() ENOMEM\n", __func__);
+ status = PTR_ERR(ctrldev_classp[i]);
+ return status;
+ }
- if (IS_ERR(ctrl_dev[n]->devicep)) {
- pr_err("%s: device_create() ENOMEM\n", __func__);
- status = -ENOMEM;
- cdev_del(&ctrl_dev[n]->cdev);
- kfree(ctrl_dev[n]);
- goto error2;
+ for (n = 0; n < insts_per_dev; n++) {
+ dev = &ctrl_devs[i][n];
+
+ /*for debug purpose*/
+ snprintf(dev->name, CTRL_DEV_MAX_LEN, "%s%d",
+ rmnet_dev_names[i], n);
+
+ dev->wq = create_singlethread_workqueue(dev->name);
+ if (!dev->wq) {
+ pr_err("unable to allocate workqueue");
+ kfree(dev);
+ return -ENOMEM;
+ }
+
+ dev->ch_id = n;
+
+ mutex_init(&dev->dev_lock);
+ spin_lock_init(&dev->rx_lock);
+ init_waitqueue_head(&dev->read_wait_queue);
+ init_waitqueue_head(&dev->open_wait_queue);
+ INIT_LIST_HEAD(&dev->rx_list);
+ init_usb_anchor(&dev->tx_submitted);
+ init_usb_anchor(&dev->rx_submitted);
+ INIT_WORK(&dev->get_encap_work, get_encap_work);
+
+ cdev_init(&dev->cdev, &ctrldev_fops);
+ dev->cdev.owner = THIS_MODULE;
+
+ status = cdev_add(&dev->cdev, (ctrldev_num[i] + n), 1);
+ if (status) {
+ pr_err("%s: cdev_add() ret %i\n", __func__,
+ status);
+ destroy_workqueue(dev->wq);
+ kfree(dev);
+ return status;
+ }
+
+ dev->devicep = device_create(ctrldev_classp[i], NULL,
+ (ctrldev_num[i] + n), NULL,
+ "%s%d", rmnet_dev_names[i],
+ n);
+ if (IS_ERR(dev->devicep)) {
+ pr_err("%s: device_create() returned %ld\n",
+ __func__, PTR_ERR(dev->devicep));
+ cdev_del(&dev->cdev);
+ destroy_workqueue(dev->wq);
+ kfree(dev);
+ return PTR_ERR(dev->devicep);
+ }
+
+ /*create /sys/class/hsicctl/hsicctlx/modem_wait*/
+ status = device_create_file(dev->devicep,
+ &dev_attr_modem_wait);
+ if (status) {
+ device_destroy(dev->devicep->class,
+ dev->devicep->devt);
+ cdev_del(&dev->cdev);
+ destroy_workqueue(dev->wq);
+ kfree(dev);
+ return status;
+ }
+ dev_set_drvdata(dev->devicep, dev);
+
+ status = rmnet_usb_ctrl_alloc_rx(dev);
+ if (status) {
+ device_remove_file(dev->devicep,
+ &dev_attr_modem_wait);
+ device_destroy(dev->devicep->class,
+ dev->devicep->devt);
+ cdev_del(&dev->cdev);
+ destroy_workqueue(dev->wq);
+ kfree(dev);
+ return status;
+ }
}
- /*create /sys/class/hsicctl/hsicctlx/modem_wait*/
- status = device_create_file(ctrl_dev[n]->devicep,
- &dev_attr_modem_wait);
- if (status) {
- device_destroy(ctrldev_classp,
- MKDEV(MAJOR(ctrldev_num), n));
- cdev_del(&ctrl_dev[n]->cdev);
- kfree(ctrl_dev[n]);
- goto error2;
- }
- dev_set_drvdata(ctrl_dev[n]->devicep, ctrl_dev[n]);
}
rmnet_usb_ctrl_debugfs_init();
pr_info("rmnet usb ctrl Initialized.\n");
return 0;
-
-error2:
- while (--n >= 0) {
- cdev_del(&ctrl_dev[n]->cdev);
- device_destroy(ctrldev_classp,
- MKDEV(MAJOR(ctrldev_num), n));
- }
-
- class_destroy(ctrldev_classp);
- n = NUM_CTRL_CHANNELS;
-error1:
- unregister_chrdev_region(MAJOR(ctrldev_num), NUM_CTRL_CHANNELS);
-error0:
- while (--n >= 0)
- kfree(ctrl_dev[n]);
-
- return status;
}
-void rmnet_usb_ctrl_exit(void)
+static void free_rmnet_ctrl_dev(struct rmnet_ctrl_dev *dev)
{
- int i;
+ kfree(dev->in_ctlreq);
+ kfree(dev->rcvbuf);
+ kfree(dev->intbuf);
+ usb_free_urb(dev->rcvurb);
+ usb_free_urb(dev->inturb);
+ device_remove_file(dev->devicep, &dev_attr_modem_wait);
+ cdev_del(&dev->cdev);
+ destroy_workqueue(dev->wq);
+ device_destroy(dev->devicep->class,
+ dev->devicep->devt);
+}
- for (i = 0; i < NUM_CTRL_CHANNELS; ++i) {
- if (!ctrl_dev[i])
- return;
+void rmnet_usb_ctrl_exit(int no_rmnet_devs, int no_rmnet_insts_per_dev)
+{
+ int i, n;
- kfree(ctrl_dev[i]->in_ctlreq);
- kfree(ctrl_dev[i]->rcvbuf);
- kfree(ctrl_dev[i]->intbuf);
- usb_free_urb(ctrl_dev[i]->rcvurb);
- usb_free_urb(ctrl_dev[i]->inturb);
-#if defined(DEBUG)
- device_remove_file(ctrl_dev[i]->devicep, &dev_attr_modem_wait);
-#endif
- cdev_del(&ctrl_dev[i]->cdev);
- destroy_workqueue(ctrl_dev[i]->wq);
- kfree(ctrl_dev[i]);
- ctrl_dev[i] = NULL;
- device_destroy(ctrldev_classp, MKDEV(MAJOR(ctrldev_num), i));
+ for (i = 0; i < no_rmnet_devs; i++) {
+ for (n = 0; n < no_rmnet_insts_per_dev; n++)
+ free_rmnet_ctrl_dev(&ctrl_devs[i][n]);
+
+ kfree(ctrl_devs[i]);
+
+ class_destroy(ctrldev_classp[i]);
+ if (ctrldev_num[i])
+ unregister_chrdev_region(ctrldev_num[i], insts_per_dev);
}
- class_destroy(ctrldev_classp);
- unregister_chrdev_region(MAJOR(ctrldev_num), NUM_CTRL_CHANNELS);
+ kfree(ctrl_devs);
rmnet_usb_ctrl_debugfs_exit();
}
diff --git a/drivers/net/usb/rmnet_usb_data.c b/drivers/net/usb/rmnet_usb_data.c
index 2a57b36..f9bb5c8 100644
--- a/drivers/net/usb/rmnet_usb_data.c
+++ b/drivers/net/usb/rmnet_usb_data.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17,14 +17,48 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/usb.h>
+#include <linux/ratelimit.h>
#include <linux/usb/usbnet.h>
#include <linux/msm_rmnet.h>
-#include "rmnet_usb_ctrl.h"
+#include "rmnet_usb.h"
#define RMNET_DATA_LEN 2000
-#define HEADROOM_FOR_QOS 8
+#define RMNET_HEADROOM_W_MUX (sizeof(struct mux_hdr) + \
+ sizeof(struct QMI_QOS_HDR_S))
+#define RMNET_HEADROOM sizeof(struct QMI_QOS_HDR_S)
+#define RMNET_TAILROOM MAX_PAD_BYTES(4);
+static unsigned int no_rmnet_devs = 1;
+module_param(no_rmnet_devs, uint, S_IRUGO | S_IWUSR);
+
+unsigned int no_rmnet_insts_per_dev = 4;
+module_param(no_rmnet_insts_per_dev, uint, S_IRUGO | S_IWUSR);
+
+/*
+ * To support mux on multiple devices, bit position represents device
+ * and value represnts if mux is enabled or disabled.
+ * e.g. bit 0: mdm over HSIC, bit1: mdm over hsusb
+ */
+static unsigned long mux_enabled;
+module_param(mux_enabled, ulong, S_IRUGO | S_IWUSR);
+
+static unsigned int no_fwd_rmnet_links;
+module_param(no_fwd_rmnet_links, uint, S_IRUGO | S_IWUSR);
+
+struct usbnet *unet_list[TOTAL_RMNET_DEV_COUNT];
+
+/* net device name prefixes, indexed by driver_info->data */
+static const char * const rmnet_names[] = {
+ "rmnet_usb%d",
+ "rmnet2_usb%d",
+};
+
+/* net device reverse link name prefixes, indexed by driver_info->data */
+static const char * const rev_rmnet_names[] = {
+ "rev_rmnet_usb%d",
+ "rev_rmnet2_usb%d",
+};
static int data_msg_dbg_mask;
enum {
@@ -80,43 +114,121 @@
#define DBG1(x...) DBG(DEBUG_MASK_LVL1, x)
#define DBG2(x...) DBG(DEBUG_MASK_LVL2, x)
-static void rmnet_usb_setup(struct net_device *);
+static int rmnet_data_start(void);
+static bool rmnet_data_init;
+
+static int rmnet_init(const char *val, const struct kernel_param *kp)
+{
+ int ret = 0;
+
+ if (rmnet_data_init) {
+ pr_err("dynamic setting rmnet params currently unsupported\n");
+ return -EINVAL;
+ }
+
+ ret = param_set_bool(val, kp);
+ if (ret)
+ return ret;
+
+ rmnet_data_start();
+
+ return ret;
+}
+
+static struct kernel_param_ops rmnet_init_ops = {
+ .set = rmnet_init,
+ .get = param_get_bool,
+};
+module_param_cb(rmnet_data_init, &rmnet_init_ops, &rmnet_data_init,
+ S_IRUGO | S_IWUSR);
+
+static void rmnet_usb_setup(struct net_device *, int mux_enabled);
static int rmnet_ioctl(struct net_device *, struct ifreq *, int);
static int rmnet_usb_suspend(struct usb_interface *iface, pm_message_t message)
{
- struct usbnet *unet;
+ struct usbnet *unet = usb_get_intfdata(iface);
struct rmnet_ctrl_dev *dev;
+ int i, n, rdev_cnt, unet_id;
+ int retval = 0;
- unet = usb_get_intfdata(iface);
+ rdev_cnt = unet->data[4] ? no_rmnet_insts_per_dev : 1;
- dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ for (n = 0; n < rdev_cnt; n++) {
+ unet_id = n + unet->driver_info->data * no_rmnet_insts_per_dev;
+ unet =
+ unet->data[4] ? unet_list[unet_id] : usb_get_intfdata(iface);
- if (work_busy(&dev->get_encap_work))
- return -EBUSY;
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ spin_lock_irq(&unet->txq.lock);
+ if (work_busy(&dev->get_encap_work) || unet->txq.qlen) {
+ spin_unlock_irq(&unet->txq.lock);
+ retval = -EBUSY;
+ goto abort_suspend;
+ }
- if (usbnet_suspend(iface, message))
- return -EBUSY;
+ set_bit(EVENT_DEV_ASLEEP, &unet->flags);
+ spin_unlock_irq(&unet->txq.lock);
- usb_kill_anchored_urbs(&dev->rx_submitted);
+ usb_kill_anchored_urbs(&dev->rx_submitted);
+ if (work_busy(&dev->get_encap_work)) {
+ spin_lock_irq(&unet->txq.lock);
+ clear_bit(EVENT_DEV_ASLEEP, &unet->flags);
+ spin_unlock_irq(&unet->txq.lock);
+ retval = -EBUSY;
+ goto abort_suspend;
+ }
+ }
+
+ for (n = 0; n < rdev_cnt; n++) {
+ unet_id = n + unet->driver_info->data * no_rmnet_insts_per_dev;
+ unet =
+ unet->data[4] ? unet_list[unet_id] : usb_get_intfdata(iface);
+
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ netif_device_detach(unet->net);
+ usbnet_terminate_urbs(unet);
+ netif_device_attach(unet->net);
+ }
return 0;
+
+abort_suspend:
+ for (i = 0; i < n; i++) {
+ unet_id = i + unet->driver_info->data * no_rmnet_insts_per_dev;
+ unet =
+ unet->data[4] ? unet_list[unet_id] : usb_get_intfdata(iface);
+
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ rmnet_usb_ctrl_start_rx(dev);
+ spin_lock_irq(&unet->txq.lock);
+ clear_bit(EVENT_DEV_ASLEEP, &unet->flags);
+ spin_unlock_irq(&unet->txq.lock);
+ }
+ return retval;
}
static int rmnet_usb_resume(struct usb_interface *iface)
{
- int retval = 0;
- struct usbnet *unet;
+ struct usbnet *unet = usb_get_intfdata(iface);
struct rmnet_ctrl_dev *dev;
+ int n, rdev_cnt, unet_id;
- unet = usb_get_intfdata(iface);
+ rdev_cnt = unet->data[4] ? no_rmnet_insts_per_dev : 1;
- dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ for (n = 0; n < rdev_cnt; n++) {
+ unet_id = n + unet->driver_info->data * no_rmnet_insts_per_dev;
+ unet =
+ unet->data[4] ? unet_list[unet_id] : usb_get_intfdata(iface);
- usbnet_resume(iface);
- retval = rmnet_usb_ctrl_start_rx(dev);
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ rmnet_usb_ctrl_start_rx(dev);
+ usb_set_intfdata(iface, unet);
+ unet->suspend_count = 1;
+ usbnet_resume(iface);
+ }
- return retval;
+ return 0;
}
static int rmnet_usb_bind(struct usbnet *usbnet, struct usb_interface *iface)
@@ -125,9 +237,13 @@
struct usb_host_endpoint *bulk_in = NULL;
struct usb_host_endpoint *bulk_out = NULL;
struct usb_host_endpoint *int_in = NULL;
+ struct driver_info *info = usbnet->driver_info;
int status = 0;
int i;
int numends;
+ bool mux;
+
+ mux = test_bit(info->data, &mux_enabled);
numends = iface->cur_altsetting->desc.bNumEndpoints;
for (i = 0; i < numends; i++) {
@@ -158,13 +274,72 @@
usbnet->status = int_in;
/*change name of net device to rmnet_usbx here*/
- strlcpy(usbnet->net->name, "rmnet_usb%d", IFNAMSIZ);
+ if (mux && (info->in > no_fwd_rmnet_links))
+ strlcpy(usbnet->net->name, rev_rmnet_names[info->data],
+ IFNAMSIZ);
+ else
+ strlcpy(usbnet->net->name, rmnet_names[info->data],
+ IFNAMSIZ);
- /*TBD: update rx_urb_size, curently set to eth frame len by usbnet*/
+ if (mux)
+ usbnet->rx_urb_size = usbnet->hard_mtu + sizeof(struct mux_hdr)
+ + MAX_PAD_BYTES(4);
out:
return status;
}
+static int rmnet_usb_data_dmux(struct sk_buff *skb, struct urb *rx_urb)
+{
+ struct mux_hdr *hdr;
+ size_t pad_len;
+ size_t total_len;
+ unsigned int mux_id;
+
+ hdr = (struct mux_hdr *)skb->data;
+ mux_id = hdr->mux_id;
+ if (!mux_id || mux_id > no_rmnet_insts_per_dev) {
+ pr_err_ratelimited("%s: Invalid data channel id %u.\n",
+ __func__, mux_id);
+ return -EINVAL;
+ }
+
+ pad_len = hdr->padding_info >> MUX_PAD_SHIFT;
+ if (pad_len > MAX_PAD_BYTES(4)) {
+ pr_err_ratelimited("%s: Invalid pad len %d\n",
+ __func__, pad_len);
+ return -EINVAL;
+ }
+
+ total_len = le16_to_cpu(hdr->pkt_len_w_padding);
+ if (!total_len || !(total_len - pad_len)) {
+ pr_err_ratelimited("%s: Invalid pkt length %d\n", __func__,
+ total_len);
+ return -EINVAL;
+ }
+
+ skb->data = (unsigned char *)(hdr + 1);
+ skb_reset_tail_pointer(skb);
+ rx_urb->actual_length = total_len - pad_len;
+
+ return mux_id - 1;
+}
+
+static void rmnet_usb_data_mux(struct sk_buff *skb, unsigned int id)
+{
+ struct mux_hdr *hdr;
+ size_t len;
+
+ hdr = (struct mux_hdr *)skb_push(skb, sizeof(struct mux_hdr));
+ hdr->mux_id = id + 1;
+ len = skb->len - sizeof(struct mux_hdr);
+
+ /*add padding if len is not 4 byte aligned*/
+ skb_put(skb, ALIGN(len, 4) - len);
+
+ hdr->pkt_len_w_padding = cpu_to_le16(skb->len - sizeof(struct mux_hdr));
+ hdr->padding_info = (ALIGN(len, 4) - len) << MUX_PAD_SHIFT;
+}
+
static struct sk_buff *rmnet_usb_tx_fixup(struct usbnet *dev,
struct sk_buff *skb, gfp_t flags)
{
@@ -178,6 +353,9 @@
qmih->flow_id = skb->mark;
}
+ if (dev->data[4])
+ rmnet_usb_data_mux(skb, dev->data[3]);
+
DBG1("[%s] Tx packet #%lu len=%d mark=0x%x\n",
dev->net->name, dev->net->stats.tx_packets, skb->len, skb->mark);
@@ -206,10 +384,35 @@
return protocol;
}
-static int rmnet_usb_rx_fixup(struct usbnet *dev,
- struct sk_buff *skb)
+static void rmnet_usb_rx_complete(struct urb *rx_urb)
{
+ struct sk_buff *skb = (struct sk_buff *) rx_urb->context;
+ struct skb_data *entry = (struct skb_data *) skb->cb;
+ struct usbnet *dev = entry->dev;
+ unsigned int unet_offset;
+ unsigned int unet_id;
+ int mux_id;
+ unet_offset = dev->driver_info->data * no_rmnet_insts_per_dev;
+
+ if (!rx_urb->status && dev->data[4]) {
+ mux_id = rmnet_usb_data_dmux(skb, rx_urb);
+ if (mux_id < 0) {
+ /*resubmit urb and free skb in rx_complete*/
+ rx_urb->status = -EINVAL;
+ } else {
+ /*map urb to actual network iface based on mux id*/
+ unet_id = unet_offset + mux_id;
+ skb->dev = unet_list[unet_id]->net;
+ entry->dev = unet_list[unet_id];
+ }
+ }
+
+ rx_complete(rx_urb);
+}
+
+static int rmnet_usb_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
if (test_bit(RMNET_MODE_LLP_IP, &dev->data[0]))
skb->protocol = rmnet_ip_type_trans(skb, dev->net);
else /*set zero for eth mode*/
@@ -270,7 +473,6 @@
.ndo_validate_addr = 0,
};
-
static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct usbnet *unet = netdev_priv(dev);
@@ -306,7 +508,6 @@
dev->mtu = prev_mtu;
dev->addr_len = 0;
dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
- dev->needed_headroom = HEADROOM_FOR_QOS;
dev->netdev_ops = &rmnet_usb_ops_ip;
clear_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
set_bit(RMNET_MODE_LLP_IP, &unet->data[0]);
@@ -365,7 +566,7 @@
return rc;
}
-static void rmnet_usb_setup(struct net_device *dev)
+static void rmnet_usb_setup(struct net_device *dev, int mux_enabled)
{
/* Using Ethernet mode by default */
dev->netdev_ops = &rmnet_usb_ops_ether;
@@ -373,7 +574,15 @@
/* set this after calling ether_setup */
dev->mtu = RMNET_DATA_LEN;
- dev->needed_headroom = HEADROOM_FOR_QOS;
+ if (mux_enabled) {
+ dev->needed_headroom = RMNET_HEADROOM_W_MUX;
+
+ /*max pad bytes for 4 byte alignment*/
+ dev->needed_tailroom = RMNET_TAILROOM;
+ } else {
+ dev->needed_headroom = RMNET_HEADROOM;
+ }
+
random_ether_addr(dev->dev_addr);
dev->watchdog_timeo = 1000; /* 10 seconds? */
}
@@ -403,7 +612,6 @@
seq_printf(s, "tx errors: %lu\n", unet->net->stats.tx_errors);
seq_printf(s, "tx packets: %lu\n", unet->net->stats.tx_packets);
seq_printf(s, "tx bytes: %lu\n", unet->net->stats.tx_bytes);
- seq_printf(s, "suspend count: %d\n", unet->suspend_count);
seq_printf(s, "EVENT_DEV_OPEN: %d\n",
test_bit(EVENT_DEV_OPEN, &unet->flags));
seq_printf(s, "EVENT_TX_HALT: %d\n",
@@ -458,21 +666,25 @@
{
struct dentry *root = (struct dentry *)unet->data[2];
- debugfs_remove_recursive(root);
- unet->data[2] = 0;
+ if (root) {
+ debugfs_remove_recursive(root);
+ unet->data[2] = 0;
+ }
}
static int rmnet_usb_probe(struct usb_interface *iface,
const struct usb_device_id *prod)
{
struct usbnet *unet;
- struct driver_info *info;
+ struct driver_info *info = (struct driver_info *)prod->driver_info;
struct usb_device *udev;
- unsigned int iface_num;
- static int first_rmnet_iface_num = -EINVAL;
int status = 0;
+ unsigned int i, unet_id, rdev_cnt, n = 0;
+ bool mux;
+ struct rmnet_ctrl_dev *dev;
- iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
+ udev = interface_to_usbdev(iface);
+
if (iface->num_altsetting != 1) {
dev_err(&iface->dev, "%s invalid num_altsetting %u\n",
__func__, iface->num_altsetting);
@@ -480,45 +692,61 @@
goto out;
}
- info = (struct driver_info *)prod->driver_info;
- if (!test_bit(iface_num, &info->data))
- return -ENODEV;
+ mux = test_bit(info->data, &mux_enabled);
+ rdev_cnt = mux ? no_rmnet_insts_per_dev : 1;
+ info->in = 0;
- status = usbnet_probe(iface, prod);
- if (status < 0) {
- dev_err(&iface->dev, "usbnet_probe failed %d\n", status);
- goto out;
+ for (n = 0; n < rdev_cnt; n++) {
+
+ /* Use this filed to increment device count this will be
+ * used by bind to determin the forward link and reverse
+ * link network interface names.
+ */
+ info->in++;
+ status = usbnet_probe(iface, prod);
+ if (status < 0) {
+ dev_err(&iface->dev, "usbnet_probe failed %d\n",
+ status);
+ goto out;
+ }
+
+ unet_id = n + info->data * no_rmnet_insts_per_dev;
+
+ unet_list[unet_id] = unet = usb_get_intfdata(iface);
+
+ /*store mux id for later access*/
+ unet->data[3] = n;
+
+ /*save mux info for control and usbnet devices*/
+ unet->data[1] = unet->data[4] = mux;
+
+ /*set rmnet operation mode to eth by default*/
+ set_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
+
+ /*update net device*/
+ rmnet_usb_setup(unet->net, mux);
+
+ /*create /sys/class/net/rmnet_usbx/dbg_mask*/
+ status = device_create_file(&unet->net->dev,
+ &dev_attr_dbg_mask);
+ if (status) {
+ usbnet_disconnect(iface);
+ goto out;
+ }
+
+ status = rmnet_usb_ctrl_probe(iface, unet->status, info->data,
+ &unet->data[1]);
+ if (status) {
+ device_remove_file(&unet->net->dev, &dev_attr_dbg_mask);
+ usbnet_disconnect(iface);
+ goto out;
+ }
+
+ status = rmnet_usb_data_debugfs_init(unet);
+ if (status)
+ dev_dbg(&iface->dev,
+ "mode debugfs file is not available\n");
}
- unet = usb_get_intfdata(iface);
-
- /*set rmnet operation mode to eth by default*/
- set_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
-
- /*update net device*/
- rmnet_usb_setup(unet->net);
-
- /*create /sys/class/net/rmnet_usbx/dbg_mask*/
- status = device_create_file(&unet->net->dev, &dev_attr_dbg_mask);
- if (status)
- goto out;
-
- if (first_rmnet_iface_num == -EINVAL)
- first_rmnet_iface_num = iface_num;
-
- /*save control device intstance */
- unet->data[1] = (unsigned long)ctrl_dev \
- [iface_num - first_rmnet_iface_num];
-
- status = rmnet_usb_ctrl_probe(iface, unet->status,
- (struct rmnet_ctrl_dev *)unet->data[1]);
- if (status)
- goto out;
-
- status = rmnet_usb_data_debugfs_init(unet);
- if (status)
- dev_dbg(&iface->dev, "mode debugfs file is not available\n");
-
- udev = unet->udev;
usb_enable_autosuspend(udev);
@@ -528,83 +756,123 @@
device_set_wakeup_enable(&udev->parent->dev, 1);
}
+ return 0;
+
out:
+ for (i = 0; i < n; i++) {
+ /* This cleanup happens only for MUX case */
+ unet_id = i + info->data * no_rmnet_insts_per_dev;
+ unet = unet_list[unet_id];
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+
+ rmnet_usb_data_debugfs_cleanup(unet);
+ rmnet_usb_ctrl_disconnect(dev);
+ device_remove_file(&unet->net->dev, &dev_attr_dbg_mask);
+ usb_set_intfdata(iface, unet_list[unet_id]);
+ usbnet_disconnect(iface);
+ unet_list[unet_id] = NULL;
+ }
+
return status;
}
static void rmnet_usb_disconnect(struct usb_interface *intf)
{
- struct usbnet *unet;
+ struct usbnet *unet = usb_get_intfdata(intf);
struct rmnet_ctrl_dev *dev;
+ unsigned int n, rdev_cnt, unet_id;
- unet = usb_get_intfdata(intf);
- if (!unet) {
- dev_err(&intf->dev, "%s:data device not found\n", __func__);
- return;
- }
+ rdev_cnt = unet->data[4] ? no_rmnet_insts_per_dev : 1;
device_set_wakeup_enable(&unet->udev->dev, 0);
- rmnet_usb_data_debugfs_cleanup(unet);
- dev = (struct rmnet_ctrl_dev *)unet->data[1];
- if (!dev) {
- dev_err(&intf->dev, "%s:ctrl device not found\n", __func__);
- return;
+ for (n = 0; n < rdev_cnt; n++) {
+ unet_id = n + unet->driver_info->data * no_rmnet_insts_per_dev;
+ unet =
+ unet->data[4] ? unet_list[unet_id] : usb_get_intfdata(intf);
+ device_remove_file(&unet->net->dev, &dev_attr_dbg_mask);
+
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ rmnet_usb_ctrl_disconnect(dev);
+ unet->data[0] = 0;
+ unet->data[1] = 0;
+ rmnet_usb_data_debugfs_cleanup(unet);
+ usb_set_intfdata(intf, unet);
+ usbnet_disconnect(intf);
+ unet_list[unet_id] = NULL;
}
- unet->data[0] = 0;
- unet->data[1] = 0;
- rmnet_usb_ctrl_disconnect(dev);
- device_remove_file(&unet->net->dev, &dev_attr_dbg_mask);
- usbnet_disconnect(intf);
}
-/*bit position represents interface number*/
-#define PID9034_IFACE_MASK 0xF0
-#define PID9048_IFACE_MASK 0x1E0
-#define PID904C_IFACE_MASK 0x1C0
-
-static const struct driver_info rmnet_info_pid9034 = {
+static struct driver_info rmnet_info = {
.description = "RmNET net device",
.flags = FLAG_SEND_ZLP,
.bind = rmnet_usb_bind,
.tx_fixup = rmnet_usb_tx_fixup,
.rx_fixup = rmnet_usb_rx_fixup,
+ .rx_complete = rmnet_usb_rx_complete,
.manage_power = rmnet_usb_manage_power,
- .data = PID9034_IFACE_MASK,
+ .data = 0,
};
-static const struct driver_info rmnet_info_pid9048 = {
+static struct driver_info rmnet_usb_info = {
.description = "RmNET net device",
.flags = FLAG_SEND_ZLP,
.bind = rmnet_usb_bind,
.tx_fixup = rmnet_usb_tx_fixup,
.rx_fixup = rmnet_usb_rx_fixup,
+ .rx_complete = rmnet_usb_rx_complete,
.manage_power = rmnet_usb_manage_power,
- .data = PID9048_IFACE_MASK,
-};
-
-static const struct driver_info rmnet_info_pid904c = {
- .description = "RmNET net device",
- .flags = FLAG_SEND_ZLP,
- .bind = rmnet_usb_bind,
- .tx_fixup = rmnet_usb_tx_fixup,
- .rx_fixup = rmnet_usb_rx_fixup,
- .manage_power = rmnet_usb_manage_power,
- .data = PID904C_IFACE_MASK,
+ .data = 1,
};
static const struct usb_device_id vidpids[] = {
- {
- USB_DEVICE(0x05c6, 0x9034), /* MDM9x15*/
- .driver_info = (unsigned long)&rmnet_info_pid9034,
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9034, 4),
+ .driver_info = (unsigned long)&rmnet_info,
},
- {
- USB_DEVICE(0x05c6, 0x9048), /* MDM9x15*/
- .driver_info = (unsigned long)&rmnet_info_pid9048,
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9034, 5),
+ .driver_info = (unsigned long)&rmnet_info,
},
- {
- USB_DEVICE(0x05c6, 0x904c), /* MDM9x15*/
- .driver_info = (unsigned long)&rmnet_info_pid904c,
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9034, 6),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9034, 7),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9048, 5),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9048, 6),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9048, 7),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9048, 8),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x904c, 6),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x904c, 7),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x904c, 8),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9075, 6), /*mux over hsic mdm*/
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9079, 5),
+ .driver_info = (unsigned long)&rmnet_usb_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9079, 6),
+ .driver_info = (unsigned long)&rmnet_usb_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9079, 7),
+ .driver_info = (unsigned long)&rmnet_usb_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9079, 8),
+ .driver_info = (unsigned long)&rmnet_usb_info,
},
{ }, /* Terminating entry */
@@ -622,31 +890,36 @@
.supports_autosuspend = true,
};
-static int __init rmnet_usb_init(void)
+static int rmnet_data_start(void)
{
int retval;
+ if (no_rmnet_devs > MAX_RMNET_DEVS) {
+ pr_err("ERROR:%s: param no_rmnet_devs(%d) > than maximum(%d)",
+ __func__, no_rmnet_devs, MAX_RMNET_DEVS);
+ return -EINVAL;
+ }
+
+ /* initialize ctrl devices */
+ retval = rmnet_usb_ctrl_init(no_rmnet_devs, no_rmnet_insts_per_dev);
+ if (retval) {
+ err("rmnet_usb_cmux_init failed: %d", retval);
+ return retval;
+ }
+
retval = usb_register(&rmnet_usb);
if (retval) {
err("usb_register failed: %d", retval);
return retval;
}
- /* initialize rmnet ctrl device here*/
- retval = rmnet_usb_ctrl_init();
- if (retval) {
- usb_deregister(&rmnet_usb);
- err("rmnet_usb_cmux_init failed: %d", retval);
- return retval;
- }
- return 0;
+ return retval;
}
-module_init(rmnet_usb_init);
static void __exit rmnet_usb_exit(void)
{
- rmnet_usb_ctrl_exit();
usb_deregister(&rmnet_usb);
+ rmnet_usb_ctrl_exit(no_rmnet_devs, no_rmnet_insts_per_dev);
}
module_exit(rmnet_usb_exit);
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index fa0c568..b42050c 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -86,7 +86,7 @@
static const char driver_name [] = "usbnet";
-static struct workqueue_struct *usbnet_wq;
+struct workqueue_struct *usbnet_wq;
static DECLARE_WAIT_QUEUE_HEAD(unlink_wakeup);
@@ -343,12 +343,11 @@
/*-------------------------------------------------------------------------*/
-static void rx_complete (struct urb *urb);
-
static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
{
struct sk_buff *skb;
struct skb_data *entry;
+ usb_complete_t complete_fn;
int retval = 0;
unsigned long lockflags;
size_t size = dev->rx_urb_size;
@@ -366,8 +365,13 @@
entry->dev = dev;
entry->length = 0;
+ if (dev->driver_info->rx_complete)
+ complete_fn = dev->driver_info->rx_complete;
+ else
+ complete_fn = rx_complete;
+
usb_fill_bulk_urb (urb, dev->udev, dev->in,
- skb->data, size, rx_complete, skb);
+ skb->data, size, complete_fn, skb);
spin_lock_irqsave (&dev->rxq.lock, lockflags);
@@ -441,7 +445,7 @@
/*-------------------------------------------------------------------------*/
-static void rx_complete (struct urb *urb)
+void rx_complete(struct urb *urb)
{
struct sk_buff *skb = (struct sk_buff *) urb->context;
struct skb_data *entry = (struct skb_data *) skb->cb;
@@ -527,6 +531,7 @@
}
netif_dbg(dev, rx_err, dev->net, "no read resubmitted\n");
}
+EXPORT_SYMBOL_GPL(rx_complete);
static void intr_complete (struct urb *urb)
{
@@ -662,7 +667,7 @@
/*-------------------------------------------------------------------------*/
// precondition: never called in_interrupt
-static void usbnet_terminate_urbs(struct usbnet *dev)
+void usbnet_terminate_urbs(struct usbnet *dev)
{
DECLARE_WAITQUEUE(wait, current);
int temp;
@@ -687,6 +692,7 @@
dev->wait = NULL;
remove_wait_queue(&unlink_wakeup, &wait);
}
+EXPORT_SYMBOL_GPL(usbnet_terminate_urbs);
int usbnet_stop (struct net_device *net)
{
diff --git a/drivers/of/gpio.c b/drivers/of/gpio.c
index 94e76d8..7b41fd8 100644
--- a/drivers/of/gpio.c
+++ b/drivers/of/gpio.c
@@ -18,6 +18,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
+#include <linux/pinctrl/pinctrl.h>
#include <linux/slab.h>
/**
@@ -206,6 +207,53 @@
}
EXPORT_SYMBOL(of_mm_gpiochip_add);
+#ifdef CONFIG_PINCTRL
+void of_gpiochip_add_pin_range(struct gpio_chip *chip)
+{
+ struct device_node *np = chip->of_node;
+ struct of_phandle_args pinspec;
+ struct pinctrl_dev *pctldev;
+ int index = 0, ret;
+
+ if (!np)
+ return;
+
+ do {
+ ret = of_parse_phandle_with_args(np, "gpio-ranges",
+ "#gpio-range-cells", index, &pinspec);
+ if (ret)
+ break;
+
+ pctldev = of_pinctrl_get(pinspec.np);
+ if (!pctldev)
+ break;
+
+ ret = gpiochip_add_pin_range(chip,
+ pinctrl_dev_get_name(pctldev),
+ pinspec.args[0],
+ pinspec.args[1]);
+
+ if (ret)
+ break;
+
+ } while (index++);
+}
+
+void of_gpiochip_remove_pin_range(struct gpio_chip *chip)
+{
+ struct gpio_pin_range *pin_range, *tmp;
+
+ list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) {
+ list_del(&pin_range->node);
+ pinctrl_remove_gpio_range(pin_range->pctldev,
+ &pin_range->range);
+ }
+}
+#else
+void of_gpiochip_add_pin_range(struct gpio_chip *chip) {}
+void of_gpiochip_remove_pin_range(struct gpio_chip *chip) {}
+#endif
+
void of_gpiochip_add(struct gpio_chip *chip)
{
if ((!chip->of_node) && (chip->dev))
@@ -219,11 +267,14 @@
chip->of_xlate = of_gpio_simple_xlate;
}
+ of_gpiochip_add_pin_range(chip);
of_node_get(chip->of_node);
}
void of_gpiochip_remove(struct gpio_chip *chip)
{
+ of_gpiochip_remove_pin_range(chip);
+
if (chip->of_node)
of_node_put(chip->of_node);
}
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index abfb964..3671ac2 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -4,7 +4,6 @@
config PINCTRL
bool
- depends on EXPERIMENTAL
if PINCTRL
@@ -35,19 +34,16 @@
bool "MMP2 pin controller driver"
depends on ARCH_MMP
select PINCTRL_PXA3xx
- select PINCONF
config PINCTRL_PXA168
bool "PXA168 pin controller driver"
depends on ARCH_MMP
select PINCTRL_PXA3xx
- select PINCONF
config PINCTRL_PXA910
bool "PXA910 pin controller driver"
depends on ARCH_MMP
select PINCTRL_PXA3xx
- select PINCONF
config PINCTRL_SIRF
bool "CSR SiRFprimaII pin controller driver"
@@ -56,17 +52,15 @@
config PINCTRL_TEGRA
bool
+ select PINMUX
+ select PINCONF
config PINCTRL_TEGRA20
bool
- select PINMUX
- select PINCONF
select PINCTRL_TEGRA
config PINCTRL_TEGRA30
bool
- select PINMUX
- select PINCONF
select PINCTRL_TEGRA
config PINCTRL_U300
@@ -77,7 +71,7 @@
config PINCTRL_COH901
bool "ST-Ericsson U300 COH 901 335/571 GPIO"
- depends on GPIOLIB && ARCH_U300 && PINMUX_U300
+ depends on GPIOLIB && ARCH_U300 && PINCTRL_U300
help
Say yes here to support GPIO interface on ST-Ericsson U300.
The names of the two IP block variants supported are
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 6d4150b..8e3c95a 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -5,6 +5,9 @@
obj-$(CONFIG_PINCTRL) += core.o
obj-$(CONFIG_PINMUX) += pinmux.o
obj-$(CONFIG_PINCONF) += pinconf.o
+ifeq ($(CONFIG_OF),y)
+obj-$(CONFIG_PINCTRL) += devicetree.o
+endif
obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o
obj-$(CONFIG_PINCTRL_PXA3xx) += pinctrl-pxa3xx.o
obj-$(CONFIG_PINCTRL_MMP2) += pinctrl-mmp2.o
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index df6296c..b0de6e7 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -14,6 +14,7 @@
#define pr_fmt(fmt) "pinctrl core: " fmt
#include <linux/kernel.h>
+#include <linux/kref.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/device.h>
@@ -23,41 +24,42 @@
#include <linux/sysfs.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/machine.h>
#include "core.h"
+#include "devicetree.h"
#include "pinmux.h"
#include "pinconf.h"
-/**
- * struct pinctrl_maps - a list item containing part of the mapping table
- * @node: mapping table list node
- * @maps: array of mapping table entries
- * @num_maps: the number of entries in @maps
- */
-struct pinctrl_maps {
- struct list_head node;
- struct pinctrl_map const *maps;
- unsigned num_maps;
-};
+
+static bool pinctrl_dummy_state;
/* Mutex taken by all entry points */
DEFINE_MUTEX(pinctrl_mutex);
/* Global list of pin control devices (struct pinctrl_dev) */
-static LIST_HEAD(pinctrldev_list);
+LIST_HEAD(pinctrldev_list);
/* List of pin controller handles (struct pinctrl) */
static LIST_HEAD(pinctrl_list);
/* List of pinctrl maps (struct pinctrl_maps) */
-static LIST_HEAD(pinctrl_maps);
+LIST_HEAD(pinctrl_maps);
-#define for_each_maps(_maps_node_, _i_, _map_) \
- list_for_each_entry(_maps_node_, &pinctrl_maps, node) \
- for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \
- _i_ < _maps_node_->num_maps; \
- i++, _map_ = &_maps_node_->maps[_i_])
+
+/**
+ * pinctrl_provide_dummies() - indicate if pinctrl provides dummy state support
+ *
+ * Usually this function is called by platforms without pinctrl driver support
+ * but run with some shared drivers using pinctrl APIs.
+ * After calling this function, the pinctrl core will return successfully
+ * with creating a dummy state for the driver to keep going smoothly.
+ */
+void pinctrl_provide_dummies(void)
+{
+ pinctrl_dummy_state = true;
+}
const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev)
{
@@ -66,6 +68,12 @@
}
EXPORT_SYMBOL_GPL(pinctrl_dev_get_name);
+const char *pinctrl_dev_get_devname(struct pinctrl_dev *pctldev)
+{
+ return dev_name(pctldev->dev);
+}
+EXPORT_SYMBOL_GPL(pinctrl_dev_get_devname);
+
void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev)
{
return pctldev->driver_data;
@@ -124,6 +132,25 @@
}
/**
+ * pin_get_name_from_id() - look up a pin name from a pin id
+ * @pctldev: the pin control device to lookup the pin on
+ * @name: the name of the pin to look up
+ */
+const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin)
+{
+ const struct pin_desc *desc;
+
+ desc = pin_desc_get(pctldev, pin);
+ if (desc == NULL) {
+ dev_err(pctldev->dev, "failed to get pin(%d) name\n",
+ pin);
+ return NULL;
+ }
+
+ return desc->name;
+}
+
+/**
* pin_is_valid() - check if pin exists on controller
* @pctldev: the pin control device to check the pin on
* @pin: pin to check, use the local pin controller index number
@@ -194,8 +221,10 @@
pindesc->name = name;
} else {
pindesc->name = kasprintf(GFP_KERNEL, "PIN%u", number);
- if (pindesc->name == NULL)
+ if (pindesc->name == NULL) {
+ kfree(pindesc);
return -ENOMEM;
+ }
pindesc->dynamic_name = true;
}
@@ -255,7 +284,8 @@
*
* Find the pin controller handling a certain GPIO pin from the pinspace of
* the GPIO subsystem, return the device and the matching GPIO range. Returns
- * negative if the GPIO range could not be found in any device.
+ * -EPROBE_DEFER if the GPIO range could not be found in any device since it
+ * may still have not been registered.
*/
static int pinctrl_get_device_gpio_range(unsigned gpio,
struct pinctrl_dev **outdev,
@@ -275,7 +305,7 @@
}
}
- return -EINVAL;
+ return -EPROBE_DEFER;
}
/**
@@ -295,6 +325,59 @@
}
EXPORT_SYMBOL_GPL(pinctrl_add_gpio_range);
+void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *ranges,
+ unsigned nranges)
+{
+ int i;
+
+ for (i = 0; i < nranges; i++)
+ pinctrl_add_gpio_range(pctldev, &ranges[i]);
+}
+EXPORT_SYMBOL_GPL(pinctrl_add_gpio_ranges);
+
+struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
+ struct pinctrl_gpio_range *range)
+{
+ struct pinctrl_dev *pctldev = get_pinctrl_dev_from_devname(devname);
+
+ /*
+ * If we can't find this device, let's assume that is because
+ * it has not probed yet, so the driver trying to register this
+ * range need to defer probing.
+ */
+ if (!pctldev)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ pinctrl_add_gpio_range(pctldev, range);
+ return pctldev;
+}
+EXPORT_SYMBOL_GPL(pinctrl_find_and_add_gpio_range);
+
+/**
+ * pinctrl_find_gpio_range_from_pin() - locate the GPIO range for a pin
+ * @pctldev: the pin controller device to look in
+ * @pin: a controller-local number to find the range for
+ */
+struct pinctrl_gpio_range *
+pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
+ unsigned int pin)
+{
+ struct pinctrl_gpio_range *range = NULL;
+
+ /* Loop over the ranges */
+ list_for_each_entry(range, &pctldev->gpio_ranges, node) {
+ /* Check if we're in the valid range */
+ if (pin >= range->pin_base &&
+ pin < range->pin_base + range->npins) {
+ return range;
+ }
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(pinctrl_find_gpio_range_from_pin);
+
/**
* pinctrl_remove_gpio_range() - remove a range of GPIOs fro a pin controller
* @pctldev: pin controller device to remove the range from
@@ -318,9 +401,10 @@
const char *pin_group)
{
const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
+ unsigned ngroups = pctlops->get_groups_count(pctldev);
unsigned group_selector = 0;
- while (pctlops->list_groups(pctldev, group_selector) >= 0) {
+ while (group_selector < ngroups) {
const char *gname = pctlops->get_group_name(pctldev,
group_selector);
if (!strcmp(gname, pin_group)) {
@@ -360,7 +444,7 @@
ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range);
if (ret) {
mutex_unlock(&pinctrl_mutex);
- return -EINVAL;
+ return ret;
}
/* Convert to the pin controllers number space */
@@ -516,13 +600,21 @@
setting->pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name);
if (setting->pctldev == NULL) {
- dev_err(p->dev, "unknown pinctrl device %s in map entry",
- map->ctrl_dev_name);
kfree(setting);
- /* Eventually, this should trigger deferred probe */
- return -ENODEV;
+ /* Do not defer probing of hogs (circular loop) */
+ if (!strcmp(map->ctrl_dev_name, map->dev_name))
+ return -ENODEV;
+ /*
+ * OK let us guess that the driver is not there yet, and
+ * let's defer obtaining this pinctrl handle to later...
+ */
+ dev_info(p->dev, "unknown pinctrl device %s in map entry, deferring probe",
+ map->ctrl_dev_name);
+ return -EPROBE_DEFER;
}
+ setting->dev_name = map->dev_name;
+
switch (map->type) {
case PIN_MAP_TYPE_MUX_GROUP:
ret = pinmux_map_to_setting(map, setting);
@@ -579,6 +671,13 @@
}
p->dev = dev;
INIT_LIST_HEAD(&p->states);
+ INIT_LIST_HEAD(&p->dt_maps);
+
+ ret = pinctrl_dt_to_map(p);
+ if (ret < 0) {
+ kfree(p);
+ return ERR_PTR(ret);
+ }
devname = dev_name(dev);
@@ -589,13 +688,33 @@
continue;
ret = add_setting(p, map);
- if (ret < 0) {
+ /*
+ * At this point the adding of a setting may:
+ *
+ * - Defer, if the pinctrl device is not yet available
+ * - Fail, if the pinctrl device is not yet available,
+ * AND the setting is a hog. We cannot defer that, since
+ * the hog will kick in immediately after the device
+ * is registered.
+ *
+ * If the error returned was not -EPROBE_DEFER then we
+ * accumulate the errors to see if we end up with
+ * an -EPROBE_DEFER later, as that is the worst case.
+ */
+ if (ret == -EPROBE_DEFER) {
pinctrl_put_locked(p, false);
return ERR_PTR(ret);
}
}
+ if (ret < 0) {
+ /* If some other error than deferral occured, return here */
+ pinctrl_put_locked(p, false);
+ return ERR_PTR(ret);
+ }
- /* Add the pinmux to the global list */
+ kref_init(&p->users);
+
+ /* Add the pinctrl handle to the global list */
list_add_tail(&p->node, &pinctrl_list);
return p;
@@ -608,15 +727,19 @@
if (WARN_ON(!dev))
return ERR_PTR(-EINVAL);
+ /*
+ * See if somebody else (such as the device core) has already
+ * obtained a handle to the pinctrl for this device. In that case,
+ * return another pointer to it.
+ */
p = find_pinctrl(dev);
- if (p != NULL)
- return ERR_PTR(-EBUSY);
-
- p = create_pinctrl(dev);
- if (IS_ERR(p))
+ if (p != NULL) {
+ dev_dbg(dev, "obtain a copy of previously claimed pinctrl\n");
+ kref_get(&p->users);
return p;
+ }
- return p;
+ return create_pinctrl(dev);
}
/**
@@ -662,19 +785,32 @@
kfree(state);
}
+ pinctrl_dt_free_maps(p);
+
if (inlist)
list_del(&p->node);
kfree(p);
}
/**
- * pinctrl_put() - release a previously claimed pinctrl handle
+ * pinctrl_release() - release the pinctrl handle
+ * @kref: the kref in the pinctrl being released
+ */
+static void pinctrl_release(struct kref *kref)
+{
+ struct pinctrl *p = container_of(kref, struct pinctrl, users);
+
+ pinctrl_put_locked(p, true);
+}
+
+/**
+ * pinctrl_put() - decrease use count on a previously claimed pinctrl handle
* @p: the pinctrl handle to release
*/
void pinctrl_put(struct pinctrl *p)
{
mutex_lock(&pinctrl_mutex);
- pinctrl_put_locked(p, true);
+ kref_put(&p->users, pinctrl_release);
mutex_unlock(&pinctrl_mutex);
}
EXPORT_SYMBOL_GPL(pinctrl_put);
@@ -685,8 +821,15 @@
struct pinctrl_state *state;
state = find_state(p, name);
- if (!state)
- return ERR_PTR(-ENODEV);
+ if (!state) {
+ if (pinctrl_dummy_state) {
+ /* create dummy state */
+ dev_dbg(p->dev, "using pinctrl dummy state (%s)\n",
+ name);
+ state = create_state(p, name);
+ } else
+ state = ERR_PTR(-ENODEV);
+ }
return state;
}
@@ -787,15 +930,63 @@
}
EXPORT_SYMBOL_GPL(pinctrl_select_state);
+static void devm_pinctrl_release(struct device *dev, void *res)
+{
+ pinctrl_put(*(struct pinctrl **)res);
+}
+
/**
- * pinctrl_register_mappings() - register a set of pin controller mappings
- * @maps: the pincontrol mappings table to register. This should probably be
- * marked with __initdata so it can be discarded after boot. This
- * function will perform a shallow copy for the mapping entries.
- * @num_maps: the number of maps in the mapping table
+ * struct devm_pinctrl_get() - Resource managed pinctrl_get()
+ * @dev: the device to obtain the handle for
+ *
+ * If there is a need to explicitly destroy the returned struct pinctrl,
+ * devm_pinctrl_put() should be used, rather than plain pinctrl_put().
*/
-int pinctrl_register_mappings(struct pinctrl_map const *maps,
- unsigned num_maps)
+struct pinctrl *devm_pinctrl_get(struct device *dev)
+{
+ struct pinctrl **ptr, *p;
+
+ ptr = devres_alloc(devm_pinctrl_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ p = pinctrl_get(dev);
+ if (!IS_ERR(p)) {
+ *ptr = p;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return p;
+}
+EXPORT_SYMBOL_GPL(devm_pinctrl_get);
+
+static int devm_pinctrl_match(struct device *dev, void *res, void *data)
+{
+ struct pinctrl **p = res;
+
+ return *p == data;
+}
+
+/**
+ * devm_pinctrl_put() - Resource managed pinctrl_put()
+ * @p: the pinctrl handle to release
+ *
+ * Deallocate a struct pinctrl obtained via devm_pinctrl_get(). Normally
+ * this function will not need to be called and the resource management
+ * code will ensure that the resource is freed.
+ */
+void devm_pinctrl_put(struct pinctrl *p)
+{
+ WARN_ON(devres_destroy(p->dev, devm_pinctrl_release,
+ devm_pinctrl_match, p));
+ pinctrl_put(p);
+}
+EXPORT_SYMBOL_GPL(devm_pinctrl_put);
+
+int pinctrl_register_map(struct pinctrl_map const *maps, unsigned num_maps,
+ bool dup, bool locked)
{
int i, ret;
struct pinctrl_maps *maps_node;
@@ -829,13 +1020,13 @@
case PIN_MAP_TYPE_MUX_GROUP:
ret = pinmux_validate_map(&maps[i], i);
if (ret < 0)
- return 0;
+ return ret;
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
ret = pinconf_validate_map(&maps[i], i);
if (ret < 0)
- return 0;
+ return ret;
break;
default:
pr_err("failed to register map %s (%d): invalid type given\n",
@@ -851,20 +1042,76 @@
}
maps_node->num_maps = num_maps;
- maps_node->maps = kmemdup(maps, sizeof(*maps) * num_maps, GFP_KERNEL);
- if (!maps_node->maps) {
- pr_err("failed to duplicate mapping table\n");
- kfree(maps_node);
- return -ENOMEM;
+ if (dup) {
+ maps_node->maps = kmemdup(maps, sizeof(*maps) * num_maps,
+ GFP_KERNEL);
+ if (!maps_node->maps) {
+ pr_err("failed to duplicate mapping table\n");
+ kfree(maps_node);
+ return -ENOMEM;
+ }
+ } else {
+ maps_node->maps = maps;
}
- mutex_lock(&pinctrl_mutex);
+ if (!locked)
+ mutex_lock(&pinctrl_mutex);
list_add_tail(&maps_node->node, &pinctrl_maps);
- mutex_unlock(&pinctrl_mutex);
+ if (!locked)
+ mutex_unlock(&pinctrl_mutex);
return 0;
}
+/**
+ * pinctrl_register_mappings() - register a set of pin controller mappings
+ * @maps: the pincontrol mappings table to register. This should probably be
+ * marked with __initdata so it can be discarded after boot. This
+ * function will perform a shallow copy for the mapping entries.
+ * @num_maps: the number of maps in the mapping table
+ */
+int pinctrl_register_mappings(struct pinctrl_map const *maps,
+ unsigned num_maps)
+{
+ return pinctrl_register_map(maps, num_maps, true, false);
+}
+
+void pinctrl_unregister_map(struct pinctrl_map const *map)
+{
+ struct pinctrl_maps *maps_node;
+
+ list_for_each_entry(maps_node, &pinctrl_maps, node) {
+ if (maps_node->maps == map) {
+ list_del(&maps_node->node);
+ return;
+ }
+ }
+}
+
+/**
+ * pinctrl_force_sleep() - turn a given controller device into sleep state
+ * @pctldev: pin controller device
+ */
+int pinctrl_force_sleep(struct pinctrl_dev *pctldev)
+{
+ if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_sleep))
+ return pinctrl_select_state(pctldev->p, pctldev->hog_sleep);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pinctrl_force_sleep);
+
+/**
+ * pinctrl_force_default() - turn a given controller device into default state
+ * @pctldev: pin controller device
+ */
+int pinctrl_force_default(struct pinctrl_dev *pctldev)
+{
+ if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_default))
+ return pinctrl_select_state(pctldev->p, pctldev->hog_default);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pinctrl_force_default);
+
#ifdef CONFIG_DEBUG_FS
static int pinctrl_pins_show(struct seq_file *s, void *what)
@@ -906,15 +1153,17 @@
{
struct pinctrl_dev *pctldev = s->private;
const struct pinctrl_ops *ops = pctldev->desc->pctlops;
- unsigned selector = 0;
+ unsigned ngroups, selector = 0;
+ ngroups = ops->get_groups_count(pctldev);
mutex_lock(&pinctrl_mutex);
seq_puts(s, "registered pin groups:\n");
- while (ops->list_groups(pctldev, selector) >= 0) {
+ while (selector < ngroups) {
const unsigned *pins;
unsigned num_pins;
const char *gname = ops->get_group_name(pctldev, selector);
+ const char *pname;
int ret;
int i;
@@ -924,10 +1173,16 @@
seq_printf(s, "%s [ERROR GETTING PINS]\n",
gname);
else {
- seq_printf(s, "group: %s, pins = [ ", gname);
- for (i = 0; i < num_pins; i++)
- seq_printf(s, "%d ", pins[i]);
- seq_puts(s, "]\n");
+ seq_printf(s, "group: %s\n", gname);
+ for (i = 0; i < num_pins; i++) {
+ pname = pin_get_name(pctldev, pins[i]);
+ if (WARN_ON(!pname)) {
+ mutex_unlock(&pinctrl_mutex);
+ return -EINVAL;
+ }
+ seq_printf(s, "pin %d (%s)\n", pins[i], pname);
+ }
+ seq_puts(s, "\n");
}
selector++;
}
@@ -1226,11 +1481,14 @@
const struct pinctrl_ops *ops = pctldev->desc->pctlops;
if (!ops ||
- !ops->list_groups ||
+ !ops->get_groups_count ||
!ops->get_group_name ||
!ops->get_group_pins)
return -EINVAL;
+ if (ops->dt_node_to_map && !ops->dt_free_map)
+ return -EINVAL;
+
return 0;
}
@@ -1246,9 +1504,9 @@
struct pinctrl_dev *pctldev;
int ret;
- if (pctldesc == NULL)
+ if (!pctldesc)
return NULL;
- if (pctldesc->name == NULL)
+ if (!pctldesc->name)
return NULL;
pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);
@@ -1266,39 +1524,28 @@
pctldev->dev = dev;
/* check core ops for sanity */
- ret = pinctrl_check_ops(pctldev);
- if (ret) {
- pr_err("%s pinctrl ops lacks necessary functions\n",
- pctldesc->name);
+ if (pinctrl_check_ops(pctldev)) {
+ dev_err(dev, "pinctrl ops lacks necessary functions\n");
goto out_err;
}
/* If we're implementing pinmuxing, check the ops for sanity */
if (pctldesc->pmxops) {
- ret = pinmux_check_ops(pctldev);
- if (ret) {
- pr_err("%s pinmux ops lacks necessary functions\n",
- pctldesc->name);
+ if (pinmux_check_ops(pctldev))
goto out_err;
- }
}
/* If we're implementing pinconfig, check the ops for sanity */
if (pctldesc->confops) {
- ret = pinconf_check_ops(pctldev);
- if (ret) {
- pr_err("%s pin config ops lacks necessary functions\n",
- pctldesc->name);
+ if (pinconf_check_ops(pctldev))
goto out_err;
- }
}
/* Register all the pins */
- pr_debug("try to register %d pins on %s...\n",
- pctldesc->npins, pctldesc->name);
+ dev_dbg(dev, "try to register %d pins ...\n", pctldesc->npins);
ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);
if (ret) {
- pr_err("error during pin registration\n");
+ dev_err(dev, "error during pin registration\n");
pinctrl_free_pindescs(pctldev, pctldesc->pins,
pctldesc->npins);
goto out_err;
@@ -1310,11 +1557,23 @@
pctldev->p = pinctrl_get_locked(pctldev->dev);
if (!IS_ERR(pctldev->p)) {
- struct pinctrl_state *s =
+ pctldev->hog_default =
pinctrl_lookup_state_locked(pctldev->p,
PINCTRL_STATE_DEFAULT);
- if (!IS_ERR(s))
- pinctrl_select_state_locked(pctldev->p, s);
+ if (IS_ERR(pctldev->hog_default)) {
+ dev_dbg(dev, "failed to lookup the default state\n");
+ } else {
+ if (pinctrl_select_state_locked(pctldev->p,
+ pctldev->hog_default))
+ dev_err(dev,
+ "failed to select default state\n");
+ }
+
+ pctldev->hog_sleep =
+ pinctrl_lookup_state_locked(pctldev->p,
+ PINCTRL_STATE_SLEEP);
+ if (IS_ERR(pctldev->hog_sleep))
+ dev_dbg(dev, "failed to lookup the sleep state\n");
}
mutex_unlock(&pinctrl_mutex);
@@ -1337,6 +1596,7 @@
*/
void pinctrl_unregister(struct pinctrl_dev *pctldev)
{
+ struct pinctrl_gpio_range *range, *n;
if (pctldev == NULL)
return;
@@ -1352,6 +1612,10 @@
/* Destroy descriptor tree */
pinctrl_free_pindescs(pctldev, pctldev->desc->pins,
pctldev->desc->npins);
+ /* remove gpio ranges map */
+ list_for_each_entry_safe(range, n, &pctldev->gpio_ranges, node)
+ list_del(&range->node);
+
kfree(pctldev);
mutex_unlock(&pinctrl_mutex);
diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h
index 17ecf65..ee72f1f 100644
--- a/drivers/pinctrl/core.h
+++ b/drivers/pinctrl/core.h
@@ -9,6 +9,7 @@
* License terms: GNU General Public License (GPL) version 2
*/
+#include <linux/kref.h>
#include <linux/mutex.h>
#include <linux/radix-tree.h>
#include <linux/pinctrl/pinconf.h>
@@ -30,6 +31,8 @@
* @driver_data: driver data for drivers registering to the pin controller
* subsystem
* @p: result of pinctrl_get() for this device
+ * @hog_default: default state for pins hogged by this device
+ * @hog_sleep: sleep state for pins hogged by this device
* @device_root: debugfs root for this device
*/
struct pinctrl_dev {
@@ -41,6 +44,8 @@
struct module *owner;
void *driver_data;
struct pinctrl *p;
+ struct pinctrl_state *hog_default;
+ struct pinctrl_state *hog_sleep;
#ifdef CONFIG_DEBUG_FS
struct dentry *device_root;
#endif
@@ -52,12 +57,17 @@
* @dev: the device using this pin control handle
* @states: a list of states for this device
* @state: the current state
+ * @dt_maps: the mapping table chunks dynamically parsed from device tree for
+ * this device, if any
+ * @users: reference count
*/
struct pinctrl {
struct list_head node;
struct device *dev;
struct list_head states;
struct pinctrl_state *state;
+ struct list_head dt_maps;
+ struct kref users;
};
/**
@@ -100,13 +110,16 @@
* struct pinctrl_setting - an individual mux or config setting
* @node: list node for struct pinctrl_settings's @settings field
* @type: the type of setting
- * @pctldev: pin control device handling to be programmed
+ * @pctldev: pin control device handling to be programmed. Not used for
+ * PIN_MAP_TYPE_DUMMY_STATE.
+ * @dev_name: the name of the device using this state
* @data: Data specific to the setting type
*/
struct pinctrl_setting {
struct list_head node;
enum pinctrl_map_type type;
struct pinctrl_dev *pctldev;
+ const char *dev_name;
union {
struct pinctrl_setting_mux mux;
struct pinctrl_setting_configs configs;
@@ -142,8 +155,21 @@
#endif
};
+/**
+ * struct pinctrl_maps - a list item containing part of the mapping table
+ * @node: mapping table list node
+ * @maps: array of mapping table entries
+ * @num_maps: the number of entries in @maps
+ */
+struct pinctrl_maps {
+ struct list_head node;
+ struct pinctrl_map const *maps;
+ unsigned num_maps;
+};
+
struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *dev_name);
int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name);
+const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin);
int pinctrl_get_group_selector(struct pinctrl_dev *pctldev,
const char *pin_group);
@@ -153,4 +179,19 @@
return radix_tree_lookup(&pctldev->pin_desc_tree, pin);
}
+int pinctrl_register_map(struct pinctrl_map const *maps, unsigned num_maps,
+ bool dup, bool locked);
+void pinctrl_unregister_map(struct pinctrl_map const *map);
+
+extern int pinctrl_force_sleep(struct pinctrl_dev *pctldev);
+extern int pinctrl_force_default(struct pinctrl_dev *pctldev);
+
extern struct mutex pinctrl_mutex;
+extern struct list_head pinctrldev_list;
+extern struct list_head pinctrl_maps;
+
+#define for_each_maps(_maps_node_, _i_, _map_) \
+ list_for_each_entry(_maps_node_, &pinctrl_maps, node) \
+ for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \
+ _i_ < _maps_node_->num_maps; \
+ _i_++, _map_ = &_maps_node_->maps[_i_])
diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c
new file mode 100644
index 0000000..fd40a11
--- /dev/null
+++ b/drivers/pinctrl/devicetree.c
@@ -0,0 +1,265 @@
+/*
+ * Device tree integration for the pin control subsystem
+ *
+ * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/slab.h>
+
+#include "core.h"
+#include "devicetree.h"
+
+/**
+ * struct pinctrl_dt_map - mapping table chunk parsed from device tree
+ * @node: list node for struct pinctrl's @dt_maps field
+ * @pctldev: the pin controller that allocated this struct, and will free it
+ * @maps: the mapping table entries
+ */
+struct pinctrl_dt_map {
+ struct list_head node;
+ struct pinctrl_dev *pctldev;
+ struct pinctrl_map *map;
+ unsigned num_maps;
+};
+
+static void dt_free_map(struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+ if (pctldev) {
+ struct pinctrl_ops *ops = pctldev->desc->pctlops;
+ ops->dt_free_map(pctldev, map, num_maps);
+ } else {
+ /* There is no pctldev for PIN_MAP_TYPE_DUMMY_STATE */
+ kfree(map);
+ }
+}
+
+void pinctrl_dt_free_maps(struct pinctrl *p)
+{
+ struct pinctrl_dt_map *dt_map, *n1;
+
+ list_for_each_entry_safe(dt_map, n1, &p->dt_maps, node) {
+ pinctrl_unregister_map(dt_map->map);
+ list_del(&dt_map->node);
+ dt_free_map(dt_map->pctldev, dt_map->map,
+ dt_map->num_maps);
+ kfree(dt_map);
+ }
+
+ of_node_put(p->dev->of_node);
+}
+
+static int dt_remember_or_free_map(struct pinctrl *p, const char *statename,
+ struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+ int i;
+ struct pinctrl_dt_map *dt_map;
+
+ /* Initialize common mapping table entry fields */
+ for (i = 0; i < num_maps; i++) {
+ map[i].dev_name = dev_name(p->dev);
+ map[i].name = statename;
+ if (pctldev)
+ map[i].ctrl_dev_name = dev_name(pctldev->dev);
+ }
+
+ /* Remember the converted mapping table entries */
+ dt_map = kzalloc(sizeof(*dt_map), GFP_KERNEL);
+ if (!dt_map) {
+ dev_err(p->dev, "failed to alloc struct pinctrl_dt_map\n");
+ dt_free_map(pctldev, map, num_maps);
+ return -ENOMEM;
+ }
+
+ dt_map->pctldev = pctldev;
+ dt_map->map = map;
+ dt_map->num_maps = num_maps;
+ list_add_tail(&dt_map->node, &p->dt_maps);
+
+ return pinctrl_register_map(map, num_maps, false, true);
+}
+
+static struct pinctrl_dev *find_pinctrl_by_of_node(struct device_node *np)
+{
+ struct pinctrl_dev *pctldev;
+
+ list_for_each_entry(pctldev, &pinctrldev_list, node)
+ if (pctldev->dev->of_node == np)
+ return pctldev;
+
+ return NULL;
+}
+
+struct pinctrl_dev *of_pinctrl_get(struct device_node *np)
+{
+ struct pinctrl_dev *pctldev;
+
+ pctldev = find_pinctrl_by_of_node(np);
+ if (!pctldev)
+ return NULL;
+
+ return pctldev;
+}
+
+static int dt_to_map_one_config(struct pinctrl *p, const char *statename,
+ struct device_node *np_config)
+{
+ struct device_node *np_pctldev;
+ struct pinctrl_dev *pctldev;
+ struct pinctrl_ops *ops;
+ int ret;
+ struct pinctrl_map *map;
+ unsigned num_maps;
+
+ /* Find the pin controller containing np_config */
+ np_pctldev = of_node_get(np_config);
+ for (;;) {
+ np_pctldev = of_get_next_parent(np_pctldev);
+ if (!np_pctldev || of_node_is_root(np_pctldev)) {
+ dev_info(p->dev, "could not find pctldev for node %s, deferring probe\n",
+ np_config->full_name);
+ of_node_put(np_pctldev);
+ /* OK let's just assume this will appear later then */
+ return -EPROBE_DEFER;
+ }
+ pctldev = find_pinctrl_by_of_node(np_pctldev);
+ if (pctldev)
+ break;
+ /* Do not defer probing of hogs (circular loop) */
+ if (np_pctldev == p->dev->of_node) {
+ of_node_put(np_pctldev);
+ return -ENODEV;
+ }
+ }
+ of_node_put(np_pctldev);
+
+ /*
+ * Call pinctrl driver to parse device tree node, and
+ * generate mapping table entries
+ */
+ ops = pctldev->desc->pctlops;
+ if (!ops->dt_node_to_map) {
+ dev_err(p->dev, "pctldev %s doesn't support DT\n",
+ dev_name(pctldev->dev));
+ return -ENODEV;
+ }
+ ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);
+ if (ret < 0)
+ return ret;
+
+ /* Stash the mapping table chunk away for later use */
+ return dt_remember_or_free_map(p, statename, pctldev, map, num_maps);
+}
+
+static int dt_remember_dummy_state(struct pinctrl *p, const char *statename)
+{
+ struct pinctrl_map *map;
+
+ map = kzalloc(sizeof(*map), GFP_KERNEL);
+ if (!map) {
+ dev_err(p->dev, "failed to alloc struct pinctrl_map\n");
+ return -ENOMEM;
+ }
+
+ /* There is no pctldev for PIN_MAP_TYPE_DUMMY_STATE */
+ map->type = PIN_MAP_TYPE_DUMMY_STATE;
+
+ return dt_remember_or_free_map(p, statename, NULL, map, 1);
+}
+
+int pinctrl_dt_to_map(struct pinctrl *p)
+{
+ struct device_node *np = p->dev->of_node;
+ int state, ret;
+ char *propname;
+ struct property *prop;
+ const char *statename;
+ const __be32 *list;
+ int size, config;
+ phandle phandle;
+ struct device_node *np_config;
+
+ /* CONFIG_OF enabled, p->dev not instantiated from DT */
+ if (!np) {
+ dev_dbg(p->dev, "no of_node; not parsing pinctrl DT\n");
+ return 0;
+ }
+
+ /* We may store pointers to property names within the node */
+ of_node_get(np);
+
+ /* For each defined state ID */
+ for (state = 0; ; state++) {
+ /* Retrieve the pinctrl-* property */
+ propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);
+ prop = of_find_property(np, propname, &size);
+ kfree(propname);
+ if (!prop)
+ break;
+ list = prop->value;
+ size /= sizeof(*list);
+
+ /* Determine whether pinctrl-names property names the state */
+ ret = of_property_read_string_index(np, "pinctrl-names",
+ state, &statename);
+ /*
+ * If not, statename is just the integer state ID. But rather
+ * than dynamically allocate it and have to free it later,
+ * just point part way into the property name for the string.
+ */
+ if (ret < 0) {
+ /* strlen("pinctrl-") == 8 */
+ statename = prop->name + 8;
+ }
+
+ /* For every referenced pin configuration node in it */
+ for (config = 0; config < size; config++) {
+ phandle = be32_to_cpup(list++);
+
+ /* Look up the pin configuration node */
+ np_config = of_find_node_by_phandle(phandle);
+ if (!np_config) {
+ dev_err(p->dev,
+ "prop %s index %i invalid phandle\n",
+ prop->name, config);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Parse the node */
+ ret = dt_to_map_one_config(p, statename, np_config);
+ of_node_put(np_config);
+ if (ret < 0)
+ goto err;
+ }
+
+ /* No entries in DT? Generate a dummy state table entry */
+ if (!size) {
+ ret = dt_remember_dummy_state(p, statename);
+ if (ret < 0)
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ pinctrl_dt_free_maps(p);
+ return ret;
+}
diff --git a/drivers/pinctrl/devicetree.h b/drivers/pinctrl/devicetree.h
new file mode 100644
index 0000000..760bc49
--- /dev/null
+++ b/drivers/pinctrl/devicetree.h
@@ -0,0 +1,35 @@
+/*
+ * Internal interface to pinctrl device tree integration
+ *
+ * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef CONFIG_OF
+
+void pinctrl_dt_free_maps(struct pinctrl *p);
+int pinctrl_dt_to_map(struct pinctrl *p);
+
+#else
+
+static inline int pinctrl_dt_to_map(struct pinctrl *p)
+{
+ return 0;
+}
+
+static inline void pinctrl_dt_free_maps(struct pinctrl *p)
+{
+}
+
+#endif
diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c
index 33fbaea..06c304a 100644
--- a/drivers/pinctrl/pinconf-generic.c
+++ b/drivers/pinctrl/pinconf-generic.c
@@ -41,10 +41,13 @@
PCONFDUMP(PIN_CONFIG_DRIVE_PUSH_PULL, "output drive push pull", NULL),
PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_DRAIN, "output drive open drain", NULL),
PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_SOURCE, "output drive open source", NULL),
+ PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT_ENABLE, "input schmitt enabled", NULL),
PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT, "input schmitt trigger", NULL),
PCONFDUMP(PIN_CONFIG_INPUT_DEBOUNCE, "input debounce", "time units"),
PCONFDUMP(PIN_CONFIG_POWER_SOURCE, "pin power source", "selector"),
+ PCONFDUMP(PIN_CONFIG_SLEW_RATE, "slew rate", NULL),
PCONFDUMP(PIN_CONFIG_LOW_POWER_MODE, "pin low power", "mode"),
+ PCONFDUMP(PIN_CONFIG_OUTPUT, "pin output", "level"),
};
void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev,
diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c
index 7321e86..d611ecf 100644
--- a/drivers/pinctrl/pinconf.c
+++ b/drivers/pinctrl/pinconf.c
@@ -28,11 +28,17 @@
const struct pinconf_ops *ops = pctldev->desc->confops;
/* We must be able to read out pin status */
- if (!ops->pin_config_get && !ops->pin_config_group_get)
+ if (!ops->pin_config_get && !ops->pin_config_group_get) {
+ dev_err(pctldev->dev,
+ "pinconf must be able to read out pin status\n");
return -EINVAL;
+ }
/* We have to be able to config the pins in SOME way */
- if (!ops->pin_config_set && !ops->pin_config_group_set)
+ if (!ops->pin_config_set && !ops->pin_config_group_set) {
+ dev_err(pctldev->dev,
+ "pinconf has to be able to set a pins config\n");
return -EINVAL;
+ }
return 0;
}
@@ -44,9 +50,9 @@
return -EINVAL;
}
- if (map->data.configs.num_configs &&
+ if (!map->data.configs.num_configs ||
!map->data.configs.configs) {
- pr_err("failed to register map %s (%d): no configs ptr given\n",
+ pr_err("failed to register map %s (%d): no configs given\n",
map->name, i);
return -EINVAL;
}
@@ -379,8 +385,16 @@
void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map)
{
+ struct pinctrl_dev *pctldev;
+ const struct pinconf_ops *confops;
int i;
+ pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name);
+ if (pctldev)
+ confops = pctldev->desc->confops;
+ else
+ confops = NULL;
+
switch (map->type) {
case PIN_MAP_TYPE_CONFIGS_PIN:
seq_printf(s, "pin ");
@@ -394,8 +408,15 @@
seq_printf(s, "%s\n", map->data.configs.group_or_pin);
- for (i = 0; i < map->data.configs.num_configs; i++)
- seq_printf(s, "config %08lx\n", map->data.configs.configs[i]);
+ for (i = 0; i < map->data.configs.num_configs; i++) {
+ seq_printf(s, "config ");
+ if (confops && confops->pin_config_config_dbg_show)
+ confops->pin_config_config_dbg_show(pctldev, s,
+ map->data.configs.configs[i]);
+ else
+ seq_printf(s, "%08lx", map->data.configs.configs[i]);
+ seq_printf(s, "\n");
+ }
}
void pinconf_show_setting(struct seq_file *s,
@@ -403,6 +424,7 @@
{
struct pinctrl_dev *pctldev = setting->pctldev;
const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
+ const struct pinconf_ops *confops = pctldev->desc->confops;
struct pin_desc *desc;
int i;
@@ -428,8 +450,15 @@
* FIXME: We should really get the pin controler to dump the config
* values, so they can be decoded to something meaningful.
*/
- for (i = 0; i < setting->data.configs.num_configs; i++)
- seq_printf(s, " %08lx", setting->data.configs.configs[i]);
+ for (i = 0; i < setting->data.configs.num_configs; i++) {
+ seq_printf(s, " ");
+ if (confops && confops->pin_config_config_dbg_show)
+ confops->pin_config_config_dbg_show(pctldev, s,
+ setting->data.configs.configs[i]);
+ else
+ seq_printf(s, "%08lx",
+ setting->data.configs.configs[i]);
+ }
seq_printf(s, "\n");
}
@@ -448,10 +477,14 @@
static int pinconf_pins_show(struct seq_file *s, void *what)
{
struct pinctrl_dev *pctldev = s->private;
+ const struct pinconf_ops *ops = pctldev->desc->confops;
unsigned i, pin;
+ if (!ops || !ops->pin_config_get)
+ return 0;
+
seq_puts(s, "Pin config settings per pin\n");
- seq_puts(s, "Format: pin (name): pinmux setting array\n");
+ seq_puts(s, "Format: pin (name): configs\n");
mutex_lock(&pinctrl_mutex);
@@ -495,17 +528,16 @@
struct pinctrl_dev *pctldev = s->private;
const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
const struct pinconf_ops *ops = pctldev->desc->confops;
+ unsigned ngroups = pctlops->get_groups_count(pctldev);
unsigned selector = 0;
if (!ops || !ops->pin_config_group_get)
return 0;
seq_puts(s, "Pin config settings per pin group\n");
- seq_puts(s, "Format: group (name): pinmux setting array\n");
+ seq_puts(s, "Format: group (name): configs\n");
- mutex_lock(&pinctrl_mutex);
-
- while (pctlops->list_groups(pctldev, selector) >= 0) {
+ while (selector < ngroups) {
const char *gname = pctlops->get_group_name(pctldev, selector);
seq_printf(s, "%u (%s):", selector, gname);
@@ -515,8 +547,6 @@
selector++;
}
- mutex_unlock(&pinctrl_mutex);
-
return 0;
}
@@ -544,6 +574,207 @@
.release = single_release,
};
+/* 32bit read/write ressources */
+#define MAX_NAME_LEN 16
+char dbg_pinname[MAX_NAME_LEN]; /* shared: name of the state of the pin*/
+char dbg_state_name[MAX_NAME_LEN]; /* shared: state of the pin*/
+static u32 dbg_config; /* shared: config to be read/set for the pin & state*/
+
+static int pinconf_dbg_pinname_print(struct seq_file *s, void *d)
+{
+ if (strlen(dbg_pinname))
+ seq_printf(s, "%s\n", dbg_pinname);
+ else
+ seq_printf(s, "No pin name set\n");
+ return 0;
+}
+
+static int pinconf_dbg_pinname_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pinconf_dbg_pinname_print, inode->i_private);
+}
+
+static int pinconf_dbg_pinname_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ int err;
+
+ if (count > MAX_NAME_LEN)
+ return -EINVAL;
+
+ err = sscanf(user_buf, "%15s", dbg_pinname);
+
+ if (err != 1)
+ return -EINVAL;
+
+ return count;
+}
+
+static const struct file_operations pinconf_dbg_pinname_fops = {
+ .open = pinconf_dbg_pinname_open,
+ .write = pinconf_dbg_pinname_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int pinconf_dbg_state_print(struct seq_file *s, void *d)
+{
+ if (strlen(dbg_state_name))
+ seq_printf(s, "%s\n", dbg_state_name);
+ else
+ seq_printf(s, "No pin state set\n");
+ return 0;
+}
+
+static int pinconf_dbg_state_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pinconf_dbg_state_print, inode->i_private);
+}
+
+static int pinconf_dbg_state_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ int err;
+
+ if (count > MAX_NAME_LEN)
+ return -EINVAL;
+
+ err = sscanf(user_buf, "%15s", dbg_state_name);
+
+ if (err != 1)
+ return -EINVAL;
+
+ return count;
+}
+
+static const struct file_operations pinconf_dbg_pinstate_fops = {
+ .open = pinconf_dbg_state_open,
+ .write = pinconf_dbg_state_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/**
+ * pinconf_dbg_config_print() - display the pinctrl config from the pinctrl
+ * map, of a pin/state pair based on pinname and state that have been
+ * selected with the debugfs entries pinconf-name and pinconf-state
+ * @s: contains the 32bits config to be written
+ * @d: not used
+ */
+static int pinconf_dbg_config_print(struct seq_file *s, void *d)
+{
+ struct pinctrl_maps *maps_node;
+ struct pinctrl_map const *map;
+ struct pinctrl_dev *pctldev = NULL;
+ struct pinconf_ops *confops = NULL;
+ int i, j;
+ bool found = false;
+
+ mutex_lock(&pinctrl_mutex);
+
+ /* Parse the pinctrl map and look for the elected pin/state */
+ for_each_maps(maps_node, i, map) {
+ if (map->type != PIN_MAP_TYPE_CONFIGS_PIN)
+ continue;
+
+ if (strncmp(map->name, dbg_state_name, MAX_NAME_LEN) > 0)
+ continue;
+
+ for (j = 0; j < map->data.configs.num_configs; j++) {
+ if (0 == strncmp(map->data.configs.group_or_pin,
+ dbg_pinname, MAX_NAME_LEN)) {
+ /* We found the right pin / state, read the
+ * config and store the pctldev */
+ dbg_config = map->data.configs.configs[j];
+ pctldev = get_pinctrl_dev_from_devname
+ (map->ctrl_dev_name);
+ found = true;
+ break;
+ }
+ }
+ }
+
+ mutex_unlock(&pinctrl_mutex);
+
+ if (found) {
+ seq_printf(s, "Config of %s in state %s: 0x%08X\n", dbg_pinname,
+ dbg_state_name, dbg_config);
+
+ if (pctldev)
+ confops = pctldev->desc->confops;
+
+ if (confops && confops->pin_config_config_dbg_show)
+ confops->pin_config_config_dbg_show(pctldev,
+ s, dbg_config);
+ } else {
+ seq_printf(s, "No pin found for defined name/state\n");
+ }
+
+ return 0;
+}
+
+static int pinconf_dbg_config_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pinconf_dbg_config_print, inode->i_private);
+}
+
+/**
+ * pinconf_dbg_config_write() - overwrite the pinctrl config in thepinctrl
+ * map, of a pin/state pair based on pinname and state that have been
+ * selected with the debugfs entries pinconf-name and pinconf-state
+ */
+static int pinconf_dbg_config_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ int err;
+ unsigned long config;
+ struct pinctrl_maps *maps_node;
+ struct pinctrl_map const *map;
+ int i, j;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &config);
+
+ if (err)
+ return err;
+
+ dbg_config = config;
+
+ mutex_lock(&pinctrl_mutex);
+
+ /* Parse the pinctrl map and look for the selected pin/state */
+ for_each_maps(maps_node, i, map) {
+ if (map->type != PIN_MAP_TYPE_CONFIGS_PIN)
+ continue;
+
+ if (strncmp(map->name, dbg_state_name, MAX_NAME_LEN) > 0)
+ continue;
+
+ /* we found the right pin / state, so overwrite config */
+ for (j = 0; j < map->data.configs.num_configs; j++) {
+ if (strncmp(map->data.configs.group_or_pin, dbg_pinname,
+ MAX_NAME_LEN) == 0)
+ map->data.configs.configs[j] = dbg_config;
+ }
+ }
+
+ mutex_unlock(&pinctrl_mutex);
+
+ return count;
+}
+
+static const struct file_operations pinconf_dbg_pinconfig_fops = {
+ .open = pinconf_dbg_config_open,
+ .write = pinconf_dbg_config_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
void pinconf_init_device_debugfs(struct dentry *devroot,
struct pinctrl_dev *pctldev)
{
@@ -551,6 +782,12 @@
devroot, pctldev, &pinconf_pins_ops);
debugfs_create_file("pinconf-groups", S_IFREG | S_IRUGO,
devroot, pctldev, &pinconf_groups_ops);
+ debugfs_create_file("pinconf-name", (S_IRUGO | S_IWUSR | S_IWGRP),
+ devroot, pctldev, &pinconf_dbg_pinname_fops);
+ debugfs_create_file("pinconf-state", (S_IRUGO | S_IWUSR | S_IWGRP),
+ devroot, pctldev, &pinconf_dbg_pinstate_fops);
+ debugfs_create_file("pinconf-config", (S_IRUGO | S_IWUSR | S_IWGRP),
+ devroot, pctldev, &pinconf_dbg_pinconfig_fops);
}
#endif
diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h
index 54510de..bfda73d 100644
--- a/drivers/pinctrl/pinconf.h
+++ b/drivers/pinctrl/pinconf.h
@@ -19,11 +19,6 @@
struct pinctrl_setting *setting);
void pinconf_free_setting(struct pinctrl_setting const *setting);
int pinconf_apply_setting(struct pinctrl_setting const *setting);
-void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map);
-void pinconf_show_setting(struct seq_file *s,
- struct pinctrl_setting const *setting);
-void pinconf_init_device_debugfs(struct dentry *devroot,
- struct pinctrl_dev *pctldev);
/*
* You will only be interested in these if you're using PINCONF
@@ -61,6 +56,18 @@
return 0;
}
+#endif
+
+#if defined(CONFIG_PINCONF) && defined(CONFIG_DEBUG_FS)
+
+void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map);
+void pinconf_show_setting(struct seq_file *s,
+ struct pinctrl_setting const *setting);
+void pinconf_init_device_debugfs(struct dentry *devroot,
+ struct pinctrl_dev *pctldev);
+
+#else
+
static inline void pinconf_show_map(struct seq_file *s,
struct pinctrl_map const *map)
{
@@ -83,7 +90,7 @@
* pin config.
*/
-#ifdef CONFIG_GENERIC_PINCONF
+#if defined(CONFIG_GENERIC_PINCONF) && defined(CONFIG_DEBUG_FS)
void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev,
struct seq_file *s, unsigned pin);
diff --git a/drivers/pinctrl/pinctrl-pxa3xx.c b/drivers/pinctrl/pinctrl-pxa3xx.c
index 079dce0..7644e42 100644
--- a/drivers/pinctrl/pinctrl-pxa3xx.c
+++ b/drivers/pinctrl/pinctrl-pxa3xx.c
@@ -25,20 +25,18 @@
.pin_base = 0,
};
-static int pxa3xx_list_groups(struct pinctrl_dev *pctrldev, unsigned selector)
+static int pxa3xx_get_groups_count(struct pinctrl_dev *pctrldev)
{
struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
- if (selector >= info->num_grps)
- return -EINVAL;
- return 0;
+
+ return info->num_grps;
}
static const char *pxa3xx_get_group_name(struct pinctrl_dev *pctrldev,
unsigned selector)
{
struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
- if (selector >= info->num_grps)
- return NULL;
+
return info->grps[selector].name;
}
@@ -48,25 +46,23 @@
unsigned *num_pins)
{
struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
- if (selector >= info->num_grps)
- return -EINVAL;
+
*pins = info->grps[selector].pins;
*num_pins = info->grps[selector].npins;
return 0;
}
static struct pinctrl_ops pxa3xx_pctrl_ops = {
- .list_groups = pxa3xx_list_groups,
+ .get_groups_count = pxa3xx_get_groups_count,
.get_group_name = pxa3xx_get_group_name,
.get_group_pins = pxa3xx_get_group_pins,
};
-static int pxa3xx_pmx_list_func(struct pinctrl_dev *pctrldev, unsigned func)
+static int pxa3xx_pmx_get_funcs_count(struct pinctrl_dev *pctrldev)
{
struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
- if (func >= info->num_funcs)
- return -EINVAL;
- return 0;
+
+ return info->num_funcs;
}
static const char *pxa3xx_pmx_get_func_name(struct pinctrl_dev *pctrldev,
@@ -170,7 +166,7 @@
}
static struct pinmux_ops pxa3xx_pmx_ops = {
- .list_functions = pxa3xx_pmx_list_func,
+ .get_functions_count = pxa3xx_pmx_get_funcs_count,
.get_function_name = pxa3xx_pmx_get_func_name,
.get_function_groups = pxa3xx_pmx_get_groups,
.enable = pxa3xx_pmx_enable,
diff --git a/drivers/pinctrl/pinctrl-sirf.c b/drivers/pinctrl/pinctrl-sirf.c
index 6b3534c..ba15b1a 100644
--- a/drivers/pinctrl/pinctrl-sirf.c
+++ b/drivers/pinctrl/pinctrl-sirf.c
@@ -853,18 +853,14 @@
SIRFSOC_PIN_GROUP("gpsgrp", gps_pins),
};
-static int sirfsoc_list_groups(struct pinctrl_dev *pctldev, unsigned selector)
+static int sirfsoc_get_groups_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(sirfsoc_pin_groups))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(sirfsoc_pin_groups);
}
static const char *sirfsoc_get_group_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
- if (selector >= ARRAY_SIZE(sirfsoc_pin_groups))
- return NULL;
return sirfsoc_pin_groups[selector].name;
}
@@ -872,8 +868,6 @@
const unsigned **pins,
unsigned *num_pins)
{
- if (selector >= ARRAY_SIZE(sirfsoc_pin_groups))
- return -EINVAL;
*pins = sirfsoc_pin_groups[selector].pins;
*num_pins = sirfsoc_pin_groups[selector].num_pins;
return 0;
@@ -886,7 +880,7 @@
}
static struct pinctrl_ops sirfsoc_pctrl_ops = {
- .list_groups = sirfsoc_list_groups,
+ .get_groups_count = sirfsoc_get_groups_count,
.get_group_name = sirfsoc_get_group_name,
.get_group_pins = sirfsoc_get_group_pins,
.pin_dbg_show = sirfsoc_pin_dbg_show,
@@ -1033,11 +1027,9 @@
sirfsoc_pinmux_endisable(spmx, selector, false);
}
-static int sirfsoc_pinmux_list_funcs(struct pinctrl_dev *pmxdev, unsigned selector)
+static int sirfsoc_pinmux_get_funcs_count(struct pinctrl_dev *pmxdev)
{
- if (selector >= ARRAY_SIZE(sirfsoc_pmx_functions))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(sirfsoc_pmx_functions);
}
static const char *sirfsoc_pinmux_get_func_name(struct pinctrl_dev *pctldev,
@@ -1074,9 +1066,9 @@
}
static struct pinmux_ops sirfsoc_pinmux_ops = {
- .list_functions = sirfsoc_pinmux_list_funcs,
.enable = sirfsoc_pinmux_enable,
.disable = sirfsoc_pinmux_disable,
+ .get_functions_count = sirfsoc_pinmux_get_funcs_count,
.get_function_name = sirfsoc_pinmux_get_func_name,
.get_function_groups = sirfsoc_pinmux_get_groups,
.gpio_request_enable = sirfsoc_pinmux_request_gpio,
diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c
index 9b32968..c4c47c5 100644
--- a/drivers/pinctrl/pinctrl-tegra.c
+++ b/drivers/pinctrl/pinctrl-tegra.c
@@ -53,15 +53,11 @@
writel(val, pmx->regs[bank] + reg);
}
-static int tegra_pinctrl_list_groups(struct pinctrl_dev *pctldev,
- unsigned group)
+static int tegra_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (group >= pmx->soc->ngroups)
- return -EINVAL;
-
- return 0;
+ return pmx->soc->ngroups;
}
static const char *tegra_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
@@ -69,9 +65,6 @@
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (group >= pmx->soc->ngroups)
- return NULL;
-
return pmx->soc->groups[group].name;
}
@@ -82,9 +75,6 @@
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (group >= pmx->soc->ngroups)
- return -EINVAL;
-
*pins = pmx->soc->groups[group].pins;
*num_pins = pmx->soc->groups[group].npins;
@@ -99,21 +89,17 @@
}
static struct pinctrl_ops tegra_pinctrl_ops = {
- .list_groups = tegra_pinctrl_list_groups,
+ .get_groups_count = tegra_pinctrl_get_groups_count,
.get_group_name = tegra_pinctrl_get_group_name,
.get_group_pins = tegra_pinctrl_get_group_pins,
.pin_dbg_show = tegra_pinctrl_pin_dbg_show,
};
-static int tegra_pinctrl_list_funcs(struct pinctrl_dev *pctldev,
- unsigned function)
+static int tegra_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev)
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (function >= pmx->soc->nfunctions)
- return -EINVAL;
-
- return 0;
+ return pmx->soc->nfunctions;
}
static const char *tegra_pinctrl_get_func_name(struct pinctrl_dev *pctldev,
@@ -121,9 +107,6 @@
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (function >= pmx->soc->nfunctions)
- return NULL;
-
return pmx->soc->functions[function].name;
}
@@ -134,9 +117,6 @@
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (function >= pmx->soc->nfunctions)
- return -EINVAL;
-
*groups = pmx->soc->functions[function].groups;
*num_groups = pmx->soc->functions[function].ngroups;
@@ -151,8 +131,6 @@
int i;
u32 val;
- if (group >= pmx->soc->ngroups)
- return -EINVAL;
g = &pmx->soc->groups[group];
if (g->mux_reg < 0)
@@ -180,8 +158,6 @@
const struct tegra_pingroup *g;
u32 val;
- if (group >= pmx->soc->ngroups)
- return;
g = &pmx->soc->groups[group];
if (g->mux_reg < 0)
@@ -194,7 +170,7 @@
}
static struct pinmux_ops tegra_pinmux_ops = {
- .list_functions = tegra_pinctrl_list_funcs,
+ .get_functions_count = tegra_pinctrl_get_funcs_count,
.get_function_name = tegra_pinctrl_get_func_name,
.get_function_groups = tegra_pinctrl_get_func_groups,
.enable = tegra_pinctrl_enable,
@@ -324,8 +300,6 @@
s16 reg;
u32 val, mask;
- if (group >= pmx->soc->ngroups)
- return -EINVAL;
g = &pmx->soc->groups[group];
ret = tegra_pinconf_reg(pmx, g, param, &bank, ®, &bit, &width);
@@ -353,8 +327,6 @@
s16 reg;
u32 val, mask;
- if (group >= pmx->soc->ngroups)
- return -EINVAL;
g = &pmx->soc->groups[group];
ret = tegra_pinconf_reg(pmx, g, param, &bank, ®, &bit, &width);
@@ -525,7 +497,6 @@
{
struct tegra_pmx *pmx = platform_get_drvdata(pdev);
- pinctrl_remove_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range);
pinctrl_unregister(pmx->pctl);
return 0;
diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c
index 26eb8cc..10de43c 100644
--- a/drivers/pinctrl/pinctrl-u300.c
+++ b/drivers/pinctrl/pinctrl-u300.c
@@ -836,18 +836,14 @@
},
};
-static int u300_list_groups(struct pinctrl_dev *pctldev, unsigned selector)
+static int u300_get_groups_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(u300_pin_groups))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(u300_pin_groups);
}
static const char *u300_get_group_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
- if (selector >= ARRAY_SIZE(u300_pin_groups))
- return NULL;
return u300_pin_groups[selector].name;
}
@@ -855,8 +851,6 @@
const unsigned **pins,
unsigned *num_pins)
{
- if (selector >= ARRAY_SIZE(u300_pin_groups))
- return -EINVAL;
*pins = u300_pin_groups[selector].pins;
*num_pins = u300_pin_groups[selector].num_pins;
return 0;
@@ -869,7 +863,7 @@
}
static struct pinctrl_ops u300_pctrl_ops = {
- .list_groups = u300_list_groups,
+ .get_groups_count = u300_get_groups_count,
.get_group_name = u300_get_group_name,
.get_group_pins = u300_get_group_pins,
.pin_dbg_show = u300_pin_dbg_show,
@@ -991,11 +985,9 @@
u300_pmx_endisable(upmx, selector, false);
}
-static int u300_pmx_list_funcs(struct pinctrl_dev *pctldev, unsigned selector)
+static int u300_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(u300_pmx_functions))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(u300_pmx_functions);
}
static const char *u300_pmx_get_func_name(struct pinctrl_dev *pctldev,
@@ -1014,7 +1006,7 @@
}
static struct pinmux_ops u300_pmx_ops = {
- .list_functions = u300_pmx_list_funcs,
+ .get_functions_count = u300_pmx_get_funcs_count,
.get_function_name = u300_pmx_get_func_name,
.get_function_groups = u300_pmx_get_groups,
.enable = u300_pmx_enable,
@@ -1185,8 +1177,6 @@
struct u300_pmx *upmx = platform_get_drvdata(pdev);
int i;
- for (i = 0; i < ARRAY_SIZE(u300_gpio_ranges); i++)
- pinctrl_remove_gpio_range(upmx->pctl, &u300_gpio_ranges[i]);
pinctrl_unregister(upmx->pctl);
iounmap(upmx->virtbase);
release_mem_region(upmx->phybase, upmx->physize);
diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c
index 4e62783..bd83c8b 100644
--- a/drivers/pinctrl/pinmux.c
+++ b/drivers/pinctrl/pinmux.c
@@ -33,22 +33,25 @@
int pinmux_check_ops(struct pinctrl_dev *pctldev)
{
const struct pinmux_ops *ops = pctldev->desc->pmxops;
+ unsigned nfuncs;
unsigned selector = 0;
/* Check that we implement required operations */
- if (!ops->list_functions ||
+ if (!ops ||
+ !ops->get_functions_count ||
!ops->get_function_name ||
!ops->get_function_groups ||
- !ops->enable ||
- !ops->disable)
+ !ops->enable) {
+ dev_err(pctldev->dev, "pinmux ops lacks necessary functions\n");
return -EINVAL;
-
+ }
/* Check that all functions registered have names */
- while (ops->list_functions(pctldev, selector) >= 0) {
+ nfuncs = ops->get_functions_count(pctldev);
+ while (selector < nfuncs) {
const char *fname = ops->get_function_name(pctldev,
selector);
if (!fname) {
- pr_err("pinmux ops has no name for function%u\n",
+ dev_err(pctldev->dev, "pinmux ops has no name for function%u\n",
selector);
return -EINVAL;
}
@@ -85,20 +88,23 @@
const struct pinmux_ops *ops = pctldev->desc->pmxops;
int status = -EINVAL;
- dev_dbg(pctldev->dev, "request pin %d for %s\n", pin, owner);
-
desc = pin_desc_get(pctldev, pin);
if (desc == NULL) {
dev_err(pctldev->dev,
- "pin is not registered so it cannot be requested\n");
+ "pin %d is not registered so it cannot be requested\n",
+ pin);
goto out;
}
+ dev_dbg(pctldev->dev, "request pin %d (%s) for %s\n",
+ pin, desc->name, owner);
+
if (gpio_range) {
/* There's no need to support multiple GPIO requests */
if (desc->gpio_owner) {
dev_err(pctldev->dev,
- "pin already requested\n");
+ "pin %s already requested by %s; cannot claim for %s\n",
+ desc->name, desc->gpio_owner, owner);
goto out;
}
@@ -106,7 +112,8 @@
} else {
if (desc->mux_usecount && strcmp(desc->mux_owner, owner)) {
dev_err(pctldev->dev,
- "pin already requested\n");
+ "pin %s already requested by %s; cannot claim for %s\n",
+ desc->name, desc->mux_owner, owner);
goto out;
}
@@ -139,8 +146,7 @@
status = 0;
if (status) {
- dev_err(pctldev->dev, "->request on device %s failed for pin %d\n",
- pctldev->desc->name, pin);
+ dev_err(pctldev->dev, "request() failed for pin %d\n", pin);
module_put(pctldev->owner);
}
@@ -157,7 +163,7 @@
out:
if (status)
dev_err(pctldev->dev, "pin-%d (%s) status %d\n",
- pin, owner, status);
+ pin, owner, status);
return status;
}
@@ -188,6 +194,11 @@
}
if (!gpio_range) {
+ /*
+ * A pin should not be freed more times than allocated.
+ */
+ if (WARN_ON(!desc->mux_usecount))
+ return NULL;
desc->mux_usecount--;
if (desc->mux_usecount)
return NULL;
@@ -226,14 +237,11 @@
struct pinctrl_gpio_range *range,
unsigned pin, unsigned gpio)
{
- char gpiostr[16];
const char *owner;
int ret;
/* Conjure some name stating what chip and pin this is taken by */
- snprintf(gpiostr, 15, "%s:%d", range->name, gpio);
-
- owner = kstrdup(gpiostr, GFP_KERNEL);
+ owner = kasprintf(GFP_KERNEL, "%s:%d", range->name, gpio);
if (!owner)
return -EINVAL;
@@ -287,10 +295,11 @@
const char *function)
{
const struct pinmux_ops *ops = pctldev->desc->pmxops;
+ unsigned nfuncs = ops->get_functions_count(pctldev);
unsigned selector = 0;
/* See if this pctldev has this function */
- while (ops->list_functions(pctldev, selector) >= 0) {
+ while (selector < nfuncs) {
const char *fname = ops->get_function_name(pctldev,
selector);
@@ -310,27 +319,38 @@
{
struct pinctrl_dev *pctldev = setting->pctldev;
const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
- const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
char const * const *groups;
unsigned num_groups;
int ret;
const char *group;
int i;
- const unsigned *pins;
- unsigned num_pins;
- setting->data.mux.func =
- pinmux_func_name_to_selector(pctldev, map->data.mux.function);
- if (setting->data.mux.func < 0)
- return setting->data.mux.func;
+ if (!pmxops) {
+ dev_err(pctldev->dev, "does not support mux function\n");
+ return -EINVAL;
+ }
+
+ ret = pinmux_func_name_to_selector(pctldev, map->data.mux.function);
+ if (ret < 0) {
+ dev_err(pctldev->dev, "invalid function %s in map table\n",
+ map->data.mux.function);
+ return ret;
+ }
+ setting->data.mux.func = ret;
ret = pmxops->get_function_groups(pctldev, setting->data.mux.func,
&groups, &num_groups);
- if (ret < 0)
+ if (ret < 0) {
+ dev_err(pctldev->dev, "can't query groups for function %s\n",
+ map->data.mux.function);
return ret;
- if (!num_groups)
+ }
+ if (!num_groups) {
+ dev_err(pctldev->dev,
+ "function %s can't be selected on any group\n",
+ map->data.mux.function);
return -EINVAL;
-
+ }
if (map->data.mux.group) {
bool found = false;
group = map->data.mux.group;
@@ -340,63 +360,30 @@
break;
}
}
- if (!found)
+ if (!found) {
+ dev_err(pctldev->dev,
+ "invalid group \"%s\" for function \"%s\"\n",
+ group, map->data.mux.function);
return -EINVAL;
+ }
} else {
group = groups[0];
}
- setting->data.mux.group = pinctrl_get_group_selector(pctldev, group);
- if (setting->data.mux.group < 0)
- return setting->data.mux.group;
-
- ret = pctlops->get_group_pins(pctldev, setting->data.mux.group, &pins,
- &num_pins);
- if (ret) {
- dev_err(pctldev->dev,
- "could not get pins for device %s group selector %d\n",
- pinctrl_dev_get_name(pctldev), setting->data.mux.group);
- return -ENODEV;
+ ret = pinctrl_get_group_selector(pctldev, group);
+ if (ret < 0) {
+ dev_err(pctldev->dev, "invalid group %s in map table\n",
+ map->data.mux.group);
+ return ret;
}
-
- /* Try to allocate all pins in this group, one by one */
- for (i = 0; i < num_pins; i++) {
- ret = pin_request(pctldev, pins[i], map->dev_name, NULL);
- if (ret) {
- dev_err(pctldev->dev,
- "could not get request pin %d on device %s\n",
- pins[i], pinctrl_dev_get_name(pctldev));
- /* On error release all taken pins */
- i--; /* this pin just failed */
- for (; i >= 0; i--)
- pin_free(pctldev, pins[i], NULL);
- return -ENODEV;
- }
- }
+ setting->data.mux.group = ret;
return 0;
}
void pinmux_free_setting(struct pinctrl_setting const *setting)
{
- struct pinctrl_dev *pctldev = setting->pctldev;
- const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
- const unsigned *pins;
- unsigned num_pins;
- int ret;
- int i;
-
- ret = pctlops->get_group_pins(pctldev, setting->data.mux.group,
- &pins, &num_pins);
- if (ret) {
- dev_err(pctldev->dev,
- "could not get pins for device %s group selector %d\n",
- pinctrl_dev_get_name(pctldev), setting->data.mux.group);
- return;
- }
-
- for (i = 0; i < num_pins; i++)
- pin_free(pctldev, pins[i], NULL);
+ /* This function is currently unused */
}
int pinmux_enable_setting(struct pinctrl_setting const *setting)
@@ -420,6 +407,18 @@
num_pins = 0;
}
+ /* Try to allocate all pins in this group, one by one */
+ for (i = 0; i < num_pins; i++) {
+ ret = pin_request(pctldev, pins[i], setting->dev_name, NULL);
+ if (ret) {
+ dev_err(pctldev->dev,
+ "could not request pin %d on device %s\n",
+ pins[i], pinctrl_dev_get_name(pctldev));
+ goto err_pin_request;
+ }
+ }
+
+ /* Now that we have acquired the pins, encode the mux setting */
for (i = 0; i < num_pins; i++) {
desc = pin_desc_get(pctldev, pins[i]);
if (desc == NULL) {
@@ -431,8 +430,26 @@
desc->mux_setting = &(setting->data.mux);
}
- return ops->enable(pctldev, setting->data.mux.func,
- setting->data.mux.group);
+ ret = ops->enable(pctldev, setting->data.mux.func,
+ setting->data.mux.group);
+
+ if (ret)
+ goto err_enable;
+
+ return 0;
+
+err_enable:
+ for (i = 0; i < num_pins; i++) {
+ desc = pin_desc_get(pctldev, pins[i]);
+ if (desc)
+ desc->mux_setting = NULL;
+ }
+err_pin_request:
+ /* On error release all taken pins */
+ while (--i >= 0)
+ pin_free(pctldev, pins[i], NULL);
+
+ return ret;
}
void pinmux_disable_setting(struct pinctrl_setting const *setting)
@@ -456,6 +473,7 @@
num_pins = 0;
}
+ /* Flag the descs that no setting is active */
for (i = 0; i < num_pins; i++) {
desc = pin_desc_get(pctldev, pins[i]);
if (desc == NULL) {
@@ -467,7 +485,12 @@
desc->mux_setting = NULL;
}
- ops->disable(pctldev, setting->data.mux.func, setting->data.mux.group);
+ /* And release the pins */
+ for (i = 0; i < num_pins; i++)
+ pin_free(pctldev, pins[i], NULL);
+
+ if (ops->disable)
+ ops->disable(pctldev, setting->data.mux.func, setting->data.mux.group);
}
#ifdef CONFIG_DEBUG_FS
@@ -477,11 +500,15 @@
{
struct pinctrl_dev *pctldev = s->private;
const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
+ unsigned nfuncs;
unsigned func_selector = 0;
- mutex_lock(&pinctrl_mutex);
+ if (!pmxops)
+ return 0;
- while (pmxops->list_functions(pctldev, func_selector) >= 0) {
+ mutex_lock(&pinctrl_mutex);
+ nfuncs = pmxops->get_functions_count(pctldev);
+ while (func_selector < nfuncs) {
const char *func = pmxops->get_function_name(pctldev,
func_selector);
const char * const *groups;
@@ -515,6 +542,9 @@
const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
unsigned i, pin;
+ if (!pmxops)
+ return 0;
+
seq_puts(s, "Pinmux settings per pin\n");
seq_puts(s, "Format: pin (name): mux_owner gpio_owner hog?\n");
diff --git a/drivers/pinctrl/pinmux.h b/drivers/pinctrl/pinmux.h
index 6fc4700..d1a98b1 100644
--- a/drivers/pinctrl/pinmux.h
+++ b/drivers/pinctrl/pinmux.h
@@ -31,12 +31,6 @@
int pinmux_enable_setting(struct pinctrl_setting const *setting);
void pinmux_disable_setting(struct pinctrl_setting const *setting);
-void pinmux_show_map(struct seq_file *s, struct pinctrl_map const *map);
-void pinmux_show_setting(struct seq_file *s,
- struct pinctrl_setting const *setting);
-void pinmux_init_device_debugfs(struct dentry *devroot,
- struct pinctrl_dev *pctldev);
-
#else
static inline int pinmux_check_ops(struct pinctrl_dev *pctldev)
@@ -89,6 +83,18 @@
{
}
+#endif
+
+#if defined(CONFIG_PINMUX) && defined(CONFIG_DEBUG_FS)
+
+void pinmux_show_map(struct seq_file *s, struct pinctrl_map const *map);
+void pinmux_show_setting(struct seq_file *s,
+ struct pinctrl_setting const *setting);
+void pinmux_init_device_debugfs(struct dentry *devroot,
+ struct pinctrl_dev *pctldev);
+
+#else
+
static inline void pinmux_show_map(struct seq_file *s,
struct pinctrl_map const *map)
{
diff --git a/drivers/platform/msm/ipa/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_debugfs.c
index aaf5cc0..87ddf59 100644
--- a/drivers/platform/msm/ipa/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_debugfs.c
@@ -346,11 +346,18 @@
uint32_t mask[4];
int i;
- if (attrib->attrib_mask & IPA_FLT_TOS) {
- nbytes = scnprintf(buff + cnt, sz - cnt, "tos:%d ",
- attrib->u.v4.tos);
+
+ if (attrib->attrib_mask & IPA_FLT_TOS_MASKED) {
+ nbytes = scnprintf(buff + cnt, sz - cnt, "tos_value:%d ",
+ attrib->tos_value);
cnt += nbytes;
}
+ if (attrib->attrib_mask & IPA_FLT_TOS_MASKED) {
+ nbytes = scnprintf(buff + cnt, sz - cnt, "tos_mask:%d ",
+ attrib->tos_mask);
+ cnt += nbytes;
+ }
+
if (attrib->attrib_mask & IPA_FLT_PROTOCOL) {
nbytes = scnprintf(buff + cnt, sz - cnt, "protocol:%d ",
attrib->u.v4.protocol);
diff --git a/drivers/platform/msm/ipa/ipa_rm_resource.c b/drivers/platform/msm/ipa/ipa_rm_resource.c
index 7142dc7..dba4430 100644
--- a/drivers/platform/msm/ipa/ipa_rm_resource.c
+++ b/drivers/platform/msm/ipa/ipa_rm_resource.c
@@ -720,6 +720,7 @@
producer->resource.name,
IPA_RM_RESOURCE_GRANTED,
true);
+ result = 0;
}
unlock_and_bail:
spin_unlock_irqrestore(&producer->resource.state_lock, flags);
diff --git a/drivers/platform/msm/ipa/ipa_rt.c b/drivers/platform/msm/ipa/ipa_rt.c
index 6430c07..5c27c40 100644
--- a/drivers/platform/msm/ipa/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_rt.c
@@ -67,13 +67,31 @@
}
rule_hdr->u.hdr.pipe_dest_idx = pipe_idx;
rule_hdr->u.hdr.system = !ipa_ctx->hdr_tbl_lcl;
- if (entry->hdr)
+ if (entry->hdr) {
rule_hdr->u.hdr.hdr_offset =
entry->hdr->offset_entry->offset >> 2;
- else
+ } else {
rule_hdr->u.hdr.hdr_offset = 0;
-
+ }
buf += sizeof(struct ipa_rt_rule_hw_hdr);
+ if ((ip == IPA_IP_v4) &&
+ (entry->rule.attrib.attrib_mask & IPA_FLT_TOS)) {
+ entry->rule.attrib.tos_value =
+ (entry->rule.attrib.u.v4.tos << 5);
+ entry->rule.attrib.tos_mask = 0xe0;
+ entry->rule.attrib.attrib_mask &= ~IPA_FLT_TOS;
+ entry->rule.attrib.attrib_mask |= IPA_FLT_TOS_MASKED;
+ }
+
+ if ((ip == IPA_IP_v6) &&
+ (entry->rule.attrib.attrib_mask & IPA_FLT_TC)) {
+ entry->rule.attrib.tos_value =
+ (entry->rule.attrib.u.v6.tc << 5);
+ entry->rule.attrib.tos_mask = 0xe0;
+ entry->rule.attrib.attrib_mask &= ~IPA_FLT_TC;
+ entry->rule.attrib.attrib_mask |= IPA_FLT_TOS_MASKED;
+ }
+
if (ipa_generate_hw_rule(ip, &rule->attrib, &buf, &en_rule)) {
IPAERR("fail to generate hw rule\n");
return -EPERM;
diff --git a/drivers/platform/msm/ipa/ipa_utils.c b/drivers/platform/msm/ipa/ipa_utils.c
index 23de300..21a6dc4 100644
--- a/drivers/platform/msm/ipa/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_utils.c
@@ -237,6 +237,20 @@
*buf = ipa_pad_to_32(*buf);
}
+ if (attrib->attrib_mask & IPA_FLT_TOS_MASKED) {
+ if (ipa_ofst_meq32[ofst_meq32] == -1) {
+ IPAERR("ran out of meq32 eq\n");
+ return -EPERM;
+ }
+ *en_rule |= ipa_ofst_meq32[ofst_meq32];
+ /* 0 => offset of TOS in v4 header */
+ *buf = ipa_write_8(0, *buf);
+ *buf = ipa_write_32((attrib->tos_mask << 16), *buf);
+ *buf = ipa_write_32(attrib->tos_value, *buf);
+ *buf = ipa_pad_to_32(*buf);
+ ofst_meq32++;
+ }
+
if (attrib->attrib_mask & IPA_FLT_PROTOCOL) {
*en_rule |= IPA_PROTOCOL_EQ;
*buf = ipa_write_8(attrib->u.v4.protocol, *buf);
@@ -568,6 +582,20 @@
*buf = ipa_pad_to_32(*buf);
}
+ if (attrib->attrib_mask & IPA_FLT_TOS_MASKED) {
+ if (ipa_ofst_meq32[ofst_meq32] == -1) {
+ IPAERR("ran out of meq32 eq\n");
+ return -EPERM;
+ }
+ *en_rule |= ipa_ofst_meq32[ofst_meq32];
+ /* 0 => offset of TOS in v4 header */
+ *buf = ipa_write_8(0, *buf);
+ *buf = ipa_write_32((attrib->tos_mask << 20), *buf);
+ *buf = ipa_write_32(attrib->tos_value, *buf);
+ *buf = ipa_pad_to_32(*buf);
+ ofst_meq32++;
+ }
+
if (attrib->attrib_mask & IPA_FLT_FLOW_LABEL) {
*en_rule |= IPA_FLT_FLOW_LABEL;
/* FIXME FL is only 20 bits */
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index b390280..fae09fc 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -153,7 +153,6 @@
bool prod_stopped;
struct completion prod_avail[MAX_BAMS];
- struct completion cons_avail[MAX_BAMS];
struct completion cons_released[MAX_BAMS];
struct completion prod_released[MAX_BAMS];
@@ -878,7 +877,6 @@
spin_lock(&usb_bam_ipa_handshake_info_lock);
info.cur_cons_state[cur_bam] = IPA_RM_RESOURCE_GRANTED;
- complete_all(&info.cons_avail[cur_bam]);
spin_lock(&usb_bam_lock);
@@ -1024,7 +1022,7 @@
}
}
-static void wait_for_prod_granted(enum usb_bam cur_bam, bool start_cons)
+static void wait_for_prod_granted(enum usb_bam cur_bam)
{
int ret;
@@ -1038,8 +1036,6 @@
__func__);
init_completion(&info.prod_avail[cur_bam]);
- if (start_cons)
- init_completion(&info.cons_avail[cur_bam]);
ret = ipa_rm_request_resource(ipa_rm_resource_prod[cur_bam]);
if (!ret) {
@@ -1056,22 +1052,14 @@
pr_err("%s: ipa_rm_request_resource ret =%d\n", __func__, ret);
}
-void wait_for_cons_granted(enum usb_bam cur_bam)
+void notify_usb_connected(enum usb_bam cur_bam)
{
- pr_debug("%s: Waiting for CONS\n", __func__);
- if (info.cur_cons_state[cur_bam] != IPA_RM_RESOURCE_GRANTED) {
- if (!wait_for_completion_timeout(&info.cons_avail[cur_bam],
- USB_BAM_TIMEOUT))
- pr_err("%s: Timeout wainting for CONS_REQUEST\n",
- __func__);
- pr_err("%s: Finished waiting for CONS\n", __func__);
- }
+ pr_debug("%s: enter\n", __func__);
spin_lock(&usb_bam_ipa_handshake_info_lock);
if (cur_bam == HSUSB_BAM)
info.connect_complete = 1;
spin_unlock(&usb_bam_ipa_handshake_info_lock);
- pr_debug("%s: CONS is granted\n", __func__);
if (info.cur_cons_state[HSUSB_BAM] == IPA_RM_RESOURCE_GRANTED) {
pr_debug("%s: Notify CONS_GRANTED\n", __func__);
@@ -1080,20 +1068,6 @@
}
}
-void usb_bam_wait_for_cons_granted(
- struct usb_bam_connect_ipa_params *ipa_params)
-{
- struct usb_bam_pipe_connect *pipe_connect;
- enum usb_bam cur_bam;
- u8 src_idx;
-
- src_idx = ipa_params->src_idx;
- pipe_connect = &usb_bam_connections[src_idx];
- cur_bam = pipe_connect->bam_type;
-
- wait_for_cons_granted(cur_bam);
-}
-
static void wait_for_prod_release(enum usb_bam cur_bam)
{
int ret;
@@ -1270,8 +1244,8 @@
info.lpm_wait_handshake[HSUSB_BAM] = true;
spin_unlock(&usb_bam_ipa_handshake_info_lock);
- wait_for_prod_granted(HSUSB_BAM, true);
- wait_for_cons_granted(HSUSB_BAM);
+ wait_for_prod_granted(HSUSB_BAM);
+ notify_usb_connected(HSUSB_BAM);
if (info.cons_stopped) {
ipa_resume_pipes();
if (info.start) {
@@ -1339,7 +1313,7 @@
usb_bam_resume_hsic_host();
/* Ensure getting the producer resource */
- wait_for_prod_granted(HSIC_BAM, false);
+ wait_for_prod_granted(HSIC_BAM);
}
void msm_bam_hsic_notify_on_resume(void)
@@ -1500,7 +1474,7 @@
if (ipa_params->dir == USB_TO_PEER_PERIPHERAL) {
pr_debug("%s: Starting connect sequence\n", __func__);
- wait_for_prod_granted(cur_bam, true);
+ wait_for_prod_granted(cur_bam);
}
ret = connect_pipe_ipa(idx, ipa_params);
@@ -1524,7 +1498,7 @@
ctx.pipes_enabled_per_bam[cur_bam] += 1;
spin_unlock(&usb_bam_lock);
if (ipa_params->dir == PEER_PERIPHERAL_TO_USB && cur_bam == HSUSB_BAM)
- wait_for_cons_granted(cur_bam);
+ notify_usb_connected(cur_bam);
if (cur_bam == HSUSB_BAM)
mutex_unlock(&info.suspend_resume_mutex);
@@ -1586,7 +1560,7 @@
if (pipe_connect->peer_bam == IPA_P_BAM &&
pipe_connect->bam_type == HSIC_BAM &&
info.cur_prod_state[HSIC_BAM] != IPA_RM_RESOURCE_GRANTED) {
- wait_for_prod_granted(HSIC_BAM, false);
+ wait_for_prod_granted(HSIC_BAM);
}
/*
@@ -1618,7 +1592,7 @@
if (pipe_connect->bam_type == HSUSB_BAM) {
/* A2 wakeup not from LPM (CONS was up) */
- wait_for_prod_granted(pipe_connect->bam_type, true);
+ wait_for_prod_granted(pipe_connect->bam_type);
if (info.start) {
pr_debug("%s: Enqueue PROD transfer", __func__);
info.start(info.start_stop_param,
@@ -2568,8 +2542,6 @@
ctx.is_bam_inactivity[i] = false;
init_completion(&info.prod_avail[i]);
complete(&info.prod_avail[i]);
- init_completion(&info.cons_avail[i]);
- complete(&info.cons_avail[i]);
init_completion(&info.cons_released[i]);
complete(&info.cons_released[i]);
init_completion(&info.prod_released[i]);
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 36a43c3..38b1967 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -671,6 +671,9 @@
#define MAX_XPORT_STR_LEN 50
static char rmnet_transports[MAX_XPORT_STR_LEN];
+/*rmnet transport name string - "rmnet_hsic[,rmnet_hsusb]" */
+static char rmnet_xport_names[MAX_XPORT_STR_LEN];
+
static void rmnet_function_cleanup(struct android_usb_function *f)
{
frmnet_cleanup();
@@ -683,18 +686,28 @@
int err = 0;
char *ctrl_name;
char *data_name;
+ char *tname = NULL;
char buf[MAX_XPORT_STR_LEN], *b;
+ char xport_name_buf[MAX_XPORT_STR_LEN], *tb;
static int rmnet_initialized, ports;
if (!rmnet_initialized) {
rmnet_initialized = 1;
strlcpy(buf, rmnet_transports, sizeof(buf));
b = strim(buf);
+
+ strlcpy(xport_name_buf, rmnet_xport_names,
+ sizeof(xport_name_buf));
+ tb = strim(xport_name_buf);
+
while (b) {
ctrl_name = strsep(&b, ",");
data_name = strsep(&b, ",");
if (ctrl_name && data_name) {
- err = frmnet_init_port(ctrl_name, data_name);
+ if (tb)
+ tname = strsep(&tb, ",");
+ err = frmnet_init_port(ctrl_name, data_name,
+ tname);
if (err) {
pr_err("rmnet: Cannot open ctrl port:"
"'%s' data port:'%s'\n",
@@ -738,12 +751,34 @@
return size;
}
+static ssize_t rmnet_xport_names_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", rmnet_xport_names);
+}
+
+static ssize_t rmnet_xport_names_store(
+ struct device *device, struct device_attribute *attr,
+ const char *buff, size_t size)
+{
+ strlcpy(rmnet_xport_names, buff, sizeof(rmnet_xport_names));
+
+ return size;
+}
+
static struct device_attribute dev_attr_rmnet_transports =
__ATTR(transports, S_IRUGO | S_IWUSR,
rmnet_transports_show,
rmnet_transports_store);
+
+static struct device_attribute dev_attr_rmnet_xport_names =
+ __ATTR(transport_names, S_IRUGO | S_IWUSR,
+ rmnet_xport_names_show,
+ rmnet_xport_names_store);
+
static struct device_attribute *rmnet_function_attributes[] = {
&dev_attr_rmnet_transports,
+ &dev_attr_rmnet_xport_names,
NULL };
static struct android_usb_function rmnet_function = {
@@ -1199,9 +1234,33 @@
return size;
}
+/*enabled FSERIAL transport names - "serial_hsic[,serial_hsusb]"*/
+static char serial_xport_names[32];
+static ssize_t serial_xport_names_store(
+ struct device *device, struct device_attribute *attr,
+ const char *buff, size_t size)
+{
+ strlcpy(serial_xport_names, buff, sizeof(serial_xport_names));
+
+ return size;
+}
+
+static ssize_t serial_xport_names_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", serial_xport_names);
+}
+
static DEVICE_ATTR(transports, S_IWUSR, NULL, serial_transports_store);
-static struct device_attribute *serial_function_attributes[] =
- { &dev_attr_transports, NULL };
+static struct device_attribute dev_attr_serial_xport_names =
+ __ATTR(transport_names, S_IRUGO | S_IWUSR,
+ serial_xport_names_show,
+ serial_xport_names_store);
+
+static struct device_attribute *serial_function_attributes[] = {
+ &dev_attr_transports,
+ &dev_attr_serial_xport_names,
+ NULL };
static void serial_function_cleanup(struct android_usb_function *f)
{
@@ -1211,8 +1270,8 @@
static int serial_function_bind_config(struct android_usb_function *f,
struct usb_configuration *c)
{
- char *name;
- char buf[32], *b;
+ char *name, *xport_name = NULL;
+ char buf[32], *b, xport_name_buf[32], *tb;
int err = -1, i;
static int serial_initialized = 0, ports = 0;
@@ -1223,11 +1282,16 @@
strlcpy(buf, serial_transports, sizeof(buf));
b = strim(buf);
+ strlcpy(xport_name_buf, serial_xport_names, sizeof(xport_name_buf));
+ tb = strim(xport_name_buf);
+
while (b) {
name = strsep(&b, ",");
if (name) {
- err = gserial_init_port(ports, name);
+ if (tb)
+ xport_name = strsep(&tb, ",");
+ err = gserial_init_port(ports, name, xport_name);
if (err) {
pr_err("serial: Cannot open port '%s'", name);
goto out;
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 6dcc3b4..6bfa203 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -1230,7 +1230,8 @@
no_data_hsuart_ports = 0;
}
-static int frmnet_init_port(const char *ctrl_name, const char *data_name)
+static int frmnet_init_port(const char *ctrl_name, const char *data_name,
+ const char *port_name)
{
struct f_rmnet *dev;
struct rmnet_ports *rmnet_port;
@@ -1272,6 +1273,7 @@
no_ctrl_qti_ports++;
break;
case USB_GADGET_XPORT_HSIC:
+ ghsic_ctrl_set_port_name(port_name, ctrl_name);
rmnet_port->ctrl_xport_num = no_ctrl_hsic_ports;
no_ctrl_hsic_ports++;
break;
@@ -1299,6 +1301,7 @@
no_data_bam2bam_ports++;
break;
case USB_GADGET_XPORT_HSIC:
+ ghsic_data_set_port_name(port_name, data_name);
rmnet_port->data_xport_num = no_data_hsic_ports;
no_data_hsic_ports++;
break;
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
index 74dba07..57cbc03 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/f_serial.c
@@ -985,9 +985,11 @@
/**
* gserial_init_port - bind a gserial_port to its transport
*/
-static int gserial_init_port(int port_num, const char *name)
+static int gserial_init_port(int port_num, const char *name,
+ const char *port_name)
{
enum transport_type transport;
+ int ret = 0;
if (port_num >= GSERIAL_NO_PORTS)
return -ENODEV;
@@ -1013,6 +1015,9 @@
no_smd_ports++;
break;
case USB_GADGET_XPORT_HSIC:
+ ghsic_ctrl_set_port_name(port_name, name);
+ ghsic_data_set_port_name(port_name, name);
+
/*client port number will be updated in gport_setup*/
no_hsic_sports++;
break;
@@ -1028,5 +1033,5 @@
nr_ports++;
- return 0;
+ return ret;
}
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
index 022e641..a775459 100644
--- a/drivers/usb/gadget/u_bam.c
+++ b/drivers/usb/gadget/u_bam.c
@@ -730,11 +730,11 @@
int ret;
if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
- teth_bridge_disconnect();
ret = usb_bam_disconnect_ipa(&d->ipa_params);
if (ret)
pr_err("%s: usb_bam_disconnect_ipa failed: err:%d\n",
__func__, ret);
+ teth_bridge_disconnect();
}
}
diff --git a/drivers/usb/gadget/u_bam_data.c b/drivers/usb/gadget/u_bam_data.c
index d470edf..3322da5 100644
--- a/drivers/usb/gadget/u_bam_data.c
+++ b/drivers/usb/gadget/u_bam_data.c
@@ -177,13 +177,14 @@
int ret;
if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
- if (d->func_type == USB_FUNC_MBIM)
- teth_bridge_disconnect();
if (d->func_type == USB_FUNC_ECM)
ecm_ipa_disconnect(d->ipa_params.priv);
ret = usb_bam_disconnect_ipa(&d->ipa_params);
if (ret)
pr_err("usb_bam_disconnect_ipa failed: err:%d\n", ret);
+ if (d->func_type == USB_FUNC_MBIM)
+ teth_bridge_disconnect();
+
}
}
diff --git a/drivers/usb/gadget/u_ctrl_hsic.c b/drivers/usb/gadget/u_ctrl_hsic.c
index 0f93ad4..6143d1b 100644
--- a/drivers/usb/gadget/u_ctrl_hsic.c
+++ b/drivers/usb/gadget/u_ctrl_hsic.c
@@ -36,12 +36,6 @@
static unsigned int no_ctrl_ports;
-static const char *ctrl_bridge_names[] = {
- "dun_ctrl_hsic0",
- "rmnet_ctrl_hsic0"
-};
-
-#define CTRL_BRIDGE_NAME_MAX_LEN 20
#define READ_BUF_LEN 1024
#define CH_OPENED 0
@@ -83,6 +77,7 @@
static struct {
struct gctrl_port *port;
struct platform_driver pdrv;
+ char port_name[BRIDGE_NAME_MAX_LEN];
} gctrl_ports[NUM_PORTS];
static int ghsic_ctrl_receive(void *dev, void *buf, size_t actual)
@@ -336,19 +331,35 @@
gser->send_modem_ctrl_bits(gser, ctrl_bits);
}
+static int ghsic_ctrl_get_port_id(const char *pdev_name)
+{
+ struct gctrl_port *port;
+ int i;
+
+ for (i = 0; i < no_ctrl_ports; i++) {
+ port = gctrl_ports[i].port;
+ if (!strncmp(port->brdg.name, pdev_name, BRIDGE_NAME_MAX_LEN))
+ return i;
+ }
+
+ return -EINVAL;
+}
+
static int ghsic_ctrl_probe(struct platform_device *pdev)
{
struct gctrl_port *port;
unsigned long flags;
+ int id;
pr_debug("%s: name:%s\n", __func__, pdev->name);
- if (pdev->id >= no_ctrl_ports) {
- pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ id = ghsic_ctrl_get_port_id(pdev->name);
+ if (id < 0 || id >= no_ctrl_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, id);
return -EINVAL;
}
- port = gctrl_ports[pdev->id].port;
+ port = gctrl_ports[id].port;
set_bit(CH_READY, &port->bridge_sts);
/* if usb is online, start read */
@@ -366,15 +377,17 @@
struct gserial *gser = NULL;
struct grmnet *gr = NULL;
unsigned long flags;
+ int id;
pr_debug("%s: name:%s\n", __func__, pdev->name);
- if (pdev->id >= no_ctrl_ports) {
- pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ id = ghsic_ctrl_get_port_id(pdev->name);
+ if (id < 0 || id >= no_ctrl_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, id);
return -EINVAL;
}
- port = gctrl_ports[pdev->id].port;
+ port = gctrl_ports[id].port;
spin_lock_irqsave(&port->port_lock, flags);
if (!port->port_usb) {
@@ -421,15 +434,17 @@
{
struct gctrl_port *port;
struct platform_driver *pdrv;
+ char *name;
port = kzalloc(sizeof(struct gctrl_port), GFP_KERNEL);
if (!port)
return -ENOMEM;
- port->wq = create_singlethread_workqueue(ctrl_bridge_names[portno]);
+ name = gctrl_ports[portno].port_name;
+
+ port->wq = create_singlethread_workqueue(name);
if (!port->wq) {
- pr_err("%s: Unable to create workqueue:%s\n",
- __func__, ctrl_bridge_names[portno]);
+ pr_err("%s: Unable to create workqueue:%s\n", __func__, name);
return -ENOMEM;
}
@@ -441,7 +456,7 @@
INIT_WORK(&port->connect_w, ghsic_ctrl_connect_w);
INIT_WORK(&port->disconnect_w, gctrl_disconnect_w);
- port->brdg.ch_id = portno;
+ port->brdg.name = name;
port->brdg.ctx = port;
port->brdg.ops.send_pkt = ghsic_ctrl_receive;
if (port->gtype == USB_GADGET_SERIAL)
@@ -451,7 +466,7 @@
pdrv = &gctrl_ports[portno].pdrv;
pdrv->probe = ghsic_ctrl_probe;
pdrv->remove = ghsic_ctrl_remove;
- pdrv->driver.name = ctrl_bridge_names[portno];
+ pdrv->driver.name = name;
pdrv->driver.owner = THIS_MODULE;
platform_driver_register(pdrv);
@@ -461,6 +476,31 @@
return 0;
}
+/*portname will be used to find the bridge channel index*/
+void ghsic_ctrl_set_port_name(const char *name, const char *xport_type)
+{
+ static unsigned int port_num;
+
+ if (port_num >= NUM_PORTS) {
+ pr_err("%s: setting xport name for invalid port num %d\n",
+ __func__, port_num);
+ return;
+ }
+
+ /*if no xport name is passed set it to xport type e.g. hsic*/
+ if (!name)
+ strlcpy(gctrl_ports[port_num].port_name, xport_type,
+ BRIDGE_NAME_MAX_LEN);
+ else
+ strlcpy(gctrl_ports[port_num].port_name, name,
+ BRIDGE_NAME_MAX_LEN);
+
+ /*append _ctrl to get ctrl bridge name e.g. serial_hsic_ctrl*/
+ strlcat(gctrl_ports[port_num].port_name, "_ctrl", BRIDGE_NAME_MAX_LEN);
+
+ port_num++;
+}
+
int ghsic_ctrl_setup(unsigned int num_ports, enum gadget_type gtype)
{
int first_port_id = no_ctrl_ports;
diff --git a/drivers/usb/gadget/u_data_hsic.c b/drivers/usb/gadget/u_data_hsic.c
index 3932bbc..92653db 100644
--- a/drivers/usb/gadget/u_data_hsic.c
+++ b/drivers/usb/gadget/u_data_hsic.c
@@ -25,13 +25,6 @@
static unsigned int no_data_ports;
-static const char *data_bridge_names[] = {
- "dun_data_hsic0",
- "rmnet_data_hsic0"
-};
-
-#define DATA_BRIDGE_NAME_MAX_LEN 20
-
#define GHSIC_DATA_RMNET_RX_Q_SIZE 50
#define GHSIC_DATA_RMNET_TX_Q_SIZE 300
#define GHSIC_DATA_SERIAL_RX_Q_SIZE 10
@@ -130,6 +123,7 @@
static struct {
struct gdata_port *port;
struct platform_driver pdrv;
+ char port_name[BRIDGE_NAME_MAX_LEN];
} gdata_ports[NUM_PORTS];
static unsigned int get_timestamp(void);
@@ -150,22 +144,25 @@
static int ghsic_data_alloc_requests(struct usb_ep *ep, struct list_head *head,
int num,
void (*cb)(struct usb_ep *ep, struct usb_request *),
- gfp_t flags)
+ spinlock_t *lock)
{
int i;
struct usb_request *req;
+ unsigned long flags;
pr_debug("%s: ep:%s head:%p num:%d cb:%p", __func__,
ep->name, head, num, cb);
for (i = 0; i < num; i++) {
- req = usb_ep_alloc_request(ep, flags);
+ req = usb_ep_alloc_request(ep, GFP_KERNEL);
if (!req) {
pr_debug("%s: req allocated:%d\n", __func__, i);
return list_empty(head) ? -ENOMEM : 0;
}
req->complete = cb;
+ spin_lock_irqsave(lock, flags);
list_add(&req->list, head);
+ spin_unlock_irqrestore(lock, flags);
}
return 0;
@@ -438,20 +435,23 @@
req = list_first_entry(&port->rx_idle,
struct usb_request, list);
+ list_del(&req->list);
+ spin_unlock_irqrestore(&port->rx_lock, flags);
created = get_timestamp();
- skb = alloc_skb(ghsic_data_rx_req_size, GFP_ATOMIC);
- if (!skb)
+ skb = alloc_skb(ghsic_data_rx_req_size, GFP_KERNEL);
+ if (!skb) {
+ spin_lock_irqsave(&port->rx_lock, flags);
+ list_add(&req->list, &port->rx_idle);
break;
+ }
info = (struct timestamp_info *)skb->cb;
info->created = created;
- list_del(&req->list);
req->buf = skb->data;
req->length = ghsic_data_rx_req_size;
req->context = skb;
info->rx_queued = get_timestamp();
- spin_unlock_irqrestore(&port->rx_lock, flags);
ret = usb_ep_queue(ep, req, GFP_KERNEL);
spin_lock_irqsave(&port->rx_lock, flags);
if (ret) {
@@ -472,7 +472,7 @@
static void ghsic_data_start_io(struct gdata_port *port)
{
unsigned long flags;
- struct usb_ep *ep;
+ struct usb_ep *ep_out, *ep_in;
int ret;
pr_debug("%s: port:%p\n", __func__, port);
@@ -481,37 +481,37 @@
return;
spin_lock_irqsave(&port->rx_lock, flags);
- ep = port->out;
- if (!ep) {
- spin_unlock_irqrestore(&port->rx_lock, flags);
+ ep_out = port->out;
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+ if (!ep_out)
return;
- }
- ret = ghsic_data_alloc_requests(ep, &port->rx_idle,
- port->rx_q_size, ghsic_data_epout_complete, GFP_ATOMIC);
+ ret = ghsic_data_alloc_requests(ep_out, &port->rx_idle,
+ port->rx_q_size, ghsic_data_epout_complete, &port->rx_lock);
if (ret) {
pr_err("%s: rx req allocation failed\n", __func__);
+ return;
+ }
+
+ spin_lock_irqsave(&port->tx_lock, flags);
+ ep_in = port->in;
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+ if (!ep_in) {
+ spin_lock_irqsave(&port->rx_lock, flags);
+ ghsic_data_free_requests(ep_out, &port->rx_idle);
spin_unlock_irqrestore(&port->rx_lock, flags);
return;
}
- spin_unlock_irqrestore(&port->rx_lock, flags);
- spin_lock_irqsave(&port->tx_lock, flags);
- ep = port->in;
- if (!ep) {
- spin_unlock_irqrestore(&port->tx_lock, flags);
- return;
- }
-
- ret = ghsic_data_alloc_requests(ep, &port->tx_idle,
- port->tx_q_size, ghsic_data_epin_complete, GFP_ATOMIC);
+ ret = ghsic_data_alloc_requests(ep_in, &port->tx_idle,
+ port->tx_q_size, ghsic_data_epin_complete, &port->tx_lock);
if (ret) {
pr_err("%s: tx req allocation failed\n", __func__);
- ghsic_data_free_requests(ep, &port->rx_idle);
- spin_unlock_irqrestore(&port->tx_lock, flags);
+ spin_lock_irqsave(&port->rx_lock, flags);
+ ghsic_data_free_requests(ep_out, &port->rx_idle);
+ spin_unlock_irqrestore(&port->rx_lock, flags);
return;
}
- spin_unlock_irqrestore(&port->tx_lock, flags);
/* queue out requests */
ghsic_data_start_rx(port);
@@ -586,19 +586,35 @@
spin_unlock_irqrestore(&port->rx_lock, flags);
}
+static int ghsic_data_get_port_id(const char *pdev_name)
+{
+ struct gdata_port *port;
+ int i;
+
+ for (i = 0; i < no_data_ports; i++) {
+ port = gdata_ports[i].port;
+ if (!strncmp(port->brdg.name, pdev_name, BRIDGE_NAME_MAX_LEN))
+ return i;
+ }
+
+ return -EINVAL;
+}
+
static int ghsic_data_probe(struct platform_device *pdev)
{
struct gdata_port *port;
+ int id;
- pr_debug("%s: name:%s no_data_ports= %d\n",
- __func__, pdev->name, no_data_ports);
+ pr_debug("%s: name:%s no_data_ports= %d\n", __func__, pdev->name,
+ no_data_ports);
- if (pdev->id >= no_data_ports) {
- pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ id = ghsic_data_get_port_id(pdev->name);
+ if (id < 0 || id >= no_data_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, id);
return -EINVAL;
}
- port = gdata_ports[pdev->id].port;
+ port = gdata_ports[id].port;
set_bit(CH_READY, &port->bridge_sts);
/* if usb is online, try opening bridge */
@@ -614,15 +630,17 @@
struct gdata_port *port;
struct usb_ep *ep_in;
struct usb_ep *ep_out;
+ int id;
pr_debug("%s: name:%s\n", __func__, pdev->name);
- if (pdev->id >= no_data_ports) {
- pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ id = ghsic_data_get_port_id(pdev->name);
+ if (id < 0 || id >= no_data_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, id);
return -EINVAL;
}
- port = gdata_ports[pdev->id].port;
+ port = gdata_ports[id].port;
ep_in = port->in;
if (ep_in)
@@ -632,6 +650,9 @@
if (ep_out)
usb_ep_fifo_flush(ep_out);
+ /* cancel pending writes to MDM */
+ cancel_work_sync(&port->write_tomdm_w);
+
ghsic_data_free_buffers(port);
cancel_work_sync(&port->connect_w);
@@ -658,15 +679,17 @@
{
struct gdata_port *port;
struct platform_driver *pdrv;
+ char *name;
port = kzalloc(sizeof(struct gdata_port), GFP_KERNEL);
if (!port)
return -ENOMEM;
- port->wq = create_singlethread_workqueue(data_bridge_names[port_num]);
+ name = gdata_ports[port_num].port_name;
+
+ port->wq = create_singlethread_workqueue(name);
if (!port->wq) {
- pr_err("%s: Unable to create workqueue:%s\n",
- __func__, data_bridge_names[port_num]);
+ pr_err("%s: Unable to create workqueue:%s\n", __func__, name);
kfree(port);
return -ENOMEM;
}
@@ -688,7 +711,7 @@
skb_queue_head_init(&port->rx_skb_q);
port->gtype = gtype;
- port->brdg.ch_id = port_num;
+ port->brdg.name = name;
port->brdg.ctx = port;
port->brdg.ops.send_pkt = ghsic_data_receive;
port->brdg.ops.unthrottle_tx = ghsic_data_unthrottle_tx;
@@ -697,7 +720,7 @@
pdrv = &gdata_ports[port_num].pdrv;
pdrv->probe = ghsic_data_probe;
pdrv->remove = ghsic_data_remove;
- pdrv->driver.name = data_bridge_names[port_num];
+ pdrv->driver.name = name;
pdrv->driver.owner = THIS_MODULE;
platform_driver_register(pdrv);
@@ -846,7 +869,7 @@
}
#if defined(CONFIG_DEBUG_FS)
-#define DEBUG_BUF_SIZE 1024
+#define DEBUG_DATA_BUF_SIZE 4096
static unsigned int record_timestamp;
module_param(record_timestamp, uint, S_IRUGO | S_IWUSR);
@@ -917,7 +940,7 @@
if (!record_timestamp)
return 0;
- buf = kzalloc(sizeof(char) * 4 * DEBUG_BUF_SIZE, GFP_KERNEL);
+ buf = kzalloc(sizeof(char) * DEBUG_DATA_BUF_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -927,7 +950,7 @@
for (dbg_inc(&i); i != dbg_data.idx; dbg_inc(&i)) {
if (!strnlen(dbg_data.buf[i], DBG_DATA_MSG))
continue;
- j += scnprintf(buf + j, (4 * DEBUG_BUF_SIZE) - j,
+ j += scnprintf(buf + j, DEBUG_DATA_BUF_SIZE - j,
"%s\n", dbg_data.buf[i]);
}
@@ -955,7 +978,7 @@
int i;
int temp = 0;
- buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+ buf = kzalloc(sizeof(char) * DEBUG_DATA_BUF_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -966,7 +989,7 @@
pdrv = &gdata_ports[i].pdrv;
spin_lock_irqsave(&port->rx_lock, flags);
- temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ temp += scnprintf(buf + temp, DEBUG_DATA_BUF_SIZE - temp,
"\nName: %s\n"
"#PORT:%d port#: %p\n"
"data_ch_open: %d\n"
@@ -991,7 +1014,7 @@
spin_unlock_irqrestore(&port->rx_lock, flags);
spin_lock_irqsave(&port->tx_lock, flags);
- temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ temp += scnprintf(buf + temp, DEBUG_DATA_BUF_SIZE - temp,
"\n******DL INFO******\n\n"
"dpkts_to_usbhost: %lu\n"
"tx_buf_len: %u\n"
@@ -1094,6 +1117,31 @@
#endif
+/*portname will be used to find the bridge channel index*/
+void ghsic_data_set_port_name(const char *name, const char *xport_type)
+{
+ static unsigned int port_num;
+
+ if (port_num >= NUM_PORTS) {
+ pr_err("%s: setting xport name for invalid port num %d\n",
+ __func__, port_num);
+ return;
+ }
+
+ /*if no xport name is passed set it to xport type e.g. hsic*/
+ if (!name)
+ strlcpy(gdata_ports[port_num].port_name, xport_type,
+ BRIDGE_NAME_MAX_LEN);
+ else
+ strlcpy(gdata_ports[port_num].port_name, name,
+ BRIDGE_NAME_MAX_LEN);
+
+ /*append _data to get data bridge name: e.g. serial_hsic_data*/
+ strlcat(gdata_ports[port_num].port_name, "_data", BRIDGE_NAME_MAX_LEN);
+
+ port_num++;
+}
+
int ghsic_data_setup(unsigned num_ports, enum gadget_type gtype)
{
int first_port_id = no_data_ports;
diff --git a/drivers/usb/misc/diag_bridge.c b/drivers/usb/misc/diag_bridge.c
index 1ee1c8e..22e2a25 100644
--- a/drivers/usb/misc/diag_bridge.c
+++ b/drivers/usb/misc/diag_bridge.c
@@ -45,6 +45,7 @@
struct diag_bridge_ops *ops;
struct platform_device *pdev;
unsigned default_autosusp_delay;
+ int id;
/* debugging counters */
unsigned long bytes_to_host;
@@ -92,7 +93,7 @@
static void diag_bridge_delete(struct kref *kref)
{
struct diag_bridge *dev = container_of(kref, struct diag_bridge, kref);
- int id = dev->pdev->id;
+ int id = dev->id;
usb_put_dev(dev->udev);
__dev[id] = 0;
@@ -470,6 +471,7 @@
return -ENOMEM;
}
__dev[devid] = dev;
+ dev->id = devid;
dev->udev = usb_get_dev(interface_to_usbdev(ifc));
dev->ifc = ifc;
diff --git a/drivers/usb/misc/mdm_ctrl_bridge.c b/drivers/usb/misc/mdm_ctrl_bridge.c
index 2a61501..75fce2f 100644
--- a/drivers/usb/misc/mdm_ctrl_bridge.c
+++ b/drivers/usb/misc/mdm_ctrl_bridge.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -26,11 +26,6 @@
#include <asm/unaligned.h>
#include <mach/usb_bridge.h>
-static const char *ctrl_bridge_names[] = {
- "dun_ctrl_hsic0",
- "rmnet_ctrl_hsic0"
-};
-
/* polling interval for Interrupt ep */
#define HS_INTERVAL 7
#define FS_LS_INTERVAL 3
@@ -40,10 +35,18 @@
#define SUSPENDED BIT(0)
+enum ctrl_bridge_rx_state {
+ RX_IDLE, /* inturb is not queued */
+ RX_WAIT, /* inturb is queued and waiting for data */
+ RX_BUSY, /* inturb is completed. processing RX */
+};
+
struct ctrl_bridge {
struct usb_device *udev;
struct usb_interface *intf;
+ char *name;
+
unsigned int int_pipe;
struct urb *inturb;
void *intbuf;
@@ -66,6 +69,9 @@
/* output control lines (DTR, RTS) */
unsigned int cbits_tomdm;
+ spinlock_t lock;
+ enum ctrl_bridge_rx_state rx_state;
+
/* counters */
unsigned int snd_encap_cmd;
unsigned int get_encap_res;
@@ -76,8 +82,19 @@
static struct ctrl_bridge *__dev[MAX_BRIDGE_DEVICES];
-/* counter used for indexing ctrl bridge devices */
-static int ch_id;
+static int get_ctrl_bridge_chid(char *xport_name)
+{
+ struct ctrl_bridge *dev;
+ int i;
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+ dev = __dev[i];
+ if (!strncmp(dev->name, xport_name, BRIDGE_NAME_MAX_LEN))
+ return i;
+ }
+
+ return -ENODEV;
+}
unsigned int ctrl_bridge_get_cbits_tohost(unsigned int id)
{
@@ -125,12 +142,43 @@
}
EXPORT_SYMBOL(ctrl_bridge_set_cbits);
+static int ctrl_bridge_start_read(struct ctrl_bridge *dev, gfp_t gfp_flags)
+{
+ int retval = 0;
+ unsigned long flags;
+
+ if (!dev->inturb) {
+ dev_err(&dev->intf->dev, "%s: inturb is NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ retval = usb_submit_urb(dev->inturb, gfp_flags);
+ if (retval < 0 && retval != -EPERM) {
+ dev_err(&dev->intf->dev,
+ "%s error submitting int urb %d\n",
+ __func__, retval);
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (retval)
+ dev->rx_state = RX_IDLE;
+ else
+ dev->rx_state = RX_WAIT;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return retval;
+}
+
static void resp_avail_cb(struct urb *urb)
{
struct ctrl_bridge *dev = urb->context;
- int status = 0;
int resubmit_urb = 1;
struct bridge *brdg = dev->brdg;
+ unsigned long flags;
+
+ /*usb device disconnect*/
+ if (urb->dev->state == USB_STATE_NOTATTACHED)
+ return;
switch (urb->status) {
case 0:
@@ -158,15 +206,14 @@
if (resubmit_urb) {
/*re- submit int urb to check response available*/
- usb_anchor_urb(dev->inturb, &dev->tx_submitted);
- status = usb_submit_urb(dev->inturb, GFP_ATOMIC);
- if (status) {
- dev_err(&dev->intf->dev,
- "%s: Error re-submitting Int URB %d\n",
- __func__, status);
- usb_unanchor_urb(dev->inturb);
- }
+ ctrl_bridge_start_read(dev, GFP_ATOMIC);
+ } else {
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->rx_state = RX_IDLE;
+ spin_unlock_irqrestore(&dev->lock, flags);
}
+
+ usb_autopm_put_interface_async(dev->intf);
}
static void notification_available_cb(struct urb *urb)
@@ -177,6 +224,15 @@
struct bridge *brdg = dev->brdg;
unsigned int ctrl_bits;
unsigned char *data;
+ unsigned long flags;
+
+ /*usb device disconnect*/
+ if (urb->dev->state == USB_STATE_NOTATTACHED)
+ return;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->rx_state = RX_IDLE;
+ spin_unlock_irqrestore(&dev->lock, flags);
switch (urb->status) {
case 0:
@@ -204,7 +260,11 @@
switch (ctrl->bNotificationType) {
case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->rx_state = RX_BUSY;
+ spin_unlock_irqrestore(&dev->lock, flags);
dev->resp_avail++;
+ usb_autopm_get_interface_no_resume(dev->intf);
usb_fill_control_urb(dev->readurb, dev->udev,
usb_rcvctrlpipe(dev->udev, 0),
(unsigned char *)dev->in_ctlreq,
@@ -212,13 +272,12 @@
DEFAULT_READ_URB_LENGTH,
resp_avail_cb, dev);
- usb_anchor_urb(dev->readurb, &dev->tx_submitted);
status = usb_submit_urb(dev->readurb, GFP_ATOMIC);
if (status) {
dev_err(&dev->intf->dev,
"%s: Error submitting Read URB %d\n",
__func__, status);
- usb_unanchor_urb(dev->readurb);
+ usb_autopm_put_interface_async(dev->intf);
goto resubmit_int_urb;
}
return;
@@ -242,56 +301,28 @@
}
resubmit_int_urb:
- usb_anchor_urb(urb, &dev->tx_submitted);
- status = usb_submit_urb(urb, GFP_ATOMIC);
- if (status) {
- dev_err(&dev->intf->dev, "%s: Error re-submitting Int URB %d\n",
- __func__, status);
- usb_unanchor_urb(urb);
- }
-}
-
-static int ctrl_bridge_start_read(struct ctrl_bridge *dev)
-{
- int retval = 0;
-
- if (!dev->inturb) {
- dev_err(&dev->intf->dev, "%s: inturb is NULL\n", __func__);
- return -ENODEV;
- }
-
- if (!dev->inturb->anchor) {
- usb_anchor_urb(dev->inturb, &dev->tx_submitted);
- retval = usb_submit_urb(dev->inturb, GFP_KERNEL);
- if (retval < 0) {
- dev_err(&dev->intf->dev,
- "%s error submitting int urb %d\n",
- __func__, retval);
- usb_unanchor_urb(dev->inturb);
- }
- }
-
- return retval;
+ ctrl_bridge_start_read(dev, GFP_ATOMIC);
}
int ctrl_bridge_open(struct bridge *brdg)
{
struct ctrl_bridge *dev;
+ int ch_id;
if (!brdg) {
err("bridge is null\n");
return -EINVAL;
}
- if (brdg->ch_id >= MAX_BRIDGE_DEVICES)
- return -EINVAL;
-
- dev = __dev[brdg->ch_id];
- if (!dev) {
- err("dev is null\n");
- return -ENODEV;
+ ch_id = get_ctrl_bridge_chid(brdg->name);
+ if (ch_id < 0 || ch_id >= MAX_BRIDGE_DEVICES) {
+ err("%s: %s dev not found\n", __func__, brdg->name);
+ return ch_id;
}
+ brdg->ch_id = ch_id;
+
+ dev = __dev[ch_id];
dev->brdg = brdg;
dev->snd_encap_cmd = 0;
dev->get_encap_res = 0;
@@ -337,7 +368,13 @@
kfree(urb->transfer_buffer);
kfree(urb->setup_packet);
usb_free_urb(urb);
- usb_autopm_put_interface_async(dev->intf);
+
+ /* if we are here after device disconnect
+ * usb_unbind_interface() takes care of
+ * residual pm_autopm_get_interface_* calls
+ */
+ if (urb->dev->state != USB_STATE_NOTATTACHED)
+ usb_autopm_put_interface_async(dev->intf);
}
int ctrl_bridge_write(unsigned int id, char *data, size_t size)
@@ -346,6 +383,7 @@
struct urb *writeurb;
struct usb_ctrlrequest *out_ctlreq;
struct ctrl_bridge *dev;
+ unsigned long flags;
if (id >= MAX_BRIDGE_DEVICES) {
result = -EINVAL;
@@ -414,12 +452,15 @@
goto free_ctrlreq;
}
+ spin_lock_irqsave(&dev->lock, flags);
if (test_bit(SUSPENDED, &dev->flags)) {
usb_anchor_urb(writeurb, &dev->tx_deferred);
+ spin_unlock_irqrestore(&dev->lock, flags);
goto deferred;
}
usb_anchor_urb(writeurb, &dev->tx_submitted);
+ spin_unlock_irqrestore(&dev->lock, flags);
result = usb_submit_urb(writeurb, GFP_ATOMIC);
if (result < 0) {
dev_err(&dev->intf->dev, "%s: submit URB error %d\n",
@@ -446,6 +487,7 @@
int ctrl_bridge_suspend(unsigned int id)
{
struct ctrl_bridge *dev;
+ unsigned long flags;
if (id >= MAX_BRIDGE_DEVICES)
return -EINVAL;
@@ -454,8 +496,27 @@
if (!dev)
return -ENODEV;
+ spin_lock_irqsave(&dev->lock, flags);
+ if (!usb_anchor_empty(&dev->tx_submitted) || dev->rx_state == RX_BUSY) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EBUSY;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_kill_urb(dev->inturb);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->rx_state != RX_IDLE) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EBUSY;
+ }
+ if (!usb_anchor_empty(&dev->tx_submitted)) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ ctrl_bridge_start_read(dev, GFP_KERNEL);
+ return -EBUSY;
+ }
set_bit(SUSPENDED, &dev->flags);
- usb_kill_anchored_urbs(&dev->tx_submitted);
+ spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
@@ -464,6 +525,8 @@
{
struct ctrl_bridge *dev;
struct urb *urb;
+ unsigned long flags;
+ int ret;
if (id >= MAX_BRIDGE_DEVICES)
return -EINVAL;
@@ -472,12 +535,19 @@
if (!dev)
return -ENODEV;
- if (!test_and_clear_bit(SUSPENDED, &dev->flags))
+ if (!test_bit(SUSPENDED, &dev->flags))
return 0;
+ spin_lock_irqsave(&dev->lock, flags);
/* submit pending write requests */
while ((urb = usb_get_from_anchor(&dev->tx_deferred))) {
- int ret;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ /*
+ * usb_get_from_anchor() does not drop the
+ * ref count incremented by the usb_anchro_urb()
+ * called in Tx submission path. Let us do it.
+ */
+ usb_put_urb(urb);
usb_anchor_urb(urb, &dev->tx_submitted);
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret < 0) {
@@ -487,9 +557,12 @@
usb_free_urb(urb);
usb_autopm_put_interface_async(dev->intf);
}
+ spin_lock_irqsave(&dev->lock, flags);
}
+ clear_bit(SUSPENDED, &dev->flags);
+ spin_unlock_irqrestore(&dev->lock, flags);
- return ctrl_bridge_start_read(dev);
+ return ctrl_bridge_start_read(dev, GFP_KERNEL);
}
#if defined(CONFIG_DEBUG_FS)
@@ -507,7 +580,7 @@
if (!buf)
return -ENOMEM;
- for (i = 0; i < ch_id; i++) {
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
dev = __dev[i];
if (!dev)
continue;
@@ -522,7 +595,7 @@
"cbits_tomdm: %d\n"
"cbits_tohost: %d\n"
"suspended: %d\n",
- dev->pdev->name, dev,
+ dev->name, dev,
dev->snd_encap_cmd,
dev->get_encap_res,
dev->resp_avail,
@@ -546,7 +619,7 @@
struct ctrl_bridge *dev;
int i;
- for (i = 0; i < ch_id; i++) {
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
dev = __dev[i];
if (!dev)
continue;
@@ -593,7 +666,7 @@
int
ctrl_bridge_probe(struct usb_interface *ifc, struct usb_host_endpoint *int_in,
- int id)
+ char *name, int id)
{
struct ctrl_bridge *dev;
struct usb_device *udev;
@@ -604,28 +677,28 @@
udev = interface_to_usbdev(ifc);
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = __dev[id];
if (!dev) {
- dev_err(&ifc->dev, "%s: unable to allocate dev\n",
- __func__);
- return -ENOMEM;
- }
- dev->pdev = platform_device_alloc(ctrl_bridge_names[id], id);
- if (!dev->pdev) {
- dev_err(&ifc->dev, "%s: unable to allocate platform device\n",
- __func__);
- retval = -ENOMEM;
- goto nomem;
+ pr_err("%s:device not found\n", __func__);
+ return -ENODEV;
}
+ dev->name = name;
+
+ dev->pdev = platform_device_alloc(name, -1);
+ if (!dev->pdev) {
+ retval = -ENOMEM;
+ dev_err(&ifc->dev, "%s: unable to allocate platform device\n",
+ __func__);
+ goto free_name;
+ }
+
+ dev->flags = 0;
dev->udev = udev;
dev->int_pipe = usb_rcvintpipe(udev,
int_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
dev->intf = ifc;
- init_usb_anchor(&dev->tx_submitted);
- init_usb_anchor(&dev->tx_deferred);
-
/*use max pkt size from ep desc*/
ep = &dev->intf->cur_altsetting->endpoint[0].desc;
@@ -633,7 +706,7 @@
if (!dev->inturb) {
dev_err(&ifc->dev, "%s: error allocating int urb\n", __func__);
retval = -ENOMEM;
- goto pdev_del;
+ goto pdev_put;
}
wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize);
@@ -685,14 +758,24 @@
dev->intf->cur_altsetting->desc.bInterfaceNumber;
dev->in_ctlreq->wLength = cpu_to_le16(DEFAULT_READ_URB_LENGTH);
- __dev[id] = dev;
+ retval = platform_device_add(dev->pdev);
+ if (retval) {
+ dev_err(&ifc->dev, "%s:fail to add pdev\n", __func__);
+ goto free_ctrlreq;
+ }
- platform_device_add(dev->pdev);
+ retval = ctrl_bridge_start_read(dev, GFP_KERNEL);
+ if (retval) {
+ dev_err(&ifc->dev, "%s:fail to start reading\n", __func__);
+ goto pdev_del;
+ }
- ch_id++;
+ return 0;
- return ctrl_bridge_start_read(dev);
-
+pdev_del:
+ platform_device_del(dev->pdev);
+free_ctrlreq:
+ kfree(dev->in_ctlreq);
free_rbuf:
kfree(dev->readbuf);
free_rurb:
@@ -701,10 +784,10 @@
kfree(dev->intbuf);
free_inturb:
usb_free_urb(dev->inturb);
-pdev_del:
- platform_device_unregister(dev->pdev);
-nomem:
- kfree(dev);
+pdev_put:
+ platform_device_put(dev->pdev);
+free_name:
+ dev->name = "none";
return retval;
}
@@ -715,9 +798,18 @@
dev_dbg(&dev->intf->dev, "%s:\n", __func__);
+ /*set device name to none to get correct channel id
+ * at the time of bridge open
+ */
+ dev->name = "none";
+
platform_device_unregister(dev->pdev);
- usb_unlink_anchored_urbs(&dev->tx_submitted);
+ usb_scuttle_anchored_urbs(&dev->tx_deferred);
+ usb_kill_anchored_urbs(&dev->tx_submitted);
+
+ usb_kill_urb(dev->inturb);
+ usb_kill_urb(dev->readurb);
kfree(dev->in_ctlreq);
kfree(dev->readbuf);
@@ -725,26 +817,54 @@
usb_free_urb(dev->readurb);
usb_free_urb(dev->inturb);
-
- __dev[id] = NULL;
- ch_id--;
-
- kfree(dev);
}
-static int __init ctrl_bridge_init(void)
+int ctrl_bridge_init(void)
{
+ struct ctrl_bridge *dev;
+ int i;
+ int retval = 0;
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ pr_err("%s: unable to allocate dev\n", __func__);
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ /*transport name will be set during probe*/
+ dev->name = "none";
+
+ spin_lock_init(&dev->lock);
+ init_usb_anchor(&dev->tx_submitted);
+ init_usb_anchor(&dev->tx_deferred);
+
+ __dev[i] = dev;
+ }
+
ctrl_bridge_debugfs_init();
return 0;
-}
-module_init(ctrl_bridge_init);
-static void __exit ctrl_bridge_exit(void)
+error:
+ while (--i >= 0) {
+ kfree(__dev[i]);
+ __dev[i] = NULL;
+ }
+
+ return retval;
+}
+
+void ctrl_bridge_exit(void)
{
- ctrl_bridge_debugfs_exit();
-}
-module_exit(ctrl_bridge_exit);
+ int i;
-MODULE_DESCRIPTION("Qualcomm modem control bridge driver");
-MODULE_LICENSE("GPL v2");
+ ctrl_bridge_debugfs_exit();
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+ kfree(__dev[i]);
+ __dev[i] = NULL;
+ }
+}
diff --git a/drivers/usb/misc/mdm_data_bridge.c b/drivers/usb/misc/mdm_data_bridge.c
index fcbf0e1..eea217a 100644
--- a/drivers/usb/misc/mdm_data_bridge.c
+++ b/drivers/usb/misc/mdm_data_bridge.c
@@ -29,11 +29,37 @@
#define FLOW_CTRL_DISABLE 300
#define FLOW_CTRL_SUPPORT 1
-static const char *data_bridge_names[] = {
- "dun_data_hsic0",
- "rmnet_data_hsic0"
+#define BRIDGE_DATA_IDX 0
+#define BRIDGE_CTRL_IDX 1
+
+/*for xport : HSIC*/
+static const char * const serial_hsic_bridge_names[] = {
+ "serial_hsic_data",
+ "serial_hsic_ctrl",
};
+static const char * const rmnet_hsic_bridge_names[] = {
+ "rmnet_hsic_data",
+ "rmnet_hsic_ctrl",
+};
+
+/*for xport : HSUSB*/
+static const char * const serial_hsusb_bridge_names[] = {
+ "serial_hsusb_data",
+ "serial_hsusb_ctrl",
+};
+
+static const char * const rmnet_hsusb_bridge_names[] = {
+ "rmnet_hsusb_data",
+ "rmnet_hsusb_ctrl",
+};
+
+/* since driver supports multiple instances, on smp systems
+ * probe might get called from multiple cores, hence use lock
+ * to identify unclaimed bridge device instance
+ */
+static DEFINE_MUTEX(brdg_claim_lock);
+
static struct workqueue_struct *bridge_wq;
static unsigned int fctrl_support = FLOW_CTRL_SUPPORT;
@@ -54,14 +80,16 @@
static unsigned tx_urb_mult = 20;
module_param(tx_urb_mult, uint, S_IRUGO|S_IWUSR);
-#define TX_HALT BIT(0)
-#define RX_HALT BIT(1)
-#define SUSPENDED BIT(2)
+#define TX_HALT 0
+#define RX_HALT 1
+#define SUSPENDED 2
+#define CLAIMED 3
struct data_bridge {
struct usb_interface *intf;
struct usb_device *udev;
int id;
+ char *name;
unsigned int bulk_in;
unsigned int bulk_out;
@@ -71,9 +99,6 @@
struct usb_anchor tx_active;
struct usb_anchor rx_active;
- /* keep track of outgoing URBs during suspend */
- struct usb_anchor delayed;
-
struct list_head rx_idle;
struct sk_buff_head rx_done;
@@ -102,14 +127,45 @@
static struct data_bridge *__dev[MAX_BRIDGE_DEVICES];
-/* counter used for indexing data bridge devices */
-static int ch_id;
-
static unsigned int get_timestamp(void);
static void dbg_timestamp(char *, struct sk_buff *);
static int submit_rx_urb(struct data_bridge *dev, struct urb *urb,
gfp_t flags);
+/* Find an unclaimed bridge device instance */
+static int get_bridge_dev_idx(void)
+{
+ struct data_bridge *dev;
+ int i;
+
+ mutex_lock(&brdg_claim_lock);
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+ dev = __dev[i];
+ if (!test_bit(CLAIMED, &dev->flags)) {
+ set_bit(CLAIMED, &dev->flags);
+ mutex_unlock(&brdg_claim_lock);
+ return i;
+ }
+ }
+ mutex_unlock(&brdg_claim_lock);
+
+ return -ENODEV;
+}
+
+static int get_data_bridge_chid(char *xport_name)
+{
+ struct data_bridge *dev;
+ int i;
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+ dev = __dev[i];
+ if (!strncmp(dev->name, xport_name, BRIDGE_NAME_MAX_LEN))
+ return i;
+ }
+
+ return -ENODEV;
+}
+
static inline bool rx_halted(struct data_bridge *dev)
{
return test_bit(RX_HALT, &dev->flags);
@@ -120,6 +176,22 @@
return test_bit(RX_THROTTLED, &brdg->flags);
}
+static void free_rx_urbs(struct data_bridge *dev)
+{
+ struct list_head *head;
+ struct urb *rx_urb;
+ unsigned long flags;
+
+ head = &dev->rx_idle;
+ spin_lock_irqsave(&dev->rx_done.lock, flags);
+ while (!list_empty(head)) {
+ rx_urb = list_entry(head->next, struct urb, urb_list);
+ list_del(&rx_urb->urb_list);
+ usb_free_urb(rx_urb);
+ }
+ spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+}
+
int data_bridge_unthrottle_rx(unsigned int id)
{
struct data_bridge *dev;
@@ -193,6 +265,10 @@
struct data_bridge *dev = info->dev;
bool queue = 0;
+ /*usb device disconnect*/
+ if (urb->dev->state == USB_STATE_NOTATTACHED)
+ urb->status = -ECONNRESET;
+
brdg = dev->brdg;
skb_put(skb, urb->actual_length);
@@ -280,35 +356,45 @@
{
int i;
struct urb *rx_urb;
+ int retval = 0;
for (i = 0; i < max_rx_urbs; i++) {
rx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!rx_urb)
- return -ENOMEM;
+ if (!rx_urb) {
+ retval = -ENOMEM;
+ goto free_urbs;
+ }
list_add_tail(&rx_urb->urb_list, &dev->rx_idle);
}
- return 0;
+
+ return 0;
+
+free_urbs:
+ free_rx_urbs(dev);
+ return retval;
}
int data_bridge_open(struct bridge *brdg)
{
struct data_bridge *dev;
+ int ch_id;
if (!brdg) {
err("bridge is null\n");
return -EINVAL;
}
- if (brdg->ch_id >= MAX_BRIDGE_DEVICES)
- return -EINVAL;
-
- dev = __dev[brdg->ch_id];
- if (!dev) {
- err("dev is null\n");
- return -ENODEV;
+ ch_id = get_data_bridge_chid(brdg->name);
+ if (ch_id < 0 || ch_id >= MAX_BRIDGE_DEVICES) {
+ err("%s: %s dev not found\n", __func__, brdg->name);
+ return ch_id;
}
+ brdg->ch_id = ch_id;
+
+ dev = __dev[ch_id];
+
dev_dbg(&dev->intf->dev, "%s: dev:%p\n", __func__, dev);
dev->brdg = brdg;
@@ -346,9 +432,8 @@
cancel_work_sync(&dev->kevent);
cancel_work_sync(&dev->process_rx_w);
- usb_unlink_anchored_urbs(&dev->tx_active);
- usb_unlink_anchored_urbs(&dev->rx_active);
- usb_unlink_anchored_urbs(&dev->delayed);
+ usb_kill_anchored_urbs(&dev->tx_active);
+ usb_kill_anchored_urbs(&dev->rx_active);
spin_lock_irqsave(&dev->rx_done.lock, flags);
while ((skb = __skb_dequeue(&dev->rx_done)))
@@ -457,7 +542,12 @@
brdg->ops.unthrottle_tx(brdg->ctx);
}
- usb_autopm_put_interface_async(dev->intf);
+ /* if we are here after device disconnect
+ * usb_unbind_interface() takes care of
+ * residual pm_autopm_get_interface_* calls
+ */
+ if (urb->dev->state != USB_STATE_NOTATTACHED)
+ usb_autopm_put_interface_async(dev->intf);
}
int data_bridge_write(unsigned int id, struct sk_buff *skb)
@@ -502,11 +592,6 @@
txurb->transfer_flags |= URB_ZERO_PACKET;
- if (test_bit(SUSPENDED, &dev->flags)) {
- usb_anchor_urb(txurb, &dev->delayed);
- goto free_urb;
- }
-
pending = atomic_inc_return(&dev->pending_txurbs);
usb_anchor_urb(txurb, &dev->tx_active);
@@ -546,110 +631,66 @@
}
EXPORT_SYMBOL(data_bridge_write);
-static int data_bridge_resume(struct data_bridge *dev)
+static int bridge_resume(struct usb_interface *iface)
{
- struct urb *urb;
- int retval;
+ int retval = 0;
+ struct data_bridge *dev = usb_get_intfdata(iface);
- if (!test_and_clear_bit(SUSPENDED, &dev->flags))
- return 0;
-
- while ((urb = usb_get_from_anchor(&dev->delayed))) {
- usb_anchor_urb(urb, &dev->tx_active);
- atomic_inc(&dev->pending_txurbs);
- retval = usb_submit_urb(urb, GFP_ATOMIC);
- if (retval < 0) {
- atomic_dec(&dev->pending_txurbs);
- usb_unanchor_urb(urb);
-
- /* TODO: need to free urb data */
- usb_scuttle_anchored_urbs(&dev->delayed);
- break;
- }
- dev->to_modem++;
- dev->txurb_drp_cnt--;
- }
+ clear_bit(SUSPENDED, &dev->flags);
if (dev->brdg)
queue_work(dev->wq, &dev->process_rx_w);
- return 0;
-}
-
-static int bridge_resume(struct usb_interface *iface)
-{
- int retval = 0;
- int oldstate;
- struct data_bridge *dev = usb_get_intfdata(iface);
-
- oldstate = iface->dev.power.power_state.event;
- iface->dev.power.power_state.event = PM_EVENT_ON;
-
- if (oldstate & PM_EVENT_SUSPEND) {
- retval = data_bridge_resume(dev);
- if (!retval)
- retval = ctrl_bridge_resume(dev->id);
- }
+ retval = ctrl_bridge_resume(dev->id);
return retval;
}
-static int data_bridge_suspend(struct data_bridge *dev, pm_message_t message)
-{
- if (atomic_read(&dev->pending_txurbs) &&
- (message.event & PM_EVENT_AUTO))
- return -EBUSY;
-
- set_bit(SUSPENDED, &dev->flags);
-
- usb_kill_anchored_urbs(&dev->tx_active);
- usb_kill_anchored_urbs(&dev->rx_active);
-
- return 0;
-}
-
static int bridge_suspend(struct usb_interface *intf, pm_message_t message)
{
int retval;
struct data_bridge *dev = usb_get_intfdata(intf);
- retval = data_bridge_suspend(dev, message);
- if (!retval) {
- retval = ctrl_bridge_suspend(dev->id);
- intf->dev.power.power_state.event = message.event;
- }
+ if (atomic_read(&dev->pending_txurbs))
+ return -EBUSY;
- return retval;
+ retval = ctrl_bridge_suspend(dev->id);
+ if (retval)
+ return retval;
+
+ set_bit(SUSPENDED, &dev->flags);
+ usb_kill_anchored_urbs(&dev->rx_active);
+
+ return 0;
}
static int data_bridge_probe(struct usb_interface *iface,
struct usb_host_endpoint *bulk_in,
- struct usb_host_endpoint *bulk_out, int id)
+ struct usb_host_endpoint *bulk_out, char *name, int id)
{
struct data_bridge *dev;
+ int retval;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = __dev[id];
if (!dev) {
- err("%s: unable to allocate dev\n", __func__);
- return -ENOMEM;
+ err("%s: device not found\n", __func__);
+ return -ENODEV;
}
- dev->pdev = platform_device_alloc(data_bridge_names[id], id);
+ dev->pdev = platform_device_alloc(name, -1);
if (!dev->pdev) {
err("%s: unable to allocate platform device\n", __func__);
kfree(dev);
return -ENOMEM;
}
- init_usb_anchor(&dev->tx_active);
- init_usb_anchor(&dev->rx_active);
- init_usb_anchor(&dev->delayed);
+ /*clear all bits except claimed bit*/
+ clear_bit(RX_HALT, &dev->flags);
+ clear_bit(TX_HALT, &dev->flags);
+ clear_bit(SUSPENDED, &dev->flags);
- INIT_LIST_HEAD(&dev->rx_idle);
- skb_queue_head_init(&dev->rx_done);
-
- dev->wq = bridge_wq;
dev->id = id;
+ dev->name = name;
dev->udev = interface_to_usbdev(iface);
dev->intf = iface;
@@ -661,13 +702,12 @@
usb_set_intfdata(iface, dev);
- INIT_WORK(&dev->kevent, defer_kevent);
- INIT_WORK(&dev->process_rx_w, data_bridge_process_rx);
-
- __dev[id] = dev;
-
/*allocate list of rx urbs*/
- data_bridge_prepare_rx(dev);
+ retval = data_bridge_prepare_rx(dev);
+ if (retval) {
+ platform_device_put(dev->pdev);
+ return retval;
+ }
platform_device_add(dev->pdev);
@@ -675,7 +715,7 @@
}
#if defined(CONFIG_DEBUG_FS)
-#define DEBUG_BUF_SIZE 1024
+#define DEBUG_BUF_SIZE 4096
static unsigned int record_timestamp;
module_param(record_timestamp, uint, S_IRUGO | S_IWUSR);
@@ -746,7 +786,7 @@
if (!record_timestamp)
return 0;
- buf = kzalloc(sizeof(char) * 4 * DEBUG_BUF_SIZE, GFP_KERNEL);
+ buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -756,7 +796,7 @@
for (dbg_inc(&i); i != dbg_data.idx; dbg_inc(&i)) {
if (!strnlen(dbg_data.buf[i], DBG_DATA_MSG))
continue;
- j += scnprintf(buf + j, (4 * DEBUG_BUF_SIZE) - j,
+ j += scnprintf(buf + j, DEBUG_BUF_SIZE - j,
"%s\n", dbg_data.buf[i]);
}
@@ -786,7 +826,7 @@
if (!buf)
return -ENOMEM;
- for (i = 0; i < ch_id; i++) {
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
dev = __dev[i];
if (!dev)
continue;
@@ -806,7 +846,7 @@
"suspended: %d\n"
"TX_HALT: %d\n"
"RX_HALT: %d\n",
- dev->pdev->name, dev,
+ dev->name, dev,
atomic_read(&dev->pending_txurbs),
dev->txurb_drp_cnt,
dev->to_host,
@@ -836,7 +876,7 @@
struct data_bridge *dev;
int i;
- for (i = 0; i < ch_id; i++) {
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
dev = __dev[i];
if (!dev)
continue;
@@ -913,9 +953,8 @@
int i;
int status = 0;
int numends;
- unsigned int iface_num;
-
- iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
+ int ch_id;
+ char **bname = (char **)id->driver_info;
if (iface->num_altsetting != 1) {
err("%s invalid num_altsetting %u\n",
@@ -923,9 +962,6 @@
return -EINVAL;
}
- if (!test_bit(iface_num, &id->driver_info))
- return -ENODEV;
-
udev = interface_to_usbdev(iface);
usb_get_dev(udev);
@@ -953,27 +989,32 @@
goto out;
}
- status = data_bridge_probe(iface, bulk_in, bulk_out, ch_id);
+ ch_id = get_bridge_dev_idx();
+ if (ch_id < 0) {
+ err("%s all bridge channels claimed. Probe failed\n", __func__);
+ return -ENODEV;
+ }
+
+ status = data_bridge_probe(iface, bulk_in, bulk_out,
+ bname[BRIDGE_DATA_IDX], ch_id);
if (status < 0) {
dev_err(&iface->dev, "data_bridge_probe failed %d\n", status);
goto out;
}
- status = ctrl_bridge_probe(iface, int_in, ch_id);
+ status = ctrl_bridge_probe(iface, int_in, bname[BRIDGE_CTRL_IDX],
+ ch_id);
if (status < 0) {
dev_err(&iface->dev, "ctrl_bridge_probe failed %d\n", status);
- goto free_data_bridge;
+ goto error;
}
- ch_id++;
-
return 0;
-free_data_bridge:
+error:
platform_device_unregister(__dev[ch_id]->pdev);
+ free_rx_urbs(__dev[ch_id]);
usb_set_intfdata(iface, NULL);
- kfree(__dev[ch_id]);
- __dev[ch_id] = NULL;
out:
usb_put_dev(udev);
@@ -983,57 +1024,65 @@
static void bridge_disconnect(struct usb_interface *intf)
{
struct data_bridge *dev = usb_get_intfdata(intf);
- struct list_head *head;
- struct urb *rx_urb;
- unsigned long flags;
if (!dev) {
err("%s: data device not found\n", __func__);
return;
}
- ch_id--;
+ /*set device name to none to get correct channel id
+ * at the time of bridge open
+ */
+ dev->name = "none";
+
ctrl_bridge_disconnect(dev->id);
platform_device_unregister(dev->pdev);
usb_set_intfdata(intf, NULL);
- __dev[dev->id] = NULL;
- /*free rx urbs*/
- head = &dev->rx_idle;
- spin_lock_irqsave(&dev->rx_done.lock, flags);
- while (!list_empty(head)) {
- rx_urb = list_entry(head->next, struct urb, urb_list);
- list_del(&rx_urb->urb_list);
- usb_free_urb(rx_urb);
- }
- spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+ free_rx_urbs(dev);
usb_put_dev(dev->udev);
- kfree(dev);
+
+ clear_bit(CLAIMED, &dev->flags);
}
-/*bit position represents interface number*/
-#define PID9001_IFACE_MASK 0xC
-#define PID9034_IFACE_MASK 0xC
-#define PID9048_IFACE_MASK 0x18
-#define PID904C_IFACE_MASK 0x28
-#define PID9075_IFACE_MASK 0x28
-
+/*driver info stores data/ctrl bridge name used to match bridge xport name*/
static const struct usb_device_id bridge_ids[] = {
- { USB_DEVICE(0x5c6, 0x9001),
- .driver_info = PID9001_IFACE_MASK,
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9001, 2),
+ .driver_info = (unsigned long)serial_hsic_bridge_names,
},
- { USB_DEVICE(0x5c6, 0x9034),
- .driver_info = PID9034_IFACE_MASK,
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9001, 3),
+ .driver_info = (unsigned long)rmnet_hsic_bridge_names,
},
- { USB_DEVICE(0x5c6, 0x9048),
- .driver_info = PID9048_IFACE_MASK,
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9034, 2),
+ .driver_info = (unsigned long)serial_hsic_bridge_names,
},
- { USB_DEVICE(0x5c6, 0x904c),
- .driver_info = PID904C_IFACE_MASK,
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9034, 3),
+ .driver_info = (unsigned long)rmnet_hsic_bridge_names,
},
- { USB_DEVICE(0x5c6, 0x9075),
- .driver_info = PID9075_IFACE_MASK,
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9048, 3),
+ .driver_info = (unsigned long)serial_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9048, 4),
+ .driver_info = (unsigned long)rmnet_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x904c, 3),
+ .driver_info = (unsigned long)serial_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x904c, 5),
+ .driver_info = (unsigned long)rmnet_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9075, 3),
+ .driver_info = (unsigned long)serial_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9075, 5),
+ .driver_info = (unsigned long)rmnet_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9079, 3),
+ .driver_info = (unsigned long)serial_hsusb_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9079, 4),
+ .driver_info = (unsigned long)rmnet_hsusb_bridge_names,
},
{ } /* Terminating entry */
@@ -1052,31 +1101,83 @@
static int __init bridge_init(void)
{
- int ret;
+ struct data_bridge *dev;
+ int ret;
+ int i = 0;
+
+ ret = ctrl_bridge_init();
+ if (ret)
+ return ret;
+
+ bridge_wq = create_singlethread_workqueue("mdm_bridge");
+ if (!bridge_wq) {
+ pr_err("%s: Unable to create workqueue:bridge\n", __func__);
+ ret = -ENOMEM;
+ goto free_ctrl;
+ }
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ err("%s: unable to allocate dev\n", __func__);
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ dev->wq = bridge_wq;
+
+ /*transport name will be set during probe*/
+ dev->name = "none";
+
+ init_usb_anchor(&dev->tx_active);
+ init_usb_anchor(&dev->rx_active);
+
+ INIT_LIST_HEAD(&dev->rx_idle);
+
+ skb_queue_head_init(&dev->rx_done);
+
+ INIT_WORK(&dev->kevent, defer_kevent);
+ INIT_WORK(&dev->process_rx_w, data_bridge_process_rx);
+
+ __dev[i] = dev;
+ }
ret = usb_register(&bridge_driver);
if (ret) {
err("%s: unable to register mdm_bridge driver", __func__);
- return ret;
- }
-
- bridge_wq = create_singlethread_workqueue("mdm_bridge");
- if (!bridge_wq) {
- usb_deregister(&bridge_driver);
- pr_err("%s: Unable to create workqueue:bridge\n", __func__);
- return -ENOMEM;
+ goto error;
}
data_bridge_debugfs_init();
return 0;
+
+error:
+ while (--i >= 0) {
+ kfree(__dev[i]);
+ __dev[i] = NULL;
+ }
+ destroy_workqueue(bridge_wq);
+free_ctrl:
+ ctrl_bridge_exit();
+ return ret;
}
static void __exit bridge_exit(void)
{
+ int i;
+
+ usb_deregister(&bridge_driver);
data_bridge_debugfs_exit();
destroy_workqueue(bridge_wq);
- usb_deregister(&bridge_driver);
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+ kfree(__dev[i]);
+ __dev[i] = NULL;
+ }
+
+ ctrl_bridge_exit();
}
module_init(bridge_init);
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c
index 339b888..fdd9666 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.c
+++ b/drivers/video/msm/mdss/mdp3_ctrl.c
@@ -30,6 +30,8 @@
static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd);
static int mdp3_overlay_unset(struct msm_fb_data_type *mfd, int ndx);
+static int mdp3_histogram_stop(struct mdp3_session_data *session,
+ u32 block);
static void mdp3_bufq_init(struct mdp3_buffer_queue *bufq)
{
@@ -495,6 +497,8 @@
pr_debug("mdp3_ctrl_off stop mdp3 dma engine\n");
+ mdp3_histogram_stop(mdp3_session, MDP_BLOCK_DMA_P);
+
rc = mdp3_session->dma->stop(mdp3_session->dma, mdp3_session->intf);
if (rc)
@@ -665,8 +669,10 @@
data = mdp3_bufq_pop(&mdp3_session->bufq_out);
mdp3_put_img(data);
}
-
mutex_unlock(&mdp3_session->lock);
+
+ mdss_fb_update_notify_update(mfd);
+
return rc;
}
@@ -738,6 +744,288 @@
return ret;
}
+static int mdp3_histogram_start(struct mdp3_session_data *session,
+ struct mdp_histogram_start_req *req)
+{
+ int ret;
+ struct mdp3_dma_histogram_config histo_config;
+
+ pr_debug("mdp3_histogram_start\n");
+ if (req->block != MDP_BLOCK_DMA_P ||
+ req->num_bins != MDP_HISTOGRAM_BIN_NUM) {
+ pr_err("mdp3_histogram_start invalid request\n");
+ return -EINVAL;
+ }
+
+ if (!session->dma->histo_op ||
+ !session->dma->config_histo) {
+ pr_err("mdp3_histogram_start not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&session->histo_lock);
+
+ if (session->histo_status) {
+ pr_err("mdp3_histogram_start already started\n");
+ ret = -EBUSY;
+ goto histogram_start_err;
+ }
+
+ ret = session->dma->histo_op(session->dma, MDP3_DMA_HISTO_OP_RESET);
+ if (ret) {
+ pr_err("mdp3_histogram_start reset error\n");
+ goto histogram_start_err;
+ }
+
+ histo_config.frame_count = req->frame_cnt;
+ histo_config.bit_mask = req->bit_mask;
+ histo_config.auto_clear_en = 1;
+ histo_config.bit_mask_polarity = 0;
+ ret = session->dma->config_histo(session->dma, &histo_config);
+ if (ret) {
+ pr_err("mdp3_histogram_start config error\n");
+ goto histogram_start_err;
+ }
+
+ ret = session->dma->histo_op(session->dma, MDP3_DMA_HISTO_OP_START);
+ if (ret) {
+ pr_err("mdp3_histogram_start config error\n");
+ goto histogram_start_err;
+ }
+
+ session->histo_status = 1;
+
+histogram_start_err:
+ mutex_unlock(&session->histo_lock);
+ return ret;
+}
+
+static int mdp3_histogram_stop(struct mdp3_session_data *session,
+ u32 block)
+{
+ int ret;
+ pr_debug("mdp3_histogram_stop\n");
+
+ if (!session->dma->histo_op || block != MDP_BLOCK_DMA_P) {
+ pr_err("mdp3_histogram_stop not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&session->histo_lock);
+
+ if (!session->histo_status) {
+ ret = 0;
+ goto histogram_stop_err;
+ }
+
+ ret = session->dma->histo_op(session->dma, MDP3_DMA_HISTO_OP_CANCEL);
+ if (ret)
+ pr_err("mdp3_histogram_stop error\n");
+
+ session->histo_status = 0;
+
+histogram_stop_err:
+ mutex_unlock(&session->histo_lock);
+ return ret;
+}
+
+static int mdp3_histogram_collect(struct mdp3_session_data *session,
+ struct mdp_histogram_data *hist)
+{
+ int ret;
+ struct mdp3_dma_histogram_data *mdp3_histo;
+
+ if (!session->dma->get_histo) {
+ pr_err("mdp3_histogram_collect not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&session->histo_lock);
+
+ if (!session->histo_status) {
+ pr_err("mdp3_histogram_collect not started\n");
+ mutex_unlock(&session->histo_lock);
+ return -EPERM;
+ }
+
+ mutex_unlock(&session->histo_lock);
+
+ ret = session->dma->get_histo(session->dma);
+ if (ret) {
+ pr_err("mdp3_histogram_collect error = %d\n", ret);
+ return ret;
+ }
+
+ mdp3_histo = &session->dma->histo_data;
+
+ ret = copy_to_user(hist->c0, mdp3_histo->r_data,
+ sizeof(uint32_t) * MDP_HISTOGRAM_BIN_NUM);
+ if (ret)
+ return ret;
+
+ ret = copy_to_user(hist->c1, mdp3_histo->g_data,
+ sizeof(uint32_t) * MDP_HISTOGRAM_BIN_NUM);
+ if (ret)
+ return ret;
+
+ ret = copy_to_user(hist->c2, mdp3_histo->b_data,
+ sizeof(uint32_t) * MDP_HISTOGRAM_BIN_NUM);
+ if (ret)
+ return ret;
+
+ ret = copy_to_user(hist->extra_info, mdp3_histo->extra,
+ sizeof(uint32_t) * 2);
+ if (ret)
+ return ret;
+
+ hist->bin_cnt = MDP_HISTOGRAM_BIN_NUM;
+ hist->block = MDP_BLOCK_DMA_P;
+ return ret;
+}
+
+static int mdp3_bl_scale_config(struct msm_fb_data_type *mfd,
+ struct mdp_bl_scale_data *data)
+{
+ int ret = 0;
+ int curr_bl;
+ mutex_lock(&mfd->bl_lock);
+ curr_bl = mfd->bl_level;
+ mfd->bl_scale = data->scale;
+ mfd->bl_min_lvl = data->min_lvl;
+ pr_debug("update scale = %d, min_lvl = %d\n", mfd->bl_scale,
+ mfd->bl_min_lvl);
+
+ /* update current backlight to use new scaling*/
+ mdss_fb_set_backlight(mfd, curr_bl);
+ mutex_unlock(&mfd->bl_lock);
+ return ret;
+}
+
+static int mdp3_pp_ioctl(struct msm_fb_data_type *mfd,
+ void __user *argp)
+{
+ int ret = -EINVAL;
+ struct msmfb_mdp_pp mdp_pp;
+
+ ret = copy_from_user(&mdp_pp, argp, sizeof(mdp_pp));
+ if (ret)
+ return ret;
+
+ switch (mdp_pp.op) {
+ case mdp_bl_scale_cfg:
+ ret = mdp3_bl_scale_config(mfd, (struct mdp_bl_scale_data *)
+ &mdp_pp.data.bl_scale_data);
+ break;
+ default:
+ pr_err("Unsupported request to MDP_PP IOCTL.\n");
+ ret = -EINVAL;
+ break;
+ }
+ if (!ret)
+ ret = copy_to_user(argp, &mdp_pp, sizeof(struct msmfb_mdp_pp));
+ return ret;
+}
+
+static int mdp3_histo_ioctl(struct msm_fb_data_type *mfd, u32 cmd,
+ void __user *argp)
+{
+ int ret = -ENOSYS;
+ struct mdp_histogram_data hist;
+ struct mdp_histogram_start_req hist_req;
+ u32 block;
+ struct mdp3_session_data *mdp3_session;
+
+ if (!mfd || !mfd->mdp.private1)
+ return -EINVAL;
+
+ mdp3_session = mfd->mdp.private1;
+
+ switch (cmd) {
+ case MSMFB_HISTOGRAM_START:
+ ret = copy_from_user(&hist_req, argp, sizeof(hist_req));
+ if (ret)
+ return ret;
+
+ ret = mdp3_histogram_start(mdp3_session, &hist_req);
+ break;
+
+ case MSMFB_HISTOGRAM_STOP:
+ ret = copy_from_user(&block, argp, sizeof(int));
+ if (ret)
+ return ret;
+
+ ret = mdp3_histogram_stop(mdp3_session, block);
+ break;
+
+ case MSMFB_HISTOGRAM:
+ ret = copy_from_user(&hist, argp, sizeof(hist));
+ if (ret)
+ return ret;
+
+ ret = mdp3_histogram_collect(mdp3_session, &hist);
+ if (!ret)
+ ret = copy_to_user(argp, &hist, sizeof(hist));
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd,
+ struct fb_cmap *cmap)
+{
+ int rc = 0;
+ struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
+ struct mdp3_dma_lut_config lut_config;
+ struct mdp3_dma_lut lut;
+ static u16 r[MDP_LUT_SIZE];
+ static u16 g[MDP_LUT_SIZE];
+ static u16 b[MDP_LUT_SIZE];
+
+ if (!mdp3_session->dma->config_lut)
+ return -EINVAL;
+
+ if (cmap->start + cmap->len > MDP_LUT_SIZE) {
+ pr_err("mdp3_ctrl_lut_update invalid arguments\n");
+ return -EINVAL;
+ }
+
+ rc = copy_from_user(r + cmap->start,
+ cmap->red, sizeof(u16)*cmap->len);
+ rc |= copy_from_user(g + cmap->start,
+ cmap->green, sizeof(u16)*cmap->len);
+ rc |= copy_from_user(b + cmap->start,
+ cmap->blue, sizeof(u16)*cmap->len);
+ if (rc)
+ return rc;
+
+ lut_config.lut_enable = 7;
+ lut_config.lut_sel = mdp3_session->lut_sel;
+ lut_config.lut_position = 0;
+ lut.color0_lut = r;
+ lut.color1_lut = g;
+ lut.color2_lut = b;
+
+ mutex_lock(&mdp3_session->lock);
+
+ if (!mdp3_session->status) {
+ pr_err("%s, display off!\n", __func__);
+ mutex_unlock(&mdp3_session->lock);
+ return -EPERM;
+ }
+
+ rc = mdp3_session->dma->config_lut(mdp3_session->dma, &lut_config,
+ &lut);
+ if (rc)
+ pr_err("mdp3_ctrl_lut_update failed\n");
+
+ mdp3_session->lut_sel = (mdp3_session->lut_sel + 1) % 2;
+
+ mutex_unlock(&mdp3_session->lock);
+ return rc;
+}
+
static int mdp3_ctrl_ioctl_handler(struct msm_fb_data_type *mfd,
u32 cmd, void __user *argp)
{
@@ -754,10 +1042,19 @@
if (!mdp3_session->status) {
pr_err("mdp3_ctrl_ioctl_handler, display off!\n");
- return -EINVAL;
+ return -EPERM;
}
switch (cmd) {
+ case MSMFB_MDP_PP:
+ rc = mdp3_pp_ioctl(mfd, argp);
+ break;
+ case MSMFB_HISTOGRAM_START:
+ case MSMFB_HISTOGRAM_STOP:
+ case MSMFB_HISTOGRAM:
+ rc = mdp3_histo_ioctl(mfd, cmd, argp);
+ break;
+
case MSMFB_VSYNC_CTRL:
case MSMFB_OVERLAY_VSYNC_CTRL:
if (!copy_from_user(&val, argp, sizeof(val))) {
@@ -836,6 +1133,7 @@
mdp3_interface->dma_fnc = mdp3_ctrl_pan_display;
mdp3_interface->ioctl_handler = mdp3_ctrl_ioctl_handler;
mdp3_interface->kickoff_fnc = mdp3_ctrl_display_commit_kickoff;
+ mdp3_interface->lut_update = mdp3_ctrl_lut_update;
mdp3_session = kmalloc(sizeof(struct mdp3_session_data), GFP_KERNEL);
if (!mdp3_session) {
@@ -845,6 +1143,7 @@
memset(mdp3_session, 0, sizeof(struct mdp3_session_data));
mutex_init(&mdp3_session->lock);
INIT_WORK(&mdp3_session->vsync_work, mdp3_dispatch_vsync);
+ mutex_init(&mdp3_session->histo_lock);
mdp3_session->dma = mdp3_get_dma_pipe(MDP3_DMA_CAP_ALL);
if (!mdp3_session->dma) {
rc = -ENODEV;
@@ -864,6 +1163,8 @@
mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
mdp3_bufq_init(&mdp3_session->bufq_in);
mdp3_bufq_init(&mdp3_session->bufq_out);
+ mdp3_session->histo_status = 0;
+ mdp3_session->lut_sel = 0;
init_timer(&mdp3_session->vsync_timer);
mdp3_session->vsync_timer.function = mdp3_vsync_timer_func;
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.h b/drivers/video/msm/mdss/mdp3_ctrl.h
index 0c2b0cd..0bbc2ac 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.h
+++ b/drivers/video/msm/mdss/mdp3_ctrl.h
@@ -47,6 +47,9 @@
struct mdp3_buffer_queue bufq_in;
struct mdp3_buffer_queue bufq_out;
struct work_struct vsync_work;
+ int histo_status;
+ struct mutex histo_lock;
+ int lut_sel;
};
int mdp3_ctrl_init(struct msm_fb_data_type *mfd);
diff --git a/drivers/video/msm/mdss/mdp3_dma.c b/drivers/video/msm/mdss/mdp3_dma.c
index 3a46034..3ddabb4 100644
--- a/drivers/video/msm/mdss/mdp3_dma.c
+++ b/drivers/video/msm/mdss/mdp3_dma.c
@@ -19,6 +19,10 @@
#define DMA_STOP_POLL_SLEEP_US 1000
#define DMA_STOP_POLL_TIMEOUT_US 16000
+#define DMA_HISTO_RESET_TIMEOUT_MS 40
+#define DMA_LUT_CONFIG_MASK 0xfffffbe8
+#define DMA_CCS_CONFIG_MASK 0xfffffc17
+#define HIST_WAIT_TIMEOUT(frame) ((75 * HZ * (frame)) / 1000)
static void mdp3_vsync_intr_handler(int type, void *arg)
{
@@ -45,12 +49,47 @@
mdp3_irq_disable_nosync(type);
}
+static void mdp3_hist_done_intr_handler(int type, void *arg)
+{
+ struct mdp3_dma *dma = (struct mdp3_dma *)arg;
+ u32 isr, mask;
+
+ isr = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_INTR_STATUS);
+ mask = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_INTR_ENABLE);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_CLEAR, isr);
+
+ isr &= mask;
+ if (isr == 0)
+ return;
+
+ if (isr & MDP3_DMA_P_HIST_INTR_HIST_DONE_BIT) {
+ spin_lock(&dma->histo_lock);
+ dma->histo_state = MDP3_DMA_HISTO_STATE_READY;
+ complete(&dma->histo_comp);
+ spin_unlock(&dma->histo_lock);
+ }
+ if (isr & MDP3_DMA_P_HIST_INTR_RESET_DONE_BIT) {
+ spin_lock(&dma->histo_lock);
+ dma->histo_state = MDP3_DMA_HISTO_STATE_IDLE;
+ complete(&dma->histo_comp);
+ spin_unlock(&dma->histo_lock);
+ }
+}
+
void mdp3_dma_callback_enable(struct mdp3_dma *dma, int type)
{
int irq_bit;
pr_debug("mdp3_dma_callback_enable type=%d\n", type);
+ if (dma->dma_sel == MDP3_DMA_P) {
+ if (type & MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE)
+ mdp3_irq_enable(MDP3_INTR_DMA_P_HISTO);
+
+ if (type & MDP3_DMA_CALLBACK_TYPE_HIST_DONE)
+ mdp3_irq_enable(MDP3_INTR_DMA_P_HISTO);
+ }
+
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC) {
if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC)
@@ -79,6 +118,14 @@
pr_debug("mdp3_dma_callback_disable type=%d\n", type);
+ if (dma->dma_sel == MDP3_DMA_P) {
+ if (type & MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE)
+ mdp3_irq_disable(MDP3_INTR_DMA_P_HISTO);
+
+ if (type & MDP3_DMA_CALLBACK_TYPE_HIST_DONE)
+ mdp3_irq_disable(MDP3_INTR_DMA_P_HISTO);
+ }
+
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC) {
if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC)
@@ -101,7 +148,7 @@
static int mdp3_dma_callback_setup(struct mdp3_dma *dma)
{
- int rc;
+ int rc = 0;
struct mdp3_intr_cb vsync_cb = {
.cb = mdp3_vsync_intr_handler,
.data = dma,
@@ -112,14 +159,22 @@
.data = dma,
};
+ struct mdp3_intr_cb hist_cb = {
+ .cb = mdp3_hist_done_intr_handler,
+ .data = dma,
+ };
+
+ if (dma->dma_sel == MDP3_DMA_P)
+ rc = mdp3_set_intr_callback(MDP3_INTR_DMA_P_HISTO, &hist_cb);
+
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC)
- rc = mdp3_set_intr_callback(MDP3_INTR_LCDC_START_OF_FRAME,
+ rc |= mdp3_set_intr_callback(MDP3_INTR_LCDC_START_OF_FRAME,
&vsync_cb);
else if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
int irq_bit = MDP3_INTR_SYNC_PRIMARY_LINE;
irq_bit += dma->dma_sel;
- rc = mdp3_set_intr_callback(irq_bit, &vsync_cb);
+ rc |= mdp3_set_intr_callback(irq_bit, &vsync_cb);
irq_bit = MDP3_INTR_DMA_P_DONE;
if (dma->dma_sel == MDP3_DMA_S)
irq_bit = MDP3_INTR_DMA_S_DONE;
@@ -128,6 +183,7 @@
pr_err("mdp3_dma_callback_setup not suppported interface\n");
rc = -ENODEV;
}
+
return rc;
}
@@ -291,24 +347,54 @@
return 0;
}
-static int mdp3_dmap_ccs_config(struct mdp3_dma *dma,
- struct mdp3_dma_color_correct_config *config,
- struct mdp3_dma_ccs *ccs,
+static int mdp3_dmap_lut_config(struct mdp3_dma *dma,
+ struct mdp3_dma_lut_config *config,
struct mdp3_dma_lut *lut)
{
+ u32 cc_config, addr, color;
int i;
- u32 addr, cc_config, color;
- cc_config = config->lut_enable;
- if (config->ccs_enable)
- cc_config |= BIT(3);
+ if (config->lut_enable && lut) {
+ addr = MDP3_REG_DMA_P_CSC_LUT1;
+ if (config->lut_sel)
+ addr = MDP3_REG_DMA_P_CSC_LUT2;
+
+ for (i = 0; i < MDP_LUT_SIZE; i++) {
+ color = lut->color0_lut[i] & 0xff;
+ color |= (lut->color1_lut[i] & 0xff) << 8;
+ color |= (lut->color2_lut[i] & 0xff) << 16;
+ MDP3_REG_WRITE(addr, color);
+ addr += 4;
+ }
+ }
+
+ cc_config = MDP3_REG_READ(MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG);
+ cc_config &= DMA_LUT_CONFIG_MASK;
+ cc_config |= config->lut_enable;
cc_config |= config->lut_position << 4;
+ cc_config |= config->lut_sel << 10;
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG, cc_config);
+ wmb();
+
+ dma->lut_config = *config;
+ return 0;
+}
+
+static int mdp3_dmap_ccs_config(struct mdp3_dma *dma,
+ struct mdp3_dma_color_correct_config *config,
+ struct mdp3_dma_ccs *ccs)
+{
+ int i;
+ u32 cc_config, addr;
+
+ cc_config = MDP3_REG_READ(MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG);
+ cc_config &= DMA_CCS_CONFIG_MASK;
+ cc_config |= BIT(3);
cc_config |= config->ccs_sel << 5;
cc_config |= config->pre_bias_sel << 6;
cc_config |= config->post_bias_sel << 7;
cc_config |= config->pre_limit_sel << 8;
cc_config |= config->post_limit_sel << 9;
- cc_config |= config->lut_sel << 10;
MDP3_REG_WRITE(MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG, cc_config);
@@ -394,30 +480,6 @@
}
}
- if (config->lut_enable && lut) {
- if (lut->color0_lut1 && lut->color1_lut1 && lut->color2_lut1) {
- addr = MDP3_REG_DMA_P_CSC_LUT1;
- for (i = 0; i < 256; i++) {
- color = lut->color0_lut1[i];
- color |= lut->color1_lut1[i] << 8;
- color |= lut->color2_lut1[i] << 16;
- MDP3_REG_WRITE(addr, color);
- addr += 4;
- }
- }
-
- if (lut->color0_lut2 && lut->color1_lut2 && lut->color2_lut2) {
- addr = MDP3_REG_DMA_P_CSC_LUT2;
- for (i = 0; i < 256; i++) {
- color = lut->color0_lut2[i];
- color |= lut->color1_lut2[i] << 8;
- color |= lut->color2_lut2[i] << 16;
- MDP3_REG_WRITE(addr, color);
- addr += 4;
- }
- }
- }
-
dma->ccs_config = *config;
return 0;
}
@@ -425,18 +487,28 @@
static int mdp3_dmap_histo_config(struct mdp3_dma *dma,
struct mdp3_dma_histogram_config *histo_config)
{
- u32 hist_bit_mask, hist_control;
+ unsigned long flag;
+ u32 histo_bit_mask, histo_control;
+ u32 histo_isr_mask = MDP3_DMA_P_HIST_INTR_HIST_DONE_BIT |
+ MDP3_DMA_P_HIST_INTR_RESET_DONE_BIT;
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
if (histo_config->bit_mask_polarity)
- hist_bit_mask = BIT(31);
- hist_bit_mask |= histo_config->bit_mask;
+ histo_bit_mask = BIT(31);
+ histo_bit_mask |= histo_config->bit_mask;
if (histo_config->auto_clear_en)
- hist_control = BIT(0);
+ histo_control = BIT(0);
MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_FRAME_CNT,
histo_config->frame_count);
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_BIT_MASK, hist_bit_mask);
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_CONTROL, hist_control);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_BIT_MASK, histo_bit_mask);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_CONTROL, histo_control);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_ENABLE, histo_isr_mask);
+
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ dma->histogram_config = *histo_config;
return 0;
}
@@ -518,78 +590,174 @@
return 0;
}
-static int mdp3_dmap_histo_get(struct mdp3_dma *dma,
- struct mdp3_dma_histogram_data *data)
+static int mdp3_dmap_histo_get(struct mdp3_dma *dma)
{
- int i;
- u32 addr, extra;
+ int i, state, timeout, ret;
+ u32 addr;
+ unsigned long flag;
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
+ state = dma->histo_state;
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ if (state != MDP3_DMA_HISTO_STATE_START &&
+ state != MDP3_DMA_HISTO_STATE_READY) {
+ pr_err("mdp3_dmap_histo_get invalid state %d\n", state);
+ return -EINVAL;
+ }
+
+ timeout = HIST_WAIT_TIMEOUT(dma->histogram_config.frame_count);
+ ret = wait_for_completion_killable_timeout(&dma->histo_comp, timeout);
+
+ if (ret == 0) {
+ pr_err("mdp3_dmap_histo_get time out\n");
+ ret = -ETIMEDOUT;
+ } else if (ret < 0) {
+ pr_err("mdp3_dmap_histo_get interrupted\n");
+ }
+
+ if (ret < 0)
+ return ret;
+
+ if (dma->histo_state != MDP3_DMA_HISTO_STATE_READY) {
+ pr_err("mdp3_dmap_histo_get after dma shut down\n");
+ return -EPERM;
+ }
addr = MDP3_REG_DMA_P_HIST_R_DATA;
- for (i = 0; i < 32; i++) {
- data->r_data[i] = MDP3_REG_READ(addr);
+ for (i = 0; i < MDP_HISTOGRAM_BIN_NUM; i++) {
+ dma->histo_data.r_data[i] = MDP3_REG_READ(addr);
addr += 4;
}
addr = MDP3_REG_DMA_P_HIST_G_DATA;
- for (i = 0; i < 32; i++) {
- data->g_data[i] = MDP3_REG_READ(addr);
+ for (i = 0; i < MDP_HISTOGRAM_BIN_NUM; i++) {
+ dma->histo_data.g_data[i] = MDP3_REG_READ(addr);
addr += 4;
}
addr = MDP3_REG_DMA_P_HIST_B_DATA;
- for (i = 0; i < 32; i++) {
- data->b_data[i] = MDP3_REG_READ(addr);
+ for (i = 0; i < MDP_HISTOGRAM_BIN_NUM; i++) {
+ dma->histo_data.b_data[i] = MDP3_REG_READ(addr);
addr += 4;
}
- extra = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_EXTRA_INFO_0);
- data->r_min_value = (extra & 0x1F0000) >> 16;
- data->r_max_value = (extra & 0x1F000000) >> 24;
- extra = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_EXTRA_INFO_1);
- data->g_min_value = extra & 0x1F;
- data->g_max_value = (extra & 0x1F00) >> 8;
- data->b_min_value = (extra & 0x1F0000) >> 16;
- data->b_max_value = (extra & 0x1F000000) >> 24;
+ dma->histo_data.extra[0] =
+ MDP3_REG_READ(MDP3_REG_DMA_P_HIST_EXTRA_INFO_0);
+ dma->histo_data.extra[1] =
+ MDP3_REG_READ(MDP3_REG_DMA_P_HIST_EXTRA_INFO_1);
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
+ init_completion(&dma->histo_comp);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_START, 1);
+ wmb();
+ dma->histo_state = MDP3_DMA_HISTO_STATE_START;
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ return 0;
+}
+
+static int mdp3_dmap_histo_start(struct mdp3_dma *dma)
+{
+ unsigned long flag;
+
+ if (dma->histo_state != MDP3_DMA_HISTO_STATE_IDLE)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
+
+ init_completion(&dma->histo_comp);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_START, 1);
+ wmb();
+ dma->histo_state = MDP3_DMA_HISTO_STATE_START;
+
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ mdp3_dma_callback_enable(dma, MDP3_DMA_CALLBACK_TYPE_HIST_DONE);
+ return 0;
+
+}
+
+static int mdp3_dmap_histo_reset(struct mdp3_dma *dma)
+{
+ unsigned long flag;
+ int ret;
+ u32 cgc;
+
+ if (dma->histo_state == MDP3_DMA_HISTO_STATE_START)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
+
+ init_completion(&dma->histo_comp);
+ cgc = MDP3_REG_READ(MDP3_REG_CGC_EN);
+ cgc &= ~BIT(10);
+ MDP3_REG_WRITE(MDP3_REG_CGC_EN, cgc);
+
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_ENABLE, BIT(0)|BIT(1));
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_RESET_SEQ_START, 1);
+ wmb();
+ dma->histo_state = MDP3_DMA_HISTO_STATE_RESET;
+
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ mdp3_dma_callback_enable(dma, MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE);
+ ret = wait_for_completion_killable_timeout(&dma->histo_comp,
+ msecs_to_jiffies(DMA_HISTO_RESET_TIMEOUT_MS));
+
+ if (ret == 0) {
+ pr_err("mdp3_dmap_histo_reset time out\n");
+ ret = -ETIMEDOUT;
+ } else if (ret < 0) {
+ pr_err("mdp3_dmap_histo_reset interrupted\n");
+ } else {
+ ret = 0;
+ }
+ mdp3_dma_callback_disable(dma, MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE);
+ cgc |= BIT(10);
+ MDP3_REG_WRITE(MDP3_REG_CGC_EN, cgc);
+
+ return ret;
+}
+
+static int mdp3_dmap_histo_stop(struct mdp3_dma *dma)
+{
+ unsigned long flag;
+ int cb_type = MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE |
+ MDP3_DMA_CALLBACK_TYPE_HIST_DONE;
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
+
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_CANCEL_REQ, 1);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_ENABLE, 0);
+ wmb();
+ dma->histo_state = MDP3_DMA_HISTO_STATE_IDLE;
+
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ mdp3_dma_callback_disable(dma, cb_type);
return 0;
}
static int mdp3_dmap_histo_op(struct mdp3_dma *dma, u32 op)
{
+ int ret;
+
switch (op) {
case MDP3_DMA_HISTO_OP_START:
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_START, 1);
+ ret = mdp3_dmap_histo_start(dma);
break;
case MDP3_DMA_HISTO_OP_STOP:
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_STOP_REQ, 1);
- break;
case MDP3_DMA_HISTO_OP_CANCEL:
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_CANCEL_REQ, 1);
+ ret = mdp3_dmap_histo_stop(dma);
break;
case MDP3_DMA_HISTO_OP_RESET:
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_RESET_SEQ_START, 1);
+ ret = mdp3_dmap_histo_reset(dma);
break;
default:
- return -EINVAL;
+ ret = -EINVAL;
}
- return 0;
-}
-
-static int mdp3_dmap_histo_intr_status(struct mdp3_dma *dma, int *status)
-{
- *status = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_INTR_STATUS);
- return 0;
-}
-
-static int mdp3_dmap_histo_intr_enable(struct mdp3_dma *dma, u32 mask)
-{
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_ENABLE, mask);
- return 0;
-}
-
-static int mdp3_dmap_histo_intr_clear(struct mdp3_dma *dma, u32 mask)
-{
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_CLEAR, mask);
- return 0;
+ return ret;
}
static int mdp3_dma_start(struct mdp3_dma *dma, struct mdp3_intf *intf)
@@ -667,13 +835,11 @@
dma->config_cursor = mdp3_dmap_cursor_config;
dma->config_ccs = mdp3_dmap_ccs_config;
dma->config_histo = mdp3_dmap_histo_config;
+ dma->config_lut = mdp3_dmap_lut_config;
dma->update = mdp3_dmap_update;
dma->update_cursor = mdp3_dmap_cursor_update;
dma->get_histo = mdp3_dmap_histo_get;
dma->histo_op = mdp3_dmap_histo_op;
- dma->histo_intr_status = mdp3_dmap_histo_intr_status;
- dma->histo_intr_enable = mdp3_dmap_histo_intr_enable;
- dma->histo_intr_clear = mdp3_dmap_histo_intr_clear;
dma->vsync_enable = mdp3_dma_vsync_enable;
dma->start = mdp3_dma_start;
dma->stop = mdp3_dma_stop;
@@ -686,13 +852,11 @@
dma->config_cursor = NULL;
dma->config_ccs = NULL;
dma->config_histo = NULL;
+ dma->config_lut = NULL;
dma->update = mdp3_dmas_update;
dma->update_cursor = NULL;
dma->get_histo = NULL;
dma->histo_op = NULL;
- dma->histo_intr_status = NULL;
- dma->histo_intr_enable = NULL;
- dma->histo_intr_clear = NULL;
dma->vsync_enable = mdp3_dma_vsync_enable;
dma->start = mdp3_dma_start;
dma->stop = mdp3_dma_stop;
@@ -704,10 +868,13 @@
}
spin_lock_init(&dma->dma_lock);
+ spin_lock_init(&dma->histo_lock);
init_completion(&dma->vsync_comp);
init_completion(&dma->dma_comp);
+ init_completion(&dma->histo_comp);
dma->vsync_client.handler = NULL;
dma->vsync_client.arg = NULL;
+ dma->histo_state = MDP3_DMA_HISTO_STATE_IDLE;
memset(&dma->cursor, 0, sizeof(dma->cursor));
memset(&dma->ccs_config, 0, sizeof(dma->ccs_config));
diff --git a/drivers/video/msm/mdss/mdp3_dma.h b/drivers/video/msm/mdss/mdp3_dma.h
index c7b5f25..518bd58 100644
--- a/drivers/video/msm/mdss/mdp3_dma.h
+++ b/drivers/video/msm/mdss/mdp3_dma.h
@@ -16,6 +16,9 @@
#include <linux/sched.h>
+#define MDP_HISTOGRAM_BIN_NUM 32
+#define MDP_LUT_SIZE 256
+
enum {
MDP3_DMA_P,
MDP3_DMA_S,
@@ -119,8 +122,18 @@
};
enum {
+ MDP3_DMA_HISTO_STATE_UNKNOWN,
+ MDP3_DMA_HISTO_STATE_IDLE,
+ MDP3_DMA_HISTO_STATE_RESET,
+ MDP3_DMA_HISTO_STATE_START,
+ MDP3_DMA_HISTO_STATE_READY,
+};
+
+enum {
MDP3_DMA_CALLBACK_TYPE_VSYNC = 0x01,
MDP3_DMA_CALLBACK_TYPE_DMA_DONE = 0x02,
+ MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE = 0x04,
+ MDP3_DMA_CALLBACK_TYPE_HIST_DONE = 0x08,
};
struct mdp3_dma_source {
@@ -176,24 +189,24 @@
};
struct mdp3_dma_lut {
- uint8_t *color0_lut1;
- uint8_t *color1_lut1;
- uint8_t *color2_lut1;
- uint8_t *color0_lut2;
- uint8_t *color1_lut2;
- uint8_t *color2_lut2;
+ u16 *color0_lut;
+ u16 *color1_lut;
+ u16 *color2_lut;
+};
+
+struct mdp3_dma_lut_config {
+ int lut_enable;
+ u32 lut_sel;
+ u32 lut_position;
};
struct mdp3_dma_color_correct_config {
int ccs_enable;
- int lut_enable;
- u32 lut_sel;
u32 post_limit_sel;
u32 pre_limit_sel;
u32 post_bias_sel;
u32 pre_bias_sel;
u32 ccs_sel;
- u32 lut_position;
};
struct mdp3_dma_histogram_config {
@@ -204,15 +217,10 @@
};
struct mdp3_dma_histogram_data {
- uint8_t r_max_value;
- uint8_t r_min_value;
- uint8_t b_max_value;
- uint8_t b_min_value;
- uint8_t g_max_value;
- uint8_t g_min_value;
- uint8_t r_data[32];
- uint8_t g_data[32];
- uint8_t b_data[32];
+ u32 r_data[MDP_HISTOGRAM_BIN_NUM];
+ u32 g_data[MDP_HISTOGRAM_BIN_NUM];
+ u32 b_data[MDP_HISTOGRAM_BIN_NUM];
+ u32 extra[2];
};
struct mdp3_vsync_notification {
@@ -229,8 +237,10 @@
int available;
spinlock_t dma_lock;
+ spinlock_t histo_lock;
struct completion vsync_comp;
struct completion dma_comp;
+ struct completion histo_comp;
struct mdp3_vsync_notification vsync_client;
struct mdp3_dma_output_config output_config;
@@ -238,7 +248,10 @@
struct mdp3_dma_cursor cursor;
struct mdp3_dma_color_correct_config ccs_config;
+ struct mdp3_dma_lut_config lut_config;
struct mdp3_dma_histogram_config histogram_config;
+ int histo_state;
+ struct mdp3_dma_histogram_data histo_data;
int (*start)(struct mdp3_dma *dma, struct mdp3_intf *intf);
@@ -249,27 +262,22 @@
int (*config_ccs)(struct mdp3_dma *dma,
struct mdp3_dma_color_correct_config *config,
- struct mdp3_dma_ccs *ccs,
+ struct mdp3_dma_ccs *ccs);
+
+ int (*config_lut)(struct mdp3_dma *dma,
+ struct mdp3_dma_lut_config *config,
struct mdp3_dma_lut *lut);
int (*update)(struct mdp3_dma *dma, void *buf, struct mdp3_intf *intf);
int (*update_cursor)(struct mdp3_dma *dma, int x, int y);
- int (*get_histo)(struct mdp3_dma *dma,
- struct mdp3_dma_histogram_data *data);
+ int (*get_histo)(struct mdp3_dma *dma);
int (*config_histo)(struct mdp3_dma *dma,
struct mdp3_dma_histogram_config *histo_config);
- int (*histo_op)(struct mdp3_dma *dma,
- u32 op);
-
- int (*histo_intr_status)(struct mdp3_dma *dma, int *status);
-
- int (*histo_intr_enable)(struct mdp3_dma *dma, u32 mask);
-
- int (*histo_intr_clear)(struct mdp3_dma *dma, u32 mask);
+ int (*histo_op)(struct mdp3_dma *dma, u32 op);
void (*vsync_enable)(struct mdp3_dma *dma,
struct mdp3_vsync_notification *vsync_client);
diff --git a/drivers/video/msm/mdss/mdp3_hwio.h b/drivers/video/msm/mdss/mdp3_hwio.h
index f10206f..4afa37c 100644
--- a/drivers/video/msm/mdss/mdp3_hwio.h
+++ b/drivers/video/msm/mdss/mdp3_hwio.h
@@ -60,6 +60,9 @@
#define MDP3_REG_EBI2_LCD0 0x003c
#define MDP3_REG_EBI2_LCD0_YSTRIDE 0x0050
+/*clock control*/
+#define MDP3_REG_CGC_EN 0x0100
+
/*DMA_P*/
#define MDP3_REG_DMA_P_CONFIG 0x90000
#define MDP3_REG_DMA_P_SIZE 0x90004
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h
index 741c7a7..4779d20 100644
--- a/drivers/video/msm/mdss/mdss_mdp_hwio.h
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h
@@ -340,6 +340,7 @@
#define MDSS_MDP_REG_WB_DST_MATRIX_ROW1 0x034
#define MDSS_MDP_REG_WB_DST_MATRIX_ROW2 0x038
#define MDSS_MDP_REG_WB_DST_MATRIX_ROW3 0x03C
+#define MDSS_MDP_REG_WB_DST_WRITE_CONFIG 0x048
#define MDSS_MDP_REG_WB_ROTATION_DNSCALER 0x050
#define MDSS_MDP_REG_WB_N16_INIT_PHASE_X_C03 0x060
#define MDSS_MDP_REG_WB_N16_INIT_PHASE_X_C12 0x064
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
index 503d8e2..b7fe1bd 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
@@ -205,6 +205,7 @@
mdp_wb_write(ctx, MDSS_MDP_REG_WB_DST_YSTRIDE0, ystride0);
mdp_wb_write(ctx, MDSS_MDP_REG_WB_DST_YSTRIDE1, ystride1);
mdp_wb_write(ctx, MDSS_MDP_REG_WB_OUT_SIZE, outsize);
+ mdp_wb_write(ctx, MDSS_MDP_REG_WB_DST_WRITE_CONFIG, 0x58);
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 43de734..8b10900 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -833,9 +833,14 @@
static int mdss_mdp_overlay_unset(struct msm_fb_data_type *mfd, int ndx)
{
int ret = 0;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ struct mdss_overlay_private *mdp5_data;
- if (!mfd || !mdp5_data->ctl)
+ if (!mfd)
+ return -ENODEV;
+
+ mdp5_data = mfd_to_mdp5_data(mfd);
+
+ if (!mdp5_data || !mdp5_data->ctl)
return -ENODEV;
ret = mutex_lock_interruptible(&mdp5_data->ov_lock);
@@ -1158,14 +1163,18 @@
struct mdss_mdp_data data;
struct mdss_mdp_pipe *pipe;
struct fb_info *fbi;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ struct mdss_overlay_private *mdp5_data;
u32 offset;
int bpp, ret;
- if (!mfd || !mdp5_data->ctl)
+ if (!mfd)
return;
fbi = mfd->fbi;
+ mdp5_data = mfd_to_mdp5_data(mfd);
+
+ if (!mdp5_data || !mdp5_data->ctl)
+ return;
if (!fbi->fix.smem_start || fbi->fix.smem_len == 0 ||
mdp5_data->borderfill_enable) {
@@ -1848,14 +1857,17 @@
static int mdss_mdp_overlay_on(struct msm_fb_data_type *mfd)
{
int rc;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
-
+ struct mdss_overlay_private *mdp5_data;
if (!mfd)
return -ENODEV;
if (mfd->key != MFD_KEY)
return -EINVAL;
+ mdp5_data = mfd_to_mdp5_data(mfd);
+ if (!mdp5_data)
+ return -EINVAL;
+
if (!mdp5_data->ctl) {
struct mdss_mdp_ctl *ctl;
struct mdss_panel_data *pdata;
@@ -1910,15 +1922,16 @@
static int mdss_mdp_overlay_off(struct msm_fb_data_type *mfd)
{
int rc;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
-
+ struct mdss_overlay_private *mdp5_data;
if (!mfd)
return -ENODEV;
if (mfd->key != MFD_KEY)
return -EINVAL;
- if (!mdp5_data->ctl) {
+ mdp5_data = mfd_to_mdp5_data(mfd);
+
+ if (!mdp5_data || !mdp5_data->ctl) {
pr_err("ctl not initialized\n");
return -ENODEV;
}
@@ -1926,6 +1939,7 @@
if (!mdp5_data->ctl->power_on)
return 0;
+ mdss_mdp_overlay_free_fb_pipe(mfd);
if (!mfd->ref_cnt) {
mdss_mdp_overlay_release_all(mfd);
} else {
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 5f52690..03efb50 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -5,6 +5,7 @@
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/of.h>
+#include <linux/pinctrl/pinctrl.h>
#ifdef CONFIG_GPIOLIB
@@ -47,6 +48,26 @@
struct module;
struct device_node;
+#ifdef CONFIG_PINCTRL
+/**
+ * struct gpio_pin_range - pin range controlled by a gpio chip
+ * @head: list for maintaining set of pin ranges, used internally
+ * @pctldev: pinctrl device which handles corresponding pins
+ * @range: actual range of pins controlled by a gpio controller
+ */
+
+struct gpio_pin_range {
+ struct list_head node;
+ struct pinctrl_dev *pctldev;
+ struct pinctrl_gpio_range range;
+};
+
+int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+ unsigned int pin_base, unsigned int npins);
+void gpiochip_remove_pin_ranges(struct gpio_chip *chip);
+
+#endif
+
/**
* struct gpio_chip - abstract a GPIO controller
* @label: for diagnostics
@@ -132,6 +153,15 @@
int (*of_xlate)(struct gpio_chip *gc,
const struct of_phandle_args *gpiospec, u32 *flags);
#endif
+#ifdef CONFIG_PINCTRL
+ /*
+ * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
+ * describe the actual pin range which they serve in an SoC. This
+ * information would be used by pinctrl subsystem to configure
+ * corresponding pins for gpio usage.
+ */
+ struct list_head pin_ranges;
+#endif
};
extern const char *gpiochip_is_requested(struct gpio_chip *chip,
diff --git a/include/linux/device.h b/include/linux/device.h
index 810337c..35862c3 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -21,6 +21,7 @@
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/mutex.h>
+#include <linux/pinctrl/devinfo.h>
#include <linux/pm.h>
#include <linux/atomic.h>
#include <asm/device.h>
@@ -595,6 +596,8 @@
* @pm_domain: Provide callbacks that are executed during system suspend,
* hibernation, system resume and during runtime PM transitions
* along with subsystem-level and driver-level callbacks.
+ * @pins: For device pin management.
+ * See Documentation/pinctrl.txt for details.
* @numa_node: NUMA node this device is close to.
* @dma_mask: Dma mask (if dma'ble device).
* @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all
@@ -646,6 +649,10 @@
struct dev_pm_info power;
struct dev_pm_domain *pm_domain;
+#ifdef CONFIG_PINCTRL
+ struct dev_pin_info *pins;
+#endif
+
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index 6155ecf..eda50bd 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -172,6 +172,21 @@
return -EINVAL;
}
-#endif
+#ifdef CONFIG_PINCTRL
+
+static inline int
+gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+ unsigned int pin_base, unsigned int npins)
+{
+}
+
+static inline void
+gpiochip_remove_pin_ranges(struct gpio_chip *chip)
+{
+}
+
+#endif /* CONFIG_PINCTRL */
+
+#endif /* ! CONFIG_GENERIC_GPIO */
#endif /* __LINUX_GPIO_H */
diff --git a/include/linux/input/lis3dh.h b/include/linux/input/lis3dh.h
index f081b06..7c0172f 100644
--- a/include/linux/input/lis3dh.h
+++ b/include/linux/input/lis3dh.h
@@ -37,7 +37,7 @@
#define LIS3DH_ACC_I2C_SAD_L ((LIS3DH_ACC_I2C_SADROOT<<1)|SAD0L)
#define LIS3DH_ACC_I2C_SAD_H ((LIS3DH_ACC_I2C_SADROOT<<1)|SAD0H)
#define LIS3DH_ACC_DEV_NAME "lis3dh_acc"
-
+#define ACCEL_INPUT_DEV_NAME "accelerometer"
/************************************************/
/* Accelerometer defines section */
diff --git a/include/linux/msm_ipa.h b/include/linux/msm_ipa.h
index 5151654..b2229d3 100644
--- a/include/linux/msm_ipa.h
+++ b/include/linux/msm_ipa.h
@@ -87,6 +87,7 @@
#define IPA_FLT_NEXT_HDR (1ul << 13)
#define IPA_FLT_META_DATA (1ul << 14)
#define IPA_FLT_FRAGMENT (1ul << 15)
+#define IPA_FLT_TOS_MASKED (1ul << 16)
/**
* enum ipa_client_type - names for the various IPA "clients"
@@ -243,6 +244,8 @@
uint16_t dst_port_hi;
uint8_t type;
uint8_t code;
+ uint8_t tos_value;
+ uint8_t tos_mask;
uint32_t spi;
uint16_t src_port;
uint16_t dst_port;
diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h
index 191e726..d281645 100644
--- a/include/linux/pinctrl/consumer.h
+++ b/include/linux/pinctrl/consumer.h
@@ -20,6 +20,7 @@
/* This struct is private to the core and should be regarded as a cookie */
struct pinctrl;
struct pinctrl_state;
+struct device;
#ifdef CONFIG_PINCTRL
@@ -36,6 +37,9 @@
const char *name);
extern int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s);
+extern struct pinctrl * __must_check devm_pinctrl_get(struct device *dev);
+extern void devm_pinctrl_put(struct pinctrl *p);
+
#else /* !CONFIG_PINCTRL */
static inline int pinctrl_request_gpio(unsigned gpio)
@@ -79,6 +83,15 @@
return 0;
}
+static inline struct pinctrl * __must_check devm_pinctrl_get(struct device *dev)
+{
+ return NULL;
+}
+
+static inline void devm_pinctrl_put(struct pinctrl *p)
+{
+}
+
#endif /* CONFIG_PINCTRL */
static inline struct pinctrl * __must_check pinctrl_get_select(
@@ -113,6 +126,38 @@
return pinctrl_get_select(dev, PINCTRL_STATE_DEFAULT);
}
+static inline struct pinctrl * __must_check devm_pinctrl_get_select(
+ struct device *dev, const char *name)
+{
+ struct pinctrl *p;
+ struct pinctrl_state *s;
+ int ret;
+
+ p = devm_pinctrl_get(dev);
+ if (IS_ERR(p))
+ return p;
+
+ s = pinctrl_lookup_state(p, name);
+ if (IS_ERR(s)) {
+ devm_pinctrl_put(p);
+ return ERR_CAST(s);
+ }
+
+ ret = pinctrl_select_state(p, s);
+ if (ret < 0) {
+ devm_pinctrl_put(p);
+ return ERR_PTR(ret);
+ }
+
+ return p;
+}
+
+static inline struct pinctrl * __must_check devm_pinctrl_get_select_default(
+ struct device *dev)
+{
+ return devm_pinctrl_get_select(dev, PINCTRL_STATE_DEFAULT);
+}
+
#ifdef CONFIG_PINCONF
extern int pin_config_get(const char *dev_name, const char *name,
diff --git a/include/linux/pinctrl/devinfo.h b/include/linux/pinctrl/devinfo.h
new file mode 100644
index 0000000..6e5f8a9
--- /dev/null
+++ b/include/linux/pinctrl/devinfo.h
@@ -0,0 +1,45 @@
+/*
+ * Per-device information from the pin control system.
+ * This is the stuff that get included into the device
+ * core.
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Written on behalf of Linaro for ST-Ericsson
+ * This interface is used in the core to keep track of pins.
+ *
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef PINCTRL_DEVINFO_H
+#define PINCTRL_DEVINFO_H
+
+#ifdef CONFIG_PINCTRL
+
+/* The device core acts as a consumer toward pinctrl */
+#include <linux/pinctrl/consumer.h>
+
+/**
+ * struct dev_pin_info - pin state container for devices
+ * @p: pinctrl handle for the containing device
+ * @default_state: the default state for the handle, if found
+ */
+struct dev_pin_info {
+ struct pinctrl *p;
+ struct pinctrl_state *default_state;
+};
+
+extern int pinctrl_bind_pins(struct device *dev);
+
+#else
+
+/* Stubs if we're not using pinctrl */
+
+static inline int pinctrl_bind_pins(struct device *dev)
+{
+ return 0;
+}
+
+#endif /* CONFIG_PINCTRL */
+#endif /* PINCTRL_DEVINFO_H */
diff --git a/include/linux/pinctrl/machine.h b/include/linux/pinctrl/machine.h
index e4d1de7..7d22ab0 100644
--- a/include/linux/pinctrl/machine.h
+++ b/include/linux/pinctrl/machine.h
@@ -154,7 +154,7 @@
extern int pinctrl_register_mappings(struct pinctrl_map const *map,
unsigned num_maps);
-
+extern void pinctrl_provide_dummies(void);
#else
static inline int pinctrl_register_mappings(struct pinctrl_map const *map,
@@ -163,5 +163,8 @@
return 0;
}
-#endif /* !CONFIG_PINMUX */
+static inline void pinctrl_provide_dummies(void)
+{
+}
+#endif /* !CONFIG_PINCTRL */
#endif
diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h
index 4f0abb9..72474e1 100644
--- a/include/linux/pinctrl/pinconf-generic.h
+++ b/include/linux/pinctrl/pinconf-generic.h
@@ -46,11 +46,15 @@
* @PIN_CONFIG_DRIVE_OPEN_SOURCE: the pin will be driven with open source
* (open emitter). Sending this config will enabale open drain mode, the
* argument is ignored.
+ * @PIN_CONFIG_DRIVE_STRENGTH: the pin will output the current passed as
+ * argument. The argument is in mA.
+ * @PIN_CONFIG_INPUT_SCHMITT_ENABLE: control schmitt-trigger mode on the pin.
+ * If the argument != 0, schmitt-trigger mode is enabled. If it's 0,
+ * schmitt-trigger mode is disabled.
* @PIN_CONFIG_INPUT_SCHMITT: this will configure an input pin to run in
* schmitt-trigger mode. If the schmitt-trigger has adjustable hysteresis,
* the threshold value is given on a custom format as argument when
- * setting pins to this mode. The argument zero turns the schmitt trigger
- * off.
+ * setting pins to this mode.
* @PIN_CONFIG_INPUT_DEBOUNCE: this will configure the pin to debounce mode,
* which means it will wait for signals to settle when reading inputs. The
* argument gives the debounce time on a custom format. Setting the
@@ -58,10 +62,15 @@
* @PIN_CONFIG_POWER_SOURCE: if the pin can select between different power
* supplies, the argument to this parameter (on a custom format) tells
* the driver which alternative power source to use.
+ * @PIN_CONFIG_SLEW_RATE: if the pin can select slew rate, the argument to
+ * this parameter (on a custom format) tells the driver which alternative
+ * slew rate to use.
* @PIN_CONFIG_LOW_POWER_MODE: this will configure the pin for low power
* operation, if several modes of operation are supported these can be
* passed in the argument on a custom form, else just use argument 1
* to indicate low power mode, argument 0 turns low power mode off.
+ * @PIN_CONFIG_OUTPUT: this will configure the pin in output, use argument
+ * 1 to indicate high level, argument 0 to indicate low level.
* @PIN_CONFIG_END: this is the last enumerator for pin configurations, if
* you need to pass in custom configurations to the pin controller, use
* PIN_CONFIG_END+1 as the base offset.
@@ -74,10 +83,14 @@
PIN_CONFIG_DRIVE_PUSH_PULL,
PIN_CONFIG_DRIVE_OPEN_DRAIN,
PIN_CONFIG_DRIVE_OPEN_SOURCE,
+ PIN_CONFIG_DRIVE_STRENGTH,
+ PIN_CONFIG_INPUT_SCHMITT_ENABLE,
PIN_CONFIG_INPUT_SCHMITT,
PIN_CONFIG_INPUT_DEBOUNCE,
PIN_CONFIG_POWER_SOURCE,
+ PIN_CONFIG_SLEW_RATE,
PIN_CONFIG_LOW_POWER_MODE,
+ PIN_CONFIG_OUTPUT,
PIN_CONFIG_END = 0x7FFF,
};
diff --git a/include/linux/pinctrl/pinconf.h b/include/linux/pinctrl/pinconf.h
index ec431f0..e7a7201 100644
--- a/include/linux/pinctrl/pinconf.h
+++ b/include/linux/pinctrl/pinconf.h
@@ -25,7 +25,6 @@
* @pin_config_get: get the config of a certain pin, if the requested config
* is not available on this controller this should return -ENOTSUPP
* and if it is available but disabled it should return -EINVAL
- * @pin_config_get: get the config of a certain pin
* @pin_config_set: configure an individual pin
* @pin_config_group_get: get configurations for an entire pin group
* @pin_config_group_set: configure all pins in a group
@@ -33,6 +32,8 @@
* per-device info for a certain pin in debugfs
* @pin_config_group_dbg_show: optional debugfs display hook that will provide
* per-device info for a certain group in debugfs
+ * @pin_config_config_dbg_show: optional debugfs display hook that will decode
+ * and display a driver's pin configuration parameter
*/
struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
@@ -56,6 +57,9 @@
void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev,
struct seq_file *s,
unsigned selector);
+ void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev,
+ struct seq_file *s,
+ unsigned long config);
};
#endif
diff --git a/include/linux/pinctrl/pinctrl-state.h b/include/linux/pinctrl/pinctrl-state.h
index 3920e28..b5919f8 100644
--- a/include/linux/pinctrl/pinctrl-state.h
+++ b/include/linux/pinctrl/pinctrl-state.h
@@ -2,5 +2,23 @@
* Standard pin control state definitions
*/
+/**
+ * @PINCTRL_STATE_DEFAULT: the state the pinctrl handle shall be put
+ * into as default, usually this means the pins are up and ready to
+ * be used by the device driver. This state is commonly used by
+ * hogs to configure muxing and pins at boot, and also as a state
+ * to go into when returning from sleep and idle in
+ * .pm_runtime_resume() or ordinary .resume() for example.
+ * @PINCTRL_STATE_IDLE: the state the pinctrl handle shall be put into
+ * when the pins are idle. This is a state where the system is relaxed
+ * but not fully sleeping - some power may be on but clocks gated for
+ * example. Could typically be set from a pm_runtime_suspend() or
+ * pm_runtime_idle() operation.
+ * @PINCTRL_STATE_SLEEP: the state the pinctrl handle shall be put into
+ * when the pins are sleeping. This is a state where the system is in
+ * its lowest sleep state. Could typically be set from an
+ * ordinary .suspend() function.
+ */
#define PINCTRL_STATE_DEFAULT "default"
#define PINCTRL_STATE_IDLE "idle"
+#define PINCTRL_STATE_SLEEP "sleep"
diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h
index 4e9f078..d19ad40 100644
--- a/include/linux/pinctrl/pinctrl.h
+++ b/include/linux/pinctrl/pinctrl.h
@@ -21,9 +21,11 @@
struct device;
struct pinctrl_dev;
+struct pinctrl_map;
struct pinmux_ops;
struct pinconf_ops;
struct gpio_chip;
+struct device_node;
/**
* struct pinctrl_pin_desc - boards/machines provide information on their
@@ -64,17 +66,24 @@
/**
* struct pinctrl_ops - global pin control operations, to be implemented by
* pin controller drivers.
- * @list_groups: list the number of selectable named groups available
- * in this pinmux driver, the core will begin on 0 and call this
- * repeatedly as long as it returns >= 0 to enumerate the groups
+ * @get_groups_count: Returns the count of total number of groups registered.
* @get_group_name: return the group name of the pin group
* @get_group_pins: return an array of pins corresponding to a certain
* group selector @pins, and the size of the array in @num_pins
* @pin_dbg_show: optional debugfs display hook that will provide per-device
* info for a certain pin in debugfs
+ * @dt_node_to_map: parse a device tree "pin configuration node", and create
+ * mapping table entries for it. These are returned through the @map and
+ * @num_maps output parameters. This function is optional, and may be
+ * omitted for pinctrl drivers that do not support device tree.
+ * @dt_free_map: free mapping table entries created via @dt_node_to_map. The
+ * top-level @map pointer must be freed, along with any dynamically
+ * allocated members of the mapping table entries themselves. This
+ * function is optional, and may be omitted for pinctrl drivers that do
+ * not support device tree.
*/
struct pinctrl_ops {
- int (*list_groups) (struct pinctrl_dev *pctldev, unsigned selector);
+ int (*get_groups_count) (struct pinctrl_dev *pctldev);
const char *(*get_group_name) (struct pinctrl_dev *pctldev,
unsigned selector);
int (*get_group_pins) (struct pinctrl_dev *pctldev,
@@ -83,6 +92,11 @@
unsigned *num_pins);
void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,
unsigned offset);
+ int (*dt_node_to_map) (struct pinctrl_dev *pctldev,
+ struct device_node *np_config,
+ struct pinctrl_map **map, unsigned *num_maps);
+ void (*dt_free_map) (struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps);
};
/**
@@ -117,9 +131,30 @@
extern bool pin_is_valid(struct pinctrl_dev *pctldev, int pin);
extern void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range);
+extern void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *ranges,
+ unsigned nranges);
extern void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range);
+
+extern struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
+ struct pinctrl_gpio_range *range);
+extern struct pinctrl_gpio_range *
+pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
+ unsigned int pin);
+
+#ifdef CONFIG_OF
+extern struct pinctrl_dev *of_pinctrl_get(struct device_node *np);
+#else
+static inline
+struct pinctrl_dev *of_pinctrl_get(struct device_node *np)
+{
+ return NULL;
+}
+#endif /* CONFIG_OF */
+
extern const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev);
+extern const char *pinctrl_dev_get_devname(struct pinctrl_dev *pctldev);
extern void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev);
#else
diff --git a/include/linux/pinctrl/pinmux.h b/include/linux/pinctrl/pinmux.h
index 47e9237..1818dcb 100644
--- a/include/linux/pinctrl/pinmux.h
+++ b/include/linux/pinctrl/pinmux.h
@@ -23,15 +23,14 @@
/**
* struct pinmux_ops - pinmux operations, to be implemented by pin controller
* drivers that support pinmuxing
- * @request: called by the core to see if a certain pin can be made available
+ * @request: called by the core to see if a certain pin can be made
* available for muxing. This is called by the core to acquire the pins
* before selecting any actual mux setting across a function. The driver
* is allowed to answer "no" by returning a negative error code
* @free: the reverse function of the request() callback, frees a pin after
* being requested
- * @list_functions: list the number of selectable named functions available
- * in this pinmux driver, the core will begin on 0 and call this
- * repeatedly as long as it returns >= 0 to enumerate mux settings
+ * @get_functions_count: returns number of selectable named functions available
+ * in this pinmux driver
* @get_function_name: return the function name of the muxing selector,
* called by the core to figure out which mux setting it shall map a
* certain device to
@@ -62,7 +61,7 @@
struct pinmux_ops {
int (*request) (struct pinctrl_dev *pctldev, unsigned offset);
int (*free) (struct pinctrl_dev *pctldev, unsigned offset);
- int (*list_functions) (struct pinctrl_dev *pctldev, unsigned selector);
+ int (*get_functions_count) (struct pinctrl_dev *pctldev);
const char *(*get_function_name) (struct pinctrl_dev *pctldev,
unsigned selector);
int (*get_function_groups) (struct pinctrl_dev *pctldev,
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 72f5c96..642c4fe 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -131,6 +131,9 @@
/* link reset handling, called from defer_kevent */
int (*link_reset)(struct usbnet *);
+ /*in case if usbnet wrapper wants to override rx_complete()*/
+ void (*rx_complete) (struct urb *);
+
/* fixup rx packet (strip framing) */
int (*rx_fixup)(struct usbnet *dev, struct sk_buff *skb);
@@ -228,5 +231,7 @@
extern void usbnet_set_msglevel(struct net_device *, u32);
extern void usbnet_get_drvinfo(struct net_device *, struct ethtool_drvinfo *);
extern int usbnet_nway_reset(struct net_device *net);
+extern void usbnet_terminate_urbs(struct usbnet *dev);
+extern void rx_complete(struct urb *urb);
#endif /* __LINUX_USB_USBNET_H */
diff --git a/sound/soc/codecs/wcd9306.c b/sound/soc/codecs/wcd9306.c
index 8949e03..09f2a51 100644
--- a/sound/soc/codecs/wcd9306.c
+++ b/sound/soc/codecs/wcd9306.c
@@ -73,6 +73,9 @@
#define TAPAN_SLIM_IRQ_OVERFLOW (1 << 0)
#define TAPAN_SLIM_IRQ_UNDERFLOW (1 << 1)
#define TAPAN_SLIM_IRQ_PORT_CLOSED (1 << 2)
+
+#define TAPAN_IRQ_MBHC_JACK_SWITCH 21
+
enum {
AIF1_PB = 0,
AIF1_CAP,
@@ -4450,6 +4453,72 @@
wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, tapan);
}
+
+static void tapan_enable_mux_bias_block(struct snd_soc_codec *codec)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x00);
+}
+
+static void tapan_put_cfilt_fast_mode(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc *mbhc)
+{
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x30, 0x30);
+}
+
+static void tapan_codec_specific_cal_setup(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc *mbhc)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x0C, 0x04);
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0xE0, 0xE0);
+}
+
+static int tapan_get_jack_detect_irq(struct snd_soc_codec *codec)
+{
+ return TAPAN_IRQ_MBHC_JACK_SWITCH;
+}
+
+static struct wcd9xxx_cfilt_mode tapan_codec_switch_cfilt_mode(
+ struct wcd9xxx_mbhc *mbhc,
+ bool fast)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct wcd9xxx_cfilt_mode cfilt_mode;
+
+ if (fast)
+ cfilt_mode.reg_mode_val = WCD9XXX_CFILT_EXT_PRCHG_EN;
+ else
+ cfilt_mode.reg_mode_val = WCD9XXX_CFILT_EXT_PRCHG_DSBL;
+
+ cfilt_mode.cur_mode_val =
+ snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x30;
+ return cfilt_mode;
+}
+
+static void tapan_select_cfilt(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc *mbhc)
+{
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60, 0x00);
+}
+
+static void tapan_free_irq(struct wcd9xxx_mbhc *mbhc)
+{
+ void *cdata = mbhc->codec->control_data;
+ wcd9xxx_free_irq(cdata, WCD9306_IRQ_MBHC_JACK_SWITCH, mbhc);
+}
+
+static const struct wcd9xxx_mbhc_cb mbhc_cb = {
+ .enable_mux_bias_block = tapan_enable_mux_bias_block,
+ .cfilt_fast_mode = tapan_put_cfilt_fast_mode,
+ .codec_specific_cal = tapan_codec_specific_cal_setup,
+ .jack_detect_irq = tapan_get_jack_detect_irq,
+ .switch_cfilt_mode = tapan_codec_switch_cfilt_mode,
+ .select_cfilt = tapan_select_cfilt,
+ .free_irq = tapan_free_irq,
+};
+
int tapan_hs_detect(struct snd_soc_codec *codec,
struct wcd9xxx_mbhc_config *mbhc_cfg)
{
@@ -4505,8 +4574,7 @@
rco_clk_rate = TAPAN_MCLK_CLK_9P6MHZ;
ret = wcd9xxx_mbhc_init(&tapan->mbhc, &tapan->resmgr, codec, NULL,
- WCD9XXX_MBHC_VERSION_TAPAN,
- rco_clk_rate);
+ &mbhc_cb, rco_clk_rate);
if (ret)
pr_err("%s: mbhc init failed %d\n", __func__, ret);
else
@@ -4578,8 +4646,8 @@
rco_clk_rate = TAPAN_MCLK_CLK_9P6MHZ;
ret = wcd9xxx_mbhc_init(&tapan->mbhc, &tapan->resmgr, codec, NULL,
- WCD9XXX_MBHC_VERSION_TAPAN,
- rco_clk_rate);
+ &mbhc_cb, rco_clk_rate);
+
if (ret) {
pr_err("%s: mbhc init failed %d\n", __func__, ret);
return ret;
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 49894ca..9d52592 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -6223,8 +6223,7 @@
ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
taiko_enable_mbhc_micbias,
- WCD9XXX_MBHC_VERSION_TAIKO,
- rco_clk_rate);
+ NULL, rco_clk_rate);
if (ret) {
pr_err("%s: mbhc init failed %d\n", __func__, ret);
} else {
@@ -6404,8 +6403,7 @@
/* init and start mbhc */
ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
taiko_enable_mbhc_micbias,
- WCD9XXX_MBHC_VERSION_TAIKO,
- rco_clk_rate);
+ NULL, rco_clk_rate);
if (ret) {
pr_err("%s: mbhc init failed %d\n", __func__, ret);
goto err_init;
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index 618070b..cd11703 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -94,6 +94,8 @@
#define WCD9XXX_USLEEP_RANGE_MARGIN_US 1000
+#define WCD9XXX_IRQ_MBHC_JACK_SWITCH_DEFAULT 28
+
static bool detect_use_vddio_switch = true;
struct wcd9xxx_mbhc_detect {
@@ -150,64 +152,6 @@
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
}
-static int wcd9xxx_enable_mux_bias_block(struct snd_soc_codec *codec,
- struct wcd9xxx_mbhc *mbhc)
-{
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
- snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
- 0x80, 0x80);
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
- 0x80, 0x00);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int wcd9xxx_put_cfilt_fast_mode(struct snd_soc_codec *codec,
- struct wcd9xxx_mbhc *mbhc)
-{
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
- 0x70, 0x00);
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
- 0x70, 0x70);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int wcd9xxx_codec_specific_cal_setup(struct snd_soc_codec *codec,
- struct wcd9xxx_mbhc *mbhc)
-{
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
- snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
- 0x04, 0x04);
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
- 0x0C, 0x04);
- snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0xE0, 0xE0);
- /* Make sure the calibration is ON */
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_val,
- 0x02, 0x02);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
/* called under codec_resource_lock acquisition */
static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
{
@@ -229,20 +173,18 @@
{
struct snd_soc_codec *codec = mbhc->codec;
int mbhc_state = mbhc->mbhc_state;
- int ret;
pr_debug("%s: enter\n", __func__);
if (!mbhc->polling_active) {
pr_debug("Polling is not active, do not start polling\n");
return;
}
-
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret) {
- pr_err("%s: Error returned, ret: %d\n", __func__, ret);
- return;
- }
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
if (!mbhc->no_mic_headset_override &&
mbhc_state == MBHC_STATE_POTENTIAL) {
@@ -481,14 +423,18 @@
(mbhc->mbhc_data.v_brl >> 8) & 0xFF);
}
-static int wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
+static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
bool fast)
{
struct snd_soc_codec *codec = mbhc->codec;
+ struct wcd9xxx_cfilt_mode cfilt_mode;
u8 reg_mode_val, cur_mode_val;
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->switch_cfilt_mode) {
+ cfilt_mode = mbhc->mbhc_cb->switch_cfilt_mode(mbhc, fast);
+ reg_mode_val = cfilt_mode.reg_mode_val;
+ cur_mode_val = cfilt_mode.cur_mode_val;
+ } else {
if (fast)
reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
else
@@ -496,50 +442,21 @@
cur_mode_val =
snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
-
- if (cur_mode_val != reg_mode_val) {
- if (mbhc->polling_active)
- wcd9xxx_pause_hs_polling(mbhc);
- snd_soc_update_bits(codec,
- mbhc->mbhc_bias_regs.cfilt_ctl,
- 0x40, reg_mode_val);
- if (mbhc->polling_active)
- wcd9xxx_start_hs_polling(mbhc);
- pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
- cur_mode_val, reg_mode_val);
- } else {
- pr_debug("%s: CFILT Value is already %x\n",
- __func__, cur_mode_val);
- }
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- if (fast)
- reg_mode_val = WCD9XXX_CFILT_EXT_PRCHG_EN;
- else
- reg_mode_val = WCD9XXX_CFILT_EXT_PRCHG_DSBL;
-
- cur_mode_val =
- snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x70;
-
- if (cur_mode_val != reg_mode_val) {
- if (mbhc->polling_active)
- wcd9xxx_pause_hs_polling(mbhc);
- snd_soc_update_bits(codec,
- mbhc->mbhc_bias_regs.cfilt_ctl,
- 0x70, reg_mode_val);
- if (mbhc->polling_active)
- wcd9xxx_start_hs_polling(mbhc);
- pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
- cur_mode_val, reg_mode_val);
- } else {
- pr_debug("%s: CFILT Value is already %x\n",
- __func__, cur_mode_val);
- }
- break;
- default:
- return -EINVAL;
}
- return 0;
+ if (cur_mode_val != reg_mode_val) {
+ if (mbhc->polling_active)
+ wcd9xxx_pause_hs_polling(mbhc);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x40, reg_mode_val);
+ if (mbhc->polling_active)
+ wcd9xxx_start_hs_polling(mbhc);
+ pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
+ cur_mode_val, reg_mode_val);
+ } else {
+ pr_debug("%s: CFILT Value is already %x\n",
+ __func__, cur_mode_val);
+ }
}
static void wcd9xxx_jack_report(struct wcd9xxx_mbhc *mbhc,
@@ -1043,7 +960,6 @@
struct snd_soc_codec *codec = mbhc->codec;
short bias_value;
u8 cfilt_mode;
- int ret;
WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
@@ -1066,14 +982,19 @@
/* Make sure CFILT is in fast mode, save current mode */
cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
- ret = wcd9xxx_put_cfilt_fast_mode(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
+ mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
+ else
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x70, 0x00);
+
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
@@ -1094,10 +1015,6 @@
snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
return bias_value;
-
-gen_err:
- pr_err("%s: Error returned, ret: %d\n", __func__, ret);
- return ret;
}
static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
@@ -2927,7 +2844,6 @@
{
u8 cfilt_mode;
u16 reg0, reg1;
- int ret;
struct snd_soc_codec *codec = mbhc->codec;
pr_debug("%s: enter\n", __func__);
@@ -2944,21 +2860,30 @@
* Need to restore defaults once calculation is done.
*/
cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
- ret = wcd9xxx_put_cfilt_fast_mode(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
+ mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
+ else
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x40, 0x00);
+
/*
* Micbias, CFILT, LDOH, MBHC MUX mode settings
* to perform ADC calibration
*/
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->select_cfilt)
+ mbhc->mbhc_cb->select_cfilt(codec, mbhc);
+ else
snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
mbhc->mbhc_cfg->micbias << 5);
snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
- ret = wcd9xxx_codec_specific_cal_setup(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_specific_cal)
+ mbhc->mbhc_cb->codec_specific_cal(codec, mbhc);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x04, 0x04);
+
/* Pull down micbias to ground */
reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 1, 1);
@@ -2967,9 +2892,11 @@
snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0);
/* Connect the MUX to micbias */
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
/*
* Hardware that has external cap can delay mic bias ramping down up
* to 50ms.
@@ -2991,9 +2918,11 @@
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
/*
* Hardware that has external cap can delay mic bias ramping down up
* to 50ms.
@@ -3008,9 +2937,11 @@
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
/*
* Hardware that has external cap can delay mic bias ramping down up
* to 50ms.
@@ -3024,19 +2955,17 @@
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
usleep_range(100, 100);
wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
wcd9xxx_turn_onoff_rel_detection(codec, true);
pr_debug("%s: leave\n", __func__);
- return;
-
-gen_err:
- pr_err("%s: Error returned, ret: %d\n", __func__, ret);
}
static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
@@ -3095,8 +3024,14 @@
{
int ret = 0;
void *core = mbhc->resmgr->core;
+ struct snd_soc_codec *codec = mbhc->codec;
int jack_irq;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->jack_detect_irq)
+ jack_irq = mbhc->mbhc_cb->jack_detect_irq(codec);
+ else
+ jack_irq = WCD9XXX_IRQ_MBHC_JACK_SWITCH_DEFAULT;
+
if (mbhc->mbhc_cfg->gpio) {
ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
wcd9xxx_mech_plug_detect_irq,
@@ -3118,17 +3053,6 @@
snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
1 << 1, 1 << 1);
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
- jack_irq = WCD9320_IRQ_MBHC_JACK_SWITCH;
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- jack_irq = WCD9306_IRQ_MBHC_JACK_SWITCH;
- break;
- default:
- return -EINVAL;
- }
-
ret = wcd9xxx_request_irq(core, jack_irq,
wcd9xxx_mech_plug_detect_irq,
"Jack Detect",
@@ -3357,7 +3281,7 @@
int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
struct wcd9xxx_mbhc_config *mbhc_cfg)
{
- int rc;
+ int rc = 0;
struct snd_soc_codec *codec = mbhc->codec;
pr_debug("%s: enter\n", __func__);
@@ -3381,10 +3305,13 @@
wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
/* Put CFILT in fast mode by default */
- rc = wcd9xxx_put_cfilt_fast_mode(codec, mbhc);
- if (rc)
- pr_err("%s: Error returned, ret: %d\n", __func__, rc);
- else if (!mbhc->mbhc_cfg->read_fw_bin)
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
+ mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
+ else
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x40, WCD9XXX_CFILT_FAST_MODE);
+
+ if (!mbhc->mbhc_cfg->read_fw_bin)
rc = wcd9xxx_init_and_calibrate(mbhc);
else
schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
@@ -3612,7 +3539,7 @@
* Switch CFILT to slow mode if MBHC CFILT is being
* used.
*/
- ret = wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
+ wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
break;
case WCD9XXX_EVENT_POST_CFILT_1_OFF:
case WCD9XXX_EVENT_POST_CFILT_2_OFF:
@@ -3623,7 +3550,7 @@
* Switch CFILT to fast mode if MBHC CFILT is not
* used anymore.
*/
- ret = wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
+ wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
break;
/* System resume */
case WCD9XXX_EVENT_POST_RESUME:
@@ -3656,8 +3583,7 @@
int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
struct snd_soc_codec *codec,
int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
- int version,
- int rco_clk_rate)
+ const struct wcd9xxx_mbhc_cb *mbhc_cb, int rco_clk_rate)
{
int ret;
void *core;
@@ -3681,8 +3607,8 @@
mbhc->resmgr = resmgr;
mbhc->resmgr->mbhc = mbhc;
mbhc->micbias_enable_cb = micbias_enable_cb;
- mbhc->mbhc_version = version;
mbhc->rco_clk_rate = rco_clk_rate;
+ mbhc->mbhc_cb = mbhc_cb;
if (mbhc->headset_jack.jack == NULL) {
ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
@@ -3805,17 +3731,11 @@
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_INSERTION, mbhc);
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
- wcd9xxx_free_irq(cdata, WCD9320_IRQ_MBHC_JACK_SWITCH, mbhc);
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- wcd9xxx_free_irq(cdata, WCD9306_IRQ_MBHC_JACK_SWITCH, mbhc);
- break;
- default:
- pr_err("%s: irq free failed! Invalid MBHC version %d\n",
- __func__, mbhc->mbhc_version);
- }
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->free_irq)
+ mbhc->mbhc_cb->free_irq(mbhc);
+ else
+ wcd9xxx_free_irq(cdata, WCD9320_IRQ_MBHC_JACK_SWITCH,
+ mbhc);
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, mbhc);
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT, mbhc);
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
index 71a62b2..02ecced 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.h
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -16,8 +16,8 @@
#define WCD9XXX_CFILT_FAST_MODE 0x00
#define WCD9XXX_CFILT_SLOW_MODE 0x40
-#define WCD9XXX_CFILT_EXT_PRCHG_EN 0x70
-#define WCD9XXX_CFILT_EXT_PRCHG_DSBL 0x40
+#define WCD9XXX_CFILT_EXT_PRCHG_EN 0x30
+#define WCD9XXX_CFILT_EXT_PRCHG_DSBL 0x00
struct mbhc_micbias_regs {
u16 cfilt_val;
@@ -61,12 +61,6 @@
s16 v_inval_ins_high;
};
-enum wcd9xxx_mbhc_version {
- WCD9XXX_MBHC_VERSION_UNKNOWN = 0,
- WCD9XXX_MBHC_VERSION_TAIKO,
- WCD9XXX_MBHC_VERSION_TAPAN,
-};
-
enum wcd9xxx_mbhc_plug_type {
PLUG_TYPE_INVALID = -1,
PLUG_TYPE_NONE,
@@ -218,6 +212,23 @@
bool (*swap_gnd_mic) (struct snd_soc_codec *);
};
+struct wcd9xxx_cfilt_mode {
+ u8 reg_mode_val;
+ u8 cur_mode_val;
+};
+
+struct wcd9xxx_mbhc_cb {
+ void (*enable_mux_bias_block) (struct snd_soc_codec *);
+ void (*cfilt_fast_mode) (struct snd_soc_codec *, struct wcd9xxx_mbhc *);
+ void (*codec_specific_cal) (struct snd_soc_codec *,
+ struct wcd9xxx_mbhc *);
+ int (*jack_detect_irq) (struct snd_soc_codec *);
+ struct wcd9xxx_cfilt_mode (*switch_cfilt_mode) (struct wcd9xxx_mbhc *,
+ bool);
+ void (*select_cfilt) (struct snd_soc_codec *, struct wcd9xxx_mbhc *);
+ void (*free_irq) (struct wcd9xxx_mbhc *);
+};
+
struct wcd9xxx_mbhc {
bool polling_active;
/* Delayed work to report long button press */
@@ -225,6 +236,7 @@
int buttons_pressed;
enum wcd9xxx_mbhc_state mbhc_state;
struct wcd9xxx_mbhc_config *mbhc_cfg;
+ const struct wcd9xxx_mbhc_cb *mbhc_cb;
struct mbhc_internal_cal_data mbhc_data;
@@ -279,8 +291,6 @@
bool micbias_enable;
int (*micbias_enable_cb) (struct snd_soc_codec*, bool);
- enum wcd9xxx_mbhc_version mbhc_version;
-
u32 rco_clk_rate;
#ifdef CONFIG_DEBUG_FS
@@ -349,7 +359,7 @@
int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
struct snd_soc_codec *codec,
int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
- int version,
+ const struct wcd9xxx_mbhc_cb *mbhc_cb,
int rco_clk_rate);
void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc);
void *wcd9xxx_mbhc_cal_btn_det_mp(
diff --git a/sound/soc/msm/msm8226.c b/sound/soc/msm/msm8226.c
index 9782b2d..5dfd326 100644
--- a/sound/soc/msm/msm8226.c
+++ b/sound/soc/msm/msm8226.c
@@ -72,7 +72,7 @@
.gpio_irq = 0,
.gpio_level_insert = 0,
.detect_extn_cable = true,
- .micbias_enable_flags = 0,
+ .micbias_enable_flags = 1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET,
.insert_detect = true,
.swap_gnd_mic = NULL,
};