<!ENTITY sub-srggb10 SYSTEM "v4l/pixfmt-srggb10.xml">
<!ENTITY sub-srggb8 SYSTEM "v4l/pixfmt-srggb8.xml">
<!ENTITY sub-y10 SYSTEM "v4l/pixfmt-y10.xml">
+<!ENTITY sub-y12 SYSTEM "v4l/pixfmt-y12.xml">
<!ENTITY sub-pixfmt SYSTEM "v4l/pixfmt.xml">
<!ENTITY sub-cropcap SYSTEM "v4l/vidioc-cropcap.xml">
<!ENTITY sub-dbg-g-register SYSTEM "v4l/vidioc-dbg-g-register.xml">
<varlistentry>
<term><parameter>request</parameter></term>
<listitem>
- <para>MEDIA_IOC_ENUM_LINKS</para>
+ <para>MEDIA_IOC_SETUP_LINK</para>
</listitem>
</varlistentry>
<varlistentry>
--- /dev/null
+<refentry id="V4L2-PIX-FMT-Y12">
+ <refmeta>
+ <refentrytitle>V4L2_PIX_FMT_Y12 ('Y12 ')</refentrytitle>
+ &manvol;
+ </refmeta>
+ <refnamediv>
+ <refname><constant>V4L2_PIX_FMT_Y12</constant></refname>
+ <refpurpose>Grey-scale image</refpurpose>
+ </refnamediv>
+ <refsect1>
+ <title>Description</title>
+
+ <para>This is a grey-scale image with a depth of 12 bits per pixel. Pixels
+are stored in 16-bit words with unused high bits padded with 0. The least
+significant byte is stored at lower memory addresses (little-endian).</para>
+
+ <example>
+ <title><constant>V4L2_PIX_FMT_Y12</constant> 4 × 4
+pixel image</title>
+
+ <formalpara>
+ <title>Byte Order.</title>
+ <para>Each cell is one byte.
+ <informaltable frame="none">
+ <tgroup cols="9" align="center">
+ <colspec align="left" colwidth="2*" />
+ <tbody valign="top">
+ <row>
+ <entry>start + 0:</entry>
+ <entry>Y'<subscript>00low</subscript></entry>
+ <entry>Y'<subscript>00high</subscript></entry>
+ <entry>Y'<subscript>01low</subscript></entry>
+ <entry>Y'<subscript>01high</subscript></entry>
+ <entry>Y'<subscript>02low</subscript></entry>
+ <entry>Y'<subscript>02high</subscript></entry>
+ <entry>Y'<subscript>03low</subscript></entry>
+ <entry>Y'<subscript>03high</subscript></entry>
+ </row>
+ <row>
+ <entry>start + 8:</entry>
+ <entry>Y'<subscript>10low</subscript></entry>
+ <entry>Y'<subscript>10high</subscript></entry>
+ <entry>Y'<subscript>11low</subscript></entry>
+ <entry>Y'<subscript>11high</subscript></entry>
+ <entry>Y'<subscript>12low</subscript></entry>
+ <entry>Y'<subscript>12high</subscript></entry>
+ <entry>Y'<subscript>13low</subscript></entry>
+ <entry>Y'<subscript>13high</subscript></entry>
+ </row>
+ <row>
+ <entry>start + 16:</entry>
+ <entry>Y'<subscript>20low</subscript></entry>
+ <entry>Y'<subscript>20high</subscript></entry>
+ <entry>Y'<subscript>21low</subscript></entry>
+ <entry>Y'<subscript>21high</subscript></entry>
+ <entry>Y'<subscript>22low</subscript></entry>
+ <entry>Y'<subscript>22high</subscript></entry>
+ <entry>Y'<subscript>23low</subscript></entry>
+ <entry>Y'<subscript>23high</subscript></entry>
+ </row>
+ <row>
+ <entry>start + 24:</entry>
+ <entry>Y'<subscript>30low</subscript></entry>
+ <entry>Y'<subscript>30high</subscript></entry>
+ <entry>Y'<subscript>31low</subscript></entry>
+ <entry>Y'<subscript>31high</subscript></entry>
+ <entry>Y'<subscript>32low</subscript></entry>
+ <entry>Y'<subscript>32high</subscript></entry>
+ <entry>Y'<subscript>33low</subscript></entry>
+ <entry>Y'<subscript>33high</subscript></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ </formalpara>
+ </example>
+ </refsect1>
+</refentry>
&sub-packed-yuv;
&sub-grey;
&sub-y10;
+ &sub-y12;
&sub-y16;
&sub-yuyv;
&sub-uyvy;
<entry>b<subscript>1</subscript></entry>
<entry>b<subscript>0</subscript></entry>
</row>
+ <row id="V4L2-MBUS-FMT-SGBRG8-1X8">
+ <entry>V4L2_MBUS_FMT_SGBRG8_1X8</entry>
+ <entry>0x3013</entry>
+ <entry></entry>
+ <entry>-</entry>
+ <entry>-</entry>
+ <entry>-</entry>
+ <entry>-</entry>
+ <entry>g<subscript>7</subscript></entry>
+ <entry>g<subscript>6</subscript></entry>
+ <entry>g<subscript>5</subscript></entry>
+ <entry>g<subscript>4</subscript></entry>
+ <entry>g<subscript>3</subscript></entry>
+ <entry>g<subscript>2</subscript></entry>
+ <entry>g<subscript>1</subscript></entry>
+ <entry>g<subscript>0</subscript></entry>
+ </row>
<row id="V4L2-MBUS-FMT-SGRBG8-1X8">
<entry>V4L2_MBUS_FMT_SGRBG8_1X8</entry>
<entry>0x3002</entry>
<entry>g<subscript>1</subscript></entry>
<entry>g<subscript>0</subscript></entry>
</row>
+ <row id="V4L2-MBUS-FMT-SRGGB8-1X8">
+ <entry>V4L2_MBUS_FMT_SRGGB8_1X8</entry>
+ <entry>0x3014</entry>
+ <entry></entry>
+ <entry>-</entry>
+ <entry>-</entry>
+ <entry>-</entry>
+ <entry>-</entry>
+ <entry>r<subscript>7</subscript></entry>
+ <entry>r<subscript>6</subscript></entry>
+ <entry>r<subscript>5</subscript></entry>
+ <entry>r<subscript>4</subscript></entry>
+ <entry>r<subscript>3</subscript></entry>
+ <entry>r<subscript>2</subscript></entry>
+ <entry>r<subscript>1</subscript></entry>
+ <entry>r<subscript>0</subscript></entry>
+ </row>
<row id="V4L2-MBUS-FMT-SBGGR10-DPCM8-1X8">
<entry>V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8</entry>
<entry>0x300b</entry>
<entry>u<subscript>1</subscript></entry>
<entry>u<subscript>0</subscript></entry>
</row>
+ <row id="V4L2-MBUS-FMT-Y12-1X12">
+ <entry>V4L2_MBUS_FMT_Y12_1X12</entry>
+ <entry>0x2013</entry>
+ <entry></entry>
+ <entry>-</entry>
+ <entry>-</entry>
+ <entry>-</entry>
+ <entry>-</entry>
+ <entry>-</entry>
+ <entry>-</entry>
+ <entry>-</entry>
+ <entry>-</entry>
+ <entry>y<subscript>11</subscript></entry>
+ <entry>y<subscript>10</subscript></entry>
+ <entry>y<subscript>9</subscript></entry>
+ <entry>y<subscript>8</subscript></entry>
+ <entry>y<subscript>7</subscript></entry>
+ <entry>y<subscript>6</subscript></entry>
+ <entry>y<subscript>5</subscript></entry>
+ <entry>y<subscript>4</subscript></entry>
+ <entry>y<subscript>3</subscript></entry>
+ <entry>y<subscript>2</subscript></entry>
+ <entry>y<subscript>1</subscript></entry>
+ <entry>y<subscript>0</subscript></entry>
+ </row>
<row id="V4L2-MBUS-FMT-UYVY8-1X16">
<entry>V4L2_MBUS_FMT_UYVY8_1X16</entry>
<entry>0x200f</entry>
tasks # attach a task(thread) and show list of threads
cgroup.procs # show list of processes
cgroup.event_control # an interface for event_fd()
- memory.usage_in_bytes # show current memory(RSS+Cache) usage.
- memory.memsw.usage_in_bytes # show current memory+Swap usage
+ memory.usage_in_bytes # show current res_counter usage for memory
+ (See 5.5 for details)
+ memory.memsw.usage_in_bytes # show current res_counter usage for memory+Swap
+ (See 5.5 for details)
memory.limit_in_bytes # set/show limit of memory usage
memory.memsw.limit_in_bytes # set/show limit of memory+Swap usage
memory.failcnt # show the number of memory usage hits limits
You can reset failcnt by writing 0 to failcnt file.
# echo 0 > .../memory.failcnt
+5.5 usage_in_bytes
+
+For efficiency, as other kernel components, memory cgroup uses some optimization
+to avoid unnecessary cacheline false sharing. usage_in_bytes is affected by the
+method and doesn't show 'exact' value of memory(and swap) usage, it's an fuzz
+value for efficient access. (Of course, when necessary, it's synchronized.)
+If you want to know more exact memory usage, you should use RSS+CACHE(+SWAP)
+value in memory.stat(see 5.2).
+
6. Hierarchy support
The memory controller supports a deep hierarchy and hierarchical accounting.
----------------------------
-What: Support for lcd_switch and display_get in asus-laptop driver
-When: March 2010
-Why: These two features use non-standard interfaces. There are the
- only features that really need multiple path to guess what's
- the right method name on a specific laptop.
-
- Removing them will allow to remove a lot of code an significantly
- clean the drivers.
-
- This will affect the backlight code which won't be able to know
- if the backlight is on or off. The platform display file will also be
- write only (like the one in eeepc-laptop).
-
- This should'nt affect a lot of user because they usually know
- when their display is on or off.
-
-Who: Corentin Chary <corentin.chary@gmail.com>
-
-----------------------------
-
What: sysfs-class-rfkill state file
When: Feb 2014
Files: net/rfkill/core.c
entering atomic context, using:
int flex_array_prealloc(struct flex_array *array, unsigned int start,
- unsigned int end, gfp_t flags);
+ unsigned int nr_elements, gfp_t flags);
This function will ensure that memory for the elements indexed in the range
-defined by start and end has been allocated. Thereafter, a
+defined by start and nr_elements has been allocated. Thereafter, a
flex_array_put() call on an element in that range is guaranteed not to
block.
Prefix: 'gl523sm'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet:
- * Intel Xeon Processor
- Prefix: - any other - may require 'force_adm1021' parameter
- Addresses scanned: none
- Datasheet: Publicly available at Intel website
* Maxim MAX1617
Prefix: 'max1617'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
ADM1021-clones do faster measurements, but there is really no good reason
for that.
-Xeon support
-------------
-Some Xeon processors have real max1617, adm1021, or compatible chips
-within them, with two temperature sensors.
+Netburst-based Xeon support
+---------------------------
-Other Xeons have chips with only one sensor.
+Some Xeon processors based on the Netburst (early Pentium 4, from 2001 to
+2003) microarchitecture had real MAX1617, ADM1021, or compatible chips
+within them, with two temperature sensors. Other Xeon processors of this
+era (with 400 MHz FSB) had chips with only one temperature sensor.
-If you have a Xeon, and the adm1021 module loads, and both temperatures
-appear valid, then things are good.
+If you have such an old Xeon, and you get two valid temperatures when
+loading the adm1021 module, then things are good.
-If the adm1021 module doesn't load, you should try this:
- modprobe adm1021 force_adm1021=BUS,ADDRESS
- ADDRESS can only be 0x18, 0x1a, 0x29, 0x2b, 0x4c, or 0x4e.
+If nothing happens when loading the adm1021 module, and you are certain
+that your specific Xeon processor model includes compatible sensors, you
+will have to explicitly instantiate the sensor chips from user-space. See
+method 4 in Documentation/i2c/instantiating-devices. Possible slave
+addresses are 0x18, 0x1a, 0x29, 0x2b, 0x4c, or 0x4e. It is likely that
+only temp2 will be correct and temp1 will have to be ignored.
-If you have dual Xeons you may have appear to have two separate
-adm1021-compatible chips, or two single-temperature sensors, at distinct
-addresses.
+Previous generations of the Xeon processor (based on Pentium II/III)
+didn't have these sensors. Next generations of Xeon processors (533 MHz
+FSB and faster) lost them, until the Core-based generation which
+introduced integrated digital thermal sensors. These are supported by
+the coretemp driver.
Addresses scanned: I2C 0x4c and 0x4d
Datasheet: Publicly available at the ON Semiconductor website
http://www.onsemi.com/PowerSolutions/product.do?id=ADT7461
+ * Analog Devices ADT7461A
+ Prefix: 'adt7461a'
+ Addresses scanned: I2C 0x4c and 0x4d
+ Datasheet: Publicly available at the ON Semiconductor website
+ http://www.onsemi.com/PowerSolutions/product.do?id=ADT7461A
+ * ON Semiconductor NCT1008
+ Prefix: 'nct1008'
+ Addresses scanned: I2C 0x4c and 0x4d
+ Datasheet: Publicly available at the ON Semiconductor website
+ http://www.onsemi.com/PowerSolutions/product.do?id=NCT1008
* Maxim MAX6646
Prefix: 'max6646'
Addresses scanned: I2C 0x4d
* ALERT is triggered by open remote sensor.
* SMBus PEC support for Write Byte and Receive Byte transactions.
-ADT7461:
+ADT7461, ADT7461A, NCT1008:
* Extended temperature range (breaks compatibility)
* Lower resolution for remote temperature
Only the local hysteresis can be set from user-space, and the same delta
applies to the remote hysteresis.
-The lm90 driver will not update its values more frequently than every
-other second; reading them more often will do no harm, but will return
-'old' values.
+The lm90 driver will not update its values more frequently than configured with
+the update_interval attribute; reading them more often will do no harm, but will
+return 'old' values.
SMBus Alert Support
-------------------
This driver has basic support for SMBus alert. When an alert is received,
the status register is read and the faulty temperature channel is logged.
-The Analog Devices chips (ADM1032 and ADT7461) do not implement the SMBus
-alert protocol properly so additional care is needed: the ALERT output is
-disabled when an alert is received, and is re-enabled only when the alarm
-is gone. Otherwise the chip would block alerts from other chips in the bus
-as long as the alarm is active.
+The Analog Devices chips (ADM1032, ADT7461 and ADT7461A) and ON
+Semiconductor chips (NCT1008) do not implement the SMBus alert protocol
+properly so additional care is needed: the ALERT output is disabled when
+an alert is received, and is re-enabled only when the alarm is gone.
+Otherwise the chip would block alerts from other chips in the bus as long
+as the alarm is active.
PEC Support
-----------
--- /dev/null
+Kernel driver max16064
+======================
+
+Supported chips:
+ * Maxim MAX16064
+ Prefix: 'max16064'
+ Addresses scanned: -
+ Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX16064.pdf
+
+Author: Guenter Roeck <guenter.roeck@ericsson.com>
+
+
+Description
+-----------
+
+This driver supports hardware montoring for Maxim MAX16064 Quad Power-Supply
+Controller with Active-Voltage Output Control and PMBus Interface.
+
+The driver is a client driver to the core PMBus driver.
+Please see Documentation/hwmon/pmbus for details on PMBus client drivers.
+
+
+Usage Notes
+-----------
+
+This driver does not auto-detect devices. You will have to instantiate the
+devices explicitly. Please see Documentation/i2c/instantiating-devices for
+details.
+
+
+Platform data support
+---------------------
+
+The driver supports standard PMBus driver platform data.
+
+
+Sysfs entries
+-------------
+
+The following attributes are supported. Limits are read-write; all other
+attributes are read-only.
+
+in[1-4]_label "vout[1-4]"
+in[1-4]_input Measured voltage. From READ_VOUT register.
+in[1-4]_min Minumum Voltage. From VOUT_UV_WARN_LIMIT register.
+in[1-4]_max Maximum voltage. From VOUT_OV_WARN_LIMIT register.
+in[1-4]_lcrit Critical minumum Voltage. VOUT_UV_FAULT_LIMIT register.
+in[1-4]_crit Critical maximum voltage. From VOUT_OV_FAULT_LIMIT register.
+in[1-4]_min_alarm Voltage low alarm. From VOLTAGE_UV_WARNING status.
+in[1-4]_max_alarm Voltage high alarm. From VOLTAGE_OV_WARNING status.
+in[1-4]_lcrit_alarm Voltage critical low alarm. From VOLTAGE_UV_FAULT status.
+in[1-4]_crit_alarm Voltage critical high alarm. From VOLTAGE_OV_FAULT status.
+
+temp1_input Measured temperature. From READ_TEMPERATURE_1 register.
+temp1_max Maximum temperature. From OT_WARN_LIMIT register.
+temp1_crit Critical high temperature. From OT_FAULT_LIMIT register.
+temp1_max_alarm Chip temperature high alarm. Set by comparing
+ READ_TEMPERATURE_1 with OT_WARN_LIMIT if TEMP_OT_WARNING
+ status is set.
+temp1_crit_alarm Chip temperature critical high alarm. Set by comparing
+ READ_TEMPERATURE_1 with OT_FAULT_LIMIT if TEMP_OT_FAULT
+ status is set.
--- /dev/null
+Kernel driver max34440
+======================
+
+Supported chips:
+ * Maxim MAX34440
+ Prefixes: 'max34440'
+ Addresses scanned: -
+ Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX34440.pdf
+ * Maxim MAX34441
+ PMBus 5-Channel Power-Supply Manager and Intelligent Fan Controller
+ Prefixes: 'max34441'
+ Addresses scanned: -
+ Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX34441.pdf
+
+Author: Guenter Roeck <guenter.roeck@ericsson.com>
+
+
+Description
+-----------
+
+This driver supports hardware montoring for Maxim MAX34440 PMBus 6-Channel
+Power-Supply Manager and MAX34441 PMBus 5-Channel Power-Supply Manager
+and Intelligent Fan Controller.
+
+The driver is a client driver to the core PMBus driver. Please see
+Documentation/hwmon/pmbus for details on PMBus client drivers.
+
+
+Usage Notes
+-----------
+
+This driver does not auto-detect devices. You will have to instantiate the
+devices explicitly. Please see Documentation/i2c/instantiating-devices for
+details.
+
+
+Platform data support
+---------------------
+
+The driver supports standard PMBus driver platform data.
+
+
+Sysfs entries
+-------------
+
+The following attributes are supported. Limits are read-write; all other
+attributes are read-only.
+
+in[1-6]_label "vout[1-6]".
+in[1-6]_input Measured voltage. From READ_VOUT register.
+in[1-6]_min Minumum Voltage. From VOUT_UV_WARN_LIMIT register.
+in[1-6]_max Maximum voltage. From VOUT_OV_WARN_LIMIT register.
+in[1-6]_lcrit Critical minumum Voltage. VOUT_UV_FAULT_LIMIT register.
+in[1-6]_crit Critical maximum voltage. From VOUT_OV_FAULT_LIMIT register.
+in[1-6]_min_alarm Voltage low alarm. From VOLTAGE_UV_WARNING status.
+in[1-6]_max_alarm Voltage high alarm. From VOLTAGE_OV_WARNING status.
+in[1-6]_lcrit_alarm Voltage critical low alarm. From VOLTAGE_UV_FAULT status.
+in[1-6]_crit_alarm Voltage critical high alarm. From VOLTAGE_OV_FAULT status.
+
+curr[1-6]_label "iout[1-6]".
+curr[1-6]_input Measured current. From READ_IOUT register.
+curr[1-6]_max Maximum current. From IOUT_OC_WARN_LIMIT register.
+curr[1-6]_crit Critical maximum current. From IOUT_OC_FAULT_LIMIT register.
+curr[1-6]_max_alarm Current high alarm. From IOUT_OC_WARNING status.
+curr[1-6]_crit_alarm Current critical high alarm. From IOUT_OC_FAULT status.
+
+ in6 and curr6 attributes only exist for MAX34440.
+
+temp[1-8]_input Measured temperatures. From READ_TEMPERATURE_1 register.
+ temp1 is the chip's internal temperature. temp2..temp5
+ are remote I2C temperature sensors. For MAX34441, temp6
+ is a remote thermal-diode sensor. For MAX34440, temp6..8
+ are remote I2C temperature sensors.
+temp[1-8]_max Maximum temperature. From OT_WARN_LIMIT register.
+temp[1-8]_crit Critical high temperature. From OT_FAULT_LIMIT register.
+temp[1-8]_max_alarm Temperature high alarm.
+temp[1-8]_crit_alarm Temperature critical high alarm.
+
+ temp7 and temp8 attributes only exist for MAX34440.
--- /dev/null
+Kernel driver max8688
+=====================
+
+Supported chips:
+ * Maxim MAX8688
+ Prefix: 'max8688'
+ Addresses scanned: -
+ Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX8688.pdf
+
+Author: Guenter Roeck <guenter.roeck@ericsson.com>
+
+
+Description
+-----------
+
+This driver supports hardware montoring for Maxim MAX8688 Digital Power-Supply
+Controller/Monitor with PMBus Interface.
+
+The driver is a client driver to the core PMBus driver. Please see
+Documentation/hwmon/pmbus for details on PMBus client drivers.
+
+
+Usage Notes
+-----------
+
+This driver does not auto-detect devices. You will have to instantiate the
+devices explicitly. Please see Documentation/i2c/instantiating-devices for
+details.
+
+
+Platform data support
+---------------------
+
+The driver supports standard PMBus driver platform data.
+
+
+Sysfs entries
+-------------
+
+The following attributes are supported. Limits are read-write; all other
+attributes are read-only.
+
+in1_label "vout1"
+in1_input Measured voltage. From READ_VOUT register.
+in1_min Minumum Voltage. From VOUT_UV_WARN_LIMIT register.
+in1_max Maximum voltage. From VOUT_OV_WARN_LIMIT register.
+in1_lcrit Critical minumum Voltage. VOUT_UV_FAULT_LIMIT register.
+in1_crit Critical maximum voltage. From VOUT_OV_FAULT_LIMIT register.
+in1_min_alarm Voltage low alarm. From VOLTAGE_UV_WARNING status.
+in1_max_alarm Voltage high alarm. From VOLTAGE_OV_WARNING status.
+in1_lcrit_alarm Voltage critical low alarm. From VOLTAGE_UV_FAULT status.
+in1_crit_alarm Voltage critical high alarm. From VOLTAGE_OV_FAULT status.
+
+curr1_label "iout1"
+curr1_input Measured current. From READ_IOUT register.
+curr1_max Maximum current. From IOUT_OC_WARN_LIMIT register.
+curr1_crit Critical maximum current. From IOUT_OC_FAULT_LIMIT register.
+curr1_max_alarm Current high alarm. From IOUT_OC_WARN_LIMIT register.
+curr1_crit_alarm Current critical high alarm. From IOUT_OC_FAULT status.
+
+temp1_input Measured temperature. From READ_TEMPERATURE_1 register.
+temp1_max Maximum temperature. From OT_WARN_LIMIT register.
+temp1_crit Critical high temperature. From OT_FAULT_LIMIT register.
+temp1_max_alarm Chip temperature high alarm. Set by comparing
+ READ_TEMPERATURE_1 with OT_WARN_LIMIT if TEMP_OT_WARNING
+ status is set.
+temp1_crit_alarm Chip temperature critical high alarm. Set by comparing
+ READ_TEMPERATURE_1 with OT_FAULT_LIMIT if TEMP_OT_FAULT
+ status is set.
Prefix: 'ltc2978'
Addresses scanned: -
Datasheet: http://cds.linear.com/docs/Datasheet/2978fa.pdf
- * Maxim MAX16064
- Quad Power-Supply Controller
- Prefix: 'max16064'
- Addresses scanned: -
- Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX16064.pdf
- * Maxim MAX34440
- PMBus 6-Channel Power-Supply Manager
- Prefixes: 'max34440'
- Addresses scanned: -
- Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX34440.pdf
- * Maxim MAX34441
- PMBus 5-Channel Power-Supply Manager and Intelligent Fan Controller
- Prefixes: 'max34441'
- Addresses scanned: -
- Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX34441.pdf
- * Maxim MAX8688
- Digital Power-Supply Controller/Monitor
- Prefix: 'max8688'
- Addresses scanned: -
- Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX8688.pdf
* Generic PMBus devices
Prefix: 'pmbus'
Addresses scanned: -
From IIN_OC_FAULT_LIMIT or IOUT_OC_FAULT_LIMIT register.
currX_alarm Current high alarm.
From IIN_OC_WARNING or IOUT_OC_WARNING status.
+currX_max_alarm Current high alarm.
+ From IIN_OC_WARN_LIMIT or IOUT_OC_WARN_LIMIT status.
currX_lcrit_alarm Output current critical low alarm.
From IOUT_UC_FAULT status.
currX_crit_alarm Current critical high alarm.
From IIN_OC_FAULT or IOUT_OC_FAULT status.
-currX_label "iin" or "vinY"
+currX_label "iin" or "ioutY"
powerX_input Measured power. From READ_PIN or READ_POUT register.
powerX_cap Output power cap. From POUT_MAX register.
From POUT_OP_FAULT status.
powerX_label "pin" or "poutY"
-tempX_input Measured tempererature.
+tempX_input Measured temperature.
From READ_TEMPERATURE_X register.
-tempX_min Mimimum tempererature. From UT_WARN_LIMIT register.
-tempX_max Maximum tempererature. From OT_WARN_LIMIT register.
-tempX_lcrit Critical low tempererature.
+tempX_min Mimimum temperature. From UT_WARN_LIMIT register.
+tempX_max Maximum temperature. From OT_WARN_LIMIT register.
+tempX_lcrit Critical low temperature.
From UT_FAULT_LIMIT register.
-tempX_crit Critical high tempererature.
+tempX_crit Critical high temperature.
From OT_FAULT_LIMIT register.
tempX_min_alarm Chip temperature low alarm. Set by comparing
READ_TEMPERATURE_X with UT_WARN_LIMIT if
in9_crit_alarm AIN1 critical alarm
in10_crit_alarm AIN2 critical alarm
-temp1_input Chip tempererature
-temp1_min Mimimum chip tempererature
-temp1_max Maximum chip tempererature
-temp1_crit Critical chip tempererature
+temp1_input Chip temperature
+temp1_min Mimimum chip temperature
+temp1_max Maximum chip temperature
+temp1_crit Critical chip temperature
temp1_crit_alarm Temperature critical alarm
--- /dev/null
+ How to Get Your Patch Accepted Into the Hwmon Subsystem
+ -------------------------------------------------------
+
+This text is is a collection of suggestions for people writing patches or
+drivers for the hwmon subsystem. Following these suggestions will greatly
+increase the chances of your change being accepted.
+
+
+1. General
+----------
+
+* It should be unnecessary to mention, but please read and follow
+ Documentation/SubmitChecklist
+ Documentation/SubmittingDrivers
+ Documentation/SubmittingPatches
+ Documentation/CodingStyle
+
+* If your patch generates checkpatch warnings, please refrain from explanations
+ such as "I don't like that coding style". Keep in mind that each unnecessary
+ warning helps hiding a real problem. If you don't like the kernel coding
+ style, don't write kernel drivers.
+
+* Please test your patch thoroughly. We are not your test group.
+ Sometimes a patch can not or not completely be tested because of missing
+ hardware. In such cases, you should test-build the code on at least one
+ architecture. If run-time testing was not achieved, it should be written
+ explicitly below the patch header.
+
+* If your patch (or the driver) is affected by configuration options such as
+ CONFIG_SMP or CONFIG_HOTPLUG, make sure it compiles for all configuration
+ variants.
+
+
+2. Adding functionality to existing drivers
+-------------------------------------------
+
+* Make sure the documentation in Documentation/hwmon/<driver_name> is up to
+ date.
+
+* Make sure the information in Kconfig is up to date.
+
+* If the added functionality requires some cleanup or structural changes, split
+ your patch into a cleanup part and the actual addition. This makes it easier
+ to review your changes, and to bisect any resulting problems.
+
+* Never mix bug fixes, cleanup, and functional enhancements in a single patch.
+
+
+3. New drivers
+--------------
+
+* Running your patch or driver file(s) through checkpatch does not mean its
+ formatting is clean. If unsure about formatting in your new driver, run it
+ through Lindent. Lindent is not perfect, and you may have to do some minor
+ cleanup, but it is a good start.
+
+* Consider adding yourself to MAINTAINERS.
+
+* Document the driver in Documentation/hwmon/<driver_name>.
+
+* Add the driver to Kconfig and Makefile in alphabetical order.
+
+* Make sure that all dependencies are listed in Kconfig. For new drivers, it
+ is most likely prudent to add a dependency on EXPERIMENTAL.
+
+* Avoid forward declarations if you can. Rearrange the code if necessary.
+
+* Avoid calculations in macros and macro-generated functions. While such macros
+ may save a line or so in the source, it obfuscates the code and makes code
+ review more difficult. It may also result in code which is more complicated
+ than necessary. Use inline functions or just regular functions instead.
+
+* If the driver has a detect function, make sure it is silent. Debug messages
+ and messages printed after a successful detection are acceptable, but it
+ must not print messages such as "Chip XXX not found/supported".
+
+ Keep in mind that the detect function will run for all drivers supporting an
+ address if a chip is detected on that address. Unnecessary messages will just
+ pollute the kernel log and not provide any value.
+
+* Provide a detect function if and only if a chip can be detected reliably.
+
+* Avoid writing to chip registers in the detect function. If you have to write,
+ only do it after you have already gathered enough data to be certain that the
+ detection is going to be successful.
+
+ Keep in mind that the chip might not be what your driver believes it is, and
+ writing to it might cause a bad misconfiguration.
+
+* Make sure there are no race conditions in the probe function. Specifically,
+ completely initialize your chip first, then create sysfs entries and register
+ with the hwmon subsystem.
+
+* Do not provide support for deprecated sysfs attributes.
+
+* Do not create non-standard attributes unless really needed. If you have to use
+ non-standard attributes, or you believe you do, discuss it on the mailing list
+ first. Either case, provide a detailed explanation why you need the
+ non-standard attribute(s).
+ Standard attributes are specified in Documentation/hwmon/sysfs-interface.
+
+* When deciding which sysfs attributes to support, look at the chip's
+ capabilities. While we do not expect your driver to support everything the
+ chip may offer, it should at least support all limits and alarms.
+
+* Last but not least, please check if a driver for your chip already exists
+ before starting to write a new driver. Especially for temperature sensors,
+ new chips are often variants of previously released chips. In some cases,
+ a presumably new chip may simply have been relabeled.
--- /dev/null
+The input protocol uses a map of types and codes to express input device values
+to userspace. This document describes the types and codes and how and when they
+may be used.
+
+A single hardware event generates multiple input events. Each input event
+contains the new value of a single data item. A special event type, EV_SYN, is
+used to separate input events into packets of input data changes occurring at
+the same moment in time. In the following, the term "event" refers to a single
+input event encompassing a type, code, and value.
+
+The input protocol is a stateful protocol. Events are emitted only when values
+of event codes have changed. However, the state is maintained within the Linux
+input subsystem; drivers do not need to maintain the state and may attempt to
+emit unchanged values without harm. Userspace may obtain the current state of
+event code values using the EVIOCG* ioctls defined in linux/input.h. The event
+reports supported by a device are also provided by sysfs in
+class/input/event*/device/capabilities/, and the properties of a device are
+provided in class/input/event*/device/properties.
+
+Types:
+==========
+Types are groupings of codes under a logical input construct. Each type has a
+set of applicable codes to be used in generating events. See the Codes section
+for details on valid codes for each type.
+
+* EV_SYN:
+ - Used as markers to separate events. Events may be separated in time or in
+ space, such as with the multitouch protocol.
+
+* EV_KEY:
+ - Used to describe state changes of keyboards, buttons, or other key-like
+ devices.
+
+* EV_REL:
+ - Used to describe relative axis value changes, e.g. moving the mouse 5 units
+ to the left.
+
+* EV_ABS:
+ - Used to describe absolute axis value changes, e.g. describing the
+ coordinates of a touch on a touchscreen.
+
+* EV_MSC:
+ - Used to describe miscellaneous input data that do not fit into other types.
+
+* EV_SW:
+ - Used to describe binary state input switches.
+
+* EV_LED:
+ - Used to turn LEDs on devices on and off.
+
+* EV_SND:
+ - Used to output sound to devices.
+
+* EV_REP:
+ - Used for autorepeating devices.
+
+* EV_FF:
+ - Used to send force feedback commands to an input device.
+
+* EV_PWR:
+ - A special type for power button and switch input.
+
+* EV_FF_STATUS:
+ - Used to receive force feedback device status.
+
+Codes:
+==========
+Codes define the precise type of event.
+
+EV_SYN:
+----------
+EV_SYN event values are undefined. Their usage is defined only by when they are
+sent in the evdev event stream.
+
+* SYN_REPORT:
+ - Used to synchronize and separate events into packets of input data changes
+ occurring at the same moment in time. For example, motion of a mouse may set
+ the REL_X and REL_Y values for one motion, then emit a SYN_REPORT. The next
+ motion will emit more REL_X and REL_Y values and send another SYN_REPORT.
+
+* SYN_CONFIG:
+ - TBD
+
+* SYN_MT_REPORT:
+ - Used to synchronize and separate touch events. See the
+ multi-touch-protocol.txt document for more information.
+
+* SYN_DROPPED:
+ - Used to indicate buffer overrun in the evdev client's event queue.
+ Client should ignore all events up to and including next SYN_REPORT
+ event and query the device (using EVIOCG* ioctls) to obtain its
+ current state.
+
+EV_KEY:
+----------
+EV_KEY events take the form KEY_<name> or BTN_<name>. For example, KEY_A is used
+to represent the 'A' key on a keyboard. When a key is depressed, an event with
+the key's code is emitted with value 1. When the key is released, an event is
+emitted with value 0. Some hardware send events when a key is repeated. These
+events have a value of 2. In general, KEY_<name> is used for keyboard keys, and
+BTN_<name> is used for other types of momentary switch events.
+
+A few EV_KEY codes have special meanings:
+
+* BTN_TOOL_<name>:
+ - These codes are used in conjunction with input trackpads, tablets, and
+ touchscreens. These devices may be used with fingers, pens, or other tools.
+ When an event occurs and a tool is used, the corresponding BTN_TOOL_<name>
+ code should be set to a value of 1. When the tool is no longer interacting
+ with the input device, the BTN_TOOL_<name> code should be reset to 0. All
+ trackpads, tablets, and touchscreens should use at least one BTN_TOOL_<name>
+ code when events are generated.
+
+* BTN_TOUCH:
+ BTN_TOUCH is used for touch contact. While an input tool is determined to be
+ within meaningful physical contact, the value of this property must be set
+ to 1. Meaningful physical contact may mean any contact, or it may mean
+ contact conditioned by an implementation defined property. For example, a
+ touchpad may set the value to 1 only when the touch pressure rises above a
+ certain value. BTN_TOUCH may be combined with BTN_TOOL_<name> codes. For
+ example, a pen tablet may set BTN_TOOL_PEN to 1 and BTN_TOUCH to 0 while the
+ pen is hovering over but not touching the tablet surface.
+
+Note: For appropriate function of the legacy mousedev emulation driver,
+BTN_TOUCH must be the first evdev code emitted in a synchronization frame.
+
+Note: Historically a touch device with BTN_TOOL_FINGER and BTN_TOUCH was
+interpreted as a touchpad by userspace, while a similar device without
+BTN_TOOL_FINGER was interpreted as a touchscreen. For backwards compatibility
+with current userspace it is recommended to follow this distinction. In the
+future, this distinction will be deprecated and the device properties ioctl
+EVIOCGPROP, defined in linux/input.h, will be used to convey the device type.
+
+* BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP, BTN_TOOL_TRIPLETAP, BTN_TOOL_QUADTAP:
+ - These codes denote one, two, three, and four finger interaction on a
+ trackpad or touchscreen. For example, if the user uses two fingers and moves
+ them on the touchpad in an effort to scroll content on screen,
+ BTN_TOOL_DOUBLETAP should be set to value 1 for the duration of the motion.
+ Note that all BTN_TOOL_<name> codes and the BTN_TOUCH code are orthogonal in
+ purpose. A trackpad event generated by finger touches should generate events
+ for one code from each group. At most only one of these BTN_TOOL_<name>
+ codes should have a value of 1 during any synchronization frame.
+
+Note: Historically some drivers emitted multiple of the finger count codes with
+a value of 1 in the same synchronization frame. This usage is deprecated.
+
+Note: In multitouch drivers, the input_mt_report_finger_count() function should
+be used to emit these codes. Please see multi-touch-protocol.txt for details.
+
+EV_REL:
+----------
+EV_REL events describe relative changes in a property. For example, a mouse may
+move to the left by a certain number of units, but its absolute position in
+space is unknown. If the absolute position is known, EV_ABS codes should be used
+instead of EV_REL codes.
+
+A few EV_REL codes have special meanings:
+
+* REL_WHEEL, REL_HWHEEL:
+ - These codes are used for vertical and horizontal scroll wheels,
+ respectively.
+
+EV_ABS:
+----------
+EV_ABS events describe absolute changes in a property. For example, a touchpad
+may emit coordinates for a touch location.
+
+A few EV_ABS codes have special meanings:
+
+* ABS_DISTANCE:
+ - Used to describe the distance of a tool from an interaction surface. This
+ event should only be emitted while the tool is hovering, meaning in close
+ proximity of the device and while the value of the BTN_TOUCH code is 0. If
+ the input device may be used freely in three dimensions, consider ABS_Z
+ instead.
+
+* ABS_MT_<name>:
+ - Used to describe multitouch input events. Please see
+ multi-touch-protocol.txt for details.
+
+EV_SW:
+----------
+EV_SW events describe stateful binary switches. For example, the SW_LID code is
+used to denote when a laptop lid is closed.
+
+Upon binding to a device or resuming from suspend, a driver must report
+the current switch state. This ensures that the device, kernel, and userspace
+state is in sync.
+
+Upon resume, if the switch state is the same as before suspend, then the input
+subsystem will filter out the duplicate switch state reports. The driver does
+not need to keep the state of the switch at any time.
+
+EV_MSC:
+----------
+EV_MSC events are used for input and output events that do not fall under other
+categories.
+
+EV_LED:
+----------
+EV_LED events are used for input and output to set and query the state of
+various LEDs on devices.
+
+EV_REP:
+----------
+EV_REP events are used for specifying autorepeating events.
+
+EV_SND:
+----------
+EV_SND events are used for sending sound commands to simple sound output
+devices.
+
+EV_FF:
+----------
+EV_FF events are used to initialize a force feedback capable device and to cause
+such device to feedback.
+
+EV_PWR:
+----------
+EV_PWR events are a special type of event used specifically for power
+mangement. Its usage is not well defined. To be addressed later.
+
+Guidelines:
+==========
+The guidelines below ensure proper single-touch and multi-finger functionality.
+For multi-touch functionality, see the multi-touch-protocol.txt document for
+more information.
+
+Mice:
+----------
+REL_{X,Y} must be reported when the mouse moves. BTN_LEFT must be used to report
+the primary button press. BTN_{MIDDLE,RIGHT,4,5,etc.} should be used to report
+further buttons of the device. REL_WHEEL and REL_HWHEEL should be used to report
+scroll wheel events where available.
+
+Touchscreens:
+----------
+ABS_{X,Y} must be reported with the location of the touch. BTN_TOUCH must be
+used to report when a touch is active on the screen.
+BTN_{MOUSE,LEFT,MIDDLE,RIGHT} must not be reported as the result of touch
+contact. BTN_TOOL_<name> events should be reported where possible.
+
+Trackpads:
+----------
+Legacy trackpads that only provide relative position information must report
+events like mice described above.
+
+Trackpads that provide absolute touch position must report ABS_{X,Y} for the
+location of the touch. BTN_TOUCH should be used to report when a touch is active
+on the trackpad. Where multi-finger support is available, BTN_TOOL_<name> should
+be used to report the number of touches active on the trackpad.
+
+Tablets:
+----------
+BTN_TOOL_<name> events must be reported when a stylus or other tool is active on
+the tablet. ABS_{X,Y} must be reported with the location of the tool. BTN_TOUCH
+should be used to report when the tool is in contact with the tablet.
+BTN_{STYLUS,STYLUS2} should be used to report buttons on the tool itself. Any
+button may be used for buttons on the tablet except BTN_{MOUSE,LEFT}.
+BTN_{0,1,2,etc} are good generic codes for unlabeled buttons. Do not use
+meaningful buttons, like BTN_FORWARD, unless the button is labeled for that
+purpose on the device.
within the array where IO will be blocked. This is currently
only supported for raid4/5/6.
+ sync_min
+ sync_max
+ The two values, given as numbers of sectors, indicate a range
+ withing the array where 'check'/'repair' will operate. Must be
+ a multiple of chunk_size. When it reaches "sync_max" it will
+ pause, rather than complete.
+ You can use 'select' or 'poll' on "sync_completed" to wait for
+ that number to reach sync_max. Then you can either increase
+ "sync_max", or can write 'idle' to "sync_action".
+
Each active md device may also have attributes specific to the
personality module that manages it.
This module supports multiple cards.
The driver requires the firmware loader support on kernel.
+ Module snd-lola
+ ---------------
+
+ Module for Digigram Lola PCI-e boards
+
+ This module supports multiple cards.
+
Module snd-lx6464es
-------------------
-1'-
In the above chart minuses and slashes represent "real" data amounts, points and
-accents represent "useful" data, basically, CEU scaled amd cropped output,
+accents represent "useful" data, basically, CEU scaled and cropped output,
mapped back onto the client's source plane.
Such a configuration can be produced by user requests:
1. Calculate current sensor scales:
- scale_s = ((3') - (3)) / ((2') - (2))
+ scale_s = ((2') - (2)) / ((3') - (3))
2. Calculate "effective" input crop (sensor subwindow) - CEU crop scaled back at
current sensor scales onto input window - this is user S_CROP:
4. Calculate sensor output window by applying combined scales to real input
window:
- width_s_out = ((2') - (2)) / scale_comb
+ width_s_out = ((7') - (7)) = ((2') - (2)) / scale_comb
5. Apply iterative sensor S_FMT for sensor output window.
4. Application Programming Interface (API)
5. Example Execution Scenarios
6. Guidelines
+7. Debugging
1. Introduction
* Unless work items are expected to consume a huge amount of CPU
cycles, using a bound wq is usually beneficial due to the increased
level of locality in wq operations and work item execution.
+
+
+7. Debugging
+
+Because the work functions are executed by generic worker threads
+there are a few tricks needed to shed some light on misbehaving
+workqueue users.
+
+Worker threads show up in the process list as:
+
+root 5671 0.0 0.0 0 0 ? S 12:07 0:00 [kworker/0:1]
+root 5672 0.0 0.0 0 0 ? S 12:07 0:00 [kworker/1:2]
+root 5673 0.0 0.0 0 0 ? S 12:12 0:00 [kworker/0:0]
+root 5674 0.0 0.0 0 0 ? S 12:13 0:00 [kworker/1:0]
+
+If kworkers are going crazy (using too much cpu), there are two types
+of possible problems:
+
+ 1. Something beeing scheduled in rapid succession
+ 2. A single work item that consumes lots of cpu cycles
+
+The first one can be tracked using tracing:
+
+ $ echo workqueue:workqueue_queue_work > /sys/kernel/debug/tracing/set_event
+ $ cat /sys/kernel/debug/tracing/trace_pipe > out.txt
+ (wait a few secs)
+ ^C
+
+If something is busy looping on work queueing, it would be dominating
+the output and the offender can be determined with the work item
+function.
+
+For the second type of problems it should be possible to just check
+the stack trace of the offending worker thread.
+
+ $ cat /proc/THE_OFFENDING_KWORKER/stack
+
+The work item's function should be trivially visible in the stack
+trace.
F: drivers/net/hamradio/6pack.c
8169 10/100/1000 GIGABIT ETHERNET DRIVER
+M: Realtek linux nic maintainers <nic_swsd@realtek.com>
M: Francois Romieu <romieu@fr.zoreil.com>
L: netdev@vger.kernel.org
S: Maintained
F: fs/9p/
A2232 SERIAL BOARD DRIVER
-M: Enver Haase <A2232@gmx.net>
L: linux-m68k@lists.linux-m68k.org
-S: Maintained
-F: drivers/char/ser_a2232*
+S: Orphan
+F: drivers/staging/generic_serial/ser_a2232*
AACRAID SCSI RAID DRIVER
M: Adaptec OEM Raid Solutions <aacraid@adaptec.com>
F: arch/arm/mach-orion5x/
F: arch/arm/plat-orion/
+ARM/Orion SoC/Technologic Systems TS-78xx platform support
+M: Alexander Clouter <alex@digriz.org.uk>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+W: http://www.digriz.org.uk/ts78xx/kernel
+S: Maintained
+F: arch/arm/mach-orion5x/ts78xx-*
+
ARM/MIOA701 MACHINE SUPPORT
M: Robert Jarzmik <robert.jarzmik@free.fr>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: arch/arm/mach-s3c64xx/
-ARM/S5P ARM ARCHITECTURES
+ARM/S5P EXYNOS ARM ARCHITECTURES
M: Kukjin Kim <kgene.kim@samsung.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
S: Maintained
F: arch/arm/mach-s5p*/
+F: arch/arm/mach-exynos*/
ARM/SAMSUNG MOBILE MACHINE SUPPORT
M: Kyungmin Park <kyungmin.park@samsung.com>
F: drivers/sh/
ARM/TELECHIPS ARM ARCHITECTURE
-M: "Hans J. Koch" <hjk@linutronix.de>
+M: "Hans J. Koch" <hjk@hansjkoch.de>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: arch/arm/plat-tcc/
F: drivers/platform/x86/compal-laptop.c
COMPUTONE INTELLIPORT MULTIPORT CARD
-M: "Michael H. Warfield" <mhw@wittsend.com>
W: http://www.wittsend.com/computone.html
-S: Maintained
+S: Orphan
F: Documentation/serial/computone.txt
-F: drivers/char/ip2/
+F: drivers/staging/tty/ip2/
CONEXANT ACCESSRUNNER USB DRIVER
M: Simon Arlott <cxacru@fire.lp0.eu>
CYCLADES ASYNC MUX DRIVER
W: http://www.cyclades.com/
S: Orphan
-F: drivers/char/cyclades.c
+F: drivers/tty/cyclades.c
F: include/linux/cyclades.h
CYCLADES PC300 DRIVER
W: http://www.digi.com
S: Orphan
F: Documentation/serial/digiepca.txt
-F: drivers/char/epca*
-F: drivers/char/digi*
+F: drivers/staging/tty/epca*
+F: drivers/staging/tty/digi*
DIOLAN U2C-12 I2C DRIVER
M: Guenter Roeck <guenter.roeck@ericsson.com>
M: Grant Likely <grant.likely@secretlab.ca>
S: Maintained
T: git git://git.secretlab.ca/git/linux-2.6.git
-F: Documentation/gpio/gpio.txt
+F: Documentation/gpio.txt
F: drivers/gpio/
F: include/linux/gpio*
F: include/linux/matroxfb.h
MAX6650 HARDWARE MONITOR AND FAN CONTROLLER DRIVER
-M: "Hans J. Koch" <hjk@linutronix.de>
+M: "Hans J. Koch" <hjk@hansjkoch.de>
L: lm-sensors@lm-sensors.org
S: Maintained
F: Documentation/hwmon/max6650
M: Jiri Slaby <jirislaby@gmail.com>
S: Maintained
F: Documentation/serial/moxa-smartio
-F: drivers/char/mxser.*
+F: drivers/tty/mxser.*
MSI LAPTOP SUPPORT
M: "Lee, Chun-Yi" <jlee@novell.com>
MULTITECH MULTIPORT CARD (ISICOM)
S: Orphan
-F: drivers/char/isicom.c
+F: drivers/tty/isicom.c
F: include/linux/isicom.h
MUSB MULTIPOINT HIGH SPEED DUAL-ROLE CONTROLLER
S: Maintained
F: drivers/net/natsemi.c
+NATIVE INSTRUMENTS USB SOUND INTERFACE DRIVER
+M: Daniel Mack <zonque@gmail.com>
+S: Maintained
+L: alsa-devel@alsa-project.org
+W: http://www.native-instruments.com
+F: sound/usb/caiaq/
+
NCP FILESYSTEM
M: Petr Vandrovec <petr@vandrovec.name>
S: Odd Fixes
RISCOM8 DRIVER
S: Orphan
F: Documentation/serial/riscom8.txt
-F: drivers/char/riscom8*
+F: drivers/staging/tty/riscom8*
ROCKETPORT DRIVER
P: Comtrol Corp.
W: http://www.comtrol.com
S: Maintained
F: Documentation/serial/rocket.txt
-F: drivers/char/rocket*
+F: drivers/tty/rocket*
ROSE NETWORK LAYER
M: Ralf Baechle <ralf@linux-mips.org>
F: sound/
SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC)
-M: Liam Girdwood <lrg@slimlogic.co.uk>
+M: Liam Girdwood <lrg@ti.com>
M: Mark Brown <broonie@opensource.wolfsonmicro.com>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
F: arch/arm/mach-spear6xx/spear600_evb.c
SPECIALIX IO8+ MULTIPORT SERIAL CARD DRIVER
-M: Roger Wolff <R.E.Wolff@BitWizard.nl>
-S: Supported
+S: Orphan
F: Documentation/serial/specialix.txt
-F: drivers/char/specialix*
+F: drivers/staging/tty/specialix*
SPI SUBSYSTEM
M: David Brownell <dbrownell@users.sourceforge.net>
STABLE BRANCH
M: Greg Kroah-Hartman <greg@kroah.com>
-M: Chris Wright <chrisw@sous-sol.org>
L: stable@kernel.org
S: Maintained
F: include/linux/tifm.h
TI TWL4030 SERIES SOC CODEC DRIVER
-M: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+M: Peter Ujfalusi <peter.ujfalusi@ti.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
S: Maintained
F: sound/soc/codecs/twl4030*
W: http://www.uclinux.org/
L: uclinux-dev@uclinux.org (subscribers-only)
S: Maintained
-F: arch/m68knommu/
+F: arch/m68k/*/*_no.*
+F: arch/m68k/include/asm/*_no.*
UCLINUX FOR RENESAS H8/300 (H8300)
M: Yoshinori Sato <ysato@users.sourceforge.jp>
F: drivers/usb/host/uhci*
USB "USBNET" DRIVER FRAMEWORK
-M: David Brownell <dbrownell@users.sourceforge.net>
+M: Oliver Neukum <oneukum@suse.de>
L: netdev@vger.kernel.org
W: http://www.linux-usb.org/usbnet
S: Maintained
F: fs/hppfs/
USERSPACE I/O (UIO)
-M: "Hans J. Koch" <hjk@linutronix.de>
+M: "Hans J. Koch" <hjk@hansjkoch.de>
M: Greg Kroah-Hartman <gregkh@suse.de>
S: Maintained
F: Documentation/DocBook/uio-howto.tmpl
F: drivers/scsi/vmw_pvscsi.h
VOLTAGE AND CURRENT REGULATOR FRAMEWORK
-M: Liam Girdwood <lrg@slimlogic.co.uk>
+M: Liam Girdwood <lrg@ti.com>
M: Mark Brown <broonie@opensource.wolfsonmicro.com>
W: http://opensource.wolfsonmicro.com/node/15
W: http://www.slimlogic.co.uk/?p=48
S: Maintained
F: drivers/platform/x86
+XEN HYPERVISOR INTERFACE
+M: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
+M: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+L: xen-devel@lists.xensource.com (moderated for non-subscribers)
+L: virtualization@lists.linux-foundation.org
+S: Supported
+F: arch/x86/xen/
+F: drivers/*/xen-*front.c
+F: drivers/xen/
+F: arch/x86/include/asm/xen/
+F: include/xen/
+
XEN NETWORK BACKEND DRIVER
M: Ian Campbell <ian.campbell@citrix.com>
L: xen-devel@lists.xensource.com (moderated for non-subscribers)
F: arch/x86/xen/*swiotlb*
F: drivers/xen/*swiotlb*
-XEN HYPERVISOR INTERFACE
-M: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
-M: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
-L: xen-devel@lists.xensource.com (moderated for non-subscribers)
-L: virtualization@lists.linux-foundation.org
-S: Supported
-F: arch/x86/xen/
-F: drivers/*/xen-*front.c
-F: drivers/xen/
-F: arch/x86/include/asm/xen/
-F: include/xen/
-
XFS FILESYSTEM
P: Silicon Graphics Inc
M: Alex Elder <aelder@sgi.com>
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 39
-EXTRAVERSION = -rc3
+EXTRAVERSION = -rc7
NAME = Flesh-Eating Bats with Fangs
# *DOCUMENTATION*
extra-y := head.o vmlinux.lds
asflags-y := $(KBUILD_CFLAGS)
-ccflags-y := -Werror -Wno-sign-compare
+ccflags-y := -Wno-sign-compare
obj-y := entry.o traps.o process.o init_task.o osf_sys.o irq.o \
irq_alpha.o signal.o setup.o ptrace.o time.o \
{
unsigned long flags;
unsigned long mid = MCPCIA_HOSE2MID(hose->index);
- unsigned int stat0, value, temp, cpu;
+ unsigned int stat0, value, cpu;
cpu = smp_processor_id();
stat0 = *(vuip)MCPCIA_CAP_ERR(mid);
*(vuip)MCPCIA_CAP_ERR(mid) = stat0;
mb();
- temp = *(vuip)MCPCIA_CAP_ERR(mid);
+ *(vuip)MCPCIA_CAP_ERR(mid);
DBG_CFG(("conf_read: MCPCIA_CAP_ERR(%d) was 0x%x\n", mid, stat0));
mb();
{
unsigned long flags;
unsigned long mid = MCPCIA_HOSE2MID(hose->index);
- unsigned int stat0, temp, cpu;
+ unsigned int stat0, cpu;
cpu = smp_processor_id();
/* Reset status register to avoid losing errors. */
stat0 = *(vuip)MCPCIA_CAP_ERR(mid);
*(vuip)MCPCIA_CAP_ERR(mid) = stat0; mb();
- temp = *(vuip)MCPCIA_CAP_ERR(mid);
+ *(vuip)MCPCIA_CAP_ERR(mid);
DBG_CFG(("conf_write: MCPCIA CAP_ERR(%d) was 0x%x\n", mid, stat0));
draina();
*((vuip)addr) = value;
mb();
mb(); /* magic */
- temp = *(vuip)MCPCIA_CAP_ERR(mid); /* read to force the write */
+ *(vuip)MCPCIA_CAP_ERR(mid); /* read to force the write */
mcheck_expected(cpu) = 0;
mb();
void
mcpcia_machine_check(unsigned long vector, unsigned long la_ptr)
{
- struct el_common *mchk_header;
struct el_MCPCIA_uncorrected_frame_mcheck *mchk_logout;
unsigned int cpu = smp_processor_id();
int expected;
- mchk_header = (struct el_common *)la_ptr;
mchk_logout = (struct el_MCPCIA_uncorrected_frame_mcheck *)la_ptr;
expected = mcheck_expected(cpu);
static struct el_subpacket *
el_process_regatta_subpacket(struct el_subpacket *header)
{
- int status;
-
if (header->class != EL_CLASS__REGATTA_FAMILY) {
printk("%s ** Unexpected header CLASS %d TYPE %d, aborting\n",
err_print_prefix,
printk("%s ** Occurred on CPU %d:\n",
err_print_prefix,
(int)header->by_type.regatta_frame.cpuid);
- status = privateer_process_logout_frame((struct el_common *)
+ privateer_process_logout_frame((struct el_common *)
header->by_type.regatta_frame.data_start, 1);
break;
default:
void __init
init_rtc_irq(void)
{
- irq_set_chip_and_handler_name(RTC_IRQ, &no_irq_chip,
+ irq_set_chip_and_handler_name(RTC_IRQ, &dummy_irq_chip,
handle_simple_irq, "RTC");
setup_irq(RTC_IRQ, &timer_irqaction);
}
case PCA56_CPU:
case PCA57_CPU:
{
- unsigned long cbox_config, size;
-
if (cpu_type == PCA56_CPU) {
L1I = CSHAPE(16*1024, 6, 1);
L1D = CSHAPE(8*1024, 5, 1);
}
L3 = -1;
+#if 0
+ unsigned long cbox_config, size;
+
cbox_config = *(vulp) phys_to_virt (0xfffff00008UL);
size = 512*1024 * (1 << ((cbox_config >> 12) & 3));
-#if 0
L2 = ((cbox_config >> 31) & 1 ? CSHAPE (size, 6, 1) : -1);
#else
L2 = external_cache_probe(512*1024, 6);
static unsigned long __init SMCConfigState(unsigned long baseAddr)
{
unsigned char devId;
- unsigned char devRev;
unsigned long configPort;
unsigned long indexPort;
devId = inb(dataPort);
if (devId == VALID_DEVICE_ID) {
outb(DEVICE_REV, indexPort);
- devRev = inb(dataPort);
+ /* unsigned char devRev = */ inb(dataPort);
break;
}
else
wildfire_init_irq_per_pca(int qbbno, int pcano)
{
int i, irq_bias;
- unsigned long io_bias;
static struct irqaction isa_enable = {
.handler = no_action,
.name = "isa_enable",
irq_bias = qbbno * (WILDFIRE_PCA_PER_QBB * WILDFIRE_IRQ_PER_PCA)
+ pcano * WILDFIRE_IRQ_PER_PCA;
+#if 0
+ unsigned long io_bias;
+
/* Only need the following for first PCI bus per PCA. */
io_bias = WILDFIRE_IO(qbbno, pcano<<1) - WILDFIRE_IO_BIAS;
-#if 0
outb(0, DMA1_RESET_REG + io_bias);
outb(0, DMA2_RESET_REG + io_bias);
outb(DMA_MODE_CASCADE, DMA2_MODE_REG + io_bias);
year += 100;
ts->tv_sec = mktime(year, mon, day, hour, min, sec);
+ ts->tv_nsec = 0;
}
config HIGHPTE
bool "Allocate 2nd-level pagetables from highmem"
depends on HIGHMEM
- depends on !OUTER_CACHE
config HW_PERF_EVENTS
bool "Enable hardware performance counter support for perf events"
config ARCH_SUSPEND_POSSIBLE
depends on !ARCH_S5P64X0 && !ARCH_S5P6442
+ depends on CPU_ARM920T || CPU_ARM926T || CPU_SA1100 || \
+ CPU_V6 || CPU_V6K || CPU_V7 || CPU_XSC3 || CPU_XSCALE
def_bool y
endmenu
8 - SIGSEGV faults
16 - SIGBUS faults
-config DEBUG_ERRORS
- bool "Verbose kernel error messages"
- depends on DEBUG_KERNEL
- help
- This option controls verbose debugging information which can be
- printed when the kernel detects an internal error. This debugging
- information is useful to kernel hackers when tracking down problems,
- but mostly meaningless to other people. It's safe to say Y unless
- you are concerned with the code size or don't want to see these
- messages.
-
config DEBUG_STACK_USAGE
bool "Enable stack utilization instrumentation"
depends on DEBUG_KERNEL
obj-$(CONFIG_ARCH_IXP2000) += uengine.o
obj-$(CONFIG_ARCH_IXP23XX) += uengine.o
obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o
-obj-$(CONFIG_COMMON_CLKDEV) += clkdev.o
obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o
--- /dev/null
+CONFIG_EXPERIMENTAL=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_EMBEDDED=y
+# CONFIG_HOTPLUG is not set
+# CONFIG_ELF_CORE is not set
+# CONFIG_FUTEX is not set
+# CONFIG_TIMERFD is not set
+# CONFIG_VM_EVENT_COUNTERS is not set
+# CONFIG_COMPAT_BRK is not set
+CONFIG_SLAB=y
+# CONFIG_LBDAF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+# CONFIG_MMU is not set
+CONFIG_ARCH_AT91=y
+CONFIG_ARCH_AT91X40=y
+CONFIG_MACH_AT91EB01=y
+CONFIG_AT91_EARLY_USART0=y
+CONFIG_CPU_ARM7TDMI=y
+CONFIG_SET_MEM_PARAM=y
+CONFIG_DRAM_BASE=0x01000000
+CONFIG_DRAM_SIZE=0x00400000
+CONFIG_FLASH_MEM_BASE=0x01400000
+CONFIG_PROCESSOR_ID=0x14000040
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_BINFMT_FLAT=y
+# CONFIG_SUSPEND is not set
+# CONFIG_FW_LOADER is not set
+CONFIG_MTD=y
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_RAM=y
+CONFIG_MTD_ROM=y
+CONFIG_BLK_DEV_RAM=y
+# CONFIG_INPUT is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_DEVKMEM is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HWMON is not set
+# CONFIG_USB_SUPPORT is not set
+CONFIG_EXT2_FS=y
+# CONFIG_DNOTIFY is not set
+CONFIG_ROMFS_FS=y
+# CONFIG_ENABLE_MUST_CHECK is not set
#define __ASM_ARM_CPUTYPE_H
#include <linux/stringify.h>
+#include <linux/kernel.h>
#define CPUID_ID 0
#define CPUID_CACHETYPE 1
struct kprobe;
typedef void (kprobe_insn_handler_t)(struct kprobe *, struct pt_regs *);
+typedef unsigned long (kprobe_check_cc)(unsigned long);
+
/* Architecture specific copy of original instruction. */
struct arch_specific_insn {
kprobe_opcode_t *insn;
kprobe_insn_handler_t *insn_handler;
+ kprobe_check_cc *insn_check_cc;
};
struct prev_kprobe {
#define THREAD_NOTIFY_FLUSH 0
#define THREAD_NOTIFY_EXIT 1
#define THREAD_NOTIFY_SWITCH 2
+#define THREAD_NOTIFY_COPY 3
#endif
#endif
#define __NR_fanotify_init (__NR_SYSCALL_BASE+367)
#define __NR_fanotify_mark (__NR_SYSCALL_BASE+368)
#define __NR_prlimit64 (__NR_SYSCALL_BASE+369)
+#define __NR_name_to_handle_at (__NR_SYSCALL_BASE+370)
+#define __NR_open_by_handle_at (__NR_SYSCALL_BASE+371)
+#define __NR_clock_adjtime (__NR_SYSCALL_BASE+372)
+#define __NR_syncfs (__NR_SYSCALL_BASE+373)
/*
* The following SWIs are ARM private.
obj-$(CONFIG_ARTHUR) += arthur.o
obj-$(CONFIG_ISA_DMA) += dma-isa.o
obj-$(CONFIG_PCI) += bios32.o isa.o
-obj-$(CONFIG_PM) += sleep.o
+obj-$(CONFIG_PM_SLEEP) += sleep.o
obj-$(CONFIG_HAVE_SCHED_CLOCK) += sched_clock.o
obj-$(CONFIG_SMP) += smp.o smp_tlb.o
obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o
CALL(sys_fanotify_init)
CALL(sys_fanotify_mark)
CALL(sys_prlimit64)
+/* 370 */ CALL(sys_name_to_handle_at)
+ CALL(sys_open_by_handle_at)
+ CALL(sys_clock_adjtime)
+ CALL(sys_syncfs)
#ifndef syscalls_counted
.equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
#define syscalls_counted
void elf_set_personality(const struct elf32_hdr *x)
{
unsigned int eflags = x->e_flags;
- unsigned int personality = PER_LINUX_32BIT;
+ unsigned int personality = current->personality & ~PER_MASK;
+
+ /*
+ * We only support Linux ELF executables, so always set the
+ * personality to LINUX.
+ */
+ personality |= PER_LINUX;
/*
* APCS-26 is only valid for OABI executables
*/
- if ((eflags & EF_ARM_EABI_MASK) == EF_ARM_EABI_UNKNOWN) {
- if (eflags & EF_ARM_APCS_26)
- personality = PER_LINUX;
- }
+ if ((eflags & EF_ARM_EABI_MASK) == EF_ARM_EABI_UNKNOWN &&
+ (eflags & EF_ARM_APCS_26))
+ personality &= ~ADDR_LIMIT_32BIT;
+ else
+ personality |= ADDR_LIMIT_32BIT;
set_personality(personality);
*/
asm volatile("mcr p14, 0, %0, c1, c0, 4" : : "r" (0));
isb();
+
+ /*
+ * Clear any configured vector-catch events before
+ * enabling monitor mode.
+ */
+ asm volatile("mcr p14, 0, %0, c0, c7, 0" : : "r" (0));
+ isb();
}
if (enable_monitor_mode())
*
* *) If the PC is written to by the instruction, the
* instruction must be fully simulated in software.
- * If it is a conditional instruction, the handler
- * will use insn[0] to copy its condition code to
- * set r0 to 1 and insn[1] to "mov pc, lr" to return.
*
* *) Otherwise, a modified form of the instruction is
* directly executed. Its handler calls the
#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
+#define is_r15(insn, bitpos) (((insn) & (0xf << bitpos)) == (0xf << bitpos))
+
+/*
+ * Test if load/store instructions writeback the address register.
+ * if P (bit 24) == 0 or W (bit 21) == 1
+ */
+#define is_writeback(insn) ((insn ^ 0x01000000) & 0x01200000)
+
#define PSR_fs (PSR_f|PSR_s)
#define KPROBE_RETURN_INSTRUCTION 0xe1a0f00e /* mov pc, lr */
-#define SET_R0_TRUE_INSTRUCTION 0xe3a00001 /* mov r0, #1 */
-
-#define truecc_insn(insn) (((insn) & 0xf0000000) | \
- (SET_R0_TRUE_INSTRUCTION & 0x0fffffff))
typedef long (insn_0arg_fn_t)(void);
typedef long (insn_1arg_fn_t)(long);
static void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs)
{
- insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
long iaddr = (long)p->addr;
int disp = branch_displacement(insn);
- if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn))
- return;
-
if (insn & (1 << 24))
regs->ARM_lr = iaddr + 4;
static void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs)
{
- insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
int rm = insn & 0xf;
long rmv = regs->uregs[rm];
- if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn))
- return;
-
if (insn & (1 << 5))
regs->ARM_lr = (long)p->addr + 4;
regs->ARM_cpsr |= PSR_T_BIT;
}
+static void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs)
+{
+ kprobe_opcode_t insn = p->opcode;
+ int rd = (insn >> 12) & 0xf;
+ unsigned long mask = 0xf8ff03df; /* Mask out execution state */
+ regs->uregs[rd] = regs->ARM_cpsr & mask;
+}
+
static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
{
- insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
int rn = (insn >> 16) & 0xf;
int lbit = insn & (1 << 20);
int reg_bit_vector;
int reg_count;
- if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn))
- return;
-
reg_count = 0;
reg_bit_vector = insn & 0xffff;
while (reg_bit_vector) {
static void __kprobes simulate_stm1_pc(struct kprobe *p, struct pt_regs *regs)
{
- insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
-
- if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn))
- return;
-
regs->ARM_pc = (long)p->addr + str_pc_offset;
simulate_ldm1stm1(p, regs);
regs->ARM_pc = (long)p->addr + 4;
regs->uregs[12] = regs->uregs[13];
}
-static void __kprobes emulate_ldcstc(struct kprobe *p, struct pt_regs *regs)
-{
- insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
- kprobe_opcode_t insn = p->opcode;
- int rn = (insn >> 16) & 0xf;
- long rnv = regs->uregs[rn];
-
- /* Save Rn in case of writeback. */
- regs->uregs[rn] = insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn);
-}
-
static void __kprobes emulate_ldrd(struct kprobe *p, struct pt_regs *regs)
{
insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
+ long ppc = (long)p->addr + 8;
int rd = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf; /* rm may be invalid, don't care. */
+ long rmv = (rm == 15) ? ppc : regs->uregs[rm];
+ long rnv = (rn == 15) ? ppc : regs->uregs[rn];
/* Not following the C calling convention here, so need asm(). */
__asm__ __volatile__ (
"str r0, %[rn] \n\t" /* in case of writeback */
"str r2, %[rd0] \n\t"
"str r3, %[rd1] \n\t"
- : [rn] "+m" (regs->uregs[rn]),
+ : [rn] "+m" (rnv),
[rd0] "=m" (regs->uregs[rd]),
[rd1] "=m" (regs->uregs[rd+1])
- : [rm] "m" (regs->uregs[rm]),
+ : [rm] "m" (rmv),
[cpsr] "r" (regs->ARM_cpsr),
[i_fn] "r" (i_fn)
: "r0", "r1", "r2", "r3", "lr", "cc"
);
+ if (is_writeback(insn))
+ regs->uregs[rn] = rnv;
}
static void __kprobes emulate_strd(struct kprobe *p, struct pt_regs *regs)
{
insn_4arg_fn_t *i_fn = (insn_4arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
+ long ppc = (long)p->addr + 8;
int rd = (insn >> 12) & 0xf;
int rn = (insn >> 16) & 0xf;
int rm = insn & 0xf;
- long rnv = regs->uregs[rn];
- long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */
+ long rnv = (rn == 15) ? ppc : regs->uregs[rn];
+ /* rm/rmv may be invalid, don't care. */
+ long rmv = (rm == 15) ? ppc : regs->uregs[rm];
+ long rnv_wb;
- regs->uregs[rn] = insnslot_4arg_rflags(rnv, rmv, regs->uregs[rd],
+ rnv_wb = insnslot_4arg_rflags(rnv, rmv, regs->uregs[rd],
regs->uregs[rd+1],
regs->ARM_cpsr, i_fn);
+ if (is_writeback(insn))
+ regs->uregs[rn] = rnv_wb;
}
static void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs)
regs->uregs[rn] = rnv_wb; /* Save Rn in case of writeback. */
}
-static void __kprobes emulate_mrrc(struct kprobe *p, struct pt_regs *regs)
-{
- insn_llret_0arg_fn_t *i_fn = (insn_llret_0arg_fn_t *)&p->ainsn.insn[0];
- kprobe_opcode_t insn = p->opcode;
- union reg_pair fnr;
- int rd = (insn >> 12) & 0xf;
- int rn = (insn >> 16) & 0xf;
-
- fnr.dr = insnslot_llret_0arg_rflags(regs->ARM_cpsr, i_fn);
- regs->uregs[rn] = fnr.r0;
- regs->uregs[rd] = fnr.r1;
-}
-
-static void __kprobes emulate_mcrr(struct kprobe *p, struct pt_regs *regs)
-{
- insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
- kprobe_opcode_t insn = p->opcode;
- int rd = (insn >> 12) & 0xf;
- int rn = (insn >> 16) & 0xf;
- long rnv = regs->uregs[rn];
- long rdv = regs->uregs[rd];
-
- insnslot_2arg_rflags(rnv, rdv, regs->ARM_cpsr, i_fn);
-}
-
static void __kprobes emulate_sat(struct kprobe *p, struct pt_regs *regs)
{
insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
insnslot_0arg_rflags(regs->ARM_cpsr, i_fn);
}
-static void __kprobes emulate_rd12(struct kprobe *p, struct pt_regs *regs)
+static void __kprobes emulate_nop(struct kprobe *p, struct pt_regs *regs)
{
- insn_0arg_fn_t *i_fn = (insn_0arg_fn_t *)&p->ainsn.insn[0];
- kprobe_opcode_t insn = p->opcode;
- int rd = (insn >> 12) & 0xf;
-
- regs->uregs[rd] = insnslot_0arg_rflags(regs->ARM_cpsr, i_fn);
}
-static void __kprobes emulate_ird12(struct kprobe *p, struct pt_regs *regs)
+static void __kprobes
+emulate_rd12_modify(struct kprobe *p, struct pt_regs *regs)
{
insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int ird = (insn >> 12) & 0xf;
+ int rd = (insn >> 12) & 0xf;
+ long rdv = regs->uregs[rd];
- insnslot_1arg_rflags(regs->uregs[ird], regs->ARM_cpsr, i_fn);
+ regs->uregs[rd] = insnslot_1arg_rflags(rdv, regs->ARM_cpsr, i_fn);
}
-static void __kprobes emulate_rn16(struct kprobe *p, struct pt_regs *regs)
+static void __kprobes
+emulate_rd12rn0_modify(struct kprobe *p, struct pt_regs *regs)
{
- insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+ insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
kprobe_opcode_t insn = p->opcode;
- int rn = (insn >> 16) & 0xf;
+ int rd = (insn >> 12) & 0xf;
+ int rn = insn & 0xf;
+ long rdv = regs->uregs[rd];
long rnv = regs->uregs[rn];
- insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn);
+ regs->uregs[rd] = insnslot_2arg_rflags(rdv, rnv, regs->ARM_cpsr, i_fn);
}
static void __kprobes emulate_rd12rm0(struct kprobe *p, struct pt_regs *regs)
regs->uregs[rd] = insnslot_1arg_rwflags(rnv, ®s->ARM_cpsr, i_fn);
}
+static void __kprobes
+emulate_alu_tests_imm(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ int rn = (insn >> 16) & 0xf;
+ long rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn];
+
+ insnslot_1arg_rwflags(rnv, ®s->ARM_cpsr, i_fn);
+}
+
static void __kprobes
emulate_alu_rflags(struct kprobe *p, struct pt_regs *regs)
{
insnslot_3arg_rwflags(rnv, rmv, rsv, ®s->ARM_cpsr, i_fn);
}
+static void __kprobes
+emulate_alu_tests(struct kprobe *p, struct pt_regs *regs)
+{
+ insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0];
+ kprobe_opcode_t insn = p->opcode;
+ long ppc = (long)p->addr + 8;
+ int rn = (insn >> 16) & 0xf;
+ int rs = (insn >> 8) & 0xf; /* rs/rsv may be invalid, don't care. */
+ int rm = insn & 0xf;
+ long rnv = (rn == 15) ? ppc : regs->uregs[rn];
+ long rmv = (rm == 15) ? ppc : regs->uregs[rm];
+ long rsv = regs->uregs[rs];
+
+ insnslot_3arg_rwflags(rnv, rmv, rsv, ®s->ARM_cpsr, i_fn);
+}
+
static enum kprobe_insn __kprobes
prep_emulate_ldr_str(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
- int ibit = (insn & (1 << 26)) ? 25 : 22;
+ int not_imm = (insn & (1 << 26)) ? (insn & (1 << 25))
+ : (~insn & (1 << 22));
+
+ if (is_writeback(insn) && is_r15(insn, 16))
+ return INSN_REJECTED; /* Writeback to PC */
insn &= 0xfff00fff;
insn |= 0x00001000; /* Rn = r0, Rd = r1 */
- if (insn & (1 << ibit)) {
+ if (not_imm) {
insn &= ~0xf;
insn |= 2; /* Rm = r2 */
}
}
static enum kprobe_insn __kprobes
-prep_emulate_rd12rm0(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+prep_emulate_rd12_modify(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
- insn &= 0xffff0ff0; /* Rd = r0, Rm = r0 */
+ if (is_r15(insn, 12))
+ return INSN_REJECTED; /* Rd is PC */
+
+ insn &= 0xffff0fff; /* Rd = r0 */
asi->insn[0] = insn;
- asi->insn_handler = emulate_rd12rm0;
+ asi->insn_handler = emulate_rd12_modify;
return INSN_GOOD;
}
static enum kprobe_insn __kprobes
-prep_emulate_rd12(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+prep_emulate_rd12rn0_modify(kprobe_opcode_t insn,
+ struct arch_specific_insn *asi)
{
- insn &= 0xffff0fff; /* Rd = r0 */
+ if (is_r15(insn, 12))
+ return INSN_REJECTED; /* Rd is PC */
+
+ insn &= 0xffff0ff0; /* Rd = r0 */
+ insn |= 0x00000001; /* Rn = r1 */
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_rd12rn0_modify;
+ return INSN_GOOD;
+}
+
+static enum kprobe_insn __kprobes
+prep_emulate_rd12rm0(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+{
+ if (is_r15(insn, 12))
+ return INSN_REJECTED; /* Rd is PC */
+
+ insn &= 0xffff0ff0; /* Rd = r0, Rm = r0 */
asi->insn[0] = insn;
- asi->insn_handler = emulate_rd12;
+ asi->insn_handler = emulate_rd12rm0;
return INSN_GOOD;
}
prep_emulate_rd12rn16rm0_wflags(kprobe_opcode_t insn,
struct arch_specific_insn *asi)
{
+ if (is_r15(insn, 12))
+ return INSN_REJECTED; /* Rd is PC */
+
insn &= 0xfff00ff0; /* Rd = r0, Rn = r0 */
insn |= 0x00000001; /* Rm = r1 */
asi->insn[0] = insn;
prep_emulate_rd16rs8rm0_wflags(kprobe_opcode_t insn,
struct arch_specific_insn *asi)
{
+ if (is_r15(insn, 16))
+ return INSN_REJECTED; /* Rd is PC */
+
insn &= 0xfff0f0f0; /* Rd = r0, Rs = r0 */
insn |= 0x00000001; /* Rm = r1 */
asi->insn[0] = insn;
prep_emulate_rd16rn12rs8rm0_wflags(kprobe_opcode_t insn,
struct arch_specific_insn *asi)
{
+ if (is_r15(insn, 16))
+ return INSN_REJECTED; /* Rd is PC */
+
insn &= 0xfff000f0; /* Rd = r0, Rn = r0 */
insn |= 0x00000102; /* Rs = r1, Rm = r2 */
asi->insn[0] = insn;
prep_emulate_rdhi16rdlo12rs8rm0_wflags(kprobe_opcode_t insn,
struct arch_specific_insn *asi)
{
+ if (is_r15(insn, 16) || is_r15(insn, 12))
+ return INSN_REJECTED; /* RdHi or RdLo is PC */
+
insn &= 0xfff000f0; /* RdHi = r0, RdLo = r1 */
insn |= 0x00001203; /* Rs = r2, Rm = r3 */
asi->insn[0] = insn;
static enum kprobe_insn __kprobes
space_1111(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
- /* CPS mmod == 1 : 1111 0001 0000 xx10 xxxx xxxx xx0x xxxx */
- /* RFE : 1111 100x x0x1 xxxx xxxx 1010 xxxx xxxx */
- /* SRS : 1111 100x x1x0 1101 xxxx 0101 xxxx xxxx */
- if ((insn & 0xfff30020) == 0xf1020000 ||
- (insn & 0xfe500f00) == 0xf8100a00 ||
- (insn & 0xfe5f0f00) == 0xf84d0500)
- return INSN_REJECTED;
-
- /* PLD : 1111 01x1 x101 xxxx xxxx xxxx xxxx xxxx : */
- if ((insn & 0xfd700000) == 0xf4500000) {
- insn &= 0xfff0ffff; /* Rn = r0 */
- asi->insn[0] = insn;
- asi->insn_handler = emulate_rn16;
- return INSN_GOOD;
+ /* memory hint : 1111 0100 x001 xxxx xxxx xxxx xxxx xxxx : */
+ /* PLDI : 1111 0100 x101 xxxx xxxx xxxx xxxx xxxx : */
+ /* PLDW : 1111 0101 x001 xxxx xxxx xxxx xxxx xxxx : */
+ /* PLD : 1111 0101 x101 xxxx xxxx xxxx xxxx xxxx : */
+ if ((insn & 0xfe300000) == 0xf4100000) {
+ asi->insn_handler = emulate_nop;
+ return INSN_GOOD_NO_SLOT;
}
/* BLX(1) : 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx : */
return INSN_GOOD_NO_SLOT;
}
- /* SETEND : 1111 0001 0000 0001 xxxx xxxx 0000 xxxx */
- /* CDP2 : 1111 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */
- if ((insn & 0xffff00f0) == 0xf1010000 ||
- (insn & 0xff000010) == 0xfe000000) {
- asi->insn[0] = insn;
- asi->insn_handler = emulate_none;
- return INSN_GOOD;
- }
+ /* CPS : 1111 0001 0000 xxx0 xxxx xxxx xx0x xxxx */
+ /* SETEND: 1111 0001 0000 0001 xxxx xxxx 0000 xxxx */
+ /* SRS : 1111 100x x1x0 xxxx xxxx xxxx xxxx xxxx */
+ /* RFE : 1111 100x x0x1 xxxx xxxx xxxx xxxx xxxx */
+
+ /* Coprocessor instructions... */
/* MCRR2 : 1111 1100 0100 xxxx xxxx xxxx xxxx xxxx : (Rd != Rn) */
/* MRRC2 : 1111 1100 0101 xxxx xxxx xxxx xxxx xxxx : (Rd != Rn) */
- if ((insn & 0xffe00000) == 0xfc400000) {
- insn &= 0xfff00fff; /* Rn = r0 */
- insn |= 0x00001000; /* Rd = r1 */
- asi->insn[0] = insn;
- asi->insn_handler =
- (insn & (1 << 20)) ? emulate_mrrc : emulate_mcrr;
- return INSN_GOOD;
- }
+ /* LDC2 : 1111 110x xxx1 xxxx xxxx xxxx xxxx xxxx */
+ /* STC2 : 1111 110x xxx0 xxxx xxxx xxxx xxxx xxxx */
+ /* CDP2 : 1111 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */
+ /* MCR2 : 1111 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */
+ /* MRC2 : 1111 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */
- /* LDC2 : 1111 110x xxx1 xxxx xxxx xxxx xxxx xxxx */
- /* STC2 : 1111 110x xxx0 xxxx xxxx xxxx xxxx xxxx */
- if ((insn & 0xfe000000) == 0xfc000000) {
- insn &= 0xfff0ffff; /* Rn = r0 */
- asi->insn[0] = insn;
- asi->insn_handler = emulate_ldcstc;
- return INSN_GOOD;
- }
-
- /* MCR2 : 1111 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */
- /* MRC2 : 1111 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */
- insn &= 0xffff0fff; /* Rd = r0 */
- asi->insn[0] = insn;
- asi->insn_handler = (insn & (1 << 20)) ? emulate_rd12 : emulate_ird12;
- return INSN_GOOD;
+ return INSN_REJECTED;
}
static enum kprobe_insn __kprobes
/* cccc 0001 0xx0 xxxx xxxx xxxx xxxx xxx0 xxxx */
if ((insn & 0x0f900010) == 0x01000000) {
- /* BXJ : cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx */
- /* MSR : cccc 0001 0x10 xxxx xxxx xxxx 0000 xxxx */
- if ((insn & 0x0ff000f0) == 0x01200020 ||
- (insn & 0x0fb000f0) == 0x01200000)
- return INSN_REJECTED;
-
- /* MRS : cccc 0001 0x00 xxxx xxxx xxxx 0000 xxxx */
- if ((insn & 0x0fb00010) == 0x01000000)
- return prep_emulate_rd12(insn, asi);
+ /* MRS cpsr : cccc 0001 0000 xxxx xxxx xxxx 0000 xxxx */
+ if ((insn & 0x0ff000f0) == 0x01000000) {
+ if (is_r15(insn, 12))
+ return INSN_REJECTED; /* Rd is PC */
+ asi->insn_handler = simulate_mrs;
+ return INSN_GOOD_NO_SLOT;
+ }
/* SMLALxy : cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx */
if ((insn & 0x0ff00090) == 0x01400080)
- return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi);
+ return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn,
+ asi);
/* SMULWy : cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx */
/* SMULxy : cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx */
return prep_emulate_rd16rs8rm0_wflags(insn, asi);
/* SMLAxy : cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx : Q */
- /* SMLAWy : cccc 0001 0010 xxxx xxxx xxxx 0x00 xxxx : Q */
- return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+ /* SMLAWy : cccc 0001 0010 xxxx xxxx xxxx 1x00 xxxx : Q */
+ if ((insn & 0x0ff00090) == 0x01000080 ||
+ (insn & 0x0ff000b0) == 0x01200080)
+ return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+
+ /* BXJ : cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx */
+ /* MSR : cccc 0001 0x10 xxxx xxxx xxxx 0000 xxxx */
+ /* MRS spsr : cccc 0001 0100 xxxx xxxx xxxx 0000 xxxx */
+ /* Other instruction encodings aren't yet defined */
+ return INSN_REJECTED;
}
/* cccc 0001 0xx0 xxxx xxxx xxxx xxxx 0xx1 xxxx */
else if ((insn & 0x0f900090) == 0x01000010) {
- /* BKPT : 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */
- if ((insn & 0xfff000f0) == 0xe1200070)
- return INSN_REJECTED;
-
/* BLX(2) : cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx */
/* BX : cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx */
if ((insn & 0x0ff000d0) == 0x01200010) {
- asi->insn[0] = truecc_insn(insn);
+ if ((insn & 0x0ff000ff) == 0x0120003f)
+ return INSN_REJECTED; /* BLX pc */
asi->insn_handler = simulate_blx2bx;
- return INSN_GOOD;
+ return INSN_GOOD_NO_SLOT;
}
/* CLZ : cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx */
/* QSUB : cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx :Q */
/* QDADD : cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx :Q */
/* QDSUB : cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx :Q */
- return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+ if ((insn & 0x0f9000f0) == 0x01000050)
+ return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+
+ /* BKPT : 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */
+ /* SMC : cccc 0001 0110 xxxx xxxx xxxx 0111 xxxx */
+
+ /* Other instruction encodings aren't yet defined */
+ return INSN_REJECTED;
}
/* cccc 0000 xxxx xxxx xxxx xxxx xxxx 1001 xxxx */
- else if ((insn & 0x0f000090) == 0x00000090) {
+ else if ((insn & 0x0f0000f0) == 0x00000090) {
/* MUL : cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx : */
/* MULS : cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx :cc */
/* MLA : cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx : */
/* MLAS : cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx :cc */
/* UMAAL : cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx : */
+ /* undef : cccc 0000 0101 xxxx xxxx xxxx 1001 xxxx : */
+ /* MLS : cccc 0000 0110 xxxx xxxx xxxx 1001 xxxx : */
+ /* undef : cccc 0000 0111 xxxx xxxx xxxx 1001 xxxx : */
/* UMULL : cccc 0000 1000 xxxx xxxx xxxx 1001 xxxx : */
/* UMULLS : cccc 0000 1001 xxxx xxxx xxxx 1001 xxxx :cc */
/* UMLAL : cccc 0000 1010 xxxx xxxx xxxx 1001 xxxx : */
/* SMULLS : cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx :cc */
/* SMLAL : cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx : */
/* SMLALS : cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx :cc */
- if ((insn & 0x0fe000f0) == 0x00000090) {
- return prep_emulate_rd16rs8rm0_wflags(insn, asi);
- } else if ((insn & 0x0fe000f0) == 0x00200090) {
- return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
- } else {
- return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi);
- }
+ if ((insn & 0x00d00000) == 0x00500000)
+ return INSN_REJECTED;
+ else if ((insn & 0x00e00000) == 0x00000000)
+ return prep_emulate_rd16rs8rm0_wflags(insn, asi);
+ else if ((insn & 0x00a00000) == 0x00200000)
+ return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+ else
+ return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn,
+ asi);
}
/* cccc 000x xxxx xxxx xxxx xxxx xxxx 1xx1 xxxx */
/* SWP : cccc 0001 0000 xxxx xxxx xxxx 1001 xxxx */
/* SWPB : cccc 0001 0100 xxxx xxxx xxxx 1001 xxxx */
- /* LDRD : cccc 000x xxx0 xxxx xxxx xxxx 1101 xxxx */
- /* STRD : cccc 000x xxx0 xxxx xxxx xxxx 1111 xxxx */
+ /* ??? : cccc 0001 0x01 xxxx xxxx xxxx 1001 xxxx */
+ /* ??? : cccc 0001 0x10 xxxx xxxx xxxx 1001 xxxx */
+ /* ??? : cccc 0001 0x11 xxxx xxxx xxxx 1001 xxxx */
/* STREX : cccc 0001 1000 xxxx xxxx xxxx 1001 xxxx */
/* LDREX : cccc 0001 1001 xxxx xxxx xxxx 1001 xxxx */
+ /* STREXD: cccc 0001 1010 xxxx xxxx xxxx 1001 xxxx */
+ /* LDREXD: cccc 0001 1011 xxxx xxxx xxxx 1001 xxxx */
+ /* STREXB: cccc 0001 1100 xxxx xxxx xxxx 1001 xxxx */
+ /* LDREXB: cccc 0001 1101 xxxx xxxx xxxx 1001 xxxx */
+ /* STREXH: cccc 0001 1110 xxxx xxxx xxxx 1001 xxxx */
+ /* LDREXH: cccc 0001 1111 xxxx xxxx xxxx 1001 xxxx */
+
+ /* LDRD : cccc 000x xxx0 xxxx xxxx xxxx 1101 xxxx */
+ /* STRD : cccc 000x xxx0 xxxx xxxx xxxx 1111 xxxx */
/* LDRH : cccc 000x xxx1 xxxx xxxx xxxx 1011 xxxx */
/* STRH : cccc 000x xxx0 xxxx xxxx xxxx 1011 xxxx */
/* LDRSB : cccc 000x xxx1 xxxx xxxx xxxx 1101 xxxx */
/* LDRSH : cccc 000x xxx1 xxxx xxxx xxxx 1111 xxxx */
- if ((insn & 0x0fb000f0) == 0x01000090) {
- /* SWP/SWPB */
- return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+ if ((insn & 0x0f0000f0) == 0x01000090) {
+ if ((insn & 0x0fb000f0) == 0x01000090) {
+ /* SWP/SWPB */
+ return prep_emulate_rd12rn16rm0_wflags(insn,
+ asi);
+ } else {
+ /* STREX/LDREX variants and unallocaed space */
+ return INSN_REJECTED;
+ }
+
} else if ((insn & 0x0e1000d0) == 0x00000d0) {
/* STRD/LDRD */
+ if ((insn & 0x0000e000) == 0x0000e000)
+ return INSN_REJECTED; /* Rd is LR or PC */
+ if (is_writeback(insn) && is_r15(insn, 16))
+ return INSN_REJECTED; /* Writeback to PC */
+
insn &= 0xfff00fff;
insn |= 0x00002000; /* Rn = r0, Rd = r2 */
- if (insn & (1 << 22)) {
- /* I bit */
+ if (!(insn & (1 << 22))) {
+ /* Register index */
insn &= ~0xf;
insn |= 1; /* Rm = r1 */
}
return INSN_GOOD;
}
+ /* LDRH/STRH/LDRSB/LDRSH */
+ if (is_r15(insn, 12))
+ return INSN_REJECTED; /* Rd is PC */
return prep_emulate_ldr_str(insn, asi);
}
/*
* ALU op with S bit and Rd == 15 :
- * cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx
+ * cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx
*/
if ((insn & 0x0e10f000) == 0x0010f000)
return INSN_REJECTED;
insn |= 0x00000200; /* Rs = r2 */
}
asi->insn[0] = insn;
- asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */
+
+ if ((insn & 0x0f900000) == 0x01100000) {
+ /*
+ * TST : cccc 0001 0001 xxxx xxxx xxxx xxxx xxxx
+ * TEQ : cccc 0001 0011 xxxx xxxx xxxx xxxx xxxx
+ * CMP : cccc 0001 0101 xxxx xxxx xxxx xxxx xxxx
+ * CMN : cccc 0001 0111 xxxx xxxx xxxx xxxx xxxx
+ */
+ asi->insn_handler = emulate_alu_tests;
+ } else {
+ /* ALU ops which write to Rd */
+ asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */
emulate_alu_rwflags : emulate_alu_rflags;
+ }
return INSN_GOOD;
}
static enum kprobe_insn __kprobes
space_cccc_001x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
+ /* MOVW : cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */
+ /* MOVT : cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */
+ if ((insn & 0x0fb00000) == 0x03000000)
+ return prep_emulate_rd12_modify(insn, asi);
+
+ /* hints : cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */
+ if ((insn & 0x0fff0000) == 0x03200000) {
+ unsigned op2 = insn & 0x000000ff;
+ if (op2 == 0x01 || op2 == 0x04) {
+ /* YIELD : cccc 0011 0010 0000 xxxx xxxx 0000 0001 */
+ /* SEV : cccc 0011 0010 0000 xxxx xxxx 0000 0100 */
+ asi->insn[0] = insn;
+ asi->insn_handler = emulate_none;
+ return INSN_GOOD;
+ } else if (op2 <= 0x03) {
+ /* NOP : cccc 0011 0010 0000 xxxx xxxx 0000 0000 */
+ /* WFE : cccc 0011 0010 0000 xxxx xxxx 0000 0010 */
+ /* WFI : cccc 0011 0010 0000 xxxx xxxx 0000 0011 */
+ /*
+ * We make WFE and WFI true NOPs to avoid stalls due
+ * to missing events whilst processing the probe.
+ */
+ asi->insn_handler = emulate_nop;
+ return INSN_GOOD_NO_SLOT;
+ }
+ /* For DBG and unallocated hints it's safest to reject them */
+ return INSN_REJECTED;
+ }
+
/*
* MSR : cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx
- * Undef : cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx
* ALU op with S bit and Rd == 15 :
* cccc 001x xxx1 xxxx 1111 xxxx xxxx xxxx
*/
if ((insn & 0x0fb00000) == 0x03200000 || /* MSR */
- (insn & 0x0ff00000) == 0x03400000 || /* Undef */
(insn & 0x0e10f000) == 0x0210f000) /* ALU s-bit, R15 */
return INSN_REJECTED;
* *S (bit 20) updates condition codes
* ADC/SBC/RSC reads the C flag
*/
- insn &= 0xffff0fff; /* Rd = r0 */
+ insn &= 0xfff00fff; /* Rn = r0 and Rd = r0 */
asi->insn[0] = insn;
- asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */
+
+ if ((insn & 0x0f900000) == 0x03100000) {
+ /*
+ * TST : cccc 0011 0001 xxxx xxxx xxxx xxxx xxxx
+ * TEQ : cccc 0011 0011 xxxx xxxx xxxx xxxx xxxx
+ * CMP : cccc 0011 0101 xxxx xxxx xxxx xxxx xxxx
+ * CMN : cccc 0011 0111 xxxx xxxx xxxx xxxx xxxx
+ */
+ asi->insn_handler = emulate_alu_tests_imm;
+ } else {
+ /* ALU ops which write to Rd */
+ asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */
emulate_alu_imm_rwflags : emulate_alu_imm_rflags;
+ }
return INSN_GOOD;
}
{
/* SEL : cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx GE: !!! */
if ((insn & 0x0ff000f0) == 0x068000b0) {
+ if (is_r15(insn, 12))
+ return INSN_REJECTED; /* Rd is PC */
insn &= 0xfff00ff0; /* Rd = r0, Rn = r0 */
insn |= 0x00000001; /* Rm = r1 */
asi->insn[0] = insn;
/* USAT16 : cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx :Q */
if ((insn & 0x0fa00030) == 0x06a00010 ||
(insn & 0x0fb000f0) == 0x06a00030) {
+ if (is_r15(insn, 12))
+ return INSN_REJECTED; /* Rd is PC */
insn &= 0xffff0ff0; /* Rd = r0, Rm = r0 */
asi->insn[0] = insn;
asi->insn_handler = emulate_sat;
/* REV : cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx */
/* REV16 : cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx */
+ /* RBIT : cccc 0110 1111 xxxx xxxx xxxx 0011 xxxx */
/* REVSH : cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx */
if ((insn & 0x0ff00070) == 0x06b00030 ||
- (insn & 0x0ff000f0) == 0x06f000b0)
+ (insn & 0x0ff00070) == 0x06f00030)
return prep_emulate_rd12rm0(insn, asi);
+ /* ??? : cccc 0110 0000 xxxx xxxx xxxx xxx1 xxxx : */
/* SADD16 : cccc 0110 0001 xxxx xxxx xxxx 0001 xxxx :GE */
/* SADDSUBX : cccc 0110 0001 xxxx xxxx xxxx 0011 xxxx :GE */
/* SSUBADDX : cccc 0110 0001 xxxx xxxx xxxx 0101 xxxx :GE */
/* SSUB16 : cccc 0110 0001 xxxx xxxx xxxx 0111 xxxx :GE */
/* SADD8 : cccc 0110 0001 xxxx xxxx xxxx 1001 xxxx :GE */
+ /* ??? : cccc 0110 0001 xxxx xxxx xxxx 1011 xxxx : */
+ /* ??? : cccc 0110 0001 xxxx xxxx xxxx 1101 xxxx : */
/* SSUB8 : cccc 0110 0001 xxxx xxxx xxxx 1111 xxxx :GE */
/* QADD16 : cccc 0110 0010 xxxx xxxx xxxx 0001 xxxx : */
/* QADDSUBX : cccc 0110 0010 xxxx xxxx xxxx 0011 xxxx : */
/* QSUBADDX : cccc 0110 0010 xxxx xxxx xxxx 0101 xxxx : */
/* QSUB16 : cccc 0110 0010 xxxx xxxx xxxx 0111 xxxx : */
/* QADD8 : cccc 0110 0010 xxxx xxxx xxxx 1001 xxxx : */
+ /* ??? : cccc 0110 0010 xxxx xxxx xxxx 1011 xxxx : */
+ /* ??? : cccc 0110 0010 xxxx xxxx xxxx 1101 xxxx : */
/* QSUB8 : cccc 0110 0010 xxxx xxxx xxxx 1111 xxxx : */
/* SHADD16 : cccc 0110 0011 xxxx xxxx xxxx 0001 xxxx : */
/* SHADDSUBX : cccc 0110 0011 xxxx xxxx xxxx 0011 xxxx : */
/* SHSUBADDX : cccc 0110 0011 xxxx xxxx xxxx 0101 xxxx : */
/* SHSUB16 : cccc 0110 0011 xxxx xxxx xxxx 0111 xxxx : */
/* SHADD8 : cccc 0110 0011 xxxx xxxx xxxx 1001 xxxx : */
+ /* ??? : cccc 0110 0011 xxxx xxxx xxxx 1011 xxxx : */
+ /* ??? : cccc 0110 0011 xxxx xxxx xxxx 1101 xxxx : */
/* SHSUB8 : cccc 0110 0011 xxxx xxxx xxxx 1111 xxxx : */
+ /* ??? : cccc 0110 0100 xxxx xxxx xxxx xxx1 xxxx : */
/* UADD16 : cccc 0110 0101 xxxx xxxx xxxx 0001 xxxx :GE */
/* UADDSUBX : cccc 0110 0101 xxxx xxxx xxxx 0011 xxxx :GE */
/* USUBADDX : cccc 0110 0101 xxxx xxxx xxxx 0101 xxxx :GE */
/* USUB16 : cccc 0110 0101 xxxx xxxx xxxx 0111 xxxx :GE */
/* UADD8 : cccc 0110 0101 xxxx xxxx xxxx 1001 xxxx :GE */
+ /* ??? : cccc 0110 0101 xxxx xxxx xxxx 1011 xxxx : */
+ /* ??? : cccc 0110 0101 xxxx xxxx xxxx 1101 xxxx : */
/* USUB8 : cccc 0110 0101 xxxx xxxx xxxx 1111 xxxx :GE */
/* UQADD16 : cccc 0110 0110 xxxx xxxx xxxx 0001 xxxx : */
/* UQADDSUBX : cccc 0110 0110 xxxx xxxx xxxx 0011 xxxx : */
/* UQSUBADDX : cccc 0110 0110 xxxx xxxx xxxx 0101 xxxx : */
/* UQSUB16 : cccc 0110 0110 xxxx xxxx xxxx 0111 xxxx : */
/* UQADD8 : cccc 0110 0110 xxxx xxxx xxxx 1001 xxxx : */
+ /* ??? : cccc 0110 0110 xxxx xxxx xxxx 1011 xxxx : */
+ /* ??? : cccc 0110 0110 xxxx xxxx xxxx 1101 xxxx : */
/* UQSUB8 : cccc 0110 0110 xxxx xxxx xxxx 1111 xxxx : */
/* UHADD16 : cccc 0110 0111 xxxx xxxx xxxx 0001 xxxx : */
/* UHADDSUBX : cccc 0110 0111 xxxx xxxx xxxx 0011 xxxx : */
/* UHSUBADDX : cccc 0110 0111 xxxx xxxx xxxx 0101 xxxx : */
/* UHSUB16 : cccc 0110 0111 xxxx xxxx xxxx 0111 xxxx : */
/* UHADD8 : cccc 0110 0111 xxxx xxxx xxxx 1001 xxxx : */
+ /* ??? : cccc 0110 0111 xxxx xxxx xxxx 1011 xxxx : */
+ /* ??? : cccc 0110 0111 xxxx xxxx xxxx 1101 xxxx : */
/* UHSUB8 : cccc 0110 0111 xxxx xxxx xxxx 1111 xxxx : */
+ if ((insn & 0x0f800010) == 0x06000010) {
+ if ((insn & 0x00300000) == 0x00000000 ||
+ (insn & 0x000000e0) == 0x000000a0 ||
+ (insn & 0x000000e0) == 0x000000c0)
+ return INSN_REJECTED; /* Unallocated space */
+ return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+ }
+
/* PKHBT : cccc 0110 1000 xxxx xxxx xxxx x001 xxxx : */
/* PKHTB : cccc 0110 1000 xxxx xxxx xxxx x101 xxxx : */
+ if ((insn & 0x0ff00030) == 0x06800010)
+ return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+
/* SXTAB16 : cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx : */
- /* SXTB : cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx : */
+ /* SXTB16 : cccc 0110 1000 1111 xxxx xxxx 0111 xxxx : */
+ /* ??? : cccc 0110 1001 xxxx xxxx xxxx 0111 xxxx : */
/* SXTAB : cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx : */
+ /* SXTB : cccc 0110 1010 1111 xxxx xxxx 0111 xxxx : */
/* SXTAH : cccc 0110 1011 xxxx xxxx xxxx 0111 xxxx : */
+ /* SXTH : cccc 0110 1011 1111 xxxx xxxx 0111 xxxx : */
/* UXTAB16 : cccc 0110 1100 xxxx xxxx xxxx 0111 xxxx : */
+ /* UXTB16 : cccc 0110 1100 1111 xxxx xxxx 0111 xxxx : */
+ /* ??? : cccc 0110 1101 xxxx xxxx xxxx 0111 xxxx : */
/* UXTAB : cccc 0110 1110 xxxx xxxx xxxx 0111 xxxx : */
+ /* UXTB : cccc 0110 1110 1111 xxxx xxxx 0111 xxxx : */
/* UXTAH : cccc 0110 1111 xxxx xxxx xxxx 0111 xxxx : */
- return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+ /* UXTH : cccc 0110 1111 1111 xxxx xxxx 0111 xxxx : */
+ if ((insn & 0x0f8000f0) == 0x06800070) {
+ if ((insn & 0x00300000) == 0x00100000)
+ return INSN_REJECTED; /* Unallocated space */
+
+ if ((insn & 0x000f0000) == 0x000f0000)
+ return prep_emulate_rd12rm0(insn, asi);
+ else
+ return prep_emulate_rd12rn16rm0_wflags(insn, asi);
+ }
+
+ /* Other instruction encodings aren't yet defined */
+ return INSN_REJECTED;
}
static enum kprobe_insn __kprobes
if ((insn & 0x0ff000f0) == 0x03f000f0)
return INSN_REJECTED;
- /* USADA8 : cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx */
- /* USAD8 : cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx */
- if ((insn & 0x0ff000f0) == 0x07800010)
- return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
-
/* SMLALD : cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx */
/* SMLSLD : cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx */
if ((insn & 0x0ff00090) == 0x07400010)
return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi);
/* SMLAD : cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx :Q */
+ /* SMUAD : cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx :Q */
/* SMLSD : cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx :Q */
+ /* SMUSD : cccc 0111 0000 xxxx 1111 xxxx 01x1 xxxx : */
/* SMMLA : cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx : */
- /* SMMLS : cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx : */
+ /* SMMUL : cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx : */
+ /* USADA8 : cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx : */
+ /* USAD8 : cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx : */
if ((insn & 0x0ff00090) == 0x07000010 ||
(insn & 0x0ff000d0) == 0x07500010 ||
- (insn & 0x0ff000d0) == 0x075000d0)
+ (insn & 0x0ff000f0) == 0x07800010) {
+
+ if ((insn & 0x0000f000) == 0x0000f000)
+ return prep_emulate_rd16rs8rm0_wflags(insn, asi);
+ else
+ return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
+ }
+
+ /* SMMLS : cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx : */
+ if ((insn & 0x0ff000d0) == 0x075000d0)
return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi);
- /* SMUSD : cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx : */
- /* SMUAD : cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx :Q */
- /* SMMUL : cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx : */
- return prep_emulate_rd16rs8rm0_wflags(insn, asi);
+ /* SBFX : cccc 0111 101x xxxx xxxx xxxx x101 xxxx : */
+ /* UBFX : cccc 0111 111x xxxx xxxx xxxx x101 xxxx : */
+ if ((insn & 0x0fa00070) == 0x07a00050)
+ return prep_emulate_rd12rm0(insn, asi);
+
+ /* BFI : cccc 0111 110x xxxx xxxx xxxx x001 xxxx : */
+ /* BFC : cccc 0111 110x xxxx xxxx xxxx x001 1111 : */
+ if ((insn & 0x0fe00070) == 0x07c00010) {
+
+ if ((insn & 0x0000000f) == 0x0000000f)
+ return prep_emulate_rd12_modify(insn, asi);
+ else
+ return prep_emulate_rd12rn0_modify(insn, asi);
+ }
+
+ return INSN_REJECTED;
}
static enum kprobe_insn __kprobes
/* STRB : cccc 01xx x1x0 xxxx xxxx xxxx xxxx xxxx */
/* STRBT : cccc 01x0 x110 xxxx xxxx xxxx xxxx xxxx */
/* STRT : cccc 01x0 x010 xxxx xxxx xxxx xxxx xxxx */
+
+ if ((insn & 0x00500000) == 0x00500000 && is_r15(insn, 12))
+ return INSN_REJECTED; /* LDRB into PC */
+
return prep_emulate_ldr_str(insn, asi);
}
/* LDM(1) : cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx */
/* STM(1) : cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx */
- asi->insn[0] = truecc_insn(insn);
asi->insn_handler = ((insn & 0x108000) == 0x008000) ? /* STM & R15 */
simulate_stm1_pc : simulate_ldm1stm1;
- return INSN_GOOD;
+ return INSN_GOOD_NO_SLOT;
}
static enum kprobe_insn __kprobes
{
/* B : cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx */
/* BL : cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx */
- asi->insn[0] = truecc_insn(insn);
asi->insn_handler = simulate_bbl;
- return INSN_GOOD;
+ return INSN_GOOD_NO_SLOT;
}
static enum kprobe_insn __kprobes
-space_cccc_1100_010x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+space_cccc_11xx(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
+ /* Coprocessor instructions... */
/* MCRR : cccc 1100 0100 xxxx xxxx xxxx xxxx xxxx : (Rd!=Rn) */
/* MRRC : cccc 1100 0101 xxxx xxxx xxxx xxxx xxxx : (Rd!=Rn) */
- insn &= 0xfff00fff;
- insn |= 0x00001000; /* Rn = r0, Rd = r1 */
- asi->insn[0] = insn;
- asi->insn_handler = (insn & (1 << 20)) ? emulate_mrrc : emulate_mcrr;
- return INSN_GOOD;
+ /* LDC : cccc 110x xxx1 xxxx xxxx xxxx xxxx xxxx */
+ /* STC : cccc 110x xxx0 xxxx xxxx xxxx xxxx xxxx */
+ /* CDP : cccc 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */
+ /* MCR : cccc 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */
+ /* MRC : cccc 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */
+
+ /* SVC : cccc 1111 xxxx xxxx xxxx xxxx xxxx xxxx */
+
+ return INSN_REJECTED;
}
-static enum kprobe_insn __kprobes
-space_cccc_110x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+static unsigned long __kprobes __check_eq(unsigned long cpsr)
{
- /* LDC : cccc 110x xxx1 xxxx xxxx xxxx xxxx xxxx */
- /* STC : cccc 110x xxx0 xxxx xxxx xxxx xxxx xxxx */
- insn &= 0xfff0ffff; /* Rn = r0 */
- asi->insn[0] = insn;
- asi->insn_handler = emulate_ldcstc;
- return INSN_GOOD;
+ return cpsr & PSR_Z_BIT;
}
-static enum kprobe_insn __kprobes
-space_cccc_111x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+static unsigned long __kprobes __check_ne(unsigned long cpsr)
{
- /* BKPT : 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */
- /* SWI : cccc 1111 xxxx xxxx xxxx xxxx xxxx xxxx */
- if ((insn & 0xfff000f0) == 0xe1200070 ||
- (insn & 0x0f000000) == 0x0f000000)
- return INSN_REJECTED;
+ return (~cpsr) & PSR_Z_BIT;
+}
- /* CDP : cccc 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */
- if ((insn & 0x0f000010) == 0x0e000000) {
- asi->insn[0] = insn;
- asi->insn_handler = emulate_none;
- return INSN_GOOD;
- }
+static unsigned long __kprobes __check_cs(unsigned long cpsr)
+{
+ return cpsr & PSR_C_BIT;
+}
- /* MCR : cccc 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */
- /* MRC : cccc 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */
- insn &= 0xffff0fff; /* Rd = r0 */
- asi->insn[0] = insn;
- asi->insn_handler = (insn & (1 << 20)) ? emulate_rd12 : emulate_ird12;
- return INSN_GOOD;
+static unsigned long __kprobes __check_cc(unsigned long cpsr)
+{
+ return (~cpsr) & PSR_C_BIT;
+}
+
+static unsigned long __kprobes __check_mi(unsigned long cpsr)
+{
+ return cpsr & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_pl(unsigned long cpsr)
+{
+ return (~cpsr) & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_vs(unsigned long cpsr)
+{
+ return cpsr & PSR_V_BIT;
+}
+
+static unsigned long __kprobes __check_vc(unsigned long cpsr)
+{
+ return (~cpsr) & PSR_V_BIT;
+}
+
+static unsigned long __kprobes __check_hi(unsigned long cpsr)
+{
+ cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
+ return cpsr & PSR_C_BIT;
}
+static unsigned long __kprobes __check_ls(unsigned long cpsr)
+{
+ cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
+ return (~cpsr) & PSR_C_BIT;
+}
+
+static unsigned long __kprobes __check_ge(unsigned long cpsr)
+{
+ cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
+ return (~cpsr) & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_lt(unsigned long cpsr)
+{
+ cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
+ return cpsr & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_gt(unsigned long cpsr)
+{
+ unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
+ temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */
+ return (~temp) & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_le(unsigned long cpsr)
+{
+ unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
+ temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */
+ return temp & PSR_N_BIT;
+}
+
+static unsigned long __kprobes __check_al(unsigned long cpsr)
+{
+ return true;
+}
+
+static kprobe_check_cc * const condition_checks[16] = {
+ &__check_eq, &__check_ne, &__check_cs, &__check_cc,
+ &__check_mi, &__check_pl, &__check_vs, &__check_vc,
+ &__check_hi, &__check_ls, &__check_ge, &__check_lt,
+ &__check_gt, &__check_le, &__check_al, &__check_al
+};
+
/* Return:
* INSN_REJECTED If instruction is one not allowed to kprobe,
* INSN_GOOD If instruction is supported and uses instruction slot,
enum kprobe_insn __kprobes
arm_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
+ asi->insn_check_cc = condition_checks[insn>>28];
asi->insn[1] = KPROBE_RETURN_INSTRUCTION;
- if ((insn & 0xf0000000) == 0xf0000000) {
+ if ((insn & 0xf0000000) == 0xf0000000)
return space_1111(insn, asi);
- } else if ((insn & 0x0e000000) == 0x00000000) {
+ else if ((insn & 0x0e000000) == 0x00000000)
return space_cccc_000x(insn, asi);
- } else if ((insn & 0x0e000000) == 0x02000000) {
+ else if ((insn & 0x0e000000) == 0x02000000)
return space_cccc_001x(insn, asi);
- } else if ((insn & 0x0f000010) == 0x06000010) {
+ else if ((insn & 0x0f000010) == 0x06000010)
return space_cccc_0110__1(insn, asi);
- } else if ((insn & 0x0f000010) == 0x07000010) {
+ else if ((insn & 0x0f000010) == 0x07000010)
return space_cccc_0111__1(insn, asi);
- } else if ((insn & 0x0c000000) == 0x04000000) {
+ else if ((insn & 0x0c000000) == 0x04000000)
return space_cccc_01xx(insn, asi);
- } else if ((insn & 0x0e000000) == 0x08000000) {
+ else if ((insn & 0x0e000000) == 0x08000000)
return space_cccc_100x(insn, asi);
- } else if ((insn & 0x0e000000) == 0x0a000000) {
+ else if ((insn & 0x0e000000) == 0x0a000000)
return space_cccc_101x(insn, asi);
- } else if ((insn & 0x0fe00000) == 0x0c400000) {
-
- return space_cccc_1100_010x(insn, asi);
-
- } else if ((insn & 0x0e000000) == 0x0c000000) {
-
- return space_cccc_110x(insn, asi);
-
- }
-
- return space_cccc_111x(insn, asi);
+ return space_cccc_11xx(insn, asi);
}
void __init arm_kprobe_decode_init(void)
{
find_str_pc_offset();
}
-
-
-/*
- * All ARM instructions listed below.
- *
- * Instructions and their general purpose registers are given.
- * If a particular register may not use R15, it is prefixed with a "!".
- * If marked with a "*" means the value returned by reading R15
- * is implementation defined.
- *
- * ADC/ADD/AND/BIC/CMN/CMP/EOR/MOV/MVN/ORR/RSB/RSC/SBC/SUB/TEQ
- * TST: Rd, Rn, Rm, !Rs
- * BX: Rm
- * BLX(2): !Rm
- * BX: Rm (R15 legal, but discouraged)
- * BXJ: !Rm,
- * CLZ: !Rd, !Rm
- * CPY: Rd, Rm
- * LDC/2,STC/2 immediate offset & unindex: Rn
- * LDC/2,STC/2 immediate pre/post-indexed: !Rn
- * LDM(1/3): !Rn, register_list
- * LDM(2): !Rn, !register_list
- * LDR,STR,PLD immediate offset: Rd, Rn
- * LDR,STR,PLD register offset: Rd, Rn, !Rm
- * LDR,STR,PLD scaled register offset: Rd, !Rn, !Rm
- * LDR,STR immediate pre/post-indexed: Rd, !Rn
- * LDR,STR register pre/post-indexed: Rd, !Rn, !Rm
- * LDR,STR scaled register pre/post-indexed: Rd, !Rn, !Rm
- * LDRB,STRB immediate offset: !Rd, Rn
- * LDRB,STRB register offset: !Rd, Rn, !Rm
- * LDRB,STRB scaled register offset: !Rd, !Rn, !Rm
- * LDRB,STRB immediate pre/post-indexed: !Rd, !Rn
- * LDRB,STRB register pre/post-indexed: !Rd, !Rn, !Rm
- * LDRB,STRB scaled register pre/post-indexed: !Rd, !Rn, !Rm
- * LDRT,LDRBT,STRBT immediate pre/post-indexed: !Rd, !Rn
- * LDRT,LDRBT,STRBT register pre/post-indexed: !Rd, !Rn, !Rm
- * LDRT,LDRBT,STRBT scaled register pre/post-indexed: !Rd, !Rn, !Rm
- * LDRH/SH/SB/D,STRH/SH/SB/D immediate offset: !Rd, Rn
- * LDRH/SH/SB/D,STRH/SH/SB/D register offset: !Rd, Rn, !Rm
- * LDRH/SH/SB/D,STRH/SH/SB/D immediate pre/post-indexed: !Rd, !Rn
- * LDRH/SH/SB/D,STRH/SH/SB/D register pre/post-indexed: !Rd, !Rn, !Rm
- * LDREX: !Rd, !Rn
- * MCR/2: !Rd
- * MCRR/2,MRRC/2: !Rd, !Rn
- * MLA: !Rd, !Rn, !Rm, !Rs
- * MOV: Rd
- * MRC/2: !Rd (if Rd==15, only changes cond codes, not the register)
- * MRS,MSR: !Rd
- * MUL: !Rd, !Rm, !Rs
- * PKH{BT,TB}: !Rd, !Rn, !Rm
- * QDADD,[U]QADD/16/8/SUBX: !Rd, !Rm, !Rn
- * QDSUB,[U]QSUB/16/8/ADDX: !Rd, !Rm, !Rn
- * REV/16/SH: !Rd, !Rm
- * RFE: !Rn
- * {S,U}[H]ADD{16,8,SUBX},{S,U}[H]SUB{16,8,ADDX}: !Rd, !Rn, !Rm
- * SEL: !Rd, !Rn, !Rm
- * SMLA<x><y>,SMLA{D,W<y>},SMLSD,SMML{A,S}: !Rd, !Rn, !Rm, !Rs
- * SMLAL<x><y>,SMLA{D,LD},SMLSLD,SMMULL,SMULW<y>: !RdHi, !RdLo, !Rm, !Rs
- * SMMUL,SMUAD,SMUL<x><y>,SMUSD: !Rd, !Rm, !Rs
- * SSAT/16: !Rd, !Rm
- * STM(1/2): !Rn, register_list* (R15 in reg list not recommended)
- * STRT immediate pre/post-indexed: Rd*, !Rn
- * STRT register pre/post-indexed: Rd*, !Rn, !Rm
- * STRT scaled register pre/post-indexed: Rd*, !Rn, !Rm
- * STREX: !Rd, !Rn, !Rm
- * SWP/B: !Rd, !Rn, !Rm
- * {S,U}XTA{B,B16,H}: !Rd, !Rn, !Rm
- * {S,U}XT{B,B16,H}: !Rd, !Rm
- * UM{AA,LA,UL}L: !RdHi, !RdLo, !Rm, !Rs
- * USA{D8,A8,T,T16}: !Rd, !Rm, !Rs
- *
- * May transfer control by writing R15 (possible mode changes or alternate
- * mode accesses marked by "*"):
- * ALU op (* with s-bit), B, BL, BKPT, BLX(1/2), BX, BXJ, CPS*, CPY,
- * LDM(1), LDM(2/3)*, LDR, MOV, RFE*, SWI*
- *
- * Instructions that do not take general registers, nor transfer control:
- * CDP/2, SETEND, SRS*
- */
struct kprobe_ctlblk *kcb)
{
regs->ARM_pc += 4;
- p->ainsn.insn_handler(p, regs);
+ if (p->ainsn.insn_check_cc(regs->ARM_cpsr))
+ p->ainsn.insn_handler(p, regs);
}
/*
prev_raw_count &= armpmu->max_period;
if (overflow)
- delta = armpmu->max_period - prev_raw_count + new_raw_count;
+ delta = armpmu->max_period - prev_raw_count + new_raw_count + 1;
else
delta = new_raw_count - prev_raw_count;
tail = (struct frame_tail __user *)regs->ARM_fp - 1;
- while (tail && !((unsigned long)tail & 0x3))
+ while ((entry->nr < PERF_MAX_STACK_DEPTH) &&
+ tail && !((unsigned long)tail & 0x3))
tail = user_backtrace(tail, entry);
}
if (clone_flags & CLONE_SETTLS)
thread->tp_value = regs->ARM_r3;
+ thread_notify(THREAD_NOTIFY_COPY, thread);
+
return 0;
}
#ifdef CONFIG_HAVE_HW_BREAKPOINT
case PTRACE_GETHBPREGS:
+ if (ptrace_get_breakpoints(child) < 0)
+ return -ESRCH;
+
ret = ptrace_gethbpregs(child, addr,
(unsigned long __user *)data);
+ ptrace_put_breakpoints(child);
break;
case PTRACE_SETHBPREGS:
+ if (ptrace_get_breakpoints(child) < 0)
+ return -ESRCH;
+
ret = ptrace_sethbpregs(child, addr,
(unsigned long __user *)data);
+ ptrace_put_breakpoints(child);
break;
#endif
{
}
-static void broadcast_timer_setup(struct clock_event_device *evt)
+static void __cpuinit broadcast_timer_setup(struct clock_event_device *evt)
{
evt->name = "dummy_timer";
evt->features = CLOCK_EVT_FEAT_ONESHOT |
long err;
int i;
- if (nsops < 1)
+ if (nsops < 1 || nsops > SEMOPM)
return -EINVAL;
sops = kmalloc(sizeof(*sops) * nsops, GFP_KERNEL);
if (!sops)
struct thread_info *thread = current_thread_info();
siginfo_t info;
- if (current->personality != PER_LINUX &&
- current->personality != PER_LINUX_32BIT &&
+ if ((current->personality & PER_MASK) != PER_LINUX &&
thread->exec_domain->handler) {
thread->exec_domain->handler(n, regs);
return regs->ARM_r0;
select CPU_ARM926T
select GENERIC_CLOCKEVENTS
select HAVE_FB_ATMEL
+ select HAVE_NET_MACB
config ARCH_AT572D940HF
bool "AT572D940HF"
#include <mach/board.h>
#include "generic.h"
+static void __init at91eb01_init_irq(void)
+{
+ at91x40_init_interrupts(NULL);
+}
+
static void __init at91eb01_map_io(void)
{
at91x40_initialize(40000000);
MACHINE_START(AT91EB01, "Atmel AT91 EB01")
/* Maintainer: Greg Ungerer <gerg@snapgear.com> */
.timer = &at91x40_timer,
- .init_irq = at91x40_init_interrupts,
+ .init_irq = at91eb01_init_irq,
.map_io = at91eb01_map_io,
MACHINE_END
#define ARCH_ID_AT91SAM9G45 0x819b05a0
#define ARCH_ID_AT91SAM9G45MRL 0x819b05a2 /* aka 9G45-ES2 & non ES lots */
#define ARCH_ID_AT91SAM9G45ES 0x819b05a1 /* 9G45-ES (Engineering Sample) */
+#define ARCH_ID_AT91SAM9X5 0x819a05a0
#define ARCH_ID_AT91CAP9 0x039A03A0
#define ARCH_ID_AT91SAM9XE128 0x329973a0
#define ARCH_EXID_AT91SAM9G46 0x00000003
#define ARCH_EXID_AT91SAM9G45 0x00000004
+#define ARCH_EXID_AT91SAM9G15 0x00000000
+#define ARCH_EXID_AT91SAM9G35 0x00000001
+#define ARCH_EXID_AT91SAM9X35 0x00000002
+#define ARCH_EXID_AT91SAM9G25 0x00000003
+#define ARCH_EXID_AT91SAM9X25 0x00000004
+
static inline unsigned long at91_exid_identify(void)
{
return at91_sys_read(AT91_DBGU_EXID);
#define cpu_is_at91sam9m11() (0)
#endif
+#ifdef CONFIG_ARCH_AT91SAM9X5
+#define cpu_is_at91sam9x5() (at91_cpu_identify() == ARCH_ID_AT91SAM9X5)
+#define cpu_is_at91sam9g15() (cpu_is_at91sam9x5() && \
+ (at91_exid_identify() == ARCH_EXID_AT91SAM9G15))
+#define cpu_is_at91sam9g35() (cpu_is_at91sam9x5() && \
+ (at91_exid_identify() == ARCH_EXID_AT91SAM9G35))
+#define cpu_is_at91sam9x35() (cpu_is_at91sam9x5() && \
+ (at91_exid_identify() == ARCH_EXID_AT91SAM9X35))
+#define cpu_is_at91sam9g25() (cpu_is_at91sam9x5() && \
+ (at91_exid_identify() == ARCH_EXID_AT91SAM9G25))
+#define cpu_is_at91sam9x25() (cpu_is_at91sam9x5() && \
+ (at91_exid_identify() == ARCH_EXID_AT91SAM9X25))
+#else
+#define cpu_is_at91sam9x5() (0)
+#define cpu_is_at91sam9g15() (0)
+#define cpu_is_at91sam9g35() (0)
+#define cpu_is_at91sam9x35() (0)
+#define cpu_is_at91sam9g25() (0)
+#define cpu_is_at91sam9x25() (0)
+#endif
+
#ifdef CONFIG_ARCH_AT91CAP9
#define cpu_is_at91cap9() (at91_cpu_identify() == ARCH_ID_AT91CAP9)
#define cpu_is_at91cap9_revB() (at91cap9_rev_identify() == ARCH_REVISION_CAP9_B)
depends on ARCH_DAVINCI_DM644x
select MISC_DEVICES
select EEPROM_AT24
+ select I2C
help
Configure this option to specify the whether the board used
for development is a DM644x EVM
depends on ARCH_DAVINCI_DM644x
select MISC_DEVICES
select EEPROM_AT24
+ select I2C
help
Say Y here to select the Lyrtech Small Form Factor
Software Defined Radio (SFFSDR) board.
select MACH_DAVINCI_DM6467TEVM
select MISC_DEVICES
select EEPROM_AT24
+ select I2C
help
Configure this option to specify the whether the board used
for development is a DM6467 EVM
depends on ARCH_DAVINCI_DM365
select MISC_DEVICES
select EEPROM_AT24
+ select I2C
help
Configure this option to specify whether the board used
for development is a DM365 EVM
select GPIO_PCF857X
select MISC_DEVICES
select EEPROM_AT24
+ select I2C
help
Say Y here to select the TI DA830/OMAP-L137/AM17x Evaluation Module.
depends on ARCH_DAVINCI_DA850
select MISC_DEVICES
select EEPROM_AT24
+ select I2C
help
Say Y here to select the Critical Link MityDSP-L138/MityARM-1808
System on Module. Information on this SoM may be found at
#include <mach/mux.h>
#include <mach/spi.h>
-#define MITYOMAPL138_PHY_ID "0:03"
+#define MITYOMAPL138_PHY_ID ""
#define FACTORY_CONFIG_MAGIC 0x012C0138
#define FACTORY_CONFIG_VERSION 0x00010001
static struct platform_device mityomapl138_nandflash_device = {
.name = "davinci_nand",
- .id = 0,
+ .id = 1,
.dev = {
.platform_data = &mityomapl138_nandflash_data,
},
#define DA8XX_GPIO_BASE 0x01e26000
#define DA8XX_I2C1_BASE 0x01e28000
#define DA8XX_SPI0_BASE 0x01c41000
-#define DA8XX_SPI1_BASE 0x01f0e000
+#define DA830_SPI1_BASE 0x01e12000
+#define DA850_SPI1_BASE 0x01f0e000
#define DA8XX_EMAC_CTRL_REG_OFFSET 0x3000
#define DA8XX_EMAC_MOD_REG_OFFSET 0x2000
static struct resource da8xx_spi1_resources[] = {
[0] = {
- .start = DA8XX_SPI1_BASE,
- .end = DA8XX_SPI1_BASE + SZ_4K - 1,
+ .start = DA830_SPI1_BASE,
+ .end = DA830_SPI1_BASE + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
da8xx_spi_pdata[instance].num_chipselect = len;
+ if (instance == 1 && cpu_is_davinci_da850()) {
+ da8xx_spi1_resources[0].start = DA850_SPI1_BASE;
+ da8xx_spi1_resources[0].end = DA850_SPI1_BASE + SZ_4K - 1;
+ }
+
return platform_device_register(&da8xx_spi_device[instance]);
}
.name = "timer2",
.parent = &pll1_aux_clk,
.lpsc = DAVINCI_LPSC_TIMER2,
- .usecount = 1, /* REVISIT: why can't' this be disabled? */
+ .usecount = 1, /* REVISIT: why can't this be disabled? */
};
static struct clk timer3_clk = {
.name = "timer2",
.parent = &pll1_aux_clk,
.lpsc = DAVINCI_LPSC_TIMER2,
- .usecount = 1, /* REVISIT: why can't' this be disabled? */
+ .usecount = 1, /* REVISIT: why can't this be disabled? */
};
static struct clk_lookup dm644x_clks[] = {
#define UART_SHIFT 2
+#define davinci_uart_v2p(x) ((x) - PAGE_OFFSET + PLAT_PHYS_OFFSET)
+#define davinci_uart_p2v(x) ((x) - PLAT_PHYS_OFFSET + PAGE_OFFSET)
+
.pushsection .data
davinci_uart_phys: .word 0
davinci_uart_virt: .word 0
/* Use davinci_uart_phys/virt if already configured */
10: mrc p15, 0, \rp, c1, c0
tst \rp, #1 @ MMU enabled?
- ldreq \rp, =__virt_to_phys(davinci_uart_phys)
+ ldreq \rp, =davinci_uart_v2p(davinci_uart_phys)
ldrne \rp, =davinci_uart_phys
add \rv, \rp, #4 @ davinci_uart_virt
ldr \rp, [\rp, #0]
tst \rp, #1 @ MMU enabled?
/* Copy uart phys address from decompressor uart info */
- ldreq \rv, =__virt_to_phys(davinci_uart_phys)
+ ldreq \rv, =davinci_uart_v2p(davinci_uart_phys)
ldrne \rv, =davinci_uart_phys
ldreq \rp, =DAVINCI_UART_INFO
- ldrne \rp, =__phys_to_virt(DAVINCI_UART_INFO)
+ ldrne \rp, =davinci_uart_p2v(DAVINCI_UART_INFO)
ldr \rp, [\rp, #0]
str \rp, [\rv]
/* Copy uart virt address from decompressor uart info */
- ldreq \rv, =__virt_to_phys(davinci_uart_virt)
+ ldreq \rv, =davinci_uart_v2p(davinci_uart_virt)
ldrne \rv, =davinci_uart_virt
ldreq \rp, =DAVINCI_UART_INFO
- ldrne \rp, =__phys_to_virt(DAVINCI_UART_INFO)
+ ldrne \rp, =davinci_uart_p2v(DAVINCI_UART_INFO)
ldr \rp, [\rp, #4]
str \rp, [\rv]
*
* This area sits just below the page tables (see arch/arm/kernel/head.S).
*/
-#define DAVINCI_UART_INFO (PHYS_OFFSET + 0x3ff8)
+#define DAVINCI_UART_INFO (PLAT_PHYS_OFFSET + 0x3ff8)
#define DAVINCI_UART0_BASE (IO_PHYS + 0x20000)
#define DAVINCI_UART1_BASE (IO_PHYS + 0x20400)
#define BANK_OFF(n) (((n) < 3) ? (n) << 2 : 0x100 + (((n) - 3) << 2))
#define GPIO_REG(x) (*((volatile u32 *)(GPIO_REGS_VIRT + (x))))
-#define NR_BUILTIN_GPIO (192)
+#define NR_BUILTIN_GPIO IRQ_GPIO_NUM
#define gpio_to_bank(gpio) ((gpio) >> 5)
#define gpio_to_irq(gpio) (IRQ_GPIO_START + (gpio))
#define MFP_DRIVE_MEDIUM (0x2 << 13)
#define MFP_DRIVE_FAST (0x3 << 13)
+#undef MFP_CFG
+#undef MFP_CFG_DRV
+
+#define MFP_CFG(pin, af) \
+ (MFP_LPM_INPUT | MFP_PIN(MFP_PIN_##pin) | MFP_##af | MFP_DRIVE_MEDIUM)
+
+#define MFP_CFG_DRV(pin, af, drv) \
+ (MFP_LPM_INPUT | MFP_PIN(MFP_PIN_##pin) | MFP_##af | MFP_DRIVE_##drv)
+
/* GPIO */
#define GPIO0_GPIO MFP_CFG(GPIO0, AF5)
#define GPIO1_GPIO MFP_CFG(GPIO1, AF5)
static void __init qsd8x50_init_mmc(void)
{
- if (machine_is_qsd8x50_ffa() || machine_is_qsd8x50a_ffa())
- vreg_mmc = vreg_get(NULL, "gp6");
- else
- vreg_mmc = vreg_get(NULL, "gp5");
+ vreg_mmc = vreg_get(NULL, "gp5");
if (IS_ERR(vreg_mmc)) {
pr_err("vreg get for vreg_mmc failed (%ld)\n",
/* Use existing clock_event for cpu 0 */
if (!smp_processor_id())
- return;
+ return 0;
writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
.workaround = FLS_USB2_WORKAROUND_ENGCM09152,
};
+static int vpr200_usbh_init(struct platform_device *pdev)
+{
+ return mx35_initialize_usb_hw(pdev->id,
+ MXC_EHCI_INTERFACE_SINGLE_UNI | MXC_EHCI_INTERNAL_PHY);
+}
+
/* USB HOST config */
static const struct mxc_usbh_platform_data usb_host_pdata __initconst = {
- .portsc = MXC_EHCI_MODE_SERIAL,
- .flags = MXC_EHCI_INTERFACE_SINGLE_UNI |
- MXC_EHCI_INTERNAL_PHY,
+ .init = vpr200_usbh_init,
+ .portsc = MXC_EHCI_MODE_SERIAL,
};
static struct platform_device *devices[] __initdata = {
.wakeup = wake, \
}
-static const struct gpio_keys_button loco_buttons[] __initconst = {
+static struct gpio_keys_button loco_buttons[] = {
GPIO_BUTTON(MX53_LOCO_POWER, KEY_POWER, 1, "power", 0),
GPIO_BUTTON(MX53_LOCO_UI1, KEY_VOLUMEUP, 1, "volume-up", 0),
GPIO_BUTTON(MX53_LOCO_UI2, KEY_VOLUMEDOWN, 1, "volume-down", 0),
unsigned long diff, parent_rate, calc_rate; \
int i; \
\
- parent_rate = clk_get_rate(clk->parent); \
div_max = BM_CLKCTRL_##dr##_DIV >> BP_CLKCTRL_##dr##_DIV; \
bm_busy = BM_CLKCTRL_##dr##_BUSY; \
\
if (clk->parent == &ref_xtal_clk) { \
+ parent_rate = clk_get_rate(clk->parent); \
div = DIV_ROUND_UP(parent_rate, rate); \
if (clk == &cpu_clk) { \
div_max = BM_CLKCTRL_CPU_DIV_XTAL >> \
if (div == 0 || div > div_max) \
return -EINVAL; \
} else { \
+ /* \
+ * hack alert: this block modifies clk->parent, too, \
+ * so the base to use it the grand parent. \
+ */ \
+ parent_rate = clk_get_rate(clk->parent->parent); \
rate >>= PARENT_RATE_SHIFT; \
parent_rate >>= PARENT_RATE_SHIFT; \
diff = parent_rate; \
obj-$(CONFIG_OMAP_SMARTREFLEX_CLASS3) += smartreflex-class3.o
AFLAGS_sleep24xx.o :=-Wa,-march=armv6
-AFLAGS_sleep34xx.o :=-Wa,-march=armv7-a
+AFLAGS_sleep34xx.o :=-Wa,-march=armv7-a$(plus_sec)
ifeq ($(CONFIG_PM_VERBOSE),y)
CFLAGS_pm_bus.o += -DDEBUG
static void __init rx51_map_io(void)
{
omap2_set_globals_3xxx();
- rx51_video_mem_init();
omap34xx_map_common_io();
}
+static void __init rx51_reserve(void)
+{
+ rx51_video_mem_init();
+ omap_reserve();
+}
+
MACHINE_START(NOKIA_RX51, "Nokia RX-51 board")
/* Maintainer: Lauri Leukkunen <lauri.leukkunen@nokia.com> */
.boot_params = 0x80000100,
- .reserve = omap_reserve,
+ .reserve = rx51_reserve,
.map_io = rx51_map_io,
.init_early = rx51_init_early,
.init_irq = omap_init_irq,
CLK(NULL, "dsp_fck", &dsp_fck, CK_443X),
CLK("omapdss_dss", "sys_clk", &dss_sys_clk, CK_443X),
CLK("omapdss_dss", "tv_clk", &dss_tv_clk, CK_443X),
- CLK("omapdss_dss", "dss_clk", &dss_dss_clk, CK_443X),
CLK("omapdss_dss", "video_clk", &dss_48mhz_clk, CK_443X),
- CLK("omapdss_dss", "fck", &dss_fck, CK_443X),
- /*
- * On OMAP4, DSS ick is a dummy clock; this is needed for compatibility
- * with OMAP2/3.
- */
- CLK("omapdss_dss", "ick", &dummy_ck, CK_443X),
+ CLK("omapdss_dss", "fck", &dss_dss_clk, CK_443X),
+ CLK("omapdss_dss", "ick", &dss_fck, CK_443X),
CLK(NULL, "efuse_ctrl_cust_fck", &efuse_ctrl_cust_fck, CK_443X),
CLK(NULL, "emif1_fck", &emif1_fck, CK_443X),
CLK(NULL, "emif2_fck", &emif2_fck, CK_443X),
u32 per_cm_clksel;
u32 emu_cm_clksel;
u32 emu_cm_clkstctrl;
+ u32 pll_cm_autoidle;
u32 pll_cm_autoidle2;
u32 pll_cm_clksel4;
u32 pll_cm_clksel5;
omap2_cm_read_mod_reg(OMAP3430_EMU_MOD, CM_CLKSEL1);
cm_context.emu_cm_clkstctrl =
omap2_cm_read_mod_reg(OMAP3430_EMU_MOD, OMAP2_CM_CLKSTCTRL);
+ /*
+ * As per erratum i671, ROM code does not respect the PER DPLL
+ * programming scheme if CM_AUTOIDLE_PLL.AUTO_PERIPH_DPLL == 1.
+ * In this case, even though this register has been saved in
+ * scratchpad contents, we need to restore AUTO_PERIPH_DPLL
+ * by ourselves. So, we need to save it anyway.
+ */
+ cm_context.pll_cm_autoidle =
+ omap2_cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE);
cm_context.pll_cm_autoidle2 =
omap2_cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE2);
cm_context.pll_cm_clksel4 =
CM_CLKSEL1);
omap2_cm_write_mod_reg(cm_context.emu_cm_clkstctrl, OMAP3430_EMU_MOD,
OMAP2_CM_CLKSTCTRL);
+ /*
+ * As per erratum i671, ROM code does not respect the PER DPLL
+ * programming scheme if CM_AUTOIDLE_PLL.AUTO_PERIPH_DPLL == 1.
+ * In this case, we need to restore AUTO_PERIPH_DPLL by ourselves.
+ */
+ omap2_cm_write_mod_reg(cm_context.pll_cm_autoidle, PLL_MOD,
+ CM_AUTOIDLE);
omap2_cm_write_mod_reg(cm_context.pll_cm_autoidle2, PLL_MOD,
CM_AUTOIDLE2);
omap2_cm_write_mod_reg(cm_context.pll_cm_clksel4, PLL_MOD,
omap2_cm_read_mod_reg(WKUP_MOD, CM_CLKSEL);
prcm_block_contents.cm_clken_pll =
omap2_cm_read_mod_reg(PLL_MOD, CM_CLKEN);
+ /*
+ * As per erratum i671, ROM code does not respect the PER DPLL
+ * programming scheme if CM_AUTOIDLE_PLL..AUTO_PERIPH_DPLL == 1.
+ * Then, in anycase, clear these bits to avoid extra latencies.
+ */
prcm_block_contents.cm_autoidle_pll =
- omap2_cm_read_mod_reg(PLL_MOD, OMAP3430_CM_AUTOIDLE_PLL);
+ omap2_cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE) &
+ ~OMAP3430_AUTO_PERIPH_DPLL_MASK;
prcm_block_contents.cm_clksel1_pll =
omap2_cm_read_mod_reg(PLL_MOD, OMAP3430_CM_CLKSEL1_PLL);
prcm_block_contents.cm_clksel2_pll =
static struct omap_hwmod omap2420_gpio1_hwmod = {
.name = "gpio1",
+ .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
.mpu_irqs = omap242x_gpio1_irqs,
.mpu_irqs_cnt = ARRAY_SIZE(omap242x_gpio1_irqs),
.main_clk = "gpios_fck",
static struct omap_hwmod omap2420_gpio2_hwmod = {
.name = "gpio2",
+ .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
.mpu_irqs = omap242x_gpio2_irqs,
.mpu_irqs_cnt = ARRAY_SIZE(omap242x_gpio2_irqs),
.main_clk = "gpios_fck",
static struct omap_hwmod omap2420_gpio3_hwmod = {
.name = "gpio3",
+ .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
.mpu_irqs = omap242x_gpio3_irqs,
.mpu_irqs_cnt = ARRAY_SIZE(omap242x_gpio3_irqs),
.main_clk = "gpios_fck",
static struct omap_hwmod omap2420_gpio4_hwmod = {
.name = "gpio4",
+ .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
.mpu_irqs = omap242x_gpio4_irqs,
.mpu_irqs_cnt = ARRAY_SIZE(omap242x_gpio4_irqs),
.main_clk = "gpios_fck",
static struct omap_hwmod_addr_space omap2420_dma_system_addrs[] = {
{
.pa_start = 0x48056000,
- .pa_end = 0x4a0560ff,
+ .pa_end = 0x48056fff,
.flags = ADDR_TYPE_RT
},
};
static struct omap_hwmod omap2430_gpio1_hwmod = {
.name = "gpio1",
+ .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
.mpu_irqs = omap243x_gpio1_irqs,
.mpu_irqs_cnt = ARRAY_SIZE(omap243x_gpio1_irqs),
.main_clk = "gpios_fck",
static struct omap_hwmod omap2430_gpio2_hwmod = {
.name = "gpio2",
+ .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
.mpu_irqs = omap243x_gpio2_irqs,
.mpu_irqs_cnt = ARRAY_SIZE(omap243x_gpio2_irqs),
.main_clk = "gpios_fck",
static struct omap_hwmod omap2430_gpio3_hwmod = {
.name = "gpio3",
+ .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
.mpu_irqs = omap243x_gpio3_irqs,
.mpu_irqs_cnt = ARRAY_SIZE(omap243x_gpio3_irqs),
.main_clk = "gpios_fck",
static struct omap_hwmod omap2430_gpio4_hwmod = {
.name = "gpio4",
+ .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
.mpu_irqs = omap243x_gpio4_irqs,
.mpu_irqs_cnt = ARRAY_SIZE(omap243x_gpio4_irqs),
.main_clk = "gpios_fck",
static struct omap_hwmod omap2430_gpio5_hwmod = {
.name = "gpio5",
+ .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
.mpu_irqs = omap243x_gpio5_irqs,
.mpu_irqs_cnt = ARRAY_SIZE(omap243x_gpio5_irqs),
.main_clk = "gpio5_fck",
static struct omap_hwmod_addr_space omap2430_dma_system_addrs[] = {
{
.pa_start = 0x48056000,
- .pa_end = 0x4a0560ff,
+ .pa_end = 0x48056fff,
.flags = ADDR_TYPE_RT
},
};
static struct omap_hwmod omap3xxx_gpio1_hwmod = {
.name = "gpio1",
+ .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
.mpu_irqs = omap3xxx_gpio1_irqs,
.mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_gpio1_irqs),
.main_clk = "gpio1_ick",
static struct omap_hwmod omap3xxx_gpio2_hwmod = {
.name = "gpio2",
+ .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
.mpu_irqs = omap3xxx_gpio2_irqs,
.mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_gpio2_irqs),
.main_clk = "gpio2_ick",
static struct omap_hwmod omap3xxx_gpio3_hwmod = {
.name = "gpio3",
+ .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
.mpu_irqs = omap3xxx_gpio3_irqs,
.mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_gpio3_irqs),
.main_clk = "gpio3_ick",
static struct omap_hwmod omap3xxx_gpio4_hwmod = {
.name = "gpio4",
+ .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
.mpu_irqs = omap3xxx_gpio4_irqs,
.mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_gpio4_irqs),
.main_clk = "gpio4_ick",
static struct omap_hwmod omap3xxx_gpio5_hwmod = {
.name = "gpio5",
+ .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
.mpu_irqs = omap3xxx_gpio5_irqs,
.mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_gpio5_irqs),
.main_clk = "gpio5_ick",
static struct omap_hwmod omap3xxx_gpio6_hwmod = {
.name = "gpio6",
+ .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET,
.mpu_irqs = omap3xxx_gpio6_irqs,
.mpu_irqs_cnt = ARRAY_SIZE(omap3xxx_gpio6_irqs),
.main_clk = "gpio6_ick",
static struct omap_hwmod_addr_space omap3xxx_dma_system_addrs[] = {
{
.pa_start = 0x48056000,
- .pa_end = 0x4a0560ff,
+ .pa_end = 0x48056fff,
.flags = ADDR_TYPE_RT
},
};
static struct omap_hwmod_addr_space omap44xx_dma_system_addrs[] = {
{
.pa_start = 0x4a056000,
- .pa_end = 0x4a0560ff,
+ .pa_end = 0x4a056fff,
.flags = ADDR_TYPE_RT
},
};
/* No timeout error for debug sources */
}
- base = ((l3->rt) + (*(omap3_l3_bases[int_type] + err_source)));
-
/* identify the error source */
for (err_source = 0; !(status & (1 << err_source)); err_source++)
;
+
+ base = l3->rt + *(omap3_l3_bases[int_type] + err_source);
error = omap3_l3_readll(base, L3_ERROR_LOG);
if (error) {
if (cpu_is_omap44xx()) {
_init_omap_device("l3_main_1", &l3_dev);
_init_omap_device("dsp", &dsp_dev);
+ _init_omap_device("iva", &iva_dev);
} else {
_init_omap_device("l3_main", &l3_dev);
}
sys_clk_speed /= 1000;
/* Generic voltage parameters */
- vdd->curr_volt = 1200000;
vdd->volt_scale = vp_forceupdate_scale_voltage;
vdd->vp_enabled = false;
static struct regulator_init_data bq24022_init_data = {
.constraints = {
.max_uA = 500000,
- .valid_ops_mask = REGULATOR_CHANGE_CURRENT,
+ .valid_ops_mask = REGULATOR_CHANGE_CURRENT|REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(bq24022_consumers),
.consumer_supplies = bq24022_consumers,
#define GAFR(x) GPIO_REG(0x54 + (((x) & 0x70) >> 2))
-#define NR_BUILTIN_GPIO 128
+#define NR_BUILTIN_GPIO PXA_GPIO_IRQ_NUM
#define gpio_to_bank(gpio) ((gpio) >> 5)
#define gpio_to_irq(gpio) IRQ_GPIO(gpio)
-#define irq_to_gpio(irq) IRQ_TO_GPIO(irq)
+
+static inline int irq_to_gpio(unsigned int irq)
+{
+ int gpio;
+
+ if (irq == IRQ_GPIO0 || irq == IRQ_GPIO1)
+ return irq - IRQ_GPIO0;
+
+ gpio = irq - PXA_GPIO_IRQ_BASE;
+ if (gpio >= 2 && gpio < NR_BUILTIN_GPIO)
+ return gpio;
+
+ return -1;
+}
#ifdef CONFIG_CPU_PXA26x
/* GPIO86/87/88/89 on PXA26x have their direction bits in GPDR2 inverted,
#define GPIO_2_x_TO_IRQ(x) (PXA_GPIO_IRQ_BASE + (x))
#define IRQ_GPIO(x) (((x) < 2) ? (IRQ_GPIO0 + (x)) : GPIO_2_x_TO_IRQ(x))
-#define IRQ_TO_GPIO_2_x(i) ((i) - PXA_GPIO_IRQ_BASE)
-#define IRQ_TO_GPIO(i) (((i) < IRQ_GPIO(2)) ? ((i) - IRQ_GPIO0) : IRQ_TO_GPIO_2_x(i))
-
/*
* The following interrupts are for board specific purposes. Since
* the kernel can only run on one machine at a time, we can re-use
static struct regulator_init_data bq24022_init_data = {
.constraints = {
.max_uA = 500000,
- .valid_ops_mask = REGULATOR_CHANGE_CURRENT,
+ .valid_ops_mask = REGULATOR_CHANGE_CURRENT | REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(bq24022_consumers),
.consumer_supplies = bq24022_consumers,
static int pxa25x_set_wake(struct irq_data *d, unsigned int on)
{
- int gpio = IRQ_TO_GPIO(d->irq);
+ int gpio = irq_to_gpio(d->irq);
uint32_t mask = 0;
if (gpio >= 0 && gpio < 85)
*/
static int pxa27x_set_wake(struct irq_data *d, unsigned int on)
{
- int gpio = IRQ_TO_GPIO(d->irq);
+ int gpio = irq_to_gpio(d->irq);
uint32_t mask;
if (gpio >= 0 && gpio < 128)
config MACH_HARMONY
bool "Harmony board"
+ select MACH_HAS_SND_SOC_TEGRA_WM8903
help
Support for nVidia Harmony development platform
config MACH_KAEN
bool "Kaen board"
select MACH_SEABOARD
+ select MACH_HAS_SND_SOC_TEGRA_WM8903
help
Support for the Kaen version of Seaboard
config MACH_SEABOARD
bool "Seaboard board"
+ select MACH_HAS_SND_SOC_TEGRA_WM8903
help
Support for nVidia Seaboard development platform. It will
also be included for some of the derivative boards that
#include <asm/mach/time.h>
#include <asm/setup.h>
-#include <mach/harmony_audio.h>
+#include <mach/tegra_wm8903_pdata.h>
#include <mach/iomap.h>
#include <mach/irqs.h>
#include <mach/sdhci.h>
},
};
-static struct harmony_audio_platform_data harmony_audio_pdata = {
+static struct tegra_wm8903_platform_data harmony_audio_pdata = {
.gpio_spkr_en = TEGRA_GPIO_SPKR_EN,
.gpio_hp_det = TEGRA_GPIO_HP_DET,
+ .gpio_hp_mute = -1,
.gpio_int_mic_en = TEGRA_GPIO_INT_MIC_EN,
.gpio_ext_mic_en = TEGRA_GPIO_EXT_MIC_EN,
};
static struct platform_device harmony_audio_device = {
- .name = "tegra-snd-harmony",
+ .name = "tegra-snd-wm8903",
.id = 0,
.dev = {
.platform_data = &harmony_audio_pdata,
void tegra_gpio_resume(void)
{
unsigned long flags;
- int b, p, i;
+ int b;
+ int p;
local_irq_save(flags);
void tegra_gpio_suspend(void)
{
unsigned long flags;
- int b, p, i;
+ int b;
+ int p;
local_irq_save(flags);
for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) {
+++ /dev/null
-/*
- * arch/arm/mach-tegra/include/mach/harmony_audio.h
- *
- * Copyright 2011 NVIDIA, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-struct harmony_audio_platform_data {
- int gpio_spkr_en;
- int gpio_hp_det;
- int gpio_int_mic_en;
- int gpio_ext_mic_en;
-};
--- /dev/null
+/*
+ * arch/arm/mach-tegra/include/mach/tegra_wm8903_pdata.h
+ *
+ * Copyright 2011 NVIDIA, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+struct tegra_wm8903_platform_data {
+ int gpio_spkr_en;
+ int gpio_hp_det;
+ int gpio_hp_mute;
+ int gpio_int_mic_en;
+ int gpio_ext_mic_en;
+};
{
unsigned long flags;
int ret;
+ long new_rate = rate;
- rate = clk_round_rate(c->parent, rate);
- if (rate < 0)
- return rate;
+ new_rate = clk_round_rate(c->parent, new_rate);
+ if (new_rate < 0)
+ return new_rate;
spin_lock_irqsave(&c->parent->spinlock, flags);
- c->u.shared_bus_user.rate = rate;
+ c->u.shared_bus_user.rate = new_rate;
ret = tegra_clk_shared_bus_update(c->parent);
spin_unlock_irqrestore(&c->parent->spinlock, flags);
.irq = NOMADIK_GPIO_TO_IRQ(217),
.platform_data = &mop500_tc35892_data,
},
-};
-
-/* I2C0 devices only available prior to HREFv60 */
-static struct i2c_board_info __initdata mop500_i2c0_old_devices[] = {
+ /* I2C0 devices only available prior to HREFv60 */
{
I2C_BOARD_INFO("tps61052", 0x33),
.platform_data = &mop500_tps61052_data,
},
};
+#define NUM_PRE_V60_I2C0_DEVICES 1
+
static struct i2c_board_info __initdata mop500_i2c2_devices[] = {
{
/* lp5521 LED driver, 1st device */
static void __init mop500_init_machine(void)
{
+ int i2c0_devs;
+
/*
* The HREFv60 board removed a GPIO expander and routed
* all these GPIO pins to the internal GPIO controller
platform_device_register(&ab8500_device);
- i2c_register_board_info(0, mop500_i2c0_devices,
- ARRAY_SIZE(mop500_i2c0_devices));
- if (!machine_is_hrefv60())
- i2c_register_board_info(0, mop500_i2c0_old_devices,
- ARRAY_SIZE(mop500_i2c0_old_devices));
+ i2c0_devs = ARRAY_SIZE(mop500_i2c0_devices);
+ if (machine_is_hrefv60())
+ i2c0_devs -= NUM_PRE_V60_I2C0_DEVICES;
+
+ i2c_register_board_info(0, mop500_i2c0_devices, i2c0_devs);
i2c_register_board_info(2, mop500_i2c2_devices,
ARRAY_SIZE(mop500_i2c2_devices));
}
#include <linux/shm.h>
#include <linux/sched.h>
#include <linux/io.h>
+#include <linux/personality.h>
#include <linux/random.h>
#include <asm/cputype.h>
#include <asm/system.h>
mm->cached_hole_size = 0;
}
/* 8 bits of randomness in 20 address space bits */
- if (current->flags & PF_RANDOMIZE)
+ if ((current->flags & PF_RANDOMIZE) &&
+ !(current->personality & ADDR_NO_RANDOMIZE))
addr += (get_random_int() % (1 << 8)) << PAGE_SHIFT;
full_search:
/* Suspend/resume support: taken from arch/arm/plat-s3c24xx/sleep.S */
.globl cpu_arm920_suspend_size
.equ cpu_arm920_suspend_size, 4 * 3
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
ENTRY(cpu_arm920_do_suspend)
stmfd sp!, {r4 - r7, lr}
mrc p15, 0, r4, c13, c0, 0 @ PID
/* Suspend/resume support: taken from arch/arm/plat-s3c24xx/sleep.S */
.globl cpu_arm926_suspend_size
.equ cpu_arm926_suspend_size, 4 * 3
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
ENTRY(cpu_arm926_do_suspend)
stmfd sp!, {r4 - r7, lr}
mrc p15, 0, r4, c13, c0, 0 @ PID
.globl cpu_sa1100_suspend_size
.equ cpu_sa1100_suspend_size, 4*4
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
ENTRY(cpu_sa1100_do_suspend)
stmfd sp!, {r4 - r7, lr}
mrc p15, 0, r4, c3, c0, 0 @ domain ID
/* Suspend/resume support: taken from arch/arm/mach-s3c64xx/sleep.S */
.globl cpu_v6_suspend_size
.equ cpu_v6_suspend_size, 4 * 8
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
ENTRY(cpu_v6_do_suspend)
stmfd sp!, {r4 - r11, lr}
mrc p15, 0, r4, c13, c0, 0 @ FCSE/PID
/* Suspend/resume support: derived from arch/arm/mach-s5pv210/sleep.S */
.globl cpu_v7_suspend_size
.equ cpu_v7_suspend_size, 4 * 8
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
ENTRY(cpu_v7_do_suspend)
stmfd sp!, {r4 - r11, lr}
mrc p15, 0, r4, c13, c0, 0 @ FCSE/PID
.globl cpu_xsc3_suspend_size
.equ cpu_xsc3_suspend_size, 4 * 8
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
ENTRY(cpu_xsc3_do_suspend)
stmfd sp!, {r4 - r10, lr}
mrc p14, 0, r4, c6, c0, 0 @ clock configuration, for turbo mode
teq r2, #DMA_TO_DEVICE
beq xscale_dma_clean_range
b xscale_dma_flush_range
-ENDPROC(xscsale_dma_a0_map_area)
+ENDPROC(xscale_dma_a0_map_area)
/*
* dma_unmap_area(start, size, dir)
.globl cpu_xscale_suspend_size
.equ cpu_xscale_suspend_size, 4 * 7
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
ENTRY(cpu_xscale_do_suspend)
stmfd sp!, {r4 - r10, lr}
mrc p14, 0, r4, c6, c0, 0 @ clock configuration, for turbo mode
return 0;
}
+/*
+ * This lock class tells lockdep that GPIO irqs are in a different
+ * category than their parents, so it won't report false recursion.
+ */
+static struct lock_class_key gpio_lock_class;
+
int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt)
{
int i, j;
__raw_writel(~0, port[i].base + GPIO_ISR);
for (j = port[i].virtual_irq_start;
j < port[i].virtual_irq_start + 32; j++) {
+ irq_set_lockdep_class(j, &gpio_lock_class);
irq_set_chip_and_handler(j, &gpio_irq_chip,
handle_level_irq);
set_irq_flags(j, IRQF_VALID);
1:
@ return from FIQ
subs pc, lr, #4
+
+ .align
imx_ssi_fiq_base:
.word 0x0
imx_ssi_fiq_rx_buffer:
#define PFX "s5p pm: "
-/* s3c_pm_check_resume_pin
- *
- * check to see if the pin is configured correctly for sleep mode, and
- * make any necessary adjustments if it is not
-*/
-
-static void s3c_pm_check_resume_pin(unsigned int pin, unsigned int irqoffs)
-{
- /* nothing here yet */
-}
-
/* s3c_pm_configure_extint
*
* configure all external interrupt pins
*/
static u32 *s3c_pm_runcheck(struct resource *res, u32 *val)
{
- void *save_at = phys_to_virt(s3c_sleep_save_phys);
unsigned long addr;
unsigned long left;
void *stkpage;
goto skip_check;
}
- if (in_region(ptr, left, save_at, 32*4 )) {
- S3C_PMDBG("skipping %08lx, has save block in\n", addr);
- goto skip_check;
- }
-
/* calculate and check the checksum */
calc = crc32_le(~0, ptr, left);
*
* print any IRQs asserted at resume time (ie, we woke from)
*/
-static void s3c_pm_show_resume_irqs(int start, unsigned long which,
- unsigned long mask)
+static void __maybe_unused s3c_pm_show_resume_irqs(int start,
+ unsigned long which,
+ unsigned long mask)
{
int i;
put_cpu();
}
+static void vfp_thread_copy(struct thread_info *thread)
+{
+ struct thread_info *parent = current_thread_info();
+
+ vfp_sync_hwstate(parent);
+ thread->vfpstate = parent->vfpstate;
+}
+
/*
* When this function is called with the following 'cmd's, the following
* is true while this function is being run:
static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v)
{
struct thread_info *thread = v;
+ u32 fpexc;
+#ifdef CONFIG_SMP
+ unsigned int cpu;
+#endif
- if (likely(cmd == THREAD_NOTIFY_SWITCH)) {
- u32 fpexc = fmrx(FPEXC);
+ switch (cmd) {
+ case THREAD_NOTIFY_SWITCH:
+ fpexc = fmrx(FPEXC);
#ifdef CONFIG_SMP
- unsigned int cpu = thread->cpu;
+ cpu = thread->cpu;
/*
* On SMP, if VFP is enabled, save the old state in
* old state.
*/
fmxr(FPEXC, fpexc & ~FPEXC_EN);
- return NOTIFY_DONE;
- }
+ break;
- if (cmd == THREAD_NOTIFY_FLUSH)
+ case THREAD_NOTIFY_FLUSH:
vfp_thread_flush(thread);
- else
+ break;
+
+ case THREAD_NOTIFY_EXIT:
vfp_thread_exit(thread);
+ break;
+
+ case THREAD_NOTIFY_COPY:
+ vfp_thread_copy(thread);
+ break;
+ }
return NOTIFY_DONE;
}
#define ETH_INVALID_PHY 0xff
+/* board information */
+#define ATAG_BOARDINFO 0x54410008
+
+struct tag_boardinfo {
+ u32 board_number;
+};
+
struct tag {
struct tag_header hdr;
union {
struct tag_cmdline cmdline;
struct tag_clock clock;
struct tag_ethernet ethernet;
+ struct tag_boardinfo boardinfo;
} u;
};
extern resource_size_t fbmem_start;
extern resource_size_t fbmem_size;
+extern u32 board_number;
void setup_processor(void);
}
__tagtable(ATAG_CLOCK, parse_tag_clock);
+/*
+ * The board_number correspond to the bd->bi_board_number in U-Boot. This
+ * parameter is only available during initialisation and can be used in some
+ * kind of board identification.
+ */
+u32 __initdata board_number;
+
+static int __init parse_tag_boardinfo(struct tag *tag)
+{
+ board_number = tag->u.boardinfo.board_number;
+
+ return 0;
+}
+__tagtable(ATAG_BOARDINFO, parse_tag_boardinfo);
+
/*
* Scan the tag table for this tag, and call its parse function. The
* tag table is built by the linker from all the __tagtable
info.si_code = code;
info.si_addr = (void __user *)addr;
force_sig_info(signr, &info, current);
-
- /*
- * Init gets no signals that it doesn't have a handler for.
- * That's all very well, but if it has caused a synchronous
- * exception and we ignore the resulting signal, it will just
- * generate the same exception over and over again and we get
- * nowhere. Better to kill it and let the kernel panic.
- */
- if (is_global_init(current)) {
- __sighandler_t handler;
-
- spin_lock_irq(¤t->sighand->siglock);
- handler = current->sighand->action[signr-1].sa.sa_handler;
- spin_unlock_irq(¤t->sighand->siglock);
- if (handler == SIG_DFL) {
- /* init has generated a synchronous exception
- and it doesn't have a handler for the signal */
- printk(KERN_CRIT "init has generated signal %ld "
- "but has no handler for it\n", signr);
- do_exit(signr);
- }
- }
}
asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs)
spin_unlock(&clk_list_lock);
}
-struct clk *clk_get(struct device *dev, const char *id)
+static struct clk *__clk_get(struct device *dev, const char *id)
{
struct clk *clk;
- spin_lock(&clk_list_lock);
-
list_for_each_entry(clk, &at32_clock_list, list) {
if (clk->dev == dev && strcmp(id, clk->name) == 0) {
- spin_unlock(&clk_list_lock);
return clk;
}
}
- spin_unlock(&clk_list_lock);
return ERR_PTR(-ENOENT);
}
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+ struct clk *clk;
+
+ spin_lock(&clk_list_lock);
+ clk = __clk_get(dev, id);
+ spin_unlock(&clk_list_lock);
+
+ return clk;
+}
+
EXPORT_SYMBOL(clk_get);
void clk_put(struct clk *clk)
spin_lock(&clk_list_lock);
/* show clock tree as derived from the three oscillators */
- clk = clk_get(NULL, "osc32k");
+ clk = __clk_get(NULL, "osc32k");
dump_clock(clk, &r);
clk_put(clk);
- clk = clk_get(NULL, "osc0");
+ clk = __clk_get(NULL, "osc0");
dump_clock(clk, &r);
clk_put(clk);
- clk = clk_get(NULL, "osc1");
+ clk = __clk_get(NULL, "osc1");
dump_clock(clk, &r);
clk_put(clk);
static struct eic *nmi_eic;
static bool nmi_enabled;
-static void eic_ack_irq(struct irq_chip *d)
+static void eic_ack_irq(struct irq_data *d)
{
- struct eic *eic = irq_data_get_irq_chip_data(data);
+ struct eic *eic = irq_data_get_irq_chip_data(d);
eic_writel(eic, ICR, 1 << (d->irq - eic->first_irq));
}
-static void eic_mask_irq(struct irq_chip *d)
+static void eic_mask_irq(struct irq_data *d)
{
- struct eic *eic = irq_data_get_irq_chip_data(data);
+ struct eic *eic = irq_data_get_irq_chip_data(d);
eic_writel(eic, IDR, 1 << (d->irq - eic->first_irq));
}
-static void eic_mask_ack_irq(struct irq_chip *d)
+static void eic_mask_ack_irq(struct irq_data *d)
{
- struct eic *eic = irq_data_get_irq_chip_data(data);
+ struct eic *eic = irq_data_get_irq_chip_data(d);
eic_writel(eic, ICR, 1 << (d->irq - eic->first_irq));
eic_writel(eic, IDR, 1 << (d->irq - eic->first_irq));
}
-static void eic_unmask_irq(struct irq_chip *d)
+static void eic_unmask_irq(struct irq_data *d)
{
- struct eic *eic = irq_data_get_irq_chip_data(data);
+ struct eic *eic = irq_data_get_irq_chip_data(d);
eic_writel(eic, IER, 1 << (d->irq - eic->first_irq));
}
-static int eic_set_irq_type(struct irq_chip *d, unsigned int flow_type)
+static int eic_set_irq_type(struct irq_data *d, unsigned int flow_type)
{
- struct eic *eic = irq_data_get_irq_chip_data(data);
+ struct eic *eic = irq_data_get_irq_chip_data(d);
unsigned int irq = d->irq;
unsigned int i = irq - eic->first_irq;
u32 mode, edge, level;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
int_irq = platform_get_irq(pdev, 0);
- if (!regs || !int_irq) {
+ if (!regs || (int)int_irq <= 0) {
dev_dbg(&pdev->dev, "missing regs and/or irq resource\n");
return -ENXIO;
}
pio_writel(pio, IDR, 1 << (gpio & 0x1f));
}
-static void gpio_irq_unmask(struct irq_data *d))
+static void gpio_irq_unmask(struct irq_data *d)
{
unsigned gpio = irq_to_gpio(d->irq);
struct pio_device *pio = &pio_dev[gpio >> 5];
st.w r8[TI_flags], r9
unmask_interrupts
sleep CPU_SLEEP_IDLE
- .size cpu_idle_sleep, . - cpu_idle_sleep
+ .size cpu_enter_idle, . - cpu_enter_idle
/*
* Common return path for PM functions that don't run from
* Force strict CPU ordering.
*/
#define nop() __asm__ __volatile__ ("nop;\n\t" : : )
-#define mb() __asm__ __volatile__ ("" : : : "memory")
-#define rmb() __asm__ __volatile__ ("" : : : "memory")
-#define wmb() __asm__ __volatile__ ("" : : : "memory")
-#define set_mb(var, value) do { (void) xchg(&var, value); } while (0)
-#define read_barrier_depends() do { } while(0)
+#define smp_mb() mb()
+#define smp_rmb() rmb()
+#define smp_wmb() wmb()
+#define set_mb(var, value) do { var = value; mb(); } while (0)
+#define smp_read_barrier_depends() read_barrier_depends()
#ifdef CONFIG_SMP
asmlinkage unsigned long __raw_xchg_1_asm(volatile void *ptr, unsigned long value);
unsigned long new, unsigned long old);
#ifdef __ARCH_SYNC_CORE_DCACHE
-# define smp_mb() do { barrier(); smp_check_barrier(); smp_mark_barrier(); } while (0)
-# define smp_rmb() do { barrier(); smp_check_barrier(); } while (0)
-# define smp_wmb() do { barrier(); smp_mark_barrier(); } while (0)
-#define smp_read_barrier_depends() do { barrier(); smp_check_barrier(); } while (0)
-
+/* Force Core data cache coherence */
+# define mb() do { barrier(); smp_check_barrier(); smp_mark_barrier(); } while (0)
+# define rmb() do { barrier(); smp_check_barrier(); } while (0)
+# define wmb() do { barrier(); smp_mark_barrier(); } while (0)
+# define read_barrier_depends() do { barrier(); smp_check_barrier(); } while (0)
#else
-# define smp_mb() barrier()
-# define smp_rmb() barrier()
-# define smp_wmb() barrier()
-#define smp_read_barrier_depends() barrier()
+# define mb() barrier()
+# define rmb() barrier()
+# define wmb() barrier()
+# define read_barrier_depends() do { } while (0)
#endif
static inline unsigned long __xchg(unsigned long x, volatile void *ptr,
#else /* !CONFIG_SMP */
-#define smp_mb() barrier()
-#define smp_rmb() barrier()
-#define smp_wmb() barrier()
-#define smp_read_barrier_depends() do { } while(0)
+#define mb() barrier()
+#define rmb() barrier()
+#define wmb() barrier()
+#define read_barrier_depends() do { } while (0)
struct __xchg_dummy {
unsigned long a[100];
_disable_gptimers(mask);
for (i = 0; i < MAX_BLACKFIN_GPTIMERS; ++i)
if (mask & (1 << i))
- group_regs[BFIN_TIMER_OCTET(i)]->status |= trun_mask[i];
+ group_regs[BFIN_TIMER_OCTET(i)]->status = trun_mask[i];
SSYNC();
}
EXPORT_SYMBOL(disable_gptimers);
{
struct clock_event_device *evt = dev_id;
smp_mb();
- evt->event_handler(evt);
+ /*
+ * We want to ACK before we handle so that we can handle smaller timer
+ * intervals. This way if the timer expires again while we're handling
+ * things, we're more likely to see that 2nd int rather than swallowing
+ * it by ACKing the int at the end of this handler.
+ */
bfin_gptmr0_ack();
+ evt->event_handler(evt);
return IRQ_HANDLED;
}
struct blackfin_flush_data *fdata = info;
/* Invalidate the memory holding the bounds of the flushed region. */
- invalidate_dcache_range((unsigned long)fdata,
- (unsigned long)fdata + sizeof(*fdata));
+ blackfin_dcache_invalidate_range((unsigned long)fdata,
+ (unsigned long)fdata + sizeof(*fdata));
+
+ /* Make sure all write buffers in the data side of the core
+ * are flushed before trying to invalidate the icache. This
+ * needs to be after the data flush and before the icache
+ * flush so that the SSYNC does the right thing in preventing
+ * the instruction prefetcher from hitting things in cached
+ * memory at the wrong time -- it runs much further ahead than
+ * the pipeline.
+ */
+ SSYNC();
- flush_icache_range(fdata->start, fdata->end);
+ /* ipi_flaush_icache is invoked by generic flush_icache_range,
+ * so call blackfin arch icache flush directly here.
+ */
+ blackfin_icache_flush_range(fdata->start, fdata->end);
}
static void ipi_call_function(unsigned int cpu, struct ipi_message *msg)
#define __NR_fanotify_init 337
#define __NR_fanotify_mark 338
#define __NR_prlimit64 339
+#define __NR_name_to_handle_at 340
+#define __NR_open_by_handle_at 341
+#define __NR_clock_adjtime 342
+#define __NR_syncfs 343
#ifdef __KERNEL__
-#define NR_syscalls 340
+#define NR_syscalls 344
#define __ARCH_WANT_IPC_PARSE_VERSION
#define __ARCH_WANT_OLD_READDIR
.long sys_fanotify_init
.long sys_fanotify_mark
.long sys_prlimit64
+ .long sys_name_to_handle_at /* 340 */
+ .long sys_open_by_handle_at
+ .long sys_clock_adjtime
+ .long sys_syncfs
.long sys_fanotify_init
.long sys_fanotify_mark
.long sys_prlimit64
+ .long sys_name_to_handle_at /* 340 */
+ .long sys_open_by_handle_at
+ .long sys_clock_adjtime
+ .long sys_syncfs
.rept NR_syscalls-(.-sys_call_table)/4
.long sys_ni_syscall
zones_size[ZONE_DMA] = m68k_memory[i].size >> PAGE_SHIFT;
free_area_init_node(i, zones_size,
m68k_memory[i].addr >> PAGE_SHIFT, NULL);
+ if (node_present_pages(i))
+ node_set_state(i, N_NORMAL_MEMORY);
}
}
select HAVE_FUNCTION_GRAPH_TRACER
select HAVE_DYNAMIC_FTRACE
select HAVE_FTRACE_MCOUNT_RECORD
- select USB_ARCH_HAS_EHCI
select ARCH_WANT_OPTIONAL_GPIOLIB
select HAVE_OPROFILE
select HAVE_ARCH_KGDB
}
memset(pfnnid_map, 0xff, sizeof(pfnnid_map));
- for (i = 0; i < npmem_ranges; i++)
+ for (i = 0; i < npmem_ranges; i++) {
+ node_set_state(i, N_NORMAL_MEMORY);
node_set_online(i);
+ }
#endif
/*
config ARCH_SUSPEND_POSSIBLE
def_bool y
depends on ADB_PMU || PPC_EFIKA || PPC_LITE5200 || PPC_83xx || \
- PPC_85xx || PPC_86xx || PPC_PSERIES || 44x || 40x
+ (PPC_85xx && !SMP) || PPC_86xx || PPC_PSERIES || 44x || 40x
config PPC_DCR_NATIVE
bool
uint fec_addr_low; /* lower 32 bits of station address */
ushort fec_addr_high; /* upper 16 bits of station address */
ushort res1; /* reserved */
- uint fec_hash_table_high; /* upper 32-bits of hash table */
- uint fec_hash_table_low; /* lower 32-bits of hash table */
+ uint fec_grp_hash_table_high; /* upper 32-bits of hash table */
+ uint fec_grp_hash_table_low; /* lower 32-bits of hash table */
uint fec_r_des_start; /* beginning of Rx descriptor ring */
uint fec_x_des_start; /* beginning of Tx descriptor ring */
uint fec_r_buff_size; /* Rx buffer size */
#define CPU_FTRS_E500_2 (CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | \
CPU_FTR_SPE_COMP | CPU_FTR_MAYBE_CAN_NAP | \
CPU_FTR_NODSISRALIGN | CPU_FTR_NOEXECUTE)
-#define CPU_FTRS_E500MC (CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | \
- CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_NODSISRALIGN | \
+#define CPU_FTRS_E500MC (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | \
CPU_FTR_L2CSR | CPU_FTR_LWSYNC | CPU_FTR_NOEXECUTE | \
CPU_FTR_DBELL)
+#define CPU_FTRS_E5500 (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | \
+ CPU_FTR_L2CSR | CPU_FTR_LWSYNC | CPU_FTR_NOEXECUTE | \
+ CPU_FTR_DBELL | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD)
#define CPU_FTRS_GENERIC_32 (CPU_FTR_COMMON | CPU_FTR_NODSISRALIGN)
/* 64-bit CPUs */
#define CPU_FTRS_COMPATIBLE (CPU_FTR_USE_TB | CPU_FTR_PPCAS_ARCH_V2)
#ifdef __powerpc64__
+#ifdef CONFIG_PPC_BOOK3E
+#define CPU_FTRS_POSSIBLE (CPU_FTRS_E5500)
+#else
#define CPU_FTRS_POSSIBLE \
(CPU_FTRS_POWER3 | CPU_FTRS_RS64 | CPU_FTRS_POWER4 | \
CPU_FTRS_PPC970 | CPU_FTRS_POWER5 | CPU_FTRS_POWER6 | \
CPU_FTRS_POWER7 | CPU_FTRS_CELL | CPU_FTRS_PA6T | \
CPU_FTR_1T_SEGMENT | CPU_FTR_VSX)
+#endif
#else
enum {
CPU_FTRS_POSSIBLE =
#endif
#ifdef CONFIG_E500
CPU_FTRS_E500 | CPU_FTRS_E500_2 | CPU_FTRS_E500MC |
+ CPU_FTRS_E5500 |
#endif
0,
};
#endif /* __powerpc64__ */
#ifdef __powerpc64__
+#ifdef CONFIG_PPC_BOOK3E
+#define CPU_FTRS_ALWAYS (CPU_FTRS_E5500)
+#else
#define CPU_FTRS_ALWAYS \
(CPU_FTRS_POWER3 & CPU_FTRS_RS64 & CPU_FTRS_POWER4 & \
CPU_FTRS_PPC970 & CPU_FTRS_POWER5 & CPU_FTRS_POWER6 & \
CPU_FTRS_POWER7 & CPU_FTRS_CELL & CPU_FTRS_PA6T & CPU_FTRS_POSSIBLE)
+#endif
#else
enum {
CPU_FTRS_ALWAYS =
#endif
#ifdef CONFIG_E500
CPU_FTRS_E500 & CPU_FTRS_E500_2 & CPU_FTRS_E500MC &
+ CPU_FTRS_E5500 &
#endif
CPU_FTRS_POSSIBLE,
};
* on platforms where such control is possible.
*/
#if defined(CONFIG_KGDB) || defined(CONFIG_XMON) || defined(CONFIG_BDI_SWITCH) ||\
- defined(CONFIG_KPROBES)
+ defined(CONFIG_KPROBES) || defined(CONFIG_DYNAMIC_FTRACE)
#define PAGE_KERNEL_TEXT PAGE_KERNEL_X
#else
#define PAGE_KERNEL_TEXT PAGE_KERNEL_ROX
*
* Obviously, the GART is not cache coherent and so any change to it
* must be flushed to memory (or maybe just make the GART space non
- * cachable). AGP memory itself does't seem to be cache coherent neither.
+ * cachable). AGP memory itself doesn't seem to be cache coherent neither.
*
* In order to invalidate the GART (which is probably necessary to inval
* the bridge internal TLBs), the following sequence has to be written,
.pvr_mask = 0xffff0000,
.pvr_value = 0x80240000,
.cpu_name = "e5500",
- .cpu_features = CPU_FTRS_E500MC,
+ .cpu_features = CPU_FTRS_E5500,
.cpu_user_features = COMMON_USER_BOOKE,
.mmu_features = MMU_FTR_TYPE_FSL_E | MMU_FTR_BIG_PHYS |
MMU_FTR_USE_TLBILX,
}
/* wait for all the CPUs to hit real mode but timeout if they don't come in */
-#if defined(CONFIG_PPC_STD_MMU_64) && defined(CONFIG_SMP)
+#ifdef CONFIG_PPC_STD_MMU_64
static void crash_kexec_wait_realmode(int cpu)
{
unsigned int msecs;
}
mb();
}
-#else
-static inline void crash_kexec_wait_realmode(int cpu) {}
-#endif
+#endif /* CONFIG_PPC_STD_MMU_64 */
/*
* This function will be called by secondary cpus or by kexec cpu
crash_ipi_callback(regs);
}
-#else
+#else /* ! CONFIG_SMP */
+static inline void crash_kexec_wait_realmode(int cpu) {}
+
static void crash_kexec_prepare_cpus(int cpu)
{
/*
{
cpus_in_sr = CPU_MASK_NONE;
}
-#endif
+#endif /* CONFIG_SMP */
/*
* Register a function to be called on shutdown. Only use this if you
#endif /* !CONFIG_SUSPEND */
-#ifdef CONFIG_HIBERNATION
+#ifdef CONFIG_HIBERNATE_CALLBACKS
static int ibmebus_bus_pm_freeze(struct device *dev)
{
return ret;
}
-#else /* !CONFIG_HIBERNATION */
+#else /* !CONFIG_HIBERNATE_CALLBACKS */
#define ibmebus_bus_pm_freeze NULL
#define ibmebus_bus_pm_thaw NULL
#define ibmebus_bus_pm_poweroff_noirq NULL
#define ibmebus_bus_pm_restore_noirq NULL
-#endif /* !CONFIG_HIBERNATION */
+#endif /* !CONFIG_HIBERNATE_CALLBACKS */
static struct dev_pm_ops ibmebus_bus_dev_pm_ops = {
.prepare = ibmebus_bus_pm_prepare,
if (!parent)
continue;
if (of_match_node(legacy_serial_parents, parent) != NULL) {
- index = add_legacy_soc_port(np, np);
- if (index >= 0 && np == stdout)
- legacy_serial_console = index;
+ if (of_device_is_available(np)) {
+ index = add_legacy_soc_port(np, np);
+ if (index >= 0 && np == stdout)
+ legacy_serial_console = index;
+ }
}
of_node_put(parent);
}
return 0;
}
+static u64 check_and_compute_delta(u64 prev, u64 val)
+{
+ u64 delta = (val - prev) & 0xfffffffful;
+
+ /*
+ * POWER7 can roll back counter values, if the new value is smaller
+ * than the previous value it will cause the delta and the counter to
+ * have bogus values unless we rolled a counter over. If a coutner is
+ * rolled back, it will be smaller, but within 256, which is the maximum
+ * number of events to rollback at once. If we dectect a rollback
+ * return 0. This can lead to a small lack of precision in the
+ * counters.
+ */
+ if (prev > val && (prev - val) < 256)
+ delta = 0;
+
+ return delta;
+}
+
static void power_pmu_read(struct perf_event *event)
{
s64 val, delta, prev;
prev = local64_read(&event->hw.prev_count);
barrier();
val = read_pmc(event->hw.idx);
+ delta = check_and_compute_delta(prev, val);
+ if (!delta)
+ return;
} while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev);
- /* The counters are only 32 bits wide */
- delta = (val - prev) & 0xfffffffful;
local64_add(delta, &event->count);
local64_sub(delta, &event->hw.period_left);
}
val = (event->hw.idx == 5) ? pmc5 : pmc6;
prev = local64_read(&event->hw.prev_count);
event->hw.idx = 0;
- delta = (val - prev) & 0xfffffffful;
- local64_add(delta, &event->count);
+ delta = check_and_compute_delta(prev, val);
+ if (delta)
+ local64_add(delta, &event->count);
}
}
unsigned long pmc5, unsigned long pmc6)
{
struct perf_event *event;
- u64 val;
+ u64 val, prev;
int i;
for (i = 0; i < cpuhw->n_limited; ++i) {
event = cpuhw->limited_counter[i];
event->hw.idx = cpuhw->limited_hwidx[i];
val = (event->hw.idx == 5) ? pmc5 : pmc6;
- local64_set(&event->hw.prev_count, val);
+ prev = local64_read(&event->hw.prev_count);
+ if (check_and_compute_delta(prev, val))
+ local64_set(&event->hw.prev_count, val);
perf_event_update_userpage(event);
}
}
/* we don't have to worry about interrupts here */
prev = local64_read(&event->hw.prev_count);
- delta = (val - prev) & 0xfffffffful;
+ delta = check_and_compute_delta(prev, val);
local64_add(delta, &event->count);
/*
if (data && !(data & DABR_TRANSLATION))
return -EIO;
#ifdef CONFIG_HAVE_HW_BREAKPOINT
+ if (ptrace_get_breakpoints(task) < 0)
+ return -ESRCH;
+
bp = thread->ptrace_bps[0];
if ((!data) || !(data & (DABR_DATA_WRITE | DABR_DATA_READ))) {
if (bp) {
unregister_hw_breakpoint(bp);
thread->ptrace_bps[0] = NULL;
}
+ ptrace_put_breakpoints(task);
return 0;
}
if (bp) {
(DABR_DATA_WRITE | DABR_DATA_READ),
&attr.bp_type);
ret = modify_user_hw_breakpoint(bp, &attr);
- if (ret)
+ if (ret) {
+ ptrace_put_breakpoints(task);
return ret;
+ }
thread->ptrace_bps[0] = bp;
+ ptrace_put_breakpoints(task);
thread->dabr = data;
return 0;
}
ptrace_triggered, task);
if (IS_ERR(bp)) {
thread->ptrace_bps[0] = NULL;
+ ptrace_put_breakpoints(task);
return PTR_ERR(bp);
}
+ ptrace_put_breakpoints(task);
+
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
/* Move contents to the DABR register */
u64 stolen = 0;
u64 dtb;
+ if (!dtl)
+ return 0;
+
if (i == vpa->dtl_idx)
return 0;
while (i < vpa->dtl_idx) {
mpic_setup_this_cpu();
}
+#ifdef CONFIG_PPC64
#ifdef CONFIG_HOTPLUG_CPU
static int smp_core99_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
static void __init smp_core99_bringup_done(void)
{
-#ifdef CONFIG_PPC64
extern void g5_phy_disable_cpu1(void);
/* Close i2c bus if it was used for tb sync */
set_cpu_present(1, false);
g5_phy_disable_cpu1();
}
-#endif /* CONFIG_PPC64 */
-
#ifdef CONFIG_HOTPLUG_CPU
register_cpu_notifier(&smp_core99_cpu_nb);
#endif
+
if (ppc_md.progress)
ppc_md.progress("smp_core99_bringup_done", 0x349);
}
+#endif /* CONFIG_PPC64 */
#ifdef CONFIG_HOTPLUG_CPU
struct smp_ops_t core99_smp_ops = {
.message_pass = smp_mpic_message_pass,
.probe = smp_core99_probe,
+#ifdef CONFIG_PPC64
.bringup_done = smp_core99_bringup_done,
+#endif
.kick_cpu = smp_core99_kick_cpu,
.setup_cpu = smp_core99_setup_cpu,
.give_timebase = smp_core99_give_timebase,
int cpu, ret;
struct paca_struct *pp;
struct dtl_entry *dtl;
+ struct kmem_cache *dtl_cache;
if (!firmware_has_feature(FW_FEATURE_SPLPAR))
return 0;
+ dtl_cache = kmem_cache_create("dtl", DISPATCH_LOG_BYTES,
+ DISPATCH_LOG_BYTES, 0, NULL);
+ if (!dtl_cache) {
+ pr_warn("Failed to create dispatch trace log buffer cache\n");
+ pr_warn("Stolen time statistics will be unreliable\n");
+ return 0;
+ }
+
for_each_possible_cpu(cpu) {
pp = &paca[cpu];
- dtl = kmalloc_node(DISPATCH_LOG_BYTES, GFP_KERNEL,
- cpu_to_node(cpu));
+ dtl = kmem_cache_alloc(dtl_cache, GFP_KERNEL);
if (!dtl) {
pr_warn("Failed to allocate dispatch trace log for cpu %d\n",
cpu);
struct resource rsrc;
const int *bus_range;
+ if (!of_device_is_available(dev)) {
+ pr_warning("%s: disabled\n", dev->full_name);
+ return -ENODEV;
+ }
+
pr_debug("Adding PCI host bridge %s\n", dev->full_name);
/* Fetch host bridge registers address */
port->ops = ops;
port->priv = priv;
port->phys_efptr = 0x100;
- rio_register_mport(port);
priv->regs_win = ioremap(regs.start, regs.end - regs.start + 1);
rio_regs_win = priv->regs_win;
dev_info(&dev->dev, "RapidIO Common Transport System size: %d\n",
port->sys_size ? 65536 : 256);
+ if (rio_register_mport(port))
+ goto err;
+
if (port->host_deviceid >= 0)
out_be32(priv->regs_win + RIO_GCCSR, RIO_PORT_GEN_HOST |
RIO_PORT_GEN_MASTER | RIO_PORT_GEN_DISCOVERED);
/* Add the entropy */
while (nbytes >= 8) {
- *((__u64 *)parm_block) ^= *((__u64 *)buf+i*8);
+ *((__u64 *)parm_block) ^= *((__u64 *)(buf+i));
prng_add_entropy();
i += 8;
nbytes -= 8;
tm __TI_flags+7(%r2),_TIF_EXIT_SIE
jz 0f
larl %r2,sie_exit # work pending, leave sie
- stg %r2,__LC_RETURN_PSW+8
+ stg %r2,SPI_PSW+8(0,%r15)
br %r14
0: larl %r2,sie_reenter # re-enter with guest id
- stg %r2,__LC_RETURN_PSW+8
+ stg %r2,SPI_PSW+8(0,%r15)
1: br %r14
/*
struct task_struct *tsk;
__u16 subcode;
- kstat_cpu(smp_processor_id()).irqs[EXTINT_PFL]++;
/*
* Get the external interruption subcode & pfault
* initial/completion signal bit. VM stores this
subcode = ext_int_code >> 16;
if ((subcode & 0xff00) != __SUBCODE_MASK)
return;
+ kstat_cpu(smp_processor_id()).irqs[EXTINT_PFL]++;
/*
* Get the token (= address of the task structure of the affected task).
*/
#ifdef CONFIG_64BIT
- tsk = *(struct task_struct **) param64;
+ tsk = (struct task_struct *) param64;
#else
- tsk = *(struct task_struct **) param32;
+ tsk = (struct task_struct *) param32;
#endif
if (subcode & 0x0080) {
WARN_ON_ONCE(1);
continue;
}
- ptep = pte_offset_kernel(pmdp, addr + i * PAGE_SIZE);
+ ptep = pte_offset_kernel(pmdp, addr);
pte = *ptep;
pte = set(pte);
- ptep_invalidate(&init_mm, addr + i * PAGE_SIZE, ptep);
+ ptep_invalidate(&init_mm, addr, ptep);
*ptep = pte;
+ addr += PAGE_SIZE;
}
}
set_tsk_thread_flag(child, TIF_SINGLESTEP);
+ if (ptrace_get_breakpoints(child) < 0)
+ return;
+
set_single_step(child, pc);
+ ptrace_put_breakpoints(child);
}
void user_disable_single_step(struct task_struct *child)
config HPPFS
tristate "HoneyPot ProcFS (EXPERIMENTAL)"
- depends on EXPERIMENTAL
+ depends on EXPERIMENTAL && PROC_FS
help
hppfs (HoneyPot ProcFS) is a filesystem which allows UML /proc
entries to be overridden, removed, or fabricated from the host.
menu "Host processor type and features"
+config CMPXCHG_LOCAL
+ bool
+ default n
+
source "arch/x86/Kconfig.cpu"
endmenu
--- /dev/null
+#ifndef __UM_BUG_H
+#define __UM_BUG_H
+
+#include <asm-generic/bug.h>
+
+#endif
{
struct thread_info *ti;
unsigned long mask = THREAD_SIZE - 1;
- ti = (struct thread_info *) (((unsigned long) &ti) & ~mask);
+ void *p;
+
+ asm volatile ("" : "=r" (p) : "0" (&ti));
+ ti = (struct thread_info *) (((unsigned long)p) & ~mask);
return ti;
}
obj-y = bug.o bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \
ptrace_user.o setjmp.o signal.o stub.o stub_segv.o syscalls.o sysrq.o \
- sys_call_table.o tls.o
+ sys_call_table.o tls.o atomic64_cx8_32.o
obj-$(CONFIG_BINFMT_ELF) += elfcore.o
--- /dev/null
+/*
+ * atomic64_t for 586+
+ *
+ * Copied from arch/x86/lib/atomic64_cx8_32.S
+ *
+ * Copyright © 2010 Luca Barbieri
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/alternative-asm.h>
+#include <asm/dwarf2.h>
+
+.macro SAVE reg
+ pushl_cfi %\reg
+ CFI_REL_OFFSET \reg, 0
+.endm
+
+.macro RESTORE reg
+ popl_cfi %\reg
+ CFI_RESTORE \reg
+.endm
+
+.macro read64 reg
+ movl %ebx, %eax
+ movl %ecx, %edx
+/* we need LOCK_PREFIX since otherwise cmpxchg8b always does the write */
+ LOCK_PREFIX
+ cmpxchg8b (\reg)
+.endm
+
+ENTRY(atomic64_read_cx8)
+ CFI_STARTPROC
+
+ read64 %ecx
+ ret
+ CFI_ENDPROC
+ENDPROC(atomic64_read_cx8)
+
+ENTRY(atomic64_set_cx8)
+ CFI_STARTPROC
+
+1:
+/* we don't need LOCK_PREFIX since aligned 64-bit writes
+ * are atomic on 586 and newer */
+ cmpxchg8b (%esi)
+ jne 1b
+
+ ret
+ CFI_ENDPROC
+ENDPROC(atomic64_set_cx8)
+
+ENTRY(atomic64_xchg_cx8)
+ CFI_STARTPROC
+
+ movl %ebx, %eax
+ movl %ecx, %edx
+1:
+ LOCK_PREFIX
+ cmpxchg8b (%esi)
+ jne 1b
+
+ ret
+ CFI_ENDPROC
+ENDPROC(atomic64_xchg_cx8)
+
+.macro addsub_return func ins insc
+ENTRY(atomic64_\func\()_return_cx8)
+ CFI_STARTPROC
+ SAVE ebp
+ SAVE ebx
+ SAVE esi
+ SAVE edi
+
+ movl %eax, %esi
+ movl %edx, %edi
+ movl %ecx, %ebp
+
+ read64 %ebp
+1:
+ movl %eax, %ebx
+ movl %edx, %ecx
+ \ins\()l %esi, %ebx
+ \insc\()l %edi, %ecx
+ LOCK_PREFIX
+ cmpxchg8b (%ebp)
+ jne 1b
+
+10:
+ movl %ebx, %eax
+ movl %ecx, %edx
+ RESTORE edi
+ RESTORE esi
+ RESTORE ebx
+ RESTORE ebp
+ ret
+ CFI_ENDPROC
+ENDPROC(atomic64_\func\()_return_cx8)
+.endm
+
+addsub_return add add adc
+addsub_return sub sub sbb
+
+.macro incdec_return func ins insc
+ENTRY(atomic64_\func\()_return_cx8)
+ CFI_STARTPROC
+ SAVE ebx
+
+ read64 %esi
+1:
+ movl %eax, %ebx
+ movl %edx, %ecx
+ \ins\()l $1, %ebx
+ \insc\()l $0, %ecx
+ LOCK_PREFIX
+ cmpxchg8b (%esi)
+ jne 1b
+
+10:
+ movl %ebx, %eax
+ movl %ecx, %edx
+ RESTORE ebx
+ ret
+ CFI_ENDPROC
+ENDPROC(atomic64_\func\()_return_cx8)
+.endm
+
+incdec_return inc add adc
+incdec_return dec sub sbb
+
+ENTRY(atomic64_dec_if_positive_cx8)
+ CFI_STARTPROC
+ SAVE ebx
+
+ read64 %esi
+1:
+ movl %eax, %ebx
+ movl %edx, %ecx
+ subl $1, %ebx
+ sbb $0, %ecx
+ js 2f
+ LOCK_PREFIX
+ cmpxchg8b (%esi)
+ jne 1b
+
+2:
+ movl %ebx, %eax
+ movl %ecx, %edx
+ RESTORE ebx
+ ret
+ CFI_ENDPROC
+ENDPROC(atomic64_dec_if_positive_cx8)
+
+ENTRY(atomic64_add_unless_cx8)
+ CFI_STARTPROC
+ SAVE ebp
+ SAVE ebx
+/* these just push these two parameters on the stack */
+ SAVE edi
+ SAVE esi
+
+ movl %ecx, %ebp
+ movl %eax, %esi
+ movl %edx, %edi
+
+ read64 %ebp
+1:
+ cmpl %eax, 0(%esp)
+ je 4f
+2:
+ movl %eax, %ebx
+ movl %edx, %ecx
+ addl %esi, %ebx
+ adcl %edi, %ecx
+ LOCK_PREFIX
+ cmpxchg8b (%ebp)
+ jne 1b
+
+ movl $1, %eax
+3:
+ addl $8, %esp
+ CFI_ADJUST_CFA_OFFSET -8
+ RESTORE ebx
+ RESTORE ebp
+ ret
+4:
+ cmpl %edx, 4(%esp)
+ jne 2b
+ xorl %eax, %eax
+ jmp 3b
+ CFI_ENDPROC
+ENDPROC(atomic64_add_unless_cx8)
+
+ENTRY(atomic64_inc_not_zero_cx8)
+ CFI_STARTPROC
+ SAVE ebx
+
+ read64 %esi
+1:
+ testl %eax, %eax
+ je 4f
+2:
+ movl %eax, %ebx
+ movl %edx, %ecx
+ addl $1, %ebx
+ adcl $0, %ecx
+ LOCK_PREFIX
+ cmpxchg8b (%esi)
+ jne 1b
+
+ movl $1, %eax
+3:
+ RESTORE ebx
+ ret
+4:
+ testl %edx, %edx
+ jne 2b
+ jmp 3b
+ CFI_ENDPROC
+ENDPROC(atomic64_inc_not_zero_cx8)
if (oreg.ax > 15*1024) {
return -1; /* Bogus! */
} else if (oreg.ax == 15*1024) {
- boot_params.alt_mem_k = (oreg.dx << 6) + oreg.ax;
+ boot_params.alt_mem_k = (oreg.bx << 6) + oreg.ax;
} else {
/*
* This ignores memory above 16MB if we have a memory
* Don't enable translation but enable GART IO and CPU accesses.
* Also, set DISTLBWALKPRB since GART tables memory is UC.
*/
- ctl = DISTLBWALKPRB | order << 1;
+ ctl = order << 1;
pci_write_config_dword(dev, AMD64_GARTAPERTURECTL, ctl);
}
{
u32 tmp, ctl;
- /* address of the mappings table */
- addr >>= 12;
- tmp = (u32) addr<<4;
- tmp &= ~0xf;
- pci_write_config_dword(dev, AMD64_GARTTABLEBASE, tmp);
-
- /* Enable GART translation for this hammer. */
- pci_read_config_dword(dev, AMD64_GARTAPERTURECTL, &ctl);
- ctl |= GARTEN;
- ctl &= ~(DISGARTCPU | DISGARTIO);
- pci_write_config_dword(dev, AMD64_GARTAPERTURECTL, ctl);
+ /* address of the mappings table */
+ addr >>= 12;
+ tmp = (u32) addr<<4;
+ tmp &= ~0xf;
+ pci_write_config_dword(dev, AMD64_GARTTABLEBASE, tmp);
+
+ /* Enable GART translation for this hammer. */
+ pci_read_config_dword(dev, AMD64_GARTAPERTURECTL, &ctl);
+ ctl |= GARTEN | DISTLBWALKPRB;
+ ctl &= ~(DISGARTCPU | DISGARTIO);
+ pci_write_config_dword(dev, AMD64_GARTAPERTURECTL, ctl);
}
static inline int aperture_valid(u64 aper_base, u32 aper_size, u32 min_size)
extern void ioapic_and_gsi_init(void);
extern void ioapic_insert_resources(void);
-int io_apic_setup_irq_pin(unsigned int irq, int node, struct io_apic_irq_attr *attr);
+int io_apic_setup_irq_pin_once(unsigned int irq, int node, struct io_apic_irq_attr *attr);
extern struct IO_APIC_route_entry **alloc_ioapic_entries(void);
extern void free_ioapic_entries(struct IO_APIC_route_entry **ioapic_entries);
#define MSR_IA32_MC0_ADDR 0x00000402
#define MSR_IA32_MC0_MISC 0x00000403
+#define MSR_AMD64_MC0_MASK 0xc0010044
+
#define MSR_IA32_MCx_CTL(x) (MSR_IA32_MC0_CTL + 4*(x))
#define MSR_IA32_MCx_STATUS(x) (MSR_IA32_MC0_STATUS + 4*(x))
#define MSR_IA32_MCx_ADDR(x) (MSR_IA32_MC0_ADDR + 4*(x))
#define MSR_IA32_MCx_MISC(x) (MSR_IA32_MC0_MISC + 4*(x))
+#define MSR_AMD64_MCx_MASK(x) (MSR_AMD64_MC0_MASK + (x))
+
/* These are consecutive and not in the normal 4er MCE bank block */
#define MSR_IA32_MC0_CTL2 0x00000280
#define MSR_IA32_MCx_CTL2(x) (MSR_IA32_MC0_CTL2 + (x))
#endif /* CONFIG_NUMA */
#ifdef CONFIG_DEBUG_PER_CPU_MAPS
-struct cpumask __cpuinit *debug_cpumask_set_cpu(int cpu, int enable);
+void debug_cpumask_set_cpu(int cpu, int node, bool enable);
#endif
#endif /* _ASM_X86_NUMA_H */
* Don't enable translation yet but enable GART IO and CPU
* accesses and set DISTLBWALKPRB since GART table memory is UC.
*/
- u32 ctl = DISTLBWALKPRB | aper_order << 1;
+ u32 ctl = aper_order << 1;
bus = amd_nb_bus_dev_ranges[i].bus;
dev_base = amd_nb_bus_dev_ranges[i].dev_base;
}
early_param("noapic", parse_noapic);
-static int io_apic_setup_irq_pin_once(unsigned int irq, int node,
- struct io_apic_irq_attr *attr);
+static int io_apic_setup_irq_pin(unsigned int irq, int node,
+ struct io_apic_irq_attr *attr);
/* Will be called in mpparse/acpi/sfi codes for saving IRQ info */
void mp_save_irq(struct mpc_intsrc *m)
}
#endif /* CONFIG_HT_IRQ */
-int
+static int
io_apic_setup_irq_pin(unsigned int irq, int node, struct io_apic_irq_attr *attr)
{
struct irq_cfg *cfg = alloc_irq_and_cfg_at(irq, node);
return ret;
}
-static int io_apic_setup_irq_pin_once(unsigned int irq, int node,
- struct io_apic_irq_attr *attr)
+int io_apic_setup_irq_pin_once(unsigned int irq, int node,
+ struct io_apic_irq_attr *attr)
{
unsigned int id = attr->ioapic, pin = attr->ioapic_pin;
int ret;
#include <linux/kthread.h>
#include <linux/jiffies.h>
#include <linux/acpi.h>
+#include <linux/syscore_ops.h>
#include <asm/system.h>
#include <asm/uaccess.h>
local_irq_disable();
sysdev_suspend(PMSG_SUSPEND);
+ syscore_suspend();
local_irq_enable();
apm_error("suspend", err);
err = (err == APM_SUCCESS) ? 0 : -EIO;
+ syscore_resume();
sysdev_resume();
local_irq_enable();
local_irq_disable();
sysdev_suspend(PMSG_SUSPEND);
+ syscore_suspend();
local_irq_enable();
err = set_system_power_state(APM_STATE_STANDBY);
apm_error("standby", err);
local_irq_disable();
+ syscore_resume();
sysdev_resume();
local_irq_enable();
/* As a rule processors have APIC timer running in deep C states */
if (c->x86 >= 0xf && !cpu_has_amd_erratum(amd_erratum_400))
set_cpu_cap(c, X86_FEATURE_ARAT);
+
+ /*
+ * Disable GART TLB Walk Errors on Fam10h. We do this here
+ * because this is always needed when GART is enabled, even in a
+ * kernel which has no MCE support built in.
+ */
+ if (c->x86 == 0x10) {
+ /*
+ * BIOS should disable GartTlbWlk Errors themself. If
+ * it doesn't do it here as suggested by the BKDG.
+ *
+ * Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=33012
+ */
+ u64 mask;
+
+ rdmsrl(MSR_AMD64_MCx_MASK(4), mask);
+ mask |= (1 << 10);
+ wrmsrl(MSR_AMD64_MCx_MASK(4), mask);
+ }
}
#ifdef CONFIG_X86_32
*/
const int amd_erratum_400[] =
- AMD_OSVW_ERRATUM(1, AMD_MODEL_RANGE(0xf, 0x41, 0x2, 0xff, 0xf),
+ AMD_OSVW_ERRATUM(1, AMD_MODEL_RANGE(0x0f, 0x4, 0x2, 0xff, 0xf),
AMD_MODEL_RANGE(0x10, 0x2, 0x1, 0xff, 0xf));
EXPORT_SYMBOL_GPL(amd_erratum_400);
return -EOPNOTSUPP;
}
+ /*
+ * Do not allow config1 (extended registers) to propagate,
+ * there's no sane user-space generalization yet:
+ */
if (attr->type == PERF_TYPE_RAW)
- return x86_pmu_extra_regs(event->attr.config, event);
+ return 0;
if (attr->type == PERF_TYPE_HW_CACHE)
return set_ext_hw_attr(hwc, event);
/*
* Branch tracing:
*/
- if ((attr->config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS) &&
- (hwc->sample_period == 1)) {
+ if (attr->config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS &&
+ !attr->freq && hwc->sample_period == 1) {
/* BTS is not supported by this architecture. */
if (!x86_pmu.bts_active)
return -EOPNOTSUPP;
cpuc = &__get_cpu_var(cpu_hw_events);
+ /*
+ * Some chipsets need to unmask the LVTPC in a particular spot
+ * inside the nmi handler. As a result, the unmasking was pushed
+ * into all the nmi handlers.
+ *
+ * This generic handler doesn't seem to have any issues where the
+ * unmasking occurs so it was left at the top.
+ */
+ apic_write(APIC_LVTPC, APIC_DM_NMI);
+
for (idx = 0; idx < x86_pmu.num_counters; idx++) {
if (!test_bit(idx, cpuc->active_mask)) {
/*
return NOTIFY_DONE;
}
- apic_write(APIC_LVTPC, APIC_DM_NMI);
-
handled = x86_pmu.handle_irq(args->regs);
if (!handled)
return NOTIFY_DONE;
[ C(L1D) ] = {
[ C(OP_READ) ] = {
[ C(RESULT_ACCESS) ] = 0x0040, /* Data Cache Accesses */
- [ C(RESULT_MISS) ] = 0x0041, /* Data Cache Misses */
+ [ C(RESULT_MISS) ] = 0x0141, /* Data Cache Misses */
},
[ C(OP_WRITE) ] = {
[ C(RESULT_ACCESS) ] = 0x0142, /* Data Cache Refills :system */
*
* Exceptions:
*
+ * 0x000 FP PERF_CTL[3], PERF_CTL[5:3] (*)
* 0x003 FP PERF_CTL[3]
+ * 0x004 FP PERF_CTL[3], PERF_CTL[5:3] (*)
* 0x00B FP PERF_CTL[3]
* 0x00D FP PERF_CTL[3]
* 0x023 DE PERF_CTL[2:0]
* 0x0DF LS PERF_CTL[5:0]
* 0x1D6 EX PERF_CTL[5:0]
* 0x1D8 EX PERF_CTL[5:0]
+ *
+ * (*) depending on the umask all FPU counters may be used
*/
static struct event_constraint amd_f15_PMC0 = EVENT_CONSTRAINT(0, 0x01, 0);
static struct event_constraint *
amd_get_event_constraints_f15h(struct cpu_hw_events *cpuc, struct perf_event *event)
{
- unsigned int event_code = amd_get_event_code(&event->hw);
+ struct hw_perf_event *hwc = &event->hw;
+ unsigned int event_code = amd_get_event_code(hwc);
switch (event_code & AMD_EVENT_TYPE_MASK) {
case AMD_EVENT_FP:
switch (event_code) {
+ case 0x000:
+ if (!(hwc->config & 0x0000F000ULL))
+ break;
+ if (!(hwc->config & 0x00000F00ULL))
+ break;
+ return &amd_f15_PMC3;
+ case 0x004:
+ if (hweight_long(hwc->config & ARCH_PERFMON_EVENTSEL_UMASK) <= 1)
+ break;
+ return &amd_f15_PMC3;
case 0x003:
case 0x00B:
case 0x00D:
return &amd_f15_PMC3;
- default:
- return &amd_f15_PMC53;
}
+ return &amd_f15_PMC53;
case AMD_EVENT_LS:
case AMD_EVENT_DC:
case AMD_EVENT_EX_LS:
/*
* Intel PerfMon, used on Core and later.
*/
-static const u64 intel_perfmon_event_map[] =
+static u64 intel_perfmon_event_map[PERF_COUNT_HW_MAX] __read_mostly =
{
[PERF_COUNT_HW_CPU_CYCLES] = 0x003c,
[PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0,
},
},
[ C(LL ) ] = {
- /*
- * TBD: Need Off-core Response Performance Monitoring support
- */
[ C(OP_READ) ] = {
- /* OFFCORE_RESPONSE_0.ANY_DATA.LOCAL_CACHE */
+ /* OFFCORE_RESPONSE.ANY_DATA.LOCAL_CACHE */
[ C(RESULT_ACCESS) ] = 0x01b7,
- /* OFFCORE_RESPONSE_1.ANY_DATA.ANY_LLC_MISS */
- [ C(RESULT_MISS) ] = 0x01bb,
+ /* OFFCORE_RESPONSE.ANY_DATA.ANY_LLC_MISS */
+ [ C(RESULT_MISS) ] = 0x01b7,
},
[ C(OP_WRITE) ] = {
- /* OFFCORE_RESPONSE_0.ANY_RFO.LOCAL_CACHE */
+ /* OFFCORE_RESPONSE.ANY_RFO.LOCAL_CACHE */
[ C(RESULT_ACCESS) ] = 0x01b7,
- /* OFFCORE_RESPONSE_1.ANY_RFO.ANY_LLC_MISS */
- [ C(RESULT_MISS) ] = 0x01bb,
+ /* OFFCORE_RESPONSE.ANY_RFO.ANY_LLC_MISS */
+ [ C(RESULT_MISS) ] = 0x01b7,
},
[ C(OP_PREFETCH) ] = {
- /* OFFCORE_RESPONSE_0.PREFETCH.LOCAL_CACHE */
+ /* OFFCORE_RESPONSE.PREFETCH.LOCAL_CACHE */
[ C(RESULT_ACCESS) ] = 0x01b7,
- /* OFFCORE_RESPONSE_1.PREFETCH.ANY_LLC_MISS */
- [ C(RESULT_MISS) ] = 0x01bb,
+ /* OFFCORE_RESPONSE.PREFETCH.ANY_LLC_MISS */
+ [ C(RESULT_MISS) ] = 0x01b7,
},
},
[ C(DTLB) ] = {
},
[ C(LL ) ] = {
[ C(OP_READ) ] = {
- /* OFFCORE_RESPONSE_0.ANY_DATA.LOCAL_CACHE */
+ /* OFFCORE_RESPONSE.ANY_DATA.LOCAL_CACHE */
[ C(RESULT_ACCESS) ] = 0x01b7,
- /* OFFCORE_RESPONSE_1.ANY_DATA.ANY_LLC_MISS */
- [ C(RESULT_MISS) ] = 0x01bb,
+ /* OFFCORE_RESPONSE.ANY_DATA.ANY_LLC_MISS */
+ [ C(RESULT_MISS) ] = 0x01b7,
},
/*
* Use RFO, not WRITEBACK, because a write miss would typically occur
* on RFO.
*/
[ C(OP_WRITE) ] = {
- /* OFFCORE_RESPONSE_1.ANY_RFO.LOCAL_CACHE */
- [ C(RESULT_ACCESS) ] = 0x01bb,
- /* OFFCORE_RESPONSE_0.ANY_RFO.ANY_LLC_MISS */
+ /* OFFCORE_RESPONSE.ANY_RFO.LOCAL_CACHE */
+ [ C(RESULT_ACCESS) ] = 0x01b7,
+ /* OFFCORE_RESPONSE.ANY_RFO.ANY_LLC_MISS */
[ C(RESULT_MISS) ] = 0x01b7,
},
[ C(OP_PREFETCH) ] = {
- /* OFFCORE_RESPONSE_0.PREFETCH.LOCAL_CACHE */
+ /* OFFCORE_RESPONSE.PREFETCH.LOCAL_CACHE */
[ C(RESULT_ACCESS) ] = 0x01b7,
- /* OFFCORE_RESPONSE_1.PREFETCH.ANY_LLC_MISS */
- [ C(RESULT_MISS) ] = 0x01bb,
+ /* OFFCORE_RESPONSE.PREFETCH.ANY_LLC_MISS */
+ [ C(RESULT_MISS) ] = 0x01b7,
},
},
[ C(DTLB) ] = {
};
/*
- * OFFCORE_RESPONSE MSR bits (subset), See IA32 SDM Vol 3 30.6.1.3
+ * Nehalem/Westmere MSR_OFFCORE_RESPONSE bits;
+ * See IA32 SDM Vol 3B 30.6.1.3
*/
-#define DMND_DATA_RD (1 << 0)
-#define DMND_RFO (1 << 1)
-#define DMND_WB (1 << 3)
-#define PF_DATA_RD (1 << 4)
-#define PF_DATA_RFO (1 << 5)
-#define RESP_UNCORE_HIT (1 << 8)
-#define RESP_MISS (0xf600) /* non uncore hit */
+#define NHM_DMND_DATA_RD (1 << 0)
+#define NHM_DMND_RFO (1 << 1)
+#define NHM_DMND_IFETCH (1 << 2)
+#define NHM_DMND_WB (1 << 3)
+#define NHM_PF_DATA_RD (1 << 4)
+#define NHM_PF_DATA_RFO (1 << 5)
+#define NHM_PF_IFETCH (1 << 6)
+#define NHM_OFFCORE_OTHER (1 << 7)
+#define NHM_UNCORE_HIT (1 << 8)
+#define NHM_OTHER_CORE_HIT_SNP (1 << 9)
+#define NHM_OTHER_CORE_HITM (1 << 10)
+ /* reserved */
+#define NHM_REMOTE_CACHE_FWD (1 << 12)
+#define NHM_REMOTE_DRAM (1 << 13)
+#define NHM_LOCAL_DRAM (1 << 14)
+#define NHM_NON_DRAM (1 << 15)
+
+#define NHM_ALL_DRAM (NHM_REMOTE_DRAM|NHM_LOCAL_DRAM)
+
+#define NHM_DMND_READ (NHM_DMND_DATA_RD)
+#define NHM_DMND_WRITE (NHM_DMND_RFO|NHM_DMND_WB)
+#define NHM_DMND_PREFETCH (NHM_PF_DATA_RD|NHM_PF_DATA_RFO)
+
+#define NHM_L3_HIT (NHM_UNCORE_HIT|NHM_OTHER_CORE_HIT_SNP|NHM_OTHER_CORE_HITM)
+#define NHM_L3_MISS (NHM_NON_DRAM|NHM_ALL_DRAM|NHM_REMOTE_CACHE_FWD)
+#define NHM_L3_ACCESS (NHM_L3_HIT|NHM_L3_MISS)
static __initconst const u64 nehalem_hw_cache_extra_regs
[PERF_COUNT_HW_CACHE_MAX]
{
[ C(LL ) ] = {
[ C(OP_READ) ] = {
- [ C(RESULT_ACCESS) ] = DMND_DATA_RD|RESP_UNCORE_HIT,
- [ C(RESULT_MISS) ] = DMND_DATA_RD|RESP_MISS,
+ [ C(RESULT_ACCESS) ] = NHM_DMND_READ|NHM_L3_ACCESS,
+ [ C(RESULT_MISS) ] = NHM_DMND_READ|NHM_L3_MISS,
},
[ C(OP_WRITE) ] = {
- [ C(RESULT_ACCESS) ] = DMND_RFO|DMND_WB|RESP_UNCORE_HIT,
- [ C(RESULT_MISS) ] = DMND_RFO|DMND_WB|RESP_MISS,
+ [ C(RESULT_ACCESS) ] = NHM_DMND_WRITE|NHM_L3_ACCESS,
+ [ C(RESULT_MISS) ] = NHM_DMND_WRITE|NHM_L3_MISS,
},
[ C(OP_PREFETCH) ] = {
- [ C(RESULT_ACCESS) ] = PF_DATA_RD|PF_DATA_RFO|RESP_UNCORE_HIT,
- [ C(RESULT_MISS) ] = PF_DATA_RD|PF_DATA_RFO|RESP_MISS,
+ [ C(RESULT_ACCESS) ] = NHM_DMND_PREFETCH|NHM_L3_ACCESS,
+ [ C(RESULT_MISS) ] = NHM_DMND_PREFETCH|NHM_L3_MISS,
},
}
};
{
[ C(L1D) ] = {
[ C(OP_READ) ] = {
- [ C(RESULT_ACCESS) ] = 0x0f40, /* L1D_CACHE_LD.MESI */
- [ C(RESULT_MISS) ] = 0x0140, /* L1D_CACHE_LD.I_STATE */
+ [ C(RESULT_ACCESS) ] = 0x010b, /* MEM_INST_RETIRED.LOADS */
+ [ C(RESULT_MISS) ] = 0x0151, /* L1D.REPL */
},
[ C(OP_WRITE) ] = {
- [ C(RESULT_ACCESS) ] = 0x0f41, /* L1D_CACHE_ST.MESI */
- [ C(RESULT_MISS) ] = 0x0141, /* L1D_CACHE_ST.I_STATE */
+ [ C(RESULT_ACCESS) ] = 0x020b, /* MEM_INST_RETURED.STORES */
+ [ C(RESULT_MISS) ] = 0x0251, /* L1D.M_REPL */
},
[ C(OP_PREFETCH) ] = {
[ C(RESULT_ACCESS) ] = 0x014e, /* L1D_PREFETCH.REQUESTS */
cpuc = &__get_cpu_var(cpu_hw_events);
+ /*
+ * Some chipsets need to unmask the LVTPC in a particular spot
+ * inside the nmi handler. As a result, the unmasking was pushed
+ * into all the nmi handlers.
+ *
+ * This handler doesn't seem to have any issues with the unmasking
+ * so it was left at the top.
+ */
+ apic_write(APIC_LVTPC, APIC_DM_NMI);
+
intel_pmu_disable_all();
handled = intel_pmu_drain_bts_buffer();
status = intel_pmu_get_status();
struct hw_perf_event *hwc = &event->hw;
unsigned int hw_event, bts_event;
+ if (event->attr.freq)
+ return NULL;
+
hw_event = hwc->config & INTEL_ARCH_EVENT_MASK;
bts_event = x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS);
* AJ106 could possibly be worked around by not allowing LBR
* usage from PEBS, including the fixup.
* AJ68 could possibly be worked around by always programming
- * a pebs_event_reset[0] value and coping with the lost events.
+ * a pebs_event_reset[0] value and coping with the lost events.
*
* But taken together it might just make sense to not enable PEBS on
* these chips.
x86_pmu.percore_constraints = intel_nehalem_percore_constraints;
x86_pmu.enable_all = intel_pmu_nhm_enable_all;
x86_pmu.extra_regs = intel_nehalem_extra_regs;
+
+ if (ebx & 0x40) {
+ /*
+ * Erratum AAJ80 detected, we work it around by using
+ * the BR_MISP_EXEC.ANY event. This will over-count
+ * branch-misses, but it's still much better than the
+ * architectural event which is often completely bogus:
+ */
+ intel_perfmon_event_map[PERF_COUNT_HW_BRANCH_MISSES] = 0x7f89;
+
+ pr_cont("erratum AAJ80 worked around, ");
+ }
pr_cont("Nehalem events, ");
break;
case 37: /* 32 nm nehalem, "Clarkdale" */
case 44: /* 32 nm nehalem, "Gulftown" */
+ case 47: /* 32 nm Xeon E7 */
memcpy(hw_cache_event_ids, westmere_hw_cache_event_ids,
sizeof(hw_cache_event_ids));
memcpy(hw_cache_extra_regs, nehalem_hw_cache_extra_regs,
if (!x86_perf_event_set_period(event))
continue;
if (perf_event_overflow(event, 1, &data, regs))
- p4_pmu_disable_event(event);
+ x86_pmu_stop(event, 0);
}
- if (handled) {
- /* p4 quirk: unmask it again */
- apic_write(APIC_LVTPC, apic_read(APIC_LVTPC) & ~APIC_LVT_MASKED);
+ if (handled)
inc_irq_stat(apic_perf_irqs);
- }
+
+ /*
+ * When dealing with the unmasking of the LVTPC on P4 perf hw, it has
+ * been observed that the OVF bit flag has to be cleared first _before_
+ * the LVTPC can be unmasked.
+ *
+ * The reason is the NMI line will continue to be asserted while the OVF
+ * bit is set. This causes a second NMI to generate if the LVTPC is
+ * unmasked before the OVF bit is cleared, leading to unknown NMI
+ * messages.
+ */
+ apic_write(APIC_LVTPC, APIC_DM_NMI);
return handled;
}
set_io_apic_irq_attr(&attr, idx, line, it->trigger, it->polarity);
- return io_apic_setup_irq_pin(*out_hwirq, cpu_to_node(0), &attr);
+ return io_apic_setup_irq_pin_once(*out_hwirq, cpu_to_node(0), &attr);
}
static void __init ioapic_add_ofnode(struct device_node *np)
#define AGPEXTERN
#endif
+/* GART can only remap to physical addresses < 1TB */
+#define GART_MAX_PHYS_ADDR (1ULL << 40)
+
/* backdoor interface to AGP driver */
AGPEXTERN int agp_memory_reserved;
AGPEXTERN __u32 *agp_gatt_table;
size_t size, int dir, unsigned long align_mask)
{
unsigned long npages = iommu_num_pages(phys_mem, size, PAGE_SIZE);
- unsigned long iommu_page = alloc_iommu(dev, npages, align_mask);
+ unsigned long iommu_page;
int i;
+ if (unlikely(phys_mem + size > GART_MAX_PHYS_ADDR))
+ return bad_dma_addr;
+
+ iommu_page = alloc_iommu(dev, npages, align_mask);
if (iommu_page == -1) {
if (!nonforced_iommu(dev, phys_mem, size))
return phys_mem;
unsigned len, type;
struct perf_event *bp;
+ if (ptrace_get_breakpoints(tsk) < 0)
+ return -ESRCH;
+
data &= ~DR_CONTROL_RESERVED;
old_dr7 = ptrace_get_dr7(thread->ptrace_bps);
restore:
}
goto restore;
}
+
+ ptrace_put_breakpoints(tsk);
+
return ((orig_ret < 0) ? orig_ret : rc);
}
if (n < HBP_NUM) {
struct perf_event *bp;
+
+ if (ptrace_get_breakpoints(tsk) < 0)
+ return -ESRCH;
+
bp = thread->ptrace_bps[n];
if (!bp)
- return 0;
- val = bp->hw.info.address;
+ val = 0;
+ else
+ val = bp->hw.info.address;
+
+ ptrace_put_breakpoints(tsk);
} else if (n == 6) {
val = thread->debugreg6;
} else if (n == 7) {
struct perf_event *bp;
struct thread_struct *t = &tsk->thread;
struct perf_event_attr attr;
+ int err = 0;
+
+ if (ptrace_get_breakpoints(tsk) < 0)
+ return -ESRCH;
if (!t->ptrace_bps[nr]) {
ptrace_breakpoint_init(&attr);
* writing for the user. And anyway this is the previous
* behaviour.
*/
- if (IS_ERR(bp))
- return PTR_ERR(bp);
+ if (IS_ERR(bp)) {
+ err = PTR_ERR(bp);
+ goto put;
+ }
t->ptrace_bps[nr] = bp;
} else {
- int err;
-
bp = t->ptrace_bps[nr];
attr = bp->attr;
attr.bp_addr = addr;
err = modify_user_hw_breakpoint(bp, &attr);
- if (err)
- return err;
}
-
- return 0;
+put:
+ ptrace_put_breakpoints(tsk);
+ return err;
}
/*
/* Get our own relocated address */
call 1f
1: popl %ebx
- subl $1b, %ebx
+ subl $(1b - r_base), %ebx
/* Compute the equivalent real-mode segment */
movl %ebx, %ecx
shrl $4, %ecx
/* Patch post-real-mode segment jump */
- movw dispatch_table(%ebx,%eax,2),%ax
- movw %ax, 101f(%ebx)
- movw %cx, 102f(%ebx)
+ movw (dispatch_table - r_base)(%ebx,%eax,2),%ax
+ movw %ax, (101f - r_base)(%ebx)
+ movw %cx, (102f - r_base)(%ebx)
/* Set up the IDT for real mode. */
- lidtl machine_real_restart_idt(%ebx)
+ lidtl (machine_real_restart_idt - r_base)(%ebx)
/*
* Set up a GDT from which we can load segment descriptors for real
* mode. The GDT is not used in real mode; it is just needed here to
* prepare the descriptors.
*/
- lgdtl machine_real_restart_gdt(%ebx)
+ lgdtl (machine_real_restart_gdt - r_base)(%ebx)
/*
* Load the data segment registers with 16-bit compatible values
return per_cpu(x86_cpu_to_node_map, cpu);
}
-struct cpumask __cpuinit *debug_cpumask_set_cpu(int cpu, int enable)
+void debug_cpumask_set_cpu(int cpu, int node, bool enable)
{
- int node = early_cpu_to_node(cpu);
struct cpumask *mask;
char buf[64];
if (node == NUMA_NO_NODE) {
/* early_cpu_to_node() already emits a warning and trace */
- return NULL;
+ return;
}
mask = node_to_cpumask_map[node];
if (!mask) {
pr_err("node_to_cpumask_map[%i] NULL\n", node);
dump_stack();
- return NULL;
+ return;
}
+ if (enable)
+ cpumask_set_cpu(cpu, mask);
+ else
+ cpumask_clear_cpu(cpu, mask);
+
cpulist_scnprintf(buf, sizeof(buf), mask);
printk(KERN_DEBUG "%s cpu %d node %d: mask now %s\n",
enable ? "numa_add_cpu" : "numa_remove_cpu",
cpu, node, buf);
- return mask;
+ return;
}
# ifndef CONFIG_NUMA_EMU
-static void __cpuinit numa_set_cpumask(int cpu, int enable)
+static void __cpuinit numa_set_cpumask(int cpu, bool enable)
{
- struct cpumask *mask;
-
- mask = debug_cpumask_set_cpu(cpu, enable);
- if (!mask)
- return;
-
- if (enable)
- cpumask_set_cpu(cpu, mask);
- else
- cpumask_clear_cpu(cpu, mask);
+ debug_cpumask_set_cpu(cpu, early_cpu_to_node(cpu), enable);
}
void __cpuinit numa_add_cpu(int cpu)
{
- numa_set_cpumask(cpu, 1);
+ numa_set_cpumask(cpu, true);
}
void __cpuinit numa_remove_cpu(int cpu)
{
- numa_set_cpumask(cpu, 0);
+ numa_set_cpumask(cpu, false);
}
# endif /* !CONFIG_NUMA_EMU */
bi->end = min(bi->end, high);
/* and there's no empty block */
- if (bi->start == bi->end) {
+ if (bi->start >= bi->end) {
numa_remove_memblk_from(i--, mi);
continue;
}
cpumask_clear_cpu(cpu, node_to_cpumask_map[i]);
}
#else /* !CONFIG_DEBUG_PER_CPU_MAPS */
-static void __cpuinit numa_set_cpumask(int cpu, int enable)
+static void __cpuinit numa_set_cpumask(int cpu, bool enable)
{
- struct cpumask *mask;
- int nid, physnid, i;
+ int nid, physnid;
nid = early_cpu_to_node(cpu);
if (nid == NUMA_NO_NODE) {
physnid = emu_nid_to_phys[nid];
- for_each_online_node(i) {
+ for_each_online_node(nid) {
if (emu_nid_to_phys[nid] != physnid)
continue;
- mask = debug_cpumask_set_cpu(cpu, enable);
- if (!mask)
- return;
-
- if (enable)
- cpumask_set_cpu(cpu, mask);
- else
- cpumask_clear_cpu(cpu, mask);
+ debug_cpumask_set_cpu(cpu, nid, enable);
}
}
void __cpuinit numa_add_cpu(int cpu)
{
- numa_set_cpumask(cpu, 1);
+ numa_set_cpumask(cpu, true);
}
void __cpuinit numa_remove_cpu(int cpu)
{
- numa_set_cpumask(cpu, 0);
+ numa_set_cpumask(cpu, false);
}
#endif /* !CONFIG_DEBUG_PER_CPU_MAPS */
compatible = "intel,ce4100-pci", "pci";
device_type = "pci";
bus-range = <1 1>;
+ reg = <0x0800 0x0 0x0 0x0 0x0>;
ranges = <0x2000000 0 0xdffe0000 0x2000000 0 0xdffe0000 0 0x1000>;
interrupt-parent = <&ioapic2>;
"pciclass0c03";
reg = <0x16800 0x0 0x0 0x0 0x0>;
- interrupts = <22 3>;
+ interrupts = <22 1>;
};
usb@d,1 {
"pciclass0c03";
reg = <0x16900 0x0 0x0 0x0 0x0>;
- interrupts = <22 3>;
+ interrupts = <22 1>;
};
sata@e,0 {
"pciclass0106";
reg = <0x17000 0x0 0x0 0x0 0x0>;
- interrupts = <23 3>;
+ interrupts = <23 1>;
};
flash@f,0 {
#address-cells = <2>;
#size-cells = <1>;
compatible = "isa";
+ reg = <0xf800 0x0 0x0 0x0 0x0>;
ranges = <1 0 0 0 0 0x100>;
rtc@70 {
pentry->freq_hz, pentry->irq);
if (!pentry->irq)
continue;
- mp_irq.type = MP_IOAPIC;
+ mp_irq.type = MP_INTSRC;
mp_irq.irqtype = mp_INT;
/* triggering mode edge bit 2-3, active high polarity bit 0-1 */
mp_irq.irqflag = 5;
- mp_irq.srcbus = 0;
+ mp_irq.srcbus = MP_BUS_ISA;
mp_irq.srcbusirq = pentry->irq; /* IRQ */
mp_irq.dstapic = MP_APIC_ALL;
mp_irq.dstirq = pentry->irq;
for (totallen = 0; totallen < sfi_mrtc_num; totallen++, pentry++) {
pr_debug("RTC[%d]: paddr = 0x%08x, irq = %d\n",
totallen, (u32)pentry->phys_addr, pentry->irq);
- mp_irq.type = MP_IOAPIC;
+ mp_irq.type = MP_INTSRC;
mp_irq.irqtype = mp_INT;
mp_irq.irqflag = 0xf; /* level trigger and active low */
- mp_irq.srcbus = 0;
+ mp_irq.srcbus = MP_BUS_ISA;
mp_irq.srcbusirq = pentry->irq; /* IRQ */
mp_irq.dstapic = MP_APIC_ALL;
mp_irq.dstirq = pentry->irq;
/* Avoid searching for BIOS MP tables */
x86_init.mpparse.find_smp_config = x86_init_noop;
x86_init.mpparse.get_smp_config = x86_init_uint_noop;
-
+ set_bit(MP_BUS_ISA, mp_bus_not_pci);
}
/*
config XEN_SAVE_RESTORE
bool
depends on XEN
+ select HIBERNATE_CALLBACKS
default y
config XEN_DEBUG_FS
static __init void xen_init_cpuid_mask(void)
{
unsigned int ax, bx, cx, dx;
+ unsigned int xsave_mask;
cpuid_leaf1_edx_mask =
~((1 << X86_FEATURE_MCE) | /* disable MCE */
cpuid_leaf1_edx_mask &=
~((1 << X86_FEATURE_APIC) | /* disable local APIC */
(1 << X86_FEATURE_ACPI)); /* disable ACPI */
-
ax = 1;
- cx = 0;
xen_cpuid(&ax, &bx, &cx, &dx);
- /* cpuid claims we support xsave; try enabling it to see what happens */
- if (cx & (1 << (X86_FEATURE_XSAVE % 32))) {
- unsigned long cr4;
-
- set_in_cr4(X86_CR4_OSXSAVE);
-
- cr4 = read_cr4();
+ xsave_mask =
+ (1 << (X86_FEATURE_XSAVE % 32)) |
+ (1 << (X86_FEATURE_OSXSAVE % 32));
- if ((cr4 & X86_CR4_OSXSAVE) == 0)
- cpuid_leaf1_ecx_mask &= ~(1 << (X86_FEATURE_XSAVE % 32));
-
- clear_in_cr4(X86_CR4_OSXSAVE);
- }
+ /* Xen will set CR4.OSXSAVE if supported and not disabled by force */
+ if ((cx & xsave_mask) != xsave_mask)
+ cpuid_leaf1_ecx_mask &= ~xsave_mask; /* disable XSAVE & OSXSAVE */
}
static void xen_set_debugreg(int reg, unsigned long val)
if (io_page &&
(xen_initial_domain() || addr >= ISA_END_ADDRESS)) {
other_addr = pfn_to_mfn(addr >> PAGE_SHIFT) << PAGE_SHIFT;
- WARN(addr != other_addr,
+ WARN_ONCE(addr != other_addr,
"0x%lx is using VM_IO, but it is 0x%lx!\n",
(unsigned long)addr, (unsigned long)other_addr);
} else {
pteval_t iomap_set = (_pte.pte & PTE_FLAGS_MASK) & _PAGE_IOMAP;
other_addr = (_pte.pte & PTE_PFN_MASK);
- WARN((addr == other_addr) && (!io_page) && (!iomap_set),
+ WARN_ONCE((addr == other_addr) && (!io_page) && (!iomap_set),
"0x%lx is missing VM_IO (and wasn't fixed)!\n",
(unsigned long)addr);
}
return ret;
}
+#ifdef CONFIG_X86_64
+static __initdata u64 __last_pgt_set_rw = 0;
+static __initdata u64 __pgt_buf_start = 0;
+static __initdata u64 __pgt_buf_end = 0;
+static __initdata u64 __pgt_buf_top = 0;
+/*
+ * As a consequence of the commit:
+ *
+ * commit 4b239f458c229de044d6905c2b0f9fe16ed9e01e
+ * Author: Yinghai Lu <yinghai@kernel.org>
+ * Date: Fri Dec 17 16:58:28 2010 -0800
+ *
+ * x86-64, mm: Put early page table high
+ *
+ * at some point init_memory_mapping is going to reach the pagetable pages
+ * area and map those pages too (mapping them as normal memory that falls
+ * in the range of addresses passed to init_memory_mapping as argument).
+ * Some of those pages are already pagetable pages (they are in the range
+ * pgt_buf_start-pgt_buf_end) therefore they are going to be mapped RO and
+ * everything is fine.
+ * Some of these pages are not pagetable pages yet (they fall in the range
+ * pgt_buf_end-pgt_buf_top; for example the page at pgt_buf_end) so they
+ * are going to be mapped RW. When these pages become pagetable pages and
+ * are hooked into the pagetable, xen will find that the guest has already
+ * a RW mapping of them somewhere and fail the operation.
+ * The reason Xen requires pagetables to be RO is that the hypervisor needs
+ * to verify that the pagetables are valid before using them. The validation
+ * operations are called "pinning".
+ *
+ * In order to fix the issue we mark all the pages in the entire range
+ * pgt_buf_start-pgt_buf_top as RO, however when the pagetable allocation
+ * is completed only the range pgt_buf_start-pgt_buf_end is reserved by
+ * init_memory_mapping. Hence the kernel is going to crash as soon as one
+ * of the pages in the range pgt_buf_end-pgt_buf_top is reused (b/c those
+ * ranges are RO).
+ *
+ * For this reason, 'mark_rw_past_pgt' is introduced which is called _after_
+ * the init_memory_mapping has completed (in a perfect world we would
+ * call this function from init_memory_mapping, but lets ignore that).
+ *
+ * Because we are called _after_ init_memory_mapping the pgt_buf_[start,
+ * end,top] have all changed to new values (b/c init_memory_mapping
+ * is called and setting up another new page-table). Hence, the first time
+ * we enter this function, we save away the pgt_buf_start value and update
+ * the pgt_buf_[end,top].
+ *
+ * When we detect that the "old" pgt_buf_start through pgt_buf_end
+ * PFNs have been reserved (so memblock_x86_reserve_range has been called),
+ * we immediately set out to RW the "old" pgt_buf_end through pgt_buf_top.
+ *
+ * And then we update those "old" pgt_buf_[end|top] with the new ones
+ * so that we can redo this on the next pagetable.
+ */
+static __init void mark_rw_past_pgt(void) {
+
+ if (pgt_buf_end > pgt_buf_start) {
+ u64 addr, size;
+
+ /* Save it away. */
+ if (!__pgt_buf_start) {
+ __pgt_buf_start = pgt_buf_start;
+ __pgt_buf_end = pgt_buf_end;
+ __pgt_buf_top = pgt_buf_top;
+ return;
+ }
+ /* If we get the range that starts at __pgt_buf_end that means
+ * the range is reserved, and that in 'init_memory_mapping'
+ * the 'memblock_x86_reserve_range' has been called with the
+ * outdated __pgt_buf_start, __pgt_buf_end (the "new"
+ * pgt_buf_[start|end|top] refer now to a new pagetable.
+ * Note: we are called _after_ the pgt_buf_[..] have been
+ * updated.*/
+
+ addr = memblock_x86_find_in_range_size(PFN_PHYS(__pgt_buf_start),
+ &size, PAGE_SIZE);
+
+ /* Still not reserved, meaning 'memblock_x86_reserve_range'
+ * hasn't been called yet. Update the _end and _top.*/
+ if (addr == PFN_PHYS(__pgt_buf_start)) {
+ __pgt_buf_end = pgt_buf_end;
+ __pgt_buf_top = pgt_buf_top;
+ return;
+ }
+
+ /* OK, the area is reserved, meaning it is time for us to
+ * set RW for the old end->top PFNs. */
+
+ /* ..unless we had already done this. */
+ if (__pgt_buf_end == __last_pgt_set_rw)
+ return;
+
+ addr = PFN_PHYS(__pgt_buf_end);
+
+ /* set as RW the rest */
+ printk(KERN_DEBUG "xen: setting RW the range %llx - %llx\n",
+ PFN_PHYS(__pgt_buf_end), PFN_PHYS(__pgt_buf_top));
+
+ while (addr < PFN_PHYS(__pgt_buf_top)) {
+ make_lowmem_page_readwrite(__va(addr));
+ addr += PAGE_SIZE;
+ }
+ /* And update everything so that we are ready for the next
+ * pagetable (the one created for regions past 4GB) */
+ __last_pgt_set_rw = __pgt_buf_end;
+ __pgt_buf_start = pgt_buf_start;
+ __pgt_buf_end = pgt_buf_end;
+ __pgt_buf_top = pgt_buf_top;
+ }
+ return;
+}
+#else
+static __init void mark_rw_past_pgt(void) { }
+#endif
static void xen_pgd_free(struct mm_struct *mm, pgd_t *pgd)
{
#ifdef CONFIG_X86_64
#endif
}
+#ifdef CONFIG_X86_32
static __init pte_t mask_rw_pte(pte_t *ptep, pte_t pte)
{
- unsigned long pfn = pte_pfn(pte);
-
-#ifdef CONFIG_X86_32
/* If there's an existing pte, then don't allow _PAGE_RW to be set */
if (pte_val_ma(*ptep) & _PAGE_PRESENT)
pte = __pte_ma(((pte_val_ma(*ptep) & _PAGE_RW) | ~_PAGE_RW) &
pte_val_ma(pte));
-#endif
+
+ return pte;
+}
+#else /* CONFIG_X86_64 */
+static __init pte_t mask_rw_pte(pte_t *ptep, pte_t pte)
+{
+ unsigned long pfn = pte_pfn(pte);
+ /*
+ * A bit of optimization. We do not need to call the workaround
+ * when xen_set_pte_init is called with a PTE with 0 as PFN.
+ * That is b/c the pagetable at that point are just being populated
+ * with empty values and we can save some cycles by not calling
+ * the 'memblock' code.*/
+ if (pfn)
+ mark_rw_past_pgt();
/*
* If the new pfn is within the range of the newly allocated
* kernel pagetable, and it isn't being mapped into an
* it is RO.
*/
if (((!is_early_ioremap_ptep(ptep) &&
- pfn >= pgt_buf_start && pfn < pgt_buf_end)) ||
+ pfn >= pgt_buf_start && pfn < pgt_buf_top)) ||
(is_early_ioremap_ptep(ptep) && pfn != (pgt_buf_end - 1)))
pte = pte_wrprotect(pte);
return pte;
}
+#endif /* CONFIG_X86_64 */
/* Init-time set_pte while constructing initial pagetables, which
doesn't allow RO pagetable pages to be remapped RW */
static __init void xen_post_allocator_init(void)
{
+ mark_rw_past_pgt();
+
#ifdef CONFIG_XEN_DEBUG
pv_mmu_ops.make_pte = PV_CALLEE_SAVE(xen_make_pte_debug);
#endif
memcpy(map_raw, map, sizeof(map));
e820.nr_map = 0;
- xen_extra_mem_start = mem_end;
+ xen_extra_mem_start = max((1ULL << 32), mem_end);
for (i = 0; i < memmap.nr_entries; i++) {
unsigned long long end;
int arch_show_interrupts(struct seq_file *p, int prec)
{
- int j;
-
- seq_printf(p, "%*s: ", prec, "NMI");
- for_each_online_cpu(j)
- seq_printf(p, "%10u ", nmi_count(j));
- seq_putc(p, '\n');
seq_printf(p, "%*s: ", prec, "ERR");
seq_printf(p, "%10u\n", atomic_read(&irq_err_count));
return 0;
}
-static void xtensa_irq_mask(struct irq_chip *d)
+static void xtensa_irq_mask(struct irq_data *d)
{
cached_irq_mask &= ~(1 << d->irq);
set_sr (cached_irq_mask, INTENABLE);
}
-static void xtensa_irq_unmask(struct irq_chip *d)
+static void xtensa_irq_unmask(struct irq_data *d)
{
cached_irq_mask |= 1 << d->irq;
set_sr (cached_irq_mask, INTENABLE);
}
-static void xtensa_irq_enable(struct irq_chip *d)
+static void xtensa_irq_enable(struct irq_data *d)
{
variant_irq_enable(d->irq);
xtensa_irq_unmask(d->irq);
}
-static void xtensa_irq_disable(struct irq_chip *d)
+static void xtensa_irq_disable(struct irq_data *d)
{
xtensa_irq_mask(d->irq);
variant_irq_disable(d->irq);
}
-static void xtensa_irq_ack(struct irq_chip *d)
+static void xtensa_irq_ack(struct irq_data *d)
{
set_sr(1 << d->irq, INTCLEAR);
}
-static int xtensa_irq_retrigger(struct irq_chip *d)
+static int xtensa_irq_retrigger(struct irq_data *d)
{
set_sr (1 << d->irq, INTSET);
return 1;
}
EXPORT_SYMBOL(blk_dump_rq_flags);
-/*
- * Make sure that plugs that were pending when this function was entered,
- * are now complete and requests pushed to the queue.
-*/
-static inline void queue_sync_plugs(struct request_queue *q)
-{
- /*
- * If the current process is plugged and has barriers submitted,
- * we will livelock if we don't unplug first.
- */
- blk_flush_plug(current);
-}
-
static void blk_delay_work(struct work_struct *work)
{
struct request_queue *q;
q = container_of(work, struct request_queue, delay_work.work);
spin_lock_irq(q->queue_lock);
- __blk_run_queue(q, false);
+ __blk_run_queue(q);
spin_unlock_irq(q->queue_lock);
}
*/
void blk_delay_queue(struct request_queue *q, unsigned long msecs)
{
- schedule_delayed_work(&q->delay_work, msecs_to_jiffies(msecs));
+ queue_delayed_work(kblockd_workqueue, &q->delay_work,
+ msecs_to_jiffies(msecs));
}
EXPORT_SYMBOL(blk_delay_queue);
WARN_ON(!irqs_disabled());
queue_flag_clear(QUEUE_FLAG_STOPPED, q);
- __blk_run_queue(q, false);
+ __blk_run_queue(q);
}
EXPORT_SYMBOL(blk_start_queue);
{
del_timer_sync(&q->timeout);
cancel_delayed_work_sync(&q->delay_work);
- queue_sync_plugs(q);
}
EXPORT_SYMBOL(blk_sync_queue);
/**
* __blk_run_queue - run a single device queue
* @q: The queue to run
- * @force_kblockd: Don't run @q->request_fn directly. Use kblockd.
*
* Description:
* See @blk_run_queue. This variant must be called with the queue lock
* held and interrupts disabled.
- *
*/
-void __blk_run_queue(struct request_queue *q, bool force_kblockd)
+void __blk_run_queue(struct request_queue *q)
{
if (unlikely(blk_queue_stopped(q)))
return;
- /*
- * Only recurse once to avoid overrunning the stack, let the unplug
- * handling reinvoke the handler shortly if we already got there.
- */
- if (!force_kblockd && !queue_flag_test_and_set(QUEUE_FLAG_REENTER, q)) {
- q->request_fn(q);
- queue_flag_clear(QUEUE_FLAG_REENTER, q);
- } else
- queue_delayed_work(kblockd_workqueue, &q->delay_work, 0);
+ q->request_fn(q);
}
EXPORT_SYMBOL(__blk_run_queue);
+/**
+ * blk_run_queue_async - run a single device queue in workqueue context
+ * @q: The queue to run
+ *
+ * Description:
+ * Tells kblockd to perform the equivalent of @blk_run_queue on behalf
+ * of us.
+ */
+void blk_run_queue_async(struct request_queue *q)
+{
+ if (likely(!blk_queue_stopped(q)))
+ queue_delayed_work(kblockd_workqueue, &q->delay_work, 0);
+}
+EXPORT_SYMBOL(blk_run_queue_async);
+
/**
* blk_run_queue - run a single device queue
* @q: The queue to run
unsigned long flags;
spin_lock_irqsave(q->queue_lock, flags);
- __blk_run_queue(q, false);
+ __blk_run_queue(q);
spin_unlock_irqrestore(q->queue_lock, flags);
}
EXPORT_SYMBOL(blk_run_queue);
blk_queue_end_tag(q, rq);
add_acct_request(q, rq, where);
- __blk_run_queue(q, false);
+ __blk_run_queue(q);
spin_unlock_irqrestore(q->queue_lock, flags);
}
EXPORT_SYMBOL(blk_insert_request);
plug = current->plug;
if (plug) {
- if (!plug->should_sort && !list_empty(&plug->list)) {
+ /*
+ * If this is the first request added after a plug, fire
+ * of a plug trace. If others have been added before, check
+ * if we have multiple devices in this plug. If so, make a
+ * note to sort the list before dispatch.
+ */
+ if (list_empty(&plug->list))
+ trace_block_plug(q);
+ else if (!plug->should_sort) {
struct request *__rq;
__rq = list_entry_rq(plug->list.prev);
} else {
spin_lock_irq(q->queue_lock);
add_acct_request(q, req, where);
- __blk_run_queue(q, false);
+ __blk_run_queue(q);
out_unlock:
spin_unlock_irq(q->queue_lock);
}
plug->magic = PLUG_MAGIC;
INIT_LIST_HEAD(&plug->list);
+ INIT_LIST_HEAD(&plug->cb_list);
plug->should_sort = 0;
/*
return !(rqa->q <= rqb->q);
}
-static void flush_plug_list(struct blk_plug *plug)
+/*
+ * If 'from_schedule' is true, then postpone the dispatch of requests
+ * until a safe kblockd context. We due this to avoid accidental big
+ * additional stack usage in driver dispatch, in places where the originally
+ * plugger did not intend it.
+ */
+static void queue_unplugged(struct request_queue *q, unsigned int depth,
+ bool from_schedule)
+ __releases(q->queue_lock)
+{
+ trace_block_unplug(q, depth, !from_schedule);
+
+ /*
+ * If we are punting this to kblockd, then we can safely drop
+ * the queue_lock before waking kblockd (which needs to take
+ * this lock).
+ */
+ if (from_schedule) {
+ spin_unlock(q->queue_lock);
+ blk_run_queue_async(q);
+ } else {
+ __blk_run_queue(q);
+ spin_unlock(q->queue_lock);
+ }
+
+}
+
+static void flush_plug_callbacks(struct blk_plug *plug)
+{
+ LIST_HEAD(callbacks);
+
+ if (list_empty(&plug->cb_list))
+ return;
+
+ list_splice_init(&plug->cb_list, &callbacks);
+
+ while (!list_empty(&callbacks)) {
+ struct blk_plug_cb *cb = list_first_entry(&callbacks,
+ struct blk_plug_cb,
+ list);
+ list_del(&cb->list);
+ cb->callback(cb);
+ }
+}
+
+void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule)
{
struct request_queue *q;
unsigned long flags;
struct request *rq;
+ LIST_HEAD(list);
+ unsigned int depth;
BUG_ON(plug->magic != PLUG_MAGIC);
+ flush_plug_callbacks(plug);
if (list_empty(&plug->list))
return;
- if (plug->should_sort)
- list_sort(NULL, &plug->list, plug_rq_cmp);
+ list_splice_init(&plug->list, &list);
+
+ if (plug->should_sort) {
+ list_sort(NULL, &list, plug_rq_cmp);
+ plug->should_sort = 0;
+ }
q = NULL;
+ depth = 0;
+
+ /*
+ * Save and disable interrupts here, to avoid doing it for every
+ * queue lock we have to take.
+ */
local_irq_save(flags);
- while (!list_empty(&plug->list)) {
- rq = list_entry_rq(plug->list.next);
+ while (!list_empty(&list)) {
+ rq = list_entry_rq(list.next);
list_del_init(&rq->queuelist);
BUG_ON(!(rq->cmd_flags & REQ_ON_PLUG));
BUG_ON(!rq->q);
if (rq->q != q) {
- if (q) {
- __blk_run_queue(q, false);
- spin_unlock(q->queue_lock);
- }
+ /*
+ * This drops the queue lock
+ */
+ if (q)
+ queue_unplugged(q, depth, from_schedule);
q = rq->q;
+ depth = 0;
spin_lock(q->queue_lock);
}
rq->cmd_flags &= ~REQ_ON_PLUG;
__elv_add_request(q, rq, ELEVATOR_INSERT_FLUSH);
else
__elv_add_request(q, rq, ELEVATOR_INSERT_SORT_MERGE);
- }
- if (q) {
- __blk_run_queue(q, false);
- spin_unlock(q->queue_lock);
+ depth++;
}
- BUG_ON(!list_empty(&plug->list));
- local_irq_restore(flags);
-}
-
-static void __blk_finish_plug(struct task_struct *tsk, struct blk_plug *plug)
-{
- flush_plug_list(plug);
+ /*
+ * This drops the queue lock
+ */
+ if (q)
+ queue_unplugged(q, depth, from_schedule);
- if (plug == tsk->plug)
- tsk->plug = NULL;
+ local_irq_restore(flags);
}
void blk_finish_plug(struct blk_plug *plug)
{
- if (plug)
- __blk_finish_plug(current, plug);
-}
-EXPORT_SYMBOL(blk_finish_plug);
+ blk_flush_plug_list(plug, false);
-void __blk_flush_plug(struct task_struct *tsk, struct blk_plug *plug)
-{
- __blk_finish_plug(tsk, plug);
- tsk->plug = plug;
+ if (plug == current->plug)
+ current->plug = NULL;
}
-EXPORT_SYMBOL(__blk_flush_plug);
+EXPORT_SYMBOL(blk_finish_plug);
int __init blk_dev_init(void)
{
WARN_ON(irqs_disabled());
spin_lock_irq(q->queue_lock);
__elv_add_request(q, rq, where);
- __blk_run_queue(q, false);
+ __blk_run_queue(q);
/* the queue is stopped so it won't be plugged+unplugged */
if (rq->cmd_type == REQ_TYPE_PM_RESUME)
q->request_fn(q);
* request_fn may confuse the driver. Always use kblockd.
*/
if (queued)
- __blk_run_queue(q, true);
+ blk_run_queue_async(q);
}
/**
* the comment in flush_end_io().
*/
if (blk_flush_complete_seq(rq, REQ_FSEQ_DATA, error))
- __blk_run_queue(q, true);
+ blk_run_queue_async(q);
}
/**
if (rl->count[BLK_RW_SYNC] >= q->nr_requests) {
blk_set_queue_full(q, BLK_RW_SYNC);
- } else if (rl->count[BLK_RW_SYNC]+1 <= q->nr_requests) {
+ } else {
blk_clear_queue_full(q, BLK_RW_SYNC);
wake_up(&rl->wait[BLK_RW_SYNC]);
}
if (rl->count[BLK_RW_ASYNC] >= q->nr_requests) {
blk_set_queue_full(q, BLK_RW_ASYNC);
- } else if (rl->count[BLK_RW_ASYNC]+1 <= q->nr_requests) {
+ } else {
blk_clear_queue_full(q, BLK_RW_ASYNC);
wake_up(&rl->wait[BLK_RW_ASYNC]);
}
{
int ret;
struct device *dev = disk_to_dev(disk);
-
struct request_queue *q = disk->queue;
if (WARN_ON(!q))
return ret;
ret = kobject_add(&q->kobj, kobject_get(&dev->kobj), "%s", "queue");
- if (ret < 0)
+ if (ret < 0) {
+ blk_trace_remove_sysfs(dev);
return ret;
+ }
kobject_uevent(&q->kobj, KOBJ_ADD);
if (ret) {
kobject_uevent(&q->kobj, KOBJ_REMOVE);
kobject_del(&q->kobj);
- blk_trace_remove_sysfs(disk_to_dev(disk));
+ blk_trace_remove_sysfs(dev);
kobject_put(&dev->kobj);
return ret;
}
}
/*
- * Must always be called with the rcu_read_lock() held
+ * Call func for each cic attached to this ioc.
*/
static void
-__call_for_each_cic(struct io_context *ioc,
- void (*func)(struct io_context *, struct cfq_io_context *))
+call_for_each_cic(struct io_context *ioc,
+ void (*func)(struct io_context *, struct cfq_io_context *))
{
struct cfq_io_context *cic;
struct hlist_node *n;
+ rcu_read_lock();
+
hlist_for_each_entry_rcu(cic, n, &ioc->cic_list, cic_list)
func(ioc, cic);
-}
-/*
- * Call func for each cic attached to this ioc.
- */
-static void
-call_for_each_cic(struct io_context *ioc,
- void (*func)(struct io_context *, struct cfq_io_context *))
-{
- rcu_read_lock();
- __call_for_each_cic(ioc, func);
rcu_read_unlock();
}
* should be ok to iterate over the known list, we will see all cic's
* since no new ones are added.
*/
- __call_for_each_cic(ioc, cic_free_func);
+ call_for_each_cic(ioc, cic_free_func);
}
static void cfq_put_cooperator(struct cfq_queue *cfqq)
cfqd->busy_queues > 1) {
cfq_del_timer(cfqd, cfqq);
cfq_clear_cfqq_wait_request(cfqq);
- __blk_run_queue(cfqd->queue, false);
+ __blk_run_queue(cfqd->queue);
} else {
cfq_blkiocg_update_idle_time_stats(
&cfqq->cfqg->blkg);
* this new queue is RT and the current one is BE
*/
cfq_preempt_queue(cfqd, cfqq);
- __blk_run_queue(cfqd->queue, false);
+ __blk_run_queue(cfqd->queue);
}
}
struct request_queue *q = cfqd->queue;
spin_lock_irq(q->queue_lock);
- __blk_run_queue(cfqd->queue, false);
+ __blk_run_queue(cfqd->queue);
spin_unlock_irq(q->queue_lock);
}
*/
elv_drain_elevator(q);
while (q->rq.elvpriv) {
- __blk_run_queue(q, false);
+ __blk_run_queue(q);
spin_unlock_irq(q->queue_lock);
msleep(10);
spin_lock_irq(q->queue_lock);
q->boundary_rq = rq;
}
} else if (!(rq->cmd_flags & REQ_ELVPRIV) &&
- where == ELEVATOR_INSERT_SORT)
+ (where == ELEVATOR_INSERT_SORT ||
+ where == ELEVATOR_INSERT_SORT_MERGE))
where = ELEVATOR_INSERT_BACK;
switch (where) {
* with anything. There's no point in delaying queue
* processing.
*/
- __blk_run_queue(q, false);
+ __blk_run_queue(q);
break;
case ELEVATOR_INSERT_SORT_MERGE:
spin_unlock_irq(&ev->lock);
- /* tell userland about new events */
+ /*
+ * Tell userland about new events. Only the events listed in
+ * @disk->events are reported. Unlisted events are processed the
+ * same internally but never get reported to userland.
+ */
for (i = 0; i < ARRAY_SIZE(disk_uevents); i++)
- if (events & (1 << i))
+ if (events & disk->events & (1 << i))
envp[nr_events++] = disk_uevents[i];
if (nr_events)
if (ACPI_SUCCESS(status))
device->flags.lockable = 1;
+ /* Power resources cannot be power manageable. */
+ if (device->device_type == ACPI_BUS_TYPE_POWER)
+ return 0;
+
/* Presence of _PS0|_PR0 indicates 'power manageable' */
status = acpi_get_handle(device->handle, "_PS0", &temp);
if (ACPI_FAILURE(status))
#endif /* !CONFIG_SUSPEND */
-#ifdef CONFIG_HIBERNATION
+#ifdef CONFIG_HIBERNATE_CALLBACKS
static int amba_pm_freeze(struct device *dev)
{
return ret;
}
-#else /* !CONFIG_HIBERNATION */
+#else /* !CONFIG_HIBERNATE_CALLBACKS */
#define amba_pm_freeze NULL
#define amba_pm_thaw NULL
#define amba_pm_poweroff_noirq NULL
#define amba_pm_restore_noirq NULL
-#endif /* !CONFIG_HIBERNATION */
+#endif /* !CONFIG_HIBERNATE_CALLBACKS */
#ifdef CONFIG_PM
{
AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP |
AHCI_HFLAG_YES_NCQ),
- .flags = AHCI_FLAG_COMMON,
+ .flags = AHCI_FLAG_COMMON | ATA_FLAG_NO_DIPM,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
{ PCI_VDEVICE(INTEL, 0x1d06), board_ahci }, /* PBG RAID */
{ PCI_VDEVICE(INTEL, 0x2826), board_ahci }, /* PBG RAID */
{ PCI_VDEVICE(INTEL, 0x2323), board_ahci }, /* DH89xxCC AHCI */
+ { PCI_VDEVICE(INTEL, 0x1e02), board_ahci }, /* Panther Point AHCI */
+ { PCI_VDEVICE(INTEL, 0x1e03), board_ahci }, /* Panther Point AHCI */
+ { PCI_VDEVICE(INTEL, 0x1e04), board_ahci }, /* Panther Point RAID */
+ { PCI_VDEVICE(INTEL, 0x1e05), board_ahci }, /* Panther Point RAID */
+ { PCI_VDEVICE(INTEL, 0x1e06), board_ahci }, /* Panther Point RAID */
+ { PCI_VDEVICE(INTEL, 0x1e07), board_ahci }, /* Panther Point RAID */
/* JMicron 360/1/3/5/6, match class to avoid IDE function */
{ PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
EM_CTL_ALHD = (1 << 26), /* Activity LED */
EM_CTL_XMT = (1 << 25), /* Transmit Only */
EM_CTL_SMB = (1 << 24), /* Single Message Buffer */
+ EM_CTL_SGPIO = (1 << 19), /* SGPIO messages supported */
+ EM_CTL_SES = (1 << 18), /* SES-2 messages supported */
+ EM_CTL_SAFTE = (1 << 17), /* SAF-TE messages supported */
+ EM_CTL_LED = (1 << 16), /* LED messages supported */
/* em message type */
EM_MSG_TYPE_LED = (1 << 0), /* LED */
{ 0x8086, 0x1d00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
/* SATA Controller IDE (PBG) */
{ 0x8086, 0x1d08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
+ /* SATA Controller IDE (Panther Point) */
+ { 0x8086, 0x1e00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
+ /* SATA Controller IDE (Panther Point) */
+ { 0x8086, 0x1e01, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
+ /* SATA Controller IDE (Panther Point) */
+ { 0x8086, 0x1e08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
+ /* SATA Controller IDE (Panther Point) */
+ { 0x8086, 0x1e09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
{ } /* terminate list */
};
static ssize_t ahci_store_em_buffer(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size);
+static ssize_t ahci_show_em_supported(struct device *dev,
+ struct device_attribute *attr, char *buf);
static DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL);
static DEVICE_ATTR(ahci_host_cap2, S_IRUGO, ahci_show_host_cap2, NULL);
static DEVICE_ATTR(ahci_port_cmd, S_IRUGO, ahci_show_port_cmd, NULL);
static DEVICE_ATTR(em_buffer, S_IWUSR | S_IRUGO,
ahci_read_em_buffer, ahci_store_em_buffer);
+static DEVICE_ATTR(em_message_supported, S_IRUGO, ahci_show_em_supported, NULL);
struct device_attribute *ahci_shost_attrs[] = {
&dev_attr_link_power_management_policy,
&dev_attr_ahci_host_version,
&dev_attr_ahci_port_cmd,
&dev_attr_em_buffer,
+ &dev_attr_em_message_supported,
NULL
};
EXPORT_SYMBOL_GPL(ahci_shost_attrs);
return size;
}
+static ssize_t ahci_show_em_supported(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct ata_port *ap = ata_shost_to_port(shost);
+ struct ahci_host_priv *hpriv = ap->host->private_data;
+ void __iomem *mmio = hpriv->mmio;
+ u32 em_ctl;
+
+ em_ctl = readl(mmio + HOST_EM_CTL);
+
+ return sprintf(buf, "%s%s%s%s\n",
+ em_ctl & EM_CTL_LED ? "led " : "",
+ em_ctl & EM_CTL_SAFTE ? "saf-te " : "",
+ em_ctl & EM_CTL_SES ? "ses-2 " : "",
+ em_ctl & EM_CTL_SGPIO ? "sgpio " : "");
+}
+
/**
* ahci_save_initial_config - Save and fixup initial config values
* @dev: target AHCI device
{
void __iomem *port_mmio = ahci_port_base(ap);
u32 tmp;
+ u8 status;
+
+ status = readl(port_mmio + PORT_TFDATA) & 0xFF;
+
+ /*
+ * At end of section 10.1 of AHCI spec (rev 1.3), it states
+ * Software shall not set PxCMD.ST to 1 until it is determined
+ * that a functoinal device is present on the port as determined by
+ * PxTFD.STS.BSY=0, PxTFD.STS.DRQ=0 and PxSSTS.DET=3h
+ *
+ * Even though most AHCI host controllers work without this check,
+ * specific controller will fail under this condition
+ */
+ if (status & (ATA_BUSY | ATA_DRQ))
+ return;
+ else {
+ ahci_scr_read(&ap->link, SCR_STATUS, &tmp);
+
+ if ((tmp & 0xf) != 0x3)
+ return;
+ }
/* start DMA */
tmp = readl(port_mmio + PORT_CMD);
ahci_enable_fbs(ap);
pp->intr_mask |= PORT_IRQ_BAD_PMP;
- writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
+
+ /*
+ * We must not change the port interrupt mask register if the
+ * port is marked frozen, the value in pp->intr_mask will be
+ * restored later when the port is thawed.
+ *
+ * Note that during initialization, the port is marked as
+ * frozen since the irq handler is not yet registered.
+ */
+ if (!(ap->pflags & ATA_PFLAG_FROZEN))
+ writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
}
static void ahci_pmp_detach(struct ata_port *ap)
writel(cmd, port_mmio + PORT_CMD);
pp->intr_mask &= ~PORT_IRQ_BAD_PMP;
- writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
+
+ /* see comment above in ahci_pmp_attach() */
+ if (!(ap->pflags & ATA_PFLAG_FROZEN))
+ writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
}
int ahci_port_resume(struct ata_port *ap)
*/
{ "PIONEER DVD-RW DVRTD08", "1.00", ATA_HORKAGE_NOSETXFER },
{ "PIONEER DVD-RW DVR-212D", "1.28", ATA_HORKAGE_NOSETXFER },
+ { "PIONEER DVD-RW DVR-216D", "1.08", ATA_HORKAGE_NOSETXFER },
/* End Marker */
{ }
if (!ap)
return NULL;
- ap->pflags |= ATA_PFLAG_INITIALIZING;
+ ap->pflags |= ATA_PFLAG_INITIALIZING | ATA_PFLAG_FROZEN;
ap->lock = &host->lock;
ap->print_id = -1;
ap->host = host;
struct ata_eh_context *ehc = &link->eh_context;
struct ata_device *dev, *link_dev = NULL, *lpm_dev = NULL;
enum ata_lpm_policy old_policy = link->lpm_policy;
+ bool no_dipm = ap->flags & ATA_FLAG_NO_DIPM;
unsigned int hints = ATA_LPM_EMPTY | ATA_LPM_HIPM;
unsigned int err_mask;
int rc;
*/
ata_for_each_dev(dev, link, ENABLED) {
bool hipm = ata_id_has_hipm(dev->id);
- bool dipm = ata_id_has_dipm(dev->id);
+ bool dipm = ata_id_has_dipm(dev->id) && !no_dipm;
/* find the first enabled and LPM enabled devices */
if (!link_dev)
/* host config updated, enable DIPM if transitioning to MIN_POWER */
ata_for_each_dev(dev, link, ENABLED) {
- if (policy == ATA_LPM_MIN_POWER && ata_id_has_dipm(dev->id)) {
+ if (policy == ATA_LPM_MIN_POWER && !no_dipm &&
+ ata_id_has_dipm(dev->id)) {
err_mask = ata_dev_set_feature(dev,
SETFEATURES_SATA_ENABLE, SATA_DIPM);
if (err_mask && err_mask != AC_ERR_DEV) {
#define DRV_NAME "pata_at91"
-#define DRV_VERSION "0.1"
+#define DRV_VERSION "0.2"
#define CF_IDE_OFFSET 0x00c00000
#define CF_ALT_IDE_OFFSET 0x00e00000
#define CF_IDE_RES_SIZE 0x08
+#define NCS_RD_PULSE_LIMIT 0x3f /* maximal value for pulse bitfields */
struct at91_ide_info {
unsigned long mode;
void __iomem *alt_addr;
};
-static const struct ata_timing initial_timing =
- {XFER_PIO_0, 70, 290, 240, 600, 165, 150, 600, 0};
+static const struct ata_timing initial_timing = {
+ .mode = XFER_PIO_0,
+ .setup = 70,
+ .act8b = 290,
+ .rec8b = 240,
+ .cyc8b = 600,
+ .active = 165,
+ .recover = 150,
+ .dmack_hold = 0,
+ .cycle = 600,
+ .udma = 0
+};
static unsigned long calc_mck_cycles(unsigned long ns, unsigned long mck_hz)
{
/* (CS0, CS1, DIR, OE) <= (CFCE1, CFCE2, CFRNW, NCSX) timings */
ncs_read_setup = 1;
ncs_read_pulse = read_cycle - 2;
+ if (ncs_read_pulse > NCS_RD_PULSE_LIMIT) {
+ ncs_read_pulse = NCS_RD_PULSE_LIMIT;
+ dev_warn(dev, "ncs_read_pulse limited to maximal value %lu\n",
+ ncs_read_pulse);
+ }
/* Write timings same as read timings */
write_cycle = read_cycle;
of_device_node_put(&pa->pdev.dev);
kfree(pa->pdev.dev.platform_data);
+ kfree(pa->pdev.mfd_cell);
kfree(pa->pdev.resource);
kfree(pa);
}
#endif /* !CONFIG_SUSPEND */
-#ifdef CONFIG_HIBERNATION
+#ifdef CONFIG_HIBERNATE_CALLBACKS
static int platform_pm_freeze(struct device *dev)
{
return ret;
}
-#else /* !CONFIG_HIBERNATION */
+#else /* !CONFIG_HIBERNATE_CALLBACKS */
#define platform_pm_freeze NULL
#define platform_pm_thaw NULL
#define platform_pm_poweroff_noirq NULL
#define platform_pm_restore_noirq NULL
-#endif /* !CONFIG_HIBERNATION */
+#endif /* !CONFIG_HIBERNATE_CALLBACKS */
#ifdef CONFIG_PM_RUNTIME
dev->power.wakeup = NULL;
spin_lock_init(&dev->power.lock);
pm_runtime_init(dev);
+ INIT_LIST_HEAD(&dev->power.entry);
}
/**
}
break;
#endif /* CONFIG_SUSPEND */
-#ifdef CONFIG_HIBERNATION
+#ifdef CONFIG_HIBERNATE_CALLBACKS
case PM_EVENT_FREEZE:
case PM_EVENT_QUIESCE:
if (ops->freeze) {
suspend_report_result(ops->restore, error);
}
break;
-#endif /* CONFIG_HIBERNATION */
+#endif /* CONFIG_HIBERNATE_CALLBACKS */
default:
error = -EINVAL;
}
}
break;
#endif /* CONFIG_SUSPEND */
-#ifdef CONFIG_HIBERNATION
+#ifdef CONFIG_HIBERNATE_CALLBACKS
case PM_EVENT_FREEZE:
case PM_EVENT_QUIESCE:
if (ops->freeze_noirq) {
suspend_report_result(ops->restore_noirq, error);
}
break;
-#endif /* CONFIG_HIBERNATION */
+#endif /* CONFIG_HIBERNATE_CALLBACKS */
default:
error = -EINVAL;
}
if (!!dev->power.can_wakeup == !!capable)
return;
- if (device_is_registered(dev)) {
+ if (device_is_registered(dev) && !list_empty(&dev->power.entry)) {
if (capable) {
if (wakeup_sysfs_add(dev))
return;
return ret;
}
+EXPORT_SYMBOL_GPL(syscore_suspend);
/**
* syscore_resume - Execute all the registered system core resume callbacks.
"Interrupts enabled after %pF\n", ops->resume);
}
}
+EXPORT_SYMBOL_GPL(syscore_resume);
#endif /* CONFIG_PM_SLEEP */
/**
ops,
false,
GFP_NOIO, pages, bio);
- if (IS_ERR(req)) {
+ if (!req) {
up_read(&header->snap_rwsem);
- ret = PTR_ERR(req);
+ ret = -ENOMEM;
goto done_pages;
}
struct agp_memory *new;
unsigned long alloc_size = num_agp_pages*sizeof(struct page *);
+ if (INT_MAX/sizeof(struct page *) < num_agp_pages)
+ return NULL;
+
new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL);
if (new == NULL)
return NULL;
int scratch_pages;
struct agp_memory *new;
size_t i;
+ int cur_memory;
if (!bridge)
return NULL;
- if ((atomic_read(&bridge->current_memory_agp) + page_count) > bridge->max_memory_agp)
+ cur_memory = atomic_read(&bridge->current_memory_agp);
+ if ((cur_memory + page_count > bridge->max_memory_agp) ||
+ (cur_memory + page_count < page_count))
return NULL;
if (type >= AGP_USER_TYPES) {
return -EINVAL;
}
- /* AK: could wrap */
- if ((pg_start + mem->page_count) > num_entries)
+ if (((pg_start + mem->page_count) > num_entries) ||
+ ((pg_start + mem->page_count) < pg_start))
return -EINVAL;
j = pg_start;
{
size_t i;
struct agp_bridge_data *bridge;
- int mask_type;
+ int mask_type, num_entries;
bridge = mem->bridge;
if (!bridge)
if (type != mem->type)
return -EINVAL;
+ num_entries = agp_num_entries();
+ if (((pg_start + mem->page_count) > num_entries) ||
+ ((pg_start + mem->page_count) < pg_start))
+ return -EINVAL;
+
mask_type = bridge->driver->agp_type_to_mask_type(bridge, type);
if (mask_type != 0) {
/* The generic routines know nothing of memory types */
spin_lock_irq(&pdrvdata_lock);
list_del(&port->cons.list);
spin_unlock_irq(&pdrvdata_lock);
-#if 0
- /*
- * hvc_remove() not called as removing one hvc port
- * results in other hvc ports getting frozen.
- *
- * Once this is resolved in hvc, this functionality
- * will be enabled. Till that is done, the -EPIPE
- * return from get_chars() above will help
- * hvc_console.c to clean up on ports we remove here.
- */
hvc_remove(port->cons.hvc);
-#endif
}
/* Remove unused data this port might have received. */
* Then we take the most specific entry - with the following
* order of precedence: dev+con > dev only > con only.
*/
-static struct clk *clk_find(const char *dev_id, const char *con_id)
+static struct clk_lookup *clk_find(const char *dev_id, const char *con_id)
{
- struct clk_lookup *p;
- struct clk *clk = NULL;
+ struct clk_lookup *p, *cl = NULL;
int match, best = 0;
list_for_each_entry(p, &clocks, node) {
}
if (match > best) {
- clk = p->clk;
+ cl = p;
if (match != 3)
best = match;
else
break;
}
}
- return clk;
+ return cl;
}
struct clk *clk_get_sys(const char *dev_id, const char *con_id)
{
- struct clk *clk;
+ struct clk_lookup *cl;
mutex_lock(&clocks_mutex);
- clk = clk_find(dev_id, con_id);
- if (clk && !__clk_get(clk))
- clk = NULL;
+ cl = clk_find(dev_id, con_id);
+ if (cl && !__clk_get(cl->clk))
+ cl = NULL;
mutex_unlock(&clocks_mutex);
- return clk ? clk : ERR_PTR(-ENOENT);
+ return cl ? cl->clk : ERR_PTR(-ENOENT);
}
EXPORT_SYMBOL(clk_get_sys);
cbq->callback(msg, nsp);
kfree_skb(skb);
cn_queue_release_callback(cbq);
+ err = 0;
}
return err;
scrubval = scrubval & 0x001F;
- amd64_debug("pci-read, sdram scrub control value: %d\n", scrubval);
-
for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
if (scrubrates[i].scrubval == scrubval) {
retval = scrubrates[i].bandwidth;
/* On F10h and later ErrAddr is MC4_ADDR[47:1] */
static u64 get_error_address(struct mce *m)
{
+ struct cpuinfo_x86 *c = &boot_cpu_data;
+ u64 addr;
u8 start_bit = 1;
u8 end_bit = 47;
- if (boot_cpu_data.x86 == 0xf) {
+ if (c->x86 == 0xf) {
start_bit = 3;
end_bit = 39;
}
- return m->addr & GENMASK(start_bit, end_bit);
+ addr = m->addr & GENMASK(start_bit, end_bit);
+
+ /*
+ * Erratum 637 workaround
+ */
+ if (c->x86 == 0x15) {
+ struct amd64_pvt *pvt;
+ u64 cc6_base, tmp_addr;
+ u32 tmp;
+ u8 mce_nid, intlv_en;
+
+ if ((addr & GENMASK(24, 47)) >> 24 != 0x00fdf7)
+ return addr;
+
+ mce_nid = amd_get_nb_id(m->extcpu);
+ pvt = mcis[mce_nid]->pvt_info;
+
+ amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp);
+ intlv_en = tmp >> 21 & 0x7;
+
+ /* add [47:27] + 3 trailing bits */
+ cc6_base = (tmp & GENMASK(0, 20)) << 3;
+
+ /* reverse and add DramIntlvEn */
+ cc6_base |= intlv_en ^ 0x7;
+
+ /* pin at [47:24] */
+ cc6_base <<= 24;
+
+ if (!intlv_en)
+ return cc6_base | (addr & GENMASK(0, 23));
+
+ amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp);
+
+ /* faster log2 */
+ tmp_addr = (addr & GENMASK(12, 23)) << __fls(intlv_en + 1);
+
+ /* OR DramIntlvSel into bits [14:12] */
+ tmp_addr |= (tmp & GENMASK(21, 23)) >> 9;
+
+ /* add remaining [11:0] bits from original MC4_ADDR */
+ tmp_addr |= addr & GENMASK(0, 11);
+
+ return cc6_base | tmp_addr;
+ }
+
+ return addr;
}
static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
{
+ struct cpuinfo_x86 *c = &boot_cpu_data;
int off = range << 3;
amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off, &pvt->ranges[range].base.lo);
amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo);
- if (boot_cpu_data.x86 == 0xf)
+ if (c->x86 == 0xf)
return;
if (!dram_rw(pvt, range))
amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off, &pvt->ranges[range].base.hi);
amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi);
+
+ /* Factor in CC6 save area by reading dst node's limit reg */
+ if (c->x86 == 0x15) {
+ struct pci_dev *f1 = NULL;
+ u8 nid = dram_dst_node(pvt, range);
+ u32 llim;
+
+ f1 = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0x18 + nid, 1));
+ if (WARN_ON(!f1))
+ return;
+
+ amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim);
+
+ pvt->ranges[range].lim.lo &= GENMASK(0, 15);
+
+ /* {[39:27],111b} */
+ pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16;
+
+ pvt->ranges[range].lim.hi &= GENMASK(0, 7);
+
+ /* [47:40] */
+ pvt->ranges[range].lim.hi |= llim >> 13;
+
+ pci_dev_put(f1);
+ }
}
static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
return -EINVAL;
}
- if (intlv_en &&
- (intlv_sel != ((sys_addr >> 12) & intlv_en))) {
- amd64_warn("Botched intlv bits, en: 0x%x, sel: 0x%x\n",
- intlv_en, intlv_sel);
+ if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en)))
return -EINVAL;
- }
sys_addr = f1x_swap_interleaved_region(pvt, sys_addr);
#define DCT_CFG_SEL 0x10C
+#define DRAM_LOCAL_NODE_BASE 0x120
+#define DRAM_LOCAL_NODE_LIM 0x124
+
#define DRAM_BASE_HI 0x140
#define DRAM_LIMIT_HI 0x144
return -EINVAL;
new_bw = mci->set_sdram_scrub_rate(mci, bandwidth);
- if (new_bw >= 0) {
- edac_printk(KERN_DEBUG, EDAC_MC, "Scrub rate set to %d\n", new_bw);
- return count;
+ if (new_bw < 0) {
+ edac_printk(KERN_WARNING, EDAC_MC,
+ "Error setting scrub rate to: %lu\n", bandwidth);
+ return -EINVAL;
}
- edac_printk(KERN_DEBUG, EDAC_MC, "Error setting scrub rate to: %lu\n", bandwidth);
- return -EINVAL;
+ return count;
}
/*
return bandwidth;
}
- edac_printk(KERN_DEBUG, EDAC_MC, "Read scrub rate: %d\n", bandwidth);
return sprintf(data, "%d\n", bandwidth);
}
{
struct fw_ohci *ohci;
unsigned long flags;
- int ret = -EBUSY;
__be32 *next_config_rom;
dma_addr_t uninitialized_var(next_config_rom_bus);
spin_lock_irqsave(&ohci->lock, flags);
+ /*
+ * If there is not an already pending config_rom update,
+ * push our new allocation into the ohci->next_config_rom
+ * and then mark the local variable as null so that we
+ * won't deallocate the new buffer.
+ *
+ * OTOH, if there is a pending config_rom update, just
+ * use that buffer with the new config_rom data, and
+ * let this routine free the unused DMA allocation.
+ */
+
if (ohci->next_config_rom == NULL) {
ohci->next_config_rom = next_config_rom;
ohci->next_config_rom_bus = next_config_rom_bus;
+ next_config_rom = NULL;
+ }
- copy_config_rom(ohci->next_config_rom, config_rom, length);
+ copy_config_rom(ohci->next_config_rom, config_rom, length);
- ohci->next_header = config_rom[0];
- ohci->next_config_rom[0] = 0;
+ ohci->next_header = config_rom[0];
+ ohci->next_config_rom[0] = 0;
- reg_write(ohci, OHCI1394_ConfigROMmap,
- ohci->next_config_rom_bus);
- ret = 0;
- }
+ reg_write(ohci, OHCI1394_ConfigROMmap, ohci->next_config_rom_bus);
spin_unlock_irqrestore(&ohci->lock, flags);
+ /* If we didn't use the DMA allocation, delete it. */
+ if (next_config_rom != NULL)
+ dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
+ next_config_rom, next_config_rom_bus);
+
/*
* Now initiate a bus reset to have the changes take
* effect. We clean up the old config rom memory and DMA
* controller could need to access it before the bus reset
* takes effect.
*/
- if (ret == 0)
- fw_schedule_bus_reset(&ohci->card, true, true);
- else
- dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
- next_config_rom, next_config_rom_bus);
- return ret;
+ fw_schedule_bus_reset(&ohci->card, true, true);
+
+ return 0;
}
static void ohci_send_request(struct fw_card *card, struct fw_packet *packet)
depends on DRM
select FB
select FRAMEBUFFER_CONSOLE if !EXPERT
+ select FRAMEBUFFER_CONSOLE_DETECT_PRIMARY if FRAMEBUFFER_CONSOLE
help
FB and CRTC helpers for KMS drivers.
# i915 depends on ACPI_VIDEO when ACPI is enabled
# but for select to work, need to select ACPI_VIDEO's dependencies, ick
select BACKLIGHT_CLASS_DEVICE if ACPI
+ select VIDEO_OUTPUT_CONTROL if ACPI
select INPUT if ACPI
select ACPI_VIDEO if ACPI
select ACPI_BUTTON if ACPI
}
EXPORT_SYMBOL(drm_fb_helper_debug_leave);
+bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
+{
+ bool error = false;
+ int i, ret;
+ for (i = 0; i < fb_helper->crtc_count; i++) {
+ struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
+ ret = drm_crtc_helper_set_config(mode_set);
+ if (ret)
+ error = true;
+ }
+ return error;
+}
+EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode);
+
bool drm_fb_helper_force_kernel_mode(void)
{
- int i = 0;
bool ret, error = false;
struct drm_fb_helper *helper;
return false;
list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
- for (i = 0; i < helper->crtc_count; i++) {
- struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
- ret = drm_crtc_helper_set_config(mode_set);
- if (ret)
- error = true;
- }
+ if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+ continue;
+
+ ret = drm_fb_helper_restore_fbdev_mode(helper);
+ if (ret)
+ error = true;
}
return error;
}
void drm_vblank_off(struct drm_device *dev, int crtc)
{
+ struct drm_pending_vblank_event *e, *t;
+ struct timeval now;
unsigned long irqflags;
+ unsigned int seq;
spin_lock_irqsave(&dev->vbl_lock, irqflags);
vblank_disable_and_save(dev, crtc);
DRM_WAKEUP(&dev->vbl_queue[crtc]);
+
+ /* Send any queued vblank events, lest the natives grow disquiet */
+ seq = drm_vblank_count_and_time(dev, crtc, &now);
+ list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
+ if (e->pipe != crtc)
+ continue;
+ DRM_DEBUG("Sending premature vblank event on disable: \
+ wanted %d, current %d\n",
+ e->event.sequence, seq);
+
+ e->event.sequence = seq;
+ e->event.tv_sec = now.tv_sec;
+ e->event.tv_usec = now.tv_usec;
+ drm_vblank_put(dev, e->pipe);
+ list_move_tail(&e->base.link, &e->base.file_priv->event_list);
+ wake_up_interruptible(&e->base.file_priv->event_wait);
+ trace_drm_vblank_event_delivered(e->base.pid, e->pipe,
+ e->event.sequence);
+ }
+
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
}
EXPORT_SYMBOL(drm_vblank_off);
void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
{
list_replace(&old->node_list, &new->node_list);
- list_replace(&old->node_list, &new->hole_stack);
+ list_replace(&old->hole_stack, &new->hole_stack);
new->hole_follows = old->hole_follows;
new->mm = old->mm;
new->start = old->start;
entry->size);
total_used += entry->size;
if (entry->hole_follows) {
- hole_start = drm_mm_hole_node_start(&mm->head_node);
- hole_end = drm_mm_hole_node_end(&mm->head_node);
+ hole_start = drm_mm_hole_node_start(entry);
+ hole_end = drm_mm_hole_node_end(entry);
hole_size = hole_end - hole_start;
seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: free\n",
hole_start, hole_end, hole_size);
drm_i915_private_t *dev_priv = dev->dev_private;
if (!dev_priv || drm_core_check_feature(dev, DRIVER_MODESET)) {
- drm_fb_helper_restore();
+ intel_fb_restore_mode(dev);
vga_switcheroo_process_delayed_switch();
return;
}
int entries, tlb_miss;
crtc = intel_get_crtc_for_plane(dev, plane);
- if (crtc->fb == NULL || !crtc->enabled)
+ if (crtc->fb == NULL || !crtc->enabled) {
+ *cursor_wm = cursor->guard_size;
+ *plane_wm = display->guard_size;
return false;
+ }
htotal = crtc->mode.htotal;
hdisplay = crtc->mode.hdisplay;
I915_WRITE(DSPCNTR(plane), dspcntr);
POSTING_READ(DSPCNTR(plane));
- if (!HAS_PCH_SPLIT(dev))
- intel_enable_plane(dev_priv, plane, pipe);
ret = intel_pipe_set_base(crtc, x, y, old_fb);
intel_clock_t clock;
if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0)
- fp = FP0(pipe);
+ fp = I915_READ(FP0(pipe));
else
- fp = FP1(pipe);
+ fp = I915_READ(FP1(pipe));
clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT;
if (IS_PINEVIEW(dev)) {
return ret;
}
-static void intel_crtc_reset(struct drm_crtc *crtc)
-{
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-
- /* Reset flags back to the 'unknown' status so that they
- * will be correctly set on the initial modeset.
- */
- intel_crtc->dpms_mode = -1;
-}
-
-static struct drm_crtc_helper_funcs intel_helper_funcs = {
- .dpms = intel_crtc_dpms,
- .mode_fixup = intel_crtc_mode_fixup,
- .mode_set = intel_crtc_mode_set,
- .mode_set_base = intel_pipe_set_base,
- .mode_set_base_atomic = intel_pipe_set_base_atomic,
- .load_lut = intel_crtc_load_lut,
- .disable = intel_crtc_disable,
-};
-
-static const struct drm_crtc_funcs intel_crtc_funcs = {
- .reset = intel_crtc_reset,
- .cursor_set = intel_crtc_cursor_set,
- .cursor_move = intel_crtc_cursor_move,
- .gamma_set = intel_crtc_gamma_set,
- .set_config = drm_crtc_helper_set_config,
- .destroy = intel_crtc_destroy,
- .page_flip = intel_crtc_page_flip,
-};
-
static void intel_sanitize_modesetting(struct drm_device *dev,
int pipe, int plane)
{
intel_disable_pipe(dev_priv, pipe);
}
+static void intel_crtc_reset(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+ /* Reset flags back to the 'unknown' status so that they
+ * will be correctly set on the initial modeset.
+ */
+ intel_crtc->dpms_mode = -1;
+
+ /* We need to fix up any BIOS configuration that conflicts with
+ * our expectations.
+ */
+ intel_sanitize_modesetting(dev, intel_crtc->pipe, intel_crtc->plane);
+}
+
+static struct drm_crtc_helper_funcs intel_helper_funcs = {
+ .dpms = intel_crtc_dpms,
+ .mode_fixup = intel_crtc_mode_fixup,
+ .mode_set = intel_crtc_mode_set,
+ .mode_set_base = intel_pipe_set_base,
+ .mode_set_base_atomic = intel_pipe_set_base_atomic,
+ .load_lut = intel_crtc_load_lut,
+ .disable = intel_crtc_disable,
+};
+
+static const struct drm_crtc_funcs intel_crtc_funcs = {
+ .reset = intel_crtc_reset,
+ .cursor_set = intel_crtc_cursor_set,
+ .cursor_move = intel_crtc_cursor_move,
+ .gamma_set = intel_crtc_gamma_set,
+ .set_config = drm_crtc_helper_set_config,
+ .destroy = intel_crtc_destroy,
+ .page_flip = intel_crtc_page_flip,
+};
+
static void intel_crtc_init(struct drm_device *dev, int pipe)
{
drm_i915_private_t *dev_priv = dev->dev_private;
setup_timer(&intel_crtc->idle_timer, intel_crtc_idle_timer,
(unsigned long)intel_crtc);
-
- intel_sanitize_modesetting(dev, intel_crtc->pipe, intel_crtc->plane);
}
int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
return ERR_PTR(-ENOENT);
intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
- if (!intel_fb)
+ if (!intel_fb) {
+ drm_gem_object_unreference_unlocked(&obj->base);
return ERR_PTR(-ENOMEM);
+ }
ret = intel_framebuffer_init(dev, intel_fb, mode_cmd, obj);
if (ret) {
if (!HAS_PCH_CPT(dev) &&
I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) {
- struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.base.crtc);
+ struct drm_crtc *crtc = intel_dp->base.base.crtc;
+
/* Hardware workaround: leaving our transcoder select
* set to transcoder B while it's off will prevent the
* corresponding HDMI output on transcoder A.
/* Changes to enable or select take place the vblank
* after being written.
*/
- intel_wait_for_vblank(dev, intel_crtc->pipe);
+ if (crtc == NULL) {
+ /* We can arrive here never having been attached
+ * to a CRTC, for instance, due to inheriting
+ * random state from the BIOS.
+ *
+ * If the pipe is not running, play safe and
+ * wait for the clocks to stabilise before
+ * continuing.
+ */
+ POSTING_READ(intel_dp->output_reg);
+ msleep(50);
+ } else
+ intel_wait_for_vblank(dev, to_intel_crtc(crtc)->pipe);
}
I915_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN);
struct drm_file *file_priv);
extern void intel_fb_output_poll_changed(struct drm_device *dev);
+extern void intel_fb_restore_mode(struct drm_device *dev);
#endif /* __INTEL_DRV_H__ */
drm_i915_private_t *dev_priv = dev->dev_private;
drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper);
}
+
+void intel_fb_restore_mode(struct drm_device *dev)
+{
+ int ret;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper);
+ if (ret)
+ DRM_DEBUG("failed to restore crtc mode\n");
+}
struct drm_device *dev = dev_priv->dev;
struct drm_connector *connector = dev_priv->int_lvds_connector;
+ if (dev->switch_power_state != DRM_SWITCH_POWER_ON)
+ return NOTIFY_OK;
+
/*
* check and update the status of LVDS connector after receiving
* the LID nofication event.
(video_levels->blank << TV_BLANK_LEVEL_SHIFT)));
{
int pipeconf_reg = PIPECONF(pipe);
- int dspcntr_reg = DSPCNTR(pipe);
+ int dspcntr_reg = DSPCNTR(intel_crtc->plane);
int pipeconf = I915_READ(pipeconf_reg);
int dspcntr = I915_READ(dspcntr_reg);
- int dspbase_reg = DSPADDR(pipe);
+ int dspbase_reg = DSPADDR(intel_crtc->plane);
int xpos = 0x0, ypos = 0x0;
unsigned int xsize, ysize;
/* Pipe must be off here */
if (type < 0)
return connector_status_disconnected;
+ intel_tv->type = type;
intel_tv_find_better_format(connector);
+
return connector_status_connected;
}
*
* More recent chipsets favour HDMI rather than integrated S-Video.
*/
- connector->polled =
- DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT;
drm_connector_init(dev, connector, &intel_tv_connector_funcs,
DRM_MODE_CONNECTOR_SVIDEO);
int (*handler)(struct nvbios *, uint16_t, struct init_exec *);
};
-static int parse_init_table(struct nvbios *, unsigned int, struct init_exec *);
+static int parse_init_table(struct nvbios *, uint16_t, struct init_exec *);
#define MACRO_INDEX_SIZE 2
#define MACRO_SIZE 8
return 3;
}
+static int
+init_jump(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+ /*
+ * INIT_JUMP opcode: 0x5C ('\')
+ *
+ * offset (8 bit): opcode
+ * offset + 1 (16 bit): offset (in bios)
+ *
+ * Continue execution of init table from 'offset'
+ */
+
+ uint16_t jmp_offset = ROM16(bios->data[offset + 1]);
+
+ if (!iexec->execute)
+ return 3;
+
+ BIOSLOG(bios, "0x%04X: Jump to 0x%04X\n", offset, jmp_offset);
+ return jmp_offset - offset;
+}
+
static int
init_i2c_if(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
{
{ "INIT_ZM_REG_SEQUENCE" , 0x58, init_zm_reg_sequence },
/* INIT_INDIRECT_REG (0x5A, 7, 0, 0) removed due to no example of use */
{ "INIT_SUB_DIRECT" , 0x5B, init_sub_direct },
+ { "INIT_JUMP" , 0x5C, init_jump },
{ "INIT_I2C_IF" , 0x5E, init_i2c_if },
{ "INIT_COPY_NV_REG" , 0x5F, init_copy_nv_reg },
{ "INIT_ZM_INDEX_IO" , 0x62, init_zm_index_io },
#define MAX_TABLE_OPS 1000
static int
-parse_init_table(struct nvbios *bios, unsigned int offset,
- struct init_exec *iexec)
+parse_init_table(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
{
/*
* Parses all commands in an init table.
}
}
+ /* XFX GT-240X-YA
+ *
+ * So many things wrong here, replace the entire encoder table..
+ */
+ if (nv_match_device(dev, 0x0ca3, 0x1682, 0x3003)) {
+ if (idx == 0) {
+ *conn = 0x02001300; /* VGA, connector 1 */
+ *conf = 0x00000028;
+ } else
+ if (idx == 1) {
+ *conn = 0x01010312; /* DVI, connector 0 */
+ *conf = 0x00020030;
+ } else
+ if (idx == 2) {
+ *conn = 0x01010310; /* VGA, connector 0 */
+ *conf = 0x00000028;
+ } else
+ if (idx == 3) {
+ *conn = 0x02022362; /* HDMI, connector 2 */
+ *conf = 0x00020010;
+ } else {
+ *conn = 0x0000000e; /* EOL */
+ *conf = 0x00000000;
+ }
+ }
+
return true;
}
return ret;
/* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier object */
- ret = nouveau_notifier_alloc(chan, NvNotify0, 32, 0xfd0, 0x1000,
+ ret = nouveau_notifier_alloc(chan, NvNotify0, 32, 0xfe0, 0x1000,
&chan->m2mf_ntfy);
if (ret)
return ret;
/* For PFIFO and PGRAPH. */
spinlock_t context_switch_lock;
+ /* VM/PRAMIN flush, legacy PRAMIN aperture */
+ spinlock_t vm_lock;
+
/* RAMIN configuration, RAMFC, RAMHT and RAMRO offsets */
struct nouveau_ramht *ramht;
struct nouveau_gpuobj *ramfc;
extern int nv50_graph_unload_context(struct drm_device *);
extern int nv50_grctx_init(struct nouveau_grctx *);
extern void nv50_graph_tlb_flush(struct drm_device *dev);
-extern void nv86_graph_tlb_flush(struct drm_device *dev);
+extern void nv84_graph_tlb_flush(struct drm_device *dev);
extern struct nouveau_enum nv50_data_error_names[];
/* nvc0_graph.c */
OUT_RING (chan, 0);
}
- nouveau_bo_wr32(chan->notifier_bo, chan->m2mf_ntfy + 3, 0xffffffff);
+ nouveau_bo_wr32(chan->notifier_bo, chan->m2mf_ntfy/4 + 3, 0xffffffff);
FIRE_RING(chan);
mutex_unlock(&chan->mutex);
ret = -EBUSY;
for (i = 0; i < 100000; i++) {
- if (!nouveau_bo_rd32(chan->notifier_bo, chan->m2mf_ntfy + 3)) {
+ if (!nouveau_bo_rd32(chan->notifier_bo, chan->m2mf_ntfy/4 + 3)) {
ret = 0;
break;
}
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- nouveau_bo_ref(NULL, &dev_priv->vga_ram);
-
ttm_bo_device_release(&dev_priv->ttm.bdev);
nouveau_ttm_global_release(dev_priv);
dma_bits = 40;
} else
if (drm_pci_device_is_pcie(dev) &&
- dev_priv->chipset != 0x40 &&
+ dev_priv->chipset > 0x40 &&
dev_priv->chipset != 0x45) {
if (pci_dma_supported(dev->pdev, DMA_BIT_MASK(39)))
dma_bits = 39;
u8 tRC; /* Byte 9 */
u8 tUNK_10, tUNK_11, tUNK_12, tUNK_13, tUNK_14;
u8 tUNK_18, tUNK_19, tUNK_20, tUNK_21;
+ u8 magic_number = 0; /* Yeah... sorry*/
u8 *mem = NULL, *entry;
int i, recordlen, entries;
if (!memtimings->timing)
return;
+ /* Get "some number" from the timing reg for NV_40
+ * Used in calculations later */
+ if(dev_priv->card_type == NV_40) {
+ magic_number = (nv_rd32(dev,0x100228) & 0x0f000000) >> 24;
+ }
+
entry = mem + mem[1];
for (i = 0; i < entries; i++, entry += recordlen) {
struct nouveau_pm_memtiming *timing = &pm->memtimings.timing[i];
/* XXX: I don't trust the -1's and +1's... they must come
* from somewhere! */
- timing->reg_100224 = ((tUNK_0 + tUNK_19 + 1) << 24 |
+ timing->reg_100224 = (tUNK_0 + tUNK_19 + 1 + magic_number) << 24 |
tUNK_18 << 16 |
- (tUNK_1 + tUNK_19 + 1) << 8 |
- (tUNK_2 - 1));
+ (tUNK_1 + tUNK_19 + 1 + magic_number) << 8;
+ if(dev_priv->chipset == 0xa8) {
+ timing->reg_100224 |= (tUNK_2 - 1);
+ } else {
+ timing->reg_100224 |= (tUNK_2 + 2 - magic_number);
+ }
timing->reg_100228 = (tUNK_12 << 16 | tUNK_11 << 8 | tUNK_10);
- if(recordlen > 19) {
- timing->reg_100228 += (tUNK_19 - 1) << 24;
- }/* I cannot back-up this else-statement right now
- else {
- timing->reg_100228 += tUNK_12 << 24;
- }*/
-
- /* XXX: reg_10022c */
- timing->reg_10022c = tUNK_2 - 1;
-
- timing->reg_100230 = (tUNK_20 << 24 | tUNK_21 << 16 |
- tUNK_13 << 8 | tUNK_13);
-
- /* XXX: +6? */
- timing->reg_100234 = (tRAS << 24 | (tUNK_19 + 6) << 8 | tRC);
- timing->reg_100234 += max(tUNK_10,tUNK_11) << 16;
-
- /* XXX; reg_100238, reg_10023c
- * reg: 0x00??????
- * reg_10023c:
- * 0 for pre-NV50 cards
- * 0x????0202 for NV50+ cards (empirical evidence) */
- if(dev_priv->card_type >= NV_50) {
+ if(dev_priv->chipset >= 0xa3 && dev_priv->chipset < 0xaa) {
+ timing->reg_100228 |= (tUNK_19 - 1) << 24;
+ }
+
+ if(dev_priv->card_type == NV_40) {
+ /* NV40: don't know what the rest of the regs are..
+ * And don't need to know either */
+ timing->reg_100228 |= 0x20200000 | magic_number << 24;
+ } else if(dev_priv->card_type >= NV_50) {
+ /* XXX: reg_10022c */
+ timing->reg_10022c = tUNK_2 - 1;
+
+ timing->reg_100230 = (tUNK_20 << 24 | tUNK_21 << 16 |
+ tUNK_13 << 8 | tUNK_13);
+
+ timing->reg_100234 = (tRAS << 24 | tRC);
+ timing->reg_100234 += max(tUNK_10,tUNK_11) << 16;
+
+ if(dev_priv->chipset < 0xa3) {
+ timing->reg_100234 |= (tUNK_2 + 2) << 8;
+ } else {
+ /* XXX: +6? */
+ timing->reg_100234 |= (tUNK_19 + 6) << 8;
+ }
+
+ /* XXX; reg_100238, reg_10023c
+ * reg_100238: 0x00??????
+ * reg_10023c: 0x!!??0202 for NV50+ cards (empirical evidence) */
timing->reg_10023c = 0x202;
+ if(dev_priv->chipset < 0xa3) {
+ timing->reg_10023c |= 0x4000000 | (tUNK_2 - 1) << 16;
+ } else {
+ /* currently unknown
+ * 10023c seen as 06xxxxxx, 0bxxxxxx or 0fxxxxxx */
+ }
}
NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", i,
timing->reg_100238, timing->reg_10023c);
}
- memtimings->nr_timing = entries;
+ memtimings->nr_timing = entries;
memtimings->supported = true;
}
{
struct drm_device *dev = chan->dev;
struct nouveau_bo *ntfy = NULL;
- uint32_t flags;
+ uint32_t flags, ttmpl;
int ret;
- if (nouveau_vram_notify)
+ if (nouveau_vram_notify) {
flags = NOUVEAU_GEM_DOMAIN_VRAM;
- else
+ ttmpl = TTM_PL_FLAG_VRAM;
+ } else {
flags = NOUVEAU_GEM_DOMAIN_GART;
+ ttmpl = TTM_PL_FLAG_TT;
+ }
ret = nouveau_gem_new(dev, NULL, PAGE_SIZE, 0, flags, 0, 0, &ntfy);
if (ret)
return ret;
- ret = nouveau_bo_pin(ntfy, flags);
+ ret = nouveau_bo_pin(ntfy, ttmpl);
if (ret)
goto out_err;
{
struct drm_nouveau_private *dev_priv = gpuobj->dev->dev_private;
struct drm_device *dev = gpuobj->dev;
+ unsigned long flags;
if (gpuobj->pinst == ~0 || !dev_priv->ramin_available) {
u64 ptr = gpuobj->vinst + offset;
u32 base = ptr >> 16;
u32 val;
- spin_lock(&dev_priv->ramin_lock);
+ spin_lock_irqsave(&dev_priv->vm_lock, flags);
if (dev_priv->ramin_base != base) {
dev_priv->ramin_base = base;
nv_wr32(dev, 0x001700, dev_priv->ramin_base);
}
val = nv_rd32(dev, 0x700000 + (ptr & 0xffff));
- spin_unlock(&dev_priv->ramin_lock);
+ spin_unlock_irqrestore(&dev_priv->vm_lock, flags);
return val;
}
{
struct drm_nouveau_private *dev_priv = gpuobj->dev->dev_private;
struct drm_device *dev = gpuobj->dev;
+ unsigned long flags;
if (gpuobj->pinst == ~0 || !dev_priv->ramin_available) {
u64 ptr = gpuobj->vinst + offset;
u32 base = ptr >> 16;
- spin_lock(&dev_priv->ramin_lock);
+ spin_lock_irqsave(&dev_priv->vm_lock, flags);
if (dev_priv->ramin_base != base) {
dev_priv->ramin_base = base;
nv_wr32(dev, 0x001700, dev_priv->ramin_base);
}
nv_wr32(dev, 0x700000 + (ptr & 0xffff), val);
- spin_unlock(&dev_priv->ramin_lock);
+ spin_unlock_irqrestore(&dev_priv->vm_lock, flags);
return;
}
case 0x13:
case 0x15:
perflvl->fanspeed = entry[55];
- perflvl->voltage = entry[56];
+ perflvl->voltage = (recordlen > 56) ? entry[56] : 0;
perflvl->core = ROM32(entry[1]) * 10;
perflvl->memory = ROM32(entry[5]) * 20;
break;
be->func->clear(be);
return -EFAULT;
}
+ nvbe->ttm_alloced[nvbe->nr_pages] = false;
}
nvbe->nr_pages++;
u32 aper_size, align;
int ret;
- if (dev_priv->card_type >= NV_50 || drm_pci_device_is_pcie(dev))
+ if (dev_priv->card_type >= NV_40 && drm_pci_device_is_pcie(dev))
aper_size = 512 * 1024 * 1024;
else
aper_size = 64 * 1024 * 1024;
dev_priv->gart_info.func = &nv50_sgdma_backend;
} else
if (drm_pci_device_is_pcie(dev) &&
- dev_priv->chipset != 0x40 && dev_priv->chipset != 0x45) {
+ dev_priv->chipset > 0x40 && dev_priv->chipset != 0x45) {
if (nv44_graph_class(dev)) {
dev_priv->gart_info.func = &nv44_sgdma_backend;
align = 512 * 1024;
engine->graph.destroy_context = nv50_graph_destroy_context;
engine->graph.load_context = nv50_graph_load_context;
engine->graph.unload_context = nv50_graph_unload_context;
- if (dev_priv->chipset != 0x86)
+ if (dev_priv->chipset == 0x50 ||
+ dev_priv->chipset == 0xac)
engine->graph.tlb_flush = nv50_graph_tlb_flush;
- else {
- /* from what i can see nvidia do this on every
- * pre-NVA3 board except NVAC, but, we've only
- * ever seen problems on NV86
- */
- engine->graph.tlb_flush = nv86_graph_tlb_flush;
- }
+ else
+ engine->graph.tlb_flush = nv84_graph_tlb_flush;
engine->fifo.channels = 128;
engine->fifo.init = nv50_fifo_init;
engine->fifo.takedown = nv50_fifo_takedown;
spin_lock_init(&dev_priv->channels.lock);
spin_lock_init(&dev_priv->tile.lock);
spin_lock_init(&dev_priv->context_switch_lock);
+ spin_lock_init(&dev_priv->vm_lock);
/* Make the CRTCs and I2C buses accessible */
ret = engine->display.early_init(dev);
engine->mc.takedown(dev);
engine->display.late_takedown(dev);
+ if (dev_priv->vga_ram) {
+ nouveau_bo_unpin(dev_priv->vga_ram);
+ nouveau_bo_ref(NULL, &dev_priv->vga_ram);
+ }
+
mutex_lock(&dev->struct_mutex);
ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM);
ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT);
int head = nv_encoder->restore.head;
if (nv_encoder->dcb->type == OUTPUT_LVDS) {
- struct drm_display_mode *native_mode = nouveau_encoder_connector_get(nv_encoder)->native_mode;
- if (native_mode)
- call_lvds_script(dev, nv_encoder->dcb, head, LVDS_PANEL_ON,
- native_mode->clock);
- else
- NV_ERROR(dev, "Not restoring LVDS without native mode\n");
+ struct nouveau_connector *connector =
+ nouveau_encoder_connector_get(nv_encoder);
+
+ if (connector && connector->native_mode)
+ call_lvds_script(dev, nv_encoder->dcb, head,
+ LVDS_PANEL_ON,
+ connector->native_mode->clock);
} else if (nv_encoder->dcb->type == OUTPUT_TMDS) {
int clock = nouveau_hw_pllvals_to_clk
start = ptimer->read(dev);
do {
- nv_wr32(dev, 0x61002c, 0x370);
- nv_wr32(dev, 0x000140, 1);
-
if (nv_ro32(disp->ntfy, 0x000))
return 0;
} while (ptimer->read(dev) - start < 2000000000ULL);
nv_mask(dev, 0x610028, 0x00000000, 0x00010001 << id);
evo->dma.max = (4096/4) - 2;
+ evo->dma.max &= ~7;
evo->dma.put = 0;
evo->dma.cur = evo->dma.put;
evo->dma.free = evo->dma.max - evo->dma.cur;
}
void
-nv86_graph_tlb_flush(struct drm_device *dev)
+nv84_graph_tlb_flush(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
nv50_instmem_flush(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ unsigned long flags;
- spin_lock(&dev_priv->ramin_lock);
+ spin_lock_irqsave(&dev_priv->vm_lock, flags);
nv_wr32(dev, 0x00330c, 0x00000001);
if (!nv_wait(dev, 0x00330c, 0x00000002, 0x00000000))
NV_ERROR(dev, "PRAMIN flush timeout\n");
- spin_unlock(&dev_priv->ramin_lock);
+ spin_unlock_irqrestore(&dev_priv->vm_lock, flags);
}
void
nv84_instmem_flush(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ unsigned long flags;
- spin_lock(&dev_priv->ramin_lock);
+ spin_lock_irqsave(&dev_priv->vm_lock, flags);
nv_wr32(dev, 0x070000, 0x00000001);
if (!nv_wait(dev, 0x070000, 0x00000002, 0x00000000))
NV_ERROR(dev, "PRAMIN flush timeout\n");
- spin_unlock(&dev_priv->ramin_lock);
+ spin_unlock_irqrestore(&dev_priv->vm_lock, flags);
}
nv50_vm_flush_engine(struct drm_device *dev, int engine)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ unsigned long flags;
- spin_lock(&dev_priv->ramin_lock);
+ spin_lock_irqsave(&dev_priv->vm_lock, flags);
nv_wr32(dev, 0x100c80, (engine << 16) | 1);
if (!nv_wait(dev, 0x100c80, 0x00000001, 0x00000000))
NV_ERROR(dev, "vm flush timeout: engine %d\n", engine);
- spin_unlock(&dev_priv->ramin_lock);
+ spin_unlock_irqrestore(&dev_priv->vm_lock, flags);
}
struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem;
struct drm_device *dev = vm->dev;
struct nouveau_vm_pgd *vpgd;
- u32 r100c80, engine;
+ unsigned long flags;
+ u32 engine = (dev_priv->chan_vm == vm) ? 1 : 5;
pinstmem->flush(vm->dev);
- if (vm == dev_priv->chan_vm)
- engine = 1;
- else
- engine = 5;
-
+ spin_lock_irqsave(&dev_priv->vm_lock, flags);
list_for_each_entry(vpgd, &vm->pgd_list, head) {
- r100c80 = nv_rd32(dev, 0x100c80);
+ /* looks like maybe a "free flush slots" counter, the
+ * faster you write to 0x100cbc to more it decreases
+ */
+ if (!nv_wait_ne(dev, 0x100c80, 0x00ff0000, 0x00000000)) {
+ NV_ERROR(dev, "vm timeout 0: 0x%08x %d\n",
+ nv_rd32(dev, 0x100c80), engine);
+ }
nv_wr32(dev, 0x100cb8, vpgd->obj->vinst >> 8);
nv_wr32(dev, 0x100cbc, 0x80000000 | engine);
- if (!nv_wait(dev, 0x100c80, 0xffffffff, r100c80))
- NV_ERROR(dev, "vm flush timeout eng %d\n", engine);
+ /* wait for flush to be queued? */
+ if (!nv_wait(dev, 0x100c80, 0x00008000, 0x00008000)) {
+ NV_ERROR(dev, "vm timeout 1: 0x%08x %d\n",
+ nv_rd32(dev, 0x100c80), engine);
+ }
}
+ spin_unlock_irqrestore(&dev_priv->vm_lock, flags);
}
#include "atom.h"
#include "atom-names.h"
#include "atom-bits.h"
+#include "radeon.h"
#define ATOM_COND_ABOVE 0
#define ATOM_COND_ABOVEOREQUAL 1
static uint32_t atom_iio_execute(struct atom_context *ctx, int base,
uint32_t index, uint32_t data)
{
+ struct radeon_device *rdev = ctx->card->dev->dev_private;
uint32_t temp = 0xCDCDCDCD;
+
while (1)
switch (CU8(base)) {
case ATOM_IIO_NOP:
base += 3;
break;
case ATOM_IIO_WRITE:
- (void)ctx->card->ioreg_read(ctx->card, CU16(base + 1));
+ if (rdev->family == CHIP_RV515)
+ (void)ctx->card->ioreg_read(ctx->card, CU16(base + 1));
ctx->card->ioreg_write(ctx->card, CU16(base + 1), temp);
base += 3;
break;
case ATOM_IIO_MOVE_INDEX:
temp &=
~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
- CU8(base + 2));
+ CU8(base + 3));
temp |=
((index >> CU8(base + 2)) &
(0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base +
case ATOM_IIO_MOVE_DATA:
temp &=
~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
- CU8(base + 2));
+ CU8(base + 3));
temp |=
((data >> CU8(base + 2)) &
(0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base +
case ATOM_IIO_MOVE_ATTR:
temp &=
~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
- CU8(base + 2));
+ CU8(base + 3));
temp |=
((ctx->
io_attr >> CU8(base + 2)) & (0xFFFFFFFF >> (32 -
pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
else
pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
+
+ if (rdev->family < CHIP_RV770)
+ pll->flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP;
} else {
pll->flags |= RADEON_PLL_LEGACY;
if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
if (ss_enabled) {
if (ss->refdiv) {
- pll->flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP;
pll->flags |= RADEON_PLL_USE_REF_DIV;
pll->reference_div = ss->refdiv;
if (ASIC_IS_AVIVO(rdev))
struct radeon_power_state *ps = &rdev->pm.power_state[req_ps_idx];
struct radeon_voltage *voltage = &ps->clock_info[req_cm_idx].voltage;
- if ((voltage->type == VOLTAGE_SW) && voltage->voltage) {
- if (voltage->voltage != rdev->pm.current_vddc) {
- radeon_atom_set_voltage(rdev, voltage->voltage);
+ if (voltage->type == VOLTAGE_SW) {
+ if (voltage->voltage && (voltage->voltage != rdev->pm.current_vddc)) {
+ radeon_atom_set_voltage(rdev, voltage->voltage, SET_VOLTAGE_TYPE_ASIC_VDDC);
rdev->pm.current_vddc = voltage->voltage;
- DRM_DEBUG("Setting: v: %d\n", voltage->voltage);
+ DRM_DEBUG("Setting: vddc: %d\n", voltage->voltage);
+ }
+ if (voltage->vddci && (voltage->vddci != rdev->pm.current_vddci)) {
+ radeon_atom_set_voltage(rdev, voltage->vddci, SET_VOLTAGE_TYPE_ASIC_VDDCI);
+ rdev->pm.current_vddci = voltage->vddci;
+ DRM_DEBUG("Setting: vddci: %d\n", voltage->vddci);
}
}
}
struct drm_display_mode *mode,
struct drm_display_mode *other_mode)
{
- u32 tmp = 0;
+ u32 tmp;
/*
* Line Buffer Setup
* There are 3 line buffers, each one shared by 2 display controllers.
* first display controller
* 0 - first half of lb (3840 * 2)
* 1 - first 3/4 of lb (5760 * 2)
- * 2 - whole lb (7680 * 2)
+ * 2 - whole lb (7680 * 2), other crtc must be disabled
* 3 - first 1/4 of lb (1920 * 2)
* second display controller
* 4 - second half of lb (3840 * 2)
* 5 - second 3/4 of lb (5760 * 2)
- * 6 - whole lb (7680 * 2)
+ * 6 - whole lb (7680 * 2), other crtc must be disabled
* 7 - last 1/4 of lb (1920 * 2)
*/
- if (mode && other_mode) {
- if (mode->hdisplay > other_mode->hdisplay) {
- if (mode->hdisplay > 2560)
- tmp = 1; /* 3/4 */
- else
- tmp = 0; /* 1/2 */
- } else if (other_mode->hdisplay > mode->hdisplay) {
- if (other_mode->hdisplay > 2560)
- tmp = 3; /* 1/4 */
- else
- tmp = 0; /* 1/2 */
- } else
+ /* this can get tricky if we have two large displays on a paired group
+ * of crtcs. Ideally for multiple large displays we'd assign them to
+ * non-linked crtcs for maximum line buffer allocation.
+ */
+ if (radeon_crtc->base.enabled && mode) {
+ if (other_mode)
tmp = 0; /* 1/2 */
- } else if (mode)
- tmp = 2; /* whole */
- else if (other_mode)
- tmp = 3; /* 1/4 */
+ else
+ tmp = 2; /* whole */
+ } else
+ tmp = 0;
/* second controller of the pair uses second half of the lb */
if (radeon_crtc->crtc_id % 2)
tmp += 4;
WREG32(DC_LB_MEMORY_SPLIT + radeon_crtc->crtc_offset, tmp);
- switch (tmp) {
- case 0:
- case 4:
- default:
- if (ASIC_IS_DCE5(rdev))
- return 4096 * 2;
- else
- return 3840 * 2;
- case 1:
- case 5:
- if (ASIC_IS_DCE5(rdev))
- return 6144 * 2;
- else
- return 5760 * 2;
- case 2:
- case 6:
- if (ASIC_IS_DCE5(rdev))
- return 8192 * 2;
- else
- return 7680 * 2;
- case 3:
- case 7:
- if (ASIC_IS_DCE5(rdev))
- return 2048 * 2;
- else
- return 1920 * 2;
+ if (radeon_crtc->base.enabled && mode) {
+ switch (tmp) {
+ case 0:
+ case 4:
+ default:
+ if (ASIC_IS_DCE5(rdev))
+ return 4096 * 2;
+ else
+ return 3840 * 2;
+ case 1:
+ case 5:
+ if (ASIC_IS_DCE5(rdev))
+ return 6144 * 2;
+ else
+ return 5760 * 2;
+ case 2:
+ case 6:
+ if (ASIC_IS_DCE5(rdev))
+ return 8192 * 2;
+ else
+ return 7680 * 2;
+ case 3:
+ case 7:
+ if (ASIC_IS_DCE5(rdev))
+ return 2048 * 2;
+ else
+ return 1920 * 2;
+ }
}
+
+ /* controller not enabled, so no lb used */
+ return 0;
}
static u32 evergreen_get_number_of_dram_channels(struct radeon_device *rdev)
SYSTEM_ACCESS_MODE_NOT_IN_SYS |
SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU |
EFFECTIVE_L1_TLB_SIZE(5) | EFFECTIVE_L1_QUEUE_SIZE(5);
- WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp);
- WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp);
- WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp);
+ if (rdev->flags & RADEON_IS_IGP) {
+ WREG32(FUS_MC_VM_MD_L1_TLB0_CNTL, tmp);
+ WREG32(FUS_MC_VM_MD_L1_TLB1_CNTL, tmp);
+ WREG32(FUS_MC_VM_MD_L1_TLB2_CNTL, tmp);
+ } else {
+ WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp);
+ WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp);
+ WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp);
+ }
WREG32(MC_VM_MB_L1_TLB0_CNTL, tmp);
WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp);
WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp);
u32 wptr, tmp;
if (rdev->wb.enabled)
- wptr = rdev->wb.wb[R600_WB_IH_WPTR_OFFSET/4];
+ wptr = le32_to_cpu(rdev->wb.wb[R600_WB_IH_WPTR_OFFSET/4]);
else
wptr = RREG32(IH_RB_WPTR);
rdev->asic->copy = NULL;
dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r);
}
- /* XXX: ontario has problems blitting to gart at the moment */
- if (rdev->family == CHIP_PALM) {
- rdev->asic->copy = NULL;
- radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size);
- }
/* allocate wb buffer */
r = radeon_wb_init(rdev);
{
int r;
- r = radeon_dummy_page_init(rdev);
- if (r)
- return r;
/* This don't do much */
r = radeon_gem_init(rdev);
if (r)
radeon_atombios_fini(rdev);
kfree(rdev->bios);
rdev->bios = NULL;
- radeon_dummy_page_fini(rdev);
}
static void evergreen_pcie_gen2_enable(struct radeon_device *rdev)
#define MC_VM_MD_L1_TLB0_CNTL 0x2654
#define MC_VM_MD_L1_TLB1_CNTL 0x2658
#define MC_VM_MD_L1_TLB2_CNTL 0x265C
+
+#define FUS_MC_VM_MD_L1_TLB0_CNTL 0x265C
+#define FUS_MC_VM_MD_L1_TLB1_CNTL 0x2660
+#define FUS_MC_VM_MD_L1_TLB2_CNTL 0x2664
+
#define MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x203C
#define MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2038
#define MC_VM_SYSTEM_APERTURE_LOW_ADDR 0x2034
if ((voltage->type == VOLTAGE_SW) && voltage->voltage) {
if (voltage->voltage != rdev->pm.current_vddc) {
- radeon_atom_set_voltage(rdev, voltage->voltage);
+ radeon_atom_set_voltage(rdev, voltage->voltage, SET_VOLTAGE_TYPE_ASIC_VDDC);
rdev->pm.current_vddc = voltage->voltage;
DRM_DEBUG_DRIVER("Setting: v: %d\n", voltage->voltage);
}
{
int r;
- r = radeon_dummy_page_init(rdev);
- if (r)
- return r;
if (r600_debugfs_mc_info_init(rdev)) {
DRM_ERROR("Failed to register debugfs file for mc !\n");
}
radeon_atombios_fini(rdev);
kfree(rdev->bios);
rdev->bios = NULL;
- radeon_dummy_page_fini(rdev);
}
u32 wptr, tmp;
if (rdev->wb.enabled)
- wptr = rdev->wb.wb[R600_WB_IH_WPTR_OFFSET/4];
+ wptr = le32_to_cpu(rdev->wb.wb[R600_WB_IH_WPTR_OFFSET/4]);
else
wptr = RREG32(IH_RB_WPTR);
void radeon_pm_resume(struct radeon_device *rdev);
void radeon_combios_get_power_modes(struct radeon_device *rdev);
void radeon_atombios_get_power_modes(struct radeon_device *rdev);
-void radeon_atom_set_voltage(struct radeon_device *rdev, u16 level);
+void radeon_atom_set_voltage(struct radeon_device *rdev, u16 voltage_level, u8 voltage_type);
void rs690_pm_info(struct radeon_device *rdev);
extern int rv6xx_get_temp(struct radeon_device *rdev);
extern int rv770_get_temp(struct radeon_device *rdev);
u8 vddci_id; /* index into vddci voltage table */
bool vddci_enabled;
/* r6xx+ sw */
- u32 voltage;
+ u16 voltage;
+ /* evergreen+ vddci */
+ u16 vddci;
};
/* clock mode flags */
int default_power_state_index;
u32 current_sclk;
u32 current_mclk;
- u32 current_vddc;
+ u16 current_vddc;
+ u16 current_vddci;
u32 default_sclk;
u32 default_mclk;
- u32 default_vddc;
+ u16 default_vddc;
+ u16 default_vddci;
struct radeon_i2c_chan *i2c_bus;
/* selected pm method */
enum radeon_pm_method pm_method;
rdev->mc_rreg = &rs600_mc_rreg;
rdev->mc_wreg = &rs600_mc_wreg;
}
- if ((rdev->family >= CHIP_R600) && (rdev->family <= CHIP_HEMLOCK)) {
+ if (rdev->family >= CHIP_R600) {
rdev->pciep_rreg = &r600_pciep_rreg;
rdev->pciep_wreg = &r600_pciep_wreg;
}
}
}
- /* Acer laptop (Acer TravelMate 5730G) has an HDMI port
+ /* Acer laptop (Acer TravelMate 5730/5730G) has an HDMI port
* on the laptop and a DVI port on the docking station and
* both share the same encoder, hpd pin, and ddc line.
* So while the bios table is technically correct,
* with different crtcs which isn't possible on the hardware
* side and leaves no crtcs for LVDS or VGA.
*/
- if ((dev->pdev->device == 0x95c4) &&
+ if (((dev->pdev->device == 0x95c4) || (dev->pdev->device == 0x9591)) &&
(dev->pdev->subsystem_vendor == 0x1025) &&
(dev->pdev->subsystem_device == 0x013c)) {
if ((*connector_type == DRM_MODE_CONNECTOR_DVII) &&
memcpy((u8 *)edid, (u8 *)&fake_edid_record->ucFakeEDIDString[0],
fake_edid_record->ucFakeEDIDLength);
- if (drm_edid_is_valid(edid))
+ if (drm_edid_is_valid(edid)) {
rdev->mode_info.bios_hardcoded_edid = edid;
- else
+ rdev->mode_info.bios_hardcoded_edid_size = edid_size;
+ } else
kfree(edid);
}
}
}
}
-static u16 radeon_atombios_get_default_vddc(struct radeon_device *rdev)
+static void radeon_atombios_get_default_voltages(struct radeon_device *rdev,
+ u16 *vddc, u16 *vddci)
{
struct radeon_mode_info *mode_info = &rdev->mode_info;
int index = GetIndexIntoMasterTable(DATA, FirmwareInfo);
u8 frev, crev;
u16 data_offset;
union firmware_info *firmware_info;
- u16 vddc = 0;
+
+ *vddc = 0;
+ *vddci = 0;
if (atom_parse_data_header(mode_info->atom_context, index, NULL,
&frev, &crev, &data_offset)) {
firmware_info =
(union firmware_info *)(mode_info->atom_context->bios +
data_offset);
- vddc = le16_to_cpu(firmware_info->info_14.usBootUpVDDCVoltage);
+ *vddc = le16_to_cpu(firmware_info->info_14.usBootUpVDDCVoltage);
+ if ((frev == 2) && (crev >= 2))
+ *vddci = le16_to_cpu(firmware_info->info_22.usBootUpVDDCIVoltage);
}
-
- return vddc;
}
static void radeon_atombios_parse_pplib_non_clock_info(struct radeon_device *rdev,
int j;
u32 misc = le32_to_cpu(non_clock_info->ulCapsAndSettings);
u32 misc2 = le16_to_cpu(non_clock_info->usClassification);
- u16 vddc = radeon_atombios_get_default_vddc(rdev);
+ u16 vddc, vddci;
+
+ radeon_atombios_get_default_voltages(rdev, &vddc, &vddci);
rdev->pm.power_state[state_index].misc = misc;
rdev->pm.power_state[state_index].misc2 = misc2;
rdev->pm.default_sclk = rdev->pm.power_state[state_index].clock_info[0].sclk;
rdev->pm.default_mclk = rdev->pm.power_state[state_index].clock_info[0].mclk;
rdev->pm.default_vddc = rdev->pm.power_state[state_index].clock_info[0].voltage.voltage;
+ rdev->pm.default_vddci = rdev->pm.power_state[state_index].clock_info[0].voltage.vddci;
} else {
/* patch the table values with the default slck/mclk from firmware info */
for (j = 0; j < mode_index; j++) {
VOLTAGE_SW;
rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage =
le16_to_cpu(clock_info->evergreen.usVDDC);
+ rdev->pm.power_state[state_index].clock_info[mode_index].voltage.vddci =
+ le16_to_cpu(clock_info->evergreen.usVDDCI);
} else {
sclk = le16_to_cpu(clock_info->r600.usEngineClockLow);
sclk |= clock_info->r600.ucEngineClockHigh << 16;
struct _SET_VOLTAGE_PARAMETERS_V2 v2;
};
-void radeon_atom_set_voltage(struct radeon_device *rdev, u16 level)
+void radeon_atom_set_voltage(struct radeon_device *rdev, u16 voltage_level, u8 voltage_type)
{
union set_voltage args;
int index = GetIndexIntoMasterTable(COMMAND, SetVoltage);
- u8 frev, crev, volt_index = level;
+ u8 frev, crev, volt_index = voltage_level;
if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
return;
switch (crev) {
case 1:
- args.v1.ucVoltageType = SET_VOLTAGE_TYPE_ASIC_VDDC;
+ args.v1.ucVoltageType = voltage_type;
args.v1.ucVoltageMode = SET_ASIC_VOLTAGE_MODE_ALL_SOURCE;
args.v1.ucVoltageIndex = volt_index;
break;
case 2:
- args.v2.ucVoltageType = SET_VOLTAGE_TYPE_ASIC_VDDC;
+ args.v2.ucVoltageType = voltage_type;
args.v2.ucVoltageMode = SET_ASIC_VOLTAGE_MODE_SET_VOLTAGE;
- args.v2.usVoltageLevel = cpu_to_le16(level);
+ args.v2.usVoltageLevel = cpu_to_le16(voltage_level);
break;
default:
DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
#define ATPX_VERSION 0
#define ATPX_GPU_PWR 2
#define ATPX_MUX_SELECT 3
+#define ATPX_I2C_MUX_SELECT 4
+#define ATPX_SWITCH_START 5
+#define ATPX_SWITCH_END 6
#define ATPX_INTEGRATED 0
#define ATPX_DISCRETE 1
return radeon_atpx_execute(handle, ATPX_MUX_SELECT, mux_id);
}
+static int radeon_atpx_switch_i2c_mux(acpi_handle handle, int mux_id)
+{
+ return radeon_atpx_execute(handle, ATPX_I2C_MUX_SELECT, mux_id);
+}
+
+static int radeon_atpx_switch_start(acpi_handle handle, int gpu_id)
+{
+ return radeon_atpx_execute(handle, ATPX_SWITCH_START, gpu_id);
+}
+
+static int radeon_atpx_switch_end(acpi_handle handle, int gpu_id)
+{
+ return radeon_atpx_execute(handle, ATPX_SWITCH_END, gpu_id);
+}
static int radeon_atpx_switchto(enum vga_switcheroo_client_id id)
{
+ int gpu_id;
+
if (id == VGA_SWITCHEROO_IGD)
- radeon_atpx_switch_mux(radeon_atpx_priv.atpx_handle, 0);
+ gpu_id = ATPX_INTEGRATED;
else
- radeon_atpx_switch_mux(radeon_atpx_priv.atpx_handle, 1);
+ gpu_id = ATPX_DISCRETE;
+
+ radeon_atpx_switch_start(radeon_atpx_priv.atpx_handle, gpu_id);
+ radeon_atpx_switch_mux(radeon_atpx_priv.atpx_handle, gpu_id);
+ radeon_atpx_switch_i2c_mux(radeon_atpx_priv.atpx_handle, gpu_id);
+ radeon_atpx_switch_end(radeon_atpx_priv.atpx_handle, gpu_id);
+
return 0;
}
if (router->ddc_valid || router->cd_valid) {
radeon_connector->router_bus = radeon_i2c_lookup(rdev, &router->i2c_info);
if (!radeon_connector->router_bus)
- goto failed;
+ DRM_ERROR("Failed to assign router i2c bus! Check dmesg for i2c errors.\n");
}
switch (connector_type) {
case DRM_MODE_CONNECTOR_VGA:
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("VGA: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
radeon_connector->dac_load_detect = true;
drm_connector_attach_property(&radeon_connector->base,
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("DVIA: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
radeon_connector->dac_load_detect = true;
drm_connector_attach_property(&radeon_connector->base,
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("DVI: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
subpixel_order = SubPixelHorizontalRGB;
drm_connector_attach_property(&radeon_connector->base,
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("HDMI: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.coherent_mode_property,
else
radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "DP-auxch");
if (!radeon_dig_connector->dp_i2c_bus)
- goto failed;
+ DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n");
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
subpixel_order = SubPixelHorizontalRGB;
drm_connector_attach_property(&radeon_connector->base,
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("LVDS: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
drm_connector_attach_property(&radeon_connector->base,
dev->mode_config.scaling_mode_property,
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("VGA: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
radeon_connector->dac_load_detect = true;
drm_connector_attach_property(&radeon_connector->base,
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("DVIA: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
radeon_connector->dac_load_detect = true;
drm_connector_attach_property(&radeon_connector->base,
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("DVI: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
if (connector_type == DRM_MODE_CONNECTOR_DVII) {
radeon_connector->dac_load_detect = true;
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("LVDS: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
drm_connector_attach_property(&radeon_connector->base,
dev->mode_config.scaling_mode_property,
radeon_legacy_backlight_init(radeon_encoder, connector);
}
}
- return;
-
-failed:
- drm_connector_cleanup(connector);
- kfree(connector);
}
return -EINVAL;
}
- radeon_crtc->cursor_width = width;
- radeon_crtc->cursor_height = height;
-
obj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
if (!obj) {
DRM_ERROR("Cannot find cursor object %x for crtc %d\n", handle, radeon_crtc->crtc_id);
if (ret)
goto fail;
+ radeon_crtc->cursor_width = width;
+ radeon_crtc->cursor_height = height;
+
radeon_lock_cursor(crtc, true);
/* XXX only 27 bit offset for legacy cursor */
radeon_set_cursor(crtc, obj, gpu_addr);
scratch_index = R600_WB_EVENT_OFFSET + rdev->fence_drv.scratch_reg - rdev->scratch.reg_base;
else
scratch_index = RADEON_WB_SCRATCH_OFFSET + rdev->fence_drv.scratch_reg - rdev->scratch.reg_base;
- seq = rdev->wb.wb[scratch_index/4];
+ seq = le32_to_cpu(rdev->wb.wb[scratch_index/4]);
} else
seq = RREG32(rdev->fence_drv.scratch_reg);
if (seq != rdev->fence_drv.last_seq) {
rdev->gart.pages = NULL;
rdev->gart.pages_addr = NULL;
rdev->gart.ttm_alloced = NULL;
+
+ radeon_dummy_page_fini(rdev);
}
*val = in_buf[0];
DRM_DEBUG("val = 0x%02x\n", *val);
} else {
- DRM_ERROR("i2c 0x%02x 0x%02x read failed\n",
+ DRM_DEBUG("i2c 0x%02x 0x%02x read failed\n",
addr, *val);
}
}
out_buf[1] = val;
if (i2c_transfer(&i2c_bus->adapter, &msg, 1) != 1)
- DRM_ERROR("i2c 0x%02x 0x%02x write failed\n",
+ DRM_DEBUG("i2c 0x%02x 0x%02x write failed\n",
addr, val);
}
if (!radeon_connector->router.ddc_valid)
return;
+ if (!radeon_connector->router_bus)
+ return;
+
radeon_i2c_get_byte(radeon_connector->router_bus,
radeon_connector->router.i2c_addr,
0x3, &val);
if (!radeon_connector->router.cd_valid)
return;
+ if (!radeon_connector->router_bus)
+ return;
+
radeon_i2c_get_byte(radeon_connector->router_bus,
radeon_connector->router.i2c_addr,
0x3, &val);
return -EINVAL;
}
break;
+ case RADEON_INFO_NUM_TILE_PIPES:
+ if (rdev->family >= CHIP_CAYMAN)
+ value = rdev->config.cayman.max_tile_pipes;
+ else if (rdev->family >= CHIP_CEDAR)
+ value = rdev->config.evergreen.max_tile_pipes;
+ else if (rdev->family >= CHIP_RV770)
+ value = rdev->config.rv770.max_tile_pipes;
+ else if (rdev->family >= CHIP_R600)
+ value = rdev->config.r600.max_tile_pipes;
+ else {
+ return -EINVAL;
+ }
+ break;
+ case RADEON_INFO_FUSION_GART_WORKING:
+ value = 1;
+ break;
default:
DRM_DEBUG_KMS("Invalid request %d\n", info->request);
return -EINVAL;
.disable = radeon_legacy_encoder_disable,
};
-#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
#define MAX_RADEON_LEVEL 0xFF
#include "drmP.h"
#include "radeon.h"
#include "avivod.h"
+#include "atom.h"
#ifdef CONFIG_ACPI
#include <linux/acpi.h>
#endif
/* set up the default clocks if the MC ucode is loaded */
if (ASIC_IS_DCE5(rdev) && rdev->mc_fw) {
if (rdev->pm.default_vddc)
- radeon_atom_set_voltage(rdev, rdev->pm.default_vddc);
+ radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
+ SET_VOLTAGE_TYPE_ASIC_VDDC);
+ if (rdev->pm.default_vddci)
+ radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
+ SET_VOLTAGE_TYPE_ASIC_VDDCI);
if (rdev->pm.default_sclk)
radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
if (rdev->pm.default_mclk)
rdev->pm.current_sclk = rdev->pm.default_sclk;
rdev->pm.current_mclk = rdev->pm.default_mclk;
rdev->pm.current_vddc = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.voltage;
+ rdev->pm.current_vddci = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.vddci;
if (rdev->pm.pm_method == PM_METHOD_DYNPM
&& rdev->pm.dynpm_state == DYNPM_STATE_SUSPENDED) {
rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE;
/* set up the default clocks if the MC ucode is loaded */
if (ASIC_IS_DCE5(rdev) && rdev->mc_fw) {
if (rdev->pm.default_vddc)
- radeon_atom_set_voltage(rdev, rdev->pm.default_vddc);
+ radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
+ SET_VOLTAGE_TYPE_ASIC_VDDC);
if (rdev->pm.default_sclk)
radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
if (rdev->pm.default_mclk)
void radeon_ring_free_size(struct radeon_device *rdev)
{
if (rdev->wb.enabled)
- rdev->cp.rptr = rdev->wb.wb[RADEON_WB_CP_RPTR_OFFSET/4];
+ rdev->cp.rptr = le32_to_cpu(rdev->wb.wb[RADEON_WB_CP_RPTR_OFFSET/4]);
else {
if (rdev->family >= CHIP_R600)
rdev->cp.rptr = RREG32(R600_CP_RB_RPTR);
0x00028D0C DB_RENDER_CONTROL
0x00028D10 DB_RENDER_OVERRIDE
0x0002880C DB_SHADER_CONTROL
+0x00028D28 DB_SRESULTS_COMPARE_STATE0
0x00028D2C DB_SRESULTS_COMPARE_STATE1
0x00028430 DB_STENCILREFMASK
0x00028434 DB_STENCILREFMASK_BF
udelay(voltage->delay);
}
} else if (voltage->type == VOLTAGE_VDDC)
- radeon_atom_set_voltage(rdev, voltage->vddc_id);
+ radeon_atom_set_voltage(rdev, voltage->vddc_id, SET_VOLTAGE_TYPE_ASIC_VDDC);
dyn_pwrmgt_sclk_length = RREG32_PLL(DYN_PWRMGT_SCLK_LENGTH);
dyn_pwrmgt_sclk_length &= ~REDUCED_POWER_SCLK_HILEN(0xf);
if ((voltage->type == VOLTAGE_SW) && voltage->voltage) {
if (voltage->voltage != rdev->pm.current_vddc) {
- radeon_atom_set_voltage(rdev, voltage->voltage);
+ radeon_atom_set_voltage(rdev, voltage->voltage, SET_VOLTAGE_TYPE_ASIC_VDDC);
rdev->pm.current_vddc = voltage->voltage;
DRM_DEBUG("Setting: v: %d\n", voltage->voltage);
}
{
int r;
- r = radeon_dummy_page_init(rdev);
- if (r)
- return r;
/* This don't do much */
r = radeon_gem_init(rdev);
if (r)
radeon_atombios_fini(rdev);
kfree(rdev->bios);
rdev->bios = NULL;
- radeon_dummy_page_fini(rdev);
}
static void rv770_pcie_gen2_enable(struct radeon_device *rdev)
gfp_flags |= GFP_HIGHUSER;
for (r = 0; r < count; ++r) {
- if ((flags & TTM_PAGE_FLAG_DMA32) && dma_address) {
- void *addr;
- addr = dma_alloc_coherent(NULL, PAGE_SIZE,
- &dma_address[r],
- gfp_flags);
- if (addr == NULL)
- return -ENOMEM;
- p = virt_to_page(addr);
- } else
- p = alloc_page(gfp_flags);
+ p = alloc_page(gfp_flags);
if (!p) {
printk(KERN_ERR TTM_PFX
"Unable to allocate page.");
return -ENOMEM;
}
+
list_add(&p->lru, pages);
}
return 0;
unsigned long irq_flags;
struct ttm_page_pool *pool = ttm_get_pool(flags, cstate);
struct page *p, *tmp;
- unsigned r;
if (pool == NULL) {
/* No pool for this memory type so free the pages */
- r = page_count-1;
list_for_each_entry_safe(p, tmp, pages, lru) {
- if ((flags & TTM_PAGE_FLAG_DMA32) && dma_address) {
- void *addr = page_address(p);
- WARN_ON(!addr || !dma_address[r]);
- if (addr)
- dma_free_coherent(NULL, PAGE_SIZE,
- addr,
- dma_address[r]);
- dma_address[r] = 0;
- } else
- __free_page(p);
- r--;
+ __free_page(p);
}
/* Make the pages list empty */
INIT_LIST_HEAD(pages);
# Poulsbo stub depends on ACPI_VIDEO when ACPI is enabled
# but for select to work, need to select ACPI_VIDEO's dependencies, ick
select BACKLIGHT_CLASS_DEVICE if ACPI
+ select VIDEO_OUTPUT_CONTROL if ACPI
select INPUT if ACPI
select ACPI_VIDEO if ACPI
select THERMAL if ACPI
help
If you say yes here you get support for Analog Devices ADM1021
and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A,
- Genesys Logic GL523SM, National Semiconductor LM84, TI THMC10,
- and the XEON processor built-in sensor.
+ Genesys Logic GL523SM, National Semiconductor LM84 and TI THMC10.
This driver can also be built as a module. If so, the module
will be called adm1021.
depends on I2C
help
If you say yes here you get support for National Semiconductor LM90,
- LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, Maxim
- MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659,
- MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, and Winbond/Nuvoton
- W83L771W/G/AWG/ASG sensor chips.
+ LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A,
+ Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659,
+ MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008,
+ and Winbond/Nuvoton W83L771W/G/AWG/ASG sensor chips.
This driver can also be built as a module. If so, the module
will be called lm90.
&sensor_dev_attr_pwm1_auto_pwm_minctl.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_pwm_minctl.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_pwm_minctl.dev_attr.attr,
+ NULL
};
static const struct attribute_group lm85_group_minctl = {
&sensor_dev_attr_temp1_auto_temp_off.dev_attr.attr,
&sensor_dev_attr_temp2_auto_temp_off.dev_attr.attr,
&sensor_dev_attr_temp3_auto_temp_off.dev_attr.attr,
+ NULL
};
static const struct attribute_group lm85_group_temp_off = {
if (data->type != emc6d103s) {
err = sysfs_create_group(&client->dev.kobj, &lm85_group_minctl);
if (err)
- goto err_kfree;
+ goto err_remove_files;
err = sysfs_create_group(&client->dev.kobj,
&lm85_group_temp_off);
if (err)
- goto err_kfree;
+ goto err_remove_files;
}
/* The ADT7463/68 have an optional VRM 10 mode where pin 21 is used
* chips, but support three temperature sensors instead of two. MAX6695
* and MAX6696 only differ in the pinout so they can be treated identically.
*
- * This driver also supports the ADT7461 chip from Analog Devices.
- * It's supported in both compatibility and extended mode. It is mostly
- * compatible with LM90 except for a data format difference for the
- * temperature value registers.
+ * This driver also supports ADT7461 and ADT7461A from Analog Devices as well as
+ * NCT1008 from ON Semiconductor. The chips are supported in both compatibility
+ * and extended mode. They are mostly compatible with LM90 except for a data
+ * format difference for the temperature value registers.
*
* Since the LM90 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and
* Addresses to scan
* Address is fully defined internally and cannot be changed except for
* MAX6659, MAX6680 and MAX6681.
- * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6649, MAX6657,
- * MAX6658 and W83L771 have address 0x4c.
- * ADM1032-2, ADT7461-2, LM89-1, LM99-1 and MAX6646 have address 0x4d.
+ * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, ADT7461A, MAX6649,
+ * MAX6657, MAX6658, NCT1008 and W83L771 have address 0x4c.
+ * ADM1032-2, ADT7461-2, ADT7461A-2, LM89-1, LM99-1, MAX6646, and NCT1008D
+ * have address 0x4d.
* MAX6647 has address 0x4e.
* MAX6659 can have address 0x4c, 0x4d or 0x4e.
* MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b,
static const struct i2c_device_id lm90_id[] = {
{ "adm1032", adm1032 },
{ "adt7461", adt7461 },
+ { "adt7461a", adt7461 },
{ "lm90", lm90 },
{ "lm86", lm86 },
{ "lm89", lm86 },
{ "max6681", max6680 },
{ "max6695", max6696 },
{ "max6696", max6696 },
+ { "nct1008", adt7461 },
{ "w83l771", w83l771 },
{ }
};
&& (reg_config1 & 0x1B) == 0x00
&& reg_convrate <= 0x0A) {
name = "adt7461";
+ } else
+ if (chip_id == 0x57 /* ADT7461A, NCT1008 */
+ && (reg_config1 & 0x1B) == 0x00
+ && reg_convrate <= 0x0A) {
+ name = "adt7461a";
}
} else
if (man_id == 0x4D) { /* Maxim */
* A single status register covers multiple attributes,
* so we keep them all together.
*/
- u8 status_bits;
u8 status[PB_NUM_STATUS_REG];
u8 currpage;
static int __devinit twl4030_madc_hwmon_probe(struct platform_device *pdev)
{
int ret;
- int status;
struct device *hwmon;
ret = sysfs_create_group(&pdev->dev.kobj, &twl4030_madc_group);
hwmon = hwmon_device_register(&pdev->dev);
if (IS_ERR(hwmon)) {
dev_err(&pdev->dev, "hwmon_device_register failed.\n");
- status = PTR_ERR(hwmon);
+ ret = PTR_ERR(hwmon);
goto err_reg;
}
* Sanity check for the adapter hardware - check the reaction of
* the bus lines only if it seems to be idle.
*/
-static int test_bus(struct i2c_algo_bit_data *adap, char *name)
+static int test_bus(struct i2c_adapter *i2c_adap)
{
- int scl, sda;
+ struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
+ const char *name = i2c_adap->name;
+ int scl, sda, ret;
+
+ if (adap->pre_xfer) {
+ ret = adap->pre_xfer(i2c_adap);
+ if (ret < 0)
+ return -ENODEV;
+ }
if (adap->getscl == NULL)
pr_info("%s: Testing SDA only, SCL is not readable\n", name);
"while pulling SCL high!\n", name);
goto bailout;
}
+
+ if (adap->post_xfer)
+ adap->post_xfer(i2c_adap);
+
pr_info("%s: Test OK\n", name);
return 0;
bailout:
sdahi(adap);
sclhi(adap);
+
+ if (adap->post_xfer)
+ adap->post_xfer(i2c_adap);
+
return -ENODEV;
}
int ret;
if (bit_test) {
- ret = test_bus(bit_adap, adap->name);
+ ret = test_bus(adap);
if (ret < 0)
return -ENODEV;
}
SMBHSTSTS_BUS_ERR | SMBHSTSTS_DEV_ERR | \
SMBHSTSTS_INTR)
+/* Older devices have their ID defined in <linux/pci_ids.h> */
+#define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22
+#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22
/* Patsburg also has three 'Integrated Device Function' SMBus controllers */
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0 0x1d70
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1 0x1d71
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2 0x1d72
+#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330
+#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
struct i801_priv {
struct i2c_adapter adapter;
/* ------------------------------------------------------------------------ *
* i2c-parport.c I2C bus over parallel port *
* ------------------------------------------------------------------------ *
- Copyright (C) 2003-2010 Jean Delvare <khali@linux-fr.org>
+ Copyright (C) 2003-2011 Jean Delvare <khali@linux-fr.org>
Based on older i2c-philips-par.c driver
Copyright (C) 1995-2000 Simon G. Vogl
#include <linux/i2c-algo-bit.h>
#include <linux/i2c-smbus.h>
#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
#include "i2c-parport.h"
/* ----- Device list ------------------------------------------------------ */
struct i2c_algo_bit_data algo_data;
struct i2c_smbus_alert_setup alert_data;
struct i2c_client *ara;
- struct i2c_par *next;
+ struct list_head node;
};
-static struct i2c_par *adapter_list;
+static LIST_HEAD(adapter_list);
+static DEFINE_MUTEX(adapter_list_lock);
/* ----- Low-level parallel port access ----------------------------------- */
}
/* Add the new adapter to the list */
- adapter->next = adapter_list;
- adapter_list = adapter;
+ mutex_lock(&adapter_list_lock);
+ list_add_tail(&adapter->node, &adapter_list);
+ mutex_unlock(&adapter_list_lock);
return;
ERROR1:
static void i2c_parport_detach (struct parport *port)
{
- struct i2c_par *adapter, *prev;
+ struct i2c_par *adapter, *_n;
/* Walk the list */
- for (prev = NULL, adapter = adapter_list; adapter;
- prev = adapter, adapter = adapter->next) {
+ mutex_lock(&adapter_list_lock);
+ list_for_each_entry_safe(adapter, _n, &adapter_list, node) {
if (adapter->pdev->port == port) {
if (adapter->ara) {
parport_disable_irq(port);
parport_release(adapter->pdev);
parport_unregister_device(adapter->pdev);
- if (prev)
- prev->next = adapter->next;
- else
- adapter_list = adapter->next;
+ list_del(&adapter->node);
kfree(adapter);
- return;
}
}
+ mutex_unlock(&adapter_list_lock);
}
static struct parport_driver i2c_parport_driver = {
/* Let legacy drivers scan this bus for matching devices */
if (driver->attach_adapter) {
- dev_warn(&adap->dev, "attach_adapter method is deprecated\n");
+ dev_warn(&adap->dev, "%s: attach_adapter method is deprecated\n",
+ driver->driver.name);
dev_warn(&adap->dev, "Please use another way to instantiate "
"your i2c_client\n");
/* We ignore the return code; if it fails, too bad */
if (!driver->detach_adapter)
return 0;
- dev_warn(&adapter->dev, "detach_adapter method is deprecated\n");
+ dev_warn(&adapter->dev, "%s: detach_adapter method is deprecated\n",
+ driver->driver.name);
res = driver->detach_adapter(adapter);
if (res)
dev_err(&adapter->dev, "detach_adapter failed (%d) "
ide_cd_read_toc(drive, &sense);
g->fops = &idecd_ops;
g->flags |= GENHD_FL_REMOVABLE;
- g->events = DISK_EVENT_MEDIA_CHANGE;
add_disk(g);
return 0;
return CDS_DRIVE_NOT_READY;
}
+/*
+ * ide-cd always generates media changed event if media is missing, which
+ * makes it impossible to use for proper event reporting, so disk->events
+ * is cleared to 0 and the following function is used only to trigger
+ * revalidation and never propagated to userland.
+ */
unsigned int ide_cdrom_check_events_real(struct cdrom_device_info *cdi,
unsigned int clearing, int slot_nr)
{
return 0;
}
+ /*
+ * The following is used to force revalidation on the first open on
+ * removeable devices, and never gets reported to userland as
+ * genhd->events is 0. This is intended as removeable ide disk
+ * can't really detect MEDIA_CHANGE events.
+ */
ret = drive->dev_flags & IDE_DFLAG_MEDIA_CHANGED;
drive->dev_flags &= ~IDE_DFLAG_MEDIA_CHANGED;
if (drive->dev_flags & IDE_DFLAG_REMOVABLE)
g->flags = GENHD_FL_REMOVABLE;
g->fops = &ide_gd_ops;
- g->events = DISK_EVENT_MEDIA_CHANGE;
add_disk(g);
return 0;
/*
* Keep chip from being accessed until we are ready. Use
* writeq() directly, to allow the write even though QIB_PRESENT
- * isn't' set.
+ * isn't set.
*/
dd->flags &= ~(QIB_INITTED | QIB_PRESENT);
dd->int_counter = 0; /* so we check interrupts work again */
/*
* Keep chip from being accessed until we are ready. Use
* writeq() directly, to allow the write even though QIB_PRESENT
- * isn't' set.
+ * isn't set.
*/
dd->flags &= ~(QIB_INITTED | QIB_PRESENT);
dd->int_counter = 0; /* so we check interrupts work again */
/*
* Keep chip from being accessed until we are ready. Use
* writeq() directly, to allow the write even though QIB_PRESENT
- * isn't' set.
+ * isn't set.
*/
dd->flags &= ~(QIB_INITTED | QIB_PRESENT | QIB_BADINTR);
dd->flags |= QIB_DOING_RESET;
};
struct evdev_client {
- int head;
- int tail;
+ unsigned int head;
+ unsigned int tail;
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
- int bufsize;
+ unsigned int bufsize;
struct input_event buffer[];
};
static void evdev_pass_event(struct evdev_client *client,
struct input_event *event)
{
- /*
- * Interrupts are disabled, just acquire the lock.
- * Make sure we don't leave with the client buffer
- * "empty" by having client->head == client->tail.
- */
+ /* Interrupts are disabled, just acquire the lock. */
spin_lock(&client->buffer_lock);
- do {
- client->buffer[client->head++] = *event;
- client->head &= client->bufsize - 1;
- } while (client->head == client->tail);
+
+ client->buffer[client->head++] = *event;
+ client->head &= client->bufsize - 1;
+
+ if (unlikely(client->head == client->tail)) {
+ /*
+ * This effectively "drops" all unconsumed events, leaving
+ * EV_SYN/SYN_DROPPED plus the newest event in the queue.
+ */
+ client->tail = (client->head - 2) & (client->bufsize - 1);
+
+ client->buffer[client->tail].time = event->time;
+ client->buffer[client->tail].type = EV_SYN;
+ client->buffer[client->tail].code = SYN_DROPPED;
+ client->buffer[client->tail].value = 0;
+ }
+
spin_unlock(&client->buffer_lock);
if (event->type == EV_SYN)
}
EXPORT_SYMBOL(input_set_capability);
+static unsigned int input_estimate_events_per_packet(struct input_dev *dev)
+{
+ int mt_slots;
+ int i;
+ unsigned int events;
+
+ if (dev->mtsize) {
+ mt_slots = dev->mtsize;
+ } else if (test_bit(ABS_MT_TRACKING_ID, dev->absbit)) {
+ mt_slots = dev->absinfo[ABS_MT_TRACKING_ID].maximum -
+ dev->absinfo[ABS_MT_TRACKING_ID].minimum + 1,
+ clamp(mt_slots, 2, 32);
+ } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
+ mt_slots = 2;
+ } else {
+ mt_slots = 0;
+ }
+
+ events = mt_slots + 1; /* count SYN_MT_REPORT and SYN_REPORT */
+
+ for (i = 0; i < ABS_CNT; i++) {
+ if (test_bit(i, dev->absbit)) {
+ if (input_is_mt_axis(i))
+ events += mt_slots;
+ else
+ events++;
+ }
+ }
+
+ for (i = 0; i < REL_CNT; i++)
+ if (test_bit(i, dev->relbit))
+ events++;
+
+ return events;
+}
+
#define INPUT_CLEANSE_BITMASK(dev, type, bits) \
do { \
if (!test_bit(EV_##type, dev->evbit)) \
/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev);
+ if (!dev->hint_events_per_packet)
+ dev->hint_events_per_packet =
+ input_estimate_events_per_packet(dev);
+
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
static int __devinit twl4030_kp_probe(struct platform_device *pdev)
{
struct twl4030_keypad_data *pdata = pdev->dev.platform_data;
- const struct matrix_keymap_data *keymap_data = pdata->keymap_data;
+ const struct matrix_keymap_data *keymap_data;
struct twl4030_keypad *kp;
struct input_dev *input;
u8 reg;
int error;
- if (!pdata || !pdata->rows || !pdata->cols ||
+ if (!pdata || !pdata->rows || !pdata->cols || !pdata->keymap_data ||
pdata->rows > TWL4030_MAX_ROWS || pdata->cols > TWL4030_MAX_COLS) {
dev_err(&pdev->dev, "Invalid platform_data\n");
return -EINVAL;
}
+ keymap_data = pdata->keymap_data;
+
kp = kzalloc(sizeof(*kp), GFP_KERNEL);
input = input_allocate_device();
if (!kp || !input) {
enum xenbus_state backend_state)
{
struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
- int val;
+ int ret, val;
switch (backend_state) {
case XenbusStateInitialising:
case XenbusStateInitWait:
InitWait:
+ ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+ "feature-abs-pointer", "%d", &val);
+ if (ret < 0)
+ val = 0;
+ if (val) {
+ ret = xenbus_printf(XBT_NIL, info->xbdev->nodename,
+ "request-abs-pointer", "1");
+ if (ret)
+ pr_warning("xenkbd: can't request abs-pointer");
+ }
+
xenbus_switch_state(dev, XenbusStateConnected);
break;
IRQF_SHARED | IRQF_DISABLED, "h3600_action", &ts->dev)) {
printk(KERN_ERR "h3600ts.c: Could not allocate Action Button IRQ!\n");
err = -EBUSY;
- goto fail2;
+ goto fail1;
}
if (request_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, npower_button_handler,
IRQF_SHARED | IRQF_DISABLED, "h3600_suspend", &ts->dev)) {
printk(KERN_ERR "h3600ts.c: Could not allocate Power Button IRQ!\n");
err = -EBUSY;
- goto fail3;
+ goto fail2;
}
serio_set_drvdata(serio, ts);
err = serio_open(serio, drv);
if (err)
- return err;
+ goto fail3;
//h3600_flite_control(1, 25); /* default brightness */
- input_register_device(ts->dev);
+ err = input_register_device(ts->dev);
+ if (err)
+ goto fail4;
return 0;
-fail3: free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev);
+fail4: serio_close(serio);
+fail3: serio_set_drvdata(serio, NULL);
+ free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev);
fail2: free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts->dev);
-fail1: serio_set_drvdata(serio, NULL);
- input_free_device(input_dev);
+fail1: input_free_device(input_dev);
kfree(ts);
return err;
}
unsigned int pd_irq;
bool pressure;
bool pen_down;
+ struct work_struct pd_data_work;
};
+static void wm831x_pd_data_work(struct work_struct *work)
+{
+ struct wm831x_ts *wm831x_ts =
+ container_of(work, struct wm831x_ts, pd_data_work);
+
+ if (wm831x_ts->pen_down) {
+ enable_irq(wm831x_ts->data_irq);
+ dev_dbg(wm831x_ts->wm831x->dev, "IRQ PD->DATA done\n");
+ } else {
+ enable_irq(wm831x_ts->pd_irq);
+ dev_dbg(wm831x_ts->wm831x->dev, "IRQ DATA->PD done\n");
+ }
+}
+
static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data)
{
struct wm831x_ts *wm831x_ts = irq_data;
}
if (!wm831x_ts->pen_down) {
+ /* Switch from data to pen down */
+ dev_dbg(wm831x->dev, "IRQ DATA->PD\n");
+
disable_irq_nosync(wm831x_ts->data_irq);
/* Don't need data any more */
ABS_PRESSURE, 0);
input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 0);
+
+ schedule_work(&wm831x_ts->pd_data_work);
+ } else {
+ input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1);
}
input_sync(wm831x_ts->input_dev);
struct wm831x *wm831x = wm831x_ts->wm831x;
int ena = 0;
+ if (wm831x_ts->pen_down)
+ return IRQ_HANDLED;
+
+ disable_irq_nosync(wm831x_ts->pd_irq);
+
/* Start collecting data */
if (wm831x_ts->pressure)
ena |= WM831X_TCH_Z_ENA;
WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA,
WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | ena);
- input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1);
- input_sync(wm831x_ts->input_dev);
-
wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
WM831X_TCHPD_EINT, WM831X_TCHPD_EINT);
wm831x_ts->pen_down = true;
- enable_irq(wm831x_ts->data_irq);
+
+ /* Switch from pen down to data */
+ dev_dbg(wm831x->dev, "IRQ PD->DATA\n");
+ schedule_work(&wm831x_ts->pd_data_work);
return IRQ_HANDLED;
}
struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
struct wm831x *wm831x = wm831x_ts->wm831x;
+ /* Shut the controller down, disabling all other functionality too */
wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
- WM831X_TCH_ENA | WM831X_TCH_CVT_ENA |
- WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
- WM831X_TCH_Z_ENA, 0);
+ WM831X_TCH_ENA | WM831X_TCH_X_ENA |
+ WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, 0);
- if (wm831x_ts->pen_down)
+ /* Make sure any pending IRQs are done, the above will prevent
+ * new ones firing.
+ */
+ synchronize_irq(wm831x_ts->data_irq);
+ synchronize_irq(wm831x_ts->pd_irq);
+
+ /* Make sure the IRQ completion work is quiesced */
+ flush_work_sync(&wm831x_ts->pd_data_work);
+
+ /* If we ended up with the pen down then make sure we revert back
+ * to pen detection state for the next time we start up.
+ */
+ if (wm831x_ts->pen_down) {
disable_irq(wm831x_ts->data_irq);
+ enable_irq(wm831x_ts->pd_irq);
+ wm831x_ts->pen_down = false;
+ }
}
static __devinit int wm831x_ts_probe(struct platform_device *pdev)
struct wm831x_pdata *core_pdata = dev_get_platdata(pdev->dev.parent);
struct wm831x_touch_pdata *pdata = NULL;
struct input_dev *input_dev;
- int error;
+ int error, irqf;
if (core_pdata)
pdata = core_pdata->touch;
wm831x_ts->wm831x = wm831x;
wm831x_ts->input_dev = input_dev;
+ INIT_WORK(&wm831x_ts->pd_data_work, wm831x_pd_data_work);
/*
* If we have a direct IRQ use it, otherwise use the interrupt
wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
WM831X_TCH_RATE_MASK, 6);
+ if (pdata && pdata->data_irqf)
+ irqf = pdata->data_irqf;
+ else
+ irqf = IRQF_TRIGGER_HIGH;
+
error = request_threaded_irq(wm831x_ts->data_irq,
NULL, wm831x_ts_data_irq,
- IRQF_ONESHOT,
+ irqf | IRQF_ONESHOT,
"Touchscreen data", wm831x_ts);
if (error) {
dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n",
}
disable_irq(wm831x_ts->data_irq);
+ if (pdata && pdata->pd_irqf)
+ irqf = pdata->pd_irqf;
+ else
+ irqf = IRQF_TRIGGER_HIGH;
+
error = request_threaded_irq(wm831x_ts->pd_irq,
NULL, wm831x_ts_pen_down_irq,
- IRQF_ONESHOT,
+ irqf | IRQF_ONESHOT,
"Touchscreen pen down", wm831x_ts);
if (error) {
dev_err(&pdev->dev, "Failed to request pen down IRQ %d: %d\n",
led->cdev.flags |= LED_CORE_SUSPENDRESUME;
led->vcc = vcc;
+ /* to handle correctly an already enabled regulator */
+ if (regulator_is_enabled(led->vcc))
+ led->enabled = 1;
+
mutex_init(&led->mutex);
INIT_WORK(&led->work, led_work);
return md_raid5_congested(&rs->md, bits);
}
-static void raid_unplug(struct dm_target_callbacks *cb)
-{
- struct raid_set *rs = container_of(cb, struct raid_set, callbacks);
-
- md_raid5_kick_device(rs->md.private);
-}
-
/*
* Construct a RAID4/5/6 mapping:
* Args:
}
rs->callbacks.congested_fn = raid_is_congested;
- rs->callbacks.unplug_fn = raid_unplug;
dm_table_add_target_callbacks(ti->table, &rs->callbacks);
return 0;
/* Support for plugging.
* This mirrors the plugging support in request_queue, but does not
- * require having a whole queue
+ * require having a whole queue or request structures.
+ * We allocate an md_plug_cb for each md device and each thread it gets
+ * plugged on. This links tot the private plug_handle structure in the
+ * personality data where we keep a count of the number of outstanding
+ * plugs so other code can see if a plug is active.
*/
-static void plugger_work(struct work_struct *work)
-{
- struct plug_handle *plug =
- container_of(work, struct plug_handle, unplug_work);
- plug->unplug_fn(plug);
-}
-static void plugger_timeout(unsigned long data)
-{
- struct plug_handle *plug = (void *)data;
- kblockd_schedule_work(NULL, &plug->unplug_work);
-}
-void plugger_init(struct plug_handle *plug,
- void (*unplug_fn)(struct plug_handle *))
-{
- plug->unplug_flag = 0;
- plug->unplug_fn = unplug_fn;
- init_timer(&plug->unplug_timer);
- plug->unplug_timer.function = plugger_timeout;
- plug->unplug_timer.data = (unsigned long)plug;
- INIT_WORK(&plug->unplug_work, plugger_work);
-}
-EXPORT_SYMBOL_GPL(plugger_init);
+struct md_plug_cb {
+ struct blk_plug_cb cb;
+ mddev_t *mddev;
+};
-void plugger_set_plug(struct plug_handle *plug)
+static void plugger_unplug(struct blk_plug_cb *cb)
{
- if (!test_and_set_bit(PLUGGED_FLAG, &plug->unplug_flag))
- mod_timer(&plug->unplug_timer, jiffies + msecs_to_jiffies(3)+1);
+ struct md_plug_cb *mdcb = container_of(cb, struct md_plug_cb, cb);
+ if (atomic_dec_and_test(&mdcb->mddev->plug_cnt))
+ md_wakeup_thread(mdcb->mddev->thread);
+ kfree(mdcb);
}
-EXPORT_SYMBOL_GPL(plugger_set_plug);
-int plugger_remove_plug(struct plug_handle *plug)
+/* Check that an unplug wakeup will come shortly.
+ * If not, wakeup the md thread immediately
+ */
+int mddev_check_plugged(mddev_t *mddev)
{
- if (test_and_clear_bit(PLUGGED_FLAG, &plug->unplug_flag)) {
- del_timer(&plug->unplug_timer);
- return 1;
- } else
+ struct blk_plug *plug = current->plug;
+ struct md_plug_cb *mdcb;
+
+ if (!plug)
return 0;
-}
-EXPORT_SYMBOL_GPL(plugger_remove_plug);
+ list_for_each_entry(mdcb, &plug->cb_list, cb.list) {
+ if (mdcb->cb.callback == plugger_unplug &&
+ mdcb->mddev == mddev) {
+ /* Already on the list, move to top */
+ if (mdcb != list_first_entry(&plug->cb_list,
+ struct md_plug_cb,
+ cb.list))
+ list_move(&mdcb->cb.list, &plug->cb_list);
+ return 1;
+ }
+ }
+ /* Not currently on the callback list */
+ mdcb = kmalloc(sizeof(*mdcb), GFP_ATOMIC);
+ if (!mdcb)
+ return 0;
+
+ mdcb->mddev = mddev;
+ mdcb->cb.callback = plugger_unplug;
+ atomic_inc(&mddev->plug_cnt);
+ list_add(&mdcb->cb.list, &plug->cb_list);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(mddev_check_plugged);
static inline mddev_t *mddev_get(mddev_t *mddev)
{
atomic_set(&mddev->active, 1);
atomic_set(&mddev->openers, 0);
atomic_set(&mddev->active_io, 0);
+ atomic_set(&mddev->plug_cnt, 0);
spin_lock_init(&mddev->write_lock);
atomic_set(&mddev->flush_pending, 0);
init_waitqueue_head(&mddev->sb_wait);
mddev->layout = mddev->new_layout;
mddev->chunk_sectors = mddev->new_chunk_sectors;
mddev->delta_disks = 0;
+ mddev->degraded = 0;
if (mddev->pers->sync_request == NULL) {
/* this is now an array without redundancy, so
* it must always be in_sync
mddev->bitmap_info.chunksize = 0;
mddev->bitmap_info.daemon_sleep = 0;
mddev->bitmap_info.max_write_behind = 0;
- mddev->plug = NULL;
}
static void __md_stop_writes(mddev_t *mddev)
}
EXPORT_SYMBOL_GPL(md_allow_write);
-void md_unplug(mddev_t *mddev)
-{
- if (mddev->plug)
- mddev->plug->unplug_fn(mddev->plug);
-}
-
#define SYNC_MARKS 10
#define SYNC_MARK_STEP (3*HZ)
void md_do_sync(mddev_t *mddev)
typedef struct mddev_s mddev_t;
typedef struct mdk_rdev_s mdk_rdev_t;
-/* generic plugging support - like that provided with request_queue,
- * but does not require a request_queue
- */
-struct plug_handle {
- void (*unplug_fn)(struct plug_handle *);
- struct timer_list unplug_timer;
- struct work_struct unplug_work;
- unsigned long unplug_flag;
-};
-#define PLUGGED_FLAG 1
-void plugger_init(struct plug_handle *plug,
- void (*unplug_fn)(struct plug_handle *));
-void plugger_set_plug(struct plug_handle *plug);
-int plugger_remove_plug(struct plug_handle *plug);
-static inline void plugger_flush(struct plug_handle *plug)
-{
- del_timer_sync(&plug->unplug_timer);
- cancel_work_sync(&plug->unplug_work);
-}
-
/*
* MD's 'extended' device
*/
int delta_disks, new_level, new_layout;
int new_chunk_sectors;
+ atomic_t plug_cnt; /* If device is expecting
+ * more bios soon.
+ */
struct mdk_thread_s *thread; /* management thread */
struct mdk_thread_s *sync_thread; /* doing resync or reconstruct */
sector_t curr_resync; /* last block scheduled */
struct list_head all_mddevs;
struct attribute_group *to_remove;
- struct plug_handle *plug; /* if used by personality */
struct bio_set *bio_set;
extern void md_integrity_add_rdev(mdk_rdev_t *rdev, mddev_t *mddev);
extern int strict_strtoul_scaled(const char *cp, unsigned long *res, int scale);
extern void restore_bitmap_write_access(struct file *file);
-extern void md_unplug(mddev_t *mddev);
extern void mddev_init(mddev_t *mddev);
extern int md_run(mddev_t *mddev);
mddev_t *mddev);
extern struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs,
mddev_t *mddev);
+extern int mddev_check_plugged(mddev_t *mddev);
#endif /* _MD_MD_H */
spin_unlock_irq(&conf->device_lock);
}
-static void md_kick_device(mddev_t *mddev)
-{
- blk_flush_plug(current);
- md_wakeup_thread(mddev->thread);
-}
-
/* Barriers....
* Sometimes we need to suspend IO while we do something else,
* either some resync/recovery, or reconfigure the array.
/* Wait until no block IO is waiting */
wait_event_lock_irq(conf->wait_barrier, !conf->nr_waiting,
- conf->resync_lock, md_kick_device(conf->mddev));
+ conf->resync_lock, );
/* block any new IO from starting */
conf->barrier++;
/* Now wait for all pending IO to complete */
wait_event_lock_irq(conf->wait_barrier,
!conf->nr_pending && conf->barrier < RESYNC_DEPTH,
- conf->resync_lock, md_kick_device(conf->mddev));
+ conf->resync_lock, );
spin_unlock_irq(&conf->resync_lock);
}
conf->nr_waiting++;
wait_event_lock_irq(conf->wait_barrier, !conf->barrier,
conf->resync_lock,
- md_kick_device(conf->mddev));
+ );
conf->nr_waiting--;
}
conf->nr_pending++;
wait_event_lock_irq(conf->wait_barrier,
conf->nr_pending == conf->nr_queued+1,
conf->resync_lock,
- ({ flush_pending_writes(conf);
- md_kick_device(conf->mddev); }));
+ flush_pending_writes(conf));
spin_unlock_irq(&conf->resync_lock);
}
static void unfreeze_array(conf_t *conf)
const unsigned long do_sync = (bio->bi_rw & REQ_SYNC);
const unsigned long do_flush_fua = (bio->bi_rw & (REQ_FLUSH | REQ_FUA));
mdk_rdev_t *blocked_rdev;
+ int plugged;
/*
* Register the new request and wait if the reconstruction
* inc refcount on their rdev. Record them by setting
* bios[x] to bio
*/
+ plugged = mddev_check_plugged(mddev);
+
disks = conf->raid_disks;
retry_write:
blocked_rdev = NULL;
/* In case raid1d snuck in to freeze_array */
wake_up(&conf->wait_barrier);
- if (do_sync || !bitmap)
+ if (do_sync || !bitmap || !plugged)
md_wakeup_thread(mddev->thread);
return 0;
conf_t *conf = mddev->private;
struct list_head *head = &conf->retry_list;
mdk_rdev_t *rdev;
+ struct blk_plug plug;
md_check_recovery(mddev);
-
+
+ blk_start_plug(&plug);
for (;;) {
char b[BDEVNAME_SIZE];
- flush_pending_writes(conf);
+ if (atomic_read(&mddev->plug_cnt) == 0)
+ flush_pending_writes(conf);
spin_lock_irqsave(&conf->device_lock, flags);
if (list_empty(head)) {
}
cond_resched();
}
+ blk_finish_plug(&plug);
}
md_unregister_thread(mddev->thread);
mddev->thread = NULL;
- blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
if (conf->r1bio_pool)
mempool_destroy(conf->r1bio_pool);
kfree(conf->mirrors);
spin_unlock_irq(&conf->device_lock);
}
-static void md_kick_device(mddev_t *mddev)
-{
- blk_flush_plug(current);
- md_wakeup_thread(mddev->thread);
-}
-
/* Barriers....
* Sometimes we need to suspend IO while we do something else,
* either some resync/recovery, or reconfigure the array.
/* Wait until no block IO is waiting (unless 'force') */
wait_event_lock_irq(conf->wait_barrier, force || !conf->nr_waiting,
- conf->resync_lock, md_kick_device(conf->mddev));
+ conf->resync_lock, );
/* block any new IO from starting */
conf->barrier++;
- /* No wait for all pending IO to complete */
+ /* Now wait for all pending IO to complete */
wait_event_lock_irq(conf->wait_barrier,
!conf->nr_pending && conf->barrier < RESYNC_DEPTH,
- conf->resync_lock, md_kick_device(conf->mddev));
+ conf->resync_lock, );
spin_unlock_irq(&conf->resync_lock);
}
conf->nr_waiting++;
wait_event_lock_irq(conf->wait_barrier, !conf->barrier,
conf->resync_lock,
- md_kick_device(conf->mddev));
+ );
conf->nr_waiting--;
}
conf->nr_pending++;
wait_event_lock_irq(conf->wait_barrier,
conf->nr_pending == conf->nr_queued+1,
conf->resync_lock,
- ({ flush_pending_writes(conf);
- md_kick_device(conf->mddev); }));
+ flush_pending_writes(conf));
+
spin_unlock_irq(&conf->resync_lock);
}
const unsigned long do_fua = (bio->bi_rw & REQ_FUA);
unsigned long flags;
mdk_rdev_t *blocked_rdev;
+ int plugged;
if (unlikely(bio->bi_rw & REQ_FLUSH)) {
md_flush_request(mddev, bio);
* inc refcount on their rdev. Record them by setting
* bios[x] to bio
*/
+ plugged = mddev_check_plugged(mddev);
+
raid10_find_phys(conf, r10_bio);
retry_write:
blocked_rdev = NULL;
/* In case raid10d snuck in to freeze_array */
wake_up(&conf->wait_barrier);
- if (do_sync || !mddev->bitmap)
+ if (do_sync || !mddev->bitmap || !plugged)
md_wakeup_thread(mddev->thread);
-
return 0;
}
conf_t *conf = mddev->private;
struct list_head *head = &conf->retry_list;
mdk_rdev_t *rdev;
+ struct blk_plug plug;
md_check_recovery(mddev);
+ blk_start_plug(&plug);
for (;;) {
char b[BDEVNAME_SIZE];
}
cond_resched();
}
+ blk_finish_plug(&plug);
}
*
* We group bitmap updates into batches. Each batch has a number.
* We may write out several batches at once, but that isn't very important.
- * conf->bm_write is the number of the last batch successfully written.
- * conf->bm_flush is the number of the last batch that was closed to
+ * conf->seq_write is the number of the last batch successfully written.
+ * conf->seq_flush is the number of the last batch that was closed to
* new additions.
* When we discover that we will need to write to any block in a stripe
* (in add_stripe_bio) we update the in-memory bitmap and record in sh->bm_seq
- * the number of the batch it will be in. This is bm_flush+1.
+ * the number of the batch it will be in. This is seq_flush+1.
* When we are ready to do a write, if that batch hasn't been written yet,
* we plug the array and queue the stripe for later.
* When an unplug happens, we increment bm_flush, thus closing the current
BUG_ON(!list_empty(&sh->lru));
BUG_ON(atomic_read(&conf->active_stripes)==0);
if (test_bit(STRIPE_HANDLE, &sh->state)) {
- if (test_bit(STRIPE_DELAYED, &sh->state)) {
+ if (test_bit(STRIPE_DELAYED, &sh->state))
list_add_tail(&sh->lru, &conf->delayed_list);
- plugger_set_plug(&conf->plug);
- } else if (test_bit(STRIPE_BIT_DELAY, &sh->state) &&
- sh->bm_seq - conf->seq_write > 0) {
+ else if (test_bit(STRIPE_BIT_DELAY, &sh->state) &&
+ sh->bm_seq - conf->seq_write > 0)
list_add_tail(&sh->lru, &conf->bitmap_list);
- plugger_set_plug(&conf->plug);
- } else {
+ else {
clear_bit(STRIPE_BIT_DELAY, &sh->state);
list_add_tail(&sh->lru, &conf->handle_list);
}
< (conf->max_nr_stripes *3/4)
|| !conf->inactive_blocked),
conf->device_lock,
- md_raid5_kick_device(conf));
+ );
conf->inactive_blocked = 0;
} else
init_stripe(sh, sector, previous);
wait_event_lock_irq(conf->wait_for_stripe,
!list_empty(&conf->inactive_list),
conf->device_lock,
- blk_flush_plug(current));
+ );
osh = get_free_stripe(conf);
spin_unlock_irq(&conf->device_lock);
atomic_set(&nsh->count, 1);
atomic_inc(&conf->preread_active_stripes);
list_add_tail(&sh->lru, &conf->hold_list);
}
- } else
- plugger_set_plug(&conf->plug);
+ }
}
static void activate_bit_delay(raid5_conf_t *conf)
}
}
-void md_raid5_kick_device(raid5_conf_t *conf)
-{
- blk_flush_plug(current);
- raid5_activate_delayed(conf);
- md_wakeup_thread(conf->mddev->thread);
-}
-EXPORT_SYMBOL_GPL(md_raid5_kick_device);
-
-static void raid5_unplug(struct plug_handle *plug)
-{
- raid5_conf_t *conf = container_of(plug, raid5_conf_t, plug);
-
- md_raid5_kick_device(conf);
-}
-
int md_raid5_congested(mddev_t *mddev, int bits)
{
raid5_conf_t *conf = mddev->private;
struct stripe_head *sh;
const int rw = bio_data_dir(bi);
int remaining;
+ int plugged;
if (unlikely(bi->bi_rw & REQ_FLUSH)) {
md_flush_request(mddev, bi);
bi->bi_next = NULL;
bi->bi_phys_segments = 1; /* over-loaded to count active stripes */
+ plugged = mddev_check_plugged(mddev);
for (;logical_sector < last_sector; logical_sector += STRIPE_SECTORS) {
DEFINE_WAIT(w);
int disks, data_disks;
* add failed due to overlap. Flush everything
* and wait a while
*/
- md_raid5_kick_device(conf);
+ md_wakeup_thread(mddev->thread);
release_stripe(sh);
schedule();
goto retry;
}
}
+ if (!plugged)
+ md_wakeup_thread(mddev->thread);
+
spin_lock_irq(&conf->device_lock);
remaining = raid5_dec_bi_phys_segments(bi);
spin_unlock_irq(&conf->device_lock);
struct stripe_head *sh;
raid5_conf_t *conf = mddev->private;
int handled;
+ struct blk_plug plug;
pr_debug("+++ raid5d active\n");
md_check_recovery(mddev);
+ blk_start_plug(&plug);
handled = 0;
spin_lock_irq(&conf->device_lock);
while (1) {
struct bio *bio;
- if (conf->seq_flush != conf->seq_write) {
- int seq = conf->seq_flush;
+ if (atomic_read(&mddev->plug_cnt) == 0 &&
+ !list_empty(&conf->bitmap_list)) {
+ /* Now is a good time to flush some bitmap updates */
+ conf->seq_flush++;
spin_unlock_irq(&conf->device_lock);
bitmap_unplug(mddev->bitmap);
spin_lock_irq(&conf->device_lock);
- conf->seq_write = seq;
+ conf->seq_write = conf->seq_flush;
activate_bit_delay(conf);
}
+ if (atomic_read(&mddev->plug_cnt) == 0)
+ raid5_activate_delayed(conf);
while ((bio = remove_bio_from_retry(conf))) {
int ok;
spin_unlock_irq(&conf->device_lock);
async_tx_issue_pending_all();
+ blk_finish_plug(&plug);
pr_debug("--- raid5d inactive\n");
}
mdname(mddev));
md_set_array_sectors(mddev, raid5_size(mddev, 0, 0));
- plugger_init(&conf->plug, raid5_unplug);
- mddev->plug = &conf->plug;
if (mddev->queue) {
int chunk_size;
/* read-ahead size must cover two whole stripes, which
mddev->queue->backing_dev_info.congested_data = mddev;
mddev->queue->backing_dev_info.congested_fn = raid5_congested;
- mddev->queue->queue_lock = &conf->device_lock;
chunk_size = mddev->chunk_sectors << 9;
blk_queue_io_min(mddev->queue, chunk_size);
mddev->thread = NULL;
if (mddev->queue)
mddev->queue->backing_dev_info.congested_fn = NULL;
- plugger_flush(&conf->plug); /* the unplug fn references 'conf'*/
free_conf(conf);
mddev->private = NULL;
mddev->to_remove = &raid5_attrs_group;
static void *raid45_takeover_raid0(mddev_t *mddev, int level)
{
struct raid0_private_data *raid0_priv = mddev->private;
+ sector_t sectors;
/* for raid0 takeover only one zone is supported */
if (raid0_priv->nr_strip_zones > 1) {
return ERR_PTR(-EINVAL);
}
+ sectors = raid0_priv->strip_zone[0].zone_end;
+ sector_div(sectors, raid0_priv->strip_zone[0].nb_dev);
+ mddev->dev_sectors = sectors;
mddev->new_level = level;
mddev->new_layout = ALGORITHM_PARITY_N;
mddev->new_chunk_sectors = mddev->chunk_sectors;
* Cleared when a sync completes.
*/
- struct plug_handle plug;
-
/* per cpu variables */
struct raid5_percpu {
struct page *spare_page; /* Used when checking P/Q in raid6 */
if (tda_fail(ret))
goto fail;
- regs[R_MPD] = (0x77 & pd);
-
- switch (priv->mode) {
- case TDA18271_ANALOG:
- regs[R_MPD] &= ~0x08;
- break;
- case TDA18271_DIGITAL:
- regs[R_MPD] |= 0x08;
- break;
- }
+ regs[R_MPD] = (0x7f & pd);
div = ((d * (freq / 1000)) << 7) / 125;
#define RF3 2
u32 rf_default[3];
u32 rf_freq[3];
- u8 prog_cal[3];
- u8 prog_tab[3];
+ s32 prog_cal[3];
+ s32 prog_tab[3];
i = tda18271_lookup_rf_band(fe, &freq, NULL);
return bcal;
tda18271_calc_rf_cal(fe, &rf_freq[rf]);
- prog_tab[rf] = regs[R_EB14];
+ prog_tab[rf] = (s32)regs[R_EB14];
if (1 == bcal)
- prog_cal[rf] = tda18271_calibrate_rf(fe, rf_freq[rf]);
+ prog_cal[rf] =
+ (s32)tda18271_calibrate_rf(fe, rf_freq[rf]);
else
prog_cal[rf] = prog_tab[rf];
switch (rf) {
case RF1:
map[i].rf_a1 = 0;
- map[i].rf_b1 = (s32)(prog_cal[RF1] - prog_tab[RF1]);
+ map[i].rf_b1 = (prog_cal[RF1] - prog_tab[RF1]);
map[i].rf1 = rf_freq[RF1] / 1000;
break;
case RF2:
- dividend = (s32)(prog_cal[RF2] - prog_tab[RF2]) -
- (s32)(prog_cal[RF1] + prog_tab[RF1]);
+ dividend = (prog_cal[RF2] - prog_tab[RF2] -
+ prog_cal[RF1] + prog_tab[RF1]);
divisor = (s32)(rf_freq[RF2] - rf_freq[RF1]) / 1000;
map[i].rf_a1 = (dividend / divisor);
map[i].rf2 = rf_freq[RF2] / 1000;
break;
case RF3:
- dividend = (s32)(prog_cal[RF3] - prog_tab[RF3]) -
- (s32)(prog_cal[RF2] + prog_tab[RF2]);
+ dividend = (prog_cal[RF3] - prog_tab[RF3] -
+ prog_cal[RF2] + prog_tab[RF2]);
divisor = (s32)(rf_freq[RF3] - rf_freq[RF2]) / 1000;
map[i].rf_a2 = (dividend / divisor);
- map[i].rf_b2 = (s32)(prog_cal[RF2] - prog_tab[RF2]);
+ map[i].rf_b2 = (prog_cal[RF2] - prog_tab[RF2]);
map[i].rf3 = rf_freq[RF3] / 1000;
break;
default:
static struct tda18271_map tda18271_rf_band[] = {
{ .rfmax = 47900, .val = 0x00 },
{ .rfmax = 61100, .val = 0x01 },
-/* { .rfmax = 152600, .val = 0x02 }, */
- { .rfmax = 121200, .val = 0x02 },
+ { .rfmax = 152600, .val = 0x02 },
{ .rfmax = 164700, .val = 0x03 },
{ .rfmax = 203500, .val = 0x04 },
{ .rfmax = 457800, .val = 0x05 },
{ .rfmax = 150000, .val = 0xb0 },
{ .rfmax = 151000, .val = 0xb1 },
{ .rfmax = 152000, .val = 0xb7 },
- { .rfmax = 153000, .val = 0xbd },
+ { .rfmax = 152600, .val = 0xbd },
{ .rfmax = 154000, .val = 0x20 },
{ .rfmax = 155000, .val = 0x22 },
{ .rfmax = 156000, .val = 0x24 },
{ .rfmax = 161000, .val = 0x2d },
{ .rfmax = 163000, .val = 0x2e },
{ .rfmax = 164000, .val = 0x2f },
- { .rfmax = 165000, .val = 0x30 },
+ { .rfmax = 164700, .val = 0x30 },
{ .rfmax = 166000, .val = 0x11 },
{ .rfmax = 167000, .val = 0x12 },
{ .rfmax = 168000, .val = 0x13 },
{ .rfmax = 236000, .val = 0x1b },
{ .rfmax = 237000, .val = 0x1c },
{ .rfmax = 240000, .val = 0x1d },
- { .rfmax = 242000, .val = 0x1f },
+ { .rfmax = 242000, .val = 0x1e },
+ { .rfmax = 244000, .val = 0x1f },
{ .rfmax = 247000, .val = 0x20 },
{ .rfmax = 249000, .val = 0x21 },
{ .rfmax = 252000, .val = 0x22 },
{ .rfmax = 453000, .val = 0x93 },
{ .rfmax = 454000, .val = 0x94 },
{ .rfmax = 456000, .val = 0x96 },
- { .rfmax = 457000, .val = 0x98 },
+ { .rfmax = 457800, .val = 0x98 },
{ .rfmax = 461000, .val = 0x11 },
{ .rfmax = 468000, .val = 0x12 },
{ .rfmax = 472000, .val = 0x13 },
DEBSTATUS);
#define DRIVER_VERSION "0.1"
-#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV PCI Driver"
+#define DRIVER_NAME "flexcop-pci"
#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de>"
struct flexcop_pci {
select DVB_TDA826X if !DVB_FE_CUSTOMISE
select DVB_STV0288 if !DVB_FE_CUSTOMISE
select DVB_IX2505V if !DVB_FE_CUSTOMISE
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ select DVB_PLL if !DVB_FE_CUSTOMISE
help
Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 .
config DVB_USB_TECHNISAT_USB2
tristate "Technisat DVB-S/S2 USB2.0 support"
depends on DVB_USB
- select DVB_STB0899 if !DVB_FE_CUSTOMISE
- select DVB_STB6100 if !DVB_FE_CUSTOMISE
+ select DVB_STV090x if !DVB_FE_CUSTOMISE
+ select DVB_STV6110x if !DVB_FE_CUSTOMISE
help
Say Y here to support the Technisat USB2 DVB-S/S2 device
.agc1_pt3 = 98,
.agc1_slope1 = 0,
.agc1_slope2 = 167,
- .agc1_pt1 = 98,
+ .agc2_pt1 = 98,
.agc2_pt2 = 255,
.agc2_slope1 = 104,
.agc2_slope2 = 0,
dib0700_set_i2c_speed(adap->dev, 340);
adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x90, &tfe7090pvr_dib7000p_config[0]);
- dib7090_slave_reset(adap->fe);
-
if (adap->fe == NULL)
return -ENODEV;
+ dib7090_slave_reset(adap->fe);
+
return 0;
}
if (dev->ci.en && (io & NGENE_IO_TSOUT)) {
dvb_ca_en50221_init(adapter, dev->ci.en, 0, 1);
set_transfer(chan, 1);
+ chan->dev->channel[2].DataFormatFlags = DF_SWAP32;
set_transfer(&chan->dev->channel[2], 1);
dvb_register_device(adapter, &chan->ci_dev,
&ngene_dvbdev_ci, (void *) chan,
static int __media_entity_setup_link_notify(struct media_link *link, u32 flags)
{
- const u32 mask = MEDIA_LNK_FL_ENABLED;
int ret;
/* Notify both entities. */
return ret;
}
- link->flags = (link->flags & ~mask) | (flags & mask);
+ link->flags = flags;
link->reverse->flags = link->flags;
return 0;
*/
int __media_entity_setup_link(struct media_link *link, u32 flags)
{
+ const u32 mask = MEDIA_LNK_FL_ENABLED;
struct media_device *mdev;
struct media_entity *source, *sink;
int ret = -EBUSY;
if (link == NULL)
return -EINVAL;
+ /* The non-modifiable link flags must not be modified. */
+ if ((link->flags & ~mask) != (flags & ~mask))
+ return -EINVAL;
+
if (link->flags & MEDIA_LNK_FL_IMMUTABLE)
return link->flags == flags ? 0 : -EINVAL;
To compile this driver as a module, choose M here: the
module will be called radio-maxiradio.
-config RADIO_MAESTRO
- tristate "Maestro on board radio"
- depends on VIDEO_V4L2 && PCI
- ---help---
- Say Y here to directly support the on-board radio tuner on the
- Maestro 2 or 2E sound card.
-
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- <file:Documentation/video4linux/API.html>.
-
- To compile this driver as a module, choose M here: the
- module will be called radio-maestro.
-
config RADIO_MIROPCM20
tristate "miroSOUND PCM20 radio"
depends on ISA && VIDEO_V4L2 && SND
obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o
obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o
-obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o
obj-$(CONFIG_USB_DSBR) += dsbr100.o
obj-$(CONFIG_RADIO_SI470X) += si470x/
+++ /dev/null
-/* Maestro PCI sound card radio driver for Linux support
- * (c) 2000 A. Tlalka, atlka@pg.gda.pl
- * Notes on the hardware
- *
- * + Frequency control is done digitally
- * + No volume control - only mute/unmute - you have to use Aux line volume
- * control on Maestro card to set the volume
- * + Radio status (tuned/not_tuned and stereo/mono) is valid some time after
- * frequency setting (>100ms) and only when the radio is unmuted.
- * version 0.02
- * + io port is automatically detected - only the first radio is used
- * version 0.03
- * + thread access locking additions
- * version 0.04
- * + code improvements
- * + VIDEO_TUNER_LOW is permanent
- *
- * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
-#include <linux/pci.h>
-#include <linux/videodev2.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-
-MODULE_AUTHOR("Adam Tlalka, atlka@pg.gda.pl");
-MODULE_DESCRIPTION("Radio driver for the Maestro PCI sound card radio.");
-MODULE_LICENSE("GPL");
-
-static int radio_nr = -1;
-module_param(radio_nr, int, 0);
-
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 6)
-#define DRIVER_VERSION "0.06"
-
-#define GPIO_DATA 0x60 /* port offset from ESS_IO_BASE */
-
-#define IO_MASK 4 /* mask register offset from GPIO_DATA
- bits 1=unmask write to given bit */
-#define IO_DIR 8 /* direction register offset from GPIO_DATA
- bits 0/1=read/write direction */
-
-#define GPIO6 0x0040 /* mask bits for GPIO lines */
-#define GPIO7 0x0080
-#define GPIO8 0x0100
-#define GPIO9 0x0200
-
-#define STR_DATA GPIO6 /* radio TEA5757 pins and GPIO bits */
-#define STR_CLK GPIO7
-#define STR_WREN GPIO8
-#define STR_MOST GPIO9
-
-#define FREQ_LO 50*16000
-#define FREQ_HI 150*16000
-
-#define FREQ_IF 171200 /* 10.7*16000 */
-#define FREQ_STEP 200 /* 12.5*16 */
-
-#define FREQ2BITS(x) ((((unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1))\
- /(FREQ_STEP<<2))<<2) /* (x==fmhz*16*1000) -> bits */
-
-#define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF)
-
-struct maestro {
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- struct pci_dev *pdev;
- struct mutex lock;
-
- u16 io; /* base of Maestro card radio io (GPIO_DATA)*/
- u16 muted; /* VIDEO_AUDIO_MUTE */
- u16 stereo; /* VIDEO_TUNER_STEREO_ON */
- u16 tuned; /* signal strength (0 or 0xffff) */
-};
-
-static inline struct maestro *to_maestro(struct v4l2_device *v4l2_dev)
-{
- return container_of(v4l2_dev, struct maestro, v4l2_dev);
-}
-
-static u32 radio_bits_get(struct maestro *dev)
-{
- u16 io = dev->io, l, rdata;
- u32 data = 0;
- u16 omask;
-
- omask = inw(io + IO_MASK);
- outw(~(STR_CLK | STR_WREN), io + IO_MASK);
- outw(0, io);
- udelay(16);
-
- for (l = 24; l--;) {
- outw(STR_CLK, io); /* HI state */
- udelay(2);
- if (!l)
- dev->tuned = inw(io) & STR_MOST ? 0 : 0xffff;
- outw(0, io); /* LO state */
- udelay(2);
- data <<= 1; /* shift data */
- rdata = inw(io);
- if (!l)
- dev->stereo = (rdata & STR_MOST) ? 0 : 1;
- else if (rdata & STR_DATA)
- data++;
- udelay(2);
- }
-
- if (dev->muted)
- outw(STR_WREN, io);
-
- udelay(4);
- outw(omask, io + IO_MASK);
-
- return data & 0x3ffe;
-}
-
-static void radio_bits_set(struct maestro *dev, u32 data)
-{
- u16 io = dev->io, l, bits;
- u16 omask, odir;
-
- omask = inw(io + IO_MASK);
- odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN);
- outw(odir | STR_DATA, io + IO_DIR);
- outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK);
- udelay(16);
- for (l = 25; l; l--) {
- bits = ((data >> 18) & STR_DATA) | STR_WREN;
- data <<= 1; /* shift data */
- outw(bits, io); /* start strobe */
- udelay(2);
- outw(bits | STR_CLK, io); /* HI level */
- udelay(2);
- outw(bits, io); /* LO level */
- udelay(4);
- }
-
- if (!dev->muted)
- outw(0, io);
-
- udelay(4);
- outw(omask, io + IO_MASK);
- outw(odir, io + IO_DIR);
- msleep(125);
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- struct maestro *dev = video_drvdata(file);
-
- strlcpy(v->driver, "radio-maestro", sizeof(v->driver));
- strlcpy(v->card, "Maestro Radio", sizeof(v->card));
- snprintf(v->bus_info, sizeof(v->bus_info), "PCI:%s", pci_name(dev->pdev));
- v->version = RADIO_VERSION;
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- struct maestro *dev = video_drvdata(file);
-
- if (v->index > 0)
- return -EINVAL;
-
- mutex_lock(&dev->lock);
- radio_bits_get(dev);
-
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = FREQ_LO;
- v->rangehigh = FREQ_HI;
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
- v->capability = V4L2_TUNER_CAP_LOW;
- if (dev->stereo)
- v->audmode = V4L2_TUNER_MODE_STEREO;
- else
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = dev->tuned;
- mutex_unlock(&dev->lock);
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- return v->index ? -EINVAL : 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct maestro *dev = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
- return -EINVAL;
- mutex_lock(&dev->lock);
- radio_bits_set(dev, FREQ2BITS(f->frequency));
- mutex_unlock(&dev->lock);
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct maestro *dev = video_drvdata(file);
-
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- mutex_lock(&dev->lock);
- f->frequency = BITS2FREQ(radio_bits_get(dev));
- mutex_unlock(&dev->lock);
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct maestro *dev = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = dev->muted;
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct maestro *dev = video_drvdata(file);
- u16 io = dev->io;
- u16 omask;
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- mutex_lock(&dev->lock);
- omask = inw(io + IO_MASK);
- outw(~STR_WREN, io + IO_MASK);
- dev->muted = ctrl->value;
- outw(dev->muted ? STR_WREN : 0, io);
- udelay(4);
- outw(omask, io + IO_MASK);
- msleep(125);
- mutex_unlock(&dev->lock);
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
-}
-
-static const struct v4l2_file_operations maestro_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops maestro_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
-};
-
-static u16 __devinit radio_power_on(struct maestro *dev)
-{
- register u16 io = dev->io;
- register u32 ofreq;
- u16 omask, odir;
-
- omask = inw(io + IO_MASK);
- odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN);
- outw(odir & ~STR_WREN, io + IO_DIR);
- dev->muted = inw(io) & STR_WREN ? 0 : 1;
- outw(odir, io + IO_DIR);
- outw(~(STR_WREN | STR_CLK), io + IO_MASK);
- outw(dev->muted ? 0 : STR_WREN, io);
- udelay(16);
- outw(omask, io + IO_MASK);
- ofreq = radio_bits_get(dev);
-
- if ((ofreq < FREQ2BITS(FREQ_LO)) || (ofreq > FREQ2BITS(FREQ_HI)))
- ofreq = FREQ2BITS(FREQ_LO);
- radio_bits_set(dev, ofreq);
-
- return (ofreq == radio_bits_get(dev));
-}
-
-static int __devinit maestro_probe(struct pci_dev *pdev,
- const struct pci_device_id *ent)
-{
- struct maestro *dev;
- struct v4l2_device *v4l2_dev;
- int retval;
-
- retval = pci_enable_device(pdev);
- if (retval) {
- dev_err(&pdev->dev, "enabling pci device failed!\n");
- goto err;
- }
-
- retval = -ENOMEM;
-
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
- dev_err(&pdev->dev, "not enough memory\n");
- goto err;
- }
-
- v4l2_dev = &dev->v4l2_dev;
- mutex_init(&dev->lock);
- dev->pdev = pdev;
-
- strlcpy(v4l2_dev->name, "maestro", sizeof(v4l2_dev->name));
-
- retval = v4l2_device_register(&pdev->dev, v4l2_dev);
- if (retval < 0) {
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- goto errfr;
- }
-
- dev->io = pci_resource_start(pdev, 0) + GPIO_DATA;
-
- strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
- dev->vdev.v4l2_dev = v4l2_dev;
- dev->vdev.fops = &maestro_fops;
- dev->vdev.ioctl_ops = &maestro_ioctl_ops;
- dev->vdev.release = video_device_release_empty;
- video_set_drvdata(&dev->vdev, dev);
-
- if (!radio_power_on(dev)) {
- retval = -EIO;
- goto errfr1;
- }
-
- retval = video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr);
- if (retval) {
- v4l2_err(v4l2_dev, "can't register video device!\n");
- goto errfr1;
- }
-
- v4l2_info(v4l2_dev, "version " DRIVER_VERSION "\n");
-
- return 0;
-errfr1:
- v4l2_device_unregister(v4l2_dev);
-errfr:
- kfree(dev);
-err:
- return retval;
-
-}
-
-static void __devexit maestro_remove(struct pci_dev *pdev)
-{
- struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
- struct maestro *dev = to_maestro(v4l2_dev);
-
- video_unregister_device(&dev->vdev);
- v4l2_device_unregister(&dev->v4l2_dev);
-}
-
-static struct pci_device_id maestro_r_pci_tbl[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ESS1968),
- .class = PCI_CLASS_MULTIMEDIA_AUDIO << 8,
- .class_mask = 0xffff00 },
- { PCI_DEVICE(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ESS1978),
- .class = PCI_CLASS_MULTIMEDIA_AUDIO << 8,
- .class_mask = 0xffff00 },
- { 0 }
-};
-MODULE_DEVICE_TABLE(pci, maestro_r_pci_tbl);
-
-static struct pci_driver maestro_r_driver = {
- .name = "maestro_radio",
- .id_table = maestro_r_pci_tbl,
- .probe = maestro_probe,
- .remove = __devexit_p(maestro_remove),
-};
-
-static int __init maestro_radio_init(void)
-{
- int retval = pci_register_driver(&maestro_r_driver);
-
- if (retval)
- printk(KERN_ERR "error during registration pci driver\n");
-
- return retval;
-}
-
-static void __exit maestro_radio_exit(void)
-{
- pci_unregister_driver(&maestro_r_driver);
-}
-
-module_init(maestro_radio_init);
-module_exit(maestro_radio_exit);
return 0;
}
-/* !!! not tested, in my card this does't work !!! */
+/* !!! not tested, in my card this doesn't work !!! */
static int fmr2_setvolume(struct fmr2 *dev)
{
int vol[16] = { 0x021, 0x084, 0x090, 0x104,
v4l_info(client, "chip found @ 0x%02x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kmalloc(sizeof(struct saa7706h_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct saa7706h_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
v4l_info(client, "chip found @ 0x%02x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kmalloc(sizeof(struct tef6862_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tef6862_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
state->freq = TEF6862_LO_FREQ;
#define MOD_AUTHOR "Jarod Wilson <jarod@wilsonet.com>"
#define MOD_DESC "Driver for SoundGraph iMON MultiMedia IR/Display"
#define MOD_NAME "imon"
-#define MOD_VERSION "0.9.2"
+#define MOD_VERSION "0.9.3"
#define DISPLAY_MINOR_BASE 144
#define DEVICE_NAME "lcd%d"
}
/**
- * Sends a packet to the device -- this function must be called
- * with ictx->lock held.
+ * Sends a packet to the device -- this function must be called with
+ * ictx->lock held, or its unlock/lock sequence while waiting for tx
+ * to complete can/will lead to a deadlock.
*/
static int send_packet(struct imon_context *ictx)
{
* the iMON remotes, and those used by the Windows MCE remotes (which is
* really just RC-6), but only one or the other at a time, as the signals
* are decoded onboard the receiver.
+ *
+ * This function gets called two different ways, one way is from
+ * rc_register_device, for initial protocol selection/setup, and the other is
+ * via a userspace-initiated protocol change request, either by direct sysfs
+ * prodding or by something like ir-keytable. In the rc_register_device case,
+ * the imon context lock is already held, but when initiated from userspace,
+ * it is not, so we must acquire it prior to calling send_packet, which
+ * requires that the lock is held.
*/
static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
{
int retval;
struct imon_context *ictx = rc->priv;
struct device *dev = ictx->dev;
+ bool unlock = false;
unsigned char ir_proto_packet[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 };
memcpy(ictx->usb_tx_buf, &ir_proto_packet, sizeof(ir_proto_packet));
+ if (!mutex_is_locked(&ictx->lock)) {
+ unlock = true;
+ mutex_lock(&ictx->lock);
+ }
+
retval = send_packet(ictx);
if (retval)
goto out;
ictx->pad_mouse = false;
out:
+ if (unlock)
+ mutex_unlock(&ictx->lock);
+
return retval;
}
goto rdev_setup_failed;
}
+ mutex_unlock(&ictx->lock);
return ictx;
rdev_setup_failed:
goto urb_submit_failed;
}
+ mutex_unlock(&ictx->lock);
return ictx;
urb_submit_failed:
usb_set_intfdata(interface, ictx);
if (ifnum == 0) {
+ mutex_lock(&ictx->lock);
+
if (product == 0xffdc && ictx->rf_device) {
sysfs_err = sysfs_create_group(&interface->dev.kobj,
&imon_rf_attr_group);
if (ictx->display_supported)
imon_init_display(ictx, interface);
+
+ mutex_unlock(&ictx->lock);
}
dev_info(dev, "iMON device (%04x:%04x, intf%d) on "
"usb<%d:%d> initialized\n", vendor, product, ifnum,
usbdev->bus->busnum, usbdev->devnum);
- mutex_unlock(&ictx->lock);
mutex_unlock(&driver_lock);
return 0;
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
+#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/bitops.h>
{ USB_DEVICE(VENDOR_PHILIPS, 0x206c) },
/* Philips/Spinel plus IR transceiver for ASUS */
{ USB_DEVICE(VENDOR_PHILIPS, 0x2088) },
+ /* Philips IR transceiver (Dell branded) */
+ { USB_DEVICE(VENDOR_PHILIPS, 0x2093) },
/* Realtek MCE IR Receiver and card reader */
{ USB_DEVICE(VENDOR_REALTEK, 0x0161),
.driver_info = MULTIFUNCTION },
{
struct rc_dev *rdev = input_get_drvdata(idev);
- rdev->close(rdev);
+ if (rdev)
+ rdev->close(rdev);
}
/* class for /sys/class/rc */
{ RC_TYPE_SONY, "sony" },
{ RC_TYPE_RC5_SZ, "rc-5-sz" },
{ RC_TYPE_LIRC, "lirc" },
+ { RC_TYPE_OTHER, "other" },
};
#define PROTO_NONE "none"
config VIDEO_MX3
tristate "i.MX3x Camera Sensor Interface driver"
depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
- select VIDEOBUF_DMA_CONTIG
+ select VIDEOBUF2_DMA_CONTIG
select MX3_VIDEO
---help---
This is a v4l2 driver for the i.MX3x Camera Sensor Interface
/* No struct video_device, but can have buffers allocated */
if (type == CX18_ENC_STREAM_TYPE_IDX) {
+ /* If the module params didn't inhibit IDX ... */
if (cx->stream_buffers[type] != 0) {
cx->stream_buffers[type] = 0;
- cx18_stream_free(&cx->streams[type]);
+ /*
+ * Before calling cx18_stream_free(),
+ * check if the IDX stream was actually set up.
+ * Needed, since the cx18_probe() error path
+ * exits through here as well as normal clean up
+ */
+ if (cx->streams[type].buffers != 0)
+ cx18_stream_free(&cx->streams[type]);
}
continue;
}
select DVB_CX24116 if !DVB_FE_CUSTOMISE
select DVB_STV0900 if !DVB_FE_CUSTOMISE
select DVB_DS3000 if !DVB_FE_CUSTOMISE
+ select DVB_STV0367 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
static int imx074_set_bus_param(struct soc_camera_device *icd,
unsigned long flags)
{
- return -1;
+ return -EINVAL;
}
static struct soc_camera_ops imx074_ops = {
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kmalloc(sizeof(struct m52790_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct m52790_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
}
switch (xclksel) {
- case 0:
+ case ISP_XCLK_A:
isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
ISPTCTRL_CTRL_DIVA_MASK,
divisor << ISPTCTRL_CTRL_DIVA_SHIFT);
dev_dbg(isp->dev, "isp_set_xclk(): cam_xclka set to %d Hz\n",
currentxclk);
break;
- case 1:
+ case ISP_XCLK_B:
isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
ISPTCTRL_CTRL_DIVB_MASK,
divisor << ISPTCTRL_CTRL_DIVB_SHIFT);
dev_dbg(isp->dev, "isp_set_xclk(): cam_xclkb set to %d Hz\n",
currentxclk);
break;
+ case ISP_XCLK_NONE:
default:
omap3isp_put(isp);
dev_dbg(isp->dev, "ISP_ERR: isp_set_xclk(): Invalid requested "
}
/* Do we go from stable whatever to clock? */
- if (divisor >= 2 && isp->xclk_divisor[xclksel] < 2)
+ if (divisor >= 2 && isp->xclk_divisor[xclksel - 1] < 2)
omap3isp_get(isp);
/* Stopping the clock. */
- else if (divisor < 2 && isp->xclk_divisor[xclksel] >= 2)
+ else if (divisor < 2 && isp->xclk_divisor[xclksel - 1] >= 2)
omap3isp_put(isp);
- isp->xclk_divisor[xclksel] = divisor;
+ isp->xclk_divisor[xclksel - 1] = divisor;
omap3isp_put(isp);
*/
void omap3isp_configure_bridge(struct isp_device *isp,
enum ccdc_input_entity input,
- const struct isp_parallel_platform_data *pdata)
+ const struct isp_parallel_platform_data *pdata,
+ unsigned int shift)
{
u32 ispctrl_val;
switch (input) {
case CCDC_INPUT_PARALLEL:
ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL;
- ispctrl_val |= pdata->data_lane_shift << ISPCTRL_SHIFT_SHIFT;
ispctrl_val |= pdata->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT;
ispctrl_val |= pdata->bridge << ISPCTRL_PAR_BRIDGE_SHIFT;
+ shift += pdata->data_lane_shift * 2;
break;
case CCDC_INPUT_CSI2A:
return;
}
+ ispctrl_val |= ((shift/2) << ISPCTRL_SHIFT_SHIFT) & ISPCTRL_SHIFT_MASK;
+
ispctrl_val &= ~ISPCTRL_SYNC_DETECT_MASK;
ispctrl_val |= ISPCTRL_SYNC_DETECT_VSRISE;
/* Apply power change to connected non-nodes. */
ret = isp_pipeline_pm_power(entity, change);
+ if (ret < 0)
+ entity->use_count -= change;
mutex_unlock(&entity->parent->graph_mutex);
}
}
+ if (failure < 0)
+ isp->needs_reset = true;
+
return failure;
}
* single-shot or continuous mode.
*
* Return 0 if successful, or the return value of the failed video::s_stream
- * operation otherwise.
+ * operation otherwise. The pipeline state is not updated when the operation
+ * fails, except when stopping the pipeline.
*/
int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,
enum isp_pipeline_stream_state state)
ret = isp_pipeline_disable(pipe);
else
ret = isp_pipeline_enable(pipe, state);
- pipe->stream_state = state;
+
+ if (ret == 0 || state == ISP_PIPELINE_STREAM_STOPPED)
+ pipe->stream_state = state;
return ret;
}
if (--isp->ref_count == 0) {
isp_disable_interrupts(isp);
isp_save_ctx(isp);
+ if (isp->needs_reset) {
+ isp_reset(isp);
+ isp->needs_reset = false;
+ }
isp_disable_clocks(isp);
}
mutex_unlock(&isp->isp_mutex);
/**
* struct isp_parallel_platform_data - Parallel interface platform data
- * @width: Parallel bus width in bits (8, 10, 11 or 12)
* @data_lane_shift: Data lane shifter
* 0 - CAMEXT[13:0] -> CAM[13:0]
* 1 - CAMEXT[13:2] -> CAM[11:0]
* ISPCTRL_PAR_BRIDGE_BENDIAN - Big endian
*/
struct isp_parallel_platform_data {
- unsigned int width;
unsigned int data_lane_shift:2;
unsigned int clk_pol:1;
unsigned int bridge:4;
/* ISP Obj */
spinlock_t stat_lock; /* common lock for statistic drivers */
struct mutex isp_mutex; /* For handling ref_count field */
+ bool needs_reset;
int has_context;
int ref_count;
unsigned int autoidle;
enum isp_pipeline_stream_state state);
void omap3isp_configure_bridge(struct isp_device *isp,
enum ccdc_input_entity input,
- const struct isp_parallel_platform_data *pdata);
+ const struct isp_parallel_platform_data *pdata,
+ unsigned int shift);
-#define ISP_XCLK_NONE -1
-#define ISP_XCLK_A 0
-#define ISP_XCLK_B 1
+#define ISP_XCLK_NONE 0
+#define ISP_XCLK_A 1
+#define ISP_XCLK_B 2
struct isp_device *omap3isp_get(struct isp_device *isp);
void omap3isp_put(struct isp_device *isp);
static const unsigned int ccdc_fmts[] = {
V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_MBUS_FMT_Y10_1X10,
+ V4L2_MBUS_FMT_Y12_1X12,
+ V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_MBUS_FMT_SGBRG8_1X8,
V4L2_MBUS_FMT_SGRBG10_1X10,
V4L2_MBUS_FMT_SRGGB10_1X10,
V4L2_MBUS_FMT_SBGGR10_1X10,
struct isp_parallel_platform_data *pdata = NULL;
struct v4l2_subdev *sensor;
struct v4l2_mbus_framefmt *format;
+ const struct isp_format_info *fmt_info;
+ struct v4l2_subdev_format fmt_src;
+ unsigned int depth_out;
+ unsigned int depth_in = 0;
struct media_pad *pad;
unsigned long flags;
+ unsigned int shift;
u32 syn_mode;
u32 ccdc_pattern;
- if (ccdc->input == CCDC_INPUT_PARALLEL) {
- pad = media_entity_remote_source(&ccdc->pads[CCDC_PAD_SINK]);
- sensor = media_entity_to_v4l2_subdev(pad->entity);
+ pad = media_entity_remote_source(&ccdc->pads[CCDC_PAD_SINK]);
+ sensor = media_entity_to_v4l2_subdev(pad->entity);
+ if (ccdc->input == CCDC_INPUT_PARALLEL)
pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv)
->bus.parallel;
+
+ /* Compute shift value for lane shifter to configure the bridge. */
+ fmt_src.pad = pad->index;
+ fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ if (!v4l2_subdev_call(sensor, pad, get_fmt, NULL, &fmt_src)) {
+ fmt_info = omap3isp_video_format_info(fmt_src.format.code);
+ depth_in = fmt_info->bpp;
}
- omap3isp_configure_bridge(isp, ccdc->input, pdata);
+ fmt_info = omap3isp_video_format_info
+ (isp->isp_ccdc.formats[CCDC_PAD_SINK].code);
+ depth_out = fmt_info->bpp;
+
+ shift = depth_in - depth_out;
+ omap3isp_configure_bridge(isp, ccdc->input, pdata, shift);
- ccdc->syncif.datsz = pdata ? pdata->width : 10;
+ ccdc->syncif.datsz = depth_out;
ccdc_config_sync_if(ccdc, &ccdc->syncif);
/* CCDC_PAD_SINK */
* @ccdc: Pointer to ISP CCDC device.
* @event: Pointing which event trigger handler
*
- * Return 1 when the event and stopping request combination is satisfyied,
+ * Return 1 when the event and stopping request combination is satisfied,
* zero otherwise.
*/
static int __ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event)
ccdc_set_outaddr(ccdc, buffer->isp_addr);
- /* We now have a buffer queued on the output, restart the pipeline in
+ /* We now have a buffer queued on the output, restart the pipeline
* on the next CCDC interrupt if running in continuous mode (or when
* starting the stream).
*/
* @configs - pointer to update config structure.
* @config - return pointer to appropriate structure field.
* @bit - for which feature to return pointers.
- * Return size of coresponding prev_params member
+ * Return size of corresponding prev_params member
*/
static u32
__preview_get_ptrs(struct prev_params *params, void **param,
up_read(¤t->mm->mmap_sem);
if (ret != buf->npages) {
- buf->npages = ret;
+ buf->npages = ret < 0 ? 0 : ret;
isp_video_buffer_cleanup(buf);
return -EFAULT;
}
* isp_video_buffer_prepare_vm_flags - Get VMA flags for a userspace address
*
* This function locates the VMAs for the buffer's userspace address and checks
- * that their flags match. The onlflag that we need to care for at the moment is
- * VM_PFNMAP.
+ * that their flags match. The only flag that we need to care for at the moment
+ * is VM_PFNMAP.
*
* The buffer vm_flags field is set to the first VMA flags.
*
* iw and ih are the input width and height after cropping. Those equations need
* to be satisfied exactly for the resizer to work correctly.
*
- * Reverting the equations, we can compute the resizing ratios with
+ * The equations can't be easily reverted, as the >> 8 operation is not linear.
+ * In addition, not all input sizes can be achieved for a given output size. To
+ * get the highest input size lower than or equal to the requested input size,
+ * we need to compute the highest resizing ratio that satisfies the following
+ * inequality (taking the 4-tap mode width equation as an example)
+ *
+ * iw >= (32 * sph + (ow - 1) * hrsz + 16) >> 8 - 7
+ *
+ * (where iw is the requested input width) which can be rewritten as
+ *
+ * iw - 7 >= (32 * sph + (ow - 1) * hrsz + 16) >> 8
+ * (iw - 7) << 8 >= 32 * sph + (ow - 1) * hrsz + 16 - b
+ * ((iw - 7) << 8) + b >= 32 * sph + (ow - 1) * hrsz + 16
+ *
+ * where b is the value of the 8 least significant bits of the right hand side
+ * expression of the last inequality. The highest resizing ratio value will be
+ * achieved when b is equal to its maximum value of 255. That resizing ratio
+ * value will still satisfy the original inequality, as b will disappear when
+ * the expression will be shifted right by 8.
+ *
+ * The reverted the equations thus become
*
* - 8-phase, 4-tap mode
- * hrsz = ((iw - 7) * 256 - 16 - 32 * sph) / (ow - 1)
- * vrsz = ((ih - 4) * 256 - 16 - 32 * spv) / (oh - 1)
+ * hrsz = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / (ow - 1)
+ * vrsz = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / (oh - 1)
* - 4-phase, 7-tap mode
- * hrsz = ((iw - 7) * 256 - 32 - 64 * sph) / (ow - 1)
- * vrsz = ((ih - 7) * 256 - 32 - 64 * spv) / (oh - 1)
+ * hrsz = ((iw - 7) * 256 + 255 - 32 - 64 * sph) / (ow - 1)
+ * vrsz = ((ih - 7) * 256 + 255 - 32 - 64 * spv) / (oh - 1)
*
- * The ratios are integer values, and must be rounded down to ensure that the
- * cropped input size is not bigger than the uncropped input size. As the ratio
- * in 7-tap mode is always smaller than the ratio in 4-tap mode, we can use the
- * 7-tap mode equations to compute a ratio approximation.
+ * The ratios are integer values, and are rounded down to ensure that the
+ * cropped input size is not bigger than the uncropped input size.
+ *
+ * As the number of phases/taps, used to select the correct equations to compute
+ * the ratio, depends on the ratio, we start with the 4-tap mode equations to
+ * compute an approximation of the ratio, and switch to the 7-tap mode equations
+ * if the approximation is higher than the ratio threshold.
+ *
+ * As the 7-tap mode equations will return a ratio smaller than or equal to the
+ * 4-tap mode equations, the resulting ratio could become lower than or equal to
+ * the ratio threshold. This 'equations loop' isn't an issue as long as the
+ * correct equations are used to compute the final input size. Starting with the
+ * 4-tap mode equations ensure that, in case of values resulting in a 'ratio
+ * loop', the smallest of the ratio values will be used, never exceeding the
+ * requested input size.
*
* We first clamp the output size according to the hardware capabilitie to avoid
* auto-cropping the input more than required to satisfy the TRM equations. The
unsigned int max_width;
unsigned int max_height;
unsigned int width_alignment;
+ unsigned int width;
+ unsigned int height;
/*
* Clamp the output height based on the hardware capabilities and
max_height = min_t(unsigned int, max_height, MAX_OUT_HEIGHT);
output->height = clamp(output->height, min_height, max_height);
- ratio->vert = ((input->height - 7) * 256 - 32 - 64 * spv)
+ ratio->vert = ((input->height - 4) * 256 + 255 - 16 - 32 * spv)
/ (output->height - 1);
+ if (ratio->vert > MID_RESIZE_VALUE)
+ ratio->vert = ((input->height - 7) * 256 + 255 - 32 - 64 * spv)
+ / (output->height - 1);
ratio->vert = clamp_t(unsigned int, ratio->vert,
MIN_RESIZE_VALUE, MAX_RESIZE_VALUE);
if (ratio->vert <= MID_RESIZE_VALUE) {
upscaled_height = (output->height - 1) * ratio->vert
+ 32 * spv + 16;
- input->height = (upscaled_height >> 8) + 4;
+ height = (upscaled_height >> 8) + 4;
} else {
upscaled_height = (output->height - 1) * ratio->vert
+ 64 * spv + 32;
- input->height = (upscaled_height >> 8) + 7;
+ height = (upscaled_height >> 8) + 7;
}
/*
max_width & ~(width_alignment - 1));
output->width = ALIGN(output->width, width_alignment);
- ratio->horz = ((input->width - 7) * 256 - 32 - 64 * sph)
+ ratio->horz = ((input->width - 7) * 256 + 255 - 16 - 32 * sph)
/ (output->width - 1);
+ if (ratio->horz > MID_RESIZE_VALUE)
+ ratio->horz = ((input->width - 7) * 256 + 255 - 32 - 64 * sph)
+ / (output->width - 1);
ratio->horz = clamp_t(unsigned int, ratio->horz,
MIN_RESIZE_VALUE, MAX_RESIZE_VALUE);
if (ratio->horz <= MID_RESIZE_VALUE) {
upscaled_width = (output->width - 1) * ratio->horz
+ 32 * sph + 16;
- input->width = (upscaled_width >> 8) + 7;
+ width = (upscaled_width >> 8) + 7;
} else {
upscaled_width = (output->width - 1) * ratio->horz
+ 64 * sph + 32;
- input->width = (upscaled_width >> 8) + 7;
+ width = (upscaled_width >> 8) + 7;
}
+
+ /* Center the new crop rectangle. */
+ input->left += (input->width - width) / 2;
+ input->top += (input->height - height) / 2;
+ input->width = width;
+ input->height = height;
}
/*
struct ispstat_generic_config {
/*
* Fields must be in the same order as in:
- * - isph3a_aewb_config
- * - isph3a_af_config
- * - isphist_config
+ * - omap3isp_h3a_aewb_config
+ * - omap3isp_h3a_af_config
+ * - omap3isp_hist_config
*/
u32 buf_size;
u16 config_counter;
static struct isp_format_info formats[] = {
{ V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
- V4L2_MBUS_FMT_Y8_1X8, V4L2_PIX_FMT_GREY, 8, },
+ V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_PIX_FMT_GREY, 8, },
+ { V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10,
+ V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_PIX_FMT_Y10, 10, },
+ { V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y10_1X10,
+ V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_PIX_FMT_Y12, 12, },
+ { V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_PIX_FMT_SBGGR8, 8, },
+ { V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_PIX_FMT_SGBRG8, 8, },
+ { V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_PIX_FMT_SGRBG8, 8, },
+ { V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_PIX_FMT_SRGGB8, 8, },
{ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
- V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10DPCM8, 8, },
+ V4L2_MBUS_FMT_SGRBG10_1X10, 0,
+ V4L2_PIX_FMT_SGRBG10DPCM8, 8, },
{ V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10,
- V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10, 10, },
+ V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_PIX_FMT_SBGGR10, 10, },
{ V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10,
- V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10, 10, },
+ V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_PIX_FMT_SGBRG10, 10, },
{ V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10,
- V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10, 10, },
+ V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_PIX_FMT_SGRBG10, 10, },
{ V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10,
- V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10, 10, },
+ V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_PIX_FMT_SRGGB10, 10, },
{ V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10,
- V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12, 12, },
+ V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_PIX_FMT_SBGGR12, 12, },
{ V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10,
- V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12, 12, },
+ V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_PIX_FMT_SGBRG12, 12, },
{ V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10,
- V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12, 12, },
+ V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_PIX_FMT_SGRBG12, 12, },
{ V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10,
- V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12, 12, },
+ V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_PIX_FMT_SRGGB12, 12, },
{ V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16,
- V4L2_MBUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 16, },
+ V4L2_MBUS_FMT_UYVY8_1X16, 0,
+ V4L2_PIX_FMT_UYVY, 16, },
{ V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16,
- V4L2_MBUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 16, },
+ V4L2_MBUS_FMT_YUYV8_1X16, 0,
+ V4L2_PIX_FMT_YUYV, 16, },
};
const struct isp_format_info *
return NULL;
}
+/*
+ * Decide whether desired output pixel code can be obtained with
+ * the lane shifter by shifting the input pixel code.
+ * @in: input pixelcode to shifter
+ * @out: output pixelcode from shifter
+ * @additional_shift: # of bits the sensor's LSB is offset from CAMEXT[0]
+ *
+ * return true if the combination is possible
+ * return false otherwise
+ */
+static bool isp_video_is_shiftable(enum v4l2_mbus_pixelcode in,
+ enum v4l2_mbus_pixelcode out,
+ unsigned int additional_shift)
+{
+ const struct isp_format_info *in_info, *out_info;
+
+ if (in == out)
+ return true;
+
+ in_info = omap3isp_video_format_info(in);
+ out_info = omap3isp_video_format_info(out);
+
+ if ((in_info->flavor == 0) || (out_info->flavor == 0))
+ return false;
+
+ if (in_info->flavor != out_info->flavor)
+ return false;
+
+ return in_info->bpp - out_info->bpp + additional_shift <= 6;
+}
+
/*
* isp_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format
* @video: ISP video instance
return -EPIPE;
while (1) {
+ unsigned int shifter_link;
/* Retrieve the sink format */
pad = &subdev->entity.pads[0];
if (!(pad->flags & MEDIA_PAD_FL_SINK))
return -ENOSPC;
}
+ /* If sink pad is on CCDC, the link has the lane shifter
+ * in the middle of it. */
+ shifter_link = subdev == &isp->isp_ccdc.subdev;
+
/* Retrieve the source format */
pad = media_entity_remote_source(pad);
if (pad == NULL ||
return -EPIPE;
/* Check if the two ends match */
- if (fmt_source.format.code != fmt_sink.format.code ||
- fmt_source.format.width != fmt_sink.format.width ||
+ if (fmt_source.format.width != fmt_sink.format.width ||
fmt_source.format.height != fmt_sink.format.height)
return -EPIPE;
+
+ if (shifter_link) {
+ unsigned int parallel_shift = 0;
+ if (isp->isp_ccdc.input == CCDC_INPUT_PARALLEL) {
+ struct isp_parallel_platform_data *pdata =
+ &((struct isp_v4l2_subdevs_group *)
+ subdev->host_priv)->bus.parallel;
+ parallel_shift = pdata->data_lane_shift * 2;
+ }
+ if (!isp_video_is_shiftable(fmt_source.format.code,
+ fmt_sink.format.code,
+ parallel_shift))
+ return -EPIPE;
+ } else if (fmt_source.format.code != fmt_sink.format.code)
+ return -EPIPE;
}
return 0;
* bits. Identical to @code if the format is 10 bits wide or less.
* @uncompressed: V4L2 media bus format code for the corresponding uncompressed
* format. Identical to @code if the format is not DPCM compressed.
+ * @flavor: V4L2 media bus format code for the same pixel layout but
+ * shifted to be 8 bits per pixel. =0 if format is not shiftable.
* @pixelformat: V4L2 pixel format FCC identifier
* @bpp: Bits per pixel
*/
enum v4l2_mbus_pixelcode code;
enum v4l2_mbus_pixelcode truncated;
enum v4l2_mbus_pixelcode uncompressed;
+ enum v4l2_mbus_pixelcode flavor;
u32 pixelformat;
unsigned int bpp;
};
if (ret)
return ret;
- if (vb2_is_streaming(&fimc->vid_cap.vbq) || fimc_capture_active(fimc))
+ if (vb2_is_busy(&fimc->vid_cap.vbq) || fimc_capture_active(fimc))
return -EBUSY;
frame = &ctx->d_frame;
return -EINVAL;
}
- for (i = 0; i < frame->fmt->colplanes; i++)
- frame->payload[i] = pix->plane_fmt[i].bytesperline * pix->height;
+ for (i = 0; i < frame->fmt->colplanes; i++) {
+ frame->payload[i] =
+ (pix->width * pix->height * frame->fmt->depth[i]) >> 3;
+ }
/* Output DMA frame pixel size and offsets. */
frame->f_width = pix->plane_fmt[0].bytesperline * 8
{
struct fimc_vid_cap *cap = &fimc->vid_cap;
struct fimc_vid_buffer *v_buf;
+ struct timeval *tv;
+ struct timespec ts;
if (!list_empty(&cap->active_buf_q) &&
test_bit(ST_CAPT_RUN, &fimc->state)) {
+ ktime_get_real_ts(&ts);
+
v_buf = active_queue_pop(cap);
+
+ tv = &v_buf->vb.v4l2_buf.timestamp;
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+ v_buf->vb.v4l2_buf.sequence = cap->frame_count++;
+
vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE);
}
mutex_unlock(&ctx->fimc_dev->lock);
}
-struct vb2_ops fimc_qops = {
+static struct vb2_ops fimc_qops = {
.queue_setup = fimc_queue_setup,
.buf_prepare = fimc_buf_prepare,
.buf_queue = fimc_buf_queue,
pix->num_planes = fmt->memplanes;
pix->colorspace = V4L2_COLORSPACE_JPEG;
- for (i = 0; i < pix->num_planes; ++i) {
- int bpl = pix->plane_fmt[i].bytesperline;
- dbg("[%d] bpl: %d, depth: %d, w: %d, h: %d",
- i, bpl, fmt->depth[i], pix->width, pix->height);
+ for (i = 0; i < pix->num_planes; ++i) {
+ u32 bpl = pix->plane_fmt[i].bytesperline;
+ u32 *sizeimage = &pix->plane_fmt[i].sizeimage;
- if (!bpl || (bpl * 8 / fmt->depth[i]) > pix->width)
- bpl = (pix->width * fmt->depth[0]) >> 3;
+ if (fmt->colplanes > 1 && (bpl == 0 || bpl < pix->width))
+ bpl = pix->width; /* Planar */
- if (!pix->plane_fmt[i].sizeimage)
- pix->plane_fmt[i].sizeimage = pix->height * bpl;
+ if (fmt->colplanes == 1 && /* Packed */
+ (bpl == 0 || ((bpl * 8) / fmt->depth[i]) < pix->width))
+ bpl = (pix->width * fmt->depth[0]) / 8;
- pix->plane_fmt[i].bytesperline = bpl;
+ if (i == 0) /* Same bytesperline for each plane. */
+ mod_x = bpl;
- dbg("[%d]: bpl: %d, sizeimage: %d",
- i, pix->plane_fmt[i].bytesperline,
- pix->plane_fmt[i].sizeimage);
+ pix->plane_fmt[i].bytesperline = mod_x;
+ *sizeimage = (pix->width * pix->height * fmt->depth[i]) / 8;
}
return 0;
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
- if (vb2_is_streaming(vq)) {
+ if (vb2_is_busy(vq)) {
v4l2_err(&fimc->m2m.v4l2_dev, "queue (%d) busy\n", f->type);
return -EBUSY;
}
if (!frame->fmt)
return -EINVAL;
- for (i = 0; i < frame->fmt->colplanes; i++)
- frame->payload[i] = pix->plane_fmt[i].bytesperline * pix->height;
+ for (i = 0; i < frame->fmt->colplanes; i++) {
+ frame->payload[i] =
+ (pix->width * pix->height * frame->fmt->depth[i]) / 8;
+ }
frame->f_width = pix->plane_fmt[0].bytesperline * 8 /
frame->fmt->depth[0];
}
/* Image pixel limits, similar across several FIMC HW revisions. */
-static struct fimc_pix_limit s5p_pix_limit[3] = {
+static struct fimc_pix_limit s5p_pix_limit[4] = {
[0] = {
.scaler_en_w = 3264,
.scaler_dis_w = 8192,
.out_rot_en_w = 1280,
.out_rot_dis_w = 1920,
},
+ [3] = {
+ .scaler_en_w = 1920,
+ .scaler_dis_w = 8192,
+ .in_rot_en_h = 1366,
+ .in_rot_dis_w = 8192,
+ .out_rot_en_w = 1366,
+ .out_rot_dis_w = 1920,
+ },
};
static struct samsung_fimc_variant fimc0_variant_s5p = {
.pix_limit = &s5p_pix_limit[2],
};
-static struct samsung_fimc_variant fimc0_variant_s5pv310 = {
+static struct samsung_fimc_variant fimc0_variant_exynos4 = {
.pix_hoff = 1,
.has_inp_rot = 1,
.has_out_rot = 1,
.pix_limit = &s5p_pix_limit[1],
};
-static struct samsung_fimc_variant fimc2_variant_s5pv310 = {
+static struct samsung_fimc_variant fimc2_variant_exynos4 = {
.pix_hoff = 1,
.has_cistatus2 = 1,
.has_mainscaler_ext = 1,
.min_out_pixsize = 16,
.hor_offs_align = 1,
.out_buf_count = 32,
- .pix_limit = &s5p_pix_limit[2],
+ .pix_limit = &s5p_pix_limit[3],
};
/* S5PC100 */
};
/* S5PV310, S5PC210 */
-static struct samsung_fimc_driverdata fimc_drvdata_s5pv310 = {
+static struct samsung_fimc_driverdata fimc_drvdata_exynos4 = {
.variant = {
- [0] = &fimc0_variant_s5pv310,
- [1] = &fimc0_variant_s5pv310,
- [2] = &fimc0_variant_s5pv310,
- [3] = &fimc2_variant_s5pv310,
+ [0] = &fimc0_variant_exynos4,
+ [1] = &fimc0_variant_exynos4,
+ [2] = &fimc0_variant_exynos4,
+ [3] = &fimc2_variant_exynos4,
},
.num_entities = 4,
.lclk_frequency = 166000000UL,
.name = "s5pv210-fimc",
.driver_data = (unsigned long)&fimc_drvdata_s5pv210,
}, {
- .name = "s5pv310-fimc",
- .driver_data = (unsigned long)&fimc_drvdata_s5pv310,
+ .name = "exynos4-fimc",
+ .driver_data = (unsigned long)&fimc_drvdata_exynos4,
},
{},
};
/* Try 2560x1920, 1280x960, 640x480, 320x240 */
mf.width = 2560 >> shift;
mf.height = 1920 >> shift;
- ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video,
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video,
s_mbus_fmt, &mf);
if (ret < 0)
return ret;
struct v4l2_cropcap cap;
int ret;
- ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video,
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video,
s_mbus_fmt, mf);
if (ret < 0)
return ret;
tmp_h = min(2 * tmp_h, max_height);
mf->width = tmp_w;
mf->height = tmp_h;
- ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video,
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video,
s_mbus_fmt, mf);
dev_geo(dev, "Camera scaled to %ux%u\n",
mf->width, mf->height);
mf.code = xlate->code;
mf.colorspace = pix->colorspace;
- ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, try_mbus_fmt, &mf);
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video, try_mbus_fmt, &mf);
if (ret < 0)
return ret;
*/
mf.width = 2560;
mf.height = 1920;
- ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video,
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video,
try_mbus_fmt, &mf);
if (ret < 0) {
/* Shouldn't actually happen... */
void __iomem *base;
struct platform_device *pdev;
struct sh_csi2_client_config *client;
+ unsigned long (*query_bus_param)(struct soc_camera_device *);
+ int (*set_bus_param)(struct soc_camera_device *, unsigned long);
};
static int sh_csi2_try_fmt(struct v4l2_subdev *sd,
case BUS_NOTIFY_BOUND_DRIVER:
snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s%s",
dev_name(v4l2_dev->dev), ".mipi-csi");
+ priv->subdev.grp_id = (long)icd;
ret = v4l2_device_register_subdev(v4l2_dev, &priv->subdev);
dev_dbg(dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret);
if (ret < 0)
priv->client = pdata->clients + i;
+ priv->set_bus_param = icd->ops->set_bus_param;
+ priv->query_bus_param = icd->ops->query_bus_param;
icd->ops->set_bus_param = sh_csi2_set_bus_param;
icd->ops->query_bus_param = sh_csi2_query_bus_param;
priv->client = NULL;
/* Driver is about to be unbound */
- icd->ops->set_bus_param = NULL;
- icd->ops->query_bus_param = NULL;
+ icd->ops->set_bus_param = priv->set_bus_param;
+ icd->ops->query_bus_param = priv->query_bus_param;
+ priv->set_bus_param = NULL;
+ priv->query_bus_param = NULL;
v4l2_device_unregister_subdev(&priv->subdev);
{
struct i2c_client *client =
to_i2c_client(to_soc_camera_control(icd));
+ struct i2c_adapter *adap = client->adapter;
dev_set_drvdata(&icd->dev, NULL);
v4l2_device_unregister_subdev(i2c_get_clientdata(client));
i2c_unregister_device(client);
- i2c_put_adapter(client->adapter);
+ i2c_put_adapter(adap);
}
#else
#define soc_camera_init_i2c(icd, icl) (-ENODEV)
}
}
+ sd = soc_camera_to_subdev(icd);
+ sd->grp_id = (long)icd;
+
/* At this point client .probe() should have run already */
ret = soc_camera_init_user_formats(icd);
if (ret < 0)
goto evidstart;
/* Try to improve our guess of a reasonable window format */
- sd = soc_camera_to_subdev(icd);
if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
icd->user_width = mf.width;
icd->user_height = mf.height;
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
if (sd == NULL)
return -ENOMEM;
v4l2_i2c_subdev_init(sd, client, &tda9840_ops);
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
if (sd == NULL)
return -ENOMEM;
v4l2_i2c_subdev_init(sd, client, &tea6415c_ops);
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
if (sd == NULL)
return -ENOMEM;
v4l2_i2c_subdev_init(sd, client, &tea6420_ops);
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kmalloc(sizeof(struct upd64031a_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct upd64031a_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kmalloc(sizeof(struct upd64083_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct upd64083_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
video_get(vdev);
mutex_unlock(&videodev_lock);
#if defined(CONFIG_MEDIA_CONTROLLER)
- if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) {
+ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
+ vdev->vfl_type != VFL_TYPE_SUBDEV) {
entity = media_entity_get(&vdev->entity);
if (!entity) {
ret = -EBUSY;
/* decrease the refcount in case of an error */
if (ret) {
#if defined(CONFIG_MEDIA_CONTROLLER)
- if (vdev->v4l2_dev && vdev->v4l2_dev->mdev)
+ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
+ vdev->vfl_type != VFL_TYPE_SUBDEV)
media_entity_put(entity);
#endif
video_put(vdev);
mutex_unlock(vdev->lock);
}
#if defined(CONFIG_MEDIA_CONTROLLER)
- if (vdev->v4l2_dev && vdev->v4l2_dev->mdev)
+ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
+ vdev->vfl_type != VFL_TYPE_SUBDEV)
media_entity_put(&vdev->entity);
#endif
/* decrease the refcount unconditionally since the release()
#if defined(CONFIG_MEDIA_CONTROLLER)
/* Part 5: Register the entity. */
- if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) {
+ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
+ vdev->vfl_type != VFL_TYPE_SUBDEV) {
vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
vdev->entity.name = vdev->name;
vdev->entity.v4l.major = VIDEO_MAJOR;
return;
#if defined(CONFIG_MEDIA_CONTROLLER)
- if (vdev->v4l2_dev && vdev->v4l2_dev->mdev)
+ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
+ vdev->vfl_type != VFL_TYPE_SUBDEV)
media_device_unregister_entity(&vdev->entity);
#endif
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
retval = remap_pfn_range(vma, vma->vm_start,
- PFN_DOWN(virt_to_phys(mem->vaddr)),
+ mem->dma_handle >> PAGE_SHIFT,
size, vma->vm_page_prot);
if (retval) {
dev_err(q->dev, "mmap: remap failed with error %d. ", retval);
#define call_qop(q, op, args...) \
(((q)->ops->op) ? ((q)->ops->op(args)) : 0)
+#define V4L2_BUFFER_STATE_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \
+ V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR)
+
/**
* __vb2_buf_mem_alloc() - allocate video memory for the given buffer
*/
for (plane = 0; plane < vb->num_planes; ++plane) {
mem_priv = call_memop(q, plane, alloc, q->alloc_ctx[plane],
plane_sizes[plane]);
- if (!mem_priv)
+ if (IS_ERR_OR_NULL(mem_priv))
goto free;
/* Associate allocator private data with this plane */
struct vb2_queue *q = vb->vb2_queue;
int ret = 0;
- /* Copy back data such as timestamp, input, etc. */
+ /* Copy back data such as timestamp, flags, input, etc. */
memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
b->input = vb->v4l2_buf.input;
b->reserved = vb->v4l2_buf.reserved;
b->m.userptr = vb->v4l2_planes[0].m.userptr;
}
- b->flags = 0;
+ /*
+ * Clear any buffer state related flags.
+ */
+ b->flags &= ~V4L2_BUFFER_STATE_FLAGS;
switch (vb->state) {
case VB2_BUF_STATE_QUEUED:
num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
memset(plane_sizes, 0, sizeof(plane_sizes));
memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
+ q->memory = req->memory;
/*
* Ask the driver how many buffers and planes per buffer it requires.
ret = num_buffers;
}
- q->memory = req->memory;
-
/*
* Return the number of successfully allocated buffers
* to the userspace.
vb->v4l2_buf.field = b->field;
vb->v4l2_buf.timestamp = b->timestamp;
+ vb->v4l2_buf.input = b->input;
+ vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS;
return 0;
}
GFP_KERNEL);
if (!buf->vaddr) {
dev_err(conf->dev, "dma_alloc_coherent of size %ld failed\n",
- buf->size);
+ size);
kfree(buf);
return ERR_PTR(-ENOMEM);
}
}
EXPORT_SYMBOL(mfd_cell_disable);
+static int mfd_platform_add_cell(struct platform_device *pdev,
+ const struct mfd_cell *cell)
+{
+ if (!cell)
+ return 0;
+
+ pdev->mfd_cell = kmemdup(cell, sizeof(*cell), GFP_KERNEL);
+ if (!pdev->mfd_cell)
+ return -ENOMEM;
+
+ return 0;
+}
+
static int mfd_add_device(struct device *parent, int id,
const struct mfd_cell *cell,
struct resource *mem_base,
pdev->dev.parent = parent;
- ret = platform_device_add_data(pdev, cell, sizeof(*cell));
+ ret = mfd_platform_add_cell(pdev, cell);
if (ret)
goto fail_res;
return 0;
-/* platform_device_del(pdev); */
fail_res:
kfree(res);
fail_device:
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
#include <linux/gpio.h>
-#include <linux/regulator/consumer.h>
#include <plat/usb.h>
#define USBHS_DRIVER_NAME "usbhs-omap"
dev_dbg(dev, "starting TI HSUSB Controller\n");
if (!pdata) {
dev_dbg(dev, "missing platform_data\n");
- ret = -ENODEV;
- goto end_enable;
+ return -ENODEV;
}
spin_lock_irqsave(&omap->lock, flags);
end_count:
omap->count++;
- goto end_enable;
+ spin_unlock_irqrestore(&omap->lock, flags);
+ return 0;
err_tll:
if (pdata->ehci_data->phy_reset) {
clk_disable(omap->usbhost_fs_fck);
clk_disable(omap->usbhost_hs_fck);
clk_disable(omap->usbhost_ick);
-
-end_enable:
spin_unlock_irqrestore(&omap->lock, flags);
return ret;
}
static int gru_irq_count[GRU_CHIPLETS_PER_BLADE];
-static void gru_noop(unsigned int irq)
+static void gru_noop(struct irq_data *d)
{
}
static struct irq_chip gru_chip[GRU_CHIPLETS_PER_BLADE] = {
[0 ... GRU_CHIPLETS_PER_BLADE - 1] {
- .mask = gru_noop,
- .unmask = gru_noop,
- .ack = gru_noop
+ .irq_mask = gru_noop,
+ .irq_unmask = gru_noop,
+ .irq_ack = gru_noop
}
};
type = "SD-combo";
if (mmc_card_blockaddr(card))
type = "SDHC-combo";
+ break;
default:
type = "?";
break;
spin_unlock_irqrestore(&host->clk_lock, flags);
return;
}
- mutex_lock(&host->clk_gate_mutex);
+ mmc_claim_host(host);
spin_lock_irqsave(&host->clk_lock, flags);
if (!host->clk_requests) {
spin_unlock_irqrestore(&host->clk_lock, flags);
pr_debug("%s: gated MCI clock\n", mmc_hostname(host));
}
spin_unlock_irqrestore(&host->clk_lock, flags);
- mutex_unlock(&host->clk_gate_mutex);
+ mmc_release_host(host);
}
/*
{
unsigned long flags;
- mutex_lock(&host->clk_gate_mutex);
+ mmc_claim_host(host);
spin_lock_irqsave(&host->clk_lock, flags);
if (host->clk_gated) {
spin_unlock_irqrestore(&host->clk_lock, flags);
}
host->clk_requests++;
spin_unlock_irqrestore(&host->clk_lock, flags);
- mutex_unlock(&host->clk_gate_mutex);
+ mmc_release_host(host);
}
/**
host->clk_gated = false;
INIT_WORK(&host->clk_gate_work, mmc_host_clk_gate_work);
spin_lock_init(&host->clk_lock);
- mutex_init(&host->clk_gate_mutex);
}
/**
return IRQ_HANDLED;
}
- if (end_command)
+ if (end_command && host->cmd)
mmc_omap_cmd_done(host, host->cmd);
if (host->data != NULL) {
if (transfer_error)
host->ioaddr = pci_ioremap_bar(pdev, bar);
if (!host->ioaddr) {
dev_err(&pdev->dev, "failed to remap registers\n");
+ ret = -ENOMEM;
goto release;
}
host = (struct sdhci_host*)param;
+ /*
+ * If this tasklet gets rescheduled while running, it will
+ * be run again afterwards but without any active request.
+ */
+ if (!host->mrq)
+ return;
+
spin_lock_irqsave(&host->lock, flags);
del_timer(&host->timer);
* upon error conditions.
*/
if (!(host->flags & SDHCI_DEVICE_DEAD) &&
- (mrq->cmd->error ||
+ ((mrq->cmd && mrq->cmd->error) ||
(mrq->data && (mrq->data->error ||
(mrq->data->stop && mrq->data->stop->error))) ||
(host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) {
tmio_mmc_set_clock(host, ios->clock);
/* Power sequence - OFF -> UP -> ON */
- if (ios->power_mode == MMC_POWER_OFF || !ios->clock) {
+ if (ios->power_mode == MMC_POWER_UP) {
+ /* power up SD bus */
+ if (host->set_pwr)
+ host->set_pwr(host->pdev, 1);
+ } else if (ios->power_mode == MMC_POWER_OFF || !ios->clock) {
/* power down SD bus */
if (ios->power_mode == MMC_POWER_OFF && host->set_pwr)
host->set_pwr(host->pdev, 0);
tmio_mmc_clk_stop(host);
- } else if (ios->power_mode == MMC_POWER_UP) {
- /* power up SD bus */
- if (host->set_pwr)
- host->set_pwr(host->pdev, 1);
} else {
/* start bus clock */
tmio_mmc_clk_start(host);
doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
- /* We can't' use dev_ready here, but at least we wait for the
+ /* We can't use dev_ready here, but at least we wait for the
* command to complete
*/
udelay(50);
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, amd8111e_pci_tbl);
module_param_array(speed_duplex, int, NULL, 0);
-MODULE_PARM_DESC(speed_duplex, "Set device speed and duplex modes, 0: Auto Negotitate, 1: 10Mbps Half Duplex, 2: 10Mbps Full Duplex, 3: 100Mbps Half Duplex, 4: 100Mbps Full Duplex");
+MODULE_PARM_DESC(speed_duplex, "Set device speed and duplex modes, 0: Auto Negotiate, 1: 10Mbps Half Duplex, 2: 10Mbps Full Duplex, 3: 100Mbps Half Duplex, 4: 100Mbps Full Duplex");
module_param_array(coalesce, bool, NULL, 0);
MODULE_PARM_DESC(coalesce, "Enable or Disable interrupt coalescing, 1: Enable, 0: Disable");
module_param_array(dynamic_ipg, bool, NULL, 0);
#define __AT_TESTING 0x0001
#define __AT_RESETTING 0x0002
#define __AT_DOWN 0x0003
- u8 work_event;
-#define ATL1C_WORK_EVENT_RESET 0x01
-#define ATL1C_WORK_EVENT_LINK_CHANGE 0x02
+ unsigned long work_event;
+#define ATL1C_WORK_EVENT_RESET 0
+#define ATL1C_WORK_EVENT_LINK_CHANGE 1
u32 msg_enable;
bool have_msi;
}
}
- adapter->work_event |= ATL1C_WORK_EVENT_LINK_CHANGE;
+ set_bit(ATL1C_WORK_EVENT_LINK_CHANGE, &adapter->work_event);
schedule_work(&adapter->common_task);
}
adapter = container_of(work, struct atl1c_adapter, common_task);
netdev = adapter->netdev;
- if (adapter->work_event & ATL1C_WORK_EVENT_RESET) {
- adapter->work_event &= ~ATL1C_WORK_EVENT_RESET;
+ if (test_and_clear_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event)) {
netif_device_detach(netdev);
atl1c_down(adapter);
atl1c_up(adapter);
netif_device_attach(netdev);
- return;
}
- if (adapter->work_event & ATL1C_WORK_EVENT_LINK_CHANGE) {
- adapter->work_event &= ~ATL1C_WORK_EVENT_LINK_CHANGE;
+ if (test_and_clear_bit(ATL1C_WORK_EVENT_LINK_CHANGE,
+ &adapter->work_event))
atl1c_check_link_status(adapter);
- }
- return;
}
struct atl1c_adapter *adapter = netdev_priv(netdev);
/* Do the reset outside of interrupt context */
- adapter->work_event |= ATL1C_WORK_EVENT_RESET;
+ set_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event);
schedule_work(&adapter->common_task);
}
be_detect_dump_ue(adapter);
reschedule:
+ adapter->work_counter++;
schedule_delayed_work(&adapter->work, msecs_to_jiffies(1000));
}
#define bfa_ioc_map_port(__ioc) ((__ioc)->ioc_hwif->ioc_map_port(__ioc))
#define bfa_ioc_notify_fail(__ioc) \
((__ioc)->ioc_hwif->ioc_notify_fail(__ioc))
+#define bfa_ioc_sync_start(__ioc) \
+ ((__ioc)->ioc_hwif->ioc_sync_start(__ioc))
#define bfa_ioc_sync_join(__ioc) \
((__ioc)->ioc_hwif->ioc_sync_join(__ioc))
#define bfa_ioc_sync_leave(__ioc) \
switch (event) {
case IOCPF_E_SEMLOCKED:
if (bfa_ioc_firmware_lock(ioc)) {
- if (bfa_ioc_sync_complete(ioc)) {
+ if (bfa_ioc_sync_start(ioc)) {
iocpf->retry_count = 0;
bfa_ioc_sync_join(ioc);
bfa_fsm_set_state(iocpf, bfa_iocpf_sm_hwinit);
* execution context (driver/bios) must match.
*/
static bool
-bfa_ioc_fwver_valid(struct bfa_ioc *ioc)
+bfa_ioc_fwver_valid(struct bfa_ioc *ioc, u32 boot_env)
{
struct bfi_ioc_image_hdr fwhdr, *drv_fwhdr;
if (fwhdr.signature != drv_fwhdr->signature)
return false;
- if (fwhdr.exec != drv_fwhdr->exec)
+ if (swab32(fwhdr.param) != boot_env)
return false;
return bfa_nw_ioc_fwver_cmp(ioc, &fwhdr);
{
enum bfi_ioc_state ioc_fwstate;
bool fwvalid;
+ u32 boot_env;
ioc_fwstate = readl(ioc->ioc_regs.ioc_fwstate);
+ boot_env = BFI_BOOT_LOADER_OS;
+
if (force)
ioc_fwstate = BFI_IOC_UNINIT;
* check if firmware is valid
*/
fwvalid = (ioc_fwstate == BFI_IOC_UNINIT) ?
- false : bfa_ioc_fwver_valid(ioc);
+ false : bfa_ioc_fwver_valid(ioc, boot_env);
if (!fwvalid) {
- bfa_ioc_boot(ioc, BFI_BOOT_TYPE_NORMAL, ioc->pcidev.device_id);
+ bfa_ioc_boot(ioc, BFI_BOOT_TYPE_NORMAL, boot_env);
return;
}
/**
* Initialize the h/w for any other states.
*/
- bfa_ioc_boot(ioc, BFI_BOOT_TYPE_NORMAL, ioc->pcidev.device_id);
+ bfa_ioc_boot(ioc, BFI_BOOT_TYPE_NORMAL, boot_env);
}
void
*/
static void
bfa_ioc_download_fw(struct bfa_ioc *ioc, u32 boot_type,
- u32 boot_param)
+ u32 boot_env)
{
u32 *fwimg;
u32 pgnum, pgoff;
/*
* Set boot type and boot param at the end.
*/
- writel((swab32(swab32(boot_type))), ((ioc->ioc_regs.smem_page_start)
+ writel(boot_type, ((ioc->ioc_regs.smem_page_start)
+ (BFI_BOOT_TYPE_OFF)));
- writel((swab32(swab32(boot_param))), ((ioc->ioc_regs.smem_page_start)
- + (BFI_BOOT_PARAM_OFF)));
+ writel(boot_env, ((ioc->ioc_regs.smem_page_start)
+ + (BFI_BOOT_LOADER_OFF)));
}
static void
* as the entry vector.
*/
static void
-bfa_ioc_boot(struct bfa_ioc *ioc, u32 boot_type, u32 boot_param)
+bfa_ioc_boot(struct bfa_ioc *ioc, u32 boot_type, u32 boot_env)
{
void __iomem *rb;
* Initialize IOC state of all functions on a chip reset.
*/
rb = ioc->pcidev.pci_bar_kva;
- if (boot_param == BFI_BOOT_TYPE_MEMTEST) {
+ if (boot_type == BFI_BOOT_TYPE_MEMTEST) {
writel(BFI_IOC_MEMTEST, (rb + BFA_IOC0_STATE_REG));
writel(BFI_IOC_MEMTEST, (rb + BFA_IOC1_STATE_REG));
} else {
}
bfa_ioc_msgflush(ioc);
- bfa_ioc_download_fw(ioc, boot_type, boot_param);
+ bfa_ioc_download_fw(ioc, boot_type, boot_env);
/**
* Enable interrupts just before starting LPU
bool msix);
void (*ioc_notify_fail) (struct bfa_ioc *ioc);
void (*ioc_ownership_reset) (struct bfa_ioc *ioc);
+ bool (*ioc_sync_start) (struct bfa_ioc *ioc);
void (*ioc_sync_join) (struct bfa_ioc *ioc);
void (*ioc_sync_leave) (struct bfa_ioc *ioc);
void (*ioc_sync_ack) (struct bfa_ioc *ioc);
static void bfa_ioc_ct_isr_mode_set(struct bfa_ioc *ioc, bool msix);
static void bfa_ioc_ct_notify_fail(struct bfa_ioc *ioc);
static void bfa_ioc_ct_ownership_reset(struct bfa_ioc *ioc);
+static bool bfa_ioc_ct_sync_start(struct bfa_ioc *ioc);
static void bfa_ioc_ct_sync_join(struct bfa_ioc *ioc);
static void bfa_ioc_ct_sync_leave(struct bfa_ioc *ioc);
static void bfa_ioc_ct_sync_ack(struct bfa_ioc *ioc);
nw_hwif_ct.ioc_isr_mode_set = bfa_ioc_ct_isr_mode_set;
nw_hwif_ct.ioc_notify_fail = bfa_ioc_ct_notify_fail;
nw_hwif_ct.ioc_ownership_reset = bfa_ioc_ct_ownership_reset;
+ nw_hwif_ct.ioc_sync_start = bfa_ioc_ct_sync_start;
nw_hwif_ct.ioc_sync_join = bfa_ioc_ct_sync_join;
nw_hwif_ct.ioc_sync_leave = bfa_ioc_ct_sync_leave;
nw_hwif_ct.ioc_sync_ack = bfa_ioc_ct_sync_ack;
bfa_nw_ioc_hw_sem_release(ioc);
}
+/**
+ * Synchronized IOC failure processing routines
+ */
+static bool
+bfa_ioc_ct_sync_start(struct bfa_ioc *ioc)
+{
+ u32 r32 = readl(ioc->ioc_regs.ioc_fail_sync);
+ u32 sync_reqd = bfa_ioc_ct_get_sync_reqd(r32);
+
+ /*
+ * Driver load time. If the sync required bit for this PCI fn
+ * is set, it is due to an unclean exit by the driver for this
+ * PCI fn in the previous incarnation. Whoever comes here first
+ * should clean it up, no matter which PCI fn.
+ */
+
+ if (sync_reqd & bfa_ioc_ct_sync_pos(ioc)) {
+ writel(0, ioc->ioc_regs.ioc_fail_sync);
+ writel(1, ioc->ioc_regs.ioc_usage_reg);
+ writel(BFI_IOC_UNINIT, ioc->ioc_regs.ioc_fwstate);
+ writel(BFI_IOC_UNINIT, ioc->ioc_regs.alt_ioc_fwstate);
+ return true;
+ }
+
+ return bfa_ioc_ct_sync_complete(ioc);
+}
/**
* Synchronized IOC failure processing routines
*/
#define BFI_IOC_MSGLEN_MAX 32 /* 32 bytes */
#define BFI_BOOT_TYPE_OFF 8
-#define BFI_BOOT_PARAM_OFF 12
+#define BFI_BOOT_LOADER_OFF 12
-#define BFI_BOOT_TYPE_NORMAL 0 /* param is device id */
+#define BFI_BOOT_TYPE_NORMAL 0
#define BFI_BOOT_TYPE_FLASH 1
#define BFI_BOOT_TYPE_MEMTEST 2
+#define BFI_BOOT_LOADER_OS 0
+
#define BFI_BOOT_MEMTEST_RES_ADDR 0x900
#define BFI_BOOT_MEMTEST_RES_SIG 0xA0A1A2A3
/* Initialize the Rx event handlers */
rx_cbfn.rcb_setup_cbfn = bnad_cb_rcb_setup;
rx_cbfn.rcb_destroy_cbfn = bnad_cb_rcb_destroy;
- rx_cbfn.rcb_destroy_cbfn = NULL;
rx_cbfn.ccb_setup_cbfn = bnad_cb_ccb_setup;
rx_cbfn.ccb_destroy_cbfn = bnad_cb_ccb_destroy;
rx_cbfn.rx_cleanup_cbfn = bnad_cb_rx_cleanup;
unregister_netdev(dev);
+ del_timer_sync(&bp->timer);
+
if (bp->mips_firmware)
release_firmware(bp->mips_firmware);
if (bp->rv2p_firmware)
static inline u8 bnx2x_set_pbd_csum_e2(struct bnx2x *bp, struct sk_buff *skb,
u32 *parsing_data, u32 xmit_type)
{
- *parsing_data |= ((tcp_hdrlen(skb)/4) <<
- ETH_TX_PARSE_BD_E2_TCP_HDR_LENGTH_DW_SHIFT) &
- ETH_TX_PARSE_BD_E2_TCP_HDR_LENGTH_DW;
+ *parsing_data |=
+ ((((u8 *)skb_transport_header(skb) - skb->data) >> 1) <<
+ ETH_TX_PARSE_BD_E2_TCP_HDR_START_OFFSET_W_SHIFT) &
+ ETH_TX_PARSE_BD_E2_TCP_HDR_START_OFFSET_W;
- *parsing_data |= ((((u8 *)tcp_hdr(skb) - skb->data) / 2) <<
- ETH_TX_PARSE_BD_E2_TCP_HDR_START_OFFSET_W_SHIFT) &
- ETH_TX_PARSE_BD_E2_TCP_HDR_START_OFFSET_W;
+ if (xmit_type & XMIT_CSUM_TCP) {
+ *parsing_data |= ((tcp_hdrlen(skb) / 4) <<
+ ETH_TX_PARSE_BD_E2_TCP_HDR_LENGTH_DW_SHIFT) &
+ ETH_TX_PARSE_BD_E2_TCP_HDR_LENGTH_DW;
- return skb_transport_header(skb) + tcp_hdrlen(skb) - skb->data;
+ return skb_transport_header(skb) + tcp_hdrlen(skb) - skb->data;
+ } else
+ /* We support checksum offload for TCP and UDP only.
+ * No need to pass the UDP header length - it's a constant.
+ */
+ return skb_transport_header(skb) +
+ sizeof(struct udphdr) - skb->data;
}
/**
struct eth_tx_parse_bd_e1x *pbd,
u32 xmit_type)
{
- u8 hlen = (skb_network_header(skb) - skb->data) / 2;
+ u8 hlen = (skb_network_header(skb) - skb->data) >> 1;
/* for now NS flag is not used in Linux */
pbd->global_data =
ETH_TX_PARSE_BD_E1X_LLC_SNAP_EN_SHIFT));
pbd->ip_hlen_w = (skb_transport_header(skb) -
- skb_network_header(skb)) / 2;
+ skb_network_header(skb)) >> 1;
- hlen += pbd->ip_hlen_w + tcp_hdrlen(skb) / 2;
+ hlen += pbd->ip_hlen_w;
+
+ /* We support checksum offload for TCP and UDP only */
+ if (xmit_type & XMIT_CSUM_TCP)
+ hlen += tcp_hdrlen(skb) / 2;
+ else
+ hlen += sizeof(struct udphdr) / 2;
pbd->total_hlen_w = cpu_to_le16(hlen);
hlen = hlen*2;
for (i = 0; i < (data * 2); i++) {
if ((i % 2) == 0)
bnx2x_set_led(&bp->link_params, &bp->link_vars,
- LED_MODE_OPER, SPEED_1000);
+ LED_MODE_ON, SPEED_1000);
else
bnx2x_set_led(&bp->link_params, &bp->link_vars,
- LED_MODE_OFF, 0);
+ LED_MODE_FRONT_PANEL_OFF, 0);
msleep_interruptible(500);
if (signal_pending(current))
break;
}
- if (bp->link_vars.link_up)
- bnx2x_set_led(&bp->link_params, &bp->link_vars, LED_MODE_OPER,
- bp->link_vars.line_speed);
+ bnx2x_set_led(&bp->link_params, &bp->link_vars,
+ LED_MODE_OPER, bp->link_vars.line_speed);
return 0;
}
static int agg_device_up(const struct aggregator *agg)
{
- return (netif_running(agg->slave->dev) &&
- netif_carrier_ok(agg->slave->dev));
+ struct port *port = agg->lag_ports;
+ if (!port)
+ return 0;
+ return (netif_running(port->slave->dev) &&
+ netif_carrier_ok(port->slave->dev));
}
/**
bond_info->tx_hashtbl = new_hashtbl;
for (i = 0; i < TLB_HASH_TABLE_SIZE; i++) {
- tlb_init_table_entry(&bond_info->tx_hashtbl[i], 1);
+ tlb_init_table_entry(&bond_info->tx_hashtbl[i], 0);
}
_unlock_tx_hashtbl(bond);
*/
rlb_choose_channel(skb, bond);
- /* The ARP relpy packets must be delayed so that
+ /* The ARP reply packets must be delayed so that
* they can cancel out the influence of the ARP request.
*/
bond->alb_info.rlb_update_delay_counter = RLB_UPDATE_DELAY;
*
* If the permanent hw address of @slave is @bond's hw address, we need to
* find a different hw address to give @slave, that isn't in use by any other
- * slave in the bond. This address must be, of course, one of the premanent
+ * slave in the bond. This address must be, of course, one of the permanent
* addresses of the other slaves.
*
* We go over the slave list, and for each slave there we compare its
* gave this entry index.
*/
u32 tx_bytes; /* Each Client accumulates the BytesTx that
- * were tranmitted to it, and after each
+ * were transmitted to it, and after each
* CallBack the LoadHistory is divided
* by the balance interval
*/
};
struct alb_bond_info {
- struct timer_list alb_timer;
struct tlb_client_info *tx_hashtbl; /* Dynamically allocated */
spinlock_t tx_hashtbl_lock;
u32 unbalanced_load;
struct slave *next_rx_slave;/* next slave to be assigned
* to a new rx client for
*/
- u32 rlb_interval_counter;
u8 primary_is_promisc; /* boolean */
u32 rlb_promisc_timeout_counter;/* counts primary
* promiscuity time
if (!ofdev->dev.of_match)
return -EINVAL;
- data = (struct mpc5xxx_can_data *)of_dev->dev.of_match->data;
+ data = (struct mpc5xxx_can_data *)ofdev->dev.of_match->data;
base = of_iomap(np, 0);
if (!base) {
if (dev->flags & IFF_UP) {
mutex_lock(&port->port_lock);
- port_napi_enable(port);
ret = ehea_restart_qps(dev);
- check_sqs(port);
- if (!ret)
+ if (!ret) {
+ check_sqs(port);
+ port_napi_enable(port);
netif_wake_queue(dev);
+ } else {
+ netdev_err(dev, "Unable to restart QPS\n");
+ }
mutex_unlock(&port->port_lock);
}
}
}
FC(fecp, r_cntrl, FEC_RCNTRL_PROM);
- FW(fecp, hash_table_high, fep->fec.hthi);
- FW(fecp, hash_table_low, fep->fec.htlo);
+ FW(fecp, grp_hash_table_high, fep->fec.hthi);
+ FW(fecp, grp_hash_table_low, fep->fec.htlo);
}
static void set_multicast_list(struct net_device *dev)
/*
* Reset all multicast.
*/
- FW(fecp, hash_table_high, fep->fec.hthi);
- FW(fecp, hash_table_low, fep->fec.htlo);
+ FW(fecp, grp_hash_table_high, fep->fec.hthi);
+ FW(fecp, grp_hash_table_low, fep->fec.htlo);
/*
* Set maximum receive buffer size.
* that hardware reset completed (what the f*ck).
* We still need to wait for a while.
*/
- usleep_range(500, 1000);
+ udelay(500);
return 0;
}
- usleep_range(1000, 10000);
+ udelay(1000);
}
netdev_err(netdev, "software reset failed\n");
if ((phycr & FTMAC100_PHYCR_MIIRD) == 0)
return phycr & FTMAC100_PHYCR_MIIRDATA;
- usleep_range(100, 1000);
+ udelay(100);
}
netdev_err(netdev, "mdio read timed out\n");
if ((phycr & FTMAC100_PHYCR_MIIWR) == 0)
return;
- usleep_range(100, 1000);
+ udelay(100);
}
netdev_err(netdev, "mdio write timed out\n");
| NETIF_F_RXCSUM
| NETIF_F_HIGHDMA
| NETIF_F_LLTX
- | NETIF_F_NETNS_LOCAL;
+ | NETIF_F_NETNS_LOCAL
+ | NETIF_F_VLAN_CHALLENGED;
dev->ethtool_ops = &loopback_ethtool_ops;
dev->header_ops = ð_header_ops;
dev->netdev_ops = &loopback_ops;
result |= ADVERTISED_100baseT_Half;
if (advert & ADVERTISE_100FULL)
result |= ADVERTISED_100baseT_Full;
+ if (advert & ADVERTISE_PAUSE_CAP)
+ result |= ADVERTISED_Pause;
+ if (advert & ADVERTISE_PAUSE_ASYM)
+ result |= ADVERTISED_Asym_Pause;
return result;
}
prev_eedata = eedata;
}
+ /* Store MAC Address in perm_addr */
+ memcpy(dev->perm_addr, dev->dev_addr, ETH_ALEN);
+
dev->base_addr = (unsigned long __force) ioaddr;
dev->irq = irq;
goto done;
spin_lock_irqsave(&target_list_lock, flags);
+restart:
list_for_each_entry(nt, &target_list, list) {
netconsole_target_get(nt);
if (nt->np.dev == dev) {
* rtnl_lock already held
*/
if (nt->np.dev) {
+ spin_unlock_irqrestore(
+ &target_list_lock,
+ flags);
__netpoll_cleanup(&nt->np);
+ spin_lock_irqsave(&target_list_lock,
+ flags);
dev_put(nt->np.dev);
nt->np.dev = NULL;
+ netconsole_target_put(nt);
+ goto restart;
}
/* Fall through */
case NETDEV_GOING_DOWN:
#define MAX_NUM_CARDS 4
-#define MAX_BUFFERS_PER_CMD 32
+#define NETXEN_MAX_FRAGS_PER_TX 14
#define MAX_TSO_HEADER_DESC 2
#define MGMT_CMD_DESC_RESV 4
#define TX_STOP_THRESH ((MAX_SKB_FRAGS >> 2) + MAX_TSO_HEADER_DESC \
*/
struct netxen_cmd_buffer {
struct sk_buff *skb;
- struct netxen_skb_frag frag_array[MAX_BUFFERS_PER_CMD + 1];
+ struct netxen_skb_frag frag_array[MAX_SKB_FRAGS + 1];
u32 frag_count;
};
struct cmd_desc_type0 *hwdesc, *first_desc;
struct pci_dev *pdev;
int i, k;
+ int delta = 0;
+ struct skb_frag_struct *frag;
u32 producer;
int frag_count, no_of_desc;
frag_count = skb_shinfo(skb)->nr_frags + 1;
+ /* 14 frags supported for normal packet and
+ * 32 frags supported for TSO packet
+ */
+ if (!skb_is_gso(skb) && frag_count > NETXEN_MAX_FRAGS_PER_TX) {
+
+ for (i = 0; i < (frag_count - NETXEN_MAX_FRAGS_PER_TX); i++) {
+ frag = &skb_shinfo(skb)->frags[i];
+ delta += frag->size;
+ }
+
+ if (!__pskb_pull_tail(skb, delta))
+ goto drop_packet;
+
+ frag_count = 1 + skb_shinfo(skb)->nr_frags;
+ }
/* 4 fragments per cmd des */
no_of_desc = (frag_count + 3) >> 2;
#define TX_UDPV6_PKT 0x0c
/* Tx defines */
+#define QLCNIC_MAX_FRAGS_PER_TX 14
#define MAX_TSO_HEADER_DESC 2
#define MGMT_CMD_DESC_RESV 4
#define TX_STOP_THRESH ((MAX_SKB_FRAGS >> 2) + MAX_TSO_HEADER_DESC \
struct cmd_desc_type0 *hwdesc, *first_desc;
struct pci_dev *pdev;
struct ethhdr *phdr;
+ int delta = 0;
int i, k;
u32 producer;
}
frag_count = skb_shinfo(skb)->nr_frags + 1;
+ /* 14 frags supported for normal packet and
+ * 32 frags supported for TSO packet
+ */
+ if (!skb_is_gso(skb) && frag_count > QLCNIC_MAX_FRAGS_PER_TX) {
+
+ for (i = 0; i < (frag_count - QLCNIC_MAX_FRAGS_PER_TX); i++)
+ delta += skb_shinfo(skb)->frags[i].size;
+
+ if (!__pskb_pull_tail(skb, delta))
+ goto drop_packet;
+
+ frag_count = 1 + skb_shinfo(skb)->nr_frags;
+ }
/* 4 fragments per cmd des */
no_of_desc = (frag_count + 3) >> 2;
};
#undef _R
+static const struct rtl_firmware_info {
+ int mac_version;
+ const char *fw_name;
+} rtl_firmware_infos[] = {
+ { .mac_version = RTL_GIGA_MAC_VER_25, .fw_name = FIRMWARE_8168D_1 },
+ { .mac_version = RTL_GIGA_MAC_VER_26, .fw_name = FIRMWARE_8168D_2 },
+ { .mac_version = RTL_GIGA_MAC_VER_29, .fw_name = FIRMWARE_8105E_1 },
+ { .mac_version = RTL_GIGA_MAC_VER_30, .fw_name = FIRMWARE_8105E_1 }
+};
+
enum cfg_version {
RTL_CFG_0 = 0x00,
RTL_CFG_1,
u32 saved_wolopts;
const struct firmware *fw;
+#define RTL_FIRMWARE_UNKNOWN ERR_PTR(-EAGAIN);
};
MODULE_AUTHOR("Realtek and the Linux r8169 crew <netdev@vger.kernel.org>");
static void rtl_release_firmware(struct rtl8169_private *tp)
{
- release_firmware(tp->fw);
- tp->fw = NULL;
+ if (!IS_ERR_OR_NULL(tp->fw))
+ release_firmware(tp->fw);
+ tp->fw = RTL_FIRMWARE_UNKNOWN;
}
-static int rtl_apply_firmware(struct rtl8169_private *tp, const char *fw_name)
+static void rtl_apply_firmware(struct rtl8169_private *tp)
{
- const struct firmware **fw = &tp->fw;
- int rc = !*fw;
-
- if (rc) {
- rc = request_firmware(fw, fw_name, &tp->pci_dev->dev);
- if (rc < 0)
- goto out;
- }
+ const struct firmware *fw = tp->fw;
/* TODO: release firmware once rtl_phy_write_fw signals failures. */
- rtl_phy_write_fw(tp, *fw);
-out:
- return rc;
+ if (!IS_ERR_OR_NULL(fw))
+ rtl_phy_write_fw(tp, fw);
+}
+
+static void rtl_apply_firmware_cond(struct rtl8169_private *tp, u8 reg, u16 val)
+{
+ if (rtl_readphy(tp, reg) != val)
+ netif_warn(tp, hw, tp->dev, "chipset not ready for firmware\n");
+ else
+ rtl_apply_firmware(tp);
}
static void rtl8169s_hw_phy_config(struct rtl8169_private *tp)
rtl_writephy(tp, 0x1f, 0x0005);
rtl_writephy(tp, 0x05, 0x001b);
- if ((rtl_readphy(tp, 0x06) != 0xbf00) ||
- (rtl_apply_firmware(tp, FIRMWARE_8168D_1) < 0)) {
- netif_warn(tp, probe, tp->dev, "unable to apply firmware patch\n");
- }
+
+ rtl_apply_firmware_cond(tp, MII_EXPANSION, 0xbf00);
rtl_writephy(tp, 0x1f, 0x0000);
}
rtl_writephy(tp, 0x1f, 0x0005);
rtl_writephy(tp, 0x05, 0x001b);
- if ((rtl_readphy(tp, 0x06) != 0xb300) ||
- (rtl_apply_firmware(tp, FIRMWARE_8168D_2) < 0)) {
- netif_warn(tp, probe, tp->dev, "unable to apply firmware patch\n");
- }
+
+ rtl_apply_firmware_cond(tp, MII_EXPANSION, 0xb300);
rtl_writephy(tp, 0x1f, 0x0000);
}
rtl_writephy(tp, 0x18, 0x0310);
msleep(100);
- if (rtl_apply_firmware(tp, FIRMWARE_8105E_1) < 0)
- netif_warn(tp, probe, tp->dev, "unable to apply firmware patch\n");
+ rtl_apply_firmware(tp);
rtl_writephy_batch(tp, phy_reg_init, ARRAY_SIZE(phy_reg_init));
}
tp->timer.data = (unsigned long) dev;
tp->timer.function = rtl8169_phy_timer;
+ tp->fw = RTL_FIRMWARE_UNKNOWN;
+
rc = register_netdev(dev);
if (rc < 0)
goto err_out_msi_4;
cancel_delayed_work_sync(&tp->task);
- rtl_release_firmware(tp);
-
unregister_netdev(dev);
+ rtl_release_firmware(tp);
+
if (pci_dev_run_wake(pdev))
pm_runtime_get_noresume(&pdev->dev);
pci_set_drvdata(pdev, NULL);
}
+static void rtl_request_firmware(struct rtl8169_private *tp)
+{
+ int i;
+
+ /* Return early if the firmware is already loaded / cached. */
+ if (!IS_ERR(tp->fw))
+ goto out;
+
+ for (i = 0; i < ARRAY_SIZE(rtl_firmware_infos); i++) {
+ const struct rtl_firmware_info *info = rtl_firmware_infos + i;
+
+ if (info->mac_version == tp->mac_version) {
+ const char *name = info->fw_name;
+ int rc;
+
+ rc = request_firmware(&tp->fw, name, &tp->pci_dev->dev);
+ if (rc < 0) {
+ netif_warn(tp, ifup, tp->dev, "unable to load "
+ "firmware patch %s (%d)\n", name, rc);
+ goto out_disable_request_firmware;
+ }
+ goto out;
+ }
+ }
+
+out_disable_request_firmware:
+ tp->fw = NULL;
+out:
+ return;
+}
+
static int rtl8169_open(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
smp_mb();
+ rtl_request_firmware(tp);
+
retval = request_irq(dev->irq, rtl8169_interrupt,
(tp->features & RTL_FEATURE_MSI) ? 0 : IRQF_SHARED,
dev->name, dev);
if (retval < 0)
- goto err_release_ring_2;
+ goto err_release_fw_2;
napi_enable(&tp->napi);
out:
return retval;
-err_release_ring_2:
+err_release_fw_2:
+ rtl_release_firmware(tp);
rtl8169_rx_clear(tp);
err_free_rx_1:
dma_free_coherent(&pdev->dev, R8169_RX_RING_BYTES, tp->RxDescArray,
* processing to finish, then directly poll (and ack ) the eventq.
* Finally reenable NAPI and interrupts.
*
- * Since we are touching interrupts the caller should hold the suspend lock
+ * This is for use only during a loopback self-test. It must not
+ * deliver any packets up the stack as this can result in deadlock.
*/
void efx_process_channel_now(struct efx_channel *channel)
{
BUG_ON(channel->channel >= efx->n_channels);
BUG_ON(!channel->enabled);
+ BUG_ON(!efx->loopback_selftest);
/* Disable interrupts and wait for ISRs to complete */
efx_nic_disable_interrupts(efx);
* restart the transmit interface early so the watchdog timer stops */
efx_start_port(efx);
- if (efx_dev_registered(efx))
+ if (efx_dev_registered(efx) && !efx->port_inhibited)
netif_tx_wake_all_queues(efx->net_dev);
efx_for_each_channel(channel, efx)
spin_lock_irqsave(&efx->biu_lock, flags);
value->u32[0] = _efx_readd(efx, reg + 0);
+ rmb();
value->u32[1] = _efx_readd(efx, reg + 4);
value->u32[2] = _efx_readd(efx, reg + 8);
value->u32[3] = _efx_readd(efx, reg + 12);
value->u64[0] = (__force __le64)__raw_readq(membase + addr);
#else
value->u32[0] = (__force __le32)__raw_readl(membase + addr);
+ rmb();
value->u32[1] = (__force __le32)__raw_readl(membase + addr + 4);
#endif
spin_unlock_irqrestore(&efx->biu_lock, flags);
* @eventq_mask: Event queue pointer mask
* @eventq_read_ptr: Event queue read pointer
* @last_eventq_read_ptr: Last event queue read pointer value.
- * @magic_count: Event queue test event count
* @irq_count: Number of IRQs since last adaptive moderation decision
* @irq_mod_score: IRQ moderation score
* @rx_alloc_level: Watermark based heuristic counter for pushing descriptors
unsigned int eventq_mask;
unsigned int eventq_read_ptr;
unsigned int last_eventq_read_ptr;
- unsigned int magic_count;
unsigned int irq_count;
unsigned int irq_mod_score;
static inline efx_qword_t *efx_event(struct efx_channel *channel,
unsigned int index)
{
- return ((efx_qword_t *) (channel->eventq.addr)) + index;
+ return ((efx_qword_t *) (channel->eventq.addr)) +
+ (index & channel->eventq_mask);
}
/* See if an event is present
efx_dword_t reg;
struct efx_nic *efx = channel->efx;
- EFX_POPULATE_DWORD_1(reg, FRF_AZ_EVQ_RPTR, channel->eventq_read_ptr);
+ EFX_POPULATE_DWORD_1(reg, FRF_AZ_EVQ_RPTR,
+ channel->eventq_read_ptr & channel->eventq_mask);
efx_writed_table(efx, ®, efx->type->evq_rptr_tbl_base,
channel->channel);
}
code = EFX_QWORD_FIELD(*event, FSF_AZ_DRV_GEN_EV_MAGIC);
if (code == EFX_CHANNEL_MAGIC_TEST(channel))
- ++channel->magic_count;
+ ; /* ignore */
else if (code == EFX_CHANNEL_MAGIC_FILL(channel))
/* The queue must be empty, so we won't receive any rx
* events, so efx_process_channel() won't refill the
/* Clear this event by marking it all ones */
EFX_SET_QWORD(*p_event);
- /* Increment read pointer */
- read_ptr = (read_ptr + 1) & channel->eventq_mask;
+ ++read_ptr;
ev_code = EFX_QWORD_FIELD(event, FSF_AZ_EV_CODE);
return spent;
}
+/* Check whether an event is present in the eventq at the current
+ * read pointer. Only useful for self-test.
+ */
+bool efx_nic_event_present(struct efx_channel *channel)
+{
+ return efx_event_present(efx_event(channel, channel->eventq_read_ptr));
+}
/* Allocate buffer table entries for event queue */
int efx_nic_probe_eventq(struct efx_channel *channel)
struct efx_tx_queue *tx_queue;
struct efx_rx_queue *rx_queue;
unsigned int read_ptr = channel->eventq_read_ptr;
- unsigned int end_ptr = (read_ptr - 1) & channel->eventq_mask;
+ unsigned int end_ptr = read_ptr + channel->eventq_mask - 1;
do {
efx_qword_t *event = efx_event(channel, read_ptr);
* it's ok to throw away every non-flush event */
EFX_SET_QWORD(*event);
- read_ptr = (read_ptr + 1) & channel->eventq_mask;
+ ++read_ptr;
} while (read_ptr != end_ptr);
channel->eventq_read_ptr = read_ptr;
extern void efx_nic_remove_eventq(struct efx_channel *channel);
extern int efx_nic_process_eventq(struct efx_channel *channel, int rx_quota);
extern void efx_nic_eventq_read_ack(struct efx_channel *channel);
+extern bool efx_nic_event_present(struct efx_channel *channel);
/* MAC/PHY */
extern void falcon_drain_tx_fifo(struct efx_nic *efx);
static int efx_test_interrupts(struct efx_nic *efx,
struct efx_self_tests *tests)
{
- struct efx_channel *channel;
-
netif_dbg(efx, drv, efx->net_dev, "testing interrupts\n");
tests->interrupt = -1;
efx->last_irq_cpu = -1;
smp_wmb();
- /* ACK each interrupting event queue. Receiving an interrupt due to
- * traffic before a test event is raised is considered a pass */
- efx_for_each_channel(channel, efx) {
- if (channel->work_pending)
- efx_process_channel_now(channel);
- if (efx->last_irq_cpu >= 0)
- goto success;
- }
-
efx_nic_generate_interrupt(efx);
/* Wait for arrival of test interrupt. */
struct efx_self_tests *tests)
{
struct efx_nic *efx = channel->efx;
- unsigned int magic_count, count;
+ unsigned int read_ptr, count;
tests->eventq_dma[channel->channel] = -1;
tests->eventq_int[channel->channel] = -1;
tests->eventq_poll[channel->channel] = -1;
- magic_count = channel->magic_count;
+ read_ptr = channel->eventq_read_ptr;
channel->efx->last_irq_cpu = -1;
smp_wmb();
do {
schedule_timeout_uninterruptible(HZ / 100);
- if (channel->work_pending)
- efx_process_channel_now(channel);
-
- if (channel->magic_count != magic_count)
+ if (ACCESS_ONCE(channel->eventq_read_ptr) != read_ptr)
goto eventq_ok;
} while (++count < 2);
}
/* Check to see if event was received even if interrupt wasn't */
- efx_process_channel_now(channel);
- if (channel->magic_count != magic_count) {
+ if (efx_nic_event_present(channel)) {
netif_err(efx, drv, efx->net_dev,
"channel %d event was generated, but "
"failed to trigger an interrupt\n", channel->channel);
__efx_reconfigure_port(efx);
mutex_unlock(&efx->mac_lock);
+ netif_tx_wake_all_queues(efx->net_dev);
+
return rc_test;
}
* queue state. */
smp_mb();
if (unlikely(netif_tx_queue_stopped(tx_queue->core_txq)) &&
- likely(efx->port_enabled)) {
+ likely(efx->port_enabled) &&
+ likely(!efx->port_inhibited)) {
fill_level = tx_queue->insert_count - tx_queue->read_count;
if (fill_level < EFX_TXQ_THRESHOLD(efx)) {
EFX_BUG_ON_PARANOID(!efx_dev_registered(efx));
* @net_dev: the net device to get address for
*
* Older SiS900 and friends, use EEPROM to store MAC address.
- * MAC address is read from read_eeprom() into @net_dev->dev_addr.
+ * MAC address is read from read_eeprom() into @net_dev->dev_addr and
+ * @net_dev->perm_addr.
*/
static int __devinit sis900_get_mac_addr(struct pci_dev * pci_dev, struct net_device *net_dev)
for (i = 0; i < 3; i++)
((u16 *)(net_dev->dev_addr))[i] = read_eeprom(ioaddr, i+EEPROMMACAddr);
+ /* Store MAC Address in perm_addr */
+ memcpy(net_dev->perm_addr, net_dev->dev_addr, ETH_ALEN);
+
return 1;
}
*
* SiS630E model, use APC CMOS RAM to store MAC address.
* APC CMOS RAM is accessed through ISA bridge.
- * MAC address is read into @net_dev->dev_addr.
+ * MAC address is read into @net_dev->dev_addr and
+ * @net_dev->perm_addr.
*/
static int __devinit sis630e_get_mac_addr(struct pci_dev * pci_dev,
outb(0x09 + i, 0x70);
((u8 *)(net_dev->dev_addr))[i] = inb(0x71);
}
+
+ /* Store MAC Address in perm_addr */
+ memcpy(net_dev->perm_addr, net_dev->dev_addr, ETH_ALEN);
+
pci_write_config_byte(isa_bridge, 0x48, reg & ~0x40);
pci_dev_put(isa_bridge);
*
* SiS635 model, set MAC Reload Bit to load Mac address from APC
* to rfdr. rfdr is accessed through rfcr. MAC address is read into
- * @net_dev->dev_addr.
+ * @net_dev->dev_addr and @net_dev->perm_addr.
*/
static int __devinit sis635_get_mac_addr(struct pci_dev * pci_dev,
*( ((u16 *)net_dev->dev_addr) + i) = inw(ioaddr + rfdr);
}
+ /* Store MAC Address in perm_addr */
+ memcpy(net_dev->perm_addr, net_dev->dev_addr, ETH_ALEN);
+
/* enable packet filtering */
outl(rfcrSave | RFEN, rfcr + ioaddr);
* EEDONE signal to refuse EEPROM access by LAN.
* The EEPROM map of SiS962 or SiS963 is different to SiS900.
* The signature field in SiS962 or SiS963 spec is meaningless.
- * MAC address is read into @net_dev->dev_addr.
+ * MAC address is read into @net_dev->dev_addr and @net_dev->perm_addr.
*/
static int __devinit sis96x_get_mac_addr(struct pci_dev * pci_dev,
for (i = 0; i < 3; i++)
((u16 *)(net_dev->dev_addr))[i] = read_eeprom(ioaddr, i+EEPROMMACAddr);
+ /* Store MAC Address in perm_addr */
+ memcpy(net_dev->perm_addr, net_dev->dev_addr, ETH_ALEN);
+
outl(EEDONE, ee_addr);
return 1;
} else {
#undef DWMAC_DMA_DEBUG
#ifdef DWMAC_DMA_DEBUG
-#define DBG(fmt, args...) printk(fmt, ## args)
+#define DWMAC_LIB_DBG(fmt, args...) printk(fmt, ## args)
#else
-#define DBG(fmt, args...) do { } while (0)
+#define DWMAC_LIB_DBG(fmt, args...) do { } while (0)
#endif
/* CSR1 enables the transmit DMA to check for new descriptor */
/* read the status register (CSR5) */
u32 intr_status = readl(ioaddr + DMA_STATUS);
- DBG(INFO, "%s: [CSR5: 0x%08x]\n", __func__, intr_status);
+ DWMAC_LIB_DBG(KERN_INFO "%s: [CSR5: 0x%08x]\n", __func__, intr_status);
#ifdef DWMAC_DMA_DEBUG
/* It displays the DMA process states (CSR5 register) */
show_tx_process_state(intr_status);
#endif
/* ABNORMAL interrupts */
if (unlikely(intr_status & DMA_STATUS_AIS)) {
- DBG(INFO, "CSR5[15] DMA ABNORMAL IRQ: ");
+ DWMAC_LIB_DBG(KERN_INFO "CSR5[15] DMA ABNORMAL IRQ: ");
if (unlikely(intr_status & DMA_STATUS_UNF)) {
- DBG(INFO, "transmit underflow\n");
+ DWMAC_LIB_DBG(KERN_INFO "transmit underflow\n");
ret = tx_hard_error_bump_tc;
x->tx_undeflow_irq++;
}
if (unlikely(intr_status & DMA_STATUS_TJT)) {
- DBG(INFO, "transmit jabber\n");
+ DWMAC_LIB_DBG(KERN_INFO "transmit jabber\n");
x->tx_jabber_irq++;
}
if (unlikely(intr_status & DMA_STATUS_OVF)) {
- DBG(INFO, "recv overflow\n");
+ DWMAC_LIB_DBG(KERN_INFO "recv overflow\n");
x->rx_overflow_irq++;
}
if (unlikely(intr_status & DMA_STATUS_RU)) {
- DBG(INFO, "receive buffer unavailable\n");
+ DWMAC_LIB_DBG(KERN_INFO "receive buffer unavailable\n");
x->rx_buf_unav_irq++;
}
if (unlikely(intr_status & DMA_STATUS_RPS)) {
- DBG(INFO, "receive process stopped\n");
+ DWMAC_LIB_DBG(KERN_INFO "receive process stopped\n");
x->rx_process_stopped_irq++;
}
if (unlikely(intr_status & DMA_STATUS_RWT)) {
- DBG(INFO, "receive watchdog\n");
+ DWMAC_LIB_DBG(KERN_INFO "receive watchdog\n");
x->rx_watchdog_irq++;
}
if (unlikely(intr_status & DMA_STATUS_ETI)) {
- DBG(INFO, "transmit early interrupt\n");
+ DWMAC_LIB_DBG(KERN_INFO "transmit early interrupt\n");
x->tx_early_irq++;
}
if (unlikely(intr_status & DMA_STATUS_TPS)) {
- DBG(INFO, "transmit process stopped\n");
+ DWMAC_LIB_DBG(KERN_INFO "transmit process stopped\n");
x->tx_process_stopped_irq++;
ret = tx_hard_error;
}
if (unlikely(intr_status & DMA_STATUS_FBI)) {
- DBG(INFO, "fatal bus error\n");
+ DWMAC_LIB_DBG(KERN_INFO "fatal bus error\n");
x->fatal_bus_error_irq++;
ret = tx_hard_error;
}
/* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */
writel((intr_status & 0x1ffff), ioaddr + DMA_STATUS);
- DBG(INFO, "\n\n");
+ DWMAC_LIB_DBG(KERN_INFO "\n\n");
return ret;
}
priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE);
priv->xstats.threshold = tc;
}
- stmmac_tx_err(priv);
} else if (unlikely(status == tx_hard_error))
stmmac_tx_err(priv);
}
stmmac_verify_args();
- ret = stmmac_init_phy(dev);
- if (unlikely(ret)) {
- pr_err("%s: Cannot attach to PHY (error: %d)\n", __func__, ret);
- return ret;
- }
-
- /* Request the IRQ lines */
- ret = request_irq(dev->irq, stmmac_interrupt,
- IRQF_SHARED, dev->name, dev);
- if (unlikely(ret < 0)) {
- pr_err("%s: ERROR: allocating the IRQ %d (error: %d)\n",
- __func__, dev->irq, ret);
- return ret;
- }
-
#ifdef CONFIG_STMMAC_TIMER
priv->tm = kzalloc(sizeof(struct stmmac_timer *), GFP_KERNEL);
if (unlikely(priv->tm == NULL)) {
} else
priv->tm->enable = 1;
#endif
+ ret = stmmac_init_phy(dev);
+ if (unlikely(ret)) {
+ pr_err("%s: Cannot attach to PHY (error: %d)\n", __func__, ret);
+ goto open_error;
+ }
/* Create and initialize the TX/RX descriptors chains. */
priv->dma_tx_size = STMMAC_ALIGN(dma_txsize);
init_dma_desc_rings(dev);
/* DMA initialization and SW reset */
- if (unlikely(priv->hw->dma->init(priv->ioaddr, priv->plat->pbl,
- priv->dma_tx_phy,
- priv->dma_rx_phy) < 0)) {
-
+ ret = priv->hw->dma->init(priv->ioaddr, priv->plat->pbl,
+ priv->dma_tx_phy, priv->dma_rx_phy);
+ if (ret < 0) {
pr_err("%s: DMA initialization failed\n", __func__);
- return -1;
+ goto open_error;
}
/* Copy the MAC addr into the HW */
writel(0xffffffff, priv->ioaddr + MMC_HIGH_INTR_MASK);
writel(0xffffffff, priv->ioaddr + MMC_LOW_INTR_MASK);
+ /* Request the IRQ lines */
+ ret = request_irq(dev->irq, stmmac_interrupt,
+ IRQF_SHARED, dev->name, dev);
+ if (unlikely(ret < 0)) {
+ pr_err("%s: ERROR: allocating the IRQ %d (error: %d)\n",
+ __func__, dev->irq, ret);
+ goto open_error;
+ }
+
/* Enable the MAC Rx/Tx */
stmmac_enable_mac(priv->ioaddr);
napi_enable(&priv->napi);
skb_queue_head_init(&priv->rx_recycle);
netif_start_queue(dev);
+
return 0;
+
+open_error:
+#ifdef CONFIG_STMMAC_TIMER
+ kfree(priv->tm);
+#endif
+ if (priv->phydev)
+ phy_disconnect(priv->phydev);
+
+ return ret;
}
/**
if (val & VCPU_CFGSHDW_ASPM_DBNC)
tp->tg3_flags |= TG3_FLAG_ASPM_WORKAROUND;
if ((val & VCPU_CFGSHDW_WOL_ENABLE) &&
- (val & VCPU_CFGSHDW_WOL_MAGPKT))
+ (val & VCPU_CFGSHDW_WOL_MAGPKT)) {
tp->tg3_flags |= TG3_FLAG_WOL_ENABLE;
+ device_set_wakeup_enable(&tp->pdev->dev, true);
+ }
goto done;
}
tp->tg3_flags &= ~TG3_FLAG_WOL_CAP;
if ((tp->tg3_flags & TG3_FLAG_WOL_CAP) &&
- (nic_cfg & NIC_SRAM_DATA_CFG_WOL_ENABLE))
+ (nic_cfg & NIC_SRAM_DATA_CFG_WOL_ENABLE)) {
tp->tg3_flags |= TG3_FLAG_WOL_ENABLE;
+ device_set_wakeup_enable(&tp->pdev->dev, true);
+ }
if (cfg2 & (1 << 17))
tp->phy_flags |= TG3_PHYFLG_CAPACITIVE_COUPLING;
/*
* The NIC has told us that a packet has been downloaded onto the card, we must
* find out which packet it has done, clear the skb and information for the packet
- * then advance around the ring for all tranmitted packets
+ * then advance around the ring for all transmitted packets
*/
static void xl_dn_comp(struct net_device *dev)
if (lan_status_diff & LSC_SOFT_ERR)
printk(KERN_WARNING "%s: Adapter transmitted Soft Error Report Mac Frame\n",dev->name);
if (lan_status_diff & LSC_TRAN_BCN)
- printk(KERN_INFO "%s: We are tranmitting the beacon, aaah\n",dev->name);
+ printk(KERN_INFO "%s: We are transmitting the beacon, aaah\n",dev->name);
if (lan_status_diff & LSC_SS)
printk(KERN_INFO "%s: Single Station on the ring\n", dev->name);
if (lan_status_diff & LSC_RING_REC)
if (lan_status_diff & LSC_SOFT_ERR)
printk(KERN_WARNING "%s: Adapter transmitted Soft Error Report Mac Frame\n", dev->name);
if (lan_status_diff & LSC_TRAN_BCN)
- printk(KERN_INFO "%s: We are tranmitting the beacon, aaah\n", dev->name);
+ printk(KERN_INFO "%s: We are transmitting the beacon, aaah\n", dev->name);
if (lan_status_diff & LSC_SS)
printk(KERN_INFO "%s: Single Station on the ring\n", dev->name);
if (lan_status_diff & LSC_RING_REC)
if (lan_status_diff & LSC_SOFT_ERR)
printk(KERN_WARNING "%s: Adapter transmitted Soft Error Report Mac Frame\n",dev->name);
if (lan_status_diff & LSC_TRAN_BCN)
- printk(KERN_INFO "%s: We are tranmitting the beacon, aaah\n",dev->name);
+ printk(KERN_INFO "%s: We are transmitting the beacon, aaah\n",dev->name);
if (lan_status_diff & LSC_SS)
printk(KERN_INFO "%s: Single Station on the ring\n", dev->name);
if (lan_status_diff & LSC_RING_REC)
.manage_power = cdc_manage_power,
};
-static const struct driver_info mbm_info = {
+static const struct driver_info wwan_info = {
.description = "Mobile Broadband Network Device",
.flags = FLAG_WWAN,
.bind = usbnet_cdc_bind,
/*-------------------------------------------------------------------------*/
+#define HUAWEI_VENDOR_ID 0x12D1
static const struct usb_device_id products [] = {
/*
}, {
USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM,
USB_CDC_PROTO_NONE),
- .driver_info = (unsigned long)&mbm_info,
+ .driver_info = (unsigned long)&wwan_info,
+}, {
+ /* Various Huawei modems with a network port like the UMG1831 */
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = HUAWEI_VENDOR_ID,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET,
+ .bInterfaceProtocol = 255,
+ .driver_info = (unsigned long)&wwan_info,
},
{ }, // END
};
#include <linux/usb/usbnet.h>
#include <linux/usb/cdc.h>
-#define DRIVER_VERSION "7-Feb-2011"
+#define DRIVER_VERSION "23-Apr-2011"
/* CDC NCM subclass 3.2.1 */
#define USB_CDC_NCM_NDP16_LENGTH_MIN 0x10
/* Maximum NTB length */
-#define CDC_NCM_NTB_MAX_SIZE_TX 16384 /* bytes */
+#define CDC_NCM_NTB_MAX_SIZE_TX (16384 + 4) /* bytes, must be short terminated */
#define CDC_NCM_NTB_MAX_SIZE_RX 16384 /* bytes */
/* Minimum value for MaxDatagramSize, ch. 6.2.9 */
msleep(10);
bmcr = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR);
timeout++;
- } while ((bmcr & MII_BMCR) && (timeout < 100));
+ } while ((bmcr & BMCR_RESET) && (timeout < 100));
if (timeout >= 100) {
netdev_warn(dev->net, "timeout on PHY Reset");
}
}
+ set_bit(EVENT_DEV_OPEN, &dev->flags);
netif_start_queue (net);
netif_info(dev, ifup, dev->net,
"open: enable queueing (rx %d, tx %d) mtu %d %s framing\n",
if (dev->driver_info->unbind)
dev->driver_info->unbind (dev, intf);
+ usb_kill_urb(dev->interrupt);
+ usb_free_urb(dev->interrupt);
+
free_netdev(net);
usb_put_dev (xdev);
}
int retval;
if (!--dev->suspend_count) {
+ /* resume interrupt URBs */
+ if (dev->interrupt && test_bit(EVENT_DEV_OPEN, &dev->flags))
+ usb_submit_urb(dev->interrupt, GFP_NOIO);
+
spin_lock_irq(&dev->txq.lock);
while ((res = usb_get_from_anchor(&dev->deferred))) {
if (tb[IFLA_ADDRESS] == NULL)
random_ether_addr(dev->dev_addr);
+ if (tb[IFLA_IFNAME])
+ nla_strlcpy(dev->name, tb[IFLA_IFNAME], IFNAMSIZ);
+ else
+ snprintf(dev->name, IFNAMSIZ, DRV_NAME "%%d");
+
+ if (strchr(dev->name, '%')) {
+ err = dev_alloc_name(dev, dev->name);
+ if (err < 0)
+ goto err_alloc_name;
+ }
+
err = register_netdevice(dev);
if (err < 0)
goto err_register_dev;
err_register_dev:
/* nothing to do */
+err_alloc_name:
err_configure_peer:
unregister_netdevice(peer);
return err;
}
ret = ath9k_htc_hw_init(hif_dev->htc_handle,
- &hif_dev->udev->dev, hif_dev->device_id,
+ &interface->dev, hif_dev->device_id,
hif_dev->udev->product, id->driver_info);
if (ret) {
ret = -EINVAL;
#endif
static struct usb_driver ath9k_hif_usb_driver = {
- .name = "ath9k_hif_usb",
+ .name = KBUILD_MODNAME,
.probe = ath9k_hif_usb_probe,
.disconnect = ath9k_hif_usb_disconnect,
#ifdef CONFIG_PM
ah->txchainmask = common->tx_chainmask;
ah->rxchainmask = common->rx_chainmask;
- if ((common->bus_ops->ath_bus_type != ATH_USB) && !ah->chip_fullsleep) {
- ath9k_hw_abortpcurecv(ah);
- if (!ath9k_hw_stopdmarecv(ah)) {
- ath_dbg(common, ATH_DBG_XMIT,
- "Failed to stop receive dma\n");
- bChannelChange = false;
- }
- }
-
if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))
return -EIO;
}
EXPORT_SYMBOL(ath9k_hw_abortpcurecv);
-bool ath9k_hw_stopdmarecv(struct ath_hw *ah)
+bool ath9k_hw_stopdmarecv(struct ath_hw *ah, bool *reset)
{
#define AH_RX_STOP_DMA_TIMEOUT 10000 /* usec */
#define AH_RX_TIME_QUANTUM 100 /* usec */
struct ath_common *common = ath9k_hw_common(ah);
+ u32 mac_status, last_mac_status = 0;
int i;
+ /* Enable access to the DMA observation bus */
+ REG_WRITE(ah, AR_MACMISC,
+ ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) |
+ (AR_MACMISC_MISC_OBS_BUS_1 <<
+ AR_MACMISC_MISC_OBS_BUS_MSB_S)));
+
REG_WRITE(ah, AR_CR, AR_CR_RXD);
/* Wait for rx enable bit to go low */
for (i = AH_RX_STOP_DMA_TIMEOUT / AH_TIME_QUANTUM; i != 0; i--) {
if ((REG_READ(ah, AR_CR) & AR_CR_RXE) == 0)
break;
+
+ if (!AR_SREV_9300_20_OR_LATER(ah)) {
+ mac_status = REG_READ(ah, AR_DMADBG_7) & 0x7f0;
+ if (mac_status == 0x1c0 && mac_status == last_mac_status) {
+ *reset = true;
+ break;
+ }
+
+ last_mac_status = mac_status;
+ }
+
udelay(AH_TIME_QUANTUM);
}
if (i == 0) {
ath_err(common,
- "DMA failed to stop in %d ms AR_CR=0x%08x AR_DIAG_SW=0x%08x\n",
+ "DMA failed to stop in %d ms AR_CR=0x%08x AR_DIAG_SW=0x%08x DMADBG_7=0x%08x\n",
AH_RX_STOP_DMA_TIMEOUT / 1000,
REG_READ(ah, AR_CR),
- REG_READ(ah, AR_DIAG_SW));
+ REG_READ(ah, AR_DIAG_SW),
+ REG_READ(ah, AR_DMADBG_7));
return false;
} else {
return true;
void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp);
void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning);
void ath9k_hw_abortpcurecv(struct ath_hw *ah);
-bool ath9k_hw_stopdmarecv(struct ath_hw *ah);
+bool ath9k_hw_stopdmarecv(struct ath_hw *ah, bool *reset);
int ath9k_hw_beaconq_setup(struct ath_hw *ah);
/* Interrupt Handling */
ath9k_calculate_iter_data(hw, vif, &iter_data);
- ath9k_ps_wakeup(sc);
/* Set BSSID mask. */
memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
ath_hw_setbssidmask(common);
}
ath9k_hw_set_interrupts(ah, ah->imask);
- ath9k_ps_restore(sc);
/* Set up ANI */
if ((iter_data.naps + iter_data.nadhocs) > 0) {
struct ath_vif *avp = (void *)vif->drv_priv;
int ret = 0;
+ ath9k_ps_wakeup(sc);
mutex_lock(&sc->mutex);
switch (vif->type) {
ath9k_do_vif_add_setup(hw, vif);
out:
mutex_unlock(&sc->mutex);
+ ath9k_ps_restore(sc);
return ret;
}
ath_dbg(common, ATH_DBG_CONFIG, "Change Interface\n");
mutex_lock(&sc->mutex);
+ ath9k_ps_wakeup(sc);
/* See if new interface type is valid. */
if ((new_type == NL80211_IFTYPE_ADHOC) &&
ath9k_do_vif_add_setup(hw, vif);
out:
+ ath9k_ps_restore(sc);
mutex_unlock(&sc->mutex);
return ret;
}
ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface\n");
+ ath9k_ps_wakeup(sc);
mutex_lock(&sc->mutex);
sc->nvifs--;
ath9k_calculate_summary_state(hw, NULL);
mutex_unlock(&sc->mutex);
+ ath9k_ps_restore(sc);
}
static void ath9k_enable_ps(struct ath_softc *sc)
txq = sc->tx.txq_map[queue];
+ ath9k_ps_wakeup(sc);
mutex_lock(&sc->mutex);
memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
ath_beaconq_config(sc);
mutex_unlock(&sc->mutex);
+ ath9k_ps_restore(sc);
return ret;
}
int slottime;
int error;
+ ath9k_ps_wakeup(sc);
mutex_lock(&sc->mutex);
if (changed & BSS_CHANGED_BSSID) {
}
mutex_unlock(&sc->mutex);
+ ath9k_ps_restore(sc);
}
static u64 ath9k_get_tsf(struct ieee80211_hw *hw)
bool ath_stoprecv(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
- bool stopped;
+ bool stopped, reset = false;
spin_lock_bh(&sc->rx.rxbuflock);
ath9k_hw_abortpcurecv(ah);
ath9k_hw_setrxfilter(ah, 0);
- stopped = ath9k_hw_stopdmarecv(ah);
+ stopped = ath9k_hw_stopdmarecv(ah, &reset);
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
ath_edma_stop_recv(sc);
"confusing the DMA engine when we start RX up\n");
ATH_DBG_WARN_ON_ONCE(!stopped);
}
- return stopped;
+ return stopped && !reset;
}
void ath_flushrecv(struct ath_softc *sc)
{APL9_WORLD, CTL_ETSI, CTL_ETSI},
{APL3_FCCA, CTL_FCC, CTL_FCC},
+ {APL7_FCCA, CTL_FCC, CTL_FCC},
{APL1_ETSIC, CTL_FCC, CTL_ETSI},
{APL2_ETSIC, CTL_FCC, CTL_ETSI},
{APL2_APLD, CTL_FCC, NO_CTL},
MODULE_FIRMWARE("b43/ucode13.fw");
MODULE_FIRMWARE("b43/ucode14.fw");
MODULE_FIRMWARE("b43/ucode15.fw");
+MODULE_FIRMWARE("b43/ucode16_mimo.fw");
MODULE_FIRMWARE("b43/ucode5.fw");
MODULE_FIRMWARE("b43/ucode9.fw");
config IWLWIFI_LEGACY
- tristate "Intel Wireless Wifi legacy devices"
- depends on PCI && MAC80211
+ tristate
select FW_LOADER
select NEW_LEDS
select LEDS_CLASS
config IWL4965
tristate "Intel Wireless WiFi 4965AGN (iwl4965)"
- depends on IWLWIFI_LEGACY
+ depends on PCI && MAC80211
+ select IWLWIFI_LEGACY
---help---
This option enables support for
config IWL3945
tristate "Intel PRO/Wireless 3945ABG/BG Network Connection (iwl3945)"
- depends on IWLWIFI_LEGACY
+ depends on PCI && MAC80211
+ select IWLWIFI_LEGACY
---help---
Select to build the driver supporting the:
/* RSSI to dBm */
#define IWL39_RSSI_OFFSET 95
-#define IWL_DEFAULT_TX_POWER 0x0F
-
/*
* EEPROM related constants, enums, and structures.
*/
#define IWL4965_DEFAULT_TX_RETRY 15
-/* Limit range of txpower output target to be between these values */
-#define IWL4965_TX_POWER_TARGET_POWER_MIN (0) /* 0 dBm: 1 milliwatt */
-
/* EEPROM */
#define IWL4965_FIRST_AMPDU_QUEUE 10
hdr_len = ieee80211_hdrlen(fc);
- /* Find index into station table for destination station */
- sta_id = iwl_legacy_sta_id_or_broadcast(priv, ctx, info->control.sta);
- if (sta_id == IWL_INVALID_STATION) {
- IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n",
- hdr->addr1);
- goto drop_unlock;
+ /* For management frames use broadcast id to do not break aggregation */
+ if (!ieee80211_is_data(fc))
+ sta_id = ctx->bcast_sta_id;
+ else {
+ /* Find index into station table for destination station */
+ sta_id = iwl_legacy_sta_id_or_broadcast(priv, ctx, info->control.sta);
+
+ if (sta_id == IWL_INVALID_STATION) {
+ IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n",
+ hdr->addr1);
+ goto drop_unlock;
+ }
}
IWL_DEBUG_TX(priv, "station Id %d\n", sta_id);
q->read_ptr = iwl_legacy_queue_inc_wrap(q->read_ptr, q->n_bd)) {
tx_info = &txq->txb[txq->q.read_ptr];
- iwl4965_tx_status(priv, tx_info,
- txq_id >= IWL4965_FIRST_AMPDU_QUEUE);
+
+ if (WARN_ON_ONCE(tx_info->skb == NULL))
+ continue;
hdr = (struct ieee80211_hdr *)tx_info->skb->data;
- if (hdr && ieee80211_is_data_qos(hdr->frame_control))
+ if (ieee80211_is_data_qos(hdr->frame_control))
nfreed++;
+
+ iwl4965_tx_status(priv, tx_info,
+ txq_id >= IWL4965_FIRST_AMPDU_QUEUE);
tx_info->skb = NULL;
priv->cfg->ops->lib->txq_free_tfd(priv, txq);
struct ieee80211_channel *geo_ch;
struct ieee80211_rate *rates;
int i = 0;
+ s8 max_tx_power = 0;
if (priv->bands[IEEE80211_BAND_2GHZ].n_bitrates ||
priv->bands[IEEE80211_BAND_5GHZ].n_bitrates) {
geo_ch->flags |= ch->ht40_extension_channel;
- if (ch->max_power_avg > priv->tx_power_device_lmt)
- priv->tx_power_device_lmt = ch->max_power_avg;
+ if (ch->max_power_avg > max_tx_power)
+ max_tx_power = ch->max_power_avg;
} else {
geo_ch->flags |= IEEE80211_CHAN_DISABLED;
}
geo_ch->flags);
}
+ priv->tx_power_device_lmt = max_tx_power;
+ priv->tx_power_user_lmt = max_tx_power;
+ priv->tx_power_next = max_tx_power;
+
if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) &&
priv->cfg->sku & IWL_SKU_A) {
IWL_INFO(priv, "Incorrectly detected BG card as ABG. "
if (!priv->cfg->ops->lib->send_tx_power)
return -EOPNOTSUPP;
- if (tx_power < IWL4965_TX_POWER_TARGET_POWER_MIN) {
+ /* 0 dBm mean 1 milliwatt */
+ if (tx_power < 0) {
IWL_WARN(priv,
- "Requested user TXPOWER %d below lower limit %d.\n",
- tx_power,
- IWL4965_TX_POWER_TARGET_POWER_MIN);
+ "Requested user TXPOWER %d below 1 mW.\n",
+ tx_power);
return -EINVAL;
}
flags & EEPROM_CHANNEL_RADAR))
? "" : "not ");
- /* Set the tx_power_user_lmt to the highest power
- * supported by any channel */
- if (eeprom_ch_info[ch].max_power_avg >
- priv->tx_power_user_lmt)
- priv->tx_power_user_lmt =
- eeprom_ch_info[ch].max_power_avg;
-
ch_info++;
}
}
MODULE_PARM_DESC(led_mode, "0=system default, "
"1=On(RF On)/Off(RF Off), 2=blinking");
+/* Throughput OFF time(ms) ON time (ms)
+ * >300 25 25
+ * >200 to 300 40 40
+ * >100 to 200 55 55
+ * >70 to 100 65 65
+ * >50 to 70 75 75
+ * >20 to 50 85 85
+ * >10 to 20 95 95
+ * >5 to 10 110 110
+ * >1 to 5 130 130
+ * >0 to 1 167 167
+ * <=0 SOLID ON
+ */
static const struct ieee80211_tpt_blink iwl_blink[] = {
- { .throughput = 0 * 1024 - 1, .blink_time = 334 },
+ { .throughput = 0, .blink_time = 334 },
{ .throughput = 1 * 1024 - 1, .blink_time = 260 },
{ .throughput = 5 * 1024 - 1, .blink_time = 220 },
{ .throughput = 10 * 1024 - 1, .blink_time = 190 },
if (priv->blink_on == on && priv->blink_off == off)
return 0;
+ if (off == 0) {
+ /* led is SOLID_ON */
+ on = IWL_LED_SOLID;
+ }
+
IWL_DEBUG_LED(priv, "Led blink time compensation=%u\n",
priv->cfg->base_params->led_compensation);
led_cmd.on = iwl_legacy_blink_compensation(priv, on,
priv->force_reset[IWL_FW_RESET].reset_duration =
IWL_DELAY_NEXT_FORCE_FW_RELOAD;
-
- priv->tx_power_user_lmt = IWL_DEFAULT_TX_POWER;
- priv->tx_power_next = IWL_DEFAULT_TX_POWER;
-
if (eeprom->version < EEPROM_3945_EEPROM_VERSION) {
IWL_WARN(priv, "Unsupported EEPROM version: 0x%04X\n",
eeprom->version);
struct iwl_priv *priv = container_of(work, struct iwl_priv,
txpower_work);
+ mutex_lock(&priv->mutex);
+
/* If a scan happened to start before we got here
* then just return; the statistics notification will
* kick off another scheduled work to compensate for
* any temperature delta we missed here. */
if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
test_bit(STATUS_SCANNING, &priv->status))
- return;
-
- mutex_lock(&priv->mutex);
+ goto out;
/* Regardless of if we are associated, we must reconfigure the
* TX power since frames can be sent on non-radar channels while
/* Update last_temperature to keep is_calib_needed from running
* when it isn't needed... */
priv->last_temperature = priv->temperature;
-
+out:
mutex_unlock(&priv->mutex);
}
iwl_legacy_init_scan_params(priv);
- /* Set the tx_power_user_lmt to the lowest power level
- * this value will get overwritten by channel max power avg
- * from eeprom */
- priv->tx_power_user_lmt = IWL4965_TX_POWER_TARGET_POWER_MIN;
- priv->tx_power_next = IWL4965_TX_POWER_TARGET_POWER_MIN;
-
ret = iwl_legacy_init_channel_map(priv);
if (ret) {
IWL_ERR(priv, "initializing regulatory failed: %d\n", ret);
struct iwl_cfg iwl5300_agn_cfg = {
.name = "Intel(R) Ultimate N WiFi Link 5300 AGN",
IWL_DEVICE_5000,
+ /* at least EEPROM 0x11A has wrong info */
+ .valid_tx_ant = ANT_ABC, /* .cfg overwrite */
+ .valid_rx_ant = ANT_ABC, /* .cfg overwrite */
.ht_params = &iwl5000_ht_params,
};
struct ieee80211_channel *channel = conf->channel;
const struct iwl_channel_info *ch_info;
int ret = 0;
- bool ht_changed[NUM_IWL_RXON_CTX] = {};
IWL_DEBUG_MAC80211(priv, "changed %#x", changed);
for_each_context(priv, ctx) {
/* Configure HT40 channels */
- if (ctx->ht.enabled != conf_is_ht(conf)) {
+ if (ctx->ht.enabled != conf_is_ht(conf))
ctx->ht.enabled = conf_is_ht(conf);
- ht_changed[ctx->ctxid] = true;
- }
if (ctx->ht.enabled) {
if (conf_is_ht40_minus(conf)) {
if (!memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging)))
continue;
iwlagn_commit_rxon(priv, ctx);
- if (ht_changed[ctx->ctxid])
- iwlagn_update_qos(priv, ctx);
}
out:
mutex_unlock(&priv->mutex);
hdr_len = ieee80211_hdrlen(fc);
- /* Find index into station table for destination station */
- sta_id = iwl_sta_id_or_broadcast(priv, ctx, info->control.sta);
- if (sta_id == IWL_INVALID_STATION) {
- IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n",
- hdr->addr1);
- goto drop_unlock;
+ /* For management frames use broadcast id to do not break aggregation */
+ if (!ieee80211_is_data(fc))
+ sta_id = ctx->bcast_sta_id;
+ else {
+ /* Find index into station table for destination station */
+ sta_id = iwl_sta_id_or_broadcast(priv, ctx, info->control.sta);
+ if (sta_id == IWL_INVALID_STATION) {
+ IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n",
+ hdr->addr1);
+ goto drop_unlock;
+ }
}
IWL_DEBUG_TX(priv, "station Id %d\n", sta_id);
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
tx_info = &txq->txb[txq->q.read_ptr];
- iwlagn_tx_status(priv, tx_info,
- txq_id >= IWLAGN_FIRST_AMPDU_QUEUE);
+
+ if (WARN_ON_ONCE(tx_info->skb == NULL))
+ continue;
hdr = (struct ieee80211_hdr *)tx_info->skb->data;
- if (hdr && ieee80211_is_data_qos(hdr->frame_control))
+ if (ieee80211_is_data_qos(hdr->frame_control))
nfreed++;
+
+ iwlagn_tx_status(priv, tx_info,
+ txq_id >= IWLAGN_FIRST_AMPDU_QUEUE);
tx_info->skb = NULL;
if (priv->cfg->ops->lib->txq_inval_byte_cnt_tbl)
struct mwl8k_priv {
struct ieee80211_hw *hw;
struct pci_dev *pdev;
+ int irq;
struct mwl8k_device_info *device_info;
rc = request_irq(priv->pdev->irq, mwl8k_interrupt,
IRQF_SHARED, MWL8K_NAME, hw);
if (rc) {
+ priv->irq = -1;
wiphy_err(hw->wiphy, "failed to register IRQ handler\n");
return -EIO;
}
+ priv->irq = priv->pdev->irq;
/* Enable TX reclaim and RX tasklets. */
tasklet_enable(&priv->poll_tx_task);
if (rc) {
iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
free_irq(priv->pdev->irq, hw);
+ priv->irq = -1;
tasklet_disable(&priv->poll_tx_task);
tasklet_disable(&priv->poll_rx_task);
}
/* Disable interrupts */
iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
- free_irq(priv->pdev->irq, hw);
+ if (priv->irq != -1) {
+ free_irq(priv->pdev->irq, hw);
+ priv->irq = -1;
+ }
/* Stop finalize join worker */
cancel_work_sync(&priv->finalize_join_worker);
struct p54_tx_info *p54info;
struct p54_hdr *hdr;
struct p54_tx_data *txhdr;
- unsigned int padding, len, extra_len;
+ unsigned int padding, len, extra_len = 0;
int i, j, ridx;
u16 hdr_flags = 0, aid = 0;
u8 rate, queue = 0, crypt_offset = 0;
const struct parport_pc_via_data *via)
{
short inta_addr[6] = { 0x2A0, 0x2C0, 0x220, 0x240, 0x1E0 };
- struct resource *base_res;
u32 ite8872set;
u32 ite8872_lpt, ite8872_lpthi;
u8 ite8872_irq, type;
/* make sure which one chip */
for (i = 0; i < 5; i++) {
- base_res = request_region(inta_addr[i], 32, "it887x");
- if (base_res) {
+ if (request_region(inta_addr[i], 32, "it887x")) {
int test;
pci_write_config_dword(pdev, 0x60,
0xe5000000 | inta_addr[i]);
test = inb(inta_addr[i]);
if (test != 0xff)
break;
- release_region(inta_addr[i], 0x8);
+ release_region(inta_addr[i], 32);
}
}
if (i >= 5) {
/*
* Release the resource so that parport_pc_probe_port can get it.
*/
- release_resource(base_res);
+ release_region(inta_addr[i], 32);
if (parport_pc_probe_port(ite8872_lpt, ite8872_lpthi,
irq, PARPORT_DMA_NONE, &pdev->dev, 0)) {
printk(KERN_INFO
depends on HOTPLUG
default y
-select NLS if (DMI || ACPI)
+config PCI_LABEL
+ def_bool y if (DMI || ACPI)
+ select NLS
# ACPI Related PCI FW Functions
# ACPI _DSM provided firmware instance and string name
#
-obj-$(CONFIG_ACPI) += pci-acpi.o pci-label.o
+obj-$(CONFIG_ACPI) += pci-acpi.o
# SMBIOS provided firmware instance and labels
-obj-$(CONFIG_DMI) += pci-label.o
+obj-$(CONFIG_PCI_LABEL) += pci-label.o
# Cardbus & CompactPCI use setup-bus
obj-$(CONFIG_HOTPLUG) += setup-bus.o
static struct iova_domain reserved_iova_list;
static struct lock_class_key reserved_rbtree_key;
-static void dmar_init_reserved_ranges(void)
+static int dmar_init_reserved_ranges(void)
{
struct pci_dev *pdev = NULL;
struct iova *iova;
/* IOAPIC ranges shouldn't be accessed by DMA */
iova = reserve_iova(&reserved_iova_list, IOVA_PFN(IOAPIC_RANGE_START),
IOVA_PFN(IOAPIC_RANGE_END));
- if (!iova)
+ if (!iova) {
printk(KERN_ERR "Reserve IOAPIC range failed\n");
+ return -ENODEV;
+ }
/* Reserve all PCI MMIO to avoid peer-to-peer access */
for_each_pci_dev(pdev) {
iova = reserve_iova(&reserved_iova_list,
IOVA_PFN(r->start),
IOVA_PFN(r->end));
- if (!iova)
+ if (!iova) {
printk(KERN_ERR "Reserve iova failed\n");
+ return -ENODEV;
+ }
}
}
-
+ return 0;
}
static void domain_reserve_special_ranges(struct dmar_domain *domain)
ret = iommu_attach_domain(domain, iommu);
if (ret) {
- domain_exit(domain);
+ free_domain_mem(domain);
goto error;
}
return 0;
}
-int __init init_dmars(void)
+static int __init init_dmars(int force_on)
{
struct dmar_drhd_unit *drhd;
struct dmar_rmrr_unit *rmrr;
* enable translation
*/
for_each_drhd_unit(drhd) {
- if (drhd->ignored)
+ if (drhd->ignored) {
+ /*
+ * we always have to disable PMRs or DMA may fail on
+ * this device
+ */
+ if (force_on)
+ iommu_disable_protect_mem_regions(drhd->iommu);
continue;
+ }
iommu = drhd->iommu;
iommu_flush_write_buffer(iommu);
if (!domain)
return 0;
- if (action == BUS_NOTIFY_UNBOUND_DRIVER && !iommu_pass_through)
+ if (action == BUS_NOTIFY_UNBOUND_DRIVER && !iommu_pass_through) {
domain_remove_one_dev_info(domain, pdev);
+ if (!(domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) &&
+ !(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY) &&
+ list_empty(&domain->devices))
+ domain_exit(domain);
+ }
+
return 0;
}
if (no_iommu || dmar_disabled)
return -ENODEV;
- iommu_init_mempool();
- dmar_init_reserved_ranges();
+ if (iommu_init_mempool()) {
+ if (force_on)
+ panic("tboot: Failed to initialize iommu memory\n");
+ return -ENODEV;
+ }
+
+ if (dmar_init_reserved_ranges()) {
+ if (force_on)
+ panic("tboot: Failed to reserve iommu ranges\n");
+ return -ENODEV;
+ }
init_no_remapping_devices();
- ret = init_dmars();
+ ret = init_dmars(force_on);
if (ret) {
if (force_on)
panic("tboot: Failed to initialize DMARs\n");
domain->iommu_count--;
domain_update_iommu_cap(domain);
spin_unlock_irqrestore(&domain->iommu_lock, tmp_flags);
+
+ spin_lock_irqsave(&iommu->lock, tmp_flags);
+ clear_bit(domain->id, iommu->domain_ids);
+ iommu->domains[domain->id] = NULL;
+ spin_unlock_irqrestore(&iommu->lock, tmp_flags);
}
spin_unlock_irqrestore(&device_domain_lock, flags);
pte = dmar_domain->pgd;
if (dma_pte_present(pte)) {
- free_pgtable_page(dmar_domain->pgd);
dmar_domain->pgd = (struct dma_pte *)
phys_to_virt(dma_pte_addr(pte));
+ free_pgtable_page(pte);
}
dmar_domain->agaw--;
}
#endif /* !CONFIG_SUSPEND */
-#ifdef CONFIG_HIBERNATION
+#ifdef CONFIG_HIBERNATE_CALLBACKS
static int pci_pm_freeze(struct device *dev)
{
return error;
}
-#else /* !CONFIG_HIBERNATION */
+#else /* !CONFIG_HIBERNATE_CALLBACKS */
#define pci_pm_freeze NULL
#define pci_pm_freeze_noirq NULL
#define pci_pm_restore NULL
#define pci_pm_restore_noirq NULL
-#endif /* !CONFIG_HIBERNATION */
+#endif /* !CONFIG_HIBERNATE_CALLBACKS */
#ifdef CONFIG_PM_RUNTIME
c = p_dev->function_config;
if (!(c->state & CONFIG_LOCKED)) {
- dev_dbg(&p_dev->dev, "Configuration isn't't locked\n");
+ dev_dbg(&p_dev->dev, "Configuration isn't locked\n");
mutex_unlock(&s->ops_mutex);
return -EACCES;
}
#include <mach/balloon3.h>
+#include <asm/mach-types.h>
+
#include "soc_common.h"
/*
{
int ret;
+ if (!machine_is_balloon3())
+ return -ENODEV;
+
balloon3_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!balloon3_pcmcia_device)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(irqs); i++) {
if (irqs[i].sock != skt->nr)
continue;
- if (gpio_request(IRQ_TO_GPIO(irqs[i].irq), irqs[i].str) < 0) {
+ if (gpio_request(irq_to_gpio(irqs[i].irq), irqs[i].str) < 0) {
pr_err("%s: sock %d unable to request gpio %d\n",
- __func__, skt->nr, IRQ_TO_GPIO(irqs[i].irq));
+ __func__, skt->nr, irq_to_gpio(irqs[i].irq));
ret = -EBUSY;
goto error;
}
- if (gpio_direction_input(IRQ_TO_GPIO(irqs[i].irq)) < 0) {
+ if (gpio_direction_input(irq_to_gpio(irqs[i].irq)) < 0) {
pr_err("%s: sock %d unable to set input gpio %d\n",
- __func__, skt->nr, IRQ_TO_GPIO(irqs[i].irq));
+ __func__, skt->nr, irq_to_gpio(irqs[i].irq));
ret = -EINVAL;
goto error;
}
error:
for (; i >= 0; i--) {
- gpio_free(IRQ_TO_GPIO(irqs[i].irq));
+ gpio_free(irq_to_gpio(irqs[i].irq));
}
return (ret);
}
/* free allocated gpio's */
gpio_free(GPIO_PRDY);
for (i = 0; i < ARRAY_SIZE(irqs); i++)
- gpio_free(IRQ_TO_GPIO(irqs[i].irq));
+ gpio_free(irq_to_gpio(irqs[i].irq));
}
static unsigned long trizeps_pcmcia_status[2];
{
int ret;
+ if (!machine_is_trizeps4() && !machine_is_trizeps4wl())
+ return -ENODEV;
+
trizeps_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!trizeps_pcmcia_device)
return -ENOMEM;
depends on ACPI
depends on BACKLIGHT_CLASS_DEVICE
depends on RFKILL
- depends on SERIO_I8042
+ depends on INPUT && SERIO_I8042
+ select INPUT_SPARSEKMAP
---help---
This is a driver for laptops built by MSI (MICRO-STAR
INTERNATIONAL):
#define ACERWMID_EVENT_GUID "676AA15E-6A47-4D9F-A2CC-1E6D18D14026"
MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
-MODULE_ALIAS("wmi:6AF4F258-B401-42Fd-BE91-3D4AC2D7C0D3");
+MODULE_ALIAS("wmi:6AF4F258-B401-42FD-BE91-3D4AC2D7C0D3");
MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026");
enum acer_wmi_event_ids {
if (!asus->inputdev)
return -ENOMEM;
- asus->inputdev->name = asus->driver->input_phys;
- asus->inputdev->phys = asus->driver->input_name;
+ asus->inputdev->name = asus->driver->input_name;
+ asus->inputdev->phys = asus->driver->input_phys;
asus->inputdev->id.bustype = BUS_HOST;
asus->inputdev->dev.parent = &asus->platform_device->dev;
return true;
}
-static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc)
+static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
{
+ struct pci_dev *port;
struct pci_dev *dev;
struct pci_bus *bus;
bool blocked = eeepc_wlan_rfkill_blocked(eeepc);
mutex_lock(&eeepc->hotplug_lock);
if (eeepc->hotplug_slot) {
- bus = pci_find_bus(0, 1);
+ port = acpi_get_pci_dev(handle);
+ if (!port) {
+ pr_warning("Unable to find port\n");
+ goto out_unlock;
+ }
+
+ bus = port->subordinate;
+
if (!bus) {
- pr_warning("Unable to find PCI bus 1?\n");
+ pr_warning("Unable to find PCI bus?\n");
goto out_unlock;
}
pr_err("Unable to read PCI config space?\n");
goto out_unlock;
}
+
absent = (l == 0xffffffff);
if (blocked != absent) {
mutex_unlock(&eeepc->hotplug_lock);
}
+static void eeepc_rfkill_hotplug_update(struct eeepc_laptop *eeepc, char *node)
+{
+ acpi_status status = AE_OK;
+ acpi_handle handle;
+
+ status = acpi_get_handle(NULL, node, &handle);
+
+ if (ACPI_SUCCESS(status))
+ eeepc_rfkill_hotplug(eeepc, handle);
+}
+
static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
{
struct eeepc_laptop *eeepc = data;
if (event != ACPI_NOTIFY_BUS_CHECK)
return;
- eeepc_rfkill_hotplug(eeepc);
+ eeepc_rfkill_hotplug(eeepc, handle);
}
static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc,
eeepc);
if (ACPI_FAILURE(status))
pr_warning("Failed to register notify on %s\n", node);
+ /*
+ * Refresh pci hotplug in case the rfkill state was
+ * changed during setup.
+ */
+ eeepc_rfkill_hotplug(eeepc, handle);
} else
return -ENODEV;
if (ACPI_FAILURE(status))
pr_err("Error removing rfkill notify handler %s\n",
node);
+ /*
+ * Refresh pci hotplug in case the rfkill
+ * state was changed after
+ * eeepc_unregister_rfkill_notifier()
+ */
+ eeepc_rfkill_hotplug(eeepc, handle);
}
}
rfkill_destroy(eeepc->wlan_rfkill);
eeepc->wlan_rfkill = NULL;
}
- /*
- * Refresh pci hotplug in case the rfkill state was changed after
- * eeepc_unregister_rfkill_notifier()
- */
- eeepc_rfkill_hotplug(eeepc);
+
if (eeepc->hotplug_slot)
pci_hp_deregister(eeepc->hotplug_slot);
eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
- /*
- * Refresh pci hotplug in case the rfkill state was changed during
- * setup.
- */
- eeepc_rfkill_hotplug(eeepc);
exit:
if (result && result != -ENODEV)
struct eeepc_laptop *eeepc = dev_get_drvdata(device);
/* Refresh both wlan rfkill state and pci hotplug */
- if (eeepc->wlan_rfkill)
- eeepc_rfkill_hotplug(eeepc);
+ if (eeepc->wlan_rfkill) {
+ eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P5");
+ eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P6");
+ eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P7");
+ }
if (eeepc->bluetooth_rfkill)
rfkill_set_sw_state(eeepc->bluetooth_rfkill,
{ KE_KEY, 0x82, { KEY_CAMERA } },
{ KE_KEY, 0x83, { KEY_CAMERA_ZOOMIN } },
{ KE_KEY, 0x88, { KEY_WLAN } },
+ { KE_KEY, 0xbd, { KEY_CAMERA } },
{ KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */
{ KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */
+ { KE_KEY, 0xe8, { KEY_SCREENLOCK } },
{ KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } },
{ KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } },
{ KE_KEY, 0xec, { KEY_CAMERA_UP } },
u32 trigger_type;
};
+static void pmic_program_irqtype(int gpio, int type)
+{
+ if (type & IRQ_TYPE_EDGE_RISING)
+ intel_scu_ipc_update_register(GPIO0 + gpio, 0x20, 0x20);
+ else
+ intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x20);
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ intel_scu_ipc_update_register(GPIO0 + gpio, 0x10, 0x10);
+ else
+ intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x10);
+};
+
static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
if (offset > 8) {
return pg->irq_base + offset;
}
+static void pmic_bus_lock(struct irq_data *data)
+{
+ struct pmic_gpio *pg = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&pg->buslock);
+}
+
+static void pmic_bus_sync_unlock(struct irq_data *data)
+{
+ struct pmic_gpio *pg = irq_data_get_irq_chip_data(data);
+
+ if (pg->update_type) {
+ unsigned int gpio = pg->update_type & ~GPIO_UPDATE_TYPE;
+
+ pmic_program_irqtype(gpio, pg->trigger_type);
+ pg->update_type = 0;
+ }
+ mutex_unlock(&pg->buslock);
+}
+
/* the gpiointr register is read-clear, so just do nothing. */
static void pmic_irq_unmask(struct irq_data *data) { }
static void pmic_irq_mask(struct irq_data *data) { }
static struct irq_chip pmic_irqchip = {
- .name = "PMIC-GPIO",
- .irq_mask = pmic_irq_mask,
- .irq_unmask = pmic_irq_unmask,
- .irq_set_type = pmic_irq_type,
+ .name = "PMIC-GPIO",
+ .irq_mask = pmic_irq_mask,
+ .irq_unmask = pmic_irq_unmask,
+ .irq_set_type = pmic_irq_type,
+ .irq_bus_lock = pmic_bus_lock,
+ .irq_bus_sync_unlock = pmic_bus_sync_unlock,
};
static irqreturn_t pmic_irq_handler(int irq, void *data)
},
.callback = dmi_check_cb,
},
+ {
+ .ident = "R410 Plus",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "R410P"),
+ DMI_MATCH(DMI_BOARD_NAME, "R460"),
+ },
+ .callback = dmi_check_cb,
+ },
{
.ident = "R518",
.matches = {
.callback = dmi_check_cb,
},
{
- .ident = "N150/N210/N220",
+ .ident = "N150/N210/N220/N230",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR,
"SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
- DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220/N230"),
+ DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220/N230"),
},
.callback = dmi_check_cb,
},
/* create a backlight device to talk to this one */
memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_PLATFORM;
props.max_brightness = sabi_config->max_brightness;
backlight_device = backlight_device_register("samsung", &sdev->dev,
NULL, &backlight_ops,
"1 for 30 seconds, 2 for 60 seconds and 3 to disable timeout "
"(default: 0)");
+static void sony_nc_kbd_backlight_resume(void);
+
enum sony_nc_rfkill {
SONY_WIFI,
SONY_BLUETOOTH,
if (!handles)
return -ENOMEM;
- sysfs_attr_init(&handles->devattr.attr);
- handles->devattr.attr.name = "handles";
- handles->devattr.attr.mode = S_IRUGO;
- handles->devattr.show = sony_nc_handles_show;
-
for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {
if (!acpi_callsetfunc(sony_nc_acpi_handle,
"SN00", i + 0x20, &result)) {
}
}
- /* allow reading capabilities via sysfs */
- if (device_create_file(&pd->dev, &handles->devattr)) {
- kfree(handles);
- handles = NULL;
- return -1;
+ if (debug) {
+ sysfs_attr_init(&handles->devattr.attr);
+ handles->devattr.attr.name = "handles";
+ handles->devattr.attr.mode = S_IRUGO;
+ handles->devattr.show = sony_nc_handles_show;
+
+ /* allow reading capabilities via sysfs */
+ if (device_create_file(&pd->dev, &handles->devattr)) {
+ kfree(handles);
+ handles = NULL;
+ return -1;
+ }
}
return 0;
static int sony_nc_handles_cleanup(struct platform_device *pd)
{
if (handles) {
- device_remove_file(&pd->dev, &handles->devattr);
+ if (debug)
+ device_remove_file(&pd->dev, &handles->devattr);
kfree(handles);
handles = NULL;
}
static int sony_find_snc_handle(int handle)
{
int i;
+
+ /* not initialized yet, return early */
+ if (!handles)
+ return -1;
+
for (i = 0; i < 0x10; i++) {
if (handles->cap[i] == handle) {
dprintk("found handle 0x%.4x (offset: 0x%.2x)\n",
/*
* Backlight device
*/
+struct sony_backlight_props {
+ struct backlight_device *dev;
+ int handle;
+ u8 offset;
+ u8 maxlvl;
+};
+struct sony_backlight_props sony_bl_props;
+
static int sony_backlight_update_status(struct backlight_device *bd)
{
return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
{
int result;
int *handle = (int *)bl_get_data(bd);
+ struct sony_backlight_props *sdev =
+ (struct sony_backlight_props *)bl_get_data(bd);
- sony_call_snc_handle(*handle, 0x0200, &result);
+ sony_call_snc_handle(sdev->handle, 0x0200, &result);
- return result & 0xff;
+ return (result & 0xff) - sdev->offset;
}
static int sony_nc_update_status_ng(struct backlight_device *bd)
{
int value, result;
int *handle = (int *)bl_get_data(bd);
+ struct sony_backlight_props *sdev =
+ (struct sony_backlight_props *)bl_get_data(bd);
- value = bd->props.brightness;
- sony_call_snc_handle(*handle, 0x0100 | (value << 16), &result);
+ value = bd->props.brightness + sdev->offset;
+ if (sony_call_snc_handle(sdev->handle, 0x0100 | (value << 16), &result))
+ return -EIO;
- return sony_nc_get_brightness_ng(bd);
+ return value;
}
static const struct backlight_ops sony_backlight_ops = {
.update_status = sony_nc_update_status_ng,
.get_brightness = sony_nc_get_brightness_ng,
};
-static int backlight_ng_handle;
-static struct backlight_device *sony_backlight_device;
/*
* New SNC-only Vaios event mapping to driver known keys
/* re-read rfkill state */
sony_nc_rfkill_update();
+ /* restore kbd backlight states */
+ sony_nc_kbd_backlight_resume();
+
return 0;
}
#define KBDBL_HANDLER 0x137
#define KBDBL_PRESENT 0xB00
#define SET_MODE 0xC00
+#define SET_STATE 0xD00
#define SET_TIMEOUT 0xE00
struct kbd_backlight {
(value << 0x10) | SET_MODE, &result))
return -EIO;
+ /* Try to turn the light on/off immediately */
+ sony_call_snc_handle(KBDBL_HANDLER, (value << 0x10) | SET_STATE,
+ &result);
+
kbdbl_handle->mode = value;
return 0;
{
int result;
- if (sony_call_snc_handle(0x137, KBDBL_PRESENT, &result))
+ if (sony_call_snc_handle(KBDBL_HANDLER, KBDBL_PRESENT, &result))
return 0;
if (!(result & 0x02))
return 0;
static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd)
{
if (kbdbl_handle) {
+ int result;
+
device_remove_file(&pd->dev, &kbdbl_handle->mode_attr);
device_remove_file(&pd->dev, &kbdbl_handle->timeout_attr);
+
+ /* restore the default hw behaviour */
+ sony_call_snc_handle(KBDBL_HANDLER, 0x1000 | SET_MODE, &result);
+ sony_call_snc_handle(KBDBL_HANDLER, SET_TIMEOUT, &result);
+
kfree(kbdbl_handle);
}
return 0;
}
+static void sony_nc_kbd_backlight_resume(void)
+{
+ int ignore = 0;
+
+ if (!kbdbl_handle)
+ return;
+
+ if (kbdbl_handle->mode == 0)
+ sony_call_snc_handle(KBDBL_HANDLER, SET_MODE, &ignore);
+
+ if (kbdbl_handle->timeout != 0)
+ sony_call_snc_handle(KBDBL_HANDLER,
+ (kbdbl_handle->timeout << 0x10) | SET_TIMEOUT,
+ &ignore);
+}
+
+static void sony_nc_backlight_ng_read_limits(int handle,
+ struct sony_backlight_props *props)
+{
+ int offset;
+ acpi_status status;
+ u8 brlvl, i;
+ u8 min = 0xff, max = 0x00;
+ struct acpi_object_list params;
+ union acpi_object in_obj;
+ union acpi_object *lvl_enum;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+
+ props->handle = handle;
+ props->offset = 0;
+ props->maxlvl = 0xff;
+
+ offset = sony_find_snc_handle(handle);
+ if (offset < 0)
+ return;
+
+ /* try to read the boundaries from ACPI tables, if we fail the above
+ * defaults should be reasonable
+ */
+ params.count = 1;
+ params.pointer = &in_obj;
+ in_obj.type = ACPI_TYPE_INTEGER;
+ in_obj.integer.value = offset;
+ status = acpi_evaluate_object(sony_nc_acpi_handle, "SN06", ¶ms,
+ &buffer);
+ if (ACPI_FAILURE(status))
+ return;
+
+ lvl_enum = (union acpi_object *) buffer.pointer;
+ if (!lvl_enum) {
+ pr_err("No SN06 return object.");
+ return;
+ }
+ if (lvl_enum->type != ACPI_TYPE_BUFFER) {
+ pr_err("Invalid SN06 return object 0x%.2x\n",
+ lvl_enum->type);
+ goto out_invalid;
+ }
+
+ /* the buffer lists brightness levels available, brightness levels are
+ * from 0 to 8 in the array, other values are used by ALS control.
+ */
+ for (i = 0; i < 9 && i < lvl_enum->buffer.length; i++) {
+
+ brlvl = *(lvl_enum->buffer.pointer + i);
+ dprintk("Brightness level: %d\n", brlvl);
+
+ if (!brlvl)
+ break;
+
+ if (brlvl > max)
+ max = brlvl;
+ if (brlvl < min)
+ min = brlvl;
+ }
+ props->offset = min;
+ props->maxlvl = max;
+ dprintk("Brightness levels: min=%d max=%d\n", props->offset,
+ props->maxlvl);
+
+out_invalid:
+ kfree(buffer.pointer);
+ return;
+}
+
static void sony_nc_backlight_setup(void)
{
acpi_handle unused;
struct backlight_properties props;
if (sony_find_snc_handle(0x12f) != -1) {
- backlight_ng_handle = 0x12f;
ops = &sony_backlight_ng_ops;
- max_brightness = 0xff;
+ sony_nc_backlight_ng_read_limits(0x12f, &sony_bl_props);
+ max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
} else if (sony_find_snc_handle(0x137) != -1) {
- backlight_ng_handle = 0x137;
ops = &sony_backlight_ng_ops;
- max_brightness = 0xff;
+ sony_nc_backlight_ng_read_limits(0x137, &sony_bl_props);
+ max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
} else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",
&unused))) {
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = max_brightness;
- sony_backlight_device = backlight_device_register("sony", NULL,
- &backlight_ng_handle,
- ops, &props);
+ sony_bl_props.dev = backlight_device_register("sony", NULL,
+ &sony_bl_props,
+ ops, &props);
- if (IS_ERR(sony_backlight_device)) {
- pr_warning(DRV_PFX "unable to register backlight device\n");
- sony_backlight_device = NULL;
+ if (IS_ERR(sony_bl_props.dev)) {
+ pr_warn(DRV_PFX "unable to register backlight device\n");
+ sony_bl_props.dev = NULL;
} else
- sony_backlight_device->props.brightness =
- ops->get_brightness(sony_backlight_device);
+ sony_bl_props.dev->props.brightness =
+ ops->get_brightness(sony_bl_props.dev);
}
static void sony_nc_backlight_cleanup(void)
{
- if (sony_backlight_device)
- backlight_device_unregister(sony_backlight_device);
+ if (sony_bl_props.dev)
+ backlight_device_unregister(sony_bl_props.dev);
}
static int sony_nc_add(struct acpi_device *device)
mutex_lock(&spic_dev.lock);
switch (cmd) {
case SONYPI_IOCGBRT:
- if (sony_backlight_device == NULL) {
+ if (sony_bl_props.dev == NULL) {
ret = -EIO;
break;
}
ret = -EFAULT;
break;
case SONYPI_IOCSBRT:
- if (sony_backlight_device == NULL) {
+ if (sony_bl_props.dev == NULL) {
ret = -EIO;
break;
}
break;
}
/* sync the backlight device status */
- sony_backlight_device->props.brightness =
- sony_backlight_get_brightness(sony_backlight_device);
+ sony_bl_props.dev->props.brightness =
+ sony_backlight_get_brightness(sony_bl_props.dev);
break;
case SONYPI_IOCGBAT1CAP:
if (ec_read16(SONYPI_BAT1_FULL, &val16)) {
};
/* ACPI HIDs */
-#define TPACPI_ACPI_HKEY_HID "IBM0068"
+#define TPACPI_ACPI_IBM_HKEY_HID "IBM0068"
+#define TPACPI_ACPI_LENOVO_HKEY_HID "LEN0068"
#define TPACPI_ACPI_EC_HID "PNP0C09"
/* Input IDs */
}
static const struct acpi_device_id ibm_htk_device_ids[] = {
- {TPACPI_ACPI_HKEY_HID, 0},
+ {TPACPI_ACPI_IBM_HKEY_HID, 0},
+ {TPACPI_ACPI_LENOVO_HKEY_HID, 0},
{"", 0},
};
tpacpi_is_fw_digit(s[1]) &&
s[2] == t && s[3] == 'T' &&
tpacpi_is_fw_digit(s[4]) &&
- tpacpi_is_fw_digit(s[5]) &&
- s[6] == 'W' && s[7] == 'W';
+ tpacpi_is_fw_digit(s[5]);
}
/* returns 0 - probe ok, or < 0 - probe error.
__setup("riohdid=", rio_hdid_setup);
-void rio_register_mport(struct rio_mport *port)
+int rio_register_mport(struct rio_mport *port)
{
if (next_portid >= RIO_MAX_MPORTS) {
pr_err("RIO: reached specified max number of mports\n");
- return;
+ return 1;
}
port->id = next_portid++;
port->host_deviceid = rio_get_hdid(port->id);
list_add_tail(&port->node, &rio_mports);
+ return 0;
}
EXPORT_SYMBOL_GPL(rio_local_get_device_id);
DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1616, idtg2_switch_init);
DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTVPS1616, idtg2_switch_init);
DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTSPS1616, idtg2_switch_init);
+DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1432, idtg2_switch_init);
err = __rtc_read_alarm(rtc, &alrm);
if (!err && !rtc_valid_tm(&alrm.time))
- rtc_set_alarm(rtc, &alrm);
+ rtc_initialize_alarm(rtc, &alrm);
strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
dev_set_name(&rtc->dev, "rtc%d", id);
}
EXPORT_SYMBOL_GPL(rtc_set_alarm);
+/* Called once per device from rtc_device_register */
+int rtc_initialize_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
+{
+ int err;
+
+ err = rtc_valid_tm(&alarm->time);
+ if (err != 0)
+ return err;
+
+ err = mutex_lock_interruptible(&rtc->ops_lock);
+ if (err)
+ return err;
+
+ rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time);
+ rtc->aie_timer.period = ktime_set(0, 0);
+ if (alarm->enabled) {
+ rtc->aie_timer.enabled = 1;
+ timerqueue_add(&rtc->timerqueue, &rtc->aie_timer.node);
+ }
+ mutex_unlock(&rtc->ops_lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtc_initialize_alarm);
+
+
+
int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled)
{
int err = mutex_lock_interruptible(&rtc->ops_lock);
bfin_rtc_int_set_alarm(rtc);
else
bfin_rtc_int_clear(~(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY));
+
+ return 0;
}
static int bfin_rtc_read_time(struct device *dev, struct rtc_time *tm)
}
clk_disable(rtap->clk);
+ platform_set_drvdata(pdev, rtap);
rtap->rtc = rtc_device_register("coh901331", &pdev->dev, &coh901331_ops,
THIS_MODULE);
if (IS_ERR(rtap->rtc)) {
goto out_no_rtc;
}
- platform_set_drvdata(pdev, rtap);
-
return 0;
out_no_rtc:
+ platform_set_drvdata(pdev, NULL);
out_no_clk_enable:
clk_put(rtap->clk);
out_no_clk:
goto out_irq;
}
+ dev_set_drvdata(&pdev->dev, info);
+
info->rtc_dev = rtc_device_register("max8925-rtc", &pdev->dev,
&max8925_rtc_ops, THIS_MODULE);
ret = PTR_ERR(info->rtc_dev);
goto out_rtc;
}
- dev_set_drvdata(&pdev->dev, info);
platform_set_drvdata(pdev, info);
return 0;
}, {
.name = "mc13892-rtc",
},
+ { }
};
static struct platform_driver mc13xxx_rtc_driver = {
return 0;
fail2:
- free_irq(omap_rtc_timer, NULL);
+ free_irq(omap_rtc_timer, rtc);
fail1:
rtc_device_unregister(rtc);
fail0:
/* do not clear AIE here, it may be needed for wake */
- s3c_rtc_setpie(dev, 0);
free_irq(s3c_rtc_alarmno, rtc_dev);
free_irq(s3c_rtc_tickno, rtc_dev);
}
platform_set_drvdata(dev, NULL);
rtc_device_unregister(rtc);
- s3c_rtc_setpie(&dev->dev, 0);
s3c_rtc_setaie(&dev->dev, 0);
clk_disable(rtc_clk);
static int dasd_open(struct block_device *bdev, fmode_t mode)
{
- struct dasd_block *block = bdev->bd_disk->private_data;
struct dasd_device *base;
int rc;
- if (!block)
+ base = dasd_device_from_gendisk(bdev->bd_disk);
+ if (!base)
return -ENODEV;
- base = block->base;
- atomic_inc(&block->open_count);
+ atomic_inc(&base->block->open_count);
if (test_bit(DASD_FLAG_OFFLINE, &base->flags)) {
rc = -ENODEV;
goto unlock;
goto out;
}
+ dasd_put_device(base);
return 0;
out:
module_put(base->discipline->owner);
unlock:
- atomic_dec(&block->open_count);
+ atomic_dec(&base->block->open_count);
+ dasd_put_device(base);
return rc;
}
static int dasd_release(struct gendisk *disk, fmode_t mode)
{
- struct dasd_block *block = disk->private_data;
+ struct dasd_device *base;
- atomic_dec(&block->open_count);
- module_put(block->base->discipline->owner);
+ base = dasd_device_from_gendisk(disk);
+ if (!base)
+ return -ENODEV;
+
+ atomic_dec(&base->block->open_count);
+ module_put(base->discipline->owner);
+ dasd_put_device(base);
return 0;
}
*/
static int dasd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
- struct dasd_block *block;
struct dasd_device *base;
- block = bdev->bd_disk->private_data;
- if (!block)
+ base = dasd_device_from_gendisk(bdev->bd_disk);
+ if (!base)
return -ENODEV;
- base = block->base;
if (!base->discipline ||
- !base->discipline->fill_geometry)
+ !base->discipline->fill_geometry) {
+ dasd_put_device(base);
return -EINVAL;
-
- base->discipline->fill_geometry(block, geo);
- geo->start = get_start_sect(bdev) >> block->s2b_shift;
+ }
+ base->discipline->fill_geometry(base->block, geo);
+ geo->start = get_start_sect(bdev) >> base->block->s2b_shift;
+ dasd_put_device(base);
return 0;
}
dasd_set_target_state(device, DASD_STATE_NEW);
/* dasd_delete_device destroys the device reference. */
block = device->block;
- device->block = NULL;
dasd_delete_device(device);
/*
* life cycle of block is bound to device, so delete it after
dasd_set_target_state(device, DASD_STATE_NEW);
/* dasd_delete_device destroys the device reference. */
block = device->block;
- device->block = NULL;
dasd_delete_device(device);
/*
* life cycle of block is bound to device, so delete it after
return device;
}
+void dasd_add_link_to_gendisk(struct gendisk *gdp, struct dasd_device *device)
+{
+ struct dasd_devmap *devmap;
+
+ devmap = dasd_find_busid(dev_name(&device->cdev->dev));
+ if (IS_ERR(devmap))
+ return;
+ spin_lock(&dasd_devmap_lock);
+ gdp->private_data = devmap;
+ spin_unlock(&dasd_devmap_lock);
+}
+
+struct dasd_device *dasd_device_from_gendisk(struct gendisk *gdp)
+{
+ struct dasd_device *device;
+ struct dasd_devmap *devmap;
+
+ if (!gdp->private_data)
+ return NULL;
+ device = NULL;
+ spin_lock(&dasd_devmap_lock);
+ devmap = gdp->private_data;
+ if (devmap && devmap->device) {
+ device = devmap->device;
+ dasd_get_device(device);
+ }
+ spin_unlock(&dasd_devmap_lock);
+ return device;
+}
+
/*
* SECTION: files in sysfs
*/
addr_t ip;
int rc;
- kstat_cpu(smp_processor_id()).irqs[EXTINT_DSD]++;
switch (ext_int_code >> 24) {
case DASD_DIAG_CODE_31BIT:
ip = (addr_t) param32;
default:
return;
}
+ kstat_cpu(smp_processor_id()).irqs[EXTINT_DSD]++;
if (!ip) { /* no intparm: unsolicited interrupt */
DBF_EVENT(DBF_NOTICE, "%s", "caught unsolicited "
"interrupt");
return;
/* summary unit check */
- if ((sense[7] == 0x0D) &&
+ if ((sense[27] & DASD_SENSE_BIT_0) && (sense[7] == 0x0D) &&
(scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK)) {
dasd_alias_handle_summary_unit_check(device, irb);
return;
/* loss of device reservation is handled via base devices only
* as alias devices may be used with several bases
*/
- if (device->block && (sense[7] == 0x3F) &&
+ if (device->block && (sense[27] & DASD_SENSE_BIT_0) &&
+ (sense[7] == 0x3F) &&
(scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK) &&
test_bit(DASD_FLAG_IS_RESERVED, &device->flags)) {
if (device->features & DASD_FEATURE_FAILONSLCK)
if (base->features & DASD_FEATURE_READONLY ||
test_bit(DASD_FLAG_DEVICE_RO, &base->flags))
set_disk_ro(gdp, 1);
- gdp->private_data = block;
+ dasd_add_link_to_gendisk(gdp, base);
gdp->queue = block->request_queue;
block->gdp = gdp;
set_capacity(block->gdp, 0);
struct dasd_device *dasd_device_from_cdev_locked(struct ccw_device *);
struct dasd_device *dasd_device_from_devindex(int);
+void dasd_add_link_to_gendisk(struct gendisk *, struct dasd_device *);
+struct dasd_device *dasd_device_from_gendisk(struct gendisk *);
+
int dasd_parse(void);
int dasd_busid_known(const char *);
static int
dasd_ioctl_enable(struct block_device *bdev)
{
- struct dasd_block *block = bdev->bd_disk->private_data;
+ struct dasd_device *base;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- dasd_enable_device(block->base);
+ base = dasd_device_from_gendisk(bdev->bd_disk);
+ if (!base)
+ return -ENODEV;
+
+ dasd_enable_device(base);
/* Formatting the dasd device can change the capacity. */
mutex_lock(&bdev->bd_mutex);
- i_size_write(bdev->bd_inode, (loff_t)get_capacity(block->gdp) << 9);
+ i_size_write(bdev->bd_inode,
+ (loff_t)get_capacity(base->block->gdp) << 9);
mutex_unlock(&bdev->bd_mutex);
+ dasd_put_device(base);
return 0;
}
static int
dasd_ioctl_disable(struct block_device *bdev)
{
- struct dasd_block *block = bdev->bd_disk->private_data;
+ struct dasd_device *base;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
+ base = dasd_device_from_gendisk(bdev->bd_disk);
+ if (!base)
+ return -ENODEV;
/*
* Man this is sick. We don't do a real disable but only downgrade
* the device to DASD_STATE_BASIC. The reason is that dasdfmt uses
* using the BIODASDFMT ioctl. Therefore the correct state for the
* device is DASD_STATE_BASIC that allows to do basic i/o.
*/
- dasd_set_target_state(block->base, DASD_STATE_BASIC);
+ dasd_set_target_state(base, DASD_STATE_BASIC);
/*
* Set i_size to zero, since read, write, etc. check against this
* value.
mutex_lock(&bdev->bd_mutex);
i_size_write(bdev->bd_inode, 0);
mutex_unlock(&bdev->bd_mutex);
+ dasd_put_device(base);
return 0;
}
static int
dasd_ioctl_format(struct block_device *bdev, void __user *argp)
{
- struct dasd_block *block = bdev->bd_disk->private_data;
+ struct dasd_device *base;
struct format_data_t fdata;
+ int rc;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (!argp)
return -EINVAL;
-
- if (block->base->features & DASD_FEATURE_READONLY ||
- test_bit(DASD_FLAG_DEVICE_RO, &block->base->flags))
+ base = dasd_device_from_gendisk(bdev->bd_disk);
+ if (!base)
+ return -ENODEV;
+ if (base->features & DASD_FEATURE_READONLY ||
+ test_bit(DASD_FLAG_DEVICE_RO, &base->flags)) {
+ dasd_put_device(base);
return -EROFS;
- if (copy_from_user(&fdata, argp, sizeof(struct format_data_t)))
+ }
+ if (copy_from_user(&fdata, argp, sizeof(struct format_data_t))) {
+ dasd_put_device(base);
return -EFAULT;
+ }
if (bdev != bdev->bd_contains) {
pr_warning("%s: The specified DASD is a partition and cannot "
"be formatted\n",
- dev_name(&block->base->cdev->dev));
+ dev_name(&base->cdev->dev));
+ dasd_put_device(base);
return -EINVAL;
}
- return dasd_format(block, &fdata);
+ rc = dasd_format(base->block, &fdata);
+ dasd_put_device(base);
+ return rc;
}
#ifdef CONFIG_DASD_PROFILE
static int
dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
{
- struct dasd_block *block = bdev->bd_disk->private_data;
- int intval;
+ struct dasd_device *base;
+ int intval, rc;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
return -EINVAL;
if (get_user(intval, (int __user *)argp))
return -EFAULT;
- if (!intval && test_bit(DASD_FLAG_DEVICE_RO, &block->base->flags))
+ base = dasd_device_from_gendisk(bdev->bd_disk);
+ if (!base)
+ return -ENODEV;
+ if (!intval && test_bit(DASD_FLAG_DEVICE_RO, &base->flags)) {
+ dasd_put_device(base);
return -EROFS;
+ }
set_disk_ro(bdev->bd_disk, intval);
- return dasd_set_feature(block->base->cdev, DASD_FEATURE_READONLY, intval);
+ rc = dasd_set_feature(base->cdev, DASD_FEATURE_READONLY, intval);
+ dasd_put_device(base);
+ return rc;
}
static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd,
int dasd_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
- struct dasd_block *block = bdev->bd_disk->private_data;
+ struct dasd_block *block;
+ struct dasd_device *base;
void __user *argp;
+ int rc;
if (is_compat_task())
argp = compat_ptr(arg);
else
argp = (void __user *)arg;
- if (!block)
- return -ENODEV;
-
if ((_IOC_DIR(cmd) != _IOC_NONE) && !arg) {
PRINT_DEBUG("empty data ptr");
return -EINVAL;
}
+ base = dasd_device_from_gendisk(bdev->bd_disk);
+ if (!base)
+ return -ENODEV;
+ block = base->block;
+ rc = 0;
switch (cmd) {
case BIODASDDISABLE:
- return dasd_ioctl_disable(bdev);
+ rc = dasd_ioctl_disable(bdev);
+ break;
case BIODASDENABLE:
- return dasd_ioctl_enable(bdev);
+ rc = dasd_ioctl_enable(bdev);
+ break;
case BIODASDQUIESCE:
- return dasd_ioctl_quiesce(block);
+ rc = dasd_ioctl_quiesce(block);
+ break;
case BIODASDRESUME:
- return dasd_ioctl_resume(block);
+ rc = dasd_ioctl_resume(block);
+ break;
case BIODASDFMT:
- return dasd_ioctl_format(bdev, argp);
+ rc = dasd_ioctl_format(bdev, argp);
+ break;
case BIODASDINFO:
- return dasd_ioctl_information(block, cmd, argp);
+ rc = dasd_ioctl_information(block, cmd, argp);
+ break;
case BIODASDINFO2:
- return dasd_ioctl_information(block, cmd, argp);
+ rc = dasd_ioctl_information(block, cmd, argp);
+ break;
case BIODASDPRRD:
- return dasd_ioctl_read_profile(block, argp);
+ rc = dasd_ioctl_read_profile(block, argp);
+ break;
case BIODASDPRRST:
- return dasd_ioctl_reset_profile(block);
+ rc = dasd_ioctl_reset_profile(block);
+ break;
case BLKROSET:
- return dasd_ioctl_set_ro(bdev, argp);
+ rc = dasd_ioctl_set_ro(bdev, argp);
+ break;
case DASDAPIVER:
- return dasd_ioctl_api_version(argp);
+ rc = dasd_ioctl_api_version(argp);
+ break;
case BIODASDCMFENABLE:
- return enable_cmf(block->base->cdev);
+ rc = enable_cmf(base->cdev);
+ break;
case BIODASDCMFDISABLE:
- return disable_cmf(block->base->cdev);
+ rc = disable_cmf(base->cdev);
+ break;
case BIODASDREADALLCMB:
- return dasd_ioctl_readall_cmb(block, cmd, argp);
+ rc = dasd_ioctl_readall_cmb(block, cmd, argp);
+ break;
default:
/* if the discipline has an ioctl method try it. */
- if (block->base->discipline->ioctl) {
- int rval = block->base->discipline->ioctl(block, cmd, argp);
- if (rval != -ENOIOCTLCMD)
- return rval;
- }
-
- return -EINVAL;
+ if (base->discipline->ioctl) {
+ rc = base->discipline->ioctl(block, cmd, argp);
+ if (rc == -ENOIOCTLCMD)
+ rc = -EINVAL;
+ } else
+ rc = -EINVAL;
}
+ dasd_put_device(base);
+ return rc;
}
q->q_stats.nr_sbals[pos]++;
}
-static void announce_buffer_error(struct qdio_q *q, int count)
+static void process_buffer_error(struct qdio_q *q, int count)
{
+ unsigned char state = (q->is_input_q) ? SLSB_P_INPUT_NOT_INIT :
+ SLSB_P_OUTPUT_NOT_INIT;
+
q->qdio_error |= QDIO_ERROR_SLSB_STATE;
/* special handling for no target buffer empty */
DBF_ERROR("F14:%2x F15:%2x",
q->sbal[q->first_to_check]->element[14].flags & 0xff,
q->sbal[q->first_to_check]->element[15].flags & 0xff);
+
+ /*
+ * Interrupts may be avoided as long as the error is present
+ * so change the buffer state immediately to avoid starvation.
+ */
+ set_buf_states(q, q->first_to_check, state, count);
}
static inline void inbound_primed(struct qdio_q *q, int count)
account_sbals(q, count);
break;
case SLSB_P_INPUT_ERROR:
- announce_buffer_error(q, count);
- /* process the buffer, the upper layer will take care of it */
+ process_buffer_error(q, count);
q->first_to_check = add_buf(q->first_to_check, count);
atomic_sub(count, &q->nr_buf_used);
if (q->irq_ptr->perf_stat_enabled)
account_sbals(q, count);
break;
case SLSB_P_OUTPUT_ERROR:
- announce_buffer_error(q, count);
- /* process the buffer, the upper layer will take care of it */
+ process_buffer_error(q, count);
q->first_to_check = add_buf(q->first_to_check, count);
atomic_sub(count, &q->nr_buf_used);
if (q->irq_ptr->perf_stat_enabled)
u16 subcode;
u32 param;
- kstat_cpu(smp_processor_id()).irqs[EXTINT_VRT]++;
subcode = ext_int_code >> 16;
if ((subcode & 0xff00) != VIRTIO_SUBCODE_64)
return;
+ kstat_cpu(smp_processor_id()).irqs[EXTINT_VRT]++;
/* The LSB might be overloaded, we have to mask it */
vq = (struct virtqueue *)(param64 & ~1UL);
unsigned long flags;
struct scsi_device *sdev;
struct scsi_device_handler *scsi_dh = NULL;
+ struct device *dev = NULL;
spin_lock_irqsave(q->queue_lock, flags);
sdev = q->queuedata;
if (sdev && sdev->scsi_dh_data)
scsi_dh = sdev->scsi_dh_data->scsi_dh;
- if (!scsi_dh || !get_device(&sdev->sdev_gendev) ||
+ dev = get_device(&sdev->sdev_gendev);
+ if (!scsi_dh || !dev ||
sdev->sdev_state == SDEV_CANCEL ||
sdev->sdev_state == SDEV_DEL)
err = SCSI_DH_NOSYS;
if (err) {
if (fn)
fn(data, err);
- return err;
+ goto out;
}
if (scsi_dh->activate)
err = scsi_dh->activate(sdev, fn, data);
- put_device(&sdev->sdev_gendev);
+out:
+ put_device(dev);
return err;
}
EXPORT_SYMBOL_GPL(scsi_dh_activate);
goto out;
}
+ /* Check for overflow and wraparound */
+ if (karg.data_sge_offset * 4 > ioc->request_sz ||
+ karg.data_sge_offset > (UINT_MAX / 4)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
/* copy in request message frame from user */
if (copy_from_user(mpi_request, mf, karg.data_sge_offset*4)) {
printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__,
Mpi2DiagBufferPostReply_t *mpi_reply;
int rc, i;
u8 buffer_type;
- unsigned long timeleft;
+ unsigned long timeleft, request_size, copy_size;
u16 smid;
u16 ioc_status;
u8 issue_reset = 0;
return -ENOMEM;
}
+ request_size = ioc->diag_buffer_sz[buffer_type];
+
if ((karg.starting_offset % 4) || (karg.bytes_to_read % 4)) {
printk(MPT2SAS_ERR_FMT "%s: either the starting_offset "
"or bytes_to_read are not 4 byte aligned\n", ioc->name,
return -EINVAL;
}
+ if (karg.starting_offset > request_size)
+ return -EINVAL;
+
diag_data = (void *)(request_data + karg.starting_offset);
dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: diag_buffer(%p), "
"offset(%d), sz(%d)\n", ioc->name, __func__,
diag_data, karg.starting_offset, karg.bytes_to_read));
+ /* Truncate data on requests that are too large */
+ if ((diag_data + karg.bytes_to_read < diag_data) ||
+ (diag_data + karg.bytes_to_read > request_data + request_size))
+ copy_size = request_size - karg.starting_offset;
+ else
+ copy_size = karg.bytes_to_read;
+
if (copy_to_user((void __user *)uarg->diagnostic_data,
- diag_data, karg.bytes_to_read)) {
+ diag_data, copy_size)) {
printk(MPT2SAS_ERR_FMT "%s: Unable to write "
"mpt_diag_read_buffer_t data @ %p\n", ioc->name,
__func__, diag_data);
rc = -EFAULT;
goto out_free_buffer;
}
+ } else if (request_size < 0) {
+ rc = -EINVAL;
+ goto out_free_buffer;
}
/* check if we have any additional command parameters */
static void scsi_run_queue(struct request_queue *q)
{
struct scsi_device *sdev = q->queuedata;
- struct Scsi_Host *shost = sdev->host;
+ struct Scsi_Host *shost;
LIST_HEAD(starved_list);
unsigned long flags;
+ /* if the device is dead, sdev will be NULL, so no queue to run */
+ if (!sdev)
+ return;
+
+ shost = sdev->host;
if (scsi_target(sdev)->single_lun)
scsi_single_lun_run(sdev);
list_splice_init(&shost->starved_list, &starved_list);
while (!list_empty(&starved_list)) {
- int flagset;
-
/*
* As long as shost is accepting commands and we have
* starved queues, call blk_run_queue. scsi_request_fn
continue;
}
- spin_unlock(shost->host_lock);
-
- spin_lock(sdev->request_queue->queue_lock);
- flagset = test_bit(QUEUE_FLAG_REENTER, &q->queue_flags) &&
- !test_bit(QUEUE_FLAG_REENTER,
- &sdev->request_queue->queue_flags);
- if (flagset)
- queue_flag_set(QUEUE_FLAG_REENTER, sdev->request_queue);
- __blk_run_queue(sdev->request_queue, false);
- if (flagset)
- queue_flag_clear(QUEUE_FLAG_REENTER, sdev->request_queue);
- spin_unlock(sdev->request_queue->queue_lock);
-
- spin_lock(shost->host_lock);
+ blk_run_queue_async(sdev->request_queue);
}
/* put any unprocessed entries back */
list_splice(&starved_list, &shost->starved_list);
kfree(evt);
}
- if (sdev->request_queue) {
- sdev->request_queue->queuedata = NULL;
- /* user context needed to free queue */
- scsi_free_queue(sdev->request_queue);
- /* temporary expedient, try to catch use of queue lock
- * after free of sdev */
- sdev->request_queue = NULL;
- }
+ /* NULL queue means the device can't be used */
+ sdev->request_queue = NULL;
scsi_target_reap(scsi_target(sdev));
if (sdev->host->hostt->slave_destroy)
sdev->host->hostt->slave_destroy(sdev);
transport_destroy_device(dev);
+
+ /* cause the request function to reject all I/O requests */
+ sdev->request_queue->queuedata = NULL;
+
+ /* Freeing the queue signals to block that we're done */
+ scsi_free_queue(sdev->request_queue);
put_device(dev);
}
static void
fc_bsg_goose_queue(struct fc_rport *rport)
{
- int flagset;
- unsigned long flags;
-
if (!rport->rqst_q)
return;
+ /*
+ * This get/put dance makes no sense
+ */
get_device(&rport->dev);
-
- spin_lock_irqsave(rport->rqst_q->queue_lock, flags);
- flagset = test_bit(QUEUE_FLAG_REENTER, &rport->rqst_q->queue_flags) &&
- !test_bit(QUEUE_FLAG_REENTER, &rport->rqst_q->queue_flags);
- if (flagset)
- queue_flag_set(QUEUE_FLAG_REENTER, rport->rqst_q);
- __blk_run_queue(rport->rqst_q, false);
- if (flagset)
- queue_flag_clear(QUEUE_FLAG_REENTER, rport->rqst_q);
- spin_unlock_irqrestore(rport->rqst_q->queue_lock, flags);
-
+ blk_run_queue_async(rport->rqst_q);
put_device(&rport->dev);
}
-
/**
* fc_bsg_rport_dispatch - process rport bsg requests and dispatch to LLDD
* @q: rport request queue
source "drivers/staging/wlags49_h25/Kconfig"
-source "drivers/staging/samsung-laptop/Kconfig"
-
source "drivers/staging/sm7xx/Kconfig"
source "drivers/staging/dt3155v4l/Kconfig"
obj-$(CONFIG_ZCACHE) += zcache/
obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/
obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/
-obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop/
obj-$(CONFIG_FB_SM7XX) += sm7xx/
obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/
obj-$(CONFIG_CRYSTALHD) += crystalhd/
free_netdev(dev);
return NULL;
}
-
-EXPORT_SYMBOL(init_ft1000_card);
-EXPORT_SYMBOL(stop_ft1000_card);
-EXPORT_SYMBOL(flarion_ft1000_cnt);
remove_proc_entry(FT1000_PROC, init_net.proc_net);
unregister_netdevice_notifier(&ft1000_netdev_notifier);
}
-
-EXPORT_SYMBOL(ft1000InitProc);
-EXPORT_SYMBOL(ft1000CleanupProc);
config DRM_PSB
tristate "Intel GMA500 KMS Framebuffer"
- depends on DRM && PCI
+ depends on DRM && PCI && X86
select FB_CFB_COPYAREA
select FB_CFB_FILLRECT
select FB_CFB_IMAGEBLIT
sst_drv_ctx->pmic_state = SND_MAD_INIT_DONE;
sst_drv_ctx->rx_time_slot_status = 0; /*default AMIC*/
card->pcm_control = sst_pmic_ops.pcm_control;
- sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT;
return 0;
} else {
pr_err("strcmp fail %s\n", card->module_name);
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
+#include <linux/firmware.h>
#include <sound/control.h>
#include <asm/mrst.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include "intel_sst.h"
#include "intel_sst_ioctl.h"
+#include "intel_sst_fw_ipc.h"
+#include "intel_sst_common.h"
#include "intelmid_snd_control.h"
#include "intelmid.h"
pr_err("sst card registration failed\n");
return ret_val;
}
+ sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT;
sst_card_vendor_id = intelmaddata->sstdrv_ops->vendor_id;
intelmaddata->pmic_status = PMIC_UNINIT;
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/pci.h>
+#include <linux/delay.h>
#include <linux/file.h>
#include <asm/mrst.h>
#include <sound/pcm.h>
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/pci.h>
+#include <linux/delay.h>
#include <linux/file.h>
#include "intel_sst.h"
#include "intelmid_snd_control.h"
*/
#include <linux/cs5535.h>
#include <linux/gpio.h>
+#include <linux/delay.h>
#include <asm/olpc.h>
#include "olpc_dcon.h"
return (NDIS_STATUS_FAILURE);
}
}
- /* Drop not U2M frames, can't's drop here because we will drop beacon in this case */
+ /* Drop not U2M frames, can't drop here because we will drop beacon in this case */
/* I am kind of doubting the U2M bit operation */
/* if (pRxD->U2M == 0) */
/* return(NDIS_STATUS_FAILURE); */
DBGPRINT_RAW(RT_DEBUG_ERROR, ("received packet too long\n"));
return NDIS_STATUS_FAILURE;
}
- /* Drop not U2M frames, can't's drop here because we will drop beacon in this case */
+ /* Drop not U2M frames, can't drop here because we will drop beacon in this case */
/* I am kind of doubting the U2M bit operation */
/* if (pRxD->U2M == 0) */
/* return(NDIS_STATUS_FAILURE); */
#define RTSX_STOR "rts_pstor: "
-#if CONFIG_RTS_PSTOR_DEBUG
+#ifdef CONFIG_RTS_PSTOR_DEBUG
#define RTSX_DEBUGP(x...) printk(KERN_DEBUG RTSX_STOR x)
#define RTSX_DEBUGPN(x...) printk(KERN_DEBUG x)
#define RTSX_DEBUGPX(x...) printk(x)
#include <linux/blkdev.h>
#include <linux/kthread.h>
#include <linux/sched.h>
+#include <linux/vmalloc.h>
#include "rtsx.h"
#include "rtsx_transport.h"
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/workqueue.h>
+#include <linux/vmalloc.h>
#include "rtsx.h"
#include "rtsx_transport.h"
#ifdef SUPPORT_OCP
if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
- #if CONFIG_RTS_PSTOR_DEBUG
+#ifdef CONFIG_RTS_PSTOR_DEBUG
if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER | MS_OC_NOW | MS_OC_EVER)) {
RTSX_DEBUGP("Over current, OCPSTAT is 0x%x\n", chip->ocp_stat);
}
- #endif
+#endif
if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER)) {
if (chip->card_exist & SD_CARD) {
#include <linux/blkdev.h>
#include <linux/kthread.h>
#include <linux/sched.h>
+#include <linux/vmalloc.h>
#include "rtsx.h"
#include "rtsx_transport.h"
RTSX_WRITE_REG(chip, SD_VPCLK0_CTL, PHASE_NOT_RESET, PHASE_NOT_RESET);
RTSX_WRITE_REG(chip, CLK_CTL, CHANGE_CLK, 0);
} else {
-#if CONFIG_RTS_PSTOR_DEBUG
+#ifdef CONFIG_RTS_PSTOR_DEBUG
rtsx_read_register(chip, SD_VP_CTL, &val);
RTSX_DEBUGP("SD_VP_CTL: 0x%x\n", val);
rtsx_read_register(chip, SD_DCMPS_CTL, &val);
return STATUS_SUCCESS;
Fail:
-#if CONFIG_RTS_PSTOR_DEBUG
+#ifdef CONFIG_RTS_PSTOR_DEBUG
rtsx_read_register(chip, SD_VP_CTL, &val);
RTSX_DEBUGP("SD_VP_CTL: 0x%x\n", val);
rtsx_read_register(chip, SD_DCMPS_CTL, &val);
#define TRACE_GOTO(chip, label) goto label
#endif
-#if CONFIG_RTS_PSTOR_DEBUG
+#ifdef CONFIG_RTS_PSTOR_DEBUG
static inline void rtsx_dump(u8 *buf, int buf_len)
{
int i;
#include <linux/blkdev.h>
#include <linux/kthread.h>
#include <linux/sched.h>
+#include <linux/vmalloc.h>
#include "rtsx.h"
#include "rtsx_transport.h"
+++ /dev/null
-config SAMSUNG_LAPTOP
- tristate "Samsung Laptop driver"
- default n
- depends on RFKILL && BACKLIGHT_CLASS_DEVICE && X86
- help
- This module implements a driver for the N128 Samsung Laptop
- providing control over the Wireless LED and the LCD backlight
-
- To compile this driver as a module, choose
- M here: the module will be called samsung-laptop.
+++ /dev/null
-obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o
+++ /dev/null
-TODO:
- - review from other developers
- - figure out ACPI video issues
-
-Please send patches to Greg Kroah-Hartman <gregkh@suse.de>
+++ /dev/null
-/*
- * Samsung Laptop driver
- *
- * Copyright (C) 2009,2011 Greg Kroah-Hartman (gregkh@suse.de)
- * Copyright (C) 2009,2011 Novell Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- */
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/backlight.h>
-#include <linux/fb.h>
-#include <linux/dmi.h>
-#include <linux/platform_device.h>
-#include <linux/rfkill.h>
-
-/*
- * This driver is needed because a number of Samsung laptops do not hook
- * their control settings through ACPI. So we have to poke around in the
- * BIOS to do things like brightness values, and "special" key controls.
- */
-
-/*
- * We have 0 - 8 as valid brightness levels. The specs say that level 0 should
- * be reserved by the BIOS (which really doesn't make much sense), we tell
- * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8
- */
-#define MAX_BRIGHT 0x07
-
-
-#define SABI_IFACE_MAIN 0x00
-#define SABI_IFACE_SUB 0x02
-#define SABI_IFACE_COMPLETE 0x04
-#define SABI_IFACE_DATA 0x05
-
-/* Structure to get data back to the calling function */
-struct sabi_retval {
- u8 retval[20];
-};
-
-struct sabi_header_offsets {
- u8 port;
- u8 re_mem;
- u8 iface_func;
- u8 en_mem;
- u8 data_offset;
- u8 data_segment;
-};
-
-struct sabi_commands {
- /*
- * Brightness is 0 - 8, as described above.
- * Value 0 is for the BIOS to use
- */
- u8 get_brightness;
- u8 set_brightness;
-
- /*
- * first byte:
- * 0x00 - wireless is off
- * 0x01 - wireless is on
- * second byte:
- * 0x02 - 3G is off
- * 0x03 - 3G is on
- * TODO, verify 3G is correct, that doesn't seem right...
- */
- u8 get_wireless_button;
- u8 set_wireless_button;
-
- /* 0 is off, 1 is on */
- u8 get_backlight;
- u8 set_backlight;
-
- /*
- * 0x80 or 0x00 - no action
- * 0x81 - recovery key pressed
- */
- u8 get_recovery_mode;
- u8 set_recovery_mode;
-
- /*
- * on seclinux: 0 is low, 1 is high,
- * on swsmi: 0 is normal, 1 is silent, 2 is turbo
- */
- u8 get_performance_level;
- u8 set_performance_level;
-
- /*
- * Tell the BIOS that Linux is running on this machine.
- * 81 is on, 80 is off
- */
- u8 set_linux;
-};
-
-struct sabi_performance_level {
- const char *name;
- u8 value;
-};
-
-struct sabi_config {
- const char *test_string;
- u16 main_function;
- const struct sabi_header_offsets header_offsets;
- const struct sabi_commands commands;
- const struct sabi_performance_level performance_levels[4];
- u8 min_brightness;
- u8 max_brightness;
-};
-
-static const struct sabi_config sabi_configs[] = {
- {
- .test_string = "SECLINUX",
-
- .main_function = 0x4c49,
-
- .header_offsets = {
- .port = 0x00,
- .re_mem = 0x02,
- .iface_func = 0x03,
- .en_mem = 0x04,
- .data_offset = 0x05,
- .data_segment = 0x07,
- },
-
- .commands = {
- .get_brightness = 0x00,
- .set_brightness = 0x01,
-
- .get_wireless_button = 0x02,
- .set_wireless_button = 0x03,
-
- .get_backlight = 0x04,
- .set_backlight = 0x05,
-
- .get_recovery_mode = 0x06,
- .set_recovery_mode = 0x07,
-
- .get_performance_level = 0x08,
- .set_performance_level = 0x09,
-
- .set_linux = 0x0a,
- },
-
- .performance_levels = {
- {
- .name = "silent",
- .value = 0,
- },
- {
- .name = "normal",
- .value = 1,
- },
- { },
- },
- .min_brightness = 1,
- .max_brightness = 8,
- },
- {
- .test_string = "SwSmi@",
-
- .main_function = 0x5843,
-
- .header_offsets = {
- .port = 0x00,
- .re_mem = 0x04,
- .iface_func = 0x02,
- .en_mem = 0x03,
- .data_offset = 0x05,
- .data_segment = 0x07,
- },
-
- .commands = {
- .get_brightness = 0x10,
- .set_brightness = 0x11,
-
- .get_wireless_button = 0x12,
- .set_wireless_button = 0x13,
-
- .get_backlight = 0x2d,
- .set_backlight = 0x2e,
-
- .get_recovery_mode = 0xff,
- .set_recovery_mode = 0xff,
-
- .get_performance_level = 0x31,
- .set_performance_level = 0x32,
-
- .set_linux = 0xff,
- },
-
- .performance_levels = {
- {
- .name = "normal",
- .value = 0,
- },
- {
- .name = "silent",
- .value = 1,
- },
- {
- .name = "overclock",
- .value = 2,
- },
- { },
- },
- .min_brightness = 0,
- .max_brightness = 8,
- },
- { },
-};
-
-static const struct sabi_config *sabi_config;
-
-static void __iomem *sabi;
-static void __iomem *sabi_iface;
-static void __iomem *f0000_segment;
-static struct backlight_device *backlight_device;
-static struct mutex sabi_mutex;
-static struct platform_device *sdev;
-static struct rfkill *rfk;
-
-static int force;
-module_param(force, bool, 0);
-MODULE_PARM_DESC(force,
- "Disable the DMI check and forces the driver to be loaded");
-
-static int debug;
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
-
-static int sabi_get_command(u8 command, struct sabi_retval *sretval)
-{
- int retval = 0;
- u16 port = readw(sabi + sabi_config->header_offsets.port);
- u8 complete, iface_data;
-
- mutex_lock(&sabi_mutex);
-
- /* enable memory to be able to write to it */
- outb(readb(sabi + sabi_config->header_offsets.en_mem), port);
-
- /* write out the command */
- writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN);
- writew(command, sabi_iface + SABI_IFACE_SUB);
- writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
- outb(readb(sabi + sabi_config->header_offsets.iface_func), port);
-
- /* write protect memory to make it safe */
- outb(readb(sabi + sabi_config->header_offsets.re_mem), port);
-
- /* see if the command actually succeeded */
- complete = readb(sabi_iface + SABI_IFACE_COMPLETE);
- iface_data = readb(sabi_iface + SABI_IFACE_DATA);
- if (complete != 0xaa || iface_data == 0xff) {
- pr_warn("SABI get command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n",
- command, complete, iface_data);
- retval = -EINVAL;
- goto exit;
- }
- /*
- * Save off the data into a structure so the caller use it.
- * Right now we only want the first 4 bytes,
- * There are commands that need more, but not for the ones we
- * currently care about.
- */
- sretval->retval[0] = readb(sabi_iface + SABI_IFACE_DATA);
- sretval->retval[1] = readb(sabi_iface + SABI_IFACE_DATA + 1);
- sretval->retval[2] = readb(sabi_iface + SABI_IFACE_DATA + 2);
- sretval->retval[3] = readb(sabi_iface + SABI_IFACE_DATA + 3);
-
-exit:
- mutex_unlock(&sabi_mutex);
- return retval;
-
-}
-
-static int sabi_set_command(u8 command, u8 data)
-{
- int retval = 0;
- u16 port = readw(sabi + sabi_config->header_offsets.port);
- u8 complete, iface_data;
-
- mutex_lock(&sabi_mutex);
-
- /* enable memory to be able to write to it */
- outb(readb(sabi + sabi_config->header_offsets.en_mem), port);
-
- /* write out the command */
- writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN);
- writew(command, sabi_iface + SABI_IFACE_SUB);
- writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
- writeb(data, sabi_iface + SABI_IFACE_DATA);
- outb(readb(sabi + sabi_config->header_offsets.iface_func), port);
-
- /* write protect memory to make it safe */
- outb(readb(sabi + sabi_config->header_offsets.re_mem), port);
-
- /* see if the command actually succeeded */
- complete = readb(sabi_iface + SABI_IFACE_COMPLETE);
- iface_data = readb(sabi_iface + SABI_IFACE_DATA);
- if (complete != 0xaa || iface_data == 0xff) {
- pr_warn("SABI set command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n",
- command, complete, iface_data);
- retval = -EINVAL;
- }
-
- mutex_unlock(&sabi_mutex);
- return retval;
-}
-
-static void test_backlight(void)
-{
- struct sabi_retval sretval;
-
- sabi_get_command(sabi_config->commands.get_backlight, &sretval);
- printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
-
- sabi_set_command(sabi_config->commands.set_backlight, 0);
- printk(KERN_DEBUG "backlight should be off\n");
-
- sabi_get_command(sabi_config->commands.get_backlight, &sretval);
- printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
-
- msleep(1000);
-
- sabi_set_command(sabi_config->commands.set_backlight, 1);
- printk(KERN_DEBUG "backlight should be on\n");
-
- sabi_get_command(sabi_config->commands.get_backlight, &sretval);
- printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
-}
-
-static void test_wireless(void)
-{
- struct sabi_retval sretval;
-
- sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
- printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
-
- sabi_set_command(sabi_config->commands.set_wireless_button, 0);
- printk(KERN_DEBUG "wireless led should be off\n");
-
- sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
- printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
-
- msleep(1000);
-
- sabi_set_command(sabi_config->commands.set_wireless_button, 1);
- printk(KERN_DEBUG "wireless led should be on\n");
-
- sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
- printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
-}
-
-static u8 read_brightness(void)
-{
- struct sabi_retval sretval;
- int user_brightness = 0;
- int retval;
-
- retval = sabi_get_command(sabi_config->commands.get_brightness,
- &sretval);
- if (!retval) {
- user_brightness = sretval.retval[0];
- if (user_brightness != 0)
- user_brightness -= sabi_config->min_brightness;
- }
- return user_brightness;
-}
-
-static void set_brightness(u8 user_brightness)
-{
- u8 user_level = user_brightness - sabi_config->min_brightness;
-
- sabi_set_command(sabi_config->commands.set_brightness, user_level);
-}
-
-static int get_brightness(struct backlight_device *bd)
-{
- return (int)read_brightness();
-}
-
-static int update_status(struct backlight_device *bd)
-{
- set_brightness(bd->props.brightness);
-
- if (bd->props.power == FB_BLANK_UNBLANK)
- sabi_set_command(sabi_config->commands.set_backlight, 1);
- else
- sabi_set_command(sabi_config->commands.set_backlight, 0);
- return 0;
-}
-
-static const struct backlight_ops backlight_ops = {
- .get_brightness = get_brightness,
- .update_status = update_status,
-};
-
-static int rfkill_set(void *data, bool blocked)
-{
- /* Do something with blocked...*/
- /*
- * blocked == false is on
- * blocked == true is off
- */
- if (blocked)
- sabi_set_command(sabi_config->commands.set_wireless_button, 0);
- else
- sabi_set_command(sabi_config->commands.set_wireless_button, 1);
-
- return 0;
-}
-
-static struct rfkill_ops rfkill_ops = {
- .set_block = rfkill_set,
-};
-
-static int init_wireless(struct platform_device *sdev)
-{
- int retval;
-
- rfk = rfkill_alloc("samsung-wifi", &sdev->dev, RFKILL_TYPE_WLAN,
- &rfkill_ops, NULL);
- if (!rfk)
- return -ENOMEM;
-
- retval = rfkill_register(rfk);
- if (retval) {
- rfkill_destroy(rfk);
- return -ENODEV;
- }
-
- return 0;
-}
-
-static void destroy_wireless(void)
-{
- rfkill_unregister(rfk);
- rfkill_destroy(rfk);
-}
-
-static ssize_t get_performance_level(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct sabi_retval sretval;
- int retval;
- int i;
-
- /* Read the state */
- retval = sabi_get_command(sabi_config->commands.get_performance_level,
- &sretval);
- if (retval)
- return retval;
-
- /* The logic is backwards, yeah, lots of fun... */
- for (i = 0; sabi_config->performance_levels[i].name; ++i) {
- if (sretval.retval[0] == sabi_config->performance_levels[i].value)
- return sprintf(buf, "%s\n", sabi_config->performance_levels[i].name);
- }
- return sprintf(buf, "%s\n", "unknown");
-}
-
-static ssize_t set_performance_level(struct device *dev,
- struct device_attribute *attr, const char *buf,
- size_t count)
-{
- if (count >= 1) {
- int i;
- for (i = 0; sabi_config->performance_levels[i].name; ++i) {
- const struct sabi_performance_level *level =
- &sabi_config->performance_levels[i];
- if (!strncasecmp(level->name, buf, strlen(level->name))) {
- sabi_set_command(sabi_config->commands.set_performance_level,
- level->value);
- break;
- }
- }
- if (!sabi_config->performance_levels[i].name)
- return -EINVAL;
- }
- return count;
-}
-static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO,
- get_performance_level, set_performance_level);
-
-
-static int __init dmi_check_cb(const struct dmi_system_id *id)
-{
- pr_info("found laptop model '%s'\n",
- id->ident);
- return 0;
-}
-
-static struct dmi_system_id __initdata samsung_dmi_table[] = {
- {
- .ident = "N128",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N128"),
- DMI_MATCH(DMI_BOARD_NAME, "N128"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N130",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N130"),
- DMI_MATCH(DMI_BOARD_NAME, "N130"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X125",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X125"),
- DMI_MATCH(DMI_BOARD_NAME, "X125"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X120/X170",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X120/X170"),
- DMI_MATCH(DMI_BOARD_NAME, "X120/X170"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "NC10",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
- DMI_MATCH(DMI_BOARD_NAME, "NC10"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "NP-Q45",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"),
- DMI_MATCH(DMI_BOARD_NAME, "SQ45S70S"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X360",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
- DMI_MATCH(DMI_BOARD_NAME, "X360"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R410 Plus",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R410P"),
- DMI_MATCH(DMI_BOARD_NAME, "R460"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R518",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R518"),
- DMI_MATCH(DMI_BOARD_NAME, "R518"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R519/R719",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R519/R719"),
- DMI_MATCH(DMI_BOARD_NAME, "R519/R719"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N150/N210/N220/N230",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220/N230"),
- DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220/N230"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N150P/N210P/N220P",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N150P/N210P/N220P"),
- DMI_MATCH(DMI_BOARD_NAME, "N150P/N210P/N220P"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R530/R730",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R530/R730"),
- DMI_MATCH(DMI_BOARD_NAME, "R530/R730"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "NF110/NF210/NF310",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
- DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N145P/N250P/N260P",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
- DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R70/R71",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R70/R71"),
- DMI_MATCH(DMI_BOARD_NAME, "R70/R71"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "P460",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "P460"),
- DMI_MATCH(DMI_BOARD_NAME, "P460"),
- },
- .callback = dmi_check_cb,
- },
- { },
-};
-MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
-
-static int find_signature(void __iomem *memcheck, const char *testStr)
-{
- int i = 0;
- int loca;
-
- for (loca = 0; loca < 0xffff; loca++) {
- char temp = readb(memcheck + loca);
-
- if (temp == testStr[i]) {
- if (i == strlen(testStr)-1)
- break;
- ++i;
- } else {
- i = 0;
- }
- }
- return loca;
-}
-
-static int __init samsung_init(void)
-{
- struct backlight_properties props;
- struct sabi_retval sretval;
- unsigned int ifaceP;
- int i;
- int loca;
- int retval;
-
- mutex_init(&sabi_mutex);
-
- if (!force && !dmi_check_system(samsung_dmi_table))
- return -ENODEV;
-
- f0000_segment = ioremap_nocache(0xf0000, 0xffff);
- if (!f0000_segment) {
- pr_err("Can't map the segment at 0xf0000\n");
- return -EINVAL;
- }
-
- /* Try to find one of the signatures in memory to find the header */
- for (i = 0; sabi_configs[i].test_string != 0; ++i) {
- sabi_config = &sabi_configs[i];
- loca = find_signature(f0000_segment, sabi_config->test_string);
- if (loca != 0xffff)
- break;
- }
-
- if (loca == 0xffff) {
- pr_err("This computer does not support SABI\n");
- goto error_no_signature;
- }
-
- /* point to the SMI port Number */
- loca += 1;
- sabi = (f0000_segment + loca);
-
- if (debug) {
- printk(KERN_DEBUG "This computer supports SABI==%x\n",
- loca + 0xf0000 - 6);
- printk(KERN_DEBUG "SABI header:\n");
- printk(KERN_DEBUG " SMI Port Number = 0x%04x\n",
- readw(sabi + sabi_config->header_offsets.port));
- printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n",
- readb(sabi + sabi_config->header_offsets.iface_func));
- printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n",
- readb(sabi + sabi_config->header_offsets.en_mem));
- printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n",
- readb(sabi + sabi_config->header_offsets.re_mem));
- printk(KERN_DEBUG " SABI data offset = 0x%04x\n",
- readw(sabi + sabi_config->header_offsets.data_offset));
- printk(KERN_DEBUG " SABI data segment = 0x%04x\n",
- readw(sabi + sabi_config->header_offsets.data_segment));
- }
-
- /* Get a pointer to the SABI Interface */
- ifaceP = (readw(sabi + sabi_config->header_offsets.data_segment) & 0x0ffff) << 4;
- ifaceP += readw(sabi + sabi_config->header_offsets.data_offset) & 0x0ffff;
- sabi_iface = ioremap_nocache(ifaceP, 16);
- if (!sabi_iface) {
- pr_err("Can't remap %x\n", ifaceP);
- goto exit;
- }
- if (debug) {
- printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP);
- printk(KERN_DEBUG "sabi_iface = %p\n", sabi_iface);
-
- test_backlight();
- test_wireless();
-
- retval = sabi_get_command(sabi_config->commands.get_brightness,
- &sretval);
- printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.retval[0]);
- }
-
- /* Turn on "Linux" mode in the BIOS */
- if (sabi_config->commands.set_linux != 0xff) {
- retval = sabi_set_command(sabi_config->commands.set_linux,
- 0x81);
- if (retval) {
- pr_warn("Linux mode was not set!\n");
- goto error_no_platform;
- }
- }
-
- /* knock up a platform device to hang stuff off of */
- sdev = platform_device_register_simple("samsung", -1, NULL, 0);
- if (IS_ERR(sdev))
- goto error_no_platform;
-
- /* create a backlight device to talk to this one */
- memset(&props, 0, sizeof(struct backlight_properties));
- props.type = BACKLIGHT_PLATFORM;
- props.max_brightness = sabi_config->max_brightness;
- backlight_device = backlight_device_register("samsung", &sdev->dev,
- NULL, &backlight_ops,
- &props);
- if (IS_ERR(backlight_device))
- goto error_no_backlight;
-
- backlight_device->props.brightness = read_brightness();
- backlight_device->props.power = FB_BLANK_UNBLANK;
- backlight_update_status(backlight_device);
-
- retval = init_wireless(sdev);
- if (retval)
- goto error_no_rfk;
-
- retval = device_create_file(&sdev->dev, &dev_attr_performance_level);
- if (retval)
- goto error_file_create;
-
-exit:
- return 0;
-
-error_file_create:
- destroy_wireless();
-
-error_no_rfk:
- backlight_device_unregister(backlight_device);
-
-error_no_backlight:
- platform_device_unregister(sdev);
-
-error_no_platform:
- iounmap(sabi_iface);
-
-error_no_signature:
- iounmap(f0000_segment);
- return -EINVAL;
-}
-
-static void __exit samsung_exit(void)
-{
- /* Turn off "Linux" mode in the BIOS */
- if (sabi_config->commands.set_linux != 0xff)
- sabi_set_command(sabi_config->commands.set_linux, 0x80);
-
- device_remove_file(&sdev->dev, &dev_attr_performance_level);
- backlight_device_unregister(backlight_device);
- destroy_wireless();
- iounmap(sabi_iface);
- iounmap(f0000_segment);
- platform_device_unregister(sdev);
-}
-
-module_init(samsung_init);
-module_exit(samsung_exit);
-
-MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
-MODULE_DESCRIPTION("Samsung Backlight driver");
-MODULE_LICENSE("GPL");
tristate "Softlogic 6x10 MPEG codec cards"
depends on PCI && VIDEO_DEV && SND && I2C
select VIDEOBUF_DMA_SG
+ select SND_PCM
---help---
This driver supports the Softlogic based MPEG-4 and h.264 codec
codec cards.
}
dev->queue->queuedata = dev;
- /* As Linux block layer does't support >4KB hardware sector, */
+ /* As Linux block layer doesn't support >4KB hardware sector, */
/* Here we force report 512 byte hardware sector size to Kernel */
blk_queue_logical_block_size(dev->queue, 512);
* as a temporary for .dllview record construction.
* Allocate storage for the whole table. Add 1 to the section count
* in case a trampoline section is auto-generated as well as the
- * size of the trampoline section name so DLLView does't get lost.
+ * size of the trampoline section name so DLLView doesn't get lost.
*/
siz = sym_count * sizeof(struct local_symbol);
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_CAR, port_No(port));
- /* The Specialix board does't implement the RTS lines.
+ /* The Specialix board doesn't implement the RTS lines.
They are used to set the IRQ level. Don't touch them. */
if (sx_crtscts(tty))
port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
}
/* kill threads related to this sdev, if v.c. exists */
- kthread_stop(vdev->ud.tcp_rx);
- kthread_stop(vdev->ud.tcp_tx);
+ if (vdev->ud.tcp_rx)
+ kthread_stop(vdev->ud.tcp_rx);
+ if (vdev->ud.tcp_tx)
+ kthread_stop(vdev->ud.tcp_tx);
usbip_uinfo("stop threads\n");
{
memset(vdev, 0, sizeof(*vdev));
- vdev->ud.tcp_rx = kthread_create(vhci_rx_loop, &vdev->ud, "vhci_rx");
- vdev->ud.tcp_tx = kthread_create(vhci_tx_loop, &vdev->ud, "vhci_tx");
-
vdev->ud.side = USBIP_VHCI;
vdev->ud.status = VDEV_ST_NULL;
/* vdev->ud.lock = SPIN_LOCK_UNLOCKED; */
usbip_uerr("create hcd failed\n");
return -ENOMEM;
}
-
+ hcd->has_tt = 1;
/* this is private data for vhci_hcd */
the_controller = hcd_to_vhci(hcd);
#include "vhci.h"
#include <linux/in.h>
+#include <linux/kthread.h>
/* TODO: refine locking ?*/
vdev->ud.tcp_socket = socket;
vdev->ud.status = VDEV_ST_NOTASSIGNED;
- wake_up_process(vdev->ud.tcp_rx);
- wake_up_process(vdev->ud.tcp_tx);
-
spin_unlock(&vdev->ud.lock);
spin_unlock(&the_controller->lock);
/* end the lock */
+ vdev->ud.tcp_rx = kthread_run(vhci_rx_loop, &vdev->ud, "vhci_rx");
+ vdev->ud.tcp_tx = kthread_run(vhci_tx_loop, &vdev->ud, "vhci_tx");
+
rh_port_connect(rhport, speed);
return count;
}
int prism2_set_default_key(struct wiphy *wiphy, struct net_device *dev,
- u8 key_index)
+ u8 key_index, bool unicast, bool multicast)
{
wlandevice_t *wlandev = dev->ml_priv;
if ((gsm->control & ~PF) == UI)
gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len);
- /* generate final CRC with received FCS */
- gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs);
+ if (gsm->encoding == 0){
+ /* WARNING: gsm->received_fcs is used for gsm->encoding = 0 only.
+ In this case it contain the last piece of data
+ required to generate final CRC */
+ gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs);
+ }
if (gsm->fcs != GOOD_FCS) {
gsm->bad_fcs++;
if (debug & 4)
static irqreturn_t imx_rtsint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
- unsigned int val = readl(sport->port.membase + USR1) & USR1_RTSS;
+ unsigned int val;
unsigned long flags;
spin_lock_irqsave(&sport->port.lock, flags);
writel(USR1_RTSD, sport->port.membase + USR1);
+ val = readl(sport->port.membase + USR1) & USR1_RTSS;
uart_handle_cts_change(&sport->port, !!val);
wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
default y if ARCH_VT8500
default y if PLAT_SPEAR
default y if ARCH_MSM
+ default y if MICROBLAZE
default PCI
# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
break;
case USB_ENDPOINT_XFER_INT:
type = "Int.";
- if (speed == USB_SPEED_HIGH)
+ if (speed == USB_SPEED_HIGH || speed == USB_SPEED_SUPER)
interval = 1 << (desc->bInterval - 1);
else
interval = desc->bInterval;
default: /* "can't happen" */
return start;
}
- interval *= (speed == USB_SPEED_HIGH) ? 125 : 1000;
+ interval *= (speed == USB_SPEED_HIGH ||
+ speed == USB_SPEED_SUPER) ? 125 : 1000;
if (interval % 1000)
unit = 'u';
else {
if (level == 0) {
int max;
- /* high speed reserves 80%, full/low reserves 90% */
- if (usbdev->speed == USB_SPEED_HIGH)
+ /* super/high speed reserves 80%, full/low reserves 90% */
+ if (usbdev->speed == USB_SPEED_HIGH ||
+ usbdev->speed == USB_SPEED_SUPER)
max = 800;
else
max = FRAME_TIME_MAX_USECS_ALLOC;
/* Streams only apply to bulk endpoints. */
for (i = 0; i < num_eps; i++)
- if (!usb_endpoint_xfer_bulk(&eps[i]->desc))
+ if (!eps[i] || !usb_endpoint_xfer_bulk(&eps[i]->desc))
return;
hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
}
/* see 7.1.7.6 */
- status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND);
+ /* Clear PORT_POWER if it's a USB3.0 device connected to USB 3.0
+ * external hub.
+ * FIXME: this is a temporary workaround to make the system able
+ * to suspend/resume.
+ */
+ if ((hub->hdev->parent != NULL) && hub_is_superspeed(hub->hdev))
+ status = clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_POWER);
+ else
+ status = set_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_SUSPEND);
if (status) {
dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
port1, status);
struct f_audio *audio = func_to_audio(f);
usb_free_descriptors(f->descriptors);
+ usb_free_descriptors(f->hs_descriptors);
kfree(audio);
}
static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req)
{
+ struct sk_buff *skb = (struct sk_buff *)req->context;
+
+ dev_kfree_skb_any(skb);
}
/*
skb_trim(skb2, len);
put_unaligned_le16(BIT(15) | BIT(11) | len,
skb_push(skb2, 2));
- skb_copy_bits(skb, 0, req->buf, skb->len);
- req->length = skb->len;
+ skb_copy_bits(skb2, 0, req->buf, skb2->len);
+ req->length = skb2->len;
req->complete = eem_cmd_complete;
req->zero = 1;
+ req->context = skb2;
if (usb_ep_queue(port->in_ep, req, GFP_ATOMIC))
DBG(cdev, "echo response queue fail\n");
break;
static int txcomplete(struct qe_ep *ep, unsigned char restart)
{
if (ep->tx_req != NULL) {
+ struct qe_req *req = ep->tx_req;
+ unsigned zlp = 0, last_len = 0;
+
+ last_len = min_t(unsigned, req->req.length - ep->sent,
+ ep->ep.maxpacket);
+
if (!restart) {
int asent = ep->last;
ep->sent += asent;
ep->last = 0;
}
+ /* zlp needed when req->re.zero is set */
+ if (req->req.zero) {
+ if (last_len == 0 ||
+ (req->req.length % ep->ep.maxpacket) != 0)
+ zlp = 0;
+ else
+ zlp = 1;
+ } else
+ zlp = 0;
+
/* a request already were transmitted completely */
- if ((ep->tx_req->req.length - ep->sent) <= 0) {
- ep->tx_req->req.actual = (unsigned int)ep->sent;
+ if (((ep->tx_req->req.length - ep->sent) <= 0) && !zlp) {
done(ep, ep->tx_req, 0);
ep->tx_req = NULL;
ep->last = 0;
buf = (u8 *)ep->tx_req->req.buf + ep->sent;
if (buf && size) {
ep->last = size;
+ ep->tx_req->req.actual += size;
frame_set_data(frame, buf);
frame_set_length(frame, size);
frame_set_status(frame, FRAME_OK);
/* halt any endpoint by doing a "wrong direction" i/o call */
if (usb_endpoint_dir_in(&data->desc)) {
- if (usb_endpoint_xfer_isoc(&data->desc))
+ if (usb_endpoint_xfer_isoc(&data->desc)) {
+ mutex_unlock(&data->lock);
return -EINVAL;
+ }
DBG (data->dev, "%s halt\n", data->name);
spin_lock_irq (&data->dev->lock);
if (likely (data->ep != NULL))
return -EINVAL;
if (!dev->driver || (dev->gadget.speed == USB_SPEED_UNKNOWN))
return -ESHUTDOWN;
- spin_lock_irqsave(&ep->dev->lock, iflags);
+ spin_lock_irqsave(&dev->lock, iflags);
/* map the buffer for dma */
if (usbreq->length &&
((usbreq->dma == DMA_ADDR_INVALID) || !usbreq->dma)) {
DMA_FROM_DEVICE);
} else {
req->buf = kzalloc(usbreq->length, GFP_ATOMIC);
- if (!req->buf)
- return -ENOMEM;
+ if (!req->buf) {
+ retval = -ENOMEM;
+ goto probe_end;
+ }
if (ep->in) {
memcpy(req->buf, usbreq->buf, usbreq->length);
req->dma = dma_map_single(&dev->pdev->dev,
if (dvsq == DS_DFLT) {
/* bus reset */
+ spin_unlock(&r8a66597->lock);
r8a66597->driver->disconnect(&r8a66597->gadget);
+ spin_lock(&r8a66597->lock);
r8a66597_update_usb_speed(r8a66597);
}
if (r8a66597->old_dvsq == DS_CNFG && dvsq != DS_CNFG)
#include <linux/slab.h>
#include <linux/usb/ulpi.h>
#include <plat/usb.h>
+#include <linux/regulator/consumer.h>
/* EHCI Register Set */
#define EHCI_INSNREG04 (0xA0)
struct ehci_hcd *omap_ehci;
int ret = -ENODEV;
int irq;
+ int i;
+ char supply[7];
if (usb_disabled())
return -ENODEV;
hcd->rsrc_len = resource_size(res);
hcd->regs = regs;
+ /* get ehci regulator and enable */
+ for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) {
+ if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY) {
+ pdata->regulator[i] = NULL;
+ continue;
+ }
+ snprintf(supply, sizeof(supply), "hsusb%d", i);
+ pdata->regulator[i] = regulator_get(dev, supply);
+ if (IS_ERR(pdata->regulator[i])) {
+ pdata->regulator[i] = NULL;
+ dev_dbg(dev,
+ "failed to get ehci port%d regulator\n", i);
+ } else {
+ regulator_enable(pdata->regulator[i]);
+ }
+ }
+
ret = omap_usbhs_enable(dev);
if (ret) {
dev_err(dev, "failed to start usbhs with err %d\n", ret);
static void scan_async (struct ehci_hcd *ehci)
{
+ bool stopped;
struct ehci_qh *qh;
enum ehci_timer_action action = TIMER_IO_WATCHDOG;
ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index);
timer_action_done (ehci, TIMER_ASYNC_SHRINK);
rescan:
+ stopped = !HC_IS_RUNNING(ehci_to_hcd(ehci)->state);
qh = ehci->async->qh_next.qh;
if (likely (qh != NULL)) {
do {
/* clean any finished work for this qh */
- if (!list_empty (&qh->qtd_list)
- && qh->stamp != ehci->stamp) {
+ if (!list_empty(&qh->qtd_list) && (stopped ||
+ qh->stamp != ehci->stamp)) {
int temp;
/* unlinks could happen here; completion
* reporting drops the lock. rescan using
* the latest schedule, but don't rescan
- * qhs we already finished (no looping).
+ * qhs we already finished (no looping)
+ * unless the controller is stopped.
*/
qh = qh_get (qh);
qh->stamp = ehci->stamp;
*/
if (list_empty(&qh->qtd_list)
&& qh->qh_state == QH_STATE_LINKED) {
- if (!ehci->reclaim
- && ((ehci->stamp - qh->stamp) & 0x1fff)
- >= (EHCI_SHRINK_FRAMES * 8))
+ if (!ehci->reclaim && (stopped ||
+ ((ehci->stamp - qh->stamp) & 0x1fff)
+ >= EHCI_SHRINK_FRAMES * 8))
start_unlink_async(ehci, qh);
else
action = TIMER_ASYNC_SHRINK;
}
dev_err(hcd->self.controller,
- "%s: Can not allocate %lu bytes of memory\n"
+ "%s: Cannot allocate %zu bytes of memory\n"
"Current memory map:\n",
__func__, qtd->length);
for (i = 0; i < BLOCKS; i++) {
ints[i].qh = NULL;
ints[i].qtd = NULL;
+ urb->status = status;
isp1760_urb_done(hcd, urb);
if (qtd)
pe(hcd, qh, qtd);
#ifdef __LITTLE_ENDIAN
#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C)
-#elif __BIG_ENDIAN
+#elif defined(__BIG_ENDIAN)
#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | \
USBH_ENABLE_BE)
#else
{
u8 rev = 0;
unsigned long flags;
+ struct amd_chipset_info info;
+ int ret;
spin_lock_irqsave(&amd_lock, flags);
- amd_chipset.probe_count++;
/* probe only once */
- if (amd_chipset.probe_count > 1) {
+ if (amd_chipset.probe_count > 0) {
+ amd_chipset.probe_count++;
spin_unlock_irqrestore(&amd_lock, flags);
return amd_chipset.probe_result;
}
+ memset(&info, 0, sizeof(info));
+ spin_unlock_irqrestore(&amd_lock, flags);
- amd_chipset.smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, 0x4385, NULL);
- if (amd_chipset.smbus_dev) {
- rev = amd_chipset.smbus_dev->revision;
+ info.smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, 0x4385, NULL);
+ if (info.smbus_dev) {
+ rev = info.smbus_dev->revision;
if (rev >= 0x40)
- amd_chipset.sb_type = 1;
+ info.sb_type = 1;
else if (rev >= 0x30 && rev <= 0x3b)
- amd_chipset.sb_type = 3;
+ info.sb_type = 3;
} else {
- amd_chipset.smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
- 0x780b, NULL);
- if (!amd_chipset.smbus_dev) {
- spin_unlock_irqrestore(&amd_lock, flags);
- return 0;
+ info.smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ 0x780b, NULL);
+ if (!info.smbus_dev) {
+ ret = 0;
+ goto commit;
}
- rev = amd_chipset.smbus_dev->revision;
+
+ rev = info.smbus_dev->revision;
if (rev >= 0x11 && rev <= 0x18)
- amd_chipset.sb_type = 2;
+ info.sb_type = 2;
}
- if (amd_chipset.sb_type == 0) {
- if (amd_chipset.smbus_dev) {
- pci_dev_put(amd_chipset.smbus_dev);
- amd_chipset.smbus_dev = NULL;
+ if (info.sb_type == 0) {
+ if (info.smbus_dev) {
+ pci_dev_put(info.smbus_dev);
+ info.smbus_dev = NULL;
}
- spin_unlock_irqrestore(&amd_lock, flags);
- return 0;
+ ret = 0;
+ goto commit;
}
- amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9601, NULL);
- if (amd_chipset.nb_dev) {
- amd_chipset.nb_type = 1;
+ info.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9601, NULL);
+ if (info.nb_dev) {
+ info.nb_type = 1;
} else {
- amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD,
- 0x1510, NULL);
- if (amd_chipset.nb_dev) {
- amd_chipset.nb_type = 2;
- } else {
- amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD,
- 0x9600, NULL);
- if (amd_chipset.nb_dev)
- amd_chipset.nb_type = 3;
+ info.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x1510, NULL);
+ if (info.nb_dev) {
+ info.nb_type = 2;
+ } else {
+ info.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ 0x9600, NULL);
+ if (info.nb_dev)
+ info.nb_type = 3;
}
}
- amd_chipset.probe_result = 1;
+ ret = info.probe_result = 1;
printk(KERN_DEBUG "QUIRK: Enable AMD PLL fix\n");
- spin_unlock_irqrestore(&amd_lock, flags);
- return amd_chipset.probe_result;
+commit:
+
+ spin_lock_irqsave(&amd_lock, flags);
+ if (amd_chipset.probe_count > 0) {
+ /* race - someone else was faster - drop devices */
+
+ /* Mark that we where here */
+ amd_chipset.probe_count++;
+ ret = amd_chipset.probe_result;
+
+ spin_unlock_irqrestore(&amd_lock, flags);
+
+ if (info.nb_dev)
+ pci_dev_put(info.nb_dev);
+ if (info.smbus_dev)
+ pci_dev_put(info.smbus_dev);
+
+ } else {
+ /* no race - commit the result */
+ info.probe_count++;
+ amd_chipset = info;
+ spin_unlock_irqrestore(&amd_lock, flags);
+ }
+
+ return ret;
}
EXPORT_SYMBOL_GPL(usb_amd_find_chipset_info);
void usb_amd_dev_put(void)
{
+ struct pci_dev *nb, *smbus;
unsigned long flags;
spin_lock_irqsave(&amd_lock, flags);
return;
}
- if (amd_chipset.nb_dev) {
- pci_dev_put(amd_chipset.nb_dev);
- amd_chipset.nb_dev = NULL;
- }
- if (amd_chipset.smbus_dev) {
- pci_dev_put(amd_chipset.smbus_dev);
- amd_chipset.smbus_dev = NULL;
- }
+ /* save them to pci_dev_put outside of spinlock */
+ nb = amd_chipset.nb_dev;
+ smbus = amd_chipset.smbus_dev;
+
+ amd_chipset.nb_dev = NULL;
+ amd_chipset.smbus_dev = NULL;
amd_chipset.nb_type = 0;
amd_chipset.sb_type = 0;
amd_chipset.isoc_reqs = 0;
amd_chipset.probe_result = 0;
spin_unlock_irqrestore(&amd_lock, flags);
+
+ if (nb)
+ pci_dev_put(nb);
+ if (smbus)
+ pci_dev_put(smbus);
}
EXPORT_SYMBOL_GPL(usb_amd_dev_put);
if (t1 != t2)
xhci_writel(xhci, t2, port_array[port_index]);
- if (DEV_HIGHSPEED(t1)) {
+ if (hcd->speed != HCD_USB3) {
/* enable remote wake up for USB 2.0 */
u32 __iomem *addr;
u32 tmp;
temp |= PORT_LINK_STROBE | XDEV_U0;
xhci_writel(xhci, temp, port_array[port_index]);
}
+ /* wait for the port to enter U0 and report port link
+ * state change.
+ */
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ msleep(20);
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ /* Clear PLC */
+ temp = xhci_readl(xhci, port_array[port_index]);
+ if (temp & PORT_PLC) {
+ temp = xhci_port_state_to_neutral(temp);
+ temp |= PORT_PLC;
+ xhci_writel(xhci, temp, port_array[port_index]);
+ }
+
slot_id = xhci_find_slot_id_by_port(hcd,
xhci, port_index + 1);
if (slot_id)
} else
xhci_writel(xhci, temp, port_array[port_index]);
- if (DEV_HIGHSPEED(temp)) {
+ if (hcd->speed != HCD_USB3) {
/* disable remote wake up for USB 2.0 */
u32 __iomem *addr;
u32 tmp;
* Skip ports that don't have known speeds, or have duplicate
* Extended Capabilities port speed entries.
*/
- if (port_speed == 0 || port_speed == -1)
+ if (port_speed == 0 || port_speed == DUPLICATE_ENTRY)
continue;
/*
return 0;
}
+/*
+ * Convert interval expressed as 2^(bInterval - 1) == interval into
+ * straight exponent value 2^n == interval.
+ *
+ */
+static unsigned int xhci_parse_exponent_interval(struct usb_device *udev,
+ struct usb_host_endpoint *ep)
+{
+ unsigned int interval;
+
+ interval = clamp_val(ep->desc.bInterval, 1, 16) - 1;
+ if (interval != ep->desc.bInterval - 1)
+ dev_warn(&udev->dev,
+ "ep %#x - rounding interval to %d microframes\n",
+ ep->desc.bEndpointAddress,
+ 1 << interval);
+
+ return interval;
+}
+
+/*
+ * Convert bInterval expressed in frames (in 1-255 range) to exponent of
+ * microframes, rounded down to nearest power of 2.
+ */
+static unsigned int xhci_parse_frame_interval(struct usb_device *udev,
+ struct usb_host_endpoint *ep)
+{
+ unsigned int interval;
+
+ interval = fls(8 * ep->desc.bInterval) - 1;
+ interval = clamp_val(interval, 3, 10);
+ if ((1 << interval) != 8 * ep->desc.bInterval)
+ dev_warn(&udev->dev,
+ "ep %#x - rounding interval to %d microframes, ep desc says %d microframes\n",
+ ep->desc.bEndpointAddress,
+ 1 << interval,
+ 8 * ep->desc.bInterval);
+
+ return interval;
+}
+
/* Return the polling or NAK interval.
*
* The polling interval is expressed in "microframes". If xHCI's Interval field
* The NAK interval is one NAK per 1 to 255 microframes, or no NAKs if interval
* is set to 0.
*/
-static inline unsigned int xhci_get_endpoint_interval(struct usb_device *udev,
+static unsigned int xhci_get_endpoint_interval(struct usb_device *udev,
struct usb_host_endpoint *ep)
{
unsigned int interval = 0;
case USB_SPEED_HIGH:
/* Max NAK rate */
if (usb_endpoint_xfer_control(&ep->desc) ||
- usb_endpoint_xfer_bulk(&ep->desc))
+ usb_endpoint_xfer_bulk(&ep->desc)) {
interval = ep->desc.bInterval;
+ break;
+ }
/* Fall through - SS and HS isoc/int have same decoding */
+
case USB_SPEED_SUPER:
if (usb_endpoint_xfer_int(&ep->desc) ||
- usb_endpoint_xfer_isoc(&ep->desc)) {
- if (ep->desc.bInterval == 0)
- interval = 0;
- else
- interval = ep->desc.bInterval - 1;
- if (interval > 15)
- interval = 15;
- if (interval != ep->desc.bInterval + 1)
- dev_warn(&udev->dev, "ep %#x - rounding interval to %d microframes\n",
- ep->desc.bEndpointAddress, 1 << interval);
+ usb_endpoint_xfer_isoc(&ep->desc)) {
+ interval = xhci_parse_exponent_interval(udev, ep);
}
break;
- /* Convert bInterval (in 1-255 frames) to microframes and round down to
- * nearest power of 2.
- */
+
case USB_SPEED_FULL:
+ if (usb_endpoint_xfer_int(&ep->desc)) {
+ interval = xhci_parse_exponent_interval(udev, ep);
+ break;
+ }
+ /*
+ * Fall through for isochronous endpoint interval decoding
+ * since it uses the same rules as low speed interrupt
+ * endpoints.
+ */
+
case USB_SPEED_LOW:
if (usb_endpoint_xfer_int(&ep->desc) ||
- usb_endpoint_xfer_isoc(&ep->desc)) {
- interval = fls(8*ep->desc.bInterval) - 1;
- if (interval > 10)
- interval = 10;
- if (interval < 3)
- interval = 3;
- if ((1 << interval) != 8*ep->desc.bInterval)
- dev_warn(&udev->dev,
- "ep %#x - rounding interval"
- " to %d microframes, "
- "ep desc says %d microframes\n",
- ep->desc.bEndpointAddress,
- 1 << interval,
- 8*ep->desc.bInterval);
+ usb_endpoint_xfer_isoc(&ep->desc)) {
+
+ interval = xhci_parse_frame_interval(udev, ep);
}
break;
+
default:
BUG();
}
* transaction opportunities per microframe", but that goes in the Max Burst
* endpoint context field.
*/
-static inline u32 xhci_get_endpoint_mult(struct usb_device *udev,
+static u32 xhci_get_endpoint_mult(struct usb_device *udev,
struct usb_host_endpoint *ep)
{
if (udev->speed != USB_SPEED_SUPER ||
return ep->ss_ep_comp.bmAttributes;
}
-static inline u32 xhci_get_endpoint_type(struct usb_device *udev,
+static u32 xhci_get_endpoint_type(struct usb_device *udev,
struct usb_host_endpoint *ep)
{
int in;
* Basically, this is the maxpacket size, multiplied by the burst size
* and mult size.
*/
-static inline u32 xhci_get_max_esit_payload(struct xhci_hcd *xhci,
+static u32 xhci_get_max_esit_payload(struct xhci_hcd *xhci,
struct usb_device *udev,
struct usb_host_endpoint *ep)
{
* found a similar duplicate.
*/
if (xhci->port_array[i] != major_revision &&
- xhci->port_array[i] != (u8) -1) {
+ xhci->port_array[i] != DUPLICATE_ENTRY) {
if (xhci->port_array[i] == 0x03)
xhci->num_usb3_ports--;
else
xhci->num_usb2_ports--;
- xhci->port_array[i] = (u8) -1;
+ xhci->port_array[i] = DUPLICATE_ENTRY;
}
/* FIXME: Should we disable the port? */
continue;
for (i = 0; i < num_ports; i++) {
if (xhci->port_array[i] == 0x03 ||
xhci->port_array[i] == 0 ||
- xhci->port_array[i] == -1)
+ xhci->port_array[i] == DUPLICATE_ENTRY)
continue;
xhci->usb2_ports[port_index] =
if (pdev->vendor == PCI_VENDOR_ID_NEC)
xhci->quirks |= XHCI_NEC_HOST;
+ /* AMD PLL quirk */
+ if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info())
+ xhci->quirks |= XHCI_AMD_PLL_FIX;
+
/* Make sure the HC is halted. */
retval = xhci_halt(xhci);
if (retval)
/* Does this link TRB point to the first segment in a ring,
* or was the previous TRB the last TRB on the last segment in the ERST?
*/
-static inline bool last_trb_on_last_seg(struct xhci_hcd *xhci, struct xhci_ring *ring,
+static bool last_trb_on_last_seg(struct xhci_hcd *xhci, struct xhci_ring *ring,
struct xhci_segment *seg, union xhci_trb *trb)
{
if (ring == xhci->event_ring)
* segment? I.e. would the updated event TRB pointer step off the end of the
* event seg?
*/
-static inline int last_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
+static int last_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
struct xhci_segment *seg, union xhci_trb *trb)
{
if (ring == xhci->event_ring)
return (trb->link.control & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK);
}
-static inline int enqueue_is_link_trb(struct xhci_ring *ring)
+static int enqueue_is_link_trb(struct xhci_ring *ring)
{
struct xhci_link_trb *link = &ring->enqueue->link;
return ((link->control & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK));
ep->ep_state |= SET_DEQ_PENDING;
}
-static inline void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci,
+static void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci,
struct xhci_virt_ep *ep)
{
ep->ep_state &= ~EP_HALT_PENDING;
/* Only giveback urb when this is the last td in urb */
if (urb_priv->td_cnt == urb_priv->length) {
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs--;
+ if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
+ if (xhci->quirks & XHCI_AMD_PLL_FIX)
+ usb_amd_quirk_pll_enable();
+ }
+ }
usb_hcd_unlink_urb_from_ep(hcd, urb);
xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, urb);
* Skip ports that don't have known speeds, or have duplicate
* Extended Capabilities port speed entries.
*/
- if (port_speed == 0 || port_speed == -1)
+ if (port_speed == 0 || port_speed == DUPLICATE_ENTRY)
continue;
/*
u8 major_revision;
struct xhci_bus_state *bus_state;
u32 __iomem **port_array;
+ bool bogus_port_status = false;
/* Port status change events always have a successful completion code */
if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) {
max_ports = HCS_MAX_PORTS(xhci->hcs_params1);
if ((port_id <= 0) || (port_id > max_ports)) {
xhci_warn(xhci, "Invalid port id %d\n", port_id);
+ bogus_port_status = true;
goto cleanup;
}
xhci_warn(xhci, "Event for port %u not in "
"Extended Capabilities, ignoring.\n",
port_id);
+ bogus_port_status = true;
goto cleanup;
}
- if (major_revision == (u8) -1) {
+ if (major_revision == DUPLICATE_ENTRY) {
xhci_warn(xhci, "Event for port %u duplicated in"
"Extended Capabilities, ignoring.\n",
port_id);
+ bogus_port_status = true;
goto cleanup;
}
/* Update event ring dequeue pointer before dropping the lock */
inc_deq(xhci, xhci->event_ring, true);
+ /* Don't make the USB core poll the roothub if we got a bad port status
+ * change event. Besides, at that point we can't tell which roothub
+ * (USB 2.0 or USB 3.0) to kick.
+ */
+ if (bogus_port_status)
+ return;
+
spin_unlock(&xhci->lock);
/* Pass this up to the core */
usb_hcd_poll_rh_status(hcd);
urb_priv->td_cnt++;
/* Giveback the urb when all the tds are completed */
- if (urb_priv->td_cnt == urb_priv->length)
+ if (urb_priv->td_cnt == urb_priv->length) {
ret = 1;
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs--;
+ if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs
+ == 0) {
+ if (xhci->quirks & XHCI_AMD_PLL_FIX)
+ usb_amd_quirk_pll_enable();
+ }
+ }
+ }
}
return ret;
struct urb_priv *urb_priv;
int idx;
int len = 0;
- int skip_td = 0;
union xhci_trb *cur_trb;
struct xhci_segment *cur_seg;
+ struct usb_iso_packet_descriptor *frame;
u32 trb_comp_code;
+ bool skip_td = false;
ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
trb_comp_code = GET_COMP_CODE(event->transfer_len);
urb_priv = td->urb->hcpriv;
idx = urb_priv->td_cnt;
+ frame = &td->urb->iso_frame_desc[idx];
- if (ep->skip) {
- /* The transfer is partly done */
- *status = -EXDEV;
- td->urb->iso_frame_desc[idx].status = -EXDEV;
- } else {
- /* handle completion code */
- switch (trb_comp_code) {
- case COMP_SUCCESS:
- td->urb->iso_frame_desc[idx].status = 0;
- xhci_dbg(xhci, "Successful isoc transfer!\n");
- break;
- case COMP_SHORT_TX:
- if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- td->urb->iso_frame_desc[idx].status =
- -EREMOTEIO;
- else
- td->urb->iso_frame_desc[idx].status = 0;
- break;
- case COMP_BW_OVER:
- td->urb->iso_frame_desc[idx].status = -ECOMM;
- skip_td = 1;
- break;
- case COMP_BUFF_OVER:
- case COMP_BABBLE:
- td->urb->iso_frame_desc[idx].status = -EOVERFLOW;
- skip_td = 1;
- break;
- case COMP_STALL:
- td->urb->iso_frame_desc[idx].status = -EPROTO;
- skip_td = 1;
- break;
- case COMP_STOP:
- case COMP_STOP_INVAL:
- break;
- default:
- td->urb->iso_frame_desc[idx].status = -1;
- break;
- }
- }
-
- /* calc actual length */
- if (ep->skip) {
- td->urb->iso_frame_desc[idx].actual_length = 0;
- /* Update ring dequeue pointer */
- while (ep_ring->dequeue != td->last_trb)
- inc_deq(xhci, ep_ring, false);
- inc_deq(xhci, ep_ring, false);
- return finish_td(xhci, td, event_trb, event, ep, status, true);
+ /* handle completion code */
+ switch (trb_comp_code) {
+ case COMP_SUCCESS:
+ frame->status = 0;
+ xhci_dbg(xhci, "Successful isoc transfer!\n");
+ break;
+ case COMP_SHORT_TX:
+ frame->status = td->urb->transfer_flags & URB_SHORT_NOT_OK ?
+ -EREMOTEIO : 0;
+ break;
+ case COMP_BW_OVER:
+ frame->status = -ECOMM;
+ skip_td = true;
+ break;
+ case COMP_BUFF_OVER:
+ case COMP_BABBLE:
+ frame->status = -EOVERFLOW;
+ skip_td = true;
+ break;
+ case COMP_STALL:
+ frame->status = -EPROTO;
+ skip_td = true;
+ break;
+ case COMP_STOP:
+ case COMP_STOP_INVAL:
+ break;
+ default:
+ frame->status = -1;
+ break;
}
- if (trb_comp_code == COMP_SUCCESS || skip_td == 1) {
- td->urb->iso_frame_desc[idx].actual_length =
- td->urb->iso_frame_desc[idx].length;
- td->urb->actual_length +=
- td->urb->iso_frame_desc[idx].length;
+ if (trb_comp_code == COMP_SUCCESS || skip_td) {
+ frame->actual_length = frame->length;
+ td->urb->actual_length += frame->length;
} else {
for (cur_trb = ep_ring->dequeue,
cur_seg = ep_ring->deq_seg; cur_trb != event_trb;
TRB_LEN(event->transfer_len);
if (trb_comp_code != COMP_STOP_INVAL) {
- td->urb->iso_frame_desc[idx].actual_length = len;
+ frame->actual_length = len;
td->urb->actual_length += len;
}
}
return finish_td(xhci, td, event_trb, event, ep, status, false);
}
+static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
+ struct xhci_transfer_event *event,
+ struct xhci_virt_ep *ep, int *status)
+{
+ struct xhci_ring *ep_ring;
+ struct urb_priv *urb_priv;
+ struct usb_iso_packet_descriptor *frame;
+ int idx;
+
+ ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
+ urb_priv = td->urb->hcpriv;
+ idx = urb_priv->td_cnt;
+ frame = &td->urb->iso_frame_desc[idx];
+
+ /* The transfer is partly done */
+ *status = -EXDEV;
+ frame->status = -EXDEV;
+
+ /* calc actual length */
+ frame->actual_length = 0;
+
+ /* Update ring dequeue pointer */
+ while (ep_ring->dequeue != td->last_trb)
+ inc_deq(xhci, ep_ring, false);
+ inc_deq(xhci, ep_ring, false);
+
+ return finish_td(xhci, td, NULL, event, ep, status, true);
+}
+
/*
* Process bulk and interrupt tds, update urb status and actual_length.
*/
}
td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
+
/* Is this a TRB in the currently executing TD? */
event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue,
td->last_trb, event_dma);
- if (event_seg && ep->skip) {
+ if (!event_seg) {
+ if (!ep->skip ||
+ !usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
+ /* HC is busted, give up! */
+ xhci_err(xhci,
+ "ERROR Transfer event TRB DMA ptr not "
+ "part of current TD\n");
+ return -ESHUTDOWN;
+ }
+
+ ret = skip_isoc_td(xhci, td, event, ep, &status);
+ goto cleanup;
+ }
+
+ if (ep->skip) {
xhci_dbg(xhci, "Found td. Clear skip flag.\n");
ep->skip = false;
}
- if (!event_seg &&
- (!ep->skip || !usb_endpoint_xfer_isoc(&td->urb->ep->desc))) {
- /* HC is busted, give up! */
- xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not "
- "part of current TD\n");
- return -ESHUTDOWN;
- }
- if (event_seg) {
- event_trb = &event_seg->trbs[(event_dma -
- event_seg->dma) / sizeof(*event_trb)];
- /*
- * No-op TRB should not trigger interrupts.
- * If event_trb is a no-op TRB, it means the
- * corresponding TD has been cancelled. Just ignore
- * the TD.
- */
- if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK)
- == TRB_TYPE(TRB_TR_NOOP)) {
- xhci_dbg(xhci, "event_trb is a no-op TRB. "
- "Skip it\n");
- goto cleanup;
- }
+ event_trb = &event_seg->trbs[(event_dma - event_seg->dma) /
+ sizeof(*event_trb)];
+ /*
+ * No-op TRB should not trigger interrupts.
+ * If event_trb is a no-op TRB, it means the
+ * corresponding TD has been cancelled. Just ignore
+ * the TD.
+ */
+ if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK)
+ == TRB_TYPE(TRB_TR_NOOP)) {
+ xhci_dbg(xhci,
+ "event_trb is a no-op TRB. Skip it\n");
+ goto cleanup;
}
/* Now update the urb's actual_length and give back to
}
}
+ if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
+ if (xhci->quirks & XHCI_AMD_PLL_FIX)
+ usb_amd_quirk_pll_disable();
+ }
+ xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs++;
+
giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
start_cycle, start_trb);
return 0;
del_timer_sync(&xhci->event_ring_timer);
#endif
+ if (xhci->quirks & XHCI_AMD_PLL_FIX)
+ usb_amd_dev_put();
+
xhci_dbg(xhci, "// Disabling event ring interrupts\n");
temp = xhci_readl(xhci, &xhci->op_regs->status);
xhci_writel(xhci, temp & ~STS_EINT, &xhci->op_regs->status);
/* If restore operation fails, re-initialize the HC during resume */
if ((temp & STS_SRE) || hibernated) {
- usb_root_hub_lost_power(hcd->self.root_hub);
+ /* Let the USB core know _both_ roothubs lost power. */
+ usb_root_hub_lost_power(xhci->main_hcd->self.root_hub);
+ usb_root_hub_lost_power(xhci->shared_hcd->self.root_hub);
xhci_dbg(xhci, "Stop HCD\n");
xhci_halt(xhci);
/* Everything but endpoint 0 is disabled, so free or cache the rings. */
last_freed_endpoint = 1;
for (i = 1; i < 31; ++i) {
- if (!virt_dev->eps[i].ring)
- continue;
- xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
- last_freed_endpoint = i;
+ struct xhci_virt_ep *ep = &virt_dev->eps[i];
+
+ if (ep->ep_state & EP_HAS_STREAMS) {
+ xhci_free_stream_info(xhci, ep->stream_info);
+ ep->stream_info = NULL;
+ ep->ep_state &= ~EP_HAS_STREAMS;
+ }
+
+ if (ep->ring) {
+ xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
+ last_freed_endpoint = i;
+ }
}
xhci_dbg(xhci, "Output context after successful reset device cmd:\n");
xhci_dbg_ctx(xhci, virt_dev->out_ctx, last_freed_endpoint);
/* Code sharing between pci-quirks and xhci hcd */
#include "xhci-ext-caps.h"
+#include "pci-quirks.h"
/* xHCI PCI Configuration Registers */
#define XHCI_SBRN_OFFSET (0x60)
* notification type that matches a bit set in this bit field.
*/
#define DEV_NOTE_MASK (0xffff)
-#define ENABLE_DEV_NOTE(x) (1 << x)
+#define ENABLE_DEV_NOTE(x) (1 << (x))
/* Most of the device notification types should only be used for debug.
* SW does need to pay attention to function wake notifications.
*/
/* Initiate a warm port reset - complete when PORT_WRC is '1' */
#define PORT_WR (1 << 31)
+/* We mark duplicate entries with -1 */
+#define DUPLICATE_ENTRY ((u8)(-1))
+
/* Port Power Management Status and Control - port_power_base bitmasks */
/* Inactivity timer value for transitions into U1, in microseconds.
* Timeout can be up to 127us. 0xFF means an infinite timeout.
#define EP_STATE_STOPPED 3
#define EP_STATE_ERROR 4
/* Mult - Max number of burtst within an interval, in EP companion desc. */
-#define EP_MULT(p) ((p & 0x3) << 8)
+#define EP_MULT(p) (((p) & 0x3) << 8)
/* bits 10:14 are Max Primary Streams */
/* bit 15 is Linear Stream Array */
/* Interval - period between requests to an endpoint - 125u increments. */
-#define EP_INTERVAL(p) ((p & 0xff) << 16)
+#define EP_INTERVAL(p) (((p) & 0xff) << 16)
#define EP_INTERVAL_TO_UFRAMES(p) (1 << (((p) >> 16) & 0xff))
#define EP_MAXPSTREAMS_MASK (0x1f << 10)
#define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK)
#define XHCI_LINK_TRB_QUIRK (1 << 0)
#define XHCI_RESET_EP_QUIRK (1 << 1)
#define XHCI_NEC_HOST (1 << 2)
+#define XHCI_AMD_PLL_FIX (1 << 3)
/* There are two roothubs to keep track of bus suspend info for */
struct xhci_bus_state bus_state[2];
/* Is each xHCI roothub port a USB 3.0, USB 2.0, or USB 1.1 port? */
select TWL4030_USB if MACH_OMAP_3430SDP
select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
select USB_OTG_UTILS
- tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
+ bool 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
help
Say Y here if your system has a dual role high speed USB
controller based on the Mentor Graphics silicon IP. Then
If you do not know what this is, please say N.
- To compile this driver as a module, choose M here; the
- module will be called "musb-hdrc".
+# To compile this driver as a module, choose M here; the
+# module will be called "musb-hdrc".
choice
prompt "Platform Glue Layer"
#include <asm/cacheflush.h>
#include "musb_core.h"
+#include "musbhsdma.h"
#include "blackfin.h"
struct bfin_glue {
return -EIO;
}
+static int bfin_musb_adjust_channel_params(struct dma_channel *channel,
+ u16 packet_sz, u8 *mode,
+ dma_addr_t *dma_addr, u32 *len)
+{
+ struct musb_dma_channel *musb_channel = channel->private_data;
+
+ /*
+ * Anomaly 05000450 might cause data corruption when using DMA
+ * MODE 1 transmits with short packet. So to work around this,
+ * we truncate all MODE 1 transfers down to a multiple of the
+ * max packet size, and then do the last short packet transfer
+ * (if there is any) using MODE 0.
+ */
+ if (ANOMALY_05000450) {
+ if (musb_channel->transmit && *mode == 1)
+ *len = *len - (*len % packet_sz);
+ }
+
+ return 0;
+}
+
static void bfin_musb_reg_init(struct musb *musb)
{
if (ANOMALY_05000346) {
.vbus_status = bfin_musb_vbus_status,
.set_vbus = bfin_musb_set_vbus,
+
+ .adjust_channel_params = bfin_musb_adjust_channel_params,
};
static u64 bfin_dmamask = DMA_BIT_MASK(32);
length = min(n_bds * maxpacket, length);
}
- DBG(4, "TX DMA%d, pktSz %d %s bds %d dma 0x%x len %u\n",
+ DBG(4, "TX DMA%d, pktSz %d %s bds %d dma 0x%llx len %u\n",
tx->index,
maxpacket,
rndis ? "rndis" : "transparent",
n_bds,
- addr, length);
+ (unsigned long long)addr, length);
cppi_rndis_update(tx, 0, musb->ctrl_base, rndis);
length = min(n_bds * maxpacket, length);
DBG(4, "RX DMA%d seg, maxp %d %s bds %d (cnt %d) "
- "dma 0x%x len %u %u/%u\n",
+ "dma 0x%llx len %u %u/%u\n",
rx->index, maxpacket,
onepacket
? (is_rndis ? "rndis" : "onepacket")
musb_readl(tibase,
DAVINCI_RXCPPI_BUFCNT0_REG + (rx->index * 4))
& 0xffff,
- addr, length, rx->channel.actual_len, rx->buf_len);
+ (unsigned long long)addr, length,
+ rx->channel.actual_len, rx->buf_len);
/* only queue one segment at a time, since the hardware prevents
* correct queue shutdown after unexpected short packets
if (!completed && (bd->hw_options & CPPI_OWN_SET))
break;
- DBG(5, "C/RXBD %08x: nxt %08x buf %08x "
+ DBG(5, "C/RXBD %llx: nxt %08x buf %08x "
"off.len %08x opt.len %08x (%d)\n",
- bd->dma, bd->hw_next, bd->hw_bufp,
+ (unsigned long long)bd->dma, bd->hw_next, bd->hw_bufp,
bd->hw_off_len, bd->hw_options,
rx->channel.actual_len);
musb_ep_select(cppi->mregs, rx->index + 1);
csr = musb_readw(regs, MUSB_RXCSR);
if (csr & MUSB_RXCSR_DMAENAB) {
- DBG(4, "list%d %p/%p, last %08x%s, csr %04x\n",
+ DBG(4, "list%d %p/%p, last %llx%s, csr %04x\n",
rx->index,
rx->head, rx->tail,
rx->last_processed
- ? rx->last_processed->dma
+ ? (unsigned long long)
+ rx->last_processed->dma
: 0,
completed ? ", completed" : "",
csr);
tx = musb_readl(tibase, DAVINCI_TXCPPI_MASKED_REG);
rx = musb_readl(tibase, DAVINCI_RXCPPI_MASKED_REG);
- if (!tx && !rx)
+ if (!tx && !rx) {
+ if (cppi->irq)
+ spin_unlock_irqrestore(&musb->lock, flags);
return IRQ_NONE;
+ }
DBG(4, "CPPI IRQ Tx%x Rx%x\n", tx, rx);
*/
if (NULL == bd) {
DBG(1, "null BD\n");
- tx_ram->tx_complete = 0;
+ musb_writel(&tx_ram->tx_complete, 0, 0);
continue;
}
* compare mode by writing 1 to the tx_complete register.
*/
cppi_reset_tx(tx_ram, 1);
- cppi_ch->head = 0;
+ cppi_ch->head = NULL;
musb_writel(&tx_ram->tx_complete, 0, 1);
cppi_dump_tx(5, cppi_ch, " (done teardown)");
struct musb *musb = dev_to_musb(&pdev->dev);
unsigned long flags;
+ pm_runtime_get_sync(musb->controller);
spin_lock_irqsave(&musb->lock, flags);
musb_platform_disable(musb);
musb_generic_disable(musb);
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
musb_platform_exit(musb);
+ pm_runtime_put(musb->controller);
/* FIXME power down */
}
* @try_ilde: tries to idle the IP
* @vbus_status: returns vbus status if possible
* @set_vbus: forces vbus status
+ * @channel_program: pre check for standard dma channel_program func
*/
struct musb_platform_ops {
int (*init)(struct musb *musb);
int (*vbus_status)(struct musb *musb);
void (*set_vbus)(struct musb *musb, int on);
+
+ int (*adjust_channel_params)(struct dma_channel *channel,
+ u16 packet_sz, u8 *mode,
+ dma_addr_t *dma_addr, u32 *len);
};
/*
is_dma = 1;
csr |= MUSB_TXCSR_P_WZC_BITS;
csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_P_UNDERRUN |
- MUSB_TXCSR_TXPKTRDY);
+ MUSB_TXCSR_TXPKTRDY | MUSB_TXCSR_AUTOSET);
musb_writew(epio, MUSB_TXCSR, csr);
/* Ensure writebuffer is empty. */
csr = musb_readw(epio, MUSB_TXCSR);
}
/* if the hardware doesn't have the request, easy ... */
- if (musb_ep->req_list.next != &request->list || musb_ep->busy)
+ if (musb_ep->req_list.next != &req->list || musb_ep->busy)
musb_g_giveback(musb_ep, request, -ECONNRESET);
/* ... else abort the dma transfer ... */
otg_set_vbus(musb->xceiv, 1);
hcd->self.uses_pio_for_control = 1;
-
- if (musb->xceiv->last_event == USB_EVENT_NONE)
- pm_runtime_put(musb->controller);
-
}
+ if (musb->xceiv->last_event == USB_EVENT_NONE)
+ pm_runtime_put(musb->controller);
return 0;
BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN ||
channel->status == MUSB_DMA_STATUS_BUSY);
+ /* Let targets check/tweak the arguments */
+ if (musb->ops->adjust_channel_params) {
+ int ret = musb->ops->adjust_channel_params(channel,
+ packet_sz, &mode, &dma_addr, &len);
+ if (ret)
+ return ret;
+ }
+
/*
* The DMA engine in RTL1.8 and above cannot handle
* DMA addresses that are not aligned to a 4 byte boundary.
case USB_EVENT_VBUS:
DBG(4, "VBUS Connect\n");
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
if (musb->gadget_driver)
pm_runtime_get_sync(musb->controller);
-
+#endif
otg_init(musb->xceiv);
break;
DBG(4, "VBUS Disconnect\n");
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
- if (is_otg_enabled(musb))
+ if (is_otg_enabled(musb) || is_peripheral_enabled(musb))
if (musb->gadget_driver)
#endif
{
}
musb->dev.parent = &pdev->dev;
+ musb->dev.dma_mask = pdev->dev.dma_mask;
+ musb->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask;
glue->dev = &pdev->dev;
glue->musb = musb;
* /sys/bus/usb/ftdi_sio/new_id, then send patch/report!
*/
static struct usb_device_id id_table_combined [] = {
+ { USB_DEVICE(FTDI_VID, FTDI_CTI_MINI_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_CTI_NANO_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CANDAPTER_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_8_PID) },
{ USB_DEVICE(IDTECH_VID, IDTECH_IDT1221U_PID) },
{ USB_DEVICE(OCT_VID, OCT_US101_PID) },
+ { USB_DEVICE(OCT_VID, OCT_DK201_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_HE_TIRA1_PID),
.driver_info = (kernel_ulong_t)&ftdi_HE_TIRA1_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID),
{ USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(FTDI_VID, HAMEG_HO820_PID) },
+ { USB_DEVICE(FTDI_VID, HAMEG_HO720_PID) },
+ { USB_DEVICE(FTDI_VID, HAMEG_HO730_PID) },
{ USB_DEVICE(FTDI_VID, HAMEG_HO870_PID) },
{ USB_DEVICE(FTDI_VID, MJSG_GENERIC_PID) },
{ USB_DEVICE(FTDI_VID, MJSG_SR_RADIO_PID) },
* Hameg HO820 and HO870 interface (using VID 0x0403)
*/
#define HAMEG_HO820_PID 0xed74
+#define HAMEG_HO730_PID 0xed73
+#define HAMEG_HO720_PID 0xed72
#define HAMEG_HO870_PID 0xed71
/*
/* Note: OCT US101 is also rebadged as Dick Smith Electronics (NZ) XH6381 */
/* Also rebadged as Dick Smith Electronics (Aus) XH6451 */
/* Also rebadged as SIIG Inc. model US2308 hardware version 1 */
+#define OCT_DK201_PID 0x0103 /* OCT DK201 USB docking station */
#define OCT_US101_PID 0x0421 /* OCT US101 USB to RS-232 */
/*
#define QIHARDWARE_VID 0x20B7
#define MILKYMISTONE_JTAGSERIAL_PID 0x0713
+/*
+ * CTI GmbH RS485 Converter http://www.cti-lean.com/
+ */
+/* USB-485-Mini*/
+#define FTDI_CTI_MINI_PID 0xF608
+/* USB-Nano-485*/
+#define FTDI_CTI_NANO_PID 0xF60B
+
+
/* ONDA MT825UP HSDPA 14.2 modem */
#define ONDA_MT825UP 0x000b
+/* Samsung products */
+#define SAMSUNG_VENDOR_ID 0x04e8
+#define SAMSUNG_PRODUCT_GT_B3730 0x6889
+
/* some devices interfaces need special handling due to a number of reasons */
enum option_blacklist_reason {
OPTION_BLACKLIST_NONE = 0,
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD100) },
{ USB_DEVICE(CELOT_VENDOR_ID, CELOT_PRODUCT_CT680M) }, /* CT-650 CDMA 450 1xEVDO modem */
{ USB_DEVICE(ONDA_VENDOR_ID, ONDA_MT825UP) }, /* ONDA MT825UP modem */
+ { USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_GT_B3730, USB_CLASS_CDC_DATA, 0x00, 0x00) }, /* Samsung GT-B3730/GT-B3710 LTE USB modem.*/
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, option_ids);
ifnum = intf->desc.bInterfaceNumber;
dbg("This Interface = %d", ifnum);
- data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private),
+ data = kzalloc(sizeof(struct usb_wwan_intf_private),
GFP_KERNEL);
if (!data)
return -ENOMEM;
usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) {
dbg("QDL port found");
- if (serial->interface->num_altsetting == 1)
- return 0;
+ if (serial->interface->num_altsetting == 1) {
+ retval = 0; /* Success */
+ break;
+ }
retval = usb_set_interface(serial->dev, ifnum, 1);
if (retval < 0) {
retval = -ENODEV;
kfree(data);
}
- return retval;
}
break;
"Could not set interface, error %d\n",
retval);
retval = -ENODEV;
+ kfree(data);
}
} else if (ifnum == 2) {
dbg("Modem port found");
retval = -ENODEV;
kfree(data);
}
- return retval;
} else if (ifnum==3) {
/*
* NMEA (serial line 9600 8N1)
"Could not set interface, error %d\n",
retval);
retval = -ENODEV;
+ kfree(data);
}
}
break;
dev_err(&serial->dev->dev,
"unknown number of interfaces: %d\n", nintf);
kfree(data);
- return -ENODEV;
+ retval = -ENODEV;
}
+ /* Set serial->private if not returning -ENODEV */
+ if (retval != -ENODEV)
+ usb_set_serial_data(serial, data);
return retval;
}
+static void qc_release(struct usb_serial *serial)
+{
+ struct usb_wwan_intf_private *priv = usb_get_serial_data(serial);
+
+ dbg("%s", __func__);
+
+ /* Call usb_wwan release & free the private data allocated in qcprobe */
+ usb_wwan_release(serial);
+ usb_set_serial_data(serial, NULL);
+ kfree(priv);
+}
+
static struct usb_serial_driver qcdevice = {
.driver = {
.owner = THIS_MODULE,
.chars_in_buffer = usb_wwan_chars_in_buffer,
.attach = usb_wwan_startup,
.disconnect = usb_wwan_disconnect,
- .release = usb_wwan_release,
+ .release = qc_release,
#ifdef CONFIG_PM
.suspend = usb_wwan_suspend,
.resume = usb_wwan_resume,
switch (val) {
case CPUFREQ_PRECHANGE:
- if (!fbi->overlay[0].usage && !fbi->overlay[1].usage)
+#ifdef CONFIG_FB_PXA_OVERLAY
+ if (!(fbi->overlay[0].usage || fbi->overlay[1].usage))
+#endif
set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE);
break;
static void virtio_pci_release_dev(struct device *_d)
{
- struct virtio_device *dev = container_of(_d, struct virtio_device, dev);
+ struct virtio_device *dev = container_of(_d, struct virtio_device,
+ dev);
struct virtio_pci_device *vp_dev = to_vp_device(dev);
- struct pci_dev *pci_dev = vp_dev->pci_dev;
- vp_del_vqs(dev);
- pci_set_drvdata(pci_dev, NULL);
- pci_iounmap(pci_dev, vp_dev->ioaddr);
- pci_release_regions(pci_dev);
- pci_disable_device(pci_dev);
kfree(vp_dev);
}
struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
unregister_virtio_device(&vp_dev->vdev);
+
+ vp_del_vqs(&vp_dev->vdev);
+ pci_set_drvdata(pci_dev, NULL);
+ pci_iounmap(pci_dev, vp_dev->ioaddr);
+ pci_release_regions(pci_dev);
+ pci_disable_device(pci_dev);
}
#ifdef CONFIG_PM
/* detach_buf clears data, so grab it now. */
buf = vq->data[i];
detach_buf(vq, i);
+ vq->vring.avail->idx--;
END_USE(vq);
return buf;
}
* document number 324645-001, 324646-001: Cougar Point (CPT)
* document number TBD : Patsburg (PBG)
* document number TBD : DH89xxCC
+ * document number TBD : Panther Point
*/
/*
TCO_PBG1, /* Patsburg */
TCO_PBG2, /* Patsburg */
TCO_DH89XXCC, /* DH89xxCC */
+ TCO_PPT0, /* Panther Point */
+ TCO_PPT1, /* Panther Point */
+ TCO_PPT2, /* Panther Point */
+ TCO_PPT3, /* Panther Point */
+ TCO_PPT4, /* Panther Point */
+ TCO_PPT5, /* Panther Point */
+ TCO_PPT6, /* Panther Point */
+ TCO_PPT7, /* Panther Point */
+ TCO_PPT8, /* Panther Point */
+ TCO_PPT9, /* Panther Point */
+ TCO_PPT10, /* Panther Point */
+ TCO_PPT11, /* Panther Point */
+ TCO_PPT12, /* Panther Point */
+ TCO_PPT13, /* Panther Point */
+ TCO_PPT14, /* Panther Point */
+ TCO_PPT15, /* Panther Point */
+ TCO_PPT16, /* Panther Point */
+ TCO_PPT17, /* Panther Point */
+ TCO_PPT18, /* Panther Point */
+ TCO_PPT19, /* Panther Point */
+ TCO_PPT20, /* Panther Point */
+ TCO_PPT21, /* Panther Point */
+ TCO_PPT22, /* Panther Point */
+ TCO_PPT23, /* Panther Point */
+ TCO_PPT24, /* Panther Point */
+ TCO_PPT25, /* Panther Point */
+ TCO_PPT26, /* Panther Point */
+ TCO_PPT27, /* Panther Point */
+ TCO_PPT28, /* Panther Point */
+ TCO_PPT29, /* Panther Point */
+ TCO_PPT30, /* Panther Point */
+ TCO_PPT31, /* Panther Point */
};
static struct {
{"Patsburg", 2},
{"Patsburg", 2},
{"DH89xxCC", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
{NULL, 0}
};
{ ITCO_PCI_DEVICE(0x1d40, TCO_PBG1)},
{ ITCO_PCI_DEVICE(0x1d41, TCO_PBG2)},
{ ITCO_PCI_DEVICE(0x2310, TCO_DH89XXCC)},
+ { ITCO_PCI_DEVICE(0x1e40, TCO_PPT0)},
+ { ITCO_PCI_DEVICE(0x1e41, TCO_PPT1)},
+ { ITCO_PCI_DEVICE(0x1e42, TCO_PPT2)},
+ { ITCO_PCI_DEVICE(0x1e43, TCO_PPT3)},
+ { ITCO_PCI_DEVICE(0x1e44, TCO_PPT4)},
+ { ITCO_PCI_DEVICE(0x1e45, TCO_PPT5)},
+ { ITCO_PCI_DEVICE(0x1e46, TCO_PPT6)},
+ { ITCO_PCI_DEVICE(0x1e47, TCO_PPT7)},
+ { ITCO_PCI_DEVICE(0x1e48, TCO_PPT8)},
+ { ITCO_PCI_DEVICE(0x1e49, TCO_PPT9)},
+ { ITCO_PCI_DEVICE(0x1e4a, TCO_PPT10)},
+ { ITCO_PCI_DEVICE(0x1e4b, TCO_PPT11)},
+ { ITCO_PCI_DEVICE(0x1e4c, TCO_PPT12)},
+ { ITCO_PCI_DEVICE(0x1e4d, TCO_PPT13)},
+ { ITCO_PCI_DEVICE(0x1e4e, TCO_PPT14)},
+ { ITCO_PCI_DEVICE(0x1e4f, TCO_PPT15)},
+ { ITCO_PCI_DEVICE(0x1e50, TCO_PPT16)},
+ { ITCO_PCI_DEVICE(0x1e51, TCO_PPT17)},
+ { ITCO_PCI_DEVICE(0x1e52, TCO_PPT18)},
+ { ITCO_PCI_DEVICE(0x1e53, TCO_PPT19)},
+ { ITCO_PCI_DEVICE(0x1e54, TCO_PPT20)},
+ { ITCO_PCI_DEVICE(0x1e55, TCO_PPT21)},
+ { ITCO_PCI_DEVICE(0x1e56, TCO_PPT22)},
+ { ITCO_PCI_DEVICE(0x1e57, TCO_PPT23)},
+ { ITCO_PCI_DEVICE(0x1e58, TCO_PPT24)},
+ { ITCO_PCI_DEVICE(0x1e59, TCO_PPT25)},
+ { ITCO_PCI_DEVICE(0x1e5a, TCO_PPT26)},
+ { ITCO_PCI_DEVICE(0x1e5b, TCO_PPT27)},
+ { ITCO_PCI_DEVICE(0x1e5c, TCO_PPT28)},
+ { ITCO_PCI_DEVICE(0x1e5d, TCO_PPT29)},
+ { ITCO_PCI_DEVICE(0x1e5e, TCO_PPT30)},
+ { ITCO_PCI_DEVICE(0x1e5f, TCO_PPT31)},
{ 0, }, /* End of list */
};
MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl);
unsigned long irqflags,
const char *devname, void *dev_id)
{
- unsigned int irq;
- int retval;
+ int irq, retval;
irq = bind_evtchn_to_irq(evtchn);
if (irq < 0)
irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
{
- unsigned int irq;
- int retval;
+ int irq, retval;
irq = bind_virq_to_irq(virq, cpu);
if (irq < 0)
#include <linux/sysrq.h>
#include <linux/stop_machine.h>
#include <linux/freezer.h>
+#include <linux/syscore_ops.h>
#include <xen/xen.h>
#include <xen/xenbus.h>
xen_mm_unpin_all();
}
-#ifdef CONFIG_HIBERNATION
+#ifdef CONFIG_HIBERNATE_CALLBACKS
static int xen_suspend(void *data)
{
struct suspend_info *si = data;
BUG_ON(!irqs_disabled());
err = sysdev_suspend(PMSG_FREEZE);
+ if (!err) {
+ err = syscore_suspend();
+ if (err)
+ sysdev_resume();
+ }
if (err) {
- printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
+ printk(KERN_ERR "xen_suspend: system core suspend failed: %d\n",
err);
return err;
}
xen_timer_resume();
}
+ syscore_resume();
sysdev_resume();
return 0;
#endif
shutting_down = SHUTDOWN_INVALID;
}
-#endif /* CONFIG_HIBERNATION */
+#endif /* CONFIG_HIBERNATE_CALLBACKS */
struct shutdown_handler {
const char *command;
{ "poweroff", do_poweroff },
{ "halt", do_poweroff },
{ "reboot", do_reboot },
-#ifdef CONFIG_HIBERNATION
+#ifdef CONFIG_HIBERNATE_CALLBACKS
{ "suspend", do_suspend },
#endif
{NULL, NULL},
struct p9_fid *v9fs_writeback_fid(struct dentry *dentry)
{
- int err, flags;
+ int err;
struct p9_fid *fid;
- struct v9fs_session_info *v9ses;
- v9ses = v9fs_dentry2v9ses(dentry);
fid = v9fs_fid_clone_with_uid(dentry, 0);
if (IS_ERR(fid))
goto error_out;
* dirty pages. We always request for the open fid in read-write
* mode so that a partial page write which result in page
* read can work.
- *
- * we don't have a tsyncfs operation for older version
- * of protocol. So make sure the write back fid is
- * opened in O_SYNC mode.
*/
- if (!v9fs_proto_dotl(v9ses))
- flags = O_RDWR | O_SYNC;
- else
- flags = O_RDWR;
-
- err = p9_client_open(fid, flags);
+ err = p9_client_open(fid, O_RDWR);
if (err < 0) {
p9_client_clunk(fid);
fid = ERR_PTR(err);
struct list_head slist; /* list of sessions registered with v9fs */
struct backing_dev_info bdi;
struct rw_semaphore rename_sem;
- struct p9_fid *root_fid; /* Used for file system sync */
};
/* cache_validity flags */
retval = v9fs_refresh_inode_dotl(fid, inode);
else
retval = v9fs_refresh_inode(fid, inode);
- if (retval <= 0)
+ if (retval == -ENOENT)
+ return 0;
+ if (retval < 0)
return retval;
}
out_valid:
fid = v9fs_fid_lookup(dentry);
if (IS_ERR(fid)) {
__putname(link);
- link = ERR_PTR(PTR_ERR(fid));
+ link = ERR_CAST(fid);
goto ndset;
}
retval = p9_client_readlink(fid, &target);
retval = PTR_ERR(inode);
goto release_sb;
}
+
root = d_alloc_root(inode);
if (!root) {
iput(inode);
p9stat_free(st);
kfree(st);
}
- v9fs_fid_add(root, fid);
retval = v9fs_get_acl(inode, fid);
if (retval)
goto release_sb;
- /*
- * Add the root fid to session info. This is used
- * for file system sync. We want a cloned fid here
- * so that we can do a sync_filesystem after a
- * shrink_dcache_for_umount
- */
- v9ses->root_fid = v9fs_fid_clone(root);
- if (IS_ERR(v9ses->root_fid)) {
- retval = PTR_ERR(v9ses->root_fid);
- goto release_sb;
- }
+ v9fs_fid_add(root, fid);
P9_DPRINTK(P9_DEBUG_VFS, " simple set mount, return 0\n");
return dget(sb->s_root);
v9fs_session_close(v9ses);
kfree(v9ses);
return ERR_PTR(retval);
+
release_sb:
/*
- * we will do the session_close and root dentry
- * release in the below call.
+ * we will do the session_close and root dentry release
+ * in the below call. But we need to clunk fid, because we haven't
+ * attached the fid to dentry so it won't get clunked
+ * automatically.
*/
+ p9_client_clunk(fid);
deactivate_locked_super(sb);
return ERR_PTR(retval);
}
P9_DPRINTK(P9_DEBUG_VFS, " %p\n", s);
kill_anon_super(s);
- p9_client_clunk(v9ses->root_fid);
+
v9fs_session_cancel(v9ses);
v9fs_session_close(v9ses);
kfree(v9ses);
return res;
}
-static int v9fs_sync_fs(struct super_block *sb, int wait)
-{
- struct v9fs_session_info *v9ses = sb->s_fs_info;
-
- P9_DPRINTK(P9_DEBUG_VFS, "v9fs_sync_fs: super_block %p\n", sb);
- return p9_client_sync_fs(v9ses->root_fid);
-}
-
static int v9fs_drop_inode(struct inode *inode)
{
struct v9fs_session_info *v9ses;
return 1;
}
+static int v9fs_write_inode(struct inode *inode,
+ struct writeback_control *wbc)
+{
+ int ret;
+ struct p9_wstat wstat;
+ struct v9fs_inode *v9inode;
+ /*
+ * send an fsync request to server irrespective of
+ * wbc->sync_mode.
+ */
+ P9_DPRINTK(P9_DEBUG_VFS, "%s: inode %p\n", __func__, inode);
+ v9inode = V9FS_I(inode);
+ if (!v9inode->writeback_fid)
+ return 0;
+ v9fs_blank_wstat(&wstat);
+
+ ret = p9_client_wstat(v9inode->writeback_fid, &wstat);
+ if (ret < 0) {
+ __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
+ return ret;
+ }
+ return 0;
+}
+
+static int v9fs_write_inode_dotl(struct inode *inode,
+ struct writeback_control *wbc)
+{
+ int ret;
+ struct v9fs_inode *v9inode;
+ /*
+ * send an fsync request to server irrespective of
+ * wbc->sync_mode.
+ */
+ P9_DPRINTK(P9_DEBUG_VFS, "%s: inode %p\n", __func__, inode);
+ v9inode = V9FS_I(inode);
+ if (!v9inode->writeback_fid)
+ return 0;
+ ret = p9_client_fsync(v9inode->writeback_fid, 0);
+ if (ret < 0) {
+ __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
+ return ret;
+ }
+ return 0;
+}
+
static const struct super_operations v9fs_super_ops = {
.alloc_inode = v9fs_alloc_inode,
.destroy_inode = v9fs_destroy_inode,
.evict_inode = v9fs_evict_inode,
.show_options = generic_show_options,
.umount_begin = v9fs_umount_begin,
+ .write_inode = v9fs_write_inode,
};
static const struct super_operations v9fs_super_ops_dotl = {
.alloc_inode = v9fs_alloc_inode,
.destroy_inode = v9fs_destroy_inode,
- .sync_fs = v9fs_sync_fs,
.statfs = v9fs_statfs,
.drop_inode = v9fs_drop_inode,
.evict_inode = v9fs_evict_inode,
.show_options = generic_show_options,
.umount_begin = v9fs_umount_begin,
+ .write_inode = v9fs_write_inode_dotl,
};
struct file_system_type v9fs_fs_type = {
current->mm->start_stack = bprm->p;
#ifdef arch_randomize_brk
- if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1))
+ if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1)) {
current->mm->brk = current->mm->start_brk =
arch_randomize_brk(current->mm);
+#ifdef CONFIG_COMPAT_BRK
+ current->brk_randomized = 1;
+#endif
+ }
#endif
if (current->personality & MMAP_PAGE_ZERO) {
if (value) {
acl = posix_acl_from_xattr(value, size);
- if (acl == NULL) {
- value = NULL;
- size = 0;
+ if (acl) {
+ ret = posix_acl_valid(acl);
+ if (ret)
+ goto out;
} else if (IS_ERR(acl)) {
return PTR_ERR(acl);
}
}
ret = btrfs_set_acl(NULL, dentry->d_inode, acl, type);
-
+out:
posix_acl_release(acl);
return ret;
u64 total_bytes; /* total bytes in the space,
this doesn't take mirrors into account */
u64 bytes_used; /* total bytes used,
- this does't take mirrors into account */
+ this doesn't take mirrors into account */
u64 bytes_pinned; /* total bytes pinned, will be freed when the
transaction finishes */
u64 bytes_reserved; /* total bytes the allocator has reserved for
*/
unsigned long reservation_progress;
- int full; /* indicates that we cannot allocate any more
+ int full:1; /* indicates that we cannot allocate any more
chunks for this space */
+ int chunk_alloc:1; /* set if we are allocating a chunk */
+
int force_alloc; /* set if we need to force a chunk alloc for
this space */
int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
struct inode *inode, u64 start, u64 end);
int btrfs_release_file(struct inode *inode, struct file *file);
+void btrfs_drop_pages(struct page **pages, size_t num_pages);
+int btrfs_dirty_pages(struct btrfs_root *root, struct inode *inode,
+ struct page **pages, size_t num_pages,
+ loff_t pos, size_t write_bytes,
+ struct extent_state **cached);
/* tree-defrag.c */
int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
spin_lock(&delayed_refs->lock);
if (delayed_refs->num_entries == 0) {
+ spin_unlock(&delayed_refs->lock);
printk(KERN_INFO "delayed_refs has NO entry\n");
return ret;
}
btrfs_destroy_pinned_extent(root,
root->fs_info->pinned_extents);
- t->use_count = 0;
+ atomic_set(&t->use_count, 0);
list_del_init(&t->list);
memset(t, 0, sizeof(*t));
kmem_cache_free(btrfs_transaction_cachep, t);
#include "locking.h"
#include "free-space-cache.h"
+/* control flags for do_chunk_alloc's force field
+ * CHUNK_ALLOC_NO_FORCE means to only allocate a chunk
+ * if we really need one.
+ *
+ * CHUNK_ALLOC_FORCE means it must try to allocate one
+ *
+ * CHUNK_ALLOC_LIMITED means to only try and allocate one
+ * if we have very few chunks already allocated. This is
+ * used as part of the clustering code to help make sure
+ * we have a good pool of storage to cluster in, without
+ * filling the FS with empty chunks
+ *
+ */
+enum {
+ CHUNK_ALLOC_NO_FORCE = 0,
+ CHUNK_ALLOC_FORCE = 1,
+ CHUNK_ALLOC_LIMITED = 2,
+};
+
static int update_block_group(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 bytenr, u64 num_bytes, int alloc);
found->bytes_readonly = 0;
found->bytes_may_use = 0;
found->full = 0;
- found->force_alloc = 0;
+ found->force_alloc = CHUNK_ALLOC_NO_FORCE;
+ found->chunk_alloc = 0;
*space_info = found;
list_add_rcu(&found->list, &info->space_info);
atomic_set(&found->caching_threads, 0);
if (!data_sinfo->full && alloc_chunk) {
u64 alloc_target;
- data_sinfo->force_alloc = 1;
+ data_sinfo->force_alloc = CHUNK_ALLOC_FORCE;
spin_unlock(&data_sinfo->lock);
alloc:
alloc_target = btrfs_get_alloc_profile(root, 1);
ret = do_chunk_alloc(trans, root->fs_info->extent_root,
bytes + 2 * 1024 * 1024,
- alloc_target, 0);
+ alloc_target,
+ CHUNK_ALLOC_NO_FORCE);
btrfs_end_transaction(trans, root);
if (ret < 0) {
if (ret != -ENOSPC)
rcu_read_lock();
list_for_each_entry_rcu(found, head, list) {
if (found->flags & BTRFS_BLOCK_GROUP_METADATA)
- found->force_alloc = 1;
+ found->force_alloc = CHUNK_ALLOC_FORCE;
}
rcu_read_unlock();
}
static int should_alloc_chunk(struct btrfs_root *root,
- struct btrfs_space_info *sinfo, u64 alloc_bytes)
+ struct btrfs_space_info *sinfo, u64 alloc_bytes,
+ int force)
{
u64 num_bytes = sinfo->total_bytes - sinfo->bytes_readonly;
+ u64 num_allocated = sinfo->bytes_used + sinfo->bytes_reserved;
u64 thresh;
- if (sinfo->bytes_used + sinfo->bytes_reserved +
- alloc_bytes + 256 * 1024 * 1024 < num_bytes)
+ if (force == CHUNK_ALLOC_FORCE)
+ return 1;
+
+ /*
+ * in limited mode, we want to have some free space up to
+ * about 1% of the FS size.
+ */
+ if (force == CHUNK_ALLOC_LIMITED) {
+ thresh = btrfs_super_total_bytes(&root->fs_info->super_copy);
+ thresh = max_t(u64, 64 * 1024 * 1024,
+ div_factor_fine(thresh, 1));
+
+ if (num_bytes - num_allocated < thresh)
+ return 1;
+ }
+
+ /*
+ * we have two similar checks here, one based on percentage
+ * and once based on a hard number of 256MB. The idea
+ * is that if we have a good amount of free
+ * room, don't allocate a chunk. A good mount is
+ * less than 80% utilized of the chunks we have allocated,
+ * or more than 256MB free
+ */
+ if (num_allocated + alloc_bytes + 256 * 1024 * 1024 < num_bytes)
return 0;
- if (sinfo->bytes_used + sinfo->bytes_reserved +
- alloc_bytes < div_factor(num_bytes, 8))
+ if (num_allocated + alloc_bytes < div_factor(num_bytes, 8))
return 0;
thresh = btrfs_super_total_bytes(&root->fs_info->super_copy);
+
+ /* 256MB or 5% of the FS */
thresh = max_t(u64, 256 * 1024 * 1024, div_factor_fine(thresh, 5));
if (num_bytes > thresh && sinfo->bytes_used < div_factor(num_bytes, 3))
return 0;
-
return 1;
}
{
struct btrfs_space_info *space_info;
struct btrfs_fs_info *fs_info = extent_root->fs_info;
+ int wait_for_alloc = 0;
int ret = 0;
- mutex_lock(&fs_info->chunk_mutex);
-
flags = btrfs_reduce_alloc_profile(extent_root, flags);
space_info = __find_space_info(extent_root->fs_info, flags);
}
BUG_ON(!space_info);
+again:
spin_lock(&space_info->lock);
if (space_info->force_alloc)
- force = 1;
+ force = space_info->force_alloc;
if (space_info->full) {
spin_unlock(&space_info->lock);
- goto out;
+ return 0;
}
- if (!force && !should_alloc_chunk(extent_root, space_info,
- alloc_bytes)) {
+ if (!should_alloc_chunk(extent_root, space_info, alloc_bytes, force)) {
spin_unlock(&space_info->lock);
- goto out;
+ return 0;
+ } else if (space_info->chunk_alloc) {
+ wait_for_alloc = 1;
+ } else {
+ space_info->chunk_alloc = 1;
}
+
spin_unlock(&space_info->lock);
+ mutex_lock(&fs_info->chunk_mutex);
+
+ /*
+ * The chunk_mutex is held throughout the entirety of a chunk
+ * allocation, so once we've acquired the chunk_mutex we know that the
+ * other guy is done and we need to recheck and see if we should
+ * allocate.
+ */
+ if (wait_for_alloc) {
+ mutex_unlock(&fs_info->chunk_mutex);
+ wait_for_alloc = 0;
+ goto again;
+ }
+
/*
* If we have mixed data/metadata chunks we want to make sure we keep
* allocating mixed chunks instead of individual chunks.
space_info->full = 1;
else
ret = 1;
- space_info->force_alloc = 0;
+
+ space_info->force_alloc = CHUNK_ALLOC_NO_FORCE;
+ space_info->chunk_alloc = 0;
spin_unlock(&space_info->lock);
-out:
mutex_unlock(&extent_root->fs_info->chunk_mutex);
return ret;
}
if (allowed_chunk_alloc) {
ret = do_chunk_alloc(trans, root, num_bytes +
- 2 * 1024 * 1024, data, 1);
+ 2 * 1024 * 1024, data,
+ CHUNK_ALLOC_LIMITED);
allowed_chunk_alloc = 0;
done_chunk_alloc = 1;
- } else if (!done_chunk_alloc) {
- space_info->force_alloc = 1;
+ } else if (!done_chunk_alloc &&
+ space_info->force_alloc == CHUNK_ALLOC_NO_FORCE) {
+ space_info->force_alloc = CHUNK_ALLOC_LIMITED;
}
if (loop < LOOP_NO_EMPTY_SIZE) {
*/
if (empty_size || root->ref_cows)
ret = do_chunk_alloc(trans, root->fs_info->extent_root,
- num_bytes + 2 * 1024 * 1024, data, 0);
+ num_bytes + 2 * 1024 * 1024, data,
+ CHUNK_ALLOC_NO_FORCE);
WARN_ON(num_bytes < root->sectorsize);
ret = find_free_extent(trans, root, num_bytes, empty_size,
num_bytes = num_bytes & ~(root->sectorsize - 1);
num_bytes = max(num_bytes, min_alloc_size);
do_chunk_alloc(trans, root->fs_info->extent_root,
- num_bytes, data, 1);
+ num_bytes, data, CHUNK_ALLOC_FORCE);
goto again;
}
if (ret == -ENOSPC && btrfs_test_opt(root, ENOSPC_DEBUG)) {
u64 group_start = group->key.objectid;
new_extents = kmalloc(sizeof(*new_extents),
GFP_NOFS);
+ if (!new_extents) {
+ ret = -ENOMEM;
+ goto out;
+ }
nr_extents = 1;
ret = get_new_locations(reloc_inode,
extent_key,
alloc_flags = update_block_group_flags(root, cache->flags);
if (alloc_flags != cache->flags)
- do_chunk_alloc(trans, root, 2 * 1024 * 1024, alloc_flags, 1);
+ do_chunk_alloc(trans, root, 2 * 1024 * 1024, alloc_flags,
+ CHUNK_ALLOC_FORCE);
ret = set_block_group_ro(cache);
if (!ret)
goto out;
alloc_flags = get_alloc_profile(root, cache->space_info->flags);
- ret = do_chunk_alloc(trans, root, 2 * 1024 * 1024, alloc_flags, 1);
+ ret = do_chunk_alloc(trans, root, 2 * 1024 * 1024, alloc_flags,
+ CHUNK_ALLOC_FORCE);
if (ret < 0)
goto out;
ret = set_block_group_ro(cache);
struct btrfs_root *root, u64 type)
{
u64 alloc_flags = get_alloc_profile(root, type);
- return do_chunk_alloc(trans, root, 2 * 1024 * 1024, alloc_flags, 1);
+ return do_chunk_alloc(trans, root, 2 * 1024 * 1024, alloc_flags,
+ CHUNK_ALLOC_FORCE);
}
/*
}
}
+static void uncache_state(struct extent_state **cached_ptr)
+{
+ if (cached_ptr && (*cached_ptr)) {
+ struct extent_state *state = *cached_ptr;
+ *cached_ptr = NULL;
+ free_extent_state(state);
+ }
+}
+
/*
* set some bits on a range in the tree. This may require allocations or
* sleeping, so the gfp mask is used to indicate what is allowed.
}
int set_extent_uptodate(struct extent_io_tree *tree, u64 start, u64 end,
- gfp_t mask)
+ struct extent_state **cached_state, gfp_t mask)
{
- return set_extent_bit(tree, start, end, EXTENT_UPTODATE, 0, NULL,
- NULL, mask);
+ return set_extent_bit(tree, start, end, EXTENT_UPTODATE, 0,
+ NULL, cached_state, mask);
}
static int clear_extent_uptodate(struct extent_io_tree *tree, u64 start,
mask);
}
-int unlock_extent(struct extent_io_tree *tree, u64 start, u64 end,
- gfp_t mask)
+int unlock_extent(struct extent_io_tree *tree, u64 start, u64 end, gfp_t mask)
{
return clear_extent_bit(tree, start, end, EXTENT_LOCKED, 1, 0, NULL,
mask);
do {
struct page *page = bvec->bv_page;
+ struct extent_state *cached = NULL;
+ struct extent_state *state;
+
tree = &BTRFS_I(page->mapping->host)->io_tree;
start = ((u64)page->index << PAGE_CACHE_SHIFT) +
if (++bvec <= bvec_end)
prefetchw(&bvec->bv_page->flags);
+ spin_lock(&tree->lock);
+ state = find_first_extent_bit_state(tree, start, EXTENT_LOCKED);
+ if (state && state->start == start) {
+ /*
+ * take a reference on the state, unlock will drop
+ * the ref
+ */
+ cache_state(state, &cached);
+ }
+ spin_unlock(&tree->lock);
+
if (uptodate && tree->ops && tree->ops->readpage_end_io_hook) {
ret = tree->ops->readpage_end_io_hook(page, start, end,
- NULL);
+ state);
if (ret)
uptodate = 0;
}
test_bit(BIO_UPTODATE, &bio->bi_flags);
if (err)
uptodate = 0;
+ uncache_state(&cached);
continue;
}
}
if (uptodate) {
- set_extent_uptodate(tree, start, end,
+ set_extent_uptodate(tree, start, end, &cached,
GFP_ATOMIC);
}
- unlock_extent(tree, start, end, GFP_ATOMIC);
+ unlock_extent_cached(tree, start, end, &cached, GFP_ATOMIC);
if (whole_page) {
if (uptodate) {
do {
struct page *page = bvec->bv_page;
+ struct extent_state *cached = NULL;
tree = &BTRFS_I(page->mapping->host)->io_tree;
start = ((u64)page->index << PAGE_CACHE_SHIFT) +
prefetchw(&bvec->bv_page->flags);
if (uptodate) {
- set_extent_uptodate(tree, start, end, GFP_ATOMIC);
+ set_extent_uptodate(tree, start, end, &cached,
+ GFP_ATOMIC);
} else {
ClearPageUptodate(page);
SetPageError(page);
}
- unlock_extent(tree, start, end, GFP_ATOMIC);
+ unlock_extent_cached(tree, start, end, &cached, GFP_ATOMIC);
} while (bvec >= bio->bi_io_vec);
while (cur <= end) {
if (cur >= last_byte) {
char *userpage;
+ struct extent_state *cached = NULL;
+
iosize = PAGE_CACHE_SIZE - page_offset;
userpage = kmap_atomic(page, KM_USER0);
memset(userpage + page_offset, 0, iosize);
flush_dcache_page(page);
kunmap_atomic(userpage, KM_USER0);
set_extent_uptodate(tree, cur, cur + iosize - 1,
- GFP_NOFS);
- unlock_extent(tree, cur, cur + iosize - 1, GFP_NOFS);
+ &cached, GFP_NOFS);
+ unlock_extent_cached(tree, cur, cur + iosize - 1,
+ &cached, GFP_NOFS);
break;
}
em = get_extent(inode, page, page_offset, cur,
/* we've found a hole, just zero and go on */
if (block_start == EXTENT_MAP_HOLE) {
char *userpage;
+ struct extent_state *cached = NULL;
+
userpage = kmap_atomic(page, KM_USER0);
memset(userpage + page_offset, 0, iosize);
flush_dcache_page(page);
kunmap_atomic(userpage, KM_USER0);
set_extent_uptodate(tree, cur, cur + iosize - 1,
- GFP_NOFS);
- unlock_extent(tree, cur, cur + iosize - 1, GFP_NOFS);
+ &cached, GFP_NOFS);
+ unlock_extent_cached(tree, cur, cur + iosize - 1,
+ &cached, GFP_NOFS);
cur = cur + iosize;
page_offset += iosize;
continue;
prefetchw(&page->flags);
list_del(&page->lru);
if (!add_to_page_cache_lru(page, mapping,
- page->index, GFP_KERNEL)) {
+ page->index, GFP_NOFS)) {
__extent_read_full_page(tree, page, get_extent,
&bio, 0, &bio_flags);
}
iocount++;
block_start = block_start + iosize;
} else {
- set_extent_uptodate(tree, block_start, cur_end,
+ struct extent_state *cached = NULL;
+
+ set_extent_uptodate(tree, block_start, cur_end, &cached,
GFP_NOFS);
- unlock_extent(tree, block_start, cur_end, GFP_NOFS);
+ unlock_extent_cached(tree, block_start, cur_end,
+ &cached, GFP_NOFS);
block_start = cur_end + 1;
}
page_offset = block_start & (PAGE_CACHE_SIZE - 1);
num_pages = num_extent_pages(eb->start, eb->len);
set_extent_uptodate(tree, eb->start, eb->start + eb->len - 1,
- GFP_NOFS);
+ NULL, GFP_NOFS);
for (i = 0; i < num_pages; i++) {
page = extent_buffer_page(eb, i);
if ((i == 0 && (eb->start & (PAGE_CACHE_SIZE - 1))) ||
kunmap_atomic(dst_kaddr, KM_USER0);
}
+static inline bool areas_overlap(unsigned long src, unsigned long dst, unsigned long len)
+{
+ unsigned long distance = (src > dst) ? src - dst : dst - src;
+ return distance < len;
+}
+
static void copy_pages(struct page *dst_page, struct page *src_page,
unsigned long dst_off, unsigned long src_off,
unsigned long len)
char *dst_kaddr = kmap_atomic(dst_page, KM_USER0);
char *src_kaddr;
- if (dst_page != src_page)
+ if (dst_page != src_page) {
src_kaddr = kmap_atomic(src_page, KM_USER1);
- else
+ } else {
src_kaddr = dst_kaddr;
+ BUG_ON(areas_overlap(src_off, dst_off, len));
+ }
memcpy(dst_kaddr + dst_off, src_kaddr + src_off, len);
kunmap_atomic(dst_kaddr, KM_USER0);
"len %lu len %lu\n", dst_offset, len, dst->len);
BUG_ON(1);
}
- if (dst_offset < src_offset) {
+ if (!areas_overlap(src_offset, dst_offset, len)) {
memcpy_extent_buffer(dst, dst_offset, src_offset, len);
return;
}
int bits, int exclusive_bits, u64 *failed_start,
struct extent_state **cached_state, gfp_t mask);
int set_extent_uptodate(struct extent_io_tree *tree, u64 start, u64 end,
- gfp_t mask);
+ struct extent_state **cached_state, gfp_t mask);
int set_extent_new(struct extent_io_tree *tree, u64 start, u64 end,
gfp_t mask);
int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
/*
* unlocks pages after btrfs_file_write is done with them
*/
-static noinline void btrfs_drop_pages(struct page **pages, size_t num_pages)
+void btrfs_drop_pages(struct page **pages, size_t num_pages)
{
size_t i;
for (i = 0; i < num_pages; i++) {
* this also makes the decision about creating an inline extent vs
* doing real data extents, marking pages dirty and delalloc as required.
*/
-static noinline int dirty_and_release_pages(struct btrfs_root *root,
- struct file *file,
- struct page **pages,
- size_t num_pages,
- loff_t pos,
- size_t write_bytes)
+int btrfs_dirty_pages(struct btrfs_root *root, struct inode *inode,
+ struct page **pages, size_t num_pages,
+ loff_t pos, size_t write_bytes,
+ struct extent_state **cached)
{
int err = 0;
int i;
- struct inode *inode = fdentry(file)->d_inode;
u64 num_bytes;
u64 start_pos;
u64 end_of_last_block;
end_of_last_block = start_pos + num_bytes - 1;
err = btrfs_set_extent_delalloc(inode, start_pos, end_of_last_block,
- NULL);
+ cached);
if (err)
return err;
}
if (copied > 0) {
- ret = dirty_and_release_pages(root, file, pages,
- dirty_pages, pos,
- copied);
+ ret = btrfs_dirty_pages(root, inode, pages,
+ dirty_pages, pos, copied,
+ NULL);
if (ret) {
btrfs_delalloc_release_space(inode,
dirty_pages << PAGE_CACHE_SHIFT);
struct inode *inode;
struct rb_node *node;
struct list_head *pos, *n;
+ struct page **pages;
struct page *page;
struct extent_state *cached_state = NULL;
struct btrfs_free_cluster *cluster = NULL;
u64 start, end, len;
u64 bytes = 0;
u32 *crc, *checksums;
- pgoff_t index = 0, last_index = 0;
unsigned long first_page_offset;
- int num_checksums;
+ int index = 0, num_pages = 0;
int entries = 0;
int bitmaps = 0;
int ret = 0;
bool next_page = false;
+ bool out_of_space = false;
root = root->fs_info->tree_root;
return 0;
}
- last_index = (i_size_read(inode) - 1) >> PAGE_CACHE_SHIFT;
+ num_pages = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
+ PAGE_CACHE_SHIFT;
filemap_write_and_wait(inode->i_mapping);
btrfs_wait_ordered_range(inode, inode->i_size &
~(root->sectorsize - 1), (u64)-1);
/* We need a checksum per page. */
- num_checksums = i_size_read(inode) / PAGE_CACHE_SIZE;
- crc = checksums = kzalloc(sizeof(u32) * num_checksums, GFP_NOFS);
+ crc = checksums = kzalloc(sizeof(u32) * num_pages, GFP_NOFS);
if (!crc) {
iput(inode);
return 0;
}
+ pages = kzalloc(sizeof(struct page *) * num_pages, GFP_NOFS);
+ if (!pages) {
+ kfree(crc);
+ iput(inode);
+ return 0;
+ }
+
/* Since the first page has all of our checksums and our generation we
* need to calculate the offset into the page that we can start writing
* our entries.
*/
- first_page_offset = (sizeof(u32) * num_checksums) + sizeof(u64);
+ first_page_offset = (sizeof(u32) * num_pages) + sizeof(u64);
/* Get the cluster for this block_group if it exists */
if (!list_empty(&block_group->cluster_list))
* after find_get_page at this point. Just putting this here so people
* know and don't freak out.
*/
- while (index <= last_index) {
+ while (index < num_pages) {
page = grab_cache_page(inode->i_mapping, index);
if (!page) {
- pgoff_t i = 0;
+ int i;
- while (i < index) {
- page = find_get_page(inode->i_mapping, i);
- unlock_page(page);
- page_cache_release(page);
- page_cache_release(page);
- i++;
+ for (i = 0; i < num_pages; i++) {
+ unlock_page(pages[i]);
+ page_cache_release(pages[i]);
}
goto out_free;
}
+ pages[index] = page;
index++;
}
offset = start_offset;
}
- page = find_get_page(inode->i_mapping, index);
+ if (index >= num_pages) {
+ out_of_space = true;
+ break;
+ }
+
+ page = pages[index];
addr = kmap(page);
entry = addr + start_offset;
bytes += PAGE_CACHE_SIZE;
- ClearPageChecked(page);
- set_page_extent_mapped(page);
- SetPageUptodate(page);
- set_page_dirty(page);
-
- /*
- * We need to release our reference we got for grab_cache_page,
- * except for the first page which will hold our checksums, we
- * do that below.
- */
- if (index != 0) {
- unlock_page(page);
- page_cache_release(page);
- }
-
- page_cache_release(page);
-
index++;
} while (node || next_page);
struct btrfs_free_space *entry =
list_entry(pos, struct btrfs_free_space, list);
- page = find_get_page(inode->i_mapping, index);
+ if (index >= num_pages) {
+ out_of_space = true;
+ break;
+ }
+ page = pages[index];
addr = kmap(page);
memcpy(addr, entry->bitmap, PAGE_CACHE_SIZE);
crc++;
bytes += PAGE_CACHE_SIZE;
- ClearPageChecked(page);
- set_page_extent_mapped(page);
- SetPageUptodate(page);
- set_page_dirty(page);
- unlock_page(page);
- page_cache_release(page);
- page_cache_release(page);
list_del_init(&entry->list);
index++;
}
+ if (out_of_space) {
+ btrfs_drop_pages(pages, num_pages);
+ unlock_extent_cached(&BTRFS_I(inode)->io_tree, 0,
+ i_size_read(inode) - 1, &cached_state,
+ GFP_NOFS);
+ ret = 0;
+ goto out_free;
+ }
+
/* Zero out the rest of the pages just to make sure */
- while (index <= last_index) {
+ while (index < num_pages) {
void *addr;
- page = find_get_page(inode->i_mapping, index);
-
+ page = pages[index];
addr = kmap(page);
memset(addr, 0, PAGE_CACHE_SIZE);
kunmap(page);
- ClearPageChecked(page);
- set_page_extent_mapped(page);
- SetPageUptodate(page);
- set_page_dirty(page);
- unlock_page(page);
- page_cache_release(page);
- page_cache_release(page);
bytes += PAGE_CACHE_SIZE;
index++;
}
- btrfs_set_extent_delalloc(inode, 0, bytes - 1, &cached_state);
-
/* Write the checksums and trans id to the first page */
{
void *addr;
u64 *gen;
- page = find_get_page(inode->i_mapping, 0);
+ page = pages[0];
addr = kmap(page);
- memcpy(addr, checksums, sizeof(u32) * num_checksums);
- gen = addr + (sizeof(u32) * num_checksums);
+ memcpy(addr, checksums, sizeof(u32) * num_pages);
+ gen = addr + (sizeof(u32) * num_pages);
*gen = trans->transid;
kunmap(page);
- ClearPageChecked(page);
- set_page_extent_mapped(page);
- SetPageUptodate(page);
- set_page_dirty(page);
- unlock_page(page);
- page_cache_release(page);
- page_cache_release(page);
}
- BTRFS_I(inode)->generation = trans->transid;
+ ret = btrfs_dirty_pages(root, inode, pages, num_pages, 0,
+ bytes, &cached_state);
+ btrfs_drop_pages(pages, num_pages);
unlock_extent_cached(&BTRFS_I(inode)->io_tree, 0,
i_size_read(inode) - 1, &cached_state, GFP_NOFS);
+ if (ret) {
+ ret = 0;
+ goto out_free;
+ }
+
+ BTRFS_I(inode)->generation = trans->transid;
+
filemap_write_and_wait(inode->i_mapping);
key.objectid = BTRFS_FREE_SPACE_OBJECTID;
BTRFS_I(inode)->generation = 0;
}
kfree(checksums);
+ kfree(pages);
btrfs_update_inode(trans, root, inode);
iput(inode);
return ret;
while ((node = rb_last(&block_group->free_space_offset)) != NULL) {
info = rb_entry(node, struct btrfs_free_space, offset_index);
- unlink_free_space(block_group, info);
- if (info->bitmap)
- kfree(info->bitmap);
- kmem_cache_free(btrfs_free_space_cachep, info);
+ if (!info->bitmap) {
+ unlink_free_space(block_group, info);
+ kmem_cache_free(btrfs_free_space_cachep, info);
+ } else {
+ free_bitmap(block_group, info);
+ }
+
if (need_resched()) {
spin_unlock(&block_group->tree_lock);
cond_resched();
start = entry->offset;
bytes = min(entry->bytes, end - start);
unlink_free_space(block_group, entry);
- kfree(entry);
+ kmem_cache_free(btrfs_free_space_cachep, entry);
}
spin_unlock(&block_group->tree_lock);
1, 0, NULL, GFP_NOFS);
while (start < end) {
async_cow = kmalloc(sizeof(*async_cow), GFP_NOFS);
+ BUG_ON(!async_cow);
async_cow->inode = inode;
async_cow->root = root;
async_cow->locked_page = locked_page;
add_pending_csums(trans, inode, ordered_extent->file_offset,
&ordered_extent->list);
- btrfs_ordered_update_i_size(inode, 0, ordered_extent);
- ret = btrfs_update_inode(trans, root, inode);
- BUG_ON(ret);
+ ret = btrfs_ordered_update_i_size(inode, 0, ordered_extent);
+ if (!ret) {
+ ret = btrfs_update_inode(trans, root, inode);
+ BUG_ON(ret);
+ }
+ ret = 0;
out:
if (nolock) {
if (trans)
struct btrfs_inode_item *item,
struct inode *inode)
{
+ if (!leaf->map_token)
+ map_private_extent_buffer(leaf, (unsigned long)item,
+ sizeof(struct btrfs_inode_item),
+ &leaf->map_token, &leaf->kaddr,
+ &leaf->map_start, &leaf->map_len,
+ KM_USER1);
+
btrfs_set_inode_uid(leaf, item, inode->i_uid);
btrfs_set_inode_gid(leaf, item, inode->i_gid);
btrfs_set_inode_size(leaf, item, BTRFS_I(inode)->disk_i_size);
btrfs_set_inode_rdev(leaf, item, inode->i_rdev);
btrfs_set_inode_flags(leaf, item, BTRFS_I(inode)->flags);
btrfs_set_inode_block_group(leaf, item, BTRFS_I(inode)->block_group);
+
+ if (leaf->map_token) {
+ unmap_extent_buffer(leaf, leaf->map_token, KM_USER1);
+ leaf->map_token = NULL;
+ }
}
/*
struct btrfs_key found_key;
struct btrfs_path *path;
int ret;
- u32 nritems;
struct extent_buffer *leaf;
int slot;
- int advance;
unsigned char d_type;
int over = 0;
u32 di_cur;
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
if (ret < 0)
goto err;
- advance = 0;
while (1) {
leaf = path->nodes[0];
- nritems = btrfs_header_nritems(leaf);
slot = path->slots[0];
- if (advance || slot >= nritems) {
- if (slot >= nritems - 1) {
- ret = btrfs_next_leaf(root, path);
- if (ret)
- break;
- leaf = path->nodes[0];
- nritems = btrfs_header_nritems(leaf);
- slot = path->slots[0];
- } else {
- slot++;
- path->slots[0]++;
- }
+ if (slot >= btrfs_header_nritems(leaf)) {
+ ret = btrfs_next_leaf(root, path);
+ if (ret < 0)
+ goto err;
+ else if (ret > 0)
+ break;
+ continue;
}
- advance = 1;
item = btrfs_item_nr(leaf, slot);
btrfs_item_key_to_cpu(leaf, &found_key, slot);
if (btrfs_key_type(&found_key) != key_type)
break;
if (found_key.offset < filp->f_pos)
- continue;
+ goto next;
filp->f_pos = found_key.offset;
di_cur += di_len;
di = (struct btrfs_dir_item *)((char *)di + di_len);
}
+next:
+ path->slots[0]++;
}
/* Reached end of directory/root. Bump pos past the last item. */
BUG_ON(!path);
inode = new_inode(root->fs_info->sb);
- if (!inode)
+ if (!inode) {
+ btrfs_free_path(path);
return ERR_PTR(-ENOMEM);
+ }
if (dir) {
trace_btrfs_inode_request(dir);
ret = btrfs_set_inode_index(dir, index);
if (ret) {
+ btrfs_free_path(path);
iput(inode);
return ERR_PTR(ret);
}
inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
dentry->d_name.len, dir->i_ino, objectid,
BTRFS_I(dir)->block_group, mode, &index);
- err = PTR_ERR(inode);
- if (IS_ERR(inode))
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
goto out_unlock;
+ }
err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);
if (err) {
inode = btrfs_new_inode(trans, root, dir, dentry->d_name.name,
dentry->d_name.len, dir->i_ino, objectid,
BTRFS_I(dir)->block_group, mode, &index);
- err = PTR_ERR(inode);
- if (IS_ERR(inode))
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
goto out_unlock;
+ }
err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);
if (err) {
if (inode->i_nlink == ~0U)
return -EMLINK;
- btrfs_inc_nlink(inode);
- inode->i_ctime = CURRENT_TIME;
-
err = btrfs_set_inode_index(dir, &index);
if (err)
goto fail;
goto fail;
}
+ btrfs_inc_nlink(inode);
+ inode->i_ctime = CURRENT_TIME;
+
btrfs_set_trans_block_group(trans, dir);
ihold(inode);
inline_size = btrfs_file_extent_inline_item_len(leaf,
btrfs_item_nr(leaf, path->slots[0]));
tmp = kmalloc(inline_size, GFP_NOFS);
+ if (!tmp)
+ return -ENOMEM;
ptr = btrfs_file_extent_inline_start(item);
read_extent_buffer(leaf, tmp, ptr, inline_size);
btrfs_mark_buffer_dirty(leaf);
}
set_extent_uptodate(io_tree, em->start,
- extent_map_end(em) - 1, GFP_NOFS);
+ extent_map_end(em) - 1, NULL, GFP_NOFS);
goto insert;
} else {
printk(KERN_ERR "btrfs unknown found_type %d\n", found_type);
}
static struct extent_map *btrfs_new_extent_direct(struct inode *inode,
+ struct extent_map *em,
u64 start, u64 len)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_trans_handle *trans;
- struct extent_map *em;
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
struct btrfs_key ins;
u64 alloc_hint;
int ret;
+ bool insert = false;
- btrfs_drop_extent_cache(inode, start, start + len - 1, 0);
+ /*
+ * Ok if the extent map we looked up is a hole and is for the exact
+ * range we want, there is no reason to allocate a new one, however if
+ * it is not right then we need to free this one and drop the cache for
+ * our range.
+ */
+ if (em->block_start != EXTENT_MAP_HOLE || em->start != start ||
+ em->len != len) {
+ free_extent_map(em);
+ em = NULL;
+ insert = true;
+ btrfs_drop_extent_cache(inode, start, start + len - 1, 0);
+ }
trans = btrfs_join_transaction(root, 0);
if (IS_ERR(trans))
goto out;
}
- em = alloc_extent_map(GFP_NOFS);
if (!em) {
- em = ERR_PTR(-ENOMEM);
- goto out;
+ em = alloc_extent_map(GFP_NOFS);
+ if (!em) {
+ em = ERR_PTR(-ENOMEM);
+ goto out;
+ }
}
em->start = start;
em->block_start = ins.objectid;
em->block_len = ins.offset;
em->bdev = root->fs_info->fs_devices->latest_bdev;
+
+ /*
+ * We need to do this because if we're using the original em we searched
+ * for, we could have EXTENT_FLAG_VACANCY set, and we don't want that.
+ */
+ em->flags = 0;
set_bit(EXTENT_FLAG_PINNED, &em->flags);
- while (1) {
+ while (insert) {
write_lock(&em_tree->lock);
ret = add_extent_mapping(em_tree, em);
write_unlock(&em_tree->lock);
* it above
*/
len = bh_result->b_size;
- free_extent_map(em);
- em = btrfs_new_extent_direct(inode, start, len);
+ em = btrfs_new_extent_direct(inode, em, start, len);
if (IS_ERR(em))
return PTR_ERR(em);
len = min(len, em->len - (start - em->start));
}
add_pending_csums(trans, inode, ordered->file_offset, &ordered->list);
- btrfs_ordered_update_i_size(inode, 0, ordered);
- btrfs_update_inode(trans, root, inode);
+ ret = btrfs_ordered_update_i_size(inode, 0, ordered);
+ if (!ret)
+ btrfs_update_inode(trans, root, inode);
+ ret = 0;
out_unlock:
unlock_extent_cached(&BTRFS_I(inode)->io_tree, ordered->file_offset,
ordered->file_offset + ordered->len - 1,
static inline int __btrfs_submit_dio_bio(struct bio *bio, struct inode *inode,
int rw, u64 file_offset, int skip_sum,
- u32 *csums)
+ u32 *csums, int async_submit)
{
int write = rw & REQ_WRITE;
struct btrfs_root *root = BTRFS_I(inode)->root;
if (ret)
goto err;
- if (write && !skip_sum) {
+ if (skip_sum)
+ goto map;
+
+ if (write && async_submit) {
ret = btrfs_wq_submit_bio(root->fs_info,
inode, rw, bio, 0, 0,
file_offset,
__btrfs_submit_bio_start_direct_io,
__btrfs_submit_bio_done);
goto err;
+ } else if (write) {
+ /*
+ * If we aren't doing async submit, calculate the csum of the
+ * bio now.
+ */
+ ret = btrfs_csum_one_bio(root, inode, bio, file_offset, 1);
+ if (ret)
+ goto err;
} else if (!skip_sum) {
ret = btrfs_lookup_bio_sums_dio(root, inode, bio,
file_offset, csums);
goto err;
}
- ret = btrfs_map_bio(root, rw, bio, 0, 1);
+map:
+ ret = btrfs_map_bio(root, rw, bio, 0, async_submit);
err:
bio_put(bio);
return ret;
int nr_pages = 0;
u32 *csums = dip->csums;
int ret = 0;
+ int async_submit = 0;
int write = rw & REQ_WRITE;
- bio = btrfs_dio_bio_alloc(orig_bio->bi_bdev, start_sector, GFP_NOFS);
- if (!bio)
- return -ENOMEM;
- bio->bi_private = dip;
- bio->bi_end_io = btrfs_end_dio_bio;
- atomic_inc(&dip->pending_bios);
-
map_length = orig_bio->bi_size;
ret = btrfs_map_block(map_tree, READ, start_sector << 9,
&map_length, NULL, 0);
if (ret) {
- bio_put(bio);
+ bio_put(orig_bio);
return -EIO;
}
+ if (map_length >= orig_bio->bi_size) {
+ bio = orig_bio;
+ goto submit;
+ }
+
+ async_submit = 1;
+ bio = btrfs_dio_bio_alloc(orig_bio->bi_bdev, start_sector, GFP_NOFS);
+ if (!bio)
+ return -ENOMEM;
+ bio->bi_private = dip;
+ bio->bi_end_io = btrfs_end_dio_bio;
+ atomic_inc(&dip->pending_bios);
+
while (bvec <= (orig_bio->bi_io_vec + orig_bio->bi_vcnt - 1)) {
if (unlikely(map_length < submit_len + bvec->bv_len ||
bio_add_page(bio, bvec->bv_page, bvec->bv_len,
atomic_inc(&dip->pending_bios);
ret = __btrfs_submit_dio_bio(bio, inode, rw,
file_offset, skip_sum,
- csums);
+ csums, async_submit);
if (ret) {
bio_put(bio);
atomic_dec(&dip->pending_bios);
}
}
+submit:
ret = __btrfs_submit_dio_bio(bio, inode, rw, file_offset, skip_sum,
- csums);
+ csums, async_submit);
if (!ret)
return 0;
unsigned long nr_segs)
{
int seg;
+ int i;
size_t size;
unsigned long addr;
unsigned blocksize_mask = root->sectorsize - 1;
addr = (unsigned long)iov[seg].iov_base;
size = iov[seg].iov_len;
end += size;
- if ((addr & blocksize_mask) || (size & blocksize_mask))
+ if ((addr & blocksize_mask) || (size & blocksize_mask))
goto out;
+
+ /* If this is a write we don't need to check anymore */
+ if (rw & WRITE)
+ continue;
+
+ /*
+ * Check to make sure we don't have duplicate iov_base's in this
+ * iovec, if so return EINVAL, otherwise we'll get csum errors
+ * when reading back.
+ */
+ for (i = seg + 1; i < nr_segs; i++) {
+ if (iov[seg].iov_base == iov[i].iov_base)
+ goto out;
+ }
}
retval = 0;
out:
dentry->d_name.len, dir->i_ino, objectid,
BTRFS_I(dir)->block_group, S_IFLNK|S_IRWXUGO,
&index);
- err = PTR_ERR(inode);
- if (IS_ERR(inode))
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
goto out_unlock;
+ }
err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);
if (err) {
struct btrfs_ioctl_space_info space;
struct btrfs_ioctl_space_info *dest;
struct btrfs_ioctl_space_info *dest_orig;
- struct btrfs_ioctl_space_info *user_dest;
+ struct btrfs_ioctl_space_info __user *user_dest;
struct btrfs_space_info *info;
u64 types[] = {BTRFS_BLOCK_GROUP_DATA,
BTRFS_BLOCK_GROUP_SYSTEM,
Opt_compress_type, Opt_compress_force, Opt_compress_force_type,
Opt_notreelog, Opt_ratio, Opt_flushoncommit, Opt_discard,
Opt_space_cache, Opt_clear_cache, Opt_user_subvol_rm_allowed,
- Opt_enospc_debug, Opt_err,
+ Opt_enospc_debug, Opt_subvolrootid, Opt_err,
};
static match_table_t tokens = {
{Opt_clear_cache, "clear_cache"},
{Opt_user_subvol_rm_allowed, "user_subvol_rm_allowed"},
{Opt_enospc_debug, "enospc_debug"},
+ {Opt_subvolrootid, "subvolrootid=%d"},
{Opt_err, NULL},
};
break;
case Opt_subvol:
case Opt_subvolid:
+ case Opt_subvolrootid:
case Opt_device:
/*
* These are parsed by btrfs_parse_early_options
*/
static int btrfs_parse_early_options(const char *options, fmode_t flags,
void *holder, char **subvol_name, u64 *subvol_objectid,
- struct btrfs_fs_devices **fs_devices)
+ u64 *subvol_rootid, struct btrfs_fs_devices **fs_devices)
{
substring_t args[MAX_OPT_ARGS];
char *opts, *orig, *p;
*subvol_objectid = intarg;
}
break;
+ case Opt_subvolrootid:
+ intarg = 0;
+ error = match_int(&args[0], &intarg);
+ if (!error) {
+ /* we want the original fs_tree */
+ if (!intarg)
+ *subvol_rootid =
+ BTRFS_FS_TREE_OBJECTID;
+ else
+ *subvol_rootid = intarg;
+ }
+ break;
case Opt_device:
error = btrfs_scan_one_device(match_strdup(&args[0]),
flags, holder, fs_devices);
fmode_t mode = FMODE_READ;
char *subvol_name = NULL;
u64 subvol_objectid = 0;
+ u64 subvol_rootid = 0;
int error = 0;
if (!(flags & MS_RDONLY))
error = btrfs_parse_early_options(data, mode, fs_type,
&subvol_name, &subvol_objectid,
- &fs_devices);
+ &subvol_rootid, &fs_devices);
if (error)
return ERR_PTR(error);
s->s_flags |= MS_ACTIVE;
}
- root = get_default_root(s, subvol_objectid);
- if (IS_ERR(root)) {
- error = PTR_ERR(root);
- deactivate_locked_super(s);
- goto error_free_subvol_name;
- }
/* if they gave us a subvolume name bind mount into that */
if (strcmp(subvol_name, ".")) {
struct dentry *new_root;
+
+ root = get_default_root(s, subvol_rootid);
+ if (IS_ERR(root)) {
+ error = PTR_ERR(root);
+ deactivate_locked_super(s);
+ goto error_free_subvol_name;
+ }
+
mutex_lock(&root->d_inode->i_mutex);
new_root = lookup_one_len(subvol_name, root,
strlen(subvol_name));
}
dput(root);
root = new_root;
+ } else {
+ root = get_default_root(s, subvol_objectid);
+ if (IS_ERR(root)) {
+ error = PTR_ERR(root);
+ deactivate_locked_super(s);
+ goto error_free_subvol_name;
+ }
}
kfree(subvol_name);
static noinline void put_transaction(struct btrfs_transaction *transaction)
{
- WARN_ON(transaction->use_count == 0);
- transaction->use_count--;
- if (transaction->use_count == 0) {
- list_del_init(&transaction->list);
+ WARN_ON(atomic_read(&transaction->use_count) == 0);
+ if (atomic_dec_and_test(&transaction->use_count)) {
memset(transaction, 0, sizeof(*transaction));
kmem_cache_free(btrfs_transaction_cachep, transaction);
}
if (!cur_trans)
return -ENOMEM;
root->fs_info->generation++;
- cur_trans->num_writers = 1;
+ atomic_set(&cur_trans->num_writers, 1);
cur_trans->num_joined = 0;
cur_trans->transid = root->fs_info->generation;
init_waitqueue_head(&cur_trans->writer_wait);
init_waitqueue_head(&cur_trans->commit_wait);
cur_trans->in_commit = 0;
cur_trans->blocked = 0;
- cur_trans->use_count = 1;
+ atomic_set(&cur_trans->use_count, 1);
cur_trans->commit_done = 0;
cur_trans->start_time = get_seconds();
root->fs_info->running_transaction = cur_trans;
spin_unlock(&root->fs_info->new_trans_lock);
} else {
- cur_trans->num_writers++;
+ atomic_inc(&cur_trans->num_writers);
cur_trans->num_joined++;
}
cur_trans = root->fs_info->running_transaction;
if (cur_trans && cur_trans->blocked) {
DEFINE_WAIT(wait);
- cur_trans->use_count++;
+ atomic_inc(&cur_trans->use_count);
while (1) {
prepare_to_wait(&root->fs_info->transaction_wait, &wait,
TASK_UNINTERRUPTIBLE);
{
struct btrfs_trans_handle *h;
struct btrfs_transaction *cur_trans;
+ int retries = 0;
int ret;
if (root->fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR)
}
cur_trans = root->fs_info->running_transaction;
- cur_trans->use_count++;
+ atomic_inc(&cur_trans->use_count);
if (type != TRANS_JOIN_NOLOCK)
mutex_unlock(&root->fs_info->trans_mutex);
if (num_items > 0) {
ret = btrfs_trans_reserve_metadata(h, root, num_items);
- if (ret == -EAGAIN) {
+ if (ret == -EAGAIN && !retries) {
+ retries++;
btrfs_commit_transaction(h, root);
goto again;
+ } else if (ret == -EAGAIN) {
+ /*
+ * We have already retried and got EAGAIN, so really we
+ * don't have space, so set ret to -ENOSPC.
+ */
+ ret = -ENOSPC;
}
+
if (ret < 0) {
btrfs_end_transaction(h, root);
return ERR_PTR(ret);
goto out_unlock; /* nothing committing|committed */
}
- cur_trans->use_count++;
+ atomic_inc(&cur_trans->use_count);
mutex_unlock(&root->fs_info->trans_mutex);
wait_for_commit(root, cur_trans);
wake_up_process(info->transaction_kthread);
}
- if (lock)
- mutex_lock(&info->trans_mutex);
WARN_ON(cur_trans != info->running_transaction);
- WARN_ON(cur_trans->num_writers < 1);
- cur_trans->num_writers--;
+ WARN_ON(atomic_read(&cur_trans->num_writers) < 1);
+ atomic_dec(&cur_trans->num_writers);
smp_mb();
if (waitqueue_active(&cur_trans->writer_wait))
wake_up(&cur_trans->writer_wait);
put_transaction(cur_trans);
- if (lock)
- mutex_unlock(&info->trans_mutex);
if (current->journal_info == trans)
current->journal_info = NULL;
/* take transaction reference */
mutex_lock(&root->fs_info->trans_mutex);
cur_trans = trans->transaction;
- cur_trans->use_count++;
+ atomic_inc(&cur_trans->use_count);
mutex_unlock(&root->fs_info->trans_mutex);
btrfs_end_transaction(trans, root);
mutex_lock(&root->fs_info->trans_mutex);
if (cur_trans->in_commit) {
- cur_trans->use_count++;
+ atomic_inc(&cur_trans->use_count);
mutex_unlock(&root->fs_info->trans_mutex);
btrfs_end_transaction(trans, root);
prev_trans = list_entry(cur_trans->list.prev,
struct btrfs_transaction, list);
if (!prev_trans->commit_done) {
- prev_trans->use_count++;
+ atomic_inc(&prev_trans->use_count);
mutex_unlock(&root->fs_info->trans_mutex);
wait_for_commit(root, prev_trans);
TASK_UNINTERRUPTIBLE);
smp_mb();
- if (cur_trans->num_writers > 1)
+ if (atomic_read(&cur_trans->num_writers) > 1)
schedule_timeout(MAX_SCHEDULE_TIMEOUT);
else if (should_grow)
schedule_timeout(1);
mutex_lock(&root->fs_info->trans_mutex);
finish_wait(&cur_trans->writer_wait, &wait);
- } while (cur_trans->num_writers > 1 ||
+ } while (atomic_read(&cur_trans->num_writers) > 1 ||
(should_grow && cur_trans->num_joined != joined));
ret = create_pending_snapshots(trans, root->fs_info);
wake_up(&cur_trans->commit_wait);
+ list_del_init(&cur_trans->list);
put_transaction(cur_trans);
put_transaction(cur_trans);
* total writers in this transaction, it must be zero before the
* transaction can end
*/
- unsigned long num_writers;
+ atomic_t num_writers;
unsigned long num_joined;
int in_commit;
- int use_count;
+ atomic_t use_count;
int commit_done;
int blocked;
struct list_head list;
log = root->log_root;
path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
+ if (!path) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
di = btrfs_lookup_dir_item(trans, log, path, dir->i_ino,
name, name_len, -1);
}
fail:
btrfs_free_path(path);
+out_unlock:
mutex_unlock(&BTRFS_I(dir)->log_mutex);
if (ret == -ENOSPC) {
root->fs_info->last_trans_log_full_commit = trans->transid;
unsigned long limit;
unsigned long last_waited = 0;
int force_reg = 0;
+ struct blk_plug plug;
+
+ /*
+ * this function runs all the bios we've collected for
+ * a particular device. We don't want to wander off to
+ * another device without first sending all of these down.
+ * So, setup a plug here and finish it off before we return
+ */
+ blk_start_plug(&plug);
bdi = blk_get_backing_dev_info(device->bdev);
fs_info = device->dev_root->fs_info;
spin_unlock(&device->io_lock);
done:
+ blk_finish_plug(&plug);
return 0;
}
struct btrfs_path *path;
struct extent_buffer *leaf;
struct btrfs_dir_item *di;
- int ret = 0, slot, advance;
+ int ret = 0, slot;
size_t total_size = 0, size_left = size;
unsigned long name_ptr;
size_t name_len;
- u32 nritems;
/*
* ok we want all objects associated with this id.
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
if (ret < 0)
goto err;
- advance = 0;
+
while (1) {
leaf = path->nodes[0];
- nritems = btrfs_header_nritems(leaf);
slot = path->slots[0];
/* this is where we start walking through the path */
- if (advance || slot >= nritems) {
+ if (slot >= btrfs_header_nritems(leaf)) {
/*
* if we've reached the last slot in this leaf we need
* to go to the next leaf and reset everything
*/
- if (slot >= nritems-1) {
- ret = btrfs_next_leaf(root, path);
- if (ret)
- break;
- leaf = path->nodes[0];
- nritems = btrfs_header_nritems(leaf);
- slot = path->slots[0];
- } else {
- /*
- * just walking through the slots on this leaf
- */
- slot++;
- path->slots[0]++;
- }
+ ret = btrfs_next_leaf(root, path);
+ if (ret < 0)
+ goto err;
+ else if (ret > 0)
+ break;
+ continue;
}
- advance = 1;
btrfs_item_key_to_cpu(leaf, &found_key, slot);
/* we are just looking for how big our buffer needs to be */
if (!size)
- continue;
+ goto next;
if (!buffer || (name_len + 1) > size_left) {
ret = -ERANGE;
size_left -= name_len + 1;
buffer += name_len + 1;
+next:
+ path->slots[0]++;
}
ret = total_size;
ci->i_truncate_seq,
ci->i_truncate_size,
&inode->i_mtime, true, 1, 0);
+
+ if (!req) {
+ rc = -ENOMEM;
+ unlock_page(page);
+ break;
+ }
+
max_pages = req->r_num_pages;
alloc_page_vec(fsc, req);
}
/*
- * Mark caps dirty. If inode is newly dirty, add to the global dirty
- * list.
+ * Mark caps dirty. If inode is newly dirty, return the dirty flags.
+ * Caller is then responsible for calling __mark_inode_dirty with the
+ * returned flags value.
*/
-void __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask)
+int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask)
{
struct ceph_mds_client *mdsc =
ceph_sb_to_client(ci->vfs_inode.i_sb)->mdsc;
list_add(&ci->i_dirty_item, &mdsc->cap_dirty);
spin_unlock(&mdsc->cap_dirty_lock);
if (ci->i_flushing_caps == 0) {
- igrab(inode);
+ ihold(inode);
dirty |= I_DIRTY_SYNC;
}
}
if (((was | ci->i_flushing_caps) & CEPH_CAP_FILE_BUFFER) &&
(mask & CEPH_CAP_FILE_BUFFER))
dirty |= I_DIRTY_DATASYNC;
- if (dirty)
- __mark_inode_dirty(inode, dirty);
__cap_delay_requeue(mdsc, ci);
+ return dirty;
}
/*
ci->i_wr_ref++;
if (got & CEPH_CAP_FILE_BUFFER) {
if (ci->i_wrbuffer_ref == 0)
- igrab(&ci->vfs_inode);
+ ihold(&ci->vfs_inode);
ci->i_wrbuffer_ref++;
dout("__take_cap_refs %p wrbuffer %d -> %d (?)\n",
&ci->vfs_inode, ci->i_wrbuffer_ref-1, ci->i_wrbuffer_ref);
}
}
if (ret >= 0) {
+ int dirty;
spin_lock(&inode->i_lock);
- __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR);
+ dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR);
spin_unlock(&inode->i_lock);
+ if (dirty)
+ __mark_inode_dirty(inode, dirty);
}
out:
int release = 0, dirtied = 0;
int mask = 0;
int err = 0;
+ int inode_dirty_flags = 0;
if (ceph_snap(inode) != CEPH_NOSNAP)
return -EROFS;
dout("setattr %p ATTR_FILE ... hrm!\n", inode);
if (dirtied) {
- __ceph_mark_dirty_caps(ci, dirtied);
+ inode_dirty_flags = __ceph_mark_dirty_caps(ci, dirtied);
inode->i_ctime = CURRENT_TIME;
}
release &= issued;
spin_unlock(&inode->i_lock);
+ if (inode_dirty_flags)
+ __mark_inode_dirty(inode, inode_dirty_flags);
+
if (mask) {
req->r_inode = igrab(inode);
req->r_inode_drop = release;
{
return ci->i_dirty_caps | ci->i_flushing_caps;
}
-extern void __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask);
+extern int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask);
extern int ceph_caps_revoking(struct ceph_inode_info *ci, int mask);
extern int __ceph_caps_used(struct ceph_inode_info *ci);
struct ceph_inode_xattr *xattr = NULL;
int issued;
int required_blob_size;
+ int dirty;
if (ceph_snap(inode) != CEPH_NOSNAP)
return -EROFS;
dout("setxattr %p issued %s\n", inode, ceph_cap_string(issued));
err = __set_xattr(ci, newname, name_len, newval,
val_len, 1, 1, 1, &xattr);
- __ceph_mark_dirty_caps(ci, CEPH_CAP_XATTR_EXCL);
+ dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_XATTR_EXCL);
ci->i_xattrs.dirty = true;
inode->i_ctime = CURRENT_TIME;
spin_unlock(&inode->i_lock);
-
+ if (dirty)
+ __mark_inode_dirty(inode, dirty);
return err;
do_sync:
struct ceph_vxattr_cb *vxattrs = ceph_inode_vxattrs(inode);
int issued;
int err;
+ int dirty;
if (ceph_snap(inode) != CEPH_NOSNAP)
return -EROFS;
goto do_sync;
err = __remove_xattr_by_name(ceph_inode(inode), name);
- __ceph_mark_dirty_caps(ci, CEPH_CAP_XATTR_EXCL);
+ dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_XATTR_EXCL);
ci->i_xattrs.dirty = true;
inode->i_ctime = CURRENT_TIME;
spin_unlock(&inode->i_lock);
-
+ if (dirty)
+ __mark_inode_dirty(inode, dirty);
return err;
do_sync:
spin_unlock(&inode->i_lock);
support and want to map the uid and gid fields
to values supplied at mount (rather than the
actual values, then set this to zero. (default 1)
-Experimental When set to 1 used to enable certain experimental
- features (currently enables multipage writes
- when signing is enabled, the multipage write
- performance enhancement was disabled when
- signing turned on in case buffer was modified
- just before it was sent, also this flag will
- be used to use the new experimental directory change
- notification code). When set to 2 enables
- an additional experimental feature, "raw ntlmssp"
- session establishment support (which allows
- specifying "sec=ntlmssp" on mount). The Linux cifs
- module will use ntlmv2 authentication encapsulated
- in "raw ntlmssp" (not using SPNEGO) when
- "sec=ntlmssp" is specified on mount.
- This support also requires building cifs with
- the CONFIG_CIFS_EXPERIMENTAL configuration flag.
These experimental features and tracing can be enabled by changing flags in
/proc/fs/cifs (after the cifs module has been installed or built into the
*/
struct cifs_server_key {
uint16_t family; /* address family */
- uint16_t port; /* IP port */
+ __be16 port; /* IP port */
union {
struct in_addr ipv4_addr;
struct in6_addr ipv6_addr;
static const struct file_operations traceSMB_proc_fops;
static const struct file_operations cifs_multiuser_mount_proc_fops;
static const struct file_operations cifs_security_flags_proc_fops;
-static const struct file_operations cifs_experimental_proc_fops;
static const struct file_operations cifs_linux_ext_proc_fops;
void
proc_create("cifsFYI", 0, proc_fs_cifs, &cifsFYI_proc_fops);
proc_create("traceSMB", 0, proc_fs_cifs, &traceSMB_proc_fops);
proc_create("OplockEnabled", 0, proc_fs_cifs, &cifs_oplock_proc_fops);
- proc_create("Experimental", 0, proc_fs_cifs,
- &cifs_experimental_proc_fops);
proc_create("LinuxExtensionsEnabled", 0, proc_fs_cifs,
&cifs_linux_ext_proc_fops);
proc_create("MultiuserMount", 0, proc_fs_cifs,
remove_proc_entry("OplockEnabled", proc_fs_cifs);
remove_proc_entry("SecurityFlags", proc_fs_cifs);
remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs);
- remove_proc_entry("Experimental", proc_fs_cifs);
remove_proc_entry("LookupCacheEnabled", proc_fs_cifs);
remove_proc_entry("fs/cifs", NULL);
}
.write = cifs_oplock_proc_write,
};
-static int cifs_experimental_proc_show(struct seq_file *m, void *v)
-{
- seq_printf(m, "%d\n", experimEnabled);
- return 0;
-}
-
-static int cifs_experimental_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, cifs_experimental_proc_show, NULL);
-}
-
-static ssize_t cifs_experimental_proc_write(struct file *file,
- const char __user *buffer, size_t count, loff_t *ppos)
-{
- char c;
- int rc;
-
- rc = get_user(c, buffer);
- if (rc)
- return rc;
- if (c == '0' || c == 'n' || c == 'N')
- experimEnabled = 0;
- else if (c == '1' || c == 'y' || c == 'Y')
- experimEnabled = 1;
- else if (c == '2')
- experimEnabled = 2;
-
- return count;
-}
-
-static const struct file_operations cifs_experimental_proc_fops = {
- .owner = THIS_MODULE,
- .open = cifs_experimental_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = cifs_experimental_proc_write,
-};
-
static int cifs_linux_ext_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "%d\n", linuxExtEnabled);
MAX_MECH_STR_LEN +
UID_KEY_LEN + (sizeof(uid_t) * 2) +
CREDUID_KEY_LEN + (sizeof(uid_t) * 2) +
- USER_KEY_LEN + strlen(sesInfo->userName) +
+ USER_KEY_LEN + strlen(sesInfo->user_name) +
PID_KEY_LEN + (sizeof(pid_t) * 2) + 1;
spnego_key = ERR_PTR(-ENOMEM);
sprintf(dp, ";creduid=0x%x", sesInfo->cred_uid);
dp = description + strlen(description);
- sprintf(dp, ";user=%s", sesInfo->userName);
+ sprintf(dp, ";user=%s", sesInfo->user_name);
dp = description + strlen(description);
sprintf(dp, ";pid=0x%x", current->pid);
case UNI_COLON:
*target = ':';
break;
- case UNI_ASTERIK:
+ case UNI_ASTERISK:
*target = '*';
break;
case UNI_QUESTION:
* names are little endian 16 bit Unicode on the wire
*/
int
-cifsConvertToUCS(__le16 *target, const char *source, int maxlen,
+cifsConvertToUCS(__le16 *target, const char *source, int srclen,
const struct nls_table *cp, int mapChars)
{
int i, j, charlen;
- int len_remaining = maxlen;
char src_char;
- __u16 temp;
+ __le16 dst_char;
+ wchar_t tmp;
if (!mapChars)
return cifs_strtoUCS(target, source, PATH_MAX, cp);
- for (i = 0, j = 0; i < maxlen; j++) {
+ for (i = 0, j = 0; i < srclen; j++) {
src_char = source[i];
switch (src_char) {
case 0:
- put_unaligned_le16(0, &target[j]);
+ put_unaligned(0, &target[j]);
goto ctoUCS_out;
case ':':
- temp = UNI_COLON;
+ dst_char = cpu_to_le16(UNI_COLON);
break;
case '*':
- temp = UNI_ASTERIK;
+ dst_char = cpu_to_le16(UNI_ASTERISK);
break;
case '?':
- temp = UNI_QUESTION;
+ dst_char = cpu_to_le16(UNI_QUESTION);
break;
case '<':
- temp = UNI_LESSTHAN;
+ dst_char = cpu_to_le16(UNI_LESSTHAN);
break;
case '>':
- temp = UNI_GRTRTHAN;
+ dst_char = cpu_to_le16(UNI_GRTRTHAN);
break;
case '|':
- temp = UNI_PIPE;
+ dst_char = cpu_to_le16(UNI_PIPE);
break;
/*
* FIXME: We can not handle remapping backslash (UNI_SLASH)
* as they use backslash as separator.
*/
default:
- charlen = cp->char2uni(source+i, len_remaining,
- &temp);
+ charlen = cp->char2uni(source + i, srclen - i, &tmp);
+ dst_char = cpu_to_le16(tmp);
+
/*
* if no match, use question mark, which at least in
* some cases serves as wild card
*/
if (charlen < 1) {
- temp = 0x003f;
+ dst_char = cpu_to_le16(0x003f);
charlen = 1;
}
- len_remaining -= charlen;
/*
* character may take more than one byte in the source
* string, but will take exactly two bytes in the
i += charlen;
continue;
}
- put_unaligned_le16(temp, &target[j]);
+ put_unaligned(dst_char, &target[j]);
i++; /* move to next char in source string */
- len_remaining--;
}
ctoUCS_out:
* reserved symbols (along with \ and /), otherwise illegal to store
* in filenames in NTFS
*/
-#define UNI_ASTERIK (__u16) ('*' + 0xF000)
+#define UNI_ASTERISK (__u16) ('*' + 0xF000)
#define UNI_QUESTION (__u16) ('?' + 0xF000)
#define UNI_COLON (__u16) (':' + 0xF000)
#define UNI_GRTRTHAN (__u16) ('>' + 0xF000)
#include <linux/ctype.h>
#include <linux/random.h>
-/* Calculate and return the CIFS signature based on the mac key and SMB PDU */
-/* the 16 byte signature must be allocated by the caller */
-/* Note we only use the 1st eight bytes */
-/* Note that the smb header signature field on input contains the
- sequence number before this function is called */
-
+/*
+ * Calculate and return the CIFS signature based on the mac key and SMB PDU.
+ * The 16 byte signature must be allocated by the caller. Note we only use the
+ * 1st eight bytes and that the smb header signature field on input contains
+ * the sequence number before this function is called. Also, this function
+ * should be called with the server->srv_mutex held.
+ */
static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu,
struct TCP_Server_Info *server, char *signature)
{
cpu_to_le32(expected_sequence_number);
cifs_pdu->Signature.Sequence.Reserved = 0;
+ mutex_lock(&server->srv_mutex);
rc = cifs_calculate_signature(cifs_pdu, server,
what_we_think_sig_should_be);
+ mutex_unlock(&server->srv_mutex);
if (rc)
return rc;
return rc;
}
- /* convert ses->userName to unicode and uppercase */
- len = strlen(ses->userName);
+ /* convert ses->user_name to unicode and uppercase */
+ len = strlen(ses->user_name);
user = kmalloc(2 + (len * 2), GFP_KERNEL);
if (user == NULL) {
cERROR(1, "calc_ntlmv2_hash: user mem alloc failure\n");
rc = -ENOMEM;
goto calc_exit_2;
}
- len = cifs_strtoUCS((__le16 *)user, ses->userName, len, nls_cp);
+ len = cifs_strtoUCS((__le16 *)user, ses->user_name, len, nls_cp);
UniStrupr(user);
crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
int cifsERROR = 1;
int traceSMB = 0;
unsigned int oplockEnabled = 1;
-unsigned int experimEnabled = 0;
unsigned int linuxExtEnabled = 1;
unsigned int lookupCacheEnabled = 1;
unsigned int multiuser_mount = 0;
kfree(cifs_sb);
return rc;
}
+ cifs_sb->bdi.ra_pages = default_backing_dev_info.ra_pages;
#ifdef CONFIG_CIFS_DFS_UPCALL
/* copy mount params to sb for use in submounts */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)
seq_printf(s, ",multiuser");
- else if (tcon->ses->userName)
- seq_printf(s, ",username=%s", tcon->ses->userName);
+ else if (tcon->ses->user_name)
+ seq_printf(s, ",username=%s", tcon->ses->user_name);
if (tcon->ses->domainName)
seq_printf(s, ",domain=%s", tcon->ses->domainName);
#define MAX_TREE_SIZE (2 + MAX_SERVER_SIZE + 1 + MAX_SHARE_SIZE + 1)
#define MAX_SERVER_SIZE 15
-#define MAX_SHARE_SIZE 64 /* used to be 20, this should still be enough */
-#define MAX_USERNAME_SIZE 32 /* 32 is to allow for 15 char names + null
- termination then *2 for unicode versions */
-#define MAX_PASSWORD_SIZE 512 /* max for windows seems to be 256 wide chars */
+#define MAX_SHARE_SIZE 80
+#define MAX_USERNAME_SIZE 256 /* reasonable maximum for current servers */
+#define MAX_PASSWORD_SIZE 512 /* max for windows seems to be 256 wide chars */
#define CIFS_MIN_RCV_POOL 4
CifsNew = 0,
CifsGood,
CifsExiting,
- CifsNeedReconnect
+ CifsNeedReconnect,
+ CifsNeedNegotiate
};
enum securityEnum {
int capabilities;
char serverName[SERVER_NAME_LEN_WITH_NULL * 2]; /* BB make bigger for
TCP names - will ipv6 and sctp addresses fit? */
- char userName[MAX_USERNAME_SIZE + 1];
+ char *user_name;
char *domainName;
char *password;
struct session_key auth_key;
have the uid/password or Kerberos credential
or equivalent for current user */
GLOBAL_EXTERN unsigned int oplockEnabled;
-GLOBAL_EXTERN unsigned int experimEnabled;
GLOBAL_EXTERN unsigned int lookupCacheEnabled;
GLOBAL_EXTERN unsigned int global_secflags; /* if on, session setup sent
with more secure ntlmssp2 challenge/resp */
*/
while (server->tcpStatus == CifsNeedReconnect) {
wait_event_interruptible_timeout(server->response_q,
- (server->tcpStatus == CifsGood), 10 * HZ);
+ (server->tcpStatus != CifsNeedReconnect), 10 * HZ);
- /* is TCP session is reestablished now ?*/
+ /* are we still trying to reconnect? */
if (server->tcpStatus != CifsNeedReconnect)
break;
return rc;
/* set up echo request */
- smb->hdr.Tid = cpu_to_le16(0xffff);
+ smb->hdr.Tid = 0xffff;
smb->hdr.WordCount = 1;
put_unaligned_le16(1, &smb->EchoCount);
put_bcc_le(1, &smb->hdr);
__constant_cpu_to_le16(CIFS_WRLCK))
pLockData->fl_type = F_WRLCK;
- pLockData->fl_start = parm_data->start;
- pLockData->fl_end = parm_data->start +
- parm_data->length - 1;
- pLockData->fl_pid = parm_data->pid;
+ pLockData->fl_start = le64_to_cpu(parm_data->start);
+ pLockData->fl_end = pLockData->fl_start +
+ le64_to_cpu(parm_data->length) - 1;
+ pLockData->fl_pid = le32_to_cpu(parm_data->pid);
}
}
}
spin_unlock(&GlobalMid_Lock);
- while ((server->tcpStatus != CifsExiting) &&
- (server->tcpStatus != CifsGood)) {
+ while (server->tcpStatus == CifsNeedReconnect) {
try_to_freeze();
/* we should try only the port we connected to before */
atomic_inc(&tcpSesReconnectCount);
spin_lock(&GlobalMid_Lock);
if (server->tcpStatus != CifsExiting)
- server->tcpStatus = CifsGood;
+ server->tcpStatus = CifsNeedNegotiate;
spin_unlock(&GlobalMid_Lock);
}
}
total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount);
data_in_this_rsp = get_unaligned_le16(&pSMBt->t2_rsp.DataCount);
- remaining = total_data_size - data_in_this_rsp;
-
- if (remaining == 0)
+ if (total_data_size == data_in_this_rsp)
return 0;
- else if (remaining < 0) {
+ else if (total_data_size < data_in_this_rsp) {
cFYI(1, "total data %d smaller than data in frame %d",
total_data_size, data_in_this_rsp);
return -EINVAL;
- } else {
- cFYI(1, "missing %d bytes from transact2, check next response",
- remaining);
- if (total_data_size > maxBufSize) {
- cERROR(1, "TotalDataSize %d is over maximum buffer %d",
- total_data_size, maxBufSize);
- return -EINVAL;
- }
- return remaining;
}
+
+ remaining = total_data_size - data_in_this_rsp;
+
+ cFYI(1, "missing %d bytes from transact2, check next response",
+ remaining);
+ if (total_data_size > maxBufSize) {
+ cERROR(1, "TotalDataSize %d is over maximum buffer %d",
+ total_data_size, maxBufSize);
+ return -EINVAL;
+ }
+ return remaining;
}
static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB)
char *data_area_of_target;
char *data_area_of_buf2;
int remaining;
- __u16 byte_count, total_data_size, total_in_buf, total_in_buf2;
+ unsigned int byte_count, total_in_buf;
+ __u16 total_data_size, total_in_buf2;
total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount);
remaining = total_data_size - total_in_buf;
if (remaining < 0)
- return -EINVAL;
+ return -EPROTO;
if (remaining == 0) /* nothing to do, ignore */
return 0;
data_area_of_target += total_in_buf;
/* copy second buffer into end of first buffer */
- memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2);
total_in_buf += total_in_buf2;
+ /* is the result too big for the field? */
+ if (total_in_buf > USHRT_MAX)
+ return -EPROTO;
put_unaligned_le16(total_in_buf, &pSMBt->t2_rsp.DataCount);
+
+ /* fix up the BCC */
byte_count = get_bcc_le(pTargetSMB);
byte_count += total_in_buf2;
+ /* is the result too big for the field? */
+ if (byte_count > USHRT_MAX)
+ return -EPROTO;
put_bcc_le(byte_count, pTargetSMB);
byte_count = pTargetSMB->smb_buf_length;
byte_count += total_in_buf2;
-
- /* BB also add check that we are not beyond maximum buffer size */
-
+ /* don't allow buffer to overflow */
+ if (byte_count > CIFSMaxBufSize)
+ return -ENOBUFS;
pTargetSMB->smb_buf_length = byte_count;
+ memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2);
+
if (remaining == total_in_buf2) {
cFYI(1, "found the last secondary response");
return 0; /* we are done */
pdu_length = 4; /* enough to get RFC1001 header */
incomplete_rcv:
- if (echo_retries > 0 &&
+ if (echo_retries > 0 && server->tcpStatus == CifsGood &&
time_after(jiffies, server->lstrp +
(echo_retries * SMB_ECHO_INTERVAL))) {
cERROR(1, "Server %s has not responded in %d seconds. "
list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
- if ((mid_entry->mid == smb_buffer->Mid) &&
- (mid_entry->midState == MID_REQUEST_SUBMITTED) &&
- (mid_entry->command == smb_buffer->Command)) {
- if (length == 0 &&
- check2ndT2(smb_buffer, server->maxBuf) > 0) {
- /* We have a multipart transact2 resp */
- isMultiRsp = true;
- if (mid_entry->resp_buf) {
- /* merge response - fix up 1st*/
- if (coalesce_t2(smb_buffer,
- mid_entry->resp_buf)) {
- mid_entry->multiRsp =
- true;
- break;
- } else {
- /* all parts received */
- mid_entry->multiEnd =
- true;
- goto multi_t2_fnd;
- }
+ if (mid_entry->mid != smb_buffer->Mid ||
+ mid_entry->midState != MID_REQUEST_SUBMITTED ||
+ mid_entry->command != smb_buffer->Command) {
+ mid_entry = NULL;
+ continue;
+ }
+
+ if (length == 0 &&
+ check2ndT2(smb_buffer, server->maxBuf) > 0) {
+ /* We have a multipart transact2 resp */
+ isMultiRsp = true;
+ if (mid_entry->resp_buf) {
+ /* merge response - fix up 1st*/
+ length = coalesce_t2(smb_buffer,
+ mid_entry->resp_buf);
+ if (length > 0) {
+ length = 0;
+ mid_entry->multiRsp = true;
+ break;
} else {
- if (!isLargeBuf) {
- cERROR(1, "1st trans2 resp needs bigbuf");
- /* BB maybe we can fix this up, switch
- to already allocated large buffer? */
- } else {
- /* Have first buffer */
- mid_entry->resp_buf =
- smb_buffer;
- mid_entry->largeBuf =
- true;
- bigbuf = NULL;
- }
+ /* all parts received or
+ * packet is malformed
+ */
+ mid_entry->multiEnd = true;
+ goto multi_t2_fnd;
+ }
+ } else {
+ if (!isLargeBuf) {
+ /*
+ * FIXME: switch to already
+ * allocated largebuf?
+ */
+ cERROR(1, "1st trans2 resp "
+ "needs bigbuf");
+ } else {
+ /* Have first buffer */
+ mid_entry->resp_buf =
+ smb_buffer;
+ mid_entry->largeBuf = true;
+ bigbuf = NULL;
}
- break;
}
- mid_entry->resp_buf = smb_buffer;
- mid_entry->largeBuf = isLargeBuf;
+ break;
+ }
+ mid_entry->resp_buf = smb_buffer;
+ mid_entry->largeBuf = isLargeBuf;
multi_t2_fnd:
- if (length == 0)
- mid_entry->midState =
- MID_RESPONSE_RECEIVED;
- else
- mid_entry->midState =
- MID_RESPONSE_MALFORMED;
+ if (length == 0)
+ mid_entry->midState = MID_RESPONSE_RECEIVED;
+ else
+ mid_entry->midState = MID_RESPONSE_MALFORMED;
#ifdef CONFIG_CIFS_STATS2
- mid_entry->when_received = jiffies;
+ mid_entry->when_received = jiffies;
#endif
- list_del_init(&mid_entry->qhead);
- mid_entry->callback(mid_entry);
- break;
- }
- mid_entry = NULL;
+ list_del_init(&mid_entry->qhead);
+ mid_entry->callback(mid_entry);
+ break;
}
spin_unlock(&GlobalMid_Lock);
cifs_parse_mount_options(char *options, const char *devname,
struct smb_vol *vol)
{
- char *value;
- char *data;
+ char *value, *data, *end;
unsigned int temp_len, i, j;
char separator[2];
short int override_uid = -1;
if (!options)
return 1;
+ end = options + strlen(options);
if (strncmp(options, "sep=", 4) == 0) {
if (options[4] != 0) {
separator[0] = options[4];
/* null user, ie anonymous, authentication */
vol->nullauth = 1;
}
- if (strnlen(value, 200) < 200) {
+ if (strnlen(value, MAX_USERNAME_SIZE) <
+ MAX_USERNAME_SIZE) {
vol->username = value;
} else {
printk(KERN_WARNING "CIFS: username too long\n");
the only illegal character in a password is null */
if ((value[temp_len] == 0) &&
+ (value + temp_len < end) &&
(value[temp_len+1] == separator[0])) {
/* reinsert comma */
value[temp_len] = separator[0];
static bool
match_port(struct TCP_Server_Info *server, struct sockaddr *addr)
{
- unsigned short int port, *sport;
+ __be16 port, *sport;
switch (addr->sa_family) {
case AF_INET:
module_put(THIS_MODULE);
goto out_err_crypto_release;
}
+ tcp_ses->tcpStatus = CifsNeedNegotiate;
/* thread spawned, put it on the list */
spin_lock(&cifs_tcp_ses_lock);
break;
default:
/* anything else takes username/password */
- if (strncmp(ses->userName, vol->username,
+ if (ses->user_name == NULL)
+ continue;
+ if (strncmp(ses->user_name, vol->username,
MAX_USERNAME_SIZE))
continue;
if (strlen(vol->username) != 0 &&
cifs_put_tcp_session(server);
}
+static bool warned_on_ntlm; /* globals init to false automatically */
+
static struct cifsSesInfo *
cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
{
else
sprintf(ses->serverName, "%pI4", &addr->sin_addr);
- if (volume_info->username)
- strncpy(ses->userName, volume_info->username,
- MAX_USERNAME_SIZE);
+ if (volume_info->username) {
+ ses->user_name = kstrdup(volume_info->username, GFP_KERNEL);
+ if (!ses->user_name)
+ goto get_ses_fail;
+ }
/* volume_info->password freed at unmount */
if (volume_info->password) {
}
ses->cred_uid = volume_info->cred_uid;
ses->linux_uid = volume_info->linux_uid;
+
+ /* ntlmv2 is much stronger than ntlm security, and has been broadly
+ supported for many years, time to update default security mechanism */
+ if ((volume_info->secFlg == 0) && warned_on_ntlm == false) {
+ warned_on_ntlm = true;
+ cERROR(1, "default security mechanism requested. The default "
+ "security mechanism will be upgraded from ntlm to "
+ "ntlmv2 in kernel release 2.6.41");
+ }
ses->overrideSecFlg = volume_info->secFlg;
mutex_lock(&ses->session_mutex);
generic_ip_connect(struct TCP_Server_Info *server)
{
int rc = 0;
- unsigned short int sport;
+ __be16 sport;
int slen, sfamily;
struct socket *socket = server->ssocket;
struct sockaddr *saddr;
static int
ip_connect(struct TCP_Server_Info *server)
{
- unsigned short int *sport;
+ __be16 *sport;
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
remote_path_check:
/* check if a whole path (including prepath) is not remote */
- if (!rc && cifs_sb->prepathlen && tcon) {
+ if (!rc && tcon) {
/* build_path_to_root works only when we have a valid tcon */
full_path = cifs_build_path_to_root(cifs_sb, tcon);
if (full_path == NULL) {
int cifs_close(struct inode *inode, struct file *file)
{
- cifsFileInfo_put(file->private_data);
- file->private_data = NULL;
+ if (file->private_data != NULL) {
+ cifsFileInfo_put(file->private_data);
+ file->private_data = NULL;
+ }
/* return code from the ->release op is always ignored */
return 0;
total_written += bytes_written) {
rc = -EAGAIN;
while (rc == -EAGAIN) {
+ struct kvec iov[2];
+ unsigned int len;
+
if (open_file->invalidHandle) {
/* we could deadlock if we called
filemap_fdatawait from here so tell
if (rc != 0)
break;
}
- if (experimEnabled || (pTcon->ses->server &&
- ((pTcon->ses->server->secMode &
- (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
- == 0))) {
- struct kvec iov[2];
- unsigned int len;
-
- len = min((size_t)cifs_sb->wsize,
- write_size - total_written);
- /* iov[0] is reserved for smb header */
- iov[1].iov_base = (char *)write_data +
- total_written;
- iov[1].iov_len = len;
- rc = CIFSSMBWrite2(xid, pTcon,
- open_file->netfid, len,
- *poffset, &bytes_written,
- iov, 1, 0);
- } else
- rc = CIFSSMBWrite(xid, pTcon,
- open_file->netfid,
- min_t(const int, cifs_sb->wsize,
- write_size - total_written),
- *poffset, &bytes_written,
- write_data + total_written,
- NULL, 0);
+
+ len = min((size_t)cifs_sb->wsize,
+ write_size - total_written);
+ /* iov[0] is reserved for smb header */
+ iov[1].iov_base = (char *)write_data + total_written;
+ iov[1].iov_len = len;
+ rc = CIFSSMBWrite2(xid, pTcon, open_file->netfid, len,
+ *poffset, &bytes_written, iov, 1, 0);
}
if (rc || (bytes_written == 0)) {
if (total_written)
}
tcon = tlink_tcon(open_file->tlink);
- if (!experimEnabled && tcon->ses->server->secMode &
- (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
- cifsFileInfo_put(open_file);
- kfree(iov);
- return generic_writepages(mapping, wbc);
- }
cifsFileInfo_put(open_file);
xid = GetXid();
return total_read;
}
+/*
+ * If the page is mmap'ed into a process' page tables, then we need to make
+ * sure that it doesn't change while being written back.
+ */
+static int
+cifs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct page *page = vmf->page;
+
+ lock_page(page);
+ return VM_FAULT_LOCKED;
+}
+
+static struct vm_operations_struct cifs_file_vm_ops = {
+ .fault = filemap_fault,
+ .page_mkwrite = cifs_page_mkwrite,
+};
+
int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma)
{
int rc, xid;
cifs_invalidate_mapping(inode);
rc = generic_file_mmap(file, vma);
+ if (rc == 0)
+ vma->vm_ops = &cifs_file_vm_ops;
FreeXid(xid);
return rc;
}
return rc;
}
rc = generic_file_mmap(file, vma);
+ if (rc == 0)
+ vma->vm_ops = &cifs_file_vm_ops;
FreeXid(xid);
return rc;
}
if (rc != 0)
return rc;
- if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) {
+ if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
CIFSSMBClose(xid, tcon, netfid);
/* it's not a symlink */
return -EINVAL;
if (rc != 0)
goto out;
- if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) {
+ if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
CIFSSMBClose(xid, pTcon, netfid);
/* it's not a symlink */
goto out;
memset(buf_to_free->password, 0, strlen(buf_to_free->password));
kfree(buf_to_free->password);
}
+ kfree(buf_to_free->user_name);
kfree(buf_to_free->domainName);
kfree(buf_to_free);
}
(struct smb_com_transaction_change_notify_rsp *)buf;
struct file_notify_information *pnotify;
__u32 data_offset = 0;
- if (pSMBr->ByteCount > sizeof(struct file_notify_information)) {
+ if (get_bcc_le(buf) > sizeof(struct file_notify_information)) {
data_offset = le32_to_cpu(pSMBr->DataOffset);
pnotify = (struct file_notify_information *)
bcc_ptr++;
} */
/* copy user */
- if (ses->userName == NULL) {
+ if (ses->user_name == NULL) {
/* null user mount */
*bcc_ptr = 0;
*(bcc_ptr+1) = 0;
} else {
- bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->userName,
+ bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->user_name,
MAX_USERNAME_SIZE, nls_cp);
}
bcc_ptr += 2 * bytes_ret;
/* copy user */
/* BB what about null user mounts - check that we do this BB */
/* copy user */
- if (ses->userName == NULL) {
- /* BB what about null user mounts - check that we do this BB */
- } else {
- strncpy(bcc_ptr, ses->userName, MAX_USERNAME_SIZE);
- }
- bcc_ptr += strnlen(ses->userName, MAX_USERNAME_SIZE);
+ if (ses->user_name != NULL)
+ strncpy(bcc_ptr, ses->user_name, MAX_USERNAME_SIZE);
+ /* else null user mount */
+
+ bcc_ptr += strnlen(ses->user_name, MAX_USERNAME_SIZE);
*bcc_ptr = 0;
bcc_ptr++; /* account for null termination */
}
static void
-decode_unicode_ssetup(char **pbcc_area, __u16 bleft, struct cifsSesInfo *ses,
+decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifsSesInfo *ses,
const struct nls_table *nls_cp)
{
int len;
cFYI(1, "bleft %d", bleft);
- /*
- * Windows servers do not always double null terminate their final
- * Unicode string. Check to see if there are an uneven number of bytes
- * left. If so, then add an extra NULL pad byte to the end of the
- * response.
- *
- * See section 2.7.2 in "Implementing CIFS" for details
- */
- if (bleft % 2) {
- data[bleft] = 0;
- ++bleft;
- }
-
kfree(ses->serverOS);
ses->serverOS = cifs_strndup_from_ucs(data, bleft, true, nls_cp);
cFYI(1, "serverOS=%s", ses->serverOS);
/* BB spec says that if AvId field of MsvAvTimestamp is populated then
we must set the MIC field of the AUTHENTICATE_MESSAGE */
ses->ntlmssp->server_flags = le32_to_cpu(pblob->NegotiateFlags);
- tioffset = cpu_to_le16(pblob->TargetInfoArray.BufferOffset);
- tilen = cpu_to_le16(pblob->TargetInfoArray.Length);
+ tioffset = le32_to_cpu(pblob->TargetInfoArray.BufferOffset);
+ tilen = le16_to_cpu(pblob->TargetInfoArray.Length);
if (tilen) {
ses->auth_key.response = kmalloc(tilen, GFP_KERNEL);
if (!ses->auth_key.response) {
tmp += len;
}
- if (ses->userName == NULL) {
+ if (ses->user_name == NULL) {
sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer);
sec_blob->UserName.Length = 0;
sec_blob->UserName.MaximumLength = 0;
tmp += 2;
} else {
int len;
- len = cifs_strtoUCS((__le16 *)tmp, ses->userName,
+ len = cifs_strtoUCS((__le16 *)tmp, ses->user_name,
MAX_USERNAME_SIZE, nls_cp);
len *= 2; /* unicode is 2 bytes each */
sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer);
}
/* BB check if Unicode and decode strings */
- if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
+ if (bytes_remaining == 0) {
+ /* no string area to decode, do nothing */
+ } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
/* unicode string area must be word-aligned */
if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) {
++bcc_ptr;
static unsigned int d_hash_mask __read_mostly;
static unsigned int d_hash_shift __read_mostly;
-struct dcache_hash_bucket {
- struct hlist_bl_head head;
-};
-static struct dcache_hash_bucket *dentry_hashtable __read_mostly;
+static struct hlist_bl_head *dentry_hashtable __read_mostly;
-static inline struct dcache_hash_bucket *d_hash(struct dentry *parent,
+static inline struct hlist_bl_head *d_hash(struct dentry *parent,
unsigned long hash)
{
hash += ((unsigned long) parent ^ GOLDEN_RATIO_PRIME) / L1_CACHE_BYTES;
return dentry_hashtable + (hash & D_HASHMASK);
}
-static inline void spin_lock_bucket(struct dcache_hash_bucket *b)
-{
- bit_spin_lock(0, (unsigned long *)&b->head.first);
-}
-
-static inline void spin_unlock_bucket(struct dcache_hash_bucket *b)
-{
- __bit_spin_unlock(0, (unsigned long *)&b->head.first);
-}
-
/* Statistics gathering. */
struct dentry_stat_t dentry_stat = {
.age_limit = 45,
if (dentry->d_op && dentry->d_op->d_release)
dentry->d_op->d_release(dentry);
- /* if dentry was never inserted into hash, immediate free is OK */
- if (hlist_bl_unhashed(&dentry->d_hash))
+ /* if dentry was never visible to RCU, immediate free is OK */
+ if (!(dentry->d_flags & DCACHE_RCUACCESS))
__d_free(&dentry->d_u.d_rcu);
else
call_rcu(&dentry->d_u.d_rcu, __d_free);
*/
void __d_drop(struct dentry *dentry)
{
- if (!(dentry->d_flags & DCACHE_UNHASHED)) {
- if (unlikely(dentry->d_flags & DCACHE_DISCONNECTED)) {
- bit_spin_lock(0,
- (unsigned long *)&dentry->d_sb->s_anon.first);
- dentry->d_flags |= DCACHE_UNHASHED;
- hlist_bl_del_init(&dentry->d_hash);
- __bit_spin_unlock(0,
- (unsigned long *)&dentry->d_sb->s_anon.first);
- } else {
- struct dcache_hash_bucket *b;
+ if (!d_unhashed(dentry)) {
+ struct hlist_bl_head *b;
+ if (unlikely(dentry->d_flags & DCACHE_DISCONNECTED))
+ b = &dentry->d_sb->s_anon;
+ else
b = d_hash(dentry->d_parent, dentry->d_name.hash);
- spin_lock_bucket(b);
- /*
- * We may not actually need to put DCACHE_UNHASHED
- * manipulations under the hash lock, but follow
- * the principle of least surprise.
- */
- dentry->d_flags |= DCACHE_UNHASHED;
- hlist_bl_del_rcu(&dentry->d_hash);
- spin_unlock_bucket(b);
- dentry_rcuwalk_barrier(dentry);
- }
+
+ hlist_bl_lock(b);
+ __hlist_bl_del(&dentry->d_hash);
+ dentry->d_hash.pprev = NULL;
+ hlist_bl_unlock(b);
+
+ dentry_rcuwalk_barrier(dentry);
}
}
EXPORT_SYMBOL(__d_drop);
dname[name->len] = 0;
dentry->d_count = 1;
- dentry->d_flags = DCACHE_UNHASHED;
+ dentry->d_flags = 0;
spin_lock_init(&dentry->d_lock);
seqcount_init(&dentry->d_seq);
dentry->d_inode = NULL;
tmp->d_inode = inode;
tmp->d_flags |= DCACHE_DISCONNECTED;
list_add(&tmp->d_alias, &inode->i_dentry);
- bit_spin_lock(0, (unsigned long *)&tmp->d_sb->s_anon.first);
- tmp->d_flags &= ~DCACHE_UNHASHED;
+ hlist_bl_lock(&tmp->d_sb->s_anon);
hlist_bl_add_head(&tmp->d_hash, &tmp->d_sb->s_anon);
- __bit_spin_unlock(0, (unsigned long *)&tmp->d_sb->s_anon.first);
+ hlist_bl_unlock(&tmp->d_sb->s_anon);
spin_unlock(&tmp->d_lock);
spin_unlock(&inode->i_lock);
security_d_instantiate(tmp, inode);
unsigned int len = name->len;
unsigned int hash = name->hash;
const unsigned char *str = name->name;
- struct dcache_hash_bucket *b = d_hash(parent, hash);
+ struct hlist_bl_head *b = d_hash(parent, hash);
struct hlist_bl_node *node;
struct dentry *dentry;
*
* See Documentation/filesystems/path-lookup.txt for more details.
*/
- hlist_bl_for_each_entry_rcu(dentry, node, &b->head, d_hash) {
+ hlist_bl_for_each_entry_rcu(dentry, node, b, d_hash) {
struct inode *i;
const char *tname;
int tlen;
unsigned int len = name->len;
unsigned int hash = name->hash;
const unsigned char *str = name->name;
- struct dcache_hash_bucket *b = d_hash(parent, hash);
+ struct hlist_bl_head *b = d_hash(parent, hash);
struct hlist_bl_node *node;
struct dentry *found = NULL;
struct dentry *dentry;
*/
rcu_read_lock();
- hlist_bl_for_each_entry_rcu(dentry, node, &b->head, d_hash) {
+ hlist_bl_for_each_entry_rcu(dentry, node, b, d_hash) {
const char *tname;
int tlen;
}
EXPORT_SYMBOL(d_delete);
-static void __d_rehash(struct dentry * entry, struct dcache_hash_bucket *b)
+static void __d_rehash(struct dentry * entry, struct hlist_bl_head *b)
{
BUG_ON(!d_unhashed(entry));
- spin_lock_bucket(b);
- entry->d_flags &= ~DCACHE_UNHASHED;
- hlist_bl_add_head_rcu(&entry->d_hash, &b->head);
- spin_unlock_bucket(b);
+ hlist_bl_lock(b);
+ entry->d_flags |= DCACHE_RCUACCESS;
+ hlist_bl_add_head_rcu(&entry->d_hash, b);
+ hlist_bl_unlock(b);
}
static void _d_rehash(struct dentry * entry)
*/
void dentry_update_name_case(struct dentry *dentry, struct qstr *name)
{
- BUG_ON(!mutex_is_locked(&dentry->d_inode->i_mutex));
+ BUG_ON(!mutex_is_locked(&dentry->d_parent->d_inode->i_mutex));
BUG_ON(dentry->d_name.len != name->len); /* d_lookup gives this */
spin_lock(&dentry->d_lock);
dentry_hashtable =
alloc_large_system_hash("Dentry cache",
- sizeof(struct dcache_hash_bucket),
+ sizeof(struct hlist_bl_head),
dhash_entries,
13,
HASH_EARLY,
0);
for (loop = 0; loop < (1 << d_hash_shift); loop++)
- INIT_HLIST_BL_HEAD(&dentry_hashtable[loop].head);
+ INIT_HLIST_BL_HEAD(dentry_hashtable + loop);
}
static void __init dcache_init(void)
dentry_hashtable =
alloc_large_system_hash("Dentry cache",
- sizeof(struct dcache_hash_bucket),
+ sizeof(struct hlist_bl_head),
dhash_entries,
13,
0,
0);
for (loop = 0; loop < (1 << d_hash_shift); loop++)
- INIT_HLIST_BL_HEAD(&dentry_hashtable[loop].head);
+ INIT_HLIST_BL_HEAD(dentry_hashtable + loop);
}
/* SLAB cache for __getname() consumers */
crypt_stat->metadata_size = ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE;
}
+void ecryptfs_i_size_init(const char *page_virt, struct inode *inode)
+{
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
+ struct ecryptfs_crypt_stat *crypt_stat;
+ u64 file_size;
+
+ crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
+ mount_crypt_stat =
+ &ecryptfs_superblock_to_private(inode->i_sb)->mount_crypt_stat;
+ if (mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED) {
+ file_size = i_size_read(ecryptfs_inode_to_lower(inode));
+ if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR)
+ file_size += crypt_stat->metadata_size;
+ } else
+ file_size = get_unaligned_be64(page_virt);
+ i_size_write(inode, (loff_t)file_size);
+ crypt_stat->flags |= ECRYPTFS_I_SIZE_INITIALIZED;
+}
+
/**
* ecryptfs_read_headers_virt
* @page_virt: The virtual address into which to read the headers
rc = -EINVAL;
goto out;
}
+ if (!(crypt_stat->flags & ECRYPTFS_I_SIZE_INITIALIZED))
+ ecryptfs_i_size_init(page_virt, ecryptfs_dentry->d_inode);
offset += MAGIC_ECRYPTFS_MARKER_SIZE_BYTES;
rc = ecryptfs_process_flags(crypt_stat, (page_virt + offset),
&bytes_read);
#define ECRYPTFS_ENCFN_USE_MOUNT_FNEK 0x00000800
#define ECRYPTFS_ENCFN_USE_FEK 0x00001000
#define ECRYPTFS_UNLINK_SIGS 0x00002000
+#define ECRYPTFS_I_SIZE_INITIALIZED 0x00004000
u32 flags;
unsigned int file_version;
size_t iv_bytes;
struct ecryptfs_inode_info {
struct inode vfs_inode;
struct inode *wii_inode;
+ struct mutex lower_file_mutex;
+ atomic_t lower_file_count;
struct file *lower_file;
struct ecryptfs_crypt_stat crypt_stat;
};
int ecryptfs_interpose(struct dentry *hidden_dentry,
struct dentry *this_dentry, struct super_block *sb,
u32 flags);
+void ecryptfs_i_size_init(const char *page_virt, struct inode *inode);
int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
struct dentry *lower_dentry,
struct inode *ecryptfs_dir_inode);
struct dentry *lower_dentry,
struct vfsmount *lower_mnt,
const struct cred *cred);
-int ecryptfs_init_persistent_file(struct dentry *ecryptfs_dentry);
+int ecryptfs_get_lower_file(struct dentry *ecryptfs_dentry);
+void ecryptfs_put_lower_file(struct inode *inode);
int
ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes,
size_t *packet_size,
| ECRYPTFS_ENCRYPTED);
}
mutex_unlock(&crypt_stat->cs_mutex);
- rc = ecryptfs_init_persistent_file(ecryptfs_dentry);
+ rc = ecryptfs_get_lower_file(ecryptfs_dentry);
if (rc) {
printk(KERN_ERR "%s: Error attempting to initialize "
- "the persistent file for the dentry with name "
+ "the lower file for the dentry with name "
"[%s]; rc = [%d]\n", __func__,
ecryptfs_dentry->d_name.name, rc);
goto out_free;
if ((ecryptfs_inode_to_private(inode)->lower_file->f_flags & O_ACCMODE)
== O_RDONLY && (file->f_flags & O_ACCMODE) != O_RDONLY) {
rc = -EPERM;
- printk(KERN_WARNING "%s: Lower persistent file is RO; eCryptfs "
+ printk(KERN_WARNING "%s: Lower file is RO; eCryptfs "
"file must hence be opened RO\n", __func__);
- goto out_free;
+ goto out_put;
}
ecryptfs_set_file_lower(
file, ecryptfs_inode_to_private(inode)->lower_file);
"Plaintext passthrough mode is not "
"enabled; returning -EIO\n");
mutex_unlock(&crypt_stat->cs_mutex);
- goto out_free;
+ goto out_put;
}
rc = 0;
- crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
+ crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
+ | ECRYPTFS_ENCRYPTED);
mutex_unlock(&crypt_stat->cs_mutex);
goto out;
}
"[0x%.16lx] size: [0x%.16llx]\n", inode, inode->i_ino,
(unsigned long long)i_size_read(inode));
goto out;
+out_put:
+ ecryptfs_put_lower_file(inode);
out_free:
kmem_cache_free(ecryptfs_file_info_cache,
ecryptfs_file_to_private(file));
static int ecryptfs_flush(struct file *file, fl_owner_t td)
{
- int rc = 0;
- struct file *lower_file = NULL;
-
- lower_file = ecryptfs_file_to_lower(file);
- if (lower_file->f_op && lower_file->f_op->flush)
- rc = lower_file->f_op->flush(lower_file, td);
- return rc;
+ return file->f_mode & FMODE_WRITE
+ ? filemap_write_and_wait(file->f_mapping) : 0;
}
static int ecryptfs_release(struct inode *inode, struct file *file)
{
+ ecryptfs_put_lower_file(inode);
kmem_cache_free(ecryptfs_file_info_cache,
ecryptfs_file_to_private(file));
return 0;
"context; rc = [%d]\n", rc);
goto out;
}
- rc = ecryptfs_init_persistent_file(ecryptfs_dentry);
+ rc = ecryptfs_get_lower_file(ecryptfs_dentry);
if (rc) {
printk(KERN_ERR "%s: Error attempting to initialize "
- "the persistent file for the dentry with name "
+ "the lower file for the dentry with name "
"[%s]; rc = [%d]\n", __func__,
ecryptfs_dentry->d_name.name, rc);
goto out;
}
rc = ecryptfs_write_metadata(ecryptfs_dentry);
- if (rc) {
+ if (rc)
printk(KERN_ERR "Error writing headers; rc = [%d]\n", rc);
- goto out;
- }
+ ecryptfs_put_lower_file(ecryptfs_dentry->d_inode);
out:
return rc;
}
struct dentry *lower_dir_dentry;
struct vfsmount *lower_mnt;
struct inode *lower_inode;
- struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
struct ecryptfs_crypt_stat *crypt_stat;
char *page_virt = NULL;
- u64 file_size;
- int rc = 0;
+ int put_lower = 0, rc = 0;
lower_dir_dentry = lower_dentry->d_parent;
lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(
rc = -ENOMEM;
goto out;
}
- rc = ecryptfs_init_persistent_file(ecryptfs_dentry);
+ rc = ecryptfs_get_lower_file(ecryptfs_dentry);
if (rc) {
printk(KERN_ERR "%s: Error attempting to initialize "
- "the persistent file for the dentry with name "
+ "the lower file for the dentry with name "
"[%s]; rc = [%d]\n", __func__,
ecryptfs_dentry->d_name.name, rc);
goto out_free_kmem;
}
+ put_lower = 1;
crypt_stat = &ecryptfs_inode_to_private(
ecryptfs_dentry->d_inode)->crypt_stat;
/* TODO: lock for crypt_stat comparison */
}
crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
}
- mount_crypt_stat = &ecryptfs_superblock_to_private(
- ecryptfs_dentry->d_sb)->mount_crypt_stat;
- if (mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED) {
- if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR)
- file_size = (crypt_stat->metadata_size
- + i_size_read(lower_dentry->d_inode));
- else
- file_size = i_size_read(lower_dentry->d_inode);
- } else {
- file_size = get_unaligned_be64(page_virt);
- }
- i_size_write(ecryptfs_dentry->d_inode, (loff_t)file_size);
+ ecryptfs_i_size_init(page_virt, ecryptfs_dentry->d_inode);
out_free_kmem:
kmem_cache_free(ecryptfs_header_cache_2, page_virt);
goto out;
mntput(lower_mnt);
d_drop(ecryptfs_dentry);
out:
+ if (put_lower)
+ ecryptfs_put_lower_file(ecryptfs_dentry->d_inode);
return rc;
}
dget(lower_dentry);
rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry);
dput(lower_dentry);
- if (!rc)
- d_delete(lower_dentry);
fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);
dir->i_nlink = lower_dir_dentry->d_inode->i_nlink;
unlock_dir(lower_dir_dentry);
fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode);
out_lock:
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
- dput(lower_new_dentry->d_parent);
- dput(lower_old_dentry->d_parent);
+ dput(lower_new_dir_dentry);
+ dput(lower_old_dir_dentry);
dput(lower_new_dentry);
dput(lower_old_dentry);
return rc;
if (unlikely((ia->ia_size == i_size))) {
lower_ia->ia_valid &= ~ATTR_SIZE;
- goto out;
+ return 0;
}
+ rc = ecryptfs_get_lower_file(dentry);
+ if (rc)
+ return rc;
crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat;
/* Switch on growing or shrinking file */
if (ia->ia_size > i_size) {
lower_ia->ia_valid &= ~ATTR_SIZE;
}
out:
+ ecryptfs_put_lower_file(inode);
return rc;
}
mount_crypt_stat = &ecryptfs_superblock_to_private(
dentry->d_sb)->mount_crypt_stat;
+ rc = ecryptfs_get_lower_file(dentry);
+ if (rc) {
+ mutex_unlock(&crypt_stat->cs_mutex);
+ goto out;
+ }
rc = ecryptfs_read_metadata(dentry);
+ ecryptfs_put_lower_file(inode);
if (rc) {
if (!(mount_crypt_stat->flags
& ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) {
goto out;
}
rc = 0;
- crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
+ crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
+ | ECRYPTFS_ENCRYPTED);
}
}
mutex_unlock(&crypt_stat->cs_mutex);
+ if (S_ISREG(inode->i_mode)) {
+ rc = filemap_write_and_wait(inode->i_mapping);
+ if (rc)
+ goto out;
+ fsstack_copy_attr_all(inode, lower_inode);
+ }
memcpy(&lower_ia, ia, sizeof(lower_ia));
if (ia->ia_valid & ATTR_FILE)
lower_ia.ia_file = ecryptfs_file_to_lower(ia->ia_file);
* @ignored: ignored
*
* The eCryptfs kernel thread that has the responsibility of getting
- * the lower persistent file with RW permissions.
+ * the lower file with RW permissions.
*
* Returns zero on success; non-zero otherwise
*/
int rc = 0;
/* Corresponding dput() and mntput() are done when the
- * persistent file is fput() when the eCryptfs inode is
- * destroyed. */
+ * lower file is fput() when all eCryptfs files for the inode are
+ * released. */
dget(lower_dentry);
mntget(lower_mnt);
flags |= IS_RDONLY(lower_dentry->d_inode) ? O_RDONLY : O_RDWR;
}
/**
- * ecryptfs_init_persistent_file
+ * ecryptfs_init_lower_file
* @ecryptfs_dentry: Fully initialized eCryptfs dentry object, with
* the lower dentry and the lower mount set
*
* inode. All I/O operations to the lower inode occur through that
* file. When the first eCryptfs dentry that interposes with the first
* lower dentry for that inode is created, this function creates the
- * persistent file struct and associates it with the eCryptfs
- * inode. When the eCryptfs inode is destroyed, the file is closed.
+ * lower file struct and associates it with the eCryptfs
+ * inode. When all eCryptfs files associated with the inode are released, the
+ * file is closed.
*
- * The persistent file will be opened with read/write permissions, if
+ * The lower file will be opened with read/write permissions, if
* possible. Otherwise, it is opened read-only.
*
- * This function does nothing if a lower persistent file is already
+ * This function does nothing if a lower file is already
* associated with the eCryptfs inode.
*
* Returns zero on success; non-zero otherwise
*/
-int ecryptfs_init_persistent_file(struct dentry *ecryptfs_dentry)
+static int ecryptfs_init_lower_file(struct dentry *dentry,
+ struct file **lower_file)
{
const struct cred *cred = current_cred();
- struct ecryptfs_inode_info *inode_info =
- ecryptfs_inode_to_private(ecryptfs_dentry->d_inode);
- int rc = 0;
+ struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
+ struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
+ int rc;
- if (!inode_info->lower_file) {
- struct dentry *lower_dentry;
- struct vfsmount *lower_mnt =
- ecryptfs_dentry_to_lower_mnt(ecryptfs_dentry);
+ rc = ecryptfs_privileged_open(lower_file, lower_dentry, lower_mnt,
+ cred);
+ if (rc) {
+ printk(KERN_ERR "Error opening lower file "
+ "for lower_dentry [0x%p] and lower_mnt [0x%p]; "
+ "rc = [%d]\n", lower_dentry, lower_mnt, rc);
+ (*lower_file) = NULL;
+ }
+ return rc;
+}
- lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
- rc = ecryptfs_privileged_open(&inode_info->lower_file,
- lower_dentry, lower_mnt, cred);
- if (rc) {
- printk(KERN_ERR "Error opening lower persistent file "
- "for lower_dentry [0x%p] and lower_mnt [0x%p]; "
- "rc = [%d]\n", lower_dentry, lower_mnt, rc);
- inode_info->lower_file = NULL;
- }
+int ecryptfs_get_lower_file(struct dentry *dentry)
+{
+ struct ecryptfs_inode_info *inode_info =
+ ecryptfs_inode_to_private(dentry->d_inode);
+ int count, rc = 0;
+
+ mutex_lock(&inode_info->lower_file_mutex);
+ count = atomic_inc_return(&inode_info->lower_file_count);
+ if (WARN_ON_ONCE(count < 1))
+ rc = -EINVAL;
+ else if (count == 1) {
+ rc = ecryptfs_init_lower_file(dentry,
+ &inode_info->lower_file);
+ if (rc)
+ atomic_set(&inode_info->lower_file_count, 0);
}
+ mutex_unlock(&inode_info->lower_file_mutex);
return rc;
}
+void ecryptfs_put_lower_file(struct inode *inode)
+{
+ struct ecryptfs_inode_info *inode_info;
+
+ inode_info = ecryptfs_inode_to_private(inode);
+ if (atomic_dec_and_mutex_lock(&inode_info->lower_file_count,
+ &inode_info->lower_file_mutex)) {
+ fput(inode_info->lower_file);
+ inode_info->lower_file = NULL;
+ mutex_unlock(&inode_info->lower_file_mutex);
+ }
+}
+
static struct inode *ecryptfs_get_inode(struct inode *lower_inode,
struct super_block *sb)
{
if (unlikely(!inode_info))
goto out;
ecryptfs_init_crypt_stat(&inode_info->crypt_stat);
+ mutex_init(&inode_info->lower_file_mutex);
+ atomic_set(&inode_info->lower_file_count, 0);
inode_info->lower_file = NULL;
inode = &inode_info->vfs_inode;
out:
*
* This is used during the final destruction of the inode. All
* allocation of memory related to the inode, including allocated
- * memory in the crypt_stat struct, will be released here. This
- * function also fput()'s the persistent file for the lower inode.
+ * memory in the crypt_stat struct, will be released here.
* There should be no chance that this deallocation will be missed.
*/
static void ecryptfs_destroy_inode(struct inode *inode)
struct ecryptfs_inode_info *inode_info;
inode_info = ecryptfs_inode_to_private(inode);
- if (inode_info->lower_file) {
- struct dentry *lower_dentry =
- inode_info->lower_file->f_dentry;
-
- BUG_ON(!lower_dentry);
- if (lower_dentry->d_inode) {
- fput(inode_info->lower_file);
- inode_info->lower_file = NULL;
- }
- }
+ BUG_ON(inode_info->lower_file);
ecryptfs_destroy_crypt_stat(&inode_info->crypt_stat);
call_rcu(&inode->i_rcu, ecryptfs_i_callback);
}
#include <linux/exportfs.h>
#include <linux/fs_struct.h>
#include <linux/fsnotify.h>
+#include <linux/personality.h>
#include <asm/uaccess.h>
#include "internal.h"
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mm.h>
+#include <linux/mmzone.h>
#include <linux/time.h>
#include <linux/sched.h>
#include <linux/slab.h>
*/
static DEFINE_PER_CPU(struct fdtable_defer, fdtable_defer_list);
-static inline void *alloc_fdmem(unsigned int size)
+static void *alloc_fdmem(unsigned int size)
{
- void *data;
-
- data = kmalloc(size, GFP_KERNEL|__GFP_NOWARN);
- if (data != NULL)
- return data;
-
+ /*
+ * Very large allocations can stress page reclaim, so fall back to
+ * vmalloc() if the allocation size will be considered "large" by the VM.
+ */
+ if (size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER)) {
+ void *data = kmalloc(size, GFP_KERNEL|__GFP_NOWARN);
+ if (data != NULL)
+ return data;
+ }
return vmalloc(size);
}
*tmp = fs->next;
fs->next = NULL;
write_unlock(&file_systems_lock);
+ synchronize_rcu();
return 0;
}
tmp = &(*tmp)->next;
}
write_unlock(&file_systems_lock);
- synchronize_rcu();
-
return -EINVAL;
}
}
brelse(dibh);
- gfs2_trans_end(sdp);
failed:
+ gfs2_trans_end(sdp);
if (al) {
gfs2_inplace_release(ip);
gfs2_quota_unlock(ip);
inode = gfs2_inode_lookup(dir->i_sb,
be16_to_cpu(dent->de_type),
be64_to_cpu(dent->de_inum.no_addr),
- be64_to_cpu(dent->de_inum.no_formal_ino));
+ be64_to_cpu(dent->de_inum.no_formal_ino), 0);
brelse(bh);
return inode;
}
return generic_file_aio_write(iocb, iov, nr_segs, pos);
}
-static void empty_write_end(struct page *page, unsigned from,
- unsigned to)
+static int empty_write_end(struct page *page, unsigned from,
+ unsigned to, int mode)
{
- struct gfs2_inode *ip = GFS2_I(page->mapping->host);
+ struct inode *inode = page->mapping->host;
+ struct gfs2_inode *ip = GFS2_I(inode);
+ struct buffer_head *bh;
+ unsigned offset, blksize = 1 << inode->i_blkbits;
+ pgoff_t end_index = i_size_read(inode) >> PAGE_CACHE_SHIFT;
zero_user(page, from, to-from);
mark_page_accessed(page);
- if (!gfs2_is_writeback(ip))
- gfs2_page_add_databufs(ip, page, from, to);
+ if (page->index < end_index || !(mode & FALLOC_FL_KEEP_SIZE)) {
+ if (!gfs2_is_writeback(ip))
+ gfs2_page_add_databufs(ip, page, from, to);
+
+ block_commit_write(page, from, to);
+ return 0;
+ }
+
+ offset = 0;
+ bh = page_buffers(page);
+ while (offset < to) {
+ if (offset >= from) {
+ set_buffer_uptodate(bh);
+ mark_buffer_dirty(bh);
+ clear_buffer_new(bh);
+ write_dirty_buffer(bh, WRITE);
+ }
+ offset += blksize;
+ bh = bh->b_this_page;
+ }
- block_commit_write(page, from, to);
+ offset = 0;
+ bh = page_buffers(page);
+ while (offset < to) {
+ if (offset >= from) {
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh))
+ return -EIO;
+ }
+ offset += blksize;
+ bh = bh->b_this_page;
+ }
+ return 0;
}
static int needs_empty_write(sector_t block, struct inode *inode)
return !buffer_mapped(&bh_map);
}
-static int write_empty_blocks(struct page *page, unsigned from, unsigned to)
+static int write_empty_blocks(struct page *page, unsigned from, unsigned to,
+ int mode)
{
struct inode *inode = page->mapping->host;
unsigned start, end, next, blksize;
gfs2_block_map);
if (unlikely(ret))
return ret;
- empty_write_end(page, start, end);
+ ret = empty_write_end(page, start, end, mode);
+ if (unlikely(ret))
+ return ret;
end = 0;
}
start = next;
ret = __block_write_begin(page, start, end - start, gfs2_block_map);
if (unlikely(ret))
return ret;
- empty_write_end(page, start, end);
+ ret = empty_write_end(page, start, end, mode);
+ if (unlikely(ret))
+ return ret;
}
return 0;
if (curr == end)
to = end_offset;
- error = write_empty_blocks(page, from, to);
+ error = write_empty_blocks(page, from, to, mode);
if (!error && offset + to > inode->i_size &&
!(mode & FALLOC_FL_KEEP_SIZE)) {
i_size_write(inode, offset + to);
static inline void spin_lock_bucket(unsigned int hash)
{
- struct hlist_bl_head *bl = &gl_hash_table[hash];
- bit_spin_lock(0, (unsigned long *)bl);
+ hlist_bl_lock(&gl_hash_table[hash]);
}
static inline void spin_unlock_bucket(unsigned int hash)
{
- struct hlist_bl_head *bl = &gl_hash_table[hash];
- __bit_spin_unlock(0, (unsigned long *)bl);
+ hlist_bl_unlock(&gl_hash_table[hash]);
}
static void gfs2_glock_dealloc(struct rcu_head *rcu)
static void iopen_go_callback(struct gfs2_glock *gl)
{
struct gfs2_inode *ip = (struct gfs2_inode *)gl->gl_object;
+ struct gfs2_sbd *sdp = gl->gl_sbd;
+
+ if (sdp->sd_vfs->s_flags & MS_RDONLY)
+ return;
if (gl->gl_demote_state == LM_ST_UNLOCKED &&
gl->gl_state == LM_ST_SHARED && ip) {
u64 ir_length;
};
+struct gfs2_skip_data {
+ u64 no_addr;
+ int skipped;
+ int non_block;
+};
+
static int iget_test(struct inode *inode, void *opaque)
{
struct gfs2_inode *ip = GFS2_I(inode);
- u64 *no_addr = opaque;
+ struct gfs2_skip_data *data = opaque;
- if (ip->i_no_addr == *no_addr)
+ if (ip->i_no_addr == data->no_addr) {
+ if (data->non_block &&
+ inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) {
+ data->skipped = 1;
+ return 0;
+ }
return 1;
-
+ }
return 0;
}
static int iget_set(struct inode *inode, void *opaque)
{
struct gfs2_inode *ip = GFS2_I(inode);
- u64 *no_addr = opaque;
+ struct gfs2_skip_data *data = opaque;
- inode->i_ino = (unsigned long)*no_addr;
- ip->i_no_addr = *no_addr;
+ if (data->skipped)
+ return -ENOENT;
+ inode->i_ino = (unsigned long)(data->no_addr);
+ ip->i_no_addr = data->no_addr;
return 0;
}
struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr)
{
unsigned long hash = (unsigned long)no_addr;
- return ilookup5(sb, hash, iget_test, &no_addr);
+ struct gfs2_skip_data data;
+
+ data.no_addr = no_addr;
+ data.skipped = 0;
+ data.non_block = 0;
+ return ilookup5(sb, hash, iget_test, &data);
}
-static struct inode *gfs2_iget(struct super_block *sb, u64 no_addr)
+static struct inode *gfs2_iget(struct super_block *sb, u64 no_addr,
+ int non_block)
{
+ struct gfs2_skip_data data;
unsigned long hash = (unsigned long)no_addr;
- return iget5_locked(sb, hash, iget_test, iget_set, &no_addr);
+
+ data.no_addr = no_addr;
+ data.skipped = 0;
+ data.non_block = non_block;
+ return iget5_locked(sb, hash, iget_test, iget_set, &data);
}
/**
* @sb: The super block
* @no_addr: The inode number
* @type: The type of the inode
+ * non_block: Can we block on inodes that are being freed?
*
* Returns: A VFS inode, or an error
*/
struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type,
- u64 no_addr, u64 no_formal_ino)
+ u64 no_addr, u64 no_formal_ino, int non_block)
{
struct inode *inode;
struct gfs2_inode *ip;
struct gfs2_glock *io_gl = NULL;
int error;
- inode = gfs2_iget(sb, no_addr);
+ inode = gfs2_iget(sb, no_addr, non_block);
ip = GFS2_I(inode);
if (!inode)
{
struct super_block *sb = sdp->sd_vfs;
struct gfs2_holder i_gh;
- struct inode *inode;
+ struct inode *inode = NULL;
int error;
+ /* Must not read in block until block type is verified */
error = gfs2_glock_nq_num(sdp, no_addr, &gfs2_inode_glops,
- LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
+ LM_ST_EXCLUSIVE, GL_SKIP, &i_gh);
if (error)
return ERR_PTR(error);
if (error)
goto fail;
- inode = gfs2_inode_lookup(sb, DT_UNKNOWN, no_addr, 0);
+ inode = gfs2_inode_lookup(sb, DT_UNKNOWN, no_addr, 0, 1);
if (IS_ERR(inode))
goto fail;
goto fail_gunlock2;
inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode), inum.no_addr,
- inum.no_formal_ino);
+ inum.no_formal_ino, 0);
if (IS_ERR(inode))
goto fail_gunlock2;
}
extern struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type,
- u64 no_addr, u64 no_formal_ino);
+ u64 no_addr, u64 no_formal_ino,
+ int non_block);
extern struct inode *gfs2_lookup_by_inum(struct gfs2_sbd *sdp, u64 no_addr,
u64 *no_formal_ino,
unsigned int blktype);
struct dentry *dentry;
struct inode *inode;
- inode = gfs2_inode_lookup(sb, DT_DIR, no_addr, 0);
+ inode = gfs2_inode_lookup(sb, DT_DIR, no_addr, 0, 0);
if (IS_ERR(inode)) {
fs_err(sdp, "can't read in %s inode: %ld\n", name, PTR_ERR(inode));
return PTR_ERR(inode);
/* rgblk_search can return a block < goal, so we need to
keep it marching forward. */
no_addr = block + rgd->rd_data0;
- goal++;
+ goal = max(block + 1, goal + 1);
if (*last_unlinked != NO_BLOCK && no_addr <= *last_unlinked)
continue;
if (no_addr == skip)
found++;
/* Limit reclaim to sensible number of tasks */
- if (found > 2*NR_CPUS)
+ if (found > NR_CPUS)
return;
}
static void gfs2_evict_inode(struct inode *inode)
{
- struct gfs2_sbd *sdp = inode->i_sb->s_fs_info;
+ struct super_block *sb = inode->i_sb;
+ struct gfs2_sbd *sdp = sb->s_fs_info;
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
int error;
- if (inode->i_nlink)
+ if (inode->i_nlink || (sb->s_flags & MS_RDONLY))
goto out;
- error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
+ /* Must not read inode block until block type has been verified */
+ error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, GL_SKIP, &gh);
if (unlikely(error)) {
gfs2_glock_dq_uninit(&ip->i_iopen_gh);
goto out;
if (error)
goto out_truncate;
+ if (test_bit(GIF_INVALID, &ip->i_flags)) {
+ error = gfs2_inode_refresh(ip);
+ if (error)
+ goto out_truncate;
+ }
+
ip->i_iopen_gh.gh_flags |= GL_NOCACHE;
gfs2_glock_dq_wait(&ip->i_iopen_gh);
gfs2_holder_reinit(LM_ST_EXCLUSIVE, LM_FLAG_TRY_1CB | GL_NOCACHE, &ip->i_iopen_gh);
config HPFS_FS
tristate "OS/2 HPFS file system support"
depends on BLOCK
- depends on BROKEN || !PREEMPT
help
OS/2 is IBM's operating system for PC's, the same as Warp, and HPFS
is the file system used for organizing files on OS/2 hard disk
#include "hpfs_fn.h"
-static int hpfs_alloc_if_possible_nolock(struct super_block *s, secno sec);
-
/*
* Check if a sector is allocated in bitmap
* This is really slow. Turned on only if chk==2
static int chk_if_allocated(struct super_block *s, secno sec, char *msg)
{
struct quad_buffer_head qbh;
- unsigned *bmp;
+ u32 *bmp;
if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "chk"))) goto fail;
- if ((bmp[(sec & 0x3fff) >> 5] >> (sec & 0x1f)) & 1) {
+ if ((cpu_to_le32(bmp[(sec & 0x3fff) >> 5]) >> (sec & 0x1f)) & 1) {
hpfs_error(s, "sector '%s' - %08x not allocated in bitmap", msg, sec);
goto fail1;
}
if (sec >= hpfs_sb(s)->sb_dirband_start && sec < hpfs_sb(s)->sb_dirband_start + hpfs_sb(s)->sb_dirband_size) {
unsigned ssec = (sec - hpfs_sb(s)->sb_dirband_start) / 4;
if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) goto fail;
- if ((bmp[ssec >> 5] >> (ssec & 0x1f)) & 1) {
+ if ((le32_to_cpu(bmp[ssec >> 5]) >> (ssec & 0x1f)) & 1) {
hpfs_error(s, "sector '%s' - %08x not allocated in directory bitmap", msg, sec);
goto fail1;
}
hpfs_error(s, "Bad allocation size: %d", n);
return 0;
}
- lock_super(s);
if (bs != ~0x3fff) {
if (!(bmp = hpfs_map_bitmap(s, near >> 14, &qbh, "aib"))) goto uls;
} else {
ret = bs + nr;
goto rt;
}
- /*if (!tstbits(bmp, nr + n, n + forward)) {
- ret = bs + nr + n;
- goto rt;
- }*/
q = nr + n; b = 0;
while ((a = tstbits(bmp, q, n + forward)) != 0) {
q += a;
goto rt;
}
nr >>= 5;
- /*for (i = nr + 1; i != nr; i++, i &= 0x1ff) {*/
+ /*for (i = nr + 1; i != nr; i++, i &= 0x1ff) */
i = nr;
do {
- if (!bmp[i]) goto cont;
- if (n + forward >= 0x3f && bmp[i] != -1) goto cont;
+ if (!le32_to_cpu(bmp[i])) goto cont;
+ if (n + forward >= 0x3f && le32_to_cpu(bmp[i]) != 0xffffffff) goto cont;
q = i<<5;
if (i > 0) {
- unsigned k = bmp[i-1];
+ unsigned k = le32_to_cpu(bmp[i-1]);
while (k & 0x80000000) {
q--; k <<= 1;
}
} while (i != nr);
rt:
if (ret) {
- if (hpfs_sb(s)->sb_chk && ((ret >> 14) != (bs >> 14) || (bmp[(ret & 0x3fff) >> 5] | ~(((1 << n) - 1) << (ret & 0x1f))) != 0xffffffff)) {
+ if (hpfs_sb(s)->sb_chk && ((ret >> 14) != (bs >> 14) || (le32_to_cpu(bmp[(ret & 0x3fff) >> 5]) | ~(((1 << n) - 1) << (ret & 0x1f))) != 0xffffffff)) {
hpfs_error(s, "Allocation doesn't work! Wanted %d, allocated at %08x", n, ret);
ret = 0;
goto b;
}
- bmp[(ret & 0x3fff) >> 5] &= ~(((1 << n) - 1) << (ret & 0x1f));
+ bmp[(ret & 0x3fff) >> 5] &= cpu_to_le32(~(((1 << n) - 1) << (ret & 0x1f)));
hpfs_mark_4buffers_dirty(&qbh);
}
b:
hpfs_brelse4(&qbh);
uls:
- unlock_super(s);
return ret;
}
* sectors
*/
-secno hpfs_alloc_sector(struct super_block *s, secno near, unsigned n, int forward, int lock)
+secno hpfs_alloc_sector(struct super_block *s, secno near, unsigned n, int forward)
{
secno sec;
int i;
forward = -forward;
f_p = 1;
}
- if (lock) hpfs_lock_creation(s);
n_bmps = (sbi->sb_fs_size + 0x4000 - 1) >> 14;
if (near && near < sbi->sb_fs_size) {
if ((sec = alloc_in_bmp(s, near, n, f_p ? forward : forward/4))) goto ret;
ret:
if (sec && f_p) {
for (i = 0; i < forward; i++) {
- if (!hpfs_alloc_if_possible_nolock(s, sec + i + 1)) {
+ if (!hpfs_alloc_if_possible(s, sec + i + 1)) {
hpfs_error(s, "Prealloc doesn't work! Wanted %d, allocated at %08x, can't allocate %d", forward, sec, i);
sec = 0;
break;
}
}
}
- if (lock) hpfs_unlock_creation(s);
return sec;
}
-static secno alloc_in_dirband(struct super_block *s, secno near, int lock)
+static secno alloc_in_dirband(struct super_block *s, secno near)
{
unsigned nr = near;
secno sec;
nr = sbi->sb_dirband_start + sbi->sb_dirband_size - 4;
nr -= sbi->sb_dirband_start;
nr >>= 2;
- if (lock) hpfs_lock_creation(s);
sec = alloc_in_bmp(s, (~0x3fff) | nr, 1, 0);
- if (lock) hpfs_unlock_creation(s);
if (!sec) return 0;
return ((sec & 0x3fff) << 2) + sbi->sb_dirband_start;
}
/* Alloc sector if it's free */
-static int hpfs_alloc_if_possible_nolock(struct super_block *s, secno sec)
+int hpfs_alloc_if_possible(struct super_block *s, secno sec)
{
struct quad_buffer_head qbh;
- unsigned *bmp;
- lock_super(s);
+ u32 *bmp;
if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "aip"))) goto end;
- if (bmp[(sec & 0x3fff) >> 5] & (1 << (sec & 0x1f))) {
- bmp[(sec & 0x3fff) >> 5] &= ~(1 << (sec & 0x1f));
+ if (le32_to_cpu(bmp[(sec & 0x3fff) >> 5]) & (1 << (sec & 0x1f))) {
+ bmp[(sec & 0x3fff) >> 5] &= cpu_to_le32(~(1 << (sec & 0x1f)));
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
- unlock_super(s);
return 1;
}
hpfs_brelse4(&qbh);
end:
- unlock_super(s);
return 0;
}
-int hpfs_alloc_if_possible(struct super_block *s, secno sec)
-{
- int r;
- hpfs_lock_creation(s);
- r = hpfs_alloc_if_possible_nolock(s, sec);
- hpfs_unlock_creation(s);
- return r;
-}
-
/* Free sectors in bitmaps */
void hpfs_free_sectors(struct super_block *s, secno sec, unsigned n)
{
struct quad_buffer_head qbh;
- unsigned *bmp;
+ u32 *bmp;
struct hpfs_sb_info *sbi = hpfs_sb(s);
/*printk("2 - ");*/
if (!n) return;
hpfs_error(s, "Trying to free reserved sector %08x", sec);
return;
}
- lock_super(s);
sbi->sb_max_fwd_alloc += n > 0xffff ? 0xffff : n;
if (sbi->sb_max_fwd_alloc > 0xffffff) sbi->sb_max_fwd_alloc = 0xffffff;
new_map:
if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "free"))) {
- unlock_super(s);
return;
}
new_tst:
- if ((bmp[(sec & 0x3fff) >> 5] >> (sec & 0x1f) & 1)) {
+ if ((le32_to_cpu(bmp[(sec & 0x3fff) >> 5]) >> (sec & 0x1f) & 1)) {
hpfs_error(s, "sector %08x not allocated", sec);
hpfs_brelse4(&qbh);
- unlock_super(s);
return;
}
- bmp[(sec & 0x3fff) >> 5] |= 1 << (sec & 0x1f);
+ bmp[(sec & 0x3fff) >> 5] |= cpu_to_le32(1 << (sec & 0x1f));
if (!--n) {
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
- unlock_super(s);
return;
}
if (!(++sec & 0x3fff)) {
int n_bmps = (hpfs_sb(s)->sb_fs_size + 0x4000 - 1) >> 14;
int b = hpfs_sb(s)->sb_c_bitmap & 0x0fffffff;
int i, j;
- unsigned *bmp;
+ u32 *bmp;
struct quad_buffer_head qbh;
if ((bmp = hpfs_map_dnode_bitmap(s, &qbh))) {
for (j = 0; j < 512; j++) {
unsigned k;
- if (!bmp[j]) continue;
- for (k = bmp[j]; k; k >>= 1) if (k & 1) if (!--n) {
+ if (!le32_to_cpu(bmp[j])) continue;
+ for (k = le32_to_cpu(bmp[j]); k; k >>= 1) if (k & 1) if (!--n) {
hpfs_brelse4(&qbh);
return 0;
}
chk_bmp:
if (bmp) {
for (j = 0; j < 512; j++) {
- unsigned k;
- if (!bmp[j]) continue;
+ u32 k;
+ if (!le32_to_cpu(bmp[j])) continue;
for (k = 0xf; k; k <<= 4)
- if ((bmp[j] & k) == k) {
+ if ((le32_to_cpu(bmp[j]) & k) == k) {
if (!--n) {
hpfs_brelse4(&qbh);
return 0;
hpfs_free_sectors(s, dno, 4);
} else {
struct quad_buffer_head qbh;
- unsigned *bmp;
+ u32 *bmp;
unsigned ssec = (dno - hpfs_sb(s)->sb_dirband_start) / 4;
- lock_super(s);
if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) {
- unlock_super(s);
return;
}
- bmp[ssec >> 5] |= 1 << (ssec & 0x1f);
+ bmp[ssec >> 5] |= cpu_to_le32(1 << (ssec & 0x1f));
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
- unlock_super(s);
}
}
struct dnode *hpfs_alloc_dnode(struct super_block *s, secno near,
- dnode_secno *dno, struct quad_buffer_head *qbh,
- int lock)
+ dnode_secno *dno, struct quad_buffer_head *qbh)
{
struct dnode *d;
if (hpfs_count_one_bitmap(s, hpfs_sb(s)->sb_dmap) > FREE_DNODES_ADD) {
- if (!(*dno = alloc_in_dirband(s, near, lock)))
- if (!(*dno = hpfs_alloc_sector(s, near, 4, 0, lock))) return NULL;
+ if (!(*dno = alloc_in_dirband(s, near)))
+ if (!(*dno = hpfs_alloc_sector(s, near, 4, 0))) return NULL;
} else {
- if (!(*dno = hpfs_alloc_sector(s, near, 4, 0, lock)))
- if (!(*dno = alloc_in_dirband(s, near, lock))) return NULL;
+ if (!(*dno = hpfs_alloc_sector(s, near, 4, 0)))
+ if (!(*dno = alloc_in_dirband(s, near))) return NULL;
}
if (!(d = hpfs_get_4sectors(s, *dno, qbh))) {
hpfs_free_dnode(s, *dno);
return NULL;
}
memset(d, 0, 2048);
- d->magic = DNODE_MAGIC;
- d->first_free = 52;
+ d->magic = cpu_to_le32(DNODE_MAGIC);
+ d->first_free = cpu_to_le32(52);
d->dirent[0] = 32;
d->dirent[2] = 8;
d->dirent[30] = 1;
d->dirent[31] = 255;
- d->self = *dno;
+ d->self = cpu_to_le32(*dno);
return d;
}
struct buffer_head **bh)
{
struct fnode *f;
- if (!(*fno = hpfs_alloc_sector(s, near, 1, FNODE_ALLOC_FWD, 1))) return NULL;
+ if (!(*fno = hpfs_alloc_sector(s, near, 1, FNODE_ALLOC_FWD))) return NULL;
if (!(f = hpfs_get_sector(s, *fno, bh))) {
hpfs_free_sectors(s, *fno, 1);
return NULL;
}
memset(f, 0, 512);
- f->magic = FNODE_MAGIC;
- f->ea_offs = 0xc4;
+ f->magic = cpu_to_le32(FNODE_MAGIC);
+ f->ea_offs = cpu_to_le16(0xc4);
f->btree.n_free_nodes = 8;
- f->btree.first_free = 8;
+ f->btree.first_free = cpu_to_le16(8);
return f;
}
struct buffer_head **bh)
{
struct anode *a;
- if (!(*ano = hpfs_alloc_sector(s, near, 1, ANODE_ALLOC_FWD, 1))) return NULL;
+ if (!(*ano = hpfs_alloc_sector(s, near, 1, ANODE_ALLOC_FWD))) return NULL;
if (!(a = hpfs_get_sector(s, *ano, bh))) {
hpfs_free_sectors(s, *ano, 1);
return NULL;
}
memset(a, 0, 512);
- a->magic = ANODE_MAGIC;
- a->self = *ano;
+ a->magic = cpu_to_le32(ANODE_MAGIC);
+ a->self = cpu_to_le32(*ano);
a->btree.n_free_nodes = 40;
a->btree.n_used_nodes = 0;
- a->btree.first_free = 8;
+ a->btree.first_free = cpu_to_le16(8);
return a;
}
if (hpfs_sb(s)->sb_chk) if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_bplus_lookup")) return -1;
if (btree->internal) {
for (i = 0; i < btree->n_used_nodes; i++)
- if (btree->u.internal[i].file_secno > sec) {
- a = btree->u.internal[i].down;
+ if (le32_to_cpu(btree->u.internal[i].file_secno) > sec) {
+ a = le32_to_cpu(btree->u.internal[i].down);
brelse(bh);
if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
btree = &anode->btree;
return -1;
}
for (i = 0; i < btree->n_used_nodes; i++)
- if (btree->u.external[i].file_secno <= sec &&
- btree->u.external[i].file_secno + btree->u.external[i].length > sec) {
- a = btree->u.external[i].disk_secno + sec - btree->u.external[i].file_secno;
+ if (le32_to_cpu(btree->u.external[i].file_secno) <= sec &&
+ le32_to_cpu(btree->u.external[i].file_secno) + le32_to_cpu(btree->u.external[i].length) > sec) {
+ a = le32_to_cpu(btree->u.external[i].disk_secno) + sec - le32_to_cpu(btree->u.external[i].file_secno);
if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, a, 1, "data")) {
brelse(bh);
return -1;
}
if (inode) {
struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
- hpfs_inode->i_file_sec = btree->u.external[i].file_secno;
- hpfs_inode->i_disk_sec = btree->u.external[i].disk_secno;
- hpfs_inode->i_n_secs = btree->u.external[i].length;
+ hpfs_inode->i_file_sec = le32_to_cpu(btree->u.external[i].file_secno);
+ hpfs_inode->i_disk_sec = le32_to_cpu(btree->u.external[i].disk_secno);
+ hpfs_inode->i_n_secs = le32_to_cpu(btree->u.external[i].length);
}
brelse(bh);
return a;
return -1;
}
if (btree->internal) {
- a = btree->u.internal[n].down;
- btree->u.internal[n].file_secno = -1;
+ a = le32_to_cpu(btree->u.internal[n].down);
+ btree->u.internal[n].file_secno = cpu_to_le32(-1);
mark_buffer_dirty(bh);
brelse(bh);
if (hpfs_sb(s)->sb_chk)
goto go_down;
}
if (n >= 0) {
- if (btree->u.external[n].file_secno + btree->u.external[n].length != fsecno) {
+ if (le32_to_cpu(btree->u.external[n].file_secno) + le32_to_cpu(btree->u.external[n].length) != fsecno) {
hpfs_error(s, "allocated size %08x, trying to add sector %08x, %cnode %08x",
- btree->u.external[n].file_secno + btree->u.external[n].length, fsecno,
+ le32_to_cpu(btree->u.external[n].file_secno) + le32_to_cpu(btree->u.external[n].length), fsecno,
fnod?'f':'a', node);
brelse(bh);
return -1;
}
- if (hpfs_alloc_if_possible(s, se = btree->u.external[n].disk_secno + btree->u.external[n].length)) {
- btree->u.external[n].length++;
+ if (hpfs_alloc_if_possible(s, se = le32_to_cpu(btree->u.external[n].disk_secno) + le32_to_cpu(btree->u.external[n].length))) {
+ btree->u.external[n].length = cpu_to_le32(le32_to_cpu(btree->u.external[n].length) + 1);
mark_buffer_dirty(bh);
brelse(bh);
return se;
}
se = !fnod ? node : (node + 16384) & ~16383;
}
- if (!(se = hpfs_alloc_sector(s, se, 1, fsecno*ALLOC_M>ALLOC_FWD_MAX ? ALLOC_FWD_MAX : fsecno*ALLOC_M<ALLOC_FWD_MIN ? ALLOC_FWD_MIN : fsecno*ALLOC_M, 1))) {
+ if (!(se = hpfs_alloc_sector(s, se, 1, fsecno*ALLOC_M>ALLOC_FWD_MAX ? ALLOC_FWD_MAX : fsecno*ALLOC_M<ALLOC_FWD_MIN ? ALLOC_FWD_MIN : fsecno*ALLOC_M))) {
brelse(bh);
return -1;
}
- fs = n < 0 ? 0 : btree->u.external[n].file_secno + btree->u.external[n].length;
+ fs = n < 0 ? 0 : le32_to_cpu(btree->u.external[n].file_secno) + le32_to_cpu(btree->u.external[n].length);
if (!btree->n_free_nodes) {
- up = a != node ? anode->up : -1;
+ up = a != node ? le32_to_cpu(anode->up) : -1;
if (!(anode = hpfs_alloc_anode(s, a, &na, &bh1))) {
brelse(bh);
hpfs_free_sectors(s, se, 1);
return -1;
}
if (a == node && fnod) {
- anode->up = node;
+ anode->up = cpu_to_le32(node);
anode->btree.fnode_parent = 1;
anode->btree.n_used_nodes = btree->n_used_nodes;
anode->btree.first_free = btree->first_free;
btree->internal = 1;
btree->n_free_nodes = 11;
btree->n_used_nodes = 1;
- btree->first_free = (char *)&(btree->u.internal[1]) - (char *)btree;
- btree->u.internal[0].file_secno = -1;
- btree->u.internal[0].down = na;
+ btree->first_free = cpu_to_le16((char *)&(btree->u.internal[1]) - (char *)btree);
+ btree->u.internal[0].file_secno = cpu_to_le32(-1);
+ btree->u.internal[0].down = cpu_to_le32(na);
mark_buffer_dirty(bh);
} else if (!(ranode = hpfs_alloc_anode(s, /*a*/0, &ra, &bh2))) {
brelse(bh);
btree = &anode->btree;
}
btree->n_free_nodes--; n = btree->n_used_nodes++;
- btree->first_free += 12;
- btree->u.external[n].disk_secno = se;
- btree->u.external[n].file_secno = fs;
- btree->u.external[n].length = 1;
+ btree->first_free = cpu_to_le16(le16_to_cpu(btree->first_free) + 12);
+ btree->u.external[n].disk_secno = cpu_to_le32(se);
+ btree->u.external[n].file_secno = cpu_to_le32(fs);
+ btree->u.external[n].length = cpu_to_le32(1);
mark_buffer_dirty(bh);
brelse(bh);
if ((a == node && fnod) || na == -1) return se;
c2 = 0;
- while (up != -1) {
+ while (up != (anode_secno)-1) {
struct anode *new_anode;
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, up, &c1, &c2, "hpfs_add_sector_to_btree #2")) return -1;
}
if (btree->n_free_nodes) {
btree->n_free_nodes--; n = btree->n_used_nodes++;
- btree->first_free += 8;
- btree->u.internal[n].file_secno = -1;
- btree->u.internal[n].down = na;
- btree->u.internal[n-1].file_secno = fs;
+ btree->first_free = cpu_to_le16(le16_to_cpu(btree->first_free) + 8);
+ btree->u.internal[n].file_secno = cpu_to_le32(-1);
+ btree->u.internal[n].down = cpu_to_le32(na);
+ btree->u.internal[n-1].file_secno = cpu_to_le32(fs);
mark_buffer_dirty(bh);
brelse(bh);
brelse(bh2);
hpfs_free_sectors(s, ra, 1);
if ((anode = hpfs_map_anode(s, na, &bh))) {
- anode->up = up;
+ anode->up = cpu_to_le32(up);
anode->btree.fnode_parent = up == node && fnod;
mark_buffer_dirty(bh);
brelse(bh);
}
return se;
}
- up = up != node ? anode->up : -1;
- btree->u.internal[btree->n_used_nodes - 1].file_secno = /*fs*/-1;
+ up = up != node ? le32_to_cpu(anode->up) : -1;
+ btree->u.internal[btree->n_used_nodes - 1].file_secno = cpu_to_le32(/*fs*/-1);
mark_buffer_dirty(bh);
brelse(bh);
a = na;
if ((new_anode = hpfs_alloc_anode(s, a, &na, &bh))) {
anode = new_anode;
- /*anode->up = up != -1 ? up : ra;*/
+ /*anode->up = cpu_to_le32(up != -1 ? up : ra);*/
anode->btree.internal = 1;
anode->btree.n_used_nodes = 1;
anode->btree.n_free_nodes = 59;
- anode->btree.first_free = 16;
- anode->btree.u.internal[0].down = a;
- anode->btree.u.internal[0].file_secno = -1;
+ anode->btree.first_free = cpu_to_le16(16);
+ anode->btree.u.internal[0].down = cpu_to_le32(a);
+ anode->btree.u.internal[0].file_secno = cpu_to_le32(-1);
mark_buffer_dirty(bh);
brelse(bh);
if ((anode = hpfs_map_anode(s, a, &bh))) {
- anode->up = na;
+ anode->up = cpu_to_le32(na);
mark_buffer_dirty(bh);
brelse(bh);
}
} else na = a;
}
if ((anode = hpfs_map_anode(s, na, &bh))) {
- anode->up = node;
+ anode->up = cpu_to_le32(node);
if (fnod) anode->btree.fnode_parent = 1;
mark_buffer_dirty(bh);
brelse(bh);
}
btree = &fnode->btree;
}
- ranode->up = node;
- memcpy(&ranode->btree, btree, btree->first_free);
+ ranode->up = cpu_to_le32(node);
+ memcpy(&ranode->btree, btree, le16_to_cpu(btree->first_free));
if (fnod) ranode->btree.fnode_parent = 1;
ranode->btree.n_free_nodes = (ranode->btree.internal ? 60 : 40) - ranode->btree.n_used_nodes;
if (ranode->btree.internal) for (n = 0; n < ranode->btree.n_used_nodes; n++) {
struct anode *unode;
- if ((unode = hpfs_map_anode(s, ranode->u.internal[n].down, &bh1))) {
- unode->up = ra;
+ if ((unode = hpfs_map_anode(s, le32_to_cpu(ranode->u.internal[n].down), &bh1))) {
+ unode->up = cpu_to_le32(ra);
unode->btree.fnode_parent = 0;
mark_buffer_dirty(bh1);
brelse(bh1);
btree->internal = 1;
btree->n_free_nodes = fnod ? 10 : 58;
btree->n_used_nodes = 2;
- btree->first_free = (char *)&btree->u.internal[2] - (char *)btree;
- btree->u.internal[0].file_secno = fs;
- btree->u.internal[0].down = ra;
- btree->u.internal[1].file_secno = -1;
- btree->u.internal[1].down = na;
+ btree->first_free = cpu_to_le16((char *)&btree->u.internal[2] - (char *)btree);
+ btree->u.internal[0].file_secno = cpu_to_le32(fs);
+ btree->u.internal[0].down = cpu_to_le32(ra);
+ btree->u.internal[1].file_secno = cpu_to_le32(-1);
+ btree->u.internal[1].down = cpu_to_le32(na);
mark_buffer_dirty(bh);
brelse(bh);
mark_buffer_dirty(bh2);
go_down:
d2 = 0;
while (btree1->internal) {
- ano = btree1->u.internal[pos].down;
+ ano = le32_to_cpu(btree1->u.internal[pos].down);
if (level) brelse(bh);
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, ano, &d1, &d2, "hpfs_remove_btree #1"))
pos = 0;
}
for (i = 0; i < btree1->n_used_nodes; i++)
- hpfs_free_sectors(s, btree1->u.external[i].disk_secno, btree1->u.external[i].length);
+ hpfs_free_sectors(s, le32_to_cpu(btree1->u.external[i].disk_secno), le32_to_cpu(btree1->u.external[i].length));
go_up:
if (!level) return;
brelse(bh);
if (hpfs_stop_cycles(s, ano, &c1, &c2, "hpfs_remove_btree #2")) return;
hpfs_free_sectors(s, ano, 1);
oano = ano;
- ano = anode->up;
+ ano = le32_to_cpu(anode->up);
if (--level) {
if (!(anode = hpfs_map_anode(s, ano, &bh))) return;
btree1 = &anode->btree;
} else btree1 = btree;
for (i = 0; i < btree1->n_used_nodes; i++) {
- if (btree1->u.internal[i].down == oano) {
+ if (le32_to_cpu(btree1->u.internal[i].down) == oano) {
if ((pos = i + 1) < btree1->n_used_nodes)
goto go_down;
else
if (fno) {
btree->n_free_nodes = 8;
btree->n_used_nodes = 0;
- btree->first_free = 8;
+ btree->first_free = cpu_to_le16(8);
btree->internal = 0;
mark_buffer_dirty(bh);
} else hpfs_free_sectors(s, f, 1);
while (btree->internal) {
nodes = btree->n_used_nodes + btree->n_free_nodes;
for (i = 0; i < btree->n_used_nodes; i++)
- if (btree->u.internal[i].file_secno >= secs) goto f;
+ if (le32_to_cpu(btree->u.internal[i].file_secno) >= secs) goto f;
brelse(bh);
hpfs_error(s, "internal btree %08x doesn't end with -1", node);
return;
f:
for (j = i + 1; j < btree->n_used_nodes; j++)
- hpfs_ea_remove(s, btree->u.internal[j].down, 1, 0);
+ hpfs_ea_remove(s, le32_to_cpu(btree->u.internal[j].down), 1, 0);
btree->n_used_nodes = i + 1;
btree->n_free_nodes = nodes - btree->n_used_nodes;
- btree->first_free = 8 + 8 * btree->n_used_nodes;
+ btree->first_free = cpu_to_le16(8 + 8 * btree->n_used_nodes);
mark_buffer_dirty(bh);
- if (btree->u.internal[i].file_secno == secs) {
+ if (btree->u.internal[i].file_secno == cpu_to_le32(secs)) {
brelse(bh);
return;
}
- node = btree->u.internal[i].down;
+ node = le32_to_cpu(btree->u.internal[i].down);
brelse(bh);
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, node, &c1, &c2, "hpfs_truncate_btree"))
}
nodes = btree->n_used_nodes + btree->n_free_nodes;
for (i = 0; i < btree->n_used_nodes; i++)
- if (btree->u.external[i].file_secno + btree->u.external[i].length >= secs) goto ff;
+ if (le32_to_cpu(btree->u.external[i].file_secno) + le32_to_cpu(btree->u.external[i].length) >= secs) goto ff;
brelse(bh);
return;
ff:
- if (secs <= btree->u.external[i].file_secno) {
+ if (secs <= le32_to_cpu(btree->u.external[i].file_secno)) {
hpfs_error(s, "there is an allocation error in file %08x, sector %08x", f, secs);
if (i) i--;
}
- else if (btree->u.external[i].file_secno + btree->u.external[i].length > secs) {
- hpfs_free_sectors(s, btree->u.external[i].disk_secno + secs -
- btree->u.external[i].file_secno, btree->u.external[i].length
- - secs + btree->u.external[i].file_secno); /* I hope gcc optimizes this :-) */
- btree->u.external[i].length = secs - btree->u.external[i].file_secno;
+ else if (le32_to_cpu(btree->u.external[i].file_secno) + le32_to_cpu(btree->u.external[i].length) > secs) {
+ hpfs_free_sectors(s, le32_to_cpu(btree->u.external[i].disk_secno) + secs -
+ le32_to_cpu(btree->u.external[i].file_secno), le32_to_cpu(btree->u.external[i].length)
+ - secs + le32_to_cpu(btree->u.external[i].file_secno)); /* I hope gcc optimizes this :-) */
+ btree->u.external[i].length = cpu_to_le32(secs - le32_to_cpu(btree->u.external[i].file_secno));
}
for (j = i + 1; j < btree->n_used_nodes; j++)
- hpfs_free_sectors(s, btree->u.external[j].disk_secno, btree->u.external[j].length);
+ hpfs_free_sectors(s, le32_to_cpu(btree->u.external[j].disk_secno), le32_to_cpu(btree->u.external[j].length));
btree->n_used_nodes = i + 1;
btree->n_free_nodes = nodes - btree->n_used_nodes;
- btree->first_free = 8 + 12 * btree->n_used_nodes;
+ btree->first_free = cpu_to_le16(8 + 12 * btree->n_used_nodes);
mark_buffer_dirty(bh);
brelse(bh);
}
struct extended_attribute *ea_end;
if (!(fnode = hpfs_map_fnode(s, fno, &bh))) return;
if (!fnode->dirflag) hpfs_remove_btree(s, &fnode->btree);
- else hpfs_remove_dtree(s, fnode->u.external[0].disk_secno);
+ else hpfs_remove_dtree(s, le32_to_cpu(fnode->u.external[0].disk_secno));
ea_end = fnode_end_ea(fnode);
for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
if (ea->indirect)
hpfs_ea_remove(s, ea_sec(ea), ea->anode, ea_len(ea));
- hpfs_ea_ext_remove(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l);
+ hpfs_ea_ext_remove(s, le32_to_cpu(fnode->ea_secno), fnode->ea_anode, le32_to_cpu(fnode->ea_size_l));
brelse(bh);
hpfs_free_sectors(s, fno, 1);
}
#include <linux/slab.h>
#include "hpfs_fn.h"
-void hpfs_lock_creation(struct super_block *s)
-{
-#ifdef DEBUG_LOCKS
- printk("lock creation\n");
-#endif
- mutex_lock(&hpfs_sb(s)->hpfs_creation_de);
-}
-
-void hpfs_unlock_creation(struct super_block *s)
-{
-#ifdef DEBUG_LOCKS
- printk("unlock creation\n");
-#endif
- mutex_unlock(&hpfs_sb(s)->hpfs_creation_de);
-}
-
/* Map a sector into a buffer and return pointers to it and to the buffer. */
void *hpfs_map_sector(struct super_block *s, unsigned secno, struct buffer_head **bhp,
{
struct buffer_head *bh;
+ hpfs_lock_assert(s);
+
cond_resched();
*bhp = bh = sb_bread(s, secno);
struct buffer_head *bh;
/*return hpfs_map_sector(s, secno, bhp, 0);*/
+ hpfs_lock_assert(s);
+
cond_resched();
if ((*bhp = bh = sb_getblk(s, secno)) != NULL) {
struct buffer_head *bh;
char *data;
+ hpfs_lock_assert(s);
+
cond_resched();
if (secno & 3) {
{
cond_resched();
+ hpfs_lock_assert(s);
+
if (secno & 3) {
printk("HPFS: hpfs_get_4sectors: unaligned read\n");
return NULL;
hpfs_error(inode->i_sb, "not a directory, fnode %08lx",
(unsigned long)inode->i_ino);
}
- if (hpfs_inode->i_dno != fno->u.external[0].disk_secno) {
+ if (hpfs_inode->i_dno != le32_to_cpu(fno->u.external[0].disk_secno)) {
e = 1;
- hpfs_error(inode->i_sb, "corrupted inode: i_dno == %08x, fnode -> dnode == %08x", hpfs_inode->i_dno, fno->u.external[0].disk_secno);
+ hpfs_error(inode->i_sb, "corrupted inode: i_dno == %08x, fnode -> dnode == %08x", hpfs_inode->i_dno, le32_to_cpu(fno->u.external[0].disk_secno));
}
brelse(bh);
if (e) {
goto again;
}
tempname = hpfs_translate_name(inode->i_sb, de->name, de->namelen, lc, de->not_8x3);
- if (filldir(dirent, tempname, de->namelen, old_pos, de->fnode, DT_UNKNOWN) < 0) {
+ if (filldir(dirent, tempname, de->namelen, old_pos, le32_to_cpu(de->fnode), DT_UNKNOWN) < 0) {
filp->f_pos = old_pos;
if (tempname != de->name) kfree(tempname);
hpfs_brelse4(&qbh);
* Get inode number, what we're after.
*/
- ino = de->fnode;
+ ino = le32_to_cpu(de->fnode);
/*
* Go find or make an inode.
hpfs_init_inode(result);
if (de->directory)
hpfs_read_inode(result);
- else if (de->ea_size && hpfs_sb(dir->i_sb)->sb_eas)
+ else if (le32_to_cpu(de->ea_size) && hpfs_sb(dir->i_sb)->sb_eas)
hpfs_read_inode(result);
else {
result->i_mode |= S_IFREG;
hpfs_result = hpfs_i(result);
if (!de->directory) hpfs_result->i_parent_dir = dir->i_ino;
- hpfs_decide_conv(result, name, len);
-
if (de->has_acl || de->has_xtd_perm) if (!(dir->i_sb->s_flags & MS_RDONLY)) {
hpfs_error(result->i_sb, "ACLs or XPERM found. This is probably HPFS386. This driver doesn't support it now. Send me some info on these structures");
goto bail1;
*/
if (!result->i_ctime.tv_sec) {
- if (!(result->i_ctime.tv_sec = local_to_gmt(dir->i_sb, de->creation_date)))
+ if (!(result->i_ctime.tv_sec = local_to_gmt(dir->i_sb, le32_to_cpu(de->creation_date))))
result->i_ctime.tv_sec = 1;
result->i_ctime.tv_nsec = 0;
- result->i_mtime.tv_sec = local_to_gmt(dir->i_sb, de->write_date);
+ result->i_mtime.tv_sec = local_to_gmt(dir->i_sb, le32_to_cpu(de->write_date));
result->i_mtime.tv_nsec = 0;
- result->i_atime.tv_sec = local_to_gmt(dir->i_sb, de->read_date);
+ result->i_atime.tv_sec = local_to_gmt(dir->i_sb, le32_to_cpu(de->read_date));
result->i_atime.tv_nsec = 0;
- hpfs_result->i_ea_size = de->ea_size;
+ hpfs_result->i_ea_size = le32_to_cpu(de->ea_size);
if (!hpfs_result->i_ea_mode && de->read_only)
result->i_mode &= ~0222;
if (!de->directory) {
if (result->i_size == -1) {
- result->i_size = de->file_size;
+ result->i_size = le32_to_cpu(de->file_size);
result->i_data.a_ops = &hpfs_aops;
hpfs_i(result)->mmu_private = result->i_size;
/*
struct hpfs_dirent *de_end = dnode_end_de(d);
int i = 1;
for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
- if (de == fde) return ((loff_t) d->self << 4) | (loff_t)i;
+ if (de == fde) return ((loff_t) le32_to_cpu(d->self) << 4) | (loff_t)i;
i++;
}
printk("HPFS: get_pos: not_found\n");
- return ((loff_t)d->self << 4) | (loff_t)1;
+ return ((loff_t)le32_to_cpu(d->self) << 4) | (loff_t)1;
}
void hpfs_add_pos(struct inode *inode, loff_t *pos)
{
struct hpfs_dirent *de;
if (!(de = dnode_last_de(d))) {
- hpfs_error(s, "set_last_pointer: empty dnode %08x", d->self);
+ hpfs_error(s, "set_last_pointer: empty dnode %08x", le32_to_cpu(d->self));
return;
}
if (hpfs_sb(s)->sb_chk) {
if (de->down) {
hpfs_error(s, "set_last_pointer: dnode %08x has already last pointer %08x",
- d->self, de_down_pointer(de));
+ le32_to_cpu(d->self), de_down_pointer(de));
return;
}
- if (de->length != 32) {
- hpfs_error(s, "set_last_pointer: bad last dirent in dnode %08x", d->self);
+ if (le16_to_cpu(de->length) != 32) {
+ hpfs_error(s, "set_last_pointer: bad last dirent in dnode %08x", le32_to_cpu(d->self));
return;
}
}
if (ptr) {
- if ((d->first_free += 4) > 2048) {
- hpfs_error(s,"set_last_pointer: too long dnode %08x", d->self);
- d->first_free -= 4;
+ d->first_free = cpu_to_le32(le32_to_cpu(d->first_free) + 4);
+ if (le32_to_cpu(d->first_free) > 2048) {
+ hpfs_error(s, "set_last_pointer: too long dnode %08x", le32_to_cpu(d->self));
+ d->first_free = cpu_to_le32(le32_to_cpu(d->first_free) - 4);
return;
}
- de->length = 36;
+ de->length = cpu_to_le16(36);
de->down = 1;
- *(dnode_secno *)((char *)de + 32) = ptr;
+ *(dnode_secno *)((char *)de + 32) = cpu_to_le32(ptr);
}
}
for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
int c = hpfs_compare_names(s, name, namelen, de->name, de->namelen, de->last);
if (!c) {
- hpfs_error(s, "name (%c,%d) already exists in dnode %08x", *name, namelen, d->self);
+ hpfs_error(s, "name (%c,%d) already exists in dnode %08x", *name, namelen, le32_to_cpu(d->self));
return NULL;
}
if (c < 0) break;
memmove((char *)de + d_size, de, (char *)de_end - (char *)de);
memset(de, 0, d_size);
if (down_ptr) {
- *(int *)((char *)de + d_size - 4) = down_ptr;
+ *(dnode_secno *)((char *)de + d_size - 4) = cpu_to_le32(down_ptr);
de->down = 1;
}
- de->length = d_size;
- if (down_ptr) de->down = 1;
+ de->length = cpu_to_le16(d_size);
de->not_8x3 = hpfs_is_name_long(name, namelen);
de->namelen = namelen;
memcpy(de->name, name, namelen);
- d->first_free += d_size;
+ d->first_free = cpu_to_le32(le32_to_cpu(d->first_free) + d_size);
return de;
}
struct hpfs_dirent *de)
{
if (de->last) {
- hpfs_error(s, "attempt to delete last dirent in dnode %08x", d->self);
+ hpfs_error(s, "attempt to delete last dirent in dnode %08x", le32_to_cpu(d->self));
return;
}
- d->first_free -= de->length;
- memmove(de, de_next_de(de), d->first_free + (char *)d - (char *)de);
+ d->first_free = cpu_to_le32(le32_to_cpu(d->first_free) - le16_to_cpu(de->length));
+ memmove(de, de_next_de(de), le32_to_cpu(d->first_free) + (char *)d - (char *)de);
}
static void fix_up_ptrs(struct super_block *s, struct dnode *d)
{
struct hpfs_dirent *de;
struct hpfs_dirent *de_end = dnode_end_de(d);
- dnode_secno dno = d->self;
+ dnode_secno dno = le32_to_cpu(d->self);
for (de = dnode_first_de(d); de < de_end; de = de_next_de(de))
if (de->down) {
struct quad_buffer_head qbh;
struct dnode *dd;
if ((dd = hpfs_map_dnode(s, de_down_pointer(de), &qbh))) {
- if (dd->up != dno || dd->root_dnode) {
- dd->up = dno;
+ if (le32_to_cpu(dd->up) != dno || dd->root_dnode) {
+ dd->up = cpu_to_le32(dno);
dd->root_dnode = 0;
hpfs_mark_4buffers_dirty(&qbh);
}
kfree(nname);
return 1;
}
- if (d->first_free + de_size(namelen, down_ptr) <= 2048) {
+ if (le32_to_cpu(d->first_free) + de_size(namelen, down_ptr) <= 2048) {
loff_t t;
copy_de(de=hpfs_add_de(i->i_sb, d, name, namelen, down_ptr), new_de);
t = get_pos(d, de);
kfree(nname);
return 1;
}
- memcpy(nd, d, d->first_free);
+ memcpy(nd, d, le32_to_cpu(d->first_free));
copy_de(de = hpfs_add_de(i->i_sb, nd, name, namelen, down_ptr), new_de);
for_all_poss(i, hpfs_pos_ins, get_pos(nd, de), 1);
h = ((char *)dnode_last_de(nd) - (char *)nd) / 2 + 10;
- if (!(ad = hpfs_alloc_dnode(i->i_sb, d->up, &adno, &qbh1, 0))) {
+ if (!(ad = hpfs_alloc_dnode(i->i_sb, le32_to_cpu(d->up), &adno, &qbh1))) {
hpfs_error(i->i_sb, "unable to alloc dnode - dnode tree will be corrupted");
hpfs_brelse4(&qbh);
kfree(nd);
down_ptr = adno;
set_last_pointer(i->i_sb, ad, de->down ? de_down_pointer(de) : 0);
de = de_next_de(de);
- memmove((char *)nd + 20, de, nd->first_free + (char *)nd - (char *)de);
- nd->first_free -= (char *)de - (char *)nd - 20;
- memcpy(d, nd, nd->first_free);
+ memmove((char *)nd + 20, de, le32_to_cpu(nd->first_free) + (char *)nd - (char *)de);
+ nd->first_free = cpu_to_le32(le32_to_cpu(nd->first_free) - ((char *)de - (char *)nd - 20));
+ memcpy(d, nd, le32_to_cpu(nd->first_free));
for_all_poss(i, hpfs_pos_del, (loff_t)dno << 4, pos);
fix_up_ptrs(i->i_sb, ad);
if (!d->root_dnode) {
- dno = ad->up = d->up;
+ ad->up = d->up;
+ dno = le32_to_cpu(ad->up);
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
hpfs_mark_4buffers_dirty(&qbh1);
hpfs_brelse4(&qbh1);
goto go_up;
}
- if (!(rd = hpfs_alloc_dnode(i->i_sb, d->up, &rdno, &qbh2, 0))) {
+ if (!(rd = hpfs_alloc_dnode(i->i_sb, le32_to_cpu(d->up), &rdno, &qbh2))) {
hpfs_error(i->i_sb, "unable to alloc dnode - dnode tree will be corrupted");
hpfs_brelse4(&qbh);
hpfs_brelse4(&qbh1);
i->i_blocks += 4;
rd->root_dnode = 1;
rd->up = d->up;
- if (!(fnode = hpfs_map_fnode(i->i_sb, d->up, &bh))) {
+ if (!(fnode = hpfs_map_fnode(i->i_sb, le32_to_cpu(d->up), &bh))) {
hpfs_free_dnode(i->i_sb, rdno);
hpfs_brelse4(&qbh);
hpfs_brelse4(&qbh1);
kfree(nname);
return 1;
}
- fnode->u.external[0].disk_secno = rdno;
+ fnode->u.external[0].disk_secno = cpu_to_le32(rdno);
mark_buffer_dirty(bh);
brelse(bh);
- d->up = ad->up = hpfs_i(i)->i_dno = rdno;
+ hpfs_i(i)->i_dno = rdno;
+ d->up = ad->up = cpu_to_le32(rdno);
d->root_dnode = ad->root_dnode = 0;
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
int hpfs_add_dirent(struct inode *i,
const unsigned char *name, unsigned namelen,
- struct hpfs_dirent *new_de, int cdepth)
+ struct hpfs_dirent *new_de)
{
struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
struct dnode *d;
}
}
hpfs_brelse4(&qbh);
- if (!cdepth) hpfs_lock_creation(i->i_sb);
if (hpfs_check_free_dnodes(i->i_sb, FREE_DNODES_ADD)) {
c = 1;
goto ret;
i->i_version++;
c = hpfs_add_to_dnode(i, dno, name, namelen, new_de, 0);
ret:
- if (!cdepth) hpfs_unlock_creation(i->i_sb);
return c;
}
return 0;
if (!(dnode = hpfs_map_dnode(i->i_sb, dno, &qbh))) return 0;
if (hpfs_sb(i->i_sb)->sb_chk) {
- if (dnode->up != chk_up) {
+ if (le32_to_cpu(dnode->up) != chk_up) {
hpfs_error(i->i_sb, "move_to_top: up pointer from %08x should be %08x, is %08x",
- dno, chk_up, dnode->up);
+ dno, chk_up, le32_to_cpu(dnode->up));
hpfs_brelse4(&qbh);
return 0;
}
hpfs_brelse4(&qbh);
}
while (!(de = dnode_pre_last_de(dnode))) {
- dnode_secno up = dnode->up;
+ dnode_secno up = le32_to_cpu(dnode->up);
hpfs_brelse4(&qbh);
hpfs_free_dnode(i->i_sb, dno);
i->i_size -= 2048;
hpfs_brelse4(&qbh);
return 0;
}
- dnode->first_free -= 4;
- de->length -= 4;
+ dnode->first_free = cpu_to_le32(le32_to_cpu(dnode->first_free) - 4);
+ de->length = cpu_to_le16(le16_to_cpu(de->length) - 4);
de->down = 0;
hpfs_mark_4buffers_dirty(&qbh);
dno = up;
t = get_pos(dnode, de);
for_all_poss(i, hpfs_pos_subst, t, 4);
for_all_poss(i, hpfs_pos_subst, t + 1, 5);
- if (!(nde = kmalloc(de->length, GFP_NOFS))) {
+ if (!(nde = kmalloc(le16_to_cpu(de->length), GFP_NOFS))) {
hpfs_error(i->i_sb, "out of memory for dirent - directory will be corrupted");
hpfs_brelse4(&qbh);
return 0;
}
- memcpy(nde, de, de->length);
+ memcpy(nde, de, le16_to_cpu(de->length));
ddno = de->down ? de_down_pointer(de) : 0;
hpfs_delete_de(i->i_sb, dnode, de);
set_last_pointer(i->i_sb, dnode, ddno);
try_it_again:
if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "delete_empty_dnode")) return;
if (!(dnode = hpfs_map_dnode(i->i_sb, dno, &qbh))) return;
- if (dnode->first_free > 56) goto end;
- if (dnode->first_free == 52 || dnode->first_free == 56) {
+ if (le32_to_cpu(dnode->first_free) > 56) goto end;
+ if (le32_to_cpu(dnode->first_free) == 52 || le32_to_cpu(dnode->first_free) == 56) {
struct hpfs_dirent *de_end;
int root = dnode->root_dnode;
- up = dnode->up;
+ up = le32_to_cpu(dnode->up);
de = dnode_first_de(dnode);
down = de->down ? de_down_pointer(de) : 0;
if (hpfs_sb(i->i_sb)->sb_chk) if (root && !down) {
return;
}
if ((d1 = hpfs_map_dnode(i->i_sb, down, &qbh1))) {
- d1->up = up;
+ d1->up = cpu_to_le32(up);
d1->root_dnode = 1;
hpfs_mark_4buffers_dirty(&qbh1);
hpfs_brelse4(&qbh1);
}
if ((fnode = hpfs_map_fnode(i->i_sb, up, &bh))) {
- fnode->u.external[0].disk_secno = down;
+ fnode->u.external[0].disk_secno = cpu_to_le32(down);
mark_buffer_dirty(bh);
brelse(bh);
}
for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, ((loff_t)up << 4) | p);
if (!down) {
de->down = 0;
- de->length -= 4;
- dnode->first_free -= 4;
+ de->length = cpu_to_le16(le16_to_cpu(de->length) - 4);
+ dnode->first_free = cpu_to_le32(le32_to_cpu(dnode->first_free) - 4);
memmove(de_next_de(de), (char *)de_next_de(de) + 4,
- (char *)dnode + dnode->first_free - (char *)de_next_de(de));
+ (char *)dnode + le32_to_cpu(dnode->first_free) - (char *)de_next_de(de));
} else {
struct dnode *d1;
struct quad_buffer_head qbh1;
- *(dnode_secno *) ((void *) de + de->length - 4) = down;
+ *(dnode_secno *) ((void *) de + le16_to_cpu(de->length) - 4) = down;
if ((d1 = hpfs_map_dnode(i->i_sb, down, &qbh1))) {
- d1->up = up;
+ d1->up = cpu_to_le32(up);
hpfs_mark_4buffers_dirty(&qbh1);
hpfs_brelse4(&qbh1);
}
}
} else {
- hpfs_error(i->i_sb, "delete_empty_dnode: dnode %08x, first_free == %03x", dno, dnode->first_free);
+ hpfs_error(i->i_sb, "delete_empty_dnode: dnode %08x, first_free == %03x", dno, le32_to_cpu(dnode->first_free));
goto end;
}
struct quad_buffer_head qbh1;
if (!de_next->down) goto endm;
ndown = de_down_pointer(de_next);
- if (!(de_cp = kmalloc(de->length, GFP_NOFS))) {
+ if (!(de_cp = kmalloc(le16_to_cpu(de->length), GFP_NOFS))) {
printk("HPFS: out of memory for dtree balancing\n");
goto endm;
}
- memcpy(de_cp, de, de->length);
+ memcpy(de_cp, de, le16_to_cpu(de->length));
hpfs_delete_de(i->i_sb, dnode, de);
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | p, 4);
for_all_poss(i, hpfs_pos_del, ((loff_t)up << 4) | p, 1);
if (de_cp->down) if ((d1 = hpfs_map_dnode(i->i_sb, de_down_pointer(de_cp), &qbh1))) {
- d1->up = ndown;
+ d1->up = cpu_to_le32(ndown);
hpfs_mark_4buffers_dirty(&qbh1);
hpfs_brelse4(&qbh1);
}
struct hpfs_dirent *del = dnode_last_de(d1);
dlp = del->down ? de_down_pointer(del) : 0;
if (!dlp && down) {
- if (d1->first_free > 2044) {
+ if (le32_to_cpu(d1->first_free) > 2044) {
if (hpfs_sb(i->i_sb)->sb_chk >= 2) {
printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n");
printk("HPFS: warning: terminating balancing operation\n");
printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n");
printk("HPFS: warning: goin'on\n");
}
- del->length += 4;
+ del->length = cpu_to_le16(le16_to_cpu(del->length) + 4);
del->down = 1;
- d1->first_free += 4;
+ d1->first_free = cpu_to_le32(le32_to_cpu(d1->first_free) + 4);
}
if (dlp && !down) {
- del->length -= 4;
+ del->length = cpu_to_le16(le16_to_cpu(del->length) - 4);
del->down = 0;
- d1->first_free -= 4;
+ d1->first_free = cpu_to_le32(le32_to_cpu(d1->first_free) - 4);
} else if (down)
- *(dnode_secno *) ((void *) del + del->length - 4) = down;
+ *(dnode_secno *) ((void *) del + le16_to_cpu(del->length) - 4) = cpu_to_le32(down);
} else goto endm;
- if (!(de_cp = kmalloc(de_prev->length, GFP_NOFS))) {
+ if (!(de_cp = kmalloc(le16_to_cpu(de_prev->length), GFP_NOFS))) {
printk("HPFS: out of memory for dtree balancing\n");
hpfs_brelse4(&qbh1);
goto endm;
}
hpfs_mark_4buffers_dirty(&qbh1);
hpfs_brelse4(&qbh1);
- memcpy(de_cp, de_prev, de_prev->length);
+ memcpy(de_cp, de_prev, le16_to_cpu(de_prev->length));
hpfs_delete_de(i->i_sb, dnode, de_prev);
if (!de_prev->down) {
- de_prev->length += 4;
+ de_prev->length = cpu_to_le16(le16_to_cpu(de_prev->length) + 4);
de_prev->down = 1;
- dnode->first_free += 4;
+ dnode->first_free = cpu_to_le32(le32_to_cpu(dnode->first_free) + 4);
}
- *(dnode_secno *) ((void *) de_prev + de_prev->length - 4) = ndown;
+ *(dnode_secno *) ((void *) de_prev + le16_to_cpu(de_prev->length) - 4) = cpu_to_le32(ndown);
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | (p - 1), 4);
for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | p, ((loff_t)up << 4) | (p - 1));
if (down) if ((d1 = hpfs_map_dnode(i->i_sb, de_down_pointer(de), &qbh1))) {
- d1->up = ndown;
+ d1->up = cpu_to_le32(ndown);
hpfs_mark_4buffers_dirty(&qbh1);
hpfs_brelse4(&qbh1);
}
{
struct dnode *dnode = qbh->data;
dnode_secno down = 0;
- int lock = 0;
loff_t t;
if (de->first || de->last) {
hpfs_error(i->i_sb, "hpfs_remove_dirent: attempt to delete first or last dirent in dnode %08x", dno);
}
if (de->down) down = de_down_pointer(de);
if (depth && (de->down || (de == dnode_first_de(dnode) && de_next_de(de)->last))) {
- lock = 1;
- hpfs_lock_creation(i->i_sb);
if (hpfs_check_free_dnodes(i->i_sb, FREE_DNODES_DEL)) {
hpfs_brelse4(qbh);
- hpfs_unlock_creation(i->i_sb);
return 2;
}
}
dnode_secno a = move_to_top(i, down, dno);
for_all_poss(i, hpfs_pos_subst, 5, t);
if (a) delete_empty_dnode(i, a);
- if (lock) hpfs_unlock_creation(i->i_sb);
return !a;
}
delete_empty_dnode(i, dno);
- if (lock) hpfs_unlock_creation(i->i_sb);
return 0;
}
ptr = 0;
go_up:
if (!(dnode = hpfs_map_dnode(s, dno, &qbh))) return;
- if (hpfs_sb(s)->sb_chk) if (odno && odno != -1 && dnode->up != odno)
- hpfs_error(s, "hpfs_count_dnodes: bad up pointer; dnode %08x, down %08x points to %08x", odno, dno, dnode->up);
+ if (hpfs_sb(s)->sb_chk) if (odno && odno != -1 && le32_to_cpu(dnode->up) != odno)
+ hpfs_error(s, "hpfs_count_dnodes: bad up pointer; dnode %08x, down %08x points to %08x", odno, dno, le32_to_cpu(dnode->up));
de = dnode_first_de(dnode);
if (ptr) while(1) {
if (de->down) if (de_down_pointer(de) == ptr) goto process_de;
if (!de->first && !de->last && n_items) (*n_items)++;
if ((de = de_next_de(de)) < dnode_end_de(dnode)) goto next_de;
ptr = dno;
- dno = dnode->up;
+ dno = le32_to_cpu(dnode->up);
if (dnode->root_dnode) {
hpfs_brelse4(&qbh);
return;
return d;
if (!(de = map_nth_dirent(s, d, 1, &qbh, NULL))) return dno;
if (hpfs_sb(s)->sb_chk)
- if (up && ((struct dnode *)qbh.data)->up != up)
- hpfs_error(s, "hpfs_de_as_down_as_possible: bad up pointer; dnode %08x, down %08x points to %08x", up, d, ((struct dnode *)qbh.data)->up);
+ if (up && le32_to_cpu(((struct dnode *)qbh.data)->up) != up)
+ hpfs_error(s, "hpfs_de_as_down_as_possible: bad up pointer; dnode %08x, down %08x points to %08x", up, d, le32_to_cpu(((struct dnode *)qbh.data)->up));
if (!de->down) {
hpfs_brelse4(&qbh);
return d;
/* Going up */
if (dnode->root_dnode) goto bail;
- if (!(up_dnode = hpfs_map_dnode(inode->i_sb, dnode->up, &qbh0)))
+ if (!(up_dnode = hpfs_map_dnode(inode->i_sb, le32_to_cpu(dnode->up), &qbh0)))
goto bail;
end_up_de = dnode_end_de(up_dnode);
for (up_de = dnode_first_de(up_dnode); up_de < end_up_de;
up_de = de_next_de(up_de)) {
if (!(++c & 077)) hpfs_error(inode->i_sb,
- "map_pos_dirent: pos crossed dnode boundary; dnode = %08x", dnode->up);
+ "map_pos_dirent: pos crossed dnode boundary; dnode = %08x", le32_to_cpu(dnode->up));
if (up_de->down && de_down_pointer(up_de) == dno) {
- *posp = ((loff_t) dnode->up << 4) + c;
+ *posp = ((loff_t) le32_to_cpu(dnode->up) << 4) + c;
hpfs_brelse4(&qbh0);
return de;
}
}
hpfs_error(inode->i_sb, "map_pos_dirent: pointer to dnode %08x not found in parent dnode %08x",
- dno, dnode->up);
+ dno, le32_to_cpu(dnode->up));
hpfs_brelse4(&qbh0);
bail:
/*name2[15] = 0xff;*/
name1len = 15; name2len = 256;
}
- if (!(upf = hpfs_map_fnode(s, f->up, &bh))) {
+ if (!(upf = hpfs_map_fnode(s, le32_to_cpu(f->up), &bh))) {
kfree(name2);
return NULL;
}
if (!upf->dirflag) {
brelse(bh);
- hpfs_error(s, "fnode %08x has non-directory parent %08x", fno, f->up);
+ hpfs_error(s, "fnode %08x has non-directory parent %08x", fno, le32_to_cpu(f->up));
kfree(name2);
return NULL;
}
- dno = upf->u.external[0].disk_secno;
+ dno = le32_to_cpu(upf->u.external[0].disk_secno);
brelse(bh);
go_down:
downd = 0;
return NULL;
}
next_de:
- if (de->fnode == fno) {
+ if (le32_to_cpu(de->fnode) == fno) {
kfree(name2);
return de;
}
goto go_down;
}
f:
- if (de->fnode == fno) {
+ if (le32_to_cpu(de->fnode) == fno) {
kfree(name2);
return de;
}
if ((de = de_next_de(de)) < de_end) goto next_de;
if (d->root_dnode) goto not_found;
downd = dno;
- dno = d->up;
+ dno = le32_to_cpu(d->up);
hpfs_brelse4(qbh);
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, downd, &d1, &d2, "map_fnode_dirent #2")) {
}
if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return;
if (ea->indirect) {
- if (ea->valuelen != 8) {
+ if (ea_valuelen(ea) != 8) {
hpfs_error(s, "ea->indirect set while ea->valuelen!=8, %s %08x, pos %08x",
ano ? "anode" : "sectors", a, pos);
return;
return;
hpfs_ea_remove(s, ea_sec(ea), ea->anode, ea_len(ea));
}
- pos += ea->namelen + ea->valuelen + 5;
+ pos += ea->namelen + ea_valuelen(ea) + 5;
}
if (!ano) hpfs_free_sectors(s, a, (len+511) >> 9);
else {
unsigned pos;
int ano, len;
secno a;
+ char ex[4 + 255 + 1 + 8];
struct extended_attribute *ea;
struct extended_attribute *ea_end = fnode_end_ea(fnode);
for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
if (!strcmp(ea->name, key)) {
if (ea->indirect)
goto indirect;
- if (ea->valuelen >= size)
+ if (ea_valuelen(ea) >= size)
return -EINVAL;
- memcpy(buf, ea_data(ea), ea->valuelen);
- buf[ea->valuelen] = 0;
+ memcpy(buf, ea_data(ea), ea_valuelen(ea));
+ buf[ea_valuelen(ea)] = 0;
return 0;
}
- a = fnode->ea_secno;
- len = fnode->ea_size_l;
+ a = le32_to_cpu(fnode->ea_secno);
+ len = le32_to_cpu(fnode->ea_size_l);
ano = fnode->ea_anode;
pos = 0;
while (pos < len) {
- char ex[4 + 255 + 1 + 8];
ea = (struct extended_attribute *)ex;
if (pos + 4 > len) {
hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
if (!strcmp(ea->name, key)) {
if (ea->indirect)
goto indirect;
- if (ea->valuelen >= size)
+ if (ea_valuelen(ea) >= size)
return -EINVAL;
- if (hpfs_ea_read(s, a, ano, pos + 4 + ea->namelen + 1, ea->valuelen, buf))
+ if (hpfs_ea_read(s, a, ano, pos + 4 + ea->namelen + 1, ea_valuelen(ea), buf))
return -EIO;
- buf[ea->valuelen] = 0;
+ buf[ea_valuelen(ea)] = 0;
return 0;
}
- pos += ea->namelen + ea->valuelen + 5;
+ pos += ea->namelen + ea_valuelen(ea) + 5;
}
return -ENOENT;
indirect:
if (!strcmp(ea->name, key)) {
if (ea->indirect)
return get_indirect_ea(s, ea->anode, ea_sec(ea), *size = ea_len(ea));
- if (!(ret = kmalloc((*size = ea->valuelen) + 1, GFP_NOFS))) {
+ if (!(ret = kmalloc((*size = ea_valuelen(ea)) + 1, GFP_NOFS))) {
printk("HPFS: out of memory for EA\n");
return NULL;
}
- memcpy(ret, ea_data(ea), ea->valuelen);
- ret[ea->valuelen] = 0;
+ memcpy(ret, ea_data(ea), ea_valuelen(ea));
+ ret[ea_valuelen(ea)] = 0;
return ret;
}
- a = fnode->ea_secno;
- len = fnode->ea_size_l;
+ a = le32_to_cpu(fnode->ea_secno);
+ len = le32_to_cpu(fnode->ea_size_l);
ano = fnode->ea_anode;
pos = 0;
while (pos < len) {
if (!strcmp(ea->name, key)) {
if (ea->indirect)
return get_indirect_ea(s, ea->anode, ea_sec(ea), *size = ea_len(ea));
- if (!(ret = kmalloc((*size = ea->valuelen) + 1, GFP_NOFS))) {
+ if (!(ret = kmalloc((*size = ea_valuelen(ea)) + 1, GFP_NOFS))) {
printk("HPFS: out of memory for EA\n");
return NULL;
}
- if (hpfs_ea_read(s, a, ano, pos + 4 + ea->namelen + 1, ea->valuelen, ret)) {
+ if (hpfs_ea_read(s, a, ano, pos + 4 + ea->namelen + 1, ea_valuelen(ea), ret)) {
kfree(ret);
return NULL;
}
- ret[ea->valuelen] = 0;
+ ret[ea_valuelen(ea)] = 0;
return ret;
}
- pos += ea->namelen + ea->valuelen + 5;
+ pos += ea->namelen + ea_valuelen(ea) + 5;
}
return NULL;
}
if (ea->indirect) {
if (ea_len(ea) == size)
set_indirect_ea(s, ea->anode, ea_sec(ea), data, size);
- } else if (ea->valuelen == size) {
+ } else if (ea_valuelen(ea) == size) {
memcpy(ea_data(ea), data, size);
}
return;
}
- a = fnode->ea_secno;
- len = fnode->ea_size_l;
+ a = le32_to_cpu(fnode->ea_secno);
+ len = le32_to_cpu(fnode->ea_size_l);
ano = fnode->ea_anode;
pos = 0;
while (pos < len) {
set_indirect_ea(s, ea->anode, ea_sec(ea), data, size);
}
else {
- if (ea->valuelen == size)
+ if (ea_valuelen(ea) == size)
hpfs_ea_write(s, a, ano, pos + 4 + ea->namelen + 1, size, data);
}
return;
}
- pos += ea->namelen + ea->valuelen + 5;
+ pos += ea->namelen + ea_valuelen(ea) + 5;
}
- if (!fnode->ea_offs) {
- /*if (fnode->ea_size_s) {
+ if (!le16_to_cpu(fnode->ea_offs)) {
+ /*if (le16_to_cpu(fnode->ea_size_s)) {
hpfs_error(s, "fnode %08x: ea_size_s == %03x, ea_offs == 0",
- inode->i_ino, fnode->ea_size_s);
+ inode->i_ino, le16_to_cpu(fnode->ea_size_s));
return;
}*/
- fnode->ea_offs = 0xc4;
+ fnode->ea_offs = cpu_to_le16(0xc4);
}
- if (fnode->ea_offs < 0xc4 || fnode->ea_offs + fnode->acl_size_s + fnode->ea_size_s > 0x200) {
+ if (le16_to_cpu(fnode->ea_offs) < 0xc4 || le16_to_cpu(fnode->ea_offs) + le16_to_cpu(fnode->acl_size_s) + le16_to_cpu(fnode->ea_size_s) > 0x200) {
hpfs_error(s, "fnode %08lx: ea_offs == %03x, ea_size_s == %03x",
(unsigned long)inode->i_ino,
- fnode->ea_offs, fnode->ea_size_s);
+ le32_to_cpu(fnode->ea_offs), le16_to_cpu(fnode->ea_size_s));
return;
}
- if ((fnode->ea_size_s || !fnode->ea_size_l) &&
- fnode->ea_offs + fnode->acl_size_s + fnode->ea_size_s + strlen(key) + size + 5 <= 0x200) {
+ if ((le16_to_cpu(fnode->ea_size_s) || !le32_to_cpu(fnode->ea_size_l)) &&
+ le16_to_cpu(fnode->ea_offs) + le16_to_cpu(fnode->acl_size_s) + le16_to_cpu(fnode->ea_size_s) + strlen(key) + size + 5 <= 0x200) {
ea = fnode_end_ea(fnode);
*(char *)ea = 0;
ea->namelen = strlen(key);
- ea->valuelen = size;
+ ea->valuelen_lo = size;
+ ea->valuelen_hi = size >> 8;
strcpy(ea->name, key);
memcpy(ea_data(ea), data, size);
- fnode->ea_size_s += strlen(key) + size + 5;
+ fnode->ea_size_s = cpu_to_le16(le16_to_cpu(fnode->ea_size_s) + strlen(key) + size + 5);
goto ret;
}
/* Most the code here is 99.9993422% unused. I hope there are no bugs.
But what .. HPFS.IFS has also bugs in ea management. */
- if (fnode->ea_size_s && !fnode->ea_size_l) {
+ if (le16_to_cpu(fnode->ea_size_s) && !le32_to_cpu(fnode->ea_size_l)) {
secno n;
struct buffer_head *bh;
char *data;
- if (!(n = hpfs_alloc_sector(s, fno, 1, 0, 1))) return;
+ if (!(n = hpfs_alloc_sector(s, fno, 1, 0))) return;
if (!(data = hpfs_get_sector(s, n, &bh))) {
hpfs_free_sectors(s, n, 1);
return;
}
- memcpy(data, fnode_ea(fnode), fnode->ea_size_s);
- fnode->ea_size_l = fnode->ea_size_s;
- fnode->ea_size_s = 0;
- fnode->ea_secno = n;
- fnode->ea_anode = 0;
+ memcpy(data, fnode_ea(fnode), le16_to_cpu(fnode->ea_size_s));
+ fnode->ea_size_l = cpu_to_le32(le16_to_cpu(fnode->ea_size_s));
+ fnode->ea_size_s = cpu_to_le16(0);
+ fnode->ea_secno = cpu_to_le32(n);
+ fnode->ea_anode = cpu_to_le32(0);
mark_buffer_dirty(bh);
brelse(bh);
}
- pos = fnode->ea_size_l + 5 + strlen(key) + size;
- len = (fnode->ea_size_l + 511) >> 9;
+ pos = le32_to_cpu(fnode->ea_size_l) + 5 + strlen(key) + size;
+ len = (le32_to_cpu(fnode->ea_size_l) + 511) >> 9;
if (pos >= 30000) goto bail;
while (((pos + 511) >> 9) > len) {
if (!len) {
- if (!(fnode->ea_secno = hpfs_alloc_sector(s, fno, 1, 0, 1)))
- goto bail;
+ secno q = hpfs_alloc_sector(s, fno, 1, 0);
+ if (!q) goto bail;
+ fnode->ea_secno = cpu_to_le32(q);
fnode->ea_anode = 0;
len++;
} else if (!fnode->ea_anode) {
- if (hpfs_alloc_if_possible(s, fnode->ea_secno + len)) {
+ if (hpfs_alloc_if_possible(s, le32_to_cpu(fnode->ea_secno) + len)) {
len++;
} else {
/* Aargh... don't know how to create ea anodes :-( */
anode_secno a_s;
if (!(anode = hpfs_alloc_anode(s, fno, &a_s, &bh)))
goto bail;
- anode->up = fno;
+ anode->up = cpu_to_le32(fno);
anode->btree.fnode_parent = 1;
anode->btree.n_free_nodes--;
anode->btree.n_used_nodes++;
- anode->btree.first_free += 12;
- anode->u.external[0].disk_secno = fnode->ea_secno;
- anode->u.external[0].file_secno = 0;
- anode->u.external[0].length = len;
+ anode->btree.first_free = cpu_to_le16(le16_to_cpu(anode->btree.first_free) + 12);
+ anode->u.external[0].disk_secno = cpu_to_le32(le32_to_cpu(fnode->ea_secno));
+ anode->u.external[0].file_secno = cpu_to_le32(0);
+ anode->u.external[0].length = cpu_to_le32(len);
mark_buffer_dirty(bh);
brelse(bh);
fnode->ea_anode = 1;
- fnode->ea_secno = a_s;*/
+ fnode->ea_secno = cpu_to_le32(a_s);*/
secno new_sec;
int i;
- if (!(new_sec = hpfs_alloc_sector(s, fno, 1, 1 - ((pos + 511) >> 9), 1)))
+ if (!(new_sec = hpfs_alloc_sector(s, fno, 1, 1 - ((pos + 511) >> 9))))
goto bail;
for (i = 0; i < len; i++) {
struct buffer_head *bh1, *bh2;
void *b1, *b2;
- if (!(b1 = hpfs_map_sector(s, fnode->ea_secno + i, &bh1, len - i - 1))) {
+ if (!(b1 = hpfs_map_sector(s, le32_to_cpu(fnode->ea_secno) + i, &bh1, len - i - 1))) {
hpfs_free_sectors(s, new_sec, (pos + 511) >> 9);
goto bail;
}
mark_buffer_dirty(bh2);
brelse(bh2);
}
- hpfs_free_sectors(s, fnode->ea_secno, len);
- fnode->ea_secno = new_sec;
+ hpfs_free_sectors(s, le32_to_cpu(fnode->ea_secno), len);
+ fnode->ea_secno = cpu_to_le32(new_sec);
len = (pos + 511) >> 9;
}
}
if (fnode->ea_anode) {
- if (hpfs_add_sector_to_btree(s, fnode->ea_secno,
+ if (hpfs_add_sector_to_btree(s, le32_to_cpu(fnode->ea_secno),
0, len) != -1) {
len++;
} else {
h[1] = strlen(key);
h[2] = size & 0xff;
h[3] = size >> 8;
- if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l, 4, h)) goto bail;
- if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l + 4, h[1] + 1, key)) goto bail;
- if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l + 5 + h[1], size, data)) goto bail;
- fnode->ea_size_l = pos;
+ if (hpfs_ea_write(s, le32_to_cpu(fnode->ea_secno), fnode->ea_anode, le32_to_cpu(fnode->ea_size_l), 4, h)) goto bail;
+ if (hpfs_ea_write(s, le32_to_cpu(fnode->ea_secno), fnode->ea_anode, le32_to_cpu(fnode->ea_size_l) + 4, h[1] + 1, key)) goto bail;
+ if (hpfs_ea_write(s, le32_to_cpu(fnode->ea_secno), fnode->ea_anode, le32_to_cpu(fnode->ea_size_l) + 5 + h[1], size, data)) goto bail;
+ fnode->ea_size_l = cpu_to_le32(pos);
ret:
hpfs_i(inode)->i_ea_size += 5 + strlen(key) + size;
return;
bail:
- if (fnode->ea_secno)
- if (fnode->ea_anode) hpfs_truncate_btree(s, fnode->ea_secno, 1, (fnode->ea_size_l + 511) >> 9);
- else hpfs_free_sectors(s, fnode->ea_secno + ((fnode->ea_size_l + 511) >> 9), len - ((fnode->ea_size_l + 511) >> 9));
- else fnode->ea_secno = fnode->ea_size_l = 0;
+ if (le32_to_cpu(fnode->ea_secno))
+ if (fnode->ea_anode) hpfs_truncate_btree(s, le32_to_cpu(fnode->ea_secno), 1, (le32_to_cpu(fnode->ea_size_l) + 511) >> 9);
+ else hpfs_free_sectors(s, le32_to_cpu(fnode->ea_secno) + ((le32_to_cpu(fnode->ea_size_l) + 511) >> 9), len - ((le32_to_cpu(fnode->ea_size_l) + 511) >> 9));
+ else fnode->ea_secno = fnode->ea_size_l = cpu_to_le32(0);
}
int hpfs_file_fsync(struct file *file, int datasync)
{
- /*return file_fsync(file, datasync);*/
- return 0; /* Don't fsync :-) */
+ struct inode *inode = file->f_mapping->host;
+ return sync_blockdev(inode->i_sb->s_bdev);
}
/*
static void hpfs_truncate(struct inode *i)
{
if (IS_IMMUTABLE(i)) return /*-EPERM*/;
- hpfs_lock(i->i_sb);
+ hpfs_lock_assert(i->i_sb);
+
hpfs_i(i)->i_n_secs = 0;
i->i_blocks = 1 + ((i->i_size + 511) >> 9);
hpfs_i(i)->mmu_private = i->i_size;
hpfs_truncate_btree(i->i_sb, i->i_ino, 1, ((i->i_size + 511) >> 9));
hpfs_write_inode(i);
hpfs_i(i)->i_n_secs = 0;
- hpfs_unlock(i->i_sb);
}
static int hpfs_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create)
{
+ int r;
secno s;
+ hpfs_lock(inode->i_sb);
s = hpfs_bmap(inode, iblock);
if (s) {
map_bh(bh_result, inode->i_sb, s);
- return 0;
+ goto ret_0;
}
- if (!create) return 0;
+ if (!create) goto ret_0;
if (iblock<<9 != hpfs_i(inode)->mmu_private) {
BUG();
- return -EIO;
+ r = -EIO;
+ goto ret_r;
}
if ((s = hpfs_add_sector_to_btree(inode->i_sb, inode->i_ino, 1, inode->i_blocks - 1)) == -1) {
hpfs_truncate_btree(inode->i_sb, inode->i_ino, 1, inode->i_blocks - 1);
- return -ENOSPC;
+ r = -ENOSPC;
+ goto ret_r;
}
inode->i_blocks++;
hpfs_i(inode)->mmu_private += 512;
set_buffer_new(bh_result);
map_bh(bh_result, inode->i_sb, s);
- return 0;
+ ret_0:
+ r = 0;
+ ret_r:
+ hpfs_unlock(inode->i_sb);
+ return r;
}
static int hpfs_writepage(struct page *page, struct writeback_control *wbc)
ssize_t retval;
retval = do_sync_write(file, buf, count, ppos);
- if (retval > 0)
+ if (retval > 0) {
+ hpfs_lock(file->f_path.dentry->d_sb);
hpfs_i(file->f_path.dentry->d_inode)->i_dirty = 1;
+ hpfs_unlock(file->f_path.dentry->d_sb);
+ }
return retval;
}
For definitive information on HPFS, ask somebody else -- this is guesswork.
There are certain to be many mistakes. */
+#if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)
+#error unknown endian
+#endif
+
/* Notation */
-typedef unsigned secno; /* sector number, partition relative */
+typedef u32 secno; /* sector number, partition relative */
typedef secno dnode_secno; /* sector number of a dnode */
typedef secno fnode_secno; /* sector number of an fnode */
struct hpfs_boot_block
{
- unsigned char jmp[3];
- unsigned char oem_id[8];
- unsigned char bytes_per_sector[2]; /* 512 */
- unsigned char sectors_per_cluster;
- unsigned char n_reserved_sectors[2];
- unsigned char n_fats;
- unsigned char n_rootdir_entries[2];
- unsigned char n_sectors_s[2];
- unsigned char media_byte;
- unsigned short sectors_per_fat;
- unsigned short sectors_per_track;
- unsigned short heads_per_cyl;
- unsigned int n_hidden_sectors;
- unsigned int n_sectors_l; /* size of partition */
- unsigned char drive_number;
- unsigned char mbz;
- unsigned char sig_28h; /* 28h */
- unsigned char vol_serno[4];
- unsigned char vol_label[11];
- unsigned char sig_hpfs[8]; /* "HPFS " */
- unsigned char pad[448];
- unsigned short magic; /* aa55 */
+ u8 jmp[3];
+ u8 oem_id[8];
+ u8 bytes_per_sector[2]; /* 512 */
+ u8 sectors_per_cluster;
+ u8 n_reserved_sectors[2];
+ u8 n_fats;
+ u8 n_rootdir_entries[2];
+ u8 n_sectors_s[2];
+ u8 media_byte;
+ u16 sectors_per_fat;
+ u16 sectors_per_track;
+ u16 heads_per_cyl;
+ u32 n_hidden_sectors;
+ u32 n_sectors_l; /* size of partition */
+ u8 drive_number;
+ u8 mbz;
+ u8 sig_28h; /* 28h */
+ u8 vol_serno[4];
+ u8 vol_label[11];
+ u8 sig_hpfs[8]; /* "HPFS " */
+ u8 pad[448];
+ u16 magic; /* aa55 */
};
struct hpfs_super_block
{
- unsigned magic; /* f995 e849 */
- unsigned magic1; /* fa53 e9c5, more magic? */
- /*unsigned huh202;*/ /* ?? 202 = N. of B. in 1.00390625 S.*/
- char version; /* version of a filesystem usually 2 */
- char funcversion; /* functional version - oldest version
+ u32 magic; /* f995 e849 */
+ u32 magic1; /* fa53 e9c5, more magic? */
+ u8 version; /* version of a filesystem usually 2 */
+ u8 funcversion; /* functional version - oldest version
of filesystem that can understand
this disk */
- unsigned short int zero; /* 0 */
+ u16 zero; /* 0 */
fnode_secno root; /* fnode of root directory */
secno n_sectors; /* size of filesystem */
- unsigned n_badblocks; /* number of bad blocks */
+ u32 n_badblocks; /* number of bad blocks */
secno bitmaps; /* pointers to free space bit maps */
- unsigned zero1; /* 0 */
+ u32 zero1; /* 0 */
secno badblocks; /* bad block list */
- unsigned zero3; /* 0 */
+ u32 zero3; /* 0 */
time32_t last_chkdsk; /* date last checked, 0 if never */
- /*unsigned zero4;*/ /* 0 */
- time32_t last_optimize; /* date last optimized, 0 if never */
+ time32_t last_optimize; /* date last optimized, 0 if never */
secno n_dir_band; /* number of sectors in dir band */
secno dir_band_start; /* first sector in dir band */
secno dir_band_end; /* last sector in dir band */
secno dir_band_bitmap; /* free space map, 1 dnode per bit */
- char volume_name[32]; /* not used */
+ u8 volume_name[32]; /* not used */
secno user_id_table; /* 8 preallocated sectors - user id */
- unsigned zero6[103]; /* 0 */
+ u32 zero6[103]; /* 0 */
};
struct hpfs_spare_block
{
- unsigned magic; /* f991 1849 */
- unsigned magic1; /* fa52 29c5, more magic? */
-
- unsigned dirty: 1; /* 0 clean, 1 "improperly stopped" */
- /*unsigned flag1234: 4;*/ /* unknown flags */
- unsigned sparedir_used: 1; /* spare dirblks used */
- unsigned hotfixes_used: 1; /* hotfixes used */
- unsigned bad_sector: 1; /* bad sector, corrupted disk (???) */
- unsigned bad_bitmap: 1; /* bad bitmap */
- unsigned fast: 1; /* partition was fast formatted */
- unsigned old_wrote: 1; /* old version wrote to partion */
- unsigned old_wrote_1: 1; /* old version wrote to partion (?) */
- unsigned install_dasd_limits: 1; /* HPFS386 flags */
- unsigned resynch_dasd_limits: 1;
- unsigned dasd_limits_operational: 1;
- unsigned multimedia_active: 1;
- unsigned dce_acls_active: 1;
- unsigned dasd_limits_dirty: 1;
- unsigned flag67: 2;
- unsigned char mm_contlgulty;
- unsigned char unused;
+ u32 magic; /* f991 1849 */
+ u32 magic1; /* fa52 29c5, more magic? */
+
+#ifdef __LITTLE_ENDIAN
+ u8 dirty: 1; /* 0 clean, 1 "improperly stopped" */
+ u8 sparedir_used: 1; /* spare dirblks used */
+ u8 hotfixes_used: 1; /* hotfixes used */
+ u8 bad_sector: 1; /* bad sector, corrupted disk (???) */
+ u8 bad_bitmap: 1; /* bad bitmap */
+ u8 fast: 1; /* partition was fast formatted */
+ u8 old_wrote: 1; /* old version wrote to partion */
+ u8 old_wrote_1: 1; /* old version wrote to partion (?) */
+#else
+ u8 old_wrote_1: 1; /* old version wrote to partion (?) */
+ u8 old_wrote: 1; /* old version wrote to partion */
+ u8 fast: 1; /* partition was fast formatted */
+ u8 bad_bitmap: 1; /* bad bitmap */
+ u8 bad_sector: 1; /* bad sector, corrupted disk (???) */
+ u8 hotfixes_used: 1; /* hotfixes used */
+ u8 sparedir_used: 1; /* spare dirblks used */
+ u8 dirty: 1; /* 0 clean, 1 "improperly stopped" */
+#endif
+
+#ifdef __LITTLE_ENDIAN
+ u8 install_dasd_limits: 1; /* HPFS386 flags */
+ u8 resynch_dasd_limits: 1;
+ u8 dasd_limits_operational: 1;
+ u8 multimedia_active: 1;
+ u8 dce_acls_active: 1;
+ u8 dasd_limits_dirty: 1;
+ u8 flag67: 2;
+#else
+ u8 flag67: 2;
+ u8 dasd_limits_dirty: 1;
+ u8 dce_acls_active: 1;
+ u8 multimedia_active: 1;
+ u8 dasd_limits_operational: 1;
+ u8 resynch_dasd_limits: 1;
+ u8 install_dasd_limits: 1; /* HPFS386 flags */
+#endif
+
+ u8 mm_contlgulty;
+ u8 unused;
secno hotfix_map; /* info about remapped bad sectors */
- unsigned n_spares_used; /* number of hotfixes */
- unsigned n_spares; /* number of spares in hotfix map */
- unsigned n_dnode_spares_free; /* spare dnodes unused */
- unsigned n_dnode_spares; /* length of spare_dnodes[] list,
+ u32 n_spares_used; /* number of hotfixes */
+ u32 n_spares; /* number of spares in hotfix map */
+ u32 n_dnode_spares_free; /* spare dnodes unused */
+ u32 n_dnode_spares; /* length of spare_dnodes[] list,
follows in this block*/
secno code_page_dir; /* code page directory block */
- unsigned n_code_pages; /* number of code pages */
- /*unsigned large_numbers[2];*/ /* ?? */
- unsigned super_crc; /* on HPFS386 and LAN Server this is
+ u32 n_code_pages; /* number of code pages */
+ u32 super_crc; /* on HPFS386 and LAN Server this is
checksum of superblock, on normal
OS/2 unused */
- unsigned spare_crc; /* on HPFS386 checksum of spareblock */
- unsigned zero1[15]; /* unused */
+ u32 spare_crc; /* on HPFS386 checksum of spareblock */
+ u32 zero1[15]; /* unused */
dnode_secno spare_dnodes[100]; /* emergency free dnode list */
- unsigned zero2[1]; /* room for more? */
+ u32 zero2[1]; /* room for more? */
};
/* The bad block list is 4 sectors long. The first word must be zero,
struct code_page_directory
{
- unsigned magic; /* 4945 21f7 */
- unsigned n_code_pages; /* number of pointers following */
- unsigned zero1[2];
+ u32 magic; /* 4945 21f7 */
+ u32 n_code_pages; /* number of pointers following */
+ u32 zero1[2];
struct {
- unsigned short ix; /* index */
- unsigned short code_page_number; /* code page number */
- unsigned bounds; /* matches corresponding word
+ u16 ix; /* index */
+ u16 code_page_number; /* code page number */
+ u32 bounds; /* matches corresponding word
in data block */
secno code_page_data; /* sector number of a code_page_data
containing c.p. array */
- unsigned short index; /* index in c.p. array in that sector*/
- unsigned short unknown; /* some unknown value; usually 0;
+ u16 index; /* index in c.p. array in that sector*/
+ u16 unknown; /* some unknown value; usually 0;
2 in Japanese version */
} array[31]; /* unknown length */
};
struct code_page_data
{
- unsigned magic; /* 8945 21f7 */
- unsigned n_used; /* # elements used in c_p_data[] */
- unsigned bounds[3]; /* looks a bit like
+ u32 magic; /* 8945 21f7 */
+ u32 n_used; /* # elements used in c_p_data[] */
+ u32 bounds[3]; /* looks a bit like
(beg1,end1), (beg2,end2)
one byte each */
- unsigned short offs[3]; /* offsets from start of sector
+ u16 offs[3]; /* offsets from start of sector
to start of c_p_data[ix] */
struct {
- unsigned short ix; /* index */
- unsigned short code_page_number; /* code page number */
- unsigned short unknown; /* the same as in cp directory */
- unsigned char map[128]; /* upcase table for chars 80..ff */
- unsigned short zero2;
+ u16 ix; /* index */
+ u16 code_page_number; /* code page number */
+ u16 unknown; /* the same as in cp directory */
+ u8 map[128]; /* upcase table for chars 80..ff */
+ u16 zero2;
} code_page[3];
- unsigned char incognita[78];
+ u8 incognita[78];
};
#define DNODE_MAGIC 0x77e40aae
struct dnode {
- unsigned magic; /* 77e4 0aae */
- unsigned first_free; /* offset from start of dnode to
+ u32 magic; /* 77e4 0aae */
+ u32 first_free; /* offset from start of dnode to
first free dir entry */
- unsigned root_dnode:1; /* Is it root dnode? */
- unsigned increment_me:31; /* some kind of activity counter?
- Neither HPFS.IFS nor CHKDSK cares
+#ifdef __LITTLE_ENDIAN
+ u8 root_dnode: 1; /* Is it root dnode? */
+ u8 increment_me: 7; /* some kind of activity counter? */
+ /* Neither HPFS.IFS nor CHKDSK cares
+ if you change this word */
+#else
+ u8 increment_me: 7; /* some kind of activity counter? */
+ /* Neither HPFS.IFS nor CHKDSK cares
if you change this word */
+ u8 root_dnode: 1; /* Is it root dnode? */
+#endif
+ u8 increment_me2[3];
secno up; /* (root dnode) directory's fnode
(nonroot) parent dnode */
dnode_secno self; /* pointer to this dnode */
- unsigned char dirent[2028]; /* one or more dirents */
+ u8 dirent[2028]; /* one or more dirents */
};
struct hpfs_dirent {
- unsigned short length; /* offset to next dirent */
- unsigned first: 1; /* set on phony ^A^A (".") entry */
- unsigned has_acl: 1;
- unsigned down: 1; /* down pointer present (after name) */
- unsigned last: 1; /* set on phony \377 entry */
- unsigned has_ea: 1; /* entry has EA */
- unsigned has_xtd_perm: 1; /* has extended perm list (???) */
- unsigned has_explicit_acl: 1;
- unsigned has_needea: 1; /* ?? some EA has NEEDEA set
+ u16 length; /* offset to next dirent */
+
+#ifdef __LITTLE_ENDIAN
+ u8 first: 1; /* set on phony ^A^A (".") entry */
+ u8 has_acl: 1;
+ u8 down: 1; /* down pointer present (after name) */
+ u8 last: 1; /* set on phony \377 entry */
+ u8 has_ea: 1; /* entry has EA */
+ u8 has_xtd_perm: 1; /* has extended perm list (???) */
+ u8 has_explicit_acl: 1;
+ u8 has_needea: 1; /* ?? some EA has NEEDEA set
+ I have no idea why this is
+ interesting in a dir entry */
+#else
+ u8 has_needea: 1; /* ?? some EA has NEEDEA set
I have no idea why this is
interesting in a dir entry */
- unsigned read_only: 1; /* dos attrib */
- unsigned hidden: 1; /* dos attrib */
- unsigned system: 1; /* dos attrib */
- unsigned flag11: 1; /* would be volume label dos attrib */
- unsigned directory: 1; /* dos attrib */
- unsigned archive: 1; /* dos attrib */
- unsigned not_8x3: 1; /* name is not 8.3 */
- unsigned flag15: 1;
+ u8 has_explicit_acl: 1;
+ u8 has_xtd_perm: 1; /* has extended perm list (???) */
+ u8 has_ea: 1; /* entry has EA */
+ u8 last: 1; /* set on phony \377 entry */
+ u8 down: 1; /* down pointer present (after name) */
+ u8 has_acl: 1;
+ u8 first: 1; /* set on phony ^A^A (".") entry */
+#endif
+
+#ifdef __LITTLE_ENDIAN
+ u8 read_only: 1; /* dos attrib */
+ u8 hidden: 1; /* dos attrib */
+ u8 system: 1; /* dos attrib */
+ u8 flag11: 1; /* would be volume label dos attrib */
+ u8 directory: 1; /* dos attrib */
+ u8 archive: 1; /* dos attrib */
+ u8 not_8x3: 1; /* name is not 8.3 */
+ u8 flag15: 1;
+#else
+ u8 flag15: 1;
+ u8 not_8x3: 1; /* name is not 8.3 */
+ u8 archive: 1; /* dos attrib */
+ u8 directory: 1; /* dos attrib */
+ u8 flag11: 1; /* would be volume label dos attrib */
+ u8 system: 1; /* dos attrib */
+ u8 hidden: 1; /* dos attrib */
+ u8 read_only: 1; /* dos attrib */
+#endif
+
fnode_secno fnode; /* fnode giving allocation info */
time32_t write_date; /* mtime */
- unsigned file_size; /* file length, bytes */
+ u32 file_size; /* file length, bytes */
time32_t read_date; /* atime */
time32_t creation_date; /* ctime */
- unsigned ea_size; /* total EA length, bytes */
- unsigned char no_of_acls : 3; /* number of ACL's */
- unsigned char reserver : 5;
- unsigned char ix; /* code page index (of filename), see
+ u32 ea_size; /* total EA length, bytes */
+ u8 no_of_acls; /* number of ACL's (low 3 bits) */
+ u8 ix; /* code page index (of filename), see
struct code_page_data */
- unsigned char namelen, name[1]; /* file name */
+ u8 namelen, name[1]; /* file name */
/* dnode_secno down; btree down pointer, if present,
follows name on next word boundary, or maybe it
precedes next dirent, which is on a word boundary. */
struct bplus_leaf_node
{
- unsigned file_secno; /* first file sector in extent */
- unsigned length; /* length, sectors */
+ u32 file_secno; /* first file sector in extent */
+ u32 length; /* length, sectors */
secno disk_secno; /* first corresponding disk sector */
};
struct bplus_internal_node
{
- unsigned file_secno; /* subtree maps sectors < this */
+ u32 file_secno; /* subtree maps sectors < this */
anode_secno down; /* pointer to subtree */
};
struct bplus_header
{
- unsigned hbff: 1; /* high bit of first free entry offset */
- unsigned flag1: 1;
- unsigned flag2: 1;
- unsigned flag3: 1;
- unsigned flag4: 1;
- unsigned fnode_parent: 1; /* ? we're pointed to by an fnode,
+#ifdef __LITTLE_ENDIAN
+ u8 hbff: 1; /* high bit of first free entry offset */
+ u8 flag1234: 4;
+ u8 fnode_parent: 1; /* ? we're pointed to by an fnode,
the data btree or some ea or the
main ea bootage pointer ea_secno */
/* also can get set in fnodes, which
may be a chkdsk glitch or may mean
this bit is irrelevant in fnodes,
or this interpretation is all wet */
- unsigned binary_search: 1; /* suggest binary search (unused) */
- unsigned internal: 1; /* 1 -> (internal) tree of anodes
+ u8 binary_search: 1; /* suggest binary search (unused) */
+ u8 internal: 1; /* 1 -> (internal) tree of anodes
+ 0 -> (leaf) list of extents */
+#else
+ u8 internal: 1; /* 1 -> (internal) tree of anodes
0 -> (leaf) list of extents */
- unsigned char fill[3];
- unsigned char n_free_nodes; /* free nodes in following array */
- unsigned char n_used_nodes; /* used nodes in following array */
- unsigned short first_free; /* offset from start of header to
+ u8 binary_search: 1; /* suggest binary search (unused) */
+ u8 fnode_parent: 1; /* ? we're pointed to by an fnode,
+ the data btree or some ea or the
+ main ea bootage pointer ea_secno */
+ /* also can get set in fnodes, which
+ may be a chkdsk glitch or may mean
+ this bit is irrelevant in fnodes,
+ or this interpretation is all wet */
+ u8 flag1234: 4;
+ u8 hbff: 1; /* high bit of first free entry offset */
+#endif
+ u8 fill[3];
+ u8 n_free_nodes; /* free nodes in following array */
+ u8 n_used_nodes; /* used nodes in following array */
+ u16 first_free; /* offset from start of header to
first free node in array */
union {
struct bplus_internal_node internal[0]; /* (internal) 2-word entries giving
struct fnode
{
- unsigned magic; /* f7e4 0aae */
- unsigned zero1[2]; /* read history */
- unsigned char len, name[15]; /* true length, truncated name */
+ u32 magic; /* f7e4 0aae */
+ u32 zero1[2]; /* read history */
+ u8 len, name[15]; /* true length, truncated name */
fnode_secno up; /* pointer to file's directory fnode */
- /*unsigned zero2[3];*/
secno acl_size_l;
secno acl_secno;
- unsigned short acl_size_s;
- char acl_anode;
- char zero2; /* history bit count */
- unsigned ea_size_l; /* length of disk-resident ea's */
+ u16 acl_size_s;
+ u8 acl_anode;
+ u8 zero2; /* history bit count */
+ u32 ea_size_l; /* length of disk-resident ea's */
secno ea_secno; /* first sector of disk-resident ea's*/
- unsigned short ea_size_s; /* length of fnode-resident ea's */
-
- unsigned flag0: 1;
- unsigned ea_anode: 1; /* 1 -> ea_secno is an anode */
- unsigned flag2: 1;
- unsigned flag3: 1;
- unsigned flag4: 1;
- unsigned flag5: 1;
- unsigned flag6: 1;
- unsigned flag7: 1;
- unsigned dirflag: 1; /* 1 -> directory. first & only extent
+ u16 ea_size_s; /* length of fnode-resident ea's */
+
+#ifdef __LITTLE_ENDIAN
+ u8 flag0: 1;
+ u8 ea_anode: 1; /* 1 -> ea_secno is an anode */
+ u8 flag234567: 6;
+#else
+ u8 flag234567: 6;
+ u8 ea_anode: 1; /* 1 -> ea_secno is an anode */
+ u8 flag0: 1;
+#endif
+
+#ifdef __LITTLE_ENDIAN
+ u8 dirflag: 1; /* 1 -> directory. first & only extent
points to dnode. */
- unsigned flag9: 1;
- unsigned flag10: 1;
- unsigned flag11: 1;
- unsigned flag12: 1;
- unsigned flag13: 1;
- unsigned flag14: 1;
- unsigned flag15: 1;
+ u8 flag9012345: 7;
+#else
+ u8 flag9012345: 7;
+ u8 dirflag: 1; /* 1 -> directory. first & only extent
+ points to dnode. */
+#endif
struct bplus_header btree; /* b+ tree, 8 extents or 12 subtrees */
union {
struct bplus_internal_node internal[12];
} u;
- unsigned file_size; /* file length, bytes */
- unsigned n_needea; /* number of EA's with NEEDEA set */
- char user_id[16]; /* unused */
- unsigned short ea_offs; /* offset from start of fnode
+ u32 file_size; /* file length, bytes */
+ u32 n_needea; /* number of EA's with NEEDEA set */
+ u8 user_id[16]; /* unused */
+ u16 ea_offs; /* offset from start of fnode
to first fnode-resident ea */
- char dasd_limit_treshhold;
- char dasd_limit_delta;
- unsigned dasd_limit;
- unsigned dasd_usage;
- /*unsigned zero5[2];*/
- unsigned char ea[316]; /* zero or more EA's, packed together
+ u8 dasd_limit_treshhold;
+ u8 dasd_limit_delta;
+ u32 dasd_limit;
+ u32 dasd_usage;
+ u8 ea[316]; /* zero or more EA's, packed together
with no alignment padding.
(Do not use this name, get here
via fnode + ea_offs. I think.) */
struct anode
{
- unsigned magic; /* 37e4 0aae */
+ u32 magic; /* 37e4 0aae */
anode_secno self; /* pointer to this anode */
secno up; /* parent anode or fnode */
struct bplus_internal_node internal[60];
} u;
- unsigned fill[3]; /* unused */
+ u32 fill[3]; /* unused */
};
struct extended_attribute
{
- unsigned indirect: 1; /* 1 -> value gives sector number
+#ifdef __LITTLE_ENDIAN
+ u8 indirect: 1; /* 1 -> value gives sector number
where real value starts */
- unsigned anode: 1; /* 1 -> sector is an anode
+ u8 anode: 1; /* 1 -> sector is an anode
+ that points to fragmented value */
+ u8 flag23456: 5;
+ u8 needea: 1; /* required ea */
+#else
+ u8 needea: 1; /* required ea */
+ u8 flag23456: 5;
+ u8 anode: 1; /* 1 -> sector is an anode
that points to fragmented value */
- unsigned flag2: 1;
- unsigned flag3: 1;
- unsigned flag4: 1;
- unsigned flag5: 1;
- unsigned flag6: 1;
- unsigned needea: 1; /* required ea */
- unsigned char namelen; /* length of name, bytes */
- unsigned short valuelen; /* length of value, bytes */
- unsigned char name[0];
+ u8 indirect: 1; /* 1 -> value gives sector number
+ where real value starts */
+#endif
+ u8 namelen; /* length of name, bytes */
+ u8 valuelen_lo; /* length of value, bytes */
+ u8 valuelen_hi; /* length of value, bytes */
+ u8 name[0];
/*
- unsigned char name[namelen]; ascii attrib name
- unsigned char nul; terminating '\0', not counted
- unsigned char value[valuelen]; value, arbitrary
+ u8 name[namelen]; ascii attrib name
+ u8 nul; terminating '\0', not counted
+ u8 value[valuelen]; value, arbitrary
if this.indirect, valuelen is 8 and the value is
- unsigned length; real length of value, bytes
+ u32 length; real length of value, bytes
secno secno; sector address where it starts
if this.anode, the above sector number is the root of an anode tree
which points to the value.
#include <linux/pagemap.h>
#include <linux/buffer_head.h>
#include <linux/slab.h>
+#include <asm/unaligned.h>
#include "hpfs.h"
unsigned i_disk_sec; /* (files) minimalist cache of alloc info */
unsigned i_n_secs; /* (files) minimalist cache of alloc info */
unsigned i_ea_size; /* size of extended attributes */
- unsigned i_conv : 2; /* (files) crlf->newline hackery */
unsigned i_ea_mode : 1; /* file's permission is stored in ea */
unsigned i_ea_uid : 1; /* file's uid is stored in ea */
unsigned i_ea_gid : 1; /* file's gid is stored in ea */
unsigned i_dirty : 1;
- struct mutex i_mutex;
- struct mutex i_parent_mutex;
loff_t **i_rddir_off;
struct inode vfs_inode;
};
struct hpfs_sb_info {
+ struct mutex hpfs_mutex; /* global hpfs lock */
ino_t sb_root; /* inode number of root dir */
unsigned sb_fs_size; /* file system size, sectors */
unsigned sb_bitmaps; /* sector number of bitmap list */
uid_t sb_uid; /* uid from mount options */
gid_t sb_gid; /* gid from mount options */
umode_t sb_mode; /* mode from mount options */
- unsigned sb_conv : 2; /* crlf->newline hackery */
unsigned sb_eas : 2; /* eas: 0-ignore, 1-ro, 2-rw */
unsigned sb_err : 2; /* on errs: 0-cont, 1-ro, 2-panic */
unsigned sb_chk : 2; /* checks: 0-no, 1-normal, 2-strict */
unsigned *sb_bmp_dir; /* main bitmap directory */
unsigned sb_c_bitmap; /* current bitmap */
unsigned sb_max_fwd_alloc; /* max forwad allocation */
- struct mutex hpfs_creation_de; /* when creating dirents, nobody else
- can alloc blocks */
- /*unsigned sb_mounting : 1;*/
int sb_timeshift;
};
-/*
- * conv= options
- */
-
-#define CONV_BINARY 0 /* no conversion */
-#define CONV_TEXT 1 /* crlf->newline */
-#define CONV_AUTO 2 /* decide based on file contents */
-
/* Four 512-byte buffers and the 2k block obtained by concatenating them */
struct quad_buffer_head {
static inline dnode_secno de_down_pointer (struct hpfs_dirent *de)
{
CHKCOND(de->down,("HPFS: de_down_pointer: !de->down\n"));
- return *(dnode_secno *) ((void *) de + de->length - 4);
+ return le32_to_cpu(*(dnode_secno *) ((void *) de + le16_to_cpu(de->length) - 4));
}
/* The first dir entry in a dnode */
static inline struct hpfs_dirent *dnode_end_de (struct dnode *dnode)
{
- CHKCOND(dnode->first_free>=0x14 && dnode->first_free<=0xa00,("HPFS: dnode_end_de: dnode->first_free = %d\n",(int)dnode->first_free));
- return (void *) dnode + dnode->first_free;
+ CHKCOND(le32_to_cpu(dnode->first_free)>=0x14 && le32_to_cpu(dnode->first_free)<=0xa00,("HPFS: dnode_end_de: dnode->first_free = %x\n",(unsigned)le32_to_cpu(dnode->first_free)));
+ return (void *) dnode + le32_to_cpu(dnode->first_free);
}
/* The dir entry after dir entry de */
static inline struct hpfs_dirent *de_next_de (struct hpfs_dirent *de)
{
- CHKCOND(de->length>=0x20 && de->length<0x800,("HPFS: de_next_de: de->length = %d\n",(int)de->length));
- return (void *) de + de->length;
+ CHKCOND(le16_to_cpu(de->length)>=0x20 && le16_to_cpu(de->length)<0x800,("HPFS: de_next_de: de->length = %x\n",(unsigned)le16_to_cpu(de->length)));
+ return (void *) de + le16_to_cpu(de->length);
}
static inline struct extended_attribute *fnode_ea(struct fnode *fnode)
{
- return (struct extended_attribute *)((char *)fnode + fnode->ea_offs + fnode->acl_size_s);
+ return (struct extended_attribute *)((char *)fnode + le16_to_cpu(fnode->ea_offs) + le16_to_cpu(fnode->acl_size_s));
}
static inline struct extended_attribute *fnode_end_ea(struct fnode *fnode)
{
- return (struct extended_attribute *)((char *)fnode + fnode->ea_offs + fnode->acl_size_s + fnode->ea_size_s);
+ return (struct extended_attribute *)((char *)fnode + le16_to_cpu(fnode->ea_offs) + le16_to_cpu(fnode->acl_size_s) + le16_to_cpu(fnode->ea_size_s));
+}
+
+static unsigned ea_valuelen(struct extended_attribute *ea)
+{
+ return ea->valuelen_lo + 256 * ea->valuelen_hi;
}
static inline struct extended_attribute *next_ea(struct extended_attribute *ea)
{
- return (struct extended_attribute *)((char *)ea + 5 + ea->namelen + ea->valuelen);
+ return (struct extended_attribute *)((char *)ea + 5 + ea->namelen + ea_valuelen(ea));
}
static inline secno ea_sec(struct extended_attribute *ea)
{
- return *(secno *)((char *)ea + 9 + ea->namelen);
+ return le32_to_cpu(get_unaligned((secno *)((char *)ea + 9 + ea->namelen)));
}
static inline secno ea_len(struct extended_attribute *ea)
{
- return *(secno *)((char *)ea + 5 + ea->namelen);
+ return le32_to_cpu(get_unaligned((secno *)((char *)ea + 5 + ea->namelen)));
}
static inline char *ea_data(struct extended_attribute *ea)
dst->not_8x3 = n;
}
-static inline unsigned tstbits(unsigned *bmp, unsigned b, unsigned n)
+static inline unsigned tstbits(u32 *bmp, unsigned b, unsigned n)
{
int i;
if ((b >= 0x4000) || (b + n - 1 >= 0x4000)) return n;
- if (!((bmp[(b & 0x3fff) >> 5] >> (b & 0x1f)) & 1)) return 1;
+ if (!((le32_to_cpu(bmp[(b & 0x3fff) >> 5]) >> (b & 0x1f)) & 1)) return 1;
for (i = 1; i < n; i++)
- if (/*b+i < 0x4000 &&*/ !((bmp[((b+i) & 0x3fff) >> 5] >> ((b+i) & 0x1f)) & 1))
+ if (!((le32_to_cpu(bmp[((b+i) & 0x3fff) >> 5]) >> ((b+i) & 0x1f)) & 1))
return i + 1;
return 0;
}
/* alloc.c */
int hpfs_chk_sectors(struct super_block *, secno, int, char *);
-secno hpfs_alloc_sector(struct super_block *, secno, unsigned, int, int);
+secno hpfs_alloc_sector(struct super_block *, secno, unsigned, int);
int hpfs_alloc_if_possible(struct super_block *, secno);
void hpfs_free_sectors(struct super_block *, secno, unsigned);
int hpfs_check_free_dnodes(struct super_block *, int);
void hpfs_free_dnode(struct super_block *, secno);
-struct dnode *hpfs_alloc_dnode(struct super_block *, secno, dnode_secno *, struct quad_buffer_head *, int);
+struct dnode *hpfs_alloc_dnode(struct super_block *, secno, dnode_secno *, struct quad_buffer_head *);
struct fnode *hpfs_alloc_fnode(struct super_block *, secno, fnode_secno *, struct buffer_head **);
struct anode *hpfs_alloc_anode(struct super_block *, secno, anode_secno *, struct buffer_head **);
/* buffer.c */
-void hpfs_lock_creation(struct super_block *);
-void hpfs_unlock_creation(struct super_block *);
void *hpfs_map_sector(struct super_block *, unsigned, struct buffer_head **, int);
void *hpfs_get_sector(struct super_block *, unsigned, struct buffer_head **);
void *hpfs_map_4sectors(struct super_block *, unsigned, struct quad_buffer_head *, int);
struct hpfs_dirent *hpfs_add_de(struct super_block *, struct dnode *,
const unsigned char *, unsigned, secno);
int hpfs_add_dirent(struct inode *, const unsigned char *, unsigned,
- struct hpfs_dirent *, int);
+ struct hpfs_dirent *);
int hpfs_remove_dirent(struct inode *, dnode_secno, struct hpfs_dirent *, struct quad_buffer_head *, int);
void hpfs_count_dnodes(struct super_block *, dnode_secno, int *, int *, int *);
dnode_secno hpfs_de_as_down_as_possible(struct super_block *, dnode_secno dno);
const unsigned char *, unsigned, int);
int hpfs_is_name_long(const unsigned char *, unsigned);
void hpfs_adjust_length(const unsigned char *, unsigned *);
-void hpfs_decide_conv(struct inode *, const unsigned char *, unsigned);
/* namei.c */
/*
* Locking:
*
- * hpfs_lock() is a leftover from the big kernel lock.
- * Right now, these functions are empty and only left
- * for documentation purposes. The file system no longer
- * works on SMP systems, so the lock is not needed
- * any more.
+ * hpfs_lock() locks the whole filesystem. It must be taken
+ * on any method called by the VFS.
*
- * If someone is interested in making it work again, this
- * would be the place to start by adding a per-superblock
- * mutex and fixing all the bugs and performance issues
- * caused by that.
+ * We don't do any per-file locking anymore, it is hard to
+ * review and HPFS is not performance-sensitive anyway.
*/
static inline void hpfs_lock(struct super_block *s)
{
+ struct hpfs_sb_info *sbi = hpfs_sb(s);
+ mutex_lock(&sbi->hpfs_mutex);
}
static inline void hpfs_unlock(struct super_block *s)
{
+ struct hpfs_sb_info *sbi = hpfs_sb(s);
+ mutex_unlock(&sbi->hpfs_mutex);
+}
+
+static inline void hpfs_lock_assert(struct super_block *s)
+{
+ struct hpfs_sb_info *sbi = hpfs_sb(s);
+ WARN_ON(!mutex_is_locked(&sbi->hpfs_mutex));
}
i->i_uid = hpfs_sb(sb)->sb_uid;
i->i_gid = hpfs_sb(sb)->sb_gid;
i->i_mode = hpfs_sb(sb)->sb_mode;
- hpfs_inode->i_conv = hpfs_sb(sb)->sb_conv;
i->i_size = -1;
i->i_blocks = -1;
i->i_mode |= S_IFDIR;
i->i_op = &hpfs_dir_iops;
i->i_fop = &hpfs_dir_ops;
- hpfs_inode->i_parent_dir = fnode->up;
- hpfs_inode->i_dno = fnode->u.external[0].disk_secno;
+ hpfs_inode->i_parent_dir = le32_to_cpu(fnode->up);
+ hpfs_inode->i_dno = le32_to_cpu(fnode->u.external[0].disk_secno);
if (hpfs_sb(sb)->sb_chk >= 2) {
struct buffer_head *bh0;
if (hpfs_map_fnode(sb, hpfs_inode->i_parent_dir, &bh0)) brelse(bh0);
i->i_op = &hpfs_file_iops;
i->i_fop = &hpfs_file_ops;
i->i_nlink = 1;
- i->i_size = fnode->file_size;
+ i->i_size = le32_to_cpu(fnode->file_size);
i->i_blocks = ((i->i_size + 511) >> 9) + 1;
i->i_data.a_ops = &hpfs_aops;
hpfs_i(i)->mmu_private = i->i_size;
static void hpfs_write_inode_ea(struct inode *i, struct fnode *fnode)
{
struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
- /*if (fnode->acl_size_l || fnode->acl_size_s) {
+ /*if (le32_to_cpu(fnode->acl_size_l) || le16_to_cpu(fnode->acl_size_s)) {
Some unknown structures like ACL may be in fnode,
we'd better not overwrite them
hpfs_error(i->i_sb, "fnode %08x has some unknown HPFS386 stuctures", i->i_ino);
kfree(hpfs_inode->i_rddir_off);
hpfs_inode->i_rddir_off = NULL;
}
- mutex_lock(&hpfs_inode->i_parent_mutex);
if (!i->i_nlink) {
- mutex_unlock(&hpfs_inode->i_parent_mutex);
return;
}
parent = iget_locked(i->i_sb, hpfs_inode->i_parent_dir);
hpfs_read_inode(parent);
unlock_new_inode(parent);
}
- mutex_lock(&hpfs_inode->i_mutex);
hpfs_write_inode_nolock(i);
- mutex_unlock(&hpfs_inode->i_mutex);
iput(parent);
- } else {
- mark_inode_dirty(i);
}
- mutex_unlock(&hpfs_inode->i_parent_mutex);
}
void hpfs_write_inode_nolock(struct inode *i)
}
} else de = NULL;
if (S_ISREG(i->i_mode)) {
- fnode->file_size = i->i_size;
- if (de) de->file_size = i->i_size;
+ fnode->file_size = cpu_to_le32(i->i_size);
+ if (de) de->file_size = cpu_to_le32(i->i_size);
} else if (S_ISDIR(i->i_mode)) {
- fnode->file_size = 0;
- if (de) de->file_size = 0;
+ fnode->file_size = cpu_to_le32(0);
+ if (de) de->file_size = cpu_to_le32(0);
}
hpfs_write_inode_ea(i, fnode);
if (de) {
- de->write_date = gmt_to_local(i->i_sb, i->i_mtime.tv_sec);
- de->read_date = gmt_to_local(i->i_sb, i->i_atime.tv_sec);
- de->creation_date = gmt_to_local(i->i_sb, i->i_ctime.tv_sec);
+ de->write_date = cpu_to_le32(gmt_to_local(i->i_sb, i->i_mtime.tv_sec));
+ de->read_date = cpu_to_le32(gmt_to_local(i->i_sb, i->i_atime.tv_sec));
+ de->creation_date = cpu_to_le32(gmt_to_local(i->i_sb, i->i_ctime.tv_sec));
de->read_only = !(i->i_mode & 0222);
- de->ea_size = hpfs_inode->i_ea_size;
+ de->ea_size = cpu_to_le32(hpfs_inode->i_ea_size);
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
}
if (S_ISDIR(i->i_mode)) {
if ((de = map_dirent(i, hpfs_inode->i_dno, "\001\001", 2, NULL, &qbh))) {
- de->write_date = gmt_to_local(i->i_sb, i->i_mtime.tv_sec);
- de->read_date = gmt_to_local(i->i_sb, i->i_atime.tv_sec);
- de->creation_date = gmt_to_local(i->i_sb, i->i_ctime.tv_sec);
+ de->write_date = cpu_to_le32(gmt_to_local(i->i_sb, i->i_mtime.tv_sec));
+ de->read_date = cpu_to_le32(gmt_to_local(i->i_sb, i->i_atime.tv_sec));
+ de->creation_date = cpu_to_le32(gmt_to_local(i->i_sb, i->i_ctime.tv_sec));
de->read_only = !(i->i_mode & 0222);
- de->ea_size = /*hpfs_inode->i_ea_size*/0;
- de->file_size = 0;
+ de->ea_size = cpu_to_le32(/*hpfs_inode->i_ea_size*/0);
+ de->file_size = cpu_to_le32(0);
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
} else
hpfs_lock(inode->i_sb);
if (inode->i_ino == hpfs_sb(inode->i_sb)->sb_root)
goto out_unlock;
+ if ((attr->ia_valid & ATTR_UID) && attr->ia_uid >= 0x10000)
+ goto out_unlock;
+ if ((attr->ia_valid & ATTR_GID) && attr->ia_gid >= 0x10000)
+ goto out_unlock;
if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size > inode->i_size)
goto out_unlock;
}
setattr_copy(inode, attr);
- mark_inode_dirty(inode);
hpfs_write_inode(inode);
hpfs_error(s, "hpfs_map_bitmap called with bad parameter: %08x at %s", bmp_block, id);
return NULL;
}
- sec = hpfs_sb(s)->sb_bmp_dir[bmp_block];
+ sec = le32_to_cpu(hpfs_sb(s)->sb_bmp_dir[bmp_block]);
if (!sec || sec > hpfs_sb(s)->sb_fs_size-4) {
hpfs_error(s, "invalid bitmap block pointer %08x -> %08x at %s", bmp_block, sec, id);
return NULL;
struct code_page_data *cpd;
struct code_page_directory *cp = hpfs_map_sector(s, cps, &bh, 0);
if (!cp) return NULL;
- if (cp->magic != CP_DIR_MAGIC) {
- printk("HPFS: Code page directory magic doesn't match (magic = %08x)\n", cp->magic);
+ if (le32_to_cpu(cp->magic) != CP_DIR_MAGIC) {
+ printk("HPFS: Code page directory magic doesn't match (magic = %08x)\n", le32_to_cpu(cp->magic));
brelse(bh);
return NULL;
}
- if (!cp->n_code_pages) {
+ if (!le32_to_cpu(cp->n_code_pages)) {
printk("HPFS: n_code_pages == 0\n");
brelse(bh);
return NULL;
}
- cpds = cp->array[0].code_page_data;
- cpi = cp->array[0].index;
+ cpds = le32_to_cpu(cp->array[0].code_page_data);
+ cpi = le16_to_cpu(cp->array[0].index);
brelse(bh);
if (cpi >= 3) {
}
if (!(cpd = hpfs_map_sector(s, cpds, &bh, 0))) return NULL;
- if ((unsigned)cpd->offs[cpi] > 0x178) {
+ if (le16_to_cpu(cpd->offs[cpi]) > 0x178) {
printk("HPFS: Code page index out of sector\n");
brelse(bh);
return NULL;
}
- ptr = (unsigned char *)cpd + cpd->offs[cpi] + 6;
+ ptr = (unsigned char *)cpd + le16_to_cpu(cpd->offs[cpi]) + 6;
if (!(cp_table = kmalloc(256, GFP_KERNEL))) {
printk("HPFS: out of memory for code page table\n");
brelse(bh);
if (hpfs_sb(s)->sb_chk) {
struct extended_attribute *ea;
struct extended_attribute *ea_end;
- if (fnode->magic != FNODE_MAGIC) {
+ if (le32_to_cpu(fnode->magic) != FNODE_MAGIC) {
hpfs_error(s, "bad magic on fnode %08lx",
(unsigned long)ino);
goto bail;
(unsigned long)ino);
goto bail;
}
- if (fnode->btree.first_free !=
+ if (le16_to_cpu(fnode->btree.first_free) !=
8 + fnode->btree.n_used_nodes * (fnode->btree.internal ? 8 : 12)) {
hpfs_error(s,
"bad first_free pointer in fnode %08lx",
goto bail;
}
}
- if (fnode->ea_size_s && ((signed int)fnode->ea_offs < 0xc4 ||
- (signed int)fnode->ea_offs + fnode->acl_size_s + fnode->ea_size_s > 0x200)) {
+ if (le16_to_cpu(fnode->ea_size_s) && (le16_to_cpu(fnode->ea_offs) < 0xc4 ||
+ le16_to_cpu(fnode->ea_offs) + le16_to_cpu(fnode->acl_size_s) + le16_to_cpu(fnode->ea_size_s) > 0x200)) {
hpfs_error(s,
"bad EA info in fnode %08lx: ea_offs == %04x ea_size_s == %04x",
(unsigned long)ino,
- fnode->ea_offs, fnode->ea_size_s);
+ le16_to_cpu(fnode->ea_offs), le16_to_cpu(fnode->ea_size_s));
goto bail;
}
ea = fnode_ea(fnode);
if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, ano, 1, "anode")) return NULL;
if ((anode = hpfs_map_sector(s, ano, bhp, ANODE_RD_AHEAD)))
if (hpfs_sb(s)->sb_chk) {
- if (anode->magic != ANODE_MAGIC || anode->self != ano) {
+ if (le32_to_cpu(anode->magic) != ANODE_MAGIC) {
hpfs_error(s, "bad magic on anode %08x", ano);
goto bail;
}
+ if (le32_to_cpu(anode->self) != ano) {
+ hpfs_error(s, "self pointer invalid on anode %08x", ano);
+ goto bail;
+ }
if ((unsigned)anode->btree.n_used_nodes + (unsigned)anode->btree.n_free_nodes !=
(anode->btree.internal ? 60 : 40)) {
hpfs_error(s, "bad number of nodes in anode %08x", ano);
goto bail;
}
- if (anode->btree.first_free !=
+ if (le16_to_cpu(anode->btree.first_free) !=
8 + anode->btree.n_used_nodes * (anode->btree.internal ? 8 : 12)) {
hpfs_error(s, "bad first_free pointer in anode %08x", ano);
goto bail;
unsigned p, pp = 0;
unsigned char *d = (unsigned char *)dnode;
int b = 0;
- if (dnode->magic != DNODE_MAGIC) {
+ if (le32_to_cpu(dnode->magic) != DNODE_MAGIC) {
hpfs_error(s, "bad magic on dnode %08x", secno);
goto bail;
}
- if (dnode->self != secno)
- hpfs_error(s, "bad self pointer on dnode %08x self = %08x", secno, dnode->self);
+ if (le32_to_cpu(dnode->self) != secno)
+ hpfs_error(s, "bad self pointer on dnode %08x self = %08x", secno, le32_to_cpu(dnode->self));
/* Check dirents - bad dirents would cause infinite
loops or shooting to memory */
- if (dnode->first_free > 2048/* || dnode->first_free < 84*/) {
- hpfs_error(s, "dnode %08x has first_free == %08x", secno, dnode->first_free);
+ if (le32_to_cpu(dnode->first_free) > 2048) {
+ hpfs_error(s, "dnode %08x has first_free == %08x", secno, le32_to_cpu(dnode->first_free));
goto bail;
}
- for (p = 20; p < dnode->first_free; p += d[p] + (d[p+1] << 8)) {
+ for (p = 20; p < le32_to_cpu(dnode->first_free); p += d[p] + (d[p+1] << 8)) {
struct hpfs_dirent *de = (struct hpfs_dirent *)((char *)dnode + p);
- if (de->length > 292 || (de->length < 32) || (de->length & 3) || p + de->length > 2048) {
+ if (le16_to_cpu(de->length) > 292 || (le16_to_cpu(de->length) < 32) || (le16_to_cpu(de->length) & 3) || p + le16_to_cpu(de->length) > 2048) {
hpfs_error(s, "bad dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp);
goto bail;
}
- if (((31 + de->namelen + de->down*4 + 3) & ~3) != de->length) {
- if (((31 + de->namelen + de->down*4 + 3) & ~3) < de->length && s->s_flags & MS_RDONLY) goto ok;
+ if (((31 + de->namelen + de->down*4 + 3) & ~3) != le16_to_cpu(de->length)) {
+ if (((31 + de->namelen + de->down*4 + 3) & ~3) < le16_to_cpu(de->length) && s->s_flags & MS_RDONLY) goto ok;
hpfs_error(s, "namelen does not match dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp);
goto bail;
}
pp = p;
}
- if (p != dnode->first_free) {
+ if (p != le32_to_cpu(dnode->first_free)) {
hpfs_error(s, "size on last dirent does not match first_free; dnode %08x", secno);
goto bail;
}
if (!fnode)
return 0;
- dno = fnode->u.external[0].disk_secno;
+ dno = le32_to_cpu(fnode->u.external[0].disk_secno);
brelse(bh);
return dno;
}
#include "hpfs_fn.h"
-static const char *text_postfix[]={
-".ASM", ".BAS", ".BAT", ".C", ".CC", ".CFG", ".CMD", ".CON", ".CPP", ".DEF",
-".DOC", ".DPR", ".ERX", ".H", ".HPP", ".HTM", ".HTML", ".JAVA", ".LOG", ".PAS",
-".RC", ".TEX", ".TXT", ".Y", ""};
-
-static const char *text_prefix[]={
-"AUTOEXEC.", "CHANGES", "COPYING", "CONFIG.", "CREDITS", "FAQ", "FILE_ID.DIZ",
-"MAKEFILE", "READ.ME", "README", "TERMCAP", ""};
-
-void hpfs_decide_conv(struct inode *inode, const unsigned char *name, unsigned len)
-{
- struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
- int i;
- if (hpfs_inode->i_conv != CONV_AUTO) return;
- for (i = 0; *text_postfix[i]; i++) {
- int l = strlen(text_postfix[i]);
- if (l <= len)
- if (!hpfs_compare_names(inode->i_sb, text_postfix[i], l, name + len - l, l, 0))
- goto text;
- }
- for (i = 0; *text_prefix[i]; i++) {
- int l = strlen(text_prefix[i]);
- if (l <= len)
- if (!hpfs_compare_names(inode->i_sb, text_prefix[i], l, name, l, 0))
- goto text;
- }
- hpfs_inode->i_conv = CONV_BINARY;
- return;
- text:
- hpfs_inode->i_conv = CONV_TEXT;
- return;
-}
-
static inline int not_allowed_char(unsigned char c)
{
return c<' ' || c=='"' || c=='*' || c=='/' || c==':' || c=='<' ||
fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh);
if (!fnode)
goto bail;
- dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0, 1);
+ dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0);
if (!dnode)
goto bail1;
memset(&dee, 0, sizeof dee);
if (!(mode & 0222)) dee.read_only = 1;
/*dee.archive = 0;*/
dee.hidden = name[0] == '.';
- dee.fnode = fno;
- dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds());
+ dee.fnode = cpu_to_le32(fno);
+ dee.creation_date = dee.write_date = dee.read_date = cpu_to_le32(gmt_to_local(dir->i_sb, get_seconds()));
result = new_inode(dir->i_sb);
if (!result)
goto bail2;
result->i_ino = fno;
hpfs_i(result)->i_parent_dir = dir->i_ino;
hpfs_i(result)->i_dno = dno;
- result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, dee.creation_date);
+ result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, le32_to_cpu(dee.creation_date));
result->i_ctime.tv_nsec = 0;
result->i_mtime.tv_nsec = 0;
result->i_atime.tv_nsec = 0;
if (dee.read_only)
result->i_mode &= ~0222;
- mutex_lock(&hpfs_i(dir)->i_mutex);
- r = hpfs_add_dirent(dir, name, len, &dee, 0);
+ r = hpfs_add_dirent(dir, name, len, &dee);
if (r == 1)
goto bail3;
if (r == -1) {
}
fnode->len = len;
memcpy(fnode->name, name, len > 15 ? 15 : len);
- fnode->up = dir->i_ino;
+ fnode->up = cpu_to_le32(dir->i_ino);
fnode->dirflag = 1;
fnode->btree.n_free_nodes = 7;
fnode->btree.n_used_nodes = 1;
- fnode->btree.first_free = 0x14;
- fnode->u.external[0].disk_secno = dno;
- fnode->u.external[0].file_secno = -1;
+ fnode->btree.first_free = cpu_to_le16(0x14);
+ fnode->u.external[0].disk_secno = cpu_to_le32(dno);
+ fnode->u.external[0].file_secno = cpu_to_le32(-1);
dnode->root_dnode = 1;
- dnode->up = fno;
+ dnode->up = cpu_to_le32(fno);
de = hpfs_add_de(dir->i_sb, dnode, "\001\001", 2, 0);
- de->creation_date = de->write_date = de->read_date = gmt_to_local(dir->i_sb, get_seconds());
+ de->creation_date = de->write_date = de->read_date = cpu_to_le32(gmt_to_local(dir->i_sb, get_seconds()));
if (!(mode & 0222)) de->read_only = 1;
de->first = de->directory = 1;
/*de->hidden = de->system = 0;*/
- de->fnode = fno;
+ de->fnode = cpu_to_le32(fno);
mark_buffer_dirty(bh);
brelse(bh);
hpfs_mark_4buffers_dirty(&qbh0);
hpfs_write_inode_nolock(result);
}
d_instantiate(dentry, result);
- mutex_unlock(&hpfs_i(dir)->i_mutex);
hpfs_unlock(dir->i_sb);
return 0;
bail3:
- mutex_unlock(&hpfs_i(dir)->i_mutex);
iput(result);
bail2:
hpfs_brelse4(&qbh0);
if (!(mode & 0222)) dee.read_only = 1;
dee.archive = 1;
dee.hidden = name[0] == '.';
- dee.fnode = fno;
- dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds());
+ dee.fnode = cpu_to_le32(fno);
+ dee.creation_date = dee.write_date = dee.read_date = cpu_to_le32(gmt_to_local(dir->i_sb, get_seconds()));
result = new_inode(dir->i_sb);
if (!result)
result->i_op = &hpfs_file_iops;
result->i_fop = &hpfs_file_ops;
result->i_nlink = 1;
- hpfs_decide_conv(result, name, len);
hpfs_i(result)->i_parent_dir = dir->i_ino;
- result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, dee.creation_date);
+ result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, le32_to_cpu(dee.creation_date));
result->i_ctime.tv_nsec = 0;
result->i_mtime.tv_nsec = 0;
result->i_atime.tv_nsec = 0;
result->i_data.a_ops = &hpfs_aops;
hpfs_i(result)->mmu_private = 0;
- mutex_lock(&hpfs_i(dir)->i_mutex);
- r = hpfs_add_dirent(dir, name, len, &dee, 0);
+ r = hpfs_add_dirent(dir, name, len, &dee);
if (r == 1)
goto bail2;
if (r == -1) {
}
fnode->len = len;
memcpy(fnode->name, name, len > 15 ? 15 : len);
- fnode->up = dir->i_ino;
+ fnode->up = cpu_to_le32(dir->i_ino);
mark_buffer_dirty(bh);
brelse(bh);
hpfs_write_inode_nolock(result);
}
d_instantiate(dentry, result);
- mutex_unlock(&hpfs_i(dir)->i_mutex);
hpfs_unlock(dir->i_sb);
return 0;
bail2:
- mutex_unlock(&hpfs_i(dir)->i_mutex);
iput(result);
bail1:
brelse(bh);
if (!(mode & 0222)) dee.read_only = 1;
dee.archive = 1;
dee.hidden = name[0] == '.';
- dee.fnode = fno;
- dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds());
+ dee.fnode = cpu_to_le32(fno);
+ dee.creation_date = dee.write_date = dee.read_date = cpu_to_le32(gmt_to_local(dir->i_sb, get_seconds()));
result = new_inode(dir->i_sb);
if (!result)
hpfs_init_inode(result);
result->i_ino = fno;
hpfs_i(result)->i_parent_dir = dir->i_ino;
- result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, dee.creation_date);
+ result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, le32_to_cpu(dee.creation_date));
result->i_ctime.tv_nsec = 0;
result->i_mtime.tv_nsec = 0;
result->i_atime.tv_nsec = 0;
result->i_blocks = 1;
init_special_inode(result, mode, rdev);
- mutex_lock(&hpfs_i(dir)->i_mutex);
- r = hpfs_add_dirent(dir, name, len, &dee, 0);
+ r = hpfs_add_dirent(dir, name, len, &dee);
if (r == 1)
goto bail2;
if (r == -1) {
}
fnode->len = len;
memcpy(fnode->name, name, len > 15 ? 15 : len);
- fnode->up = dir->i_ino;
+ fnode->up = cpu_to_le32(dir->i_ino);
mark_buffer_dirty(bh);
insert_inode_hash(result);
hpfs_write_inode_nolock(result);
d_instantiate(dentry, result);
- mutex_unlock(&hpfs_i(dir)->i_mutex);
brelse(bh);
hpfs_unlock(dir->i_sb);
return 0;
bail2:
- mutex_unlock(&hpfs_i(dir)->i_mutex);
iput(result);
bail1:
brelse(bh);
memset(&dee, 0, sizeof dee);
dee.archive = 1;
dee.hidden = name[0] == '.';
- dee.fnode = fno;
- dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds());
+ dee.fnode = cpu_to_le32(fno);
+ dee.creation_date = dee.write_date = dee.read_date = cpu_to_le32(gmt_to_local(dir->i_sb, get_seconds()));
result = new_inode(dir->i_sb);
if (!result)
result->i_ino = fno;
hpfs_init_inode(result);
hpfs_i(result)->i_parent_dir = dir->i_ino;
- result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, dee.creation_date);
+ result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, le32_to_cpu(dee.creation_date));
result->i_ctime.tv_nsec = 0;
result->i_mtime.tv_nsec = 0;
result->i_atime.tv_nsec = 0;
result->i_op = &page_symlink_inode_operations;
result->i_data.a_ops = &hpfs_symlink_aops;
- mutex_lock(&hpfs_i(dir)->i_mutex);
- r = hpfs_add_dirent(dir, name, len, &dee, 0);
+ r = hpfs_add_dirent(dir, name, len, &dee);
if (r == 1)
goto bail2;
if (r == -1) {
}
fnode->len = len;
memcpy(fnode->name, name, len > 15 ? 15 : len);
- fnode->up = dir->i_ino;
+ fnode->up = cpu_to_le32(dir->i_ino);
hpfs_set_ea(result, fnode, "SYMLINK", symlink, strlen(symlink));
mark_buffer_dirty(bh);
brelse(bh);
hpfs_write_inode_nolock(result);
d_instantiate(dentry, result);
- mutex_unlock(&hpfs_i(dir)->i_mutex);
hpfs_unlock(dir->i_sb);
return 0;
bail2:
- mutex_unlock(&hpfs_i(dir)->i_mutex);
iput(result);
bail1:
brelse(bh);
struct hpfs_dirent *de;
struct inode *inode = dentry->d_inode;
dnode_secno dno;
- fnode_secno fno;
int r;
int rep = 0;
int err;
hpfs_lock(dir->i_sb);
hpfs_adjust_length(name, &len);
again:
- mutex_lock(&hpfs_i(inode)->i_parent_mutex);
- mutex_lock(&hpfs_i(dir)->i_mutex);
err = -ENOENT;
de = map_dirent(dir, hpfs_i(dir)->i_dno, name, len, &dno, &qbh);
if (!de)
if (de->directory)
goto out1;
- fno = de->fnode;
r = hpfs_remove_dirent(dir, dno, de, &qbh, 1);
switch (r) {
case 1:
if (rep++)
break;
- mutex_unlock(&hpfs_i(dir)->i_mutex);
- mutex_unlock(&hpfs_i(inode)->i_parent_mutex);
dentry_unhash(dentry);
if (!d_unhashed(dentry)) {
dput(dentry);
out1:
hpfs_brelse4(&qbh);
out:
- mutex_unlock(&hpfs_i(dir)->i_mutex);
- mutex_unlock(&hpfs_i(inode)->i_parent_mutex);
hpfs_unlock(dir->i_sb);
return err;
}
struct hpfs_dirent *de;
struct inode *inode = dentry->d_inode;
dnode_secno dno;
- fnode_secno fno;
int n_items = 0;
int err;
int r;
hpfs_adjust_length(name, &len);
hpfs_lock(dir->i_sb);
- mutex_lock(&hpfs_i(inode)->i_parent_mutex);
- mutex_lock(&hpfs_i(dir)->i_mutex);
err = -ENOENT;
de = map_dirent(dir, hpfs_i(dir)->i_dno, name, len, &dno, &qbh);
if (!de)
if (n_items)
goto out1;
- fno = de->fnode;
r = hpfs_remove_dirent(dir, dno, de, &qbh, 1);
switch (r) {
case 1:
out1:
hpfs_brelse4(&qbh);
out:
- mutex_unlock(&hpfs_i(dir)->i_mutex);
- mutex_unlock(&hpfs_i(inode)->i_parent_mutex);
hpfs_unlock(dir->i_sb);
return err;
}
hpfs_lock(i->i_sb);
/* order doesn't matter, due to VFS exclusion */
- mutex_lock(&hpfs_i(i)->i_parent_mutex);
- if (new_inode)
- mutex_lock(&hpfs_i(new_inode)->i_parent_mutex);
- mutex_lock(&hpfs_i(old_dir)->i_mutex);
- if (new_dir != old_dir)
- mutex_lock(&hpfs_i(new_dir)->i_mutex);
/* Erm? Moving over the empty non-busy directory is perfectly legal */
if (new_inode && S_ISDIR(new_inode->i_mode)) {
if (new_dir == old_dir) hpfs_brelse4(&qbh);
- hpfs_lock_creation(i->i_sb);
- if ((r = hpfs_add_dirent(new_dir, new_name, new_len, &de, 1))) {
- hpfs_unlock_creation(i->i_sb);
+ if ((r = hpfs_add_dirent(new_dir, new_name, new_len, &de))) {
if (r == -1) hpfs_error(new_dir->i_sb, "hpfs_rename: dirent already exists!");
err = r == 1 ? -ENOSPC : -EFSERROR;
if (new_dir != old_dir) hpfs_brelse4(&qbh);
if (new_dir == old_dir)
if (!(dep = map_dirent(old_dir, hpfs_i(old_dir)->i_dno, old_name, old_len, &dno, &qbh))) {
- hpfs_unlock_creation(i->i_sb);
hpfs_error(i->i_sb, "lookup succeeded but map dirent failed at #2");
err = -ENOENT;
goto end1;
}
if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 0))) {
- hpfs_unlock_creation(i->i_sb);
hpfs_error(i->i_sb, "hpfs_rename: could not remove dirent");
err = r == 2 ? -ENOSPC : -EFSERROR;
goto end1;
}
- hpfs_unlock_creation(i->i_sb);
-
+
end:
hpfs_i(i)->i_parent_dir = new_dir->i_ino;
if (S_ISDIR(i->i_mode)) {
drop_nlink(old_dir);
}
if ((fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) {
- fnode->up = new_dir->i_ino;
+ fnode->up = cpu_to_le32(new_dir->i_ino);
fnode->len = new_len;
memcpy(fnode->name, new_name, new_len>15?15:new_len);
if (new_len < 15) memset(&fnode->name[new_len], 0, 15 - new_len);
mark_buffer_dirty(bh);
brelse(bh);
}
- hpfs_i(i)->i_conv = hpfs_sb(i->i_sb)->sb_conv;
- hpfs_decide_conv(i, new_name, new_len);
end1:
- if (old_dir != new_dir)
- mutex_unlock(&hpfs_i(new_dir)->i_mutex);
- mutex_unlock(&hpfs_i(old_dir)->i_mutex);
- mutex_unlock(&hpfs_i(i)->i_parent_mutex);
- if (new_inode)
- mutex_unlock(&hpfs_i(new_inode)->i_parent_mutex);
hpfs_unlock(i->i_sb);
return err;
}
/* Mark the filesystem dirty, so that chkdsk checks it when os/2 booted */
-static void mark_dirty(struct super_block *s)
+static void mark_dirty(struct super_block *s, int remount)
{
- if (hpfs_sb(s)->sb_chkdsk && !(s->s_flags & MS_RDONLY)) {
+ if (hpfs_sb(s)->sb_chkdsk && (remount || !(s->s_flags & MS_RDONLY))) {
struct buffer_head *bh;
struct hpfs_spare_block *sb;
if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
sb->dirty = 1;
sb->old_wrote = 0;
mark_buffer_dirty(bh);
+ sync_dirty_buffer(bh);
brelse(bh);
}
}
struct buffer_head *bh;
struct hpfs_spare_block *sb;
if (s->s_flags & MS_RDONLY) return;
+ sync_blockdev(s->s_bdev);
if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
sb->dirty = hpfs_sb(s)->sb_chkdsk > 1 - hpfs_sb(s)->sb_was_error;
sb->old_wrote = hpfs_sb(s)->sb_chkdsk >= 2 && !hpfs_sb(s)->sb_was_error;
mark_buffer_dirty(bh);
+ sync_dirty_buffer(bh);
brelse(bh);
}
}
if (!hpfs_sb(s)->sb_was_error) {
if (hpfs_sb(s)->sb_err == 2) {
printk("; crashing the system because you wanted it\n");
- mark_dirty(s);
+ mark_dirty(s, 0);
panic("HPFS panic");
} else if (hpfs_sb(s)->sb_err == 1) {
if (s->s_flags & MS_RDONLY) printk("; already mounted read-only\n");
else {
printk("; remounting read-only\n");
- mark_dirty(s);
+ mark_dirty(s, 0);
s->s_flags |= MS_RDONLY;
}
} else if (s->s_flags & MS_RDONLY) printk("; going on - but anything won't be destroyed because it's read-only\n");
{
struct hpfs_sb_info *sbi = hpfs_sb(s);
+ hpfs_lock(s);
+ unmark_dirty(s);
+ hpfs_unlock(s);
+
kfree(sbi->sb_cp_table);
kfree(sbi->sb_bmp_dir);
- unmark_dirty(s);
s->s_fs_info = NULL;
kfree(sbi);
}
n_bands = (hpfs_sb(s)->sb_fs_size + 0x3fff) >> 14;
count = 0;
for (n = 0; n < n_bands; n++)
- count += hpfs_count_one_bitmap(s, hpfs_sb(s)->sb_bmp_dir[n]);
+ count += hpfs_count_one_bitmap(s, le32_to_cpu(hpfs_sb(s)->sb_bmp_dir[n]));
return count;
}
{
struct hpfs_inode_info *ei = (struct hpfs_inode_info *) foo;
- mutex_init(&ei->i_mutex);
- mutex_init(&ei->i_parent_mutex);
inode_init_once(&ei->vfs_inode);
}
enum {
Opt_help, Opt_uid, Opt_gid, Opt_umask, Opt_case_lower, Opt_case_asis,
- Opt_conv_binary, Opt_conv_text, Opt_conv_auto,
Opt_check_none, Opt_check_normal, Opt_check_strict,
Opt_err_cont, Opt_err_ro, Opt_err_panic,
Opt_eas_no, Opt_eas_ro, Opt_eas_rw,
{Opt_umask, "umask=%o"},
{Opt_case_lower, "case=lower"},
{Opt_case_asis, "case=asis"},
- {Opt_conv_binary, "conv=binary"},
- {Opt_conv_text, "conv=text"},
- {Opt_conv_auto, "conv=auto"},
{Opt_check_none, "check=none"},
{Opt_check_normal, "check=normal"},
{Opt_check_strict, "check=strict"},
};
static int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
- int *lowercase, int *conv, int *eas, int *chk, int *errs,
+ int *lowercase, int *eas, int *chk, int *errs,
int *chkdsk, int *timeshift)
{
char *p;
case Opt_case_asis:
*lowercase = 0;
break;
- case Opt_conv_binary:
- *conv = CONV_BINARY;
- break;
- case Opt_conv_text:
- *conv = CONV_TEXT;
- break;
- case Opt_conv_auto:
- *conv = CONV_AUTO;
- break;
case Opt_check_none:
*chk = 0;
break;
umask=xxx set mode of files that don't have mode specified in eas\n\
case=lower lowercase all files\n\
case=asis do not lowercase files (default)\n\
- conv=binary do not convert CR/LF -> LF (default)\n\
- conv=auto convert only files with known text extensions\n\
- conv=text convert all files\n\
check=none no fs checks - kernel may crash on corrupted filesystem\n\
check=normal do some checks - it should not crash (default)\n\
check=strict do extra time-consuming checks, used for debugging\n\
uid_t uid;
gid_t gid;
umode_t umask;
- int lowercase, conv, eas, chk, errs, chkdsk, timeshift;
+ int lowercase, eas, chk, errs, chkdsk, timeshift;
int o;
struct hpfs_sb_info *sbi = hpfs_sb(s);
char *new_opts = kstrdup(data, GFP_KERNEL);
lock_super(s);
uid = sbi->sb_uid; gid = sbi->sb_gid;
umask = 0777 & ~sbi->sb_mode;
- lowercase = sbi->sb_lowercase; conv = sbi->sb_conv;
+ lowercase = sbi->sb_lowercase;
eas = sbi->sb_eas; chk = sbi->sb_chk; chkdsk = sbi->sb_chkdsk;
errs = sbi->sb_err; timeshift = sbi->sb_timeshift;
- if (!(o = parse_opts(data, &uid, &gid, &umask, &lowercase, &conv,
+ if (!(o = parse_opts(data, &uid, &gid, &umask, &lowercase,
&eas, &chk, &errs, &chkdsk, ×hift))) {
printk("HPFS: bad mount options.\n");
goto out_err;
sbi->sb_uid = uid; sbi->sb_gid = gid;
sbi->sb_mode = 0777 & ~umask;
- sbi->sb_lowercase = lowercase; sbi->sb_conv = conv;
+ sbi->sb_lowercase = lowercase;
sbi->sb_eas = eas; sbi->sb_chk = chk; sbi->sb_chkdsk = chkdsk;
sbi->sb_err = errs; sbi->sb_timeshift = timeshift;
- if (!(*flags & MS_RDONLY)) mark_dirty(s);
+ if (!(*flags & MS_RDONLY)) mark_dirty(s, 1);
replace_mount_options(s, new_opts);
uid_t uid;
gid_t gid;
umode_t umask;
- int lowercase, conv, eas, chk, errs, chkdsk, timeshift;
+ int lowercase, eas, chk, errs, chkdsk, timeshift;
dnode_secno root_dno;
struct hpfs_dirent *de = NULL;
int o;
- if (num_possible_cpus() > 1) {
- printk(KERN_ERR "HPFS is not SMP safe\n");
- return -EINVAL;
- }
-
save_mount_options(s, options);
sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
sbi->sb_bmp_dir = NULL;
sbi->sb_cp_table = NULL;
- mutex_init(&sbi->hpfs_creation_de);
+ mutex_init(&sbi->hpfs_mutex);
+ hpfs_lock(s);
uid = current_uid();
gid = current_gid();
umask = current_umask();
lowercase = 0;
- conv = CONV_BINARY;
eas = 2;
chk = 1;
errs = 1;
chkdsk = 1;
timeshift = 0;
- if (!(o = parse_opts(options, &uid, &gid, &umask, &lowercase, &conv,
+ if (!(o = parse_opts(options, &uid, &gid, &umask, &lowercase,
&eas, &chk, &errs, &chkdsk, ×hift))) {
printk("HPFS: bad mount options.\n");
goto bail0;
if (!(spareblock = hpfs_map_sector(s, 17, &bh2, 0))) goto bail3;
/* Check magics */
- if (/*bootblock->magic != BB_MAGIC
- ||*/ superblock->magic != SB_MAGIC
- || spareblock->magic != SP_MAGIC) {
+ if (/*le16_to_cpu(bootblock->magic) != BB_MAGIC
+ ||*/ le32_to_cpu(superblock->magic) != SB_MAGIC
+ || le32_to_cpu(spareblock->magic) != SP_MAGIC) {
if (!silent) printk("HPFS: Bad magic ... probably not HPFS\n");
goto bail4;
}
s->s_op = &hpfs_sops;
s->s_d_op = &hpfs_dentry_operations;
- sbi->sb_root = superblock->root;
- sbi->sb_fs_size = superblock->n_sectors;
- sbi->sb_bitmaps = superblock->bitmaps;
- sbi->sb_dirband_start = superblock->dir_band_start;
- sbi->sb_dirband_size = superblock->n_dir_band;
- sbi->sb_dmap = superblock->dir_band_bitmap;
+ sbi->sb_root = le32_to_cpu(superblock->root);
+ sbi->sb_fs_size = le32_to_cpu(superblock->n_sectors);
+ sbi->sb_bitmaps = le32_to_cpu(superblock->bitmaps);
+ sbi->sb_dirband_start = le32_to_cpu(superblock->dir_band_start);
+ sbi->sb_dirband_size = le32_to_cpu(superblock->n_dir_band);
+ sbi->sb_dmap = le32_to_cpu(superblock->dir_band_bitmap);
sbi->sb_uid = uid;
sbi->sb_gid = gid;
sbi->sb_mode = 0777 & ~umask;
sbi->sb_n_free = -1;
sbi->sb_n_free_dnodes = -1;
sbi->sb_lowercase = lowercase;
- sbi->sb_conv = conv;
sbi->sb_eas = eas;
sbi->sb_chk = chk;
sbi->sb_chkdsk = chkdsk;
sbi->sb_max_fwd_alloc = 0xffffff;
/* Load bitmap directory */
- if (!(sbi->sb_bmp_dir = hpfs_load_bitmap_directory(s, superblock->bitmaps)))
+ if (!(sbi->sb_bmp_dir = hpfs_load_bitmap_directory(s, le32_to_cpu(superblock->bitmaps))))
goto bail4;
/* Check for general fs errors*/
mark_buffer_dirty(bh2);
}
- if (spareblock->hotfixes_used || spareblock->n_spares_used) {
+ if (le32_to_cpu(spareblock->hotfixes_used) || le32_to_cpu(spareblock->n_spares_used)) {
if (errs >= 2) {
printk("HPFS: Hotfixes not supported here, try chkdsk\n");
- mark_dirty(s);
+ mark_dirty(s, 0);
goto bail4;
}
hpfs_error(s, "hotfixes not supported here, try chkdsk");
if (errs == 0) printk("HPFS: Proceeding, but your filesystem will be probably corrupted by this driver...\n");
else printk("HPFS: This driver may read bad files or crash when operating on disk with hotfixes.\n");
}
- if (spareblock->n_dnode_spares != spareblock->n_dnode_spares_free) {
+ if (le32_to_cpu(spareblock->n_dnode_spares) != le32_to_cpu(spareblock->n_dnode_spares_free)) {
if (errs >= 2) {
printk("HPFS: Spare dnodes used, try chkdsk\n");
- mark_dirty(s);
+ mark_dirty(s, 0);
goto bail4;
}
hpfs_error(s, "warning: spare dnodes used, try chkdsk");
}
if (chk) {
unsigned a;
- if (superblock->dir_band_end - superblock->dir_band_start + 1 != superblock->n_dir_band ||
- superblock->dir_band_end < superblock->dir_band_start || superblock->n_dir_band > 0x4000) {
+ if (le32_to_cpu(superblock->dir_band_end) - le32_to_cpu(superblock->dir_band_start) + 1 != le32_to_cpu(superblock->n_dir_band) ||
+ le32_to_cpu(superblock->dir_band_end) < le32_to_cpu(superblock->dir_band_start) || le32_to_cpu(superblock->n_dir_band) > 0x4000) {
hpfs_error(s, "dir band size mismatch: dir_band_start==%08x, dir_band_end==%08x, n_dir_band==%08x",
- superblock->dir_band_start, superblock->dir_band_end, superblock->n_dir_band);
+ le32_to_cpu(superblock->dir_band_start), le32_to_cpu(superblock->dir_band_end), le32_to_cpu(superblock->n_dir_band));
goto bail4;
}
a = sbi->sb_dirband_size;
sbi->sb_dirband_size = 0;
- if (hpfs_chk_sectors(s, superblock->dir_band_start, superblock->n_dir_band, "dir_band") ||
- hpfs_chk_sectors(s, superblock->dir_band_bitmap, 4, "dir_band_bitmap") ||
- hpfs_chk_sectors(s, superblock->bitmaps, 4, "bitmaps")) {
- mark_dirty(s);
+ if (hpfs_chk_sectors(s, le32_to_cpu(superblock->dir_band_start), le32_to_cpu(superblock->n_dir_band), "dir_band") ||
+ hpfs_chk_sectors(s, le32_to_cpu(superblock->dir_band_bitmap), 4, "dir_band_bitmap") ||
+ hpfs_chk_sectors(s, le32_to_cpu(superblock->bitmaps), 4, "bitmaps")) {
+ mark_dirty(s, 0);
goto bail4;
}
sbi->sb_dirband_size = a;
} else printk("HPFS: You really don't want any checks? You are crazy...\n");
/* Load code page table */
- if (spareblock->n_code_pages)
- if (!(sbi->sb_cp_table = hpfs_load_code_page(s, spareblock->code_page_dir)))
+ if (le32_to_cpu(spareblock->n_code_pages))
+ if (!(sbi->sb_cp_table = hpfs_load_code_page(s, le32_to_cpu(spareblock->code_page_dir))))
printk("HPFS: Warning: code page support is disabled\n");
brelse(bh2);
if (!de)
hpfs_error(s, "unable to find root dir");
else {
- root->i_atime.tv_sec = local_to_gmt(s, de->read_date);
+ root->i_atime.tv_sec = local_to_gmt(s, le32_to_cpu(de->read_date));
root->i_atime.tv_nsec = 0;
- root->i_mtime.tv_sec = local_to_gmt(s, de->write_date);
+ root->i_mtime.tv_sec = local_to_gmt(s, le32_to_cpu(de->write_date));
root->i_mtime.tv_nsec = 0;
- root->i_ctime.tv_sec = local_to_gmt(s, de->creation_date);
+ root->i_ctime.tv_sec = local_to_gmt(s, le32_to_cpu(de->creation_date));
root->i_ctime.tv_nsec = 0;
- hpfs_i(root)->i_ea_size = de->ea_size;
+ hpfs_i(root)->i_ea_size = le16_to_cpu(de->ea_size);
hpfs_i(root)->i_parent_dir = root->i_ino;
if (root->i_size == -1)
root->i_size = 2048;
root->i_blocks = 5;
hpfs_brelse4(&qbh);
}
+ hpfs_unlock(s);
return 0;
bail4: brelse(bh2);
bail2: brelse(bh0);
bail1:
bail0:
+ hpfs_unlock(s);
kfree(sbi->sb_bmp_dir);
kfree(sbi->sb_cp_table);
s->s_fs_info = NULL;
!read_only)
return -EIO;
- mutex_init(&super->s_dirop_mutex);
- mutex_init(&super->s_object_alias_mutex);
- INIT_LIST_HEAD(&super->s_freeing_list);
-
ret = logfs_init_rw(sb);
if (ret)
return ret;
if (!super)
return ERR_PTR(-ENOMEM);
+ mutex_init(&super->s_dirop_mutex);
+ mutex_init(&super->s_object_alias_mutex);
+ INIT_LIST_HEAD(&super->s_freeing_list);
+
if (!devname)
err = logfs_get_sb_bdev(super, type, devname);
else if (strncmp(devname, "mtd", 3))
do {
seq = read_seqcount_begin(&fs->seq);
nd->root = fs->root;
+ nd->seq = __read_seqcount_begin(&nd->root.dentry->d_seq);
} while (read_seqcount_retry(&fs->seq, seq));
}
}
.show = show_vfsmnt
};
-static int uuid_is_nil(u8 *uuid)
-{
- int i;
- u8 *cp = (u8 *)uuid;
-
- for (i = 0; i < 16; i++) {
- if (*cp++)
- return 0;
- }
- return 1;
-}
-
static int show_mountinfo(struct seq_file *m, void *v)
{
struct proc_mounts *p = m->private;
if (IS_MNT_UNBINDABLE(mnt))
seq_puts(m, " unbindable");
- if (!uuid_is_nil(mnt->mnt_sb->s_uuid))
- /* print the uuid */
- seq_printf(m, " uuid:%pU", mnt->mnt_sb->s_uuid);
-
/* Filesystem specific data */
seq_puts(m, " - ");
show_type(m, sb);
}
#ifdef CONFIG_NFS_V4
-static rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors, struct inode *inode)
+static rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
{
struct gss_api_mech *mech;
struct xdr_netobj oid;
}
flavors = page_address(page);
ret = secinfo(parent->d_inode, &dentry->d_name, flavors);
- *flavor = nfs_find_best_sec(flavors, dentry->d_inode);
+ *flavor = nfs_find_best_sec(flavors);
put_page(page);
}
NFS4CLNT_LAYOUTRECALL,
NFS4CLNT_SESSION_RESET,
NFS4CLNT_RECALL_SLOT,
+ NFS4CLNT_LEASE_CONFIRM,
};
enum nfs4_session_state {
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_page.h>
+#include <linux/nfs_mount.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/module.h>
if (res->sr_status == 1)
res->sr_status = NFS_OK;
- /* -ERESTARTSYS can result in skipping nfs41_sequence_setup */
- if (!res->sr_slot)
+ /* don't increment the sequence number if the task wasn't sent */
+ if (!RPC_WAS_SENT(task))
goto out;
/* Check the SEQUENCE operation status */
struct nfs4_exception exception = { };
int err;
do {
- err = nfs4_handle_exception(server,
- _nfs4_lookup_root(server, fhandle, info),
- &exception);
+ err = _nfs4_lookup_root(server, fhandle, info);
+ switch (err) {
+ case 0:
+ case -NFS4ERR_WRONGSEC:
+ break;
+ default:
+ err = nfs4_handle_exception(server, err, &exception);
+ }
} while (exception.retry);
return err;
}
return ret;
}
-/*
- * get the file handle for the "/" directory on the server
- */
-static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
+static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *info)
{
int i, len, status = 0;
- rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS + 2];
+ rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS];
- flav_array[0] = RPC_AUTH_UNIX;
- len = gss_mech_list_pseudoflavors(&flav_array[1]);
- flav_array[1+len] = RPC_AUTH_NULL;
- len += 2;
+ len = gss_mech_list_pseudoflavors(&flav_array[0]);
+ flav_array[len] = RPC_AUTH_NULL;
+ len += 1;
for (i = 0; i < len; i++) {
status = nfs4_lookup_root_sec(server, fhandle, info, flav_array[i]);
- if (status != -EPERM)
- break;
+ if (status == -NFS4ERR_WRONGSEC || status == -EACCES)
+ continue;
+ break;
}
+ /*
+ * -EACCESS could mean that the user doesn't have correct permissions
+ * to access the mount. It could also mean that we tried to mount
+ * with a gss auth flavor, but rpc.gssd isn't running. Either way,
+ * existing mount programs don't handle -EACCES very well so it should
+ * be mapped to -EPERM instead.
+ */
+ if (status == -EACCES)
+ status = -EPERM;
+ return status;
+}
+
+/*
+ * get the file handle for the "/" directory on the server
+ */
+static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fsinfo *info)
+{
+ int status = nfs4_lookup_root(server, fhandle, info);
+ if ((status == -NFS4ERR_WRONGSEC) && !(server->flags & NFS_MOUNT_SECFLAVOUR))
+ /*
+ * A status of -NFS4ERR_WRONGSEC will be mapped to -EPERM
+ * by nfs4_map_errors() as this function exits.
+ */
+ status = nfs4_find_root_sec(server, fhandle, info);
if (status == 0)
status = nfs4_server_capabilities(server, fhandle);
if (status == 0)
sizeof(setclientid.sc_uaddr), "%s.%u.%u",
clp->cl_ipaddr, port >> 8, port & 255);
- status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
+ status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
if (status != -NFS4ERR_CLID_INUSE)
break;
- if (signalled())
+ if (loop != 0) {
+ ++clp->cl_id_uniquifier;
break;
- if (loop++ & 1)
- ssleep(clp->cl_lease_time / HZ + 1);
- else
- if (++clp->cl_id_uniquifier == 0)
- break;
+ }
+ ++loop;
+ ssleep(clp->cl_lease_time / HZ + 1);
}
return status;
}
-static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp,
+int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
struct nfs4_setclientid_res *arg,
struct rpc_cred *cred)
{
int status;
now = jiffies;
- status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
+ status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
if (status == 0) {
spin_lock(&clp->cl_lock);
clp->cl_lease_time = fsinfo.lease_time * HZ;
return status;
}
-int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
- struct nfs4_setclientid_res *arg,
- struct rpc_cred *cred)
-{
- long timeout = 0;
- int err;
- do {
- err = _nfs4_proc_setclientid_confirm(clp, arg, cred);
- switch (err) {
- case 0:
- return err;
- case -NFS4ERR_RESOURCE:
- /* The IBM lawyers misread another document! */
- case -NFS4ERR_DELAY:
- err = nfs4_delay(clp->cl_rpcclient, &timeout);
- }
- } while (err == 0);
- return err;
-}
-
struct nfs4_delegreturndata {
struct nfs4_delegreturnargs args;
struct nfs4_delegreturnres res;
init_utsname()->domainname,
clp->cl_rpcclient->cl_auth->au_flavor);
- status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
+ status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
if (!status)
status = nfs4_check_cl_exchange_flags(clp->cl_exchange_flags);
dprintk("<-- %s status= %d\n", __func__, status);
.rpc_client = clp->cl_rpcclient,
.rpc_message = &msg,
.callback_ops = &nfs4_get_lease_time_ops,
- .callback_data = &data
+ .callback_data = &data,
+ .flags = RPC_TASK_TIMEOUT,
};
int status;
nfs4_init_channel_attrs(&args);
args.flags = (SESSION4_PERSIST | SESSION4_BACK_CHAN);
- status = rpc_call_sync(session->clp->cl_rpcclient, &msg, 0);
+ status = rpc_call_sync(session->clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
if (!status)
/* Verify the session's negotiated channel_attrs values */
int status;
unsigned *ptr;
struct nfs4_session *session = clp->cl_session;
- long timeout = 0;
- int err;
dprintk("--> %s clp=%p session=%p\n", __func__, clp, session);
- do {
- status = _nfs4_proc_create_session(clp);
- if (status == -NFS4ERR_DELAY) {
- err = nfs4_delay(clp->cl_rpcclient, &timeout);
- if (err)
- status = err;
- }
- } while (status == -NFS4ERR_DELAY);
-
+ status = _nfs4_proc_create_session(clp);
if (status)
goto out;
msg.rpc_argp = session;
msg.rpc_resp = NULL;
msg.rpc_cred = NULL;
- status = rpc_call_sync(session->clp->cl_rpcclient, &msg, 0);
+ status = rpc_call_sync(session->clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
if (status)
printk(KERN_WARNING
int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
{
- struct nfs4_setclientid_res clid;
+ struct nfs4_setclientid_res clid = {
+ .clientid = clp->cl_clientid,
+ .confirm = clp->cl_confirm,
+ };
unsigned short port;
int status;
+ if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state))
+ goto do_confirm;
port = nfs_callback_tcpport;
if (clp->cl_addr.ss_family == AF_INET6)
port = nfs_callback_tcpport6;
status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid);
if (status != 0)
goto out;
+ clp->cl_clientid = clid.clientid;
+ clp->cl_confirm = clid.confirm;
+ set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
+do_confirm:
status = nfs4_proc_setclientid_confirm(clp, &clid, cred);
if (status != 0)
goto out;
- clp->cl_clientid = clid.clientid;
+ clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
nfs4_schedule_state_renewal(clp);
out:
return status;
{
int status;
+ if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state))
+ goto do_confirm;
nfs4_begin_drain_session(clp);
status = nfs4_proc_exchange_id(clp, cred);
if (status != 0)
goto out;
+ set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
+do_confirm:
status = nfs4_proc_create_session(clp);
if (status != 0)
goto out;
+ clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
nfs41_setup_state_renewal(clp);
nfs_mark_client_ready(clp, NFS_CS_READY);
out:
*/
static void nfs4_set_lease_expired(struct nfs_client *clp, int status)
{
- if (nfs4_has_session(clp)) {
- switch (status) {
- case -NFS4ERR_DELAY:
- case -NFS4ERR_CLID_INUSE:
- case -EAGAIN:
- break;
+ switch (status) {
+ case -NFS4ERR_CLID_INUSE:
+ case -NFS4ERR_STALE_CLIENTID:
+ clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
+ break;
+ case -NFS4ERR_DELAY:
+ case -ETIMEDOUT:
+ case -EAGAIN:
+ ssleep(1);
+ break;
- case -EKEYEXPIRED:
- nfs4_warn_keyexpired(clp->cl_hostname);
- case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
- * in nfs4_exchange_id */
- default:
- return;
- }
+ case -EKEYEXPIRED:
+ nfs4_warn_keyexpired(clp->cl_hostname);
+ case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
+ * in nfs4_exchange_id */
+ default:
+ return;
}
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
}
int status = 0;
/* Ensure exclusive access to NFSv4 state */
- for(;;) {
+ do {
if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) {
/* We're going to have to re-establish a clientid */
status = nfs4_reclaim_lease(clp);
break;
if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
break;
- }
+ } while (atomic_read(&clp->cl_count) > 1);
return;
out_error:
printk(KERN_WARNING "Error: state manager failed on NFSv4 server %s"
static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req, struct compound_hdr *hdr)
{
- uint32_t attrs[2] = {0, 0};
+ uint32_t attrs[2] = {
+ FATTR4_WORD0_RDATTR_ERROR,
+ FATTR4_WORD1_MOUNTED_ON_FILEID,
+ };
uint32_t dircount = readdir->count >> 1;
__be32 *p;
if (readdir->plus) {
attrs[0] |= FATTR4_WORD0_TYPE|FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE|
- FATTR4_WORD0_FSID|FATTR4_WORD0_FILEHANDLE;
+ FATTR4_WORD0_FSID|FATTR4_WORD0_FILEHANDLE|FATTR4_WORD0_FILEID;
attrs[1] |= FATTR4_WORD1_MODE|FATTR4_WORD1_NUMLINKS|FATTR4_WORD1_OWNER|
FATTR4_WORD1_OWNER_GROUP|FATTR4_WORD1_RAWDEV|
FATTR4_WORD1_SPACE_USED|FATTR4_WORD1_TIME_ACCESS|
FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY;
dircount >>= 1;
}
- attrs[0] |= FATTR4_WORD0_RDATTR_ERROR|FATTR4_WORD0_FILEID;
- attrs[1] |= FATTR4_WORD1_MOUNTED_ON_FILEID;
- /* Switch to mounted_on_fileid if the server supports it */
- if (readdir->bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)
- attrs[0] &= ~FATTR4_WORD0_FILEID;
- else
- attrs[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;
+ /* Use mounted_on_fileid only if the server supports it */
+ if (!(readdir->bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID))
+ attrs[0] |= FATTR4_WORD0_FILEID;
p = reserve_space(xdr, 12+NFS4_VERIFIER_SIZE+20);
*p++ = cpu_to_be32(OP_READDIR);
goto out_overflow;
xdr_decode_hyper(p, fileid);
bitmap[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;
- ret = NFS_ATTR_FATTR_FILEID;
+ ret = NFS_ATTR_FATTR_MOUNTED_ON_FILEID;
}
dprintk("%s: fileid=%Lu\n", __func__, (unsigned long long)*fileid);
return ret;
{
int status;
umode_t fmode = 0;
- uint64_t fileid;
uint32_t type;
status = decode_attr_type(xdr, bitmap, &type);
goto xdr_error;
fattr->valid |= status;
- status = decode_attr_mounted_on_fileid(xdr, bitmap, &fileid);
+ status = decode_attr_mounted_on_fileid(xdr, bitmap, &fattr->mounted_on_fileid);
if (status < 0)
goto xdr_error;
- if (status != 0 && !(fattr->valid & status)) {
- fattr->fileid = fileid;
- fattr->valid |= status;
- }
+ fattr->valid |= status;
xdr_error:
dprintk("%s: xdr returned %d\n", __func__, -status);
struct nfs4_secinfo_flavor *sec_flavor;
int status;
__be32 *p;
- int i;
+ int i, num_flavors;
status = decode_op_hdr(xdr, OP_SECINFO);
+ if (status)
+ goto out;
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
- res->flavors->num_flavors = be32_to_cpup(p);
- for (i = 0; i < res->flavors->num_flavors; i++) {
+ res->flavors->num_flavors = 0;
+ num_flavors = be32_to_cpup(p);
+
+ for (i = 0; i < num_flavors; i++) {
sec_flavor = &res->flavors->flavors[i];
- if ((char *)&sec_flavor[1] - (char *)res > PAGE_SIZE)
+ if ((char *)&sec_flavor[1] - (char *)res->flavors > PAGE_SIZE)
break;
p = xdr_inline_decode(xdr, 4);
sec_flavor->flavor = be32_to_cpup(p);
if (sec_flavor->flavor == RPC_AUTH_GSS) {
- if (decode_secinfo_gss(xdr, sec_flavor))
- break;
+ status = decode_secinfo_gss(xdr, sec_flavor);
+ if (status)
+ goto out;
}
+ res->flavors->num_flavors++;
}
- return 0;
-
+out:
+ return status;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh,
entry->server, 1) < 0)
goto out_overflow;
- if (entry->fattr->valid & NFS_ATTR_FATTR_FILEID)
+ if (entry->fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID)
+ entry->ino = entry->fattr->mounted_on_fileid;
+ else if (entry->fattr->valid & NFS_ATTR_FATTR_FILEID)
entry->ino = entry->fattr->fileid;
entry->d_type = DT_UNKNOWN;
{
struct nfs_inode *nfsi = NFS_I(wdata->inode);
loff_t end_pos = wdata->args.offset + wdata->res.count;
+ bool mark_as_dirty = false;
spin_lock(&nfsi->vfs_inode.i_lock);
if (!test_and_set_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)) {
get_lseg(wdata->lseg);
wdata->lseg->pls_lc_cred =
get_rpccred(wdata->args.context->state->owner->so_cred);
- mark_inode_dirty_sync(wdata->inode);
+ mark_as_dirty = true;
dprintk("%s: Set layoutcommit for inode %lu ",
__func__, wdata->inode->i_ino);
}
if (end_pos > wdata->lseg->pls_end_pos)
wdata->lseg->pls_end_pos = end_pos;
spin_unlock(&nfsi->vfs_inode.i_lock);
+
+ /* if pnfs_layoutcommit_inode() runs between inode locks, the next one
+ * will be a noop because NFS_INO_LAYOUTCOMMIT will not be set */
+ if (mark_as_dirty)
+ mark_inode_dirty_sync(wdata->inode);
}
EXPORT_SYMBOL_GPL(pnfs_set_layoutcommit);
return 0;
}
+ mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
return 1;
}
if (error < 0)
goto out;
+ /*
+ * noac is a special case. It implies -o sync, but that's not
+ * necessarily reflected in the mtab options. do_remount_sb
+ * will clear MS_SYNCHRONOUS if -o sync wasn't specified in the
+ * remount options, so we have to explicitly reset it.
+ */
+ if (data->flags & NFS_MOUNT_NOAC)
+ *flags |= MS_SYNCHRONOUS;
+
/* compare new mount options with old ones */
error = nfs_compare_remount_data(nfss, data);
out:
if (!s->s_root) {
/* initial superblock/root creation */
nfs_fill_super(s, data);
- nfs_fscache_get_super_cookie(
- s, data ? data->fscache_uniq : NULL, NULL);
+ nfs_fscache_get_super_cookie(s, data->fscache_uniq, NULL);
}
mntroot = nfs_get_root(s, mntfh, dev_name);
if (!nfs_need_commit(nfsi))
return 0;
+ spin_lock(&inode->i_lock);
ret = nfs_scan_list(nfsi, dst, idx_start, npages, NFS_PAGE_TAG_COMMIT);
if (ret > 0)
nfsi->ncommit -= ret;
+ spin_unlock(&inode->i_lock);
+
if (nfs_need_commit(NFS_I(inode)))
__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
+
return ret;
}
#else
req = nfs_setup_write_request(ctx, page, offset, count);
if (IS_ERR(req))
return PTR_ERR(req);
- nfs_mark_request_dirty(req);
/* Update file length */
nfs_grow_file(page, offset, count);
nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes);
task->tk_pid, task->tk_status);
/* Call the NFS version-specific code */
- if (NFS_PROTO(data->inode)->commit_done(task, data) != 0)
- return;
+ NFS_PROTO(data->inode)->commit_done(task, data);
}
void nfs_commit_release_pages(struct nfs_write_data *data)
res = nfs_commit_set_lock(NFS_I(inode), may_wait);
if (res <= 0)
goto out_mark_dirty;
- spin_lock(&inode->i_lock);
res = nfs_scan_commit(inode, &head, 0, 0);
- spin_unlock(&inode->i_lock);
if (res) {
int error;
if (atomic_dec_and_test(&fp->fi_delegees)) {
vfs_setlease(fp->fi_deleg_file, F_UNLCK, &fp->fi_lease);
fp->fi_lease = NULL;
+ fput(fp->fi_deleg_file);
fp->fi_deleg_file = NULL;
}
}
if (stp->st_access_bmap) {
oflag = nfs4_access_bmap_to_omode(stp);
nfs4_file_put_access(stp->st_file, oflag);
- put_nfs4_file(stp->st_file);
}
+ put_nfs4_file(stp->st_file);
kmem_cache_free(stateid_slab, stp);
}
goto out;
if (!(iap->ia_valid & ATTR_MODE))
iap->ia_mode = 0;
- err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
+ err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC);
if (err)
goto out;
if (IS_ERR(dchild))
goto out_nfserr;
+ /* If file doesn't exist, check for permissions to create one */
+ if (!dchild->d_inode) {
+ err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
+ if (err)
+ goto out;
+ }
+
err = fh_compose(resfhp, fhp->fh_export, dchild, fhp);
if (err)
goto out;
__le16 xe_name_offset; /* byte offset from the 1st entry in the
local xattr storage(inode, xattr block or
xattr bucket). */
- __u8 xe_name_len; /* xattr name len, does't include prefix. */
+ __u8 xe_name_len; /* xattr name len, doesn't include prefix. */
__u8 xe_type; /* the low 7 bits indicate the name prefix
* type and the highest bit indicates whether
* the EA is stored in the local storage. */
goto fail;
}
+ /* Check that sizeof_partition_entry has the correct value */
+ if (le32_to_cpu((*gpt)->sizeof_partition_entry) != sizeof(gpt_entry)) {
+ pr_debug("GUID Partitition Entry Size check failed.\n");
+ goto fail;
+ }
+
if (!(*ptes = alloc_read_gpt_entries(state, *gpt)))
goto fail;
BUG_ON (!data || !frags);
+ if (size < 2 * VBLK_SIZE_HEAD) {
+ ldm_error("Value of size is to small.");
+ return false;
+ }
+
group = get_unaligned_be32(data + 0x08);
rec = get_unaligned_be16(data + 0x0C);
num = get_unaligned_be16(data + 0x0E);
ldm_error ("A VBLK claims to have %d parts.", num);
return false;
}
+ if (rec >= num) {
+ ldm_error("REC value (%d) exceeds NUM value (%d)", rec, num);
+ return false;
+ }
list_for_each (item, frags) {
f = list_entry (item, struct frag, list);
f->map |= (1 << rec);
- if (num > 0) {
- data += VBLK_SIZE_HEAD;
- size -= VBLK_SIZE_HEAD;
- }
+ data += VBLK_SIZE_HEAD;
+ size -= VBLK_SIZE_HEAD;
+
memcpy (f->data+rec*(size-VBLK_SIZE_HEAD)+VBLK_SIZE_HEAD, data, size);
return true;
/* for the /proc/ directory itself, after non-process stuff has been done */
int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir)
{
- unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY;
- struct task_struct *reaper = get_proc_task(filp->f_path.dentry->d_inode);
+ unsigned int nr;
+ struct task_struct *reaper;
struct tgid_iter iter;
struct pid_namespace *ns;
+ if (filp->f_pos >= PID_MAX_LIMIT + TGID_OFFSET)
+ goto out_no_task;
+ nr = filp->f_pos - FIRST_PROCESS_ENTRY;
+
+ reaper = get_proc_task(filp->f_path.dentry->d_inode);
if (!reaper)
goto out_no_task;
int flags = vma->vm_flags;
unsigned long ino = 0;
unsigned long long pgoff = 0;
- unsigned long start;
+ unsigned long start, end;
dev_t dev = 0;
int len;
/* We don't show the stack guard page in /proc/maps */
start = vma->vm_start;
- if (vma->vm_flags & VM_GROWSDOWN)
- if (!vma_stack_continue(vma->vm_prev, vma->vm_start))
- start += PAGE_SIZE;
+ if (stack_guard_page_start(vma, start))
+ start += PAGE_SIZE;
+ end = vma->vm_end;
+ if (stack_guard_page_end(vma, end))
+ end -= PAGE_SIZE;
seq_printf(m, "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu %n",
start,
- vma->vm_end,
+ end,
flags & VM_READ ? 'r' : '-',
flags & VM_WRITE ? 'w' : '-',
flags & VM_EXEC ? 'x' : '-',
SetPageDirty(page);
unlock_page(page);
+ put_page(page);
}
return 0;
#ifndef __UBIFS_DEBUG_H__
#define __UBIFS_DEBUG_H__
+/* Checking helper functions */
+typedef int (*dbg_leaf_callback)(struct ubifs_info *c,
+ struct ubifs_zbranch *zbr, void *priv);
+typedef int (*dbg_znode_callback)(struct ubifs_info *c,
+ struct ubifs_znode *znode, void *priv);
+
#ifdef CONFIG_UBIFS_FS_DEBUG
/**
void dbg_dump_index(struct ubifs_info *c);
void dbg_dump_lpt_lebs(const struct ubifs_info *c);
-/* Checking helper functions */
-typedef int (*dbg_leaf_callback)(struct ubifs_info *c,
- struct ubifs_zbranch *zbr, void *priv);
-typedef int (*dbg_znode_callback)(struct ubifs_info *c,
- struct ubifs_znode *znode, void *priv);
int dbg_walk_index(struct ubifs_info *c, dbg_leaf_callback leaf_cb,
dbg_znode_callback znode_cb, void *priv);
int dbg_check_filesystem(struct ubifs_info *c);
void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat,
int add_pos);
-int dbg_check_lprops(struct ubifs_info *c);
int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode,
int row, int col);
int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode,
#define DBGKEY(key) ((char *)(key))
#define DBGKEY1(key) ((char *)(key))
-#define ubifs_debugging_init(c) 0
-#define ubifs_debugging_exit(c) ({})
-
-#define dbg_ntype(type) ""
-#define dbg_cstate(cmt_state) ""
-#define dbg_jhead(jhead) ""
-#define dbg_get_key_dump(c, key) ({})
-#define dbg_dump_inode(c, inode) ({})
-#define dbg_dump_node(c, node) ({})
-#define dbg_dump_lpt_node(c, node, lnum, offs) ({})
-#define dbg_dump_budget_req(req) ({})
-#define dbg_dump_lstats(lst) ({})
-#define dbg_dump_budg(c) ({})
-#define dbg_dump_lprop(c, lp) ({})
-#define dbg_dump_lprops(c) ({})
-#define dbg_dump_lpt_info(c) ({})
-#define dbg_dump_leb(c, lnum) ({})
-#define dbg_dump_znode(c, znode) ({})
-#define dbg_dump_heap(c, heap, cat) ({})
-#define dbg_dump_pnode(c, pnode, parent, iip) ({})
-#define dbg_dump_tnc(c) ({})
-#define dbg_dump_index(c) ({})
-#define dbg_dump_lpt_lebs(c) ({})
-
-#define dbg_walk_index(c, leaf_cb, znode_cb, priv) 0
-#define dbg_old_index_check_init(c, zroot) 0
-#define dbg_save_space_info(c) ({})
-#define dbg_check_space_info(c) 0
-#define dbg_check_old_index(c, zroot) 0
-#define dbg_check_cats(c) 0
-#define dbg_check_ltab(c) 0
-#define dbg_chk_lpt_free_spc(c) 0
-#define dbg_chk_lpt_sz(c, action, len) 0
-#define dbg_check_synced_i_size(inode) 0
-#define dbg_check_dir_size(c, dir) 0
-#define dbg_check_tnc(c, x) 0
-#define dbg_check_idx_size(c, idx_size) 0
-#define dbg_check_filesystem(c) 0
-#define dbg_check_heap(c, heap, cat, add_pos) ({})
-#define dbg_check_lprops(c) 0
-#define dbg_check_lpt_nodes(c, cnode, row, col) 0
-#define dbg_check_inode_size(c, inode, size) 0
-#define dbg_check_data_nodes_order(c, head) 0
-#define dbg_check_nondata_nodes_order(c, head) 0
-#define dbg_force_in_the_gaps_enabled 0
-#define dbg_force_in_the_gaps() 0
-#define dbg_failure_mode 0
-
-#define dbg_debugfs_init() 0
-#define dbg_debugfs_exit()
-#define dbg_debugfs_init_fs(c) 0
-#define dbg_debugfs_exit_fs(c) 0
+static inline int ubifs_debugging_init(struct ubifs_info *c) { return 0; }
+static inline void ubifs_debugging_exit(struct ubifs_info *c) { return; }
+static inline const char *dbg_ntype(int type) { return ""; }
+static inline const char *dbg_cstate(int cmt_state) { return ""; }
+static inline const char *dbg_jhead(int jhead) { return ""; }
+static inline const char *
+dbg_get_key_dump(const struct ubifs_info *c,
+ const union ubifs_key *key) { return ""; }
+static inline void dbg_dump_inode(const struct ubifs_info *c,
+ const struct inode *inode) { return; }
+static inline void dbg_dump_node(const struct ubifs_info *c,
+ const void *node) { return; }
+static inline void dbg_dump_lpt_node(const struct ubifs_info *c,
+ void *node, int lnum,
+ int offs) { return; }
+static inline void
+dbg_dump_budget_req(const struct ubifs_budget_req *req) { return; }
+static inline void
+dbg_dump_lstats(const struct ubifs_lp_stats *lst) { return; }
+static inline void dbg_dump_budg(struct ubifs_info *c) { return; }
+static inline void dbg_dump_lprop(const struct ubifs_info *c,
+ const struct ubifs_lprops *lp) { return; }
+static inline void dbg_dump_lprops(struct ubifs_info *c) { return; }
+static inline void dbg_dump_lpt_info(struct ubifs_info *c) { return; }
+static inline void dbg_dump_leb(const struct ubifs_info *c,
+ int lnum) { return; }
+static inline void
+dbg_dump_znode(const struct ubifs_info *c,
+ const struct ubifs_znode *znode) { return; }
+static inline void dbg_dump_heap(struct ubifs_info *c,
+ struct ubifs_lpt_heap *heap,
+ int cat) { return; }
+static inline void dbg_dump_pnode(struct ubifs_info *c,
+ struct ubifs_pnode *pnode,
+ struct ubifs_nnode *parent,
+ int iip) { return; }
+static inline void dbg_dump_tnc(struct ubifs_info *c) { return; }
+static inline void dbg_dump_index(struct ubifs_info *c) { return; }
+static inline void dbg_dump_lpt_lebs(const struct ubifs_info *c) { return; }
+
+static inline int dbg_walk_index(struct ubifs_info *c,
+ dbg_leaf_callback leaf_cb,
+ dbg_znode_callback znode_cb,
+ void *priv) { return 0; }
+static inline void dbg_save_space_info(struct ubifs_info *c) { return; }
+static inline int dbg_check_space_info(struct ubifs_info *c) { return 0; }
+static inline int dbg_check_lprops(struct ubifs_info *c) { return 0; }
+static inline int
+dbg_old_index_check_init(struct ubifs_info *c,
+ struct ubifs_zbranch *zroot) { return 0; }
+static inline int
+dbg_check_old_index(struct ubifs_info *c,
+ struct ubifs_zbranch *zroot) { return 0; }
+static inline int dbg_check_cats(struct ubifs_info *c) { return 0; }
+static inline int dbg_check_ltab(struct ubifs_info *c) { return 0; }
+static inline int dbg_chk_lpt_free_spc(struct ubifs_info *c) { return 0; }
+static inline int dbg_chk_lpt_sz(struct ubifs_info *c,
+ int action, int len) { return 0; }
+static inline int dbg_check_synced_i_size(struct inode *inode) { return 0; }
+static inline int dbg_check_dir_size(struct ubifs_info *c,
+ const struct inode *dir) { return 0; }
+static inline int dbg_check_tnc(struct ubifs_info *c, int extra) { return 0; }
+static inline int dbg_check_idx_size(struct ubifs_info *c,
+ long long idx_size) { return 0; }
+static inline int dbg_check_filesystem(struct ubifs_info *c) { return 0; }
+static inline void dbg_check_heap(struct ubifs_info *c,
+ struct ubifs_lpt_heap *heap,
+ int cat, int add_pos) { return; }
+static inline int dbg_check_lpt_nodes(struct ubifs_info *c,
+ struct ubifs_cnode *cnode, int row, int col) { return 0; }
+static inline int dbg_check_inode_size(struct ubifs_info *c,
+ const struct inode *inode,
+ loff_t size) { return 0; }
+static inline int
+dbg_check_data_nodes_order(struct ubifs_info *c,
+ struct list_head *head) { return 0; }
+static inline int
+dbg_check_nondata_nodes_order(struct ubifs_info *c,
+ struct list_head *head) { return 0; }
+
+static inline int dbg_force_in_the_gaps(void) { return 0; }
+#define dbg_force_in_the_gaps_enabled 0
+#define dbg_failure_mode 0
+
+static inline int dbg_debugfs_init(void) { return 0; }
+static inline void dbg_debugfs_exit(void) { return; }
+static inline int dbg_debugfs_init_fs(struct ubifs_info *c) { return 0; }
+static inline int dbg_debugfs_exit_fs(struct ubifs_info *c) { return 0; }
#endif /* !CONFIG_UBIFS_FS_DEBUG */
#endif /* !__UBIFS_DEBUG_H__ */
dbg_gen("syncing inode %lu", inode->i_ino);
+ if (inode->i_sb->s_flags & MS_RDONLY)
+ return 0;
+
/*
* VFS has already synchronized dirty pages for this inode. Synchronize
* the inode unless this is a 'datasync()' call.
spin_unlock(&c->buds_lock);
}
-/**
- * ubifs_create_buds_lists - create journal head buds lists for remount rw.
- * @c: UBIFS file-system description object
- */
-void ubifs_create_buds_lists(struct ubifs_info *c)
-{
- struct rb_node *p;
-
- spin_lock(&c->buds_lock);
- p = rb_first(&c->buds);
- while (p) {
- struct ubifs_bud *bud = rb_entry(p, struct ubifs_bud, rb);
- struct ubifs_jhead *jhead = &c->jheads[bud->jhead];
-
- list_add_tail(&bud->list, &jhead->buds_list);
- p = rb_next(p);
- }
- spin_unlock(&c->buds_lock);
-}
-
/**
* ubifs_add_bud_to_log - add a new bud to the log.
* @c: UBIFS file-system description object
goto out_free;
}
memcpy(c->rcvrd_mst_node, c->mst_node, UBIFS_MST_NODE_SZ);
+
+ /*
+ * We had to recover the master node, which means there was an
+ * unclean reboot. However, it is possible that the master node
+ * is clean at this point, i.e., %UBIFS_MST_DIRTY is not set.
+ * E.g., consider the following chain of events:
+ *
+ * 1. UBIFS was cleanly unmounted, so the master node is clean
+ * 2. UBIFS is being mounted R/W and starts changing the master
+ * node in the first (%UBIFS_MST_LNUM). A power cut happens,
+ * so this LEB ends up with some amount of garbage at the
+ * end.
+ * 3. UBIFS is being mounted R/O. We reach this place and
+ * recover the master node from the second LEB
+ * (%UBIFS_MST_LNUM + 1). But we cannot update the media
+ * because we are being mounted R/O. We have to defer the
+ * operation.
+ * 4. However, this master node (@c->mst_node) is marked as
+ * clean (since the step 1). And if we just return, the
+ * mount code will be confused and won't recover the master
+ * node when it is re-mounter R/W later.
+ *
+ * Thus, to force the recovery by marking the master node as
+ * dirty.
+ */
+ c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY);
} else {
/* Write the recovered master node */
c->max_sqnum = le64_to_cpu(mst->ch.sqnum) - 1;
* @new_size: truncation new size
* @free: amount of free space in a bud
* @dirty: amount of dirty space in a bud from padding and deletion nodes
+ * @jhead: journal head number of the bud
*
* UBIFS journal replay must compare node sequence numbers, which means it must
* build a tree of node information to insert into the TNC.
struct {
int free;
int dirty;
+ int jhead;
};
};
};
err = PTR_ERR(lp);
goto out;
}
+
+ /* Make sure the journal head points to the latest bud */
+ err = ubifs_wbuf_seek_nolock(&c->jheads[r->jhead].wbuf, r->lnum,
+ c->leb_size - r->free, UBI_SHORTTERM);
+
out:
ubifs_release_lprops(c);
return err;
ubifs_assert(sleb->endpt - offs >= used);
ubifs_assert(sleb->endpt % c->min_io_size == 0);
- if (sleb->endpt + c->min_io_size <= c->leb_size && !c->ro_mount)
- err = ubifs_wbuf_seek_nolock(&c->jheads[jhead].wbuf, lnum,
- sleb->endpt, UBI_SHORTTERM);
-
*dirty = sleb->endpt - offs - used;
*free = c->leb_size - sleb->endpt;
* @sqnum: sequence number
* @free: amount of free space in bud
* @dirty: amount of dirty space from padding and deletion nodes
+ * @jhead: journal head number for the bud
*
* This function inserts a reference node to the replay tree and returns zero
* in case of success or a negative error code in case of failure.
*/
static int insert_ref_node(struct ubifs_info *c, int lnum, int offs,
- unsigned long long sqnum, int free, int dirty)
+ unsigned long long sqnum, int free, int dirty,
+ int jhead)
{
struct rb_node **p = &c->replay_tree.rb_node, *parent = NULL;
struct replay_entry *r;
r->flags = REPLAY_REF;
r->free = free;
r->dirty = dirty;
+ r->jhead = jhead;
rb_link_node(&r->rb, parent, p);
rb_insert_color(&r->rb, &c->replay_tree);
if (err)
return err;
err = insert_ref_node(c, b->bud->lnum, b->bud->start, b->sqnum,
- free, dirty);
+ free, dirty, b->bud->jhead);
if (err)
return err;
}
goto out_free;
}
+ err = alloc_wbufs(c);
+ if (err)
+ goto out_cbuf;
+
sprintf(c->bgt_name, BGT_NAME_PATTERN, c->vi.ubi_num, c->vi.vol_id);
if (!c->ro_mount) {
- err = alloc_wbufs(c);
- if (err)
- goto out_cbuf;
-
/* Create background thread */
c->bgt = kthread_create(ubifs_bg_thread, c, "%s", c->bgt_name);
if (IS_ERR(c->bgt)) {
if (err)
goto out;
- err = alloc_wbufs(c);
- if (err)
- goto out;
-
- ubifs_create_buds_lists(c);
-
/* Create background thread */
c->bgt = kthread_create(ubifs_bg_thread, c, "%s", c->bgt_name);
if (IS_ERR(c->bgt)) {
if (err)
goto out;
+ dbg_gen("re-mounted read-write");
+ c->remounting_rw = 0;
+
if (c->need_recovery) {
c->need_recovery = 0;
ubifs_msg("deferred recovery completed");
+ } else {
+ /*
+ * Do not run the debugging space check if the were doing
+ * recovery, because when we saved the information we had the
+ * file-system in a state where the TNC and lprops has been
+ * modified in memory, but all the I/O operations (including a
+ * commit) were deferred. So the file-system was in
+ * "non-committed" state. Now the file-system is in committed
+ * state, and of course the amount of free space will change
+ * because, for example, the old index size was imprecise.
+ */
+ err = dbg_check_space_info(c);
}
-
- dbg_gen("re-mounted read-write");
- c->remounting_rw = 0;
- err = dbg_check_space_info(c);
mutex_unlock(&c->umount_mutex);
return err;
if (err)
ubifs_ro_mode(c, err);
- free_wbufs(c);
vfree(c->orph_buf);
c->orph_buf = NULL;
kfree(c->write_reserve_buf);
* of the media. For example, there will be dirty inodes if we failed
* to write them back because of I/O errors.
*/
- ubifs_assert(atomic_long_read(&c->dirty_pg_cnt) == 0);
- ubifs_assert(c->budg_idx_growth == 0);
- ubifs_assert(c->budg_dd_growth == 0);
- ubifs_assert(c->budg_data_growth == 0);
+ if (!c->ro_error) {
+ ubifs_assert(atomic_long_read(&c->dirty_pg_cnt) == 0);
+ ubifs_assert(c->budg_idx_growth == 0);
+ ubifs_assert(c->budg_dd_growth == 0);
+ ubifs_assert(c->budg_data_growth == 0);
+ }
/*
* The 'c->umount_lock' prevents races between UBIFS memory shrinker
handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name);
if (!handler)
return -EOPNOTSUPP;
- return handler->set(dentry, name, value, size, 0, handler->flags);
+ return handler->set(dentry, name, value, size, flags, handler->flags);
}
/*
const struct xfs_mount *mp,
struct va_format *vaf)
{
- if (mp && mp->m_fsname)
+ if (mp && mp->m_fsname) {
printk("%sXFS (%s): %pV\n", level, mp->m_fsname, vaf);
+ return;
+ }
printk("%sXFS: %pV\n", level, vaf);
}
unsigned transp,
struct fb_info *info);
+bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper);
void drm_fb_helper_restore(void);
void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
uint32_t fb_width, uint32_t fb_height);
}
#define drm_mm_for_each_node(entry, mm) list_for_each_entry(entry, \
&(mm)->head_node.node_list, \
- node_list);
+ node_list)
#define drm_mm_for_each_scanned_node_reverse(entry, n, mm) \
for (entry = (mm)->prev_scanned_node, \
next = entry ? list_entry(entry->node_list.next, \
{0x1002, 0x6719, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x671c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x671d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x671f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6720, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6721, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6722, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6729, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6739, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x673e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6740, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6741, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6742, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x688D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6898, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6899, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x689b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x689c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HEMLOCK|RADEON_NEW_MEMMAP}, \
{0x1002, 0x689d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HEMLOCK|RADEON_NEW_MEMMAP}, \
{0x1002, 0x689e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68b0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68b8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68b9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x68ba, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68be, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x68bf, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68c1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68c7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
#define RADEON_INFO_WANT_CMASK 0x08 /* get access to CMASK on r300 */
#define RADEON_INFO_CLOCK_CRYSTAL_FREQ 0x09 /* clock crystal frequency */
#define RADEON_INFO_NUM_BACKENDS 0x0a /* DB/backends for r600+ - need for OQ */
+#define RADEON_INFO_NUM_TILE_PIPES 0x0b /* tile pipes for r600+ */
+#define RADEON_INFO_FUSION_GART_WORKING 0x0c /* fusion writes to GTT were broken before this */
struct drm_radeon_info {
uint32_t request;
preempt_disable();
#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)
while (unlikely(test_and_set_bit_lock(bitnum, addr))) {
- while (test_bit(bitnum, addr)) {
- preempt_enable();
+ preempt_enable();
+ do {
cpu_relax();
- preempt_disable();
- }
+ } while (test_bit(bitnum, addr));
+ preempt_disable();
}
#endif
__acquire(bitlock);
#define QUEUE_FLAG_SYNCFULL 3 /* read queue has been filled */
#define QUEUE_FLAG_ASYNCFULL 4 /* write queue has been filled */
#define QUEUE_FLAG_DEAD 5 /* queue being torn down */
-#define QUEUE_FLAG_REENTER 6 /* Re-entrancy avoidance */
-#define QUEUE_FLAG_ELVSWITCH 7 /* don't use elevator, just do FIFO */
-#define QUEUE_FLAG_BIDI 8 /* queue supports bidi requests */
-#define QUEUE_FLAG_NOMERGES 9 /* disable merge attempts */
-#define QUEUE_FLAG_SAME_COMP 10 /* force complete on same CPU */
-#define QUEUE_FLAG_FAIL_IO 11 /* fake timeout */
-#define QUEUE_FLAG_STACKABLE 12 /* supports request stacking */
-#define QUEUE_FLAG_NONROT 13 /* non-rotational device (SSD) */
+#define QUEUE_FLAG_ELVSWITCH 6 /* don't use elevator, just do FIFO */
+#define QUEUE_FLAG_BIDI 7 /* queue supports bidi requests */
+#define QUEUE_FLAG_NOMERGES 8 /* disable merge attempts */
+#define QUEUE_FLAG_SAME_COMP 9 /* force complete on same CPU */
+#define QUEUE_FLAG_FAIL_IO 10 /* fake timeout */
+#define QUEUE_FLAG_STACKABLE 11 /* supports request stacking */
+#define QUEUE_FLAG_NONROT 12 /* non-rotational device (SSD) */
#define QUEUE_FLAG_VIRT QUEUE_FLAG_NONROT /* paravirt device */
-#define QUEUE_FLAG_IO_STAT 15 /* do IO stats */
-#define QUEUE_FLAG_DISCARD 16 /* supports DISCARD */
-#define QUEUE_FLAG_NOXMERGES 17 /* No extended merges */
-#define QUEUE_FLAG_ADD_RANDOM 18 /* Contributes to random pool */
-#define QUEUE_FLAG_SECDISCARD 19 /* supports SECDISCARD */
+#define QUEUE_FLAG_IO_STAT 13 /* do IO stats */
+#define QUEUE_FLAG_DISCARD 14 /* supports DISCARD */
+#define QUEUE_FLAG_NOXMERGES 15 /* No extended merges */
+#define QUEUE_FLAG_ADD_RANDOM 16 /* Contributes to random pool */
+#define QUEUE_FLAG_SECDISCARD 17 /* supports SECDISCARD */
#define QUEUE_FLAG_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \
(1 << QUEUE_FLAG_STACKABLE) | \
extern void blk_stop_queue(struct request_queue *q);
extern void blk_sync_queue(struct request_queue *q);
extern void __blk_stop_queue(struct request_queue *q);
-extern void __blk_run_queue(struct request_queue *q, bool force_kblockd);
+extern void __blk_run_queue(struct request_queue *q);
extern void blk_run_queue(struct request_queue *);
+extern void blk_run_queue_async(struct request_queue *q);
extern int blk_rq_map_user(struct request_queue *, struct request *,
struct rq_map_data *, void __user *, unsigned long,
gfp_t);
struct blk_plug {
unsigned long magic;
struct list_head list;
+ struct list_head cb_list;
unsigned int should_sort;
};
+struct blk_plug_cb {
+ struct list_head list;
+ void (*callback)(struct blk_plug_cb *);
+};
extern void blk_start_plug(struct blk_plug *);
extern void blk_finish_plug(struct blk_plug *);
-extern void __blk_flush_plug(struct task_struct *, struct blk_plug *);
+extern void blk_flush_plug_list(struct blk_plug *, bool);
static inline void blk_flush_plug(struct task_struct *tsk)
{
struct blk_plug *plug = tsk->plug;
- if (unlikely(plug))
- __blk_flush_plug(tsk, plug);
+ if (plug)
+ blk_flush_plug_list(plug, false);
+}
+
+static inline void blk_schedule_flush_plug(struct task_struct *tsk)
+{
+ struct blk_plug *plug = tsk->plug;
+
+ if (plug)
+ blk_flush_plug_list(plug, true);
}
static inline bool blk_needs_flush_plug(struct task_struct *tsk)
{
struct blk_plug *plug = tsk->plug;
- return plug && !list_empty(&plug->list);
+ return plug && (!list_empty(&plug->list) || !list_empty(&plug->cb_list));
}
/*
{
}
+static inline void blk_schedule_flush_plug(struct task_struct *task)
+{
+}
+
+
static inline bool blk_needs_flush_plug(struct task_struct *tsk)
{
return false;
* typically using d_splice_alias. */
#define DCACHE_REFERENCED 0x0008 /* Recently used, don't discard. */
-#define DCACHE_UNHASHED 0x0010
+#define DCACHE_RCUACCESS 0x0010 /* Entry has ever been RCU-visible */
#define DCACHE_INOTIFY_PARENT_WATCHED 0x0020
/* Parent inode is watched by inotify */
static inline int d_unhashed(struct dentry *dentry)
{
- return (dentry->d_flags & DCACHE_UNHASHED);
+ return hlist_bl_unhashed(&dentry->d_hash);
}
static inline int d_unlinked(struct dentry *dentry)
struct dm_target_callbacks {
struct list_head list;
int (*congested_fn) (struct dm_target_callbacks *, int);
- void (*unplug_fn)(struct dm_target_callbacks *);
};
int dm_register_target(struct target_type *t);
struct flex_array *flex_array_alloc(int element_size, unsigned int total,
gfp_t flags);
int flex_array_prealloc(struct flex_array *fa, unsigned int start,
- unsigned int end, gfp_t flags);
+ unsigned int nr_elements, gfp_t flags);
void flex_array_free(struct flex_array *fa);
void flex_array_free_parts(struct flex_array *fa);
int flex_array_put(struct flex_array *fa, unsigned int element_nr, void *src,
unsigned char flags;
unsigned char preempt_count;
int pid;
+ int padding;
};
#define FTRACE_MAX_EVENT \
unsigned long end,
long adjust_next)
{
- if (!vma->anon_vma || vma->vm_ops || vma->vm_file)
+ if (!vma->anon_vma || vma->vm_ops)
return;
__vma_adjust_trans_huge(vma, start, end, adjust_next);
}
#define SYN_REPORT 0
#define SYN_CONFIG 1
#define SYN_MT_REPORT 2
+#define SYN_DROPPED 3
/*
* Keys and buttons
#define KEY_DVD 0x185 /* Media Select DVD */
#define KEY_AUX 0x186
#define KEY_MP3 0x187
-#define KEY_AUDIO 0x188
-#define KEY_VIDEO 0x189
+#define KEY_AUDIO 0x188 /* AL Audio Browser */
+#define KEY_VIDEO 0x189 /* AL Movie Browser */
#define KEY_DIRECTORY 0x18a
#define KEY_LIST 0x18b
#define KEY_MEMO 0x18c /* Media Select Messages */
#define KEY_FRAMEFORWARD 0x1b5
#define KEY_CONTEXT_MENU 0x1b6 /* GenDesc - system context menu */
#define KEY_MEDIA_REPEAT 0x1b7 /* Consumer - transport control */
-#define KEY_10CHANNELSUP 0x1b8 /* 10 channels up (10+) */
-#define KEY_10CHANNELSDOWN 0x1b9 /* 10 channels down (10-) */
+#define KEY_10CHANNELSUP 0x1b8 /* 10 channels up (10+) */
+#define KEY_10CHANNELSDOWN 0x1b9 /* 10 channels down (10-) */
+#define KEY_IMAGES 0x1ba /* AL Image Browser */
#define KEY_DEL_EOL 0x1c0
#define KEY_DEL_EOS 0x1c1
input_event(dev, EV_ABS, ABS_MT_SLOT, slot);
}
+static inline bool input_is_mt_axis(int axis)
+{
+ return axis == ABS_MT_SLOT ||
+ (axis >= ABS_MT_FIRST && axis <= ABS_MT_LAST);
+}
+
void input_mt_report_slot_state(struct input_dev *dev,
unsigned int tool_type, bool active);
ATA_DFLAG_ACPI_PENDING = (1 << 5), /* ACPI resume action pending */
ATA_DFLAG_ACPI_FAILED = (1 << 6), /* ACPI on devcfg has failed */
ATA_DFLAG_AN = (1 << 7), /* AN configured */
- ATA_DFLAG_HIPM = (1 << 8), /* device supports HIPM */
- ATA_DFLAG_DIPM = (1 << 9), /* device supports DIPM */
ATA_DFLAG_DMADIR = (1 << 10), /* device requires DMADIR */
ATA_DFLAG_CFG_MASK = (1 << 12) - 1,
* management */
ATA_FLAG_SW_ACTIVITY = (1 << 22), /* driver supports sw activity
* led */
+ ATA_FLAG_NO_DIPM = (1 << 23), /* host not happy with DIPM */
/* bits 24:31 of ap->flags are reserved for LLD specific flags */
#define _LINUX_LIST_BL_H
#include <linux/list.h>
+#include <linux/bit_spinlock.h>
/*
* Special version of lists, where head of the list has a lock in the lowest
}
}
+static inline void hlist_bl_lock(struct hlist_bl_head *b)
+{
+ bit_spin_lock(0, (unsigned long *)b);
+}
+
+static inline void hlist_bl_unlock(struct hlist_bl_head *b)
+{
+ __bit_spin_unlock(0, (unsigned long *)b);
+}
+
/**
* hlist_bl_for_each_entry - iterate over list of given type
* @tpos: the type * to use as a loop cursor.
return ;
}
-static inline inline void mem_cgroup_rotate_reclaimable_page(struct page *page)
+static inline void mem_cgroup_rotate_reclaimable_page(struct page *page)
{
return ;
}
*/
static inline const struct mfd_cell *mfd_get_cell(struct platform_device *pdev)
{
- return pdev->dev.platform_data;
+ return pdev->mfd_cell;
}
/*
* Given a platform device that's been created by mfd_add_devices(), fetch
* the .mfd_data entry from the mfd_cell that created it.
+ * Otherwise just return the platform_data pointer.
+ * This maintains compatibility with platform drivers whose devices aren't
+ * created by the mfd layer, and expect platform_data to contain what would've
+ * otherwise been in mfd_data.
*/
static inline void *mfd_get_data(struct platform_device *pdev)
{
- return mfd_get_cell(pdev)->mfd_data;
+ const struct mfd_cell *cell = mfd_get_cell(pdev);
+
+ if (cell)
+ return cell->mfd_data;
+ else
+ return pdev->dev.platform_data;
}
extern int mfd_add_devices(struct device *parent, int id,
int rpu; /** Pen down sensitivity resistor divider */
int pressure; /** Report pressure (boolean) */
unsigned int data_irq; /** Touch data ready IRQ */
+ int data_irqf; /** IRQ flags for data ready IRQ */
unsigned int pd_irq; /** Touch pendown detect IRQ */
+ int pd_irqf; /** IRQ flags for pen down IRQ */
};
enum wm831x_watchdog_action {
#define WM8994_EQ_REGS 20
#define WM8958_MBC_CUTOFF_REGS 20
#define WM8958_MBC_COEFF_REGS 48
+#define WM8958_MBC_COMBINED_REGS 56
+#define WM8958_VSS_HPF_REGS 2
+#define WM8958_VSS_REGS 148
+#define WM8958_ENH_EQ_REGS 32
/**
* DRC configurations are specified with a label and a set of register
const char *name;
u16 cutoff_regs[WM8958_MBC_CUTOFF_REGS];
u16 coeff_regs[WM8958_MBC_COEFF_REGS];
+
+ /* Coefficient layout when using MBC+VSS firmware */
+ u16 combined_regs[WM8958_MBC_COMBINED_REGS];
+};
+
+/**
+ * VSS HPF configurations are specified with a label and two values to
+ * write. Configurations are expected to be generated using the
+ * multiband compressor configuration panel in WISCE - see
+ * http://www.wolfsonmicro.com/wisce/
+ */
+struct wm8958_vss_hpf_cfg {
+ const char *name;
+ u16 regs[WM8958_VSS_HPF_REGS];
+};
+
+/**
+ * VSS configurations are specified with a label and array of values
+ * to write. Configurations are expected to be generated using the
+ * multiband compressor configuration panel in WISCE - see
+ * http://www.wolfsonmicro.com/wisce/
+ */
+struct wm8958_vss_cfg {
+ const char *name;
+ u16 regs[WM8958_VSS_REGS];
+};
+
+/**
+ * Enhanced EQ configurations are specified with a label and array of
+ * values to write. Configurations are expected to be generated using
+ * the multiband compressor configuration panel in WISCE - see
+ * http://www.wolfsonmicro.com/wisce/
+ */
+struct wm8958_enh_eq_cfg {
+ const char *name;
+ u16 regs[WM8958_ENH_EQ_REGS];
};
struct wm8994_pdata {
int num_mbc_cfgs;
struct wm8958_mbc_cfg *mbc_cfgs;
+ int num_vss_cfgs;
+ struct wm8958_vss_cfg *vss_cfgs;
+
+ int num_vss_hpf_cfgs;
+ struct wm8958_vss_hpf_cfg *vss_hpf_cfgs;
+
+ int num_enh_eq_cfgs;
+ struct wm8958_enh_eq_cfg *enh_eq_cfgs;
+
/* LINEOUT can be differential or single ended */
unsigned int lineout1_diff:1;
unsigned int lineout2_diff:1;
#define VM_RandomReadHint(v) ((v)->vm_flags & VM_RAND_READ)
/*
- * special vmas that are non-mergable, non-mlock()able
+ * Special vmas that are non-mergable, non-mlock()able.
+ * Note: mm/huge_memory.c VM_NO_THP depends on this definition.
*/
#define VM_SPECIAL (VM_IO | VM_DONTEXPAND | VM_RESERVED | VM_PFNMAP)
int clear_page_dirty_for_io(struct page *page);
/* Is the vma a continuation of the stack vma above it? */
-static inline int vma_stack_continue(struct vm_area_struct *vma, unsigned long addr)
+static inline int vma_growsdown(struct vm_area_struct *vma, unsigned long addr)
{
return vma && (vma->vm_end == addr) && (vma->vm_flags & VM_GROWSDOWN);
}
+static inline int stack_guard_page_start(struct vm_area_struct *vma,
+ unsigned long addr)
+{
+ return (vma->vm_flags & VM_GROWSDOWN) &&
+ (vma->vm_start == addr) &&
+ !vma_growsdown(vma->vm_prev, addr);
+}
+
+/* Is the vma a continuation of the stack vma below it? */
+static inline int vma_growsup(struct vm_area_struct *vma, unsigned long addr)
+{
+ return vma && (vma->vm_start == addr) && (vma->vm_flags & VM_GROWSUP);
+}
+
+static inline int stack_guard_page_end(struct vm_area_struct *vma,
+ unsigned long addr)
+{
+ return (vma->vm_flags & VM_GROWSUP) &&
+ (vma->vm_end == addr) &&
+ !vma_growsup(vma->vm_next, addr);
+}
+
extern unsigned long move_page_tables(struct vm_area_struct *vma,
unsigned long old_addr, struct vm_area_struct *new_vma,
unsigned long new_addr, unsigned long len);
struct work_struct clk_gate_work; /* delayed clock gate */
unsigned int clk_old; /* old clock value cache */
spinlock_t clk_lock; /* lock for clk fields */
- struct mutex clk_gate_mutex; /* mutex for clock gating */
#endif
/* host specific block data */
#ifdef CONFIG_NFS_V4
u64 cl_clientid; /* constant */
+ nfs4_verifier cl_confirm; /* Clientid verifier */
unsigned long cl_state;
spinlock_t cl_lock;
} du;
struct nfs_fsid fsid;
__u64 fileid;
+ __u64 mounted_on_fileid;
struct timespec atime;
struct timespec mtime;
struct timespec ctime;
#define NFS_ATTR_FATTR_PRECHANGE (1U << 18)
#define NFS_ATTR_FATTR_V4_REFERRAL (1U << 19) /* NFSv4 referral */
#define NFS_ATTR_FATTR_MOUNTPOINT (1U << 20) /* Treat as mountpoint */
+#define NFS_ATTR_FATTR_MOUNTED_ON_FILEID (1U << 21)
#define NFS_ATTR_FATTR (NFS_ATTR_FATTR_TYPE \
| NFS_ATTR_FATTR_MODE \
#define PCI_DEVICE_ID_INTEL_82840_HB 0x1a21
#define PCI_DEVICE_ID_INTEL_82845_HB 0x1a30
#define PCI_DEVICE_ID_INTEL_IOAT 0x1a38
-#define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22
#define PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MIN 0x1c41
#define PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MAX 0x1c5f
-#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22
#define PCI_DEVICE_ID_INTEL_PATSBURG_LPC_0 0x1d40
#define PCI_DEVICE_ID_INTEL_PATSBURG_LPC_1 0x1d41
#define PCI_DEVICE_ID_INTEL_DH89XXCC_LPC_MIN 0x2310
#define PCI_DEVICE_ID_INTEL_DH89XXCC_LPC_MAX 0x231f
-#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330
#define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410
#define PCI_DEVICE_ID_INTEL_82801AA_1 0x2411
#define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413
#define PCI_DEVICE_ID_INTEL_ICH10_5 0x3a60
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MIN 0x3b00
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MAX 0x3b1f
-#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
#define PCI_DEVICE_ID_INTEL_IOAT_SNB 0x402f
#define PCI_DEVICE_ID_INTEL_5100_16 0x65f0
#define PCI_DEVICE_ID_INTEL_5100_21 0x65f5
irqsafe_generic_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2)
# endif
# define irqsafe_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \
- __pcpu_double_call_return_int(irqsafe_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2))
+ __pcpu_double_call_return_bool(irqsafe_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2))
#endif
#endif /* __LINUX_PERCPU_H */
*/
extern struct pid *find_get_pid(int nr);
extern struct pid *find_ge_pid(int nr, struct pid_namespace *);
-int next_pidmap(struct pid_namespace *pid_ns, int last);
+int next_pidmap(struct pid_namespace *pid_ns, unsigned int last);
extern struct pid *alloc_pid(struct pid_namespace *ns);
extern void free_pid(struct pid *pid);
#include <linux/device.h>
#include <linux/mod_devicetable.h>
+struct mfd_cell;
+
struct platform_device {
const char * name;
int id;
const struct platform_device_id *id_entry;
+ /* MFD cell pointer */
+ struct mfd_cell *mfd_cell;
+
/* arch specific additions */
struct pdev_archdata archdata;
};
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/posix-timers.h>
+#include <linux/rwsem.h>
struct posix_clock;
* @ops: Functional interface to the clock
* @cdev: Character device instance for this clock
* @kref: Reference count.
- * @mutex: Protects the 'zombie' field from concurrent access.
+ * @rwsem: Protects the 'zombie' field from concurrent access.
* @zombie: If 'zombie' is true, then the hardware has disappeared.
* @release: A function to free the structure when the reference count reaches
* zero. May be NULL if structure is statically allocated.
struct posix_clock_operations ops;
struct cdev cdev;
struct kref kref;
- struct mutex mutex;
+ struct rw_semaphore rwsem;
bool zombie;
void (*release)(struct posix_clock *clk);
};
child->ptrace = current->ptrace;
__ptrace_link(child, current->parent);
}
+
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+ atomic_set(&child->ptrace_bp_refcnt, 1);
+#endif
}
/**
unsigned long args[6], unsigned int maxargs,
unsigned long *sp, unsigned long *pc);
-#endif
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+extern int ptrace_get_breakpoints(struct task_struct *tsk);
+extern void ptrace_put_breakpoints(struct task_struct *tsk);
+#else
+static inline void ptrace_put_breakpoints(struct task_struct *tsk) { }
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
+
+#endif /* __KERNEL */
#endif
};
/* Architecture and hardware-specific functions */
-extern void rio_register_mport(struct rio_mport *);
+extern int rio_register_mport(struct rio_mport *);
extern int rio_open_inb_mbox(struct rio_mport *, void *, int, int);
extern void rio_close_inb_mbox(struct rio_mport *, int);
extern int rio_open_outb_mbox(struct rio_mport *, void *, int, int);
#define RIO_DID_IDTCPS6Q 0x035f
#define RIO_DID_IDTCPS10Q 0x035e
#define RIO_DID_IDTCPS1848 0x0374
+#define RIO_DID_IDTCPS1432 0x0375
#define RIO_DID_IDTCPS1616 0x0379
#define RIO_DID_IDTVPS1616 0x0377
#define RIO_DID_IDTSPS1616 0x0378
struct rtc_wkalrm *alrm);
extern int rtc_set_alarm(struct rtc_device *rtc,
struct rtc_wkalrm *alrm);
+extern int rtc_initialize_alarm(struct rtc_device *rtc,
+ struct rtc_wkalrm *alrm);
extern void rtc_update_irq(struct rtc_device *rtc,
unsigned long num, unsigned long events);
#endif
struct mm_struct *mm, *active_mm;
+#ifdef CONFIG_COMPAT_BRK
+ unsigned brk_randomized:1;
+#endif
#if defined(SPLIT_RSS_COUNTING)
struct task_rss_stat rss_stat;
#endif
unsigned long memsw_nr_pages; /* uncharged mem+swap usage */
} memcg_batch;
#endif
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+ atomic_t ptrace_bp_refcnt;
+#endif
};
/* Future-safe accessor for struct task_struct's cpus_allowed. */
struct inode *new_dir, struct dentry *new_dentry);
int (*inode_readlink) (struct dentry *dentry);
int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd);
- int (*inode_permission) (struct inode *inode, int mask);
+ int (*inode_permission) (struct inode *inode, int mask, unsigned flags);
int (*inode_setattr) (struct dentry *dentry, struct iattr *attr);
int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry);
int (*inode_setxattr) (struct dentry *dentry, const char *name,
#define RPC_TASK_KILLED 0x0100 /* task was killed */
#define RPC_TASK_SOFT 0x0200 /* Use soft timeouts */
#define RPC_TASK_SOFTCONN 0x0400 /* Fail if can't connect */
+#define RPC_TASK_SENT 0x0800 /* message was sent */
+#define RPC_TASK_TIMEOUT 0x1000 /* fail with ETIMEDOUT on timeout */
#define RPC_IS_ASYNC(t) ((t)->tk_flags & RPC_TASK_ASYNC)
#define RPC_IS_SWAPPER(t) ((t)->tk_flags & RPC_TASK_SWAPPER)
#define RPC_DO_ROOTOVERRIDE(t) ((t)->tk_flags & RPC_TASK_ROOTCREDS)
#define RPC_ASSASSINATED(t) ((t)->tk_flags & RPC_TASK_KILLED)
-#define RPC_IS_SOFT(t) ((t)->tk_flags & RPC_TASK_SOFT)
+#define RPC_IS_SOFT(t) ((t)->tk_flags & (RPC_TASK_SOFT|RPC_TASK_TIMEOUT))
#define RPC_IS_SOFTCONN(t) ((t)->tk_flags & RPC_TASK_SOFTCONN)
+#define RPC_WAS_SENT(t) ((t)->tk_flags & RPC_TASK_SENT)
#define RPC_TASK_RUNNING 0
#define RPC_TASK_QUEUED 1
extern int hibernate(void);
extern bool system_entering_hibernation(void);
#else /* CONFIG_HIBERNATION */
+static inline void register_nosave_region(unsigned long b, unsigned long e) {}
+static inline void register_nosave_region_late(unsigned long b, unsigned long e) {}
static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
static inline void swsusp_set_page_free(struct page *p) {}
static inline void swsusp_unset_page_free(struct page *p) {}
extern struct mutex pm_mutex;
-#ifndef CONFIG_HIBERNATION
-static inline void register_nosave_region(unsigned long b, unsigned long e)
-{
-}
-static inline void register_nosave_region_late(unsigned long b, unsigned long e)
-{
-}
-
+#ifndef CONFIG_HIBERNATE_CALLBACKS
static inline void lock_system_sleep(void) {}
static inline void unlock_system_sleep(void) {}
# define EVENT_RX_PAUSED 5
# define EVENT_DEV_WAKING 6
# define EVENT_DEV_ASLEEP 7
+# define EVENT_DEV_OPEN 8
};
static inline struct usb_driver *driver_of(struct usb_interface *intf)
* Indicates to usbnet, that USB driver accumulates multiple IP packets.
* Affects statistic (counters) and short packet handling.
*/
-#define FLAG_MULTI_PACKET 0x1000
-#define FLAG_RX_ASSEMBLE 0x2000 /* rx packets may span >1 frames */
+#define FLAG_MULTI_PACKET 0x2000
+#define FLAG_RX_ASSEMBLE 0x4000 /* rx packets may span >1 frames */
/* init device ... can sleep, or cause probe() failure */
int (*bind)(struct usbnet *, struct usb_interface *);
V4L2_MBUS_FMT_RGB565_2X8_BE = 0x1007,
V4L2_MBUS_FMT_RGB565_2X8_LE = 0x1008,
- /* YUV (including grey) - next is 0x2013 */
+ /* YUV (including grey) - next is 0x2014 */
V4L2_MBUS_FMT_Y8_1X8 = 0x2001,
V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002,
V4L2_MBUS_FMT_VYUY8_1_5X8 = 0x2003,
V4L2_MBUS_FMT_Y10_1X10 = 0x200a,
V4L2_MBUS_FMT_YUYV10_2X10 = 0x200b,
V4L2_MBUS_FMT_YVYU10_2X10 = 0x200c,
+ V4L2_MBUS_FMT_Y12_1X12 = 0x2013,
V4L2_MBUS_FMT_UYVY8_1X16 = 0x200f,
V4L2_MBUS_FMT_VYUY8_1X16 = 0x2010,
V4L2_MBUS_FMT_YUYV8_1X16 = 0x2011,
V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d,
V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e,
- /* Bayer - next is 0x3013 */
+ /* Bayer - next is 0x3015 */
V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001,
+ V4L2_MBUS_FMT_SGBRG8_1X8 = 0x3013,
V4L2_MBUS_FMT_SGRBG8_1X8 = 0x3002,
+ V4L2_MBUS_FMT_SRGGB8_1X8 = 0x3014,
V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8 = 0x300b,
V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8 = 0x300c,
V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 = 0x3009,
#define V4L2_PIX_FMT_Y4 v4l2_fourcc('Y', '0', '4', ' ') /* 4 Greyscale */
#define V4L2_PIX_FMT_Y6 v4l2_fourcc('Y', '0', '6', ' ') /* 6 Greyscale */
#define V4L2_PIX_FMT_Y10 v4l2_fourcc('Y', '1', '0', ' ') /* 10 Greyscale */
+#define V4L2_PIX_FMT_Y12 v4l2_fourcc('Y', '1', '2', ' ') /* 12 Greyscale */
#define V4L2_PIX_FMT_Y16 v4l2_fourcc('Y', '1', '6', ' ') /* 16 Greyscale */
/* Palette formats */
UNEVICTABLE_PGCLEARED, /* on COW, page truncate */
UNEVICTABLE_PGSTRANDED, /* unable to isolate on unlock */
UNEVICTABLE_MLOCKFREED,
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ THP_FAULT_ALLOC,
+ THP_FAULT_FALLBACK,
+ THP_COLLAPSE_ALLOC,
+ THP_COLLAPSE_ALLOC_FAILED,
+ THP_SPLIT,
+#endif
NR_VM_EVENT_ITEMS
};
({ \
struct v4l2_subdev *__sd; \
__v4l2_device_call_subdevs_until_err_p(v4l2_dev, __sd, cond, o, \
- f, args...); \
+ f , ##args); \
})
/* Call the specified callback for all subdevs matching grp_id (if 0, then
*/
enum p9_msg_t {
- P9_TSYNCFS = 0,
- P9_RSYNCFS,
P9_TLERROR = 6,
P9_RLERROR,
P9_TSTATFS = 8,
void p9_client_begin_disconnect(struct p9_client *clnt);
struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid,
char *uname, u32 n_uname, char *aname);
-struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames,
- int clone);
+struct p9_fid *p9_client_walk(struct p9_fid *oldfid, uint16_t nwname,
+ char **wnames, int clone);
int p9_client_open(struct p9_fid *fid, int mode);
int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode,
char *extension);
gid_t gid, struct p9_qid *qid);
int p9_client_clunk(struct p9_fid *fid);
int p9_client_fsync(struct p9_fid *fid, int datasync);
-int p9_client_sync_fs(struct p9_fid *fid);
int p9_client_remove(struct p9_fid *fid);
int p9_client_read(struct p9_fid *fid, char *data, char __user *udata,
u64 offset, u32 count);
--- /dev/null
+/*
+ * AK4641 ALSA SoC Codec driver
+ *
+ * Copyright 2009 Philipp Zabel
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __AK4641_H
+#define __AK4641_H
+
+/**
+ * struct ak4641_platform_data - platform specific AK4641 configuration
+ * @gpio_power: GPIO to control external power to AK4641
+ * @gpio_npdn: GPIO connected to AK4641 nPDN pin
+ *
+ * Both GPIO parameters are optional.
+ */
+struct ak4641_platform_data {
+ int gpio_power;
+ int gpio_npdn;
+};
+
+#endif /* __AK4641_H */
void snd_ctl_free_one(struct snd_kcontrol * kcontrol);
int snd_ctl_add(struct snd_card * card, struct snd_kcontrol * kcontrol);
int snd_ctl_remove(struct snd_card * card, struct snd_kcontrol * kcontrol);
+int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, bool add_on_replace);
int snd_ctl_remove_id(struct snd_card * card, struct snd_ctl_elem_id *id);
int snd_ctl_rename_id(struct snd_card * card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id);
int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
--- /dev/null
+/*
+ * Platform data for MAX98095
+ *
+ * Copyright 2011 Maxim Integrated Products
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __SOUND_MAX98095_PDATA_H__
+#define __SOUND_MAX98095_PDATA_H__
+
+/* Equalizer filter response configuration */
+struct max98095_eq_cfg {
+ const char *name;
+ unsigned int rate;
+ u16 band1[5];
+ u16 band2[5];
+ u16 band3[5];
+ u16 band4[5];
+ u16 band5[5];
+};
+
+/* Biquad filter response configuration */
+struct max98095_biquad_cfg {
+ const char *name;
+ unsigned int rate;
+ u16 band1[5];
+ u16 band2[5];
+};
+
+/* codec platform data */
+struct max98095_pdata {
+
+ /* Equalizers for DAI1 and DAI2 */
+ struct max98095_eq_cfg *eq_cfg;
+ unsigned int eq_cfgcnt;
+
+ /* Biquad filter for DAI1 and DAI2 */
+ struct max98095_biquad_cfg *bq_cfg;
+ unsigned int bq_cfgcnt;
+
+ /* Analog/digital microphone configuration:
+ * 0 = analog microphone input (normal setting)
+ * 1 = digital microphone input
+ */
+ unsigned int digmic_left_mode:1;
+ unsigned int digmic_right_mode:1;
+};
+
+#endif
* SoC dynamic audio power management
*
* We can have up to 4 power domains
- * 1. Codec domain - VREF, VMID
+ * 1. Codec domain - VREF, VMID
* Usually controlled at codec probe/remove, although can be set
* at stream time if power is not needed for sidetone, etc.
* 2. Platform/Machine domain - physically connected inputs and outputs
/* codec domain */
#define SND_SOC_DAPM_VMID(wname) \
-{ .id = snd_soc_dapm_vmid, .name = wname, .kcontrols = NULL, \
+{ .id = snd_soc_dapm_vmid, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0}
/* platform domain */
#define SND_SOC_DAPM_INPUT(wname) \
-{ .id = snd_soc_dapm_input, .name = wname, .kcontrols = NULL, \
+{ .id = snd_soc_dapm_input, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM }
#define SND_SOC_DAPM_OUTPUT(wname) \
-{ .id = snd_soc_dapm_output, .name = wname, .kcontrols = NULL, \
+{ .id = snd_soc_dapm_output, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM }
#define SND_SOC_DAPM_MIC(wname, wevent) \
-{ .id = snd_soc_dapm_mic, .name = wname, .kcontrols = NULL, \
+{ .id = snd_soc_dapm_mic, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
#define SND_SOC_DAPM_HP(wname, wevent) \
-{ .id = snd_soc_dapm_hp, .name = wname, .kcontrols = NULL, \
+{ .id = snd_soc_dapm_hp, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
#define SND_SOC_DAPM_SPK(wname, wevent) \
-{ .id = snd_soc_dapm_spk, .name = wname, .kcontrols = NULL, \
+{ .id = snd_soc_dapm_spk, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
#define SND_SOC_DAPM_LINE(wname, wevent) \
-{ .id = snd_soc_dapm_line, .name = wname, .kcontrols = NULL, \
+{ .id = snd_soc_dapm_line, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
#define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\
wcontrols, wncontrols) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
+ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_OUT_DRV(wname, wreg, wshift, winvert,\
wcontrols, wncontrols) \
{ .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
+ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \
wcontrols, wncontrols)\
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
+ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, \
wcontrols, wncontrols)\
{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
- .shift = wshift, .invert = winvert, .kcontrols = wcontrols, \
+ .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = wncontrols}
#define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \
{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = NULL, .num_kcontrols = 0}
+ .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0}
#define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1}
+ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
#define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1}
+ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
#define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1}
+ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
#define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \
- .shift = wshift, .invert = winvert, .kcontrols = wcontrols, \
+ .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = 1}
/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
#define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\
wcontrols) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
+ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
#define SOC_MIXER_ARRAY(wname, wreg, wshift, winvert, \
wcontrols)\
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
+ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
#define SOC_MIXER_NAMED_CTL_ARRAY(wname, wreg, wshift, winvert, \
wcontrols)\
{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
- .shift = wshift, .invert = winvert, .kcontrols = wcontrols, \
+ .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = ARRAY_SIZE(wcontrols)}
/* path domain with event - event handler must return 0 for success */
#define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \
wncontrols, wevent, wflags) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \
+ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
.event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_OUT_DRV_E(wname, wreg, wshift, winvert, wcontrols, \
wncontrols, wevent, wflags) \
{ .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \
+ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
.event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \
wncontrols, wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \
+ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
.event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MIXER_NAMED_CTL_E(wname, wreg, wshift, winvert, \
wcontrols, wncontrols, wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, \
+ .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \
{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = NULL, .num_kcontrols = 0, \
+ .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0, \
.event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \
{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
+ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
.event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \
{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
+ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
.event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \
{ .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
+ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
.event = wevent, .event_flags = wflags}
/* additional sequencing control within an event type */
#define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
+ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
.event = wevent, .event_flags = wflags}
#define SOC_MIXER_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
+ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
.event = wevent, .event_flags = wflags}
#define SOC_MIXER_NAMED_CTL_E_ARRAY(wname, wreg, wshift, winvert, \
wcontrols, wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrols = wcontrols, \
+ .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = ARRAY_SIZE(wcontrols), .event = wevent, .event_flags = wflags}
/* events that are pre and post DAPM */
#define SND_SOC_DAPM_PRE(wname, wevent) \
-{ .id = snd_soc_dapm_pre, .name = wname, .kcontrols = NULL, \
+{ .id = snd_soc_dapm_pre, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD}
#define SND_SOC_DAPM_POST(wname, wevent) \
-{ .id = snd_soc_dapm_post, .name = wname, .kcontrols = NULL, \
+{ .id = snd_soc_dapm_post, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
/* generic widgets */
#define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \
-{ .id = wid, .name = wname, .kcontrols = NULL, .num_kcontrols = 0, \
+{ .id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \
.reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \
.on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
/* dapm sys fs - used by the core */
int snd_soc_dapm_sys_add(struct device *dev);
-void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm);
+void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,
+ struct dentry *parent);
/* dapm audio pin control and status */
int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm,
/* kcontrols that relate to this widget */
int num_kcontrols;
- const struct snd_kcontrol_new *kcontrols;
+ const struct snd_kcontrol_new *kcontrol_news;
+ struct snd_kcontrol **kcontrols;
/* widget input and outputs */
struct list_head sources;
#endif
};
+/* A list of widgets associated with an object, typically a snd_kcontrol */
+struct snd_soc_dapm_widget_list {
+ int num_widgets;
+ struct snd_soc_dapm_widget *widgets[0];
+};
+
#endif
extern struct snd_ac97_bus_ops soc_ac97_ops;
enum snd_soc_control_type {
- SND_SOC_CUSTOM,
+ SND_SOC_CUSTOM = 1,
SND_SOC_I2C,
SND_SOC_SPI,
};
void snd_soc_unregister_codec(struct device *dev);
int snd_soc_codec_volatile_register(struct snd_soc_codec *codec,
unsigned int reg);
+int snd_soc_codec_readable_register(struct snd_soc_codec *codec,
+ unsigned int reg);
+int snd_soc_codec_writable_register(struct snd_soc_codec *codec,
+ unsigned int reg);
int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
int addr_bits, int data_bits,
enum snd_soc_control_type control);
unsigned int reg);
int snd_soc_default_readable_register(struct snd_soc_codec *codec,
unsigned int reg);
+int snd_soc_default_writable_register(struct snd_soc_codec *codec,
+ unsigned int reg);
/* Utility functions to get clock rates from various things */
int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
size_t reg_size; /* reg_cache_size * reg_word_size */
int (*volatile_register)(struct snd_soc_codec *, unsigned int);
int (*readable_register)(struct snd_soc_codec *, unsigned int);
+ int (*writable_register)(struct snd_soc_codec *, unsigned int);
/* runtime */
struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */
/* codec IO */
void *control_data; /* codec control (i2c/3wire) data */
+ enum snd_soc_control_type control_type;
hw_write_t hw_write;
unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int);
unsigned int (*read)(struct snd_soc_codec *, unsigned int);
int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
+ int (*bulk_write_raw)(struct snd_soc_codec *, unsigned int, const void *, size_t);
void *reg_cache;
const void *reg_def_copy;
const struct snd_soc_cache_ops *cache_ops;
pm_message_t state);
int (*resume)(struct snd_soc_codec *);
- /* Default DAPM setup, added after probe() is run */
+ /* Default control and setup, added after probe() is run */
+ const struct snd_kcontrol_new *controls;
+ int num_controls;
const struct snd_soc_dapm_widget *dapm_widgets;
int num_dapm_widgets;
const struct snd_soc_dapm_route *dapm_routes;
size_t, unsigned int);
int (*volatile_register)(struct snd_soc_codec *, unsigned int);
int (*readable_register)(struct snd_soc_codec *, unsigned int);
+ int (*writable_register)(struct snd_soc_codec *, unsigned int);
short reg_cache_size;
short reg_cache_step;
short reg_word_size;
/* SoC card */
struct snd_soc_card {
const char *name;
+ const char *long_name;
+ const char *driver_name;
struct device *dev;
struct snd_card *snd_card;
struct module *owner;
struct snd_soc_pcm_runtime *rtd_aux;
int num_aux_rtd;
+ const struct snd_kcontrol_new *controls;
+ int num_controls;
+
/*
* Card-specific routes and widgets.
*/
- struct snd_soc_dapm_widget *dapm_widgets;
+ const struct snd_soc_dapm_widget *dapm_widgets;
int num_dapm_widgets;
- struct snd_soc_dapm_route *dapm_routes;
+ const struct snd_soc_dapm_route *dapm_routes;
int num_dapm_routes;
struct work_struct deferred_resume_work;
unsigned char shift_r;
unsigned int max;
unsigned int mask;
- const char **texts;
+ const char * const *texts;
const unsigned int *values;
void *dapm;
};
unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg);
unsigned int snd_soc_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int val);
+unsigned int snd_soc_bulk_write_raw(struct snd_soc_codec *codec,
+ unsigned int reg, const void *data, size_t len);
/* device driver data */
INIT_LIST_HEAD(&card->dapm_list);
}
+int snd_soc_util_init(void);
+void snd_soc_util_exit(void);
+
#include <sound/soc-dai.h>
#ifdef CONFIG_DEBUG_FS
#include <media/v4l2-dev.h>
#include <media/v4l2-ioctl.h>
+#define TEA575X_FMIF 10700
+
+#define TEA575X_DATA (1 << 0)
+#define TEA575X_CLK (1 << 1)
+#define TEA575X_WREN (1 << 2)
+#define TEA575X_MOST (1 << 3)
+
struct snd_tea575x;
struct snd_tea575x_ops {
- void (*write)(struct snd_tea575x *tea, unsigned int val);
- unsigned int (*read)(struct snd_tea575x *tea);
- void (*mute)(struct snd_tea575x *tea, unsigned int mute);
+ void (*set_pins)(struct snd_tea575x *tea, u8 pins);
+ u8 (*get_pins)(struct snd_tea575x *tea);
+ void (*set_direction)(struct snd_tea575x *tea, bool output);
};
struct snd_tea575x {
- struct snd_card *card;
struct video_device *vd; /* video device */
- int dev_nr; /* requested device number + 1 */
- int tea5759; /* 5759 chip is present */
- int mute; /* Device is muted? */
- unsigned int freq_fixup; /* crystal onboard */
+ bool tea5759; /* 5759 chip is present */
+ bool mute; /* Device is muted? */
+ bool stereo; /* receiving stereo */
+ bool tuned; /* tuned to a station */
unsigned int val; /* hw value */
unsigned long freq; /* frequency */
unsigned long in_use; /* set if the device is in use */
struct snd_tea575x_ops *ops;
void *private_data;
+ u8 card[32];
+ u8 bus_info[32];
};
-void snd_tea575x_init(struct snd_tea575x *tea);
+int snd_tea575x_init(struct snd_tea575x *tea);
void snd_tea575x_exit(struct snd_tea575x *tea);
#endif /* __SOUND_TEA575X_TUNER_H */
/*
* Platform header for Texas Instruments TLV320DAC33 codec driver
*
- * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* Copyright: (C) 2009 Nokia Corporation
*
*
* Copyright (C) Nokia Corporation
*
- * Written by Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
--- /dev/null
+/*
+ * linux/sound/wm8915.h -- Platform data for WM8915
+ *
+ * Copyright 2011 Wolfson Microelectronics. PLC.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_SND_WM8903_H
+#define __LINUX_SND_WM8903_H
+
+enum wm8915_inmode {
+ WM8915_DIFFERRENTIAL_1 = 0, /* IN1xP - IN1xN */
+ WM8915_INVERTING = 1, /* IN1xN */
+ WM8915_NON_INVERTING = 2, /* IN1xP */
+ WM8915_DIFFERENTIAL_2 = 3, /* IN2xP - IN2xP */
+};
+
+/**
+ * ReTune Mobile configurations are specified with a label, sample
+ * rate and set of values to write (the enable bits will be ignored).
+ *
+ * Configurations are expected to be generated using the ReTune Mobile
+ * control panel in WISCE - see http://www.wolfsonmicro.com/wisce/
+ */
+struct wm8915_retune_mobile_config {
+ const char *name;
+ int rate;
+ u16 regs[20];
+};
+
+#define WM8915_SET_DEFAULT 0x10000
+
+struct wm8915_pdata {
+ int irq_flags; /** Set IRQ trigger flags; default active low */
+
+ int ldo_ena; /** GPIO for LDO1; -1 for none */
+
+ int micdet_def; /** Default MICDET_SRC/HP1FB_SRC/MICD_BIAS */
+
+ enum wm8915_inmode inl_mode;
+ enum wm8915_inmode inr_mode;
+
+ u32 spkmute_seq; /** Value for register 0x802 */
+
+ int gpio_base;
+ u32 gpio_default[5];
+
+ int num_retune_mobile_cfgs;
+ struct wm8915_retune_mobile_config *retune_mobile_cfgs;
+};
+
+#endif
/* Use to set GPIO default values to zero */
#define WM8962_GPIO_SET 0x10000
+#define WM8962_GPIO_FN_CLKOUT 0
+#define WM8962_GPIO_FN_LOGIC 1
+#define WM8962_GPIO_FN_SDOUT 2
+#define WM8962_GPIO_FN_IRQ 3
+#define WM8962_GPIO_FN_THERMAL 4
+#define WM8962_GPIO_FN_PLL2_LOCK 6
+#define WM8962_GPIO_FN_PLL3_LOCK 7
+#define WM8962_GPIO_FN_FLL_LOCK 9
+#define WM8962_GPIO_FN_DRC_ACT 10
+#define WM8962_GPIO_FN_WSEQ_DONE 11
+#define WM8962_GPIO_FN_ALC_NG_ACT 12
+#define WM8962_GPIO_FN_ALC_PEAK_LIMIT 13
+#define WM8962_GPIO_FN_ALC_SATURATION 14
+#define WM8962_GPIO_FN_ALC_LEVEL_THR 15
+#define WM8962_GPIO_FN_ALC_LEVEL_LOCK 16
+#define WM8962_GPIO_FN_FIFO_ERR 17
+#define WM8962_GPIO_FN_OPCLK 18
+#define WM8962_GPIO_FN_DMICCLK 19
+#define WM8962_GPIO_FN_DMICDAT 20
+#define WM8962_GPIO_FN_MICD 21
+#define WM8962_GPIO_FN_MICSCD 22
+
struct wm8962_pdata {
int gpio_base;
u32 gpio_init[WM8962_MAX_GPIO];
DECLARE_EVENT_CLASS(block_unplug,
- TP_PROTO(struct request_queue *q),
+ TP_PROTO(struct request_queue *q, unsigned int depth, bool explicit),
- TP_ARGS(q),
+ TP_ARGS(q, depth, explicit),
TP_STRUCT__entry(
__field( int, nr_rq )
),
TP_fast_assign(
- __entry->nr_rq = q->rq.count[READ] + q->rq.count[WRITE];
+ __entry->nr_rq = depth;
memcpy(__entry->comm, current->comm, TASK_COMM_LEN);
),
);
/**
- * block_unplug_timer - timed release of operations requests in queue to device driver
- * @q: request queue to unplug
- *
- * Unplug the request queue @q because a timer expired and allow block
- * operation requests to be sent to the device driver.
- */
-DEFINE_EVENT(block_unplug, block_unplug_timer,
-
- TP_PROTO(struct request_queue *q),
-
- TP_ARGS(q)
-);
-
-/**
- * block_unplug_io - release of operations requests in request queue
+ * block_unplug - release of operations requests in request queue
* @q: request queue to unplug
+ * @depth: number of requests just added to the queue
+ * @explicit: whether this was an explicit unplug, or one from schedule()
*
* Unplug request queue @q because device driver is scheduled to work
* on elements in the request queue.
*/
-DEFINE_EVENT(block_unplug, block_unplug_io,
+DEFINE_EVENT(block_unplug, block_unplug,
- TP_PROTO(struct request_queue *q),
+ TP_PROTO(struct request_queue *q, unsigned int depth, bool explicit),
- TP_ARGS(q)
+ TP_ARGS(q, depth, explicit)
);
/**
environments which can tolerate a "non-standard" kernel.
Only use this if you really know what you are doing.
-config EMBEDDED
- bool "Embedded system"
- select EXPERT
- help
- This option should be enabled if compiling the kernel for
- an embedded system so certain expert options are available
- for configuration.
-
config UID16
bool "Enable 16-bit UID system calls" if EXPERT
depends on ARM || BLACKFIN || CRIS || FRV || H8300 || X86_32 || M68K || (S390 && !64BIT) || SUPERH || SPARC32 || (SPARC64 && COMPAT) || UML || (X86_64 && IA32_EMULATION)
by some high performance threaded applications. Disabling
this option saves about 7k.
+config EMBEDDED
+ bool "Embedded system"
+ select EXPERT
+ help
+ This option should be enabled if compiling the kernel for
+ an embedded system so certain expert options are available
+ for configuration.
+
config HAVE_PERF_EVENTS
bool
help
per cpu and per node queues.
config SLUB
+ depends on BROKEN || NUMA || !DISCONTIGMEM
bool "SLUB (Unqueued Allocator)"
help
SLUB is a slab allocator that minimizes cache line usage
/*
* FIXME: do that only when needed, using sched_exit tracepoint
*/
- flush_ptrace_hw_breakpoint(tsk);
+ ptrace_put_breakpoints(tsk);
exit_notify(tsk, group_dead);
#ifdef CONFIG_NUMA
restart->futex.val = val;
restart->futex.time = abs_time->tv64;
restart->futex.bitset = bitset;
- restart->futex.flags = flags;
+ restart->futex.flags = flags | FLAGS_HAS_TIMEOUT;
ret = -ERESTART_RESTARTBLOCK;
}
};
-static int hrtimer_clock_to_base_table[MAX_CLOCKS];
+static int hrtimer_clock_to_base_table[MAX_CLOCKS] = {
+ [CLOCK_REALTIME] = HRTIMER_BASE_REALTIME,
+ [CLOCK_MONOTONIC] = HRTIMER_BASE_MONOTONIC,
+ [CLOCK_BOOTTIME] = HRTIMER_BASE_BOOTTIME,
+};
static inline int hrtimer_clockid_to_base(clockid_t clock_id)
{
void __init hrtimers_init(void)
{
- hrtimer_clock_to_base_table[CLOCK_REALTIME] = HRTIMER_BASE_REALTIME;
- hrtimer_clock_to_base_table[CLOCK_MONOTONIC] = HRTIMER_BASE_MONOTONIC;
- hrtimer_clock_to_base_table[CLOCK_BOOTTIME] = HRTIMER_BASE_BOOTTIME;
-
hrtimer_cpu_notify(&hrtimers_nb, (unsigned long)CPU_UP_PREPARE,
(void *)(long)smp_processor_id());
register_cpu_notifier(&hrtimers_nb);
} else {
seq_printf(p, " %8s", "None");
}
-#ifdef CONFIG_GENIRC_IRQ_SHOW_LEVEL
+#ifdef CONFIG_GENERIC_IRQ_SHOW_LEVEL
seq_printf(p, " %-8s", irqd_is_level_type(&desc->irq_data) ? "Level" : "Edge");
#endif
if (desc->name)
#include <linux/vmalloc.h>
#include <linux/swap.h>
#include <linux/kmsg_dump.h>
+#include <linux/syscore_ops.h>
#include <asm/page.h>
#include <asm/uaccess.h>
local_irq_disable();
/* Suspend system devices */
error = sysdev_suspend(PMSG_FREEZE);
+ if (!error) {
+ error = syscore_suspend();
+ if (error)
+ sysdev_resume();
+ }
if (error)
goto Enable_irqs;
} else
#ifdef CONFIG_KEXEC_JUMP
if (kexec_image->preserve_context) {
+ syscore_resume();
sysdev_resume();
Enable_irqs:
local_irq_enable();
}
if (mode & PERF_CGROUP_SWIN) {
+ WARN_ON_ONCE(cpuctx->cgrp);
/* set cgrp before ctxsw in to
* allow event_filter_match() to not
* have to pass task around
if (!ctx || !ctx->nr_events)
goto out;
+ /*
+ * We must ctxsw out cgroup events to avoid conflict
+ * when invoking perf_task_event_sched_in() later on
+ * in this function. Otherwise we end up trying to
+ * ctxswin cgroup events which are already scheduled
+ * in.
+ */
+ perf_cgroup_sched_out(current);
task_ctx_sched_out(ctx, EVENT_ALL);
raw_spin_lock(&ctx->lock);
raw_spin_unlock(&ctx->lock);
+ /*
+ * Also calls ctxswin for cgroup events, if any:
+ */
perf_event_context_sched_in(ctx, ctx->task);
out:
local_irq_restore(flags);
return -1;
}
-int next_pidmap(struct pid_namespace *pid_ns, int last)
+int next_pidmap(struct pid_namespace *pid_ns, unsigned int last)
{
int offset;
struct pidmap *map, *end;
+ if (last >= PID_MAX_LIMIT)
+ return -1;
+
offset = (last + 1) & BITS_PER_PAGE_MASK;
map = &pid_ns->pidmap[(last + 1)/BITS_PER_PAGE];
end = &pid_ns->pidmap[PIDMAP_ENTRIES];
Turning OFF this setting is NOT recommended! If in doubt, say Y.
+config HIBERNATE_CALLBACKS
+ bool
+
config HIBERNATION
bool "Hibernation (aka 'suspend to disk')"
depends on SWAP && ARCH_HIBERNATION_POSSIBLE
+ select HIBERNATE_CALLBACKS
select LZO_COMPRESS
select LZO_DECOMPRESS
---help---
config PM_SLEEP
def_bool y
- depends on SUSPEND || HIBERNATION || XEN_SAVE_RESTORE
+ depends on SUSPEND || HIBERNATE_CALLBACKS
config PM_SLEEP_SMP
def_bool y
local_irq_disable();
error = sysdev_suspend(PMSG_FREEZE);
- if (!error)
+ if (!error) {
error = syscore_suspend();
+ if (error)
+ sysdev_resume();
+ }
if (error) {
printk(KERN_ERR "PM: Some system devices failed to power down, "
"aborting hibernation\n");
local_irq_disable();
error = sysdev_suspend(PMSG_QUIESCE);
- if (!error)
+ if (!error) {
error = syscore_suspend();
+ if (error)
+ sysdev_resume();
+ }
if (error)
goto Enable_irqs;
BUG_ON(!irqs_disabled());
error = sysdev_suspend(PMSG_SUSPEND);
- if (!error)
+ if (!error) {
error = syscore_suspend();
+ if (error)
+ sysdev_resume();
+ }
if (!error) {
if (!(suspend_test(TEST_CORE) || pm_wakeup_pending())) {
error = suspend_ops->enter(state);
#include <linux/syscalls.h>
#include <linux/uaccess.h>
#include <linux/regset.h>
+#include <linux/hw_breakpoint.h>
/*
return ret;
}
#endif /* CONFIG_COMPAT */
+
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+int ptrace_get_breakpoints(struct task_struct *tsk)
+{
+ if (atomic_inc_not_zero(&tsk->ptrace_bp_refcnt))
+ return 0;
+
+ return -1;
+}
+
+void ptrace_put_breakpoints(struct task_struct *tsk)
+{
+ if (atomic_dec_and_test(&tsk->ptrace_bp_refcnt))
+ flush_ptrace_hw_breakpoint(tsk);
+}
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
try_to_wake_up_local(to_wakeup);
}
deactivate_task(rq, prev, DEQUEUE_SLEEP);
+
+ /*
+ * If we are going to sleep and we have plugged IO queued, make
+ * sure to submit it to avoid deadlocks.
+ */
+ if (blk_needs_flush_plug(prev)) {
+ raw_spin_unlock(&rq->lock);
+ blk_schedule_flush_plug(prev);
+ raw_spin_lock(&rq->lock);
+ }
}
switch_count = &prev->nvcsw;
}
- /*
- * If we are going to sleep and we have plugged IO queued, make
- * sure to submit it to avoid deadlocks.
- */
- if (prev->state != TASK_RUNNING && blk_needs_flush_plug(prev)) {
- raw_spin_unlock(&rq->lock);
- blk_flush_plug(prev);
- raw_spin_lock(&rq->lock);
- }
-
pre_schedule(rq, prev);
if (unlikely(!rq->nr_running))
enum cpu_idle_type idle, int *all_pinned,
int *this_best_prio, struct cfs_rq *busiest_cfs_rq)
{
- int loops = 0, pulled = 0, pinned = 0;
+ int loops = 0, pulled = 0;
long rem_load_move = max_load_move;
struct task_struct *p, *n;
if (max_load_move == 0)
goto out;
- pinned = 1;
-
list_for_each_entry_safe(p, n, &busiest_cfs_rq->tasks, se.group_node) {
if (loops++ > sysctl_sched_nr_migrate)
break;
if ((p->se.load.weight >> 1) > rem_load_move ||
- !can_migrate_task(p, busiest, this_cpu, sd, idle, &pinned))
+ !can_migrate_task(p, busiest, this_cpu, sd, idle,
+ all_pinned))
continue;
pull_task(busiest, p, this_rq, this_cpu);
*/
schedstat_add(sd, lb_gained[idle], pulled);
- if (all_pinned)
- *all_pinned = pinned;
-
return max_load_move - rem_load_move;
}
if (!sds.busiest || sds.busiest_nr_running == 0)
goto out_balanced;
+ sds.avg_load = (SCHED_LOAD_SCALE * sds.total_load) / sds.total_pwr;
+
/*
* If the busiest group is imbalanced the below checks don't
* work because they assumes all things are equal, which typically
* Don't pull any tasks if this group is already above the domain
* average load.
*/
- sds.avg_load = (SCHED_LOAD_SCALE * sds.total_load) / sds.total_pwr;
if (sds.this_load >= sds.avg_load)
goto out_balanced;
* still unbalanced. ld_moved simply stays zero, so it is
* correctly treated as an imbalance.
*/
+ all_pinned = 1;
local_irq_save(flags);
double_rq_lock(this_rq, busiest);
ld_moved = move_tasks(this_rq, this_cpu, busiest,
*/
#include <linux/device.h>
#include <linux/file.h>
-#include <linux/mutex.h>
#include <linux/posix-clock.h>
#include <linux/slab.h>
#include <linux/syscalls.h>
{
struct posix_clock *clk = fp->private_data;
- mutex_lock(&clk->mutex);
+ down_read(&clk->rwsem);
if (!clk->zombie)
return clk;
- mutex_unlock(&clk->mutex);
+ up_read(&clk->rwsem);
return NULL;
}
static void put_posix_clock(struct posix_clock *clk)
{
- mutex_unlock(&clk->mutex);
+ up_read(&clk->rwsem);
}
static ssize_t posix_clock_read(struct file *fp, char __user *buf,
struct posix_clock *clk =
container_of(inode->i_cdev, struct posix_clock, cdev);
- mutex_lock(&clk->mutex);
+ down_read(&clk->rwsem);
if (clk->zombie) {
err = -ENODEV;
fp->private_data = clk;
}
out:
- mutex_unlock(&clk->mutex);
+ up_read(&clk->rwsem);
return err;
}
int err;
kref_init(&clk->kref);
- mutex_init(&clk->mutex);
+ init_rwsem(&clk->rwsem);
cdev_init(&clk->cdev, &posix_clock_file_operations);
clk->cdev.owner = clk->ops.owner;
err = cdev_add(&clk->cdev, devid, 1);
- if (err)
- goto no_cdev;
return err;
-no_cdev:
- mutex_destroy(&clk->mutex);
- return err;
}
EXPORT_SYMBOL_GPL(posix_clock_register);
static void delete_clock(struct kref *kref)
{
struct posix_clock *clk = container_of(kref, struct posix_clock, kref);
- mutex_destroy(&clk->mutex);
+
if (clk->release)
clk->release(clk);
}
{
cdev_del(&clk->cdev);
- mutex_lock(&clk->mutex);
+ down_write(&clk->rwsem);
clk->zombie = true;
- mutex_unlock(&clk->mutex);
+ up_write(&clk->rwsem);
kref_put(&clk->kref, delete_clock);
}
config FUNCTION_TRACER
bool "Kernel Function Tracer"
depends on HAVE_FUNCTION_TRACER
- select FRAME_POINTER if !ARM_UNWIND && !S390
+ select FRAME_POINTER if !ARM_UNWIND && !S390 && !MICROBLAZE
select KALLSYMS
select GENERIC_TRACER
select CONTEXT_SWITCH_TRACER
__blk_add_trace(bt, 0, 0, 0, BLK_TA_PLUG, 0, 0, NULL);
}
-static void blk_add_trace_unplug_io(void *ignore, struct request_queue *q)
+static void blk_add_trace_unplug(void *ignore, struct request_queue *q,
+ unsigned int depth, bool explicit)
{
struct blk_trace *bt = q->blk_trace;
if (bt) {
- unsigned int pdu = q->rq.count[READ] + q->rq.count[WRITE];
- __be64 rpdu = cpu_to_be64(pdu);
+ __be64 rpdu = cpu_to_be64(depth);
+ u32 what;
- __blk_add_trace(bt, 0, 0, 0, BLK_TA_UNPLUG_IO, 0,
- sizeof(rpdu), &rpdu);
- }
-}
-
-static void blk_add_trace_unplug_timer(void *ignore, struct request_queue *q)
-{
- struct blk_trace *bt = q->blk_trace;
-
- if (bt) {
- unsigned int pdu = q->rq.count[READ] + q->rq.count[WRITE];
- __be64 rpdu = cpu_to_be64(pdu);
+ if (explicit)
+ what = BLK_TA_UNPLUG_IO;
+ else
+ what = BLK_TA_UNPLUG_TIMER;
- __blk_add_trace(bt, 0, 0, 0, BLK_TA_UNPLUG_TIMER, 0,
- sizeof(rpdu), &rpdu);
+ __blk_add_trace(bt, 0, 0, 0, what, 0, sizeof(rpdu), &rpdu);
}
}
WARN_ON(ret);
ret = register_trace_block_plug(blk_add_trace_plug, NULL);
WARN_ON(ret);
- ret = register_trace_block_unplug_timer(blk_add_trace_unplug_timer, NULL);
- WARN_ON(ret);
- ret = register_trace_block_unplug_io(blk_add_trace_unplug_io, NULL);
+ ret = register_trace_block_unplug(blk_add_trace_unplug, NULL);
WARN_ON(ret);
ret = register_trace_block_split(blk_add_trace_split, NULL);
WARN_ON(ret);
unregister_trace_block_rq_remap(blk_add_trace_rq_remap, NULL);
unregister_trace_block_bio_remap(blk_add_trace_bio_remap, NULL);
unregister_trace_block_split(blk_add_trace_split, NULL);
- unregister_trace_block_unplug_io(blk_add_trace_unplug_io, NULL);
- unregister_trace_block_unplug_timer(blk_add_trace_unplug_timer, NULL);
+ unregister_trace_block_unplug(blk_add_trace_unplug, NULL);
unregister_trace_block_plug(blk_add_trace_plug, NULL);
unregister_trace_block_sleeprq(blk_add_trace_sleeprq, NULL);
unregister_trace_block_getrq(blk_add_trace_getrq, NULL);
entry->preempt_count = pc & 0xff;
entry->pid = (tsk) ? tsk->pid : 0;
+ entry->padding = 0;
entry->flags =
#ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT
(irqs_disabled_flags(flags) ? TRACE_FLAG_IRQS_OFF : 0) |
__common_field(unsigned char, flags);
__common_field(unsigned char, preempt_count);
__common_field(int, pid);
+ __common_field(int, padding);
return ret;
}
p = kthread_create(watchdog, (void *)(unsigned long)cpu, "watchdog/%d", cpu);
if (IS_ERR(p)) {
printk(KERN_ERR "softlockup watchdog for %i failed\n", cpu);
- if (!err)
+ if (!err) {
/* if hardlockup hasn't already set this */
err = PTR_ERR(p);
+ /* and disable the perf event */
+ watchdog_nmi_disable(cpu);
+ }
goto out;
}
kthread_bind(p, cpu);
return true;
spin_unlock_irq(&gcwq->lock);
- /* CPU has come up in between, retry migration */
+ /*
+ * We've raced with CPU hot[un]plug. Give it a breather
+ * and retry migration. cond_resched() is required here;
+ * otherwise, we might deadlock against cpu_stop trying to
+ * bring down the CPU on non-preemptive kernel.
+ */
cpu_relax();
+ cond_resched();
}
}
/**
* flex_array_prealloc - guarantee that array space exists
- * @fa: the flex array for which to preallocate parts
- * @start: index of first array element for which space is allocated
- * @end: index of last (inclusive) element for which space is allocated
- * @flags: page allocation flags
+ * @fa: the flex array for which to preallocate parts
+ * @start: index of first array element for which space is allocated
+ * @nr_elements: number of elements for which space is allocated
+ * @flags: page allocation flags
*
* This will guarantee that no future calls to flex_array_put()
* will allocate memory. It can be used if you are expecting to
* Locking must be provided by the caller.
*/
int flex_array_prealloc(struct flex_array *fa, unsigned int start,
- unsigned int end, gfp_t flags)
+ unsigned int nr_elements, gfp_t flags)
{
int start_part;
int end_part;
int part_nr;
+ unsigned int end;
struct flex_array_part *part;
- if (start >= fa->total_nr_elements || end >= fa->total_nr_elements)
+ if (!start && !nr_elements)
+ return 0;
+ if (start >= fa->total_nr_elements)
+ return -ENOSPC;
+ if (!nr_elements)
+ return 0;
+
+ end = start + nr_elements - 1;
+
+ if (end >= fa->total_nr_elements)
return -ENOSPC;
if (elements_fit_in_base(fa))
return 0;
int part_nr;
int ret = 0;
+ if (!fa->total_nr_elements)
+ return 0;
if (elements_fit_in_base(fa))
return ret;
for (part_nr = 0; part_nr < FLEX_ARRAY_NR_BASE_PTRS; part_nr++) {
val = *s - '0';
else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f')
val = _tolower(*s) - 'a' + 10;
- else if (*s == '\n') {
- if (*(s + 1) == '\0')
- break;
- else
- return -EINVAL;
- } else
+ else if (*s == '\n' && *(s + 1) == '\0')
+ break;
+ else
return -EINVAL;
if (val >= base)
{"65537", 10, 65537},
{"2147483646", 10, 2147483646},
{"2147483647", 10, 2147483647},
- {"2147483648", 10, 2147483648},
- {"2147483649", 10, 2147483649},
- {"4294967294", 10, 4294967294},
- {"4294967295", 10, 4294967295},
- {"4294967296", 10, 4294967296},
- {"4294967297", 10, 4294967297},
+ {"2147483648", 10, 2147483648ULL},
+ {"2147483649", 10, 2147483649ULL},
+ {"4294967294", 10, 4294967294ULL},
+ {"4294967295", 10, 4294967295ULL},
+ {"4294967296", 10, 4294967296ULL},
+ {"4294967297", 10, 4294967297ULL},
{"9223372036854775806", 10, 9223372036854775806ULL},
{"9223372036854775807", 10, 9223372036854775807ULL},
{"9223372036854775808", 10, 9223372036854775808ULL},
{"65537", 10, 65537},
{"2147483646", 10, 2147483646},
{"2147483647", 10, 2147483647},
- {"2147483648", 10, 2147483648},
- {"2147483649", 10, 2147483649},
- {"4294967294", 10, 4294967294},
- {"4294967295", 10, 4294967295},
- {"4294967296", 10, 4294967296},
- {"4294967297", 10, 4294967297},
+ {"2147483648", 10, 2147483648LL},
+ {"2147483649", 10, 2147483649LL},
+ {"4294967294", 10, 4294967294LL},
+ {"4294967295", 10, 4294967295LL},
+ {"4294967296", 10, 4294967296LL},
+ {"4294967297", 10, 4294967297LL},
{"9223372036854775806", 10, 9223372036854775806LL},
{"9223372036854775807", 10, 9223372036854775807LL},
};
{"65537", 10, 65537},
{"2147483646", 10, 2147483646},
{"2147483647", 10, 2147483647},
- {"2147483648", 10, 2147483648},
- {"2147483649", 10, 2147483649},
- {"4294967294", 10, 4294967294},
- {"4294967295", 10, 4294967295},
+ {"2147483648", 10, 2147483648U},
+ {"2147483649", 10, 2147483649U},
+ {"4294967294", 10, 4294967294U},
+ {"4294967295", 10, 4294967295U},
};
TEST_OK(kstrtou32, u32, "%u", test_u32_ok);
}
*/
tmp = b->in[b->in_pos++];
+ if (tmp == 0x00)
+ return XZ_STREAM_END;
+
if (tmp >= 0xE0 || tmp == 0x01) {
s->lzma2.need_props = true;
s->lzma2.need_dict_reset = false;
lzma_reset(s);
}
} else {
- if (tmp == 0x00)
- return XZ_STREAM_END;
-
if (tmp > 0x02)
return XZ_DATA_ERROR;
struct kobj_attribute *attr, char *buf,
enum transparent_hugepage_flag flag)
{
- if (test_bit(flag, &transparent_hugepage_flags))
- return sprintf(buf, "[yes] no\n");
- else
- return sprintf(buf, "yes [no]\n");
+ return sprintf(buf, "%d\n",
+ !!test_bit(flag, &transparent_hugepage_flags));
}
+
static ssize_t single_flag_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count,
enum transparent_hugepage_flag flag)
{
- if (!memcmp("yes", buf,
- min(sizeof("yes")-1, count))) {
+ unsigned long value;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &value);
+ if (ret < 0)
+ return ret;
+ if (value > 1)
+ return -EINVAL;
+
+ if (value)
set_bit(flag, &transparent_hugepage_flags);
- } else if (!memcmp("no", buf,
- min(sizeof("no")-1, count))) {
+ else
clear_bit(flag, &transparent_hugepage_flags);
- } else
- return -EINVAL;
return count;
}
return VM_FAULT_OOM;
page = alloc_hugepage_vma(transparent_hugepage_defrag(vma),
vma, haddr, numa_node_id(), 0);
- if (unlikely(!page))
+ if (unlikely(!page)) {
+ count_vm_event(THP_FAULT_FALLBACK);
goto out;
+ }
+ count_vm_event(THP_FAULT_ALLOC);
if (unlikely(mem_cgroup_newpage_charge(page, mm, GFP_KERNEL))) {
put_page(page);
goto out;
new_page = NULL;
if (unlikely(!new_page)) {
+ count_vm_event(THP_FAULT_FALLBACK);
ret = do_huge_pmd_wp_page_fallback(mm, vma, address,
pmd, orig_pmd, page, haddr);
put_page(page);
goto out;
}
+ count_vm_event(THP_FAULT_ALLOC);
if (unlikely(mem_cgroup_newpage_charge(new_page, mm, GFP_KERNEL))) {
put_page(new_page);
BUG_ON(!PageSwapBacked(page));
__split_huge_page(page, anon_vma);
+ count_vm_event(THP_SPLIT);
BUG_ON(PageCompound(page));
out_unlock:
return ret;
}
+#define VM_NO_THP (VM_SPECIAL|VM_INSERTPAGE|VM_MIXEDMAP|VM_SAO| \
+ VM_HUGETLB|VM_SHARED|VM_MAYSHARE)
+
int hugepage_madvise(struct vm_area_struct *vma,
unsigned long *vm_flags, int advice)
{
/*
* Be somewhat over-protective like KSM for now!
*/
- if (*vm_flags & (VM_HUGEPAGE |
- VM_SHARED | VM_MAYSHARE |
- VM_PFNMAP | VM_IO | VM_DONTEXPAND |
- VM_RESERVED | VM_HUGETLB | VM_INSERTPAGE |
- VM_MIXEDMAP | VM_SAO))
+ if (*vm_flags & (VM_HUGEPAGE | VM_NO_THP))
return -EINVAL;
*vm_flags &= ~VM_NOHUGEPAGE;
*vm_flags |= VM_HUGEPAGE;
/*
* Be somewhat over-protective like KSM for now!
*/
- if (*vm_flags & (VM_NOHUGEPAGE |
- VM_SHARED | VM_MAYSHARE |
- VM_PFNMAP | VM_IO | VM_DONTEXPAND |
- VM_RESERVED | VM_HUGETLB | VM_INSERTPAGE |
- VM_MIXEDMAP | VM_SAO))
+ if (*vm_flags & (VM_NOHUGEPAGE | VM_NO_THP))
return -EINVAL;
*vm_flags &= ~VM_HUGEPAGE;
*vm_flags |= VM_NOHUGEPAGE;
* page fault if needed.
*/
return 0;
- if (vma->vm_file || vma->vm_ops)
+ if (vma->vm_ops)
/* khugepaged not yet working on file or special mappings */
return 0;
- VM_BUG_ON(is_linear_pfn_mapping(vma) || is_pfn_mapping(vma));
+ /*
+ * If is_pfn_mapping() is true is_learn_pfn_mapping() must be
+ * true too, verify it here.
+ */
+ VM_BUG_ON(is_linear_pfn_mapping(vma) || vma->vm_flags & VM_NO_THP);
hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK;
hend = vma->vm_end & HPAGE_PMD_MASK;
if (hstart < hend)
node, __GFP_OTHER_NODE);
if (unlikely(!new_page)) {
up_read(&mm->mmap_sem);
+ count_vm_event(THP_COLLAPSE_ALLOC_FAILED);
*hpage = ERR_PTR(-ENOMEM);
return;
}
+ count_vm_event(THP_COLLAPSE_ALLOC);
if (unlikely(mem_cgroup_newpage_charge(new_page, mm, GFP_KERNEL))) {
up_read(&mm->mmap_sem);
put_page(new_page);
(vma->vm_flags & VM_NOHUGEPAGE))
goto out;
- /* VM_PFNMAP vmas may have vm_ops null but vm_file set */
- if (!vma->anon_vma || vma->vm_ops || vma->vm_file)
+ if (!vma->anon_vma || vma->vm_ops)
goto out;
if (is_vma_temporary_stack(vma))
goto out;
- VM_BUG_ON(is_linear_pfn_mapping(vma) || is_pfn_mapping(vma));
+ /*
+ * If is_pfn_mapping() is true is_learn_pfn_mapping() must be
+ * true too, verify it here.
+ */
+ VM_BUG_ON(is_linear_pfn_mapping(vma) || vma->vm_flags & VM_NO_THP);
pgd = pgd_offset(mm, address);
if (!pgd_present(*pgd))
progress++;
continue;
}
- /* VM_PFNMAP vmas may have vm_ops null but vm_file set */
- if (!vma->anon_vma || vma->vm_ops || vma->vm_file)
+ if (!vma->anon_vma || vma->vm_ops)
goto skip;
if (is_vma_temporary_stack(vma))
goto skip;
-
- VM_BUG_ON(is_linear_pfn_mapping(vma) || is_pfn_mapping(vma));
+ /*
+ * If is_pfn_mapping() is true is_learn_pfn_mapping()
+ * must be true too, verify it here.
+ */
+ VM_BUG_ON(is_linear_pfn_mapping(vma) ||
+ vma->vm_flags & VM_NO_THP);
hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK;
hend = vma->vm_end & HPAGE_PMD_MASK;
#ifndef CONFIG_NUMA
if (!*hpage) {
*hpage = alloc_hugepage(khugepaged_defrag());
- if (unlikely(!*hpage))
+ if (unlikely(!*hpage)) {
+ count_vm_event(THP_COLLAPSE_ALLOC_FAILED);
break;
+ }
+ count_vm_event(THP_COLLAPSE_ALLOC);
}
#else
if (IS_ERR(*hpage))
do {
hpage = alloc_hugepage(khugepaged_defrag());
- if (!hpage)
+ if (!hpage) {
+ count_vm_event(THP_COLLAPSE_ALLOC_FAILED);
khugepaged_alloc_sleep();
+ } else
+ count_vm_event(THP_COLLAPSE_ALLOC);
} while (unlikely(!hpage) &&
likely(khugepaged_enabled()));
return hpage;
while (likely(khugepaged_enabled())) {
#ifndef CONFIG_NUMA
hpage = khugepaged_alloc_hugepage();
- if (unlikely(!hpage))
+ if (unlikely(!hpage)) {
+ count_vm_event(THP_COLLAPSE_ALLOC_FAILED);
break;
+ }
+ count_vm_event(THP_COLLAPSE_ALLOC);
#else
if (IS_ERR(hpage)) {
khugepaged_alloc_sleep();
*/
mark_page_accessed(page);
}
- if (flags & FOLL_MLOCK) {
+ if ((flags & FOLL_MLOCK) && (vma->vm_flags & VM_LOCKED)) {
/*
* The preliminary mapping check is mainly to avoid the
* pointless overhead of lock_page on the ZERO_PAGE
return page;
}
+static inline int stack_guard_page(struct vm_area_struct *vma, unsigned long addr)
+{
+ return stack_guard_page_start(vma, addr) ||
+ stack_guard_page_end(vma, addr+PAGE_SIZE);
+}
+
/**
* __get_user_pages() - pin user pages in memory
* @tsk: task_struct of target task
vma = find_extend_vma(mm, start);
if (!vma && in_gate_area(mm, start)) {
unsigned long pg = start & PAGE_MASK;
- struct vm_area_struct *gate_vma = get_gate_vma(mm);
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_unmap(pte);
return i ? : -EFAULT;
}
+ vma = get_gate_vma(mm);
if (pages) {
struct page *page;
- page = vm_normal_page(gate_vma, start, *pte);
+ page = vm_normal_page(vma, start, *pte);
if (!page) {
if (!(gup_flags & FOLL_DUMP) &&
is_zero_pfn(pte_pfn(*pte)))
get_page(page);
}
pte_unmap(pte);
- if (vmas)
- vmas[i] = gate_vma;
- i++;
- start += PAGE_SIZE;
- nr_pages--;
- continue;
+ goto next_page;
}
if (!vma ||
int ret;
unsigned int fault_flags = 0;
+ /* For mlock, just skip the stack guard page. */
+ if (foll_flags & FOLL_MLOCK) {
+ if (stack_guard_page(vma, start))
+ goto next_page;
+ }
if (foll_flags & FOLL_WRITE)
fault_flags |= FAULT_FLAG_WRITE;
if (nonblocking)
flush_anon_page(vma, page, start);
flush_dcache_page(page);
}
+next_page:
if (vmas)
vmas[i] = vma;
i++;
* run pte_offset_map on the pmd, if an huge pmd could
* materialize from under us from a different thread.
*/
- if (unlikely(__pte_alloc(mm, vma, pmd, address)))
+ if (unlikely(pmd_none(*pmd)) && __pte_alloc(mm, vma, pmd, address))
return VM_FAULT_OOM;
/* if an huge pmd materialized from under us just retry later */
if (unlikely(pmd_trans_huge(*pmd)))
*/
#ifdef CONFIG_HAVE_IOREMAP_PROT
vma = find_vma(mm, addr);
- if (!vma)
+ if (!vma || vma->vm_start > addr)
break;
if (vma->vm_ops && vma->vm_ops->access)
ret = vma->vm_ops->access(vma, addr, buf,
#endif
#ifdef CONFIG_FLATMEM
- max_mapnr = max(page_to_pfn(page), max_mapnr);
+ max_mapnr = max(pfn, max_mapnr);
#endif
ClearPageReserved(page);
}
}
-static inline int stack_guard_page(struct vm_area_struct *vma, unsigned long addr)
-{
- return (vma->vm_flags & VM_GROWSDOWN) &&
- (vma->vm_start == addr) &&
- !vma_stack_continue(vma->vm_prev, addr);
-}
-
/**
* __mlock_vma_pages_range() - mlock a range of pages in the vma.
* @vma: target vma
VM_BUG_ON(end > vma->vm_end);
VM_BUG_ON(!rwsem_is_locked(&mm->mmap_sem));
- gup_flags = FOLL_TOUCH;
+ gup_flags = FOLL_TOUCH | FOLL_MLOCK;
/*
* We want to touch writable mappings with a write fault in order
* to break COW, except for shared mappings because these don't COW
if (vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC))
gup_flags |= FOLL_FORCE;
- if (vma->vm_flags & VM_LOCKED)
- gup_flags |= FOLL_MLOCK;
-
- /* We don't try to access the guard page of a stack vma */
- if (stack_guard_page(vma, start)) {
- addr += PAGE_SIZE;
- nr_pages--;
- }
-
return __get_user_pages(current, mm, addr, nr_pages, gup_flags,
NULL, NULL, nonblocking);
}
* randomize_va_space to 2, which will still cause mm->start_brk
* to be arbitrarily shifted
*/
- if (mm->start_brk > PAGE_ALIGN(mm->end_data))
+ if (current->brk_randomized)
min_brk = mm->start_brk;
else
min_brk = mm->end_data;
size = address - vma->vm_start;
grow = (address - vma->vm_end) >> PAGE_SHIFT;
- error = acct_stack_growth(vma, size, grow);
- if (!error) {
- vma->vm_end = address;
- perf_event_mmap(vma);
+ error = -ENOMEM;
+ if (vma->vm_pgoff + (size >> PAGE_SHIFT) >= vma->vm_pgoff) {
+ error = acct_stack_growth(vma, size, grow);
+ if (!error) {
+ vma->vm_end = address;
+ perf_event_mmap(vma);
+ }
}
}
vma_unlock_anon_vma(vma);
size = vma->vm_end - address;
grow = (vma->vm_start - address) >> PAGE_SHIFT;
- error = acct_stack_growth(vma, size, grow);
- if (!error) {
- vma->vm_start = address;
- vma->vm_pgoff -= grow;
- perf_event_mmap(vma);
+ error = -ENOMEM;
+ if (grow <= vma->vm_pgoff) {
+ error = acct_stack_growth(vma, size, grow);
+ if (!error) {
+ vma->vm_start = address;
+ vma->vm_pgoff -= grow;
+ perf_event_mmap(vma);
+ }
}
}
vma_unlock_anon_vma(vma);
}
#endif /* CONFIG_NUMA */
-/*
- * If this is a system OOM (not a memcg OOM) and the task selected to be
- * killed is not already running at high (RT) priorities, speed up the
- * recovery by boosting the dying task to the lowest FIFO priority.
- * That helps with the recovery and avoids interfering with RT tasks.
- */
-static void boost_dying_task_prio(struct task_struct *p,
- struct mem_cgroup *mem)
-{
- struct sched_param param = { .sched_priority = 1 };
-
- if (mem)
- return;
-
- if (!rt_task(p))
- sched_setscheduler_nocheck(p, SCHED_FIFO, ¶m);
-}
-
/*
* The process p may have detached its own ->mm while exiting or through
* use_mm(), but one or more of its subthreads may still have a valid
/*
* The baseline for the badness score is the proportion of RAM that each
- * task's rss and swap space use.
+ * task's rss, pagetable and swap space use.
*/
- points = (get_mm_rss(p->mm) + get_mm_counter(p->mm, MM_SWAPENTS)) * 1000 /
- totalpages;
+ points = get_mm_rss(p->mm) + p->mm->nr_ptes;
+ points += get_mm_counter(p->mm, MM_SWAPENTS);
+
+ points *= 1000;
+ points /= totalpages;
task_unlock(p);
/*
set_tsk_thread_flag(p, TIF_MEMDIE);
force_sig(SIGKILL, p);
- /*
- * We give our sacrificial lamb high priority and access to
- * all the memory it needs. That way it should be able to
- * exit() and clear out its resources quickly...
- */
- boost_dying_task_prio(p, mem);
-
return 0;
}
#undef K
*/
if (p->flags & PF_EXITING) {
set_tsk_thread_flag(p, TIF_MEMDIE);
- boost_dying_task_prio(p, mem);
return 0;
}
*/
if (fatal_signal_pending(current)) {
set_thread_flag(TIF_MEMDIE);
- boost_dying_task_prio(current, NULL);
return;
}
*/
if (fatal_signal_pending(current)) {
set_thread_flag(TIF_MEMDIE);
- boost_dying_task_prio(current, NULL);
return;
}
* Called with zonelists_mutex held always
* unless system_state == SYSTEM_BOOTING.
*/
-void build_all_zonelists(void *data)
+void __ref build_all_zonelists(void *data)
{
set_zonelist_order();
* a waste to allocate index if we cannot allocate data.
*/
if (sbinfo->max_blocks) {
- if (percpu_counter_compare(&sbinfo->used_blocks, (sbinfo->max_blocks - 1)) > 0)
+ if (percpu_counter_compare(&sbinfo->used_blocks,
+ sbinfo->max_blocks - 1) >= 0)
return ERR_PTR(-ENOSPC);
percpu_counter_inc(&sbinfo->used_blocks);
spin_lock(&inode->i_lock);
shmem_swp_unmap(entry);
sbinfo = SHMEM_SB(inode->i_sb);
if (sbinfo->max_blocks) {
- if ((percpu_counter_compare(&sbinfo->used_blocks, sbinfo->max_blocks) > 0) ||
+ if (percpu_counter_compare(&sbinfo->used_blocks,
+ sbinfo->max_blocks) >= 0 ||
shmem_acct_block(info->flags)) {
spin_unlock(&info->lock);
error = -ENOSPC;
* Since this is without lock semantics the protection is only against
* code executing on this cpu *not* from access by other cpus.
*/
- if (unlikely(!this_cpu_cmpxchg_double(
+ if (unlikely(!irqsafe_cpu_cmpxchg_double(
s->cpu_slab->freelist, s->cpu_slab->tid,
object, tid,
get_freepointer(s, object), next_tid(tid)))) {
set_freepointer(s, object, c->freelist);
#ifdef CONFIG_CMPXCHG_LOCAL
- if (unlikely(!this_cpu_cmpxchg_double(
+ if (unlikely(!irqsafe_cpu_cmpxchg_double(
s->cpu_slab->freelist, s->cpu_slab->tid,
c->freelist, tid,
object, next_tid(tid)))) {
#include <linux/memcontrol.h>
#include <linux/delayacct.h>
#include <linux/sysctl.h>
+#include <linux/oom.h>
#include <asm/tlbflush.h>
#include <asm/div64.h>
return zone->pages_scanned < zone_reclaimable_pages(zone) * 6;
}
-/*
- * As hibernation is going on, kswapd is freezed so that it can't mark
- * the zone into all_unreclaimable. It can't handle OOM during hibernation.
- * So let's check zone's unreclaimable in direct reclaim as well as kswapd.
- */
+/* All zones in zonelist are unreclaimable? */
static bool all_unreclaimable(struct zonelist *zonelist,
struct scan_control *sc)
{
struct zoneref *z;
struct zone *zone;
- bool all_unreclaimable = true;
for_each_zone_zonelist_nodemask(zone, z, zonelist,
gfp_zone(sc->gfp_mask), sc->nodemask) {
continue;
if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL))
continue;
- if (zone_reclaimable(zone)) {
- all_unreclaimable = false;
- break;
- }
+ if (!zone->all_unreclaimable)
+ return false;
}
- return all_unreclaimable;
+ return true;
}
/*
if (sc->nr_reclaimed)
return sc->nr_reclaimed;
+ /*
+ * As hibernation is going on, kswapd is freezed so that it can't mark
+ * the zone into all_unreclaimable. Thus bypassing all_unreclaimable
+ * check.
+ */
+ if (oom_killer_disabled)
+ return 0;
+
/* top priority shrink_zones still had more to do? don't OOM, then */
if (scanning_global_lru(sc) && !all_unreclaimable(zonelist, sc))
return 1;
/*
* The fetching of the stat_threshold is racy. We may apply
* a counter threshold to the wrong the cpu if we get
- * rescheduled while executing here. However, the following
- * will apply the threshold again and therefore bring the
- * counter under the threshold.
+ * rescheduled while executing here. However, the next
+ * counter update will apply the threshold again and
+ * therefore bring the counter under the threshold again.
+ *
+ * Most of the time the thresholds are the same anyways
+ * for all cpus in a zone.
*/
t = this_cpu_read(pcp->stat_threshold);
"unevictable_pgs_cleared",
"unevictable_pgs_stranded",
"unevictable_pgs_mlockfreed",
+
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ "thp_fault_alloc",
+ "thp_fault_fallback",
+ "thp_collapse_alloc",
+ "thp_collapse_alloc_failed",
+ "thp_split",
#endif
+
+#endif /* CONFIG_VM_EVENTS_COUNTERS */
};
static void zoneinfo_show_print(struct seq_file *m, pg_data_t *pgdat,
}
EXPORT_SYMBOL(p9_client_attach);
-struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames,
- int clone)
+struct p9_fid *p9_client_walk(struct p9_fid *oldfid, uint16_t nwname,
+ char **wnames, int clone)
{
int err;
struct p9_client *clnt;
struct p9_fid *fid;
struct p9_qid *wqids;
struct p9_req_t *req;
- int16_t nwqids, count;
+ uint16_t nwqids, count;
err = 0;
wqids = NULL;
fid = oldfid;
- P9_DPRINTK(P9_DEBUG_9P, ">>> TWALK fids %d,%d nwname %d wname[0] %s\n",
+ P9_DPRINTK(P9_DEBUG_9P, ">>> TWALK fids %d,%d nwname %ud wname[0] %s\n",
oldfid->fid, fid->fid, nwname, wnames ? wnames[0] : NULL);
req = p9_client_rpc(clnt, P9_TWALK, "ddT", oldfid->fid, fid->fid,
}
EXPORT_SYMBOL(p9_client_fsync);
-int p9_client_sync_fs(struct p9_fid *fid)
-{
- int err = 0;
- struct p9_req_t *req;
- struct p9_client *clnt;
-
- P9_DPRINTK(P9_DEBUG_9P, ">>> TSYNC_FS fid %d\n", fid->fid);
-
- clnt = fid->clnt;
- req = p9_client_rpc(clnt, P9_TSYNCFS, "d", fid->fid);
- if (IS_ERR(req)) {
- err = PTR_ERR(req);
- goto error;
- }
- P9_DPRINTK(P9_DEBUG_9P, "<<< RSYNCFS fid %d\n", fid->fid);
- p9_free_req(clnt, req);
-error:
- return err;
-}
-EXPORT_SYMBOL(p9_client_sync_fs);
-
int p9_client_clunk(struct p9_fid *fid)
{
int err;
}
break;
case 'T':{
- int16_t *nwname = va_arg(ap, int16_t *);
+ uint16_t *nwname = va_arg(ap, uint16_t *);
char ***wnames = va_arg(ap, char ***);
errcode = p9pdu_readf(pdu, proto_version,
case 'E':{
int32_t cnt = va_arg(ap, int32_t);
const char *k = va_arg(ap, const void *);
- const char *u = va_arg(ap, const void *);
+ const char __user *u = va_arg(ap,
+ const void __user *);
errcode = p9pdu_writef(pdu, proto_version, "d",
cnt);
if (!errcode && pdu_write_urw(pdu, k, u, cnt))
}
break;
case 'T':{
- int16_t nwname = va_arg(ap, int);
+ uint16_t nwname = va_arg(ap, int);
const char **wnames = va_arg(ap, const char **);
errcode = p9pdu_writef(pdu, proto_version, "w",
uint32_t pdata_mapped_pages;
struct trans_rpage_info *rpinfo;
- *pdata_off = (size_t)req->tc->pubuf & (PAGE_SIZE-1);
+ *pdata_off = (__force size_t)req->tc->pubuf & (PAGE_SIZE-1);
if (*pdata_off)
first_page_bytes = min(((size_t)PAGE_SIZE - *pdata_off),
outp = pack_sg_list_p(chan->sg, out, VIRTQUEUE_NUM,
pdata_off, rpinfo->rp_data, pdata_len);
} else {
- char *pbuf = req->tc->pubuf ? req->tc->pubuf :
- req->tc->pkbuf;
+ char *pbuf;
+ if (req->tc->pubuf)
+ pbuf = (__force char *) req->tc->pubuf;
+ else
+ pbuf = req->tc->pkbuf;
outp = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM, pbuf,
req->tc->pbuf_size);
}
in = pack_sg_list_p(chan->sg, out+inp, VIRTQUEUE_NUM,
pdata_off, rpinfo->rp_data, pdata_len);
} else {
- char *pbuf = req->tc->pubuf ? req->tc->pubuf :
- req->tc->pkbuf;
+ char *pbuf;
+ if (req->tc->pubuf)
+ pbuf = (__force char *) req->tc->pubuf;
+ else
+ pbuf = req->tc->pkbuf;
+
in = pack_sg_list(chan->sg, out+inp, VIRTQUEUE_NUM,
pbuf, req->tc->pbuf_size);
}
hci_req_cancel(hdev, ENODEV);
hci_req_lock(hdev);
- /* Stop timer, it might be running */
- del_timer_sync(&hdev->cmd_timer);
-
if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
+ del_timer_sync(&hdev->cmd_timer);
hci_req_unlock(hdev);
return 0;
}
/* Drop last sent command */
if (hdev->sent_cmd) {
+ del_timer_sync(&hdev->cmd_timer);
kfree_skb(hdev->sent_cmd);
hdev->sent_cmd = NULL;
}
if (!conn)
goto unlock;
- hci_conn_hold(conn);
-
conn->remote_cap = ev->capability;
conn->remote_oob = ev->oob_data;
conn->remote_auth = ev->authentication;
tx_skb = skb_clone(skb, GFP_ATOMIC);
bt_cb(skb)->retries++;
control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
+ control &= L2CAP_CTRL_SAR;
if (pi->conn_state & L2CAP_CONN_SEND_FBIT) {
control |= L2CAP_CTRL_FINAL;
case BT_CONNECTED:
case BT_CONFIG:
+ if (sco_pi(sk)->conn) {
+ sk->sk_state = BT_DISCONN;
+ sco_sock_set_timer(sk, SCO_DISCONN_TIMEOUT);
+ hci_conn_put(sco_pi(sk)->conn->hcon);
+ sco_pi(sk)->conn = NULL;
+ } else
+ sco_chan_del(sk, ECONNRESET);
+ break;
+
case BT_CONNECT:
case BT_DISCONN:
sco_chan_del(sk, ECONNRESET);
goto drop;
/* If STP is turned off, then forward */
- if (p->br->stp_enabled == BR_NO_STP)
+ if (p->br->stp_enabled == BR_NO_STP && dest[5] == 0)
goto forward;
if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
goto drop;
}
- /* Zero out the CB buffer if no options present */
- if (iph->ihl == 5) {
- memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
+ memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
+ if (iph->ihl == 5)
return 0;
- }
opt->optlen = iph->ihl*4 - sizeof(struct iphdr);
if (ip_options_compile(dev_net(dev), opt, skb))
#include <net/caif/cfsrvl.h>
#include <net/caif/cfpkt.h>
+
#define container_obj(layr) ((struct cfsrvl *) layr)
#define DGM_CMD_BIT 0x80
static int cfdgml_transmit(struct cflayer *layr, struct cfpkt *pkt)
{
+ u8 packet_type;
u32 zero = 0;
struct caif_payload_info *info;
struct cfsrvl *service = container_obj(layr);
if (cfpkt_getlen(pkt) > DGM_MTU)
return -EMSGSIZE;
- cfpkt_add_head(pkt, &zero, 4);
+ cfpkt_add_head(pkt, &zero, 3);
+ packet_type = 0x08; /* B9 set - UNCLASSIFIED */
+ cfpkt_add_head(pkt, &packet_type, 1);
/* Add info for MUX-layer to route the packet out. */
info = cfpkt_info(pkt);
int phyid)
{
struct cfmuxl *muxl = container_obj(layr);
- struct list_head *node;
+ struct list_head *node, *next;
struct cflayer *layer;
- list_for_each(node, &muxl->srvl_list) {
+ list_for_each_safe(node, next, &muxl->srvl_list) {
layer = list_entry(node, struct cflayer, node);
if (cfsrvl_phyid_match(layer, phyid))
layer->ctrlcmd(layer, ctrl, phyid);
static int bcm_release(struct socket *sock)
{
struct sock *sk = sock->sk;
- struct bcm_sock *bo = bcm_sk(sk);
+ struct bcm_sock *bo;
struct bcm_op *op, *next;
+ if (sk == NULL)
+ return 0;
+
+ bo = bcm_sk(sk);
+
/* remove bcm_ops, timer, rx_unregister(), etc. */
unregister_netdevice_notifier(&bo->notifier);
static int raw_release(struct socket *sock)
{
struct sock *sk = sock->sk;
- struct raw_sock *ro = raw_sk(sk);
+ struct raw_sock *ro;
+
+ if (!sk)
+ return 0;
+
+ ro = raw_sk(sk);
unregister_netdevice_notifier(&ro->notifier);
m->more_to_follow = false;
m->pool = NULL;
+ /* middle */
+ m->middle = NULL;
+
+ /* data */
+ m->nr_pages = 0;
+ m->page_alignment = 0;
+ m->pages = NULL;
+ m->pagelist = NULL;
+ m->bio = NULL;
+ m->bio_iter = NULL;
+ m->bio_seg = 0;
+ m->trail = NULL;
+
/* front */
if (front_len) {
if (front_len > PAGE_CACHE_SIZE) {
}
m->front.iov_len = front_len;
- /* middle */
- m->middle = NULL;
-
- /* data */
- m->nr_pages = 0;
- m->page_alignment = 0;
- m->pages = NULL;
- m->pagelist = NULL;
- m->bio = NULL;
- m->bio_iter = NULL;
- m->bio_seg = 0;
- m->trail = NULL;
-
dout("ceph_msg_new %p front %d\n", m, front_len);
return m;
snapc, ops,
use_mempool,
GFP_NOFS, NULL, NULL);
- if (IS_ERR(req))
- return req;
+ if (!req)
+ return NULL;
/* calculate max write size */
calc_layout(osdc, vino, layout, off, plen, req, ops);
list_for_each_entry_safe(req, nreq, &osd->o_linger_requests,
r_linger_osd) {
- __unregister_linger_request(osdc, req);
+ /*
+ * reregister request prior to unregistering linger so
+ * that r_osd is preserved.
+ */
+ BUG_ON(!list_empty(&req->r_req_lru_item));
__register_request(osdc, req);
- list_move(&req->r_req_lru_item, &osdc->req_unsent);
+ list_add(&req->r_req_lru_item, &osdc->req_unsent);
+ list_add(&req->r_osd_item, &req->r_osd->o_requests);
+ __unregister_linger_request(osdc, req);
dout("requeued lingering %p tid %llu osd%d\n", req, req->r_tid,
osd->o_osd);
}
req->r_request->hdr.tid = cpu_to_le64(req->r_tid);
INIT_LIST_HEAD(&req->r_req_lru_item);
- dout("register_request %p tid %lld\n", req, req->r_tid);
+ dout("__register_request %p tid %lld\n", req, req->r_tid);
__insert_request(osdc, req);
ceph_osdc_get_request(req);
osdc->num_requests++;
* is never reached
*/
WARN_ON(1);
- err = -EINVAL;
+ err = -ENOTTY;
break;
}
/* Set the per device memory buffer space.
* Not applicable in our case */
case SIOCSIFLINK:
- return -EINVAL;
+ return -ENOTTY;
/*
* Unknown or private ioctl.
/* Take care of Wireless Extensions */
if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)
return wext_handle_ioctl(net, &ifr, cmd, arg);
- return -EINVAL;
+ return -ENOTTY;
}
}
}
/* TSO requires that SG is present as well. */
- if ((features & NETIF_F_TSO) && !(features & NETIF_F_SG)) {
- netdev_info(dev, "Dropping NETIF_F_TSO since no SG feature.\n");
- features &= ~NETIF_F_TSO;
+ if ((features & NETIF_F_ALL_TSO) && !(features & NETIF_F_SG)) {
+ netdev_info(dev, "Dropping TSO features since no SG feature.\n");
+ features &= ~NETIF_F_ALL_TSO;
}
+ /* TSO ECN requires that TSO is present as well. */
+ if ((features & NETIF_F_ALL_TSO) == NETIF_F_TSO_ECN)
+ features &= ~NETIF_F_TSO_ECN;
+
/* Software GSO depends on SG. */
if ((features & NETIF_F_GSO) && !(features & NETIF_F_SG)) {
netdev_info(dev, "Dropping NETIF_F_GSO since no SG feature.\n");
default n
config NET_DSA_MV88E6131
- bool "Marvell 88E6095/6095F/6131 ethernet switch chip support"
+ bool "Marvell 88E6085/6095/6095F/6131 ethernet switch chip support"
select NET_DSA_MV88E6XXX
select NET_DSA_MV88E6XXX_NEED_PPU
select NET_DSA_TAG_DSA
---help---
- This enables support for the Marvell 88E6095/6095F/6131
+ This enables support for the Marvell 88E6085/6095/6095F/6131
ethernet switch chips.
config NET_DSA_MV88E6123_61_65
* mode, but do not enable forwarding of unknown unicasts.
*/
val = 0x0433;
- if (p == dsa_upstream_port(ds))
+ if (p == dsa_upstream_port(ds)) {
val |= 0x0104;
+ /*
+ * On 6085, unknown multicast forward is controlled
+ * here rather than in Port Control 2 register.
+ */
+ if (ps->id == ID_6085)
+ val |= 0x0008;
+ }
if (ds->dsa_port_mask & (1 << p))
val |= 0x0100;
REG_WRITE(addr, 0x04, val);
* If this is the upstream port for this switch, enable
* forwarding of unknown multicast addresses.
*/
- val = 0x0080 | dsa_upstream_port(ds);
- if (p == dsa_upstream_port(ds))
- val |= 0x0040;
- REG_WRITE(addr, 0x08, val);
+ if (ps->id == ID_6085)
+ /*
+ * on 6085, bits 3:0 are reserved, bit 6 control ARP
+ * mirroring, and multicast forward is handled in
+ * Port Control register.
+ */
+ REG_WRITE(addr, 0x08, 0x0080);
+ else {
+ val = 0x0080 | dsa_upstream_port(ds);
+ if (p == dsa_upstream_port(ds))
+ val |= 0x0040;
+ REG_WRITE(addr, 0x08, val);
+ }
/*
* Rate Control: disable ingress rate limiting.
obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o
ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o
af_802154-y := af_ieee802154.o raw.o dgram.o
-
-ccflags-y += -Wall -DDEBUG
return;
cnf->sysctl = NULL;
- unregister_sysctl_table(t->sysctl_header);
+ unregister_net_sysctl_table(t->sysctl_header);
kfree(t->dev_name);
kfree(t);
}
t = (struct trie *) tb->tb_data;
memset(t, 0, sizeof(*t));
- if (id == RT_TABLE_LOCAL)
- pr_info("IPv4 FIB: Using LC-trie version %s\n", VERSION);
-
return tb;
}
!sk2->sk_bound_dev_if ||
sk->sk_bound_dev_if == sk2->sk_bound_dev_if)) {
if (!reuse || !sk2->sk_reuse ||
- ((1 << sk2->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) {
+ sk2->sk_state == TCP_LISTEN) {
const __be32 sk2_rcv_saddr = sk_rcv_saddr(sk2);
if (!sk2_rcv_saddr || !sk_rcv_saddr(sk) ||
sk2_rcv_saddr == sk_rcv_saddr(sk))
(tb->num_owners < smallest_size || smallest_size == -1)) {
smallest_size = tb->num_owners;
smallest_rover = rover;
- if (atomic_read(&hashinfo->bsockets) > (high - low) + 1 &&
- !inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb)) {
+ if (atomic_read(&hashinfo->bsockets) > (high - low) + 1) {
spin_unlock(&head->lock);
snum = smallest_rover;
goto have_snum;
}
/* May be called with local BH enabled. */
-static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base)
+static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base,
+ struct inet_peer __rcu **stack[PEER_MAXDEPTH])
{
int do_free;
* We use refcnt=-1 to alert lockless readers this entry is deleted.
*/
if (atomic_cmpxchg(&p->refcnt, 1, -1) == 1) {
- struct inet_peer __rcu **stack[PEER_MAXDEPTH];
struct inet_peer __rcu ***stackptr, ***delp;
if (lookup(&p->daddr, stack, base) != p)
BUG();
}
/* May be called with local BH enabled. */
-static int cleanup_once(unsigned long ttl)
+static int cleanup_once(unsigned long ttl, struct inet_peer __rcu **stack[PEER_MAXDEPTH])
{
struct inet_peer *p = NULL;
* happen because of entry limits in route cache. */
return -1;
- unlink_from_pool(p, peer_to_base(p));
+ unlink_from_pool(p, peer_to_base(p), stack);
return 0;
}
if (base->total >= inet_peer_threshold)
/* Remove one less-recently-used entry. */
- cleanup_once(0);
+ cleanup_once(0, stack);
return p;
}
{
unsigned long now = jiffies;
int ttl, total;
+ struct inet_peer __rcu **stack[PEER_MAXDEPTH];
total = compute_total();
if (total >= inet_peer_threshold)
ttl = inet_peer_maxttl
- (inet_peer_maxttl - inet_peer_minttl) / HZ *
total / inet_peer_threshold * HZ;
- while (!cleanup_once(ttl)) {
+ while (!cleanup_once(ttl, stack)) {
if (jiffies != now)
break;
}
pp_ptr = optptr + 2;
goto error;
}
- if (skb) {
+ if (rt) {
memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4);
opt->is_changed = 1;
}
goto error;
}
opt->ts = optptr - iph;
- if (skb) {
+ if (rt) {
memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4);
timeptr = (__be32*)&optptr[optptr[2]+3];
}
unsigned long orefdst;
int err;
- if (!opt->srr)
+ if (!opt->srr || !rt)
return 0;
if (skb->pkt_type != PACKET_HOST)
{
}
+static u32 *ipv4_rt_blackhole_cow_metrics(struct dst_entry *dst,
+ unsigned long old)
+{
+ return NULL;
+}
+
static struct dst_ops ipv4_dst_blackhole_ops = {
.family = AF_INET,
.protocol = cpu_to_be16(ETH_P_IP),
.default_mtu = ipv4_blackhole_default_mtu,
.default_advmss = ipv4_default_advmss,
.update_pmtu = ipv4_rt_blackhole_update_pmtu,
+ .cow_metrics = ipv4_rt_blackhole_cow_metrics,
};
struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_orig)
.mode = 0644,
.proc_handler = proc_do_large_bitmap,
},
-#ifdef CONFIG_IP_MULTICAST
{
.procname = "igmp_max_memberships",
.data = &sysctl_igmp_max_memberships,
.mode = 0644,
.proc_handler = proc_dointvec
},
-
-#endif
{
.procname = "igmp_max_msf",
.data = &sysctl_igmp_max_msf,
t = p->sysctl;
p->sysctl = NULL;
- unregister_sysctl_table(t->sysctl_header);
+ unregister_net_sysctl_table(t->sysctl_header);
kfree(t->dev_name);
kfree(t);
}
iv = esp_tmp_iv(aead, tmp, seqhilen);
req = esp_tmp_req(aead, iv);
asg = esp_req_sg(aead, req);
- sg = asg + 1;
+ sg = asg + sglists;
skb->ip_summed = CHECKSUM_NONE;
!sk2->sk_bound_dev_if ||
sk->sk_bound_dev_if == sk2->sk_bound_dev_if) &&
(!sk->sk_reuse || !sk2->sk_reuse ||
- ((1 << sk2->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) &&
+ sk2->sk_state == TCP_LISTEN) &&
ipv6_rcv_saddr_equal(sk, sk2))
break;
}
{
}
+static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst,
+ unsigned long old)
+{
+ return NULL;
+}
+
static struct dst_ops ip6_dst_blackhole_ops = {
.family = AF_INET6,
.protocol = cpu_to_be16(ETH_P_IPV6),
.default_mtu = ip6_blackhole_default_mtu,
.default_advmss = ip6_default_advmss,
.update_pmtu = ip6_rt_blackhole_update_pmtu,
+ .cow_metrics = ip6_rt_blackhole_cow_metrics,
};
static const u32 ip6_template_metrics[RTAX_MAX] = {
rt->dst.output = ip6_output;
rt->rt6i_dev = net->loopback_dev;
rt->rt6i_idev = idev;
- dst_metric_set(&rt->dst, RTAX_HOPLIMIT, -1);
rt->dst.obsolete = -1;
rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
skb->ip_summed = CHECKSUM_NONE;
/* Check if there is enough headroom to insert fragment header. */
- if ((skb_headroom(skb) < frag_hdr_sz) &&
+ if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) &&
pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC))
goto out;
/* Note : socket.c set MSG_EOR on SEQPACKET sockets */
if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_EOR | MSG_CMSG_COMPAT |
MSG_NOSIGNAL)) {
- err = -EINVAL;
- goto out;
+ return -EINVAL;
}
lock_sock(sk);
MODULE_DESCRIPTION("L2TP over IP");
MODULE_VERSION("1.0");
-/* Use the value of SOCK_DGRAM (2) directory, because __stringify does't like
+/* Use the value of SOCK_DGRAM (2) directory, because __stringify doesn't like
* enums
*/
MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET, 2, IPPROTO_L2TP);
s32 data_size = ntohs(pdulen) - llc_len;
if (data_size < 0 ||
- ((skb_tail_pointer(skb) -
- (u8 *)pdu) - llc_len) < data_size)
+ !pskb_may_pull(skb, data_size))
return 0;
if (unlikely(pskb_trim_rcsum(skb, data_size)))
return 0;
enum ieee80211_smps_mode old_req;
int err;
+ lockdep_assert_held(&sdata->u.mgd.mtx);
+
old_req = sdata->u.mgd.req_smps;
sdata->u.mgd.req_smps = smps_mode;
if (sdata->vif.type != NL80211_IFTYPE_STATION)
return -EOPNOTSUPP;
- mutex_lock(&local->iflist_mtx);
+ mutex_lock(&sdata->u.mgd.mtx);
err = __ieee80211_request_smps(sdata, smps_mode);
- mutex_unlock(&local->iflist_mtx);
+ mutex_unlock(&sdata->u.mgd.mtx);
return err;
}
ipset_adtfn adtfn = set->variant->adt[adt];
struct ipmac data;
+ /* MAC can be src only */
+ if (!(flags & IPSET_DIM_TWO_SRC))
+ return 0;
+
data.id = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC));
if (data.id < map->first_ip || data.id > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
if (cb->args[1] >= ip_set_max)
goto out;
- pr_debug("args[0]: %ld args[1]: %ld\n", cb->args[0], cb->args[1]);
max = cb->args[0] == DUMP_ONE ? cb->args[1] + 1 : ip_set_max;
+dump_last:
+ pr_debug("args[0]: %ld args[1]: %ld\n", cb->args[0], cb->args[1]);
for (; cb->args[1] < max; cb->args[1]++) {
index = (ip_set_id_t) cb->args[1];
set = ip_set_list[index];
* so that lists (unions of sets) are dumped last.
*/
if (cb->args[0] != DUMP_ONE &&
- !((cb->args[0] == DUMP_ALL) ^
- (set->type->features & IPSET_DUMP_LAST)))
+ ((cb->args[0] == DUMP_ALL) ==
+ !!(set->type->features & IPSET_DUMP_LAST)))
continue;
pr_debug("List set: %s\n", set->name);
if (!cb->args[2]) {
goto release_refcount;
}
}
+ /* If we dump all sets, continue with dumping last ones */
+ if (cb->args[0] == DUMP_ALL) {
+ cb->args[0] = DUMP_LAST;
+ cb->args[1] = 0;
+ goto dump_last;
+ }
goto out;
nla_put_failure:
pr_debug("release set %s\n", ip_set_list[index]->name);
ip_set_put_byindex(index);
}
-
- /* If we dump all sets, continue with dumping last ones */
- if (cb->args[0] == DUMP_ALL && cb->args[1] >= max && !cb->args[2])
- cb->args[0] = DUMP_LAST;
-
out:
if (nlh) {
nlmsg_end(skb, nlh);
if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
pr_warning("Protocol error: set match dimension "
"is over the limit!\n");
+ ip_set_nfnl_put(info->match_set.index);
return -ERANGE;
}
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find del_set index %u as target\n",
info->del_set.index);
+ if (info->add_set.index != IPSET_INVALID_ID)
+ ip_set_nfnl_put(info->add_set.index);
return -ENOENT;
}
}
info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
pr_warning("Protocol error: SET target dimension "
"is over the limit!\n");
+ if (info->add_set.index != IPSET_INVALID_ID)
+ ip_set_nfnl_put(info->add_set.index);
+ if (info->del_set.index != IPSET_INVALID_ID)
+ ip_set_nfnl_put(info->del_set.index);
return -ERANGE;
}
if (info->match_set.dim > IPSET_DIM_MAX) {
pr_warning("Protocol error: set match dimension "
"is over the limit!\n");
+ ip_set_nfnl_put(info->match_set.index);
return -ERANGE;
}
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_del(info->del_set.index,
skb, par->family,
- info->add_set.dim,
+ info->del_set.dim,
info->del_set.flags);
return XT_CONTINUE;
if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find del_set index %u as target\n",
info->del_set.index);
+ if (info->add_set.index != IPSET_INVALID_ID)
+ ip_set_nfnl_put(info->add_set.index);
return -ENOENT;
}
}
if (info->add_set.dim > IPSET_DIM_MAX ||
- info->del_set.flags > IPSET_DIM_MAX) {
+ info->del_set.dim > IPSET_DIM_MAX) {
pr_warning("Protocol error: SET target dimension "
"is over the limit!\n");
+ if (info->add_set.index != IPSET_INVALID_ID)
+ ip_set_nfnl_put(info->add_set.index);
+ if (info->del_set.index != IPSET_INVALID_ID)
+ ip_set_nfnl_put(info->del_set.index);
return -ERANGE;
}
sctp_assoc_set_primary(asoc, transport);
if (asoc->peer.active_path == peer)
asoc->peer.active_path = transport;
+ if (asoc->peer.retran_path == peer)
+ asoc->peer.retran_path = transport;
if (asoc->peer.last_data_from == peer)
asoc->peer.last_data_from = transport;
if (t)
asoc->peer.retran_path = t;
+ else
+ t = asoc->peer.retran_path;
SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_update_retran_path:association"
" %p addr: ",
memcpy(&ssf->ssf_info, &chunk->sinfo, sizeof(struct sctp_sndrcvinfo));
/* Per TSVWG discussion with Randy. Allow the application to
- * resemble a fragmented message.
+ * reassemble a fragmented message.
*/
ssf->ssf_info.sinfo_flags = chunk->chunk_hdr->flags;
If unsure, say N.
config RPCSEC_GSS_KRB5
- tristate
+ tristate "Secure RPC: Kerberos V mechanism"
depends on SUNRPC && CRYPTO
- prompt "Secure RPC: Kerberos V mechanism" if !(NFS_V4 || NFSD_V4)
+ depends on CRYPTO_MD5 && CRYPTO_DES && CRYPTO_CBC && CRYPTO_CTS
+ depends on CRYPTO_ECB && CRYPTO_HMAC && CRYPTO_SHA1 && CRYPTO_AES
+ depends on CRYPTO_ARC4
default y
select SUNRPC_GSS
- select CRYPTO_MD5
- select CRYPTO_DES
- select CRYPTO_CBC
help
Choose Y here to enable Secure RPC using the Kerberos version 5
GSS-API mechanism (RFC 1964).
warn_gssd();
task->tk_timeout = 15*HZ;
rpc_sleep_on(&pipe_version_rpc_waitqueue, task, NULL);
- return 0;
+ return -EAGAIN;
}
if (IS_ERR(gss_msg)) {
err = PTR_ERR(gss_msg);
if (PTR_ERR(gss_msg) == -EAGAIN) {
err = wait_event_interruptible_timeout(pipe_version_waitqueue,
pipe_version >= 0, 15*HZ);
+ if (pipe_version < 0) {
+ warn_gssd();
+ err = -EACCES;
+ }
if (err)
goto out;
- if (pipe_version < 0)
- warn_gssd();
goto retry;
}
if (IS_ERR(gss_msg)) {
if (clnt->cl_chatty)
printk(KERN_NOTICE "%s: server %s not responding, timed out\n",
clnt->cl_protname, clnt->cl_server);
- rpc_exit(task, -EIO);
+ if (task->tk_flags & RPC_TASK_TIMEOUT)
+ rpc_exit(task, -ETIMEDOUT);
+ else
+ rpc_exit(task, -EIO);
return;
}
}
dprintk("RPC: %5u xmit complete\n", task->tk_pid);
+ task->tk_flags |= RPC_TASK_SENT;
spin_lock_bh(&xprt->transport_lock);
xprt->ops->set_retrans_timeout(task);
int, int);
static int unix_seqpacket_sendmsg(struct kiocb *, struct socket *,
struct msghdr *, size_t);
+static int unix_seqpacket_recvmsg(struct kiocb *, struct socket *,
+ struct msghdr *, size_t, int);
static const struct proto_ops unix_stream_ops = {
.family = PF_UNIX,
.setsockopt = sock_no_setsockopt,
.getsockopt = sock_no_getsockopt,
.sendmsg = unix_seqpacket_sendmsg,
- .recvmsg = unix_dgram_recvmsg,
+ .recvmsg = unix_seqpacket_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
};
return unix_dgram_sendmsg(kiocb, sock, msg, len);
}
+static int unix_seqpacket_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size,
+ int flags)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk->sk_state != TCP_ESTABLISHED)
+ return -ENOTCONN;
+
+ return unix_dgram_recvmsg(iocb, sock, msg, size, flags);
+}
+
static void unix_copy_addr(struct msghdr *msg, struct sock *sk)
{
struct unix_sock *u = unix_sk(sk);
if (replay_esn) {
if (replay_esn->replay_window >
- replay_esn->bmp_len * sizeof(__u32))
+ replay_esn->bmp_len * sizeof(__u32) * 8)
return -EINVAL;
if ((x->props.flags & XFRM_STATE_ESN) && x->replay_esn)
{
struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
+ if ((p->flags & XFRM_STATE_ESN) && !rt)
+ return -EINVAL;
+
if (!rt)
return 0;
}
if (!child)
continue;
- if (line[strlen(line) - 1] == '?') {
+ if (line[0] && line[strlen(line) - 1] == '?') {
print_help(child);
continue;
}
return 0;
}
-static int cap_inode_permission(struct inode *inode, int mask)
+static int cap_inode_permission(struct inode *inode, int mask, unsigned flags)
{
return 0;
}
{
if (unlikely(IS_PRIVATE(inode)))
return 0;
- return security_ops->inode_permission(inode, mask);
+ return security_ops->inode_permission(inode, mask, 0);
}
int security_inode_exec_permission(struct inode *inode, unsigned int flags)
{
if (unlikely(IS_PRIVATE(inode)))
return 0;
- if (flags)
- return -ECHILD;
- return security_ops->inode_permission(inode, MAY_EXEC);
+ return security_ops->inode_permission(inode, MAY_EXEC, flags);
}
int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
* @avd: access vector decisions
* @result: result from avc_has_perm_noaudit
* @a: auxiliary audit data
+ * @flags: VFS walk flags
*
* Audit the granting or denial of permissions in accordance
* with the policy. This function is typically called by
* be performed under a lock, to allow the lock to be released
* before calling the auditing code.
*/
-void avc_audit(u32 ssid, u32 tsid,
+int avc_audit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
- struct av_decision *avd, int result, struct common_audit_data *a)
+ struct av_decision *avd, int result, struct common_audit_data *a,
+ unsigned flags)
{
struct common_audit_data stack_data;
u32 denied, audited;
else
audited = requested & avd->auditallow;
if (!audited)
- return;
+ return 0;
+
if (!a) {
a = &stack_data;
COMMON_AUDIT_DATA_INIT(a, NONE);
}
+
+ /*
+ * When in a RCU walk do the audit on the RCU retry. This is because
+ * the collection of the dname in an inode audit message is not RCU
+ * safe. Note this may drop some audits when the situation changes
+ * during retry. However this is logically just as if the operation
+ * happened a little later.
+ */
+ if ((a->type == LSM_AUDIT_DATA_FS) &&
+ (flags & IPERM_FLAG_RCU))
+ return -ECHILD;
+
a->selinux_audit_data.tclass = tclass;
a->selinux_audit_data.requested = requested;
a->selinux_audit_data.ssid = ssid;
a->lsm_pre_audit = avc_audit_pre_callback;
a->lsm_post_audit = avc_audit_post_callback;
common_lsm_audit(a);
+ return 0;
}
/**
* @tclass: target security class
* @requested: requested permissions, interpreted based on @tclass
* @auditdata: auxiliary audit data
+ * @flags: VFS walk flags
*
* Check the AVC to determine whether the @requested permissions are granted
* for the SID pair (@ssid, @tsid), interpreting the permissions
* permissions are granted, -%EACCES if any permissions are denied, or
* another -errno upon other errors.
*/
-int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
- u32 requested, struct common_audit_data *auditdata)
+int avc_has_perm_flags(u32 ssid, u32 tsid, u16 tclass,
+ u32 requested, struct common_audit_data *auditdata,
+ unsigned flags)
{
struct av_decision avd;
- int rc;
+ int rc, rc2;
rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
- avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
+
+ rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata,
+ flags);
+ if (rc2)
+ return rc2;
return rc;
}
}
rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd);
- if (audit == SECURITY_CAP_AUDIT)
- avc_audit(sid, sid, sclass, av, &avd, rc, &ad);
+ if (audit == SECURITY_CAP_AUDIT) {
+ int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad, 0);
+ if (rc2)
+ return rc2;
+ }
return rc;
}
static int inode_has_perm(const struct cred *cred,
struct inode *inode,
u32 perms,
- struct common_audit_data *adp)
+ struct common_audit_data *adp,
+ unsigned flags)
{
struct inode_security_struct *isec;
struct common_audit_data ad;
ad.u.fs.inode = inode;
}
- return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);
+ return avc_has_perm_flags(sid, isec->sid, isec->sclass, perms, adp, flags);
}
/* Same as inode_has_perm, but pass explicit audit data containing
COMMON_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.mnt = mnt;
ad.u.fs.path.dentry = dentry;
- return inode_has_perm(cred, inode, av, &ad);
+ return inode_has_perm(cred, inode, av, &ad, 0);
}
/* Check whether a task can use an open file descriptor to
/* av is zero if only checking access to the descriptor. */
rc = 0;
if (av)
- rc = inode_has_perm(cred, inode, av, &ad);
+ rc = inode_has_perm(cred, inode, av, &ad, 0);
out:
return rc;
return rc;
if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
- rc = security_transition_sid(sid, dsec->sid, tclass, NULL, &newsid);
+ rc = security_transition_sid(sid, dsec->sid, tclass,
+ &dentry->d_name, &newsid);
if (rc)
return rc;
}
file = file_priv->file;
inode = file->f_path.dentry->d_inode;
if (inode_has_perm(cred, inode,
- FILE__READ | FILE__WRITE, NULL)) {
+ FILE__READ | FILE__WRITE, NULL, 0)) {
drop_tty = 1;
}
}
return dentry_has_perm(cred, NULL, dentry, FILE__READ);
}
-static int selinux_inode_permission(struct inode *inode, int mask)
+static int selinux_inode_permission(struct inode *inode, int mask, unsigned flags)
{
const struct cred *cred = current_cred();
struct common_audit_data ad;
perms = file_mask_to_av(inode->i_mode, mask);
- return inode_has_perm(cred, inode, perms, &ad);
+ return inode_has_perm(cred, inode, perms, &ad, flags);
}
static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
* new inode label or new policy.
* This check is not redundant - do not remove.
*/
- return inode_has_perm(cred, inode, open_file_to_av(file), NULL);
+ return inode_has_perm(cred, inode, open_file_to_av(file), NULL, 0);
}
/* task security operations */
void __init avc_init(void);
-void avc_audit(u32 ssid, u32 tsid,
+int avc_audit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct av_decision *avd,
int result,
- struct common_audit_data *a);
+ struct common_audit_data *a, unsigned flags);
#define AVC_STRICT 1 /* Ignore permissive mode. */
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
unsigned flags,
struct av_decision *avd);
-int avc_has_perm(u32 ssid, u32 tsid,
- u16 tclass, u32 requested,
- struct common_audit_data *auditdata);
+int avc_has_perm_flags(u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ struct common_audit_data *auditdata,
+ unsigned);
+
+static inline int avc_has_perm(u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ struct common_audit_data *auditdata)
+{
+ return avc_has_perm_flags(ssid, tsid, tclass, requested, auditdata, 0);
+}
u32 avc_policy_seqno(void);
goto out;
rc = flex_array_prealloc(p->type_val_to_struct_array, 0,
- p->p_types.nprim - 1, GFP_KERNEL | __GFP_ZERO);
+ p->p_types.nprim, GFP_KERNEL | __GFP_ZERO);
if (rc)
goto out;
goto out;
rc = flex_array_prealloc(p->sym_val_to_name[i],
- 0, p->symtab[i].nprim - 1,
+ 0, p->symtab[i].nprim,
GFP_KERNEL | __GFP_ZERO);
if (rc)
goto out;
goto bad;
/* preallocate so we don't have to worry about the put ever failing */
- rc = flex_array_prealloc(p->type_attr_map_array, 0, p->p_types.nprim - 1,
+ rc = flex_array_prealloc(p->type_attr_map_array, 0, p->p_types.nprim,
GFP_KERNEL | __GFP_ZERO);
if (rc)
goto bad;
*
* Returns 0 if access is permitted, -EACCES otherwise
*/
-static int smack_inode_permission(struct inode *inode, int mask)
+static int smack_inode_permission(struct inode *inode, int mask, unsigned flags)
{
struct smk_audit_info ad;
*/
if (mask == 0)
return 0;
+
+ /* May be droppable after audit */
+ if (flags & IPERM_FLAG_RCU)
+ return -ECHILD;
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
smk_ad_setfield_u_fs_inode(&ad, inode);
return smk_curacc(smk_of_inode(inode), mask, &ad);
/* analysing the volume and mixer tables shows
* that they are similar enough when we shift
* the mixer table down by 4 bits. The error
- * is minuscule, in just one item the error
+ * is miniscule, in just one item the error
* is 1, at a value of 0x07f17b (mixer table
* value is 0x07f17a) */
tmp = tas_gaintable[left];
EXPORT_SYMBOL(snd_ctl_add);
+/**
+ * snd_ctl_replace - replace the control instance of the card
+ * @card: the card instance
+ * @kcontrol: the control instance to replace
+ * @add_on_replace: add the control if not already added
+ *
+ * Replaces the given control. If the given control does not exist
+ * and the add_on_replace flag is set, the control is added. If the
+ * control exists, it is destroyed first.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ *
+ * It frees automatically the control which cannot be added or replaced.
+ */
+int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol,
+ bool add_on_replace)
+{
+ struct snd_ctl_elem_id id;
+ unsigned int idx;
+ struct snd_kcontrol *old;
+ int ret;
+
+ if (!kcontrol)
+ return -EINVAL;
+ if (snd_BUG_ON(!card || !kcontrol->info)) {
+ ret = -EINVAL;
+ goto error;
+ }
+ id = kcontrol->id;
+ down_write(&card->controls_rwsem);
+ old = snd_ctl_find_id(card, &id);
+ if (!old) {
+ if (add_on_replace)
+ goto add;
+ up_write(&card->controls_rwsem);
+ ret = -EINVAL;
+ goto error;
+ }
+ ret = snd_ctl_remove(card, old);
+ if (ret < 0) {
+ up_write(&card->controls_rwsem);
+ goto error;
+ }
+add:
+ if (snd_ctl_find_hole(card, kcontrol->count) < 0) {
+ up_write(&card->controls_rwsem);
+ ret = -ENOMEM;
+ goto error;
+ }
+ list_add_tail(&kcontrol->list, &card->controls);
+ card->controls_count += kcontrol->count;
+ kcontrol->id.numid = card->last_numid + 1;
+ card->last_numid += kcontrol->count;
+ up_write(&card->controls_rwsem);
+ for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
+ return 0;
+
+error:
+ snd_ctl_free_one(kcontrol);
+ return ret;
+}
+EXPORT_SYMBOL(snd_ctl_replace);
+
/**
* snd_ctl_remove - remove the control from the card and release it
* @card: the card instance
id = card->id;
if (*id == '\0')
- strcpy(id, "default");
+ strcpy(id, "Default");
while (1) {
if (loops-- == 0) {
#define XRUN_LOG_CNT 10
struct hwptr_log_entry {
+ unsigned int in_interrupt;
unsigned long jiffies;
snd_pcm_uframes_t pos;
snd_pcm_uframes_t period_size;
};
static void xrun_log(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t pos)
+ snd_pcm_uframes_t pos, int in_interrupt)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hwptr_log *log = runtime->hwptr_log;
return;
}
entry = &log->entries[log->idx];
+ entry->in_interrupt = in_interrupt;
entry->jiffies = jiffies;
entry->pos = pos;
entry->period_size = runtime->period_size;
entry = &log->entries[idx];
if (entry->period_size == 0)
break;
- snd_printd("hwptr log: %s: j=%lu, pos=%ld/%ld/%ld, "
+ snd_printd("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, "
"hwptr=%ld/%ld\n",
- name, entry->jiffies, (unsigned long)entry->pos,
+ name, entry->in_interrupt ? "[Q] " : "",
+ entry->jiffies,
+ (unsigned long)entry->pos,
(unsigned long)entry->period_size,
(unsigned long)entry->buffer_size,
(unsigned long)entry->old_hw_ptr,
#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */
#define hw_ptr_error(substream, fmt, args...) do { } while (0)
-#define xrun_log(substream, pos) do { } while (0)
+#define xrun_log(substream, pos, in_interrupt) do { } while (0)
#define xrun_log_show(substream) do { } while (0)
#endif
}
pos -= pos % runtime->min_align;
if (xrun_debug(substream, XRUN_DEBUG_LOG))
- xrun_log(substream, pos);
+ xrun_log(substream, pos, in_interrupt);
hw_base = runtime->hw_ptr_base;
new_hw_ptr = hw_base + pos;
if (in_interrupt) {
To compile this driver as a module, choose M here: the module
will be called snd-firewire-speakers.
+config SND_ISIGHT
+ tristate "Apple iSight microphone"
+ select SND_PCM
+ select SND_FIREWIRE_LIB
+ help
+ Say Y here to include support for the front and rear microphones
+ of the Apple iSight web camera.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-isight.
+
endif # SND_FIREWIRE
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
fcp.o cmp.o amdtp.o
snd-firewire-speakers-objs := speakers.o
+snd-isight-objs := isight.o
obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
+obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
--- /dev/null
+/*
+ * Apple iSight audio driver
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <asm/byteorder.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include "lib.h"
+#include "iso-resources.h"
+#include "packets-buffer.h"
+
+#define OUI_APPLE 0x000a27
+#define MODEL_APPLE_ISIGHT 0x000008
+#define SW_ISIGHT_AUDIO 0x000010
+
+#define REG_AUDIO_ENABLE 0x000
+#define AUDIO_ENABLE 0x80000000
+#define REG_DEF_AUDIO_GAIN 0x204
+#define REG_GAIN_RAW_START 0x210
+#define REG_GAIN_RAW_END 0x214
+#define REG_GAIN_DB_START 0x218
+#define REG_GAIN_DB_END 0x21c
+#define REG_SAMPLE_RATE_INQUIRY 0x280
+#define REG_ISO_TX_CONFIG 0x300
+#define SPEED_SHIFT 16
+#define REG_SAMPLE_RATE 0x400
+#define RATE_48000 0x80000000
+#define REG_GAIN 0x500
+#define REG_MUTE 0x504
+
+#define MAX_FRAMES_PER_PACKET 475
+
+#define QUEUE_LENGTH 20
+
+struct isight {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ struct fw_device *device;
+ u64 audio_base;
+ struct fw_address_handler iris_handler;
+ struct snd_pcm_substream *pcm;
+ struct mutex mutex;
+ struct iso_packets_buffer buffer;
+ struct fw_iso_resources resources;
+ struct fw_iso_context *context;
+ bool pcm_active;
+ bool pcm_running;
+ bool first_packet;
+ int packet_index;
+ u32 total_samples;
+ unsigned int buffer_pointer;
+ unsigned int period_counter;
+ s32 gain_min, gain_max;
+ unsigned int gain_tlv[4];
+};
+
+struct audio_payload {
+ __be32 sample_count;
+ __be32 signature;
+ __be32 sample_total;
+ __be32 reserved;
+ __be16 samples[2 * MAX_FRAMES_PER_PACKET];
+};
+
+MODULE_DESCRIPTION("iSight audio driver");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL v2");
+
+static struct fw_iso_packet audio_packet = {
+ .payload_length = sizeof(struct audio_payload),
+ .interrupt = 1,
+ .header_length = 4,
+};
+
+static void isight_update_pointers(struct isight *isight, unsigned int count)
+{
+ struct snd_pcm_runtime *runtime = isight->pcm->runtime;
+ unsigned int ptr;
+
+ smp_wmb(); /* update buffer data before buffer pointer */
+
+ ptr = isight->buffer_pointer;
+ ptr += count;
+ if (ptr >= runtime->buffer_size)
+ ptr -= runtime->buffer_size;
+ ACCESS_ONCE(isight->buffer_pointer) = ptr;
+
+ isight->period_counter += count;
+ if (isight->period_counter >= runtime->period_size) {
+ isight->period_counter -= runtime->period_size;
+ snd_pcm_period_elapsed(isight->pcm);
+ }
+}
+
+static void isight_samples(struct isight *isight,
+ const __be16 *samples, unsigned int count)
+{
+ struct snd_pcm_runtime *runtime;
+ unsigned int count1;
+
+ if (!ACCESS_ONCE(isight->pcm_running))
+ return;
+
+ runtime = isight->pcm->runtime;
+ if (isight->buffer_pointer + count <= runtime->buffer_size) {
+ memcpy(runtime->dma_area + isight->buffer_pointer * 4,
+ samples, count * 4);
+ } else {
+ count1 = runtime->buffer_size - isight->buffer_pointer;
+ memcpy(runtime->dma_area + isight->buffer_pointer * 4,
+ samples, count1 * 4);
+ samples += count1 * 2;
+ memcpy(runtime->dma_area, samples, (count - count1) * 4);
+ }
+
+ isight_update_pointers(isight, count);
+}
+
+static void isight_pcm_abort(struct isight *isight)
+{
+ unsigned long flags;
+
+ if (ACCESS_ONCE(isight->pcm_active)) {
+ snd_pcm_stream_lock_irqsave(isight->pcm, flags);
+ if (snd_pcm_running(isight->pcm))
+ snd_pcm_stop(isight->pcm, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irqrestore(isight->pcm, flags);
+ }
+}
+
+static void isight_dropped_samples(struct isight *isight, unsigned int total)
+{
+ struct snd_pcm_runtime *runtime;
+ u32 dropped;
+ unsigned int count1;
+
+ if (!ACCESS_ONCE(isight->pcm_running))
+ return;
+
+ runtime = isight->pcm->runtime;
+ dropped = total - isight->total_samples;
+ if (dropped < runtime->buffer_size) {
+ if (isight->buffer_pointer + dropped <= runtime->buffer_size) {
+ memset(runtime->dma_area + isight->buffer_pointer * 4,
+ 0, dropped * 4);
+ } else {
+ count1 = runtime->buffer_size - isight->buffer_pointer;
+ memset(runtime->dma_area + isight->buffer_pointer * 4,
+ 0, count1 * 4);
+ memset(runtime->dma_area, 0, (dropped - count1) * 4);
+ }
+ isight_update_pointers(isight, dropped);
+ } else {
+ isight_pcm_abort(isight);
+ }
+}
+
+static void isight_packet(struct fw_iso_context *context, u32 cycle,
+ size_t header_length, void *header, void *data)
+{
+ struct isight *isight = data;
+ const struct audio_payload *payload;
+ unsigned int index, length, count, total;
+ int err;
+
+ if (isight->packet_index < 0)
+ return;
+ index = isight->packet_index;
+ payload = isight->buffer.packets[index].buffer;
+ length = be32_to_cpup(header) >> 16;
+
+ if (likely(length >= 16 &&
+ payload->signature == cpu_to_be32(0x73676874/*"sght"*/))) {
+ count = be32_to_cpu(payload->sample_count);
+ if (likely(count <= (length - 16) / 4)) {
+ total = be32_to_cpu(payload->sample_total);
+ if (unlikely(total != isight->total_samples)) {
+ if (!isight->first_packet)
+ isight_dropped_samples(isight, total);
+ isight->first_packet = false;
+ isight->total_samples = total;
+ }
+
+ isight_samples(isight, payload->samples, count);
+ isight->total_samples += count;
+ }
+ }
+
+ err = fw_iso_context_queue(isight->context, &audio_packet,
+ &isight->buffer.iso_buffer,
+ isight->buffer.packets[index].offset);
+ if (err < 0) {
+ dev_err(&isight->unit->device, "queueing error: %d\n", err);
+ isight_pcm_abort(isight);
+ isight->packet_index = -1;
+ return;
+ }
+
+ if (++index >= QUEUE_LENGTH)
+ index = 0;
+ isight->packet_index = index;
+}
+
+static int isight_connect(struct isight *isight)
+{
+ int ch, err, rcode, errors = 0;
+ __be32 value;
+
+retry_after_bus_reset:
+ ch = fw_iso_resources_allocate(&isight->resources,
+ sizeof(struct audio_payload),
+ isight->device->max_speed);
+ if (ch < 0) {
+ err = ch;
+ goto error;
+ }
+
+ value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT));
+ for (;;) {
+ rcode = fw_run_transaction(
+ isight->device->card,
+ TCODE_WRITE_QUADLET_REQUEST,
+ isight->device->node_id,
+ isight->resources.generation,
+ isight->device->max_speed,
+ isight->audio_base + REG_ISO_TX_CONFIG,
+ &value, 4);
+ if (rcode == RCODE_COMPLETE) {
+ return 0;
+ } else if (rcode == RCODE_GENERATION) {
+ fw_iso_resources_free(&isight->resources);
+ goto retry_after_bus_reset;
+ } else if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
+ err = -EIO;
+ goto err_resources;
+ }
+ msleep(5);
+ }
+
+err_resources:
+ fw_iso_resources_free(&isight->resources);
+error:
+ return err;
+}
+
+static int isight_open(struct snd_pcm_substream *substream)
+{
+ static const struct snd_pcm_hardware hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .formats = SNDRV_PCM_FMTBIT_S16_BE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 4 * 1024 * 1024,
+ .period_bytes_min = MAX_FRAMES_PER_PACKET * 4,
+ .period_bytes_max = 1024 * 1024,
+ .periods_min = 2,
+ .periods_max = UINT_MAX,
+ };
+ struct isight *isight = substream->private_data;
+
+ substream->runtime->hw = hardware;
+
+ return iso_packets_buffer_init(&isight->buffer, isight->unit,
+ QUEUE_LENGTH,
+ sizeof(struct audio_payload),
+ DMA_FROM_DEVICE);
+}
+
+static int isight_close(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+
+ iso_packets_buffer_destroy(&isight->buffer, isight->unit);
+
+ return 0;
+}
+
+static int isight_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct isight *isight = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ ACCESS_ONCE(isight->pcm_active) = true;
+
+ return 0;
+}
+
+static int reg_read(struct isight *isight, int offset, __be32 *value)
+{
+ return snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST,
+ isight->audio_base + offset, value, 4);
+}
+
+static int reg_write(struct isight *isight, int offset, __be32 value)
+{
+ return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
+ isight->audio_base + offset, &value, 4);
+}
+
+static void isight_stop_streaming(struct isight *isight)
+{
+ if (!isight->context)
+ return;
+
+ fw_iso_context_stop(isight->context);
+ fw_iso_context_destroy(isight->context);
+ isight->context = NULL;
+ fw_iso_resources_free(&isight->resources);
+ reg_write(isight, REG_AUDIO_ENABLE, 0);
+}
+
+static int isight_hw_free(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+
+ ACCESS_ONCE(isight->pcm_active) = false;
+
+ mutex_lock(&isight->mutex);
+ isight_stop_streaming(isight);
+ mutex_unlock(&isight->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int isight_start_streaming(struct isight *isight)
+{
+ unsigned int i;
+ int err;
+
+ if (isight->context) {
+ if (isight->packet_index < 0)
+ isight_stop_streaming(isight);
+ else
+ return 0;
+ }
+
+ err = reg_write(isight, REG_SAMPLE_RATE, cpu_to_be32(RATE_48000));
+ if (err < 0)
+ goto error;
+
+ err = isight_connect(isight);
+ if (err < 0)
+ goto error;
+
+ err = reg_write(isight, REG_AUDIO_ENABLE, cpu_to_be32(AUDIO_ENABLE));
+ if (err < 0)
+ goto err_resources;
+
+ isight->context = fw_iso_context_create(isight->device->card,
+ FW_ISO_CONTEXT_RECEIVE,
+ isight->resources.channel,
+ isight->device->max_speed,
+ 4, isight_packet, isight);
+ if (IS_ERR(isight->context)) {
+ err = PTR_ERR(isight->context);
+ isight->context = NULL;
+ goto err_resources;
+ }
+
+ for (i = 0; i < QUEUE_LENGTH; ++i) {
+ err = fw_iso_context_queue(isight->context, &audio_packet,
+ &isight->buffer.iso_buffer,
+ isight->buffer.packets[i].offset);
+ if (err < 0)
+ goto err_context;
+ }
+
+ isight->first_packet = true;
+ isight->packet_index = 0;
+
+ err = fw_iso_context_start(isight->context, -1, 0,
+ FW_ISO_CONTEXT_MATCH_ALL_TAGS/*?*/);
+ if (err < 0)
+ goto err_context;
+
+ return 0;
+
+err_context:
+ fw_iso_context_destroy(isight->context);
+ isight->context = NULL;
+err_resources:
+ fw_iso_resources_free(&isight->resources);
+ reg_write(isight, REG_AUDIO_ENABLE, 0);
+error:
+ return err;
+}
+
+static int isight_prepare(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+ int err;
+
+ isight->buffer_pointer = 0;
+ isight->period_counter = 0;
+
+ mutex_lock(&isight->mutex);
+ err = isight_start_streaming(isight);
+ mutex_unlock(&isight->mutex);
+
+ return err;
+}
+
+static int isight_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct isight *isight = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ ACCESS_ONCE(isight->pcm_running) = true;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ ACCESS_ONCE(isight->pcm_running) = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static snd_pcm_uframes_t isight_pointer(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+
+ return ACCESS_ONCE(isight->buffer_pointer);
+}
+
+static int isight_create_pcm(struct isight *isight)
+{
+ static struct snd_pcm_ops ops = {
+ .open = isight_open,
+ .close = isight_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = isight_hw_params,
+ .hw_free = isight_hw_free,
+ .prepare = isight_prepare,
+ .trigger = isight_trigger,
+ .pointer = isight_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(isight->card, "iSight", 0, 0, 1, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = isight;
+ strcpy(pcm->name, "iSight");
+ isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ isight->pcm->ops = &ops;
+
+ return 0;
+}
+
+static int isight_gain_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ struct isight *isight = ctl->private_data;
+
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 1;
+ info->value.integer.min = isight->gain_min;
+ info->value.integer.max = isight->gain_max;
+
+ return 0;
+}
+
+static int isight_gain_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+ __be32 gain;
+ int err;
+
+ err = reg_read(isight, REG_GAIN, &gain);
+ if (err < 0)
+ return err;
+
+ value->value.integer.value[0] = (s32)be32_to_cpu(gain);
+
+ return 0;
+}
+
+static int isight_gain_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+
+ if (value->value.integer.value[0] < isight->gain_min ||
+ value->value.integer.value[0] > isight->gain_max)
+ return -EINVAL;
+
+ return reg_write(isight, REG_GAIN,
+ cpu_to_be32(value->value.integer.value[0]));
+}
+
+static int isight_mute_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+ __be32 mute;
+ int err;
+
+ err = reg_read(isight, REG_MUTE, &mute);
+ if (err < 0)
+ return err;
+
+ value->value.integer.value[0] = !mute;
+
+ return 0;
+}
+
+static int isight_mute_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+
+ return reg_write(isight, REG_MUTE,
+ (__force __be32)!value->value.integer.value[0]);
+}
+
+static int isight_create_mixer(struct isight *isight)
+{
+ static const struct snd_kcontrol_new gain_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Capture Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = isight_gain_info,
+ .get = isight_gain_get,
+ .put = isight_gain_put,
+ };
+ static const struct snd_kcontrol_new mute_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Capture Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = isight_mute_get,
+ .put = isight_mute_put,
+ };
+ __be32 value;
+ struct snd_kcontrol *ctl;
+ int err;
+
+ err = reg_read(isight, REG_GAIN_RAW_START, &value);
+ if (err < 0)
+ return err;
+ isight->gain_min = be32_to_cpu(value);
+
+ err = reg_read(isight, REG_GAIN_RAW_END, &value);
+ if (err < 0)
+ return err;
+ isight->gain_max = be32_to_cpu(value);
+
+ isight->gain_tlv[0] = SNDRV_CTL_TLVT_DB_MINMAX;
+ isight->gain_tlv[1] = 2 * sizeof(unsigned int);
+
+ err = reg_read(isight, REG_GAIN_DB_START, &value);
+ if (err < 0)
+ return err;
+ isight->gain_tlv[2] = (s32)be32_to_cpu(value) * 100;
+
+ err = reg_read(isight, REG_GAIN_DB_END, &value);
+ if (err < 0)
+ return err;
+ isight->gain_tlv[3] = (s32)be32_to_cpu(value) * 100;
+
+ ctl = snd_ctl_new1(&gain_control, isight);
+ if (ctl)
+ ctl->tlv.p = isight->gain_tlv;
+ err = snd_ctl_add(isight->card, ctl);
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(isight->card, snd_ctl_new1(&mute_control, isight));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static void isight_card_free(struct snd_card *card)
+{
+ struct isight *isight = card->private_data;
+
+ fw_iso_resources_destroy(&isight->resources);
+ fw_unit_put(isight->unit);
+ fw_device_put(isight->device);
+ mutex_destroy(&isight->mutex);
+}
+
+static u64 get_unit_base(struct fw_unit *unit)
+{
+ struct fw_csr_iterator i;
+ int key, value;
+
+ fw_csr_iterator_init(&i, unit->directory);
+ while (fw_csr_iterator_next(&i, &key, &value))
+ if (key == CSR_OFFSET)
+ return CSR_REGISTER_BASE + value * 4;
+ return 0;
+}
+
+static int isight_probe(struct device *unit_dev)
+{
+ struct fw_unit *unit = fw_unit(unit_dev);
+ struct fw_device *fw_dev = fw_parent_device(unit);
+ struct snd_card *card;
+ struct isight *isight;
+ int err;
+
+ err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*isight), &card);
+ if (err < 0)
+ return err;
+ snd_card_set_dev(card, unit_dev);
+
+ isight = card->private_data;
+ isight->card = card;
+ mutex_init(&isight->mutex);
+ isight->unit = fw_unit_get(unit);
+ isight->device = fw_device_get(fw_dev);
+ isight->audio_base = get_unit_base(unit);
+ if (!isight->audio_base) {
+ dev_err(&unit->device, "audio unit base not found\n");
+ err = -ENXIO;
+ goto err_unit;
+ }
+ fw_iso_resources_init(&isight->resources, unit);
+
+ card->private_free = isight_card_free;
+
+ strcpy(card->driver, "iSight");
+ strcpy(card->shortname, "Apple iSight");
+ snprintf(card->longname, sizeof(card->longname),
+ "Apple iSight (GUID %08x%08x) at %s, S%d",
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&unit->device), 100 << fw_dev->max_speed);
+ strcpy(card->mixername, "iSight");
+
+ err = isight_create_pcm(isight);
+ if (err < 0)
+ goto error;
+
+ err = isight_create_mixer(isight);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ dev_set_drvdata(unit_dev, isight);
+
+ return 0;
+
+err_unit:
+ fw_unit_put(isight->unit);
+ fw_device_put(isight->device);
+ mutex_destroy(&isight->mutex);
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static int isight_remove(struct device *dev)
+{
+ struct isight *isight = dev_get_drvdata(dev);
+
+ isight_pcm_abort(isight);
+
+ snd_card_disconnect(isight->card);
+
+ mutex_lock(&isight->mutex);
+ isight_stop_streaming(isight);
+ mutex_unlock(&isight->mutex);
+
+ snd_card_free_when_closed(isight->card);
+
+ return 0;
+}
+
+static void isight_bus_reset(struct fw_unit *unit)
+{
+ struct isight *isight = dev_get_drvdata(&unit->device);
+
+ if (fw_iso_resources_update(&isight->resources) < 0) {
+ isight_pcm_abort(isight);
+
+ mutex_lock(&isight->mutex);
+ isight_stop_streaming(isight);
+ mutex_unlock(&isight->mutex);
+ }
+}
+
+static const struct ieee1394_device_id isight_id_table[] = {
+ {
+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .specifier_id = OUI_APPLE,
+ .version = SW_ISIGHT_AUDIO,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(ieee1394, isight_id_table);
+
+static struct fw_driver isight_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ .probe = isight_probe,
+ .remove = isight_remove,
+ },
+ .update = isight_bus_reset,
+ .id_table = isight_id_table,
+};
+
+static int __init alsa_isight_init(void)
+{
+ return driver_register(&isight_driver.driver);
+}
+
+static void __exit alsa_isight_exit(void)
+{
+ driver_unregister(&isight_driver.driver);
+}
+
+module_init(alsa_isight_init);
+module_exit(alsa_isight_exit);
return 0;
}
+EXPORT_SYMBOL(fw_iso_resources_init);
/**
* fw_iso_resources_destroy - destroy a resource manager
mutex_destroy(&r->mutex);
fw_unit_put(r->unit);
}
+EXPORT_SYMBOL(fw_iso_resources_destroy);
static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed)
{
return channel;
}
+EXPORT_SYMBOL(fw_iso_resources_allocate);
/**
* fw_iso_resources_update - update resource allocations after a bus reset
return channel;
}
+EXPORT_SYMBOL(fw_iso_resources_update);
/**
* fw_iso_resources_free - frees allocated resources
mutex_unlock(&r->mutex);
}
+EXPORT_SYMBOL(fw_iso_resources_free);
error:
return err;
}
+EXPORT_SYMBOL(iso_packets_buffer_init);
/**
* iso_packets_buffer_destroy - frees packet buffer resources
fw_iso_buffer_destroy(&b->iso_buffer, fw_parent_device(unit)->card);
kfree(b->packets);
}
+EXPORT_SYMBOL(iso_packets_buffer_destroy);
obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o
obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4113.o snd-ak4xxx-adda.o snd-pt2258.o
-obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o
+obj-$(CONFIG_SND_TEA575X) += snd-tea575x-tuner.o
module_param(radio_nr, int, 0);
#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-#define FREQ_LO (87 * 16000)
-#define FREQ_HI (108 * 16000)
+#define FREQ_LO (50UL * 16000)
+#define FREQ_HI (150UL * 16000)
/*
* definitions
* lowlevel part
*/
+static void snd_tea575x_write(struct snd_tea575x *tea, unsigned int val)
+{
+ u16 l;
+ u8 data;
+
+ tea->ops->set_direction(tea, 1);
+ udelay(16);
+
+ for (l = 25; l > 0; l--) {
+ data = (val >> 24) & TEA575X_DATA;
+ val <<= 1; /* shift data */
+ tea->ops->set_pins(tea, data | TEA575X_WREN);
+ udelay(2);
+ tea->ops->set_pins(tea, data | TEA575X_WREN | TEA575X_CLK);
+ udelay(2);
+ tea->ops->set_pins(tea, data | TEA575X_WREN);
+ udelay(2);
+ }
+
+ if (!tea->mute)
+ tea->ops->set_pins(tea, 0);
+}
+
+static unsigned int snd_tea575x_read(struct snd_tea575x *tea)
+{
+ u16 l, rdata;
+ u32 data = 0;
+
+ tea->ops->set_direction(tea, 0);
+ tea->ops->set_pins(tea, 0);
+ udelay(16);
+
+ for (l = 24; l--;) {
+ tea->ops->set_pins(tea, TEA575X_CLK);
+ udelay(2);
+ if (!l)
+ tea->tuned = tea->ops->get_pins(tea) & TEA575X_MOST ? 0 : 1;
+ tea->ops->set_pins(tea, 0);
+ udelay(2);
+ data <<= 1; /* shift data */
+ rdata = tea->ops->get_pins(tea);
+ if (!l)
+ tea->stereo = (rdata & TEA575X_MOST) ? 0 : 1;
+ if (rdata & TEA575X_DATA)
+ data++;
+ udelay(2);
+ }
+
+ if (tea->mute)
+ tea->ops->set_pins(tea, TEA575X_WREN);
+
+ return data;
+}
+
+static void snd_tea575x_get_freq(struct snd_tea575x *tea)
+{
+ unsigned long freq;
+
+ freq = snd_tea575x_read(tea) & TEA575X_BIT_FREQ_MASK;
+ /* freq *= 12.5 */
+ freq *= 125;
+ freq /= 10;
+ /* crystal fixup */
+ if (tea->tea5759)
+ freq += TEA575X_FMIF;
+ else
+ freq -= TEA575X_FMIF;
+
+ tea->freq = freq * 16; /* from kHz */
+}
+
static void snd_tea575x_set_freq(struct snd_tea575x *tea)
{
unsigned long freq;
- freq = tea->freq / 16; /* to kHz */
- if (freq > 108000)
- freq = 108000;
- if (freq < 87000)
- freq = 87000;
+ freq = clamp(tea->freq, FREQ_LO, FREQ_HI);
+ freq /= 16; /* to kHz */
/* crystal fixup */
if (tea->tea5759)
- freq -= tea->freq_fixup;
+ freq -= TEA575X_FMIF;
else
- freq += tea->freq_fixup;
+ freq += TEA575X_FMIF;
/* freq /= 12.5 */
freq *= 10;
freq /= 125;
tea->val &= ~TEA575X_BIT_FREQ_MASK;
tea->val |= freq & TEA575X_BIT_FREQ_MASK;
- tea->ops->write(tea, tea->val);
+ snd_tea575x_write(tea, tea->val);
}
/*
{
struct snd_tea575x *tea = video_drvdata(file);
- strcpy(v->card, tea->tea5759 ? "TEA5759" : "TEA5757");
strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver));
- strlcpy(v->card, "Maestro Radio", sizeof(v->card));
- sprintf(v->bus_info, "PCI");
+ strlcpy(v->card, tea->card, sizeof(v->card));
+ strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
+ strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
v->version = RADIO_VERSION;
- v->capabilities = V4L2_CAP_TUNER;
+ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
+ struct snd_tea575x *tea = video_drvdata(file);
+
if (v->index > 0)
return -EINVAL;
+ snd_tea575x_read(tea);
+
strcpy(v->name, "FM");
v->type = V4L2_TUNER_RADIO;
+ v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
v->rangelow = FREQ_LO;
v->rangehigh = FREQ_HI;
- v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
- v->capability = V4L2_TUNER_CAP_LOW;
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xffff;
+ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ v->audmode = tea->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO;
+ v->signal = tea->tuned ? 0xffff : 0;
+
return 0;
}
{
struct snd_tea575x *tea = video_drvdata(file);
+ if (f->tuner != 0)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
+ snd_tea575x_get_freq(tea);
f->frequency = tea->freq;
return 0;
}
{
struct snd_tea575x *tea = video_drvdata(file);
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+
if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
return -EINVAL;
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
- if (tea->ops->mute) {
- ctrl->value = tea->mute;
- return 0;
- }
+ ctrl->value = tea->mute;
+ return 0;
}
return -EINVAL;
}
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
- if (tea->ops->mute) {
- tea->ops->mute(tea, ctrl->value);
+ if (tea->mute != ctrl->value) {
tea->mute = ctrl->value;
- return 0;
+ snd_tea575x_set_freq(tea);
}
+ return 0;
}
return -EINVAL;
}
/*
* initialize all the tea575x chips
*/
-void snd_tea575x_init(struct snd_tea575x *tea)
+int snd_tea575x_init(struct snd_tea575x *tea)
{
int retval;
- unsigned int val;
struct video_device *tea575x_radio_inst;
- val = tea->ops->read(tea);
- if (val == 0x1ffffff || val == 0) {
- snd_printk(KERN_ERR
- "tea575x-tuner: Cannot find TEA575x chip\n");
- return;
- }
+ tea->mute = 1;
+
+ snd_tea575x_write(tea, 0x55AA);
+ if (snd_tea575x_read(tea) != 0x55AA)
+ return -ENODEV;
tea->in_use = 0;
tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
tea575x_radio_inst = video_device_alloc();
if (tea575x_radio_inst == NULL) {
printk(KERN_ERR "tea575x-tuner: not enough memory\n");
- return;
+ return -ENOMEM;
}
memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
if (retval) {
printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
kfree(tea575x_radio_inst);
- return;
+ return retval;
}
snd_tea575x_set_freq(tea);
-
- /* mute on init */
- if (tea->ops->mute) {
- tea->ops->mute(tea, 1);
- tea->mute = 1;
- }
tea->vd = tea575x_radio_inst;
+
+ return 0;
}
void snd_tea575x_exit(struct snd_tea575x *tea)
<file:Documentation/sound/oss/vwsnd> for more info on this driver's
capabilities.
-config SOUND_AU1550_AC97
- tristate "Au1550/Au1200 AC97 Sound"
- depends on SOC_AU1550 || SOC_AU1200
-
config SOUND_MSNDCLAS
tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey"
depends on (m || !STANDALONE) && ISA
obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o
obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o
obj-$(CONFIG_SOUND_VWSND) += vwsnd.o
-obj-$(CONFIG_SOUND_AU1550_AC97) += au1550_ac97.o ac97_codec.o
obj-$(CONFIG_SOUND_BCM_CS4297A) += swarm_cs4297a.o
obj-$(CONFIG_DMASOUND) += dmasound/
+++ /dev/null
-/*
- * ac97_codec.c: Generic AC97 mixer/modem module
- *
- * Derived from ac97 mixer in maestro and trident driver.
- *
- * Copyright 2000 Silicon Integrated System Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- **************************************************************************
- *
- * The Intel Audio Codec '97 specification is available at:
- * http://download.intel.com/support/motherboards/desktop/sb/ac97_r23.pdf
- *
- **************************************************************************
- *
- * History
- * May 02, 2003 Liam Girdwood <lrg@slimlogic.co.uk>
- * Removed non existent WM9700
- * Added support for WM9705, WM9708, WM9709, WM9710, WM9711
- * WM9712 and WM9717
- * Mar 28, 2002 Randolph Bentson <bentson@holmsjoen.com>
- * corrections to support WM9707 in ViewPad 1000
- * v0.4 Mar 15 2000 Ollie Lho
- * dual codecs support verified with 4 channels output
- * v0.3 Feb 22 2000 Ollie Lho
- * bug fix for record mask setting
- * v0.2 Feb 10 2000 Ollie Lho
- * add ac97_read_proc for /proc/driver/{vendor}/ac97
- * v0.1 Jan 14 2000 Ollie Lho <ollie@sis.com.tw>
- * Isolated from trident.c to support multiple ac97 codec
- */
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/bitops.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/ac97_codec.h>
-#include <asm/uaccess.h>
-#include <linux/mutex.h>
-
-#define CODEC_ID_BUFSZ 14
-
-static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel);
-static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel,
- unsigned int left, unsigned int right);
-static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val );
-static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask);
-static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg);
-
-static int ac97_init_mixer(struct ac97_codec *codec);
-
-static int wolfson_init03(struct ac97_codec * codec);
-static int wolfson_init04(struct ac97_codec * codec);
-static int wolfson_init05(struct ac97_codec * codec);
-static int wolfson_init11(struct ac97_codec * codec);
-static int wolfson_init13(struct ac97_codec * codec);
-static int tritech_init(struct ac97_codec * codec);
-static int tritech_maestro_init(struct ac97_codec * codec);
-static int sigmatel_9708_init(struct ac97_codec *codec);
-static int sigmatel_9721_init(struct ac97_codec *codec);
-static int sigmatel_9744_init(struct ac97_codec *codec);
-static int ad1886_init(struct ac97_codec *codec);
-static int eapd_control(struct ac97_codec *codec, int);
-static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
-static int cmedia_init(struct ac97_codec * codec);
-static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
-static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
-
-
-/*
- * AC97 operations.
- *
- * If you are adding a codec then you should be able to use
- * eapd_ops - any codec that supports EAPD amp control (most)
- * null_ops - any ancient codec that supports nothing
- *
- * The three functions are
- * init - used for non AC97 standard initialisation
- * amplifier - used to do amplifier control (1=on 0=off)
- * digital - switch to digital modes (0 = analog)
- *
- * Not all codecs support all features, not all drivers use all the
- * operations yet
- */
-
-static struct ac97_ops null_ops = { NULL, NULL, NULL };
-static struct ac97_ops default_ops = { NULL, eapd_control, NULL };
-static struct ac97_ops default_digital_ops = { NULL, eapd_control, generic_digital_control};
-static struct ac97_ops wolfson_ops03 = { wolfson_init03, NULL, NULL };
-static struct ac97_ops wolfson_ops04 = { wolfson_init04, NULL, NULL };
-static struct ac97_ops wolfson_ops05 = { wolfson_init05, NULL, NULL };
-static struct ac97_ops wolfson_ops11 = { wolfson_init11, NULL, NULL };
-static struct ac97_ops wolfson_ops13 = { wolfson_init13, NULL, NULL };
-static struct ac97_ops tritech_ops = { tritech_init, NULL, NULL };
-static struct ac97_ops tritech_m_ops = { tritech_maestro_init, NULL, NULL };
-static struct ac97_ops sigmatel_9708_ops = { sigmatel_9708_init, NULL, NULL };
-static struct ac97_ops sigmatel_9721_ops = { sigmatel_9721_init, NULL, NULL };
-static struct ac97_ops sigmatel_9744_ops = { sigmatel_9744_init, NULL, NULL };
-static struct ac97_ops crystal_digital_ops = { NULL, eapd_control, crystal_digital_control };
-static struct ac97_ops ad1886_ops = { ad1886_init, eapd_control, NULL };
-static struct ac97_ops cmedia_ops = { NULL, eapd_control, NULL};
-static struct ac97_ops cmedia_digital_ops = { cmedia_init, eapd_control, cmedia_digital_control};
-
-/* sorted by vendor/device id */
-static const struct {
- u32 id;
- char *name;
- struct ac97_ops *ops;
- int flags;
-} ac97_codec_ids[] = {
- {0x41445303, "Analog Devices AD1819", &null_ops},
- {0x41445340, "Analog Devices AD1881", &null_ops},
- {0x41445348, "Analog Devices AD1881A", &null_ops},
- {0x41445360, "Analog Devices AD1885", &default_ops},
- {0x41445361, "Analog Devices AD1886", &ad1886_ops},
- {0x41445370, "Analog Devices AD1981", &null_ops},
- {0x41445372, "Analog Devices AD1981A", &null_ops},
- {0x41445374, "Analog Devices AD1981B", &null_ops},
- {0x41445460, "Analog Devices AD1885", &default_ops},
- {0x41445461, "Analog Devices AD1886", &ad1886_ops},
- {0x414B4D00, "Asahi Kasei AK4540", &null_ops},
- {0x414B4D01, "Asahi Kasei AK4542", &null_ops},
- {0x414B4D02, "Asahi Kasei AK4543", &null_ops},
- {0x414C4326, "ALC100P", &null_ops},
- {0x414C4710, "ALC200/200P", &null_ops},
- {0x414C4720, "ALC650", &default_digital_ops},
- {0x434D4941, "CMedia", &cmedia_ops, AC97_NO_PCM_VOLUME },
- {0x434D4942, "CMedia", &cmedia_ops, AC97_NO_PCM_VOLUME },
- {0x434D4961, "CMedia", &cmedia_digital_ops, AC97_NO_PCM_VOLUME },
- {0x43525900, "Cirrus Logic CS4297", &default_ops},
- {0x43525903, "Cirrus Logic CS4297", &default_ops},
- {0x43525913, "Cirrus Logic CS4297A rev A", &default_ops},
- {0x43525914, "Cirrus Logic CS4297A rev B", &default_ops},
- {0x43525923, "Cirrus Logic CS4298", &null_ops},
- {0x4352592B, "Cirrus Logic CS4294", &null_ops},
- {0x4352592D, "Cirrus Logic CS4294", &null_ops},
- {0x43525931, "Cirrus Logic CS4299 rev A", &crystal_digital_ops},
- {0x43525933, "Cirrus Logic CS4299 rev C", &crystal_digital_ops},
- {0x43525934, "Cirrus Logic CS4299 rev D", &crystal_digital_ops},
- {0x43585430, "CXT48", &default_ops, AC97_DELUDED_MODEM },
- {0x43585442, "CXT66", &default_ops, AC97_DELUDED_MODEM },
- {0x44543031, "Diamond Technology DT0893", &default_ops},
- {0x45838308, "ESS Allegro ES1988", &null_ops},
- {0x49434511, "ICE1232", &null_ops}, /* I hope --jk */
- {0x4e534331, "National Semiconductor LM4549", &null_ops},
- {0x53494c22, "Silicon Laboratory Si3036", &null_ops},
- {0x53494c23, "Silicon Laboratory Si3038", &null_ops},
- {0x545200FF, "TriTech TR?????", &tritech_m_ops},
- {0x54524102, "TriTech TR28022", &null_ops},
- {0x54524103, "TriTech TR28023", &null_ops},
- {0x54524106, "TriTech TR28026", &null_ops},
- {0x54524108, "TriTech TR28028", &tritech_ops},
- {0x54524123, "TriTech TR A5", &null_ops},
- {0x574D4C03, "Wolfson WM9703/07/08/17", &wolfson_ops03},
- {0x574D4C04, "Wolfson WM9704M/WM9704Q", &wolfson_ops04},
- {0x574D4C05, "Wolfson WM9705/WM9710", &wolfson_ops05},
- {0x574D4C09, "Wolfson WM9709", &null_ops},
- {0x574D4C12, "Wolfson WM9711/9712", &wolfson_ops11},
- {0x574D4C13, "Wolfson WM9713", &wolfson_ops13, AC97_DEFAULT_POWER_OFF},
- {0x83847600, "SigmaTel STAC????", &null_ops},
- {0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops},
- {0x83847605, "SigmaTel STAC9704", &null_ops},
- {0x83847608, "SigmaTel STAC9708", &sigmatel_9708_ops},
- {0x83847609, "SigmaTel STAC9721/23", &sigmatel_9721_ops},
- {0x83847644, "SigmaTel STAC9744/45", &sigmatel_9744_ops},
- {0x83847652, "SigmaTel STAC9752/53", &default_ops},
- {0x83847656, "SigmaTel STAC9756/57", &sigmatel_9744_ops},
- {0x83847666, "SigmaTel STAC9750T", &sigmatel_9744_ops},
- {0x83847684, "SigmaTel STAC9783/84?", &null_ops},
- {0x57454301, "Winbond 83971D", &null_ops},
-};
-
-/* this table has default mixer values for all OSS mixers. */
-static struct mixer_defaults {
- int mixer;
- unsigned int value;
-} mixer_defaults[SOUND_MIXER_NRDEVICES] = {
- /* all values 0 -> 100 in bytes */
- {SOUND_MIXER_VOLUME, 0x4343},
- {SOUND_MIXER_BASS, 0x4343},
- {SOUND_MIXER_TREBLE, 0x4343},
- {SOUND_MIXER_PCM, 0x4343},
- {SOUND_MIXER_SPEAKER, 0x4343},
- {SOUND_MIXER_LINE, 0x4343},
- {SOUND_MIXER_MIC, 0x0000},
- {SOUND_MIXER_CD, 0x4343},
- {SOUND_MIXER_ALTPCM, 0x4343},
- {SOUND_MIXER_IGAIN, 0x4343},
- {SOUND_MIXER_LINE1, 0x4343},
- {SOUND_MIXER_PHONEIN, 0x4343},
- {SOUND_MIXER_PHONEOUT, 0x4343},
- {SOUND_MIXER_VIDEO, 0x4343},
- {-1,0}
-};
-
-/* table to scale scale from OSS mixer value to AC97 mixer register value */
-static struct ac97_mixer_hw {
- unsigned char offset;
- int scale;
-} ac97_hw[SOUND_MIXER_NRDEVICES]= {
- [SOUND_MIXER_VOLUME] = {AC97_MASTER_VOL_STEREO,64},
- [SOUND_MIXER_BASS] = {AC97_MASTER_TONE, 16},
- [SOUND_MIXER_TREBLE] = {AC97_MASTER_TONE, 16},
- [SOUND_MIXER_PCM] = {AC97_PCMOUT_VOL, 32},
- [SOUND_MIXER_SPEAKER] = {AC97_PCBEEP_VOL, 16},
- [SOUND_MIXER_LINE] = {AC97_LINEIN_VOL, 32},
- [SOUND_MIXER_MIC] = {AC97_MIC_VOL, 32},
- [SOUND_MIXER_CD] = {AC97_CD_VOL, 32},
- [SOUND_MIXER_ALTPCM] = {AC97_HEADPHONE_VOL, 64},
- [SOUND_MIXER_IGAIN] = {AC97_RECORD_GAIN, 16},
- [SOUND_MIXER_LINE1] = {AC97_AUX_VOL, 32},
- [SOUND_MIXER_PHONEIN] = {AC97_PHONE_VOL, 32},
- [SOUND_MIXER_PHONEOUT] = {AC97_MASTER_VOL_MONO, 64},
- [SOUND_MIXER_VIDEO] = {AC97_VIDEO_VOL, 32},
-};
-
-/* the following tables allow us to go from OSS <-> ac97 quickly. */
-enum ac97_recsettings {
- AC97_REC_MIC=0,
- AC97_REC_CD,
- AC97_REC_VIDEO,
- AC97_REC_AUX,
- AC97_REC_LINE,
- AC97_REC_STEREO, /* combination of all enabled outputs.. */
- AC97_REC_MONO, /*.. or the mono equivalent */
- AC97_REC_PHONE
-};
-
-static const unsigned int ac97_rm2oss[] = {
- [AC97_REC_MIC] = SOUND_MIXER_MIC,
- [AC97_REC_CD] = SOUND_MIXER_CD,
- [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO,
- [AC97_REC_AUX] = SOUND_MIXER_LINE1,
- [AC97_REC_LINE] = SOUND_MIXER_LINE,
- [AC97_REC_STEREO]= SOUND_MIXER_IGAIN,
- [AC97_REC_PHONE] = SOUND_MIXER_PHONEIN
-};
-
-/* indexed by bit position */
-static const unsigned int ac97_oss_rm[] = {
- [SOUND_MIXER_MIC] = AC97_REC_MIC,
- [SOUND_MIXER_CD] = AC97_REC_CD,
- [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO,
- [SOUND_MIXER_LINE1] = AC97_REC_AUX,
- [SOUND_MIXER_LINE] = AC97_REC_LINE,
- [SOUND_MIXER_IGAIN] = AC97_REC_STEREO,
- [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE
-};
-
-static LIST_HEAD(codecs);
-static LIST_HEAD(codec_drivers);
-static DEFINE_MUTEX(codec_mutex);
-
-/* reads the given OSS mixer from the ac97 the caller must have insured that the ac97 knows
- about that given mixer, and should be holding a spinlock for the card */
-static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel)
-{
- u16 val;
- int ret = 0;
- int scale;
- struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];
-
- val = codec->codec_read(codec , mh->offset);
-
- if (val & AC97_MUTE) {
- ret = 0;
- } else if (AC97_STEREO_MASK & (1 << oss_channel)) {
- /* nice stereo mixers .. */
- int left,right;
-
- left = (val >> 8) & 0x7f;
- right = val & 0x7f;
-
- if (oss_channel == SOUND_MIXER_IGAIN) {
- right = (right * 100) / mh->scale;
- left = (left * 100) / mh->scale;
- } else {
- /* these may have 5 or 6 bit resolution */
- if(oss_channel == SOUND_MIXER_VOLUME || oss_channel == SOUND_MIXER_ALTPCM)
- scale = (1 << codec->bit_resolution);
- else
- scale = mh->scale;
-
- right = 100 - ((right * 100) / scale);
- left = 100 - ((left * 100) / scale);
- }
- ret = left | (right << 8);
- } else if (oss_channel == SOUND_MIXER_SPEAKER) {
- ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale);
- } else if (oss_channel == SOUND_MIXER_PHONEIN) {
- ret = 100 - (((val & 0x1f) * 100) / mh->scale);
- } else if (oss_channel == SOUND_MIXER_PHONEOUT) {
- scale = (1 << codec->bit_resolution);
- ret = 100 - (((val & 0x1f) * 100) / scale);
- } else if (oss_channel == SOUND_MIXER_MIC) {
- ret = 100 - (((val & 0x1f) * 100) / mh->scale);
- /* the low bit is optional in the tone sliders and masking
- it lets us avoid the 0xf 'bypass'.. */
- } else if (oss_channel == SOUND_MIXER_BASS) {
- ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale);
- } else if (oss_channel == SOUND_MIXER_TREBLE) {
- ret = 100 - (((val & 0xe) * 100) / mh->scale);
- }
-
-#ifdef DEBUG
- printk("ac97_codec: read OSS mixer %2d (%s ac97 register 0x%02x), "
- "0x%04x -> 0x%04x\n",
- oss_channel, codec->id ? "Secondary" : "Primary",
- mh->offset, val, ret);
-#endif
-
- return ret;
-}
-
-/* write the OSS encoded volume to the given OSS encoded mixer, again caller's job to
- make sure all is well in arg land, call with spinlock held */
-static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel,
- unsigned int left, unsigned int right)
-{
- u16 val = 0;
- int scale;
- struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];
-
-#ifdef DEBUG
- printk("ac97_codec: wrote OSS mixer %2d (%s ac97 register 0x%02x), "
- "left vol:%2d, right vol:%2d:",
- oss_channel, codec->id ? "Secondary" : "Primary",
- mh->offset, left, right);
-#endif
-
- if (AC97_STEREO_MASK & (1 << oss_channel)) {
- /* stereo mixers */
- if (left == 0 && right == 0) {
- val = AC97_MUTE;
- } else {
- if (oss_channel == SOUND_MIXER_IGAIN) {
- right = (right * mh->scale) / 100;
- left = (left * mh->scale) / 100;
- if (right >= mh->scale)
- right = mh->scale-1;
- if (left >= mh->scale)
- left = mh->scale-1;
- } else {
- /* these may have 5 or 6 bit resolution */
- if (oss_channel == SOUND_MIXER_VOLUME ||
- oss_channel == SOUND_MIXER_ALTPCM)
- scale = (1 << codec->bit_resolution);
- else
- scale = mh->scale;
-
- right = ((100 - right) * scale) / 100;
- left = ((100 - left) * scale) / 100;
- if (right >= scale)
- right = scale-1;
- if (left >= scale)
- left = scale-1;
- }
- val = (left << 8) | right;
- }
- } else if (oss_channel == SOUND_MIXER_BASS) {
- val = codec->codec_read(codec , mh->offset) & ~0x0f00;
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val |= (left << 8) & 0x0e00;
- } else if (oss_channel == SOUND_MIXER_TREBLE) {
- val = codec->codec_read(codec , mh->offset) & ~0x000f;
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val |= left & 0x000e;
- } else if(left == 0) {
- val = AC97_MUTE;
- } else if (oss_channel == SOUND_MIXER_SPEAKER) {
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val = left << 1;
- } else if (oss_channel == SOUND_MIXER_PHONEIN) {
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val = left;
- } else if (oss_channel == SOUND_MIXER_PHONEOUT) {
- scale = (1 << codec->bit_resolution);
- left = ((100 - left) * scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val = left;
- } else if (oss_channel == SOUND_MIXER_MIC) {
- val = codec->codec_read(codec , mh->offset) & ~0x801f;
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val |= left;
- /* the low bit is optional in the tone sliders and masking
- it lets us avoid the 0xf 'bypass'.. */
- }
-#ifdef DEBUG
- printk(" 0x%04x", val);
-#endif
-
- codec->codec_write(codec, mh->offset, val);
-
-#ifdef DEBUG
- val = codec->codec_read(codec, mh->offset);
- printk(" -> 0x%04x\n", val);
-#endif
-}
-
-/* a thin wrapper for write_mixer */
-static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val )
-{
- unsigned int left,right;
-
- /* cleanse input a little */
- right = ((val >> 8) & 0xff) ;
- left = (val & 0xff) ;
-
- if (right > 100) right = 100;
- if (left > 100) left = 100;
-
- codec->mixer_state[oss_mixer] = (right << 8) | left;
- codec->write_mixer(codec, oss_mixer, left, right);
-}
-
-/* read or write the recmask, the ac97 can really have left and right recording
- inputs independently set, but OSS doesn't seem to want us to express that to
- the user. the caller guarantees that we have a supported bit set, and they
- must be holding the card's spinlock */
-static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask)
-{
- unsigned int val;
-
- if (rw) {
- /* read it from the card */
- val = codec->codec_read(codec, AC97_RECORD_SELECT);
-#ifdef DEBUG
- printk("ac97_codec: ac97 recmask to set to 0x%04x\n", val);
-#endif
- return (1 << ac97_rm2oss[val & 0x07]);
- }
-
- /* else, write the first set in the mask as the
- output */
- /* clear out current set value first (AC97 supports only 1 input!) */
- val = (1 << ac97_rm2oss[codec->codec_read(codec, AC97_RECORD_SELECT) & 0x07]);
- if (mask != val)
- mask &= ~val;
-
- val = ffs(mask);
- val = ac97_oss_rm[val-1];
- val |= val << 8; /* set both channels */
-
-#ifdef DEBUG
- printk("ac97_codec: setting ac97 recmask to 0x%04x\n", val);
-#endif
-
- codec->codec_write(codec, AC97_RECORD_SELECT, val);
-
- return 0;
-};
-
-static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg)
-{
- int i, val = 0;
-
- if (cmd == SOUND_MIXER_INFO) {
- mixer_info info;
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, codec->name, sizeof(info.id));
- strlcpy(info.name, codec->name, sizeof(info.name));
- info.modify_counter = codec->modcnt;
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
- if (cmd == SOUND_OLD_MIXER_INFO) {
- _old_mixer_info info;
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, codec->name, sizeof(info.id));
- strlcpy(info.name, codec->name, sizeof(info.name));
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
-
- if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int))
- return -EINVAL;
-
- if (cmd == OSS_GETVERSION)
- return put_user(SOUND_VERSION, (int __user *)arg);
-
- if (_SIOC_DIR(cmd) == _SIOC_READ) {
- switch (_IOC_NR(cmd)) {
- case SOUND_MIXER_RECSRC: /* give them the current record source */
- if (!codec->recmask_io) {
- val = 0;
- } else {
- val = codec->recmask_io(codec, 1, 0);
- }
- break;
-
- case SOUND_MIXER_DEVMASK: /* give them the supported mixers */
- val = codec->supported_mixers;
- break;
-
- case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
- val = codec->record_sources;
- break;
-
- case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
- val = codec->stereo_mixers;
- break;
-
- case SOUND_MIXER_CAPS:
- val = SOUND_CAP_EXCL_INPUT;
- break;
-
- default: /* read a specific mixer */
- i = _IOC_NR(cmd);
-
- if (!supported_mixer(codec, i))
- return -EINVAL;
-
- /* do we ever want to touch the hardware? */
- /* val = codec->read_mixer(codec, i); */
- val = codec->mixer_state[i];
- break;
- }
- return put_user(val, (int __user *)arg);
- }
-
- if (_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) {
- codec->modcnt++;
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
-
- switch (_IOC_NR(cmd)) {
- case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
- if (!codec->recmask_io) return -EINVAL;
- if (!val) return 0;
- if (!(val &= codec->record_sources)) return -EINVAL;
-
- codec->recmask_io(codec, 0, val);
-
- return 0;
- default: /* write a specific mixer */
- i = _IOC_NR(cmd);
-
- if (!supported_mixer(codec, i))
- return -EINVAL;
-
- ac97_set_mixer(codec, i, val);
-
- return 0;
- }
- }
- return -EINVAL;
-}
-
-/**
- * codec_id - Turn id1/id2 into a PnP string
- * @id1: Vendor ID1
- * @id2: Vendor ID2
- * @buf: CODEC_ID_BUFSZ byte buffer
- *
- * Fills buf with a zero terminated PnP ident string for the id1/id2
- * pair. For convenience the return is the passed in buffer pointer.
- */
-
-static char *codec_id(u16 id1, u16 id2, char *buf)
-{
- if(id1&0x8080) {
- snprintf(buf, CODEC_ID_BUFSZ, "0x%04x:0x%04x", id1, id2);
- } else {
- buf[0] = (id1 >> 8);
- buf[1] = (id1 & 0xFF);
- buf[2] = (id2 >> 8);
- snprintf(buf+3, CODEC_ID_BUFSZ - 3, "%d", id2&0xFF);
- }
- return buf;
-}
-
-/**
- * ac97_check_modem - Check if the Codec is a modem
- * @codec: codec to check
- *
- * Return true if the device is an AC97 1.0 or AC97 2.0 modem
- */
-
-static int ac97_check_modem(struct ac97_codec *codec)
-{
- /* Check for an AC97 1.0 soft modem (ID1) */
- if(codec->codec_read(codec, AC97_RESET) & 2)
- return 1;
- /* Check for an AC97 2.x soft modem */
- codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L);
- if(codec->codec_read(codec, AC97_EXTENDED_MODEM_ID) & 1)
- return 1;
- return 0;
-}
-
-
-/**
- * ac97_alloc_codec - Allocate an AC97 codec
- *
- * Returns a new AC97 codec structure. AC97 codecs may become
- * refcounted soon so this interface is needed. Returns with
- * one reference taken.
- */
-
-struct ac97_codec *ac97_alloc_codec(void)
-{
- struct ac97_codec *codec = kzalloc(sizeof(struct ac97_codec), GFP_KERNEL);
- if(!codec)
- return NULL;
-
- spin_lock_init(&codec->lock);
- INIT_LIST_HEAD(&codec->list);
- return codec;
-}
-
-EXPORT_SYMBOL(ac97_alloc_codec);
-
-/**
- * ac97_release_codec - Release an AC97 codec
- * @codec: codec to release
- *
- * Release an allocated AC97 codec. This will be refcounted in
- * time but for the moment is trivial. Calls the unregister
- * handler if the codec is now defunct.
- */
-
-void ac97_release_codec(struct ac97_codec *codec)
-{
- /* Remove from the list first, we don't want to be
- "rediscovered" */
- mutex_lock(&codec_mutex);
- list_del(&codec->list);
- mutex_unlock(&codec_mutex);
- /*
- * The driver needs to deal with internal
- * locking to avoid accidents here.
- */
- if(codec->driver)
- codec->driver->remove(codec, codec->driver);
- kfree(codec);
-}
-
-EXPORT_SYMBOL(ac97_release_codec);
-
-/**
- * ac97_probe_codec - Initialize and setup AC97-compatible codec
- * @codec: (in/out) Kernel info for a single AC97 codec
- *
- * Reset the AC97 codec, then initialize the mixer and
- * the rest of the @codec structure.
- *
- * The codec_read and codec_write fields of @codec are
- * required to be setup and working when this function
- * is called. All other fields are set by this function.
- *
- * codec_wait field of @codec can optionally be provided
- * when calling this function. If codec_wait is not %NULL,
- * this function will call codec_wait any time it is
- * necessary to wait for the audio chip to reach the
- * codec-ready state. If codec_wait is %NULL, then
- * the default behavior is to call schedule_timeout.
- * Currently codec_wait is used to wait for AC97 codec
- * reset to complete.
- *
- * Some codecs will power down when a register reset is
- * performed. We now check for such codecs.
- *
- * Returns 1 (true) on success, or 0 (false) on failure.
- */
-
-int ac97_probe_codec(struct ac97_codec *codec)
-{
- u16 id1, id2;
- u16 audio;
- int i;
- char cidbuf[CODEC_ID_BUFSZ];
- u16 f;
- struct list_head *l;
- struct ac97_driver *d;
-
- /* wait for codec-ready state */
- if (codec->codec_wait)
- codec->codec_wait(codec);
- else
- udelay(10);
-
- /* will the codec power down if register reset ? */
- id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
- id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
- codec->name = NULL;
- codec->codec_ops = &null_ops;
- for (i = 0; i < ARRAY_SIZE(ac97_codec_ids); i++) {
- if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) {
- codec->type = ac97_codec_ids[i].id;
- codec->name = ac97_codec_ids[i].name;
- codec->codec_ops = ac97_codec_ids[i].ops;
- codec->flags = ac97_codec_ids[i].flags;
- break;
- }
- }
-
- codec->model = (id1 << 16) | id2;
- if ((codec->flags & AC97_DEFAULT_POWER_OFF) == 0) {
- /* reset codec and wait for the ready bit before we continue */
- codec->codec_write(codec, AC97_RESET, 0L);
- if (codec->codec_wait)
- codec->codec_wait(codec);
- else
- udelay(10);
- }
-
- /* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should
- * be read zero.
- *
- * FIXME: is the following comment outdated? -jgarzik
- * Probing of AC97 in this way is not reliable, it is not even SAFE !!
- */
- if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) {
- printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n",
- (codec->id & 0x2) ? (codec->id&1 ? "4th" : "Tertiary")
- : (codec->id&1 ? "Secondary": "Primary"));
- return 0;
- }
-
- /* probe for Modem Codec */
- codec->modem = ac97_check_modem(codec);
-
- /* enable SPDIF */
- f = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- if((codec->codec_ops == &null_ops) && (f & 4))
- codec->codec_ops = &default_digital_ops;
-
- /* A device which thinks its a modem but isn't */
- if(codec->flags & AC97_DELUDED_MODEM)
- codec->modem = 0;
-
- if (codec->name == NULL)
- codec->name = "Unknown";
- printk(KERN_INFO "ac97_codec: AC97 %s codec, id: %s (%s)\n",
- codec->modem ? "Modem" : (audio ? "Audio" : ""),
- codec_id(id1, id2, cidbuf), codec->name);
-
- if(!ac97_init_mixer(codec))
- return 0;
-
- /*
- * Attach last so the caller can override the mixer
- * callbacks.
- */
-
- mutex_lock(&codec_mutex);
- list_add(&codec->list, &codecs);
-
- list_for_each(l, &codec_drivers) {
- d = list_entry(l, struct ac97_driver, list);
- if ((codec->model ^ d->codec_id) & d->codec_mask)
- continue;
- if(d->probe(codec, d) == 0)
- {
- codec->driver = d;
- break;
- }
- }
-
- mutex_unlock(&codec_mutex);
- return 1;
-}
-
-static int ac97_init_mixer(struct ac97_codec *codec)
-{
- u16 cap;
- int i;
-
- cap = codec->codec_read(codec, AC97_RESET);
-
- /* mixer masks */
- codec->supported_mixers = AC97_SUPPORTED_MASK;
- codec->stereo_mixers = AC97_STEREO_MASK;
- codec->record_sources = AC97_RECORD_MASK;
- if (!(cap & 0x04))
- codec->supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE);
- if (!(cap & 0x10))
- codec->supported_mixers &= ~SOUND_MASK_ALTPCM;
-
-
- /* detect bit resolution */
- codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0x2020);
- if(codec->codec_read(codec, AC97_MASTER_VOL_STEREO) == 0x2020)
- codec->bit_resolution = 6;
- else
- codec->bit_resolution = 5;
-
- /* generic OSS to AC97 wrapper */
- codec->read_mixer = ac97_read_mixer;
- codec->write_mixer = ac97_write_mixer;
- codec->recmask_io = ac97_recmask_io;
- codec->mixer_ioctl = ac97_mixer_ioctl;
-
- /* initialize mixer channel volumes */
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- struct mixer_defaults *md = &mixer_defaults[i];
- if (md->mixer == -1)
- break;
- if (!supported_mixer(codec, md->mixer))
- continue;
- ac97_set_mixer(codec, md->mixer, md->value);
- }
-
- /* codec specific initialization for 4-6 channel output or secondary codec stuff */
- if (codec->codec_ops->init != NULL) {
- codec->codec_ops->init(codec);
- }
-
- /*
- * Volume is MUTE only on this device. We have to initialise
- * it but its useless beyond that.
- */
- if(codec->flags & AC97_NO_PCM_VOLUME)
- {
- codec->supported_mixers &= ~SOUND_MASK_PCM;
- printk(KERN_WARNING "AC97 codec does not have proper volume support.\n");
- }
- return 1;
-}
-
-#define AC97_SIGMATEL_ANALOG 0x6c /* Analog Special */
-#define AC97_SIGMATEL_DAC2INVERT 0x6e
-#define AC97_SIGMATEL_BIAS1 0x70
-#define AC97_SIGMATEL_BIAS2 0x72
-#define AC97_SIGMATEL_MULTICHN 0x74 /* Multi-Channel programming */
-#define AC97_SIGMATEL_CIC1 0x76
-#define AC97_SIGMATEL_CIC2 0x78
-
-
-static int sigmatel_9708_init(struct ac97_codec * codec)
-{
- u16 codec72, codec6c;
-
- codec72 = codec->codec_read(codec, AC97_SIGMATEL_BIAS2) & 0x8000;
- codec6c = codec->codec_read(codec, AC97_SIGMATEL_ANALOG);
-
- if ((codec72==0) && (codec6c==0)) {
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1000);
- codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0007);
- } else if ((codec72==0x8000) && (codec6c==0)) {
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1001);
- codec->codec_write(codec, AC97_SIGMATEL_DAC2INVERT, 0x0008);
- } else if ((codec72==0x8000) && (codec6c==0x0080)) {
- /* nothing */
- }
- codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000);
- return 0;
-}
-
-
-static int sigmatel_9721_init(struct ac97_codec * codec)
-{
- /* Only set up secondary codec */
- if (codec->id == 0)
- return 0;
-
- codec->codec_write(codec, AC97_SURROUND_MASTER, 0L);
-
- /* initialize SigmaTel STAC9721/23 as secondary codec, decoding AC link
- sloc 3,4 = 0x01, slot 7,8 = 0x00, */
- codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x00);
-
- /* we don't have the crystal when we are on an AMR card, so use
- BIT_CLK as our clock source. Write the magic word ABBA and read
- back to enable register 0x78 */
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_read(codec, AC97_SIGMATEL_CIC1);
-
- /* sync all the clocks*/
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x3802);
-
- return 0;
-}
-
-
-static int sigmatel_9744_init(struct ac97_codec * codec)
-{
- // patch for SigmaTel
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x0000); // is this correct? --jk
- codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0002);
- codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000);
- return 0;
-}
-
-static int cmedia_init(struct ac97_codec *codec)
-{
- /* Initialise the CMedia 9739 */
- /*
- We could set various options here
- Register 0x20 bit 0x100 sets mic as center bass
- Also do multi_channel_ctrl &=~0x3000 |=0x1000
-
- For now we set up the GPIO and PC beep
- */
-
- u16 v;
-
- /* MIC */
- codec->codec_write(codec, 0x64, 0x3000);
- v = codec->codec_read(codec, 0x64);
- v &= ~0x8000;
- codec->codec_write(codec, 0x64, v);
- codec->codec_write(codec, 0x70, 0x0100);
- codec->codec_write(codec, 0x72, 0x0020);
- return 0;
-}
-
-#define AC97_WM97XX_FMIXER_VOL 0x72
-#define AC97_WM97XX_RMIXER_VOL 0x74
-#define AC97_WM97XX_TEST 0x5a
-#define AC97_WM9704_RPCM_VOL 0x70
-#define AC97_WM9711_OUT3VOL 0x16
-
-static int wolfson_init03(struct ac97_codec * codec)
-{
- /* this is known to work for the ViewSonic ViewPad 1000 */
- codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
- codec->codec_write(codec, AC97_GENERAL_PURPOSE, 0x8000);
- return 0;
-}
-
-static int wolfson_init04(struct ac97_codec * codec)
-{
- codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
- codec->codec_write(codec, AC97_WM97XX_RMIXER_VOL, 0x0808);
-
- // patch for DVD noise
- codec->codec_write(codec, AC97_WM97XX_TEST, 0x0200);
-
- // init vol as PCM vol
- codec->codec_write(codec, AC97_WM9704_RPCM_VOL,
- codec->codec_read(codec, AC97_PCMOUT_VOL));
-
- /* set rear surround volume */
- codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000);
- return 0;
-}
-
-/* WM9705, WM9710 */
-static int wolfson_init05(struct ac97_codec * codec)
-{
- /* set front mixer volume */
- codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
- return 0;
-}
-
-/* WM9711, WM9712 */
-static int wolfson_init11(struct ac97_codec * codec)
-{
- /* stop pop's during suspend/resume */
- codec->codec_write(codec, AC97_WM97XX_TEST,
- codec->codec_read(codec, AC97_WM97XX_TEST) & 0xffbf);
-
- /* set out3 volume */
- codec->codec_write(codec, AC97_WM9711_OUT3VOL, 0x0808);
- return 0;
-}
-
-/* WM9713 */
-static int wolfson_init13(struct ac97_codec * codec)
-{
- codec->codec_write(codec, AC97_RECORD_GAIN, 0x00a0);
- codec->codec_write(codec, AC97_POWER_CONTROL, 0x0000);
- codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0xDA00);
- codec->codec_write(codec, AC97_EXTEND_MODEM_STAT, 0x3810);
- codec->codec_write(codec, AC97_PHONE_VOL, 0x0808);
- codec->codec_write(codec, AC97_PCBEEP_VOL, 0x0808);
-
- return 0;
-}
-
-static int tritech_init(struct ac97_codec * codec)
-{
- codec->codec_write(codec, 0x26, 0x0300);
- codec->codec_write(codec, 0x26, 0x0000);
- codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000);
- codec->codec_write(codec, AC97_RESERVED_3A, 0x0000);
- return 0;
-}
-
-
-/* copied from drivers/sound/maestro.c */
-static int tritech_maestro_init(struct ac97_codec * codec)
-{
- /* no idea what this does */
- codec->codec_write(codec, 0x2A, 0x0001);
- codec->codec_write(codec, 0x2C, 0x0000);
- codec->codec_write(codec, 0x2C, 0XFFFF);
- return 0;
-}
-
-
-
-/*
- * Presario700 workaround
- * for Jack Sense/SPDIF Register mis-setting causing
- * no audible output
- * by Santiago Nullo 04/05/2002
- */
-
-#define AC97_AD1886_JACK_SENSE 0x72
-
-static int ad1886_init(struct ac97_codec * codec)
-{
- /* from AD1886 Specs */
- codec->codec_write(codec, AC97_AD1886_JACK_SENSE, 0x0010);
- return 0;
-}
-
-
-
-
-/*
- * This is basically standard AC97. It should work as a default for
- * almost all modern codecs. Note that some cards wire EAPD *backwards*
- * That side of it is up to the card driver not us to cope with.
- *
- */
-
-static int eapd_control(struct ac97_codec * codec, int on)
-{
- if(on)
- codec->codec_write(codec, AC97_POWER_CONTROL,
- codec->codec_read(codec, AC97_POWER_CONTROL)|0x8000);
- else
- codec->codec_write(codec, AC97_POWER_CONTROL,
- codec->codec_read(codec, AC97_POWER_CONTROL)&~0x8000);
- return 0;
-}
-
-static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
-{
- u16 reg;
-
- reg = codec->codec_read(codec, AC97_SPDIF_CONTROL);
-
- switch(rate)
- {
- /* Off by default */
- default:
- case 0:
- reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- codec->codec_write(codec, AC97_EXTENDED_STATUS, (reg & ~AC97_EA_SPDIF));
- if(rate == 0)
- return 0;
- return -EINVAL;
- case 1:
- reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K;
- break;
- case 2:
- reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K;
- break;
- case 3:
- reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K;
- break;
- }
-
- reg &= ~AC97_SC_CC_MASK;
- reg |= (mode & AUDIO_CCMASK) << 6;
-
- if(mode & AUDIO_DIGITAL)
- reg |= 2;
- if(mode & AUDIO_PRO)
- reg |= 1;
- if(mode & AUDIO_DRS)
- reg |= 0x4000;
-
- codec->codec_write(codec, AC97_SPDIF_CONTROL, reg);
-
- reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- reg &= (AC97_EA_SLOT_MASK);
- reg |= AC97_EA_VRA | AC97_EA_SPDIF | slots;
- codec->codec_write(codec, AC97_EXTENDED_STATUS, reg);
-
- reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- if(!(reg & 0x0400))
- {
- codec->codec_write(codec, AC97_EXTENDED_STATUS, reg & ~ AC97_EA_SPDIF);
- return -EINVAL;
- }
- return 0;
-}
-
-/*
- * Crystal digital audio control (CS4299)
- */
-
-static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
-{
- u16 cv;
-
- if(mode & AUDIO_DIGITAL)
- return -EINVAL;
-
- switch(rate)
- {
- case 0: cv = 0x0; break; /* SPEN off */
- case 48000: cv = 0x8004; break; /* 48KHz digital */
- case 44100: cv = 0x8104; break; /* 44.1KHz digital */
- case 32768: /* 32Khz */
- default:
- return -EINVAL;
- }
- codec->codec_write(codec, 0x68, cv);
- return 0;
-}
-
-/*
- * CMedia digital audio control
- * Needs more work.
- */
-
-static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
-{
- u16 cv;
-
- if(mode & AUDIO_DIGITAL)
- return -EINVAL;
-
- switch(rate)
- {
- case 0: cv = 0x0001; break; /* SPEN off */
- case 48000: cv = 0x0009; break; /* 48KHz digital */
- default:
- return -EINVAL;
- }
- codec->codec_write(codec, 0x2A, 0x05c4);
- codec->codec_write(codec, 0x6C, cv);
-
- /* Switch on mix to surround */
- cv = codec->codec_read(codec, 0x64);
- cv &= ~0x0200;
- if(mode)
- cv |= 0x0200;
- codec->codec_write(codec, 0x64, cv);
- return 0;
-}
-
-
-/* copied from drivers/sound/maestro.c */
-#if 0 /* there has been 1 person on the planet with a pt101 that we
- know of. If they care, they can put this back in :) */
-static int pt101_init(struct ac97_codec * codec)
-{
- printk(KERN_INFO "ac97_codec: PT101 Codec detected, initializing but _not_ installing mixer device.\n");
- /* who knows.. */
- codec->codec_write(codec, 0x2A, 0x0001);
- codec->codec_write(codec, 0x2C, 0x0000);
- codec->codec_write(codec, 0x2C, 0xFFFF);
- codec->codec_write(codec, 0x10, 0x9F1F);
- codec->codec_write(codec, 0x12, 0x0808);
- codec->codec_write(codec, 0x14, 0x9F1F);
- codec->codec_write(codec, 0x16, 0x9F1F);
- codec->codec_write(codec, 0x18, 0x0404);
- codec->codec_write(codec, 0x1A, 0x0000);
- codec->codec_write(codec, 0x1C, 0x0000);
- codec->codec_write(codec, 0x02, 0x0404);
- codec->codec_write(codec, 0x04, 0x0808);
- codec->codec_write(codec, 0x0C, 0x801F);
- codec->codec_write(codec, 0x0E, 0x801F);
- return 0;
-}
-#endif
-
-
-EXPORT_SYMBOL(ac97_probe_codec);
-
-MODULE_LICENSE("GPL");
-
+++ /dev/null
-/*
- * au1550_ac97.c -- Sound driver for Alchemy Au1550 MIPS Internet Edge
- * Processor.
- *
- * Copyright 2004 Embedded Edge, LLC
- * dan@embeddededge.com
- *
- * Mostly copied from the au1000.c driver and some from the
- * PowerMac dbdma driver.
- * We assume the processor can do memory coherent DMA.
- *
- * Ported to 2.6 by Matt Porter <mporter@kernel.crashing.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
- * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#undef DEBUG
-
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/delay.h>
-#include <linux/sound.h>
-#include <linux/slab.h>
-#include <linux/soundcard.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/poll.h>
-#include <linux/bitops.h>
-#include <linux/spinlock.h>
-#include <linux/ac97_codec.h>
-#include <linux/mutex.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/hardirq.h>
-#include <asm/mach-au1x00/au1xxx_psc.h>
-#include <asm/mach-au1x00/au1xxx_dbdma.h>
-#include <asm/mach-au1x00/au1xxx.h>
-
-#undef OSS_DOCUMENTED_MIXER_SEMANTICS
-
-/* misc stuff */
-#define POLL_COUNT 0x50000
-#define AC97_EXT_DACS (AC97_EXTID_SDAC | AC97_EXTID_CDAC | AC97_EXTID_LDAC)
-
-/* The number of DBDMA ring descriptors to allocate. No sense making
- * this too large....if you can't keep up with a few you aren't likely
- * to be able to with lots of them, either.
- */
-#define NUM_DBDMA_DESCRIPTORS 4
-
-#define err(format, arg...) printk(KERN_ERR format "\n" , ## arg)
-
-/* Boot options
- * 0 = no VRA, 1 = use VRA if codec supports it
- */
-static DEFINE_MUTEX(au1550_ac97_mutex);
-static int vra = 1;
-module_param(vra, bool, 0);
-MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it");
-
-static struct au1550_state {
- /* soundcore stuff */
- int dev_audio;
-
- struct ac97_codec *codec;
- unsigned codec_base_caps; /* AC'97 reg 00h, "Reset Register" */
- unsigned codec_ext_caps; /* AC'97 reg 28h, "Extended Audio ID" */
- int no_vra; /* do not use VRA */
-
- spinlock_t lock;
- struct mutex open_mutex;
- struct mutex sem;
- fmode_t open_mode;
- wait_queue_head_t open_wait;
-
- struct dmabuf {
- u32 dmanr;
- unsigned sample_rate;
- unsigned src_factor;
- unsigned sample_size;
- int num_channels;
- int dma_bytes_per_sample;
- int user_bytes_per_sample;
- int cnt_factor;
-
- void *rawbuf;
- unsigned buforder;
- unsigned numfrag;
- unsigned fragshift;
- void *nextIn;
- void *nextOut;
- int count;
- unsigned total_bytes;
- unsigned error;
- wait_queue_head_t wait;
-
- /* redundant, but makes calculations easier */
- unsigned fragsize;
- unsigned dma_fragsize;
- unsigned dmasize;
- unsigned dma_qcount;
-
- /* OSS stuff */
- unsigned mapped:1;
- unsigned ready:1;
- unsigned stopped:1;
- unsigned ossfragshift;
- int ossmaxfrags;
- unsigned subdivision;
- } dma_dac, dma_adc;
-} au1550_state;
-
-static unsigned
-ld2(unsigned int x)
-{
- unsigned r = 0;
-
- if (x >= 0x10000) {
- x >>= 16;
- r += 16;
- }
- if (x >= 0x100) {
- x >>= 8;
- r += 8;
- }
- if (x >= 0x10) {
- x >>= 4;
- r += 4;
- }
- if (x >= 4) {
- x >>= 2;
- r += 2;
- }
- if (x >= 2)
- r++;
- return r;
-}
-
-static void
-au1550_delay(int msec)
-{
- if (in_interrupt())
- return;
-
- schedule_timeout_uninterruptible(msecs_to_jiffies(msec));
-}
-
-static u16
-rdcodec(struct ac97_codec *codec, u8 addr)
-{
- struct au1550_state *s = codec->private_data;
- unsigned long flags;
- u32 cmd, val;
- u16 data;
- int i;
-
- spin_lock_irqsave(&s->lock, flags);
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT)
- err("rdcodec: codec cmd pending expired!");
-
- cmd = (u32)PSC_AC97CDC_INDX(addr);
- cmd |= PSC_AC97CDC_RD; /* read command */
- au_writel(cmd, PSC_AC97CDC);
- au_sync();
-
- /* now wait for the data
- */
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT) {
- err("rdcodec: read poll expired!");
- data = 0;
- goto out;
- }
-
- /* wait for command done?
- */
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97EVNT);
- au_sync();
- if (val & PSC_AC97EVNT_CD)
- break;
- }
- if (i == POLL_COUNT) {
- err("rdcodec: read cmdwait expired!");
- data = 0;
- goto out;
- }
-
- data = au_readl(PSC_AC97CDC) & 0xffff;
- au_sync();
-
- /* Clear command done event.
- */
- au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT);
- au_sync();
-
- out:
- spin_unlock_irqrestore(&s->lock, flags);
-
- return data;
-}
-
-
-static void
-wrcodec(struct ac97_codec *codec, u8 addr, u16 data)
-{
- struct au1550_state *s = codec->private_data;
- unsigned long flags;
- u32 cmd, val;
- int i;
-
- spin_lock_irqsave(&s->lock, flags);
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT)
- err("wrcodec: codec cmd pending expired!");
-
- cmd = (u32)PSC_AC97CDC_INDX(addr);
- cmd |= (u32)data;
- au_writel(cmd, PSC_AC97CDC);
- au_sync();
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT)
- err("wrcodec: codec cmd pending expired!");
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97EVNT);
- au_sync();
- if (val & PSC_AC97EVNT_CD)
- break;
- }
- if (i == POLL_COUNT)
- err("wrcodec: read cmdwait expired!");
-
- /* Clear command done event.
- */
- au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT);
- au_sync();
-
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-static void
-waitcodec(struct ac97_codec *codec)
-{
- u16 temp;
- u32 val;
- int i;
-
- /* codec_wait is used to wait for a ready state after
- * an AC97C_RESET.
- */
- au1550_delay(10);
-
- /* first poll the CODEC_READY tag bit
- */
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (val & PSC_AC97STAT_CR)
- break;
- }
- if (i == POLL_COUNT) {
- err("waitcodec: CODEC_READY poll expired!");
- return;
- }
-
- /* get AC'97 powerdown control/status register
- */
- temp = rdcodec(codec, AC97_POWER_CONTROL);
-
- /* If anything is powered down, power'em up
- */
- if (temp & 0x7f00) {
- /* Power on
- */
- wrcodec(codec, AC97_POWER_CONTROL, 0);
- au1550_delay(100);
-
- /* Reread
- */
- temp = rdcodec(codec, AC97_POWER_CONTROL);
- }
-
- /* Check if Codec REF,ANL,DAC,ADC ready
- */
- if ((temp & 0x7f0f) != 0x000f)
- err("codec reg 26 status (0x%x) not ready!!", temp);
-}
-
-/* stop the ADC before calling */
-static void
-set_adc_rate(struct au1550_state *s, unsigned rate)
-{
- struct dmabuf *adc = &s->dma_adc;
- struct dmabuf *dac = &s->dma_dac;
- unsigned adc_rate, dac_rate;
- u16 ac97_extstat;
-
- if (s->no_vra) {
- /* calc SRC factor
- */
- adc->src_factor = ((96000 / rate) + 1) >> 1;
- adc->sample_rate = 48000 / adc->src_factor;
- return;
- }
-
- adc->src_factor = 1;
-
- ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
-
- rate = rate > 48000 ? 48000 : rate;
-
- /* enable VRA
- */
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ac97_extstat | AC97_EXTSTAT_VRA);
-
- /* now write the sample rate
- */
- wrcodec(s->codec, AC97_PCM_LR_ADC_RATE, (u16) rate);
-
- /* read it back for actual supported rate
- */
- adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE);
-
- pr_debug("set_adc_rate: set to %d Hz\n", adc_rate);
-
- /* some codec's don't allow unequal DAC and ADC rates, in which case
- * writing one rate reg actually changes both.
- */
- dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);
- if (dac->num_channels > 2)
- wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, dac_rate);
- if (dac->num_channels > 4)
- wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, dac_rate);
-
- adc->sample_rate = adc_rate;
- dac->sample_rate = dac_rate;
-}
-
-/* stop the DAC before calling */
-static void
-set_dac_rate(struct au1550_state *s, unsigned rate)
-{
- struct dmabuf *dac = &s->dma_dac;
- struct dmabuf *adc = &s->dma_adc;
- unsigned adc_rate, dac_rate;
- u16 ac97_extstat;
-
- if (s->no_vra) {
- /* calc SRC factor
- */
- dac->src_factor = ((96000 / rate) + 1) >> 1;
- dac->sample_rate = 48000 / dac->src_factor;
- return;
- }
-
- dac->src_factor = 1;
-
- ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
-
- rate = rate > 48000 ? 48000 : rate;
-
- /* enable VRA
- */
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ac97_extstat | AC97_EXTSTAT_VRA);
-
- /* now write the sample rate
- */
- wrcodec(s->codec, AC97_PCM_FRONT_DAC_RATE, (u16) rate);
-
- /* I don't support different sample rates for multichannel,
- * so make these channels the same.
- */
- if (dac->num_channels > 2)
- wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, (u16) rate);
- if (dac->num_channels > 4)
- wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, (u16) rate);
- /* read it back for actual supported rate
- */
- dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);
-
- pr_debug("set_dac_rate: set to %d Hz\n", dac_rate);
-
- /* some codec's don't allow unequal DAC and ADC rates, in which case
- * writing one rate reg actually changes both.
- */
- adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE);
-
- dac->sample_rate = dac_rate;
- adc->sample_rate = adc_rate;
-}
-
-static void
-stop_dac(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_dac;
- u32 stat;
- unsigned long flags;
-
- if (db->stopped)
- return;
-
- spin_lock_irqsave(&s->lock, flags);
-
- au_writel(PSC_AC97PCR_TP, PSC_AC97PCR);
- au_sync();
-
- /* Wait for Transmit Busy to show disabled.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_TB) != 0);
-
- au1xxx_dbdma_reset(db->dmanr);
-
- db->stopped = 1;
-
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-static void
-stop_adc(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_adc;
- unsigned long flags;
- u32 stat;
-
- if (db->stopped)
- return;
-
- spin_lock_irqsave(&s->lock, flags);
-
- au_writel(PSC_AC97PCR_RP, PSC_AC97PCR);
- au_sync();
-
- /* Wait for Receive Busy to show disabled.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_RB) != 0);
-
- au1xxx_dbdma_reset(db->dmanr);
-
- db->stopped = 1;
-
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-
-static void
-set_xmit_slots(int num_channels)
-{
- u32 ac97_config, stat;
-
- ac97_config = au_readl(PSC_AC97CFG);
- au_sync();
- ac97_config &= ~(PSC_AC97CFG_TXSLOT_MASK | PSC_AC97CFG_DE_ENABLE);
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- switch (num_channels) {
- case 6: /* stereo with surround and center/LFE,
- * slots 3,4,6,7,8,9
- */
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(6);
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(9);
-
- case 4: /* stereo with surround, slots 3,4,7,8 */
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(7);
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(8);
-
- case 2: /* stereo, slots 3,4 */
- case 1: /* mono */
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(3);
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(4);
- }
-
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- ac97_config |= PSC_AC97CFG_DE_ENABLE;
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- /* Wait for Device ready.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_DR) == 0);
-}
-
-static void
-set_recv_slots(int num_channels)
-{
- u32 ac97_config, stat;
-
- ac97_config = au_readl(PSC_AC97CFG);
- au_sync();
- ac97_config &= ~(PSC_AC97CFG_RXSLOT_MASK | PSC_AC97CFG_DE_ENABLE);
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- /* Always enable slots 3 and 4 (stereo). Slot 6 is
- * optional Mic ADC, which we don't support yet.
- */
- ac97_config |= PSC_AC97CFG_RXSLOT_ENA(3);
- ac97_config |= PSC_AC97CFG_RXSLOT_ENA(4);
-
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- ac97_config |= PSC_AC97CFG_DE_ENABLE;
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- /* Wait for Device ready.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_DR) == 0);
-}
-
-/* Hold spinlock for both start_dac() and start_adc() calls */
-static void
-start_dac(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_dac;
-
- if (!db->stopped)
- return;
-
- set_xmit_slots(db->num_channels);
- au_writel(PSC_AC97PCR_TC, PSC_AC97PCR);
- au_sync();
- au_writel(PSC_AC97PCR_TS, PSC_AC97PCR);
- au_sync();
-
- au1xxx_dbdma_start(db->dmanr);
-
- db->stopped = 0;
-}
-
-static void
-start_adc(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_adc;
- int i;
-
- if (!db->stopped)
- return;
-
- /* Put two buffers on the ring to get things started.
- */
- for (i=0; i<2; i++) {
- au1xxx_dbdma_put_dest(db->dmanr, virt_to_phys(db->nextIn),
- db->dma_fragsize, DDMA_FLAGS_IE);
-
- db->nextIn += db->dma_fragsize;
- if (db->nextIn >= db->rawbuf + db->dmasize)
- db->nextIn -= db->dmasize;
- }
-
- set_recv_slots(db->num_channels);
- au1xxx_dbdma_start(db->dmanr);
- au_writel(PSC_AC97PCR_RC, PSC_AC97PCR);
- au_sync();
- au_writel(PSC_AC97PCR_RS, PSC_AC97PCR);
- au_sync();
-
- db->stopped = 0;
-}
-
-static int
-prog_dmabuf(struct au1550_state *s, struct dmabuf *db)
-{
- unsigned user_bytes_per_sec;
- unsigned bufs;
- unsigned rate = db->sample_rate;
-
- if (!db->rawbuf) {
- db->ready = db->mapped = 0;
- db->buforder = 5; /* 32 * PAGE_SIZE */
- db->rawbuf = kmalloc((PAGE_SIZE << db->buforder), GFP_KERNEL);
- if (!db->rawbuf)
- return -ENOMEM;
- }
-
- db->cnt_factor = 1;
- if (db->sample_size == 8)
- db->cnt_factor *= 2;
- if (db->num_channels == 1)
- db->cnt_factor *= 2;
- db->cnt_factor *= db->src_factor;
-
- db->count = 0;
- db->dma_qcount = 0;
- db->nextIn = db->nextOut = db->rawbuf;
-
- db->user_bytes_per_sample = (db->sample_size>>3) * db->num_channels;
- db->dma_bytes_per_sample = 2 * ((db->num_channels == 1) ?
- 2 : db->num_channels);
-
- user_bytes_per_sec = rate * db->user_bytes_per_sample;
- bufs = PAGE_SIZE << db->buforder;
- if (db->ossfragshift) {
- if ((1000 << db->ossfragshift) < user_bytes_per_sec)
- db->fragshift = ld2(user_bytes_per_sec/1000);
- else
- db->fragshift = db->ossfragshift;
- } else {
- db->fragshift = ld2(user_bytes_per_sec / 100 /
- (db->subdivision ? db->subdivision : 1));
- if (db->fragshift < 3)
- db->fragshift = 3;
- }
-
- db->fragsize = 1 << db->fragshift;
- db->dma_fragsize = db->fragsize * db->cnt_factor;
- db->numfrag = bufs / db->dma_fragsize;
-
- while (db->numfrag < 4 && db->fragshift > 3) {
- db->fragshift--;
- db->fragsize = 1 << db->fragshift;
- db->dma_fragsize = db->fragsize * db->cnt_factor;
- db->numfrag = bufs / db->dma_fragsize;
- }
-
- if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
- db->numfrag = db->ossmaxfrags;
-
- db->dmasize = db->dma_fragsize * db->numfrag;
- memset(db->rawbuf, 0, bufs);
-
- pr_debug("prog_dmabuf: rate=%d, samplesize=%d, channels=%d\n",
- rate, db->sample_size, db->num_channels);
- pr_debug("prog_dmabuf: fragsize=%d, cnt_factor=%d, dma_fragsize=%d\n",
- db->fragsize, db->cnt_factor, db->dma_fragsize);
- pr_debug("prog_dmabuf: numfrag=%d, dmasize=%d\n", db->numfrag, db->dmasize);
-
- db->ready = 1;
- return 0;
-}
-
-static int
-prog_dmabuf_adc(struct au1550_state *s)
-{
- stop_adc(s);
- return prog_dmabuf(s, &s->dma_adc);
-
-}
-
-static int
-prog_dmabuf_dac(struct au1550_state *s)
-{
- stop_dac(s);
- return prog_dmabuf(s, &s->dma_dac);
-}
-
-
-static void dac_dma_interrupt(int irq, void *dev_id)
-{
- struct au1550_state *s = (struct au1550_state *) dev_id;
- struct dmabuf *db = &s->dma_dac;
- u32 ac97c_stat;
-
- spin_lock(&s->lock);
-
- ac97c_stat = au_readl(PSC_AC97STAT);
- if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE))
- pr_debug("AC97C status = 0x%08x\n", ac97c_stat);
- db->dma_qcount--;
-
- if (db->count >= db->fragsize) {
- if (au1xxx_dbdma_put_source(db->dmanr,
- virt_to_phys(db->nextOut), db->fragsize,
- DDMA_FLAGS_IE) == 0) {
- err("qcount < 2 and no ring room!");
- }
- db->nextOut += db->fragsize;
- if (db->nextOut >= db->rawbuf + db->dmasize)
- db->nextOut -= db->dmasize;
- db->count -= db->fragsize;
- db->total_bytes += db->dma_fragsize;
- db->dma_qcount++;
- }
-
- /* wake up anybody listening */
- if (waitqueue_active(&db->wait))
- wake_up(&db->wait);
-
- spin_unlock(&s->lock);
-}
-
-
-static void adc_dma_interrupt(int irq, void *dev_id)
-{
- struct au1550_state *s = (struct au1550_state *)dev_id;
- struct dmabuf *dp = &s->dma_adc;
- u32 obytes;
- char *obuf;
-
- spin_lock(&s->lock);
-
- /* Pull the buffer from the dma queue.
- */
- au1xxx_dbdma_get_dest(dp->dmanr, (void *)(&obuf), &obytes);
-
- if ((dp->count + obytes) > dp->dmasize) {
- /* Overrun. Stop ADC and log the error
- */
- spin_unlock(&s->lock);
- stop_adc(s);
- dp->error++;
- err("adc overrun");
- return;
- }
-
- /* Put a new empty buffer on the destination DMA.
- */
- au1xxx_dbdma_put_dest(dp->dmanr, virt_to_phys(dp->nextIn),
- dp->dma_fragsize, DDMA_FLAGS_IE);
-
- dp->nextIn += dp->dma_fragsize;
- if (dp->nextIn >= dp->rawbuf + dp->dmasize)
- dp->nextIn -= dp->dmasize;
-
- dp->count += obytes;
- dp->total_bytes += obytes;
-
- /* wake up anybody listening
- */
- if (waitqueue_active(&dp->wait))
- wake_up(&dp->wait);
-
- spin_unlock(&s->lock);
-}
-
-static loff_t
-au1550_llseek(struct file *file, loff_t offset, int origin)
-{
- return -ESPIPE;
-}
-
-
-static int
-au1550_open_mixdev(struct inode *inode, struct file *file)
-{
- mutex_lock(&au1550_ac97_mutex);
- file->private_data = &au1550_state;
- mutex_unlock(&au1550_ac97_mutex);
- return 0;
-}
-
-static int
-au1550_release_mixdev(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static int
-mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd,
- unsigned long arg)
-{
- return codec->mixer_ioctl(codec, cmd, arg);
-}
-
-static long
-au1550_ioctl_mixdev(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct au1550_state *s = file->private_data;
- struct ac97_codec *codec = s->codec;
- int ret;
-
- mutex_lock(&au1550_ac97_mutex);
- ret = mixdev_ioctl(codec, cmd, arg);
- mutex_unlock(&au1550_ac97_mutex);
-
- return ret;
-}
-
-static /*const */ struct file_operations au1550_mixer_fops = {
- .owner = THIS_MODULE,
- .llseek = au1550_llseek,
- .unlocked_ioctl = au1550_ioctl_mixdev,
- .open = au1550_open_mixdev,
- .release = au1550_release_mixdev,
-};
-
-static int
-drain_dac(struct au1550_state *s, int nonblock)
-{
- unsigned long flags;
- int count, tmo;
-
- if (s->dma_dac.mapped || !s->dma_dac.ready || s->dma_dac.stopped)
- return 0;
-
- for (;;) {
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_dac.count;
- spin_unlock_irqrestore(&s->lock, flags);
- if (count <= s->dma_dac.fragsize)
- break;
- if (signal_pending(current))
- break;
- if (nonblock)
- return -EBUSY;
- tmo = 1000 * count / (s->no_vra ?
- 48000 : s->dma_dac.sample_rate);
- tmo /= s->dma_dac.dma_bytes_per_sample;
- au1550_delay(tmo);
- }
- if (signal_pending(current))
- return -ERESTARTSYS;
- return 0;
-}
-
-static inline u8 S16_TO_U8(s16 ch)
-{
- return (u8) (ch >> 8) + 0x80;
-}
-static inline s16 U8_TO_S16(u8 ch)
-{
- return (s16) (ch - 0x80) << 8;
-}
-
-/*
- * Translates user samples to dma buffer suitable for AC'97 DAC data:
- * If mono, copy left channel to right channel in dma buffer.
- * If 8 bit samples, cvt to 16-bit before writing to dma buffer.
- * If interpolating (no VRA), duplicate every audio frame src_factor times.
- */
-static int
-translate_from_user(struct dmabuf *db, char* dmabuf, char* userbuf,
- int dmacount)
-{
- int sample, i;
- int interp_bytes_per_sample;
- int num_samples;
- int mono = (db->num_channels == 1);
- char usersample[12];
- s16 ch, dmasample[6];
-
- if (db->sample_size == 16 && !mono && db->src_factor == 1) {
- /* no translation necessary, just copy
- */
- if (copy_from_user(dmabuf, userbuf, dmacount))
- return -EFAULT;
- return dmacount;
- }
-
- interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor;
- num_samples = dmacount / interp_bytes_per_sample;
-
- for (sample = 0; sample < num_samples; sample++) {
- if (copy_from_user(usersample, userbuf,
- db->user_bytes_per_sample)) {
- return -EFAULT;
- }
-
- for (i = 0; i < db->num_channels; i++) {
- if (db->sample_size == 8)
- ch = U8_TO_S16(usersample[i]);
- else
- ch = *((s16 *) (&usersample[i * 2]));
- dmasample[i] = ch;
- if (mono)
- dmasample[i + 1] = ch; /* right channel */
- }
-
- /* duplicate every audio frame src_factor times
- */
- for (i = 0; i < db->src_factor; i++)
- memcpy(dmabuf, dmasample, db->dma_bytes_per_sample);
-
- userbuf += db->user_bytes_per_sample;
- dmabuf += interp_bytes_per_sample;
- }
-
- return num_samples * interp_bytes_per_sample;
-}
-
-/*
- * Translates AC'97 ADC samples to user buffer:
- * If mono, send only left channel to user buffer.
- * If 8 bit samples, cvt from 16 to 8 bit before writing to user buffer.
- * If decimating (no VRA), skip over src_factor audio frames.
- */
-static int
-translate_to_user(struct dmabuf *db, char* userbuf, char* dmabuf,
- int dmacount)
-{
- int sample, i;
- int interp_bytes_per_sample;
- int num_samples;
- int mono = (db->num_channels == 1);
- char usersample[12];
-
- if (db->sample_size == 16 && !mono && db->src_factor == 1) {
- /* no translation necessary, just copy
- */
- if (copy_to_user(userbuf, dmabuf, dmacount))
- return -EFAULT;
- return dmacount;
- }
-
- interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor;
- num_samples = dmacount / interp_bytes_per_sample;
-
- for (sample = 0; sample < num_samples; sample++) {
- for (i = 0; i < db->num_channels; i++) {
- if (db->sample_size == 8)
- usersample[i] =
- S16_TO_U8(*((s16 *) (&dmabuf[i * 2])));
- else
- *((s16 *) (&usersample[i * 2])) =
- *((s16 *) (&dmabuf[i * 2]));
- }
-
- if (copy_to_user(userbuf, usersample,
- db->user_bytes_per_sample)) {
- return -EFAULT;
- }
-
- userbuf += db->user_bytes_per_sample;
- dmabuf += interp_bytes_per_sample;
- }
-
- return num_samples * interp_bytes_per_sample;
-}
-
-/*
- * Copy audio data to/from user buffer from/to dma buffer, taking care
- * that we wrap when reading/writing the dma buffer. Returns actual byte
- * count written to or read from the dma buffer.
- */
-static int
-copy_dmabuf_user(struct dmabuf *db, char* userbuf, int count, int to_user)
-{
- char *bufptr = to_user ? db->nextOut : db->nextIn;
- char *bufend = db->rawbuf + db->dmasize;
- int cnt, ret;
-
- if (bufptr + count > bufend) {
- int partial = (int) (bufend - bufptr);
- if (to_user) {
- if ((cnt = translate_to_user(db, userbuf,
- bufptr, partial)) < 0)
- return cnt;
- ret = cnt;
- if ((cnt = translate_to_user(db, userbuf + partial,
- db->rawbuf,
- count - partial)) < 0)
- return cnt;
- ret += cnt;
- } else {
- if ((cnt = translate_from_user(db, bufptr, userbuf,
- partial)) < 0)
- return cnt;
- ret = cnt;
- if ((cnt = translate_from_user(db, db->rawbuf,
- userbuf + partial,
- count - partial)) < 0)
- return cnt;
- ret += cnt;
- }
- } else {
- if (to_user)
- ret = translate_to_user(db, userbuf, bufptr, count);
- else
- ret = translate_from_user(db, bufptr, userbuf, count);
- }
-
- return ret;
-}
-
-
-static ssize_t
-au1550_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
-{
- struct au1550_state *s = file->private_data;
- struct dmabuf *db = &s->dma_adc;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret;
- unsigned long flags;
- int cnt, usercnt, avail;
-
- if (db->mapped)
- return -ENXIO;
- if (!access_ok(VERIFY_WRITE, buffer, count))
- return -EFAULT;
- ret = 0;
-
- count *= db->cnt_factor;
-
- mutex_lock(&s->sem);
- add_wait_queue(&db->wait, &wait);
-
- while (count > 0) {
- /* wait for samples in ADC dma buffer
- */
- do {
- spin_lock_irqsave(&s->lock, flags);
- if (db->stopped)
- start_adc(s);
- avail = db->count;
- if (avail <= 0)
- __set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&s->lock, flags);
- if (avail <= 0) {
- if (file->f_flags & O_NONBLOCK) {
- if (!ret)
- ret = -EAGAIN;
- goto out;
- }
- mutex_unlock(&s->sem);
- schedule();
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- goto out2;
- }
- mutex_lock(&s->sem);
- }
- } while (avail <= 0);
-
- /* copy from nextOut to user
- */
- if ((cnt = copy_dmabuf_user(db, buffer,
- count > avail ?
- avail : count, 1)) < 0) {
- if (!ret)
- ret = -EFAULT;
- goto out;
- }
-
- spin_lock_irqsave(&s->lock, flags);
- db->count -= cnt;
- db->nextOut += cnt;
- if (db->nextOut >= db->rawbuf + db->dmasize)
- db->nextOut -= db->dmasize;
- spin_unlock_irqrestore(&s->lock, flags);
-
- count -= cnt;
- usercnt = cnt / db->cnt_factor;
- buffer += usercnt;
- ret += usercnt;
- } /* while (count > 0) */
-
-out:
- mutex_unlock(&s->sem);
-out2:
- remove_wait_queue(&db->wait, &wait);
- set_current_state(TASK_RUNNING);
- return ret;
-}
-
-static ssize_t
-au1550_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
-{
- struct au1550_state *s = file->private_data;
- struct dmabuf *db = &s->dma_dac;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret = 0;
- unsigned long flags;
- int cnt, usercnt, avail;
-
- pr_debug("write: count=%d\n", count);
-
- if (db->mapped)
- return -ENXIO;
- if (!access_ok(VERIFY_READ, buffer, count))
- return -EFAULT;
-
- count *= db->cnt_factor;
-
- mutex_lock(&s->sem);
- add_wait_queue(&db->wait, &wait);
-
- while (count > 0) {
- /* wait for space in playback buffer
- */
- do {
- spin_lock_irqsave(&s->lock, flags);
- avail = (int) db->dmasize - db->count;
- if (avail <= 0)
- __set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&s->lock, flags);
- if (avail <= 0) {
- if (file->f_flags & O_NONBLOCK) {
- if (!ret)
- ret = -EAGAIN;
- goto out;
- }
- mutex_unlock(&s->sem);
- schedule();
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- goto out2;
- }
- mutex_lock(&s->sem);
- }
- } while (avail <= 0);
-
- /* copy from user to nextIn
- */
- if ((cnt = copy_dmabuf_user(db, (char *) buffer,
- count > avail ?
- avail : count, 0)) < 0) {
- if (!ret)
- ret = -EFAULT;
- goto out;
- }
-
- spin_lock_irqsave(&s->lock, flags);
- db->count += cnt;
- db->nextIn += cnt;
- if (db->nextIn >= db->rawbuf + db->dmasize)
- db->nextIn -= db->dmasize;
-
- /* If the data is available, we want to keep two buffers
- * on the dma queue. If the queue count reaches zero,
- * we know the dma has stopped.
- */
- while ((db->dma_qcount < 2) && (db->count >= db->fragsize)) {
- if (au1xxx_dbdma_put_source(db->dmanr,
- virt_to_phys(db->nextOut), db->fragsize,
- DDMA_FLAGS_IE) == 0) {
- err("qcount < 2 and no ring room!");
- }
- db->nextOut += db->fragsize;
- if (db->nextOut >= db->rawbuf + db->dmasize)
- db->nextOut -= db->dmasize;
- db->total_bytes += db->dma_fragsize;
- if (db->dma_qcount == 0)
- start_dac(s);
- db->dma_qcount++;
- }
- spin_unlock_irqrestore(&s->lock, flags);
-
- count -= cnt;
- usercnt = cnt / db->cnt_factor;
- buffer += usercnt;
- ret += usercnt;
- } /* while (count > 0) */
-
-out:
- mutex_unlock(&s->sem);
-out2:
- remove_wait_queue(&db->wait, &wait);
- set_current_state(TASK_RUNNING);
- return ret;
-}
-
-
-/* No kernel lock - we have our own spinlock */
-static unsigned int
-au1550_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct au1550_state *s = file->private_data;
- unsigned long flags;
- unsigned int mask = 0;
-
- if (file->f_mode & FMODE_WRITE) {
- if (!s->dma_dac.ready)
- return 0;
- poll_wait(file, &s->dma_dac.wait, wait);
- }
- if (file->f_mode & FMODE_READ) {
- if (!s->dma_adc.ready)
- return 0;
- poll_wait(file, &s->dma_adc.wait, wait);
- }
-
- spin_lock_irqsave(&s->lock, flags);
-
- if (file->f_mode & FMODE_READ) {
- if (s->dma_adc.count >= (signed)s->dma_adc.dma_fragsize)
- mask |= POLLIN | POLLRDNORM;
- }
- if (file->f_mode & FMODE_WRITE) {
- if (s->dma_dac.mapped) {
- if (s->dma_dac.count >=
- (signed)s->dma_dac.dma_fragsize)
- mask |= POLLOUT | POLLWRNORM;
- } else {
- if ((signed) s->dma_dac.dmasize >=
- s->dma_dac.count + (signed)s->dma_dac.dma_fragsize)
- mask |= POLLOUT | POLLWRNORM;
- }
- }
- spin_unlock_irqrestore(&s->lock, flags);
- return mask;
-}
-
-static int
-au1550_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct au1550_state *s = file->private_data;
- struct dmabuf *db;
- unsigned long size;
- int ret = 0;
-
- mutex_lock(&au1550_ac97_mutex);
- mutex_lock(&s->sem);
- if (vma->vm_flags & VM_WRITE)
- db = &s->dma_dac;
- else if (vma->vm_flags & VM_READ)
- db = &s->dma_adc;
- else {
- ret = -EINVAL;
- goto out;
- }
- if (vma->vm_pgoff != 0) {
- ret = -EINVAL;
- goto out;
- }
- size = vma->vm_end - vma->vm_start;
- if (size > (PAGE_SIZE << db->buforder)) {
- ret = -EINVAL;
- goto out;
- }
- if (remap_pfn_range(vma, vma->vm_start, page_to_pfn(virt_to_page(db->rawbuf)),
- size, vma->vm_page_prot)) {
- ret = -EAGAIN;
- goto out;
- }
- vma->vm_flags &= ~VM_IO;
- db->mapped = 1;
-out:
- mutex_unlock(&s->sem);
- mutex_unlock(&au1550_ac97_mutex);
- return ret;
-}
-
-#ifdef DEBUG
-static struct ioctl_str_t {
- unsigned int cmd;
- const char *str;
-} ioctl_str[] = {
- {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"},
- {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"},
- {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"},
- {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"},
- {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"},
- {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"},
- {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"},
- {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"},
- {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"},
- {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"},
- {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"},
- {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"},
- {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"},
- {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"},
- {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"},
- {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"},
- {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"},
- {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"},
- {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"},
- {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"},
- {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"},
- {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"},
- {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"},
- {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"},
- {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"},
- {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"},
- {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"},
- {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"},
- {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"},
- {OSS_GETVERSION, "OSS_GETVERSION"},
- {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"},
- {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"},
- {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"},
- {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"}
-};
-#endif
-
-static int
-dma_count_done(struct dmabuf *db)
-{
- if (db->stopped)
- return 0;
-
- return db->dma_fragsize - au1xxx_get_dma_residue(db->dmanr);
-}
-
-
-static int
-au1550_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct au1550_state *s = file->private_data;
- unsigned long flags;
- audio_buf_info abinfo;
- count_info cinfo;
- int count;
- int val, mapped, ret, diff;
-
- mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
- ((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
-
-#ifdef DEBUG
- for (count = 0; count < ARRAY_SIZE(ioctl_str); count++) {
- if (ioctl_str[count].cmd == cmd)
- break;
- }
- if (count < ARRAY_SIZE(ioctl_str))
- pr_debug("ioctl %s, arg=0x%lxn", ioctl_str[count].str, arg);
- else
- pr_debug("ioctl 0x%x unknown, arg=0x%lx\n", cmd, arg);
-#endif
-
- switch (cmd) {
- case OSS_GETVERSION:
- return put_user(SOUND_VERSION, (int *) arg);
-
- case SNDCTL_DSP_SYNC:
- if (file->f_mode & FMODE_WRITE)
- return drain_dac(s, file->f_flags & O_NONBLOCK);
- return 0;
-
- case SNDCTL_DSP_SETDUPLEX:
- return 0;
-
- case SNDCTL_DSP_GETCAPS:
- return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME |
- DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
-
- case SNDCTL_DSP_RESET:
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- synchronize_irq();
- s->dma_dac.count = s->dma_dac.total_bytes = 0;
- s->dma_dac.nextIn = s->dma_dac.nextOut =
- s->dma_dac.rawbuf;
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- synchronize_irq();
- s->dma_adc.count = s->dma_adc.total_bytes = 0;
- s->dma_adc.nextIn = s->dma_adc.nextOut =
- s->dma_adc.rawbuf;
- }
- return 0;
-
- case SNDCTL_DSP_SPEED:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val >= 0) {
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- set_adc_rate(s, val);
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- set_dac_rate(s, val);
- }
- if (s->open_mode & FMODE_READ)
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- if (s->open_mode & FMODE_WRITE)
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return put_user((file->f_mode & FMODE_READ) ?
- s->dma_adc.sample_rate :
- s->dma_dac.sample_rate,
- (int *)arg);
-
- case SNDCTL_DSP_STEREO:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.num_channels = val ? 2 : 1;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.num_channels = val ? 2 : 1;
- if (s->codec_ext_caps & AC97_EXT_DACS) {
- /* disable surround and center/lfe in AC'97
- */
- u16 ext_stat = rdcodec(s->codec,
- AC97_EXTENDED_STATUS);
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ext_stat | (AC97_EXTSTAT_PRI |
- AC97_EXTSTAT_PRJ |
- AC97_EXTSTAT_PRK));
- }
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return 0;
-
- case SNDCTL_DSP_CHANNELS:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != 0) {
- if (file->f_mode & FMODE_READ) {
- if (val < 0 || val > 2)
- return -EINVAL;
- stop_adc(s);
- s->dma_adc.num_channels = val;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- switch (val) {
- case 1:
- case 2:
- break;
- case 3:
- case 5:
- return -EINVAL;
- case 4:
- if (!(s->codec_ext_caps &
- AC97_EXTID_SDAC))
- return -EINVAL;
- break;
- case 6:
- if ((s->codec_ext_caps &
- AC97_EXT_DACS) != AC97_EXT_DACS)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
-
- stop_dac(s);
- if (val <= 2 &&
- (s->codec_ext_caps & AC97_EXT_DACS)) {
- /* disable surround and center/lfe
- * channels in AC'97
- */
- u16 ext_stat =
- rdcodec(s->codec,
- AC97_EXTENDED_STATUS);
- wrcodec(s->codec,
- AC97_EXTENDED_STATUS,
- ext_stat | (AC97_EXTSTAT_PRI |
- AC97_EXTSTAT_PRJ |
- AC97_EXTSTAT_PRK));
- } else if (val >= 4) {
- /* enable surround, center/lfe
- * channels in AC'97
- */
- u16 ext_stat =
- rdcodec(s->codec,
- AC97_EXTENDED_STATUS);
- ext_stat &= ~AC97_EXTSTAT_PRJ;
- if (val == 6)
- ext_stat &=
- ~(AC97_EXTSTAT_PRI |
- AC97_EXTSTAT_PRK);
- wrcodec(s->codec,
- AC97_EXTENDED_STATUS,
- ext_stat);
- }
-
- s->dma_dac.num_channels = val;
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- }
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_GETFMTS: /* Returns a mask */
- return put_user(AFMT_S16_LE | AFMT_U8, (int *) arg);
-
- case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != AFMT_QUERY) {
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- if (val == AFMT_S16_LE)
- s->dma_adc.sample_size = 16;
- else {
- val = AFMT_U8;
- s->dma_adc.sample_size = 8;
- }
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- if (val == AFMT_S16_LE)
- s->dma_dac.sample_size = 16;
- else {
- val = AFMT_U8;
- s->dma_dac.sample_size = 8;
- }
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- } else {
- if (file->f_mode & FMODE_READ)
- val = (s->dma_adc.sample_size == 16) ?
- AFMT_S16_LE : AFMT_U8;
- else
- val = (s->dma_dac.sample_size == 16) ?
- AFMT_S16_LE : AFMT_U8;
- }
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_POST:
- return 0;
-
- case SNDCTL_DSP_GETTRIGGER:
- val = 0;
- spin_lock_irqsave(&s->lock, flags);
- if (file->f_mode & FMODE_READ && !s->dma_adc.stopped)
- val |= PCM_ENABLE_INPUT;
- if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped)
- val |= PCM_ENABLE_OUTPUT;
- spin_unlock_irqrestore(&s->lock, flags);
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_SETTRIGGER:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- if (val & PCM_ENABLE_INPUT) {
- spin_lock_irqsave(&s->lock, flags);
- start_adc(s);
- spin_unlock_irqrestore(&s->lock, flags);
- } else
- stop_adc(s);
- }
- if (file->f_mode & FMODE_WRITE) {
- if (val & PCM_ENABLE_OUTPUT) {
- spin_lock_irqsave(&s->lock, flags);
- start_dac(s);
- spin_unlock_irqrestore(&s->lock, flags);
- } else
- stop_dac(s);
- }
- return 0;
-
- case SNDCTL_DSP_GETOSPACE:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- abinfo.fragsize = s->dma_dac.fragsize;
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_dac.count;
- count -= dma_count_done(&s->dma_dac);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- abinfo.bytes = (s->dma_dac.dmasize - count) /
- s->dma_dac.cnt_factor;
- abinfo.fragstotal = s->dma_dac.numfrag;
- abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;
- pr_debug("ioctl SNDCTL_DSP_GETOSPACE: bytes=%d, fragments=%d\n", abinfo.bytes, abinfo.fragments);
- return copy_to_user((void *) arg, &abinfo,
- sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_GETISPACE:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- abinfo.fragsize = s->dma_adc.fragsize;
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_adc.count;
- count += dma_count_done(&s->dma_adc);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- abinfo.bytes = count / s->dma_adc.cnt_factor;
- abinfo.fragstotal = s->dma_adc.numfrag;
- abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;
- return copy_to_user((void *) arg, &abinfo,
- sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_NONBLOCK:
- spin_lock(&file->f_lock);
- file->f_flags |= O_NONBLOCK;
- spin_unlock(&file->f_lock);
- return 0;
-
- case SNDCTL_DSP_GETODELAY:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_dac.count;
- count -= dma_count_done(&s->dma_dac);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- count /= s->dma_dac.cnt_factor;
- return put_user(count, (int *) arg);
-
- case SNDCTL_DSP_GETIPTR:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- spin_lock_irqsave(&s->lock, flags);
- cinfo.bytes = s->dma_adc.total_bytes;
- count = s->dma_adc.count;
- if (!s->dma_adc.stopped) {
- diff = dma_count_done(&s->dma_adc);
- count += diff;
- cinfo.bytes += diff;
- cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) + diff -
- virt_to_phys(s->dma_adc.rawbuf);
- } else
- cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) -
- virt_to_phys(s->dma_adc.rawbuf);
- if (s->dma_adc.mapped)
- s->dma_adc.count &= (s->dma_adc.dma_fragsize-1);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- cinfo.blocks = count >> s->dma_adc.fragshift;
- return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
-
- case SNDCTL_DSP_GETOPTR:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- spin_lock_irqsave(&s->lock, flags);
- cinfo.bytes = s->dma_dac.total_bytes;
- count = s->dma_dac.count;
- if (!s->dma_dac.stopped) {
- diff = dma_count_done(&s->dma_dac);
- count -= diff;
- cinfo.bytes += diff;
- cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) + diff -
- virt_to_phys(s->dma_dac.rawbuf);
- } else
- cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) -
- virt_to_phys(s->dma_dac.rawbuf);
- if (s->dma_dac.mapped)
- s->dma_dac.count &= (s->dma_dac.dma_fragsize-1);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- cinfo.blocks = count >> s->dma_dac.fragshift;
- return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
-
- case SNDCTL_DSP_GETBLKSIZE:
- if (file->f_mode & FMODE_WRITE)
- return put_user(s->dma_dac.fragsize, (int *) arg);
- else
- return put_user(s->dma_adc.fragsize, (int *) arg);
-
- case SNDCTL_DSP_SETFRAGMENT:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.ossfragshift = val & 0xffff;
- s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
- if (s->dma_adc.ossfragshift < 4)
- s->dma_adc.ossfragshift = 4;
- if (s->dma_adc.ossfragshift > 15)
- s->dma_adc.ossfragshift = 15;
- if (s->dma_adc.ossmaxfrags < 4)
- s->dma_adc.ossmaxfrags = 4;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.ossfragshift = val & 0xffff;
- s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
- if (s->dma_dac.ossfragshift < 4)
- s->dma_dac.ossfragshift = 4;
- if (s->dma_dac.ossfragshift > 15)
- s->dma_dac.ossfragshift = 15;
- if (s->dma_dac.ossmaxfrags < 4)
- s->dma_dac.ossmaxfrags = 4;
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return 0;
-
- case SNDCTL_DSP_SUBDIVIDE:
- if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
- (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
- return -EINVAL;
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != 1 && val != 2 && val != 4)
- return -EINVAL;
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.subdivision = val;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.subdivision = val;
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return 0;
-
- case SOUND_PCM_READ_RATE:
- return put_user((file->f_mode & FMODE_READ) ?
- s->dma_adc.sample_rate :
- s->dma_dac.sample_rate,
- (int *)arg);
-
- case SOUND_PCM_READ_CHANNELS:
- if (file->f_mode & FMODE_READ)
- return put_user(s->dma_adc.num_channels, (int *)arg);
- else
- return put_user(s->dma_dac.num_channels, (int *)arg);
-
- case SOUND_PCM_READ_BITS:
- if (file->f_mode & FMODE_READ)
- return put_user(s->dma_adc.sample_size, (int *)arg);
- else
- return put_user(s->dma_dac.sample_size, (int *)arg);
-
- case SOUND_PCM_WRITE_FILTER:
- case SNDCTL_DSP_SETSYNCRO:
- case SOUND_PCM_READ_FILTER:
- return -EINVAL;
- }
-
- return mixdev_ioctl(s->codec, cmd, arg);
-}
-
-static long
-au1550_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- int ret;
-
- mutex_lock(&au1550_ac97_mutex);
- ret = au1550_ioctl(file, cmd, arg);
- mutex_unlock(&au1550_ac97_mutex);
-
- return ret;
-}
-
-static int
-au1550_open(struct inode *inode, struct file *file)
-{
- int minor = MINOR(inode->i_rdev);
- DECLARE_WAITQUEUE(wait, current);
- struct au1550_state *s = &au1550_state;
- int ret;
-
-#ifdef DEBUG
- if (file->f_flags & O_NONBLOCK)
- pr_debug("open: non-blocking\n");
- else
- pr_debug("open: blocking\n");
-#endif
-
- file->private_data = s;
- mutex_lock(&au1550_ac97_mutex);
- /* wait for device to become free */
- mutex_lock(&s->open_mutex);
- while (s->open_mode & file->f_mode) {
- ret = -EBUSY;
- if (file->f_flags & O_NONBLOCK)
- goto out;
- add_wait_queue(&s->open_wait, &wait);
- __set_current_state(TASK_INTERRUPTIBLE);
- mutex_unlock(&s->open_mutex);
- schedule();
- remove_wait_queue(&s->open_wait, &wait);
- set_current_state(TASK_RUNNING);
- ret = -ERESTARTSYS;
- if (signal_pending(current))
- goto out2;
- mutex_lock(&s->open_mutex);
- }
-
- stop_dac(s);
- stop_adc(s);
-
- if (file->f_mode & FMODE_READ) {
- s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags =
- s->dma_adc.subdivision = s->dma_adc.total_bytes = 0;
- s->dma_adc.num_channels = 1;
- s->dma_adc.sample_size = 8;
- set_adc_rate(s, 8000);
- if ((minor & 0xf) == SND_DEV_DSP16)
- s->dma_adc.sample_size = 16;
- }
-
- if (file->f_mode & FMODE_WRITE) {
- s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags =
- s->dma_dac.subdivision = s->dma_dac.total_bytes = 0;
- s->dma_dac.num_channels = 1;
- s->dma_dac.sample_size = 8;
- set_dac_rate(s, 8000);
- if ((minor & 0xf) == SND_DEV_DSP16)
- s->dma_dac.sample_size = 16;
- }
-
- if (file->f_mode & FMODE_READ) {
- if ((ret = prog_dmabuf_adc(s)))
- goto out;
- }
- if (file->f_mode & FMODE_WRITE) {
- if ((ret = prog_dmabuf_dac(s)))
- goto out;
- }
-
- s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
- mutex_init(&s->sem);
- ret = 0;
-out:
- mutex_unlock(&s->open_mutex);
-out2:
- mutex_unlock(&au1550_ac97_mutex);
- return ret;
-}
-
-static int
-au1550_release(struct inode *inode, struct file *file)
-{
- struct au1550_state *s = file->private_data;
-
- mutex_lock(&au1550_ac97_mutex);
-
- if (file->f_mode & FMODE_WRITE) {
- mutex_unlock(&au1550_ac97_mutex);
- drain_dac(s, file->f_flags & O_NONBLOCK);
- mutex_lock(&au1550_ac97_mutex);
- }
-
- mutex_lock(&s->open_mutex);
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- kfree(s->dma_dac.rawbuf);
- s->dma_dac.rawbuf = NULL;
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- kfree(s->dma_adc.rawbuf);
- s->dma_adc.rawbuf = NULL;
- }
- s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE));
- mutex_unlock(&s->open_mutex);
- wake_up(&s->open_wait);
- mutex_unlock(&au1550_ac97_mutex);
- return 0;
-}
-
-static /*const */ struct file_operations au1550_audio_fops = {
- .owner = THIS_MODULE,
- .llseek = au1550_llseek,
- .read = au1550_read,
- .write = au1550_write,
- .poll = au1550_poll,
- .unlocked_ioctl = au1550_unlocked_ioctl,
- .mmap = au1550_mmap,
- .open = au1550_open,
- .release = au1550_release,
-};
-
-MODULE_AUTHOR("Advanced Micro Devices (AMD), dan@embeddededge.com");
-MODULE_DESCRIPTION("Au1550 AC97 Audio Driver");
-MODULE_LICENSE("GPL");
-
-
-static int __devinit
-au1550_probe(void)
-{
- struct au1550_state *s = &au1550_state;
- int val;
-
- memset(s, 0, sizeof(struct au1550_state));
-
- init_waitqueue_head(&s->dma_adc.wait);
- init_waitqueue_head(&s->dma_dac.wait);
- init_waitqueue_head(&s->open_wait);
- mutex_init(&s->open_mutex);
- spin_lock_init(&s->lock);
-
- s->codec = ac97_alloc_codec();
- if(s->codec == NULL) {
- err("Out of memory");
- return -1;
- }
- s->codec->private_data = s;
- s->codec->id = 0;
- s->codec->codec_read = rdcodec;
- s->codec->codec_write = wrcodec;
- s->codec->codec_wait = waitcodec;
-
- if (!request_mem_region(CPHYSADDR(AC97_PSC_SEL),
- 0x30, "Au1550 AC97")) {
- err("AC'97 ports in use");
- }
-
- /* Allocate the DMA Channels
- */
- if ((s->dma_dac.dmanr = au1xxx_dbdma_chan_alloc(DBDMA_MEM_CHAN,
- DBDMA_AC97_TX_CHAN, dac_dma_interrupt, (void *)s)) == 0) {
- err("Can't get DAC DMA");
- goto err_dma1;
- }
- au1xxx_dbdma_set_devwidth(s->dma_dac.dmanr, 16);
- if (au1xxx_dbdma_ring_alloc(s->dma_dac.dmanr,
- NUM_DBDMA_DESCRIPTORS) == 0) {
- err("Can't get DAC DMA descriptors");
- goto err_dma1;
- }
-
- if ((s->dma_adc.dmanr = au1xxx_dbdma_chan_alloc(DBDMA_AC97_RX_CHAN,
- DBDMA_MEM_CHAN, adc_dma_interrupt, (void *)s)) == 0) {
- err("Can't get ADC DMA");
- goto err_dma2;
- }
- au1xxx_dbdma_set_devwidth(s->dma_adc.dmanr, 16);
- if (au1xxx_dbdma_ring_alloc(s->dma_adc.dmanr,
- NUM_DBDMA_DESCRIPTORS) == 0) {
- err("Can't get ADC DMA descriptors");
- goto err_dma2;
- }
-
- pr_info("DAC: DMA%d, ADC: DMA%d", DBDMA_AC97_TX_CHAN, DBDMA_AC97_RX_CHAN);
-
- /* register devices */
-
- if ((s->dev_audio = register_sound_dsp(&au1550_audio_fops, -1)) < 0)
- goto err_dev1;
- if ((s->codec->dev_mixer =
- register_sound_mixer(&au1550_mixer_fops, -1)) < 0)
- goto err_dev2;
-
- /* The GPIO for the appropriate PSC was configured by the
- * board specific start up.
- *
- * configure PSC for AC'97
- */
- au_writel(0, AC97_PSC_CTRL); /* Disable PSC */
- au_sync();
- au_writel((PSC_SEL_CLK_SERCLK | PSC_SEL_PS_AC97MODE), AC97_PSC_SEL);
- au_sync();
-
- /* cold reset the AC'97
- */
- au_writel(PSC_AC97RST_RST, PSC_AC97RST);
- au_sync();
- au1550_delay(10);
- au_writel(0, PSC_AC97RST);
- au_sync();
-
- /* need to delay around 500msec(bleech) to give
- some CODECs enough time to wakeup */
- au1550_delay(500);
-
- /* warm reset the AC'97 to start the bitclk
- */
- au_writel(PSC_AC97RST_SNC, PSC_AC97RST);
- au_sync();
- udelay(100);
- au_writel(0, PSC_AC97RST);
- au_sync();
-
- /* Enable PSC
- */
- au_writel(PSC_CTRL_ENABLE, AC97_PSC_CTRL);
- au_sync();
-
- /* Wait for PSC ready.
- */
- do {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((val & PSC_AC97STAT_SR) == 0);
-
- /* Configure AC97 controller.
- * Deep FIFO, 16-bit sample, DMA, make sure DMA matches fifo size.
- */
- val = PSC_AC97CFG_SET_LEN(16);
- val |= PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8;
-
- /* Enable device so we can at least
- * talk over the AC-link.
- */
- au_writel(val, PSC_AC97CFG);
- au_writel(PSC_AC97MSK_ALLMASK, PSC_AC97MSK);
- au_sync();
- val |= PSC_AC97CFG_DE_ENABLE;
- au_writel(val, PSC_AC97CFG);
- au_sync();
-
- /* Wait for Device ready.
- */
- do {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((val & PSC_AC97STAT_DR) == 0);
-
- /* codec init */
- if (!ac97_probe_codec(s->codec))
- goto err_dev3;
-
- s->codec_base_caps = rdcodec(s->codec, AC97_RESET);
- s->codec_ext_caps = rdcodec(s->codec, AC97_EXTENDED_ID);
- pr_info("AC'97 Base/Extended ID = %04x/%04x",
- s->codec_base_caps, s->codec_ext_caps);
-
- if (!(s->codec_ext_caps & AC97_EXTID_VRA)) {
- /* codec does not support VRA
- */
- s->no_vra = 1;
- } else if (!vra) {
- /* Boot option says disable VRA
- */
- u16 ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ac97_extstat & ~AC97_EXTSTAT_VRA);
- s->no_vra = 1;
- }
- if (s->no_vra)
- pr_info("no VRA, interpolating and decimating");
-
- /* set mic to be the recording source */
- val = SOUND_MASK_MIC;
- mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_RECSRC,
- (unsigned long) &val);
-
- return 0;
-
- err_dev3:
- unregister_sound_mixer(s->codec->dev_mixer);
- err_dev2:
- unregister_sound_dsp(s->dev_audio);
- err_dev1:
- au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
- err_dma2:
- au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
- err_dma1:
- release_mem_region(CPHYSADDR(AC97_PSC_SEL), 0x30);
-
- ac97_release_codec(s->codec);
- return -1;
-}
-
-static void __devinit
-au1550_remove(void)
-{
- struct au1550_state *s = &au1550_state;
-
- if (!s)
- return;
- synchronize_irq();
- au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
- au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
- release_mem_region(CPHYSADDR(AC97_PSC_SEL), 0x30);
- unregister_sound_dsp(s->dev_audio);
- unregister_sound_mixer(s->codec->dev_mixer);
- ac97_release_codec(s->codec);
-}
-
-static int __init
-init_au1550(void)
-{
- return au1550_probe();
-}
-
-static void __exit
-cleanup_au1550(void)
-{
- au1550_remove();
-}
-
-module_init(init_au1550);
-module_exit(cleanup_au1550);
-
-#ifndef MODULE
-
-static int __init
-au1550_setup(char *options)
-{
- char *this_opt;
-
- if (!options || !*options)
- return 0;
-
- while ((this_opt = strsep(&options, ","))) {
- if (!*this_opt)
- continue;
- if (!strncmp(this_opt, "vra", 3)) {
- vra = 1;
- }
- }
-
- return 1;
-}
-
-__setup("au1550_audio=", au1550_setup);
-
-#endif /* MODULE */
If you say N the buttons will directly control the master volume.
It is recommended to say Y.
+config SND_ES1968_RADIO
+ bool "Enable TEA5757 radio tuner support for es1968"
+ depends on SND_ES1968
+ depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_ES1968
+ help
+ Say Y here to include support for TEA5757 radio tuner integrated on
+ some MediaForte cards (e.g. SF64-PCE2).
+
config SND_FM801
tristate "ForteMedia FM801"
select SND_OPL3_LIB
depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_FM801
help
Say Y here to include support for soundcards based on the ForteMedia
- FM801 chip with a TEA5757 tuner connected to GPIO1-3 pins (Media
- Forte SF256-PCS-02) into the snd-fm801 driver.
+ FM801 chip with a TEA5757 tuner (MediaForte SF256-PCS, SF256-PCP and
+ SF64-PCR) into the snd-fm801 driver.
-config SND_FM801_TEA575X
+config SND_TEA575X
tristate
- depends on SND_FM801_TEA575X_BOOL
- default SND_FM801
+ depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO
+ default SND_FM801 || SND_ES1968
source "sound/pci/hda/Kconfig"
To compile this driver as a module, choose M here: the module
will be called snd-korg1212.
+config SND_LOLA
+ tristate "Digigram Lola"
+ select SND_PCM
+ help
+ Say Y to include support for Digigram Lola boards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-lola.
+
config SND_LX6464ES
tristate "Digigram LX6464ES"
select SND_PCM
ca0106/ \
cs46xx/ \
cs5535audio/ \
+ lola/ \
lx6464es/ \
echoaudio/ \
emu10k1/ \
#include <sound/tlv.h>
#include <sound/hwdep.h>
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AudioScience inc. <support@audioscience.com>");
MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx");
+#if defined CONFIG_SND_DEBUG
+/* copied from pcm_lib.c, hope later patch will make that version public
+and this copy can be removed */
+static void pcm_debug_name(struct snd_pcm_substream *substream,
+ char *name, size_t len)
+{
+ snprintf(name, len, "pcmC%dD%d%c:%d",
+ substream->pcm->card->number,
+ substream->pcm->device,
+ substream->stream ? 'c' : 'p',
+ substream->number);
+}
+#define DEBUG_NAME(substream, name) char name[16]; pcm_debug_name(substream, name, sizeof(name))
+#else
+#define pcm_debug_name(s, n, l) do { } while (0)
+#define DEBUG_NAME(name, substream) do { } while (0)
+#endif
+
#if defined CONFIG_SND_DEBUG_VERBOSE
/**
* snd_printddd - very verbose debug printk
#define snd_printddd(format, args...) \
__snd_printk(3, __FILE__, __LINE__, format, ##args)
#else
-#define snd_printddd(format, args...) do { } while (0)
+#define snd_printddd(format, args...) do { } while (0)
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* index 0-MAX */
#define PERIOD_BYTES_MIN 2048
#define BUFFER_BYTES_MAX (512 * 1024)
-/* convert stream to character */
-#define SCHR(s) ((s == SNDRV_PCM_STREAM_PLAYBACK) ? 'P' : 'C')
-
-/*#define TIMER_MILLISECONDS 20
-#define FORCE_TIMER_JIFFIES ((TIMER_MILLISECONDS * HZ + 999)/1000)
-*/
-
#define MAX_CLOCKSOURCES (HPI_SAMPLECLOCK_SOURCE_LAST + 1 + 7)
struct clk_source {
u32 h_mixer;
struct clk_cache cc;
- u16 support_mmap;
+ u16 can_dma;
u16 support_grouping;
u16 support_mrx;
u16 update_interval_frames;
unsigned int pcm_buf_host_rw_ofs; /* Host R/W pos */
unsigned int pcm_buf_dma_ofs; /* DMA R/W offset in buffer */
unsigned int pcm_buf_elapsed_dma_ofs; /* DMA R/W offset in buffer */
+ unsigned int drained_count;
struct snd_pcm_substream *substream;
u32 h_stream;
struct hpi_format format;
#define hpi_handle_error(x) handle_error(x, __LINE__, __FILE__)
/***************************** GENERAL PCM ****************/
-static void print_hwparams(struct snd_pcm_hw_params *p)
+
+static void print_hwparams(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *p)
{
- snd_printd("HWPARAMS \n");
- snd_printd("samplerate %d \n", params_rate(p));
- snd_printd("Channels %d \n", params_channels(p));
- snd_printd("Format %d \n", params_format(p));
- snd_printd("subformat %d \n", params_subformat(p));
- snd_printd("Buffer bytes %d \n", params_buffer_bytes(p));
- snd_printd("Period bytes %d \n", params_period_bytes(p));
- snd_printd("access %d \n", params_access(p));
- snd_printd("period_size %d \n", params_period_size(p));
- snd_printd("periods %d \n", params_periods(p));
- snd_printd("buffer_size %d \n", params_buffer_size(p));
+ DEBUG_NAME(substream, name);
+ snd_printd("%s HWPARAMS\n", name);
+ snd_printd(" samplerate %d Hz\n", params_rate(p));
+ snd_printd(" channels %d\n", params_channels(p));
+ snd_printd(" format %d\n", params_format(p));
+ snd_printd(" subformat %d\n", params_subformat(p));
+ snd_printd(" buffer %d B\n", params_buffer_bytes(p));
+ snd_printd(" period %d B\n", params_period_bytes(p));
+ snd_printd(" access %d\n", params_access(p));
+ snd_printd(" period_size %d\n", params_period_size(p));
+ snd_printd(" periods %d\n", params_periods(p));
+ snd_printd(" buffer_size %d\n", params_buffer_size(p));
+ snd_printd(" %d B/s\n", params_rate(p) *
+ params_channels(p) *
+ snd_pcm_format_width(params_format(p)) / 8);
+
}
static snd_pcm_format_t hpi_to_alsa_formats[] = {
int width;
unsigned int bytes_per_sec;
- print_hwparams(params);
+ print_hwparams(substream, params);
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
if (err < 0)
return err;
if (err)
return err;
- snd_printdd("format %d, %d chans, %d_hz\n",
- format, params_channels(params),
- params_rate(params));
-
hpi_handle_error(hpi_format_create(&dpcm->format,
params_channels(params),
format, params_rate(params), 0, 0));
}
dpcm->hpi_buffer_attached = 0;
- if (card->support_mmap) {
-
+ if (card->can_dma) {
err = hpi_stream_host_buffer_attach(dpcm->h_stream,
params_buffer_bytes(params), runtime->dma_addr);
if (err == 0) {
dpcm->bytes_per_sec = bytes_per_sec;
dpcm->buffer_bytes = params_buffer_bytes(params);
dpcm->period_bytes = params_period_bytes(params);
- snd_printdd("buffer_bytes=%d, period_bytes=%d, bps=%d\n",
- dpcm->buffer_bytes, dpcm->period_bytes, bytes_per_sec);
return 0;
}
struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
struct snd_pcm_substream *s;
u16 e;
+ DEBUG_NAME(substream, name);
+
+ snd_printdd("%s trigger\n", name);
- snd_printdd("%c%d trigger\n",
- SCHR(substream->stream), substream->number);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
snd_pcm_group_for_each_entry(s, substream) {
if (substream->stream != s->stream)
continue;
- if ((s->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
- (card->support_mmap)) {
+ ds->drained_count = 0;
+ if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* How do I know how much valid data is present
* in buffer? Must be at least one period!
* Guessing 2 periods, but if
}
if (card->support_grouping) {
- snd_printdd("\t%c%d group\n",
- SCHR(s->stream),
- s->number);
+ snd_printdd("%d group\n", s->number);
e = hpi_stream_group_add(
dpcm->h_stream,
ds->h_stream);
/* start the master stream */
snd_card_asihpi_pcm_timer_start(substream);
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ||
- !card->support_mmap)
+ !card->can_dma)
hpi_handle_error(hpi_stream_start(dpcm->h_stream));
break;
s->runtime->status->state = SNDRV_PCM_STATE_SETUP;
if (card->support_grouping) {
- snd_printdd("\t%c%d group\n",
- SCHR(s->stream),
- s->number);
+ snd_printdd("%d group\n", s->number);
snd_pcm_trigger_done(s, substream);
} else
break;
int loops = 0;
u16 state;
u32 buffer_size, bytes_avail, samples_played, on_card_bytes;
+ DEBUG_NAME(substream, name);
- snd_printdd("%c%d snd_card_asihpi_timer_function\n",
- SCHR(substream->stream), substream->number);
+ snd_printdd("%s snd_card_asihpi_timer_function\n", name);
/* find minimum newdata and buffer pos in group */
snd_pcm_group_for_each_entry(s, substream) {
/* number of bytes in on-card buffer */
runtime->delay = on_card_bytes;
+ if (!card->can_dma)
+ on_card_bytes = bytes_avail;
+
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
pcm_buf_dma_ofs = ds->pcm_buf_host_rw_ofs - bytes_avail;
if (state == HPI_STATE_STOPPED) {
(on_card_bytes < ds->pcm_buf_host_rw_ofs)) {
hpi_handle_error(hpi_stream_start(ds->h_stream));
snd_printdd("P%d start\n", s->number);
+ ds->drained_count = 0;
}
} else if (state == HPI_STATE_DRAINED) {
snd_printd(KERN_WARNING "P%d drained\n",
s->number);
- /*snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN);
- continue; */
+ ds->drained_count++;
+ if (ds->drained_count > 2) {
+ snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN);
+ continue;
+ }
+ } else {
+ ds->drained_count = 0;
}
} else
pcm_buf_dma_ofs = bytes_avail + ds->pcm_buf_host_rw_ofs;
newdata);
}
- snd_printdd("hw_ptr x%04lX, appl_ptr x%04lX\n",
+ snd_printdd("hw_ptr 0x%04lX, appl_ptr 0x%04lX\n",
(unsigned long)frames_to_bytes(runtime,
runtime->status->hw_ptr),
(unsigned long)frames_to_bytes(runtime,
runtime->control->appl_ptr));
- snd_printdd("%d %c%d S=%d, rw=%04X, dma=x%04X, left=x%04X,"
- " aux=x%04X space=x%04X\n",
- loops, SCHR(s->stream), s->number,
- state, ds->pcm_buf_host_rw_ofs, pcm_buf_dma_ofs, (int)bytes_avail,
+ snd_printdd("%d S=%d, "
+ "rw=0x%04X, dma=0x%04X, left=0x%04X, "
+ "aux=0x%04X space=0x%04X\n",
+ s->number, state,
+ ds->pcm_buf_host_rw_ofs, pcm_buf_dma_ofs,
+ (int)bytes_avail,
(int)on_card_bytes, buffer_size-bytes_avail);
loops++;
}
next_jiffies = max(next_jiffies, 1U);
dpcm->timer.expires = jiffies + next_jiffies;
- snd_printdd("jif %d buf pos x%04X newdata x%04X xfer x%04X\n",
+ snd_printdd("jif %d buf pos 0x%04X newdata 0x%04X xfer 0x%04X\n",
next_jiffies, pcm_buf_dma_ofs, newdata, xfercount);
snd_pcm_group_for_each_entry(s, substream) {
ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs;
- if (xfercount && (on_card_bytes <= ds->period_bytes)) {
- if (card->support_mmap) {
- if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- snd_printddd("P%d write x%04x\n",
+ if (xfercount &&
+ /* Limit use of on card fifo for playback */
+ ((on_card_bytes <= ds->period_bytes) ||
+ (s->stream == SNDRV_PCM_STREAM_CAPTURE)))
+
+ {
+
+ unsigned int buf_ofs = ds->pcm_buf_host_rw_ofs % ds->buffer_bytes;
+ unsigned int xfer1, xfer2;
+ char *pd = &s->runtime->dma_area[buf_ofs];
+
+ if (card->can_dma) { /* buffer wrap is handled at lower level */
+ xfer1 = xfercount;
+ xfer2 = 0;
+ } else {
+ xfer1 = min(xfercount, ds->buffer_bytes - buf_ofs);
+ xfer2 = xfercount - xfer1;
+ }
+
+ if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ snd_printddd("P%d write1 0x%04X 0x%04X\n",
+ s->number, xfer1, buf_ofs);
+ hpi_handle_error(
+ hpi_outstream_write_buf(
+ ds->h_stream, pd, xfer1,
+ &ds->format));
+
+ if (xfer2) {
+ pd = s->runtime->dma_area;
+
+ snd_printddd("P%d write2 0x%04X 0x%04X\n",
s->number,
- ds->period_bytes);
+ xfercount - xfer1, buf_ofs);
hpi_handle_error(
hpi_outstream_write_buf(
- ds->h_stream,
- &s->runtime->
- dma_area[0],
- xfercount,
+ ds->h_stream, pd,
+ xfercount - xfer1,
&ds->format));
- } else {
- snd_printddd("C%d read x%04x\n",
- s->number,
- xfercount);
+ }
+ } else {
+ snd_printddd("C%d read1 0x%04x\n",
+ s->number, xfer1);
+ hpi_handle_error(
+ hpi_instream_read_buf(
+ ds->h_stream,
+ pd, xfer1));
+ if (xfer2) {
+ pd = s->runtime->dma_area;
+ snd_printddd("C%d read2 0x%04x\n",
+ s->number, xfer2);
hpi_handle_error(
hpi_instream_read_buf(
ds->h_stream,
- NULL, xfercount));
+ pd, xfer2));
}
- ds->pcm_buf_host_rw_ofs = ds->pcm_buf_host_rw_ofs + xfercount;
- } /* else R/W will be handled by read/write callbacks */
+ }
+ ds->pcm_buf_host_rw_ofs = ds->pcm_buf_host_rw_ofs + xfercount;
ds->pcm_buf_elapsed_dma_ofs = pcm_buf_dma_ofs;
snd_pcm_period_elapsed(s);
}
static int snd_card_asihpi_playback_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
- snd_printdd(KERN_INFO "Playback ioctl %d\n", cmd);
+ snd_printddd(KERN_INFO "P%d ioctl %d\n", substream->number, cmd);
return snd_pcm_lib_ioctl(substream, cmd, arg);
}
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- snd_printdd("playback prepare %d\n", substream->number);
+ snd_printdd("P%d prepare\n", substream->number);
hpi_handle_error(hpi_outstream_reset(dpcm->h_stream));
dpcm->pcm_buf_host_rw_ofs = 0;
snd_pcm_uframes_t ptr;
ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes);
- snd_printddd("playback_pointer=x%04lx\n", (unsigned long)ptr);
+ snd_printddd("P%d pointer = 0x%04lx\n", substream->number, (unsigned long)ptr);
return ptr;
}
SNDRV_PCM_INFO_DOUBLE |
SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_PAUSE;
-
- if (card->support_mmap)
- snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID;
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID;
if (card->support_grouping)
snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_SYNC_START;
/* struct is copied, so can create initializer dynamically */
runtime->hw = snd_card_asihpi_playback;
- if (card->support_mmap)
+ if (card->can_dma)
err = snd_pcm_hw_constraint_pow2(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
if (err < 0)
return 0;
}
-static int snd_card_asihpi_playback_copy(struct snd_pcm_substream *substream,
- int channel,
- snd_pcm_uframes_t pos,
- void __user *src,
- snd_pcm_uframes_t count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- unsigned int len;
-
- len = frames_to_bytes(runtime, count);
-
- if (copy_from_user(runtime->dma_area, src, len))
- return -EFAULT;
-
- snd_printddd("playback copy%d %u bytes\n",
- substream->number, len);
-
- hpi_handle_error(hpi_outstream_write_buf(dpcm->h_stream,
- runtime->dma_area, len, &dpcm->format));
-
- dpcm->pcm_buf_host_rw_ofs += len;
-
- return 0;
-}
-
-static int snd_card_asihpi_playback_silence(struct snd_pcm_substream *
- substream, int channel,
- snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
-{
- /* Usually writes silence to DMA buffer, which should be overwritten
- by real audio later. Our fifos cannot be overwritten, and are not
- free-running DMAs. Silence is output on fifo underflow.
- This callback is still required to allow the copy callback to be used.
- */
- return 0;
-}
-
-static struct snd_pcm_ops snd_card_asihpi_playback_ops = {
- .open = snd_card_asihpi_playback_open,
- .close = snd_card_asihpi_playback_close,
- .ioctl = snd_card_asihpi_playback_ioctl,
- .hw_params = snd_card_asihpi_pcm_hw_params,
- .hw_free = snd_card_asihpi_hw_free,
- .prepare = snd_card_asihpi_playback_prepare,
- .trigger = snd_card_asihpi_trigger,
- .pointer = snd_card_asihpi_playback_pointer,
- .copy = snd_card_asihpi_playback_copy,
- .silence = snd_card_asihpi_playback_silence,
-};
-
static struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = {
.open = snd_card_asihpi_playback_open,
.close = snd_card_asihpi_playback_close,
snd_card_asihpi_capture_format(card, dpcm->h_stream,
&snd_card_asihpi_capture);
snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_capture);
- snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED;
-
- if (card->support_mmap)
- snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID;
+ snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID;
if (card->support_grouping)
snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_SYNC_START;
runtime->hw = snd_card_asihpi_capture;
- if (card->support_mmap)
+ if (card->can_dma)
err = snd_pcm_hw_constraint_pow2(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
if (err < 0)
return 0;
}
-static int snd_card_asihpi_capture_copy(struct snd_pcm_substream *substream,
- int channel, snd_pcm_uframes_t pos,
- void __user *dst, snd_pcm_uframes_t count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- u32 len;
-
- len = frames_to_bytes(runtime, count);
-
- snd_printddd("capture copy%d %d bytes\n", substream->number, len);
- hpi_handle_error(hpi_instream_read_buf(dpcm->h_stream,
- runtime->dma_area, len));
-
- dpcm->pcm_buf_host_rw_ofs = dpcm->pcm_buf_host_rw_ofs + len;
-
- if (copy_to_user(dst, runtime->dma_area, len))
- return -EFAULT;
-
- return 0;
-}
-
static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
.open = snd_card_asihpi_capture_open,
.close = snd_card_asihpi_capture_close,
.pointer = snd_card_asihpi_capture_pointer,
};
-static struct snd_pcm_ops snd_card_asihpi_capture_ops = {
- .open = snd_card_asihpi_capture_open,
- .close = snd_card_asihpi_capture_close,
- .ioctl = snd_card_asihpi_capture_ioctl,
- .hw_params = snd_card_asihpi_pcm_hw_params,
- .hw_free = snd_card_asihpi_hw_free,
- .prepare = snd_card_asihpi_capture_prepare,
- .trigger = snd_card_asihpi_trigger,
- .pointer = snd_card_asihpi_capture_pointer,
- .copy = snd_card_asihpi_capture_copy
-};
-
static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi,
int device, int substreams)
{
if (err < 0)
return err;
/* pointer to ops struct is stored, dont change ops afterwards! */
- if (asihpi->support_mmap) {
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_card_asihpi_playback_mmap_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_card_asihpi_capture_mmap_ops);
- } else {
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- &snd_card_asihpi_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &snd_card_asihpi_capture_ops);
- }
pcm->private_data = asihpi;
pcm->info_flags = 0;
struct hpi_control *hpi_ctl,
char *name)
{
- char *dir = "";
+ char *dir;
memset(snd_control, 0, sizeof(*snd_control));
snd_control->name = hpi_ctl->name;
snd_control->private_value = hpi_ctl->h_control;
snd_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
snd_control->index = 0;
- if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM)
+ if (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE == HPI_SOURCENODE_CLOCK_SOURCE)
+ dir = ""; /* clock is neither capture nor playback */
+ else if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM)
dir = "Capture "; /* On or towards a PCM capture destination*/
else if ((hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) &&
(!hpi_ctl->dst_node_type))
dir = "Playback "; /* PCM Playback source, or output node */
if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type)
- sprintf(hpi_ctl->name, "%s%d %s%d %s%s",
+ sprintf(hpi_ctl->name, "%s %d %s %d %s%s",
asihpi_src_names[hpi_ctl->src_node_type],
hpi_ctl->src_node_index,
asihpi_dst_names[hpi_ctl->dst_node_type],
if (err)
asihpi->update_interval_frames = 512;
- if (!asihpi->support_mmap)
+ if (!asihpi->can_dma)
asihpi->update_interval_frames *= 2;
hpi_handle_error(hpi_instream_open(asihpi->adapter_index,
0, &h_stream));
err = hpi_instream_host_buffer_free(h_stream);
- asihpi->support_mmap = (!err);
+ asihpi->can_dma = (!err);
hpi_handle_error(hpi_instream_close(h_stream));
asihpi->out_max_chans = 2;
}
- snd_printk(KERN_INFO "supports mmap:%d grouping:%d mrx:%d\n",
- asihpi->support_mmap,
+ snd_printk(KERN_INFO "has dma:%d, grouping:%d, mrx:%d\n",
+ asihpi->can_dma,
asihpi->support_grouping,
asihpi->support_mrx
);
by enable_hwdep module param*/
snd_asihpi_hpi_new(asihpi, 0, NULL);
- if (asihpi->support_mmap)
- strcpy(card->driver, "ASIHPI-MMAP");
- else
- strcpy(card->driver, "ASIHPI");
+ strcpy(card->driver, "ASIHPI");
sprintf(card->shortname, "AudioScience ASI%4X", asihpi->type);
sprintf(card->longname, "%s %i",
static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_response *phr);
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr);
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr);
static void adapter_get_asserts(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
case HPI_SUBSYS_CREATE_ADAPTER:
subsys_create_adapter(phm, phr);
break;
- case HPI_SUBSYS_DELETE_ADAPTER:
- subsys_delete_adapter(phm, phr);
- break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
adapter_get_asserts(pao, phm, phr);
break;
+ case HPI_ADAPTER_DELETE:
+ adapter_delete(pao, phm, phr);
+ break;
+
default:
hw_message(pao, phm, phr);
break;
{
struct hpi_adapter_obj *pao = NULL;
- /* subsytem messages get executed by every HPI. */
- /* All other messages are ignored unless the adapter index matches */
- /* an adapter in the HPI */
- /*HPI_DEBUG_LOG(DEBUG, "O %d,F %x\n", phm->wObject, phm->wFunction); */
-
- /* if Dsp has crashed then do not communicate with it any more */
if (phm->object != HPI_OBJ_SUBSYSTEM) {
pao = hpi_find_adapter(phm->adapter_index);
if (!pao) {
- HPI_DEBUG_LOG(DEBUG,
- " %d,%d refused, for another HPI?\n",
- phm->object, phm->function);
+ hpi_init_response(phr, phm->object, phm->function,
+ HPI_ERROR_BAD_ADAPTER_NUMBER);
+ HPI_DEBUG_LOG(DEBUG, "invalid adapter index: %d \n",
+ phm->adapter_index);
return;
}
+ /* Don't even try to communicate with crashed DSP */
if (pao->dsp_crashed >= 10) {
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_DSP_HARDWARE);
- HPI_DEBUG_LOG(DEBUG, " %d,%d dsp crashed.\n",
- phm->object, phm->function);
+ HPI_DEBUG_LOG(DEBUG, "adapter %d dsp crashed\n",
+ phm->adapter_index);
return;
}
}
phr->error = 0;
}
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr)
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr)
{
- struct hpi_adapter_obj *pao = NULL;
-
- pao = hpi_find_adapter(phm->obj_index);
- if (!pao)
- return;
-
delete_adapter_obj(pao);
hpi_delete_adapter(pao);
phr->error = 0;
static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_response *phr);
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr);
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr);
static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
u32 *pos_error_code);
/*****************************************************************************/
-static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
+static void subsys_message(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_SUBSYS_CREATE_ADAPTER:
subsys_create_adapter(phm, phr);
break;
- case HPI_SUBSYS_DELETE_ADAPTER:
- subsys_delete_adapter(phm, phr);
- break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
+ case HPI_ADAPTER_DELETE:
+ adapter_delete(pao, phm, phr);
+ break;
+
default:
hw_message(pao, phm, phr);
break;
/** Entry point to this HPI backend
* All calls to the HPI start here
*/
-void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
+void _HPI_6205(struct hpi_adapter_obj *pao, struct hpi_message *phm,
+ struct hpi_response *phr)
{
- struct hpi_adapter_obj *pao = NULL;
-
- /* subsytem messages are processed by every HPI.
- * All other messages are ignored unless the adapter index matches
- * an adapter in the HPI
- */
- /* HPI_DEBUG_LOG(DEBUG, "HPI Obj=%d, Func=%d\n", phm->wObject,
- phm->wFunction); */
-
- /* if Dsp has crashed then do not communicate with it any more */
- if (phm->object != HPI_OBJ_SUBSYSTEM) {
- pao = hpi_find_adapter(phm->adapter_index);
- if (!pao) {
- HPI_DEBUG_LOG(DEBUG,
- " %d,%d refused, for another HPI?\n",
- phm->object, phm->function);
- return;
- }
-
- if ((pao->dsp_crashed >= 10)
- && (phm->function != HPI_ADAPTER_DEBUG_READ)) {
- /* allow last resort debug read even after crash */
- hpi_init_response(phr, phm->object, phm->function,
- HPI_ERROR_DSP_HARDWARE);
- HPI_DEBUG_LOG(WARNING, " %d,%d dsp crashed.\n",
- phm->object, phm->function);
- return;
- }
+ if (pao && (pao->dsp_crashed >= 10)
+ && (phm->function != HPI_ADAPTER_DEBUG_READ)) {
+ /* allow last resort debug read even after crash */
+ hpi_init_response(phr, phm->object, phm->function,
+ HPI_ERROR_DSP_HARDWARE);
+ HPI_DEBUG_LOG(WARNING, " %d,%d dsp crashed.\n", phm->object,
+ phm->function);
+ return;
}
/* Init default response */
case HPI_TYPE_MESSAGE:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
- subsys_message(phm, phr);
+ subsys_message(pao, phm, phr);
break;
case HPI_OBJ_ADAPTER:
}
}
+void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
+{
+ struct hpi_adapter_obj *pao = NULL;
+
+ if (phm->object != HPI_OBJ_SUBSYSTEM) {
+ /* normal messages must have valid adapter index */
+ pao = hpi_find_adapter(phm->adapter_index);
+ } else {
+ /* subsys messages don't address an adapter */
+ _HPI_6205(NULL, phm, phr);
+ return;
+ }
+
+ if (pao)
+ _HPI_6205(pao, phm, phr);
+ else
+ hpi_init_response(phr, phm->object, phm->function,
+ HPI_ERROR_BAD_ADAPTER_NUMBER);
+}
+
/*****************************************************************************/
/* SUBSYSTEM */
}
/** delete an adapter - required by WDM driver */
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr)
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr)
{
- struct hpi_adapter_obj *pao;
struct hpi_hw_obj *phw;
- pao = hpi_find_adapter(phm->obj_index);
if (!pao) {
phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
return;
}
err = adapter_boot_load_dsp(pao, pos_error_code);
- if (err)
+ if (err) {
+ HPI_DEBUG_LOG(ERROR, "DSP code load failed\n");
/* no need to clean up as SubSysCreateAdapter */
/* calls DeleteAdapter on error. */
return err;
-
+ }
HPI_DEBUG_LOG(INFO, "load DSP code OK\n");
/* allow boot load even if mem alloc wont work */
control_cache.number_of_controls,
interface->control_cache.size_in_bytes,
p_control_cache_virtual);
+
if (!phw->p_cache)
err = HPI_ERROR_MEMORY_ALLOC;
}
}
/** Free memory areas allocated by adapter
- * this routine is called from SubSysDeleteAdapter,
+ * this routine is called from AdapterDelete,
* and SubSysCreateAdapter if duplicate index
*/
static void delete_adapter_obj(struct hpi_adapter_obj *pao)
{
- struct hpi_hw_obj *phw;
+ struct hpi_hw_obj *phw = pao->priv;
int i;
- phw = pao->priv;
-
if (hpios_locked_mem_valid(&phw->h_control_cache)) {
hpios_locked_mem_free(&phw->h_control_cache);
hpi_free_control_cache(phw->p_cache);
case HPI_ADAPTER_FAMILY_ASI(0x6300):
boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6400);
break;
+ case HPI_ADAPTER_FAMILY_ASI(0x5500):
case HPI_ADAPTER_FAMILY_ASI(0x5600):
case HPI_ADAPTER_FAMILY_ASI(0x6500):
boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6600);
static void send_dsp_command(struct hpi_hw_obj *phw, int cmd)
{
struct bus_master_interface *interface = phw->p_interface_buffer;
-
u32 r;
interface->host_cmd = cmd;
/* These defines are used to fill in protocol information for an Ethernet packet
sent using HMI on CS18102 */
-/** ID supplied by Cirrius for ASI packets. */
+/** ID supplied by Cirrus for ASI packets. */
#define HPI_ETHERNET_PACKET_ID 0x85
/** Simple packet - no special routing required */
#define HPI_ETHERNET_PACKET_V1 0x01
/** This packet must make its way to the host across the HPI interface */
#define HPI_ETHERNET_PACKET_HOSTED_VIA_HPI_V1 0x41
-#define HPI_ETHERNET_UDP_PORT (44600) /*!< UDP messaging port */
+#define HPI_ETHERNET_UDP_PORT 44600 /**< HPI UDP service */
/** Default network timeout in milli-seconds. */
#define HPI_ETHERNET_TIMEOUT_MS 500
HPI_SUBSYS_OPEN = HPI_FUNC_ID(SUBSYSTEM, 1),
HPI_SUBSYS_GET_VERSION = HPI_FUNC_ID(SUBSYSTEM, 2),
HPI_SUBSYS_GET_INFO = HPI_FUNC_ID(SUBSYSTEM, 3),
- HPI_SUBSYS_FIND_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 4),
+ /* HPI_SUBSYS_FIND_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 4), */
HPI_SUBSYS_CREATE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 5),
HPI_SUBSYS_CLOSE = HPI_FUNC_ID(SUBSYSTEM, 6),
- HPI_SUBSYS_DELETE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 7),
+ /* HPI_SUBSYS_DELETE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 7), */
HPI_SUBSYS_DRIVER_LOAD = HPI_FUNC_ID(SUBSYSTEM, 8),
HPI_SUBSYS_DRIVER_UNLOAD = HPI_FUNC_ID(SUBSYSTEM, 9),
- HPI_SUBSYS_READ_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 10),
- HPI_SUBSYS_WRITE_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 11),
+ /* HPI_SUBSYS_READ_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 10), */
+ /* HPI_SUBSYS_WRITE_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 11), */
HPI_SUBSYS_GET_NUM_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 12),
HPI_SUBSYS_GET_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 13),
HPI_SUBSYS_SET_NETWORK_INTERFACE = HPI_FUNC_ID(SUBSYSTEM, 14),
HPI_ADAPTER_DEBUG_READ = HPI_FUNC_ID(ADAPTER, 18),
HPI_ADAPTER_IRQ_QUERY_AND_CLEAR = HPI_FUNC_ID(ADAPTER, 19),
HPI_ADAPTER_IRQ_CALLBACK = HPI_FUNC_ID(ADAPTER, 20),
-#define HPI_ADAPTER_FUNCTION_COUNT 20
+ HPI_ADAPTER_DELETE = HPI_FUNC_ID(ADAPTER, 21),
+#define HPI_ADAPTER_FUNCTION_COUNT 21
HPI_OSTREAM_OPEN = HPI_FUNC_ID(OSTREAM, 1),
HPI_OSTREAM_CLOSE = HPI_FUNC_ID(OSTREAM, 2),
u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource,
u16 *pw_adapter_index);
-u16 hpi_subsys_delete_adapter(u16 adapter_index);
-
u16 hpi_outstream_host_buffer_get_info(u32 h_outstream, u8 **pp_buffer,
struct hpi_hostbuffer_status **pp_status);
/*////////////////////////////////////////////////////////////////////////// */
/* declarations for individual HPI entry points */
-hpi_handler_func HPI_1000;
hpi_handler_func HPI_6000;
hpi_handler_func HPI_6205;
-hpi_handler_func HPI_COMMON;
#endif /* _HPI_INTERNAL_H_ */
if (info->control_type) {
pC->p_info[info->control_index] = info;
cached++;
- } else /* dummy cache entry */
+ } else { /* dummy cache entry */
pC->p_info[info->control_index] = NULL;
+ }
byte_count += info->size_in32bit_words * 4;
unsigned int field_size;
};
-static struct pad_ofs_size pad_desc[] = {
+static const struct pad_ofs_size pad_desc[] = {
HPICMN_PAD_OFS_AND_SIZE(c_channel), /* HPI_PAD_CHANNEL_NAME */
HPICMN_PAD_OFS_AND_SIZE(c_artist), /* HPI_PAD_ARTIST */
HPICMN_PAD_OFS_AND_SIZE(c_title), /* HPI_PAD_TITLE */
}
}
+/** Allocate control cache.
+
+\return Cache pointer, or NULL if allocation fails.
+*/
struct hpi_control_cache *hpi_alloc_control_cache(const u32 control_count,
const u32 size_in_bytes, u8 *p_dsp_control_buffer)
{
phr->u.s.num_adapters = adapters.gw_num_adapters;
break;
case HPI_SUBSYS_CREATE_ADAPTER:
- case HPI_SUBSYS_DELETE_ADAPTER:
break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
struct hpi_message *phm, struct hpi_response *phr);
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr);
+
+hpi_handler_func HPI_COMMON;
return hr.error;
}
-u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource,
- u16 *pw_adapter_index)
-{
- struct hpi_message hm;
- struct hpi_response hr;
-
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_CREATE_ADAPTER);
- hm.u.s.resource = *p_resource;
-
- hpi_send_recv(&hm, &hr);
-
- *pw_adapter_index = hr.u.s.adapter_index;
- return hr.error;
-}
-
-u16 hpi_subsys_delete_adapter(u16 adapter_index)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_DELETE_ADAPTER);
- hm.obj_index = adapter_index;
- hpi_send_recv(&hm, &hr);
- return hr.error;
-}
-
u16 hpi_subsys_get_num_adapters(int *pn_num_adapters)
{
struct hpi_message hm;
HPIMSGX__init(phm, phr);
break;
- case HPI_SUBSYS_DELETE_ADAPTER:
- HPIMSGX__cleanup(phm->obj_index, h_owner);
- {
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_CLOSE);
- hm.adapter_index = phm->obj_index;
- hw_entry_point(&hm, &hr);
- }
- if ((phm->obj_index < HPI_MAX_ADAPTERS)
- && hpi_entry_points[phm->obj_index]) {
- hpi_entry_points[phm->obj_index] (phm, phr);
- hpi_entry_points[phm->obj_index] = NULL;
- } else
- phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
-
- break;
default:
/* Must explicitly handle every subsys message in this switch */
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function,
case HPI_ADAPTER_CLOSE:
adapter_close(phm, phr);
break;
+ case HPI_ADAPTER_DELETE:
+ HPIMSGX__cleanup(phm->adapter_index, h_owner);
+ {
+ struct hpi_message hm;
+ struct hpi_response hr;
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_CLOSE);
+ hm.adapter_index = phm->adapter_index;
+ hw_entry_point(&hm, &hr);
+ }
+ hw_entry_point(phm, phr);
+ break;
+
default:
hw_entry_point(phm, phr);
break;
#include "hpidebug.h"
#include "hpimsgx.h"
#include "hpioctl.h"
+#include "hpicmn.h"
#include <linux/fs.h>
#include <linux/slab.h>
goto out;
}
- pa = &adapters[hm->h.adapter_index];
+ switch (hm->h.function) {
+ case HPI_SUBSYS_CREATE_ADAPTER:
+ case HPI_ADAPTER_DELETE:
+ /* Application must not use these functions! */
+ hr->h.size = sizeof(hr->h);
+ hr->h.error = HPI_ERROR_INVALID_OPERATION;
+ hr->h.function = hm->h.function;
+ uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
+ if (uncopied_bytes)
+ err = -EFAULT;
+ else
+ err = 0;
+ goto out;
+ }
+
hr->h.size = res_max_size;
if (hm->h.object == HPI_OBJ_SUBSYSTEM) {
- switch (hm->h.function) {
- case HPI_SUBSYS_CREATE_ADAPTER:
- case HPI_SUBSYS_DELETE_ADAPTER:
- /* Application must not use these functions! */
- hr->h.size = sizeof(hr->h);
- hr->h.error = HPI_ERROR_INVALID_OPERATION;
- hr->h.function = hm->h.function;
- uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
- if (uncopied_bytes)
- err = -EFAULT;
- else
- err = 0;
- goto out;
-
- default:
- hpi_send_recv_f(&hm->m0, &hr->r0, file);
- }
+ hpi_send_recv_f(&hm->m0, &hr->r0, file);
} else {
u16 __user *ptr = NULL;
u32 size = 0;
/* -1=no data 0=read from user mem, 1=write to user mem */
int wrflag = -1;
u32 adapter = hm->h.adapter_index;
+ pa = &adapters[adapter];
- if ((hm->h.adapter_index > HPI_MAX_ADAPTERS) || (!pa->type)) {
+ if ((adapter > HPI_MAX_ADAPTERS) || (!pa->type)) {
hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER,
HPI_ADAPTER_OPEN,
HPI_ERROR_BAD_ADAPTER_NUMBER);
int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
- int err, idx, nm;
+ int idx, nm;
unsigned int memlen;
struct hpi_message hm;
struct hpi_response hr;
nm = HPI_MAX_ADAPTER_MEM_SPACES;
for (idx = 0; idx < nm; idx++) {
- HPI_DEBUG_LOG(INFO, "resource %d %s %08llx-%08llx %04llx\n",
- idx, pci_dev->resource[idx].name,
- (unsigned long long)pci_resource_start(pci_dev, idx),
- (unsigned long long)pci_resource_end(pci_dev, idx),
- (unsigned long long)pci_resource_flags(pci_dev, idx));
+ HPI_DEBUG_LOG(INFO, "resource %d %pR\n", idx,
+ &pci_dev->resource[idx]);
if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) {
memlen = pci_resource_len(pci_dev, idx);
adapter.index = hr.u.s.adapter_index;
adapter.type = hr.u.s.adapter_type;
+
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_OPEN);
hm.adapter_index = adapter.index;
+ hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
- err = hpi_adapter_open(adapter.index);
- if (err)
+ if (hr.error)
goto err;
adapter.snd_card_asihpi = NULL;
/* WARNING can't init mutex in 'adapter'
* and then copy it to adapters[] ?!?!
*/
- adapters[hr.u.s.adapter_index] = adapter;
+ adapters[adapter.index] = adapter;
mutex_init(&adapters[adapter.index].mutex);
pci_set_drvdata(pci_dev, &adapters[adapter.index]);
struct hpi_adapter *pa;
pa = pci_get_drvdata(pci_dev);
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_DELETE_ADAPTER);
- hm.obj_index = pa->index;
- hm.adapter_index = HPI_ADAPTER_INDEX_INVALID;
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_DELETE);
+ hm.adapter_index = pa->index;
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
/* unmap PCI memory space, mapped during device init. */
#define CHIP_AU8810
-#define CARD_NAME "Aureal Advantage 3D Sound Processor"
+#define CARD_NAME "Aureal Advantage"
#define CARD_NAME_SHORT "au8810"
#define NR_ADB 0x10
#define CHIP_AU8820
-#define CARD_NAME "Aureal Vortex 3D Sound Processor"
+#define CARD_NAME "Aureal Vortex"
#define CARD_NAME_SHORT "au8820"
/* Number of ADB and WT channels */
#define CHIP_AU8830
-#define CARD_NAME "Aureal Vortex 2 3D Sound Processor"
+#define CARD_NAME "Aureal Vortex 2"
#define CARD_NAME_SHORT "au8830"
#define NR_ADB 0x20
*/
static char *vortex_pcm_prettyname[VORTEX_PCM_LAST] = {
- "AU88x0 ADB",
- "AU88x0 SPDIF",
- "AU88x0 A3D",
- "AU88x0 WT",
- "AU88x0 I2S",
+ CARD_NAME " ADB",
+ CARD_NAME " SPDIF",
+ CARD_NAME " A3D",
+ CARD_NAME " WT",
+ CARD_NAME " I2S",
};
static char *vortex_pcm_name[VORTEX_PCM_LAST] = {
"adb",
nr_capt, &pcm);
if (err < 0)
return err;
- strcpy(pcm->name, vortex_pcm_name[idx]);
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s %s", CARD_NAME_SHORT, vortex_pcm_name[idx]);
chip->pcm[idx] = pcm;
// This is an evil hack, but it saves a lot of duplicated code.
VORTEX_PCM_TYPE(pcm) = idx;
static const DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1);
static const DECLARE_TLV_DB_LINEAR(snd_emu10k1_db_linear, TLV_DB_GAIN_MUTE, 0);
+/* EMU10K1 bass/treble db gain */
+static const DECLARE_TLV_DB_SCALE(snd_emu10k1_bass_treble_db_scale, -1200, 60, 0);
+
static const u32 onoff_table[2] = {
0x00000000, 0x00000001
};
ctl->min = 0;
ctl->max = 40;
ctl->value[0] = ctl->value[1] = 20;
+ ctl->tlv = snd_emu10k1_bass_treble_db_scale;
ctl->translation = EMU10K1_GPR_TRANSLATION_BASS;
ctl = &controls[i + 1];
ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
ctl->min = 0;
ctl->max = 40;
ctl->value[0] = ctl->value[1] = 20;
+ ctl->tlv = snd_emu10k1_bass_treble_db_scale;
ctl->translation = EMU10K1_GPR_TRANSLATION_TREBLE;
#define BASS_GPR 0x8c
"Master Mono Playback Volume",
"PCM Out Path & Mute",
"Mono Output Select",
- "Front Playback Switch",
- "Front Playback Volume",
"Surround Playback Switch",
"Surround Playback Volume",
"Center Playback Switch",
emu->rear_ac97 = 1;
snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE|AC97SLOT_REAR_LEFT|AC97SLOT_REAR_RIGHT);
snd_ac97_write_cache(emu->ac97, AC97_HEADPHONE, 0x0202);
+ remove_ctl(card,"Front Playback Volume");
+ remove_ctl(card,"Front Playback Switch");
}
/* remove unused AC97 controls */
snd_ac97_write_cache(emu->ac97, AC97_SURROUND_MASTER, 0x0202);
for (; *c; c += 2)
rename_ctl(card, c[0], c[1]);
+ if (emu->card_capabilities->subsystem == 0x80401102) { /* SB Live! Platinum CT4760P */
+ remove_ctl(card, "Center Playback Volume");
+ remove_ctl(card, "LFE Playback Volume");
+ remove_ctl(card, "Wave Center Playback Volume");
+ remove_ctl(card, "Wave LFE Playback Volume");
+ }
if (emu->card_capabilities->subsystem == 0x20071102) { /* Audigy 4 Pro */
rename_ctl(card, "Line2 Capture Volume", "Line1/Mic Capture Volume");
rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume");
#include <sound/ac97_codec.h>
#include <sound/initval.h>
+#ifdef CONFIG_SND_ES1968_RADIO
+#include <sound/tea575x-tuner.h>
+#endif
+
#define CARD_NAME "ESS Maestro1/2"
#define DRIVER_NAME "ES1968"
spinlock_t ac97_lock;
struct tasklet_struct hwvol_tq;
#endif
+
+#ifdef CONFIG_SND_ES1968_RADIO
+ struct snd_tea575x tea;
+#endif
};
static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id);
}
#endif /* CONFIG_SND_ES1968_INPUT */
+#ifdef CONFIG_SND_ES1968_RADIO
+#define GPIO_DATA 0x60
+#define IO_MASK 4 /* mask register offset from GPIO_DATA
+ bits 1=unmask write to given bit */
+#define IO_DIR 8 /* direction register offset from GPIO_DATA
+ bits 0/1=read/write direction */
+/* mask bits for GPIO lines */
+#define STR_DATA 0x0040 /* GPIO6 */
+#define STR_CLK 0x0080 /* GPIO7 */
+#define STR_WREN 0x0100 /* GPIO8 */
+#define STR_MOST 0x0200 /* GPIO9 */
+
+static void snd_es1968_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
+{
+ struct es1968 *chip = tea->private_data;
+ unsigned long io = chip->io_port + GPIO_DATA;
+ u16 val = 0;
+
+ val |= (pins & TEA575X_DATA) ? STR_DATA : 0;
+ val |= (pins & TEA575X_CLK) ? STR_CLK : 0;
+ val |= (pins & TEA575X_WREN) ? STR_WREN : 0;
+
+ outw(val, io);
+}
+
+static u8 snd_es1968_tea575x_get_pins(struct snd_tea575x *tea)
+{
+ struct es1968 *chip = tea->private_data;
+ unsigned long io = chip->io_port + GPIO_DATA;
+ u16 val = inw(io);
+
+ return (val & STR_DATA) ? TEA575X_DATA : 0 |
+ (val & STR_MOST) ? TEA575X_MOST : 0;
+}
+
+static void snd_es1968_tea575x_set_direction(struct snd_tea575x *tea, bool output)
+{
+ struct es1968 *chip = tea->private_data;
+ unsigned long io = chip->io_port + GPIO_DATA;
+ u16 odir = inw(io + IO_DIR);
+
+ if (output) {
+ outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK);
+ outw(odir | STR_DATA | STR_CLK | STR_WREN, io + IO_DIR);
+ } else {
+ outw(~(STR_CLK | STR_WREN | STR_DATA | STR_MOST), io + IO_MASK);
+ outw((odir & ~(STR_DATA | STR_MOST)) | STR_CLK | STR_WREN, io + IO_DIR);
+ }
+}
+
+static struct snd_tea575x_ops snd_es1968_tea_ops = {
+ .set_pins = snd_es1968_tea575x_set_pins,
+ .get_pins = snd_es1968_tea575x_get_pins,
+ .set_direction = snd_es1968_tea575x_set_direction,
+};
+#endif
+
static int snd_es1968_free(struct es1968 *chip)
{
#ifdef CONFIG_SND_ES1968_INPUT
outw(0, chip->io_port + ESM_PORT_HOST_IRQ); /* disable IRQ */
}
+#ifdef CONFIG_SND_ES1968_RADIO
+ snd_tea575x_exit(&chip->tea);
+#endif
+
if (chip->irq >= 0)
free_irq(chip->irq, chip);
snd_es1968_free_gameport(chip);
snd_card_set_dev(card, &pci->dev);
+#ifdef CONFIG_SND_ES1968_RADIO
+ chip->tea.private_data = chip;
+ chip->tea.ops = &snd_es1968_tea_ops;
+ strlcpy(chip->tea.card, "SF64-PCE2", sizeof(chip->tea.card));
+ sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
+ if (!snd_tea575x_init(&chip->tea))
+ printk(KERN_INFO "es1968: detected TEA575x radio\n");
+#endif
+
*chip_ret = chip;
return 0;
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
#include <sound/tea575x-tuner.h>
-#define TEA575X_RADIO 1
#endif
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
/*
* Enable TEA575x tuner
* 1 = MediaForte 256-PCS
- * 2 = MediaForte 256-PCPR
+ * 2 = MediaForte 256-PCP
* 3 = MediaForte 64-PCR
* 16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card
* High 16-bits are video (radio) device number + 1
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable FM801 soundcard.");
module_param_array(tea575x_tuner, int, NULL, 0444);
-MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (1 = SF256-PCS, 2=SF256-PCPR, 3=SF64-PCR, +16=tuner-only).");
+MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (0 = auto, 1 = SF256-PCS, 2=SF256-PCP, 3=SF64-PCR, 8=disable, +16=tuner-only).");
#define TUNER_ONLY (1<<4)
#define TUNER_TYPE_MASK (~TUNER_ONLY & 0xFFFF)
spinlock_t reg_lock;
struct snd_info_entry *proc_entry;
-#ifdef TEA575X_RADIO
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
struct snd_tea575x tea;
#endif
* TEA5757 radio
*/
-#ifdef TEA575X_RADIO
-
-/* 256PCS GPIO numbers */
-#define TEA_256PCS_DATA 1
-#define TEA_256PCS_WRITE_ENABLE 2 /* inverted */
-#define TEA_256PCS_BUS_CLOCK 3
-
-static void snd_fm801_tea575x_256pcs_write(struct snd_tea575x *tea, unsigned int val)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
- int i = 25;
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines and set write enable bit */
- reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
- FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK);
- /* all of lines are in the write direction */
- /* clear data and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCS_DATA) |
- FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCS_DATA) |
- FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE));
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- while (i--) {
- if (val & (1 << i))
- reg |= FM801_GPIO_GP(TEA_256PCS_DATA);
- else
- reg &= ~FM801_GPIO_GP(TEA_256PCS_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- }
+/* GPIO to TEA575x maps */
+struct snd_fm801_tea575x_gpio {
+ u8 data, clk, wren, most;
+ char *name;
+};
- /* and reset the write enable bit */
- reg |= FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GP(TEA_256PCS_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- spin_unlock_irq(&chip->reg_lock);
-}
+static struct snd_fm801_tea575x_gpio snd_fm801_tea575x_gpios[] = {
+ { .data = 1, .clk = 3, .wren = 2, .most = 0, .name = "SF256-PCS" },
+ { .data = 1, .clk = 0, .wren = 2, .most = 3, .name = "SF256-PCP" },
+ { .data = 2, .clk = 0, .wren = 1, .most = 3, .name = "SF64-PCR" },
+};
-static unsigned int snd_fm801_tea575x_256pcs_read(struct snd_tea575x *tea)
+static void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg;
- unsigned int val = 0;
- int i;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines, set data direction to input */
- reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
- FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GD(TEA_256PCS_DATA) |
- FM801_GPIO_GP(TEA_256PCS_DATA) |
- FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE);
- /* all of lines are in the write direction, except data */
- /* clear data, write enable and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK));
-
- for (i = 0; i < 24; i++) {
- reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- val <<= 1;
- if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCS_DATA))
- val |= 1;
- }
+ unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
+ struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1];
- spin_unlock_irq(&chip->reg_lock);
+ reg &= ~(FM801_GPIO_GP(gpio.data) |
+ FM801_GPIO_GP(gpio.clk) |
+ FM801_GPIO_GP(gpio.wren));
- return val;
-}
+ reg |= (pins & TEA575X_DATA) ? FM801_GPIO_GP(gpio.data) : 0;
+ reg |= (pins & TEA575X_CLK) ? FM801_GPIO_GP(gpio.clk) : 0;
+ /* WRITE_ENABLE is inverted */
+ reg |= (pins & TEA575X_WREN) ? 0 : FM801_GPIO_GP(gpio.wren);
-/* 256PCPR GPIO numbers */
-#define TEA_256PCPR_BUS_CLOCK 0
-#define TEA_256PCPR_DATA 1
-#define TEA_256PCPR_WRITE_ENABLE 2 /* inverted */
-
-static void snd_fm801_tea575x_256pcpr_write(struct snd_tea575x *tea, unsigned int val)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
- int i = 25;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines and set write enable bit */
- reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
- FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK);
- /* all of lines are in the write direction */
- /* clear data and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCPR_DATA) |
- FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCPR_DATA) |
- FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE));
outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- while (i--) {
- if (val & (1 << i))
- reg |= FM801_GPIO_GP(TEA_256PCPR_DATA);
- else
- reg &= ~FM801_GPIO_GP(TEA_256PCPR_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- }
-
- /* and reset the write enable bit */
- reg |= FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GP(TEA_256PCPR_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- spin_unlock_irq(&chip->reg_lock);
}
-static unsigned int snd_fm801_tea575x_256pcpr_read(struct snd_tea575x *tea)
+static u8 snd_fm801_tea575x_get_pins(struct snd_tea575x *tea)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg;
- unsigned int val = 0;
- int i;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines, set data direction to input */
- reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
- FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GD(TEA_256PCPR_DATA) |
- FM801_GPIO_GP(TEA_256PCPR_DATA) |
- FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE);
- /* all of lines are in the write direction, except data */
- /* clear data, write enable and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK));
-
- for (i = 0; i < 24; i++) {
- reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- val <<= 1;
- if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCPR_DATA))
- val |= 1;
- }
+ unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
+ struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1];
- spin_unlock_irq(&chip->reg_lock);
-
- return val;
+ return (reg & FM801_GPIO_GP(gpio.data)) ? TEA575X_DATA : 0 |
+ (reg & FM801_GPIO_GP(gpio.most)) ? TEA575X_MOST : 0;
}
-/* 64PCR GPIO numbers */
-#define TEA_64PCR_BUS_CLOCK 0
-#define TEA_64PCR_WRITE_ENABLE 1 /* inverted */
-#define TEA_64PCR_DATA 2
-
-static void snd_fm801_tea575x_64pcr_write(struct snd_tea575x *tea, unsigned int val)
+static void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg;
- int i = 25;
+ unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
+ struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1];
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
/* use GPIO lines and set write enable bit */
- reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
- FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK);
- /* all of lines are in the write direction */
- /* clear data and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_64PCR_DATA) |
- FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_64PCR_DATA) |
- FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE));
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- while (i--) {
- if (val & (1 << i))
- reg |= FM801_GPIO_GP(TEA_64PCR_DATA);
- else
- reg &= ~FM801_GPIO_GP(TEA_64PCR_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
+ reg |= FM801_GPIO_GS(gpio.data) |
+ FM801_GPIO_GS(gpio.wren) |
+ FM801_GPIO_GS(gpio.clk) |
+ FM801_GPIO_GS(gpio.most);
+ if (output) {
+ /* all of lines are in the write direction */
+ /* clear data and clock lines */
+ reg &= ~(FM801_GPIO_GD(gpio.data) |
+ FM801_GPIO_GD(gpio.wren) |
+ FM801_GPIO_GD(gpio.clk) |
+ FM801_GPIO_GP(gpio.data) |
+ FM801_GPIO_GP(gpio.clk) |
+ FM801_GPIO_GP(gpio.wren));
+ } else {
+ /* use GPIO lines, set data direction to input */
+ reg |= FM801_GPIO_GD(gpio.data) |
+ FM801_GPIO_GD(gpio.most) |
+ FM801_GPIO_GP(gpio.data) |
+ FM801_GPIO_GP(gpio.most) |
+ FM801_GPIO_GP(gpio.wren);
+ /* all of lines are in the write direction, except data */
+ /* clear data, write enable and clock lines */
+ reg &= ~(FM801_GPIO_GD(gpio.wren) |
+ FM801_GPIO_GD(gpio.clk) |
+ FM801_GPIO_GP(gpio.clk));
}
- /* and reset the write enable bit */
- reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GP(TEA_64PCR_DATA);
outw(reg, FM801_REG(chip, GPIO_CTRL));
- spin_unlock_irq(&chip->reg_lock);
-}
-
-static unsigned int snd_fm801_tea575x_64pcr_read(struct snd_tea575x *tea)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
- unsigned int val = 0;
- int i;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines, set data direction to input */
- reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
- FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GD(TEA_64PCR_DATA) |
- FM801_GPIO_GP(TEA_64PCR_DATA) |
- FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
- /* all of lines are in the write direction, except data */
- /* clear data, write enable and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK));
-
- for (i = 0; i < 24; i++) {
- reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- val <<= 1;
- if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_64PCR_DATA))
- val |= 1;
- }
-
- spin_unlock_irq(&chip->reg_lock);
-
- return val;
}
-static void snd_fm801_tea575x_64pcr_mute(struct snd_tea575x *tea,
- unsigned int mute)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
-
- spin_lock_irq(&chip->reg_lock);
-
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- if (mute)
- /* 0xf800 (mute) */
- reg &= ~FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
- else
- /* 0xf802 (unmute) */
- reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- spin_unlock_irq(&chip->reg_lock);
-}
-
-static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
- {
- /* 1 = MediaForte 256-PCS */
- .write = snd_fm801_tea575x_256pcs_write,
- .read = snd_fm801_tea575x_256pcs_read,
- },
- {
- /* 2 = MediaForte 256-PCPR */
- .write = snd_fm801_tea575x_256pcpr_write,
- .read = snd_fm801_tea575x_256pcpr_read,
- },
- {
- /* 3 = MediaForte 64-PCR */
- .write = snd_fm801_tea575x_64pcr_write,
- .read = snd_fm801_tea575x_64pcr_read,
- .mute = snd_fm801_tea575x_64pcr_mute,
- }
+static struct snd_tea575x_ops snd_fm801_tea_ops = {
+ .set_pins = snd_fm801_tea575x_set_pins,
+ .get_pins = snd_fm801_tea575x_get_pins,
+ .set_direction = snd_fm801_tea575x_set_direction,
};
#endif
outw(cmdw, FM801_REG(chip, IRQ_MASK));
__end_hw:
-#ifdef TEA575X_RADIO
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
snd_tea575x_exit(&chip->tea);
#endif
if (chip->irq >= 0)
snd_card_set_dev(card, &pci->dev);
-#ifdef TEA575X_RADIO
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
+ chip->tea.private_data = chip;
+ chip->tea.ops = &snd_fm801_tea_ops;
+ sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
(tea575x_tuner & TUNER_TYPE_MASK) < 4) {
- chip->tea.dev_nr = tea575x_tuner >> 16;
- chip->tea.card = card;
- chip->tea.freq_fixup = 10700;
- chip->tea.private_data = chip;
- chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & TUNER_TYPE_MASK) - 1];
- snd_tea575x_init(&chip->tea);
- }
+ if (snd_tea575x_init(&chip->tea))
+ snd_printk(KERN_ERR "TEA575x radio not found\n");
+ } else if ((tea575x_tuner & TUNER_TYPE_MASK) == 0)
+ /* autodetect tuner connection */
+ for (tea575x_tuner = 1; tea575x_tuner <= 3; tea575x_tuner++) {
+ chip->tea575x_tuner = tea575x_tuner;
+ if (!snd_tea575x_init(&chip->tea)) {
+ snd_printk(KERN_INFO "detected TEA575x radio type %s\n",
+ snd_fm801_tea575x_gpios[tea575x_tuner - 1].name);
+ break;
+ }
+ }
+ strlcpy(chip->tea.card, snd_fm801_tea575x_gpios[(tea575x_tuner & TUNER_TYPE_MASK) - 1].name, sizeof(chip->tea.card));
#endif
*rchip = chip;
{ PCI_VDEVICE(NVIDIA, 0x0069), DEVICE_NFORCE }, /* NFORCE2 */
{ PCI_VDEVICE(NVIDIA, 0x0089), DEVICE_NFORCE }, /* NFORCE2s */
{ PCI_VDEVICE(NVIDIA, 0x00d9), DEVICE_NFORCE }, /* NFORCE3 */
+ { PCI_VDEVICE(AMD, 0x746e), DEVICE_INTEL }, /* AMD8111 */
#if 0
- { PCI_VDEVICE(AMD, 0x746d), DEVICE_INTEL }, /* AMD8111 */
{ PCI_VDEVICE(AL, 0x5455), DEVICE_ALI }, /* Ali5455 */
#endif
{ 0, }
{ PCI_DEVICE_ID_NVIDIA_MCP2_MODEM, "NVidia nForce2" },
{ PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM, "NVidia nForce2s" },
{ PCI_DEVICE_ID_NVIDIA_MCP3_MODEM, "NVidia nForce3" },
+ { 0x746e, "AMD AMD8111" },
#if 0
{ 0x5455, "ALi M5455" },
- { 0x746d, "AMD AMD8111" },
#endif
{ 0 },
};
--- /dev/null
+snd-lola-y := lola.o lola_pcm.o lola_clock.o lola_mixer.o
+snd-lola-$(CONFIG_SND_DEBUG) += lola_proc.o
+
+obj-$(CONFIG_SND_LOLA) += snd-lola.o
--- /dev/null
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include "lola.h"
+
+/* Standard options */
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Digigram Lola driver.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Digigram Lola driver.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Digigram Lola driver.");
+
+/* Lola-specific options */
+
+/* for instance use always max granularity which is compatible
+ * with all sample rates
+ */
+static int granularity[SNDRV_CARDS] = {
+ [0 ... (SNDRV_CARDS - 1)] = LOLA_GRANULARITY_MAX
+};
+
+/* below a sample_rate of 16kHz the analogue audio quality is NOT excellent */
+static int sample_rate_min[SNDRV_CARDS] = {
+ [0 ... (SNDRV_CARDS - 1) ] = 16000
+};
+
+module_param_array(granularity, int, NULL, 0444);
+MODULE_PARM_DESC(granularity, "Granularity value");
+module_param_array(sample_rate_min, int, NULL, 0444);
+MODULE_PARM_DESC(sample_rate_min, "Minimal sample rate");
+
+/*
+ */
+
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Digigram, Lola}}");
+MODULE_DESCRIPTION("Digigram Lola driver");
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+static int debug;
+module_param(debug, int, 0644);
+#define verbose_debug(fmt, args...) \
+ do { if (debug > 1) printk(KERN_DEBUG SFX fmt, ##args); } while (0)
+#else
+#define verbose_debug(fmt, args...)
+#endif
+
+/*
+ * pseudo-codec read/write via CORB/RIRB
+ */
+
+static int corb_send_verb(struct lola *chip, unsigned int nid,
+ unsigned int verb, unsigned int data,
+ unsigned int extdata)
+{
+ unsigned long flags;
+ int ret = -EIO;
+
+ chip->last_cmd_nid = nid;
+ chip->last_verb = verb;
+ chip->last_data = data;
+ chip->last_extdata = extdata;
+ data |= (nid << 20) | (verb << 8);
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (chip->rirb.cmds < LOLA_CORB_ENTRIES - 1) {
+ unsigned int wp = chip->corb.wp + 1;
+ wp %= LOLA_CORB_ENTRIES;
+ chip->corb.wp = wp;
+ chip->corb.buf[wp * 2] = cpu_to_le32(data);
+ chip->corb.buf[wp * 2 + 1] = cpu_to_le32(extdata);
+ lola_writew(chip, BAR0, CORBWP, wp);
+ chip->rirb.cmds++;
+ smp_wmb();
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return ret;
+}
+
+static void lola_queue_unsol_event(struct lola *chip, unsigned int res,
+ unsigned int res_ex)
+{
+ lola_update_ext_clock_freq(chip, res);
+}
+
+/* retrieve RIRB entry - called from interrupt handler */
+static void lola_update_rirb(struct lola *chip)
+{
+ unsigned int rp, wp;
+ u32 res, res_ex;
+
+ wp = lola_readw(chip, BAR0, RIRBWP);
+ if (wp == chip->rirb.wp)
+ return;
+ chip->rirb.wp = wp;
+
+ while (chip->rirb.rp != wp) {
+ chip->rirb.rp++;
+ chip->rirb.rp %= LOLA_CORB_ENTRIES;
+
+ rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
+ res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
+ res = le32_to_cpu(chip->rirb.buf[rp]);
+ if (res_ex & LOLA_RIRB_EX_UNSOL_EV)
+ lola_queue_unsol_event(chip, res, res_ex);
+ else if (chip->rirb.cmds) {
+ chip->res = res;
+ chip->res_ex = res_ex;
+ smp_wmb();
+ chip->rirb.cmds--;
+ }
+ }
+}
+
+static int rirb_get_response(struct lola *chip, unsigned int *val,
+ unsigned int *extval)
+{
+ unsigned long timeout;
+
+ again:
+ timeout = jiffies + msecs_to_jiffies(1000);
+ for (;;) {
+ if (chip->polling_mode) {
+ spin_lock_irq(&chip->reg_lock);
+ lola_update_rirb(chip);
+ spin_unlock_irq(&chip->reg_lock);
+ }
+ if (!chip->rirb.cmds) {
+ *val = chip->res;
+ if (extval)
+ *extval = chip->res_ex;
+ verbose_debug("get_response: %x, %x\n",
+ chip->res, chip->res_ex);
+ if (chip->res_ex & LOLA_RIRB_EX_ERROR) {
+ printk(KERN_WARNING SFX "RIRB ERROR: "
+ "NID=%x, verb=%x, data=%x, ext=%x\n",
+ chip->last_cmd_nid,
+ chip->last_verb, chip->last_data,
+ chip->last_extdata);
+ return -EIO;
+ }
+ return 0;
+ }
+ if (time_after(jiffies, timeout))
+ break;
+ udelay(20);
+ cond_resched();
+ }
+ printk(KERN_WARNING SFX "RIRB response error\n");
+ if (!chip->polling_mode) {
+ printk(KERN_WARNING SFX "switching to polling mode\n");
+ chip->polling_mode = 1;
+ goto again;
+ }
+ return -EIO;
+}
+
+/* aynchronous write of a codec verb with data */
+int lola_codec_write(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata)
+{
+ verbose_debug("codec_write NID=%x, verb=%x, data=%x, ext=%x\n",
+ nid, verb, data, extdata);
+ return corb_send_verb(chip, nid, verb, data, extdata);
+}
+
+/* write a codec verb with data and read the returned status */
+int lola_codec_read(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata,
+ unsigned int *val, unsigned int *extval)
+{
+ int err;
+
+ verbose_debug("codec_read NID=%x, verb=%x, data=%x, ext=%x\n",
+ nid, verb, data, extdata);
+ err = corb_send_verb(chip, nid, verb, data, extdata);
+ if (err < 0)
+ return err;
+ err = rirb_get_response(chip, val, extval);
+ return err;
+}
+
+/* flush all pending codec writes */
+int lola_codec_flush(struct lola *chip)
+{
+ unsigned int tmp;
+ return rirb_get_response(chip, &tmp, NULL);
+}
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t lola_interrupt(int irq, void *dev_id)
+{
+ struct lola *chip = dev_id;
+ unsigned int notify_ins, notify_outs, error_ins, error_outs;
+ int handled = 0;
+ int i;
+
+ notify_ins = notify_outs = error_ins = error_outs = 0;
+ spin_lock(&chip->reg_lock);
+ for (;;) {
+ unsigned int status, in_sts, out_sts;
+ unsigned int reg;
+
+ status = lola_readl(chip, BAR1, DINTSTS);
+ if (!status || status == -1)
+ break;
+
+ in_sts = lola_readl(chip, BAR1, DIINTSTS);
+ out_sts = lola_readl(chip, BAR1, DOINTSTS);
+
+ /* clear Input Interrupts */
+ for (i = 0; in_sts && i < chip->pcm[CAPT].num_streams; i++) {
+ if (!(in_sts & (1 << i)))
+ continue;
+ in_sts &= ~(1 << i);
+ reg = lola_dsd_read(chip, i, STS);
+ if (reg & LOLA_DSD_STS_DESE) /* error */
+ error_ins |= (1 << i);
+ if (reg & LOLA_DSD_STS_BCIS) /* notify */
+ notify_ins |= (1 << i);
+ /* clear */
+ lola_dsd_write(chip, i, STS, reg);
+ }
+
+ /* clear Output Interrupts */
+ for (i = 0; out_sts && i < chip->pcm[PLAY].num_streams; i++) {
+ if (!(out_sts & (1 << i)))
+ continue;
+ out_sts &= ~(1 << i);
+ reg = lola_dsd_read(chip, i + MAX_STREAM_IN_COUNT, STS);
+ if (reg & LOLA_DSD_STS_DESE) /* error */
+ error_outs |= (1 << i);
+ if (reg & LOLA_DSD_STS_BCIS) /* notify */
+ notify_outs |= (1 << i);
+ lola_dsd_write(chip, i + MAX_STREAM_IN_COUNT, STS, reg);
+ }
+
+ if (status & LOLA_DINT_CTRL) {
+ unsigned char rbsts; /* ring status is byte access */
+ rbsts = lola_readb(chip, BAR0, RIRBSTS);
+ rbsts &= LOLA_RIRB_INT_MASK;
+ if (rbsts)
+ lola_writeb(chip, BAR0, RIRBSTS, rbsts);
+ rbsts = lola_readb(chip, BAR0, CORBSTS);
+ rbsts &= LOLA_CORB_INT_MASK;
+ if (rbsts)
+ lola_writeb(chip, BAR0, CORBSTS, rbsts);
+
+ lola_update_rirb(chip);
+ }
+
+ if (status & (LOLA_DINT_FIFOERR | LOLA_DINT_MUERR)) {
+ /* clear global fifo error interrupt */
+ lola_writel(chip, BAR1, DINTSTS,
+ (status & (LOLA_DINT_FIFOERR | LOLA_DINT_MUERR)));
+ }
+ handled = 1;
+ }
+ spin_unlock(&chip->reg_lock);
+
+ lola_pcm_update(chip, &chip->pcm[CAPT], notify_ins);
+ lola_pcm_update(chip, &chip->pcm[PLAY], notify_outs);
+
+ return IRQ_RETVAL(handled);
+}
+
+
+/*
+ * controller
+ */
+static int reset_controller(struct lola *chip)
+{
+ unsigned int gctl = lola_readl(chip, BAR0, GCTL);
+ unsigned long end_time;
+
+ if (gctl) {
+ /* to be sure */
+ lola_writel(chip, BAR1, BOARD_MODE, 0);
+ return 0;
+ }
+
+ chip->cold_reset = 1;
+ lola_writel(chip, BAR0, GCTL, LOLA_GCTL_RESET);
+ end_time = jiffies + msecs_to_jiffies(200);
+ do {
+ msleep(1);
+ gctl = lola_readl(chip, BAR0, GCTL);
+ if (gctl)
+ break;
+ } while (time_before(jiffies, end_time));
+ if (!gctl) {
+ printk(KERN_ERR SFX "cannot reset controller\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static void lola_irq_enable(struct lola *chip)
+{
+ unsigned int val;
+
+ /* enalbe all I/O streams */
+ val = (1 << chip->pcm[PLAY].num_streams) - 1;
+ lola_writel(chip, BAR1, DOINTCTL, val);
+ val = (1 << chip->pcm[CAPT].num_streams) - 1;
+ lola_writel(chip, BAR1, DIINTCTL, val);
+
+ /* enable global irqs */
+ val = LOLA_DINT_GLOBAL | LOLA_DINT_CTRL | LOLA_DINT_FIFOERR |
+ LOLA_DINT_MUERR;
+ lola_writel(chip, BAR1, DINTCTL, val);
+}
+
+static void lola_irq_disable(struct lola *chip)
+{
+ lola_writel(chip, BAR1, DINTCTL, 0);
+ lola_writel(chip, BAR1, DIINTCTL, 0);
+ lola_writel(chip, BAR1, DOINTCTL, 0);
+}
+
+static int setup_corb_rirb(struct lola *chip)
+{
+ int err;
+ unsigned char tmp;
+ unsigned long end_time;
+
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ PAGE_SIZE, &chip->rb);
+ if (err < 0)
+ return err;
+
+ chip->corb.addr = chip->rb.addr;
+ chip->corb.buf = (u32 *)chip->rb.area;
+ chip->rirb.addr = chip->rb.addr + 2048;
+ chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
+
+ /* disable ringbuffer DMAs */
+ lola_writeb(chip, BAR0, RIRBCTL, 0);
+ lola_writeb(chip, BAR0, CORBCTL, 0);
+
+ end_time = jiffies + msecs_to_jiffies(200);
+ do {
+ if (!lola_readb(chip, BAR0, RIRBCTL) &&
+ !lola_readb(chip, BAR0, CORBCTL))
+ break;
+ msleep(1);
+ } while (time_before(jiffies, end_time));
+
+ /* CORB set up */
+ lola_writel(chip, BAR0, CORBLBASE, (u32)chip->corb.addr);
+ lola_writel(chip, BAR0, CORBUBASE, upper_32_bits(chip->corb.addr));
+ /* set the corb size to 256 entries */
+ lola_writeb(chip, BAR0, CORBSIZE, 0x02);
+ /* set the corb write pointer to 0 */
+ lola_writew(chip, BAR0, CORBWP, 0);
+ /* reset the corb hw read pointer */
+ lola_writew(chip, BAR0, CORBRP, LOLA_RBRWP_CLR);
+ /* enable corb dma */
+ lola_writeb(chip, BAR0, CORBCTL, LOLA_RBCTL_DMA_EN);
+ /* clear flags if set */
+ tmp = lola_readb(chip, BAR0, CORBSTS) & LOLA_CORB_INT_MASK;
+ if (tmp)
+ lola_writeb(chip, BAR0, CORBSTS, tmp);
+ chip->corb.wp = 0;
+
+ /* RIRB set up */
+ lola_writel(chip, BAR0, RIRBLBASE, (u32)chip->rirb.addr);
+ lola_writel(chip, BAR0, RIRBUBASE, upper_32_bits(chip->rirb.addr));
+ /* set the rirb size to 256 entries */
+ lola_writeb(chip, BAR0, RIRBSIZE, 0x02);
+ /* reset the rirb hw write pointer */
+ lola_writew(chip, BAR0, RIRBWP, LOLA_RBRWP_CLR);
+ /* set N=1, get RIRB response interrupt for new entry */
+ lola_writew(chip, BAR0, RINTCNT, 1);
+ /* enable rirb dma and response irq */
+ lola_writeb(chip, BAR0, RIRBCTL, LOLA_RBCTL_DMA_EN | LOLA_RBCTL_IRQ_EN);
+ /* clear flags if set */
+ tmp = lola_readb(chip, BAR0, RIRBSTS) & LOLA_RIRB_INT_MASK;
+ if (tmp)
+ lola_writeb(chip, BAR0, RIRBSTS, tmp);
+ chip->rirb.rp = chip->rirb.cmds = 0;
+
+ return 0;
+}
+
+static void stop_corb_rirb(struct lola *chip)
+{
+ /* disable ringbuffer DMAs */
+ lola_writeb(chip, BAR0, RIRBCTL, 0);
+ lola_writeb(chip, BAR0, CORBCTL, 0);
+}
+
+static void lola_reset_setups(struct lola *chip)
+{
+ /* update the granularity */
+ lola_set_granularity(chip, chip->granularity, true);
+ /* update the sample clock */
+ lola_set_clock_index(chip, chip->clock.cur_index);
+ /* enable unsolicited events of the clock widget */
+ lola_enable_clock_events(chip);
+ /* update the analog gains */
+ lola_setup_all_analog_gains(chip, CAPT, false); /* input, update */
+ /* update SRC configuration if applicable */
+ lola_set_src_config(chip, chip->input_src_mask, false);
+ /* update the analog outputs */
+ lola_setup_all_analog_gains(chip, PLAY, false); /* output, update */
+}
+
+static int lola_parse_tree(struct lola *chip)
+{
+ unsigned int val;
+ int nid, err;
+
+ err = lola_read_param(chip, 0, LOLA_PAR_VENDOR_ID, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read VENDOR_ID\n");
+ return err;
+ }
+ val >>= 16;
+ if (val != 0x1369) {
+ printk(KERN_ERR SFX "Unknown codec vendor 0x%x\n", val);
+ return -EINVAL;
+ }
+
+ err = lola_read_param(chip, 1, LOLA_PAR_FUNCTION_TYPE, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read FUNCTION_TYPE for 0x%x\n", nid);
+ return err;
+ }
+ if (val != 1) {
+ printk(KERN_ERR SFX "Unknown function type %d\n", val);
+ return -EINVAL;
+ }
+
+ err = lola_read_param(chip, 1, LOLA_PAR_SPECIFIC_CAPS, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read SPECCAPS\n");
+ return err;
+ }
+ chip->lola_caps = val;
+ chip->pin[CAPT].num_pins = LOLA_AFG_INPUT_PIN_COUNT(chip->lola_caps);
+ chip->pin[PLAY].num_pins = LOLA_AFG_OUTPUT_PIN_COUNT(chip->lola_caps);
+ snd_printdd(SFX "speccaps=0x%x, pins in=%d, out=%d\n",
+ chip->lola_caps,
+ chip->pin[CAPT].num_pins, chip->pin[PLAY].num_pins);
+
+ if (chip->pin[CAPT].num_pins > MAX_AUDIO_INOUT_COUNT ||
+ chip->pin[PLAY].num_pins > MAX_AUDIO_INOUT_COUNT) {
+ printk(KERN_ERR SFX "Invalid Lola-spec caps 0x%x\n", val);
+ return -EINVAL;
+ }
+
+ nid = 0x02;
+ err = lola_init_pcm(chip, CAPT, &nid);
+ if (err < 0)
+ return err;
+ err = lola_init_pcm(chip, PLAY, &nid);
+ if (err < 0)
+ return err;
+
+ err = lola_init_pins(chip, CAPT, &nid);
+ if (err < 0)
+ return err;
+ err = lola_init_pins(chip, PLAY, &nid);
+ if (err < 0)
+ return err;
+
+ if (LOLA_AFG_CLOCK_WIDGET_PRESENT(chip->lola_caps)) {
+ err = lola_init_clock_widget(chip, nid);
+ if (err < 0)
+ return err;
+ nid++;
+ }
+ if (LOLA_AFG_MIXER_WIDGET_PRESENT(chip->lola_caps)) {
+ err = lola_init_mixer_widget(chip, nid);
+ if (err < 0)
+ return err;
+ nid++;
+ }
+
+ /* enable unsolicited events of the clock widget */
+ err = lola_enable_clock_events(chip);
+ if (err < 0)
+ return err;
+
+ /* if last ResetController was not a ColdReset, we don't know
+ * the state of the card; initialize here again
+ */
+ if (!chip->cold_reset) {
+ lola_reset_setups(chip);
+ chip->cold_reset = 1;
+ } else {
+ /* set the granularity if it is not the default */
+ if (chip->granularity != LOLA_GRANULARITY_MIN)
+ lola_set_granularity(chip, chip->granularity, true);
+ }
+
+ return 0;
+}
+
+static void lola_stop_hw(struct lola *chip)
+{
+ stop_corb_rirb(chip);
+ lola_irq_disable(chip);
+}
+
+static void lola_free(struct lola *chip)
+{
+ if (chip->initialized)
+ lola_stop_hw(chip);
+ lola_free_pcm(chip);
+ lola_free_mixer(chip);
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+ if (chip->bar[0].remap_addr)
+ iounmap(chip->bar[0].remap_addr);
+ if (chip->bar[1].remap_addr)
+ iounmap(chip->bar[1].remap_addr);
+ if (chip->rb.area)
+ snd_dma_free_pages(&chip->rb);
+ pci_release_regions(chip->pci);
+ pci_disable_device(chip->pci);
+ kfree(chip);
+}
+
+static int lola_dev_free(struct snd_device *device)
+{
+ lola_free(device->device_data);
+ return 0;
+}
+
+static int __devinit lola_create(struct snd_card *card, struct pci_dev *pci,
+ int dev, struct lola **rchip)
+{
+ struct lola *chip;
+ int err;
+ unsigned int dever;
+ static struct snd_device_ops ops = {
+ .dev_free = lola_dev_free,
+ };
+
+ *rchip = NULL;
+
+ err = pci_enable_device(pci);
+ if (err < 0)
+ return err;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip) {
+ snd_printk(KERN_ERR SFX "cannot allocate chip\n");
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&chip->reg_lock);
+ mutex_init(&chip->open_mutex);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+
+ chip->granularity = granularity[dev];
+ switch (chip->granularity) {
+ case 8:
+ chip->sample_rate_max = 48000;
+ break;
+ case 16:
+ chip->sample_rate_max = 96000;
+ break;
+ case 32:
+ chip->sample_rate_max = 192000;
+ break;
+ default:
+ snd_printk(KERN_WARNING SFX
+ "Invalid granularity %d, reset to %d\n",
+ chip->granularity, LOLA_GRANULARITY_MAX);
+ chip->granularity = LOLA_GRANULARITY_MAX;
+ chip->sample_rate_max = 192000;
+ break;
+ }
+ chip->sample_rate_min = sample_rate_min[dev];
+ if (chip->sample_rate_min > chip->sample_rate_max) {
+ snd_printk(KERN_WARNING SFX
+ "Invalid sample_rate_min %d, reset to 16000\n",
+ chip->sample_rate_min);
+ chip->sample_rate_min = 16000;
+ }
+
+ err = pci_request_regions(pci, DRVNAME);
+ if (err < 0) {
+ kfree(chip);
+ pci_disable_device(pci);
+ return err;
+ }
+
+ chip->bar[0].addr = pci_resource_start(pci, 0);
+ chip->bar[0].remap_addr = pci_ioremap_bar(pci, 0);
+ chip->bar[1].addr = pci_resource_start(pci, 2);
+ chip->bar[1].remap_addr = pci_ioremap_bar(pci, 2);
+ if (!chip->bar[0].remap_addr || !chip->bar[1].remap_addr) {
+ snd_printk(KERN_ERR SFX "ioremap error\n");
+ err = -ENXIO;
+ goto errout;
+ }
+
+ pci_set_master(pci);
+
+ err = reset_controller(chip);
+ if (err < 0)
+ goto errout;
+
+ if (request_irq(pci->irq, lola_interrupt, IRQF_SHARED,
+ DRVNAME, chip)) {
+ printk(KERN_ERR SFX "unable to grab IRQ %d\n", pci->irq);
+ err = -EBUSY;
+ goto errout;
+ }
+ chip->irq = pci->irq;
+ synchronize_irq(chip->irq);
+
+ dever = lola_readl(chip, BAR1, DEVER);
+ chip->pcm[CAPT].num_streams = (dever >> 0) & 0x3ff;
+ chip->pcm[PLAY].num_streams = (dever >> 10) & 0x3ff;
+ chip->version = (dever >> 24) & 0xff;
+ snd_printdd(SFX "streams in=%d, out=%d, version=0x%x\n",
+ chip->pcm[CAPT].num_streams, chip->pcm[PLAY].num_streams,
+ chip->version);
+
+ /* Test LOLA_BAR1_DEVER */
+ if (chip->pcm[CAPT].num_streams > MAX_STREAM_IN_COUNT ||
+ chip->pcm[PLAY].num_streams > MAX_STREAM_OUT_COUNT ||
+ (!chip->pcm[CAPT].num_streams &&
+ !chip->pcm[PLAY].num_streams)) {
+ printk(KERN_ERR SFX "invalid DEVER = %x\n", dever);
+ err = -EINVAL;
+ goto errout;
+ }
+
+ err = setup_corb_rirb(chip);
+ if (err < 0)
+ goto errout;
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ snd_printk(KERN_ERR SFX "Error creating device [card]!\n");
+ goto errout;
+ }
+
+ strcpy(card->driver, "Lola");
+ strlcpy(card->shortname, "Digigram Lola", sizeof(card->shortname));
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx irq %i",
+ card->shortname, chip->bar[0].addr, chip->irq);
+ strcpy(card->mixername, card->shortname);
+
+ lola_irq_enable(chip);
+
+ chip->initialized = 1;
+ *rchip = chip;
+ return 0;
+
+ errout:
+ lola_free(chip);
+ return err;
+}
+
+static int __devinit lola_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ struct snd_card *card;
+ struct lola *chip;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0) {
+ snd_printk(KERN_ERR SFX "Error creating card!\n");
+ return err;
+ }
+
+ snd_card_set_dev(card, &pci->dev);
+
+ err = lola_create(card, pci, dev, &chip);
+ if (err < 0)
+ goto out_free;
+ card->private_data = chip;
+
+ err = lola_parse_tree(chip);
+ if (err < 0)
+ goto out_free;
+
+ err = lola_create_pcm(chip);
+ if (err < 0)
+ goto out_free;
+
+ err = lola_create_mixer(chip);
+ if (err < 0)
+ goto out_free;
+
+ lola_proc_debug_new(chip);
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto out_free;
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return err;
+out_free:
+ snd_card_free(card);
+ return err;
+}
+
+static void __devexit lola_remove(struct pci_dev *pci)
+{
+ snd_card_free(pci_get_drvdata(pci));
+ pci_set_drvdata(pci, NULL);
+}
+
+/* PCI IDs */
+static DEFINE_PCI_DEVICE_TABLE(lola_ids) = {
+ { PCI_VDEVICE(DIGIGRAM, 0x0001) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, lola_ids);
+
+/* pci_driver definition */
+static struct pci_driver driver = {
+ .name = DRVNAME,
+ .id_table = lola_ids,
+ .probe = lola_probe,
+ .remove = __devexit_p(lola_remove),
+};
+
+static int __init alsa_card_lola_init(void)
+{
+ return pci_register_driver(&driver);
+}
+
+static void __exit alsa_card_lola_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_lola_init)
+module_exit(alsa_card_lola_exit)
--- /dev/null
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _LOLA_H
+#define _LOLA_H
+
+#define DRVNAME "snd-lola"
+#define SFX DRVNAME ": "
+
+/*
+ * Lola HD Audio Registers BAR0
+ */
+#define LOLA_BAR0_GCAP 0x00
+#define LOLA_BAR0_VMIN 0x02
+#define LOLA_BAR0_VMAJ 0x03
+#define LOLA_BAR0_OUTPAY 0x04
+#define LOLA_BAR0_INPAY 0x06
+#define LOLA_BAR0_GCTL 0x08
+#define LOLA_BAR0_WAKEEN 0x0c
+#define LOLA_BAR0_STATESTS 0x0e
+#define LOLA_BAR0_GSTS 0x10
+#define LOLA_BAR0_OUTSTRMPAY 0x18
+#define LOLA_BAR0_INSTRMPAY 0x1a
+#define LOLA_BAR0_INTCTL 0x20
+#define LOLA_BAR0_INTSTS 0x24
+#define LOLA_BAR0_WALCLK 0x30
+#define LOLA_BAR0_SSYNC 0x38
+
+#define LOLA_BAR0_CORBLBASE 0x40
+#define LOLA_BAR0_CORBUBASE 0x44
+#define LOLA_BAR0_CORBWP 0x48 /* no ULONG access */
+#define LOLA_BAR0_CORBRP 0x4a /* no ULONG access */
+#define LOLA_BAR0_CORBCTL 0x4c /* no ULONG access */
+#define LOLA_BAR0_CORBSTS 0x4d /* UCHAR access only */
+#define LOLA_BAR0_CORBSIZE 0x4e /* no ULONG access */
+
+#define LOLA_BAR0_RIRBLBASE 0x50
+#define LOLA_BAR0_RIRBUBASE 0x54
+#define LOLA_BAR0_RIRBWP 0x58
+#define LOLA_BAR0_RINTCNT 0x5a /* no ULONG access */
+#define LOLA_BAR0_RIRBCTL 0x5c
+#define LOLA_BAR0_RIRBSTS 0x5d /* UCHAR access only */
+#define LOLA_BAR0_RIRBSIZE 0x5e /* no ULONG access */
+
+#define LOLA_BAR0_ICW 0x60
+#define LOLA_BAR0_IRR 0x64
+#define LOLA_BAR0_ICS 0x68
+#define LOLA_BAR0_DPLBASE 0x70
+#define LOLA_BAR0_DPUBASE 0x74
+
+/* stream register offsets from stream base 0x80 */
+#define LOLA_BAR0_SD0_OFFSET 0x80
+#define LOLA_REG0_SD_CTL 0x00
+#define LOLA_REG0_SD_STS 0x03
+#define LOLA_REG0_SD_LPIB 0x04
+#define LOLA_REG0_SD_CBL 0x08
+#define LOLA_REG0_SD_LVI 0x0c
+#define LOLA_REG0_SD_FIFOW 0x0e
+#define LOLA_REG0_SD_FIFOSIZE 0x10
+#define LOLA_REG0_SD_FORMAT 0x12
+#define LOLA_REG0_SD_BDLPL 0x18
+#define LOLA_REG0_SD_BDLPU 0x1c
+
+/*
+ * Lola Digigram Registers BAR1
+ */
+#define LOLA_BAR1_FPGAVER 0x00
+#define LOLA_BAR1_DEVER 0x04
+#define LOLA_BAR1_UCBMV 0x08
+#define LOLA_BAR1_JTAG 0x0c
+#define LOLA_BAR1_UARTRX 0x10
+#define LOLA_BAR1_UARTTX 0x14
+#define LOLA_BAR1_UARTCR 0x18
+#define LOLA_BAR1_NVRAMVER 0x1c
+#define LOLA_BAR1_CTRLSPI 0x20
+#define LOLA_BAR1_DSPI 0x24
+#define LOLA_BAR1_AISPI 0x28
+#define LOLA_BAR1_GRAN 0x2c
+
+#define LOLA_BAR1_DINTCTL 0x80
+#define LOLA_BAR1_DIINTCTL 0x84
+#define LOLA_BAR1_DOINTCTL 0x88
+#define LOLA_BAR1_LRC 0x90
+#define LOLA_BAR1_DINTSTS 0x94
+#define LOLA_BAR1_DIINTSTS 0x98
+#define LOLA_BAR1_DOINTSTS 0x9c
+
+#define LOLA_BAR1_DSD0_OFFSET 0xa0
+#define LOLA_BAR1_DSD_SIZE 0x18
+
+#define LOLA_BAR1_DSDnSTS 0x00
+#define LOLA_BAR1_DSDnLPIB 0x04
+#define LOLA_BAR1_DSDnCTL 0x08
+#define LOLA_BAR1_DSDnLVI 0x0c
+#define LOLA_BAR1_DSDnBDPL 0x10
+#define LOLA_BAR1_DSDnBDPU 0x14
+
+#define LOLA_BAR1_SSYNC 0x03e8
+
+#define LOLA_BAR1_BOARD_CTRL 0x0f00
+#define LOLA_BAR1_BOARD_MODE 0x0f02
+
+#define LOLA_BAR1_SOURCE_GAIN_ENABLE 0x1000
+#define LOLA_BAR1_DEST00_MIX_GAIN_ENABLE 0x1004
+#define LOLA_BAR1_DEST31_MIX_GAIN_ENABLE 0x1080
+#define LOLA_BAR1_SOURCE00_01_GAIN 0x1084
+#define LOLA_BAR1_SOURCE30_31_GAIN 0x10c0
+#define LOLA_BAR1_SOURCE_GAIN(src) \
+ (LOLA_BAR1_SOURCE00_01_GAIN + (src) * 2)
+#define LOLA_BAR1_DEST00_MIX00_01_GAIN 0x10c4
+#define LOLA_BAR1_DEST00_MIX30_31_GAIN 0x1100
+#define LOLA_BAR1_DEST01_MIX00_01_GAIN 0x1104
+#define LOLA_BAR1_DEST01_MIX30_31_GAIN 0x1140
+#define LOLA_BAR1_DEST31_MIX00_01_GAIN 0x1884
+#define LOLA_BAR1_DEST31_MIX30_31_GAIN 0x18c0
+#define LOLA_BAR1_MIX_GAIN(dest, mix) \
+ (LOLA_BAR1_DEST00_MIX00_01_GAIN + (dest) * 0x40 + (mix) * 2)
+#define LOLA_BAR1_ANALOG_CLIP_IN 0x18c4
+#define LOLA_BAR1_PEAKMETERS_SOURCE00_01 0x18c8
+#define LOLA_BAR1_PEAKMETERS_SOURCE30_31 0x1904
+#define LOLA_BAR1_PEAKMETERS_SOURCE(src) \
+ (LOLA_BAR1_PEAKMETERS_SOURCE00_01 + (src) * 2)
+#define LOLA_BAR1_PEAKMETERS_DEST00_01 0x1908
+#define LOLA_BAR1_PEAKMETERS_DEST30_31 0x1944
+#define LOLA_BAR1_PEAKMETERS_DEST(dest) \
+ (LOLA_BAR1_PEAKMETERS_DEST00_01 + (dest) * 2)
+#define LOLA_BAR1_PEAKMETERS_AGC00_01 0x1948
+#define LOLA_BAR1_PEAKMETERS_AGC14_15 0x1964
+#define LOLA_BAR1_PEAKMETERS_AGC(x) \
+ (LOLA_BAR1_PEAKMETERS_AGC00_01 + (x) * 2)
+
+/* GCTL reset bit */
+#define LOLA_GCTL_RESET (1 << 0)
+/* GCTL unsolicited response enable bit */
+#define LOLA_GCTL_UREN (1 << 8)
+
+/* CORB/RIRB control, read/write pointer */
+#define LOLA_RBCTL_DMA_EN 0x02 /* enable DMA */
+#define LOLA_RBCTL_IRQ_EN 0x01 /* enable IRQ */
+#define LOLA_RBRWP_CLR 0x8000 /* read/write pointer clear */
+
+#define LOLA_RIRB_EX_UNSOL_EV 0x40000000
+#define LOLA_RIRB_EX_ERROR 0x80000000
+
+/* CORB int mask: CMEI[0] */
+#define LOLA_CORB_INT_CMEI 0x01
+#define LOLA_CORB_INT_MASK LOLA_CORB_INT_CMEI
+
+/* RIRB int mask: overrun[2], response[0] */
+#define LOLA_RIRB_INT_RESPONSE 0x01
+#define LOLA_RIRB_INT_OVERRUN 0x04
+#define LOLA_RIRB_INT_MASK (LOLA_RIRB_INT_RESPONSE | LOLA_RIRB_INT_OVERRUN)
+
+/* DINTCTL and DINTSTS */
+#define LOLA_DINT_GLOBAL 0x80000000 /* global interrupt enable bit */
+#define LOLA_DINT_CTRL 0x40000000 /* controller interrupt enable bit */
+#define LOLA_DINT_FIFOERR 0x20000000 /* global fifo error enable bit */
+#define LOLA_DINT_MUERR 0x10000000 /* global microcontroller underrun error */
+
+/* DSDnCTL bits */
+#define LOLA_DSD_CTL_SRST 0x01 /* stream reset bit */
+#define LOLA_DSD_CTL_SRUN 0x02 /* stream DMA start bit */
+#define LOLA_DSD_CTL_IOCE 0x04 /* interrupt on completion enable */
+#define LOLA_DSD_CTL_DEIE 0x10 /* descriptor error interrupt enable */
+#define LOLA_DSD_CTL_VLRCV 0x20 /* valid LRCountValue information in bits 8..31 */
+#define LOLA_LRC_MASK 0xffffff00
+
+/* DSDnSTS */
+#define LOLA_DSD_STS_BCIS 0x04 /* buffer completion interrupt status */
+#define LOLA_DSD_STS_DESE 0x10 /* descriptor error interrupt */
+#define LOLA_DSD_STS_FIFORDY 0x20 /* fifo ready */
+
+#define LOLA_CORB_ENTRIES 256
+
+#define MAX_STREAM_IN_COUNT 16
+#define MAX_STREAM_OUT_COUNT 16
+#define MAX_STREAM_COUNT 16
+#define MAX_PINS MAX_STREAM_COUNT
+#define MAX_STREAM_BUFFER_COUNT 16
+#define MAX_AUDIO_INOUT_COUNT 16
+
+#define LOLA_CLOCK_TYPE_INTERNAL 0
+#define LOLA_CLOCK_TYPE_AES 1
+#define LOLA_CLOCK_TYPE_AES_SYNC 2
+#define LOLA_CLOCK_TYPE_WORDCLOCK 3
+#define LOLA_CLOCK_TYPE_ETHERSOUND 4
+#define LOLA_CLOCK_TYPE_VIDEO 5
+
+#define LOLA_CLOCK_FORMAT_NONE 0
+#define LOLA_CLOCK_FORMAT_NTSC 1
+#define LOLA_CLOCK_FORMAT_PAL 2
+
+#define MAX_SAMPLE_CLOCK_COUNT 48
+
+/* parameters used with mixer widget's mixer capabilities */
+#define LOLA_PEAK_METER_CAN_AGC_MASK 1
+#define LOLA_PEAK_METER_CAN_ANALOG_CLIP_MASK 2
+
+struct lola_bar {
+ unsigned long addr;
+ void __iomem *remap_addr;
+};
+
+/* CORB/RIRB */
+struct lola_rb {
+ u32 *buf; /* CORB/RIRB buffer, 8 byte per each entry */
+ dma_addr_t addr; /* physical address of CORB/RIRB buffer */
+ unsigned short rp, wp; /* read/write pointers */
+ int cmds; /* number of pending requests */
+};
+
+/* Pin widget setup */
+struct lola_pin {
+ unsigned int nid;
+ bool is_analog;
+ unsigned int amp_mute;
+ unsigned int amp_step_size;
+ unsigned int amp_num_steps;
+ unsigned int amp_offset;
+ unsigned int max_level;
+ unsigned int config_default_reg;
+ unsigned int fixed_gain_list_len;
+ unsigned int cur_gain_step;
+};
+
+struct lola_pin_array {
+ unsigned int num_pins;
+ unsigned int num_analog_pins;
+ struct lola_pin pins[MAX_PINS];
+};
+
+/* Clock widget setup */
+struct lola_sample_clock {
+ unsigned int type;
+ unsigned int format;
+ unsigned int freq;
+};
+
+struct lola_clock_widget {
+ unsigned int nid;
+ unsigned int items;
+ unsigned int cur_index;
+ unsigned int cur_freq;
+ bool cur_valid;
+ struct lola_sample_clock sample_clock[MAX_SAMPLE_CLOCK_COUNT];
+ unsigned int idx_lookup[MAX_SAMPLE_CLOCK_COUNT];
+};
+
+#define LOLA_MIXER_DIM 32
+struct lola_mixer_array {
+ u32 src_gain_enable;
+ u32 dest_mix_gain_enable[LOLA_MIXER_DIM];
+ u16 src_gain[LOLA_MIXER_DIM];
+ u16 dest_mix_gain[LOLA_MIXER_DIM][LOLA_MIXER_DIM];
+};
+
+/* Mixer widget setup */
+struct lola_mixer_widget {
+ unsigned int nid;
+ unsigned int caps;
+ struct lola_mixer_array __user *array;
+ struct lola_mixer_array *array_saved;
+ unsigned int src_stream_outs;
+ unsigned int src_phys_ins;
+ unsigned int dest_stream_ins;
+ unsigned int dest_phys_outs;
+ unsigned int src_stream_out_ofs;
+ unsigned int dest_phys_out_ofs;
+ unsigned int src_mask;
+ unsigned int dest_mask;
+};
+
+/* Audio stream */
+struct lola_stream {
+ unsigned int nid; /* audio widget NID */
+ unsigned int index; /* array index */
+ unsigned int dsd; /* DSD index */
+ bool can_float;
+ struct snd_pcm_substream *substream; /* assigned PCM substream */
+ struct lola_stream *master; /* master stream (for multi-channel) */
+
+ /* buffer setup */
+ unsigned int bufsize;
+ unsigned int period_bytes;
+ unsigned int frags;
+
+ /* format + channel setup */
+ unsigned int format_verb;
+
+ /* flags */
+ unsigned int opened:1;
+ unsigned int prepared:1;
+ unsigned int paused:1;
+ unsigned int running:1;
+};
+
+#define PLAY SNDRV_PCM_STREAM_PLAYBACK
+#define CAPT SNDRV_PCM_STREAM_CAPTURE
+
+struct lola_pcm {
+ unsigned int num_streams;
+ struct snd_dma_buffer bdl; /* BDL buffer */
+ struct lola_stream streams[MAX_STREAM_COUNT];
+};
+
+/* card instance */
+struct lola {
+ struct snd_card *card;
+ struct pci_dev *pci;
+
+ /* pci resources */
+ struct lola_bar bar[2];
+ int irq;
+
+ /* locks */
+ spinlock_t reg_lock;
+ struct mutex open_mutex;
+
+ /* CORB/RIRB */
+ struct lola_rb corb;
+ struct lola_rb rirb;
+ unsigned int res, res_ex; /* last read values */
+ /* last command (for debugging) */
+ unsigned int last_cmd_nid, last_verb, last_data, last_extdata;
+
+ /* CORB/RIRB buffers */
+ struct snd_dma_buffer rb;
+
+ /* unsolicited events */
+ unsigned int last_unsol_res;
+
+ /* streams */
+ struct lola_pcm pcm[2];
+
+ /* input src */
+ unsigned int input_src_caps_mask;
+ unsigned int input_src_mask;
+
+ /* pins */
+ struct lola_pin_array pin[2];
+
+ /* clock */
+ struct lola_clock_widget clock;
+ int ref_count_rate;
+ unsigned int sample_rate;
+
+ /* mixer */
+ struct lola_mixer_widget mixer;
+
+ /* hw info */
+ unsigned int version;
+ unsigned int lola_caps;
+
+ /* parameters */
+ unsigned int granularity;
+ unsigned int sample_rate_min;
+ unsigned int sample_rate_max;
+
+ /* flags */
+ unsigned int initialized:1;
+ unsigned int cold_reset:1;
+ unsigned int polling_mode:1;
+
+ /* for debugging */
+ unsigned int debug_res;
+ unsigned int debug_res_ex;
+};
+
+#define BAR0 0
+#define BAR1 1
+
+/* Helper macros */
+#define lola_readl(chip, idx, name) \
+ readl((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_readw(chip, idx, name) \
+ readw((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_readb(chip, idx, name) \
+ readb((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_writel(chip, idx, name, val) \
+ writel((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_writew(chip, idx, name, val) \
+ writew((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_writeb(chip, idx, name, val) \
+ writeb((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+
+#define lola_dsd_read(chip, dsd, name) \
+ readl((chip)->bar[BAR1].remap_addr + LOLA_BAR1_DSD0_OFFSET + \
+ (LOLA_BAR1_DSD_SIZE * (dsd)) + LOLA_BAR1_DSDn##name)
+#define lola_dsd_write(chip, dsd, name, val) \
+ writel((val), (chip)->bar[BAR1].remap_addr + LOLA_BAR1_DSD0_OFFSET + \
+ (LOLA_BAR1_DSD_SIZE * (dsd)) + LOLA_BAR1_DSDn##name)
+
+/* GET verbs HDAudio */
+#define LOLA_VERB_GET_STREAM_FORMAT 0xa00
+#define LOLA_VERB_GET_AMP_GAIN_MUTE 0xb00
+#define LOLA_VERB_PARAMETERS 0xf00
+#define LOLA_VERB_GET_POWER_STATE 0xf05
+#define LOLA_VERB_GET_CONV 0xf06
+#define LOLA_VERB_GET_UNSOLICITED_RESPONSE 0xf08
+#define LOLA_VERB_GET_DIGI_CONVERT_1 0xf0d
+#define LOLA_VERB_GET_CONFIG_DEFAULT 0xf1c
+#define LOLA_VERB_GET_SUBSYSTEM_ID 0xf20
+/* GET verbs Digigram */
+#define LOLA_VERB_GET_FIXED_GAIN 0xfc0
+#define LOLA_VERB_GET_GAIN_SELECT 0xfc1
+#define LOLA_VERB_GET_MAX_LEVEL 0xfc2
+#define LOLA_VERB_GET_CLOCK_LIST 0xfc3
+#define LOLA_VERB_GET_CLOCK_SELECT 0xfc4
+#define LOLA_VERB_GET_CLOCK_STATUS 0xfc5
+
+/* SET verbs HDAudio */
+#define LOLA_VERB_SET_STREAM_FORMAT 0x200
+#define LOLA_VERB_SET_AMP_GAIN_MUTE 0x300
+#define LOLA_VERB_SET_POWER_STATE 0x705
+#define LOLA_VERB_SET_CHANNEL_STREAMID 0x706
+#define LOLA_VERB_SET_UNSOLICITED_ENABLE 0x708
+#define LOLA_VERB_SET_DIGI_CONVERT_1 0x70d
+/* SET verbs Digigram */
+#define LOLA_VERB_SET_GAIN_SELECT 0xf81
+#define LOLA_VERB_SET_CLOCK_SELECT 0xf84
+#define LOLA_VERB_SET_GRANULARITY_STEPS 0xf86
+#define LOLA_VERB_SET_SOURCE_GAIN 0xf87
+#define LOLA_VERB_SET_MIX_GAIN 0xf88
+#define LOLA_VERB_SET_DESTINATION_GAIN 0xf89
+#define LOLA_VERB_SET_SRC 0xf8a
+
+/* Parameter IDs used with LOLA_VERB_PARAMETERS */
+#define LOLA_PAR_VENDOR_ID 0x00
+#define LOLA_PAR_FUNCTION_TYPE 0x05
+#define LOLA_PAR_AUDIO_WIDGET_CAP 0x09
+#define LOLA_PAR_PCM 0x0a
+#define LOLA_PAR_STREAM_FORMATS 0x0b
+#define LOLA_PAR_PIN_CAP 0x0c
+#define LOLA_PAR_AMP_IN_CAP 0x0d
+#define LOLA_PAR_CONNLIST_LEN 0x0e
+#define LOLA_PAR_POWER_STATE 0x0f
+#define LOLA_PAR_GPIO_CAP 0x11
+#define LOLA_PAR_AMP_OUT_CAP 0x12
+#define LOLA_PAR_SPECIFIC_CAPS 0x80
+#define LOLA_PAR_FIXED_GAIN_LIST 0x81
+
+/* extract results of LOLA_PAR_SPECIFIC_CAPS */
+#define LOLA_AFG_MIXER_WIDGET_PRESENT(res) ((res & (1 << 21)) != 0)
+#define LOLA_AFG_CLOCK_WIDGET_PRESENT(res) ((res & (1 << 20)) != 0)
+#define LOLA_AFG_INPUT_PIN_COUNT(res) ((res >> 10) & 0x2ff)
+#define LOLA_AFG_OUTPUT_PIN_COUNT(res) ((res) & 0x2ff)
+
+/* extract results of LOLA_PAR_AMP_IN_CAP / LOLA_PAR_AMP_OUT_CAP */
+#define LOLA_AMP_MUTE_CAPABLE(res) ((res & (1 << 31)) != 0)
+#define LOLA_AMP_STEP_SIZE(res) ((res >> 24) & 0x7f)
+#define LOLA_AMP_NUM_STEPS(res) ((res >> 12) & 0x3ff)
+#define LOLA_AMP_OFFSET(res) ((res) & 0x3ff)
+
+#define LOLA_GRANULARITY_MIN 8
+#define LOLA_GRANULARITY_MAX 32
+#define LOLA_GRANULARITY_STEP 8
+
+/* parameters used with unsolicited command/response */
+#define LOLA_UNSOLICITED_TAG_MASK 0x3f
+#define LOLA_UNSOLICITED_TAG 0x1a
+#define LOLA_UNSOLICITED_ENABLE 0x80
+#define LOLA_UNSOL_RESP_TAG_OFFSET 26
+
+/* count values in the Vendor Specific Mixer Widget's Audio Widget Capabilities */
+#define LOLA_MIXER_SRC_INPUT_PLAY_SEPARATION(res) ((res >> 2) & 0x1f)
+#define LOLA_MIXER_DEST_REC_OUTPUT_SEPATATION(res) ((res >> 7) & 0x1f)
+
+int lola_codec_write(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata);
+int lola_codec_read(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata,
+ unsigned int *val, unsigned int *extval);
+int lola_codec_flush(struct lola *chip);
+#define lola_read_param(chip, nid, param, val) \
+ lola_codec_read(chip, nid, LOLA_VERB_PARAMETERS, param, 0, val, NULL)
+
+/* PCM */
+int lola_create_pcm(struct lola *chip);
+void lola_free_pcm(struct lola *chip);
+int lola_init_pcm(struct lola *chip, int dir, int *nidp);
+void lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits);
+
+/* clock */
+int lola_init_clock_widget(struct lola *chip, int nid);
+int lola_set_granularity(struct lola *chip, unsigned int val, bool force);
+int lola_enable_clock_events(struct lola *chip);
+int lola_set_clock_index(struct lola *chip, unsigned int idx);
+int lola_set_clock(struct lola *chip, int idx);
+int lola_set_sample_rate(struct lola *chip, int rate);
+bool lola_update_ext_clock_freq(struct lola *chip, unsigned int val);
+unsigned int lola_sample_rate_convert(unsigned int coded);
+
+/* mixer */
+int lola_init_pins(struct lola *chip, int dir, int *nidp);
+int lola_init_mixer_widget(struct lola *chip, int nid);
+void lola_free_mixer(struct lola *chip);
+int lola_create_mixer(struct lola *chip);
+int lola_setup_all_analog_gains(struct lola *chip, int dir, bool mute);
+void lola_save_mixer(struct lola *chip);
+void lola_restore_mixer(struct lola *chip);
+int lola_set_src_config(struct lola *chip, unsigned int src_mask, bool update);
+
+/* proc */
+#ifdef CONFIG_SND_DEBUG
+void lola_proc_debug_new(struct lola *chip);
+#else
+#define lola_proc_debug_new(chip)
+#endif
+
+#endif /* _LOLA_H */
--- /dev/null
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "lola.h"
+
+unsigned int lola_sample_rate_convert(unsigned int coded)
+{
+ unsigned int freq;
+
+ /* base frequency */
+ switch (coded & 0x3) {
+ case 0: freq = 48000; break;
+ case 1: freq = 44100; break;
+ case 2: freq = 32000; break;
+ default: return 0; /* error */
+ }
+
+ /* multiplier / devisor */
+ switch (coded & 0x1c) {
+ case (0 << 2): break;
+ case (4 << 2): break;
+ case (1 << 2): freq *= 2; break;
+ case (2 << 2): freq *= 4; break;
+ case (5 << 2): freq /= 2; break;
+ case (6 << 2): freq /= 4; break;
+ default: return 0; /* error */
+ }
+
+ /* ajustement */
+ switch (coded & 0x60) {
+ case (0 << 5): break;
+ case (1 << 5): freq = (freq * 999) / 1000; break;
+ case (2 << 5): freq = (freq * 1001) / 1000; break;
+ default: return 0; /* error */
+ }
+ return freq;
+}
+
+/*
+ * Granualrity
+ */
+
+#define LOLA_MAXFREQ_AT_GRANULARITY_MIN 48000
+#define LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX 96000
+
+static bool check_gran_clock_compatibility(struct lola *chip,
+ unsigned int val,
+ unsigned int freq)
+{
+ if (!chip->granularity)
+ return true;
+
+ if (val < LOLA_GRANULARITY_MIN || val > LOLA_GRANULARITY_MAX ||
+ (val % LOLA_GRANULARITY_STEP) != 0)
+ return false;
+
+ if (val == LOLA_GRANULARITY_MIN) {
+ if (freq > LOLA_MAXFREQ_AT_GRANULARITY_MIN)
+ return false;
+ } else if (val < LOLA_GRANULARITY_MAX) {
+ if (freq > LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX)
+ return false;
+ }
+ return true;
+}
+
+int lola_set_granularity(struct lola *chip, unsigned int val, bool force)
+{
+ int err;
+
+ if (!force) {
+ if (val == chip->granularity)
+ return 0;
+#if 0
+ /* change Gran only if there are no streams allocated ! */
+ if (chip->audio_in_alloc_mask || chip->audio_out_alloc_mask)
+ return -EBUSY;
+#endif
+ if (!check_gran_clock_compatibility(chip, val,
+ chip->clock.cur_freq))
+ return -EINVAL;
+ }
+
+ chip->granularity = val;
+ val /= LOLA_GRANULARITY_STEP;
+
+ /* audio function group */
+ err = lola_codec_write(chip, 1, LOLA_VERB_SET_GRANULARITY_STEPS,
+ val, 0);
+ if (err < 0)
+ return err;
+ /* this can be a very slow function !!! */
+ usleep_range(400 * val, 20000);
+ return lola_codec_flush(chip);
+}
+
+/*
+ * Clock widget handling
+ */
+
+int __devinit lola_init_clock_widget(struct lola *chip, int nid)
+{
+ unsigned int val;
+ int i, j, nitems, nb_verbs, idx, idx_list;
+ int err;
+
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+
+ if ((val & 0xfff00000) != 0x01f00000) { /* test SubType and Type */
+ snd_printdd("No valid clock widget\n");
+ return 0;
+ }
+
+ chip->clock.nid = nid;
+ chip->clock.items = val & 0xff;
+ snd_printdd("clock_list nid=%x, entries=%d\n", nid,
+ chip->clock.items);
+ if (chip->clock.items > MAX_SAMPLE_CLOCK_COUNT) {
+ printk(KERN_ERR SFX "CLOCK_LIST too big: %d\n",
+ chip->clock.items);
+ return -EINVAL;
+ }
+
+ nitems = chip->clock.items;
+ nb_verbs = (nitems + 3) / 4;
+ idx = 0;
+ idx_list = 0;
+ for (i = 0; i < nb_verbs; i++) {
+ unsigned int res_ex;
+ unsigned short items[4];
+
+ err = lola_codec_read(chip, nid, LOLA_VERB_GET_CLOCK_LIST,
+ idx, 0, &val, &res_ex);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read CLOCK_LIST\n");
+ return -EINVAL;
+ }
+
+ items[0] = val & 0xfff;
+ items[1] = (val >> 16) & 0xfff;
+ items[2] = res_ex & 0xfff;
+ items[3] = (res_ex >> 16) & 0xfff;
+
+ for (j = 0; j < 4; j++) {
+ unsigned char type = items[j] >> 8;
+ unsigned int freq = items[j] & 0xff;
+ int format = LOLA_CLOCK_FORMAT_NONE;
+ bool add_clock = true;
+ if (type == LOLA_CLOCK_TYPE_INTERNAL) {
+ freq = lola_sample_rate_convert(freq);
+ if (freq < chip->sample_rate_min)
+ add_clock = false;
+ else if (freq == 48000) {
+ chip->clock.cur_index = idx_list;
+ chip->clock.cur_freq = 48000;
+ chip->clock.cur_valid = true;
+ }
+ } else if (type == LOLA_CLOCK_TYPE_VIDEO) {
+ freq = lola_sample_rate_convert(freq);
+ if (freq < chip->sample_rate_min)
+ add_clock = false;
+ /* video clock has a format (0:NTSC, 1:PAL)*/
+ if (items[j] & 0x80)
+ format = LOLA_CLOCK_FORMAT_NTSC;
+ else
+ format = LOLA_CLOCK_FORMAT_PAL;
+ }
+ if (add_clock) {
+ struct lola_sample_clock *sc;
+ sc = &chip->clock.sample_clock[idx_list];
+ sc->type = type;
+ sc->format = format;
+ sc->freq = freq;
+ /* keep the index used with the board */
+ chip->clock.idx_lookup[idx_list] = idx;
+ idx_list++;
+ } else {
+ chip->clock.items--;
+ }
+ if (++idx >= nitems)
+ break;
+ }
+ }
+ return 0;
+}
+
+/* enable unsolicited events of the clock widget */
+int lola_enable_clock_events(struct lola *chip)
+{
+ unsigned int res;
+ int err;
+
+ err = lola_codec_read(chip, chip->clock.nid,
+ LOLA_VERB_SET_UNSOLICITED_ENABLE,
+ LOLA_UNSOLICITED_ENABLE | LOLA_UNSOLICITED_TAG,
+ 0, &res, NULL);
+ if (err < 0)
+ return err;
+ if (res) {
+ printk(KERN_WARNING SFX "error in enable_clock_events %d\n",
+ res);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int lola_set_clock_index(struct lola *chip, unsigned int idx)
+{
+ unsigned int res;
+ int err;
+
+ err = lola_codec_read(chip, chip->clock.nid,
+ LOLA_VERB_SET_CLOCK_SELECT,
+ chip->clock.idx_lookup[idx],
+ 0, &res, NULL);
+ if (err < 0)
+ return err;
+ if (res) {
+ printk(KERN_WARNING SFX "error in set_clock %d\n", res);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+bool lola_update_ext_clock_freq(struct lola *chip, unsigned int val)
+{
+ unsigned int tag;
+
+ /* the current EXTERNAL clock information gets updated by interrupt
+ * with an unsolicited response
+ */
+ if (!val)
+ return false;
+ tag = (val >> LOLA_UNSOL_RESP_TAG_OFFSET) & LOLA_UNSOLICITED_TAG_MASK;
+ if (tag != LOLA_UNSOLICITED_TAG)
+ return false;
+
+ /* only for current = external clocks */
+ if (chip->clock.sample_clock[chip->clock.cur_index].type !=
+ LOLA_CLOCK_TYPE_INTERNAL) {
+ chip->clock.cur_freq = lola_sample_rate_convert(val & 0x7f);
+ chip->clock.cur_valid = (val & 0x100) != 0;
+ }
+ return true;
+}
+
+int lola_set_clock(struct lola *chip, int idx)
+{
+ int freq = 0;
+ bool valid = false;
+
+ if (idx == chip->clock.cur_index) {
+ /* current clock is allowed */
+ freq = chip->clock.cur_freq;
+ valid = chip->clock.cur_valid;
+ } else if (chip->clock.sample_clock[idx].type ==
+ LOLA_CLOCK_TYPE_INTERNAL) {
+ /* internal clocks allowed */
+ freq = chip->clock.sample_clock[idx].freq;
+ valid = true;
+ }
+
+ if (!freq || !valid)
+ return -EINVAL;
+
+ if (!check_gran_clock_compatibility(chip, chip->granularity, freq))
+ return -EINVAL;
+
+ if (idx != chip->clock.cur_index) {
+ int err = lola_set_clock_index(chip, idx);
+ if (err < 0)
+ return err;
+ /* update new settings */
+ chip->clock.cur_index = idx;
+ chip->clock.cur_freq = freq;
+ chip->clock.cur_valid = true;
+ }
+ return 0;
+}
+
+int lola_set_sample_rate(struct lola *chip, int rate)
+{
+ int i;
+
+ if (chip->clock.cur_freq == rate && chip->clock.cur_valid)
+ return 0;
+ /* search for new dwClockIndex */
+ for (i = 0; i < chip->clock.items; i++) {
+ if (chip->clock.sample_clock[i].type == LOLA_CLOCK_TYPE_INTERNAL &&
+ chip->clock.sample_clock[i].freq == rate)
+ break;
+ }
+ if (i >= chip->clock.items)
+ return -EINVAL;
+ return lola_set_clock(chip, i);
+}
+
--- /dev/null
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include "lola.h"
+
+static int __devinit lola_init_pin(struct lola *chip, struct lola_pin *pin,
+ int dir, int nid)
+{
+ unsigned int val;
+ int err;
+
+ pin->nid = nid;
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+ val &= 0x00f00fff; /* test TYPE and bits 0..11 */
+ if (val == 0x00400200) /* Type = 4, Digital = 1 */
+ pin->is_analog = false;
+ else if (val == 0x0040000a && dir == CAPT) /* Dig=0, InAmp/ovrd */
+ pin->is_analog = true;
+ else if (val == 0x0040000c && dir == PLAY) /* Dig=0, OutAmp/ovrd */
+ pin->is_analog = true;
+ else {
+ printk(KERN_ERR SFX "Invalid wcaps 0x%x for 0x%x\n", val, nid);
+ return -EINVAL;
+ }
+
+ /* analog parameters only following, so continue in case of Digital pin
+ */
+ if (!pin->is_analog)
+ return 0;
+
+ if (dir == PLAY)
+ err = lola_read_param(chip, nid, LOLA_PAR_AMP_OUT_CAP, &val);
+ else
+ err = lola_read_param(chip, nid, LOLA_PAR_AMP_IN_CAP, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read AMP-caps for 0x%x\n", nid);
+ return err;
+ }
+
+ pin->amp_mute = LOLA_AMP_MUTE_CAPABLE(val);
+ pin->amp_step_size = LOLA_AMP_STEP_SIZE(val);
+ pin->amp_num_steps = LOLA_AMP_NUM_STEPS(val);
+ if (pin->amp_num_steps) {
+ /* zero as mute state */
+ pin->amp_num_steps++;
+ pin->amp_step_size++;
+ }
+ pin->amp_offset = LOLA_AMP_OFFSET(val);
+
+ err = lola_codec_read(chip, nid, LOLA_VERB_GET_MAX_LEVEL, 0, 0, &val,
+ NULL);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't get MAX_LEVEL 0x%x\n", nid);
+ return err;
+ }
+ pin->max_level = val & 0x3ff; /* 10 bits */
+
+ pin->config_default_reg = 0;
+ pin->fixed_gain_list_len = 0;
+ pin->cur_gain_step = 0;
+
+ return 0;
+}
+
+int __devinit lola_init_pins(struct lola *chip, int dir, int *nidp)
+{
+ int i, err, nid;
+ nid = *nidp;
+ for (i = 0; i < chip->pin[dir].num_pins; i++, nid++) {
+ err = lola_init_pin(chip, &chip->pin[dir].pins[i], dir, nid);
+ if (err < 0)
+ return err;
+ if (chip->pin[dir].pins[i].is_analog)
+ chip->pin[dir].num_analog_pins++;
+ }
+ *nidp = nid;
+ return 0;
+}
+
+void lola_free_mixer(struct lola *chip)
+{
+ if (chip->mixer.array_saved)
+ vfree(chip->mixer.array_saved);
+}
+
+int __devinit lola_init_mixer_widget(struct lola *chip, int nid)
+{
+ unsigned int val;
+ int err;
+
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+
+ if ((val & 0xfff00000) != 0x02f00000) { /* test SubType and Type */
+ snd_printdd("No valid mixer widget\n");
+ return 0;
+ }
+
+ chip->mixer.nid = nid;
+ chip->mixer.caps = val;
+ chip->mixer.array = (struct lola_mixer_array __iomem *)
+ (chip->bar[BAR1].remap_addr + LOLA_BAR1_SOURCE_GAIN_ENABLE);
+
+ /* reserve memory to copy mixer data for sleep mode transitions */
+ chip->mixer.array_saved = vmalloc(sizeof(struct lola_mixer_array));
+
+ /* mixer matrix sources are physical input data and play streams */
+ chip->mixer.src_stream_outs = chip->pcm[PLAY].num_streams;
+ chip->mixer.src_phys_ins = chip->pin[CAPT].num_pins;
+
+ /* mixer matrix destinations are record streams and physical output */
+ chip->mixer.dest_stream_ins = chip->pcm[CAPT].num_streams;
+ chip->mixer.dest_phys_outs = chip->pin[PLAY].num_pins;
+
+ /* mixer matrix can have unused areas between PhysIn and
+ * Play or Record and PhysOut zones
+ */
+ chip->mixer.src_stream_out_ofs = chip->mixer.src_phys_ins +
+ LOLA_MIXER_SRC_INPUT_PLAY_SEPARATION(val);
+ chip->mixer.dest_phys_out_ofs = chip->mixer.dest_stream_ins +
+ LOLA_MIXER_DEST_REC_OUTPUT_SEPATATION(val);
+
+ /* example : MixerMatrix of LoLa881
+ * 0-------8------16-------8------16
+ * | | | | |
+ * | INPUT | | INPUT | |
+ * | -> |unused | -> |unused |
+ * | RECORD| | OUTPUT| |
+ * | | | | |
+ * 8--------------------------------
+ * | | | | |
+ * | | | | |
+ * |unused |unused |unused |unused |
+ * | | | | |
+ * | | | | |
+ * 16-------------------------------
+ * | | | | |
+ * | PLAY | | PLAY | |
+ * | -> |unused | -> |unused |
+ * | RECORD| | OUTPUT| |
+ * | | | | |
+ * 8--------------------------------
+ * | | | | |
+ * | | | | |
+ * |unused |unused |unused |unused |
+ * | | | | |
+ * | | | | |
+ * 16-------------------------------
+ */
+ if (chip->mixer.src_stream_out_ofs > MAX_AUDIO_INOUT_COUNT ||
+ chip->mixer.dest_phys_out_ofs > MAX_STREAM_IN_COUNT) {
+ printk(KERN_ERR SFX "Invalid mixer widget size\n");
+ return -EINVAL;
+ }
+
+ chip->mixer.src_mask = ((1U << chip->mixer.src_phys_ins) - 1) |
+ (((1U << chip->mixer.src_stream_outs) - 1)
+ << chip->mixer.src_stream_out_ofs);
+ chip->mixer.dest_mask = ((1U << chip->mixer.dest_stream_ins) - 1) |
+ (((1U << chip->mixer.dest_phys_outs) - 1)
+ << chip->mixer.dest_phys_out_ofs);
+
+ return 0;
+}
+
+static int lola_mixer_set_src_gain(struct lola *chip, unsigned int id,
+ unsigned short gain, bool on)
+{
+ unsigned int oldval, val;
+
+ if (!(chip->mixer.src_mask & (1 << id)))
+ return -EINVAL;
+ writew(gain, &chip->mixer.array->src_gain[id]);
+ oldval = val = readl(&chip->mixer.array->src_gain_enable);
+ if (on)
+ val |= (1 << id);
+ else
+ val &= ~(1 << id);
+ writel(val, &chip->mixer.array->src_gain_enable);
+ lola_codec_flush(chip);
+ /* inform micro-controller about the new source gain */
+ return lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN, id, 0);
+}
+
+#if 0 /* not used */
+static int lola_mixer_set_src_gains(struct lola *chip, unsigned int mask,
+ unsigned short *gains)
+{
+ int i;
+
+ if ((chip->mixer.src_mask & mask) != mask)
+ return -EINVAL;
+ for (i = 0; i < LOLA_MIXER_DIM; i++) {
+ if (mask & (1 << i)) {
+ writew(*gains, &chip->mixer.array->src_gain[i]);
+ gains++;
+ }
+ }
+ writel(mask, &chip->mixer.array->src_gain_enable);
+ lola_codec_flush(chip);
+ if (chip->mixer.caps & LOLA_PEAK_METER_CAN_AGC_MASK) {
+ /* update for all srcs at once */
+ return lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN, 0x80, 0);
+ }
+ /* update manually */
+ for (i = 0; i < LOLA_MIXER_DIM; i++) {
+ if (mask & (1 << i)) {
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN, i, 0);
+ }
+ }
+ return 0;
+}
+#endif /* not used */
+
+static int lola_mixer_set_mapping_gain(struct lola *chip,
+ unsigned int src, unsigned int dest,
+ unsigned short gain, bool on)
+{
+ unsigned int val;
+
+ if (!(chip->mixer.src_mask & (1 << src)) ||
+ !(chip->mixer.dest_mask & (1 << dest)))
+ return -EINVAL;
+ if (on)
+ writew(gain, &chip->mixer.array->dest_mix_gain[dest][src]);
+ val = readl(&chip->mixer.array->dest_mix_gain_enable[dest]);
+ if (on)
+ val |= (1 << src);
+ else
+ val &= ~(1 << src);
+ writel(val, &chip->mixer.array->dest_mix_gain_enable[dest]);
+ lola_codec_flush(chip);
+ return lola_codec_write(chip, chip->mixer.nid, LOLA_VERB_SET_MIX_GAIN,
+ src, dest);
+}
+
+static int lola_mixer_set_dest_gains(struct lola *chip, unsigned int id,
+ unsigned int mask, unsigned short *gains)
+{
+ int i;
+
+ if (!(chip->mixer.dest_mask & (1 << id)) ||
+ (chip->mixer.src_mask & mask) != mask)
+ return -EINVAL;
+ for (i = 0; i < LOLA_MIXER_DIM; i++) {
+ if (mask & (1 << i)) {
+ writew(*gains, &chip->mixer.array->dest_mix_gain[id][i]);
+ gains++;
+ }
+ }
+ writel(mask, &chip->mixer.array->dest_mix_gain_enable[id]);
+ lola_codec_flush(chip);
+ /* update for all dests at once */
+ return lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN, id, 0);
+}
+
+/*
+ */
+
+static int set_analog_volume(struct lola *chip, int dir,
+ unsigned int idx, unsigned int val,
+ bool external_call);
+
+int lola_setup_all_analog_gains(struct lola *chip, int dir, bool mute)
+{
+ struct lola_pin *pin;
+ int idx, max_idx;
+
+ pin = chip->pin[dir].pins;
+ max_idx = chip->pin[dir].num_pins;
+ for (idx = 0; idx < max_idx; idx++) {
+ if (pin[idx].is_analog) {
+ unsigned int val = mute ? 0 : pin[idx].cur_gain_step;
+ /* set volume and do not save the value */
+ set_analog_volume(chip, dir, idx, val, false);
+ }
+ }
+ return lola_codec_flush(chip);
+}
+
+void lola_save_mixer(struct lola *chip)
+{
+ /* mute analog output */
+ if (chip->mixer.array_saved) {
+ /* store contents of mixer array */
+ memcpy_fromio(chip->mixer.array_saved, chip->mixer.array,
+ sizeof(*chip->mixer.array));
+ }
+ lola_setup_all_analog_gains(chip, PLAY, true); /* output mute */
+}
+
+void lola_restore_mixer(struct lola *chip)
+{
+ int i;
+
+ /*lola_reset_setups(chip);*/
+ if (chip->mixer.array_saved) {
+ /* restore contents of mixer array */
+ memcpy_toio(chip->mixer.array, chip->mixer.array_saved,
+ sizeof(*chip->mixer.array));
+ /* inform micro-controller about all restored values
+ * and ignore return values
+ */
+ for (i = 0; i < chip->mixer.src_phys_ins; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN,
+ i, 0);
+ for (i = 0; i < chip->mixer.src_stream_outs; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN,
+ chip->mixer.src_stream_out_ofs + i, 0);
+ for (i = 0; i < chip->mixer.dest_stream_ins; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN,
+ i, 0);
+ for (i = 0; i < chip->mixer.dest_phys_outs; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN,
+ chip->mixer.dest_phys_out_ofs + i, 0);
+ lola_codec_flush(chip);
+ }
+}
+
+/*
+ */
+
+static int set_analog_volume(struct lola *chip, int dir,
+ unsigned int idx, unsigned int val,
+ bool external_call)
+{
+ struct lola_pin *pin;
+ int err;
+
+ if (idx >= chip->pin[dir].num_pins)
+ return -EINVAL;
+ pin = &chip->pin[dir].pins[idx];
+ if (!pin->is_analog || pin->amp_num_steps <= val)
+ return -EINVAL;
+ if (external_call && pin->cur_gain_step == val)
+ return 0;
+ if (external_call)
+ lola_codec_flush(chip);
+ err = lola_codec_write(chip, pin->nid,
+ LOLA_VERB_SET_AMP_GAIN_MUTE, val, 0);
+ if (err < 0)
+ return err;
+ if (external_call)
+ pin->cur_gain_step = val;
+ return 0;
+}
+
+int lola_set_src_config(struct lola *chip, unsigned int src_mask, bool update)
+{
+ int ret = 0;
+ int success = 0;
+ int n, err;
+
+ /* SRC can be activated and the dwInputSRCMask is valid? */
+ if ((chip->input_src_caps_mask & src_mask) != src_mask)
+ return -EINVAL;
+ /* handle all even Inputs - SRC is a stereo setting !!! */
+ for (n = 0; n < chip->pin[CAPT].num_pins; n += 2) {
+ unsigned int mask = 3U << n; /* handle the stereo case */
+ unsigned int new_src, src_state;
+ if (!(chip->input_src_caps_mask & mask))
+ continue;
+ /* if one IO needs SRC, both stereo IO will get SRC */
+ new_src = (src_mask & mask) != 0;
+ if (update) {
+ src_state = (chip->input_src_mask & mask) != 0;
+ if (src_state == new_src)
+ continue; /* nothing to change for this IO */
+ }
+ err = lola_codec_write(chip, chip->pcm[CAPT].streams[n].nid,
+ LOLA_VERB_SET_SRC, new_src, 0);
+ if (!err)
+ success++;
+ else
+ ret = err;
+ }
+ if (success)
+ ret = lola_codec_flush(chip);
+ if (!ret)
+ chip->input_src_mask = src_mask;
+ return ret;
+}
+
+/*
+ */
+static int init_mixer_values(struct lola *chip)
+{
+ int i;
+
+ /* all src on */
+ lola_set_src_config(chip, (1 << chip->pin[CAPT].num_pins) - 1, false);
+
+ /* clear all matrix */
+ memset_io(chip->mixer.array, 0, sizeof(*chip->mixer.array));
+ /* set src gain to 0dB */
+ for (i = 0; i < chip->mixer.src_phys_ins; i++)
+ lola_mixer_set_src_gain(chip, i, 336, true); /* 0dB */
+ for (i = 0; i < chip->mixer.src_stream_outs; i++)
+ lola_mixer_set_src_gain(chip,
+ i + chip->mixer.src_stream_out_ofs,
+ 336, true); /* 0dB */
+ /* set 1:1 dest gain */
+ for (i = 0; i < chip->mixer.dest_stream_ins; i++) {
+ int src = i % chip->mixer.src_phys_ins;
+ lola_mixer_set_mapping_gain(chip, src, i, 336, true);
+ }
+ for (i = 0; i < chip->mixer.src_stream_outs; i++) {
+ int src = chip->mixer.src_stream_out_ofs + i;
+ int dst = chip->mixer.dest_phys_out_ofs +
+ i % chip->mixer.dest_phys_outs;
+ lola_mixer_set_mapping_gain(chip, src, dst, 336, true);
+ }
+ return 0;
+}
+
+/*
+ * analog mixer control element
+ */
+static int lola_analog_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chip->pin[dir].num_pins;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = chip->pin[dir].pins[0].amp_num_steps;
+ return 0;
+}
+
+static int lola_analog_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+ int i;
+
+ for (i = 0; i < chip->pin[dir].num_pins; i++)
+ ucontrol->value.integer.value[i] =
+ chip->pin[dir].pins[i].cur_gain_step;
+ return 0;
+}
+
+static int lola_analog_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+ int i, err;
+
+ for (i = 0; i < chip->pin[dir].num_pins; i++) {
+ err = set_analog_volume(chip, dir, i,
+ ucontrol->value.integer.value[i],
+ true);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int lola_analog_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+ unsigned int val1, val2;
+ struct lola_pin *pin;
+
+ if (size < 4 * sizeof(unsigned int))
+ return -ENOMEM;
+ pin = &chip->pin[dir].pins[0];
+
+ val2 = pin->amp_step_size * 25;
+ val1 = -1 * (int)pin->amp_offset * (int)val2;
+#ifdef TLV_DB_SCALE_MUTE
+ val2 |= TLV_DB_SCALE_MUTE;
+#endif
+ if (put_user(SNDRV_CTL_TLVT_DB_SCALE, tlv))
+ return -EFAULT;
+ if (put_user(2 * sizeof(unsigned int), tlv + 1))
+ return -EFAULT;
+ if (put_user(val1, tlv + 2))
+ return -EFAULT;
+ if (put_user(val2, tlv + 3))
+ return -EFAULT;
+ return 0;
+}
+
+static struct snd_kcontrol_new lola_analog_mixer __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK),
+ .info = lola_analog_vol_info,
+ .get = lola_analog_vol_get,
+ .put = lola_analog_vol_put,
+ .tlv.c = lola_analog_vol_tlv,
+};
+
+static int __devinit create_analog_mixer(struct lola *chip, int dir, char *name)
+{
+ if (!chip->pin[dir].num_pins)
+ return 0;
+ /* no analog volumes on digital only adapters */
+ if (chip->pin[dir].num_pins != chip->pin[dir].num_analog_pins)
+ return 0;
+ lola_analog_mixer.name = name;
+ lola_analog_mixer.private_value = dir;
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_analog_mixer, chip));
+}
+
+/*
+ * Hardware sample rate converter on digital input
+ */
+static int lola_input_src_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = chip->pin[CAPT].num_pins;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int lola_input_src_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int i;
+
+ for (i = 0; i < chip->pin[CAPT].num_pins; i++)
+ ucontrol->value.integer.value[i] =
+ !!(chip->input_src_mask & (1 << i));
+ return 0;
+}
+
+static int lola_input_src_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int i;
+ unsigned int mask;
+
+ mask = 0;
+ for (i = 0; i < chip->pin[CAPT].num_pins; i++)
+ if (ucontrol->value.integer.value[i])
+ mask |= 1 << i;
+ return lola_set_src_config(chip, mask, true);
+}
+
+static struct snd_kcontrol_new lola_input_src_mixer __devinitdata = {
+ .name = "Digital SRC Capture Switch",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = lola_input_src_info,
+ .get = lola_input_src_get,
+ .put = lola_input_src_put,
+};
+
+/*
+ * Lola16161 or Lola881 can have Hardware sample rate converters
+ * on its digital input pins
+ */
+static int __devinit create_input_src_mixer(struct lola *chip)
+{
+ if (!chip->input_src_caps_mask)
+ return 0;
+
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_input_src_mixer, chip));
+}
+
+/*
+ * src gain mixer
+ */
+static int lola_src_gain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int count = (kcontrol->private_value >> 8) & 0xff;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 409;
+ return 0;
+}
+
+static int lola_src_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int ofs = kcontrol->private_value & 0xff;
+ unsigned int count = (kcontrol->private_value >> 8) & 0xff;
+ unsigned int mask, i;
+
+ mask = readl(&chip->mixer.array->src_gain_enable);
+ for (i = 0; i < count; i++) {
+ unsigned int idx = ofs + i;
+ unsigned short val;
+ if (!(chip->mixer.src_mask & (1 << idx)))
+ return -EINVAL;
+ if (mask & (1 << idx))
+ val = readw(&chip->mixer.array->src_gain[idx]) + 1;
+ else
+ val = 0;
+ ucontrol->value.integer.value[i] = val;
+ }
+ return 0;
+}
+
+static int lola_src_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int ofs = kcontrol->private_value & 0xff;
+ unsigned int count = (kcontrol->private_value >> 8) & 0xff;
+ int i, err;
+
+ for (i = 0; i < count; i++) {
+ unsigned int idx = ofs + i;
+ unsigned short val = ucontrol->value.integer.value[i];
+ if (val)
+ val--;
+ err = lola_mixer_set_src_gain(chip, idx, val, !!val);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* raw value: 0 = -84dB, 336 = 0dB, 408=18dB, incremented 1 for mute */
+static const DECLARE_TLV_DB_SCALE(lola_src_gain_tlv, -8425, 25, 1);
+
+static struct snd_kcontrol_new lola_src_gain_mixer __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = lola_src_gain_info,
+ .get = lola_src_gain_get,
+ .put = lola_src_gain_put,
+ .tlv.p = lola_src_gain_tlv,
+};
+
+static int __devinit create_src_gain_mixer(struct lola *chip,
+ int num, int ofs, char *name)
+{
+ lola_src_gain_mixer.name = name;
+ lola_src_gain_mixer.private_value = ofs + (num << 8);
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_src_gain_mixer, chip));
+}
+
+/*
+ * destination gain (matrix-like) mixer
+ */
+static int lola_dest_gain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = src_num;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 433;
+ return 0;
+}
+
+static int lola_dest_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int src_ofs = kcontrol->private_value & 0xff;
+ unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
+ unsigned int dst_ofs = (kcontrol->private_value >> 16) & 0xff;
+ unsigned int dst, mask, i;
+
+ dst = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + dst_ofs;
+ mask = readl(&chip->mixer.array->dest_mix_gain_enable[dst]);
+ for (i = 0; i < src_num; i++) {
+ unsigned int src = src_ofs + i;
+ unsigned short val;
+ if (!(chip->mixer.src_mask & (1 << src)))
+ return -EINVAL;
+ if (mask & (1 << dst))
+ val = readw(&chip->mixer.array->dest_mix_gain[dst][src]) + 1;
+ else
+ val = 0;
+ ucontrol->value.integer.value[i] = val;
+ }
+ return 0;
+}
+
+static int lola_dest_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int src_ofs = kcontrol->private_value & 0xff;
+ unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
+ unsigned int dst_ofs = (kcontrol->private_value >> 16) & 0xff;
+ unsigned int dst, mask;
+ unsigned short gains[MAX_STREAM_COUNT];
+ int i, num;
+
+ mask = 0;
+ num = 0;
+ for (i = 0; i < src_num; i++) {
+ unsigned short val = ucontrol->value.integer.value[i];
+ if (val) {
+ gains[num++] = val - 1;
+ mask |= 1 << i;
+ }
+ }
+ mask <<= src_ofs;
+ dst = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + dst_ofs;
+ return lola_mixer_set_dest_gains(chip, dst, mask, gains);
+}
+
+static const DECLARE_TLV_DB_SCALE(lola_dest_gain_tlv, -8425, 25, 1);
+
+static struct snd_kcontrol_new lola_dest_gain_mixer __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = lola_dest_gain_info,
+ .get = lola_dest_gain_get,
+ .put = lola_dest_gain_put,
+ .tlv.p = lola_dest_gain_tlv,
+};
+
+static int __devinit create_dest_gain_mixer(struct lola *chip,
+ int src_num, int src_ofs,
+ int num, int ofs, char *name)
+{
+ lola_dest_gain_mixer.count = num;
+ lola_dest_gain_mixer.name = name;
+ lola_dest_gain_mixer.private_value =
+ src_ofs + (src_num << 8) + (ofs << 16) + (num << 24);
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_dest_gain_mixer, chip));
+}
+
+/*
+ */
+int __devinit lola_create_mixer(struct lola *chip)
+{
+ int err;
+
+ err = create_analog_mixer(chip, PLAY, "Analog Playback Volume");
+ if (err < 0)
+ return err;
+ err = create_analog_mixer(chip, CAPT, "Analog Capture Volume");
+ if (err < 0)
+ return err;
+ err = create_input_src_mixer(chip);
+ if (err < 0)
+ return err;
+ err = create_src_gain_mixer(chip, chip->mixer.src_phys_ins, 0,
+ "Line Source Gain Volume");
+ if (err < 0)
+ return err;
+ err = create_src_gain_mixer(chip, chip->mixer.src_stream_outs,
+ chip->mixer.src_stream_out_ofs,
+ "Stream Source Gain Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_phys_ins, 0,
+ chip->mixer.dest_stream_ins, 0,
+ "Line Capture Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_stream_outs,
+ chip->mixer.src_stream_out_ofs,
+ chip->mixer.dest_stream_ins, 0,
+ "Stream-Loopback Capture Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_phys_ins, 0,
+ chip->mixer.dest_phys_outs,
+ chip->mixer.dest_phys_out_ofs,
+ "Line-Loopback Playback Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_stream_outs,
+ chip->mixer.src_stream_out_ofs,
+ chip->mixer.dest_phys_outs,
+ chip->mixer.dest_phys_out_ofs,
+ "Stream Playback Volume");
+ if (err < 0)
+ return err;
+
+ return init_mixer_values(chip);
+}
--- /dev/null
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "lola.h"
+
+#define LOLA_MAX_BDL_ENTRIES 8
+#define LOLA_MAX_BUF_SIZE (1024*1024*1024)
+#define LOLA_BDL_ENTRY_SIZE (16 * 16)
+
+static struct lola_pcm *lola_get_pcm(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ return &chip->pcm[substream->stream];
+}
+
+static struct lola_stream *lola_get_stream(struct snd_pcm_substream *substream)
+{
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ unsigned int idx = substream->number;
+ return &pcm->streams[idx];
+}
+
+static unsigned int lola_get_lrc(struct lola *chip)
+{
+ return lola_readl(chip, BAR1, LRC);
+}
+
+static unsigned int lola_get_tstamp(struct lola *chip, bool quick_no_sync)
+{
+ unsigned int tstamp = lola_get_lrc(chip) >> 8;
+ if (chip->granularity) {
+ unsigned int wait_banks = quick_no_sync ? 0 : 8;
+ tstamp += (wait_banks + 1) * chip->granularity - 1;
+ tstamp -= tstamp % chip->granularity;
+ }
+ return tstamp << 8;
+}
+
+/* clear any pending interrupt status */
+static void lola_stream_clear_pending_irq(struct lola *chip,
+ struct lola_stream *str)
+{
+ unsigned int val = lola_dsd_read(chip, str->dsd, STS);
+ val &= LOLA_DSD_STS_DESE | LOLA_DSD_STS_BCIS;
+ if (val)
+ lola_dsd_write(chip, str->dsd, STS, val);
+}
+
+static void lola_stream_start(struct lola *chip, struct lola_stream *str,
+ unsigned int tstamp)
+{
+ lola_stream_clear_pending_irq(chip, str);
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_SRUN |
+ LOLA_DSD_CTL_IOCE |
+ LOLA_DSD_CTL_DEIE |
+ LOLA_DSD_CTL_VLRCV |
+ tstamp);
+}
+
+static void lola_stream_stop(struct lola *chip, struct lola_stream *str,
+ unsigned int tstamp)
+{
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_IOCE |
+ LOLA_DSD_CTL_DEIE |
+ LOLA_DSD_CTL_VLRCV |
+ tstamp);
+ lola_stream_clear_pending_irq(chip, str);
+}
+
+static void wait_for_srst_clear(struct lola *chip, struct lola_stream *str)
+{
+ unsigned long end_time = jiffies + msecs_to_jiffies(200);
+ while (time_before(jiffies, end_time)) {
+ unsigned int val;
+ val = lola_dsd_read(chip, str->dsd, CTL);
+ if (!(val & LOLA_DSD_CTL_SRST))
+ return;
+ msleep(1);
+ }
+ printk(KERN_WARNING SFX "SRST not clear (stream %d)\n", str->dsd);
+}
+
+static int lola_stream_wait_for_fifo(struct lola *chip,
+ struct lola_stream *str,
+ bool ready)
+{
+ unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0;
+ unsigned long end_time = jiffies + msecs_to_jiffies(200);
+ while (time_before(jiffies, end_time)) {
+ unsigned int reg = lola_dsd_read(chip, str->dsd, STS);
+ if ((reg & LOLA_DSD_STS_FIFORDY) == val)
+ return 0;
+ msleep(1);
+ }
+ printk(KERN_WARNING SFX "FIFO not ready (stream %d)\n", str->dsd);
+ return -EIO;
+}
+
+/* sync for FIFO ready/empty for all linked streams;
+ * clear paused flag when FIFO gets ready again
+ */
+static int lola_sync_wait_for_fifo(struct lola *chip,
+ struct snd_pcm_substream *substream,
+ bool ready)
+{
+ unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0;
+ unsigned long end_time = jiffies + msecs_to_jiffies(200);
+ struct snd_pcm_substream *s;
+ int pending = 0;
+
+ while (time_before(jiffies, end_time)) {
+ pending = 0;
+ snd_pcm_group_for_each_entry(s, substream) {
+ struct lola_stream *str;
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ str = lola_get_stream(s);
+ if (str->prepared && str->paused) {
+ unsigned int reg;
+ reg = lola_dsd_read(chip, str->dsd, STS);
+ if ((reg & LOLA_DSD_STS_FIFORDY) != val) {
+ pending = str->dsd + 1;
+ break;
+ }
+ if (ready)
+ str->paused = 0;
+ }
+ }
+ if (!pending)
+ return 0;
+ msleep(1);
+ }
+ printk(KERN_WARNING SFX "FIFO not ready (pending %d)\n", pending - 1);
+ return -EIO;
+}
+
+/* finish pause - prepare for a new resume */
+static void lola_sync_pause(struct lola *chip,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_substream *s;
+
+ lola_sync_wait_for_fifo(chip, substream, false);
+ snd_pcm_group_for_each_entry(s, substream) {
+ struct lola_stream *str;
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ str = lola_get_stream(s);
+ if (str->paused && str->prepared)
+ lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRUN |
+ LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE);
+ }
+ lola_sync_wait_for_fifo(chip, substream, true);
+}
+
+static void lola_stream_reset(struct lola *chip, struct lola_stream *str)
+{
+ if (str->prepared) {
+ if (str->paused)
+ lola_sync_pause(chip, str->substream);
+ str->prepared = 0;
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE);
+ lola_stream_wait_for_fifo(chip, str, false);
+ lola_stream_clear_pending_irq(chip, str);
+ lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRST);
+ lola_dsd_write(chip, str->dsd, LVI, 0);
+ lola_dsd_write(chip, str->dsd, BDPU, 0);
+ lola_dsd_write(chip, str->dsd, BDPL, 0);
+ wait_for_srst_clear(chip, str);
+ }
+}
+
+static struct snd_pcm_hardware lola_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_FLOAT_LE),
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = LOLA_MAX_BUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = LOLA_MAX_BUF_SIZE / 2,
+ .periods_min = 2,
+ .periods_max = LOLA_MAX_BDL_ENTRIES,
+ .fifo_size = 0,
+};
+
+static int lola_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ mutex_lock(&chip->open_mutex);
+ if (str->opened) {
+ mutex_unlock(&chip->open_mutex);
+ return -EBUSY;
+ }
+ str->substream = substream;
+ str->master = NULL;
+ str->opened = 1;
+ runtime->hw = lola_pcm_hw;
+ runtime->hw.channels_max = pcm->num_streams - str->index;
+ if (chip->sample_rate) {
+ /* sample rate is locked */
+ runtime->hw.rate_min = chip->sample_rate;
+ runtime->hw.rate_max = chip->sample_rate;
+ } else {
+ runtime->hw.rate_min = chip->sample_rate_min;
+ runtime->hw.rate_max = chip->sample_rate_max;
+ }
+ chip->ref_count_rate++;
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ /* period size = multiple of chip->granularity (8, 16 or 32 frames)*/
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ chip->granularity);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ chip->granularity);
+ mutex_unlock(&chip->open_mutex);
+ return 0;
+}
+
+static void lola_cleanup_slave_streams(struct lola_pcm *pcm,
+ struct lola_stream *str)
+{
+ int i;
+ for (i = str->index + 1; i < pcm->num_streams; i++) {
+ struct lola_stream *s = &pcm->streams[i];
+ if (s->master != str)
+ break;
+ s->master = NULL;
+ s->opened = 0;
+ }
+}
+
+static int lola_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+
+ mutex_lock(&chip->open_mutex);
+ if (str->substream == substream) {
+ str->substream = NULL;
+ str->opened = 0;
+ }
+ if (--chip->ref_count_rate == 0) {
+ /* release sample rate */
+ chip->sample_rate = 0;
+ }
+ mutex_unlock(&chip->open_mutex);
+ return 0;
+}
+
+static int lola_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct lola_stream *str = lola_get_stream(substream);
+
+ str->bufsize = 0;
+ str->period_bytes = 0;
+ str->format_verb = 0;
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int lola_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+
+ mutex_lock(&chip->open_mutex);
+ lola_stream_reset(chip, str);
+ lola_cleanup_slave_streams(pcm, str);
+ mutex_unlock(&chip->open_mutex);
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/*
+ * set up a BDL entry
+ */
+static int setup_bdle(struct snd_pcm_substream *substream,
+ struct lola_stream *str, u32 **bdlp,
+ int ofs, int size)
+{
+ u32 *bdl = *bdlp;
+
+ while (size > 0) {
+ dma_addr_t addr;
+ int chunk;
+
+ if (str->frags >= LOLA_MAX_BDL_ENTRIES)
+ return -EINVAL;
+
+ addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+ /* program the address field of the BDL entry */
+ bdl[0] = cpu_to_le32((u32)addr);
+ bdl[1] = cpu_to_le32(upper_32_bits(addr));
+ /* program the size field of the BDL entry */
+ chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size);
+ bdl[2] = cpu_to_le32(chunk);
+ /* program the IOC to enable interrupt
+ * only when the whole fragment is processed
+ */
+ size -= chunk;
+ bdl[3] = size ? 0 : cpu_to_le32(0x01);
+ bdl += 4;
+ str->frags++;
+ ofs += chunk;
+ }
+ *bdlp = bdl;
+ return ofs;
+}
+
+/*
+ * set up BDL entries
+ */
+static int lola_setup_periods(struct lola *chip, struct lola_pcm *pcm,
+ struct snd_pcm_substream *substream,
+ struct lola_stream *str)
+{
+ u32 *bdl;
+ int i, ofs, periods, period_bytes;
+
+ period_bytes = str->period_bytes;
+ periods = str->bufsize / period_bytes;
+
+ /* program the initial BDL entries */
+ bdl = (u32 *)(pcm->bdl.area + LOLA_BDL_ENTRY_SIZE * str->index);
+ ofs = 0;
+ str->frags = 0;
+ for (i = 0; i < periods; i++) {
+ ofs = setup_bdle(substream, str, &bdl, ofs, period_bytes);
+ if (ofs < 0)
+ goto error;
+ }
+ return 0;
+
+ error:
+ snd_printk(KERN_ERR SFX "Too many BDL entries: buffer=%d, period=%d\n",
+ str->bufsize, period_bytes);
+ return -EINVAL;
+}
+
+static unsigned int lola_get_format_verb(struct snd_pcm_substream *substream)
+{
+ unsigned int verb;
+
+ switch (substream->runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ verb = 0x00000000;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ verb = 0x00000200;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ verb = 0x00000300;
+ break;
+ case SNDRV_PCM_FORMAT_FLOAT_LE:
+ verb = 0x00001300;
+ break;
+ default:
+ return 0;
+ }
+ verb |= substream->runtime->channels;
+ return verb;
+}
+
+static int lola_set_stream_config(struct lola *chip,
+ struct lola_stream *str,
+ int channels)
+{
+ int i, err;
+ unsigned int verb, val;
+
+ /* set format info for all channels
+ * (with only one command for the first channel)
+ */
+ err = lola_codec_read(chip, str->nid, LOLA_VERB_SET_STREAM_FORMAT,
+ str->format_verb, 0, &val, NULL);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Cannot set stream format 0x%x\n",
+ str->format_verb);
+ return err;
+ }
+
+ /* update stream - channel config */
+ for (i = 0; i < channels; i++) {
+ verb = (str->index << 6) | i;
+ err = lola_codec_read(chip, str[i].nid,
+ LOLA_VERB_SET_CHANNEL_STREAMID, 0, verb,
+ &val, NULL);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Cannot set stream channel %d\n", i);
+ return err;
+ }
+ }
+ return 0;
+}
+
+/*
+ * set up the SD for streaming
+ */
+static int lola_setup_controller(struct lola *chip, struct lola_pcm *pcm,
+ struct lola_stream *str)
+{
+ dma_addr_t bdl;
+
+ if (str->prepared)
+ return -EINVAL;
+
+ /* set up BDL */
+ bdl = pcm->bdl.addr + LOLA_BDL_ENTRY_SIZE * str->index;
+ lola_dsd_write(chip, str->dsd, BDPL, (u32)bdl);
+ lola_dsd_write(chip, str->dsd, BDPU, upper_32_bits(bdl));
+ /* program the stream LVI (last valid index) of the BDL */
+ lola_dsd_write(chip, str->dsd, LVI, str->frags - 1);
+ lola_stream_clear_pending_irq(chip, str);
+
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE | LOLA_DSD_CTL_SRUN);
+
+ str->prepared = 1;
+
+ return lola_stream_wait_for_fifo(chip, str, true);
+}
+
+static int lola_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int bufsize, period_bytes, format_verb;
+ int i, err;
+
+ mutex_lock(&chip->open_mutex);
+ lola_stream_reset(chip, str);
+ lola_cleanup_slave_streams(pcm, str);
+ if (str->index + runtime->channels > pcm->num_streams) {
+ mutex_unlock(&chip->open_mutex);
+ return -EINVAL;
+ }
+ for (i = 1; i < runtime->channels; i++) {
+ str[i].master = str;
+ str[i].opened = 1;
+ }
+ mutex_unlock(&chip->open_mutex);
+
+ bufsize = snd_pcm_lib_buffer_bytes(substream);
+ period_bytes = snd_pcm_lib_period_bytes(substream);
+ format_verb = lola_get_format_verb(substream);
+
+ str->bufsize = bufsize;
+ str->period_bytes = period_bytes;
+ str->format_verb = format_verb;
+
+ err = lola_setup_periods(chip, pcm, substream, str);
+ if (err < 0)
+ return err;
+
+ err = lola_set_sample_rate(chip, runtime->rate);
+ if (err < 0)
+ return err;
+ chip->sample_rate = runtime->rate; /* sample rate gets locked */
+
+ err = lola_set_stream_config(chip, str, runtime->channels);
+ if (err < 0)
+ return err;
+
+ err = lola_setup_controller(chip, pcm, str);
+ if (err < 0) {
+ lola_stream_reset(chip, str);
+ return err;
+ }
+
+ return 0;
+}
+
+static int lola_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_stream *str;
+ struct snd_pcm_substream *s;
+ unsigned int start;
+ unsigned int tstamp;
+ bool sync_streams;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ start = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ start = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * sample correct synchronization is only needed starting several
+ * streams. On stop or if only one stream do as quick as possible
+ */
+ sync_streams = (start && snd_pcm_stream_linked(substream));
+ tstamp = lola_get_tstamp(chip, !sync_streams);
+ spin_lock(&chip->reg_lock);
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ str = lola_get_stream(s);
+ if (start)
+ lola_stream_start(chip, str, tstamp);
+ else
+ lola_stream_stop(chip, str, tstamp);
+ str->running = start;
+ str->paused = !start;
+ snd_pcm_trigger_done(s, substream);
+ }
+ spin_unlock(&chip->reg_lock);
+ return 0;
+}
+
+static snd_pcm_uframes_t lola_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+ unsigned int pos = lola_dsd_read(chip, str->dsd, LPIB);
+
+ if (pos >= str->bufsize)
+ pos = 0;
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+void lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits)
+{
+ int i;
+
+ for (i = 0; bits && i < pcm->num_streams; i++) {
+ if (bits & (1 << i)) {
+ struct lola_stream *str = &pcm->streams[i];
+ if (str->substream && str->running)
+ snd_pcm_period_elapsed(str->substream);
+ bits &= ~(1 << i);
+ }
+ }
+}
+
+static struct snd_pcm_ops lola_pcm_ops = {
+ .open = lola_pcm_open,
+ .close = lola_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = lola_pcm_hw_params,
+ .hw_free = lola_pcm_hw_free,
+ .prepare = lola_pcm_prepare,
+ .trigger = lola_pcm_trigger,
+ .pointer = lola_pcm_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+int __devinit lola_create_pcm(struct lola *chip)
+{
+ struct snd_pcm *pcm;
+ int i, err;
+
+ for (i = 0; i < 2; i++) {
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ PAGE_SIZE, &chip->pcm[i].bdl);
+ if (err < 0)
+ return err;
+ }
+
+ err = snd_pcm_new(chip->card, "Digigram Lola", 0,
+ chip->pcm[SNDRV_PCM_STREAM_PLAYBACK].num_streams,
+ chip->pcm[SNDRV_PCM_STREAM_CAPTURE].num_streams,
+ &pcm);
+ if (err < 0)
+ return err;
+ strlcpy(pcm->name, "Digigram Lola", sizeof(pcm->name));
+ pcm->private_data = chip;
+ for (i = 0; i < 2; i++) {
+ if (chip->pcm[i].num_streams)
+ snd_pcm_set_ops(pcm, i, &lola_pcm_ops);
+ }
+ /* buffer pre-allocation */
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci),
+ 1024 * 64, 32 * 1024 * 1024);
+ return 0;
+}
+
+void lola_free_pcm(struct lola *chip)
+{
+ snd_dma_free_pages(&chip->pcm[0].bdl);
+ snd_dma_free_pages(&chip->pcm[1].bdl);
+}
+
+/*
+ */
+
+static int lola_init_stream(struct lola *chip, struct lola_stream *str,
+ int idx, int nid, int dir)
+{
+ unsigned int val;
+ int err;
+
+ str->nid = nid;
+ str->index = idx;
+ str->dsd = idx;
+ if (dir == PLAY)
+ str->dsd += MAX_STREAM_IN_COUNT;
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+ if (dir == PLAY) {
+ /* test TYPE and bits 0..11 (no test bit9 : Digital = 0/1) */
+ if ((val & 0x00f00dff) != 0x00000010) {
+ printk(KERN_ERR SFX "Invalid wcaps 0x%x for 0x%x\n",
+ val, nid);
+ return -EINVAL;
+ }
+ } else {
+ /* test TYPE and bits 0..11 (no test bit9 : Digital = 0/1)
+ * (bug : ignore bit8: Conn list = 0/1)
+ */
+ if ((val & 0x00f00cff) != 0x00100010) {
+ printk(KERN_ERR SFX "Invalid wcaps 0x%x for 0x%x\n",
+ val, nid);
+ return -EINVAL;
+ }
+ /* test bit9:DIGITAL and bit12:SRC_PRESENT*/
+ if ((val & 0x00001200) == 0x00001200)
+ chip->input_src_caps_mask |= (1 << idx);
+ }
+
+ err = lola_read_param(chip, nid, LOLA_PAR_STREAM_FORMATS, &val);
+ if (err < 0) {
+ printk(KERN_ERR SFX "Can't read FORMATS 0x%x\n", nid);
+ return err;
+ }
+ val &= 3;
+ if (val == 3)
+ str->can_float = true;
+ if (!(val & 1)) {
+ printk(KERN_ERR SFX "Invalid formats 0x%x for 0x%x", val, nid);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int __devinit lola_init_pcm(struct lola *chip, int dir, int *nidp)
+{
+ struct lola_pcm *pcm = &chip->pcm[dir];
+ int i, nid, err;
+
+ nid = *nidp;
+ for (i = 0; i < pcm->num_streams; i++, nid++) {
+ err = lola_init_stream(chip, &pcm->streams[i], i, nid, dir);
+ if (err < 0)
+ return err;
+ }
+ *nidp = nid;
+ return 0;
+}
--- /dev/null
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include "lola.h"
+
+static void print_audio_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid, const char *name)
+{
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x %s wcaps 0x%x\n", nid, name, val);
+ lola_read_param(chip, nid, LOLA_PAR_STREAM_FORMATS, &val);
+ snd_iprintf(buffer, " Formats: 0x%x\n", val);
+}
+
+static void print_pin_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid, unsigned int ampcap,
+ const char *name)
+{
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x %s wcaps 0x%x\n", nid, name, val);
+ if (val == 0x00400200)
+ return;
+ lola_read_param(chip, nid, ampcap, &val);
+ snd_iprintf(buffer, " Amp-Caps: 0x%x\n", val);
+ snd_iprintf(buffer, " mute=%d, step-size=%d, steps=%d, ofs=%d\n",
+ LOLA_AMP_MUTE_CAPABLE(val),
+ LOLA_AMP_STEP_SIZE(val),
+ LOLA_AMP_NUM_STEPS(val),
+ LOLA_AMP_OFFSET(val));
+ lola_codec_read(chip, nid, LOLA_VERB_GET_MAX_LEVEL, 0, 0, &val, NULL);
+ snd_iprintf(buffer, " Max-level: 0x%x\n", val);
+}
+
+static void print_clock_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid)
+{
+ int i, j, num_clocks;
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x [Clock] wcaps 0x%x\n", nid, val);
+ num_clocks = val & 0xff;
+ for (i = 0; i < num_clocks; i += 4) {
+ unsigned int res_ex;
+ unsigned short items[4];
+ const char *name;
+
+ lola_codec_read(chip, nid, LOLA_VERB_GET_CLOCK_LIST,
+ i, 0, &val, &res_ex);
+ items[0] = val & 0xfff;
+ items[1] = (val >> 16) & 0xfff;
+ items[2] = res_ex & 0xfff;
+ items[3] = (res_ex >> 16) & 0xfff;
+ for (j = 0; j < 4; j++) {
+ unsigned char type = items[j] >> 8;
+ unsigned int freq = items[j] & 0xff;
+ if (i + j >= num_clocks)
+ break;
+ if (type == LOLA_CLOCK_TYPE_INTERNAL) {
+ name = "Internal";
+ freq = lola_sample_rate_convert(freq);
+ } else if (type == LOLA_CLOCK_TYPE_VIDEO) {
+ name = "Video";
+ freq = lola_sample_rate_convert(freq);
+ } else {
+ name = "Other";
+ }
+ snd_iprintf(buffer, " Clock %d: Type %d:%s, freq=%d\n",
+ i + j, type, name, freq);
+ }
+ }
+}
+
+static void print_mixer_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid)
+{
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x [Mixer] wcaps 0x%x\n", nid, val);
+}
+
+static void lola_proc_codec_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ unsigned int val;
+ int i, nid;
+
+ lola_read_param(chip, 0, LOLA_PAR_VENDOR_ID, &val);
+ snd_iprintf(buffer, "Vendor: 0x%08x\n", val);
+ lola_read_param(chip, 1, LOLA_PAR_FUNCTION_TYPE, &val);
+ snd_iprintf(buffer, "Function Type: %d\n", val);
+ lola_read_param(chip, 1, LOLA_PAR_SPECIFIC_CAPS, &val);
+ snd_iprintf(buffer, "Specific-Caps: 0x%08x\n", val);
+ snd_iprintf(buffer, " Pins-In %d, Pins-Out %d\n",
+ chip->pin[CAPT].num_pins, chip->pin[PLAY].num_pins);
+ nid = 2;
+ for (i = 0; i < chip->pcm[CAPT].num_streams; i++, nid++)
+ print_audio_widget(buffer, chip, nid, "[Audio-In]");
+ for (i = 0; i < chip->pcm[PLAY].num_streams; i++, nid++)
+ print_audio_widget(buffer, chip, nid, "[Audio-Out]");
+ for (i = 0; i < chip->pin[CAPT].num_pins; i++, nid++)
+ print_pin_widget(buffer, chip, nid, LOLA_PAR_AMP_IN_CAP,
+ "[Pin-In]");
+ for (i = 0; i < chip->pin[PLAY].num_pins; i++, nid++)
+ print_pin_widget(buffer, chip, nid, LOLA_PAR_AMP_OUT_CAP,
+ "[Pin-Out]");
+ if (LOLA_AFG_CLOCK_WIDGET_PRESENT(chip->lola_caps)) {
+ print_clock_widget(buffer, chip, nid);
+ nid++;
+ }
+ if (LOLA_AFG_MIXER_WIDGET_PRESENT(chip->lola_caps)) {
+ print_mixer_widget(buffer, chip, nid);
+ nid++;
+ }
+}
+
+/* direct codec access for debugging */
+static void lola_proc_codec_rw_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ char line[64];
+ unsigned int id, verb, data, extdata;
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "%i %i %i %i", &id, &verb, &data, &extdata) != 4)
+ continue;
+ lola_codec_read(chip, id, verb, data, extdata,
+ &chip->debug_res,
+ &chip->debug_res_ex);
+ }
+}
+
+static void lola_proc_codec_rw_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ snd_iprintf(buffer, "0x%x 0x%x\n", chip->debug_res, chip->debug_res_ex);
+}
+
+/*
+ * dump some registers
+ */
+static void lola_proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ int i;
+
+ for (i = 0; i < 0x40; i += 4) {
+ snd_iprintf(buffer, "BAR0 %02x: %08x\n", i,
+ readl(chip->bar[BAR0].remap_addr + i));
+ }
+ snd_iprintf(buffer, "\n");
+ for (i = 0; i < 0x30; i += 4) {
+ snd_iprintf(buffer, "BAR1 %02x: %08x\n", i,
+ readl(chip->bar[BAR1].remap_addr + i));
+ }
+ snd_iprintf(buffer, "\n");
+ for (i = 0x80; i < 0xa0; i += 4) {
+ snd_iprintf(buffer, "BAR1 %02x: %08x\n", i,
+ readl(chip->bar[BAR1].remap_addr + i));
+ }
+ snd_iprintf(buffer, "\n");
+ for (i = 0; i < 32; i++) {
+ snd_iprintf(buffer, "DSD %02x STS %08x\n", i,
+ lola_dsd_read(chip, i, STS));
+ snd_iprintf(buffer, "DSD %02x LPIB %08x\n", i,
+ lola_dsd_read(chip, i, LPIB));
+ snd_iprintf(buffer, "DSD %02x CTL %08x\n", i,
+ lola_dsd_read(chip, i, CTL));
+ snd_iprintf(buffer, "DSD %02x LVIL %08x\n", i,
+ lola_dsd_read(chip, i, LVI));
+ snd_iprintf(buffer, "DSD %02x BDPL %08x\n", i,
+ lola_dsd_read(chip, i, BDPL));
+ snd_iprintf(buffer, "DSD %02x BDPU %08x\n", i,
+ lola_dsd_read(chip, i, BDPU));
+ }
+}
+
+void __devinit lola_proc_debug_new(struct lola *chip)
+{
+ struct snd_info_entry *entry;
+
+ if (!snd_card_proc_new(chip->card, "codec", &entry))
+ snd_info_set_text_ops(entry, chip, lola_proc_codec_read);
+ if (!snd_card_proc_new(chip->card, "codec_rw", &entry)) {
+ snd_info_set_text_ops(entry, chip, lola_proc_codec_rw_read);
+ entry->mode |= S_IWUSR;
+ entry->c.text.write = lola_proc_codec_rw_write;
+ }
+ if (!snd_card_proc_new(chip->card, "regs", &entry))
+ snd_info_set_text_ops(entry, chip, lola_proc_regs_read);
+}
chip->lineout_sw_ctl);
if (mix->anded_reset)
msleep(10);
- check_mute(chip, &mix->amp_mute, 1, mix->auto_mute_notify,
+ check_mute(chip, &mix->amp_mute, !IS_G4DA, mix->auto_mute_notify,
chip->speaker_sw_ctl);
} else {
/* unmute speaker, mute others */
.codec_dai_name = "wm8731-hifi",
.init = at91sam9g20ek_wm8731_init,
.platform_name = "atmel-pcm-audio",
- .codec_name = "wm8731-codec.0-001b",
+ .codec_name = "wm8731.0-001b",
.ops = &at91sam9g20ek_ops,
};
.codec_dai_name = "wm8731-hifi",
.cpu_dai_name = "au1xpsc_i2s.1",
.platform_name = "au1xpsc-pcm.1",
- .codec_name = "wm8731-codec.0-001b",
+ .codec_name = "wm8731.0-001b",
.ops = &db1200_i2s_wm8731_ops,
};
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
+ struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = bf5xx_pcm_hardware.buffer_bytes_max
struct snd_dma_buffer *buf;
int stream;
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
+ struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
sizeof(struct ac97_frame) / 4;
#endif
}
#endif
}
- if (sport_handle)
- sport_done(sport_handle);
}
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
static struct platform_driver bf5xx_pcm_driver = {
.driver = {
- .name = "bf5xx-pcm-audio",
+ .name = "bfin-ac97-pcm-audio",
.owner = THIS_MODULE,
},
* anomaly does not affect blackfin sound drivers.
*/
-static int *cmd_count;
-static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
-
-#define SPORT_REQ(x) \
- [x] = {P_SPORT##x##_TFS, P_SPORT##x##_DTPRI, P_SPORT##x##_TSCLK, \
- P_SPORT##x##_RFS, P_SPORT##x##_DRPRI, P_SPORT##x##_RSCLK, 0}
-static u16 sport_req[][7] = {
-#ifdef SPORT0_TCR1
- SPORT_REQ(0),
-#endif
-#ifdef SPORT1_TCR1
- SPORT_REQ(1),
-#endif
-#ifdef SPORT2_TCR1
- SPORT_REQ(2),
-#endif
-#ifdef SPORT3_TCR1
- SPORT_REQ(3),
-#endif
-};
-
-#define SPORT_PARAMS(x) \
- [x] = { \
- .dma_rx_chan = CH_SPORT##x##_RX, \
- .dma_tx_chan = CH_SPORT##x##_TX, \
- .err_irq = IRQ_SPORT##x##_ERROR, \
- .regs = (struct sport_register *)SPORT##x##_TCR1, \
- }
-static struct sport_param sport_params[4] = {
-#ifdef SPORT0_TCR1
- SPORT_PARAMS(0),
-#endif
-#ifdef SPORT1_TCR1
- SPORT_PARAMS(1),
-#endif
-#ifdef SPORT2_TCR1
- SPORT_PARAMS(2),
-#endif
-#ifdef SPORT3_TCR1
- SPORT_PARAMS(3),
-#endif
-};
+static struct sport_device *ac97_sport_handle;
void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src,
size_t count, unsigned int chan_mask)
static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
{
- struct sport_device *sport = sport_handle;
+ struct sport_device *sport = ac97_sport_handle;
+ int *cmd_count = sport->private_data;
int nextfrag = sport_tx_curr_frag(sport);
struct ac97_frame *nextwrite;
static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
+ struct sport_device *sport_handle = ac97_sport_handle;
struct ac97_frame out_frame[2], in_frame[2];
pr_debug("%s enter 0x%x\n", __func__, reg);
void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val)
{
+ struct sport_device *sport_handle = ac97_sport_handle;
+
pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val);
if (sport_handle->tx_run) {
static void bf5xx_ac97_warm_reset(struct snd_ac97 *ac97)
{
-#if defined(CONFIG_BF54x) || defined(CONFIG_BF561) || \
- (defined(BF537_FAMILY) && (CONFIG_SND_BF5XX_SPORT_NUM == 1))
-
-#define CONCAT(a, b, c) a ## b ## c
-#define BFIN_SPORT_RFS(x) CONCAT(P_SPORT, x, _RFS)
-
- u16 per = BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM);
- u16 gpio = P_IDENT(BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM));
+ struct sport_device *sport_handle = ac97_sport_handle;
+ u16 gpio = P_IDENT(sport_handle->pin_req[3]);
pr_debug("%s enter\n", __func__);
- peripheral_free(per);
+ peripheral_free_list(sport_handle->pin_req);
gpio_request(gpio, "bf5xx-ac97");
gpio_direction_output(gpio, 1);
udelay(2);
gpio_set_value(gpio, 0);
udelay(1);
gpio_free(gpio);
- peripheral_request(per, "soc-audio");
-#else
- pr_info("%s: Not implemented\n", __func__);
-#endif
+ peripheral_request_list(sport_handle->pin_req, "soc-audio");
}
static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97)
#define bf5xx_ac97_resume NULL
#endif
-static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
+static struct snd_soc_dai_driver bfin_ac97_dai = {
+ .ac97_control = 1,
+ .suspend = bf5xx_ac97_suspend,
+ .resume = bf5xx_ac97_resume,
+ .playback = {
+ .stream_name = "AC97 Playback",
+ .channels_min = 2,
+#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
+ .channels_max = 6,
+#else
+ .channels_max = 2,
+#endif
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+ .capture = {
+ .stream_name = "AC97 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+};
+
+static int __devinit asoc_bfin_ac97_probe(struct platform_device *pdev)
{
- int ret = 0;
- cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
- if (cmd_count == NULL)
- return -ENOMEM;
-
- if (peripheral_request_list(sport_req[sport_num], "soc-audio")) {
- pr_err("Requesting Peripherals failed\n");
- ret = -EFAULT;
- goto peripheral_err;
- }
+ struct sport_device *sport_handle;
+ int ret;
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
/* Request PB3 as reset pin */
}
gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
#endif
- sport_handle = sport_init(&sport_params[sport_num], 2, \
- sizeof(struct ac97_frame), NULL);
+
+ sport_handle = sport_init(pdev, 2, sizeof(struct ac97_frame),
+ PAGE_SIZE);
if (!sport_handle) {
ret = -ENODEV;
goto sport_err;
}
+
/*SPORT works in TDM mode to simulate AC97 transfers*/
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 1);
goto sport_config_err;
}
+ ret = snd_soc_register_dai(&pdev->dev, &bfin_ac97_dai);
+ if (ret) {
+ pr_err("Failed to register DAI: %d\n", ret);
+ goto sport_config_err;
+ }
+
+ ac97_sport_handle = sport_handle;
+
return 0;
sport_config_err:
- kfree(sport_handle);
+ sport_done(sport_handle);
sport_err:
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
gpio_err:
#endif
- peripheral_free_list(sport_req[sport_num]);
-peripheral_err:
- free_page((unsigned long)cmd_count);
- cmd_count = NULL;
return ret;
}
-static int bf5xx_ac97_remove(struct snd_soc_dai *dai)
+static int __devexit asoc_bfin_ac97_remove(struct platform_device *pdev)
{
- free_page((unsigned long)cmd_count);
- cmd_count = NULL;
- peripheral_free_list(sport_req[sport_num]);
+ struct sport_device *sport_handle = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_dai(&pdev->dev);
+ sport_done(sport_handle);
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
#endif
- return 0;
-}
-
-struct snd_soc_dai_driver bfin_ac97_dai = {
- .ac97_control = 1,
- .probe = bf5xx_ac97_probe,
- .remove = bf5xx_ac97_remove,
- .suspend = bf5xx_ac97_suspend,
- .resume = bf5xx_ac97_resume,
- .playback = {
- .stream_name = "AC97 Playback",
- .channels_min = 2,
-#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
- .channels_max = 6,
-#else
- .channels_max = 2,
-#endif
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE, },
- .capture = {
- .stream_name = "AC97 Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE, },
-};
-EXPORT_SYMBOL_GPL(bfin_ac97_dai);
-
-static __devinit int asoc_bfin_ac97_probe(struct platform_device *pdev)
-{
- return snd_soc_register_dai(&pdev->dev, &bfin_ac97_dai);
-}
-static int __devexit asoc_bfin_ac97_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_dai(&pdev->dev);
return 0;
}
#include <asm/portmux.h>
#include "../codecs/ad1836.h"
-#include "bf5xx-sport.h"
#include "bf5xx-tdm-pcm.h"
#include "bf5xx-tdm.h"
static struct snd_soc_card bf5xx_ad1836;
-static int bf5xx_ad1836_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
- return 0;
-}
-
static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
}
static struct snd_soc_ops bf5xx_ad1836_ops = {
- .startup = bf5xx_ad1836_startup,
.hw_params = bf5xx_ad1836_hw_params,
};
-static struct snd_soc_dai_link bf5xx_ad1836_dai = {
- .name = "ad1836",
- .stream_name = "AD1836",
- .cpu_dai_name = "bf5xx-tdm",
- .codec_dai_name = "ad1836-hifi",
- .platform_name = "bf5xx-tdm-pcm-audio",
- .codec_name = "ad1836-codec.0",
- .ops = &bf5xx_ad1836_ops,
+static struct snd_soc_dai_link bf5xx_ad1836_dai[] = {
+ {
+ .name = "ad1836",
+ .stream_name = "AD1836",
+ .cpu_dai_name = "bfin-tdm.0",
+ .codec_dai_name = "ad1836-hifi",
+ .platform_name = "bfin-tdm-pcm-audio",
+ .codec_name = "ad1836.0",
+ .ops = &bf5xx_ad1836_ops,
+ },
+ {
+ .name = "ad1836",
+ .stream_name = "AD1836",
+ .cpu_dai_name = "bfin-tdm.1",
+ .codec_dai_name = "ad1836-hifi",
+ .platform_name = "bfin-tdm-pcm-audio",
+ .codec_name = "ad1836.0",
+ .ops = &bf5xx_ad1836_ops,
+ },
};
static struct snd_soc_card bf5xx_ad1836 = {
- .name = "bf5xx_ad1836",
- .dai_link = &bf5xx_ad1836_dai,
+ .name = "bfin-ad1836",
+ .dai_link = &bf5xx_ad1836_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};
#include <asm/portmux.h>
#include "../codecs/ad193x.h"
-#include "bf5xx-sport.h"
#include "bf5xx-tdm-pcm.h"
#include "bf5xx-tdm.h"
static struct snd_soc_card bf5xx_ad193x;
-static int bf5xx_ad193x_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
- return 0;
-}
-
static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ unsigned int clk = 0;
unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
int ret = 0;
+
+ switch (params_rate(params)) {
+ case 48000:
+ clk = 12288000;
+ break;
+ }
+
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
+ /* set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
/* set codec DAI slots, 8 channels, all channels are enabled */
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32);
if (ret < 0)
}
static struct snd_soc_ops bf5xx_ad193x_ops = {
- .startup = bf5xx_ad193x_startup,
.hw_params = bf5xx_ad193x_hw_params,
};
-static struct snd_soc_dai_link bf5xx_ad193x_dai = {
- .name = "ad193x",
- .stream_name = "AD193X",
- .cpu_dai_name = "bf5xx-tdm",
- .codec_dai_name ="ad193x-hifi",
- .platform_name = "bf5xx-tdm-pcm-audio",
- .codec_name = "ad193x-codec.5",
- .ops = &bf5xx_ad193x_ops,
+static struct snd_soc_dai_link bf5xx_ad193x_dai[] = {
+ {
+ .name = "ad193x",
+ .stream_name = "AD193X",
+ .cpu_dai_name = "bfin-tdm.0",
+ .codec_dai_name ="ad193x-hifi",
+ .platform_name = "bfin-tdm-pcm-audio",
+ .codec_name = "ad193x.5",
+ .ops = &bf5xx_ad193x_ops,
+ },
+ {
+ .name = "ad193x",
+ .stream_name = "AD193X",
+ .cpu_dai_name = "bfin-tdm.1",
+ .codec_dai_name ="ad193x-hifi",
+ .platform_name = "bfin-tdm-pcm-audio",
+ .codec_name = "ad193x.5",
+ .ops = &bf5xx_ad193x_ops,
+ },
};
static struct snd_soc_card bf5xx_ad193x = {
- .name = "bf5xx_ad193x",
- .dai_link = &bf5xx_ad193x_dai,
+ .name = "bfin-ad193x",
+ .dai_link = &bf5xx_ad193x_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};
#include <asm/portmux.h>
#include "../codecs/ad1980.h"
-#include "bf5xx-sport.h"
+
#include "bf5xx-ac97-pcm.h"
#include "bf5xx-ac97.h"
static struct snd_soc_card bf5xx_board;
-static int bf5xx_board_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- pr_debug("%s enter\n", __func__);
- snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
- return 0;
-}
-
-static struct snd_soc_ops bf5xx_board_ops = {
- .startup = bf5xx_board_startup,
-};
-
-static struct snd_soc_dai_link bf5xx_board_dai = {
- .name = "AC97",
- .stream_name = "AC97 HiFi",
- .cpu_dai_name = "bfin-ac97",
- .codec_dai_name = "ad1980-hifi",
- .platform_name = "bfin-pcm-audio",
- .codec_name = "ad1980-codec",
- .ops = &bf5xx_board_ops,
+static struct snd_soc_dai_link bf5xx_board_dai[] = {
+ {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai_name = "bfin-ac97.0",
+ .codec_dai_name = "ad1980-hifi",
+ .platform_name = "bfin-ac97-pcm-audio",
+ .codec_name = "ad1980",
+ },
+ {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai_name = "bfin-ac97.1",
+ .codec_dai_name = "ad1980-hifi",
+ .platform_name = "bfin-ac97-pcm-audio",
+ .codec_name = "ad1980",
+ },
};
static struct snd_soc_card bf5xx_board = {
- .name = "bf5xx-board",
- .dai_link = &bf5xx_board_dai,
+ .name = "bfin-ad1980",
+ .dai_link = &bf5xx_board_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};
return 0;
}
-static int bf5xx_ad73311_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- pr_debug("%s enter\n", __func__);
- snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
- return 0;
-}
-
static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
static struct snd_soc_ops bf5xx_ad73311_ops = {
- .startup = bf5xx_ad73311_startup,
.hw_params = bf5xx_ad73311_hw_params,
};
-static struct snd_soc_dai_link bf5xx_ad73311_dai = {
- .name = "ad73311",
- .stream_name = "AD73311",
- .cpu_dai_name = "bf5xx-i2s",
- .codec_dai_name = "ad73311-hifi",
- .platform_name = "bfin-pcm-audio",
- .codec_name = "ad73311-codec",
- .ops = &bf5xx_ad73311_ops,
+static struct snd_soc_dai_link bf5xx_ad73311_dai[] = {
+ {
+ .name = "ad73311",
+ .stream_name = "AD73311",
+ .cpu_dai_name = "bfin-i2s.0",
+ .codec_dai_name = "ad73311-hifi",
+ .platform_name = "bfin-i2s-pcm-audio",
+ .codec_name = "ad73311",
+ .ops = &bf5xx_ad73311_ops,
+ },
+ {
+ .name = "ad73311",
+ .stream_name = "AD73311",
+ .cpu_dai_name = "bfin-i2s.1",
+ .codec_dai_name = "ad73311-hifi",
+ .platform_name = "bfin-i2s-pcm-audio",
+ .codec_name = "ad73311",
+ .ops = &bf5xx_ad73311_ops,
+ },
};
static struct snd_soc_card bf5xx_ad73311 = {
- .name = "bf5xx_ad73311",
+ .name = "bfin-ad73311",
.probe = bf5xx_probe,
- .dai_link = &bf5xx_ad73311_dai,
+ .dai_link = &bf5xx_ad73311_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
int ret;
pr_debug("%s enter\n", __func__);
+
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
ret = snd_pcm_hw_constraint_integer(runtime, \
if (ret < 0)
goto out;
- if (sport_handle != NULL)
+ if (sport_handle != NULL) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sport_handle->tx_buf = buf->area;
+ else
+ sport_handle->rx_buf = buf->area;
+
runtime->private_data = sport_handle;
- else {
+ } else {
pr_err("sport_handle is NULL\n");
return -1;
}
pr_debug("%s, area:%p, size:0x%08lx\n", __func__,
buf->area, buf->bytes);
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- sport_handle->tx_buf = buf->area;
- else
- sport_handle->rx_buf = buf->area;
-
return 0;
}
dma_free_coherent(NULL, buf->bytes, buf->area, 0);
buf->area = NULL;
}
- if (sport_handle)
- sport_done(sport_handle);
}
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
static struct platform_driver bfin_i2s_pcm_driver = {
.driver = {
- .name = "bfin-pcm-audio",
+ .name = "bfin-i2s-pcm-audio",
.owner = THIS_MODULE,
},
int configured;
};
-static struct bf5xx_i2s_port bf5xx_i2s;
-static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
-
-static struct sport_param sport_params[2] = {
- {
- .dma_rx_chan = CH_SPORT0_RX,
- .dma_tx_chan = CH_SPORT0_TX,
- .err_irq = IRQ_SPORT0_ERROR,
- .regs = (struct sport_register *)SPORT0_TCR1,
- },
- {
- .dma_rx_chan = CH_SPORT1_RX,
- .dma_tx_chan = CH_SPORT1_TX,
- .err_irq = IRQ_SPORT1_ERROR,
- .regs = (struct sport_register *)SPORT1_TCR1,
- }
-};
-
-/*
- * Setting the TFS pin selector for SPORT 0 based on whether the selected
- * port id F or G. If the port is F then no conflict should exist for the
- * TFS. When Port G is selected and EMAC then there is a conflict between
- * the PHY interrupt line and TFS. Current settings prevent the conflict
- * by ignoring the TFS pin when Port G is selected. This allows both
- * codecs and EMAC using Port G concurrently.
- */
-#ifdef CONFIG_BF527_SPORT0_PORTG
-#define LOCAL_SPORT0_TFS (0)
-#else
-#define LOCAL_SPORT0_TFS (P_SPORT0_TFS)
-#endif
-
-static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
- P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0},
- {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI,
- P_SPORT1_RSCLK, P_SPORT1_TFS, 0} };
-
static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret = 0;
/* interface format:support I2S,slave mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- bf5xx_i2s.tcr1 |= TFSR | TCKFE;
- bf5xx_i2s.rcr1 |= RFSR | RCKFE;
- bf5xx_i2s.tcr2 |= TSFSE;
- bf5xx_i2s.rcr2 |= RSFSE;
+ bf5xx_i2s->tcr1 |= TFSR | TCKFE;
+ bf5xx_i2s->rcr1 |= RFSR | RCKFE;
+ bf5xx_i2s->tcr2 |= TSFSE;
+ bf5xx_i2s->rcr2 |= RSFSE;
break;
case SND_SOC_DAIFMT_DSP_A:
- bf5xx_i2s.tcr1 |= TFSR;
- bf5xx_i2s.rcr1 |= RFSR;
+ bf5xx_i2s->tcr1 |= TFSR;
+ bf5xx_i2s->rcr1 |= RFSR;
break;
case SND_SOC_DAIFMT_LEFT_J:
ret = -EINVAL;
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret = 0;
- bf5xx_i2s.tcr2 &= ~0x1f;
- bf5xx_i2s.rcr2 &= ~0x1f;
+ bf5xx_i2s->tcr2 &= ~0x1f;
+ bf5xx_i2s->rcr2 &= ~0x1f;
switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ bf5xx_i2s->tcr2 |= 7;
+ bf5xx_i2s->rcr2 |= 7;
+ sport_handle->wdsize = 1;
case SNDRV_PCM_FORMAT_S16_LE:
- bf5xx_i2s.tcr2 |= 15;
- bf5xx_i2s.rcr2 |= 15;
+ bf5xx_i2s->tcr2 |= 15;
+ bf5xx_i2s->rcr2 |= 15;
sport_handle->wdsize = 2;
break;
case SNDRV_PCM_FORMAT_S24_LE:
- bf5xx_i2s.tcr2 |= 23;
- bf5xx_i2s.rcr2 |= 23;
+ bf5xx_i2s->tcr2 |= 23;
+ bf5xx_i2s->rcr2 |= 23;
sport_handle->wdsize = 3;
break;
case SNDRV_PCM_FORMAT_S32_LE:
- bf5xx_i2s.tcr2 |= 31;
- bf5xx_i2s.rcr2 |= 31;
+ bf5xx_i2s->tcr2 |= 31;
+ bf5xx_i2s->rcr2 |= 31;
sport_handle->wdsize = 4;
break;
}
- if (!bf5xx_i2s.configured) {
+ if (!bf5xx_i2s->configured) {
/*
* TX and RX are not independent,they are enabled at the
* same time, even if only one side is running. So, we
*
* CPU DAI:slave mode.
*/
- bf5xx_i2s.configured = 1;
- ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1,
- bf5xx_i2s.rcr2, 0, 0);
+ bf5xx_i2s->configured = 1;
+ ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
+ bf5xx_i2s->rcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1,
- bf5xx_i2s.tcr2, 0, 0);
+ ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
+ bf5xx_i2s->tcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
+
pr_debug("%s enter\n", __func__);
/* No active stream, SPORT is allowed to be configured again. */
if (!dai->active)
- bf5xx_i2s.configured = 0;
-}
-
-static int bf5xx_i2s_probe(struct snd_soc_dai *dai)
-{
- pr_debug("%s enter\n", __func__);
- if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
- pr_err("Requesting Peripherals failed\n");
- return -EFAULT;
- }
-
- /* request DMA for SPORT */
- sport_handle = sport_init(&sport_params[sport_num], 4, \
- 2 * sizeof(u32), NULL);
- if (!sport_handle) {
- peripheral_free_list(&sport_req[sport_num][0]);
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int bf5xx_i2s_remove(struct snd_soc_dai *dai)
-{
- pr_debug("%s enter\n", __func__);
- peripheral_free_list(&sport_req[sport_num][0]);
- return 0;
+ bf5xx_i2s->configured = 0;
}
#ifdef CONFIG_PM
static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
pr_debug("%s : sport %d\n", __func__, dai->id);
static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret;
pr_debug("%s : sport %d\n", __func__, dai->id);
- ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1,
- bf5xx_i2s.rcr2, 0, 0);
+ ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
+ bf5xx_i2s->rcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1,
- bf5xx_i2s.tcr2, 0, 0);
+ ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
+ bf5xx_i2s->tcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_96000)
-#define BF5XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
- SNDRV_PCM_FMTBIT_S32_LE)
+#define BF5XX_I2S_FORMATS \
+ (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
.shutdown = bf5xx_i2s_shutdown,
};
static struct snd_soc_dai_driver bf5xx_i2s_dai = {
- .probe = bf5xx_i2s_probe,
- .remove = bf5xx_i2s_remove,
.suspend = bf5xx_i2s_suspend,
.resume = bf5xx_i2s_resume,
.playback = {
.ops = &bf5xx_i2s_dai_ops,
};
-static int bfin_i2s_drv_probe(struct platform_device *pdev)
+static int __devinit bf5xx_i2s_probe(struct platform_device *pdev)
{
- return snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai);
+ struct sport_device *sport_handle;
+ int ret;
+
+ /* configure SPORT for I2S */
+ sport_handle = sport_init(pdev, 4, 2 * sizeof(u32),
+ sizeof(struct bf5xx_i2s_port));
+ if (!sport_handle)
+ return -ENODEV;
+
+ /* register with the ASoC layers */
+ ret = snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai);
+ if (ret) {
+ pr_err("Failed to register DAI: %d\n", ret);
+ sport_done(sport_handle);
+ return ret;
+ }
+
+ return 0;
}
-static int __devexit bfin_i2s_drv_remove(struct platform_device *pdev)
+static int __devexit bf5xx_i2s_remove(struct platform_device *pdev)
{
+ struct sport_device *sport_handle = platform_get_drvdata(pdev);
+
+ pr_debug("%s enter\n", __func__);
+
snd_soc_unregister_dai(&pdev->dev);
+ sport_done(sport_handle);
+
return 0;
}
static struct platform_driver bfin_i2s_driver = {
- .probe = bfin_i2s_drv_probe,
- .remove = __devexit_p(bfin_i2s_drv_remove),
-
+ .probe = bf5xx_i2s_probe,
+ .remove = __devexit_p(bf5xx_i2s_remove),
.driver = {
- .name = "bf5xx-i2s",
+ .name = "bfin-i2s",
.owner = THIS_MODULE,
},
};
/* delay between frame sync pulse and first data bit in multichannel mode */
#define FRAME_DELAY (1<<12)
-struct sport_device *sport_handle;
-EXPORT_SYMBOL(sport_handle);
/* note: multichannel is in units of 8 channels,
* tdm_count is # channels NOT / 8 ! */
int sport_set_multichannel(struct sport_device *sport,
}
EXPORT_SYMBOL(sport_set_err_callback);
-struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
- unsigned dummy_count, void *private_data)
+static int sport_config_pdev(struct platform_device *pdev, struct sport_param *param)
{
- int ret;
+ /* Extract settings from platform data */
+ struct device *dev = &pdev->dev;
+ struct bfin_snd_platform_data *pdata = dev->platform_data;
+ struct resource *res;
+
+ param->num = pdev->id;
+
+ if (!pdata) {
+ dev_err(dev, "no platform_data\n");
+ return -ENODEV;
+ }
+ param->pin_req = pdata->pin_req;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "no MEM resource\n");
+ return -ENODEV;
+ }
+ param->regs = (struct sport_register *)res->start;
+
+ /* first RX, then TX */
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!res) {
+ dev_err(dev, "no rx DMA resource\n");
+ return -ENODEV;
+ }
+ param->dma_rx_chan = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!res) {
+ dev_err(dev, "no tx DMA resource\n");
+ return -ENODEV;
+ }
+ param->dma_tx_chan = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev, "no irq resource\n");
+ return -ENODEV;
+ }
+ param->err_irq = res->start;
+
+ return 0;
+}
+
+struct sport_device *sport_init(struct platform_device *pdev,
+ unsigned int wdsize, unsigned int dummy_count, size_t priv_size)
+{
+ struct device *dev = &pdev->dev;
+ struct sport_param param;
struct sport_device *sport;
- pr_debug("%s enter\n", __func__);
- BUG_ON(param == NULL);
- BUG_ON(wdsize == 0 || dummy_count == 0);
- sport = kmalloc(sizeof(struct sport_device), GFP_KERNEL);
- if (!sport) {
- pr_err("Failed to allocate for sport device\n");
+ int ret;
+
+ dev_dbg(dev, "%s enter\n", __func__);
+
+ param.wdsize = wdsize;
+ param.dummy_count = dummy_count;
+ BUG_ON(param.wdsize == 0 || param.dummy_count == 0);
+
+ ret = sport_config_pdev(pdev, ¶m);
+ if (ret)
+ return NULL;
+
+ if (peripheral_request_list(param.pin_req, "soc-audio")) {
+ dev_err(dev, "requesting Peripherals failed\n");
return NULL;
}
- memset(sport, 0, sizeof(struct sport_device));
- sport->dma_rx_chan = param->dma_rx_chan;
- sport->dma_tx_chan = param->dma_tx_chan;
- sport->err_irq = param->err_irq;
- sport->regs = param->regs;
- sport->private_data = private_data;
+ sport = kzalloc(sizeof(*sport), GFP_KERNEL);
+ if (!sport) {
+ dev_err(dev, "failed to allocate for sport device\n");
+ goto __init_err0;
+ }
+
+ sport->num = param.num;
+ sport->dma_rx_chan = param.dma_rx_chan;
+ sport->dma_tx_chan = param.dma_tx_chan;
+ sport->err_irq = param.err_irq;
+ sport->regs = param.regs;
+ sport->pin_req = param.pin_req;
if (request_dma(sport->dma_rx_chan, "SPORT RX Data") == -EBUSY) {
- pr_err("Failed to request RX dma %d\n", \
- sport->dma_rx_chan);
+ dev_err(dev, "failed to request RX dma %d\n", sport->dma_rx_chan);
goto __init_err1;
}
if (set_dma_callback(sport->dma_rx_chan, rx_handler, sport) != 0) {
- pr_err("Failed to request RX irq %d\n", \
- sport->dma_rx_chan);
+ dev_err(dev, "failed to request RX irq %d\n", sport->dma_rx_chan);
goto __init_err2;
}
if (request_dma(sport->dma_tx_chan, "SPORT TX Data") == -EBUSY) {
- pr_err("Failed to request TX dma %d\n", \
- sport->dma_tx_chan);
+ dev_err(dev, "failed to request TX dma %d\n", sport->dma_tx_chan);
goto __init_err2;
}
if (set_dma_callback(sport->dma_tx_chan, tx_handler, sport) != 0) {
- pr_err("Failed to request TX irq %d\n", \
- sport->dma_tx_chan);
+ dev_err(dev, "failed to request TX irq %d\n", sport->dma_tx_chan);
goto __init_err3;
}
if (request_irq(sport->err_irq, err_handler, IRQF_SHARED, "SPORT err",
sport) < 0) {
- pr_err("Failed to request err irq:%d\n", \
- sport->err_irq);
+ dev_err(dev, "failed to request err irq %d\n", sport->err_irq);
goto __init_err3;
}
- pr_err("dma rx:%d tx:%d, err irq:%d, regs:%p\n",
+ dev_info(dev, "dma rx:%d tx:%d, err irq:%d, regs:%p\n",
sport->dma_rx_chan, sport->dma_tx_chan,
sport->err_irq, sport->regs);
- sport->wdsize = wdsize;
- sport->dummy_count = dummy_count;
+ sport->wdsize = param.wdsize;
+ sport->dummy_count = param.dummy_count;
+
+ sport->private_data = kzalloc(priv_size, GFP_KERNEL);
+ if (!sport->private_data) {
+ dev_err(dev, "could not alloc priv data %zu bytes\n", priv_size);
+ goto __init_err4;
+ }
if (L1_DATA_A_LENGTH)
- sport->dummy_buf = l1_data_sram_zalloc(dummy_count * 2);
+ sport->dummy_buf = l1_data_sram_zalloc(param.dummy_count * 2);
else
- sport->dummy_buf = kzalloc(dummy_count * 2, GFP_KERNEL);
+ sport->dummy_buf = kzalloc(param.dummy_count * 2, GFP_KERNEL);
if (sport->dummy_buf == NULL) {
- pr_err("Failed to allocate dummy buffer\n");
- goto __error;
+ dev_err(dev, "failed to allocate dummy buffer\n");
+ goto __error1;
}
ret = sport_config_rx_dummy(sport);
if (ret) {
- pr_err("Failed to config rx dummy ring\n");
- goto __error;
+ dev_err(dev, "failed to config rx dummy ring\n");
+ goto __error2;
}
ret = sport_config_tx_dummy(sport);
if (ret) {
- pr_err("Failed to config tx dummy ring\n");
- goto __error;
+ dev_err(dev, "failed to config tx dummy ring\n");
+ goto __error3;
}
+ platform_set_drvdata(pdev, sport);
+
return sport;
-__error:
+__error3:
+ if (L1_DATA_A_LENGTH)
+ l1_data_sram_free(sport->dummy_rx_desc);
+ else
+ dma_free_coherent(NULL, 2*sizeof(struct dmasg),
+ sport->dummy_rx_desc, 0);
+__error2:
+ if (L1_DATA_A_LENGTH)
+ l1_data_sram_free(sport->dummy_buf);
+ else
+ kfree(sport->dummy_buf);
+__error1:
+ kfree(sport->private_data);
+__init_err4:
free_irq(sport->err_irq, sport);
__init_err3:
free_dma(sport->dma_tx_chan);
free_dma(sport->dma_rx_chan);
__init_err1:
kfree(sport);
+__init_err0:
+ peripheral_free_list(param.pin_req);
return NULL;
}
EXPORT_SYMBOL(sport_init);
free_dma(sport->dma_tx_chan);
free_irq(sport->err_irq, sport);
+ kfree(sport->private_data);
+ peripheral_free_list(sport->pin_req);
kfree(sport);
- sport = NULL;
}
EXPORT_SYMBOL(sport_done);
/*
- * File: bf5xx_ac97_sport.h
+ * File: bf5xx_sport.h
* Based on:
* Author: Roy Huang <roy.huang@analog.com>
*
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
+#include <linux/platform_device.h>
#include <asm/dma.h>
#include <asm/bfin_sport.h>
#define DESC_ELEMENT_COUNT 9
struct sport_device {
+ int num;
int dma_rx_chan;
int dma_tx_chan;
int err_irq;
+ const unsigned short *pin_req;
struct sport_register *regs;
unsigned char *rx_buf;
void *private_data;
};
-extern struct sport_device *sport_handle;
-
struct sport_param {
+ int num;
int dma_rx_chan;
int dma_tx_chan;
int err_irq;
+ const unsigned short *pin_req;
struct sport_register *regs;
+ unsigned int wdsize;
+ unsigned int dummy_count;
+ void *private_data;
};
-struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
- unsigned dummy_count, void *private_data);
+struct sport_device *sport_init(struct platform_device *pdev,
+ unsigned int wdsize, unsigned int dummy_count, size_t priv_size);
void sport_done(struct sport_device *sport);
static struct snd_soc_card bf5xx_ssm2602;
-static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-
- pr_debug("%s enter\n", __func__);
- snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
- return 0;
-}
-
static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
}
static struct snd_soc_ops bf5xx_ssm2602_ops = {
- .startup = bf5xx_ssm2602_startup,
.hw_params = bf5xx_ssm2602_hw_params,
};
-static struct snd_soc_dai_link bf5xx_ssm2602_dai = {
- .name = "ssm2602",
- .stream_name = "SSM2602",
- .cpu_dai_name = "bf5xx-i2s",
- .codec_dai_name = "ssm2602-hifi",
- .platform_name = "bf5xx-pcm-audio",
- .codec_name = "ssm2602-codec.0-001b",
- .ops = &bf5xx_ssm2602_ops,
+static struct snd_soc_dai_link bf5xx_ssm2602_dai[] = {
+ {
+ .name = "ssm2602",
+ .stream_name = "SSM2602",
+ .cpu_dai_name = "bfin-i2s.0",
+ .codec_dai_name = "ssm2602-hifi",
+ .platform_name = "bfin-i2s-pcm-audio",
+ .codec_name = "ssm2602.0-001b",
+ .ops = &bf5xx_ssm2602_ops,
+ },
+ {
+ .name = "ssm2602",
+ .stream_name = "SSM2602",
+ .cpu_dai_name = "bfin-i2s.1",
+ .codec_dai_name = "ssm2602-hifi",
+ .platform_name = "bfin-i2s-pcm-audio",
+ .codec_name = "ssm2602.0-001b",
+ .ops = &bf5xx_ssm2602_ops,
+ },
};
static struct snd_soc_card bf5xx_ssm2602 = {
- .name = "bf5xx_ssm2602",
- .dai_link = &bf5xx_ssm2602_dai,
+ .name = "bfin-ssm2602",
+ .dai_link = &bf5xx_ssm2602_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+
int ret = 0;
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
if (ret < 0)
goto out;
- if (sport_handle != NULL)
+ if (sport_handle != NULL) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sport_handle->tx_buf = buf->area;
+ else
+ sport_handle->rx_buf = buf->area;
+
runtime->private_data = sport_handle;
- else {
+ } else {
pr_err("sport_handle is NULL\n");
ret = -ENODEV;
}
}
buf->bytes = size;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- sport_handle->tx_buf = buf->area;
- else
- sport_handle->rx_buf = buf->area;
-
return 0;
}
dma_free_coherent(NULL, buf->bytes, buf->area, 0);
buf->area = NULL;
}
- if (sport_handle)
- sport_done(sport_handle);
}
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
static struct platform_driver bfin_tdm_driver = {
.driver = {
- .name = "bf5xx-tdm-pcm-audio",
+ .name = "bfin-tdm-pcm-audio",
.owner = THIS_MODULE,
},
#include "bf5xx-sport.h"
#include "bf5xx-tdm.h"
-static struct bf5xx_tdm_port bf5xx_tdm;
-static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
-
-static struct sport_param sport_params[2] = {
- {
- .dma_rx_chan = CH_SPORT0_RX,
- .dma_tx_chan = CH_SPORT0_TX,
- .err_irq = IRQ_SPORT0_ERROR,
- .regs = (struct sport_register *)SPORT0_TCR1,
- },
- {
- .dma_rx_chan = CH_SPORT1_RX,
- .dma_tx_chan = CH_SPORT1_TX,
- .err_irq = IRQ_SPORT1_ERROR,
- .regs = (struct sport_register *)SPORT1_TCR1,
- }
-};
-
-/*
- * Setting the TFS pin selector for SPORT 0 based on whether the selected
- * port id F or G. If the port is F then no conflict should exist for the
- * TFS. When Port G is selected and EMAC then there is a conflict between
- * the PHY interrupt line and TFS. Current settings prevent the conflict
- * by ignoring the TFS pin when Port G is selected. This allows both
- * codecs and EMAC using Port G concurrently.
- */
-#ifdef CONFIG_BF527_SPORT0_PORTG
-#define LOCAL_SPORT0_TFS (0)
-#else
-#define LOCAL_SPORT0_TFS (P_SPORT0_TFS)
-#endif
-
-static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
- P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0},
- {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI,
- P_SPORT1_RSCLK, P_SPORT1_TFS, 0} };
-
static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
int ret = 0;
- bf5xx_tdm.tcr2 &= ~0x1f;
- bf5xx_tdm.rcr2 &= ~0x1f;
+ bf5xx_tdm->tcr2 &= ~0x1f;
+ bf5xx_tdm->rcr2 &= ~0x1f;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S32_LE:
- bf5xx_tdm.tcr2 |= 31;
- bf5xx_tdm.rcr2 |= 31;
+ bf5xx_tdm->tcr2 |= 31;
+ bf5xx_tdm->rcr2 |= 31;
sport_handle->wdsize = 4;
break;
/* at present, we only support 32bit transfer */
break;
}
- if (!bf5xx_tdm.configured) {
+ if (!bf5xx_tdm->configured) {
/*
* TX and RX are not independent,they are enabled at the
* same time, even if only one side is running. So, we
*
* CPU DAI:slave mode.
*/
- ret = sport_config_rx(sport_handle, bf5xx_tdm.rcr1,
- bf5xx_tdm.rcr2, 0, 0);
+ ret = sport_config_rx(sport_handle, bf5xx_tdm->rcr1,
+ bf5xx_tdm->rcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- ret = sport_config_tx(sport_handle, bf5xx_tdm.tcr1,
- bf5xx_tdm.tcr2, 0, 0);
+ ret = sport_config_tx(sport_handle, bf5xx_tdm->tcr1,
+ bf5xx_tdm->tcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- bf5xx_tdm.configured = 1;
+ bf5xx_tdm->configured = 1;
}
return 0;
static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
+
/* No active stream, SPORT is allowed to be configured again. */
if (!dai->active)
- bf5xx_tdm.configured = 0;
+ bf5xx_tdm->configured = 0;
}
static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot)
{
+ struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
+ struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
int i;
unsigned int slot;
unsigned int tx_mapped = 0, rx_mapped = 0;
slot = tx_slot[i];
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
(!(tx_mapped & (1 << slot)))) {
- bf5xx_tdm.tx_map[i] = slot;
+ bf5xx_tdm->tx_map[i] = slot;
tx_mapped |= 1 << slot;
} else
return -EINVAL;
slot = rx_slot[i];
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
(!(rx_mapped & (1 << slot)))) {
- bf5xx_tdm.rx_map[i] = slot;
+ bf5xx_tdm->rx_map[i] = slot;
rx_mapped |= 1 << slot;
} else
return -EINVAL;
{
struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
- if (!dai->active)
- return 0;
- if (dai->capture_active)
- sport_rx_stop(sport);
if (dai->playback_active)
sport_tx_stop(sport);
+ if (dai->capture_active)
+ sport_rx_stop(sport);
+
+ /* isolate sync/clock pins from codec while sports resume */
+ peripheral_free_list(sport->pin_req);
+
return 0;
}
int ret;
struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
- if (!dai->active)
- return 0;
-
ret = sport_set_multichannel(sport, 8, 0xFF, 1);
if (ret) {
pr_err("SPORT is busy!\n");
ret = -EBUSY;
}
+ peripheral_request_list(sport->pin_req, "soc-audio");
+
return 0;
}
static int __devinit bfin_tdm_probe(struct platform_device *pdev)
{
- int ret = 0;
-
- if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
- pr_err("Requesting Peripherals failed\n");
- return -EFAULT;
- }
+ struct sport_device *sport_handle;
+ int ret;
- /* request DMA for SPORT */
- sport_handle = sport_init(&sport_params[sport_num], 4, \
- 8 * sizeof(u32), NULL);
- if (!sport_handle) {
- peripheral_free_list(&sport_req[sport_num][0]);
+ /* configure SPORT for TDM */
+ sport_handle = sport_init(pdev, 4, 8 * sizeof(u32),
+ sizeof(struct bf5xx_tdm_port));
+ if (!sport_handle)
return -ENODEV;
- }
/* SPORT works in TDM mode */
ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1);
goto sport_config_err;
}
- sport_handle->private_data = &bf5xx_tdm;
return 0;
sport_config_err:
- peripheral_free_list(&sport_req[sport_num][0]);
+ sport_done(sport_handle);
return ret;
}
static int __devexit bfin_tdm_remove(struct platform_device *pdev)
{
- peripheral_free_list(&sport_req[sport_num][0]);
+ struct sport_device *sport_handle = platform_get_drvdata(pdev);
+
snd_soc_unregister_dai(&pdev->dev);
+ sport_done(sport_handle);
return 0;
}
*/
#define PM860X_DAPM_OUTPUT(wname, wevent) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
- .shift = 0, .invert = 0, .kcontrols = NULL, \
+ .shift = 0, .invert = 0, .kcontrol_news = NULL, \
.num_kcontrols = 0, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD, }
select SND_SOC_AD1836 if SPI_MASTER
select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
+ select SND_SOC_AD73311
select SND_SOC_ADS117X
- select SND_SOC_AD73311 if I2C
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
+ select SND_SOC_AK4641 if I2C
select SND_SOC_AK4642 if I2C
select SND_SOC_AK4671 if I2C
select SND_SOC_ALC5623 if I2C
select SND_SOC_JZ4740_CODEC if SOC_JZ4740
select SND_SOC_LM4857 if I2C
select SND_SOC_MAX98088 if I2C
+ select SND_SOC_MAX98095 if I2C
select SND_SOC_MAX9850 if I2C
select SND_SOC_MAX9877 if I2C
select SND_SOC_PCM3008
select SND_SOC_SGTL5000 if I2C
select SND_SOC_SN95031 if INTEL_SCU_IPC
select SND_SOC_SPDIF
- select SND_SOC_SSM2602 if I2C
+ select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
select SND_SOC_TLV320AIC23 if I2C
select SND_SOC_TLV320AIC26 if SPI_MASTER
select SND_SOC_UDA134X
select SND_SOC_UDA1380 if I2C
select SND_SOC_WL1273 if MFD_WL1273_CORE
+ select SND_SOC_WM1250_EV1 if I2C
select SND_SOC_WM2000 if I2C
select SND_SOC_WM8350 if MFD_WM8350
select SND_SOC_WM8400 if MFD_WM8400
select SND_SOC_WM8900 if I2C
select SND_SOC_WM8903 if I2C
select SND_SOC_WM8904 if I2C
+ select SND_SOC_WM8915 if I2C
select SND_SOC_WM8940 if I2C
select SND_SOC_WM8955 if I2C
select SND_SOC_WM8960 if I2C
config SND_SOC_AK4535
tristate
+config SND_SOC_AK4641
+ tristate
+
config SND_SOC_AK4642
tristate
config SND_SOC_MAX98088
tristate
+config SND_SOC_MAX98095
+ tristate
+
config SND_SOC_MAX9850
tristate
config SND_SOC_WL1273
tristate
+config SND_SOC_WM1250_EV1
+ tristate
+
config SND_SOC_WM8350
tristate
config SND_SOC_WM8904
tristate
+config SND_SOC_WM8915
+ tristate
+
config SND_SOC_WM8940
tristate
snd-soc-ads117x-objs := ads117x.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
+snd-soc-ak4641-objs := ak4641.o
snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
snd-soc-cq93vc-objs := cq93vc.o
snd-soc-dmic-objs := dmic.o
snd-soc-l3-objs := l3.o
snd-soc-max98088-objs := max98088.o
+snd-soc-max98095-objs := max98095.o
snd-soc-max9850-objs := max9850.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-sgtl5000-objs := sgtl5000.o
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
snd-soc-wl1273-objs := wl1273.o
+snd-soc-wm1250-ev1-objs := wm1250-ev1.o
snd-soc-wm8350-objs := wm8350.o
snd-soc-wm8400-objs := wm8400.o
snd-soc-wm8510-objs := wm8510.o
snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o
snd-soc-wm8904-objs := wm8904.o
+snd-soc-wm8915-objs := wm8915.o
snd-soc-wm8940-objs := wm8940.o
snd-soc-wm8955-objs := wm8955.o
snd-soc-wm8960-objs := wm8960.o
snd-soc-wm8990-objs := wm8990.o
snd-soc-wm8991-objs := wm8991.o
snd-soc-wm8993-objs := wm8993.o
-snd-soc-wm8994-objs := wm8994.o wm8994-tables.o
+snd-soc-wm8994-objs := wm8994.o wm8994-tables.o wm8958-dsp2.o
snd-soc-wm8995-objs := wm8995.o
snd-soc-wm9081-objs := wm9081.o
snd-soc-wm9705-objs := wm9705.o
obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
+obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
+obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
+obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.o
+obj-$(CONFIG_SND_SOC_WM8915) += snd-soc-wm8915.o
obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o
obj-$(CONFIG_SND_SOC_WM8955) += snd-soc-wm8955.o
obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o
/* codec private data */
struct ad193x_priv {
- enum snd_soc_control_type bus_type;
- void *control_data;
+ enum snd_soc_control_type control_type;
int sysclk;
};
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
- codec->control_data = ad193x->control_data;
- if (ad193x->bus_type == SND_SOC_I2C)
- ret = snd_soc_codec_set_cache_io(codec, 8, 8, ad193x->bus_type);
+ if (ad193x->control_type == SND_SOC_I2C)
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, ad193x->control_type);
else
- ret = snd_soc_codec_set_cache_io(codec, 16, 8, ad193x->bus_type);
+ ret = snd_soc_codec_set_cache_io(codec, 16, 8, ad193x->control_type);
if (ret < 0) {
- dev_err(codec->dev, "failed to set cache I/O: %d\n",
- ret);
+ dev_err(codec->dev, "failed to set cache I/O: %d\n", ret);
return ret;
}
return -ENOMEM;
spi_set_drvdata(spi, ad193x);
- ad193x->control_data = spi;
- ad193x->bus_type = SND_SOC_SPI;
+ ad193x->control_type = SND_SOC_SPI;
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_ad193x, &ad193x_dai, 1);
static struct spi_driver ad193x_spi_driver = {
.driver = {
- .name = "ad193x-codec",
+ .name = "ad193x",
.owner = THIS_MODULE,
},
.probe = ad193x_spi_probe,
return -ENOMEM;
i2c_set_clientdata(client, ad193x);
- ad193x->control_data = client;
- ad193x->bus_type = SND_SOC_I2C;
+ ad193x->control_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&client->dev,
&soc_codec_dev_ad193x, &ad193x_dai, 1);
static struct i2c_driver ad193x_i2c_driver = {
.driver = {
- .name = "ad193x-codec",
+ .name = "ad193x",
},
.probe = ad193x_i2c_probe,
.remove = __devexit_p(ad193x_i2c_remove),
static struct platform_driver ad1980_codec_driver = {
.driver = {
- .name = "ad1980-codec",
+ .name = "ad1980",
.owner = THIS_MODULE,
},
static struct platform_driver ad73311_codec_driver = {
.driver = {
- .name = "ad73311-codec",
+ .name = "ad73311",
.owner = THIS_MODULE,
},
SND_SOC_DAPM_INPUT("AIN"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route ak4535_audio_map[] = {
/*stereo mixer */
{"Stereo Mixer", "Playback Switch", "DAC"},
{"Stereo Mixer", "Mic Sidetone Switch", "Mic"},
{"Input Mixer", "Aux Capture Switch", "Aux In"},
};
-static int ak4535_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, ak4535_dapm_widgets,
- ARRAY_SIZE(ak4535_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- return 0;
-}
-
static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
snd_soc_add_controls(codec, ak4535_snd_controls,
ARRAY_SIZE(ak4535_snd_controls));
- ak4535_add_widgets(codec);
-
return 0;
}
.reg_cache_size = ARRAY_SIZE(ak4535_reg),
.reg_word_size = sizeof(u8),
.reg_cache_default = ak4535_reg,
+ .dapm_widgets = ak4535_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets),
+ .dapm_routes = ak4535_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(ak4535_audio_map),
};
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
--- /dev/null
+/*
+ * ak4641.c -- AK4641 ALSA Soc Audio driver
+ *
+ * Copyright (C) 2008 Harald Welte <laforge@gnufiish.org>
+ * Copyright (C) 2011 Dmitry Artamonow <mad_soft@inbox.ru>
+ *
+ * Based on ak4535.c by Richard Purdie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/ak4641.h>
+
+#include "ak4641.h"
+
+/* codec private data */
+struct ak4641_priv {
+ struct snd_soc_codec *codec;
+ unsigned int sysclk;
+ int deemph;
+ int playback_fs;
+};
+
+/*
+ * ak4641 register cache
+ */
+static const u8 ak4641_reg[AK4641_CACHEREGNUM] = {
+ 0x00, 0x80, 0x00, 0x80,
+ 0x02, 0x00, 0x11, 0x05,
+ 0x00, 0x00, 0x36, 0x10,
+ 0x00, 0x00, 0x57, 0x00,
+ 0x88, 0x88, 0x08, 0x08
+};
+
+static const int deemph_settings[] = {44100, 0, 48000, 32000};
+
+static int ak4641_set_deemph(struct snd_soc_codec *codec)
+{
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ int i, best = 0;
+
+ for (i = 0 ; i < ARRAY_SIZE(deemph_settings); i++) {
+ /* if deemphasis is on, select the nearest available rate */
+ if (ak4641->deemph && deemph_settings[i] != 0 &&
+ abs(deemph_settings[i] - ak4641->playback_fs) <
+ abs(deemph_settings[best] - ak4641->playback_fs))
+ best = i;
+
+ if (!ak4641->deemph && deemph_settings[i] == 0)
+ best = i;
+ }
+
+ dev_dbg(codec->dev, "Set deemphasis %d\n", best);
+
+ return snd_soc_update_bits(codec, AK4641_DAC, 0x3, best);
+}
+
+static int ak4641_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ int deemph = ucontrol->value.enumerated.item[0];
+
+ if (deemph > 1)
+ return -EINVAL;
+
+ ak4641->deemph = deemph;
+
+ return ak4641_set_deemph(codec);
+}
+
+static int ak4641_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = ak4641->deemph;
+ return 0;
+};
+
+static const char *ak4641_mono_out[] = {"(L + R)/2", "Hi-Z"};
+static const char *ak4641_hp_out[] = {"Stereo", "Mono"};
+static const char *ak4641_mic_select[] = {"Internal", "External"};
+static const char *ak4641_mic_or_dac[] = {"Microphone", "Voice DAC"};
+
+
+static const DECLARE_TLV_DB_SCALE(mono_gain_tlv, -1700, 2300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, 0, 2000, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(master_tlv, -12750, 50, 0);
+static const DECLARE_TLV_DB_SCALE(mic_stereo_sidetone_tlv, -2700, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_mono_sidetone_tlv, -400, 400, 0);
+static const DECLARE_TLV_DB_SCALE(capture_tlv, -800, 50, 0);
+static const DECLARE_TLV_DB_SCALE(alc_tlv, -800, 50, 0);
+static const DECLARE_TLV_DB_SCALE(aux_in_tlv, -2100, 300, 0);
+
+
+static const struct soc_enum ak4641_mono_out_enum =
+ SOC_ENUM_SINGLE(AK4641_SIG1, 6, 2, ak4641_mono_out);
+static const struct soc_enum ak4641_hp_out_enum =
+ SOC_ENUM_SINGLE(AK4641_MODE2, 2, 2, ak4641_hp_out);
+static const struct soc_enum ak4641_mic_select_enum =
+ SOC_ENUM_SINGLE(AK4641_MIC, 1, 2, ak4641_mic_select);
+static const struct soc_enum ak4641_mic_or_dac_enum =
+ SOC_ENUM_SINGLE(AK4641_BTIF, 4, 2, ak4641_mic_or_dac);
+
+static const struct snd_kcontrol_new ak4641_snd_controls[] = {
+ SOC_ENUM("Mono 1 Output", ak4641_mono_out_enum),
+ SOC_SINGLE_TLV("Mono 1 Gain Volume", AK4641_SIG1, 7, 1, 1,
+ mono_gain_tlv),
+ SOC_ENUM("Headphone Output", ak4641_hp_out_enum),
+ SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0,
+ ak4641_get_deemph, ak4641_put_deemph),
+
+ SOC_SINGLE_TLV("Mic Boost Volume", AK4641_MIC, 0, 1, 0, mic_boost_tlv),
+
+ SOC_SINGLE("ALC Operation Time", AK4641_TIMER, 0, 3, 0),
+ SOC_SINGLE("ALC Recovery Time", AK4641_TIMER, 2, 3, 0),
+ SOC_SINGLE("ALC ZC Time", AK4641_TIMER, 4, 3, 0),
+
+ SOC_SINGLE("ALC 1 Switch", AK4641_ALC1, 5, 1, 0),
+
+ SOC_SINGLE_TLV("ALC Volume", AK4641_ALC2, 0, 71, 0, alc_tlv),
+ SOC_SINGLE("Left Out Enable Switch", AK4641_SIG2, 1, 1, 0),
+ SOC_SINGLE("Right Out Enable Switch", AK4641_SIG2, 0, 1, 0),
+
+ SOC_SINGLE_TLV("Capture Volume", AK4641_PGA, 0, 71, 0, capture_tlv),
+
+ SOC_DOUBLE_R_TLV("Master Playback Volume", AK4641_LATT,
+ AK4641_RATT, 0, 255, 1, master_tlv),
+
+ SOC_SINGLE_TLV("AUX In Volume", AK4641_VOL, 0, 15, 0, aux_in_tlv),
+
+ SOC_SINGLE("Equalizer Switch", AK4641_DAC, 2, 1, 0),
+ SOC_SINGLE_TLV("EQ1 100 Hz Volume", AK4641_EQLO, 0, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ2 250 Hz Volume", AK4641_EQLO, 4, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ3 1 kHz Volume", AK4641_EQMID, 0, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ4 3.5 kHz Volume", AK4641_EQMID, 4, 15, 1, eq_tlv),
+ SOC_SINGLE_TLV("EQ5 10 kHz Volume", AK4641_EQHI, 0, 15, 1, eq_tlv),
+};
+
+/* Mono 1 Mixer */
+static const struct snd_kcontrol_new ak4641_mono1_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Mic Mono Sidetone Volume", AK4641_VOL, 7, 1, 0,
+ mic_mono_sidetone_tlv),
+ SOC_DAPM_SINGLE("Mic Mono Sidetone Switch", AK4641_SIG1, 4, 1, 0),
+ SOC_DAPM_SINGLE("Mono Playback Switch", AK4641_SIG1, 5, 1, 0),
+};
+
+/* Stereo Mixer */
+static const struct snd_kcontrol_new ak4641_stereo_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("Mic Sidetone Volume", AK4641_VOL, 4, 7, 0,
+ mic_stereo_sidetone_tlv),
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4641_SIG2, 4, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", AK4641_SIG2, 7, 1, 0),
+ SOC_DAPM_SINGLE("Aux Bypass Switch", AK4641_SIG2, 5, 1, 0),
+};
+
+/* Input Mixer */
+static const struct snd_kcontrol_new ak4641_input_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Mic Capture Switch", AK4641_MIC, 2, 1, 0),
+ SOC_DAPM_SINGLE("Aux Capture Switch", AK4641_MIC, 5, 1, 0),
+};
+
+/* Mic mux */
+static const struct snd_kcontrol_new ak4641_mic_mux_control =
+ SOC_DAPM_ENUM("Mic Select", ak4641_mic_select_enum);
+
+/* Input mux */
+static const struct snd_kcontrol_new ak4641_input_mux_control =
+ SOC_DAPM_ENUM("Input Select", ak4641_mic_or_dac_enum);
+
+/* mono 2 switch */
+static const struct snd_kcontrol_new ak4641_mono2_control =
+ SOC_DAPM_SINGLE("Switch", AK4641_SIG1, 0, 1, 0);
+
+/* ak4641 dapm widgets */
+static const struct snd_soc_dapm_widget ak4641_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4641_stereo_mixer_controls[0],
+ ARRAY_SIZE(ak4641_stereo_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4641_mono1_mixer_controls[0],
+ ARRAY_SIZE(ak4641_mono1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4641_input_mixer_controls[0],
+ ARRAY_SIZE(ak4641_input_mixer_controls)),
+ SND_SOC_DAPM_MUX("Mic Mux", SND_SOC_NOPM, 0, 0,
+ &ak4641_mic_mux_control),
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+ &ak4641_input_mux_control),
+ SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
+ &ak4641_mono2_control),
+
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+ SND_SOC_DAPM_OUTPUT("MOUT1"),
+ SND_SOC_DAPM_OUTPUT("MOUT2"),
+ SND_SOC_DAPM_OUTPUT("MICOUT"),
+
+ SND_SOC_DAPM_ADC("ADC", "HiFi Capture", AK4641_PM1, 0, 0),
+ SND_SOC_DAPM_PGA("Mic", AK4641_PM1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("AUX In", AK4641_PM1, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono Out", AK4641_PM1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Line Out", AK4641_PM1, 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("DAC", "HiFi Playback", AK4641_PM2, 0, 0),
+ SND_SOC_DAPM_PGA("Mono Out 2", AK4641_PM2, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("Voice ADC", "Voice Capture", AK4641_BTIF, 0, 0),
+ SND_SOC_DAPM_ADC("Voice DAC", "Voice Playback", AK4641_BTIF, 1, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4641_MIC, 3, 0),
+ SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4641_MIC, 4, 0),
+
+ SND_SOC_DAPM_INPUT("MICIN"),
+ SND_SOC_DAPM_INPUT("MICEXT"),
+ SND_SOC_DAPM_INPUT("AUX"),
+ SND_SOC_DAPM_INPUT("AIN"),
+};
+
+static const struct snd_soc_dapm_route ak4641_audio_map[] = {
+ /* Stereo Mixer */
+ {"Stereo Mixer", "Playback Switch", "DAC"},
+ {"Stereo Mixer", "Mic Sidetone Switch", "Input Mux"},
+ {"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
+
+ /* Mono 1 Mixer */
+ {"Mono1 Mixer", "Mic Mono Sidetone Switch", "Input Mux"},
+ {"Mono1 Mixer", "Mono Playback Switch", "DAC"},
+
+ /* Mic */
+ {"Mic", NULL, "AIN"},
+ {"Mic Mux", "Internal", "Mic Int Bias"},
+ {"Mic Mux", "External", "Mic Ext Bias"},
+ {"Mic Int Bias", NULL, "MICIN"},
+ {"Mic Ext Bias", NULL, "MICEXT"},
+ {"MICOUT", NULL, "Mic Mux"},
+
+ /* Input Mux */
+ {"Input Mux", "Microphone", "Mic"},
+ {"Input Mux", "Voice DAC", "Voice DAC"},
+
+ /* Line Out */
+ {"LOUT", NULL, "Line Out"},
+ {"ROUT", NULL, "Line Out"},
+ {"Line Out", NULL, "Stereo Mixer"},
+
+ /* Mono 1 Out */
+ {"MOUT1", NULL, "Mono Out"},
+ {"Mono Out", NULL, "Mono1 Mixer"},
+
+ /* Mono 2 Out */
+ {"MOUT2", NULL, "Mono 2 Enable"},
+ {"Mono 2 Enable", "Switch", "Mono Out 2"},
+ {"Mono Out 2", NULL, "Stereo Mixer"},
+
+ {"Voice ADC", NULL, "Mono 2 Enable"},
+
+ /* Aux In */
+ {"AUX In", NULL, "AUX"},
+
+ /* ADC */
+ {"ADC", NULL, "Input Mixer"},
+ {"Input Mixer", "Mic Capture Switch", "Mic"},
+ {"Input Mixer", "Aux Capture Switch", "AUX In"},
+};
+
+static int ak4641_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+
+ ak4641->sysclk = freq;
+ return 0;
+}
+
+static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ int rate = params_rate(params), fs = 256;
+ u8 mode2;
+
+ if (rate)
+ fs = ak4641->sysclk / rate;
+ else
+ return -EINVAL;
+
+ /* set fs */
+ switch (fs) {
+ case 1024:
+ mode2 = (0x2 << 5);
+ break;
+ case 512:
+ mode2 = (0x1 << 5);
+ break;
+ case 256:
+ mode2 = (0x0 << 5);
+ break;
+ default:
+ dev_err(codec->dev, "Error: unsupported fs=%d\n", fs);
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, AK4641_MODE2, (0x3 << 5), mode2);
+
+ /* Update de-emphasis filter for the new rate */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ak4641->playback_fs = rate;
+ ak4641_set_deemph(codec);
+ };
+
+ return 0;
+}
+
+static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 btif;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ btif = (0x3 << 5);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ btif = (0x2 << 5);
+ break;
+ case SND_SOC_DAIFMT_DSP_A: /* MSB after FRM */
+ btif = (0x0 << 5);
+ break;
+ case SND_SOC_DAIFMT_DSP_B: /* MSB during FRM */
+ btif = (0x1 << 5);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_update_bits(codec, AK4641_BTIF, (0x3 << 5), btif);
+}
+
+static int ak4641_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 mode1 = 0;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ mode1 = 0x02;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ mode1 = 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_write(codec, AK4641_MODE1, mode1);
+}
+
+static int ak4641_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ return snd_soc_update_bits(codec, AK4641_DAC, 0x20, mute ? 0x20 : 0);
+}
+
+static int ak4641_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct ak4641_platform_data *pdata = codec->dev->platform_data;
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* unmute */
+ snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* mute */
+ snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0x20);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (pdata && gpio_is_valid(pdata->gpio_power))
+ gpio_set_value(pdata->gpio_power, 1);
+ mdelay(1);
+ if (pdata && gpio_is_valid(pdata->gpio_npdn))
+ gpio_set_value(pdata->gpio_npdn, 1);
+ mdelay(1);
+
+ ret = snd_soc_cache_sync(codec);
+ if (ret) {
+ dev_err(codec->dev,
+ "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ }
+ snd_soc_update_bits(codec, AK4641_PM1, 0x80, 0x80);
+ snd_soc_update_bits(codec, AK4641_PM2, 0x80, 0);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, AK4641_PM1, 0x80, 0);
+ if (pdata && gpio_is_valid(pdata->gpio_npdn))
+ gpio_set_value(pdata->gpio_npdn, 0);
+ if (pdata && gpio_is_valid(pdata->gpio_power))
+ gpio_set_value(pdata->gpio_power, 0);
+ codec->cache_sync = 1;
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define AK4641_RATES (SNDRV_PCM_RATE_8000_48000)
+#define AK4641_RATES_BT (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000)
+#define AK4641_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+static struct snd_soc_dai_ops ak4641_i2s_dai_ops = {
+ .hw_params = ak4641_i2s_hw_params,
+ .set_fmt = ak4641_i2s_set_dai_fmt,
+ .digital_mute = ak4641_mute,
+ .set_sysclk = ak4641_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_ops ak4641_pcm_dai_ops = {
+ .hw_params = NULL, /* rates are controlled by BT chip */
+ .set_fmt = ak4641_pcm_set_dai_fmt,
+ .digital_mute = ak4641_mute,
+ .set_sysclk = ak4641_set_dai_sysclk,
+};
+
+struct snd_soc_dai_driver ak4641_dai[] = {
+{
+ .name = "ak4641-hifi",
+ .id = 1,
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4641_RATES,
+ .formats = AK4641_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4641_RATES,
+ .formats = AK4641_FORMATS,
+ },
+ .ops = &ak4641_i2s_dai_ops,
+ .symmetric_rates = 1,
+},
+{
+ .name = "ak4641-voice",
+ .id = 1,
+ .playback = {
+ .stream_name = "Voice Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = AK4641_RATES_BT,
+ .formats = AK4641_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Voice Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = AK4641_RATES_BT,
+ .formats = AK4641_FORMATS,
+ },
+ .ops = &ak4641_pcm_dai_ops,
+ .symmetric_rates = 1,
+},
+};
+
+static int ak4641_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+ ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int ak4641_resume(struct snd_soc_codec *codec)
+{
+ ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ return 0;
+}
+
+static int ak4641_probe(struct snd_soc_codec *codec)
+{
+ struct ak4641_platform_data *pdata = codec->dev->platform_data;
+ int ret;
+
+
+ if (pdata) {
+ if (gpio_is_valid(pdata->gpio_power)) {
+ ret = gpio_request_one(pdata->gpio_power,
+ GPIOF_OUT_INIT_LOW, "ak4641 power");
+ if (ret)
+ goto err_out;
+ }
+ if (gpio_is_valid(pdata->gpio_npdn)) {
+ ret = gpio_request_one(pdata->gpio_npdn,
+ GPIOF_OUT_INIT_LOW, "ak4641 npdn");
+ if (ret)
+ goto err_gpio;
+
+ udelay(1); /* > 150 ns */
+ gpio_set_value(pdata->gpio_npdn, 1);
+ }
+ }
+
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err_register;
+ }
+
+ /* power on device */
+ ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+
+err_register:
+ if (pdata) {
+ if (gpio_is_valid(pdata->gpio_power))
+ gpio_set_value(pdata->gpio_power, 0);
+ if (gpio_is_valid(pdata->gpio_npdn))
+ gpio_free(pdata->gpio_npdn);
+ }
+err_gpio:
+ if (pdata && gpio_is_valid(pdata->gpio_power))
+ gpio_free(pdata->gpio_power);
+err_out:
+ return ret;
+}
+
+static int ak4641_remove(struct snd_soc_codec *codec)
+{
+ struct ak4641_platform_data *pdata = codec->dev->platform_data;
+
+ ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ if (pdata) {
+ if (gpio_is_valid(pdata->gpio_power)) {
+ gpio_set_value(pdata->gpio_power, 0);
+ gpio_free(pdata->gpio_power);
+ }
+ if (gpio_is_valid(pdata->gpio_npdn))
+ gpio_free(pdata->gpio_npdn);
+ }
+ return 0;
+}
+
+
+static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
+ .probe = ak4641_probe,
+ .remove = ak4641_remove,
+ .suspend = ak4641_suspend,
+ .resume = ak4641_resume,
+ .controls = ak4641_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4641_snd_controls),
+ .dapm_widgets = ak4641_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4641_dapm_widgets),
+ .dapm_routes = ak4641_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(ak4641_audio_map),
+ .set_bias_level = ak4641_set_bias_level,
+ .reg_cache_size = ARRAY_SIZE(ak4641_reg),
+ .reg_word_size = sizeof(u8),
+ .reg_cache_default = ak4641_reg,
+ .reg_cache_step = 1,
+};
+
+
+static int __devinit ak4641_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct ak4641_priv *ak4641;
+ int ret;
+
+ ak4641 = kzalloc(sizeof(struct ak4641_priv), GFP_KERNEL);
+ if (!ak4641)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, ak4641);
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_ak4641,
+ ak4641_dai, ARRAY_SIZE(ak4641_dai));
+ if (ret < 0)
+ kfree(ak4641);
+
+ return ret;
+}
+
+static int __devexit ak4641_i2c_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+ kfree(i2c_get_clientdata(i2c));
+ return 0;
+}
+
+static const struct i2c_device_id ak4641_i2c_id[] = {
+ { "ak4641", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4641_i2c_id);
+
+static struct i2c_driver ak4641_i2c_driver = {
+ .driver = {
+ .name = "ak4641",
+ .owner = THIS_MODULE,
+ },
+ .probe = ak4641_i2c_probe,
+ .remove = __devexit_p(ak4641_i2c_remove),
+ .id_table = ak4641_i2c_id,
+};
+
+static int __init ak4641_modinit(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&ak4641_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register AK4641 I2C driver: %d\n", ret);
+
+ return ret;
+}
+module_init(ak4641_modinit);
+
+static void __exit ak4641_exit(void)
+{
+ i2c_del_driver(&ak4641_i2c_driver);
+}
+module_exit(ak4641_exit);
+
+MODULE_DESCRIPTION("SoC AK4641 driver");
+MODULE_AUTHOR("Harald Welte <laforge@gnufiish.org>");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * ak4641.h -- AK4641 SoC Audio driver
+ *
+ * Copyright 2008 Harald Welte <laforge@gnufiish.org>
+ *
+ * Based on ak4535.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _AK4641_H
+#define _AK4641_H
+
+/* AK4641 register space */
+
+#define AK4641_PM1 0x00
+#define AK4641_PM2 0x01
+#define AK4641_SIG1 0x02
+#define AK4641_SIG2 0x03
+#define AK4641_MODE1 0x04
+#define AK4641_MODE2 0x05
+#define AK4641_DAC 0x06
+#define AK4641_MIC 0x07
+#define AK4641_TIMER 0x08
+#define AK4641_ALC1 0x09
+#define AK4641_ALC2 0x0a
+#define AK4641_PGA 0x0b
+#define AK4641_LATT 0x0c
+#define AK4641_RATT 0x0d
+#define AK4641_VOL 0x0e
+#define AK4641_STATUS 0x0f
+#define AK4641_EQLO 0x10
+#define AK4641_EQMID 0x11
+#define AK4641_EQHI 0x12
+#define AK4641_BTIF 0x13
+
+#define AK4641_CACHEREGNUM 0x14
+
+
+
+#define AK4641_DAI_HIFI 0
+#define AK4641_DAI_VOICE 1
+
+
+#endif
SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0),
};
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route ak4671_intercon[] = {
{"DAC Left", "NULL", "PMPLL"},
{"DAC Right", "NULL", "PMPLL"},
{"ADC Left", "NULL", "PMPLL"},
{"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"},
};
-static int ak4671_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, ak4671_dapm_widgets,
- ARRAY_SIZE(ak4671_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
static int ak4671_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
snd_soc_add_controls(codec, ak4671_snd_controls,
ARRAY_SIZE(ak4671_snd_controls));
- ak4671_add_widgets(codec);
ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
.reg_cache_size = AK4671_CACHEREGNUM,
.reg_word_size = sizeof(u8),
.reg_cache_default = ak4671_reg,
+ .dapm_widgets = ak4671_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4671_dapm_widgets),
+ .dapm_routes = ak4671_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4671_intercon),
};
static int __devinit ak4671_i2c_probe(struct i2c_client *client,
{"ADC", NULL, "Input Mixer"},
};
-static int cx20442_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, cx20442_dapm_widgets,
- ARRAY_SIZE(cx20442_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, cx20442_audio_map,
- ARRAY_SIZE(cx20442_audio_map));
-
- return 0;
-}
-
static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
return -ENOMEM;
snd_soc_codec_set_drvdata(codec, cx20442);
- cx20442_add_widgets(codec);
-
cx20442->control_data = NULL;
codec->hw_write = NULL;
codec->card->pop_time = 0;
.reg_word_size = sizeof(u8),
.read = cx20442_read_reg_cache,
.write = cx20442_write,
+ .dapm_widgets = cx20442_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cx20442_dapm_widgets),
+ .dapm_routes = cx20442_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cx20442_audio_map),
};
static int cx20442_platform_probe(struct platform_device *pdev)
},
};
-static struct snd_soc_codec_driver soc_dmic = {};
+static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_OUT("DMIC AIF", "Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_INPUT("DMic"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"DMIC AIF", NULL, "DMic"},
+};
+
+static int dmic_probe(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ snd_soc_dapm_new_controls(dapm, dmic_dapm_widgets,
+ ARRAY_SIZE(dmic_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
+ snd_soc_dapm_new_widgets(dapm);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_dmic = {
+ .probe = dmic_probe,
+};
static int __devinit dmic_dev_probe(struct platform_device *pdev)
{
static int jz4740_codec_dev_probe(struct snd_soc_codec *codec)
{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
- snd_soc_add_controls(codec, jz4740_codec_controls,
- ARRAY_SIZE(jz4740_codec_controls));
-
- snd_soc_dapm_new_controls(dapm, jz4740_codec_dapm_widgets,
- ARRAY_SIZE(jz4740_codec_dapm_widgets));
-
- snd_soc_dapm_add_routes(dapm, jz4740_codec_dapm_routes,
- ARRAY_SIZE(jz4740_codec_dapm_routes));
-
jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
.reg_cache_default = jz4740_codec_regs,
.reg_word_size = sizeof(u32),
.reg_cache_size = 2,
+
+ .controls = jz4740_codec_controls,
+ .num_controls = ARRAY_SIZE(jz4740_codec_controls),
+ .dapm_widgets = jz4740_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(jz4740_codec_dapm_widgets),
+ .dapm_routes = jz4740_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(jz4740_codec_dapm_routes),
};
static int __devinit jz4740_codec_probe(struct platform_device *pdev)
ARRAY_SIZE(max98088_exmode_texts),
max98088_exmode_texts,
max98088_exmode_values);
-static const struct snd_kcontrol_new max98088_exmode_controls =
- SOC_DAPM_VALUE_ENUM("Route", max98088_exmode_enum);
static const char *max98088_ex_thresh[] = { /* volts PP */
"0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"};
SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
+ SOC_ENUM("EX Limiter Mode", max98088_exmode_enum),
SOC_ENUM("EX Limiter Threshold", max98088_ex_thresh_enum),
SOC_ENUM("DAI1 Filter Mode", max98088_filter_mode_enum),
/* Left speaker mixer switch */
static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
/* Left headphone mixer switch */
static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
/* Left earpiece/receiver mixer switch */
static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
- SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
- SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
SND_SOC_DAPM_MICBIAS("MICBIAS", M98088_REG_4C_PWR_EN_IN, 3, 0),
- SND_SOC_DAPM_MUX("EX Limiter Mode", SND_SOC_NOPM, 0, 0,
- &max98088_exmode_controls),
-
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
SND_SOC_DAPM_OUTPUT("SPKL"),
SND_SOC_DAPM_INPUT("INB2"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route max98088_audio_map[] = {
/* Left headphone output mixer */
{"Left HP Mixer", "Left DAC1 Switch", "DACL1"},
{"Left HP Mixer", "Left DAC2 Switch", "DACL2"},
{"MIC2 Input", NULL, "MIC2"},
};
-static int max98088_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, max98088_dapm_widgets,
- ARRAY_SIZE(max98088_dapm_widgets));
-
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- snd_soc_add_controls(codec, max98088_snd_controls,
- ARRAY_SIZE(max98088_snd_controls));
-
- snd_soc_dapm_new_widgets(dapm);
- return 0;
-}
-
/* codec mclk clock divider coefficients */
static const struct {
u32 rate;
return 0;
}
+static int max98088_dai1_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int reg;
+
+ if (mute)
+ reg = M98088_DAI_MUTE;
+ else
+ reg = 0;
+
+ snd_soc_update_bits(codec, M98088_REG_2F_LVL_DAI1_PLAY,
+ M98088_DAI_MUTE_MASK, reg);
+ return 0;
+}
+
+static int max98088_dai2_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int reg;
+
+ if (mute)
+ reg = M98088_DAI_MUTE;
+ else
+ reg = 0;
+
+ snd_soc_update_bits(codec, M98088_REG_31_LVL_DAI2_PLAY,
+ M98088_DAI_MUTE_MASK, reg);
+ return 0;
+}
+
static void max98088_sync_cache(struct snd_soc_codec *codec)
{
u16 *reg_cache = codec->reg_cache;
.set_sysclk = max98088_dai_set_sysclk,
.set_fmt = max98088_dai1_set_fmt,
.hw_params = max98088_dai1_hw_params,
+ .digital_mute = max98088_dai1_digital_mute,
};
static struct snd_soc_dai_ops max98088_dai2_ops = {
.set_sysclk = max98088_dai_set_sysclk,
.set_fmt = max98088_dai2_set_fmt,
.hw_params = max98088_dai2_hw_params,
+ .digital_mute = max98088_dai2_digital_mute,
};
static struct snd_soc_dai_driver max98088_dai[] = {
max98088_handle_pdata(codec);
- max98088_add_widgets(codec);
+ snd_soc_add_controls(codec, max98088_snd_controls,
+ ARRAY_SIZE(max98088_snd_controls));
err_access:
return ret;
.reg_word_size = sizeof(u8),
.reg_cache_default = max98088_reg,
.volatile_register = max98088_volatile_register,
+ .dapm_widgets = max98088_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98088_dapm_widgets),
+ .dapm_routes = max98088_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98088_audio_map),
};
static int max98088_i2c_probe(struct i2c_client *i2c,
#define M98088_REC_LINEMODE (1<<7)
#define M98088_REC_LINEMODE_MASK (1<<7)
+/* M98088_REG_2D_MIX_SPK_CNTL */
+ #define M98088_MIX_SPKR_GAIN_MASK (3<<2)
+ #define M98088_MIX_SPKR_GAIN_SHIFT 2
+ #define M98088_MIX_SPKL_GAIN_MASK (3<<0)
+ #define M98088_MIX_SPKL_GAIN_SHIFT 0
+
+/* M98088_REG_2F_LVL_DAI1_PLAY, M98088_REG_31_LVL_DAI2_PLAY */
+ #define M98088_DAI_MUTE (1<<7)
+ #define M98088_DAI_MUTE_MASK (1<<7)
+ #define M98088_DAI_VOICE_GAIN_MASK (3<<4)
+ #define M98088_DAI_ATTENUATION_MASK (0xF<<0)
+ #define M98088_DAI_ATTENUATION_SHIFT 0
+
/* M98088_REG_35_LVL_MIC1, M98088_REG_36_LVL_MIC2 */
#define M98088_MICPRE_MASK (3<<5)
#define M98088_MICPRE_SHIFT 5
--- /dev/null
+/*
+ * max98095.c -- MAX98095 ALSA SoC Audio driver
+ *
+ * Copyright 2011 Maxim Integrated Products
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+#include <sound/max98095.h>
+#include "max98095.h"
+
+enum max98095_type {
+ MAX98095,
+};
+
+struct max98095_cdata {
+ unsigned int rate;
+ unsigned int fmt;
+ int eq_sel;
+ int bq_sel;
+};
+
+struct max98095_priv {
+ enum max98095_type devtype;
+ void *control_data;
+ struct max98095_pdata *pdata;
+ unsigned int sysclk;
+ struct max98095_cdata dai[3];
+ const char **eq_texts;
+ const char **bq_texts;
+ struct soc_enum eq_enum;
+ struct soc_enum bq_enum;
+ int eq_textcnt;
+ int bq_textcnt;
+ u8 lin_state;
+ unsigned int mic1pre;
+ unsigned int mic2pre;
+};
+
+static const u8 max98095_reg_def[M98095_REG_CNT] = {
+ 0x00, /* 00 */
+ 0x00, /* 01 */
+ 0x00, /* 02 */
+ 0x00, /* 03 */
+ 0x00, /* 04 */
+ 0x00, /* 05 */
+ 0x00, /* 06 */
+ 0x00, /* 07 */
+ 0x00, /* 08 */
+ 0x00, /* 09 */
+ 0x00, /* 0A */
+ 0x00, /* 0B */
+ 0x00, /* 0C */
+ 0x00, /* 0D */
+ 0x00, /* 0E */
+ 0x00, /* 0F */
+ 0x00, /* 10 */
+ 0x00, /* 11 */
+ 0x00, /* 12 */
+ 0x00, /* 13 */
+ 0x00, /* 14 */
+ 0x00, /* 15 */
+ 0x00, /* 16 */
+ 0x00, /* 17 */
+ 0x00, /* 18 */
+ 0x00, /* 19 */
+ 0x00, /* 1A */
+ 0x00, /* 1B */
+ 0x00, /* 1C */
+ 0x00, /* 1D */
+ 0x00, /* 1E */
+ 0x00, /* 1F */
+ 0x00, /* 20 */
+ 0x00, /* 21 */
+ 0x00, /* 22 */
+ 0x00, /* 23 */
+ 0x00, /* 24 */
+ 0x00, /* 25 */
+ 0x00, /* 26 */
+ 0x00, /* 27 */
+ 0x00, /* 28 */
+ 0x00, /* 29 */
+ 0x00, /* 2A */
+ 0x00, /* 2B */
+ 0x00, /* 2C */
+ 0x00, /* 2D */
+ 0x00, /* 2E */
+ 0x00, /* 2F */
+ 0x00, /* 30 */
+ 0x00, /* 31 */
+ 0x00, /* 32 */
+ 0x00, /* 33 */
+ 0x00, /* 34 */
+ 0x00, /* 35 */
+ 0x00, /* 36 */
+ 0x00, /* 37 */
+ 0x00, /* 38 */
+ 0x00, /* 39 */
+ 0x00, /* 3A */
+ 0x00, /* 3B */
+ 0x00, /* 3C */
+ 0x00, /* 3D */
+ 0x00, /* 3E */
+ 0x00, /* 3F */
+ 0x00, /* 40 */
+ 0x00, /* 41 */
+ 0x00, /* 42 */
+ 0x00, /* 43 */
+ 0x00, /* 44 */
+ 0x00, /* 45 */
+ 0x00, /* 46 */
+ 0x00, /* 47 */
+ 0x00, /* 48 */
+ 0x00, /* 49 */
+ 0x00, /* 4A */
+ 0x00, /* 4B */
+ 0x00, /* 4C */
+ 0x00, /* 4D */
+ 0x00, /* 4E */
+ 0x00, /* 4F */
+ 0x00, /* 50 */
+ 0x00, /* 51 */
+ 0x00, /* 52 */
+ 0x00, /* 53 */
+ 0x00, /* 54 */
+ 0x00, /* 55 */
+ 0x00, /* 56 */
+ 0x00, /* 57 */
+ 0x00, /* 58 */
+ 0x00, /* 59 */
+ 0x00, /* 5A */
+ 0x00, /* 5B */
+ 0x00, /* 5C */
+ 0x00, /* 5D */
+ 0x00, /* 5E */
+ 0x00, /* 5F */
+ 0x00, /* 60 */
+ 0x00, /* 61 */
+ 0x00, /* 62 */
+ 0x00, /* 63 */
+ 0x00, /* 64 */
+ 0x00, /* 65 */
+ 0x00, /* 66 */
+ 0x00, /* 67 */
+ 0x00, /* 68 */
+ 0x00, /* 69 */
+ 0x00, /* 6A */
+ 0x00, /* 6B */
+ 0x00, /* 6C */
+ 0x00, /* 6D */
+ 0x00, /* 6E */
+ 0x00, /* 6F */
+ 0x00, /* 70 */
+ 0x00, /* 71 */
+ 0x00, /* 72 */
+ 0x00, /* 73 */
+ 0x00, /* 74 */
+ 0x00, /* 75 */
+ 0x00, /* 76 */
+ 0x00, /* 77 */
+ 0x00, /* 78 */
+ 0x00, /* 79 */
+ 0x00, /* 7A */
+ 0x00, /* 7B */
+ 0x00, /* 7C */
+ 0x00, /* 7D */
+ 0x00, /* 7E */
+ 0x00, /* 7F */
+ 0x00, /* 80 */
+ 0x00, /* 81 */
+ 0x00, /* 82 */
+ 0x00, /* 83 */
+ 0x00, /* 84 */
+ 0x00, /* 85 */
+ 0x00, /* 86 */
+ 0x00, /* 87 */
+ 0x00, /* 88 */
+ 0x00, /* 89 */
+ 0x00, /* 8A */
+ 0x00, /* 8B */
+ 0x00, /* 8C */
+ 0x00, /* 8D */
+ 0x00, /* 8E */
+ 0x00, /* 8F */
+ 0x00, /* 90 */
+ 0x00, /* 91 */
+ 0x30, /* 92 */
+ 0xF0, /* 93 */
+ 0x00, /* 94 */
+ 0x00, /* 95 */
+ 0x3F, /* 96 */
+ 0x00, /* 97 */
+ 0x00, /* 98 */
+ 0x00, /* 99 */
+ 0x00, /* 9A */
+ 0x00, /* 9B */
+ 0x00, /* 9C */
+ 0x00, /* 9D */
+ 0x00, /* 9E */
+ 0x00, /* 9F */
+ 0x00, /* A0 */
+ 0x00, /* A1 */
+ 0x00, /* A2 */
+ 0x00, /* A3 */
+ 0x00, /* A4 */
+ 0x00, /* A5 */
+ 0x00, /* A6 */
+ 0x00, /* A7 */
+ 0x00, /* A8 */
+ 0x00, /* A9 */
+ 0x00, /* AA */
+ 0x00, /* AB */
+ 0x00, /* AC */
+ 0x00, /* AD */
+ 0x00, /* AE */
+ 0x00, /* AF */
+ 0x00, /* B0 */
+ 0x00, /* B1 */
+ 0x00, /* B2 */
+ 0x00, /* B3 */
+ 0x00, /* B4 */
+ 0x00, /* B5 */
+ 0x00, /* B6 */
+ 0x00, /* B7 */
+ 0x00, /* B8 */
+ 0x00, /* B9 */
+ 0x00, /* BA */
+ 0x00, /* BB */
+ 0x00, /* BC */
+ 0x00, /* BD */
+ 0x00, /* BE */
+ 0x00, /* BF */
+ 0x00, /* C0 */
+ 0x00, /* C1 */
+ 0x00, /* C2 */
+ 0x00, /* C3 */
+ 0x00, /* C4 */
+ 0x00, /* C5 */
+ 0x00, /* C6 */
+ 0x00, /* C7 */
+ 0x00, /* C8 */
+ 0x00, /* C9 */
+ 0x00, /* CA */
+ 0x00, /* CB */
+ 0x00, /* CC */
+ 0x00, /* CD */
+ 0x00, /* CE */
+ 0x00, /* CF */
+ 0x00, /* D0 */
+ 0x00, /* D1 */
+ 0x00, /* D2 */
+ 0x00, /* D3 */
+ 0x00, /* D4 */
+ 0x00, /* D5 */
+ 0x00, /* D6 */
+ 0x00, /* D7 */
+ 0x00, /* D8 */
+ 0x00, /* D9 */
+ 0x00, /* DA */
+ 0x00, /* DB */
+ 0x00, /* DC */
+ 0x00, /* DD */
+ 0x00, /* DE */
+ 0x00, /* DF */
+ 0x00, /* E0 */
+ 0x00, /* E1 */
+ 0x00, /* E2 */
+ 0x00, /* E3 */
+ 0x00, /* E4 */
+ 0x00, /* E5 */
+ 0x00, /* E6 */
+ 0x00, /* E7 */
+ 0x00, /* E8 */
+ 0x00, /* E9 */
+ 0x00, /* EA */
+ 0x00, /* EB */
+ 0x00, /* EC */
+ 0x00, /* ED */
+ 0x00, /* EE */
+ 0x00, /* EF */
+ 0x00, /* F0 */
+ 0x00, /* F1 */
+ 0x00, /* F2 */
+ 0x00, /* F3 */
+ 0x00, /* F4 */
+ 0x00, /* F5 */
+ 0x00, /* F6 */
+ 0x00, /* F7 */
+ 0x00, /* F8 */
+ 0x00, /* F9 */
+ 0x00, /* FA */
+ 0x00, /* FB */
+ 0x00, /* FC */
+ 0x00, /* FD */
+ 0x00, /* FE */
+ 0x00, /* FF */
+};
+
+static struct {
+ int readable;
+ int writable;
+} max98095_access[M98095_REG_CNT] = {
+ { 0x00, 0x00 }, /* 00 */
+ { 0xFF, 0x00 }, /* 01 */
+ { 0xFF, 0x00 }, /* 02 */
+ { 0xFF, 0x00 }, /* 03 */
+ { 0xFF, 0x00 }, /* 04 */
+ { 0xFF, 0x00 }, /* 05 */
+ { 0xFF, 0x00 }, /* 06 */
+ { 0xFF, 0x00 }, /* 07 */
+ { 0xFF, 0x00 }, /* 08 */
+ { 0xFF, 0x00 }, /* 09 */
+ { 0xFF, 0x00 }, /* 0A */
+ { 0xFF, 0x00 }, /* 0B */
+ { 0xFF, 0x00 }, /* 0C */
+ { 0xFF, 0x00 }, /* 0D */
+ { 0xFF, 0x00 }, /* 0E */
+ { 0xFF, 0x9F }, /* 0F */
+ { 0xFF, 0xFF }, /* 10 */
+ { 0xFF, 0xFF }, /* 11 */
+ { 0xFF, 0xFF }, /* 12 */
+ { 0xFF, 0xFF }, /* 13 */
+ { 0xFF, 0xFF }, /* 14 */
+ { 0xFF, 0xFF }, /* 15 */
+ { 0xFF, 0xFF }, /* 16 */
+ { 0xFF, 0xFF }, /* 17 */
+ { 0xFF, 0xFF }, /* 18 */
+ { 0xFF, 0xFF }, /* 19 */
+ { 0xFF, 0xFF }, /* 1A */
+ { 0xFF, 0xFF }, /* 1B */
+ { 0xFF, 0xFF }, /* 1C */
+ { 0xFF, 0xFF }, /* 1D */
+ { 0xFF, 0x77 }, /* 1E */
+ { 0xFF, 0x77 }, /* 1F */
+ { 0xFF, 0x77 }, /* 20 */
+ { 0xFF, 0x77 }, /* 21 */
+ { 0xFF, 0x77 }, /* 22 */
+ { 0xFF, 0x77 }, /* 23 */
+ { 0xFF, 0xFF }, /* 24 */
+ { 0xFF, 0x7F }, /* 25 */
+ { 0xFF, 0x31 }, /* 26 */
+ { 0xFF, 0xFF }, /* 27 */
+ { 0xFF, 0xFF }, /* 28 */
+ { 0xFF, 0xFF }, /* 29 */
+ { 0xFF, 0xF7 }, /* 2A */
+ { 0xFF, 0x2F }, /* 2B */
+ { 0xFF, 0xEF }, /* 2C */
+ { 0xFF, 0xFF }, /* 2D */
+ { 0xFF, 0xFF }, /* 2E */
+ { 0xFF, 0xFF }, /* 2F */
+ { 0xFF, 0xFF }, /* 30 */
+ { 0xFF, 0xFF }, /* 31 */
+ { 0xFF, 0xFF }, /* 32 */
+ { 0xFF, 0xFF }, /* 33 */
+ { 0xFF, 0xF7 }, /* 34 */
+ { 0xFF, 0x2F }, /* 35 */
+ { 0xFF, 0xCF }, /* 36 */
+ { 0xFF, 0xFF }, /* 37 */
+ { 0xFF, 0xFF }, /* 38 */
+ { 0xFF, 0xFF }, /* 39 */
+ { 0xFF, 0xFF }, /* 3A */
+ { 0xFF, 0xFF }, /* 3B */
+ { 0xFF, 0xFF }, /* 3C */
+ { 0xFF, 0xFF }, /* 3D */
+ { 0xFF, 0xF7 }, /* 3E */
+ { 0xFF, 0x2F }, /* 3F */
+ { 0xFF, 0xCF }, /* 40 */
+ { 0xFF, 0xFF }, /* 41 */
+ { 0xFF, 0x77 }, /* 42 */
+ { 0xFF, 0xFF }, /* 43 */
+ { 0xFF, 0xFF }, /* 44 */
+ { 0xFF, 0xFF }, /* 45 */
+ { 0xFF, 0xFF }, /* 46 */
+ { 0xFF, 0xFF }, /* 47 */
+ { 0xFF, 0xFF }, /* 48 */
+ { 0xFF, 0x0F }, /* 49 */
+ { 0xFF, 0xFF }, /* 4A */
+ { 0xFF, 0xFF }, /* 4B */
+ { 0xFF, 0x3F }, /* 4C */
+ { 0xFF, 0x3F }, /* 4D */
+ { 0xFF, 0x3F }, /* 4E */
+ { 0xFF, 0xFF }, /* 4F */
+ { 0xFF, 0x7F }, /* 50 */
+ { 0xFF, 0x7F }, /* 51 */
+ { 0xFF, 0x0F }, /* 52 */
+ { 0xFF, 0x3F }, /* 53 */
+ { 0xFF, 0x3F }, /* 54 */
+ { 0xFF, 0x3F }, /* 55 */
+ { 0xFF, 0xFF }, /* 56 */
+ { 0xFF, 0xFF }, /* 57 */
+ { 0xFF, 0xBF }, /* 58 */
+ { 0xFF, 0x1F }, /* 59 */
+ { 0xFF, 0xBF }, /* 5A */
+ { 0xFF, 0x1F }, /* 5B */
+ { 0xFF, 0xBF }, /* 5C */
+ { 0xFF, 0x3F }, /* 5D */
+ { 0xFF, 0x3F }, /* 5E */
+ { 0xFF, 0x7F }, /* 5F */
+ { 0xFF, 0x7F }, /* 60 */
+ { 0xFF, 0x47 }, /* 61 */
+ { 0xFF, 0x9F }, /* 62 */
+ { 0xFF, 0x9F }, /* 63 */
+ { 0xFF, 0x9F }, /* 64 */
+ { 0xFF, 0x9F }, /* 65 */
+ { 0xFF, 0x9F }, /* 66 */
+ { 0xFF, 0xBF }, /* 67 */
+ { 0xFF, 0xBF }, /* 68 */
+ { 0xFF, 0xFF }, /* 69 */
+ { 0xFF, 0xFF }, /* 6A */
+ { 0xFF, 0x7F }, /* 6B */
+ { 0xFF, 0xF7 }, /* 6C */
+ { 0xFF, 0xFF }, /* 6D */
+ { 0xFF, 0xFF }, /* 6E */
+ { 0xFF, 0x1F }, /* 6F */
+ { 0xFF, 0xF7 }, /* 70 */
+ { 0xFF, 0xFF }, /* 71 */
+ { 0xFF, 0xFF }, /* 72 */
+ { 0xFF, 0x1F }, /* 73 */
+ { 0xFF, 0xF7 }, /* 74 */
+ { 0xFF, 0xFF }, /* 75 */
+ { 0xFF, 0xFF }, /* 76 */
+ { 0xFF, 0x1F }, /* 77 */
+ { 0xFF, 0xF7 }, /* 78 */
+ { 0xFF, 0xFF }, /* 79 */
+ { 0xFF, 0xFF }, /* 7A */
+ { 0xFF, 0x1F }, /* 7B */
+ { 0xFF, 0xF7 }, /* 7C */
+ { 0xFF, 0xFF }, /* 7D */
+ { 0xFF, 0xFF }, /* 7E */
+ { 0xFF, 0x1F }, /* 7F */
+ { 0xFF, 0xF7 }, /* 80 */
+ { 0xFF, 0xFF }, /* 81 */
+ { 0xFF, 0xFF }, /* 82 */
+ { 0xFF, 0x1F }, /* 83 */
+ { 0xFF, 0x7F }, /* 84 */
+ { 0xFF, 0x0F }, /* 85 */
+ { 0xFF, 0xD8 }, /* 86 */
+ { 0xFF, 0xFF }, /* 87 */
+ { 0xFF, 0xEF }, /* 88 */
+ { 0xFF, 0xFE }, /* 89 */
+ { 0xFF, 0xFE }, /* 8A */
+ { 0xFF, 0xFF }, /* 8B */
+ { 0xFF, 0xFF }, /* 8C */
+ { 0xFF, 0x3F }, /* 8D */
+ { 0xFF, 0xFF }, /* 8E */
+ { 0xFF, 0x3F }, /* 8F */
+ { 0xFF, 0x8F }, /* 90 */
+ { 0xFF, 0xFF }, /* 91 */
+ { 0xFF, 0x3F }, /* 92 */
+ { 0xFF, 0xFF }, /* 93 */
+ { 0xFF, 0xFF }, /* 94 */
+ { 0xFF, 0x0F }, /* 95 */
+ { 0xFF, 0x3F }, /* 96 */
+ { 0xFF, 0x8C }, /* 97 */
+ { 0x00, 0x00 }, /* 98 */
+ { 0x00, 0x00 }, /* 99 */
+ { 0x00, 0x00 }, /* 9A */
+ { 0x00, 0x00 }, /* 9B */
+ { 0x00, 0x00 }, /* 9C */
+ { 0x00, 0x00 }, /* 9D */
+ { 0x00, 0x00 }, /* 9E */
+ { 0x00, 0x00 }, /* 9F */
+ { 0x00, 0x00 }, /* A0 */
+ { 0x00, 0x00 }, /* A1 */
+ { 0x00, 0x00 }, /* A2 */
+ { 0x00, 0x00 }, /* A3 */
+ { 0x00, 0x00 }, /* A4 */
+ { 0x00, 0x00 }, /* A5 */
+ { 0x00, 0x00 }, /* A6 */
+ { 0x00, 0x00 }, /* A7 */
+ { 0x00, 0x00 }, /* A8 */
+ { 0x00, 0x00 }, /* A9 */
+ { 0x00, 0x00 }, /* AA */
+ { 0x00, 0x00 }, /* AB */
+ { 0x00, 0x00 }, /* AC */
+ { 0x00, 0x00 }, /* AD */
+ { 0x00, 0x00 }, /* AE */
+ { 0x00, 0x00 }, /* AF */
+ { 0x00, 0x00 }, /* B0 */
+ { 0x00, 0x00 }, /* B1 */
+ { 0x00, 0x00 }, /* B2 */
+ { 0x00, 0x00 }, /* B3 */
+ { 0x00, 0x00 }, /* B4 */
+ { 0x00, 0x00 }, /* B5 */
+ { 0x00, 0x00 }, /* B6 */
+ { 0x00, 0x00 }, /* B7 */
+ { 0x00, 0x00 }, /* B8 */
+ { 0x00, 0x00 }, /* B9 */
+ { 0x00, 0x00 }, /* BA */
+ { 0x00, 0x00 }, /* BB */
+ { 0x00, 0x00 }, /* BC */
+ { 0x00, 0x00 }, /* BD */
+ { 0x00, 0x00 }, /* BE */
+ { 0x00, 0x00 }, /* BF */
+ { 0x00, 0x00 }, /* C0 */
+ { 0x00, 0x00 }, /* C1 */
+ { 0x00, 0x00 }, /* C2 */
+ { 0x00, 0x00 }, /* C3 */
+ { 0x00, 0x00 }, /* C4 */
+ { 0x00, 0x00 }, /* C5 */
+ { 0x00, 0x00 }, /* C6 */
+ { 0x00, 0x00 }, /* C7 */
+ { 0x00, 0x00 }, /* C8 */
+ { 0x00, 0x00 }, /* C9 */
+ { 0x00, 0x00 }, /* CA */
+ { 0x00, 0x00 }, /* CB */
+ { 0x00, 0x00 }, /* CC */
+ { 0x00, 0x00 }, /* CD */
+ { 0x00, 0x00 }, /* CE */
+ { 0x00, 0x00 }, /* CF */
+ { 0x00, 0x00 }, /* D0 */
+ { 0x00, 0x00 }, /* D1 */
+ { 0x00, 0x00 }, /* D2 */
+ { 0x00, 0x00 }, /* D3 */
+ { 0x00, 0x00 }, /* D4 */
+ { 0x00, 0x00 }, /* D5 */
+ { 0x00, 0x00 }, /* D6 */
+ { 0x00, 0x00 }, /* D7 */
+ { 0x00, 0x00 }, /* D8 */
+ { 0x00, 0x00 }, /* D9 */
+ { 0x00, 0x00 }, /* DA */
+ { 0x00, 0x00 }, /* DB */
+ { 0x00, 0x00 }, /* DC */
+ { 0x00, 0x00 }, /* DD */
+ { 0x00, 0x00 }, /* DE */
+ { 0x00, 0x00 }, /* DF */
+ { 0x00, 0x00 }, /* E0 */
+ { 0x00, 0x00 }, /* E1 */
+ { 0x00, 0x00 }, /* E2 */
+ { 0x00, 0x00 }, /* E3 */
+ { 0x00, 0x00 }, /* E4 */
+ { 0x00, 0x00 }, /* E5 */
+ { 0x00, 0x00 }, /* E6 */
+ { 0x00, 0x00 }, /* E7 */
+ { 0x00, 0x00 }, /* E8 */
+ { 0x00, 0x00 }, /* E9 */
+ { 0x00, 0x00 }, /* EA */
+ { 0x00, 0x00 }, /* EB */
+ { 0x00, 0x00 }, /* EC */
+ { 0x00, 0x00 }, /* ED */
+ { 0x00, 0x00 }, /* EE */
+ { 0x00, 0x00 }, /* EF */
+ { 0x00, 0x00 }, /* F0 */
+ { 0x00, 0x00 }, /* F1 */
+ { 0x00, 0x00 }, /* F2 */
+ { 0x00, 0x00 }, /* F3 */
+ { 0x00, 0x00 }, /* F4 */
+ { 0x00, 0x00 }, /* F5 */
+ { 0x00, 0x00 }, /* F6 */
+ { 0x00, 0x00 }, /* F7 */
+ { 0x00, 0x00 }, /* F8 */
+ { 0x00, 0x00 }, /* F9 */
+ { 0x00, 0x00 }, /* FA */
+ { 0x00, 0x00 }, /* FB */
+ { 0x00, 0x00 }, /* FC */
+ { 0x00, 0x00 }, /* FD */
+ { 0x00, 0x00 }, /* FE */
+ { 0xFF, 0x00 }, /* FF */
+};
+
+static int max98095_readable(struct snd_soc_codec *codec, unsigned int reg)
+{
+ if (reg >= M98095_REG_CNT)
+ return 0;
+ return max98095_access[reg].readable != 0;
+}
+
+static int max98095_volatile(struct snd_soc_codec *codec, unsigned int reg)
+{
+ if (reg > M98095_REG_MAX_CACHED)
+ return 1;
+
+ switch (reg) {
+ case M98095_000_HOST_DATA:
+ case M98095_001_HOST_INT_STS:
+ case M98095_002_HOST_RSP_STS:
+ case M98095_003_HOST_CMD_STS:
+ case M98095_004_CODEC_STS:
+ case M98095_005_DAI1_ALC_STS:
+ case M98095_006_DAI2_ALC_STS:
+ case M98095_007_JACK_AUTO_STS:
+ case M98095_008_JACK_MANUAL_STS:
+ case M98095_009_JACK_VBAT_STS:
+ case M98095_00A_ACC_ADC_STS:
+ case M98095_00B_MIC_NG_AGC_STS:
+ case M98095_00C_SPK_L_VOLT_STS:
+ case M98095_00D_SPK_R_VOLT_STS:
+ case M98095_00E_TEMP_SENSOR_STS:
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Filter coefficients are in a separate register segment
+ * and they share the address space of the normal registers.
+ * The coefficient registers do not need or share the cache.
+ */
+static int max98095_hw_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ data[0] = reg;
+ data[1] = value;
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+/*
+ * Load equalizer DSP coefficient configurations registers
+ */
+static void m98095_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+ unsigned int band, u16 *coefs)
+{
+ unsigned int eq_reg;
+ unsigned int i;
+
+ BUG_ON(band > 4);
+ BUG_ON(dai > 1);
+
+ /* Load the base register address */
+ eq_reg = dai ? M98095_142_DAI2_EQ_BASE : M98095_110_DAI1_EQ_BASE;
+
+ /* Add the band address offset, note adjustment for word address */
+ eq_reg += band * (M98095_COEFS_PER_BAND << 1);
+
+ /* Step through the registers and coefs */
+ for (i = 0; i < M98095_COEFS_PER_BAND; i++) {
+ max98095_hw_write(codec, eq_reg++, M98095_BYTE1(coefs[i]));
+ max98095_hw_write(codec, eq_reg++, M98095_BYTE0(coefs[i]));
+ }
+}
+
+/*
+ * Load biquad filter coefficient configurations registers
+ */
+static void m98095_biquad_band(struct snd_soc_codec *codec, unsigned int dai,
+ unsigned int band, u16 *coefs)
+{
+ unsigned int bq_reg;
+ unsigned int i;
+
+ BUG_ON(band > 1);
+ BUG_ON(dai > 1);
+
+ /* Load the base register address */
+ bq_reg = dai ? M98095_17E_DAI2_BQ_BASE : M98095_174_DAI1_BQ_BASE;
+
+ /* Add the band address offset, note adjustment for word address */
+ bq_reg += band * (M98095_COEFS_PER_BAND << 1);
+
+ /* Step through the registers and coefs */
+ for (i = 0; i < M98095_COEFS_PER_BAND; i++) {
+ max98095_hw_write(codec, bq_reg++, M98095_BYTE1(coefs[i]));
+ max98095_hw_write(codec, bq_reg++, M98095_BYTE0(coefs[i]));
+ }
+}
+
+static const char * const max98095_fltr_mode[] = { "Voice", "Music" };
+static const struct soc_enum max98095_dai1_filter_mode_enum[] = {
+ SOC_ENUM_SINGLE(M98095_02E_DAI1_FILTERS, 7, 2, max98095_fltr_mode),
+};
+static const struct soc_enum max98095_dai2_filter_mode_enum[] = {
+ SOC_ENUM_SINGLE(M98095_038_DAI2_FILTERS, 7, 2, max98095_fltr_mode),
+};
+
+static const char * const max98095_extmic_text[] = { "None", "MIC1", "MIC2" };
+
+static const struct soc_enum max98095_extmic_enum =
+ SOC_ENUM_SINGLE(M98095_087_CFG_MIC, 0, 3, max98095_extmic_text);
+
+static const struct snd_kcontrol_new max98095_extmic_mux =
+ SOC_DAPM_ENUM("External MIC Mux", max98095_extmic_enum);
+
+static const char * const max98095_linein_text[] = { "INA", "INB" };
+
+static const struct soc_enum max98095_linein_enum =
+ SOC_ENUM_SINGLE(M98095_086_CFG_LINE, 6, 2, max98095_linein_text);
+
+static const struct snd_kcontrol_new max98095_linein_mux =
+ SOC_DAPM_ENUM("Linein Input Mux", max98095_linein_enum);
+
+static const char * const max98095_line_mode_text[] = {
+ "Stereo", "Differential"};
+
+static const struct soc_enum max98095_linein_mode_enum =
+ SOC_ENUM_SINGLE(M98095_086_CFG_LINE, 7, 2, max98095_line_mode_text);
+
+static const struct soc_enum max98095_lineout_mode_enum =
+ SOC_ENUM_SINGLE(M98095_086_CFG_LINE, 4, 2, max98095_line_mode_text);
+
+static const char * const max98095_dai_fltr[] = {
+ "Off", "Elliptical-HPF-16k", "Butterworth-HPF-16k",
+ "Elliptical-HPF-8k", "Butterworth-HPF-8k", "Butterworth-HPF-Fs/240"};
+static const struct soc_enum max98095_dai1_dac_filter_enum[] = {
+ SOC_ENUM_SINGLE(M98095_02E_DAI1_FILTERS, 0, 6, max98095_dai_fltr),
+};
+static const struct soc_enum max98095_dai2_dac_filter_enum[] = {
+ SOC_ENUM_SINGLE(M98095_038_DAI2_FILTERS, 0, 6, max98095_dai_fltr),
+};
+static const struct soc_enum max98095_dai3_dac_filter_enum[] = {
+ SOC_ENUM_SINGLE(M98095_042_DAI3_FILTERS, 0, 6, max98095_dai_fltr),
+};
+
+static int max98095_mic1pre_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ max98095->mic1pre = sel;
+ snd_soc_update_bits(codec, M98095_05F_LVL_MIC1, M98095_MICPRE_MASK,
+ (1+sel)<<M98095_MICPRE_SHIFT);
+
+ return 0;
+}
+
+static int max98095_mic1pre_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = max98095->mic1pre;
+ return 0;
+}
+
+static int max98095_mic2pre_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ max98095->mic2pre = sel;
+ snd_soc_update_bits(codec, M98095_060_LVL_MIC2, M98095_MICPRE_MASK,
+ (1+sel)<<M98095_MICPRE_SHIFT);
+
+ return 0;
+}
+
+static int max98095_mic2pre_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = max98095->mic2pre;
+ return 0;
+}
+
+static const unsigned int max98095_micboost_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
+};
+
+static const DECLARE_TLV_DB_SCALE(max98095_mic_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(max98095_adc_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(max98095_adcboost_tlv, 0, 600, 0);
+
+static const unsigned int max98095_hp_tlv[] = {
+ TLV_DB_RANGE_HEAD(5),
+ 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0),
+};
+
+static const unsigned int max98095_spk_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 10, TLV_DB_SCALE_ITEM(-5900, 400, 0),
+ 11, 18, TLV_DB_SCALE_ITEM(-1700, 200, 0),
+ 19, 27, TLV_DB_SCALE_ITEM(-200, 100, 0),
+ 28, 39, TLV_DB_SCALE_ITEM(650, 50, 0),
+};
+
+static const unsigned int max98095_rcv_lout_tlv[] = {
+ TLV_DB_RANGE_HEAD(5),
+ 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0),
+ 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0),
+ 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+ 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0),
+ 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0),
+};
+
+static const unsigned int max98095_lin_tlv[] = {
+ TLV_DB_RANGE_HEAD(3),
+ 0, 2, TLV_DB_SCALE_ITEM(-600, 300, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(300, 1100, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0),
+};
+
+static const struct snd_kcontrol_new max98095_snd_controls[] = {
+
+ SOC_DOUBLE_R_TLV("Headphone Volume", M98095_064_LVL_HP_L,
+ M98095_065_LVL_HP_R, 0, 31, 0, max98095_hp_tlv),
+
+ SOC_DOUBLE_R_TLV("Speaker Volume", M98095_067_LVL_SPK_L,
+ M98095_068_LVL_SPK_R, 0, 39, 0, max98095_spk_tlv),
+
+ SOC_SINGLE_TLV("Receiver Volume", M98095_066_LVL_RCV,
+ 0, 31, 0, max98095_rcv_lout_tlv),
+
+ SOC_DOUBLE_R_TLV("Lineout Volume", M98095_062_LVL_LINEOUT1,
+ M98095_063_LVL_LINEOUT2, 0, 31, 0, max98095_rcv_lout_tlv),
+
+ SOC_DOUBLE_R("Headphone Switch", M98095_064_LVL_HP_L,
+ M98095_065_LVL_HP_R, 7, 1, 1),
+
+ SOC_DOUBLE_R("Speaker Switch", M98095_067_LVL_SPK_L,
+ M98095_068_LVL_SPK_R, 7, 1, 1),
+
+ SOC_SINGLE("Receiver Switch", M98095_066_LVL_RCV, 7, 1, 1),
+
+ SOC_DOUBLE_R("Lineout Switch", M98095_062_LVL_LINEOUT1,
+ M98095_063_LVL_LINEOUT2, 7, 1, 1),
+
+ SOC_SINGLE_TLV("MIC1 Volume", M98095_05F_LVL_MIC1, 0, 20, 1,
+ max98095_mic_tlv),
+
+ SOC_SINGLE_TLV("MIC2 Volume", M98095_060_LVL_MIC2, 0, 20, 1,
+ max98095_mic_tlv),
+
+ SOC_SINGLE_EXT_TLV("MIC1 Boost Volume",
+ M98095_05F_LVL_MIC1, 5, 2, 0,
+ max98095_mic1pre_get, max98095_mic1pre_set,
+ max98095_micboost_tlv),
+ SOC_SINGLE_EXT_TLV("MIC2 Boost Volume",
+ M98095_060_LVL_MIC2, 5, 2, 0,
+ max98095_mic2pre_get, max98095_mic2pre_set,
+ max98095_micboost_tlv),
+
+ SOC_SINGLE_TLV("Linein Volume", M98095_061_LVL_LINEIN, 0, 5, 1,
+ max98095_lin_tlv),
+
+ SOC_SINGLE_TLV("ADCL Volume", M98095_05D_LVL_ADC_L, 0, 15, 1,
+ max98095_adc_tlv),
+ SOC_SINGLE_TLV("ADCR Volume", M98095_05E_LVL_ADC_R, 0, 15, 1,
+ max98095_adc_tlv),
+
+ SOC_SINGLE_TLV("ADCL Boost Volume", M98095_05D_LVL_ADC_L, 4, 3, 0,
+ max98095_adcboost_tlv),
+ SOC_SINGLE_TLV("ADCR Boost Volume", M98095_05E_LVL_ADC_R, 4, 3, 0,
+ max98095_adcboost_tlv),
+
+ SOC_SINGLE("EQ1 Switch", M98095_088_CFG_LEVEL, 0, 1, 0),
+ SOC_SINGLE("EQ2 Switch", M98095_088_CFG_LEVEL, 1, 1, 0),
+
+ SOC_SINGLE("Biquad1 Switch", M98095_088_CFG_LEVEL, 2, 1, 0),
+ SOC_SINGLE("Biquad2 Switch", M98095_088_CFG_LEVEL, 3, 1, 0),
+
+ SOC_ENUM("DAI1 Filter Mode", max98095_dai1_filter_mode_enum),
+ SOC_ENUM("DAI2 Filter Mode", max98095_dai2_filter_mode_enum),
+ SOC_ENUM("DAI1 DAC Filter", max98095_dai1_dac_filter_enum),
+ SOC_ENUM("DAI2 DAC Filter", max98095_dai2_dac_filter_enum),
+ SOC_ENUM("DAI3 DAC Filter", max98095_dai3_dac_filter_enum),
+
+ SOC_ENUM("Linein Mode", max98095_linein_mode_enum),
+ SOC_ENUM("Lineout Mode", max98095_lineout_mode_enum),
+};
+
+/* Left speaker mixer switch */
+static const struct snd_kcontrol_new max98095_left_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_050_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_050_MIX_SPK_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC2 Switch", M98095_050_MIX_SPK_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC3 Switch", M98095_050_MIX_SPK_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_050_MIX_SPK_LEFT, 4, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_050_MIX_SPK_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_050_MIX_SPK_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_050_MIX_SPK_LEFT, 2, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct snd_kcontrol_new max98095_right_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_051_MIX_SPK_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_051_MIX_SPK_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC2 Switch", M98095_051_MIX_SPK_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("Mono DAC3 Switch", M98095_051_MIX_SPK_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_051_MIX_SPK_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_051_MIX_SPK_RIGHT, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_051_MIX_SPK_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_051_MIX_SPK_RIGHT, 2, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98095_left_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04C_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04C_MIX_HP_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04C_MIX_HP_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04C_MIX_HP_LEFT, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04C_MIX_HP_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04C_MIX_HP_LEFT, 2, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98095_right_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04D_MIX_HP_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04D_MIX_HP_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04D_MIX_HP_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04D_MIX_HP_RIGHT, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04D_MIX_HP_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04D_MIX_HP_RIGHT, 2, 1, 0),
+};
+
+/* Receiver earpiece mixer switch */
+static const struct snd_kcontrol_new max98095_mono_rcv_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04F_MIX_RCV, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04F_MIX_RCV, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04F_MIX_RCV, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04F_MIX_RCV, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04F_MIX_RCV, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04F_MIX_RCV, 2, 1, 0),
+};
+
+/* Left lineout mixer switch */
+static const struct snd_kcontrol_new max98095_left_lineout_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_053_MIX_LINEOUT1, 5, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_053_MIX_LINEOUT1, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_053_MIX_LINEOUT1, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_053_MIX_LINEOUT1, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_053_MIX_LINEOUT1, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_053_MIX_LINEOUT1, 2, 1, 0),
+};
+
+/* Right lineout mixer switch */
+static const struct snd_kcontrol_new max98095_right_lineout_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_054_MIX_LINEOUT2, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_054_MIX_LINEOUT2, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_054_MIX_LINEOUT2, 3, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_054_MIX_LINEOUT2, 4, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_054_MIX_LINEOUT2, 1, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_054_MIX_LINEOUT2, 2, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98095_left_ADC_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04A_MIX_ADC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04A_MIX_ADC_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04A_MIX_ADC_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04A_MIX_ADC_LEFT, 2, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98095_right_ADC_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1 Switch", M98095_04B_MIX_ADC_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98095_04B_MIX_ADC_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("IN1 Switch", M98095_04B_MIX_ADC_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Switch", M98095_04B_MIX_ADC_RIGHT, 2, 1, 0),
+};
+
+static int max98095_mic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (w->reg == M98095_05F_LVL_MIC1) {
+ snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK,
+ (1+max98095->mic1pre)<<M98095_MICPRE_SHIFT);
+ } else {
+ snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK,
+ (1+max98095->mic2pre)<<M98095_MICPRE_SHIFT);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * The line inputs are stereo inputs with the left and right
+ * channels sharing a common PGA power control signal.
+ */
+static int max98095_line_pga(struct snd_soc_dapm_widget *w,
+ int event, u8 channel)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ u8 *state;
+
+ BUG_ON(!((channel == 1) || (channel == 2)));
+
+ state = &max98095->lin_state;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ *state |= channel;
+ snd_soc_update_bits(codec, w->reg,
+ (1 << w->shift), (1 << w->shift));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ *state &= ~channel;
+ if (*state == 0) {
+ snd_soc_update_bits(codec, w->reg,
+ (1 << w->shift), 0);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int max98095_pga_in1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98095_line_pga(w, event, 1);
+}
+
+static int max98095_pga_in2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98095_line_pga(w, event, 2);
+}
+
+/*
+ * The stereo line out mixer outputs to two stereo line outs.
+ * The 2nd pair has a separate set of enables.
+ */
+static int max98095_lineout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, w->reg,
+ (1 << (w->shift+2)), (1 << (w->shift+2)));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, w->reg,
+ (1 << (w->shift+2)), 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget max98095_dapm_widgets[] = {
+
+ SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98095_090_PWR_EN_IN, 0, 0),
+ SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98095_090_PWR_EN_IN, 1, 0),
+
+ SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
+ M98095_091_PWR_EN_OUT, 0, 0),
+ SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
+ M98095_091_PWR_EN_OUT, 1, 0),
+ SND_SOC_DAPM_DAC("DACM2", "Aux Playback",
+ M98095_091_PWR_EN_OUT, 2, 0),
+ SND_SOC_DAPM_DAC("DACM3", "Voice Playback",
+ M98095_091_PWR_EN_OUT, 2, 0),
+
+ SND_SOC_DAPM_PGA("HP Left Out", M98095_091_PWR_EN_OUT,
+ 6, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP Right Out", M98095_091_PWR_EN_OUT,
+ 7, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("SPK Left Out", M98095_091_PWR_EN_OUT,
+ 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPK Right Out", M98095_091_PWR_EN_OUT,
+ 5, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("RCV Mono Out", M98095_091_PWR_EN_OUT,
+ 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA_E("LINE Left Out", M98095_092_PWR_EN_OUT,
+ 0, 0, NULL, 0, max98095_lineout_event, SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("LINE Right Out", M98095_092_PWR_EN_OUT,
+ 1, 0, NULL, 0, max98095_lineout_event, SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX("External MIC", SND_SOC_NOPM, 0, 0,
+ &max98095_extmic_mux),
+
+ SND_SOC_DAPM_MUX("Linein Mux", SND_SOC_NOPM, 0, 0,
+ &max98095_linein_mux),
+
+ SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_hp_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_hp_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Speaker Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Speaker Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Receiver Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_mono_rcv_mixer_controls[0],
+ ARRAY_SIZE(max98095_mono_rcv_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Lineout Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_lineout_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_lineout_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Lineout Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_lineout_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_lineout_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_left_ADC_mixer_controls[0],
+ ARRAY_SIZE(max98095_left_ADC_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98095_right_ADC_mixer_controls[0],
+ ARRAY_SIZE(max98095_right_ADC_mixer_controls)),
+
+ SND_SOC_DAPM_PGA_E("MIC1 Input", M98095_05F_LVL_MIC1,
+ 5, 0, NULL, 0, max98095_mic_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("MIC2 Input", M98095_060_LVL_MIC2,
+ 5, 0, NULL, 0, max98095_mic_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("IN1 Input", M98095_090_PWR_EN_IN,
+ 7, 0, NULL, 0, max98095_pga_in1_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("IN2 Input", M98095_090_PWR_EN_IN,
+ 7, 0, NULL, 0, max98095_pga_in2_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MICBIAS("MICBIAS1", M98095_090_PWR_EN_IN, 2, 0),
+ SND_SOC_DAPM_MICBIAS("MICBIAS2", M98095_090_PWR_EN_IN, 3, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+ SND_SOC_DAPM_OUTPUT("RCV"),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_OUTPUT("OUT4"),
+
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("INA1"),
+ SND_SOC_DAPM_INPUT("INA2"),
+ SND_SOC_DAPM_INPUT("INB1"),
+ SND_SOC_DAPM_INPUT("INB2"),
+};
+
+static const struct snd_soc_dapm_route max98095_audio_map[] = {
+ /* Left headphone output mixer */
+ {"Left Headphone Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left Headphone Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left Headphone Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Headphone Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Headphone Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left Headphone Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right headphone output mixer */
+ {"Right Headphone Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right Headphone Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right Headphone Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Headphone Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Headphone Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right Headphone Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Left speaker output mixer */
+ {"Left Speaker Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left Speaker Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left Speaker Mixer", "Mono DAC2 Switch", "DACM2"},
+ {"Left Speaker Mixer", "Mono DAC3 Switch", "DACM3"},
+ {"Left Speaker Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Speaker Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Speaker Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left Speaker Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right speaker output mixer */
+ {"Right Speaker Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right Speaker Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right Speaker Mixer", "Mono DAC2 Switch", "DACM2"},
+ {"Right Speaker Mixer", "Mono DAC3 Switch", "DACM3"},
+ {"Right Speaker Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Speaker Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Speaker Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right Speaker Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Earpiece/Receiver output mixer */
+ {"Receiver Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Receiver Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Receiver Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Receiver Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Receiver Mixer", "IN1 Switch", "IN1 Input"},
+ {"Receiver Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Left Lineout output mixer */
+ {"Left Lineout Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left Lineout Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left Lineout Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left Lineout Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left Lineout Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left Lineout Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right lineout output mixer */
+ {"Right Lineout Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right Lineout Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right Lineout Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right Lineout Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right Lineout Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right Lineout Mixer", "IN2 Switch", "IN2 Input"},
+
+ {"HP Left Out", NULL, "Left Headphone Mixer"},
+ {"HP Right Out", NULL, "Right Headphone Mixer"},
+ {"SPK Left Out", NULL, "Left Speaker Mixer"},
+ {"SPK Right Out", NULL, "Right Speaker Mixer"},
+ {"RCV Mono Out", NULL, "Receiver Mixer"},
+ {"LINE Left Out", NULL, "Left Lineout Mixer"},
+ {"LINE Right Out", NULL, "Right Lineout Mixer"},
+
+ {"HPL", NULL, "HP Left Out"},
+ {"HPR", NULL, "HP Right Out"},
+ {"SPKL", NULL, "SPK Left Out"},
+ {"SPKR", NULL, "SPK Right Out"},
+ {"RCV", NULL, "RCV Mono Out"},
+ {"OUT1", NULL, "LINE Left Out"},
+ {"OUT2", NULL, "LINE Right Out"},
+ {"OUT3", NULL, "LINE Left Out"},
+ {"OUT4", NULL, "LINE Right Out"},
+
+ /* Left ADC input mixer */
+ {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left ADC Mixer", "IN1 Switch", "IN1 Input"},
+ {"Left ADC Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Right ADC input mixer */
+ {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right ADC Mixer", "IN1 Switch", "IN1 Input"},
+ {"Right ADC Mixer", "IN2 Switch", "IN2 Input"},
+
+ /* Inputs */
+ {"ADCL", NULL, "Left ADC Mixer"},
+ {"ADCR", NULL, "Right ADC Mixer"},
+
+ {"IN1 Input", NULL, "INA1"},
+ {"IN2 Input", NULL, "INA2"},
+
+ {"MIC1 Input", NULL, "MIC1"},
+ {"MIC2 Input", NULL, "MIC2"},
+};
+
+static int max98095_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_add_controls(codec, max98095_snd_controls,
+ ARRAY_SIZE(max98095_snd_controls));
+
+ return 0;
+}
+
+/* codec mclk clock divider coefficients */
+static const struct {
+ u32 rate;
+ u8 sr;
+} rate_table[] = {
+ {8000, 0x01},
+ {11025, 0x02},
+ {16000, 0x03},
+ {22050, 0x04},
+ {24000, 0x05},
+ {32000, 0x06},
+ {44100, 0x07},
+ {48000, 0x08},
+ {88200, 0x09},
+ {96000, 0x0A},
+};
+
+static int rate_value(int rate, u8 *value)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i].rate >= rate) {
+ *value = rate_table[i].sr;
+ return 0;
+ }
+ }
+ *value = rate_table[0].sr;
+ return -EINVAL;
+}
+
+static int max98095_dai1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98095->dai[0];
+
+ rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ M98095_DAI_WS, 0);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ M98095_DAI_WS, M98095_DAI_WS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rate_value(rate, ®val))
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, M98095_027_DAI1_CLKMODE,
+ M98095_CLKMODE_MASK, regval);
+ cdata->rate = rate;
+
+ /* Configure NI when operating as master */
+ if (snd_soc_read(codec, M98095_02A_DAI1_FORMAT) & M98095_DAI_MAS) {
+ if (max98095->sysclk == 0) {
+ dev_err(codec->dev, "Invalid system clock frequency\n");
+ return -EINVAL;
+ }
+ ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)rate;
+ do_div(ni, (unsigned long long int)max98095->sysclk);
+ snd_soc_write(codec, M98095_028_DAI1_CLKCFG_HI,
+ (ni >> 8) & 0x7F);
+ snd_soc_write(codec, M98095_029_DAI1_CLKCFG_LO,
+ ni & 0xFF);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_update_bits(codec, M98095_02E_DAI1_FILTERS,
+ M98095_DAI_DHF, 0);
+ else
+ snd_soc_update_bits(codec, M98095_02E_DAI1_FILTERS,
+ M98095_DAI_DHF, M98095_DAI_DHF);
+
+ return 0;
+}
+
+static int max98095_dai2_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98095->dai[1];
+
+ rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ M98095_DAI_WS, 0);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ M98095_DAI_WS, M98095_DAI_WS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rate_value(rate, ®val))
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, M98095_031_DAI2_CLKMODE,
+ M98095_CLKMODE_MASK, regval);
+ cdata->rate = rate;
+
+ /* Configure NI when operating as master */
+ if (snd_soc_read(codec, M98095_034_DAI2_FORMAT) & M98095_DAI_MAS) {
+ if (max98095->sysclk == 0) {
+ dev_err(codec->dev, "Invalid system clock frequency\n");
+ return -EINVAL;
+ }
+ ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)rate;
+ do_div(ni, (unsigned long long int)max98095->sysclk);
+ snd_soc_write(codec, M98095_032_DAI2_CLKCFG_HI,
+ (ni >> 8) & 0x7F);
+ snd_soc_write(codec, M98095_033_DAI2_CLKCFG_LO,
+ ni & 0xFF);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_update_bits(codec, M98095_038_DAI2_FILTERS,
+ M98095_DAI_DHF, 0);
+ else
+ snd_soc_update_bits(codec, M98095_038_DAI2_FILTERS,
+ M98095_DAI_DHF, M98095_DAI_DHF);
+
+ return 0;
+}
+
+static int max98095_dai3_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98095->dai[2];
+
+ rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ M98095_DAI_WS, 0);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ M98095_DAI_WS, M98095_DAI_WS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rate_value(rate, ®val))
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, M98095_03B_DAI3_CLKMODE,
+ M98095_CLKMODE_MASK, regval);
+ cdata->rate = rate;
+
+ /* Configure NI when operating as master */
+ if (snd_soc_read(codec, M98095_03E_DAI3_FORMAT) & M98095_DAI_MAS) {
+ if (max98095->sysclk == 0) {
+ dev_err(codec->dev, "Invalid system clock frequency\n");
+ return -EINVAL;
+ }
+ ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)rate;
+ do_div(ni, (unsigned long long int)max98095->sysclk);
+ snd_soc_write(codec, M98095_03C_DAI3_CLKCFG_HI,
+ (ni >> 8) & 0x7F);
+ snd_soc_write(codec, M98095_03D_DAI3_CLKCFG_LO,
+ ni & 0xFF);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_update_bits(codec, M98095_042_DAI3_FILTERS,
+ M98095_DAI_DHF, 0);
+ else
+ snd_soc_update_bits(codec, M98095_042_DAI3_FILTERS,
+ M98095_DAI_DHF, M98095_DAI_DHF);
+
+ return 0;
+}
+
+static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+
+ /* Requested clock frequency is already setup */
+ if (freq == max98095->sysclk)
+ return 0;
+
+ max98095->sysclk = freq; /* remember current sysclk */
+
+ /* Setup clocks for slave mode, and using the PLL
+ * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+ * 0x02 (when master clk is 20MHz to 40MHz)..
+ * 0x03 (when master clk is 40MHz to 60MHz)..
+ */
+ if ((freq >= 10000000) && (freq < 20000000)) {
+ snd_soc_write(codec, M98095_026_SYS_CLK, 0x10);
+ } else if ((freq >= 20000000) && (freq < 40000000)) {
+ snd_soc_write(codec, M98095_026_SYS_CLK, 0x20);
+ } else if ((freq >= 40000000) && (freq < 60000000)) {
+ snd_soc_write(codec, M98095_026_SYS_CLK, 0x30);
+ } else {
+ dev_err(codec->dev, "Invalid master clock frequency\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+ max98095->sysclk = freq;
+ return 0;
+}
+
+static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ u8 regval = 0;
+
+ cdata = &max98095->dai[0];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* Slave mode PLL */
+ snd_soc_write(codec, M98095_028_DAI1_CLKCFG_HI,
+ 0x80);
+ snd_soc_write(codec, M98095_029_DAI1_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Set to master mode */
+ regval |= M98095_DAI_MAS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ default:
+ dev_err(codec->dev, "Clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ regval |= M98095_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval |= M98095_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval |= M98095_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval |= M98095_DAI_BCI|M98095_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI |
+ M98095_DAI_WCI, regval);
+
+ snd_soc_write(codec, M98095_02B_DAI1_CLOCK, M98095_DAI_BSEL64);
+ }
+
+ return 0;
+}
+
+static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ u8 regval = 0;
+
+ cdata = &max98095->dai[1];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* Slave mode PLL */
+ snd_soc_write(codec, M98095_032_DAI2_CLKCFG_HI,
+ 0x80);
+ snd_soc_write(codec, M98095_033_DAI2_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Set to master mode */
+ regval |= M98095_DAI_MAS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ default:
+ dev_err(codec->dev, "Clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ regval |= M98095_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval |= M98095_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval |= M98095_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval |= M98095_DAI_BCI|M98095_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI |
+ M98095_DAI_WCI, regval);
+
+ snd_soc_write(codec, M98095_035_DAI2_CLOCK,
+ M98095_DAI_BSEL64);
+ }
+
+ return 0;
+}
+
+static int max98095_dai3_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ u8 regval = 0;
+
+ cdata = &max98095->dai[2];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* Slave mode PLL */
+ snd_soc_write(codec, M98095_03C_DAI3_CLKCFG_HI,
+ 0x80);
+ snd_soc_write(codec, M98095_03D_DAI3_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Set to master mode */
+ regval |= M98095_DAI_MAS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ default:
+ dev_err(codec->dev, "Clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ regval |= M98095_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regval |= M98095_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regval |= M98095_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ regval |= M98095_DAI_BCI|M98095_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI |
+ M98095_DAI_WCI, regval);
+
+ snd_soc_write(codec, M98095_03F_DAI3_CLOCK,
+ M98095_DAI_BSEL64);
+ }
+
+ return 0;
+}
+
+static int max98095_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = snd_soc_cache_sync(codec);
+
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+ }
+
+ snd_soc_update_bits(codec, M98095_090_PWR_EN_IN,
+ M98095_MBEN, M98095_MBEN);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, M98095_090_PWR_EN_IN,
+ M98095_MBEN, 0);
+ codec->cache_sync = 1;
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define MAX98095_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98095_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops max98095_dai1_ops = {
+ .set_sysclk = max98095_dai_set_sysclk,
+ .set_fmt = max98095_dai1_set_fmt,
+ .hw_params = max98095_dai1_hw_params,
+};
+
+static struct snd_soc_dai_ops max98095_dai2_ops = {
+ .set_sysclk = max98095_dai_set_sysclk,
+ .set_fmt = max98095_dai2_set_fmt,
+ .hw_params = max98095_dai2_hw_params,
+};
+
+static struct snd_soc_dai_ops max98095_dai3_ops = {
+ .set_sysclk = max98095_dai_set_sysclk,
+ .set_fmt = max98095_dai3_set_fmt,
+ .hw_params = max98095_dai3_hw_params,
+};
+
+static struct snd_soc_dai_driver max98095_dai[] = {
+{
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .ops = &max98095_dai1_ops,
+},
+{
+ .name = "Aux",
+ .playback = {
+ .stream_name = "Aux Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .ops = &max98095_dai2_ops,
+},
+{
+ .name = "Voice",
+ .playback = {
+ .stream_name = "Voice Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MAX98095_RATES,
+ .formats = MAX98095_FORMATS,
+ },
+ .ops = &max98095_dai3_ops,
+}
+
+};
+
+static int max98095_get_eq_channel(const char *name)
+{
+ if (strcmp(name, "EQ1 Mode") == 0)
+ return 0;
+ if (strcmp(name, "EQ2 Mode") == 0)
+ return 1;
+ return -EINVAL;
+}
+
+static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ int channel = max98095_get_eq_channel(kcontrol->id.name);
+ struct max98095_cdata *cdata;
+ int sel = ucontrol->value.integer.value[0];
+ struct max98095_eq_cfg *coef_set;
+ int fs, best, best_val, i;
+ int regmask, regsave;
+
+ BUG_ON(channel > 1);
+
+ if (!pdata || !max98095->eq_textcnt)
+ return 0;
+
+ if (sel >= pdata->eq_cfgcnt)
+ return -EINVAL;
+
+ cdata = &max98095->dai[channel];
+ cdata->eq_sel = sel;
+ fs = cdata->rate;
+
+ /* Find the selected configuration with nearest sample rate */
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->eq_cfgcnt; i++) {
+ if (strcmp(pdata->eq_cfg[i].name, max98095->eq_texts[sel]) == 0 &&
+ abs(pdata->eq_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->eq_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ pdata->eq_cfg[best].name,
+ pdata->eq_cfg[best].rate, fs);
+
+ coef_set = &pdata->eq_cfg[best];
+
+ regmask = (channel == 0) ? M98095_EQ1EN : M98095_EQ2EN;
+
+ /* Disable filter while configuring, and save current on/off state */
+ regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
+ snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
+
+ mutex_lock(&codec->mutex);
+ snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
+ m98095_eq_band(codec, channel, 0, coef_set->band1);
+ m98095_eq_band(codec, channel, 1, coef_set->band2);
+ m98095_eq_band(codec, channel, 2, coef_set->band3);
+ m98095_eq_band(codec, channel, 3, coef_set->band4);
+ m98095_eq_band(codec, channel, 4, coef_set->band5);
+ snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
+ mutex_unlock(&codec->mutex);
+
+ /* Restore the original on/off state */
+ snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
+ return 0;
+}
+
+static int max98095_get_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ int channel = max98095_get_eq_channel(kcontrol->id.name);
+ struct max98095_cdata *cdata;
+
+ cdata = &max98095->dai[channel];
+ ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+
+ return 0;
+}
+
+static void max98095_handle_eq_pdata(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ struct max98095_eq_cfg *cfg;
+ unsigned int cfgcnt;
+ int i, j;
+ const char **t;
+ int ret;
+
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("EQ1 Mode",
+ max98095->eq_enum,
+ max98095_get_eq_enum,
+ max98095_put_eq_enum),
+ SOC_ENUM_EXT("EQ2 Mode",
+ max98095->eq_enum,
+ max98095_get_eq_enum,
+ max98095_put_eq_enum),
+ };
+
+ cfg = pdata->eq_cfg;
+ cfgcnt = pdata->eq_cfgcnt;
+
+ /* Setup an array of texts for the equalizer enum.
+ * This is based on Mark Brown's equalizer driver code.
+ */
+ max98095->eq_textcnt = 0;
+ max98095->eq_texts = NULL;
+ for (i = 0; i < cfgcnt; i++) {
+ for (j = 0; j < max98095->eq_textcnt; j++) {
+ if (strcmp(cfg[i].name, max98095->eq_texts[j]) == 0)
+ break;
+ }
+
+ if (j != max98095->eq_textcnt)
+ continue;
+
+ /* Expand the array */
+ t = krealloc(max98095->eq_texts,
+ sizeof(char *) * (max98095->eq_textcnt + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* Store the new entry */
+ t[max98095->eq_textcnt] = cfg[i].name;
+ max98095->eq_textcnt++;
+ max98095->eq_texts = t;
+ }
+
+ /* Now point the soc_enum to .texts array items */
+ max98095->eq_enum.texts = max98095->eq_texts;
+ max98095->eq_enum.max = max98095->eq_textcnt;
+
+ ret = snd_soc_add_controls(codec, controls, ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+}
+
+static int max98095_get_bq_channel(const char *name)
+{
+ if (strcmp(name, "Biquad1 Mode") == 0)
+ return 0;
+ if (strcmp(name, "Biquad2 Mode") == 0)
+ return 1;
+ return -EINVAL;
+}
+
+static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ int channel = max98095_get_bq_channel(kcontrol->id.name);
+ struct max98095_cdata *cdata;
+ int sel = ucontrol->value.integer.value[0];
+ struct max98095_biquad_cfg *coef_set;
+ int fs, best, best_val, i;
+ int regmask, regsave;
+
+ BUG_ON(channel > 1);
+
+ if (!pdata || !max98095->bq_textcnt)
+ return 0;
+
+ if (sel >= pdata->bq_cfgcnt)
+ return -EINVAL;
+
+ cdata = &max98095->dai[channel];
+ cdata->bq_sel = sel;
+ fs = cdata->rate;
+
+ /* Find the selected configuration with nearest sample rate */
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->bq_cfgcnt; i++) {
+ if (strcmp(pdata->bq_cfg[i].name, max98095->bq_texts[sel]) == 0 &&
+ abs(pdata->bq_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->bq_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ pdata->bq_cfg[best].name,
+ pdata->bq_cfg[best].rate, fs);
+
+ coef_set = &pdata->bq_cfg[best];
+
+ regmask = (channel == 0) ? M98095_BQ1EN : M98095_BQ2EN;
+
+ /* Disable filter while configuring, and save current on/off state */
+ regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
+ snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
+
+ mutex_lock(&codec->mutex);
+ snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
+ m98095_biquad_band(codec, channel, 0, coef_set->band1);
+ m98095_biquad_band(codec, channel, 1, coef_set->band2);
+ snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
+ mutex_unlock(&codec->mutex);
+
+ /* Restore the original on/off state */
+ snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
+ return 0;
+}
+
+static int max98095_get_bq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ int channel = max98095_get_bq_channel(kcontrol->id.name);
+ struct max98095_cdata *cdata;
+
+ cdata = &max98095->dai[channel];
+ ucontrol->value.enumerated.item[0] = cdata->bq_sel;
+
+ return 0;
+}
+
+static void max98095_handle_bq_pdata(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ struct max98095_biquad_cfg *cfg;
+ unsigned int cfgcnt;
+ int i, j;
+ const char **t;
+ int ret;
+
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("Biquad1 Mode",
+ max98095->bq_enum,
+ max98095_get_bq_enum,
+ max98095_put_bq_enum),
+ SOC_ENUM_EXT("Biquad2 Mode",
+ max98095->bq_enum,
+ max98095_get_bq_enum,
+ max98095_put_bq_enum),
+ };
+
+ cfg = pdata->bq_cfg;
+ cfgcnt = pdata->bq_cfgcnt;
+
+ /* Setup an array of texts for the biquad enum.
+ * This is based on Mark Brown's equalizer driver code.
+ */
+ max98095->bq_textcnt = 0;
+ max98095->bq_texts = NULL;
+ for (i = 0; i < cfgcnt; i++) {
+ for (j = 0; j < max98095->bq_textcnt; j++) {
+ if (strcmp(cfg[i].name, max98095->bq_texts[j]) == 0)
+ break;
+ }
+
+ if (j != max98095->bq_textcnt)
+ continue;
+
+ /* Expand the array */
+ t = krealloc(max98095->bq_texts,
+ sizeof(char *) * (max98095->bq_textcnt + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* Store the new entry */
+ t[max98095->bq_textcnt] = cfg[i].name;
+ max98095->bq_textcnt++;
+ max98095->bq_texts = t;
+ }
+
+ /* Now point the soc_enum to .texts array items */
+ max98095->bq_enum.texts = max98095->bq_texts;
+ max98095->bq_enum.max = max98095->bq_textcnt;
+
+ ret = snd_soc_add_controls(codec, controls, ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add Biquad control: %d\n", ret);
+}
+
+static void max98095_handle_pdata(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_pdata *pdata = max98095->pdata;
+ u8 regval = 0;
+
+ if (!pdata) {
+ dev_dbg(codec->dev, "No platform data\n");
+ return;
+ }
+
+ /* Configure mic for analog/digital mic mode */
+ if (pdata->digmic_left_mode)
+ regval |= M98095_DIGMIC_L;
+
+ if (pdata->digmic_right_mode)
+ regval |= M98095_DIGMIC_R;
+
+ snd_soc_write(codec, M98095_087_CFG_MIC, regval);
+
+ /* Configure equalizers */
+ if (pdata->eq_cfgcnt)
+ max98095_handle_eq_pdata(codec);
+
+ /* Configure bi-quad filters */
+ if (pdata->bq_cfgcnt)
+ max98095_handle_bq_pdata(codec);
+}
+
+#ifdef CONFIG_PM
+static int max98095_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+ max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int max98095_resume(struct snd_soc_codec *codec)
+{
+ max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define max98095_suspend NULL
+#define max98095_resume NULL
+#endif
+
+static int max98095_reset(struct snd_soc_codec *codec)
+{
+ int i, ret;
+
+ /* Gracefully reset the DSP core and the codec hardware
+ * in a proper sequence */
+ ret = snd_soc_write(codec, M98095_00F_HOST_CFG, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to reset DSP: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_write(codec, M98095_097_PWR_SYS, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to reset codec: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset to hardware default for registers, as there is not
+ * a soft reset hardware control register */
+ for (i = M98095_010_HOST_INT_CFG; i < M98095_REG_MAX_CACHED; i++) {
+ ret = snd_soc_write(codec, i, max98095_reg_def[i]);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to reset: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int max98095_probe(struct snd_soc_codec *codec)
+{
+ struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_cdata *cdata;
+ int ret = 0;
+
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ /* reset the codec, the DSP core, and disable all interrupts */
+ max98095_reset(codec);
+
+ /* initialize private data */
+
+ max98095->sysclk = (unsigned)-1;
+ max98095->eq_textcnt = 0;
+ max98095->bq_textcnt = 0;
+
+ cdata = &max98095->dai[0];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+ cdata->bq_sel = 0;
+
+ cdata = &max98095->dai[1];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+ cdata->bq_sel = 0;
+
+ cdata = &max98095->dai[2];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+ cdata->bq_sel = 0;
+
+ max98095->lin_state = 0;
+ max98095->mic1pre = 0;
+ max98095->mic2pre = 0;
+
+ ret = snd_soc_read(codec, M98095_0FF_REV_ID);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read device revision: %d\n",
+ ret);
+ goto err_access;
+ }
+ dev_info(codec->dev, "revision %c\n", ret + 'A');
+
+ snd_soc_write(codec, M98095_097_PWR_SYS, M98095_PWRSV);
+
+ /* initialize registers cache to hardware default */
+ max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ snd_soc_write(codec, M98095_048_MIX_DAC_LR,
+ M98095_DAI1L_TO_DACL|M98095_DAI1R_TO_DACR);
+
+ snd_soc_write(codec, M98095_049_MIX_DAC_M,
+ M98095_DAI2M_TO_DACM|M98095_DAI3M_TO_DACM);
+
+ snd_soc_write(codec, M98095_092_PWR_EN_OUT, M98095_SPK_SPREADSPECTRUM);
+ snd_soc_write(codec, M98095_045_CFG_DSP, M98095_DSPNORMAL);
+ snd_soc_write(codec, M98095_04E_CFG_HP, M98095_HPNORMAL);
+
+ snd_soc_write(codec, M98095_02C_DAI1_IOCFG,
+ M98095_S1NORMAL|M98095_SDATA);
+
+ snd_soc_write(codec, M98095_036_DAI2_IOCFG,
+ M98095_S2NORMAL|M98095_SDATA);
+
+ snd_soc_write(codec, M98095_040_DAI3_IOCFG,
+ M98095_S3NORMAL|M98095_SDATA);
+
+ max98095_handle_pdata(codec);
+
+ /* take the codec out of the shut down */
+ snd_soc_update_bits(codec, M98095_097_PWR_SYS, M98095_SHDNRUN,
+ M98095_SHDNRUN);
+
+ max98095_add_widgets(codec);
+
+err_access:
+ return ret;
+}
+
+static int max98095_remove(struct snd_soc_codec *codec)
+{
+ max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_max98095 = {
+ .probe = max98095_probe,
+ .remove = max98095_remove,
+ .suspend = max98095_suspend,
+ .resume = max98095_resume,
+ .set_bias_level = max98095_set_bias_level,
+ .reg_cache_size = ARRAY_SIZE(max98095_reg_def),
+ .reg_word_size = sizeof(u8),
+ .reg_cache_default = max98095_reg_def,
+ .readable_register = max98095_readable,
+ .volatile_register = max98095_volatile,
+ .dapm_widgets = max98095_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98095_dapm_widgets),
+ .dapm_routes = max98095_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98095_audio_map),
+};
+
+static int max98095_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max98095_priv *max98095;
+ int ret;
+
+ max98095 = kzalloc(sizeof(struct max98095_priv), GFP_KERNEL);
+ if (max98095 == NULL)
+ return -ENOMEM;
+
+ max98095->devtype = id->driver_data;
+ i2c_set_clientdata(i2c, max98095);
+ max98095->control_data = i2c;
+ max98095->pdata = i2c->dev.platform_data;
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_max98095, &max98095_dai[0], 3);
+ if (ret < 0)
+ kfree(max98095);
+ return ret;
+}
+
+static int __devexit max98095_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ kfree(i2c_get_clientdata(client));
+
+ return 0;
+}
+
+static const struct i2c_device_id max98095_i2c_id[] = {
+ { "max98095", MAX98095 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98095_i2c_id);
+
+static struct i2c_driver max98095_i2c_driver = {
+ .driver = {
+ .name = "max98095",
+ .owner = THIS_MODULE,
+ },
+ .probe = max98095_i2c_probe,
+ .remove = __devexit_p(max98095_i2c_remove),
+ .id_table = max98095_i2c_id,
+};
+
+static int __init max98095_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&max98095_i2c_driver);
+ if (ret)
+ pr_err("Failed to register max98095 I2C driver: %d\n", ret);
+
+ return ret;
+}
+module_init(max98095_init);
+
+static void __exit max98095_exit(void)
+{
+ i2c_del_driver(&max98095_i2c_driver);
+}
+module_exit(max98095_exit);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98095 driver");
+MODULE_AUTHOR("Peter Hsiang");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * max98095.h -- MAX98095 ALSA SoC Audio driver
+ *
+ * Copyright 2011 Maxim Integrated Products
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MAX98095_H
+#define _MAX98095_H
+
+/*
+ * MAX98095 Registers Definition
+ */
+
+#define M98095_000_HOST_DATA 0x00
+#define M98095_001_HOST_INT_STS 0x01
+#define M98095_002_HOST_RSP_STS 0x02
+#define M98095_003_HOST_CMD_STS 0x03
+#define M98095_004_CODEC_STS 0x04
+#define M98095_005_DAI1_ALC_STS 0x05
+#define M98095_006_DAI2_ALC_STS 0x06
+#define M98095_007_JACK_AUTO_STS 0x07
+#define M98095_008_JACK_MANUAL_STS 0x08
+#define M98095_009_JACK_VBAT_STS 0x09
+#define M98095_00A_ACC_ADC_STS 0x0A
+#define M98095_00B_MIC_NG_AGC_STS 0x0B
+#define M98095_00C_SPK_L_VOLT_STS 0x0C
+#define M98095_00D_SPK_R_VOLT_STS 0x0D
+#define M98095_00E_TEMP_SENSOR_STS 0x0E
+#define M98095_00F_HOST_CFG 0x0F
+#define M98095_010_HOST_INT_CFG 0x10
+#define M98095_011_HOST_INT_EN 0x11
+#define M98095_012_CODEC_INT_EN 0x12
+#define M98095_013_JACK_INT_EN 0x13
+#define M98095_014_JACK_INT_EN 0x14
+#define M98095_015_DEC 0x15
+#define M98095_016_RESERVED 0x16
+#define M98095_017_RESERVED 0x17
+#define M98095_018_KEYCODE3 0x18
+#define M98095_019_KEYCODE2 0x19
+#define M98095_01A_KEYCODE1 0x1A
+#define M98095_01B_KEYCODE0 0x1B
+#define M98095_01C_OEMCODE1 0x1C
+#define M98095_01D_OEMCODE0 0x1D
+#define M98095_01E_XCFG1 0x1E
+#define M98095_01F_XCFG2 0x1F
+#define M98095_020_XCFG3 0x20
+#define M98095_021_XCFG4 0x21
+#define M98095_022_XCFG5 0x22
+#define M98095_023_XCFG6 0x23
+#define M98095_024_XGPIO 0x24
+#define M98095_025_XCLKCFG 0x25
+#define M98095_026_SYS_CLK 0x26
+#define M98095_027_DAI1_CLKMODE 0x27
+#define M98095_028_DAI1_CLKCFG_HI 0x28
+#define M98095_029_DAI1_CLKCFG_LO 0x29
+#define M98095_02A_DAI1_FORMAT 0x2A
+#define M98095_02B_DAI1_CLOCK 0x2B
+#define M98095_02C_DAI1_IOCFG 0x2C
+#define M98095_02D_DAI1_TDM 0x2D
+#define M98095_02E_DAI1_FILTERS 0x2E
+#define M98095_02F_DAI1_LVL1 0x2F
+#define M98095_030_DAI1_LVL2 0x30
+#define M98095_031_DAI2_CLKMODE 0x31
+#define M98095_032_DAI2_CLKCFG_HI 0x32
+#define M98095_033_DAI2_CLKCFG_LO 0x33
+#define M98095_034_DAI2_FORMAT 0x34
+#define M98095_035_DAI2_CLOCK 0x35
+#define M98095_036_DAI2_IOCFG 0x36
+#define M98095_037_DAI2_TDM 0x37
+#define M98095_038_DAI2_FILTERS 0x38
+#define M98095_039_DAI2_LVL1 0x39
+#define M98095_03A_DAI2_LVL2 0x3A
+#define M98095_03B_DAI3_CLKMODE 0x3B
+#define M98095_03C_DAI3_CLKCFG_HI 0x3C
+#define M98095_03D_DAI3_CLKCFG_LO 0x3D
+#define M98095_03E_DAI3_FORMAT 0x3E
+#define M98095_03F_DAI3_CLOCK 0x3F
+#define M98095_040_DAI3_IOCFG 0x40
+#define M98095_041_DAI3_TDM 0x41
+#define M98095_042_DAI3_FILTERS 0x42
+#define M98095_043_DAI3_LVL1 0x43
+#define M98095_044_DAI3_LVL2 0x44
+#define M98095_045_CFG_DSP 0x45
+#define M98095_046_DAC_CTRL1 0x46
+#define M98095_047_DAC_CTRL2 0x47
+#define M98095_048_MIX_DAC_LR 0x48
+#define M98095_049_MIX_DAC_M 0x49
+#define M98095_04A_MIX_ADC_LEFT 0x4A
+#define M98095_04B_MIX_ADC_RIGHT 0x4B
+#define M98095_04C_MIX_HP_LEFT 0x4C
+#define M98095_04D_MIX_HP_RIGHT 0x4D
+#define M98095_04E_CFG_HP 0x4E
+#define M98095_04F_MIX_RCV 0x4F
+#define M98095_050_MIX_SPK_LEFT 0x50
+#define M98095_051_MIX_SPK_RIGHT 0x51
+#define M98095_052_MIX_SPK_CFG 0x52
+#define M98095_053_MIX_LINEOUT1 0x53
+#define M98095_054_MIX_LINEOUT2 0x54
+#define M98095_055_MIX_LINEOUT_CFG 0x55
+#define M98095_056_LVL_SIDETONE_DAI12 0x56
+#define M98095_057_LVL_SIDETONE_DAI3 0x57
+#define M98095_058_LVL_DAI1_PLAY 0x58
+#define M98095_059_LVL_DAI1_EQ 0x59
+#define M98095_05A_LVL_DAI2_PLAY 0x5A
+#define M98095_05B_LVL_DAI2_EQ 0x5B
+#define M98095_05C_LVL_DAI3_PLAY 0x5C
+#define M98095_05D_LVL_ADC_L 0x5D
+#define M98095_05E_LVL_ADC_R 0x5E
+#define M98095_05F_LVL_MIC1 0x5F
+#define M98095_060_LVL_MIC2 0x60
+#define M98095_061_LVL_LINEIN 0x61
+#define M98095_062_LVL_LINEOUT1 0x62
+#define M98095_063_LVL_LINEOUT2 0x63
+#define M98095_064_LVL_HP_L 0x64
+#define M98095_065_LVL_HP_R 0x65
+#define M98095_066_LVL_RCV 0x66
+#define M98095_067_LVL_SPK_L 0x67
+#define M98095_068_LVL_SPK_R 0x68
+#define M98095_069_MICAGC_CFG 0x69
+#define M98095_06A_MICAGC_THRESH 0x6A
+#define M98095_06B_SPK_NOISEGATE 0x6B
+#define M98095_06C_DAI1_ALC1_TIME 0x6C
+#define M98095_06D_DAI1_ALC1_COMP 0x6D
+#define M98095_06E_DAI1_ALC1_EXPN 0x6E
+#define M98095_06F_DAI1_ALC1_GAIN 0x6F
+#define M98095_070_DAI1_ALC2_TIME 0x70
+#define M98095_071_DAI1_ALC2_COMP 0x71
+#define M98095_072_DAI1_ALC2_EXPN 0x72
+#define M98095_073_DAI1_ALC2_GAIN 0x73
+#define M98095_074_DAI1_ALC3_TIME 0x74
+#define M98095_075_DAI1_ALC3_COMP 0x75
+#define M98095_076_DAI1_ALC3_EXPN 0x76
+#define M98095_077_DAI1_ALC3_GAIN 0x77
+#define M98095_078_DAI2_ALC1_TIME 0x78
+#define M98095_079_DAI2_ALC1_COMP 0x79
+#define M98095_07A_DAI2_ALC1_EXPN 0x7A
+#define M98095_07B_DAI2_ALC1_GAIN 0x7B
+#define M98095_07C_DAI2_ALC2_TIME 0x7C
+#define M98095_07D_DAI2_ALC2_COMP 0x7D
+#define M98095_07E_DAI2_ALC2_EXPN 0x7E
+#define M98095_07F_DAI2_ALC2_GAIN 0x7F
+#define M98095_080_DAI2_ALC3_TIME 0x80
+#define M98095_081_DAI2_ALC3_COMP 0x81
+#define M98095_082_DAI2_ALC3_EXPN 0x82
+#define M98095_083_DAI2_ALC3_GAIN 0x83
+#define M98095_084_HP_NOISE_GATE 0x84
+#define M98095_085_AUX_ADC 0x85
+#define M98095_086_CFG_LINE 0x86
+#define M98095_087_CFG_MIC 0x87
+#define M98095_088_CFG_LEVEL 0x88
+#define M98095_089_JACK_DET_AUTO 0x89
+#define M98095_08A_JACK_DET_MANUAL 0x8A
+#define M98095_08B_JACK_KEYSCAN_DBC 0x8B
+#define M98095_08C_JACK_KEYSCAN_DLY 0x8C
+#define M98095_08D_JACK_KEY_THRESH 0x8D
+#define M98095_08E_JACK_DC_SLEW 0x8E
+#define M98095_08F_JACK_TEST_CFG 0x8F
+#define M98095_090_PWR_EN_IN 0x90
+#define M98095_091_PWR_EN_OUT 0x91
+#define M98095_092_PWR_EN_OUT 0x92
+#define M98095_093_BIAS_CTRL 0x93
+#define M98095_094_PWR_DAC_21 0x94
+#define M98095_095_PWR_DAC_03 0x95
+#define M98095_096_PWR_DAC_CK 0x96
+#define M98095_097_PWR_SYS 0x97
+
+#define M98095_0FF_REV_ID 0xFF
+
+#define M98095_REG_CNT (0xFF+1)
+#define M98095_REG_MAX_CACHED 0X97
+
+/* MAX98095 Registers Bit Fields */
+
+/* M98095_00F_HOST_CFG */
+ #define M98095_SEG (1<<0)
+ #define M98095_XTEN (1<<1)
+ #define M98095_MDLLEN (1<<2)
+
+/* M98095_027_DAI1_CLKMODE, M98095_031_DAI2_CLKMODE, M98095_03B_DAI3_CLKMODE */
+ #define M98095_CLKMODE_MASK 0xFF
+
+/* M98095_02A_DAI1_FORMAT, M98095_034_DAI2_FORMAT, M98095_03E_DAI3_FORMAT */
+ #define M98095_DAI_MAS (1<<7)
+ #define M98095_DAI_WCI (1<<6)
+ #define M98095_DAI_BCI (1<<5)
+ #define M98095_DAI_DLY (1<<4)
+ #define M98095_DAI_TDM (1<<2)
+ #define M98095_DAI_FSW (1<<1)
+ #define M98095_DAI_WS (1<<0)
+
+/* M98095_02B_DAI1_CLOCK, M98095_035_DAI2_CLOCK, M98095_03F_DAI3_CLOCK */
+ #define M98095_DAI_BSEL64 (1<<0)
+ #define M98095_DAI_DOSR_DIV2 (0<<5)
+ #define M98095_DAI_DOSR_DIV4 (1<<5)
+
+/* M98095_02C_DAI1_IOCFG, M98095_036_DAI2_IOCFG, M98095_040_DAI3_IOCFG */
+ #define M98095_S1NORMAL (1<<6)
+ #define M98095_S2NORMAL (2<<6)
+ #define M98095_S3NORMAL (3<<6)
+ #define M98095_SDATA (3<<0)
+
+/* M98095_02E_DAI1_FILTERS, M98095_038_DAI2_FILTERS, M98095_042_DAI3_FILTERS */
+ #define M98095_DAI_DHF (1<<3)
+
+/* M98095_045_DSP_CFG */
+ #define M98095_DSPNORMAL (5<<4)
+
+/* M98095_048_MIX_DAC_LR */
+ #define M98095_DAI1L_TO_DACR (1<<7)
+ #define M98095_DAI1R_TO_DACR (1<<6)
+ #define M98095_DAI2M_TO_DACR (1<<5)
+ #define M98095_DAI1L_TO_DACL (1<<3)
+ #define M98095_DAI1R_TO_DACL (1<<2)
+ #define M98095_DAI2M_TO_DACL (1<<1)
+ #define M98095_DAI3M_TO_DACL (1<<0)
+
+/* M98095_049_MIX_DAC_M */
+ #define M98095_DAI1L_TO_DACM (1<<3)
+ #define M98095_DAI1R_TO_DACM (1<<2)
+ #define M98095_DAI2M_TO_DACM (1<<1)
+ #define M98095_DAI3M_TO_DACM (1<<0)
+
+/* M98095_04E_MIX_HP_CFG */
+ #define M98095_HPNORMAL (3<<4)
+
+/* M98095_05F_LVL_MIC1, M98095_060_LVL_MIC2 */
+ #define M98095_MICPRE_MASK (3<<5)
+ #define M98095_MICPRE_SHIFT 5
+
+/* M98095_064_LVL_HP_L, M98095_065_LVL_HP_R */
+ #define M98095_HP_MUTE (1<<7)
+
+/* M98095_066_LVL_RCV */
+ #define M98095_REC_MUTE (1<<7)
+
+/* M98095_067_LVL_SPK_L, M98095_068_LVL_SPK_R */
+ #define M98095_SP_MUTE (1<<7)
+
+/* M98095_087_CFG_MIC */
+ #define M98095_MICSEL_MASK (3<<0)
+ #define M98095_DIGMIC_L (1<<2)
+ #define M98095_DIGMIC_R (1<<3)
+ #define M98095_DIGMIC2L (1<<4)
+ #define M98095_DIGMIC2R (1<<5)
+
+/* M98095_088_CFG_LEVEL */
+ #define M98095_VSEN (1<<6)
+ #define M98095_ZDEN (1<<5)
+ #define M98095_BQ2EN (1<<3)
+ #define M98095_BQ1EN (1<<2)
+ #define M98095_EQ2EN (1<<1)
+ #define M98095_EQ1EN (1<<0)
+
+/* M98095_090_PWR_EN_IN */
+ #define M98095_INEN (1<<7)
+ #define M98095_MB2EN (1<<3)
+ #define M98095_MB1EN (1<<2)
+ #define M98095_MBEN (3<<2)
+ #define M98095_ADREN (1<<1)
+ #define M98095_ADLEN (1<<0)
+
+/* M98095_091_PWR_EN_OUT */
+ #define M98095_HPLEN (1<<7)
+ #define M98095_HPREN (1<<6)
+ #define M98095_SPLEN (1<<5)
+ #define M98095_SPREN (1<<4)
+ #define M98095_RECEN (1<<3)
+ #define M98095_DALEN (1<<1)
+ #define M98095_DAREN (1<<0)
+
+/* M98095_092_PWR_EN_OUT */
+ #define M98095_SPK_FIXEDSPECTRUM (0<<4)
+ #define M98095_SPK_SPREADSPECTRUM (1<<4)
+
+/* M98095_097_PWR_SYS */
+ #define M98095_SHDNRUN (1<<7)
+ #define M98095_PERFMODE (1<<3)
+ #define M98095_HPPLYBACK (1<<2)
+ #define M98095_PWRSV8K (1<<1)
+ #define M98095_PWRSV (1<<0)
+
+#define M98095_COEFS_PER_BAND 5
+
+#define M98095_BYTE1(w) ((w >> 8) & 0xff)
+#define M98095_BYTE0(w) (w & 0xff)
+
+/* Equalizer filter coefficients */
+#define M98095_110_DAI1_EQ_BASE 0x10
+#define M98095_142_DAI2_EQ_BASE 0x42
+
+/* Biquad filter coefficients */
+#define M98095_174_DAI1_BQ_BASE 0x74
+#define M98095_17E_DAI2_BQ_BASE 0x7E
+
+#endif
/* codec registration */
static int sn95031_codec_probe(struct snd_soc_codec *codec)
{
- int ret;
-
pr_debug("codec_probe called\n");
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
snd_soc_add_controls(codec, sn95031_snd_controls,
ARRAY_SIZE(sn95031_snd_controls));
- ret = snd_soc_dapm_new_controls(&codec->dapm, sn95031_dapm_widgets,
- ARRAY_SIZE(sn95031_dapm_widgets));
- if (ret)
- pr_err("soc_dapm_new_control failed %d", ret);
- ret = snd_soc_dapm_add_routes(&codec->dapm, sn95031_audio_map,
- ARRAY_SIZE(sn95031_audio_map));
- if (ret)
- pr_err("soc_dapm_add_routes failed %d", ret);
-
- return ret;
+ return 0;
}
static int sn95031_codec_remove(struct snd_soc_codec *codec)
.read = sn95031_read,
.write = sn95031_write,
.set_bias_level = sn95031_set_vaud_bias,
+ .dapm_widgets = sn95031_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sn95031_dapm_widgets),
+ .dapm_routes = sn95031_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(sn95031_audio_map),
};
static int __devinit sn95031_device_probe(struct platform_device *pdev)
#include <sound/pcm.h>
#include <sound/initval.h>
-MODULE_LICENSE("GPL");
+#define DRV_NAME "spdif-dit"
#define STUB_RATES SNDRV_PCM_RATE_8000_96000
#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE
.probe = spdif_dit_probe,
.remove = spdif_dit_remove,
.driver = {
- .name = "spdif-dit",
+ .name = DRV_NAME,
.owner = THIS_MODULE,
},
};
module_init(dit_modinit);
module_exit(dit_exit);
+MODULE_AUTHOR("Steve Chen <schen@mvista.com>");
+MODULE_DESCRIPTION("SPDIF dummy codec driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/spi/spi.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
+#include <sound/tlv.h>
#include "ssm2602.h"
#define SSM2602_VERSION "0.1"
+enum ssm2602_type {
+ SSM2602,
+ SSM2604,
+};
+
/* codec private data */
struct ssm2602_priv {
unsigned int sysclk;
enum snd_soc_control_type control_type;
- void *control_data;
struct snd_pcm_substream *master_substream;
struct snd_pcm_substream *slave_substream;
+
+ enum ssm2602_type type;
};
/*
* There is no point in caching the reset register
*/
static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = {
- 0x0017, 0x0017, 0x0079, 0x0079,
- 0x0000, 0x0000, 0x0000, 0x000a,
+ 0x0097, 0x0097, 0x0079, 0x0079,
+ 0x000a, 0x0008, 0x009f, 0x000a,
0x0000, 0x0000
};
-/*
- * read ssm2602 register cache
- */
-static inline unsigned int ssm2602_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- if (reg == SSM2602_RESET)
- return 0;
- if (reg >= SSM2602_CACHEREGNUM)
- return -1;
- return cache[reg];
-}
-
-/*
- * write ssm2602 register cache
- */
-static inline void ssm2602_write_reg_cache(struct snd_soc_codec *codec,
- u16 reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- if (reg >= SSM2602_CACHEREGNUM)
- return;
- cache[reg] = value;
-}
-
-/*
- * write to the ssm2602 register space
- */
-static int ssm2602_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- /* data is
- * D15..D9 ssm2602 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- ssm2602_write_reg_cache(codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
-#define ssm2602_reset(c) ssm2602_write(c, SSM2602_RESET, 0)
+#define ssm2602_reset(c) snd_soc_write(c, SSM2602_RESET, 0)
/*Appending several "None"s just for OSS mixer use*/
static const char *ssm2602_input_select[] = {
SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph),
};
-static const struct snd_kcontrol_new ssm2602_snd_controls[] = {
+static const unsigned int ssm260x_outmix_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 47, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
+ 48, 127, TLV_DB_SCALE_ITEM(-7400, 100, 0),
+};
-SOC_DOUBLE_R("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V,
- 0, 127, 0),
-SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V,
- 7, 1, 0),
+static const DECLARE_TLV_DB_SCALE(ssm260x_inpga_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(ssm260x_sidetone_tlv, -1500, 300, 0);
-SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0),
+static const struct snd_kcontrol_new ssm260x_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 45, 0,
+ ssm260x_inpga_tlv),
SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1),
-SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
-SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 7, 1, 0),
-SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
-
-SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1),
-
SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1),
SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0),
-SOC_ENUM("Capture Source", ssm2602_enum[0]),
-
SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]),
};
+static const struct snd_kcontrol_new ssm2602_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V,
+ 0, 127, 0, ssm260x_outmix_tlv),
+SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V,
+ 7, 1, 0),
+SOC_SINGLE_TLV("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1,
+ ssm260x_sidetone_tlv),
+
+SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
+SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0),
+SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
+};
+
/* Output Mixer */
-static const struct snd_kcontrol_new ssm2602_output_mixer_controls[] = {
+static const struct snd_kcontrol_new ssm260x_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0),
-SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0),
+SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
};
/* Input mux */
static const struct snd_kcontrol_new ssm2602_input_mux_controls =
SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]);
-static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = {
-SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
- &ssm2602_output_mixer_controls[0],
- ARRAY_SIZE(ssm2602_output_mixer_controls)),
+static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1),
+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1),
+SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("Digital Core Power", SSM2602_ACTIVE, 0, 0, NULL, 0),
+
SND_SOC_DAPM_OUTPUT("LOUT"),
-SND_SOC_DAPM_OUTPUT("LHPOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
-SND_SOC_DAPM_OUTPUT("RHPOUT"),
-SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1),
+SND_SOC_DAPM_INPUT("RLINEIN"),
+SND_SOC_DAPM_INPUT("LLINEIN"),
+};
+
+static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
+ ssm260x_output_mixer_controls,
+ ARRAY_SIZE(ssm260x_output_mixer_controls)),
+
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls),
-SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0),
SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1),
+
+SND_SOC_DAPM_OUTPUT("LHPOUT"),
+SND_SOC_DAPM_OUTPUT("RHPOUT"),
SND_SOC_DAPM_INPUT("MICIN"),
-SND_SOC_DAPM_INPUT("RLINEIN"),
-SND_SOC_DAPM_INPUT("LLINEIN"),
};
-static const struct snd_soc_dapm_route audio_conn[] = {
- /* output mixer */
+static const struct snd_soc_dapm_widget ssm2604_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0,
+ ssm260x_output_mixer_controls,
+ ARRAY_SIZE(ssm260x_output_mixer_controls) - 1), /* Last element is the mic */
+};
+
+static const struct snd_soc_dapm_route ssm260x_routes[] = {
+ {"DAC", NULL, "Digital Core Power"},
+ {"ADC", NULL, "Digital Core Power"},
+
{"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "HiFi Playback Switch", "DAC"},
+
+ {"ROUT", NULL, "Output Mixer"},
+ {"LOUT", NULL, "Output Mixer"},
+
+ {"Line Input", NULL, "LLINEIN"},
+ {"Line Input", NULL, "RLINEIN"},
+};
+
+static const struct snd_soc_dapm_route ssm2602_routes[] = {
{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
- /* outputs */
{"RHPOUT", NULL, "Output Mixer"},
- {"ROUT", NULL, "Output Mixer"},
{"LHPOUT", NULL, "Output Mixer"},
- {"LOUT", NULL, "Output Mixer"},
- /* input mux */
{"Input Mux", "Line", "Line Input"},
{"Input Mux", "Mic", "Mic Bias"},
{"ADC", NULL, "Input Mux"},
- /* inputs */
- {"Line Input", NULL, "LLINEIN"},
- {"Line Input", NULL, "RLINEIN"},
{"Mic Bias", NULL, "MICIN"},
};
-static int ssm2602_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets,
- ARRAY_SIZE(ssm2602_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, audio_conn, ARRAY_SIZE(audio_conn));
-
- return 0;
-}
+static const struct snd_soc_dapm_route ssm2604_routes[] = {
+ {"ADC", NULL, "Line Input"},
+};
-struct _coeff_div {
+struct ssm2602_coeff {
u32 mclk;
u32 rate;
- u16 fs;
- u8 sr:4;
- u8 bosr:1;
- u8 usb:1;
+ u8 srate;
};
-/* codec mclk clock divider coefficients */
-static const struct _coeff_div coeff_div[] = {
+#define SSM2602_COEFF_SRATE(sr, bosr, usb) (((sr) << 2) | ((bosr) << 1) | (usb))
+
+/* codec mclk clock coefficients */
+static const struct ssm2602_coeff ssm2602_coeff_table[] = {
/* 48k */
- {12288000, 48000, 256, 0x0, 0x0, 0x0},
- {18432000, 48000, 384, 0x0, 0x1, 0x0},
- {12000000, 48000, 250, 0x0, 0x0, 0x1},
+ {12288000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x0)},
+ {18432000, 48000, SSM2602_COEFF_SRATE(0x0, 0x1, 0x0)},
+ {12000000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x1)},
/* 32k */
- {12288000, 32000, 384, 0x6, 0x0, 0x0},
- {18432000, 32000, 576, 0x6, 0x1, 0x0},
- {12000000, 32000, 375, 0x6, 0x0, 0x1},
+ {12288000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x0)},
+ {18432000, 32000, SSM2602_COEFF_SRATE(0x6, 0x1, 0x0)},
+ {12000000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x1)},
/* 8k */
- {12288000, 8000, 1536, 0x3, 0x0, 0x0},
- {18432000, 8000, 2304, 0x3, 0x1, 0x0},
- {11289600, 8000, 1408, 0xb, 0x0, 0x0},
- {16934400, 8000, 2112, 0xb, 0x1, 0x0},
- {12000000, 8000, 1500, 0x3, 0x0, 0x1},
+ {12288000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x0)},
+ {18432000, 8000, SSM2602_COEFF_SRATE(0x3, 0x1, 0x0)},
+ {11289600, 8000, SSM2602_COEFF_SRATE(0xb, 0x0, 0x0)},
+ {16934400, 8000, SSM2602_COEFF_SRATE(0xb, 0x1, 0x0)},
+ {12000000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x1)},
/* 96k */
- {12288000, 96000, 128, 0x7, 0x0, 0x0},
- {18432000, 96000, 192, 0x7, 0x1, 0x0},
- {12000000, 96000, 125, 0x7, 0x0, 0x1},
+ {12288000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x0)},
+ {18432000, 96000, SSM2602_COEFF_SRATE(0x7, 0x1, 0x0)},
+ {12000000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x1)},
/* 44.1k */
- {11289600, 44100, 256, 0x8, 0x0, 0x0},
- {16934400, 44100, 384, 0x8, 0x1, 0x0},
- {12000000, 44100, 272, 0x8, 0x1, 0x1},
+ {11289600, 44100, SSM2602_COEFF_SRATE(0x8, 0x0, 0x0)},
+ {16934400, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x0)},
+ {12000000, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x1)},
/* 88.2k */
- {11289600, 88200, 128, 0xf, 0x0, 0x0},
- {16934400, 88200, 192, 0xf, 0x1, 0x0},
- {12000000, 88200, 136, 0xf, 0x1, 0x1},
+ {11289600, 88200, SSM2602_COEFF_SRATE(0xf, 0x0, 0x0)},
+ {16934400, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x0)},
+ {12000000, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x1)},
};
-static inline int get_coeff(int mclk, int rate)
+static inline int ssm2602_get_coeff(int mclk, int rate)
{
int i;
- for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
- if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
- return i;
+ for (i = 0; i < ARRAY_SIZE(ssm2602_coeff_table); i++) {
+ if (ssm2602_coeff_table[i].rate == rate &&
+ ssm2602_coeff_table[i].mclk == mclk)
+ return ssm2602_coeff_table[i].srate;
}
- return i;
+ return -EINVAL;
}
static int ssm2602_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- u16 srate;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *i2c = codec->control_data;
- u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3;
- int i = get_coeff(ssm2602->sysclk, params_rate(params));
+ u16 iface = snd_soc_read(codec, SSM2602_IFACE) & 0xfff3;
+ int srate = ssm2602_get_coeff(ssm2602->sysclk, params_rate(params));
if (substream == ssm2602->slave_substream) {
- dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n");
+ dev_dbg(codec->dev, "Ignoring hw_params for slave substream\n");
return 0;
}
- /*no match is found*/
- if (i == ARRAY_SIZE(coeff_div))
- return -EINVAL;
-
- srate = (coeff_div[i].sr << 2) |
- (coeff_div[i].bosr << 1) | coeff_div[i].usb;
+ if (srate < 0)
+ return srate;
- ssm2602_write(codec, SSM2602_ACTIVE, 0);
- ssm2602_write(codec, SSM2602_SRATE, srate);
+ snd_soc_write(codec, SSM2602_SRATE, srate);
/* bit size */
switch (params_format(params)) {
iface |= 0x000c;
break;
}
- ssm2602_write(codec, SSM2602_IFACE, iface);
- ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
+ snd_soc_write(codec, SSM2602_IFACE, iface);
return 0;
}
return 0;
}
-static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
- /* set active */
- ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
-
- return 0;
-}
-
static void ssm2602_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = rtd->codec;
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
- /* deactivate */
- if (!codec->active)
- ssm2602_write(codec, SSM2602_ACTIVE, 0);
-
if (ssm2602->master_substream == substream)
ssm2602->master_substream = ssm2602->slave_substream;
ssm2602->slave_substream = NULL;
}
+
static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = ssm2602_read_reg_cache(codec, SSM2602_APDIGI) & ~APDIGI_ENABLE_DAC_MUTE;
+ u16 mute_reg = snd_soc_read(codec, SSM2602_APDIGI) & ~APDIGI_ENABLE_DAC_MUTE;
if (mute)
- ssm2602_write(codec, SSM2602_APDIGI,
+ snd_soc_write(codec, SSM2602_APDIGI,
mute_reg | APDIGI_ENABLE_DAC_MUTE);
else
- ssm2602_write(codec, SSM2602_APDIGI, mute_reg);
+ snd_soc_write(codec, SSM2602_APDIGI, mute_reg);
return 0;
}
}
/* set iface */
- ssm2602_write(codec, SSM2602_IFACE, iface);
+ snd_soc_write(codec, SSM2602_IFACE, iface);
return 0;
}
static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- u16 reg = ssm2602_read_reg_cache(codec, SSM2602_PWR) & 0xff7f;
+ u16 reg = snd_soc_read(codec, SSM2602_PWR) & 0xff7f;
switch (level) {
case SND_SOC_BIAS_ON:
/* vref/mid, osc on, dac unmute */
- ssm2602_write(codec, SSM2602_PWR, reg);
+ snd_soc_write(codec, SSM2602_PWR, reg);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* everything off except vref/vmid, */
- ssm2602_write(codec, SSM2602_PWR, reg | PWR_CLK_OUT_PDN);
+ snd_soc_write(codec, SSM2602_PWR, reg | PWR_CLK_OUT_PDN);
break;
case SND_SOC_BIAS_OFF:
/* everything off, dac mute, inactive */
- ssm2602_write(codec, SSM2602_ACTIVE, 0);
- ssm2602_write(codec, SSM2602_PWR, 0xffff);
+ snd_soc_write(codec, SSM2602_PWR, 0xffff);
break;
}
static struct snd_soc_dai_ops ssm2602_dai_ops = {
.startup = ssm2602_startup,
- .prepare = ssm2602_pcm_prepare,
.hw_params = ssm2602_hw_params,
.shutdown = ssm2602_shutdown,
.digital_mute = ssm2602_mute,
static int ssm2602_resume(struct snd_soc_codec *codec)
{
- int i;
- u8 data[2];
- u16 *cache = codec->reg_cache;
-
- /* Sync reg_cache with the hardware */
- for (i = 0; i < ARRAY_SIZE(ssm2602_reg); i++) {
- data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
- data[1] = cache[i] & 0x00ff;
- codec->hw_write(codec->control_data, data, 2);
- }
+ snd_soc_cache_sync(codec);
+
ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
return 0;
}
static int ssm2602_probe(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int ret, reg;
+
+ reg = snd_soc_read(codec, SSM2602_LOUT1V);
+ snd_soc_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH);
+ reg = snd_soc_read(codec, SSM2602_ROUT1V);
+ snd_soc_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH);
+
+ ret = snd_soc_add_controls(codec, ssm2602_snd_controls,
+ ARRAY_SIZE(ssm2602_snd_controls));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets,
+ ARRAY_SIZE(ssm2602_dapm_widgets));
+ if (ret)
+ return ret;
+
+ return snd_soc_dapm_add_routes(dapm, ssm2602_routes,
+ ARRAY_SIZE(ssm2602_routes));
+}
+
+static int ssm2604_probe(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets,
+ ARRAY_SIZE(ssm2604_dapm_widgets));
+ if (ret)
+ return ret;
+
+ return snd_soc_dapm_add_routes(dapm, ssm2604_routes,
+ ARRAY_SIZE(ssm2604_routes));
+}
+
+static int ssm260x_probe(struct snd_soc_codec *codec)
{
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
- int ret = 0, reg;
+ int ret, reg;
pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION);
- codec->control_data = ssm2602->control_data;
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, ssm2602->control_type);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
- ssm2602_reset(codec);
+ ret = ssm2602_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ return ret;
+ }
- /*power on device*/
- ssm2602_write(codec, SSM2602_ACTIVE, 0);
/* set the update bits */
- reg = ssm2602_read_reg_cache(codec, SSM2602_LINVOL);
- ssm2602_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH);
- reg = ssm2602_read_reg_cache(codec, SSM2602_RINVOL);
- ssm2602_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH);
- reg = ssm2602_read_reg_cache(codec, SSM2602_LOUT1V);
- ssm2602_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH);
- reg = ssm2602_read_reg_cache(codec, SSM2602_ROUT1V);
- ssm2602_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH);
+ reg = snd_soc_read(codec, SSM2602_LINVOL);
+ snd_soc_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH);
+ reg = snd_soc_read(codec, SSM2602_RINVOL);
+ snd_soc_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH);
/*select Line in as default input*/
- ssm2602_write(codec, SSM2602_APANA, APANA_SELECT_DAC |
+ snd_soc_write(codec, SSM2602_APANA, APANA_SELECT_DAC |
APANA_ENABLE_MIC_BOOST);
- ssm2602_write(codec, SSM2602_PWR, 0);
- snd_soc_add_controls(codec, ssm2602_snd_controls,
- ARRAY_SIZE(ssm2602_snd_controls));
- ssm2602_add_widgets(codec);
+ switch (ssm2602->type) {
+ case SSM2602:
+ ret = ssm2602_probe(codec);
+ break;
+ case SSM2604:
+ ret = ssm2604_probe(codec);
+ break;
+ }
return ret;
}
}
static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
- .probe = ssm2602_probe,
+ .probe = ssm260x_probe,
.remove = ssm2602_remove,
.suspend = ssm2602_suspend,
.resume = ssm2602_resume,
- .read = ssm2602_read_reg_cache,
- .write = ssm2602_write,
.set_bias_level = ssm2602_set_bias_level,
- .reg_cache_size = sizeof(ssm2602_reg),
+ .reg_cache_size = ARRAY_SIZE(ssm2602_reg),
.reg_word_size = sizeof(u16),
.reg_cache_default = ssm2602_reg,
+
+ .controls = ssm260x_snd_controls,
+ .num_controls = ARRAY_SIZE(ssm260x_snd_controls),
+ .dapm_widgets = ssm260x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets),
+ .dapm_routes = ssm260x_routes,
+ .num_dapm_routes = ARRAY_SIZE(ssm260x_routes),
};
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit ssm2602_spi_probe(struct spi_device *spi)
+{
+ struct ssm2602_priv *ssm2602;
+ int ret;
+
+ ssm2602 = kzalloc(sizeof(struct ssm2602_priv), GFP_KERNEL);
+ if (ssm2602 == NULL)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, ssm2602);
+ ssm2602->control_type = SND_SOC_SPI;
+ ssm2602->type = SSM2602;
+
+ ret = snd_soc_register_codec(&spi->dev,
+ &soc_codec_dev_ssm2602, &ssm2602_dai, 1);
+ if (ret < 0)
+ kfree(ssm2602);
+ return ret;
+}
+
+static int __devexit ssm2602_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ kfree(spi_get_drvdata(spi));
+ return 0;
+}
+
+static struct spi_driver ssm2602_spi_driver = {
+ .driver = {
+ .name = "ssm2602",
+ .owner = THIS_MODULE,
+ },
+ .probe = ssm2602_spi_probe,
+ .remove = __devexit_p(ssm2602_spi_remove),
+};
+#endif
+
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
/*
* ssm2602 2 wire address is determined by GPIO5
* low = 0x1a
* high = 0x1b
*/
-static int ssm2602_i2c_probe(struct i2c_client *i2c,
+static int __devinit ssm2602_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct ssm2602_priv *ssm2602;
return -ENOMEM;
i2c_set_clientdata(i2c, ssm2602);
- ssm2602->control_data = i2c;
ssm2602->control_type = SND_SOC_I2C;
+ ssm2602->type = id->driver_data;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_ssm2602, &ssm2602_dai, 1);
return ret;
}
-static int ssm2602_i2c_remove(struct i2c_client *client)
+static int __devexit ssm2602_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
kfree(i2c_get_clientdata(client));
}
static const struct i2c_device_id ssm2602_i2c_id[] = {
- { "ssm2602", 0 },
+ { "ssm2602", SSM2602 },
+ { "ssm2603", SSM2602 },
+ { "ssm2604", SSM2604 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
/* corgi i2c codec control layer */
static struct i2c_driver ssm2602_i2c_driver = {
.driver = {
- .name = "ssm2602-codec",
+ .name = "ssm2602",
.owner = THIS_MODULE,
},
.probe = ssm2602_i2c_probe,
- .remove = ssm2602_i2c_remove,
+ .remove = __devexit_p(ssm2602_i2c_remove),
.id_table = ssm2602_i2c_id,
};
#endif
static int __init ssm2602_modinit(void)
{
int ret = 0;
+
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&ssm2602_spi_driver);
+ if (ret)
+ return ret;
+#endif
+
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&ssm2602_i2c_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register SSM2602 I2C driver: %d\n",
- ret);
- }
+ if (ret)
+ return ret;
#endif
+
return ret;
}
module_init(ssm2602_modinit);
static void __exit ssm2602_exit(void)
{
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&ssm2602_spi_driver);
+#endif
+
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&ssm2602_i2c_driver);
#endif
}
module_exit(ssm2602_exit);
-MODULE_DESCRIPTION("ASoC ssm2602 driver");
+MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 driver");
MODULE_AUTHOR("Cliff Cai");
MODULE_LICENSE("GPL");
#define SSM2602_CACHEREGNUM 10
#define SSM2602_SYSCLK 0
-#define SSM2602_DAI 0
-
-struct ssm2602_setup_data {
- int i2c_bus;
- unsigned short i2c_address;
-};
#endif
SND_SOC_DAPM_INPUT("MICIN"),
};
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route tlv320aic23_intercon[] = {
/* Output Mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "Playback Switch", "DAC"},
return 0;
}
-static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, tlv320aic23_dapm_widgets,
- ARRAY_SIZE(tlv320aic23_dapm_widgets));
- /* set up audio path interconnects */
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
snd_soc_add_controls(codec, tlv320aic23_snd_controls,
ARRAY_SIZE(tlv320aic23_snd_controls));
- tlv320aic23_add_widgets(codec);
return 0;
}
.read = tlv320aic23_read_reg_cache,
.write = tlv320aic23_write,
.set_bias_level = tlv320aic23_set_bias_level,
+ .dapm_widgets = tlv320aic23_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
+ .dapm_routes = tlv320aic23_intercon,
+ .num_dapm_routes = ARRAY_SIZE(tlv320aic23_intercon),
};
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
/*
* ALSA SoC Texas Instruments TLV320DAC33 codec driver
*
- * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* Copyright: (C) 2009 Nokia Corporation
*
SND_SOC_DAPM_SUPPLY("Right DAC Power",
DAC33_RDAC_PWR_CTRL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Codec Power",
+ DAC33_PWR_CTRL, 4, 0, NULL, 0),
+
SND_SOC_DAPM_PRE("Pre Playback", dac33_playback_event),
SND_SOC_DAPM_POST("Post Playback", dac33_playback_event),
};
/* output */
{"LEFT_LO", NULL, "Output Left Amplifier"},
{"RIGHT_LO", NULL, "Output Right Amplifier"},
+
+ {"LEFT_LO", NULL, "Codec Power"},
+ {"RIGHT_LO", NULL, "Codec Power"},
};
static int dac33_add_widgets(struct snd_soc_codec *codec)
static int dac33_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
int ret;
switch (level) {
case SND_SOC_BIAS_ON:
- if (!dac33->substream)
- dac33_soft_power(codec, 1);
break;
case SND_SOC_BIAS_PREPARE:
break;
/* Write registers 0x08 and 0x09 (MSB, LSB) */
dac33_write16(codec, DAC33_INT_OSC_FREQ_RAT_A, oscset);
- /* calib time: 128 is a nice number ;) */
- dac33_write(codec, DAC33_CALIB_TIME, 128);
+ /* OSC calibration time */
+ dac33_write(codec, DAC33_CALIB_TIME, 96);
/* adjustment treshold & step */
dac33_write(codec, DAC33_INT_OSC_CTRL_B, DAC33_ADJTHRSHLD(2) |
MODULE_DESCRIPTION("ASoC TLV320DAC33 codec driver");
-MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
MODULE_LICENSE("GPL");
/*
* ALSA SoC Texas Instruments TLV320DAC33 codec driver
*
- * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* Copyright: (C) 2009 Nokia Corporation
*
*
* Copyright (C) Nokia Corporation
*
- * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
i2c_del_driver(&tpa6130a2_i2c_driver);
}
-MODULE_AUTHOR("Peter Ujfalusi");
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver");
MODULE_LICENSE("GPL");
*
* Copyright (C) Nokia Corporation
*
- * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
/*
* AFMGAIN volume control:
- * from 18 to 24 dB in 6 dB steps
+ * from -18 to 24 dB in 6 dB steps
*/
-static DECLARE_TLV_DB_SCALE(afm_amp_tlv, 1800, 600, 0);
+static DECLARE_TLV_DB_SCALE(afm_amp_tlv, -1800, 600, 0);
/*
* HSGAIN volume control:
/* AFM gains */
SOC_DOUBLE_TLV("Aux FM Volume",
- TWL6040_REG_LINEGAIN, 0, 4, 0xF, 0, afm_amp_tlv),
+ TWL6040_REG_LINEGAIN, 0, 3, 7, 0, afm_amp_tlv),
/* Playback gains */
SOC_TWL6040_DOUBLE_TLV("Headset Playback Volume",
.reg_cache_step = 1,
.read = uda134x_read_reg_cache,
.write = uda134x_write,
-#ifdef POWER_OFF_ON_STANDBY
.set_bias_level = uda134x_set_bias_level,
-#endif
};
static int __devinit uda134x_codec_probe(struct platform_device *pdev)
--- /dev/null
+/*
+ * Driver for the 1250-EV1 audio I/O module
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+static const struct snd_soc_dapm_widget wm1250_ev1_dapm_widgets[] = {
+SND_SOC_DAPM_ADC("ADC", "wm1250-ev1 Capture", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_DAC("DAC", "wm1250-ev1 Playback", SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_INPUT("WM1250 Input"),
+SND_SOC_DAPM_INPUT("WM1250 Output"),
+};
+
+static const struct snd_soc_dapm_route wm1250_ev1_dapm_routes[] = {
+ { "ADC", NULL, "WM1250 Input" },
+ { "WM1250 Output", NULL, "DAC" },
+};
+
+static struct snd_soc_dai_driver wm1250_ev1_dai = {
+ .name = "wm1250-ev1",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm1250_ev1 = {
+ .dapm_widgets = wm1250_ev1_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm1250_ev1_dapm_widgets),
+ .dapm_routes = wm1250_ev1_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm1250_ev1_dapm_routes),
+};
+
+static int __devinit wm1250_ev1_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm1250_ev1,
+ &wm1250_ev1_dai, 1);
+}
+
+static int __devexit wm1250_ev1_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id wm1250_ev1_i2c_id[] = {
+ { "wm1250-ev1", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm1250_ev1_i2c_id);
+
+static struct i2c_driver wm1250_ev1_i2c_driver = {
+ .driver = {
+ .name = "wm1250-ev1",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm1250_ev1_probe,
+ .remove = __devexit_p(wm1250_ev1_remove),
+ .id_table = wm1250_ev1_i2c_id,
+};
+
+static int __init wm1250_ev1_modinit(void)
+{
+ int ret = 0;
+
+ ret = i2c_add_driver(&wm1250_ev1_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM1250-EV1 I2C driver: %d\n", ret);
+
+ return ret;
+}
+module_init(wm1250_ev1_modinit);
+
+static void __exit wm1250_ev1_exit(void)
+{
+ i2c_del_driver(&wm1250_ev1_i2c_driver);
+}
+module_exit(wm1250_ev1_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM1250-EV1 audio I/O module driver");
+MODULE_LICENSE("GPL");
SND_SOC_DAPM_OUTPUT("RHPOUT"),
};
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route wm8711_intercon[] = {
/* output mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "HiFi Playback Switch", "DAC"},
{"LOUT", NULL, "Output Mixer"},
};
-static int wm8711_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm8711_dapm_widgets,
- ARRAY_SIZE(wm8711_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
struct _coeff_div {
u32 mclk;
u32 rate;
snd_soc_add_controls(codec, wm8711_snd_controls,
ARRAY_SIZE(wm8711_snd_controls));
- wm8711_add_widgets(codec);
return ret;
.reg_cache_size = ARRAY_SIZE(wm8711_reg),
.reg_word_size = sizeof(u16),
.reg_cache_default = wm8711_reg,
+ .dapm_widgets = wm8711_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8711_dapm_widgets),
+ .dapm_routes = wm8711_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8711_intercon),
};
#if defined(CONFIG_SPI_MASTER)
SND_SOC_DAPM_OUTPUT("VOUTR"),
};
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route wm8728_intercon[] = {
{"VOUTL", NULL, "DAC"},
{"VOUTR", NULL, "DAC"},
};
-static int wm8728_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm8728_dapm_widgets,
- ARRAY_SIZE(wm8728_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
static int wm8728_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
snd_soc_add_controls(codec, wm8728_snd_controls,
ARRAY_SIZE(wm8728_snd_controls));
- wm8728_add_widgets(codec);
return ret;
}
.reg_cache_size = ARRAY_SIZE(wm8728_reg_defaults),
.reg_word_size = sizeof(u16),
.reg_cache_default = wm8728_reg_defaults,
+ .dapm_widgets = wm8728_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8728_dapm_widgets),
+ .dapm_routes = wm8728_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8728_intercon),
};
#if defined(CONFIG_SPI_MASTER)
return wm8731->sysclk_type == WM8731_SYSCLK_MCLK;
}
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route wm8731_intercon[] = {
{"DAC", NULL, "OSC", wm8731_check_osc},
{"ADC", NULL, "OSC", wm8731_check_osc},
{"Mic Bias", NULL, "MICIN"},
};
-static int wm8731_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm8731_dapm_widgets,
- ARRAY_SIZE(wm8731_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
struct _coeff_div {
u32 mclk;
u32 rate;
snd_soc_add_controls(codec, wm8731_snd_controls,
ARRAY_SIZE(wm8731_snd_controls));
- wm8731_add_widgets(codec);
/* Regulators will have been enabled by bias management */
regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
.reg_cache_size = ARRAY_SIZE(wm8731_reg),
.reg_word_size = sizeof(u16),
.reg_cache_default = wm8731_reg,
+ .dapm_widgets = wm8731_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
+ .dapm_routes = wm8731_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8731_intercon),
};
#if defined(CONFIG_SPI_MASTER)
static struct spi_driver wm8731_spi_driver = {
.driver = {
- .name = "wm8731-codec",
+ .name = "wm8731",
.owner = THIS_MODULE,
},
.probe = wm8731_spi_probe,
static struct i2c_driver wm8731_i2c_driver = {
.driver = {
- .name = "wm8731-codec",
+ .name = "wm8731",
.owner = THIS_MODULE,
},
.probe = wm8731_i2c_probe,
static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec;
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
u16 reg;
static const struct soc_enum rsidetone_enum =
SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 0, 3, sidetone_text);
+static const char *adcinput_text[] = {
+ "ADC", "DMIC"
+};
+
+static const struct soc_enum adcinput_enum =
+ SOC_ENUM_SINGLE(WM8903_CLOCK_RATE_TEST_4, 9, 2, adcinput_text);
+
static const char *aif_text[] = {
"Left", "Right"
};
SOC_SINGLE_TLV("DRC Startup Volume", WM8903_DRC_0, 6, 18, 0, drc_tlv_startup),
SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8903_ADC_DIGITAL_VOLUME_LEFT,
- WM8903_ADC_DIGITAL_VOLUME_RIGHT, 1, 96, 0, digital_tlv),
+ WM8903_ADC_DIGITAL_VOLUME_RIGHT, 1, 120, 0, digital_tlv),
SOC_ENUM("ADC Companding Mode", adc_companding),
SOC_SINGLE("ADC Companding Switch", WM8903_AUDIO_INTERFACE_0, 3, 1, 0),
static const struct snd_kcontrol_new rsidetone_mux =
SOC_DAPM_ENUM("DACR Sidetone Mux", rsidetone_enum);
+static const struct snd_kcontrol_new adcinput_mux =
+ SOC_DAPM_ENUM("ADC Input", adcinput_enum);
+
static const struct snd_kcontrol_new lcapture_mux =
SOC_DAPM_ENUM("Left Capture Mux", lcapture_enum);
SND_SOC_DAPM_INPUT("IN2R"),
SND_SOC_DAPM_INPUT("IN3L"),
SND_SOC_DAPM_INPUT("IN3R"),
+SND_SOC_DAPM_INPUT("DMICDAT"),
SND_SOC_DAPM_OUTPUT("HPOUTL"),
SND_SOC_DAPM_OUTPUT("HPOUTR"),
SND_SOC_DAPM_PGA("Left Input PGA", WM8903_POWER_MANAGEMENT_0, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("Right Input PGA", WM8903_POWER_MANAGEMENT_0, 0, 0, NULL, 0),
+SND_SOC_DAPM_MUX("Left ADC Input", SND_SOC_NOPM, 0, 0, &adcinput_mux),
+SND_SOC_DAPM_MUX("Right ADC Input", SND_SOC_NOPM, 0, 0, &adcinput_mux),
+
SND_SOC_DAPM_ADC("ADCL", NULL, WM8903_POWER_MANAGEMENT_6, 1, 0),
SND_SOC_DAPM_ADC("ADCR", NULL, WM8903_POWER_MANAGEMENT_6, 0, 0),
SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8903_CLOCK_RATES_2, 2, 0, NULL, 0),
};
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route wm8903_intercon[] = {
{ "CLK_DSP", NULL, "CLK_SYS" },
{ "Mic Bias", NULL, "CLK_SYS" },
{ "Left Input PGA", NULL, "Left Input Mode Mux" },
{ "Right Input PGA", NULL, "Right Input Mode Mux" },
+ { "Left ADC Input", "ADC", "Left Input PGA" },
+ { "Left ADC Input", "DMIC", "DMICDAT" },
+ { "Right ADC Input", "ADC", "Right Input PGA" },
+ { "Right ADC Input", "DMIC", "DMICDAT" },
+
{ "Left Capture Mux", "Left", "ADCL" },
{ "Left Capture Mux", "Right", "ADCR" },
{ "AIFTXL", NULL, "Left Capture Mux" },
{ "AIFTXR", NULL, "Right Capture Mux" },
- { "ADCL", NULL, "Left Input PGA" },
+ { "ADCL", NULL, "Left ADC Input" },
{ "ADCL", NULL, "CLK_DSP" },
- { "ADCR", NULL, "Right Input PGA" },
+ { "ADCR", NULL, "Right ADC Input" },
{ "ADCR", NULL, "CLK_DSP" },
{ "Left Playback Mux", "Left", "AIFRXL" },
{ "Right Line Output PGA", NULL, "Charge Pump" },
};
-static int wm8903_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm8903_dapm_widgets,
- ARRAY_SIZE(wm8903_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
- return 0;
-}
-
static int wm8903_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
snd_soc_add_controls(codec, wm8903_snd_controls,
ARRAY_SIZE(wm8903_snd_controls));
- wm8903_add_widgets(codec);
wm8903_init_gpio(codec);
.reg_cache_default = wm8903_reg_defaults,
.volatile_register = wm8903_volatile_register,
.seq_notifier = wm8903_seq_notifier,
+ .dapm_widgets = wm8903_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8903_dapm_widgets),
+ .dapm_routes = wm8903_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8903_intercon),
};
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
--- /dev/null
+/*
+ * wm8915.c - WM8915 audio codec interface
+ *
+ * Copyright 2011 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/gcd.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <trace/events/asoc.h>
+
+#include <sound/wm8915.h>
+#include "wm8915.h"
+
+#define WM8915_AIFS 2
+
+#define HPOUT1L 1
+#define HPOUT1R 2
+#define HPOUT2L 4
+#define HPOUT2R 8
+
+#define WM8915_NUM_SUPPLIES 6
+static const char *wm8915_supply_names[WM8915_NUM_SUPPLIES] = {
+ "DCVDD",
+ "DBVDD",
+ "AVDD1",
+ "AVDD2",
+ "CPVDD",
+ "MICVDD",
+};
+
+struct wm8915_priv {
+ struct snd_soc_codec *codec;
+
+ int ldo1ena;
+
+ int sysclk;
+
+ int fll_src;
+ int fll_fref;
+ int fll_fout;
+
+ struct completion fll_lock;
+
+ u16 dcs_pending;
+ struct completion dcs_done;
+
+ u16 hpout_ena;
+ u16 hpout_pending;
+
+ struct regulator_bulk_data supplies[WM8915_NUM_SUPPLIES];
+ struct notifier_block disable_nb[WM8915_NUM_SUPPLIES];
+
+ struct wm8915_pdata pdata;
+
+ int rx_rate[WM8915_AIFS];
+
+ /* Platform dependant ReTune mobile configuration */
+ int num_retune_mobile_texts;
+ const char **retune_mobile_texts;
+ int retune_mobile_cfg[2];
+ struct soc_enum retune_mobile_enum;
+
+ struct snd_soc_jack *jack;
+ bool detecting;
+ bool jack_mic;
+ wm8915_polarity_fn polarity_cb;
+
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gpio_chip;
+#endif
+};
+
+/* We can't use the same notifier block for more than one supply and
+ * there's no way I can see to get from a callback to the caller
+ * except container_of().
+ */
+#define WM8915_REGULATOR_EVENT(n) \
+static int wm8915_regulator_event_##n(struct notifier_block *nb, \
+ unsigned long event, void *data) \
+{ \
+ struct wm8915_priv *wm8915 = container_of(nb, struct wm8915_priv, \
+ disable_nb[n]); \
+ if (event & REGULATOR_EVENT_DISABLE) { \
+ wm8915->codec->cache_sync = 1; \
+ } \
+ return 0; \
+}
+
+WM8915_REGULATOR_EVENT(0)
+WM8915_REGULATOR_EVENT(1)
+WM8915_REGULATOR_EVENT(2)
+WM8915_REGULATOR_EVENT(3)
+WM8915_REGULATOR_EVENT(4)
+WM8915_REGULATOR_EVENT(5)
+
+static const u16 wm8915_reg[WM8915_MAX_REGISTER] = {
+ [WM8915_SOFTWARE_RESET] = 0x8915,
+ [WM8915_POWER_MANAGEMENT_7] = 0x10,
+ [WM8915_DAC1_HPOUT1_VOLUME] = 0x88,
+ [WM8915_DAC2_HPOUT2_VOLUME] = 0x88,
+ [WM8915_DAC1_LEFT_VOLUME] = 0x2c0,
+ [WM8915_DAC1_RIGHT_VOLUME] = 0x2c0,
+ [WM8915_DAC2_LEFT_VOLUME] = 0x2c0,
+ [WM8915_DAC2_RIGHT_VOLUME] = 0x2c0,
+ [WM8915_OUTPUT1_LEFT_VOLUME] = 0x80,
+ [WM8915_OUTPUT1_RIGHT_VOLUME] = 0x80,
+ [WM8915_OUTPUT2_LEFT_VOLUME] = 0x80,
+ [WM8915_OUTPUT2_RIGHT_VOLUME] = 0x80,
+ [WM8915_MICBIAS_1] = 0x39,
+ [WM8915_MICBIAS_2] = 0x39,
+ [WM8915_LDO_1] = 0x3,
+ [WM8915_LDO_2] = 0x13,
+ [WM8915_ACCESSORY_DETECT_MODE_1] = 0x4,
+ [WM8915_HEADPHONE_DETECT_1] = 0x20,
+ [WM8915_MIC_DETECT_1] = 0x7600,
+ [WM8915_MIC_DETECT_2] = 0xbf,
+ [WM8915_CHARGE_PUMP_1] = 0x1f25,
+ [WM8915_CHARGE_PUMP_2] = 0xab19,
+ [WM8915_DC_SERVO_5] = 0x2a2a,
+ [WM8915_CONTROL_INTERFACE_1] = 0x8004,
+ [WM8915_CLOCKING_1] = 0x10,
+ [WM8915_AIF_RATE] = 0x83,
+ [WM8915_FLL_CONTROL_4] = 0x5dc0,
+ [WM8915_FLL_CONTROL_5] = 0xc84,
+ [WM8915_FLL_EFS_2] = 0x2,
+ [WM8915_AIF1_TX_LRCLK_1] = 0x80,
+ [WM8915_AIF1_TX_LRCLK_2] = 0x8,
+ [WM8915_AIF1_RX_LRCLK_1] = 0x80,
+ [WM8915_AIF1TX_DATA_CONFIGURATION_1] = 0x1818,
+ [WM8915_AIF1RX_DATA_CONFIGURATION] = 0x1818,
+ [WM8915_AIF1TX_TEST] = 0x7,
+ [WM8915_AIF2_TX_LRCLK_1] = 0x80,
+ [WM8915_AIF2_TX_LRCLK_2] = 0x8,
+ [WM8915_AIF2_RX_LRCLK_1] = 0x80,
+ [WM8915_AIF2TX_DATA_CONFIGURATION_1] = 0x1818,
+ [WM8915_AIF2RX_DATA_CONFIGURATION] = 0x1818,
+ [WM8915_AIF2TX_TEST] = 0x1,
+ [WM8915_DSP1_TX_LEFT_VOLUME] = 0xc0,
+ [WM8915_DSP1_TX_RIGHT_VOLUME] = 0xc0,
+ [WM8915_DSP1_RX_LEFT_VOLUME] = 0xc0,
+ [WM8915_DSP1_RX_RIGHT_VOLUME] = 0xc0,
+ [WM8915_DSP1_TX_FILTERS] = 0x2000,
+ [WM8915_DSP1_RX_FILTERS_1] = 0x200,
+ [WM8915_DSP1_RX_FILTERS_2] = 0x10,
+ [WM8915_DSP1_DRC_1] = 0x98,
+ [WM8915_DSP1_DRC_2] = 0x845,
+ [WM8915_DSP1_RX_EQ_GAINS_1] = 0x6318,
+ [WM8915_DSP1_RX_EQ_GAINS_2] = 0x6300,
+ [WM8915_DSP1_RX_EQ_BAND_1_A] = 0xfca,
+ [WM8915_DSP1_RX_EQ_BAND_1_B] = 0x400,
+ [WM8915_DSP1_RX_EQ_BAND_1_PG] = 0xd8,
+ [WM8915_DSP1_RX_EQ_BAND_2_A] = 0x1eb5,
+ [WM8915_DSP1_RX_EQ_BAND_2_B] = 0xf145,
+ [WM8915_DSP1_RX_EQ_BAND_2_C] = 0xb75,
+ [WM8915_DSP1_RX_EQ_BAND_2_PG] = 0x1c5,
+ [WM8915_DSP1_RX_EQ_BAND_3_A] = 0x1c58,
+ [WM8915_DSP1_RX_EQ_BAND_3_B] = 0xf373,
+ [WM8915_DSP1_RX_EQ_BAND_3_C] = 0xa54,
+ [WM8915_DSP1_RX_EQ_BAND_3_PG] = 0x558,
+ [WM8915_DSP1_RX_EQ_BAND_4_A] = 0x168e,
+ [WM8915_DSP1_RX_EQ_BAND_4_B] = 0xf829,
+ [WM8915_DSP1_RX_EQ_BAND_4_C] = 0x7ad,
+ [WM8915_DSP1_RX_EQ_BAND_4_PG] = 0x1103,
+ [WM8915_DSP1_RX_EQ_BAND_5_A] = 0x564,
+ [WM8915_DSP1_RX_EQ_BAND_5_B] = 0x559,
+ [WM8915_DSP1_RX_EQ_BAND_5_PG] = 0x4000,
+ [WM8915_DSP2_TX_LEFT_VOLUME] = 0xc0,
+ [WM8915_DSP2_TX_RIGHT_VOLUME] = 0xc0,
+ [WM8915_DSP2_RX_LEFT_VOLUME] = 0xc0,
+ [WM8915_DSP2_RX_RIGHT_VOLUME] = 0xc0,
+ [WM8915_DSP2_TX_FILTERS] = 0x2000,
+ [WM8915_DSP2_RX_FILTERS_1] = 0x200,
+ [WM8915_DSP2_RX_FILTERS_2] = 0x10,
+ [WM8915_DSP2_DRC_1] = 0x98,
+ [WM8915_DSP2_DRC_2] = 0x845,
+ [WM8915_DSP2_RX_EQ_GAINS_1] = 0x6318,
+ [WM8915_DSP2_RX_EQ_GAINS_2] = 0x6300,
+ [WM8915_DSP2_RX_EQ_BAND_1_A] = 0xfca,
+ [WM8915_DSP2_RX_EQ_BAND_1_B] = 0x400,
+ [WM8915_DSP2_RX_EQ_BAND_1_PG] = 0xd8,
+ [WM8915_DSP2_RX_EQ_BAND_2_A] = 0x1eb5,
+ [WM8915_DSP2_RX_EQ_BAND_2_B] = 0xf145,
+ [WM8915_DSP2_RX_EQ_BAND_2_C] = 0xb75,
+ [WM8915_DSP2_RX_EQ_BAND_2_PG] = 0x1c5,
+ [WM8915_DSP2_RX_EQ_BAND_3_A] = 0x1c58,
+ [WM8915_DSP2_RX_EQ_BAND_3_B] = 0xf373,
+ [WM8915_DSP2_RX_EQ_BAND_3_C] = 0xa54,
+ [WM8915_DSP2_RX_EQ_BAND_3_PG] = 0x558,
+ [WM8915_DSP2_RX_EQ_BAND_4_A] = 0x168e,
+ [WM8915_DSP2_RX_EQ_BAND_4_B] = 0xf829,
+ [WM8915_DSP2_RX_EQ_BAND_4_C] = 0x7ad,
+ [WM8915_DSP2_RX_EQ_BAND_4_PG] = 0x1103,
+ [WM8915_DSP2_RX_EQ_BAND_5_A] = 0x564,
+ [WM8915_DSP2_RX_EQ_BAND_5_B] = 0x559,
+ [WM8915_DSP2_RX_EQ_BAND_5_PG] = 0x4000,
+ [WM8915_OVERSAMPLING] = 0xd,
+ [WM8915_SIDETONE] = 0x1040,
+ [WM8915_GPIO_1] = 0xa101,
+ [WM8915_GPIO_2] = 0xa101,
+ [WM8915_GPIO_3] = 0xa101,
+ [WM8915_GPIO_4] = 0xa101,
+ [WM8915_GPIO_5] = 0xa101,
+ [WM8915_PULL_CONTROL_2] = 0x140,
+ [WM8915_INTERRUPT_STATUS_1_MASK] = 0x1f,
+ [WM8915_INTERRUPT_STATUS_2_MASK] = 0x1ecf,
+ [WM8915_RIGHT_PDM_SPEAKER] = 0x1,
+ [WM8915_PDM_SPEAKER_MUTE_SEQUENCE] = 0x69,
+ [WM8915_PDM_SPEAKER_VOLUME] = 0x66,
+ [WM8915_WRITE_SEQUENCER_0] = 0x1,
+ [WM8915_WRITE_SEQUENCER_1] = 0x1,
+ [WM8915_WRITE_SEQUENCER_3] = 0x6,
+ [WM8915_WRITE_SEQUENCER_4] = 0x40,
+ [WM8915_WRITE_SEQUENCER_5] = 0x1,
+ [WM8915_WRITE_SEQUENCER_6] = 0xf,
+ [WM8915_WRITE_SEQUENCER_7] = 0x6,
+ [WM8915_WRITE_SEQUENCER_8] = 0x1,
+ [WM8915_WRITE_SEQUENCER_9] = 0x3,
+ [WM8915_WRITE_SEQUENCER_10] = 0x104,
+ [WM8915_WRITE_SEQUENCER_12] = 0x60,
+ [WM8915_WRITE_SEQUENCER_13] = 0x11,
+ [WM8915_WRITE_SEQUENCER_14] = 0x401,
+ [WM8915_WRITE_SEQUENCER_16] = 0x50,
+ [WM8915_WRITE_SEQUENCER_17] = 0x3,
+ [WM8915_WRITE_SEQUENCER_18] = 0x100,
+ [WM8915_WRITE_SEQUENCER_20] = 0x51,
+ [WM8915_WRITE_SEQUENCER_21] = 0x3,
+ [WM8915_WRITE_SEQUENCER_22] = 0x104,
+ [WM8915_WRITE_SEQUENCER_23] = 0xa,
+ [WM8915_WRITE_SEQUENCER_24] = 0x60,
+ [WM8915_WRITE_SEQUENCER_25] = 0x3b,
+ [WM8915_WRITE_SEQUENCER_26] = 0x502,
+ [WM8915_WRITE_SEQUENCER_27] = 0x100,
+ [WM8915_WRITE_SEQUENCER_28] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_32] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_36] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_40] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_44] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_48] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_52] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_56] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_60] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_64] = 0x1,
+ [WM8915_WRITE_SEQUENCER_65] = 0x1,
+ [WM8915_WRITE_SEQUENCER_67] = 0x6,
+ [WM8915_WRITE_SEQUENCER_68] = 0x40,
+ [WM8915_WRITE_SEQUENCER_69] = 0x1,
+ [WM8915_WRITE_SEQUENCER_70] = 0xf,
+ [WM8915_WRITE_SEQUENCER_71] = 0x6,
+ [WM8915_WRITE_SEQUENCER_72] = 0x1,
+ [WM8915_WRITE_SEQUENCER_73] = 0x3,
+ [WM8915_WRITE_SEQUENCER_74] = 0x104,
+ [WM8915_WRITE_SEQUENCER_76] = 0x60,
+ [WM8915_WRITE_SEQUENCER_77] = 0x11,
+ [WM8915_WRITE_SEQUENCER_78] = 0x401,
+ [WM8915_WRITE_SEQUENCER_80] = 0x50,
+ [WM8915_WRITE_SEQUENCER_81] = 0x3,
+ [WM8915_WRITE_SEQUENCER_82] = 0x100,
+ [WM8915_WRITE_SEQUENCER_84] = 0x60,
+ [WM8915_WRITE_SEQUENCER_85] = 0x3b,
+ [WM8915_WRITE_SEQUENCER_86] = 0x502,
+ [WM8915_WRITE_SEQUENCER_87] = 0x100,
+ [WM8915_WRITE_SEQUENCER_88] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_92] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_96] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_100] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_104] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_108] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_112] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_116] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_120] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_124] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_128] = 0x1,
+ [WM8915_WRITE_SEQUENCER_129] = 0x1,
+ [WM8915_WRITE_SEQUENCER_131] = 0x6,
+ [WM8915_WRITE_SEQUENCER_132] = 0x40,
+ [WM8915_WRITE_SEQUENCER_133] = 0x1,
+ [WM8915_WRITE_SEQUENCER_134] = 0xf,
+ [WM8915_WRITE_SEQUENCER_135] = 0x6,
+ [WM8915_WRITE_SEQUENCER_136] = 0x1,
+ [WM8915_WRITE_SEQUENCER_137] = 0x3,
+ [WM8915_WRITE_SEQUENCER_138] = 0x106,
+ [WM8915_WRITE_SEQUENCER_140] = 0x61,
+ [WM8915_WRITE_SEQUENCER_141] = 0x11,
+ [WM8915_WRITE_SEQUENCER_142] = 0x401,
+ [WM8915_WRITE_SEQUENCER_144] = 0x50,
+ [WM8915_WRITE_SEQUENCER_145] = 0x3,
+ [WM8915_WRITE_SEQUENCER_146] = 0x102,
+ [WM8915_WRITE_SEQUENCER_148] = 0x51,
+ [WM8915_WRITE_SEQUENCER_149] = 0x3,
+ [WM8915_WRITE_SEQUENCER_150] = 0x106,
+ [WM8915_WRITE_SEQUENCER_151] = 0xa,
+ [WM8915_WRITE_SEQUENCER_152] = 0x61,
+ [WM8915_WRITE_SEQUENCER_153] = 0x3b,
+ [WM8915_WRITE_SEQUENCER_154] = 0x502,
+ [WM8915_WRITE_SEQUENCER_155] = 0x100,
+ [WM8915_WRITE_SEQUENCER_156] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_160] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_164] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_168] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_172] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_176] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_180] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_184] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_188] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_192] = 0x1,
+ [WM8915_WRITE_SEQUENCER_193] = 0x1,
+ [WM8915_WRITE_SEQUENCER_195] = 0x6,
+ [WM8915_WRITE_SEQUENCER_196] = 0x40,
+ [WM8915_WRITE_SEQUENCER_197] = 0x1,
+ [WM8915_WRITE_SEQUENCER_198] = 0xf,
+ [WM8915_WRITE_SEQUENCER_199] = 0x6,
+ [WM8915_WRITE_SEQUENCER_200] = 0x1,
+ [WM8915_WRITE_SEQUENCER_201] = 0x3,
+ [WM8915_WRITE_SEQUENCER_202] = 0x106,
+ [WM8915_WRITE_SEQUENCER_204] = 0x61,
+ [WM8915_WRITE_SEQUENCER_205] = 0x11,
+ [WM8915_WRITE_SEQUENCER_206] = 0x401,
+ [WM8915_WRITE_SEQUENCER_208] = 0x50,
+ [WM8915_WRITE_SEQUENCER_209] = 0x3,
+ [WM8915_WRITE_SEQUENCER_210] = 0x102,
+ [WM8915_WRITE_SEQUENCER_212] = 0x61,
+ [WM8915_WRITE_SEQUENCER_213] = 0x3b,
+ [WM8915_WRITE_SEQUENCER_214] = 0x502,
+ [WM8915_WRITE_SEQUENCER_215] = 0x100,
+ [WM8915_WRITE_SEQUENCER_216] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_220] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_224] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_228] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_232] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_236] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_240] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_244] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_248] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_252] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_256] = 0x60,
+ [WM8915_WRITE_SEQUENCER_258] = 0x601,
+ [WM8915_WRITE_SEQUENCER_260] = 0x50,
+ [WM8915_WRITE_SEQUENCER_262] = 0x100,
+ [WM8915_WRITE_SEQUENCER_264] = 0x1,
+ [WM8915_WRITE_SEQUENCER_266] = 0x104,
+ [WM8915_WRITE_SEQUENCER_267] = 0x100,
+ [WM8915_WRITE_SEQUENCER_268] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_272] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_276] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_280] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_284] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_288] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_292] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_296] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_300] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_304] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_308] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_312] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_316] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_320] = 0x61,
+ [WM8915_WRITE_SEQUENCER_322] = 0x601,
+ [WM8915_WRITE_SEQUENCER_324] = 0x50,
+ [WM8915_WRITE_SEQUENCER_326] = 0x102,
+ [WM8915_WRITE_SEQUENCER_328] = 0x1,
+ [WM8915_WRITE_SEQUENCER_330] = 0x106,
+ [WM8915_WRITE_SEQUENCER_331] = 0x100,
+ [WM8915_WRITE_SEQUENCER_332] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_336] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_340] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_344] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_348] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_352] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_356] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_360] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_364] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_368] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_372] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_376] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_380] = 0x2fff,
+ [WM8915_WRITE_SEQUENCER_384] = 0x60,
+ [WM8915_WRITE_SEQUENCER_386] = 0x601,
+ [WM8915_WRITE_SEQUENCER_388] = 0x61,
+ [WM8915_WRITE_SEQUENCER_390] = 0x601,
+ [WM8915_WRITE_SEQUENCER_392] = 0x50,
+ [WM8915_WRITE_SEQUENCER_394] = 0x300,
+ [WM8915_WRITE_SEQUENCER_396] = 0x1,
+ [WM8915_WRITE_SEQUENCER_398] = 0x304,
+ [WM8915_WRITE_SEQUENCER_400] = 0x40,
+ [WM8915_WRITE_SEQUENCER_402] = 0xf,
+ [WM8915_WRITE_SEQUENCER_404] = 0x1,
+ [WM8915_WRITE_SEQUENCER_407] = 0x100,
+};
+
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 150, 0);
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(out_digital_tlv, -1200, 150, 0);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -900, 75, 0);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -900, 150, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+
+static const char *sidetone_hpf_text[] = {
+ "2.9kHz", "1.5kHz", "735Hz", "403Hz", "196Hz", "98Hz", "49Hz"
+};
+
+static const struct soc_enum sidetone_hpf =
+ SOC_ENUM_SINGLE(WM8915_SIDETONE, 7, 6, sidetone_hpf_text);
+
+static const char *hpf_mode_text[] = {
+ "HiFi", "Custom", "Voice"
+};
+
+static const struct soc_enum dsp1tx_hpf_mode =
+ SOC_ENUM_SINGLE(WM8915_DSP1_TX_FILTERS, 3, 3, hpf_mode_text);
+
+static const struct soc_enum dsp2tx_hpf_mode =
+ SOC_ENUM_SINGLE(WM8915_DSP2_TX_FILTERS, 3, 3, hpf_mode_text);
+
+static const char *hpf_cutoff_text[] = {
+ "50Hz", "75Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static const struct soc_enum dsp1tx_hpf_cutoff =
+ SOC_ENUM_SINGLE(WM8915_DSP1_TX_FILTERS, 0, 7, hpf_cutoff_text);
+
+static const struct soc_enum dsp2tx_hpf_cutoff =
+ SOC_ENUM_SINGLE(WM8915_DSP2_TX_FILTERS, 0, 7, hpf_cutoff_text);
+
+static void wm8915_set_retune_mobile(struct snd_soc_codec *codec, int block)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct wm8915_pdata *pdata = &wm8915->pdata;
+ int base, best, best_val, save, i, cfg, iface;
+
+ if (!wm8915->num_retune_mobile_texts)
+ return;
+
+ switch (block) {
+ case 0:
+ base = WM8915_DSP1_RX_EQ_GAINS_1;
+ if (snd_soc_read(codec, WM8915_POWER_MANAGEMENT_8) &
+ WM8915_DSP1RX_SRC)
+ iface = 1;
+ else
+ iface = 0;
+ break;
+ case 1:
+ base = WM8915_DSP1_RX_EQ_GAINS_2;
+ if (snd_soc_read(codec, WM8915_POWER_MANAGEMENT_8) &
+ WM8915_DSP2RX_SRC)
+ iface = 1;
+ else
+ iface = 0;
+ break;
+ default:
+ return;
+ }
+
+ /* Find the version of the currently selected configuration
+ * with the nearest sample rate. */
+ cfg = wm8915->retune_mobile_cfg[block];
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+ if (strcmp(pdata->retune_mobile_cfgs[i].name,
+ wm8915->retune_mobile_texts[cfg]) == 0 &&
+ abs(pdata->retune_mobile_cfgs[i].rate
+ - wm8915->rx_rate[iface]) < best_val) {
+ best = i;
+ best_val = abs(pdata->retune_mobile_cfgs[i].rate
+ - wm8915->rx_rate[iface]);
+ }
+ }
+
+ dev_dbg(codec->dev, "ReTune Mobile %d %s/%dHz for %dHz sample rate\n",
+ block,
+ pdata->retune_mobile_cfgs[best].name,
+ pdata->retune_mobile_cfgs[best].rate,
+ wm8915->rx_rate[iface]);
+
+ /* The EQ will be disabled while reconfiguring it, remember the
+ * current configuration.
+ */
+ save = snd_soc_read(codec, base);
+ save &= WM8915_DSP1RX_EQ_ENA;
+
+ for (i = 0; i < ARRAY_SIZE(pdata->retune_mobile_cfgs[best].regs); i++)
+ snd_soc_update_bits(codec, base + i, 0xffff,
+ pdata->retune_mobile_cfgs[best].regs[i]);
+
+ snd_soc_update_bits(codec, base, WM8915_DSP1RX_EQ_ENA, save);
+}
+
+/* Icky as hell but saves code duplication */
+static int wm8915_get_retune_mobile_block(const char *name)
+{
+ if (strcmp(name, "DSP1 EQ Mode") == 0)
+ return 0;
+ if (strcmp(name, "DSP2 EQ Mode") == 0)
+ return 1;
+ return -EINVAL;
+}
+
+static int wm8915_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct wm8915_pdata *pdata = &wm8915->pdata;
+ int block = wm8915_get_retune_mobile_block(kcontrol->id.name);
+ int value = ucontrol->value.integer.value[0];
+
+ if (block < 0)
+ return block;
+
+ if (value >= pdata->num_retune_mobile_cfgs)
+ return -EINVAL;
+
+ wm8915->retune_mobile_cfg[block] = value;
+
+ wm8915_set_retune_mobile(codec, block);
+
+ return 0;
+}
+
+static int wm8915_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int block = wm8915_get_retune_mobile_block(kcontrol->id.name);
+
+ ucontrol->value.enumerated.item[0] = wm8915->retune_mobile_cfg[block];
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new wm8915_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Capture Volume", WM8915_LEFT_LINE_INPUT_VOLUME,
+ WM8915_RIGHT_LINE_INPUT_VOLUME, 0, 31, 0, inpga_tlv),
+SOC_DOUBLE_R("Capture ZC Switch", WM8915_LEFT_LINE_INPUT_VOLUME,
+ WM8915_RIGHT_LINE_INPUT_VOLUME, 5, 1, 0),
+
+SOC_DOUBLE_TLV("DAC1 Sidetone Volume", WM8915_DAC1_MIXER_VOLUMES,
+ 0, 5, 24, 0, sidetone_tlv),
+SOC_DOUBLE_TLV("DAC2 Sidetone Volume", WM8915_DAC2_MIXER_VOLUMES,
+ 0, 5, 24, 0, sidetone_tlv),
+SOC_SINGLE("Sidetone LPF Switch", WM8915_SIDETONE, 12, 1, 0),
+SOC_ENUM("Sidetone HPF Cut-off", sidetone_hpf),
+SOC_SINGLE("Sidetone HPF Switch", WM8915_SIDETONE, 6, 1, 0),
+
+SOC_DOUBLE_R_TLV("DSP1 Capture Volume", WM8915_DSP1_TX_LEFT_VOLUME,
+ WM8915_DSP1_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("DSP2 Capture Volume", WM8915_DSP2_TX_LEFT_VOLUME,
+ WM8915_DSP2_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+
+SOC_SINGLE("DSP1 Capture Notch Filter Switch", WM8915_DSP1_TX_FILTERS,
+ 13, 1, 0),
+SOC_DOUBLE("DSP1 Capture HPF Switch", WM8915_DSP1_TX_FILTERS, 12, 11, 1, 0),
+SOC_ENUM("DSP1 Capture HPF Mode", dsp1tx_hpf_mode),
+SOC_ENUM("DSP1 Capture HPF Cutoff", dsp1tx_hpf_cutoff),
+
+SOC_SINGLE("DSP2 Capture Notch Filter Switch", WM8915_DSP2_TX_FILTERS,
+ 13, 1, 0),
+SOC_DOUBLE("DSP2 Capture HPF Switch", WM8915_DSP2_TX_FILTERS, 12, 11, 1, 0),
+SOC_ENUM("DSP2 Capture HPF Mode", dsp2tx_hpf_mode),
+SOC_ENUM("DSP2 Capture HPF Cutoff", dsp2tx_hpf_cutoff),
+
+SOC_DOUBLE_R_TLV("DSP1 Playback Volume", WM8915_DSP1_RX_LEFT_VOLUME,
+ WM8915_DSP1_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_SINGLE("DSP1 Playback Switch", WM8915_DSP1_RX_FILTERS_1, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DSP2 Playback Volume", WM8915_DSP2_RX_LEFT_VOLUME,
+ WM8915_DSP2_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_SINGLE("DSP2 Playback Switch", WM8915_DSP2_RX_FILTERS_1, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DAC1 Volume", WM8915_DAC1_LEFT_VOLUME,
+ WM8915_DAC1_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_DOUBLE_R("DAC1 Switch", WM8915_DAC1_LEFT_VOLUME,
+ WM8915_DAC1_RIGHT_VOLUME, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DAC2 Volume", WM8915_DAC2_LEFT_VOLUME,
+ WM8915_DAC2_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
+SOC_DOUBLE_R("DAC2 Switch", WM8915_DAC2_LEFT_VOLUME,
+ WM8915_DAC2_RIGHT_VOLUME, 9, 1, 1),
+
+SOC_SINGLE("Speaker High Performance Switch", WM8915_OVERSAMPLING, 3, 1, 0),
+SOC_SINGLE("DMIC High Performance Switch", WM8915_OVERSAMPLING, 2, 1, 0),
+SOC_SINGLE("ADC High Performance Switch", WM8915_OVERSAMPLING, 1, 1, 0),
+SOC_SINGLE("DAC High Performance Switch", WM8915_OVERSAMPLING, 0, 1, 0),
+
+SOC_SINGLE("DAC Soft Mute Switch", WM8915_DAC_SOFTMUTE, 1, 1, 0),
+SOC_SINGLE("DAC Slow Soft Mute Switch", WM8915_DAC_SOFTMUTE, 0, 1, 0),
+
+SOC_DOUBLE_TLV("Digital Output 1 Volume", WM8915_DAC1_HPOUT1_VOLUME, 0, 4,
+ 8, 0, out_digital_tlv),
+SOC_DOUBLE_TLV("Digital Output 2 Volume", WM8915_DAC2_HPOUT2_VOLUME, 0, 4,
+ 8, 0, out_digital_tlv),
+
+SOC_DOUBLE_R_TLV("Output 1 Volume", WM8915_OUTPUT1_LEFT_VOLUME,
+ WM8915_OUTPUT1_RIGHT_VOLUME, 0, 12, 0, out_tlv),
+SOC_DOUBLE_R("Output 1 ZC Switch", WM8915_OUTPUT1_LEFT_VOLUME,
+ WM8915_OUTPUT1_RIGHT_VOLUME, 7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Output 2 Volume", WM8915_OUTPUT2_LEFT_VOLUME,
+ WM8915_OUTPUT2_RIGHT_VOLUME, 0, 12, 0, out_tlv),
+SOC_DOUBLE_R("Output 2 ZC Switch", WM8915_OUTPUT2_LEFT_VOLUME,
+ WM8915_OUTPUT2_RIGHT_VOLUME, 7, 1, 0),
+
+SOC_DOUBLE_TLV("Speaker Volume", WM8915_PDM_SPEAKER_VOLUME, 0, 4, 8, 0,
+ spk_tlv),
+SOC_DOUBLE_R("Speaker Switch", WM8915_LEFT_PDM_SPEAKER,
+ WM8915_RIGHT_PDM_SPEAKER, 3, 1, 1),
+SOC_DOUBLE_R("Speaker ZC Switch", WM8915_LEFT_PDM_SPEAKER,
+ WM8915_RIGHT_PDM_SPEAKER, 2, 1, 0),
+
+SOC_SINGLE("DSP1 EQ Switch", WM8915_DSP1_RX_EQ_GAINS_1, 0, 1, 0),
+SOC_SINGLE("DSP2 EQ Switch", WM8915_DSP2_RX_EQ_GAINS_1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8915_eq_controls[] = {
+SOC_SINGLE_TLV("DSP1 EQ B1 Volume", WM8915_DSP1_RX_EQ_GAINS_1, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B2 Volume", WM8915_DSP1_RX_EQ_GAINS_1, 6, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B3 Volume", WM8915_DSP1_RX_EQ_GAINS_1, 1, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B4 Volume", WM8915_DSP1_RX_EQ_GAINS_2, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP1 EQ B5 Volume", WM8915_DSP1_RX_EQ_GAINS_2, 6, 31, 0,
+ eq_tlv),
+
+SOC_SINGLE_TLV("DSP2 EQ B1 Volume", WM8915_DSP2_RX_EQ_GAINS_1, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B2 Volume", WM8915_DSP2_RX_EQ_GAINS_1, 6, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B3 Volume", WM8915_DSP2_RX_EQ_GAINS_1, 1, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B4 Volume", WM8915_DSP2_RX_EQ_GAINS_2, 11, 31, 0,
+ eq_tlv),
+SOC_SINGLE_TLV("DSP2 EQ B5 Volume", WM8915_DSP2_RX_EQ_GAINS_2, 6, 31, 0,
+ eq_tlv),
+};
+
+static int cp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(5);
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rmv_short_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(w->codec);
+
+ /* Record which outputs we enabled */
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ wm8915->hpout_pending &= ~w->shift;
+ break;
+ case SND_SOC_DAPM_PRE_PMU:
+ wm8915->hpout_pending |= w->shift;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void wait_for_dc_servo(struct snd_soc_codec *codec, u16 mask)
+{
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int i, ret;
+ unsigned long timeout = 200;
+
+ snd_soc_write(codec, WM8915_DC_SERVO_2, mask);
+
+ /* Use the interrupt if possible */
+ do {
+ if (i2c->irq) {
+ timeout = wait_for_completion_timeout(&wm8915->dcs_done,
+ msecs_to_jiffies(200));
+ if (timeout == 0)
+ dev_err(codec->dev, "DC servo timed out\n");
+
+ } else {
+ msleep(1);
+ if (--i) {
+ timeout = 0;
+ break;
+ }
+ }
+
+ ret = snd_soc_read(codec, WM8915_DC_SERVO_2);
+ dev_dbg(codec->dev, "DC servo state: %x\n", ret);
+ } while (ret & mask);
+
+ if (timeout == 0)
+ dev_err(codec->dev, "DC servo timed out for %x\n", mask);
+ else
+ dev_dbg(codec->dev, "DC servo complete for %x\n", mask);
+}
+
+static void wm8915_seq_notifier(struct snd_soc_dapm_context *dapm,
+ enum snd_soc_dapm_type event, int subseq)
+{
+ struct snd_soc_codec *codec = container_of(dapm,
+ struct snd_soc_codec, dapm);
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ u16 val, mask;
+
+ /* Complete any pending DC servo starts */
+ if (wm8915->dcs_pending) {
+ dev_dbg(codec->dev, "Starting DC servo for %x\n",
+ wm8915->dcs_pending);
+
+ /* Trigger a startup sequence */
+ wait_for_dc_servo(codec, wm8915->dcs_pending
+ << WM8915_DCS_TRIG_STARTUP_0_SHIFT);
+
+ wm8915->dcs_pending = 0;
+ }
+
+ if (wm8915->hpout_pending != wm8915->hpout_ena) {
+ dev_dbg(codec->dev, "Applying RMV_SHORTs %x->%x\n",
+ wm8915->hpout_ena, wm8915->hpout_pending);
+
+ val = 0;
+ mask = 0;
+ if (wm8915->hpout_pending & HPOUT1L) {
+ val |= WM8915_HPOUT1L_RMV_SHORT;
+ mask |= WM8915_HPOUT1L_RMV_SHORT;
+ } else {
+ mask |= WM8915_HPOUT1L_RMV_SHORT |
+ WM8915_HPOUT1L_OUTP |
+ WM8915_HPOUT1L_DLY;
+ }
+
+ if (wm8915->hpout_pending & HPOUT1R) {
+ val |= WM8915_HPOUT1R_RMV_SHORT;
+ mask |= WM8915_HPOUT1R_RMV_SHORT;
+ } else {
+ mask |= WM8915_HPOUT1R_RMV_SHORT |
+ WM8915_HPOUT1R_OUTP |
+ WM8915_HPOUT1R_DLY;
+ }
+
+ snd_soc_update_bits(codec, WM8915_ANALOGUE_HP_1, mask, val);
+
+ val = 0;
+ mask = 0;
+ if (wm8915->hpout_pending & HPOUT2L) {
+ val |= WM8915_HPOUT2L_RMV_SHORT;
+ mask |= WM8915_HPOUT2L_RMV_SHORT;
+ } else {
+ mask |= WM8915_HPOUT2L_RMV_SHORT |
+ WM8915_HPOUT2L_OUTP |
+ WM8915_HPOUT2L_DLY;
+ }
+
+ if (wm8915->hpout_pending & HPOUT2R) {
+ val |= WM8915_HPOUT2R_RMV_SHORT;
+ mask |= WM8915_HPOUT2R_RMV_SHORT;
+ } else {
+ mask |= WM8915_HPOUT2R_RMV_SHORT |
+ WM8915_HPOUT2R_OUTP |
+ WM8915_HPOUT2R_DLY;
+ }
+
+ snd_soc_update_bits(codec, WM8915_ANALOGUE_HP_2, mask, val);
+
+ wm8915->hpout_ena = wm8915->hpout_pending;
+ }
+}
+
+static int dcs_start(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(w->codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ wm8915->dcs_pending |= 1 << w->shift;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const char *sidetone_text[] = {
+ "IN1", "IN2",
+};
+
+static const struct soc_enum left_sidetone_enum =
+ SOC_ENUM_SINGLE(WM8915_SIDETONE, 0, 2, sidetone_text);
+
+static const struct snd_kcontrol_new left_sidetone =
+ SOC_DAPM_ENUM("Left Sidetone", left_sidetone_enum);
+
+static const struct soc_enum right_sidetone_enum =
+ SOC_ENUM_SINGLE(WM8915_SIDETONE, 1, 2, sidetone_text);
+
+static const struct snd_kcontrol_new right_sidetone =
+ SOC_DAPM_ENUM("Right Sidetone", right_sidetone_enum);
+
+static const char *spk_text[] = {
+ "DAC1L", "DAC1R", "DAC2L", "DAC2R"
+};
+
+static const struct soc_enum spkl_enum =
+ SOC_ENUM_SINGLE(WM8915_LEFT_PDM_SPEAKER, 0, 4, spk_text);
+
+static const struct snd_kcontrol_new spkl_mux =
+ SOC_DAPM_ENUM("SPKL", spkl_enum);
+
+static const struct soc_enum spkr_enum =
+ SOC_ENUM_SINGLE(WM8915_RIGHT_PDM_SPEAKER, 0, 4, spk_text);
+
+static const struct snd_kcontrol_new spkr_mux =
+ SOC_DAPM_ENUM("SPKR", spkr_enum);
+
+static const char *dsp1rx_text[] = {
+ "AIF1", "AIF2"
+};
+
+static const struct soc_enum dsp1rx_enum =
+ SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_8, 0, 2, dsp1rx_text);
+
+static const struct snd_kcontrol_new dsp1rx =
+ SOC_DAPM_ENUM("DSP1RX", dsp1rx_enum);
+
+static const char *dsp2rx_text[] = {
+ "AIF2", "AIF1"
+};
+
+static const struct soc_enum dsp2rx_enum =
+ SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_8, 4, 2, dsp2rx_text);
+
+static const struct snd_kcontrol_new dsp2rx =
+ SOC_DAPM_ENUM("DSP2RX", dsp2rx_enum);
+
+static const char *aif2tx_text[] = {
+ "DSP2", "DSP1", "AIF1"
+};
+
+static const struct soc_enum aif2tx_enum =
+ SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_8, 6, 3, aif2tx_text);
+
+static const struct snd_kcontrol_new aif2tx =
+ SOC_DAPM_ENUM("AIF2TX", aif2tx_enum);
+
+static const char *inmux_text[] = {
+ "ADC", "DMIC1", "DMIC2"
+};
+
+static const struct soc_enum in1_enum =
+ SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_7, 0, 3, inmux_text);
+
+static const struct snd_kcontrol_new in1_mux =
+ SOC_DAPM_ENUM("IN1 Mux", in1_enum);
+
+static const struct soc_enum in2_enum =
+ SOC_ENUM_SINGLE(WM8915_POWER_MANAGEMENT_7, 4, 3, inmux_text);
+
+static const struct snd_kcontrol_new in2_mux =
+ SOC_DAPM_ENUM("IN2 Mux", in2_enum);
+
+static const struct snd_kcontrol_new dac2r_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8915_DAC2_RIGHT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8915_DAC2_RIGHT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8915_DAC2_RIGHT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8915_DAC2_RIGHT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac2l_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8915_DAC2_LEFT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8915_DAC2_LEFT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8915_DAC2_LEFT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8915_DAC2_LEFT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac1r_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8915_DAC1_RIGHT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8915_DAC1_RIGHT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8915_DAC1_RIGHT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8915_DAC1_RIGHT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac1l_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8915_DAC1_LEFT_MIXER_ROUTING,
+ 5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8915_DAC1_LEFT_MIXER_ROUTING,
+ 4, 1, 0),
+SOC_DAPM_SINGLE("DSP2 Switch", WM8915_DAC1_LEFT_MIXER_ROUTING, 1, 1, 0),
+SOC_DAPM_SINGLE("DSP1 Switch", WM8915_DAC1_LEFT_MIXER_ROUTING, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp1txl[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8915_DSP1_TX_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8915_DSP1_TX_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp1txr[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8915_DSP1_TX_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8915_DSP1_TX_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp2txl[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8915_DSP2_TX_LEFT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8915_DSP2_TX_LEFT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dsp2txr[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM8915_DSP2_TX_RIGHT_MIXER_ROUTING,
+ 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8915_DSP2_TX_RIGHT_MIXER_ROUTING,
+ 0, 1, 0),
+};
+
+
+static const struct snd_soc_dapm_widget wm8915_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1LN"),
+SND_SOC_DAPM_INPUT("IN1LP"),
+SND_SOC_DAPM_INPUT("IN1RN"),
+SND_SOC_DAPM_INPUT("IN1RP"),
+
+SND_SOC_DAPM_INPUT("IN2LN"),
+SND_SOC_DAPM_INPUT("IN2LP"),
+SND_SOC_DAPM_INPUT("IN2RN"),
+SND_SOC_DAPM_INPUT("IN2RP"),
+
+SND_SOC_DAPM_INPUT("DMIC1DAT"),
+SND_SOC_DAPM_INPUT("DMIC2DAT"),
+
+SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, WM8915_AIF_CLOCKING_1, 0, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8915_CLOCKING_1, 1, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8915_CLOCKING_1, 2, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("Charge Pump", 2, WM8915_CHARGE_PUMP_1, 15, 0, cp_event,
+ SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_SUPPLY("LDO2", WM8915_POWER_MANAGEMENT_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_MICBIAS("MICB2", WM8915_POWER_MANAGEMENT_1, 9, 0),
+SND_SOC_DAPM_MICBIAS("MICB1", WM8915_POWER_MANAGEMENT_1, 8, 0),
+
+SND_SOC_DAPM_PGA("IN1L PGA", WM8915_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN1R PGA", WM8915_POWER_MANAGEMENT_2, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &in1_mux),
+SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &in1_mux),
+SND_SOC_DAPM_MUX("IN2L Mux", SND_SOC_NOPM, 0, 0, &in2_mux),
+SND_SOC_DAPM_MUX("IN2R Mux", SND_SOC_NOPM, 0, 0, &in2_mux),
+
+SND_SOC_DAPM_PGA("IN1L", WM8915_POWER_MANAGEMENT_7, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN1R", WM8915_POWER_MANAGEMENT_7, 3, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN2L", WM8915_POWER_MANAGEMENT_7, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("IN2R", WM8915_POWER_MANAGEMENT_7, 7, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("DMIC2", WM8915_POWER_MANAGEMENT_7, 9, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DMIC1", WM8915_POWER_MANAGEMENT_7, 8, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("DMIC2L", NULL, WM8915_POWER_MANAGEMENT_3, 5, 0),
+SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8915_POWER_MANAGEMENT_3, 4, 0),
+SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8915_POWER_MANAGEMENT_3, 3, 0),
+SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8915_POWER_MANAGEMENT_3, 2, 0),
+
+SND_SOC_DAPM_ADC("ADCL", NULL, WM8915_POWER_MANAGEMENT_3, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, WM8915_POWER_MANAGEMENT_3, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &left_sidetone),
+SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &right_sidetone),
+
+SND_SOC_DAPM_AIF_IN("DSP2RXL", NULL, 0, WM8915_POWER_MANAGEMENT_3, 11, 0),
+SND_SOC_DAPM_AIF_IN("DSP2RXR", NULL, 1, WM8915_POWER_MANAGEMENT_3, 10, 0),
+SND_SOC_DAPM_AIF_IN("DSP1RXL", NULL, 0, WM8915_POWER_MANAGEMENT_3, 9, 0),
+SND_SOC_DAPM_AIF_IN("DSP1RXR", NULL, 1, WM8915_POWER_MANAGEMENT_3, 8, 0),
+
+SND_SOC_DAPM_MIXER("DSP2TXL", WM8915_POWER_MANAGEMENT_5, 11, 0,
+ dsp2txl, ARRAY_SIZE(dsp2txl)),
+SND_SOC_DAPM_MIXER("DSP2TXR", WM8915_POWER_MANAGEMENT_5, 10, 0,
+ dsp2txr, ARRAY_SIZE(dsp2txr)),
+SND_SOC_DAPM_MIXER("DSP1TXL", WM8915_POWER_MANAGEMENT_5, 9, 0,
+ dsp1txl, ARRAY_SIZE(dsp1txl)),
+SND_SOC_DAPM_MIXER("DSP1TXR", WM8915_POWER_MANAGEMENT_5, 8, 0,
+ dsp1txr, ARRAY_SIZE(dsp1txr)),
+
+SND_SOC_DAPM_MIXER("DAC2L Mixer", SND_SOC_NOPM, 0, 0,
+ dac2l_mix, ARRAY_SIZE(dac2l_mix)),
+SND_SOC_DAPM_MIXER("DAC2R Mixer", SND_SOC_NOPM, 0, 0,
+ dac2r_mix, ARRAY_SIZE(dac2r_mix)),
+SND_SOC_DAPM_MIXER("DAC1L Mixer", SND_SOC_NOPM, 0, 0,
+ dac1l_mix, ARRAY_SIZE(dac1l_mix)),
+SND_SOC_DAPM_MIXER("DAC1R Mixer", SND_SOC_NOPM, 0, 0,
+ dac1r_mix, ARRAY_SIZE(dac1r_mix)),
+
+SND_SOC_DAPM_DAC("DAC2L", NULL, WM8915_POWER_MANAGEMENT_5, 3, 0),
+SND_SOC_DAPM_DAC("DAC2R", NULL, WM8915_POWER_MANAGEMENT_5, 2, 0),
+SND_SOC_DAPM_DAC("DAC1L", NULL, WM8915_POWER_MANAGEMENT_5, 1, 0),
+SND_SOC_DAPM_DAC("DAC1R", NULL, WM8915_POWER_MANAGEMENT_5, 0, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", "AIF2 Playback", 1,
+ WM8915_POWER_MANAGEMENT_4, 9, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX0", "AIF2 Playback", 2,
+ WM8915_POWER_MANAGEMENT_4, 8, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2TX1", "AIF2 Capture", 1,
+ WM8915_POWER_MANAGEMENT_6, 9, 0),
+SND_SOC_DAPM_AIF_IN("AIF2TX0", "AIF2 Capture", 2,
+ WM8915_POWER_MANAGEMENT_6, 8, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX5", "AIF1 Playback", 5,
+ WM8915_POWER_MANAGEMENT_4, 5, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", "AIF1 Playback", 4,
+ WM8915_POWER_MANAGEMENT_4, 4, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", "AIF1 Playback", 3,
+ WM8915_POWER_MANAGEMENT_4, 3, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", "AIF1 Playback", 2,
+ WM8915_POWER_MANAGEMENT_4, 2, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX1", "AIF1 Playback", 1,
+ WM8915_POWER_MANAGEMENT_4, 1, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX0", "AIF1 Playback", 0,
+ WM8915_POWER_MANAGEMENT_4, 0, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", "AIF1 Capture", 5,
+ WM8915_POWER_MANAGEMENT_6, 5, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", "AIF1 Capture", 4,
+ WM8915_POWER_MANAGEMENT_6, 4, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", "AIF1 Capture", 3,
+ WM8915_POWER_MANAGEMENT_6, 3, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", "AIF1 Capture", 2,
+ WM8915_POWER_MANAGEMENT_6, 2, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", "AIF1 Capture", 1,
+ WM8915_POWER_MANAGEMENT_6, 1, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX0", "AIF1 Capture", 0,
+ WM8915_POWER_MANAGEMENT_6, 0, 0),
+
+/* We route as stereo pairs so define some dummy widgets to squash
+ * things down for now. RXA = 0,1, RXB = 2,3 and so on */
+SND_SOC_DAPM_PGA("AIF1RXA", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("AIF1RXB", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("AIF1RXC", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("AIF2RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DSP2TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("DSP1RX", SND_SOC_NOPM, 0, 0, &dsp1rx),
+SND_SOC_DAPM_MUX("DSP2RX", SND_SOC_NOPM, 0, 0, &dsp2rx),
+SND_SOC_DAPM_MUX("AIF2TX", SND_SOC_NOPM, 0, 0, &aif2tx),
+
+SND_SOC_DAPM_MUX("SPKL", SND_SOC_NOPM, 0, 0, &spkl_mux),
+SND_SOC_DAPM_MUX("SPKR", SND_SOC_NOPM, 0, 0, &spkr_mux),
+SND_SOC_DAPM_PGA("SPKL PGA", WM8915_LEFT_PDM_SPEAKER, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPKR PGA", WM8915_RIGHT_PDM_SPEAKER, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_S("HPOUT2L PGA", 0, WM8915_POWER_MANAGEMENT_1, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2L_DLY", 1, WM8915_ANALOGUE_HP_2, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2L_DCS", 2, WM8915_DC_SERVO_1, 2, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT2L_OUTP", 3, WM8915_ANALOGUE_HP_2, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2L, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA_S("HPOUT2R PGA", 0, WM8915_POWER_MANAGEMENT_1, 6, 0,NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2R_DLY", 1, WM8915_ANALOGUE_HP_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2R_DCS", 2, WM8915_DC_SERVO_1, 3, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT2R_OUTP", 3, WM8915_ANALOGUE_HP_2, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT2R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2R, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA_S("HPOUT1L PGA", 0, WM8915_POWER_MANAGEMENT_1, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1L_DLY", 1, WM8915_ANALOGUE_HP_1, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1L_DCS", 2, WM8915_DC_SERVO_1, 0, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT1L_OUTP", 3, WM8915_ANALOGUE_HP_1, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1L, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_PGA_S("HPOUT1R PGA", 0, WM8915_POWER_MANAGEMENT_1, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1R_DLY", 1, WM8915_ANALOGUE_HP_1, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1R_DCS", 2, WM8915_DC_SERVO_1, 1, 0, dcs_start,
+ SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_S("HPOUT1R_OUTP", 3, WM8915_ANALOGUE_HP_1, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPOUT1R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1R, 0,
+ rmv_short_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2L"),
+SND_SOC_DAPM_OUTPUT("HPOUT2R"),
+SND_SOC_DAPM_OUTPUT("SPKDAT"),
+};
+
+static const struct snd_soc_dapm_route wm8915_dapm_routes[] = {
+ { "AIFCLK", NULL, "SYSCLK" },
+ { "SYSDSPCLK", NULL, "SYSCLK" },
+ { "Charge Pump", NULL, "SYSCLK" },
+
+ { "MICB1", NULL, "LDO2" },
+ { "MICB2", NULL, "LDO2" },
+
+ { "IN1L PGA", NULL, "IN2LN" },
+ { "IN1L PGA", NULL, "IN2LP" },
+ { "IN1L PGA", NULL, "IN1LN" },
+ { "IN1L PGA", NULL, "IN1LP" },
+
+ { "IN1R PGA", NULL, "IN2RN" },
+ { "IN1R PGA", NULL, "IN2RP" },
+ { "IN1R PGA", NULL, "IN1RN" },
+ { "IN1R PGA", NULL, "IN1RP" },
+
+ { "ADCL", NULL, "IN1L PGA" },
+
+ { "ADCR", NULL, "IN1R PGA" },
+
+ { "DMIC1L", NULL, "DMIC1DAT" },
+ { "DMIC1R", NULL, "DMIC1DAT" },
+ { "DMIC2L", NULL, "DMIC2DAT" },
+ { "DMIC2R", NULL, "DMIC2DAT" },
+
+ { "DMIC2L", NULL, "DMIC2" },
+ { "DMIC2R", NULL, "DMIC2" },
+ { "DMIC1L", NULL, "DMIC1" },
+ { "DMIC1R", NULL, "DMIC1" },
+
+ { "IN1L Mux", "ADC", "ADCL" },
+ { "IN1L Mux", "DMIC1", "DMIC1L" },
+ { "IN1L Mux", "DMIC2", "DMIC2L" },
+
+ { "IN1R Mux", "ADC", "ADCR" },
+ { "IN1R Mux", "DMIC1", "DMIC1R" },
+ { "IN1R Mux", "DMIC2", "DMIC2R" },
+
+ { "IN2L Mux", "ADC", "ADCL" },
+ { "IN2L Mux", "DMIC1", "DMIC1L" },
+ { "IN2L Mux", "DMIC2", "DMIC2L" },
+
+ { "IN2R Mux", "ADC", "ADCR" },
+ { "IN2R Mux", "DMIC1", "DMIC1R" },
+ { "IN2R Mux", "DMIC2", "DMIC2R" },
+
+ { "Left Sidetone", "IN1", "IN1L Mux" },
+ { "Left Sidetone", "IN2", "IN2L Mux" },
+
+ { "Right Sidetone", "IN1", "IN1R Mux" },
+ { "Right Sidetone", "IN2", "IN2R Mux" },
+
+ { "DSP1TXL", "IN1 Switch", "IN1L Mux" },
+ { "DSP1TXR", "IN1 Switch", "IN1R Mux" },
+
+ { "DSP2TXL", "IN1 Switch", "IN2L Mux" },
+ { "DSP2TXR", "IN1 Switch", "IN2R Mux" },
+
+ { "AIF1TX0", NULL, "DSP1TXL" },
+ { "AIF1TX1", NULL, "DSP1TXR" },
+ { "AIF1TX2", NULL, "DSP2TXL" },
+ { "AIF1TX3", NULL, "DSP2TXR" },
+ { "AIF1TX4", NULL, "AIF2RX0" },
+ { "AIF1TX5", NULL, "AIF2RX1" },
+
+ { "AIF1RX0", NULL, "AIFCLK" },
+ { "AIF1RX1", NULL, "AIFCLK" },
+ { "AIF1RX2", NULL, "AIFCLK" },
+ { "AIF1RX3", NULL, "AIFCLK" },
+ { "AIF1RX4", NULL, "AIFCLK" },
+ { "AIF1RX5", NULL, "AIFCLK" },
+
+ { "AIF2RX0", NULL, "AIFCLK" },
+ { "AIF2RX1", NULL, "AIFCLK" },
+
+ { "DSP1RXL", NULL, "SYSDSPCLK" },
+ { "DSP1RXR", NULL, "SYSDSPCLK" },
+ { "DSP2RXL", NULL, "SYSDSPCLK" },
+ { "DSP2RXR", NULL, "SYSDSPCLK" },
+ { "DSP1TXL", NULL, "SYSDSPCLK" },
+ { "DSP1TXR", NULL, "SYSDSPCLK" },
+ { "DSP2TXL", NULL, "SYSDSPCLK" },
+ { "DSP2TXR", NULL, "SYSDSPCLK" },
+
+ { "AIF1RXA", NULL, "AIF1RX0" },
+ { "AIF1RXA", NULL, "AIF1RX1" },
+ { "AIF1RXB", NULL, "AIF1RX2" },
+ { "AIF1RXB", NULL, "AIF1RX3" },
+ { "AIF1RXC", NULL, "AIF1RX4" },
+ { "AIF1RXC", NULL, "AIF1RX5" },
+
+ { "AIF2RX", NULL, "AIF2RX0" },
+ { "AIF2RX", NULL, "AIF2RX1" },
+
+ { "AIF2TX", "DSP2", "DSP2TX" },
+ { "AIF2TX", "DSP1", "DSP1RX" },
+ { "AIF2TX", "AIF1", "AIF1RXC" },
+
+ { "DSP1RXL", NULL, "DSP1RX" },
+ { "DSP1RXR", NULL, "DSP1RX" },
+ { "DSP2RXL", NULL, "DSP2RX" },
+ { "DSP2RXR", NULL, "DSP2RX" },
+
+ { "DSP2TX", NULL, "DSP2TXL" },
+ { "DSP2TX", NULL, "DSP2TXR" },
+
+ { "DSP1RX", "AIF1", "AIF1RXA" },
+ { "DSP1RX", "AIF2", "AIF2RX" },
+
+ { "DSP2RX", "AIF1", "AIF1RXB" },
+ { "DSP2RX", "AIF2", "AIF2RX" },
+
+ { "DAC2L Mixer", "DSP2 Switch", "DSP2RXL" },
+ { "DAC2L Mixer", "DSP1 Switch", "DSP1RXL" },
+ { "DAC2L Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC2L Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC2R Mixer", "DSP2 Switch", "DSP2RXR" },
+ { "DAC2R Mixer", "DSP1 Switch", "DSP1RXR" },
+ { "DAC2R Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC2R Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC1L Mixer", "DSP2 Switch", "DSP2RXL" },
+ { "DAC1L Mixer", "DSP1 Switch", "DSP1RXL" },
+ { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC1R Mixer", "DSP2 Switch", "DSP2RXR" },
+ { "DAC1R Mixer", "DSP1 Switch", "DSP1RXR" },
+ { "DAC1R Mixer", "Right Sidetone Switch", "Right Sidetone" },
+ { "DAC1R Mixer", "Left Sidetone Switch", "Left Sidetone" },
+
+ { "DAC1L", NULL, "DAC1L Mixer" },
+ { "DAC1R", NULL, "DAC1R Mixer" },
+ { "DAC2L", NULL, "DAC2L Mixer" },
+ { "DAC2R", NULL, "DAC2R Mixer" },
+
+ { "HPOUT2L PGA", NULL, "Charge Pump" },
+ { "HPOUT2L PGA", NULL, "DAC2L" },
+ { "HPOUT2L_DLY", NULL, "HPOUT2L PGA" },
+ { "HPOUT2L_DCS", NULL, "HPOUT2L_DLY" },
+ { "HPOUT2L_OUTP", NULL, "HPOUT2L_DCS" },
+ { "HPOUT2L_RMV_SHORT", NULL, "HPOUT2L_OUTP" },
+
+ { "HPOUT2R PGA", NULL, "Charge Pump" },
+ { "HPOUT2R PGA", NULL, "DAC2R" },
+ { "HPOUT2R_DLY", NULL, "HPOUT2R PGA" },
+ { "HPOUT2R_DCS", NULL, "HPOUT2R_DLY" },
+ { "HPOUT2R_OUTP", NULL, "HPOUT2R_DCS" },
+ { "HPOUT2R_RMV_SHORT", NULL, "HPOUT2R_OUTP" },
+
+ { "HPOUT1L PGA", NULL, "Charge Pump" },
+ { "HPOUT1L PGA", NULL, "DAC1L" },
+ { "HPOUT1L_DLY", NULL, "HPOUT1L PGA" },
+ { "HPOUT1L_DCS", NULL, "HPOUT1L_DLY" },
+ { "HPOUT1L_OUTP", NULL, "HPOUT1L_DCS" },
+ { "HPOUT1L_RMV_SHORT", NULL, "HPOUT1L_OUTP" },
+
+ { "HPOUT1R PGA", NULL, "Charge Pump" },
+ { "HPOUT1R PGA", NULL, "DAC1R" },
+ { "HPOUT1R_DLY", NULL, "HPOUT1R PGA" },
+ { "HPOUT1R_DCS", NULL, "HPOUT1R_DLY" },
+ { "HPOUT1R_OUTP", NULL, "HPOUT1R_DCS" },
+ { "HPOUT1R_RMV_SHORT", NULL, "HPOUT1R_OUTP" },
+
+ { "HPOUT2L", NULL, "HPOUT2L_RMV_SHORT" },
+ { "HPOUT2R", NULL, "HPOUT2R_RMV_SHORT" },
+ { "HPOUT1L", NULL, "HPOUT1L_RMV_SHORT" },
+ { "HPOUT1R", NULL, "HPOUT1R_RMV_SHORT" },
+
+ { "SPKL", "DAC1L", "DAC1L" },
+ { "SPKL", "DAC1R", "DAC1R" },
+ { "SPKL", "DAC2L", "DAC2L" },
+ { "SPKL", "DAC2R", "DAC2R" },
+
+ { "SPKR", "DAC1L", "DAC1L" },
+ { "SPKR", "DAC1R", "DAC1R" },
+ { "SPKR", "DAC2L", "DAC2L" },
+ { "SPKR", "DAC2R", "DAC2R" },
+
+ { "SPKL PGA", NULL, "SPKL" },
+ { "SPKR PGA", NULL, "SPKR" },
+
+ { "SPKDAT", NULL, "SPKL PGA" },
+ { "SPKDAT", NULL, "SPKR PGA" },
+};
+
+static int wm8915_readable_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ /* Due to the sparseness of the register map the compiler
+ * output from an explicit switch statement ends up being much
+ * more efficient than a table.
+ */
+ switch (reg) {
+ case WM8915_SOFTWARE_RESET:
+ case WM8915_POWER_MANAGEMENT_1:
+ case WM8915_POWER_MANAGEMENT_2:
+ case WM8915_POWER_MANAGEMENT_3:
+ case WM8915_POWER_MANAGEMENT_4:
+ case WM8915_POWER_MANAGEMENT_5:
+ case WM8915_POWER_MANAGEMENT_6:
+ case WM8915_POWER_MANAGEMENT_7:
+ case WM8915_POWER_MANAGEMENT_8:
+ case WM8915_LEFT_LINE_INPUT_VOLUME:
+ case WM8915_RIGHT_LINE_INPUT_VOLUME:
+ case WM8915_LINE_INPUT_CONTROL:
+ case WM8915_DAC1_HPOUT1_VOLUME:
+ case WM8915_DAC2_HPOUT2_VOLUME:
+ case WM8915_DAC1_LEFT_VOLUME:
+ case WM8915_DAC1_RIGHT_VOLUME:
+ case WM8915_DAC2_LEFT_VOLUME:
+ case WM8915_DAC2_RIGHT_VOLUME:
+ case WM8915_OUTPUT1_LEFT_VOLUME:
+ case WM8915_OUTPUT1_RIGHT_VOLUME:
+ case WM8915_OUTPUT2_LEFT_VOLUME:
+ case WM8915_OUTPUT2_RIGHT_VOLUME:
+ case WM8915_MICBIAS_1:
+ case WM8915_MICBIAS_2:
+ case WM8915_LDO_1:
+ case WM8915_LDO_2:
+ case WM8915_ACCESSORY_DETECT_MODE_1:
+ case WM8915_ACCESSORY_DETECT_MODE_2:
+ case WM8915_HEADPHONE_DETECT_1:
+ case WM8915_HEADPHONE_DETECT_2:
+ case WM8915_MIC_DETECT_1:
+ case WM8915_MIC_DETECT_2:
+ case WM8915_MIC_DETECT_3:
+ case WM8915_CHARGE_PUMP_1:
+ case WM8915_CHARGE_PUMP_2:
+ case WM8915_DC_SERVO_1:
+ case WM8915_DC_SERVO_2:
+ case WM8915_DC_SERVO_3:
+ case WM8915_DC_SERVO_5:
+ case WM8915_DC_SERVO_6:
+ case WM8915_DC_SERVO_7:
+ case WM8915_DC_SERVO_READBACK_0:
+ case WM8915_ANALOGUE_HP_1:
+ case WM8915_ANALOGUE_HP_2:
+ case WM8915_CHIP_REVISION:
+ case WM8915_CONTROL_INTERFACE_1:
+ case WM8915_WRITE_SEQUENCER_CTRL_1:
+ case WM8915_WRITE_SEQUENCER_CTRL_2:
+ case WM8915_AIF_CLOCKING_1:
+ case WM8915_AIF_CLOCKING_2:
+ case WM8915_CLOCKING_1:
+ case WM8915_CLOCKING_2:
+ case WM8915_AIF_RATE:
+ case WM8915_FLL_CONTROL_1:
+ case WM8915_FLL_CONTROL_2:
+ case WM8915_FLL_CONTROL_3:
+ case WM8915_FLL_CONTROL_4:
+ case WM8915_FLL_CONTROL_5:
+ case WM8915_FLL_CONTROL_6:
+ case WM8915_FLL_EFS_1:
+ case WM8915_FLL_EFS_2:
+ case WM8915_AIF1_CONTROL:
+ case WM8915_AIF1_BCLK:
+ case WM8915_AIF1_TX_LRCLK_1:
+ case WM8915_AIF1_TX_LRCLK_2:
+ case WM8915_AIF1_RX_LRCLK_1:
+ case WM8915_AIF1_RX_LRCLK_2:
+ case WM8915_AIF1TX_DATA_CONFIGURATION_1:
+ case WM8915_AIF1TX_DATA_CONFIGURATION_2:
+ case WM8915_AIF1RX_DATA_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_0_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_1_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_2_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_3_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_4_CONFIGURATION:
+ case WM8915_AIF1TX_CHANNEL_5_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_0_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_1_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_2_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_3_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_4_CONFIGURATION:
+ case WM8915_AIF1RX_CHANNEL_5_CONFIGURATION:
+ case WM8915_AIF1RX_MONO_CONFIGURATION:
+ case WM8915_AIF1TX_TEST:
+ case WM8915_AIF2_CONTROL:
+ case WM8915_AIF2_BCLK:
+ case WM8915_AIF2_TX_LRCLK_1:
+ case WM8915_AIF2_TX_LRCLK_2:
+ case WM8915_AIF2_RX_LRCLK_1:
+ case WM8915_AIF2_RX_LRCLK_2:
+ case WM8915_AIF2TX_DATA_CONFIGURATION_1:
+ case WM8915_AIF2TX_DATA_CONFIGURATION_2:
+ case WM8915_AIF2RX_DATA_CONFIGURATION:
+ case WM8915_AIF2TX_CHANNEL_0_CONFIGURATION:
+ case WM8915_AIF2TX_CHANNEL_1_CONFIGURATION:
+ case WM8915_AIF2RX_CHANNEL_0_CONFIGURATION:
+ case WM8915_AIF2RX_CHANNEL_1_CONFIGURATION:
+ case WM8915_AIF2RX_MONO_CONFIGURATION:
+ case WM8915_AIF2TX_TEST:
+ case WM8915_DSP1_TX_LEFT_VOLUME:
+ case WM8915_DSP1_TX_RIGHT_VOLUME:
+ case WM8915_DSP1_RX_LEFT_VOLUME:
+ case WM8915_DSP1_RX_RIGHT_VOLUME:
+ case WM8915_DSP1_TX_FILTERS:
+ case WM8915_DSP1_RX_FILTERS_1:
+ case WM8915_DSP1_RX_FILTERS_2:
+ case WM8915_DSP1_DRC_1:
+ case WM8915_DSP1_DRC_2:
+ case WM8915_DSP1_DRC_3:
+ case WM8915_DSP1_DRC_4:
+ case WM8915_DSP1_DRC_5:
+ case WM8915_DSP1_RX_EQ_GAINS_1:
+ case WM8915_DSP1_RX_EQ_GAINS_2:
+ case WM8915_DSP1_RX_EQ_BAND_1_A:
+ case WM8915_DSP1_RX_EQ_BAND_1_B:
+ case WM8915_DSP1_RX_EQ_BAND_1_PG:
+ case WM8915_DSP1_RX_EQ_BAND_2_A:
+ case WM8915_DSP1_RX_EQ_BAND_2_B:
+ case WM8915_DSP1_RX_EQ_BAND_2_C:
+ case WM8915_DSP1_RX_EQ_BAND_2_PG:
+ case WM8915_DSP1_RX_EQ_BAND_3_A:
+ case WM8915_DSP1_RX_EQ_BAND_3_B:
+ case WM8915_DSP1_RX_EQ_BAND_3_C:
+ case WM8915_DSP1_RX_EQ_BAND_3_PG:
+ case WM8915_DSP1_RX_EQ_BAND_4_A:
+ case WM8915_DSP1_RX_EQ_BAND_4_B:
+ case WM8915_DSP1_RX_EQ_BAND_4_C:
+ case WM8915_DSP1_RX_EQ_BAND_4_PG:
+ case WM8915_DSP1_RX_EQ_BAND_5_A:
+ case WM8915_DSP1_RX_EQ_BAND_5_B:
+ case WM8915_DSP1_RX_EQ_BAND_5_PG:
+ case WM8915_DSP2_TX_LEFT_VOLUME:
+ case WM8915_DSP2_TX_RIGHT_VOLUME:
+ case WM8915_DSP2_RX_LEFT_VOLUME:
+ case WM8915_DSP2_RX_RIGHT_VOLUME:
+ case WM8915_DSP2_TX_FILTERS:
+ case WM8915_DSP2_RX_FILTERS_1:
+ case WM8915_DSP2_RX_FILTERS_2:
+ case WM8915_DSP2_DRC_1:
+ case WM8915_DSP2_DRC_2:
+ case WM8915_DSP2_DRC_3:
+ case WM8915_DSP2_DRC_4:
+ case WM8915_DSP2_DRC_5:
+ case WM8915_DSP2_RX_EQ_GAINS_1:
+ case WM8915_DSP2_RX_EQ_GAINS_2:
+ case WM8915_DSP2_RX_EQ_BAND_1_A:
+ case WM8915_DSP2_RX_EQ_BAND_1_B:
+ case WM8915_DSP2_RX_EQ_BAND_1_PG:
+ case WM8915_DSP2_RX_EQ_BAND_2_A:
+ case WM8915_DSP2_RX_EQ_BAND_2_B:
+ case WM8915_DSP2_RX_EQ_BAND_2_C:
+ case WM8915_DSP2_RX_EQ_BAND_2_PG:
+ case WM8915_DSP2_RX_EQ_BAND_3_A:
+ case WM8915_DSP2_RX_EQ_BAND_3_B:
+ case WM8915_DSP2_RX_EQ_BAND_3_C:
+ case WM8915_DSP2_RX_EQ_BAND_3_PG:
+ case WM8915_DSP2_RX_EQ_BAND_4_A:
+ case WM8915_DSP2_RX_EQ_BAND_4_B:
+ case WM8915_DSP2_RX_EQ_BAND_4_C:
+ case WM8915_DSP2_RX_EQ_BAND_4_PG:
+ case WM8915_DSP2_RX_EQ_BAND_5_A:
+ case WM8915_DSP2_RX_EQ_BAND_5_B:
+ case WM8915_DSP2_RX_EQ_BAND_5_PG:
+ case WM8915_DAC1_MIXER_VOLUMES:
+ case WM8915_DAC1_LEFT_MIXER_ROUTING:
+ case WM8915_DAC1_RIGHT_MIXER_ROUTING:
+ case WM8915_DAC2_MIXER_VOLUMES:
+ case WM8915_DAC2_LEFT_MIXER_ROUTING:
+ case WM8915_DAC2_RIGHT_MIXER_ROUTING:
+ case WM8915_DSP1_TX_LEFT_MIXER_ROUTING:
+ case WM8915_DSP1_TX_RIGHT_MIXER_ROUTING:
+ case WM8915_DSP2_TX_LEFT_MIXER_ROUTING:
+ case WM8915_DSP2_TX_RIGHT_MIXER_ROUTING:
+ case WM8915_DSP_TX_MIXER_SELECT:
+ case WM8915_DAC_SOFTMUTE:
+ case WM8915_OVERSAMPLING:
+ case WM8915_SIDETONE:
+ case WM8915_GPIO_1:
+ case WM8915_GPIO_2:
+ case WM8915_GPIO_3:
+ case WM8915_GPIO_4:
+ case WM8915_GPIO_5:
+ case WM8915_PULL_CONTROL_1:
+ case WM8915_PULL_CONTROL_2:
+ case WM8915_INTERRUPT_STATUS_1:
+ case WM8915_INTERRUPT_STATUS_2:
+ case WM8915_INTERRUPT_RAW_STATUS_2:
+ case WM8915_INTERRUPT_STATUS_1_MASK:
+ case WM8915_INTERRUPT_STATUS_2_MASK:
+ case WM8915_INTERRUPT_CONTROL:
+ case WM8915_LEFT_PDM_SPEAKER:
+ case WM8915_RIGHT_PDM_SPEAKER:
+ case WM8915_PDM_SPEAKER_MUTE_SEQUENCE:
+ case WM8915_PDM_SPEAKER_VOLUME:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int wm8915_volatile_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ switch (reg) {
+ case WM8915_SOFTWARE_RESET:
+ case WM8915_CHIP_REVISION:
+ case WM8915_LDO_1:
+ case WM8915_LDO_2:
+ case WM8915_INTERRUPT_STATUS_1:
+ case WM8915_INTERRUPT_STATUS_2:
+ case WM8915_INTERRUPT_RAW_STATUS_2:
+ case WM8915_DC_SERVO_READBACK_0:
+ case WM8915_DC_SERVO_2:
+ case WM8915_DC_SERVO_6:
+ case WM8915_DC_SERVO_7:
+ case WM8915_FLL_CONTROL_6:
+ case WM8915_MIC_DETECT_3:
+ case WM8915_HEADPHONE_DETECT_1:
+ case WM8915_HEADPHONE_DETECT_2:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int wm8915_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, WM8915_SOFTWARE_RESET, 0x8915);
+}
+
+static int wm8915_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+ snd_soc_update_bits(codec, WM8915_POWER_MANAGEMENT_1,
+ WM8915_BG_ENA, WM8915_BG_ENA);
+ msleep(2);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8915->supplies),
+ wm8915->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (wm8915->pdata.ldo_ena >= 0) {
+ gpio_set_value_cansleep(wm8915->pdata.ldo_ena,
+ 1);
+ msleep(5);
+ }
+
+ codec->cache_only = false;
+ snd_soc_cache_sync(codec);
+ }
+
+ snd_soc_update_bits(codec, WM8915_POWER_MANAGEMENT_1,
+ WM8915_BG_ENA, 0);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ codec->cache_only = true;
+ if (wm8915->pdata.ldo_ena >= 0)
+ gpio_set_value_cansleep(wm8915->pdata.ldo_ena, 0);
+ regulator_bulk_disable(ARRAY_SIZE(wm8915->supplies),
+ wm8915->supplies);
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int wm8915_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int aifctrl = 0;
+ int bclk = 0;
+ int lrclk_tx = 0;
+ int lrclk_rx = 0;
+ int aifctrl_reg, bclk_reg, lrclk_tx_reg, lrclk_rx_reg;
+
+ switch (dai->id) {
+ case 0:
+ aifctrl_reg = WM8915_AIF1_CONTROL;
+ bclk_reg = WM8915_AIF1_BCLK;
+ lrclk_tx_reg = WM8915_AIF1_TX_LRCLK_2;
+ lrclk_rx_reg = WM8915_AIF1_RX_LRCLK_2;
+ break;
+ case 1:
+ aifctrl_reg = WM8915_AIF2_CONTROL;
+ bclk_reg = WM8915_AIF2_BCLK;
+ lrclk_tx_reg = WM8915_AIF2_TX_LRCLK_2;
+ lrclk_rx_reg = WM8915_AIF2_RX_LRCLK_2;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ bclk |= WM8915_AIF1_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ lrclk_tx |= WM8915_AIF1TX_LRCLK_INV;
+ lrclk_rx |= WM8915_AIF1RX_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ bclk |= WM8915_AIF1_BCLK_INV;
+ lrclk_tx |= WM8915_AIF1TX_LRCLK_INV;
+ lrclk_rx |= WM8915_AIF1RX_LRCLK_INV;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ lrclk_tx |= WM8915_AIF1TX_LRCLK_MSTR;
+ lrclk_rx |= WM8915_AIF1RX_LRCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ bclk |= WM8915_AIF1_BCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ bclk |= WM8915_AIF1_BCLK_MSTR;
+ lrclk_tx |= WM8915_AIF1TX_LRCLK_MSTR;
+ lrclk_rx |= WM8915_AIF1RX_LRCLK_MSTR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ aifctrl |= 1;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aifctrl |= 2;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aifctrl |= 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, aifctrl_reg, WM8915_AIF1_FMT_MASK, aifctrl);
+ snd_soc_update_bits(codec, bclk_reg,
+ WM8915_AIF1_BCLK_INV | WM8915_AIF1_BCLK_MSTR,
+ bclk);
+ snd_soc_update_bits(codec, lrclk_tx_reg,
+ WM8915_AIF1TX_LRCLK_INV |
+ WM8915_AIF1TX_LRCLK_MSTR,
+ lrclk_tx);
+ snd_soc_update_bits(codec, lrclk_rx_reg,
+ WM8915_AIF1RX_LRCLK_INV |
+ WM8915_AIF1RX_LRCLK_MSTR,
+ lrclk_rx);
+
+ return 0;
+}
+
+static const int bclk_divs[] = {
+ 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96
+};
+
+static const int dsp_divs[] = {
+ 48000, 32000, 16000, 8000
+};
+
+static int wm8915_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int bits, i, bclk_rate, best, cur_val;
+ int aifdata = 0;
+ int bclk = 0;
+ int lrclk = 0;
+ int dsp = 0;
+ int aifdata_reg, bclk_reg, lrclk_reg, dsp_shift;
+
+ if (!wm8915->sysclk) {
+ dev_err(codec->dev, "SYSCLK not configured\n");
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case 0:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+ (snd_soc_read(codec, WM8915_GPIO_1)) & WM8915_GP1_FN_MASK) {
+ aifdata_reg = WM8915_AIF1RX_DATA_CONFIGURATION;
+ lrclk_reg = WM8915_AIF1_RX_LRCLK_1;
+ } else {
+ aifdata_reg = WM8915_AIF1TX_DATA_CONFIGURATION_1;
+ lrclk_reg = WM8915_AIF1_TX_LRCLK_1;
+ }
+ bclk_reg = WM8915_AIF1_BCLK;
+ dsp_shift = 0;
+ break;
+ case 1:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+ (snd_soc_read(codec, WM8915_GPIO_2)) & WM8915_GP2_FN_MASK) {
+ aifdata_reg = WM8915_AIF2RX_DATA_CONFIGURATION;
+ lrclk_reg = WM8915_AIF2_RX_LRCLK_1;
+ } else {
+ aifdata_reg = WM8915_AIF2TX_DATA_CONFIGURATION_1;
+ lrclk_reg = WM8915_AIF2_TX_LRCLK_1;
+ }
+ bclk_reg = WM8915_AIF2_BCLK;
+ dsp_shift = WM8915_DSP2_DIV_SHIFT;
+ break;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ bclk_rate = snd_soc_params_to_bclk(params);
+ if (bclk_rate < 0) {
+ dev_err(codec->dev, "Unsupported BCLK rate: %d\n", bclk_rate);
+ return bclk_rate;
+ }
+
+ /* Needs looking at for TDM */
+ bits = snd_pcm_format_width(params_format(params));
+ if (bits < 0)
+ return bits;
+ aifdata |= (bits << WM8915_AIF1TX_WL_SHIFT) | bits;
+
+ for (i = 0; i < ARRAY_SIZE(dsp_divs); i++) {
+ if (dsp_divs[i] == params_rate(params))
+ break;
+ }
+ if (i == ARRAY_SIZE(dsp_divs)) {
+ dev_err(codec->dev, "Unsupported sample rate %dHz\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ dsp |= i << dsp_shift;
+
+ /* Pick a divisor for BCLK as close as we can get to ideal */
+ best = 0;
+ for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+ cur_val = (wm8915->sysclk / bclk_divs[i]) - bclk_rate;
+ if (cur_val < 0) /* BCLK table is sorted */
+ break;
+ best = i;
+ }
+ bclk_rate = wm8915->sysclk / bclk_divs[best];
+ dev_dbg(dai->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
+ bclk_divs[best], bclk_rate);
+ bclk |= best;
+
+ lrclk = bclk_rate / params_rate(params);
+ dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
+ lrclk, bclk_rate / lrclk);
+
+ snd_soc_update_bits(codec, aifdata_reg,
+ WM8915_AIF1TX_WL_MASK |
+ WM8915_AIF1TX_SLOT_LEN_MASK,
+ aifdata);
+ snd_soc_update_bits(codec, bclk_reg, WM8915_AIF1_BCLK_DIV_MASK, bclk);
+ snd_soc_update_bits(codec, lrclk_reg, WM8915_AIF1RX_RATE_MASK,
+ lrclk);
+ snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_2,
+ WM8915_DSP1_DIV_SHIFT << dsp_shift, dsp);
+
+ wm8915->rx_rate[dai->id] = params_rate(params);
+
+ return 0;
+}
+
+static int wm8915_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int lfclk = 0;
+ int ratediv = 0;
+ int src;
+ int old;
+
+ /* Disable SYSCLK while we reconfigure */
+ old = snd_soc_read(codec, WM8915_AIF_CLOCKING_1);
+ snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
+ WM8915_SYSCLK_ENA, 0);
+
+ switch (clk_id) {
+ case WM8915_SYSCLK_MCLK1:
+ wm8915->sysclk = freq;
+ src = 0;
+ break;
+ case WM8915_SYSCLK_MCLK2:
+ wm8915->sysclk = freq;
+ src = 1;
+ break;
+ case WM8915_SYSCLK_FLL:
+ wm8915->sysclk = freq;
+ src = 2;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported clock source %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ switch (wm8915->sysclk) {
+ case 6144000:
+ snd_soc_update_bits(codec, WM8915_AIF_RATE,
+ WM8915_SYSCLK_RATE, 0);
+ break;
+ case 24576000:
+ ratediv = WM8915_SYSCLK_DIV;
+ case 12288000:
+ snd_soc_update_bits(codec, WM8915_AIF_RATE,
+ WM8915_SYSCLK_RATE, WM8915_SYSCLK_RATE);
+ break;
+ case 32000:
+ case 32768:
+ lfclk = WM8915_LFCLK_ENA;
+ break;
+ default:
+ dev_warn(codec->dev, "Unsupported clock rate %dHz\n",
+ wm8915->sysclk);
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
+ WM8915_SYSCLK_SRC_MASK | WM8915_SYSCLK_DIV_MASK,
+ src << WM8915_SYSCLK_SRC_SHIFT | ratediv);
+ snd_soc_update_bits(codec, WM8915_CLOCKING_1, WM8915_LFCLK_ENA, lfclk);
+ snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
+ WM8915_SYSCLK_ENA, old);
+
+ return 0;
+}
+
+struct _fll_div {
+ u16 fll_fratio;
+ u16 fll_outdiv;
+ u16 fll_refclk_div;
+ u16 fll_loop_gain;
+ u16 fll_ref_freq;
+ u16 n;
+ u16 theta;
+ u16 lambda;
+};
+
+static struct {
+ unsigned int min;
+ unsigned int max;
+ u16 fll_fratio;
+ int ratio;
+} fll_fratios[] = {
+ { 0, 64000, 4, 16 },
+ { 64000, 128000, 3, 8 },
+ { 128000, 256000, 2, 4 },
+ { 256000, 1000000, 1, 2 },
+ { 1000000, 13500000, 0, 1 },
+};
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+ unsigned int Fout)
+{
+ unsigned int target;
+ unsigned int div;
+ unsigned int fratio, gcd_fll;
+ int i;
+
+ /* Fref must be <=13.5MHz */
+ div = 1;
+ fll_div->fll_refclk_div = 0;
+ while ((Fref / div) > 13500000) {
+ div *= 2;
+ fll_div->fll_refclk_div++;
+
+ if (div > 8) {
+ pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
+ Fref);
+ return -EINVAL;
+ }
+ }
+
+ pr_debug("FLL Fref=%u Fout=%u\n", Fref, Fout);
+
+ /* Apply the division for our remaining calculations */
+ Fref /= div;
+
+ if (Fref >= 3000000)
+ fll_div->fll_loop_gain = 5;
+ else
+ fll_div->fll_loop_gain = 0;
+
+ if (Fref >= 48000)
+ fll_div->fll_ref_freq = 0;
+ else
+ fll_div->fll_ref_freq = 1;
+
+ /* Fvco should be 90-100MHz; don't check the upper bound */
+ div = 2;
+ while (Fout * div < 90000000) {
+ div++;
+ if (div > 64) {
+ pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+ Fout);
+ return -EINVAL;
+ }
+ }
+ target = Fout * div;
+ fll_div->fll_outdiv = div - 1;
+
+ pr_debug("FLL Fvco=%dHz\n", target);
+
+ /* Find an appropraite FLL_FRATIO and factor it out of the target */
+ for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+ if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+ fll_div->fll_fratio = fll_fratios[i].fll_fratio;
+ fratio = fll_fratios[i].ratio;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(fll_fratios)) {
+ pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
+ return -EINVAL;
+ }
+
+ fll_div->n = target / (fratio * Fref);
+
+ if (target % Fref == 0) {
+ fll_div->theta = 0;
+ fll_div->lambda = 0;
+ } else {
+ gcd_fll = gcd(target, fratio * Fref);
+
+ fll_div->theta = (target - (fll_div->n * fratio * Fref))
+ / gcd_fll;
+ fll_div->lambda = (fratio * Fref) / gcd_fll;
+ }
+
+ pr_debug("FLL N=%x THETA=%x LAMBDA=%x\n",
+ fll_div->n, fll_div->theta, fll_div->lambda);
+ pr_debug("FLL_FRATIO=%x FLL_OUTDIV=%x FLL_REFCLK_DIV=%x\n",
+ fll_div->fll_fratio, fll_div->fll_outdiv,
+ fll_div->fll_refclk_div);
+
+ return 0;
+}
+
+static int wm8915_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct _fll_div fll_div;
+ unsigned long timeout;
+ int ret, reg;
+
+ /* Any change? */
+ if (source == wm8915->fll_src && Fref == wm8915->fll_fref &&
+ Fout == wm8915->fll_fout)
+ return 0;
+
+ if (Fout == 0) {
+ dev_dbg(codec->dev, "FLL disabled\n");
+
+ wm8915->fll_fref = 0;
+ wm8915->fll_fout = 0;
+
+ snd_soc_update_bits(codec, WM8915_FLL_CONTROL_1,
+ WM8915_FLL_ENA, 0);
+
+ return 0;
+ }
+
+ ret = fll_factors(&fll_div, Fref, Fout);
+ if (ret != 0)
+ return ret;
+
+ switch (source) {
+ case WM8915_FLL_MCLK1:
+ reg = 0;
+ break;
+ case WM8915_FLL_MCLK2:
+ reg = 1;
+ case WM8915_FLL_DACLRCLK1:
+ reg = 2;
+ break;
+ case WM8915_FLL_BCLK1:
+ reg = 3;
+ break;
+ default:
+ dev_err(codec->dev, "Unknown FLL source %d\n", ret);
+ return -EINVAL;
+ }
+
+ reg |= fll_div.fll_refclk_div << WM8915_FLL_REFCLK_DIV_SHIFT;
+ reg |= fll_div.fll_ref_freq << WM8915_FLL_REF_FREQ_SHIFT;
+
+ snd_soc_update_bits(codec, WM8915_FLL_CONTROL_5,
+ WM8915_FLL_REFCLK_DIV_MASK | WM8915_FLL_REF_FREQ |
+ WM8915_FLL_REFCLK_SRC_MASK, reg);
+
+ reg = 0;
+ if (fll_div.theta || fll_div.lambda)
+ reg |= WM8915_FLL_EFS_ENA | (3 << WM8915_FLL_LFSR_SEL_SHIFT);
+ else
+ reg |= 1 << WM8915_FLL_LFSR_SEL_SHIFT;
+ snd_soc_write(codec, WM8915_FLL_EFS_2, reg);
+
+ snd_soc_update_bits(codec, WM8915_FLL_CONTROL_2,
+ WM8915_FLL_OUTDIV_MASK |
+ WM8915_FLL_FRATIO_MASK,
+ (fll_div.fll_outdiv << WM8915_FLL_OUTDIV_SHIFT) |
+ (fll_div.fll_fratio));
+
+ snd_soc_write(codec, WM8915_FLL_CONTROL_3, fll_div.theta);
+
+ snd_soc_update_bits(codec, WM8915_FLL_CONTROL_4,
+ WM8915_FLL_N_MASK | WM8915_FLL_LOOP_GAIN_MASK,
+ (fll_div.n << WM8915_FLL_N_SHIFT) |
+ fll_div.fll_loop_gain);
+
+ snd_soc_write(codec, WM8915_FLL_EFS_1, fll_div.lambda);
+
+ snd_soc_update_bits(codec, WM8915_FLL_CONTROL_1,
+ WM8915_FLL_ENA, WM8915_FLL_ENA);
+
+ /* The FLL supports live reconfiguration - kick that in case we were
+ * already enabled.
+ */
+ snd_soc_write(codec, WM8915_FLL_CONTROL_6, WM8915_FLL_SWITCH_CLK);
+
+ /* Wait for the FLL to lock, using the interrupt if possible */
+ if (Fref > 1000000)
+ timeout = usecs_to_jiffies(300);
+ else
+ timeout = msecs_to_jiffies(2);
+
+ wait_for_completion_timeout(&wm8915->fll_lock, timeout);
+
+ dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
+
+ wm8915->fll_fref = Fref;
+ wm8915->fll_fout = Fout;
+ wm8915->fll_src = source;
+
+ return 0;
+}
+
+#ifdef CONFIG_GPIOLIB
+static inline struct wm8915_priv *gpio_to_wm8915(struct gpio_chip *chip)
+{
+ return container_of(chip, struct wm8915_priv, gpio_chip);
+}
+
+static void wm8915_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct wm8915_priv *wm8915 = gpio_to_wm8915(chip);
+ struct snd_soc_codec *codec = wm8915->codec;
+
+ snd_soc_update_bits(codec, WM8915_GPIO_1 + offset,
+ WM8915_GP1_LVL, !!value << WM8915_GP1_LVL_SHIFT);
+}
+
+static int wm8915_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct wm8915_priv *wm8915 = gpio_to_wm8915(chip);
+ struct snd_soc_codec *codec = wm8915->codec;
+ int val;
+
+ val = (1 << WM8915_GP1_FN_SHIFT) | (!!value << WM8915_GP1_LVL_SHIFT);
+
+ return snd_soc_update_bits(codec, WM8915_GPIO_1 + offset,
+ WM8915_GP1_FN_MASK | WM8915_GP1_DIR |
+ WM8915_GP1_LVL, val);
+}
+
+static int wm8915_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8915_priv *wm8915 = gpio_to_wm8915(chip);
+ struct snd_soc_codec *codec = wm8915->codec;
+ int ret;
+
+ ret = snd_soc_read(codec, WM8915_GPIO_1 + offset);
+ if (ret < 0)
+ return ret;
+
+ return (ret & WM8915_GP1_LVL) != 0;
+}
+
+static int wm8915_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct wm8915_priv *wm8915 = gpio_to_wm8915(chip);
+ struct snd_soc_codec *codec = wm8915->codec;
+
+ return snd_soc_update_bits(codec, WM8915_GPIO_1 + offset,
+ WM8915_GP1_FN_MASK | WM8915_GP1_DIR,
+ (1 << WM8915_GP1_FN_SHIFT) |
+ (1 << WM8915_GP1_DIR_SHIFT));
+}
+
+static struct gpio_chip wm8915_template_chip = {
+ .label = "wm8915",
+ .owner = THIS_MODULE,
+ .direction_output = wm8915_gpio_direction_out,
+ .set = wm8915_gpio_set,
+ .direction_input = wm8915_gpio_direction_in,
+ .get = wm8915_gpio_get,
+ .can_sleep = 1,
+};
+
+static void wm8915_init_gpio(struct snd_soc_codec *codec)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ wm8915->gpio_chip = wm8915_template_chip;
+ wm8915->gpio_chip.ngpio = 5;
+ wm8915->gpio_chip.dev = codec->dev;
+
+ if (wm8915->pdata.gpio_base)
+ wm8915->gpio_chip.base = wm8915->pdata.gpio_base;
+ else
+ wm8915->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&wm8915->gpio_chip);
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
+}
+
+static void wm8915_free_gpio(struct snd_soc_codec *codec)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = gpiochip_remove(&wm8915->gpio_chip);
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret);
+}
+#else
+static void wm8915_init_gpio(struct snd_soc_codec *codec)
+{
+}
+
+static void wm8915_free_gpio(struct snd_soc_codec *codec)
+{
+}
+#endif
+
+/**
+ * wm8915_detect - Enable default WM8915 jack detection
+ *
+ * The WM8915 has advanced accessory detection support for headsets.
+ * This function provides a default implementation which integrates
+ * the majority of this functionality with minimal user configuration.
+ *
+ * This will detect headset, headphone and short circuit button and
+ * will also detect inverted microphone ground connections and update
+ * the polarity of the connections.
+ */
+int wm8915_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ wm8915_polarity_fn polarity_cb)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+
+ wm8915->jack = jack;
+ wm8915->detecting = true;
+ wm8915->polarity_cb = polarity_cb;
+
+ if (wm8915->polarity_cb)
+ wm8915->polarity_cb(codec, 0);
+
+ /* Clear discarge to avoid noise during detection */
+ snd_soc_update_bits(codec, WM8915_MICBIAS_1,
+ WM8915_MICB1_DISCH, 0);
+ snd_soc_update_bits(codec, WM8915_MICBIAS_2,
+ WM8915_MICB2_DISCH, 0);
+
+ /* LDO2 powers the microphones, SYSCLK clocks detection */
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2");
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "SYSCLK");
+
+ /* We start off just enabling microphone detection - even a
+ * plain headphone will trigger detection.
+ */
+ snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
+ WM8915_MICD_ENA, WM8915_MICD_ENA);
+
+ /* Slowest detection rate, gives debounce for initial detection */
+ snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
+ WM8915_MICD_RATE_MASK,
+ WM8915_MICD_RATE_MASK);
+
+ /* Enable interrupts and we're off */
+ snd_soc_update_bits(codec, WM8915_INTERRUPT_STATUS_2_MASK,
+ WM8915_IM_MICD_EINT, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm8915_detect);
+
+static void wm8915_micd(struct snd_soc_codec *codec)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int val, reg;
+
+ val = snd_soc_read(codec, WM8915_MIC_DETECT_3);
+
+ dev_dbg(codec->dev, "Microphone event: %x\n", val);
+
+ if (!(val & WM8915_MICD_VALID)) {
+ dev_warn(codec->dev, "Microphone detection state invalid\n");
+ return;
+ }
+
+ /* No accessory, reset everything and report removal */
+ if (!(val & WM8915_MICD_STS)) {
+ dev_dbg(codec->dev, "Jack removal detected\n");
+ wm8915->jack_mic = false;
+ wm8915->detecting = true;
+ snd_soc_jack_report(wm8915->jack, 0,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
+ WM8915_MICD_RATE_MASK,
+ WM8915_MICD_RATE_MASK);
+ return;
+ }
+
+ /* If the measurement is very high we've got a microphone but
+ * do a little debounce to account for mechanical issues.
+ */
+ if (val & 0x400) {
+ dev_dbg(codec->dev, "Microphone detected\n");
+ snd_soc_jack_report(wm8915->jack, SND_JACK_HEADSET,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ wm8915->jack_mic = true;
+ wm8915->detecting = false;
+ }
+
+ /* If we detected a lower impedence during initial startup
+ * then we probably have the wrong polarity, flip it. Don't
+ * do this for the lowest impedences to speed up detection of
+ * plain headphones.
+ */
+ if (wm8915->detecting && (val & 0x3f0)) {
+ reg = snd_soc_read(codec, WM8915_ACCESSORY_DETECT_MODE_2);
+ reg ^= WM8915_HPOUT1FB_SRC | WM8915_MICD_SRC |
+ WM8915_MICD_BIAS_SRC;
+ snd_soc_update_bits(codec, WM8915_ACCESSORY_DETECT_MODE_2,
+ WM8915_HPOUT1FB_SRC | WM8915_MICD_SRC |
+ WM8915_MICD_BIAS_SRC, reg);
+
+ if (wm8915->polarity_cb)
+ wm8915->polarity_cb(codec,
+ (reg & WM8915_MICD_SRC) != 0);
+
+ dev_dbg(codec->dev, "Set microphone polarity to %d\n",
+ (reg & WM8915_MICD_SRC) != 0);
+
+ return;
+ }
+
+ /* Don't distinguish between buttons, just report any low
+ * impedence as BTN_0.
+ */
+ if (val & 0x3fc) {
+ if (wm8915->jack_mic) {
+ dev_dbg(codec->dev, "Mic button detected\n");
+ snd_soc_jack_report(wm8915->jack,
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+ } else {
+ dev_dbg(codec->dev, "Headphone detected\n");
+ snd_soc_jack_report(wm8915->jack,
+ SND_JACK_HEADPHONE,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0);
+ wm8915->detecting = false;
+ }
+ }
+
+ /* Increase poll rate to give better responsiveness for buttons */
+ if (!wm8915->detecting)
+ snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
+ WM8915_MICD_RATE_MASK,
+ 5 << WM8915_MICD_RATE_SHIFT);
+}
+
+static irqreturn_t wm8915_irq(int irq, void *data)
+{
+ struct snd_soc_codec *codec = data;
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ int irq_val;
+
+ irq_val = snd_soc_read(codec, WM8915_INTERRUPT_STATUS_2);
+ if (irq_val < 0) {
+ dev_err(codec->dev, "Failed to read IRQ status: %d\n",
+ irq_val);
+ return IRQ_NONE;
+ }
+ irq_val &= ~snd_soc_read(codec, WM8915_INTERRUPT_STATUS_2_MASK);
+
+ if (irq_val & (WM8915_DCS_DONE_01_EINT | WM8915_DCS_DONE_23_EINT)) {
+ dev_dbg(codec->dev, "DC servo IRQ\n");
+ complete(&wm8915->dcs_done);
+ }
+
+ if (irq_val & WM8915_FIFOS_ERR_EINT)
+ dev_err(codec->dev, "Digital core FIFO error\n");
+
+ if (irq_val & WM8915_FLL_LOCK_EINT) {
+ dev_dbg(codec->dev, "FLL locked\n");
+ complete(&wm8915->fll_lock);
+ }
+
+ if (irq_val & WM8915_MICD_EINT)
+ wm8915_micd(codec);
+
+ if (irq_val) {
+ snd_soc_write(codec, WM8915_INTERRUPT_STATUS_2, irq_val);
+
+ return IRQ_HANDLED;
+ } else {
+ return IRQ_NONE;
+ }
+}
+
+static void wm8915_retune_mobile_pdata(struct snd_soc_codec *codec)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct wm8915_pdata *pdata = &wm8915->pdata;
+
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("DSP1 EQ Mode",
+ wm8915->retune_mobile_enum,
+ wm8915_get_retune_mobile_enum,
+ wm8915_put_retune_mobile_enum),
+ SOC_ENUM_EXT("DSP2 EQ Mode",
+ wm8915->retune_mobile_enum,
+ wm8915_get_retune_mobile_enum,
+ wm8915_put_retune_mobile_enum),
+ };
+ int ret, i, j;
+ const char **t;
+
+ /* We need an array of texts for the enum API but the number
+ * of texts is likely to be less than the number of
+ * configurations due to the sample rate dependency of the
+ * configurations. */
+ wm8915->num_retune_mobile_texts = 0;
+ wm8915->retune_mobile_texts = NULL;
+ for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+ for (j = 0; j < wm8915->num_retune_mobile_texts; j++) {
+ if (strcmp(pdata->retune_mobile_cfgs[i].name,
+ wm8915->retune_mobile_texts[j]) == 0)
+ break;
+ }
+
+ if (j != wm8915->num_retune_mobile_texts)
+ continue;
+
+ /* Expand the array... */
+ t = krealloc(wm8915->retune_mobile_texts,
+ sizeof(char *) *
+ (wm8915->num_retune_mobile_texts + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* ...store the new entry... */
+ t[wm8915->num_retune_mobile_texts] =
+ pdata->retune_mobile_cfgs[i].name;
+
+ /* ...and remember the new version. */
+ wm8915->num_retune_mobile_texts++;
+ wm8915->retune_mobile_texts = t;
+ }
+
+ dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
+ wm8915->num_retune_mobile_texts);
+
+ wm8915->retune_mobile_enum.max = wm8915->num_retune_mobile_texts;
+ wm8915->retune_mobile_enum.texts = wm8915->retune_mobile_texts;
+
+ ret = snd_soc_add_controls(codec, controls, ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(codec->dev,
+ "Failed to add ReTune Mobile controls: %d\n", ret);
+}
+
+static int wm8915_probe(struct snd_soc_codec *codec)
+{
+ int ret;
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int i, irq_flags;
+
+ wm8915->codec = codec;
+
+ init_completion(&wm8915->dcs_done);
+ init_completion(&wm8915->fll_lock);
+
+ dapm->idle_bias_off = true;
+ dapm->bias_level = SND_SOC_BIAS_OFF;
+
+ ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8915->supplies); i++)
+ wm8915->supplies[i].supply = wm8915_supply_names[i];
+
+ ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8915->supplies),
+ wm8915->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ goto err;
+ }
+
+ wm8915->disable_nb[0].notifier_call = wm8915_regulator_event_0;
+ wm8915->disable_nb[1].notifier_call = wm8915_regulator_event_1;
+ wm8915->disable_nb[2].notifier_call = wm8915_regulator_event_2;
+ wm8915->disable_nb[3].notifier_call = wm8915_regulator_event_3;
+ wm8915->disable_nb[4].notifier_call = wm8915_regulator_event_4;
+ wm8915->disable_nb[5].notifier_call = wm8915_regulator_event_5;
+
+ /* This should really be moved into the regulator core */
+ for (i = 0; i < ARRAY_SIZE(wm8915->supplies); i++) {
+ ret = regulator_register_notifier(wm8915->supplies[i].consumer,
+ &wm8915->disable_nb[i]);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to register regulator notifier: %d\n",
+ ret);
+ }
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8915->supplies),
+ wm8915->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_get;
+ }
+
+ if (wm8915->pdata.ldo_ena >= 0) {
+ gpio_set_value_cansleep(wm8915->pdata.ldo_ena, 1);
+ msleep(5);
+ }
+
+ ret = snd_soc_read(codec, WM8915_SOFTWARE_RESET);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read ID register: %d\n", ret);
+ goto err_enable;
+ }
+ if (ret != 0x8915) {
+ dev_err(codec->dev, "Device is not a WM8915, ID %x\n", ret);
+ ret = -EINVAL;
+ goto err_enable;
+ }
+
+ ret = snd_soc_read(codec, WM8915_CHIP_REVISION);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read device revision: %d\n",
+ ret);
+ goto err_enable;
+ }
+
+ dev_info(codec->dev, "revision %c\n",
+ (ret & WM8915_CHIP_REV_MASK) + 'A');
+
+ if (wm8915->pdata.ldo_ena >= 0) {
+ gpio_set_value_cansleep(wm8915->pdata.ldo_ena, 0);
+ } else {
+ ret = wm8915_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ goto err_enable;
+ }
+ }
+
+ codec->cache_only = true;
+
+ /* Apply platform data settings */
+ snd_soc_update_bits(codec, WM8915_LINE_INPUT_CONTROL,
+ WM8915_INL_MODE_MASK | WM8915_INR_MODE_MASK,
+ wm8915->pdata.inl_mode << WM8915_INL_MODE_SHIFT |
+ wm8915->pdata.inr_mode);
+
+ for (i = 0; i < ARRAY_SIZE(wm8915->pdata.gpio_default); i++) {
+ if (!wm8915->pdata.gpio_default[i])
+ continue;
+
+ snd_soc_write(codec, WM8915_GPIO_1 + i,
+ wm8915->pdata.gpio_default[i] & 0xffff);
+ }
+
+ if (wm8915->pdata.spkmute_seq)
+ snd_soc_update_bits(codec, WM8915_PDM_SPEAKER_MUTE_SEQUENCE,
+ WM8915_SPK_MUTE_ENDIAN |
+ WM8915_SPK_MUTE_SEQ1_MASK,
+ wm8915->pdata.spkmute_seq);
+
+ snd_soc_update_bits(codec, WM8915_ACCESSORY_DETECT_MODE_2,
+ WM8915_MICD_BIAS_SRC | WM8915_HPOUT1FB_SRC |
+ WM8915_MICD_SRC, wm8915->pdata.micdet_def);
+
+ /* Latch volume update bits */
+ snd_soc_update_bits(codec, WM8915_LEFT_LINE_INPUT_VOLUME,
+ WM8915_IN1_VU, WM8915_IN1_VU);
+ snd_soc_update_bits(codec, WM8915_RIGHT_LINE_INPUT_VOLUME,
+ WM8915_IN1_VU, WM8915_IN1_VU);
+
+ snd_soc_update_bits(codec, WM8915_DAC1_LEFT_VOLUME,
+ WM8915_DAC1_VU, WM8915_DAC1_VU);
+ snd_soc_update_bits(codec, WM8915_DAC1_RIGHT_VOLUME,
+ WM8915_DAC1_VU, WM8915_DAC1_VU);
+ snd_soc_update_bits(codec, WM8915_DAC2_LEFT_VOLUME,
+ WM8915_DAC2_VU, WM8915_DAC2_VU);
+ snd_soc_update_bits(codec, WM8915_DAC2_RIGHT_VOLUME,
+ WM8915_DAC2_VU, WM8915_DAC2_VU);
+
+ snd_soc_update_bits(codec, WM8915_OUTPUT1_LEFT_VOLUME,
+ WM8915_DAC1_VU, WM8915_DAC1_VU);
+ snd_soc_update_bits(codec, WM8915_OUTPUT1_RIGHT_VOLUME,
+ WM8915_DAC1_VU, WM8915_DAC1_VU);
+ snd_soc_update_bits(codec, WM8915_OUTPUT2_LEFT_VOLUME,
+ WM8915_DAC2_VU, WM8915_DAC2_VU);
+ snd_soc_update_bits(codec, WM8915_OUTPUT2_RIGHT_VOLUME,
+ WM8915_DAC2_VU, WM8915_DAC2_VU);
+
+ snd_soc_update_bits(codec, WM8915_DSP1_TX_LEFT_VOLUME,
+ WM8915_DSP1TX_VU, WM8915_DSP1TX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP1_TX_RIGHT_VOLUME,
+ WM8915_DSP1TX_VU, WM8915_DSP1TX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP2_TX_LEFT_VOLUME,
+ WM8915_DSP2TX_VU, WM8915_DSP2TX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP2_TX_RIGHT_VOLUME,
+ WM8915_DSP2TX_VU, WM8915_DSP2TX_VU);
+
+ snd_soc_update_bits(codec, WM8915_DSP1_RX_LEFT_VOLUME,
+ WM8915_DSP1RX_VU, WM8915_DSP1RX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP1_RX_RIGHT_VOLUME,
+ WM8915_DSP1RX_VU, WM8915_DSP1RX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP2_RX_LEFT_VOLUME,
+ WM8915_DSP2RX_VU, WM8915_DSP2RX_VU);
+ snd_soc_update_bits(codec, WM8915_DSP2_RX_RIGHT_VOLUME,
+ WM8915_DSP2RX_VU, WM8915_DSP2RX_VU);
+
+ /* No support currently for the underclocked TDM modes and
+ * pick a default TDM layout with each channel pair working with
+ * slots 0 and 1. */
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_0_CONFIGURATION,
+ WM8915_AIF1RX_CHAN0_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN0_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_1_CONFIGURATION,
+ WM8915_AIF1RX_CHAN1_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN1_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN1_SLOTS_SHIFT | 1);
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_2_CONFIGURATION,
+ WM8915_AIF1RX_CHAN2_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN2_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN2_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_3_CONFIGURATION,
+ WM8915_AIF1RX_CHAN3_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN3_SLOTS_SHIFT | 1);
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_4_CONFIGURATION,
+ WM8915_AIF1RX_CHAN4_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN4_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1RX_CHANNEL_5_CONFIGURATION,
+ WM8915_AIF1RX_CHAN5_SLOTS_MASK |
+ WM8915_AIF1RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1RX_CHAN5_SLOTS_SHIFT | 1);
+
+ snd_soc_update_bits(codec, WM8915_AIF2RX_CHANNEL_0_CONFIGURATION,
+ WM8915_AIF2RX_CHAN0_SLOTS_MASK |
+ WM8915_AIF2RX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF2RX_CHAN0_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF2RX_CHANNEL_1_CONFIGURATION,
+ WM8915_AIF2RX_CHAN1_SLOTS_MASK |
+ WM8915_AIF2RX_CHAN1_START_SLOT_MASK,
+ 1 << WM8915_AIF2RX_CHAN1_SLOTS_SHIFT | 1);
+
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_0_CONFIGURATION,
+ WM8915_AIF1TX_CHAN0_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN0_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_1_CONFIGURATION,
+ WM8915_AIF1TX_CHAN1_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN1_SLOTS_SHIFT | 1);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_2_CONFIGURATION,
+ WM8915_AIF1TX_CHAN2_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN2_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_3_CONFIGURATION,
+ WM8915_AIF1TX_CHAN3_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN3_SLOTS_SHIFT | 1);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_4_CONFIGURATION,
+ WM8915_AIF1TX_CHAN4_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN4_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_5_CONFIGURATION,
+ WM8915_AIF1TX_CHAN5_SLOTS_MASK |
+ WM8915_AIF1TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN5_SLOTS_SHIFT | 1);
+
+ snd_soc_update_bits(codec, WM8915_AIF2TX_CHANNEL_0_CONFIGURATION,
+ WM8915_AIF2TX_CHAN0_SLOTS_MASK |
+ WM8915_AIF2TX_CHAN0_START_SLOT_MASK,
+ 1 << WM8915_AIF2TX_CHAN0_SLOTS_SHIFT | 0);
+ snd_soc_update_bits(codec, WM8915_AIF1TX_CHANNEL_1_CONFIGURATION,
+ WM8915_AIF2TX_CHAN1_SLOTS_MASK |
+ WM8915_AIF2TX_CHAN1_START_SLOT_MASK,
+ 1 << WM8915_AIF1TX_CHAN1_SLOTS_SHIFT | 1);
+
+ if (wm8915->pdata.num_retune_mobile_cfgs)
+ wm8915_retune_mobile_pdata(codec);
+ else
+ snd_soc_add_controls(codec, wm8915_eq_controls,
+ ARRAY_SIZE(wm8915_eq_controls));
+
+ /* If the TX LRCLK pins are not in LRCLK mode configure the
+ * AIFs to source their clocks from the RX LRCLKs.
+ */
+ if ((snd_soc_read(codec, WM8915_GPIO_1)))
+ snd_soc_update_bits(codec, WM8915_AIF1_TX_LRCLK_2,
+ WM8915_AIF1TX_LRCLK_MODE,
+ WM8915_AIF1TX_LRCLK_MODE);
+
+ if ((snd_soc_read(codec, WM8915_GPIO_2)))
+ snd_soc_update_bits(codec, WM8915_AIF2_TX_LRCLK_2,
+ WM8915_AIF2TX_LRCLK_MODE,
+ WM8915_AIF2TX_LRCLK_MODE);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8915->supplies), wm8915->supplies);
+
+ wm8915_init_gpio(codec);
+
+ if (i2c->irq) {
+ if (wm8915->pdata.irq_flags)
+ irq_flags = wm8915->pdata.irq_flags;
+ else
+ irq_flags = IRQF_TRIGGER_LOW;
+
+ irq_flags |= IRQF_ONESHOT;
+
+ ret = request_threaded_irq(i2c->irq, NULL, wm8915_irq,
+ irq_flags, "wm8915", codec);
+ if (ret == 0) {
+ /* Unmask the interrupt */
+ snd_soc_update_bits(codec, WM8915_INTERRUPT_CONTROL,
+ WM8915_IM_IRQ, 0);
+
+ /* Enable error reporting and DC servo status */
+ snd_soc_update_bits(codec,
+ WM8915_INTERRUPT_STATUS_2_MASK,
+ WM8915_IM_DCS_DONE_23_EINT |
+ WM8915_IM_DCS_DONE_01_EINT |
+ WM8915_IM_FLL_LOCK_EINT |
+ WM8915_IM_FIFOS_ERR_EINT,
+ 0);
+ } else {
+ dev_err(codec->dev, "Failed to request IRQ: %d\n",
+ ret);
+ }
+ }
+
+ return 0;
+
+err_enable:
+ if (wm8915->pdata.ldo_ena >= 0)
+ gpio_set_value_cansleep(wm8915->pdata.ldo_ena, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8915->supplies), wm8915->supplies);
+err_get:
+ regulator_bulk_free(ARRAY_SIZE(wm8915->supplies), wm8915->supplies);
+err:
+ return ret;
+}
+
+static int wm8915_remove(struct snd_soc_codec *codec)
+{
+ struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ int i;
+
+ snd_soc_update_bits(codec, WM8915_INTERRUPT_CONTROL,
+ WM8915_IM_IRQ, WM8915_IM_IRQ);
+
+ if (i2c->irq)
+ free_irq(i2c->irq, codec);
+
+ wm8915_free_gpio(codec);
+
+ for (i = 0; i < ARRAY_SIZE(wm8915->supplies); i++)
+ regulator_unregister_notifier(wm8915->supplies[i].consumer,
+ &wm8915->disable_nb[i]);
+ regulator_bulk_free(ARRAY_SIZE(wm8915->supplies), wm8915->supplies);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8915 = {
+ .probe = wm8915_probe,
+ .remove = wm8915_remove,
+ .set_bias_level = wm8915_set_bias_level,
+ .seq_notifier = wm8915_seq_notifier,
+ .reg_cache_size = WM8915_MAX_REGISTER + 1,
+ .reg_word_size = sizeof(u16),
+ .reg_cache_default = wm8915_reg,
+ .volatile_register = wm8915_volatile_register,
+ .readable_register = wm8915_readable_register,
+ .compress_type = SND_SOC_RBTREE_COMPRESSION,
+ .controls = wm8915_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8915_snd_controls),
+ .dapm_widgets = wm8915_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8915_dapm_widgets),
+ .dapm_routes = wm8915_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8915_dapm_routes),
+ .set_pll = wm8915_set_fll,
+};
+
+#define WM8915_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000)
+#define WM8915_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops wm8915_dai_ops = {
+ .set_fmt = wm8915_set_fmt,
+ .hw_params = wm8915_hw_params,
+ .set_sysclk = wm8915_set_sysclk,
+};
+
+static struct snd_soc_dai_driver wm8915_dai[] = {
+ {
+ .name = "wm8915-aif1",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8915_RATES,
+ .formats = WM8915_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM8915_RATES,
+ .formats = WM8915_FORMATS,
+ },
+ .ops = &wm8915_dai_ops,
+ },
+ {
+ .name = "wm8915-aif2",
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8915_RATES,
+ .formats = WM8915_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8915_RATES,
+ .formats = WM8915_FORMATS,
+ },
+ .ops = &wm8915_dai_ops,
+ },
+};
+
+static __devinit int wm8915_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8915_priv *wm8915;
+ int ret;
+
+ wm8915 = kzalloc(sizeof(struct wm8915_priv), GFP_KERNEL);
+ if (wm8915 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, wm8915);
+
+ if (dev_get_platdata(&i2c->dev))
+ memcpy(&wm8915->pdata, dev_get_platdata(&i2c->dev),
+ sizeof(wm8915->pdata));
+
+ if (wm8915->pdata.ldo_ena > 0) {
+ ret = gpio_request_one(wm8915->pdata.ldo_ena,
+ GPIOF_OUT_INIT_LOW, "WM8915 ENA");
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request GPIO %d: %d\n",
+ wm8915->pdata.ldo_ena, ret);
+ goto err;
+ }
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_wm8915, wm8915_dai,
+ ARRAY_SIZE(wm8915_dai));
+ if (ret < 0)
+ goto err_gpio;
+
+ return ret;
+
+err_gpio:
+ if (wm8915->pdata.ldo_ena > 0)
+ gpio_free(wm8915->pdata.ldo_ena);
+err:
+ kfree(wm8915);
+
+ return ret;
+}
+
+static __devexit int wm8915_i2c_remove(struct i2c_client *client)
+{
+ struct wm8915_priv *wm8915 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+ if (wm8915->pdata.ldo_ena > 0)
+ gpio_free(wm8915->pdata.ldo_ena);
+ kfree(i2c_get_clientdata(client));
+ return 0;
+}
+
+static const struct i2c_device_id wm8915_i2c_id[] = {
+ { "wm8915", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8915_i2c_id);
+
+static struct i2c_driver wm8915_i2c_driver = {
+ .driver = {
+ .name = "wm8915",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8915_i2c_probe,
+ .remove = __devexit_p(wm8915_i2c_remove),
+ .id_table = wm8915_i2c_id,
+};
+
+static int __init wm8915_modinit(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&wm8915_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8915 I2C driver: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+module_init(wm8915_modinit);
+
+static void __exit wm8915_exit(void)
+{
+ i2c_del_driver(&wm8915_i2c_driver);
+}
+module_exit(wm8915_exit);
+
+MODULE_DESCRIPTION("ASoC WM8915 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * wm8915.h - WM8915 audio codec interface
+ *
+ * Copyright 2011 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef _WM8915_H
+#define _WM8915_H
+
+#define WM8915_SYSCLK_MCLK1 1
+#define WM8915_SYSCLK_MCLK2 2
+#define WM8915_SYSCLK_FLL 3
+
+#define WM8915_FLL_MCLK1 1
+#define WM8915_FLL_MCLK2 2
+#define WM8915_FLL_DACLRCLK1 3
+#define WM8915_FLL_BCLK1 4
+
+typedef void (*wm8915_polarity_fn)(struct snd_soc_codec *codec, int polarity);
+
+int wm8915_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ wm8915_polarity_fn polarity_cb);
+
+/*
+ * Register values.
+ */
+#define WM8915_SOFTWARE_RESET 0x00
+#define WM8915_POWER_MANAGEMENT_1 0x01
+#define WM8915_POWER_MANAGEMENT_2 0x02
+#define WM8915_POWER_MANAGEMENT_3 0x03
+#define WM8915_POWER_MANAGEMENT_4 0x04
+#define WM8915_POWER_MANAGEMENT_5 0x05
+#define WM8915_POWER_MANAGEMENT_6 0x06
+#define WM8915_POWER_MANAGEMENT_7 0x07
+#define WM8915_POWER_MANAGEMENT_8 0x08
+#define WM8915_LEFT_LINE_INPUT_VOLUME 0x10
+#define WM8915_RIGHT_LINE_INPUT_VOLUME 0x11
+#define WM8915_LINE_INPUT_CONTROL 0x12
+#define WM8915_DAC1_HPOUT1_VOLUME 0x15
+#define WM8915_DAC2_HPOUT2_VOLUME 0x16
+#define WM8915_DAC1_LEFT_VOLUME 0x18
+#define WM8915_DAC1_RIGHT_VOLUME 0x19
+#define WM8915_DAC2_LEFT_VOLUME 0x1A
+#define WM8915_DAC2_RIGHT_VOLUME 0x1B
+#define WM8915_OUTPUT1_LEFT_VOLUME 0x1C
+#define WM8915_OUTPUT1_RIGHT_VOLUME 0x1D
+#define WM8915_OUTPUT2_LEFT_VOLUME 0x1E
+#define WM8915_OUTPUT2_RIGHT_VOLUME 0x1F
+#define WM8915_MICBIAS_1 0x20
+#define WM8915_MICBIAS_2 0x21
+#define WM8915_LDO_1 0x28
+#define WM8915_LDO_2 0x29
+#define WM8915_ACCESSORY_DETECT_MODE_1 0x30
+#define WM8915_ACCESSORY_DETECT_MODE_2 0x31
+#define WM8915_HEADPHONE_DETECT_1 0x34
+#define WM8915_HEADPHONE_DETECT_2 0x35
+#define WM8915_MIC_DETECT_1 0x38
+#define WM8915_MIC_DETECT_2 0x39
+#define WM8915_MIC_DETECT_3 0x3A
+#define WM8915_CHARGE_PUMP_1 0x40
+#define WM8915_CHARGE_PUMP_2 0x41
+#define WM8915_DC_SERVO_1 0x50
+#define WM8915_DC_SERVO_2 0x51
+#define WM8915_DC_SERVO_3 0x52
+#define WM8915_DC_SERVO_5 0x54
+#define WM8915_DC_SERVO_6 0x55
+#define WM8915_DC_SERVO_7 0x56
+#define WM8915_DC_SERVO_READBACK_0 0x57
+#define WM8915_ANALOGUE_HP_1 0x60
+#define WM8915_ANALOGUE_HP_2 0x61
+#define WM8915_CHIP_REVISION 0x100
+#define WM8915_CONTROL_INTERFACE_1 0x101
+#define WM8915_WRITE_SEQUENCER_CTRL_1 0x110
+#define WM8915_WRITE_SEQUENCER_CTRL_2 0x111
+#define WM8915_AIF_CLOCKING_1 0x200
+#define WM8915_AIF_CLOCKING_2 0x201
+#define WM8915_CLOCKING_1 0x208
+#define WM8915_CLOCKING_2 0x209
+#define WM8915_AIF_RATE 0x210
+#define WM8915_FLL_CONTROL_1 0x220
+#define WM8915_FLL_CONTROL_2 0x221
+#define WM8915_FLL_CONTROL_3 0x222
+#define WM8915_FLL_CONTROL_4 0x223
+#define WM8915_FLL_CONTROL_5 0x224
+#define WM8915_FLL_CONTROL_6 0x225
+#define WM8915_FLL_EFS_1 0x226
+#define WM8915_FLL_EFS_2 0x227
+#define WM8915_AIF1_CONTROL 0x300
+#define WM8915_AIF1_BCLK 0x301
+#define WM8915_AIF1_TX_LRCLK_1 0x302
+#define WM8915_AIF1_TX_LRCLK_2 0x303
+#define WM8915_AIF1_RX_LRCLK_1 0x304
+#define WM8915_AIF1_RX_LRCLK_2 0x305
+#define WM8915_AIF1TX_DATA_CONFIGURATION_1 0x306
+#define WM8915_AIF1TX_DATA_CONFIGURATION_2 0x307
+#define WM8915_AIF1RX_DATA_CONFIGURATION 0x308
+#define WM8915_AIF1TX_CHANNEL_0_CONFIGURATION 0x309
+#define WM8915_AIF1TX_CHANNEL_1_CONFIGURATION 0x30A
+#define WM8915_AIF1TX_CHANNEL_2_CONFIGURATION 0x30B
+#define WM8915_AIF1TX_CHANNEL_3_CONFIGURATION 0x30C
+#define WM8915_AIF1TX_CHANNEL_4_CONFIGURATION 0x30D
+#define WM8915_AIF1TX_CHANNEL_5_CONFIGURATION 0x30E
+#define WM8915_AIF1RX_CHANNEL_0_CONFIGURATION 0x30F
+#define WM8915_AIF1RX_CHANNEL_1_CONFIGURATION 0x310
+#define WM8915_AIF1RX_CHANNEL_2_CONFIGURATION 0x311
+#define WM8915_AIF1RX_CHANNEL_3_CONFIGURATION 0x312
+#define WM8915_AIF1RX_CHANNEL_4_CONFIGURATION 0x313
+#define WM8915_AIF1RX_CHANNEL_5_CONFIGURATION 0x314
+#define WM8915_AIF1RX_MONO_CONFIGURATION 0x315
+#define WM8915_AIF1TX_TEST 0x31A
+#define WM8915_AIF2_CONTROL 0x320
+#define WM8915_AIF2_BCLK 0x321
+#define WM8915_AIF2_TX_LRCLK_1 0x322
+#define WM8915_AIF2_TX_LRCLK_2 0x323
+#define WM8915_AIF2_RX_LRCLK_1 0x324
+#define WM8915_AIF2_RX_LRCLK_2 0x325
+#define WM8915_AIF2TX_DATA_CONFIGURATION_1 0x326
+#define WM8915_AIF2TX_DATA_CONFIGURATION_2 0x327
+#define WM8915_AIF2RX_DATA_CONFIGURATION 0x328
+#define WM8915_AIF2TX_CHANNEL_0_CONFIGURATION 0x329
+#define WM8915_AIF2TX_CHANNEL_1_CONFIGURATION 0x32A
+#define WM8915_AIF2RX_CHANNEL_0_CONFIGURATION 0x32B
+#define WM8915_AIF2RX_CHANNEL_1_CONFIGURATION 0x32C
+#define WM8915_AIF2RX_MONO_CONFIGURATION 0x32D
+#define WM8915_AIF2TX_TEST 0x32F
+#define WM8915_DSP1_TX_LEFT_VOLUME 0x400
+#define WM8915_DSP1_TX_RIGHT_VOLUME 0x401
+#define WM8915_DSP1_RX_LEFT_VOLUME 0x402
+#define WM8915_DSP1_RX_RIGHT_VOLUME 0x403
+#define WM8915_DSP1_TX_FILTERS 0x410
+#define WM8915_DSP1_RX_FILTERS_1 0x420
+#define WM8915_DSP1_RX_FILTERS_2 0x421
+#define WM8915_DSP1_DRC_1 0x440
+#define WM8915_DSP1_DRC_2 0x441
+#define WM8915_DSP1_DRC_3 0x442
+#define WM8915_DSP1_DRC_4 0x443
+#define WM8915_DSP1_DRC_5 0x444
+#define WM8915_DSP1_RX_EQ_GAINS_1 0x480
+#define WM8915_DSP1_RX_EQ_GAINS_2 0x481
+#define WM8915_DSP1_RX_EQ_BAND_1_A 0x482
+#define WM8915_DSP1_RX_EQ_BAND_1_B 0x483
+#define WM8915_DSP1_RX_EQ_BAND_1_PG 0x484
+#define WM8915_DSP1_RX_EQ_BAND_2_A 0x485
+#define WM8915_DSP1_RX_EQ_BAND_2_B 0x486
+#define WM8915_DSP1_RX_EQ_BAND_2_C 0x487
+#define WM8915_DSP1_RX_EQ_BAND_2_PG 0x488
+#define WM8915_DSP1_RX_EQ_BAND_3_A 0x489
+#define WM8915_DSP1_RX_EQ_BAND_3_B 0x48A
+#define WM8915_DSP1_RX_EQ_BAND_3_C 0x48B
+#define WM8915_DSP1_RX_EQ_BAND_3_PG 0x48C
+#define WM8915_DSP1_RX_EQ_BAND_4_A 0x48D
+#define WM8915_DSP1_RX_EQ_BAND_4_B 0x48E
+#define WM8915_DSP1_RX_EQ_BAND_4_C 0x48F
+#define WM8915_DSP1_RX_EQ_BAND_4_PG 0x490
+#define WM8915_DSP1_RX_EQ_BAND_5_A 0x491
+#define WM8915_DSP1_RX_EQ_BAND_5_B 0x492
+#define WM8915_DSP1_RX_EQ_BAND_5_PG 0x493
+#define WM8915_DSP2_TX_LEFT_VOLUME 0x500
+#define WM8915_DSP2_TX_RIGHT_VOLUME 0x501
+#define WM8915_DSP2_RX_LEFT_VOLUME 0x502
+#define WM8915_DSP2_RX_RIGHT_VOLUME 0x503
+#define WM8915_DSP2_TX_FILTERS 0x510
+#define WM8915_DSP2_RX_FILTERS_1 0x520
+#define WM8915_DSP2_RX_FILTERS_2 0x521
+#define WM8915_DSP2_DRC_1 0x540
+#define WM8915_DSP2_DRC_2 0x541
+#define WM8915_DSP2_DRC_3 0x542
+#define WM8915_DSP2_DRC_4 0x543
+#define WM8915_DSP2_DRC_5 0x544
+#define WM8915_DSP2_RX_EQ_GAINS_1 0x580
+#define WM8915_DSP2_RX_EQ_GAINS_2 0x581
+#define WM8915_DSP2_RX_EQ_BAND_1_A 0x582
+#define WM8915_DSP2_RX_EQ_BAND_1_B 0x583
+#define WM8915_DSP2_RX_EQ_BAND_1_PG 0x584
+#define WM8915_DSP2_RX_EQ_BAND_2_A 0x585
+#define WM8915_DSP2_RX_EQ_BAND_2_B 0x586
+#define WM8915_DSP2_RX_EQ_BAND_2_C 0x587
+#define WM8915_DSP2_RX_EQ_BAND_2_PG 0x588
+#define WM8915_DSP2_RX_EQ_BAND_3_A 0x589
+#define WM8915_DSP2_RX_EQ_BAND_3_B 0x58A
+#define WM8915_DSP2_RX_EQ_BAND_3_C 0x58B
+#define WM8915_DSP2_RX_EQ_BAND_3_PG 0x58C
+#define WM8915_DSP2_RX_EQ_BAND_4_A 0x58D
+#define WM8915_DSP2_RX_EQ_BAND_4_B 0x58E
+#define WM8915_DSP2_RX_EQ_BAND_4_C 0x58F
+#define WM8915_DSP2_RX_EQ_BAND_4_PG 0x590
+#define WM8915_DSP2_RX_EQ_BAND_5_A 0x591
+#define WM8915_DSP2_RX_EQ_BAND_5_B 0x592
+#define WM8915_DSP2_RX_EQ_BAND_5_PG 0x593
+#define WM8915_DAC1_MIXER_VOLUMES 0x600
+#define WM8915_DAC1_LEFT_MIXER_ROUTING 0x601
+#define WM8915_DAC1_RIGHT_MIXER_ROUTING 0x602
+#define WM8915_DAC2_MIXER_VOLUMES 0x603
+#define WM8915_DAC2_LEFT_MIXER_ROUTING 0x604
+#define WM8915_DAC2_RIGHT_MIXER_ROUTING 0x605
+#define WM8915_DSP1_TX_LEFT_MIXER_ROUTING 0x606
+#define WM8915_DSP1_TX_RIGHT_MIXER_ROUTING 0x607
+#define WM8915_DSP2_TX_LEFT_MIXER_ROUTING 0x608
+#define WM8915_DSP2_TX_RIGHT_MIXER_ROUTING 0x609
+#define WM8915_DSP_TX_MIXER_SELECT 0x60A
+#define WM8915_DAC_SOFTMUTE 0x610
+#define WM8915_OVERSAMPLING 0x620
+#define WM8915_SIDETONE 0x621
+#define WM8915_GPIO_1 0x700
+#define WM8915_GPIO_2 0x701
+#define WM8915_GPIO_3 0x702
+#define WM8915_GPIO_4 0x703
+#define WM8915_GPIO_5 0x704
+#define WM8915_PULL_CONTROL_1 0x720
+#define WM8915_PULL_CONTROL_2 0x721
+#define WM8915_INTERRUPT_STATUS_1 0x730
+#define WM8915_INTERRUPT_STATUS_2 0x731
+#define WM8915_INTERRUPT_RAW_STATUS_2 0x732
+#define WM8915_INTERRUPT_STATUS_1_MASK 0x738
+#define WM8915_INTERRUPT_STATUS_2_MASK 0x739
+#define WM8915_INTERRUPT_CONTROL 0x740
+#define WM8915_LEFT_PDM_SPEAKER 0x800
+#define WM8915_RIGHT_PDM_SPEAKER 0x801
+#define WM8915_PDM_SPEAKER_MUTE_SEQUENCE 0x802
+#define WM8915_PDM_SPEAKER_VOLUME 0x803
+#define WM8915_WRITE_SEQUENCER_0 0x3000
+#define WM8915_WRITE_SEQUENCER_1 0x3001
+#define WM8915_WRITE_SEQUENCER_2 0x3002
+#define WM8915_WRITE_SEQUENCER_3 0x3003
+#define WM8915_WRITE_SEQUENCER_4 0x3004
+#define WM8915_WRITE_SEQUENCER_5 0x3005
+#define WM8915_WRITE_SEQUENCER_6 0x3006
+#define WM8915_WRITE_SEQUENCER_7 0x3007
+#define WM8915_WRITE_SEQUENCER_8 0x3008
+#define WM8915_WRITE_SEQUENCER_9 0x3009
+#define WM8915_WRITE_SEQUENCER_10 0x300A
+#define WM8915_WRITE_SEQUENCER_11 0x300B
+#define WM8915_WRITE_SEQUENCER_12 0x300C
+#define WM8915_WRITE_SEQUENCER_13 0x300D
+#define WM8915_WRITE_SEQUENCER_14 0x300E
+#define WM8915_WRITE_SEQUENCER_15 0x300F
+#define WM8915_WRITE_SEQUENCER_16 0x3010
+#define WM8915_WRITE_SEQUENCER_17 0x3011
+#define WM8915_WRITE_SEQUENCER_18 0x3012
+#define WM8915_WRITE_SEQUENCER_19 0x3013
+#define WM8915_WRITE_SEQUENCER_20 0x3014
+#define WM8915_WRITE_SEQUENCER_21 0x3015
+#define WM8915_WRITE_SEQUENCER_22 0x3016
+#define WM8915_WRITE_SEQUENCER_23 0x3017
+#define WM8915_WRITE_SEQUENCER_24 0x3018
+#define WM8915_WRITE_SEQUENCER_25 0x3019
+#define WM8915_WRITE_SEQUENCER_26 0x301A
+#define WM8915_WRITE_SEQUENCER_27 0x301B
+#define WM8915_WRITE_SEQUENCER_28 0x301C
+#define WM8915_WRITE_SEQUENCER_29 0x301D
+#define WM8915_WRITE_SEQUENCER_30 0x301E
+#define WM8915_WRITE_SEQUENCER_31 0x301F
+#define WM8915_WRITE_SEQUENCER_32 0x3020
+#define WM8915_WRITE_SEQUENCER_33 0x3021
+#define WM8915_WRITE_SEQUENCER_34 0x3022
+#define WM8915_WRITE_SEQUENCER_35 0x3023
+#define WM8915_WRITE_SEQUENCER_36 0x3024
+#define WM8915_WRITE_SEQUENCER_37 0x3025
+#define WM8915_WRITE_SEQUENCER_38 0x3026
+#define WM8915_WRITE_SEQUENCER_39 0x3027
+#define WM8915_WRITE_SEQUENCER_40 0x3028
+#define WM8915_WRITE_SEQUENCER_41 0x3029
+#define WM8915_WRITE_SEQUENCER_42 0x302A
+#define WM8915_WRITE_SEQUENCER_43 0x302B
+#define WM8915_WRITE_SEQUENCER_44 0x302C
+#define WM8915_WRITE_SEQUENCER_45 0x302D
+#define WM8915_WRITE_SEQUENCER_46 0x302E
+#define WM8915_WRITE_SEQUENCER_47 0x302F
+#define WM8915_WRITE_SEQUENCER_48 0x3030
+#define WM8915_WRITE_SEQUENCER_49 0x3031
+#define WM8915_WRITE_SEQUENCER_50 0x3032
+#define WM8915_WRITE_SEQUENCER_51 0x3033
+#define WM8915_WRITE_SEQUENCER_52 0x3034
+#define WM8915_WRITE_SEQUENCER_53 0x3035
+#define WM8915_WRITE_SEQUENCER_54 0x3036
+#define WM8915_WRITE_SEQUENCER_55 0x3037
+#define WM8915_WRITE_SEQUENCER_56 0x3038
+#define WM8915_WRITE_SEQUENCER_57 0x3039
+#define WM8915_WRITE_SEQUENCER_58 0x303A
+#define WM8915_WRITE_SEQUENCER_59 0x303B
+#define WM8915_WRITE_SEQUENCER_60 0x303C
+#define WM8915_WRITE_SEQUENCER_61 0x303D
+#define WM8915_WRITE_SEQUENCER_62 0x303E
+#define WM8915_WRITE_SEQUENCER_63 0x303F
+#define WM8915_WRITE_SEQUENCER_64 0x3040
+#define WM8915_WRITE_SEQUENCER_65 0x3041
+#define WM8915_WRITE_SEQUENCER_66 0x3042
+#define WM8915_WRITE_SEQUENCER_67 0x3043
+#define WM8915_WRITE_SEQUENCER_68 0x3044
+#define WM8915_WRITE_SEQUENCER_69 0x3045
+#define WM8915_WRITE_SEQUENCER_70 0x3046
+#define WM8915_WRITE_SEQUENCER_71 0x3047
+#define WM8915_WRITE_SEQUENCER_72 0x3048
+#define WM8915_WRITE_SEQUENCER_73 0x3049
+#define WM8915_WRITE_SEQUENCER_74 0x304A
+#define WM8915_WRITE_SEQUENCER_75 0x304B
+#define WM8915_WRITE_SEQUENCER_76 0x304C
+#define WM8915_WRITE_SEQUENCER_77 0x304D
+#define WM8915_WRITE_SEQUENCER_78 0x304E
+#define WM8915_WRITE_SEQUENCER_79 0x304F
+#define WM8915_WRITE_SEQUENCER_80 0x3050
+#define WM8915_WRITE_SEQUENCER_81 0x3051
+#define WM8915_WRITE_SEQUENCER_82 0x3052
+#define WM8915_WRITE_SEQUENCER_83 0x3053
+#define WM8915_WRITE_SEQUENCER_84 0x3054
+#define WM8915_WRITE_SEQUENCER_85 0x3055
+#define WM8915_WRITE_SEQUENCER_86 0x3056
+#define WM8915_WRITE_SEQUENCER_87 0x3057
+#define WM8915_WRITE_SEQUENCER_88 0x3058
+#define WM8915_WRITE_SEQUENCER_89 0x3059
+#define WM8915_WRITE_SEQUENCER_90 0x305A
+#define WM8915_WRITE_SEQUENCER_91 0x305B
+#define WM8915_WRITE_SEQUENCER_92 0x305C
+#define WM8915_WRITE_SEQUENCER_93 0x305D
+#define WM8915_WRITE_SEQUENCER_94 0x305E
+#define WM8915_WRITE_SEQUENCER_95 0x305F
+#define WM8915_WRITE_SEQUENCER_96 0x3060
+#define WM8915_WRITE_SEQUENCER_97 0x3061
+#define WM8915_WRITE_SEQUENCER_98 0x3062
+#define WM8915_WRITE_SEQUENCER_99 0x3063
+#define WM8915_WRITE_SEQUENCER_100 0x3064
+#define WM8915_WRITE_SEQUENCER_101 0x3065
+#define WM8915_WRITE_SEQUENCER_102 0x3066
+#define WM8915_WRITE_SEQUENCER_103 0x3067
+#define WM8915_WRITE_SEQUENCER_104 0x3068
+#define WM8915_WRITE_SEQUENCER_105 0x3069
+#define WM8915_WRITE_SEQUENCER_106 0x306A
+#define WM8915_WRITE_SEQUENCER_107 0x306B
+#define WM8915_WRITE_SEQUENCER_108 0x306C
+#define WM8915_WRITE_SEQUENCER_109 0x306D
+#define WM8915_WRITE_SEQUENCER_110 0x306E
+#define WM8915_WRITE_SEQUENCER_111 0x306F
+#define WM8915_WRITE_SEQUENCER_112 0x3070
+#define WM8915_WRITE_SEQUENCER_113 0x3071
+#define WM8915_WRITE_SEQUENCER_114 0x3072
+#define WM8915_WRITE_SEQUENCER_115 0x3073
+#define WM8915_WRITE_SEQUENCER_116 0x3074
+#define WM8915_WRITE_SEQUENCER_117 0x3075
+#define WM8915_WRITE_SEQUENCER_118 0x3076
+#define WM8915_WRITE_SEQUENCER_119 0x3077
+#define WM8915_WRITE_SEQUENCER_120 0x3078
+#define WM8915_WRITE_SEQUENCER_121 0x3079
+#define WM8915_WRITE_SEQUENCER_122 0x307A
+#define WM8915_WRITE_SEQUENCER_123 0x307B
+#define WM8915_WRITE_SEQUENCER_124 0x307C
+#define WM8915_WRITE_SEQUENCER_125 0x307D
+#define WM8915_WRITE_SEQUENCER_126 0x307E
+#define WM8915_WRITE_SEQUENCER_127 0x307F
+#define WM8915_WRITE_SEQUENCER_128 0x3080
+#define WM8915_WRITE_SEQUENCER_129 0x3081
+#define WM8915_WRITE_SEQUENCER_130 0x3082
+#define WM8915_WRITE_SEQUENCER_131 0x3083
+#define WM8915_WRITE_SEQUENCER_132 0x3084
+#define WM8915_WRITE_SEQUENCER_133 0x3085
+#define WM8915_WRITE_SEQUENCER_134 0x3086
+#define WM8915_WRITE_SEQUENCER_135 0x3087
+#define WM8915_WRITE_SEQUENCER_136 0x3088
+#define WM8915_WRITE_SEQUENCER_137 0x3089
+#define WM8915_WRITE_SEQUENCER_138 0x308A
+#define WM8915_WRITE_SEQUENCER_139 0x308B
+#define WM8915_WRITE_SEQUENCER_140 0x308C
+#define WM8915_WRITE_SEQUENCER_141 0x308D
+#define WM8915_WRITE_SEQUENCER_142 0x308E
+#define WM8915_WRITE_SEQUENCER_143 0x308F
+#define WM8915_WRITE_SEQUENCER_144 0x3090
+#define WM8915_WRITE_SEQUENCER_145 0x3091
+#define WM8915_WRITE_SEQUENCER_146 0x3092
+#define WM8915_WRITE_SEQUENCER_147 0x3093
+#define WM8915_WRITE_SEQUENCER_148 0x3094
+#define WM8915_WRITE_SEQUENCER_149 0x3095
+#define WM8915_WRITE_SEQUENCER_150 0x3096
+#define WM8915_WRITE_SEQUENCER_151 0x3097
+#define WM8915_WRITE_SEQUENCER_152 0x3098
+#define WM8915_WRITE_SEQUENCER_153 0x3099
+#define WM8915_WRITE_SEQUENCER_154 0x309A
+#define WM8915_WRITE_SEQUENCER_155 0x309B
+#define WM8915_WRITE_SEQUENCER_156 0x309C
+#define WM8915_WRITE_SEQUENCER_157 0x309D
+#define WM8915_WRITE_SEQUENCER_158 0x309E
+#define WM8915_WRITE_SEQUENCER_159 0x309F
+#define WM8915_WRITE_SEQUENCER_160 0x30A0
+#define WM8915_WRITE_SEQUENCER_161 0x30A1
+#define WM8915_WRITE_SEQUENCER_162 0x30A2
+#define WM8915_WRITE_SEQUENCER_163 0x30A3
+#define WM8915_WRITE_SEQUENCER_164 0x30A4
+#define WM8915_WRITE_SEQUENCER_165 0x30A5
+#define WM8915_WRITE_SEQUENCER_166 0x30A6
+#define WM8915_WRITE_SEQUENCER_167 0x30A7
+#define WM8915_WRITE_SEQUENCER_168 0x30A8
+#define WM8915_WRITE_SEQUENCER_169 0x30A9
+#define WM8915_WRITE_SEQUENCER_170 0x30AA
+#define WM8915_WRITE_SEQUENCER_171 0x30AB
+#define WM8915_WRITE_SEQUENCER_172 0x30AC
+#define WM8915_WRITE_SEQUENCER_173 0x30AD
+#define WM8915_WRITE_SEQUENCER_174 0x30AE
+#define WM8915_WRITE_SEQUENCER_175 0x30AF
+#define WM8915_WRITE_SEQUENCER_176 0x30B0
+#define WM8915_WRITE_SEQUENCER_177 0x30B1
+#define WM8915_WRITE_SEQUENCER_178 0x30B2
+#define WM8915_WRITE_SEQUENCER_179 0x30B3
+#define WM8915_WRITE_SEQUENCER_180 0x30B4
+#define WM8915_WRITE_SEQUENCER_181 0x30B5
+#define WM8915_WRITE_SEQUENCER_182 0x30B6
+#define WM8915_WRITE_SEQUENCER_183 0x30B7
+#define WM8915_WRITE_SEQUENCER_184 0x30B8
+#define WM8915_WRITE_SEQUENCER_185 0x30B9
+#define WM8915_WRITE_SEQUENCER_186 0x30BA
+#define WM8915_WRITE_SEQUENCER_187 0x30BB
+#define WM8915_WRITE_SEQUENCER_188 0x30BC
+#define WM8915_WRITE_SEQUENCER_189 0x30BD
+#define WM8915_WRITE_SEQUENCER_190 0x30BE
+#define WM8915_WRITE_SEQUENCER_191 0x30BF
+#define WM8915_WRITE_SEQUENCER_192 0x30C0
+#define WM8915_WRITE_SEQUENCER_193 0x30C1
+#define WM8915_WRITE_SEQUENCER_194 0x30C2
+#define WM8915_WRITE_SEQUENCER_195 0x30C3
+#define WM8915_WRITE_SEQUENCER_196 0x30C4
+#define WM8915_WRITE_SEQUENCER_197 0x30C5
+#define WM8915_WRITE_SEQUENCER_198 0x30C6
+#define WM8915_WRITE_SEQUENCER_199 0x30C7
+#define WM8915_WRITE_SEQUENCER_200 0x30C8
+#define WM8915_WRITE_SEQUENCER_201 0x30C9
+#define WM8915_WRITE_SEQUENCER_202 0x30CA
+#define WM8915_WRITE_SEQUENCER_203 0x30CB
+#define WM8915_WRITE_SEQUENCER_204 0x30CC
+#define WM8915_WRITE_SEQUENCER_205 0x30CD
+#define WM8915_WRITE_SEQUENCER_206 0x30CE
+#define WM8915_WRITE_SEQUENCER_207 0x30CF
+#define WM8915_WRITE_SEQUENCER_208 0x30D0
+#define WM8915_WRITE_SEQUENCER_209 0x30D1
+#define WM8915_WRITE_SEQUENCER_210 0x30D2
+#define WM8915_WRITE_SEQUENCER_211 0x30D3
+#define WM8915_WRITE_SEQUENCER_212 0x30D4
+#define WM8915_WRITE_SEQUENCER_213 0x30D5
+#define WM8915_WRITE_SEQUENCER_214 0x30D6
+#define WM8915_WRITE_SEQUENCER_215 0x30D7
+#define WM8915_WRITE_SEQUENCER_216 0x30D8
+#define WM8915_WRITE_SEQUENCER_217 0x30D9
+#define WM8915_WRITE_SEQUENCER_218 0x30DA
+#define WM8915_WRITE_SEQUENCER_219 0x30DB
+#define WM8915_WRITE_SEQUENCER_220 0x30DC
+#define WM8915_WRITE_SEQUENCER_221 0x30DD
+#define WM8915_WRITE_SEQUENCER_222 0x30DE
+#define WM8915_WRITE_SEQUENCER_223 0x30DF
+#define WM8915_WRITE_SEQUENCER_224 0x30E0
+#define WM8915_WRITE_SEQUENCER_225 0x30E1
+#define WM8915_WRITE_SEQUENCER_226 0x30E2
+#define WM8915_WRITE_SEQUENCER_227 0x30E3
+#define WM8915_WRITE_SEQUENCER_228 0x30E4
+#define WM8915_WRITE_SEQUENCER_229 0x30E5
+#define WM8915_WRITE_SEQUENCER_230 0x30E6
+#define WM8915_WRITE_SEQUENCER_231 0x30E7
+#define WM8915_WRITE_SEQUENCER_232 0x30E8
+#define WM8915_WRITE_SEQUENCER_233 0x30E9
+#define WM8915_WRITE_SEQUENCER_234 0x30EA
+#define WM8915_WRITE_SEQUENCER_235 0x30EB
+#define WM8915_WRITE_SEQUENCER_236 0x30EC
+#define WM8915_WRITE_SEQUENCER_237 0x30ED
+#define WM8915_WRITE_SEQUENCER_238 0x30EE
+#define WM8915_WRITE_SEQUENCER_239 0x30EF
+#define WM8915_WRITE_SEQUENCER_240 0x30F0
+#define WM8915_WRITE_SEQUENCER_241 0x30F1
+#define WM8915_WRITE_SEQUENCER_242 0x30F2
+#define WM8915_WRITE_SEQUENCER_243 0x30F3
+#define WM8915_WRITE_SEQUENCER_244 0x30F4
+#define WM8915_WRITE_SEQUENCER_245 0x30F5
+#define WM8915_WRITE_SEQUENCER_246 0x30F6
+#define WM8915_WRITE_SEQUENCER_247 0x30F7
+#define WM8915_WRITE_SEQUENCER_248 0x30F8
+#define WM8915_WRITE_SEQUENCER_249 0x30F9
+#define WM8915_WRITE_SEQUENCER_250 0x30FA
+#define WM8915_WRITE_SEQUENCER_251 0x30FB
+#define WM8915_WRITE_SEQUENCER_252 0x30FC
+#define WM8915_WRITE_SEQUENCER_253 0x30FD
+#define WM8915_WRITE_SEQUENCER_254 0x30FE
+#define WM8915_WRITE_SEQUENCER_255 0x30FF
+#define WM8915_WRITE_SEQUENCER_256 0x3100
+#define WM8915_WRITE_SEQUENCER_257 0x3101
+#define WM8915_WRITE_SEQUENCER_258 0x3102
+#define WM8915_WRITE_SEQUENCER_259 0x3103
+#define WM8915_WRITE_SEQUENCER_260 0x3104
+#define WM8915_WRITE_SEQUENCER_261 0x3105
+#define WM8915_WRITE_SEQUENCER_262 0x3106
+#define WM8915_WRITE_SEQUENCER_263 0x3107
+#define WM8915_WRITE_SEQUENCER_264 0x3108
+#define WM8915_WRITE_SEQUENCER_265 0x3109
+#define WM8915_WRITE_SEQUENCER_266 0x310A
+#define WM8915_WRITE_SEQUENCER_267 0x310B
+#define WM8915_WRITE_SEQUENCER_268 0x310C
+#define WM8915_WRITE_SEQUENCER_269 0x310D
+#define WM8915_WRITE_SEQUENCER_270 0x310E
+#define WM8915_WRITE_SEQUENCER_271 0x310F
+#define WM8915_WRITE_SEQUENCER_272 0x3110
+#define WM8915_WRITE_SEQUENCER_273 0x3111
+#define WM8915_WRITE_SEQUENCER_274 0x3112
+#define WM8915_WRITE_SEQUENCER_275 0x3113
+#define WM8915_WRITE_SEQUENCER_276 0x3114
+#define WM8915_WRITE_SEQUENCER_277 0x3115
+#define WM8915_WRITE_SEQUENCER_278 0x3116
+#define WM8915_WRITE_SEQUENCER_279 0x3117
+#define WM8915_WRITE_SEQUENCER_280 0x3118
+#define WM8915_WRITE_SEQUENCER_281 0x3119
+#define WM8915_WRITE_SEQUENCER_282 0x311A
+#define WM8915_WRITE_SEQUENCER_283 0x311B
+#define WM8915_WRITE_SEQUENCER_284 0x311C
+#define WM8915_WRITE_SEQUENCER_285 0x311D
+#define WM8915_WRITE_SEQUENCER_286 0x311E
+#define WM8915_WRITE_SEQUENCER_287 0x311F
+#define WM8915_WRITE_SEQUENCER_288 0x3120
+#define WM8915_WRITE_SEQUENCER_289 0x3121
+#define WM8915_WRITE_SEQUENCER_290 0x3122
+#define WM8915_WRITE_SEQUENCER_291 0x3123
+#define WM8915_WRITE_SEQUENCER_292 0x3124
+#define WM8915_WRITE_SEQUENCER_293 0x3125
+#define WM8915_WRITE_SEQUENCER_294 0x3126
+#define WM8915_WRITE_SEQUENCER_295 0x3127
+#define WM8915_WRITE_SEQUENCER_296 0x3128
+#define WM8915_WRITE_SEQUENCER_297 0x3129
+#define WM8915_WRITE_SEQUENCER_298 0x312A
+#define WM8915_WRITE_SEQUENCER_299 0x312B
+#define WM8915_WRITE_SEQUENCER_300 0x312C
+#define WM8915_WRITE_SEQUENCER_301 0x312D
+#define WM8915_WRITE_SEQUENCER_302 0x312E
+#define WM8915_WRITE_SEQUENCER_303 0x312F
+#define WM8915_WRITE_SEQUENCER_304 0x3130
+#define WM8915_WRITE_SEQUENCER_305 0x3131
+#define WM8915_WRITE_SEQUENCER_306 0x3132
+#define WM8915_WRITE_SEQUENCER_307 0x3133
+#define WM8915_WRITE_SEQUENCER_308 0x3134
+#define WM8915_WRITE_SEQUENCER_309 0x3135
+#define WM8915_WRITE_SEQUENCER_310 0x3136
+#define WM8915_WRITE_SEQUENCER_311 0x3137
+#define WM8915_WRITE_SEQUENCER_312 0x3138
+#define WM8915_WRITE_SEQUENCER_313 0x3139
+#define WM8915_WRITE_SEQUENCER_314 0x313A
+#define WM8915_WRITE_SEQUENCER_315 0x313B
+#define WM8915_WRITE_SEQUENCER_316 0x313C
+#define WM8915_WRITE_SEQUENCER_317 0x313D
+#define WM8915_WRITE_SEQUENCER_318 0x313E
+#define WM8915_WRITE_SEQUENCER_319 0x313F
+#define WM8915_WRITE_SEQUENCER_320 0x3140
+#define WM8915_WRITE_SEQUENCER_321 0x3141
+#define WM8915_WRITE_SEQUENCER_322 0x3142
+#define WM8915_WRITE_SEQUENCER_323 0x3143
+#define WM8915_WRITE_SEQUENCER_324 0x3144
+#define WM8915_WRITE_SEQUENCER_325 0x3145
+#define WM8915_WRITE_SEQUENCER_326 0x3146
+#define WM8915_WRITE_SEQUENCER_327 0x3147
+#define WM8915_WRITE_SEQUENCER_328 0x3148
+#define WM8915_WRITE_SEQUENCER_329 0x3149
+#define WM8915_WRITE_SEQUENCER_330 0x314A
+#define WM8915_WRITE_SEQUENCER_331 0x314B
+#define WM8915_WRITE_SEQUENCER_332 0x314C
+#define WM8915_WRITE_SEQUENCER_333 0x314D
+#define WM8915_WRITE_SEQUENCER_334 0x314E
+#define WM8915_WRITE_SEQUENCER_335 0x314F
+#define WM8915_WRITE_SEQUENCER_336 0x3150
+#define WM8915_WRITE_SEQUENCER_337 0x3151
+#define WM8915_WRITE_SEQUENCER_338 0x3152
+#define WM8915_WRITE_SEQUENCER_339 0x3153
+#define WM8915_WRITE_SEQUENCER_340 0x3154
+#define WM8915_WRITE_SEQUENCER_341 0x3155
+#define WM8915_WRITE_SEQUENCER_342 0x3156
+#define WM8915_WRITE_SEQUENCER_343 0x3157
+#define WM8915_WRITE_SEQUENCER_344 0x3158
+#define WM8915_WRITE_SEQUENCER_345 0x3159
+#define WM8915_WRITE_SEQUENCER_346 0x315A
+#define WM8915_WRITE_SEQUENCER_347 0x315B
+#define WM8915_WRITE_SEQUENCER_348 0x315C
+#define WM8915_WRITE_SEQUENCER_349 0x315D
+#define WM8915_WRITE_SEQUENCER_350 0x315E
+#define WM8915_WRITE_SEQUENCER_351 0x315F
+#define WM8915_WRITE_SEQUENCER_352 0x3160
+#define WM8915_WRITE_SEQUENCER_353 0x3161
+#define WM8915_WRITE_SEQUENCER_354 0x3162
+#define WM8915_WRITE_SEQUENCER_355 0x3163
+#define WM8915_WRITE_SEQUENCER_356 0x3164
+#define WM8915_WRITE_SEQUENCER_357 0x3165
+#define WM8915_WRITE_SEQUENCER_358 0x3166
+#define WM8915_WRITE_SEQUENCER_359 0x3167
+#define WM8915_WRITE_SEQUENCER_360 0x3168
+#define WM8915_WRITE_SEQUENCER_361 0x3169
+#define WM8915_WRITE_SEQUENCER_362 0x316A
+#define WM8915_WRITE_SEQUENCER_363 0x316B
+#define WM8915_WRITE_SEQUENCER_364 0x316C
+#define WM8915_WRITE_SEQUENCER_365 0x316D
+#define WM8915_WRITE_SEQUENCER_366 0x316E
+#define WM8915_WRITE_SEQUENCER_367 0x316F
+#define WM8915_WRITE_SEQUENCER_368 0x3170
+#define WM8915_WRITE_SEQUENCER_369 0x3171
+#define WM8915_WRITE_SEQUENCER_370 0x3172
+#define WM8915_WRITE_SEQUENCER_371 0x3173
+#define WM8915_WRITE_SEQUENCER_372 0x3174
+#define WM8915_WRITE_SEQUENCER_373 0x3175
+#define WM8915_WRITE_SEQUENCER_374 0x3176
+#define WM8915_WRITE_SEQUENCER_375 0x3177
+#define WM8915_WRITE_SEQUENCER_376 0x3178
+#define WM8915_WRITE_SEQUENCER_377 0x3179
+#define WM8915_WRITE_SEQUENCER_378 0x317A
+#define WM8915_WRITE_SEQUENCER_379 0x317B
+#define WM8915_WRITE_SEQUENCER_380 0x317C
+#define WM8915_WRITE_SEQUENCER_381 0x317D
+#define WM8915_WRITE_SEQUENCER_382 0x317E
+#define WM8915_WRITE_SEQUENCER_383 0x317F
+#define WM8915_WRITE_SEQUENCER_384 0x3180
+#define WM8915_WRITE_SEQUENCER_385 0x3181
+#define WM8915_WRITE_SEQUENCER_386 0x3182
+#define WM8915_WRITE_SEQUENCER_387 0x3183
+#define WM8915_WRITE_SEQUENCER_388 0x3184
+#define WM8915_WRITE_SEQUENCER_389 0x3185
+#define WM8915_WRITE_SEQUENCER_390 0x3186
+#define WM8915_WRITE_SEQUENCER_391 0x3187
+#define WM8915_WRITE_SEQUENCER_392 0x3188
+#define WM8915_WRITE_SEQUENCER_393 0x3189
+#define WM8915_WRITE_SEQUENCER_394 0x318A
+#define WM8915_WRITE_SEQUENCER_395 0x318B
+#define WM8915_WRITE_SEQUENCER_396 0x318C
+#define WM8915_WRITE_SEQUENCER_397 0x318D
+#define WM8915_WRITE_SEQUENCER_398 0x318E
+#define WM8915_WRITE_SEQUENCER_399 0x318F
+#define WM8915_WRITE_SEQUENCER_400 0x3190
+#define WM8915_WRITE_SEQUENCER_401 0x3191
+#define WM8915_WRITE_SEQUENCER_402 0x3192
+#define WM8915_WRITE_SEQUENCER_403 0x3193
+#define WM8915_WRITE_SEQUENCER_404 0x3194
+#define WM8915_WRITE_SEQUENCER_405 0x3195
+#define WM8915_WRITE_SEQUENCER_406 0x3196
+#define WM8915_WRITE_SEQUENCER_407 0x3197
+#define WM8915_WRITE_SEQUENCER_408 0x3198
+#define WM8915_WRITE_SEQUENCER_409 0x3199
+#define WM8915_WRITE_SEQUENCER_410 0x319A
+#define WM8915_WRITE_SEQUENCER_411 0x319B
+#define WM8915_WRITE_SEQUENCER_412 0x319C
+#define WM8915_WRITE_SEQUENCER_413 0x319D
+#define WM8915_WRITE_SEQUENCER_414 0x319E
+#define WM8915_WRITE_SEQUENCER_415 0x319F
+#define WM8915_WRITE_SEQUENCER_416 0x31A0
+#define WM8915_WRITE_SEQUENCER_417 0x31A1
+#define WM8915_WRITE_SEQUENCER_418 0x31A2
+#define WM8915_WRITE_SEQUENCER_419 0x31A3
+#define WM8915_WRITE_SEQUENCER_420 0x31A4
+#define WM8915_WRITE_SEQUENCER_421 0x31A5
+#define WM8915_WRITE_SEQUENCER_422 0x31A6
+#define WM8915_WRITE_SEQUENCER_423 0x31A7
+#define WM8915_WRITE_SEQUENCER_424 0x31A8
+#define WM8915_WRITE_SEQUENCER_425 0x31A9
+#define WM8915_WRITE_SEQUENCER_426 0x31AA
+#define WM8915_WRITE_SEQUENCER_427 0x31AB
+#define WM8915_WRITE_SEQUENCER_428 0x31AC
+#define WM8915_WRITE_SEQUENCER_429 0x31AD
+#define WM8915_WRITE_SEQUENCER_430 0x31AE
+#define WM8915_WRITE_SEQUENCER_431 0x31AF
+#define WM8915_WRITE_SEQUENCER_432 0x31B0
+#define WM8915_WRITE_SEQUENCER_433 0x31B1
+#define WM8915_WRITE_SEQUENCER_434 0x31B2
+#define WM8915_WRITE_SEQUENCER_435 0x31B3
+#define WM8915_WRITE_SEQUENCER_436 0x31B4
+#define WM8915_WRITE_SEQUENCER_437 0x31B5
+#define WM8915_WRITE_SEQUENCER_438 0x31B6
+#define WM8915_WRITE_SEQUENCER_439 0x31B7
+#define WM8915_WRITE_SEQUENCER_440 0x31B8
+#define WM8915_WRITE_SEQUENCER_441 0x31B9
+#define WM8915_WRITE_SEQUENCER_442 0x31BA
+#define WM8915_WRITE_SEQUENCER_443 0x31BB
+#define WM8915_WRITE_SEQUENCER_444 0x31BC
+#define WM8915_WRITE_SEQUENCER_445 0x31BD
+#define WM8915_WRITE_SEQUENCER_446 0x31BE
+#define WM8915_WRITE_SEQUENCER_447 0x31BF
+#define WM8915_WRITE_SEQUENCER_448 0x31C0
+#define WM8915_WRITE_SEQUENCER_449 0x31C1
+#define WM8915_WRITE_SEQUENCER_450 0x31C2
+#define WM8915_WRITE_SEQUENCER_451 0x31C3
+#define WM8915_WRITE_SEQUENCER_452 0x31C4
+#define WM8915_WRITE_SEQUENCER_453 0x31C5
+#define WM8915_WRITE_SEQUENCER_454 0x31C6
+#define WM8915_WRITE_SEQUENCER_455 0x31C7
+#define WM8915_WRITE_SEQUENCER_456 0x31C8
+#define WM8915_WRITE_SEQUENCER_457 0x31C9
+#define WM8915_WRITE_SEQUENCER_458 0x31CA
+#define WM8915_WRITE_SEQUENCER_459 0x31CB
+#define WM8915_WRITE_SEQUENCER_460 0x31CC
+#define WM8915_WRITE_SEQUENCER_461 0x31CD
+#define WM8915_WRITE_SEQUENCER_462 0x31CE
+#define WM8915_WRITE_SEQUENCER_463 0x31CF
+#define WM8915_WRITE_SEQUENCER_464 0x31D0
+#define WM8915_WRITE_SEQUENCER_465 0x31D1
+#define WM8915_WRITE_SEQUENCER_466 0x31D2
+#define WM8915_WRITE_SEQUENCER_467 0x31D3
+#define WM8915_WRITE_SEQUENCER_468 0x31D4
+#define WM8915_WRITE_SEQUENCER_469 0x31D5
+#define WM8915_WRITE_SEQUENCER_470 0x31D6
+#define WM8915_WRITE_SEQUENCER_471 0x31D7
+#define WM8915_WRITE_SEQUENCER_472 0x31D8
+#define WM8915_WRITE_SEQUENCER_473 0x31D9
+#define WM8915_WRITE_SEQUENCER_474 0x31DA
+#define WM8915_WRITE_SEQUENCER_475 0x31DB
+#define WM8915_WRITE_SEQUENCER_476 0x31DC
+#define WM8915_WRITE_SEQUENCER_477 0x31DD
+#define WM8915_WRITE_SEQUENCER_478 0x31DE
+#define WM8915_WRITE_SEQUENCER_479 0x31DF
+#define WM8915_WRITE_SEQUENCER_480 0x31E0
+#define WM8915_WRITE_SEQUENCER_481 0x31E1
+#define WM8915_WRITE_SEQUENCER_482 0x31E2
+#define WM8915_WRITE_SEQUENCER_483 0x31E3
+#define WM8915_WRITE_SEQUENCER_484 0x31E4
+#define WM8915_WRITE_SEQUENCER_485 0x31E5
+#define WM8915_WRITE_SEQUENCER_486 0x31E6
+#define WM8915_WRITE_SEQUENCER_487 0x31E7
+#define WM8915_WRITE_SEQUENCER_488 0x31E8
+#define WM8915_WRITE_SEQUENCER_489 0x31E9
+#define WM8915_WRITE_SEQUENCER_490 0x31EA
+#define WM8915_WRITE_SEQUENCER_491 0x31EB
+#define WM8915_WRITE_SEQUENCER_492 0x31EC
+#define WM8915_WRITE_SEQUENCER_493 0x31ED
+#define WM8915_WRITE_SEQUENCER_494 0x31EE
+#define WM8915_WRITE_SEQUENCER_495 0x31EF
+#define WM8915_WRITE_SEQUENCER_496 0x31F0
+#define WM8915_WRITE_SEQUENCER_497 0x31F1
+#define WM8915_WRITE_SEQUENCER_498 0x31F2
+#define WM8915_WRITE_SEQUENCER_499 0x31F3
+#define WM8915_WRITE_SEQUENCER_500 0x31F4
+#define WM8915_WRITE_SEQUENCER_501 0x31F5
+#define WM8915_WRITE_SEQUENCER_502 0x31F6
+#define WM8915_WRITE_SEQUENCER_503 0x31F7
+#define WM8915_WRITE_SEQUENCER_504 0x31F8
+#define WM8915_WRITE_SEQUENCER_505 0x31F9
+#define WM8915_WRITE_SEQUENCER_506 0x31FA
+#define WM8915_WRITE_SEQUENCER_507 0x31FB
+#define WM8915_WRITE_SEQUENCER_508 0x31FC
+#define WM8915_WRITE_SEQUENCER_509 0x31FD
+#define WM8915_WRITE_SEQUENCER_510 0x31FE
+#define WM8915_WRITE_SEQUENCER_511 0x31FF
+
+#define WM8915_REGISTER_COUNT 706
+#define WM8915_MAX_REGISTER 0x31FF
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM8915_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */
+#define WM8915_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */
+#define WM8915_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8915_MICB2_ENA 0x0200 /* MICB2_ENA */
+#define WM8915_MICB2_ENA_MASK 0x0200 /* MICB2_ENA */
+#define WM8915_MICB2_ENA_SHIFT 9 /* MICB2_ENA */
+#define WM8915_MICB2_ENA_WIDTH 1 /* MICB2_ENA */
+#define WM8915_MICB1_ENA 0x0100 /* MICB1_ENA */
+#define WM8915_MICB1_ENA_MASK 0x0100 /* MICB1_ENA */
+#define WM8915_MICB1_ENA_SHIFT 8 /* MICB1_ENA */
+#define WM8915_MICB1_ENA_WIDTH 1 /* MICB1_ENA */
+#define WM8915_HPOUT2L_ENA 0x0080 /* HPOUT2L_ENA */
+#define WM8915_HPOUT2L_ENA_MASK 0x0080 /* HPOUT2L_ENA */
+#define WM8915_HPOUT2L_ENA_SHIFT 7 /* HPOUT2L_ENA */
+#define WM8915_HPOUT2L_ENA_WIDTH 1 /* HPOUT2L_ENA */
+#define WM8915_HPOUT2R_ENA 0x0040 /* HPOUT2R_ENA */
+#define WM8915_HPOUT2R_ENA_MASK 0x0040 /* HPOUT2R_ENA */
+#define WM8915_HPOUT2R_ENA_SHIFT 6 /* HPOUT2R_ENA */
+#define WM8915_HPOUT2R_ENA_WIDTH 1 /* HPOUT2R_ENA */
+#define WM8915_HPOUT1L_ENA 0x0020 /* HPOUT1L_ENA */
+#define WM8915_HPOUT1L_ENA_MASK 0x0020 /* HPOUT1L_ENA */
+#define WM8915_HPOUT1L_ENA_SHIFT 5 /* HPOUT1L_ENA */
+#define WM8915_HPOUT1L_ENA_WIDTH 1 /* HPOUT1L_ENA */
+#define WM8915_HPOUT1R_ENA 0x0010 /* HPOUT1R_ENA */
+#define WM8915_HPOUT1R_ENA_MASK 0x0010 /* HPOUT1R_ENA */
+#define WM8915_HPOUT1R_ENA_SHIFT 4 /* HPOUT1R_ENA */
+#define WM8915_HPOUT1R_ENA_WIDTH 1 /* HPOUT1R_ENA */
+#define WM8915_BG_ENA 0x0001 /* BG_ENA */
+#define WM8915_BG_ENA_MASK 0x0001 /* BG_ENA */
+#define WM8915_BG_ENA_SHIFT 0 /* BG_ENA */
+#define WM8915_BG_ENA_WIDTH 1 /* BG_ENA */
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8915_OPCLK_ENA 0x0800 /* OPCLK_ENA */
+#define WM8915_OPCLK_ENA_MASK 0x0800 /* OPCLK_ENA */
+#define WM8915_OPCLK_ENA_SHIFT 11 /* OPCLK_ENA */
+#define WM8915_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */
+#define WM8915_INL_ENA 0x0020 /* INL_ENA */
+#define WM8915_INL_ENA_MASK 0x0020 /* INL_ENA */
+#define WM8915_INL_ENA_SHIFT 5 /* INL_ENA */
+#define WM8915_INL_ENA_WIDTH 1 /* INL_ENA */
+#define WM8915_INR_ENA 0x0010 /* INR_ENA */
+#define WM8915_INR_ENA_MASK 0x0010 /* INR_ENA */
+#define WM8915_INR_ENA_SHIFT 4 /* INR_ENA */
+#define WM8915_INR_ENA_WIDTH 1 /* INR_ENA */
+#define WM8915_LDO2_ENA 0x0002 /* LDO2_ENA */
+#define WM8915_LDO2_ENA_MASK 0x0002 /* LDO2_ENA */
+#define WM8915_LDO2_ENA_SHIFT 1 /* LDO2_ENA */
+#define WM8915_LDO2_ENA_WIDTH 1 /* LDO2_ENA */
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8915_DSP2RXL_ENA 0x0800 /* DSP2RXL_ENA */
+#define WM8915_DSP2RXL_ENA_MASK 0x0800 /* DSP2RXL_ENA */
+#define WM8915_DSP2RXL_ENA_SHIFT 11 /* DSP2RXL_ENA */
+#define WM8915_DSP2RXL_ENA_WIDTH 1 /* DSP2RXL_ENA */
+#define WM8915_DSP2RXR_ENA 0x0400 /* DSP2RXR_ENA */
+#define WM8915_DSP2RXR_ENA_MASK 0x0400 /* DSP2RXR_ENA */
+#define WM8915_DSP2RXR_ENA_SHIFT 10 /* DSP2RXR_ENA */
+#define WM8915_DSP2RXR_ENA_WIDTH 1 /* DSP2RXR_ENA */
+#define WM8915_DSP1RXL_ENA 0x0200 /* DSP1RXL_ENA */
+#define WM8915_DSP1RXL_ENA_MASK 0x0200 /* DSP1RXL_ENA */
+#define WM8915_DSP1RXL_ENA_SHIFT 9 /* DSP1RXL_ENA */
+#define WM8915_DSP1RXL_ENA_WIDTH 1 /* DSP1RXL_ENA */
+#define WM8915_DSP1RXR_ENA 0x0100 /* DSP1RXR_ENA */
+#define WM8915_DSP1RXR_ENA_MASK 0x0100 /* DSP1RXR_ENA */
+#define WM8915_DSP1RXR_ENA_SHIFT 8 /* DSP1RXR_ENA */
+#define WM8915_DSP1RXR_ENA_WIDTH 1 /* DSP1RXR_ENA */
+#define WM8915_DMIC2L_ENA 0x0020 /* DMIC2L_ENA */
+#define WM8915_DMIC2L_ENA_MASK 0x0020 /* DMIC2L_ENA */
+#define WM8915_DMIC2L_ENA_SHIFT 5 /* DMIC2L_ENA */
+#define WM8915_DMIC2L_ENA_WIDTH 1 /* DMIC2L_ENA */
+#define WM8915_DMIC2R_ENA 0x0010 /* DMIC2R_ENA */
+#define WM8915_DMIC2R_ENA_MASK 0x0010 /* DMIC2R_ENA */
+#define WM8915_DMIC2R_ENA_SHIFT 4 /* DMIC2R_ENA */
+#define WM8915_DMIC2R_ENA_WIDTH 1 /* DMIC2R_ENA */
+#define WM8915_DMIC1L_ENA 0x0008 /* DMIC1L_ENA */
+#define WM8915_DMIC1L_ENA_MASK 0x0008 /* DMIC1L_ENA */
+#define WM8915_DMIC1L_ENA_SHIFT 3 /* DMIC1L_ENA */
+#define WM8915_DMIC1L_ENA_WIDTH 1 /* DMIC1L_ENA */
+#define WM8915_DMIC1R_ENA 0x0004 /* DMIC1R_ENA */
+#define WM8915_DMIC1R_ENA_MASK 0x0004 /* DMIC1R_ENA */
+#define WM8915_DMIC1R_ENA_SHIFT 2 /* DMIC1R_ENA */
+#define WM8915_DMIC1R_ENA_WIDTH 1 /* DMIC1R_ENA */
+#define WM8915_ADCL_ENA 0x0002 /* ADCL_ENA */
+#define WM8915_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */
+#define WM8915_ADCL_ENA_SHIFT 1 /* ADCL_ENA */
+#define WM8915_ADCL_ENA_WIDTH 1 /* ADCL_ENA */
+#define WM8915_ADCR_ENA 0x0001 /* ADCR_ENA */
+#define WM8915_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */
+#define WM8915_ADCR_ENA_SHIFT 0 /* ADCR_ENA */
+#define WM8915_ADCR_ENA_WIDTH 1 /* ADCR_ENA */
+
+/*
+ * R4 (0x04) - Power Management (4)
+ */
+#define WM8915_AIF2RX_CHAN1_ENA 0x0200 /* AIF2RX_CHAN1_ENA */
+#define WM8915_AIF2RX_CHAN1_ENA_MASK 0x0200 /* AIF2RX_CHAN1_ENA */
+#define WM8915_AIF2RX_CHAN1_ENA_SHIFT 9 /* AIF2RX_CHAN1_ENA */
+#define WM8915_AIF2RX_CHAN1_ENA_WIDTH 1 /* AIF2RX_CHAN1_ENA */
+#define WM8915_AIF2RX_CHAN0_ENA 0x0100 /* AIF2RX_CHAN0_ENA */
+#define WM8915_AIF2RX_CHAN0_ENA_MASK 0x0100 /* AIF2RX_CHAN0_ENA */
+#define WM8915_AIF2RX_CHAN0_ENA_SHIFT 8 /* AIF2RX_CHAN0_ENA */
+#define WM8915_AIF2RX_CHAN0_ENA_WIDTH 1 /* AIF2RX_CHAN0_ENA */
+#define WM8915_AIF1RX_CHAN5_ENA 0x0020 /* AIF1RX_CHAN5_ENA */
+#define WM8915_AIF1RX_CHAN5_ENA_MASK 0x0020 /* AIF1RX_CHAN5_ENA */
+#define WM8915_AIF1RX_CHAN5_ENA_SHIFT 5 /* AIF1RX_CHAN5_ENA */
+#define WM8915_AIF1RX_CHAN5_ENA_WIDTH 1 /* AIF1RX_CHAN5_ENA */
+#define WM8915_AIF1RX_CHAN4_ENA 0x0010 /* AIF1RX_CHAN4_ENA */
+#define WM8915_AIF1RX_CHAN4_ENA_MASK 0x0010 /* AIF1RX_CHAN4_ENA */
+#define WM8915_AIF1RX_CHAN4_ENA_SHIFT 4 /* AIF1RX_CHAN4_ENA */
+#define WM8915_AIF1RX_CHAN4_ENA_WIDTH 1 /* AIF1RX_CHAN4_ENA */
+#define WM8915_AIF1RX_CHAN3_ENA 0x0008 /* AIF1RX_CHAN3_ENA */
+#define WM8915_AIF1RX_CHAN3_ENA_MASK 0x0008 /* AIF1RX_CHAN3_ENA */
+#define WM8915_AIF1RX_CHAN3_ENA_SHIFT 3 /* AIF1RX_CHAN3_ENA */
+#define WM8915_AIF1RX_CHAN3_ENA_WIDTH 1 /* AIF1RX_CHAN3_ENA */
+#define WM8915_AIF1RX_CHAN2_ENA 0x0004 /* AIF1RX_CHAN2_ENA */
+#define WM8915_AIF1RX_CHAN2_ENA_MASK 0x0004 /* AIF1RX_CHAN2_ENA */
+#define WM8915_AIF1RX_CHAN2_ENA_SHIFT 2 /* AIF1RX_CHAN2_ENA */
+#define WM8915_AIF1RX_CHAN2_ENA_WIDTH 1 /* AIF1RX_CHAN2_ENA */
+#define WM8915_AIF1RX_CHAN1_ENA 0x0002 /* AIF1RX_CHAN1_ENA */
+#define WM8915_AIF1RX_CHAN1_ENA_MASK 0x0002 /* AIF1RX_CHAN1_ENA */
+#define WM8915_AIF1RX_CHAN1_ENA_SHIFT 1 /* AIF1RX_CHAN1_ENA */
+#define WM8915_AIF1RX_CHAN1_ENA_WIDTH 1 /* AIF1RX_CHAN1_ENA */
+#define WM8915_AIF1RX_CHAN0_ENA 0x0001 /* AIF1RX_CHAN0_ENA */
+#define WM8915_AIF1RX_CHAN0_ENA_MASK 0x0001 /* AIF1RX_CHAN0_ENA */
+#define WM8915_AIF1RX_CHAN0_ENA_SHIFT 0 /* AIF1RX_CHAN0_ENA */
+#define WM8915_AIF1RX_CHAN0_ENA_WIDTH 1 /* AIF1RX_CHAN0_ENA */
+
+/*
+ * R5 (0x05) - Power Management (5)
+ */
+#define WM8915_DSP2TXL_ENA 0x0800 /* DSP2TXL_ENA */
+#define WM8915_DSP2TXL_ENA_MASK 0x0800 /* DSP2TXL_ENA */
+#define WM8915_DSP2TXL_ENA_SHIFT 11 /* DSP2TXL_ENA */
+#define WM8915_DSP2TXL_ENA_WIDTH 1 /* DSP2TXL_ENA */
+#define WM8915_DSP2TXR_ENA 0x0400 /* DSP2TXR_ENA */
+#define WM8915_DSP2TXR_ENA_MASK 0x0400 /* DSP2TXR_ENA */
+#define WM8915_DSP2TXR_ENA_SHIFT 10 /* DSP2TXR_ENA */
+#define WM8915_DSP2TXR_ENA_WIDTH 1 /* DSP2TXR_ENA */
+#define WM8915_DSP1TXL_ENA 0x0200 /* DSP1TXL_ENA */
+#define WM8915_DSP1TXL_ENA_MASK 0x0200 /* DSP1TXL_ENA */
+#define WM8915_DSP1TXL_ENA_SHIFT 9 /* DSP1TXL_ENA */
+#define WM8915_DSP1TXL_ENA_WIDTH 1 /* DSP1TXL_ENA */
+#define WM8915_DSP1TXR_ENA 0x0100 /* DSP1TXR_ENA */
+#define WM8915_DSP1TXR_ENA_MASK 0x0100 /* DSP1TXR_ENA */
+#define WM8915_DSP1TXR_ENA_SHIFT 8 /* DSP1TXR_ENA */
+#define WM8915_DSP1TXR_ENA_WIDTH 1 /* DSP1TXR_ENA */
+#define WM8915_DAC2L_ENA 0x0008 /* DAC2L_ENA */
+#define WM8915_DAC2L_ENA_MASK 0x0008 /* DAC2L_ENA */
+#define WM8915_DAC2L_ENA_SHIFT 3 /* DAC2L_ENA */
+#define WM8915_DAC2L_ENA_WIDTH 1 /* DAC2L_ENA */
+#define WM8915_DAC2R_ENA 0x0004 /* DAC2R_ENA */
+#define WM8915_DAC2R_ENA_MASK 0x0004 /* DAC2R_ENA */
+#define WM8915_DAC2R_ENA_SHIFT 2 /* DAC2R_ENA */
+#define WM8915_DAC2R_ENA_WIDTH 1 /* DAC2R_ENA */
+#define WM8915_DAC1L_ENA 0x0002 /* DAC1L_ENA */
+#define WM8915_DAC1L_ENA_MASK 0x0002 /* DAC1L_ENA */
+#define WM8915_DAC1L_ENA_SHIFT 1 /* DAC1L_ENA */
+#define WM8915_DAC1L_ENA_WIDTH 1 /* DAC1L_ENA */
+#define WM8915_DAC1R_ENA 0x0001 /* DAC1R_ENA */
+#define WM8915_DAC1R_ENA_MASK 0x0001 /* DAC1R_ENA */
+#define WM8915_DAC1R_ENA_SHIFT 0 /* DAC1R_ENA */
+#define WM8915_DAC1R_ENA_WIDTH 1 /* DAC1R_ENA */
+
+/*
+ * R6 (0x06) - Power Management (6)
+ */
+#define WM8915_AIF2TX_CHAN1_ENA 0x0200 /* AIF2TX_CHAN1_ENA */
+#define WM8915_AIF2TX_CHAN1_ENA_MASK 0x0200 /* AIF2TX_CHAN1_ENA */
+#define WM8915_AIF2TX_CHAN1_ENA_SHIFT 9 /* AIF2TX_CHAN1_ENA */
+#define WM8915_AIF2TX_CHAN1_ENA_WIDTH 1 /* AIF2TX_CHAN1_ENA */
+#define WM8915_AIF2TX_CHAN0_ENA 0x0100 /* AIF2TX_CHAN0_ENA */
+#define WM8915_AIF2TX_CHAN0_ENA_MASK 0x0100 /* AIF2TX_CHAN0_ENA */
+#define WM8915_AIF2TX_CHAN0_ENA_SHIFT 8 /* AIF2TX_CHAN0_ENA */
+#define WM8915_AIF2TX_CHAN0_ENA_WIDTH 1 /* AIF2TX_CHAN0_ENA */
+#define WM8915_AIF1TX_CHAN5_ENA 0x0020 /* AIF1TX_CHAN5_ENA */
+#define WM8915_AIF1TX_CHAN5_ENA_MASK 0x0020 /* AIF1TX_CHAN5_ENA */
+#define WM8915_AIF1TX_CHAN5_ENA_SHIFT 5 /* AIF1TX_CHAN5_ENA */
+#define WM8915_AIF1TX_CHAN5_ENA_WIDTH 1 /* AIF1TX_CHAN5_ENA */
+#define WM8915_AIF1TX_CHAN4_ENA 0x0010 /* AIF1TX_CHAN4_ENA */
+#define WM8915_AIF1TX_CHAN4_ENA_MASK 0x0010 /* AIF1TX_CHAN4_ENA */
+#define WM8915_AIF1TX_CHAN4_ENA_SHIFT 4 /* AIF1TX_CHAN4_ENA */
+#define WM8915_AIF1TX_CHAN4_ENA_WIDTH 1 /* AIF1TX_CHAN4_ENA */
+#define WM8915_AIF1TX_CHAN3_ENA 0x0008 /* AIF1TX_CHAN3_ENA */
+#define WM8915_AIF1TX_CHAN3_ENA_MASK 0x0008 /* AIF1TX_CHAN3_ENA */
+#define WM8915_AIF1TX_CHAN3_ENA_SHIFT 3 /* AIF1TX_CHAN3_ENA */
+#define WM8915_AIF1TX_CHAN3_ENA_WIDTH 1 /* AIF1TX_CHAN3_ENA */
+#define WM8915_AIF1TX_CHAN2_ENA 0x0004 /* AIF1TX_CHAN2_ENA */
+#define WM8915_AIF1TX_CHAN2_ENA_MASK 0x0004 /* AIF1TX_CHAN2_ENA */
+#define WM8915_AIF1TX_CHAN2_ENA_SHIFT 2 /* AIF1TX_CHAN2_ENA */
+#define WM8915_AIF1TX_CHAN2_ENA_WIDTH 1 /* AIF1TX_CHAN2_ENA */
+#define WM8915_AIF1TX_CHAN1_ENA 0x0002 /* AIF1TX_CHAN1_ENA */
+#define WM8915_AIF1TX_CHAN1_ENA_MASK 0x0002 /* AIF1TX_CHAN1_ENA */
+#define WM8915_AIF1TX_CHAN1_ENA_SHIFT 1 /* AIF1TX_CHAN1_ENA */
+#define WM8915_AIF1TX_CHAN1_ENA_WIDTH 1 /* AIF1TX_CHAN1_ENA */
+#define WM8915_AIF1TX_CHAN0_ENA 0x0001 /* AIF1TX_CHAN0_ENA */
+#define WM8915_AIF1TX_CHAN0_ENA_MASK 0x0001 /* AIF1TX_CHAN0_ENA */
+#define WM8915_AIF1TX_CHAN0_ENA_SHIFT 0 /* AIF1TX_CHAN0_ENA */
+#define WM8915_AIF1TX_CHAN0_ENA_WIDTH 1 /* AIF1TX_CHAN0_ENA */
+
+/*
+ * R7 (0x07) - Power Management (7)
+ */
+#define WM8915_DMIC2_FN 0x0200 /* DMIC2_FN */
+#define WM8915_DMIC2_FN_MASK 0x0200 /* DMIC2_FN */
+#define WM8915_DMIC2_FN_SHIFT 9 /* DMIC2_FN */
+#define WM8915_DMIC2_FN_WIDTH 1 /* DMIC2_FN */
+#define WM8915_DMIC1_FN 0x0100 /* DMIC1_FN */
+#define WM8915_DMIC1_FN_MASK 0x0100 /* DMIC1_FN */
+#define WM8915_DMIC1_FN_SHIFT 8 /* DMIC1_FN */
+#define WM8915_DMIC1_FN_WIDTH 1 /* DMIC1_FN */
+#define WM8915_ADC_DMIC_DSP2R_ENA 0x0080 /* ADC_DMIC_DSP2R_ENA */
+#define WM8915_ADC_DMIC_DSP2R_ENA_MASK 0x0080 /* ADC_DMIC_DSP2R_ENA */
+#define WM8915_ADC_DMIC_DSP2R_ENA_SHIFT 7 /* ADC_DMIC_DSP2R_ENA */
+#define WM8915_ADC_DMIC_DSP2R_ENA_WIDTH 1 /* ADC_DMIC_DSP2R_ENA */
+#define WM8915_ADC_DMIC_DSP2L_ENA 0x0040 /* ADC_DMIC_DSP2L_ENA */
+#define WM8915_ADC_DMIC_DSP2L_ENA_MASK 0x0040 /* ADC_DMIC_DSP2L_ENA */
+#define WM8915_ADC_DMIC_DSP2L_ENA_SHIFT 6 /* ADC_DMIC_DSP2L_ENA */
+#define WM8915_ADC_DMIC_DSP2L_ENA_WIDTH 1 /* ADC_DMIC_DSP2L_ENA */
+#define WM8915_ADC_DMIC_SRC2_MASK 0x0030 /* ADC_DMIC_SRC2 - [5:4] */
+#define WM8915_ADC_DMIC_SRC2_SHIFT 4 /* ADC_DMIC_SRC2 - [5:4] */
+#define WM8915_ADC_DMIC_SRC2_WIDTH 2 /* ADC_DMIC_SRC2 - [5:4] */
+#define WM8915_ADC_DMIC_DSP1R_ENA 0x0008 /* ADC_DMIC_DSP1R_ENA */
+#define WM8915_ADC_DMIC_DSP1R_ENA_MASK 0x0008 /* ADC_DMIC_DSP1R_ENA */
+#define WM8915_ADC_DMIC_DSP1R_ENA_SHIFT 3 /* ADC_DMIC_DSP1R_ENA */
+#define WM8915_ADC_DMIC_DSP1R_ENA_WIDTH 1 /* ADC_DMIC_DSP1R_ENA */
+#define WM8915_ADC_DMIC_DSP1L_ENA 0x0004 /* ADC_DMIC_DSP1L_ENA */
+#define WM8915_ADC_DMIC_DSP1L_ENA_MASK 0x0004 /* ADC_DMIC_DSP1L_ENA */
+#define WM8915_ADC_DMIC_DSP1L_ENA_SHIFT 2 /* ADC_DMIC_DSP1L_ENA */
+#define WM8915_ADC_DMIC_DSP1L_ENA_WIDTH 1 /* ADC_DMIC_DSP1L_ENA */
+#define WM8915_ADC_DMIC_SRC1_MASK 0x0003 /* ADC_DMIC_SRC1 - [1:0] */
+#define WM8915_ADC_DMIC_SRC1_SHIFT 0 /* ADC_DMIC_SRC1 - [1:0] */
+#define WM8915_ADC_DMIC_SRC1_WIDTH 2 /* ADC_DMIC_SRC1 - [1:0] */
+
+/*
+ * R8 (0x08) - Power Management (8)
+ */
+#define WM8915_AIF2TX_SRC_MASK 0x00C0 /* AIF2TX_SRC - [7:6] */
+#define WM8915_AIF2TX_SRC_SHIFT 6 /* AIF2TX_SRC - [7:6] */
+#define WM8915_AIF2TX_SRC_WIDTH 2 /* AIF2TX_SRC - [7:6] */
+#define WM8915_DSP2RX_SRC 0x0010 /* DSP2RX_SRC */
+#define WM8915_DSP2RX_SRC_MASK 0x0010 /* DSP2RX_SRC */
+#define WM8915_DSP2RX_SRC_SHIFT 4 /* DSP2RX_SRC */
+#define WM8915_DSP2RX_SRC_WIDTH 1 /* DSP2RX_SRC */
+#define WM8915_DSP1RX_SRC 0x0001 /* DSP1RX_SRC */
+#define WM8915_DSP1RX_SRC_MASK 0x0001 /* DSP1RX_SRC */
+#define WM8915_DSP1RX_SRC_SHIFT 0 /* DSP1RX_SRC */
+#define WM8915_DSP1RX_SRC_WIDTH 1 /* DSP1RX_SRC */
+
+/*
+ * R16 (0x10) - Left Line Input Volume
+ */
+#define WM8915_IN1_VU 0x0080 /* IN1_VU */
+#define WM8915_IN1_VU_MASK 0x0080 /* IN1_VU */
+#define WM8915_IN1_VU_SHIFT 7 /* IN1_VU */
+#define WM8915_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM8915_IN1L_ZC 0x0020 /* IN1L_ZC */
+#define WM8915_IN1L_ZC_MASK 0x0020 /* IN1L_ZC */
+#define WM8915_IN1L_ZC_SHIFT 5 /* IN1L_ZC */
+#define WM8915_IN1L_ZC_WIDTH 1 /* IN1L_ZC */
+#define WM8915_IN1L_VOL_MASK 0x001F /* IN1L_VOL - [4:0] */
+#define WM8915_IN1L_VOL_SHIFT 0 /* IN1L_VOL - [4:0] */
+#define WM8915_IN1L_VOL_WIDTH 5 /* IN1L_VOL - [4:0] */
+
+/*
+ * R17 (0x11) - Right Line Input Volume
+ */
+#define WM8915_IN1_VU 0x0080 /* IN1_VU */
+#define WM8915_IN1_VU_MASK 0x0080 /* IN1_VU */
+#define WM8915_IN1_VU_SHIFT 7 /* IN1_VU */
+#define WM8915_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM8915_IN1R_ZC 0x0020 /* IN1R_ZC */
+#define WM8915_IN1R_ZC_MASK 0x0020 /* IN1R_ZC */
+#define WM8915_IN1R_ZC_SHIFT 5 /* IN1R_ZC */
+#define WM8915_IN1R_ZC_WIDTH 1 /* IN1R_ZC */
+#define WM8915_IN1R_VOL_MASK 0x001F /* IN1R_VOL - [4:0] */
+#define WM8915_IN1R_VOL_SHIFT 0 /* IN1R_VOL - [4:0] */
+#define WM8915_IN1R_VOL_WIDTH 5 /* IN1R_VOL - [4:0] */
+
+/*
+ * R18 (0x12) - Line Input Control
+ */
+#define WM8915_INL_MODE_MASK 0x000C /* INL_MODE - [3:2] */
+#define WM8915_INL_MODE_SHIFT 2 /* INL_MODE - [3:2] */
+#define WM8915_INL_MODE_WIDTH 2 /* INL_MODE - [3:2] */
+#define WM8915_INR_MODE_MASK 0x0003 /* INR_MODE - [1:0] */
+#define WM8915_INR_MODE_SHIFT 0 /* INR_MODE - [1:0] */
+#define WM8915_INR_MODE_WIDTH 2 /* INR_MODE - [1:0] */
+
+/*
+ * R21 (0x15) - DAC1 HPOUT1 Volume
+ */
+#define WM8915_DAC1R_HPOUT1R_VOL_MASK 0x00F0 /* DAC1R_HPOUT1R_VOL - [7:4] */
+#define WM8915_DAC1R_HPOUT1R_VOL_SHIFT 4 /* DAC1R_HPOUT1R_VOL - [7:4] */
+#define WM8915_DAC1R_HPOUT1R_VOL_WIDTH 4 /* DAC1R_HPOUT1R_VOL - [7:4] */
+#define WM8915_DAC1L_HPOUT1L_VOL_MASK 0x000F /* DAC1L_HPOUT1L_VOL - [3:0] */
+#define WM8915_DAC1L_HPOUT1L_VOL_SHIFT 0 /* DAC1L_HPOUT1L_VOL - [3:0] */
+#define WM8915_DAC1L_HPOUT1L_VOL_WIDTH 4 /* DAC1L_HPOUT1L_VOL - [3:0] */
+
+/*
+ * R22 (0x16) - DAC2 HPOUT2 Volume
+ */
+#define WM8915_DAC2R_HPOUT2R_VOL_MASK 0x00F0 /* DAC2R_HPOUT2R_VOL - [7:4] */
+#define WM8915_DAC2R_HPOUT2R_VOL_SHIFT 4 /* DAC2R_HPOUT2R_VOL - [7:4] */
+#define WM8915_DAC2R_HPOUT2R_VOL_WIDTH 4 /* DAC2R_HPOUT2R_VOL - [7:4] */
+#define WM8915_DAC2L_HPOUT2L_VOL_MASK 0x000F /* DAC2L_HPOUT2L_VOL - [3:0] */
+#define WM8915_DAC2L_HPOUT2L_VOL_SHIFT 0 /* DAC2L_HPOUT2L_VOL - [3:0] */
+#define WM8915_DAC2L_HPOUT2L_VOL_WIDTH 4 /* DAC2L_HPOUT2L_VOL - [3:0] */
+
+/*
+ * R24 (0x18) - DAC1 Left Volume
+ */
+#define WM8915_DAC1L_MUTE 0x0200 /* DAC1L_MUTE */
+#define WM8915_DAC1L_MUTE_MASK 0x0200 /* DAC1L_MUTE */
+#define WM8915_DAC1L_MUTE_SHIFT 9 /* DAC1L_MUTE */
+#define WM8915_DAC1L_MUTE_WIDTH 1 /* DAC1L_MUTE */
+#define WM8915_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8915_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8915_DAC1L_VOL_MASK 0x00FF /* DAC1L_VOL - [7:0] */
+#define WM8915_DAC1L_VOL_SHIFT 0 /* DAC1L_VOL - [7:0] */
+#define WM8915_DAC1L_VOL_WIDTH 8 /* DAC1L_VOL - [7:0] */
+
+/*
+ * R25 (0x19) - DAC1 Right Volume
+ */
+#define WM8915_DAC1R_MUTE 0x0200 /* DAC1R_MUTE */
+#define WM8915_DAC1R_MUTE_MASK 0x0200 /* DAC1R_MUTE */
+#define WM8915_DAC1R_MUTE_SHIFT 9 /* DAC1R_MUTE */
+#define WM8915_DAC1R_MUTE_WIDTH 1 /* DAC1R_MUTE */
+#define WM8915_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8915_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8915_DAC1R_VOL_MASK 0x00FF /* DAC1R_VOL - [7:0] */
+#define WM8915_DAC1R_VOL_SHIFT 0 /* DAC1R_VOL - [7:0] */
+#define WM8915_DAC1R_VOL_WIDTH 8 /* DAC1R_VOL - [7:0] */
+
+/*
+ * R26 (0x1A) - DAC2 Left Volume
+ */
+#define WM8915_DAC2L_MUTE 0x0200 /* DAC2L_MUTE */
+#define WM8915_DAC2L_MUTE_MASK 0x0200 /* DAC2L_MUTE */
+#define WM8915_DAC2L_MUTE_SHIFT 9 /* DAC2L_MUTE */
+#define WM8915_DAC2L_MUTE_WIDTH 1 /* DAC2L_MUTE */
+#define WM8915_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8915_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8915_DAC2L_VOL_MASK 0x00FF /* DAC2L_VOL - [7:0] */
+#define WM8915_DAC2L_VOL_SHIFT 0 /* DAC2L_VOL - [7:0] */
+#define WM8915_DAC2L_VOL_WIDTH 8 /* DAC2L_VOL - [7:0] */
+
+/*
+ * R27 (0x1B) - DAC2 Right Volume
+ */
+#define WM8915_DAC2R_MUTE 0x0200 /* DAC2R_MUTE */
+#define WM8915_DAC2R_MUTE_MASK 0x0200 /* DAC2R_MUTE */
+#define WM8915_DAC2R_MUTE_SHIFT 9 /* DAC2R_MUTE */
+#define WM8915_DAC2R_MUTE_WIDTH 1 /* DAC2R_MUTE */
+#define WM8915_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8915_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8915_DAC2R_VOL_MASK 0x00FF /* DAC2R_VOL - [7:0] */
+#define WM8915_DAC2R_VOL_SHIFT 0 /* DAC2R_VOL - [7:0] */
+#define WM8915_DAC2R_VOL_WIDTH 8 /* DAC2R_VOL - [7:0] */
+
+/*
+ * R28 (0x1C) - Output1 Left Volume
+ */
+#define WM8915_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8915_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8915_HPOUT1L_ZC 0x0080 /* HPOUT1L_ZC */
+#define WM8915_HPOUT1L_ZC_MASK 0x0080 /* HPOUT1L_ZC */
+#define WM8915_HPOUT1L_ZC_SHIFT 7 /* HPOUT1L_ZC */
+#define WM8915_HPOUT1L_ZC_WIDTH 1 /* HPOUT1L_ZC */
+#define WM8915_HPOUT1L_VOL_MASK 0x000F /* HPOUT1L_VOL - [3:0] */
+#define WM8915_HPOUT1L_VOL_SHIFT 0 /* HPOUT1L_VOL - [3:0] */
+#define WM8915_HPOUT1L_VOL_WIDTH 4 /* HPOUT1L_VOL - [3:0] */
+
+/*
+ * R29 (0x1D) - Output1 Right Volume
+ */
+#define WM8915_DAC1_VU 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_MASK 0x0100 /* DAC1_VU */
+#define WM8915_DAC1_VU_SHIFT 8 /* DAC1_VU */
+#define WM8915_DAC1_VU_WIDTH 1 /* DAC1_VU */
+#define WM8915_HPOUT1R_ZC 0x0080 /* HPOUT1R_ZC */
+#define WM8915_HPOUT1R_ZC_MASK 0x0080 /* HPOUT1R_ZC */
+#define WM8915_HPOUT1R_ZC_SHIFT 7 /* HPOUT1R_ZC */
+#define WM8915_HPOUT1R_ZC_WIDTH 1 /* HPOUT1R_ZC */
+#define WM8915_HPOUT1R_VOL_MASK 0x000F /* HPOUT1R_VOL - [3:0] */
+#define WM8915_HPOUT1R_VOL_SHIFT 0 /* HPOUT1R_VOL - [3:0] */
+#define WM8915_HPOUT1R_VOL_WIDTH 4 /* HPOUT1R_VOL - [3:0] */
+
+/*
+ * R30 (0x1E) - Output2 Left Volume
+ */
+#define WM8915_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8915_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8915_HPOUT2L_ZC 0x0080 /* HPOUT2L_ZC */
+#define WM8915_HPOUT2L_ZC_MASK 0x0080 /* HPOUT2L_ZC */
+#define WM8915_HPOUT2L_ZC_SHIFT 7 /* HPOUT2L_ZC */
+#define WM8915_HPOUT2L_ZC_WIDTH 1 /* HPOUT2L_ZC */
+#define WM8915_HPOUT2L_VOL_MASK 0x000F /* HPOUT2L_VOL - [3:0] */
+#define WM8915_HPOUT2L_VOL_SHIFT 0 /* HPOUT2L_VOL - [3:0] */
+#define WM8915_HPOUT2L_VOL_WIDTH 4 /* HPOUT2L_VOL - [3:0] */
+
+/*
+ * R31 (0x1F) - Output2 Right Volume
+ */
+#define WM8915_DAC2_VU 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_MASK 0x0100 /* DAC2_VU */
+#define WM8915_DAC2_VU_SHIFT 8 /* DAC2_VU */
+#define WM8915_DAC2_VU_WIDTH 1 /* DAC2_VU */
+#define WM8915_HPOUT2R_ZC 0x0080 /* HPOUT2R_ZC */
+#define WM8915_HPOUT2R_ZC_MASK 0x0080 /* HPOUT2R_ZC */
+#define WM8915_HPOUT2R_ZC_SHIFT 7 /* HPOUT2R_ZC */
+#define WM8915_HPOUT2R_ZC_WIDTH 1 /* HPOUT2R_ZC */
+#define WM8915_HPOUT2R_VOL_MASK 0x000F /* HPOUT2R_VOL - [3:0] */
+#define WM8915_HPOUT2R_VOL_SHIFT 0 /* HPOUT2R_VOL - [3:0] */
+#define WM8915_HPOUT2R_VOL_WIDTH 4 /* HPOUT2R_VOL - [3:0] */
+
+/*
+ * R32 (0x20) - MICBIAS (1)
+ */
+#define WM8915_MICB1_RATE 0x0020 /* MICB1_RATE */
+#define WM8915_MICB1_RATE_MASK 0x0020 /* MICB1_RATE */
+#define WM8915_MICB1_RATE_SHIFT 5 /* MICB1_RATE */
+#define WM8915_MICB1_RATE_WIDTH 1 /* MICB1_RATE */
+#define WM8915_MICB1_MODE 0x0010 /* MICB1_MODE */
+#define WM8915_MICB1_MODE_MASK 0x0010 /* MICB1_MODE */
+#define WM8915_MICB1_MODE_SHIFT 4 /* MICB1_MODE */
+#define WM8915_MICB1_MODE_WIDTH 1 /* MICB1_MODE */
+#define WM8915_MICB1_LVL_MASK 0x000E /* MICB1_LVL - [3:1] */
+#define WM8915_MICB1_LVL_SHIFT 1 /* MICB1_LVL - [3:1] */
+#define WM8915_MICB1_LVL_WIDTH 3 /* MICB1_LVL - [3:1] */
+#define WM8915_MICB1_DISCH 0x0001 /* MICB1_DISCH */
+#define WM8915_MICB1_DISCH_MASK 0x0001 /* MICB1_DISCH */
+#define WM8915_MICB1_DISCH_SHIFT 0 /* MICB1_DISCH */
+#define WM8915_MICB1_DISCH_WIDTH 1 /* MICB1_DISCH */
+
+/*
+ * R33 (0x21) - MICBIAS (2)
+ */
+#define WM8915_MICB2_RATE 0x0020 /* MICB2_RATE */
+#define WM8915_MICB2_RATE_MASK 0x0020 /* MICB2_RATE */
+#define WM8915_MICB2_RATE_SHIFT 5 /* MICB2_RATE */
+#define WM8915_MICB2_RATE_WIDTH 1 /* MICB2_RATE */
+#define WM8915_MICB2_MODE 0x0010 /* MICB2_MODE */
+#define WM8915_MICB2_MODE_MASK 0x0010 /* MICB2_MODE */
+#define WM8915_MICB2_MODE_SHIFT 4 /* MICB2_MODE */
+#define WM8915_MICB2_MODE_WIDTH 1 /* MICB2_MODE */
+#define WM8915_MICB2_LVL_MASK 0x000E /* MICB2_LVL - [3:1] */
+#define WM8915_MICB2_LVL_SHIFT 1 /* MICB2_LVL - [3:1] */
+#define WM8915_MICB2_LVL_WIDTH 3 /* MICB2_LVL - [3:1] */
+#define WM8915_MICB2_DISCH 0x0001 /* MICB2_DISCH */
+#define WM8915_MICB2_DISCH_MASK 0x0001 /* MICB2_DISCH */
+#define WM8915_MICB2_DISCH_SHIFT 0 /* MICB2_DISCH */
+#define WM8915_MICB2_DISCH_WIDTH 1 /* MICB2_DISCH */
+
+/*
+ * R40 (0x28) - LDO 1
+ */
+#define WM8915_LDO1_MODE 0x0020 /* LDO1_MODE */
+#define WM8915_LDO1_MODE_MASK 0x0020 /* LDO1_MODE */
+#define WM8915_LDO1_MODE_SHIFT 5 /* LDO1_MODE */
+#define WM8915_LDO1_MODE_WIDTH 1 /* LDO1_MODE */
+#define WM8915_LDO1_VSEL_MASK 0x0006 /* LDO1_VSEL - [2:1] */
+#define WM8915_LDO1_VSEL_SHIFT 1 /* LDO1_VSEL - [2:1] */
+#define WM8915_LDO1_VSEL_WIDTH 2 /* LDO1_VSEL - [2:1] */
+#define WM8915_LDO1_DISCH 0x0001 /* LDO1_DISCH */
+#define WM8915_LDO1_DISCH_MASK 0x0001 /* LDO1_DISCH */
+#define WM8915_LDO1_DISCH_SHIFT 0 /* LDO1_DISCH */
+#define WM8915_LDO1_DISCH_WIDTH 1 /* LDO1_DISCH */
+
+/*
+ * R41 (0x29) - LDO 2
+ */
+#define WM8915_LDO2_MODE 0x0020 /* LDO2_MODE */
+#define WM8915_LDO2_MODE_MASK 0x0020 /* LDO2_MODE */
+#define WM8915_LDO2_MODE_SHIFT 5 /* LDO2_MODE */
+#define WM8915_LDO2_MODE_WIDTH 1 /* LDO2_MODE */
+#define WM8915_LDO2_VSEL_MASK 0x001E /* LDO2_VSEL - [4:1] */
+#define WM8915_LDO2_VSEL_SHIFT 1 /* LDO2_VSEL - [4:1] */
+#define WM8915_LDO2_VSEL_WIDTH 4 /* LDO2_VSEL - [4:1] */
+#define WM8915_LDO2_DISCH 0x0001 /* LDO2_DISCH */
+#define WM8915_LDO2_DISCH_MASK 0x0001 /* LDO2_DISCH */
+#define WM8915_LDO2_DISCH_SHIFT 0 /* LDO2_DISCH */
+#define WM8915_LDO2_DISCH_WIDTH 1 /* LDO2_DISCH */
+
+/*
+ * R48 (0x30) - Accessory Detect Mode 1
+ */
+#define WM8915_JD_MODE_MASK 0x0003 /* JD_MODE - [1:0] */
+#define WM8915_JD_MODE_SHIFT 0 /* JD_MODE - [1:0] */
+#define WM8915_JD_MODE_WIDTH 2 /* JD_MODE - [1:0] */
+
+/*
+ * R49 (0x31) - Accessory Detect Mode 2
+ */
+#define WM8915_HPOUT1FB_SRC 0x0004 /* HPOUT1FB_SRC */
+#define WM8915_HPOUT1FB_SRC_MASK 0x0004 /* HPOUT1FB_SRC */
+#define WM8915_HPOUT1FB_SRC_SHIFT 2 /* HPOUT1FB_SRC */
+#define WM8915_HPOUT1FB_SRC_WIDTH 1 /* HPOUT1FB_SRC */
+#define WM8915_MICD_SRC 0x0002 /* MICD_SRC */
+#define WM8915_MICD_SRC_MASK 0x0002 /* MICD_SRC */
+#define WM8915_MICD_SRC_SHIFT 1 /* MICD_SRC */
+#define WM8915_MICD_SRC_WIDTH 1 /* MICD_SRC */
+#define WM8915_MICD_BIAS_SRC 0x0001 /* MICD_BIAS_SRC */
+#define WM8915_MICD_BIAS_SRC_MASK 0x0001 /* MICD_BIAS_SRC */
+#define WM8915_MICD_BIAS_SRC_SHIFT 0 /* MICD_BIAS_SRC */
+#define WM8915_MICD_BIAS_SRC_WIDTH 1 /* MICD_BIAS_SRC */
+
+/*
+ * R52 (0x34) - Headphone Detect 1
+ */
+#define WM8915_HP_HOLDTIME_MASK 0x00E0 /* HP_HOLDTIME - [7:5] */
+#define WM8915_HP_HOLDTIME_SHIFT 5 /* HP_HOLDTIME - [7:5] */
+#define WM8915_HP_HOLDTIME_WIDTH 3 /* HP_HOLDTIME - [7:5] */
+#define WM8915_HP_CLK_DIV_MASK 0x0018 /* HP_CLK_DIV - [4:3] */
+#define WM8915_HP_CLK_DIV_SHIFT 3 /* HP_CLK_DIV - [4:3] */
+#define WM8915_HP_CLK_DIV_WIDTH 2 /* HP_CLK_DIV - [4:3] */
+#define WM8915_HP_STEP_SIZE 0x0002 /* HP_STEP_SIZE */
+#define WM8915_HP_STEP_SIZE_MASK 0x0002 /* HP_STEP_SIZE */
+#define WM8915_HP_STEP_SIZE_SHIFT 1 /* HP_STEP_SIZE */
+#define WM8915_HP_STEP_SIZE_WIDTH 1 /* HP_STEP_SIZE */
+#define WM8915_HP_POLL 0x0001 /* HP_POLL */
+#define WM8915_HP_POLL_MASK 0x0001 /* HP_POLL */
+#define WM8915_HP_POLL_SHIFT 0 /* HP_POLL */
+#define WM8915_HP_POLL_WIDTH 1 /* HP_POLL */
+
+/*
+ * R53 (0x35) - Headphone Detect 2
+ */
+#define WM8915_HP_DONE 0x0080 /* HP_DONE */
+#define WM8915_HP_DONE_MASK 0x0080 /* HP_DONE */
+#define WM8915_HP_DONE_SHIFT 7 /* HP_DONE */
+#define WM8915_HP_DONE_WIDTH 1 /* HP_DONE */
+#define WM8915_HP_LVL_MASK 0x007F /* HP_LVL - [6:0] */
+#define WM8915_HP_LVL_SHIFT 0 /* HP_LVL - [6:0] */
+#define WM8915_HP_LVL_WIDTH 7 /* HP_LVL - [6:0] */
+
+/*
+ * R56 (0x38) - Mic Detect 1
+ */
+#define WM8915_MICD_BIAS_STARTTIME_MASK 0xF000 /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8915_MICD_BIAS_STARTTIME_SHIFT 12 /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8915_MICD_BIAS_STARTTIME_WIDTH 4 /* MICD_BIAS_STARTTIME - [15:12] */
+#define WM8915_MICD_RATE_MASK 0x0F00 /* MICD_RATE - [11:8] */
+#define WM8915_MICD_RATE_SHIFT 8 /* MICD_RATE - [11:8] */
+#define WM8915_MICD_RATE_WIDTH 4 /* MICD_RATE - [11:8] */
+#define WM8915_MICD_DBTIME 0x0002 /* MICD_DBTIME */
+#define WM8915_MICD_DBTIME_MASK 0x0002 /* MICD_DBTIME */
+#define WM8915_MICD_DBTIME_SHIFT 1 /* MICD_DBTIME */
+#define WM8915_MICD_DBTIME_WIDTH 1 /* MICD_DBTIME */
+#define WM8915_MICD_ENA 0x0001 /* MICD_ENA */
+#define WM8915_MICD_ENA_MASK 0x0001 /* MICD_ENA */
+#define WM8915_MICD_ENA_SHIFT 0 /* MICD_ENA */
+#define WM8915_MICD_ENA_WIDTH 1 /* MICD_ENA */
+
+/*
+ * R57 (0x39) - Mic Detect 2
+ */
+#define WM8915_MICD_LVL_SEL_MASK 0x00FF /* MICD_LVL_SEL - [7:0] */
+#define WM8915_MICD_LVL_SEL_SHIFT 0 /* MICD_LVL_SEL - [7:0] */
+#define WM8915_MICD_LVL_SEL_WIDTH 8 /* MICD_LVL_SEL - [7:0] */
+
+/*
+ * R58 (0x3A) - Mic Detect 3
+ */
+#define WM8915_MICD_LVL_MASK 0x07FC /* MICD_LVL - [10:2] */
+#define WM8915_MICD_LVL_SHIFT 2 /* MICD_LVL - [10:2] */
+#define WM8915_MICD_LVL_WIDTH 9 /* MICD_LVL - [10:2] */
+#define WM8915_MICD_VALID 0x0002 /* MICD_VALID */
+#define WM8915_MICD_VALID_MASK 0x0002 /* MICD_VALID */
+#define WM8915_MICD_VALID_SHIFT 1 /* MICD_VALID */
+#define WM8915_MICD_VALID_WIDTH 1 /* MICD_VALID */
+#define WM8915_MICD_STS 0x0001 /* MICD_STS */
+#define WM8915_MICD_STS_MASK 0x0001 /* MICD_STS */
+#define WM8915_MICD_STS_SHIFT 0 /* MICD_STS */
+#define WM8915_MICD_STS_WIDTH 1 /* MICD_STS */
+
+/*
+ * R64 (0x40) - Charge Pump (1)
+ */
+#define WM8915_CP_ENA 0x8000 /* CP_ENA */
+#define WM8915_CP_ENA_MASK 0x8000 /* CP_ENA */
+#define WM8915_CP_ENA_SHIFT 15 /* CP_ENA */
+#define WM8915_CP_ENA_WIDTH 1 /* CP_ENA */
+
+/*
+ * R65 (0x41) - Charge Pump (2)
+ */
+#define WM8915_CP_DISCH 0x8000 /* CP_DISCH */
+#define WM8915_CP_DISCH_MASK 0x8000 /* CP_DISCH */
+#define WM8915_CP_DISCH_SHIFT 15 /* CP_DISCH */
+#define WM8915_CP_DISCH_WIDTH 1 /* CP_DISCH */
+
+/*
+ * R80 (0x50) - DC Servo (1)
+ */
+#define WM8915_DCS_ENA_CHAN_3 0x0008 /* DCS_ENA_CHAN_3 */
+#define WM8915_DCS_ENA_CHAN_3_MASK 0x0008 /* DCS_ENA_CHAN_3 */
+#define WM8915_DCS_ENA_CHAN_3_SHIFT 3 /* DCS_ENA_CHAN_3 */
+#define WM8915_DCS_ENA_CHAN_3_WIDTH 1 /* DCS_ENA_CHAN_3 */
+#define WM8915_DCS_ENA_CHAN_2 0x0004 /* DCS_ENA_CHAN_2 */
+#define WM8915_DCS_ENA_CHAN_2_MASK 0x0004 /* DCS_ENA_CHAN_2 */
+#define WM8915_DCS_ENA_CHAN_2_SHIFT 2 /* DCS_ENA_CHAN_2 */
+#define WM8915_DCS_ENA_CHAN_2_WIDTH 1 /* DCS_ENA_CHAN_2 */
+#define WM8915_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8915_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8915_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */
+#define WM8915_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */
+#define WM8915_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8915_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8915_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */
+#define WM8915_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */
+
+/*
+ * R81 (0x51) - DC Servo (2)
+ */
+#define WM8915_DCS_TRIG_SINGLE_3 0x8000 /* DCS_TRIG_SINGLE_3 */
+#define WM8915_DCS_TRIG_SINGLE_3_MASK 0x8000 /* DCS_TRIG_SINGLE_3 */
+#define WM8915_DCS_TRIG_SINGLE_3_SHIFT 15 /* DCS_TRIG_SINGLE_3 */
+#define WM8915_DCS_TRIG_SINGLE_3_WIDTH 1 /* DCS_TRIG_SINGLE_3 */
+#define WM8915_DCS_TRIG_SINGLE_2 0x4000 /* DCS_TRIG_SINGLE_2 */
+#define WM8915_DCS_TRIG_SINGLE_2_MASK 0x4000 /* DCS_TRIG_SINGLE_2 */
+#define WM8915_DCS_TRIG_SINGLE_2_SHIFT 14 /* DCS_TRIG_SINGLE_2 */
+#define WM8915_DCS_TRIG_SINGLE_2_WIDTH 1 /* DCS_TRIG_SINGLE_2 */
+#define WM8915_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8915_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8915_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */
+#define WM8915_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */
+#define WM8915_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8915_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8915_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */
+#define WM8915_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */
+#define WM8915_DCS_TRIG_SERIES_3 0x0800 /* DCS_TRIG_SERIES_3 */
+#define WM8915_DCS_TRIG_SERIES_3_MASK 0x0800 /* DCS_TRIG_SERIES_3 */
+#define WM8915_DCS_TRIG_SERIES_3_SHIFT 11 /* DCS_TRIG_SERIES_3 */
+#define WM8915_DCS_TRIG_SERIES_3_WIDTH 1 /* DCS_TRIG_SERIES_3 */
+#define WM8915_DCS_TRIG_SERIES_2 0x0400 /* DCS_TRIG_SERIES_2 */
+#define WM8915_DCS_TRIG_SERIES_2_MASK 0x0400 /* DCS_TRIG_SERIES_2 */
+#define WM8915_DCS_TRIG_SERIES_2_SHIFT 10 /* DCS_TRIG_SERIES_2 */
+#define WM8915_DCS_TRIG_SERIES_2_WIDTH 1 /* DCS_TRIG_SERIES_2 */
+#define WM8915_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8915_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8915_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */
+#define WM8915_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */
+#define WM8915_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8915_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8915_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */
+#define WM8915_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */
+#define WM8915_DCS_TRIG_STARTUP_3 0x0080 /* DCS_TRIG_STARTUP_3 */
+#define WM8915_DCS_TRIG_STARTUP_3_MASK 0x0080 /* DCS_TRIG_STARTUP_3 */
+#define WM8915_DCS_TRIG_STARTUP_3_SHIFT 7 /* DCS_TRIG_STARTUP_3 */
+#define WM8915_DCS_TRIG_STARTUP_3_WIDTH 1 /* DCS_TRIG_STARTUP_3 */
+#define WM8915_DCS_TRIG_STARTUP_2 0x0040 /* DCS_TRIG_STARTUP_2 */
+#define WM8915_DCS_TRIG_STARTUP_2_MASK 0x0040 /* DCS_TRIG_STARTUP_2 */
+#define WM8915_DCS_TRIG_STARTUP_2_SHIFT 6 /* DCS_TRIG_STARTUP_2 */
+#define WM8915_DCS_TRIG_STARTUP_2_WIDTH 1 /* DCS_TRIG_STARTUP_2 */
+#define WM8915_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8915_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8915_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */
+#define WM8915_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */
+#define WM8915_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8915_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8915_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */
+#define WM8915_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */
+#define WM8915_DCS_TRIG_DAC_WR_3 0x0008 /* DCS_TRIG_DAC_WR_3 */
+#define WM8915_DCS_TRIG_DAC_WR_3_MASK 0x0008 /* DCS_TRIG_DAC_WR_3 */
+#define WM8915_DCS_TRIG_DAC_WR_3_SHIFT 3 /* DCS_TRIG_DAC_WR_3 */
+#define WM8915_DCS_TRIG_DAC_WR_3_WIDTH 1 /* DCS_TRIG_DAC_WR_3 */
+#define WM8915_DCS_TRIG_DAC_WR_2 0x0004 /* DCS_TRIG_DAC_WR_2 */
+#define WM8915_DCS_TRIG_DAC_WR_2_MASK 0x0004 /* DCS_TRIG_DAC_WR_2 */
+#define WM8915_DCS_TRIG_DAC_WR_2_SHIFT 2 /* DCS_TRIG_DAC_WR_2 */
+#define WM8915_DCS_TRIG_DAC_WR_2_WIDTH 1 /* DCS_TRIG_DAC_WR_2 */
+#define WM8915_DCS_TRIG_DAC_WR_1 0x0002 /* DCS_TRIG_DAC_WR_1 */
+#define WM8915_DCS_TRIG_DAC_WR_1_MASK 0x0002 /* DCS_TRIG_DAC_WR_1 */
+#define WM8915_DCS_TRIG_DAC_WR_1_SHIFT 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM8915_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM8915_DCS_TRIG_DAC_WR_0 0x0001 /* DCS_TRIG_DAC_WR_0 */
+#define WM8915_DCS_TRIG_DAC_WR_0_MASK 0x0001 /* DCS_TRIG_DAC_WR_0 */
+#define WM8915_DCS_TRIG_DAC_WR_0_SHIFT 0 /* DCS_TRIG_DAC_WR_0 */
+#define WM8915_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */
+
+/*
+ * R82 (0x52) - DC Servo (3)
+ */
+#define WM8915_DCS_TIMER_PERIOD_23_MASK 0x0F00 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8915_DCS_TIMER_PERIOD_23_SHIFT 8 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8915_DCS_TIMER_PERIOD_23_WIDTH 4 /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8915_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8915_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8915_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */
+
+/*
+ * R84 (0x54) - DC Servo (5)
+ */
+#define WM8915_DCS_SERIES_NO_23_MASK 0x7F00 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8915_DCS_SERIES_NO_23_SHIFT 8 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8915_DCS_SERIES_NO_23_WIDTH 7 /* DCS_SERIES_NO_23 - [14:8] */
+#define WM8915_DCS_SERIES_NO_01_MASK 0x007F /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8915_DCS_SERIES_NO_01_SHIFT 0 /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8915_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [6:0] */
+
+/*
+ * R85 (0x55) - DC Servo (6)
+ */
+#define WM8915_DCS_DAC_WR_VAL_3_MASK 0xFF00 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_3_SHIFT 8 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_3_WIDTH 8 /* DCS_DAC_WR_VAL_3 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_2_MASK 0x00FF /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8915_DCS_DAC_WR_VAL_2_SHIFT 0 /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8915_DCS_DAC_WR_VAL_2_WIDTH 8 /* DCS_DAC_WR_VAL_2 - [7:0] */
+
+/*
+ * R86 (0x56) - DC Servo (7)
+ */
+#define WM8915_DCS_DAC_WR_VAL_1_MASK 0xFF00 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_1_SHIFT 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8915_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8915_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8915_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */
+
+/*
+ * R87 (0x57) - DC Servo Readback 0
+ */
+#define WM8915_DCS_CAL_COMPLETE_MASK 0x0F00 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8915_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8915_DCS_CAL_COMPLETE_WIDTH 4 /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8915_DCS_DAC_WR_COMPLETE_MASK 0x00F0 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8915_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8915_DCS_DAC_WR_COMPLETE_WIDTH 4 /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8915_DCS_STARTUP_COMPLETE_MASK 0x000F /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8915_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8915_DCS_STARTUP_COMPLETE_WIDTH 4 /* DCS_STARTUP_COMPLETE - [3:0] */
+
+/*
+ * R96 (0x60) - Analogue HP (1)
+ */
+#define WM8915_HPOUT1L_RMV_SHORT 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM8915_HPOUT1L_RMV_SHORT_MASK 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM8915_HPOUT1L_RMV_SHORT_SHIFT 7 /* HPOUT1L_RMV_SHORT */
+#define WM8915_HPOUT1L_RMV_SHORT_WIDTH 1 /* HPOUT1L_RMV_SHORT */
+#define WM8915_HPOUT1L_OUTP 0x0040 /* HPOUT1L_OUTP */
+#define WM8915_HPOUT1L_OUTP_MASK 0x0040 /* HPOUT1L_OUTP */
+#define WM8915_HPOUT1L_OUTP_SHIFT 6 /* HPOUT1L_OUTP */
+#define WM8915_HPOUT1L_OUTP_WIDTH 1 /* HPOUT1L_OUTP */
+#define WM8915_HPOUT1L_DLY 0x0020 /* HPOUT1L_DLY */
+#define WM8915_HPOUT1L_DLY_MASK 0x0020 /* HPOUT1L_DLY */
+#define WM8915_HPOUT1L_DLY_SHIFT 5 /* HPOUT1L_DLY */
+#define WM8915_HPOUT1L_DLY_WIDTH 1 /* HPOUT1L_DLY */
+#define WM8915_HPOUT1R_RMV_SHORT 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM8915_HPOUT1R_RMV_SHORT_MASK 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM8915_HPOUT1R_RMV_SHORT_SHIFT 3 /* HPOUT1R_RMV_SHORT */
+#define WM8915_HPOUT1R_RMV_SHORT_WIDTH 1 /* HPOUT1R_RMV_SHORT */
+#define WM8915_HPOUT1R_OUTP 0x0004 /* HPOUT1R_OUTP */
+#define WM8915_HPOUT1R_OUTP_MASK 0x0004 /* HPOUT1R_OUTP */
+#define WM8915_HPOUT1R_OUTP_SHIFT 2 /* HPOUT1R_OUTP */
+#define WM8915_HPOUT1R_OUTP_WIDTH 1 /* HPOUT1R_OUTP */
+#define WM8915_HPOUT1R_DLY 0x0002 /* HPOUT1R_DLY */
+#define WM8915_HPOUT1R_DLY_MASK 0x0002 /* HPOUT1R_DLY */
+#define WM8915_HPOUT1R_DLY_SHIFT 1 /* HPOUT1R_DLY */
+#define WM8915_HPOUT1R_DLY_WIDTH 1 /* HPOUT1R_DLY */
+
+/*
+ * R97 (0x61) - Analogue HP (2)
+ */
+#define WM8915_HPOUT2L_RMV_SHORT 0x0080 /* HPOUT2L_RMV_SHORT */
+#define WM8915_HPOUT2L_RMV_SHORT_MASK 0x0080 /* HPOUT2L_RMV_SHORT */
+#define WM8915_HPOUT2L_RMV_SHORT_SHIFT 7 /* HPOUT2L_RMV_SHORT */
+#define WM8915_HPOUT2L_RMV_SHORT_WIDTH 1 /* HPOUT2L_RMV_SHORT */
+#define WM8915_HPOUT2L_OUTP 0x0040 /* HPOUT2L_OUTP */
+#define WM8915_HPOUT2L_OUTP_MASK 0x0040 /* HPOUT2L_OUTP */
+#define WM8915_HPOUT2L_OUTP_SHIFT 6 /* HPOUT2L_OUTP */
+#define WM8915_HPOUT2L_OUTP_WIDTH 1 /* HPOUT2L_OUTP */
+#define WM8915_HPOUT2L_DLY 0x0020 /* HPOUT2L_DLY */
+#define WM8915_HPOUT2L_DLY_MASK 0x0020 /* HPOUT2L_DLY */
+#define WM8915_HPOUT2L_DLY_SHIFT 5 /* HPOUT2L_DLY */
+#define WM8915_HPOUT2L_DLY_WIDTH 1 /* HPOUT2L_DLY */
+#define WM8915_HPOUT2R_RMV_SHORT 0x0008 /* HPOUT2R_RMV_SHORT */
+#define WM8915_HPOUT2R_RMV_SHORT_MASK 0x0008 /* HPOUT2R_RMV_SHORT */
+#define WM8915_HPOUT2R_RMV_SHORT_SHIFT 3 /* HPOUT2R_RMV_SHORT */
+#define WM8915_HPOUT2R_RMV_SHORT_WIDTH 1 /* HPOUT2R_RMV_SHORT */
+#define WM8915_HPOUT2R_OUTP 0x0004 /* HPOUT2R_OUTP */
+#define WM8915_HPOUT2R_OUTP_MASK 0x0004 /* HPOUT2R_OUTP */
+#define WM8915_HPOUT2R_OUTP_SHIFT 2 /* HPOUT2R_OUTP */
+#define WM8915_HPOUT2R_OUTP_WIDTH 1 /* HPOUT2R_OUTP */
+#define WM8915_HPOUT2R_DLY 0x0002 /* HPOUT2R_DLY */
+#define WM8915_HPOUT2R_DLY_MASK 0x0002 /* HPOUT2R_DLY */
+#define WM8915_HPOUT2R_DLY_SHIFT 1 /* HPOUT2R_DLY */
+#define WM8915_HPOUT2R_DLY_WIDTH 1 /* HPOUT2R_DLY */
+
+/*
+ * R256 (0x100) - Chip Revision
+ */
+#define WM8915_CHIP_REV_MASK 0x000F /* CHIP_REV - [3:0] */
+#define WM8915_CHIP_REV_SHIFT 0 /* CHIP_REV - [3:0] */
+#define WM8915_CHIP_REV_WIDTH 4 /* CHIP_REV - [3:0] */
+
+/*
+ * R257 (0x101) - Control Interface (1)
+ */
+#define WM8915_AUTO_INC 0x0004 /* AUTO_INC */
+#define WM8915_AUTO_INC_MASK 0x0004 /* AUTO_INC */
+#define WM8915_AUTO_INC_SHIFT 2 /* AUTO_INC */
+#define WM8915_AUTO_INC_WIDTH 1 /* AUTO_INC */
+
+/*
+ * R272 (0x110) - Write Sequencer Ctrl (1)
+ */
+#define WM8915_WSEQ_ENA 0x8000 /* WSEQ_ENA */
+#define WM8915_WSEQ_ENA_MASK 0x8000 /* WSEQ_ENA */
+#define WM8915_WSEQ_ENA_SHIFT 15 /* WSEQ_ENA */
+#define WM8915_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
+#define WM8915_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */
+#define WM8915_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */
+#define WM8915_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */
+#define WM8915_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
+#define WM8915_WSEQ_START 0x0100 /* WSEQ_START */
+#define WM8915_WSEQ_START_MASK 0x0100 /* WSEQ_START */
+#define WM8915_WSEQ_START_SHIFT 8 /* WSEQ_START */
+#define WM8915_WSEQ_START_WIDTH 1 /* WSEQ_START */
+#define WM8915_WSEQ_START_INDEX_MASK 0x007F /* WSEQ_START_INDEX - [6:0] */
+#define WM8915_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [6:0] */
+#define WM8915_WSEQ_START_INDEX_WIDTH 7 /* WSEQ_START_INDEX - [6:0] */
+
+/*
+ * R273 (0x111) - Write Sequencer Ctrl (2)
+ */
+#define WM8915_WSEQ_BUSY 0x0100 /* WSEQ_BUSY */
+#define WM8915_WSEQ_BUSY_MASK 0x0100 /* WSEQ_BUSY */
+#define WM8915_WSEQ_BUSY_SHIFT 8 /* WSEQ_BUSY */
+#define WM8915_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
+#define WM8915_WSEQ_CURRENT_INDEX_MASK 0x007F /* WSEQ_CURRENT_INDEX - [6:0] */
+#define WM8915_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [6:0] */
+#define WM8915_WSEQ_CURRENT_INDEX_WIDTH 7 /* WSEQ_CURRENT_INDEX - [6:0] */
+
+/*
+ * R512 (0x200) - AIF Clocking (1)
+ */
+#define WM8915_SYSCLK_SRC_MASK 0x0018 /* SYSCLK_SRC - [4:3] */
+#define WM8915_SYSCLK_SRC_SHIFT 3 /* SYSCLK_SRC - [4:3] */
+#define WM8915_SYSCLK_SRC_WIDTH 2 /* SYSCLK_SRC - [4:3] */
+#define WM8915_SYSCLK_INV 0x0004 /* SYSCLK_INV */
+#define WM8915_SYSCLK_INV_MASK 0x0004 /* SYSCLK_INV */
+#define WM8915_SYSCLK_INV_SHIFT 2 /* SYSCLK_INV */
+#define WM8915_SYSCLK_INV_WIDTH 1 /* SYSCLK_INV */
+#define WM8915_SYSCLK_DIV 0x0002 /* SYSCLK_DIV */
+#define WM8915_SYSCLK_DIV_MASK 0x0002 /* SYSCLK_DIV */
+#define WM8915_SYSCLK_DIV_SHIFT 1 /* SYSCLK_DIV */
+#define WM8915_SYSCLK_DIV_WIDTH 1 /* SYSCLK_DIV */
+#define WM8915_SYSCLK_ENA 0x0001 /* SYSCLK_ENA */
+#define WM8915_SYSCLK_ENA_MASK 0x0001 /* SYSCLK_ENA */
+#define WM8915_SYSCLK_ENA_SHIFT 0 /* SYSCLK_ENA */
+#define WM8915_SYSCLK_ENA_WIDTH 1 /* SYSCLK_ENA */
+
+/*
+ * R513 (0x201) - AIF Clocking (2)
+ */
+#define WM8915_DSP2_DIV_MASK 0x0018 /* DSP2_DIV - [4:3] */
+#define WM8915_DSP2_DIV_SHIFT 3 /* DSP2_DIV - [4:3] */
+#define WM8915_DSP2_DIV_WIDTH 2 /* DSP2_DIV - [4:3] */
+#define WM8915_DSP1_DIV_MASK 0x0003 /* DSP1_DIV - [1:0] */
+#define WM8915_DSP1_DIV_SHIFT 0 /* DSP1_DIV - [1:0] */
+#define WM8915_DSP1_DIV_WIDTH 2 /* DSP1_DIV - [1:0] */
+
+/*
+ * R520 (0x208) - Clocking (1)
+ */
+#define WM8915_LFCLK_ENA 0x0020 /* LFCLK_ENA */
+#define WM8915_LFCLK_ENA_MASK 0x0020 /* LFCLK_ENA */
+#define WM8915_LFCLK_ENA_SHIFT 5 /* LFCLK_ENA */
+#define WM8915_LFCLK_ENA_WIDTH 1 /* LFCLK_ENA */
+#define WM8915_TOCLK_ENA 0x0010 /* TOCLK_ENA */
+#define WM8915_TOCLK_ENA_MASK 0x0010 /* TOCLK_ENA */
+#define WM8915_TOCLK_ENA_SHIFT 4 /* TOCLK_ENA */
+#define WM8915_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */
+#define WM8915_AIFCLK_ENA 0x0004 /* AIFCLK_ENA */
+#define WM8915_AIFCLK_ENA_MASK 0x0004 /* AIFCLK_ENA */
+#define WM8915_AIFCLK_ENA_SHIFT 2 /* AIFCLK_ENA */
+#define WM8915_AIFCLK_ENA_WIDTH 1 /* AIFCLK_ENA */
+#define WM8915_SYSDSPCLK_ENA 0x0002 /* SYSDSPCLK_ENA */
+#define WM8915_SYSDSPCLK_ENA_MASK 0x0002 /* SYSDSPCLK_ENA */
+#define WM8915_SYSDSPCLK_ENA_SHIFT 1 /* SYSDSPCLK_ENA */
+#define WM8915_SYSDSPCLK_ENA_WIDTH 1 /* SYSDSPCLK_ENA */
+
+/*
+ * R521 (0x209) - Clocking (2)
+ */
+#define WM8915_TOCLK_DIV_MASK 0x0700 /* TOCLK_DIV - [10:8] */
+#define WM8915_TOCLK_DIV_SHIFT 8 /* TOCLK_DIV - [10:8] */
+#define WM8915_TOCLK_DIV_WIDTH 3 /* TOCLK_DIV - [10:8] */
+#define WM8915_DBCLK_DIV_MASK 0x00F0 /* DBCLK_DIV - [7:4] */
+#define WM8915_DBCLK_DIV_SHIFT 4 /* DBCLK_DIV - [7:4] */
+#define WM8915_DBCLK_DIV_WIDTH 4 /* DBCLK_DIV - [7:4] */
+#define WM8915_OPCLK_DIV_MASK 0x0007 /* OPCLK_DIV - [2:0] */
+#define WM8915_OPCLK_DIV_SHIFT 0 /* OPCLK_DIV - [2:0] */
+#define WM8915_OPCLK_DIV_WIDTH 3 /* OPCLK_DIV - [2:0] */
+
+/*
+ * R528 (0x210) - AIF Rate
+ */
+#define WM8915_SYSCLK_RATE 0x0001 /* SYSCLK_RATE */
+#define WM8915_SYSCLK_RATE_MASK 0x0001 /* SYSCLK_RATE */
+#define WM8915_SYSCLK_RATE_SHIFT 0 /* SYSCLK_RATE */
+#define WM8915_SYSCLK_RATE_WIDTH 1 /* SYSCLK_RATE */
+
+/*
+ * R544 (0x220) - FLL Control (1)
+ */
+#define WM8915_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */
+#define WM8915_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */
+#define WM8915_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */
+#define WM8915_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */
+#define WM8915_FLL_ENA 0x0001 /* FLL_ENA */
+#define WM8915_FLL_ENA_MASK 0x0001 /* FLL_ENA */
+#define WM8915_FLL_ENA_SHIFT 0 /* FLL_ENA */
+#define WM8915_FLL_ENA_WIDTH 1 /* FLL_ENA */
+
+/*
+ * R545 (0x221) - FLL Control (2)
+ */
+#define WM8915_FLL_OUTDIV_MASK 0x3F00 /* FLL_OUTDIV - [13:8] */
+#define WM8915_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [13:8] */
+#define WM8915_FLL_OUTDIV_WIDTH 6 /* FLL_OUTDIV - [13:8] */
+#define WM8915_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */
+#define WM8915_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */
+#define WM8915_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */
+
+/*
+ * R546 (0x222) - FLL Control (3)
+ */
+#define WM8915_FLL_THETA_MASK 0xFFFF /* FLL_THETA - [15:0] */
+#define WM8915_FLL_THETA_SHIFT 0 /* FLL_THETA - [15:0] */
+#define WM8915_FLL_THETA_WIDTH 16 /* FLL_THETA - [15:0] */
+
+/*
+ * R547 (0x223) - FLL Control (4)
+ */
+#define WM8915_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */
+#define WM8915_FLL_N_SHIFT 5 /* FLL_N - [14:5] */
+#define WM8915_FLL_N_WIDTH 10 /* FLL_N - [14:5] */
+#define WM8915_FLL_LOOP_GAIN_MASK 0x000F /* FLL_LOOP_GAIN - [3:0] */
+#define WM8915_FLL_LOOP_GAIN_SHIFT 0 /* FLL_LOOP_GAIN - [3:0] */
+#define WM8915_FLL_LOOP_GAIN_WIDTH 4 /* FLL_LOOP_GAIN - [3:0] */
+
+/*
+ * R548 (0x224) - FLL Control (5)
+ */
+#define WM8915_FLL_FRC_NCO_VAL_MASK 0x1F80 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8915_FLL_FRC_NCO_VAL_SHIFT 7 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8915_FLL_FRC_NCO_VAL_WIDTH 6 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8915_FLL_FRC_NCO 0x0040 /* FLL_FRC_NCO */
+#define WM8915_FLL_FRC_NCO_MASK 0x0040 /* FLL_FRC_NCO */
+#define WM8915_FLL_FRC_NCO_SHIFT 6 /* FLL_FRC_NCO */
+#define WM8915_FLL_FRC_NCO_WIDTH 1 /* FLL_FRC_NCO */
+#define WM8915_FLL_REFCLK_DIV_MASK 0x0018 /* FLL_REFCLK_DIV - [4:3] */
+#define WM8915_FLL_REFCLK_DIV_SHIFT 3 /* FLL_REFCLK_DIV - [4:3] */
+#define WM8915_FLL_REFCLK_DIV_WIDTH 2 /* FLL_REFCLK_DIV - [4:3] */
+#define WM8915_FLL_REF_FREQ 0x0004 /* FLL_REF_FREQ */
+#define WM8915_FLL_REF_FREQ_MASK 0x0004 /* FLL_REF_FREQ */
+#define WM8915_FLL_REF_FREQ_SHIFT 2 /* FLL_REF_FREQ */
+#define WM8915_FLL_REF_FREQ_WIDTH 1 /* FLL_REF_FREQ */
+#define WM8915_FLL_REFCLK_SRC_MASK 0x0003 /* FLL_REFCLK_SRC - [1:0] */
+#define WM8915_FLL_REFCLK_SRC_SHIFT 0 /* FLL_REFCLK_SRC - [1:0] */
+#define WM8915_FLL_REFCLK_SRC_WIDTH 2 /* FLL_REFCLK_SRC - [1:0] */
+
+/*
+ * R549 (0x225) - FLL Control (6)
+ */
+#define WM8915_FLL_REFCLK_SRC_STS_MASK 0x000C /* FLL_REFCLK_SRC_STS - [3:2] */
+#define WM8915_FLL_REFCLK_SRC_STS_SHIFT 2 /* FLL_REFCLK_SRC_STS - [3:2] */
+#define WM8915_FLL_REFCLK_SRC_STS_WIDTH 2 /* FLL_REFCLK_SRC_STS - [3:2] */
+#define WM8915_FLL_SWITCH_CLK 0x0001 /* FLL_SWITCH_CLK */
+#define WM8915_FLL_SWITCH_CLK_MASK 0x0001 /* FLL_SWITCH_CLK */
+#define WM8915_FLL_SWITCH_CLK_SHIFT 0 /* FLL_SWITCH_CLK */
+#define WM8915_FLL_SWITCH_CLK_WIDTH 1 /* FLL_SWITCH_CLK */
+
+/*
+ * R550 (0x226) - FLL EFS 1
+ */
+#define WM8915_FLL_LAMBDA_MASK 0xFFFF /* FLL_LAMBDA - [15:0] */
+#define WM8915_FLL_LAMBDA_SHIFT 0 /* FLL_LAMBDA - [15:0] */
+#define WM8915_FLL_LAMBDA_WIDTH 16 /* FLL_LAMBDA - [15:0] */
+
+/*
+ * R551 (0x227) - FLL EFS 2
+ */
+#define WM8915_FLL_LFSR_SEL_MASK 0x0006 /* FLL_LFSR_SEL - [2:1] */
+#define WM8915_FLL_LFSR_SEL_SHIFT 1 /* FLL_LFSR_SEL - [2:1] */
+#define WM8915_FLL_LFSR_SEL_WIDTH 2 /* FLL_LFSR_SEL - [2:1] */
+#define WM8915_FLL_EFS_ENA 0x0001 /* FLL_EFS_ENA */
+#define WM8915_FLL_EFS_ENA_MASK 0x0001 /* FLL_EFS_ENA */
+#define WM8915_FLL_EFS_ENA_SHIFT 0 /* FLL_EFS_ENA */
+#define WM8915_FLL_EFS_ENA_WIDTH 1 /* FLL_EFS_ENA */
+
+/*
+ * R768 (0x300) - AIF1 Control
+ */
+#define WM8915_AIF1_TRI 0x0004 /* AIF1_TRI */
+#define WM8915_AIF1_TRI_MASK 0x0004 /* AIF1_TRI */
+#define WM8915_AIF1_TRI_SHIFT 2 /* AIF1_TRI */
+#define WM8915_AIF1_TRI_WIDTH 1 /* AIF1_TRI */
+#define WM8915_AIF1_FMT_MASK 0x0003 /* AIF1_FMT - [1:0] */
+#define WM8915_AIF1_FMT_SHIFT 0 /* AIF1_FMT - [1:0] */
+#define WM8915_AIF1_FMT_WIDTH 2 /* AIF1_FMT - [1:0] */
+
+/*
+ * R769 (0x301) - AIF1 BCLK
+ */
+#define WM8915_AIF1_BCLK_INV 0x0400 /* AIF1_BCLK_INV */
+#define WM8915_AIF1_BCLK_INV_MASK 0x0400 /* AIF1_BCLK_INV */
+#define WM8915_AIF1_BCLK_INV_SHIFT 10 /* AIF1_BCLK_INV */
+#define WM8915_AIF1_BCLK_INV_WIDTH 1 /* AIF1_BCLK_INV */
+#define WM8915_AIF1_BCLK_FRC 0x0200 /* AIF1_BCLK_FRC */
+#define WM8915_AIF1_BCLK_FRC_MASK 0x0200 /* AIF1_BCLK_FRC */
+#define WM8915_AIF1_BCLK_FRC_SHIFT 9 /* AIF1_BCLK_FRC */
+#define WM8915_AIF1_BCLK_FRC_WIDTH 1 /* AIF1_BCLK_FRC */
+#define WM8915_AIF1_BCLK_MSTR 0x0100 /* AIF1_BCLK_MSTR */
+#define WM8915_AIF1_BCLK_MSTR_MASK 0x0100 /* AIF1_BCLK_MSTR */
+#define WM8915_AIF1_BCLK_MSTR_SHIFT 8 /* AIF1_BCLK_MSTR */
+#define WM8915_AIF1_BCLK_MSTR_WIDTH 1 /* AIF1_BCLK_MSTR */
+#define WM8915_AIF1_BCLK_DIV_MASK 0x000F /* AIF1_BCLK_DIV - [3:0] */
+#define WM8915_AIF1_BCLK_DIV_SHIFT 0 /* AIF1_BCLK_DIV - [3:0] */
+#define WM8915_AIF1_BCLK_DIV_WIDTH 4 /* AIF1_BCLK_DIV - [3:0] */
+
+/*
+ * R770 (0x302) - AIF1 TX LRCLK(1)
+ */
+#define WM8915_AIF1TX_RATE_MASK 0x07FF /* AIF1TX_RATE - [10:0] */
+#define WM8915_AIF1TX_RATE_SHIFT 0 /* AIF1TX_RATE - [10:0] */
+#define WM8915_AIF1TX_RATE_WIDTH 11 /* AIF1TX_RATE - [10:0] */
+
+/*
+ * R771 (0x303) - AIF1 TX LRCLK(2)
+ */
+#define WM8915_AIF1TX_LRCLK_MODE 0x0008 /* AIF1TX_LRCLK_MODE */
+#define WM8915_AIF1TX_LRCLK_MODE_MASK 0x0008 /* AIF1TX_LRCLK_MODE */
+#define WM8915_AIF1TX_LRCLK_MODE_SHIFT 3 /* AIF1TX_LRCLK_MODE */
+#define WM8915_AIF1TX_LRCLK_MODE_WIDTH 1 /* AIF1TX_LRCLK_MODE */
+#define WM8915_AIF1TX_LRCLK_INV 0x0004 /* AIF1TX_LRCLK_INV */
+#define WM8915_AIF1TX_LRCLK_INV_MASK 0x0004 /* AIF1TX_LRCLK_INV */
+#define WM8915_AIF1TX_LRCLK_INV_SHIFT 2 /* AIF1TX_LRCLK_INV */
+#define WM8915_AIF1TX_LRCLK_INV_WIDTH 1 /* AIF1TX_LRCLK_INV */
+#define WM8915_AIF1TX_LRCLK_FRC 0x0002 /* AIF1TX_LRCLK_FRC */
+#define WM8915_AIF1TX_LRCLK_FRC_MASK 0x0002 /* AIF1TX_LRCLK_FRC */
+#define WM8915_AIF1TX_LRCLK_FRC_SHIFT 1 /* AIF1TX_LRCLK_FRC */
+#define WM8915_AIF1TX_LRCLK_FRC_WIDTH 1 /* AIF1TX_LRCLK_FRC */
+#define WM8915_AIF1TX_LRCLK_MSTR 0x0001 /* AIF1TX_LRCLK_MSTR */
+#define WM8915_AIF1TX_LRCLK_MSTR_MASK 0x0001 /* AIF1TX_LRCLK_MSTR */
+#define WM8915_AIF1TX_LRCLK_MSTR_SHIFT 0 /* AIF1TX_LRCLK_MSTR */
+#define WM8915_AIF1TX_LRCLK_MSTR_WIDTH 1 /* AIF1TX_LRCLK_MSTR */
+
+/*
+ * R772 (0x304) - AIF1 RX LRCLK(1)
+ */
+#define WM8915_AIF1RX_RATE_MASK 0x07FF /* AIF1RX_RATE - [10:0] */
+#define WM8915_AIF1RX_RATE_SHIFT 0 /* AIF1RX_RATE - [10:0] */
+#define WM8915_AIF1RX_RATE_WIDTH 11 /* AIF1RX_RATE - [10:0] */
+
+/*
+ * R773 (0x305) - AIF1 RX LRCLK(2)
+ */
+#define WM8915_AIF1RX_LRCLK_INV 0x0004 /* AIF1RX_LRCLK_INV */
+#define WM8915_AIF1RX_LRCLK_INV_MASK 0x0004 /* AIF1RX_LRCLK_INV */
+#define WM8915_AIF1RX_LRCLK_INV_SHIFT 2 /* AIF1RX_LRCLK_INV */
+#define WM8915_AIF1RX_LRCLK_INV_WIDTH 1 /* AIF1RX_LRCLK_INV */
+#define WM8915_AIF1RX_LRCLK_FRC 0x0002 /* AIF1RX_LRCLK_FRC */
+#define WM8915_AIF1RX_LRCLK_FRC_MASK 0x0002 /* AIF1RX_LRCLK_FRC */
+#define WM8915_AIF1RX_LRCLK_FRC_SHIFT 1 /* AIF1RX_LRCLK_FRC */
+#define WM8915_AIF1RX_LRCLK_FRC_WIDTH 1 /* AIF1RX_LRCLK_FRC */
+#define WM8915_AIF1RX_LRCLK_MSTR 0x0001 /* AIF1RX_LRCLK_MSTR */
+#define WM8915_AIF1RX_LRCLK_MSTR_MASK 0x0001 /* AIF1RX_LRCLK_MSTR */
+#define WM8915_AIF1RX_LRCLK_MSTR_SHIFT 0 /* AIF1RX_LRCLK_MSTR */
+#define WM8915_AIF1RX_LRCLK_MSTR_WIDTH 1 /* AIF1RX_LRCLK_MSTR */
+
+/*
+ * R774 (0x306) - AIF1TX Data Configuration (1)
+ */
+#define WM8915_AIF1TX_WL_MASK 0xFF00 /* AIF1TX_WL - [15:8] */
+#define WM8915_AIF1TX_WL_SHIFT 8 /* AIF1TX_WL - [15:8] */
+#define WM8915_AIF1TX_WL_WIDTH 8 /* AIF1TX_WL - [15:8] */
+#define WM8915_AIF1TX_SLOT_LEN_MASK 0x00FF /* AIF1TX_SLOT_LEN - [7:0] */
+#define WM8915_AIF1TX_SLOT_LEN_SHIFT 0 /* AIF1TX_SLOT_LEN - [7:0] */
+#define WM8915_AIF1TX_SLOT_LEN_WIDTH 8 /* AIF1TX_SLOT_LEN - [7:0] */
+
+/*
+ * R775 (0x307) - AIF1TX Data Configuration (2)
+ */
+#define WM8915_AIF1TX_DAT_TRI 0x0001 /* AIF1TX_DAT_TRI */
+#define WM8915_AIF1TX_DAT_TRI_MASK 0x0001 /* AIF1TX_DAT_TRI */
+#define WM8915_AIF1TX_DAT_TRI_SHIFT 0 /* AIF1TX_DAT_TRI */
+#define WM8915_AIF1TX_DAT_TRI_WIDTH 1 /* AIF1TX_DAT_TRI */
+
+/*
+ * R776 (0x308) - AIF1RX Data Configuration
+ */
+#define WM8915_AIF1RX_WL_MASK 0xFF00 /* AIF1RX_WL - [15:8] */
+#define WM8915_AIF1RX_WL_SHIFT 8 /* AIF1RX_WL - [15:8] */
+#define WM8915_AIF1RX_WL_WIDTH 8 /* AIF1RX_WL - [15:8] */
+#define WM8915_AIF1RX_SLOT_LEN_MASK 0x00FF /* AIF1RX_SLOT_LEN - [7:0] */
+#define WM8915_AIF1RX_SLOT_LEN_SHIFT 0 /* AIF1RX_SLOT_LEN - [7:0] */
+#define WM8915_AIF1RX_SLOT_LEN_WIDTH 8 /* AIF1RX_SLOT_LEN - [7:0] */
+
+/*
+ * R777 (0x309) - AIF1TX Channel 0 Configuration
+ */
+#define WM8915_AIF1TX_CHAN0_DAT_INV 0x8000 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8915_AIF1TX_CHAN0_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8915_AIF1TX_CHAN0_DAT_INV_SHIFT 15 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8915_AIF1TX_CHAN0_DAT_INV_WIDTH 1 /* AIF1TX_CHAN0_DAT_INV */
+#define WM8915_AIF1TX_CHAN0_SPACING_MASK 0x7E00 /* AIF1TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN0_SPACING_SHIFT 9 /* AIF1TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN0_SPACING_WIDTH 6 /* AIF1TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN0_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN0_SLOTS_SHIFT 6 /* AIF1TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN0_SLOTS_WIDTH 3 /* AIF1TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN0_START_SLOT_MASK 0x003F /* AIF1TX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN0_START_SLOT_SHIFT 0 /* AIF1TX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN0_START_SLOT_WIDTH 6 /* AIF1TX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R778 (0x30A) - AIF1TX Channel 1 Configuration
+ */
+#define WM8915_AIF1TX_CHAN1_DAT_INV 0x8000 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8915_AIF1TX_CHAN1_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8915_AIF1TX_CHAN1_DAT_INV_SHIFT 15 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8915_AIF1TX_CHAN1_DAT_INV_WIDTH 1 /* AIF1TX_CHAN1_DAT_INV */
+#define WM8915_AIF1TX_CHAN1_SPACING_MASK 0x7E00 /* AIF1TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN1_SPACING_SHIFT 9 /* AIF1TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN1_SPACING_WIDTH 6 /* AIF1TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN1_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN1_SLOTS_SHIFT 6 /* AIF1TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN1_SLOTS_WIDTH 3 /* AIF1TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN1_START_SLOT_MASK 0x003F /* AIF1TX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN1_START_SLOT_SHIFT 0 /* AIF1TX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN1_START_SLOT_WIDTH 6 /* AIF1TX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R779 (0x30B) - AIF1TX Channel 2 Configuration
+ */
+#define WM8915_AIF1TX_CHAN2_DAT_INV 0x8000 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8915_AIF1TX_CHAN2_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8915_AIF1TX_CHAN2_DAT_INV_SHIFT 15 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8915_AIF1TX_CHAN2_DAT_INV_WIDTH 1 /* AIF1TX_CHAN2_DAT_INV */
+#define WM8915_AIF1TX_CHAN2_SPACING_MASK 0x7E00 /* AIF1TX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN2_SPACING_SHIFT 9 /* AIF1TX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN2_SPACING_WIDTH 6 /* AIF1TX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN2_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN2_SLOTS_SHIFT 6 /* AIF1TX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN2_SLOTS_WIDTH 3 /* AIF1TX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN2_START_SLOT_MASK 0x003F /* AIF1TX_CHAN2_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN2_START_SLOT_SHIFT 0 /* AIF1TX_CHAN2_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN2_START_SLOT_WIDTH 6 /* AIF1TX_CHAN2_START_SLOT - [5:0] */
+
+/*
+ * R780 (0x30C) - AIF1TX Channel 3 Configuration
+ */
+#define WM8915_AIF1TX_CHAN3_DAT_INV 0x8000 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8915_AIF1TX_CHAN3_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8915_AIF1TX_CHAN3_DAT_INV_SHIFT 15 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8915_AIF1TX_CHAN3_DAT_INV_WIDTH 1 /* AIF1TX_CHAN3_DAT_INV */
+#define WM8915_AIF1TX_CHAN3_SPACING_MASK 0x7E00 /* AIF1TX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN3_SPACING_SHIFT 9 /* AIF1TX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN3_SPACING_WIDTH 6 /* AIF1TX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN3_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN3_SLOTS_SHIFT 6 /* AIF1TX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN3_SLOTS_WIDTH 3 /* AIF1TX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN3_START_SLOT_MASK 0x003F /* AIF1TX_CHAN3_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN3_START_SLOT_SHIFT 0 /* AIF1TX_CHAN3_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN3_START_SLOT_WIDTH 6 /* AIF1TX_CHAN3_START_SLOT - [5:0] */
+
+/*
+ * R781 (0x30D) - AIF1TX Channel 4 Configuration
+ */
+#define WM8915_AIF1TX_CHAN4_DAT_INV 0x8000 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8915_AIF1TX_CHAN4_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8915_AIF1TX_CHAN4_DAT_INV_SHIFT 15 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8915_AIF1TX_CHAN4_DAT_INV_WIDTH 1 /* AIF1TX_CHAN4_DAT_INV */
+#define WM8915_AIF1TX_CHAN4_SPACING_MASK 0x7E00 /* AIF1TX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN4_SPACING_SHIFT 9 /* AIF1TX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN4_SPACING_WIDTH 6 /* AIF1TX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN4_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN4_SLOTS_SHIFT 6 /* AIF1TX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN4_SLOTS_WIDTH 3 /* AIF1TX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN4_START_SLOT_MASK 0x003F /* AIF1TX_CHAN4_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN4_START_SLOT_SHIFT 0 /* AIF1TX_CHAN4_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN4_START_SLOT_WIDTH 6 /* AIF1TX_CHAN4_START_SLOT - [5:0] */
+
+/*
+ * R782 (0x30E) - AIF1TX Channel 5 Configuration
+ */
+#define WM8915_AIF1TX_CHAN5_DAT_INV 0x8000 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8915_AIF1TX_CHAN5_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8915_AIF1TX_CHAN5_DAT_INV_SHIFT 15 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8915_AIF1TX_CHAN5_DAT_INV_WIDTH 1 /* AIF1TX_CHAN5_DAT_INV */
+#define WM8915_AIF1TX_CHAN5_SPACING_MASK 0x7E00 /* AIF1TX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN5_SPACING_SHIFT 9 /* AIF1TX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN5_SPACING_WIDTH 6 /* AIF1TX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1TX_CHAN5_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN5_SLOTS_SHIFT 6 /* AIF1TX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN5_SLOTS_WIDTH 3 /* AIF1TX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1TX_CHAN5_START_SLOT_MASK 0x003F /* AIF1TX_CHAN5_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN5_START_SLOT_SHIFT 0 /* AIF1TX_CHAN5_START_SLOT - [5:0] */
+#define WM8915_AIF1TX_CHAN5_START_SLOT_WIDTH 6 /* AIF1TX_CHAN5_START_SLOT - [5:0] */
+
+/*
+ * R783 (0x30F) - AIF1RX Channel 0 Configuration
+ */
+#define WM8915_AIF1RX_CHAN0_DAT_INV 0x8000 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8915_AIF1RX_CHAN0_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8915_AIF1RX_CHAN0_DAT_INV_SHIFT 15 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8915_AIF1RX_CHAN0_DAT_INV_WIDTH 1 /* AIF1RX_CHAN0_DAT_INV */
+#define WM8915_AIF1RX_CHAN0_SPACING_MASK 0x7E00 /* AIF1RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN0_SPACING_SHIFT 9 /* AIF1RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN0_SPACING_WIDTH 6 /* AIF1RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN0_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN0_SLOTS_SHIFT 6 /* AIF1RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN0_SLOTS_WIDTH 3 /* AIF1RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN0_START_SLOT_MASK 0x003F /* AIF1RX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN0_START_SLOT_SHIFT 0 /* AIF1RX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN0_START_SLOT_WIDTH 6 /* AIF1RX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R784 (0x310) - AIF1RX Channel 1 Configuration
+ */
+#define WM8915_AIF1RX_CHAN1_DAT_INV 0x8000 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8915_AIF1RX_CHAN1_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8915_AIF1RX_CHAN1_DAT_INV_SHIFT 15 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8915_AIF1RX_CHAN1_DAT_INV_WIDTH 1 /* AIF1RX_CHAN1_DAT_INV */
+#define WM8915_AIF1RX_CHAN1_SPACING_MASK 0x7E00 /* AIF1RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN1_SPACING_SHIFT 9 /* AIF1RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN1_SPACING_WIDTH 6 /* AIF1RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN1_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN1_SLOTS_SHIFT 6 /* AIF1RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN1_SLOTS_WIDTH 3 /* AIF1RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN1_START_SLOT_MASK 0x003F /* AIF1RX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN1_START_SLOT_SHIFT 0 /* AIF1RX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN1_START_SLOT_WIDTH 6 /* AIF1RX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R785 (0x311) - AIF1RX Channel 2 Configuration
+ */
+#define WM8915_AIF1RX_CHAN2_DAT_INV 0x8000 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8915_AIF1RX_CHAN2_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8915_AIF1RX_CHAN2_DAT_INV_SHIFT 15 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8915_AIF1RX_CHAN2_DAT_INV_WIDTH 1 /* AIF1RX_CHAN2_DAT_INV */
+#define WM8915_AIF1RX_CHAN2_SPACING_MASK 0x7E00 /* AIF1RX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN2_SPACING_SHIFT 9 /* AIF1RX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN2_SPACING_WIDTH 6 /* AIF1RX_CHAN2_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN2_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN2_SLOTS_SHIFT 6 /* AIF1RX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN2_SLOTS_WIDTH 3 /* AIF1RX_CHAN2_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN2_START_SLOT_MASK 0x003F /* AIF1RX_CHAN2_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN2_START_SLOT_SHIFT 0 /* AIF1RX_CHAN2_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN2_START_SLOT_WIDTH 6 /* AIF1RX_CHAN2_START_SLOT - [5:0] */
+
+/*
+ * R786 (0x312) - AIF1RX Channel 3 Configuration
+ */
+#define WM8915_AIF1RX_CHAN3_DAT_INV 0x8000 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8915_AIF1RX_CHAN3_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8915_AIF1RX_CHAN3_DAT_INV_SHIFT 15 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8915_AIF1RX_CHAN3_DAT_INV_WIDTH 1 /* AIF1RX_CHAN3_DAT_INV */
+#define WM8915_AIF1RX_CHAN3_SPACING_MASK 0x7E00 /* AIF1RX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN3_SPACING_SHIFT 9 /* AIF1RX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN3_SPACING_WIDTH 6 /* AIF1RX_CHAN3_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN3_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN3_SLOTS_SHIFT 6 /* AIF1RX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN3_SLOTS_WIDTH 3 /* AIF1RX_CHAN3_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN3_START_SLOT_MASK 0x003F /* AIF1RX_CHAN3_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN3_START_SLOT_SHIFT 0 /* AIF1RX_CHAN3_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN3_START_SLOT_WIDTH 6 /* AIF1RX_CHAN3_START_SLOT - [5:0] */
+
+/*
+ * R787 (0x313) - AIF1RX Channel 4 Configuration
+ */
+#define WM8915_AIF1RX_CHAN4_DAT_INV 0x8000 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8915_AIF1RX_CHAN4_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8915_AIF1RX_CHAN4_DAT_INV_SHIFT 15 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8915_AIF1RX_CHAN4_DAT_INV_WIDTH 1 /* AIF1RX_CHAN4_DAT_INV */
+#define WM8915_AIF1RX_CHAN4_SPACING_MASK 0x7E00 /* AIF1RX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN4_SPACING_SHIFT 9 /* AIF1RX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN4_SPACING_WIDTH 6 /* AIF1RX_CHAN4_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN4_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN4_SLOTS_SHIFT 6 /* AIF1RX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN4_SLOTS_WIDTH 3 /* AIF1RX_CHAN4_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN4_START_SLOT_MASK 0x003F /* AIF1RX_CHAN4_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN4_START_SLOT_SHIFT 0 /* AIF1RX_CHAN4_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN4_START_SLOT_WIDTH 6 /* AIF1RX_CHAN4_START_SLOT - [5:0] */
+
+/*
+ * R788 (0x314) - AIF1RX Channel 5 Configuration
+ */
+#define WM8915_AIF1RX_CHAN5_DAT_INV 0x8000 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8915_AIF1RX_CHAN5_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8915_AIF1RX_CHAN5_DAT_INV_SHIFT 15 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8915_AIF1RX_CHAN5_DAT_INV_WIDTH 1 /* AIF1RX_CHAN5_DAT_INV */
+#define WM8915_AIF1RX_CHAN5_SPACING_MASK 0x7E00 /* AIF1RX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN5_SPACING_SHIFT 9 /* AIF1RX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN5_SPACING_WIDTH 6 /* AIF1RX_CHAN5_SPACING - [14:9] */
+#define WM8915_AIF1RX_CHAN5_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN5_SLOTS_SHIFT 6 /* AIF1RX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN5_SLOTS_WIDTH 3 /* AIF1RX_CHAN5_SLOTS - [8:6] */
+#define WM8915_AIF1RX_CHAN5_START_SLOT_MASK 0x003F /* AIF1RX_CHAN5_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN5_START_SLOT_SHIFT 0 /* AIF1RX_CHAN5_START_SLOT - [5:0] */
+#define WM8915_AIF1RX_CHAN5_START_SLOT_WIDTH 6 /* AIF1RX_CHAN5_START_SLOT - [5:0] */
+
+/*
+ * R789 (0x315) - AIF1RX Mono Configuration
+ */
+#define WM8915_AIF1RX_CHAN4_MONO_MODE 0x0004 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8915_AIF1RX_CHAN4_MONO_MODE_MASK 0x0004 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8915_AIF1RX_CHAN4_MONO_MODE_SHIFT 2 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8915_AIF1RX_CHAN4_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN4_MONO_MODE */
+#define WM8915_AIF1RX_CHAN2_MONO_MODE 0x0002 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8915_AIF1RX_CHAN2_MONO_MODE_MASK 0x0002 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8915_AIF1RX_CHAN2_MONO_MODE_SHIFT 1 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8915_AIF1RX_CHAN2_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN2_MONO_MODE */
+#define WM8915_AIF1RX_CHAN0_MONO_MODE 0x0001 /* AIF1RX_CHAN0_MONO_MODE */
+#define WM8915_AIF1RX_CHAN0_MONO_MODE_MASK 0x0001 /* AIF1RX_CHAN0_MONO_MODE */
+#define WM8915_AIF1RX_CHAN0_MONO_MODE_SHIFT 0 /* AIF1RX_CHAN0_MONO_MODE */
+#define WM8915_AIF1RX_CHAN0_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN0_MONO_MODE */
+
+/*
+ * R794 (0x31A) - AIF1TX Test
+ */
+#define WM8915_AIF1TX45_DITHER_ENA 0x0004 /* AIF1TX45_DITHER_ENA */
+#define WM8915_AIF1TX45_DITHER_ENA_MASK 0x0004 /* AIF1TX45_DITHER_ENA */
+#define WM8915_AIF1TX45_DITHER_ENA_SHIFT 2 /* AIF1TX45_DITHER_ENA */
+#define WM8915_AIF1TX45_DITHER_ENA_WIDTH 1 /* AIF1TX45_DITHER_ENA */
+#define WM8915_AIF1TX23_DITHER_ENA 0x0002 /* AIF1TX23_DITHER_ENA */
+#define WM8915_AIF1TX23_DITHER_ENA_MASK 0x0002 /* AIF1TX23_DITHER_ENA */
+#define WM8915_AIF1TX23_DITHER_ENA_SHIFT 1 /* AIF1TX23_DITHER_ENA */
+#define WM8915_AIF1TX23_DITHER_ENA_WIDTH 1 /* AIF1TX23_DITHER_ENA */
+#define WM8915_AIF1TX01_DITHER_ENA 0x0001 /* AIF1TX01_DITHER_ENA */
+#define WM8915_AIF1TX01_DITHER_ENA_MASK 0x0001 /* AIF1TX01_DITHER_ENA */
+#define WM8915_AIF1TX01_DITHER_ENA_SHIFT 0 /* AIF1TX01_DITHER_ENA */
+#define WM8915_AIF1TX01_DITHER_ENA_WIDTH 1 /* AIF1TX01_DITHER_ENA */
+
+/*
+ * R800 (0x320) - AIF2 Control
+ */
+#define WM8915_AIF2_TRI 0x0004 /* AIF2_TRI */
+#define WM8915_AIF2_TRI_MASK 0x0004 /* AIF2_TRI */
+#define WM8915_AIF2_TRI_SHIFT 2 /* AIF2_TRI */
+#define WM8915_AIF2_TRI_WIDTH 1 /* AIF2_TRI */
+#define WM8915_AIF2_FMT_MASK 0x0003 /* AIF2_FMT - [1:0] */
+#define WM8915_AIF2_FMT_SHIFT 0 /* AIF2_FMT - [1:0] */
+#define WM8915_AIF2_FMT_WIDTH 2 /* AIF2_FMT - [1:0] */
+
+/*
+ * R801 (0x321) - AIF2 BCLK
+ */
+#define WM8915_AIF2_BCLK_INV 0x0400 /* AIF2_BCLK_INV */
+#define WM8915_AIF2_BCLK_INV_MASK 0x0400 /* AIF2_BCLK_INV */
+#define WM8915_AIF2_BCLK_INV_SHIFT 10 /* AIF2_BCLK_INV */
+#define WM8915_AIF2_BCLK_INV_WIDTH 1 /* AIF2_BCLK_INV */
+#define WM8915_AIF2_BCLK_FRC 0x0200 /* AIF2_BCLK_FRC */
+#define WM8915_AIF2_BCLK_FRC_MASK 0x0200 /* AIF2_BCLK_FRC */
+#define WM8915_AIF2_BCLK_FRC_SHIFT 9 /* AIF2_BCLK_FRC */
+#define WM8915_AIF2_BCLK_FRC_WIDTH 1 /* AIF2_BCLK_FRC */
+#define WM8915_AIF2_BCLK_MSTR 0x0100 /* AIF2_BCLK_MSTR */
+#define WM8915_AIF2_BCLK_MSTR_MASK 0x0100 /* AIF2_BCLK_MSTR */
+#define WM8915_AIF2_BCLK_MSTR_SHIFT 8 /* AIF2_BCLK_MSTR */
+#define WM8915_AIF2_BCLK_MSTR_WIDTH 1 /* AIF2_BCLK_MSTR */
+#define WM8915_AIF2_BCLK_DIV_MASK 0x000F /* AIF2_BCLK_DIV - [3:0] */
+#define WM8915_AIF2_BCLK_DIV_SHIFT 0 /* AIF2_BCLK_DIV - [3:0] */
+#define WM8915_AIF2_BCLK_DIV_WIDTH 4 /* AIF2_BCLK_DIV - [3:0] */
+
+/*
+ * R802 (0x322) - AIF2 TX LRCLK(1)
+ */
+#define WM8915_AIF2TX_RATE_MASK 0x07FF /* AIF2TX_RATE - [10:0] */
+#define WM8915_AIF2TX_RATE_SHIFT 0 /* AIF2TX_RATE - [10:0] */
+#define WM8915_AIF2TX_RATE_WIDTH 11 /* AIF2TX_RATE - [10:0] */
+
+/*
+ * R803 (0x323) - AIF2 TX LRCLK(2)
+ */
+#define WM8915_AIF2TX_LRCLK_MODE 0x0008 /* AIF2TX_LRCLK_MODE */
+#define WM8915_AIF2TX_LRCLK_MODE_MASK 0x0008 /* AIF2TX_LRCLK_MODE */
+#define WM8915_AIF2TX_LRCLK_MODE_SHIFT 3 /* AIF2TX_LRCLK_MODE */
+#define WM8915_AIF2TX_LRCLK_MODE_WIDTH 1 /* AIF2TX_LRCLK_MODE */
+#define WM8915_AIF2TX_LRCLK_INV 0x0004 /* AIF2TX_LRCLK_INV */
+#define WM8915_AIF2TX_LRCLK_INV_MASK 0x0004 /* AIF2TX_LRCLK_INV */
+#define WM8915_AIF2TX_LRCLK_INV_SHIFT 2 /* AIF2TX_LRCLK_INV */
+#define WM8915_AIF2TX_LRCLK_INV_WIDTH 1 /* AIF2TX_LRCLK_INV */
+#define WM8915_AIF2TX_LRCLK_FRC 0x0002 /* AIF2TX_LRCLK_FRC */
+#define WM8915_AIF2TX_LRCLK_FRC_MASK 0x0002 /* AIF2TX_LRCLK_FRC */
+#define WM8915_AIF2TX_LRCLK_FRC_SHIFT 1 /* AIF2TX_LRCLK_FRC */
+#define WM8915_AIF2TX_LRCLK_FRC_WIDTH 1 /* AIF2TX_LRCLK_FRC */
+#define WM8915_AIF2TX_LRCLK_MSTR 0x0001 /* AIF2TX_LRCLK_MSTR */
+#define WM8915_AIF2TX_LRCLK_MSTR_MASK 0x0001 /* AIF2TX_LRCLK_MSTR */
+#define WM8915_AIF2TX_LRCLK_MSTR_SHIFT 0 /* AIF2TX_LRCLK_MSTR */
+#define WM8915_AIF2TX_LRCLK_MSTR_WIDTH 1 /* AIF2TX_LRCLK_MSTR */
+
+/*
+ * R804 (0x324) - AIF2 RX LRCLK(1)
+ */
+#define WM8915_AIF2RX_RATE_MASK 0x07FF /* AIF2RX_RATE - [10:0] */
+#define WM8915_AIF2RX_RATE_SHIFT 0 /* AIF2RX_RATE - [10:0] */
+#define WM8915_AIF2RX_RATE_WIDTH 11 /* AIF2RX_RATE - [10:0] */
+
+/*
+ * R805 (0x325) - AIF2 RX LRCLK(2)
+ */
+#define WM8915_AIF2RX_LRCLK_INV 0x0004 /* AIF2RX_LRCLK_INV */
+#define WM8915_AIF2RX_LRCLK_INV_MASK 0x0004 /* AIF2RX_LRCLK_INV */
+#define WM8915_AIF2RX_LRCLK_INV_SHIFT 2 /* AIF2RX_LRCLK_INV */
+#define WM8915_AIF2RX_LRCLK_INV_WIDTH 1 /* AIF2RX_LRCLK_INV */
+#define WM8915_AIF2RX_LRCLK_FRC 0x0002 /* AIF2RX_LRCLK_FRC */
+#define WM8915_AIF2RX_LRCLK_FRC_MASK 0x0002 /* AIF2RX_LRCLK_FRC */
+#define WM8915_AIF2RX_LRCLK_FRC_SHIFT 1 /* AIF2RX_LRCLK_FRC */
+#define WM8915_AIF2RX_LRCLK_FRC_WIDTH 1 /* AIF2RX_LRCLK_FRC */
+#define WM8915_AIF2RX_LRCLK_MSTR 0x0001 /* AIF2RX_LRCLK_MSTR */
+#define WM8915_AIF2RX_LRCLK_MSTR_MASK 0x0001 /* AIF2RX_LRCLK_MSTR */
+#define WM8915_AIF2RX_LRCLK_MSTR_SHIFT 0 /* AIF2RX_LRCLK_MSTR */
+#define WM8915_AIF2RX_LRCLK_MSTR_WIDTH 1 /* AIF2RX_LRCLK_MSTR */
+
+/*
+ * R806 (0x326) - AIF2TX Data Configuration (1)
+ */
+#define WM8915_AIF2TX_WL_MASK 0xFF00 /* AIF2TX_WL - [15:8] */
+#define WM8915_AIF2TX_WL_SHIFT 8 /* AIF2TX_WL - [15:8] */
+#define WM8915_AIF2TX_WL_WIDTH 8 /* AIF2TX_WL - [15:8] */
+#define WM8915_AIF2TX_SLOT_LEN_MASK 0x00FF /* AIF2TX_SLOT_LEN - [7:0] */
+#define WM8915_AIF2TX_SLOT_LEN_SHIFT 0 /* AIF2TX_SLOT_LEN - [7:0] */
+#define WM8915_AIF2TX_SLOT_LEN_WIDTH 8 /* AIF2TX_SLOT_LEN - [7:0] */
+
+/*
+ * R807 (0x327) - AIF2TX Data Configuration (2)
+ */
+#define WM8915_AIF2TX_DAT_TRI 0x0001 /* AIF2TX_DAT_TRI */
+#define WM8915_AIF2TX_DAT_TRI_MASK 0x0001 /* AIF2TX_DAT_TRI */
+#define WM8915_AIF2TX_DAT_TRI_SHIFT 0 /* AIF2TX_DAT_TRI */
+#define WM8915_AIF2TX_DAT_TRI_WIDTH 1 /* AIF2TX_DAT_TRI */
+
+/*
+ * R808 (0x328) - AIF2RX Data Configuration
+ */
+#define WM8915_AIF2RX_WL_MASK 0xFF00 /* AIF2RX_WL - [15:8] */
+#define WM8915_AIF2RX_WL_SHIFT 8 /* AIF2RX_WL - [15:8] */
+#define WM8915_AIF2RX_WL_WIDTH 8 /* AIF2RX_WL - [15:8] */
+#define WM8915_AIF2RX_SLOT_LEN_MASK 0x00FF /* AIF2RX_SLOT_LEN - [7:0] */
+#define WM8915_AIF2RX_SLOT_LEN_SHIFT 0 /* AIF2RX_SLOT_LEN - [7:0] */
+#define WM8915_AIF2RX_SLOT_LEN_WIDTH 8 /* AIF2RX_SLOT_LEN - [7:0] */
+
+/*
+ * R809 (0x329) - AIF2TX Channel 0 Configuration
+ */
+#define WM8915_AIF2TX_CHAN0_DAT_INV 0x8000 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8915_AIF2TX_CHAN0_DAT_INV_MASK 0x8000 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8915_AIF2TX_CHAN0_DAT_INV_SHIFT 15 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8915_AIF2TX_CHAN0_DAT_INV_WIDTH 1 /* AIF2TX_CHAN0_DAT_INV */
+#define WM8915_AIF2TX_CHAN0_SPACING_MASK 0x7E00 /* AIF2TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN0_SPACING_SHIFT 9 /* AIF2TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN0_SPACING_WIDTH 6 /* AIF2TX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN0_SLOTS_MASK 0x01C0 /* AIF2TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN0_SLOTS_SHIFT 6 /* AIF2TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN0_SLOTS_WIDTH 3 /* AIF2TX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN0_START_SLOT_MASK 0x003F /* AIF2TX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF2TX_CHAN0_START_SLOT_SHIFT 0 /* AIF2TX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF2TX_CHAN0_START_SLOT_WIDTH 6 /* AIF2TX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R810 (0x32A) - AIF2TX Channel 1 Configuration
+ */
+#define WM8915_AIF2TX_CHAN1_DAT_INV 0x8000 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8915_AIF2TX_CHAN1_DAT_INV_MASK 0x8000 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8915_AIF2TX_CHAN1_DAT_INV_SHIFT 15 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8915_AIF2TX_CHAN1_DAT_INV_WIDTH 1 /* AIF2TX_CHAN1_DAT_INV */
+#define WM8915_AIF2TX_CHAN1_SPACING_MASK 0x7E00 /* AIF2TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN1_SPACING_SHIFT 9 /* AIF2TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN1_SPACING_WIDTH 6 /* AIF2TX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2TX_CHAN1_SLOTS_MASK 0x01C0 /* AIF2TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN1_SLOTS_SHIFT 6 /* AIF2TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN1_SLOTS_WIDTH 3 /* AIF2TX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2TX_CHAN1_START_SLOT_MASK 0x003F /* AIF2TX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF2TX_CHAN1_START_SLOT_SHIFT 0 /* AIF2TX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF2TX_CHAN1_START_SLOT_WIDTH 6 /* AIF2TX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R811 (0x32B) - AIF2RX Channel 0 Configuration
+ */
+#define WM8915_AIF2RX_CHAN0_DAT_INV 0x8000 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8915_AIF2RX_CHAN0_DAT_INV_MASK 0x8000 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8915_AIF2RX_CHAN0_DAT_INV_SHIFT 15 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8915_AIF2RX_CHAN0_DAT_INV_WIDTH 1 /* AIF2RX_CHAN0_DAT_INV */
+#define WM8915_AIF2RX_CHAN0_SPACING_MASK 0x7E00 /* AIF2RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN0_SPACING_SHIFT 9 /* AIF2RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN0_SPACING_WIDTH 6 /* AIF2RX_CHAN0_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN0_SLOTS_MASK 0x01C0 /* AIF2RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN0_SLOTS_SHIFT 6 /* AIF2RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN0_SLOTS_WIDTH 3 /* AIF2RX_CHAN0_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN0_START_SLOT_MASK 0x003F /* AIF2RX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF2RX_CHAN0_START_SLOT_SHIFT 0 /* AIF2RX_CHAN0_START_SLOT - [5:0] */
+#define WM8915_AIF2RX_CHAN0_START_SLOT_WIDTH 6 /* AIF2RX_CHAN0_START_SLOT - [5:0] */
+
+/*
+ * R812 (0x32C) - AIF2RX Channel 1 Configuration
+ */
+#define WM8915_AIF2RX_CHAN1_DAT_INV 0x8000 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8915_AIF2RX_CHAN1_DAT_INV_MASK 0x8000 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8915_AIF2RX_CHAN1_DAT_INV_SHIFT 15 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8915_AIF2RX_CHAN1_DAT_INV_WIDTH 1 /* AIF2RX_CHAN1_DAT_INV */
+#define WM8915_AIF2RX_CHAN1_SPACING_MASK 0x7E00 /* AIF2RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN1_SPACING_SHIFT 9 /* AIF2RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN1_SPACING_WIDTH 6 /* AIF2RX_CHAN1_SPACING - [14:9] */
+#define WM8915_AIF2RX_CHAN1_SLOTS_MASK 0x01C0 /* AIF2RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN1_SLOTS_SHIFT 6 /* AIF2RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN1_SLOTS_WIDTH 3 /* AIF2RX_CHAN1_SLOTS - [8:6] */
+#define WM8915_AIF2RX_CHAN1_START_SLOT_MASK 0x003F /* AIF2RX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF2RX_CHAN1_START_SLOT_SHIFT 0 /* AIF2RX_CHAN1_START_SLOT - [5:0] */
+#define WM8915_AIF2RX_CHAN1_START_SLOT_WIDTH 6 /* AIF2RX_CHAN1_START_SLOT - [5:0] */
+
+/*
+ * R813 (0x32D) - AIF2RX Mono Configuration
+ */
+#define WM8915_AIF2RX_CHAN0_MONO_MODE 0x0001 /* AIF2RX_CHAN0_MONO_MODE */
+#define WM8915_AIF2RX_CHAN0_MONO_MODE_MASK 0x0001 /* AIF2RX_CHAN0_MONO_MODE */
+#define WM8915_AIF2RX_CHAN0_MONO_MODE_SHIFT 0 /* AIF2RX_CHAN0_MONO_MODE */
+#define WM8915_AIF2RX_CHAN0_MONO_MODE_WIDTH 1 /* AIF2RX_CHAN0_MONO_MODE */
+
+/*
+ * R815 (0x32F) - AIF2TX Test
+ */
+#define WM8915_AIF2TX_DITHER_ENA 0x0001 /* AIF2TX_DITHER_ENA */
+#define WM8915_AIF2TX_DITHER_ENA_MASK 0x0001 /* AIF2TX_DITHER_ENA */
+#define WM8915_AIF2TX_DITHER_ENA_SHIFT 0 /* AIF2TX_DITHER_ENA */
+#define WM8915_AIF2TX_DITHER_ENA_WIDTH 1 /* AIF2TX_DITHER_ENA */
+
+/*
+ * R1024 (0x400) - DSP1 TX Left Volume
+ */
+#define WM8915_DSP1TX_VU 0x0100 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_MASK 0x0100 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_SHIFT 8 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_WIDTH 1 /* DSP1TX_VU */
+#define WM8915_DSP1TXL_VOL_MASK 0x00FF /* DSP1TXL_VOL - [7:0] */
+#define WM8915_DSP1TXL_VOL_SHIFT 0 /* DSP1TXL_VOL - [7:0] */
+#define WM8915_DSP1TXL_VOL_WIDTH 8 /* DSP1TXL_VOL - [7:0] */
+
+/*
+ * R1025 (0x401) - DSP1 TX Right Volume
+ */
+#define WM8915_DSP1TX_VU 0x0100 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_MASK 0x0100 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_SHIFT 8 /* DSP1TX_VU */
+#define WM8915_DSP1TX_VU_WIDTH 1 /* DSP1TX_VU */
+#define WM8915_DSP1TXR_VOL_MASK 0x00FF /* DSP1TXR_VOL - [7:0] */
+#define WM8915_DSP1TXR_VOL_SHIFT 0 /* DSP1TXR_VOL - [7:0] */
+#define WM8915_DSP1TXR_VOL_WIDTH 8 /* DSP1TXR_VOL - [7:0] */
+
+/*
+ * R1026 (0x402) - DSP1 RX Left Volume
+ */
+#define WM8915_DSP1RX_VU 0x0100 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_MASK 0x0100 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_SHIFT 8 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_WIDTH 1 /* DSP1RX_VU */
+#define WM8915_DSP1RXL_VOL_MASK 0x00FF /* DSP1RXL_VOL - [7:0] */
+#define WM8915_DSP1RXL_VOL_SHIFT 0 /* DSP1RXL_VOL - [7:0] */
+#define WM8915_DSP1RXL_VOL_WIDTH 8 /* DSP1RXL_VOL - [7:0] */
+
+/*
+ * R1027 (0x403) - DSP1 RX Right Volume
+ */
+#define WM8915_DSP1RX_VU 0x0100 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_MASK 0x0100 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_SHIFT 8 /* DSP1RX_VU */
+#define WM8915_DSP1RX_VU_WIDTH 1 /* DSP1RX_VU */
+#define WM8915_DSP1RXR_VOL_MASK 0x00FF /* DSP1RXR_VOL - [7:0] */
+#define WM8915_DSP1RXR_VOL_SHIFT 0 /* DSP1RXR_VOL - [7:0] */
+#define WM8915_DSP1RXR_VOL_WIDTH 8 /* DSP1RXR_VOL - [7:0] */
+
+/*
+ * R1040 (0x410) - DSP1 TX Filters
+ */
+#define WM8915_DSP1TX_NF 0x2000 /* DSP1TX_NF */
+#define WM8915_DSP1TX_NF_MASK 0x2000 /* DSP1TX_NF */
+#define WM8915_DSP1TX_NF_SHIFT 13 /* DSP1TX_NF */
+#define WM8915_DSP1TX_NF_WIDTH 1 /* DSP1TX_NF */
+#define WM8915_DSP1TXL_HPF 0x1000 /* DSP1TXL_HPF */
+#define WM8915_DSP1TXL_HPF_MASK 0x1000 /* DSP1TXL_HPF */
+#define WM8915_DSP1TXL_HPF_SHIFT 12 /* DSP1TXL_HPF */
+#define WM8915_DSP1TXL_HPF_WIDTH 1 /* DSP1TXL_HPF */
+#define WM8915_DSP1TXR_HPF 0x0800 /* DSP1TXR_HPF */
+#define WM8915_DSP1TXR_HPF_MASK 0x0800 /* DSP1TXR_HPF */
+#define WM8915_DSP1TXR_HPF_SHIFT 11 /* DSP1TXR_HPF */
+#define WM8915_DSP1TXR_HPF_WIDTH 1 /* DSP1TXR_HPF */
+#define WM8915_DSP1TX_HPF_MODE_MASK 0x0018 /* DSP1TX_HPF_MODE - [4:3] */
+#define WM8915_DSP1TX_HPF_MODE_SHIFT 3 /* DSP1TX_HPF_MODE - [4:3] */
+#define WM8915_DSP1TX_HPF_MODE_WIDTH 2 /* DSP1TX_HPF_MODE - [4:3] */
+#define WM8915_DSP1TX_HPF_CUT_MASK 0x0007 /* DSP1TX_HPF_CUT - [2:0] */
+#define WM8915_DSP1TX_HPF_CUT_SHIFT 0 /* DSP1TX_HPF_CUT - [2:0] */
+#define WM8915_DSP1TX_HPF_CUT_WIDTH 3 /* DSP1TX_HPF_CUT - [2:0] */
+
+/*
+ * R1056 (0x420) - DSP1 RX Filters (1)
+ */
+#define WM8915_DSP1RX_MUTE 0x0200 /* DSP1RX_MUTE */
+#define WM8915_DSP1RX_MUTE_MASK 0x0200 /* DSP1RX_MUTE */
+#define WM8915_DSP1RX_MUTE_SHIFT 9 /* DSP1RX_MUTE */
+#define WM8915_DSP1RX_MUTE_WIDTH 1 /* DSP1RX_MUTE */
+#define WM8915_DSP1RX_MONO 0x0080 /* DSP1RX_MONO */
+#define WM8915_DSP1RX_MONO_MASK 0x0080 /* DSP1RX_MONO */
+#define WM8915_DSP1RX_MONO_SHIFT 7 /* DSP1RX_MONO */
+#define WM8915_DSP1RX_MONO_WIDTH 1 /* DSP1RX_MONO */
+#define WM8915_DSP1RX_MUTERATE 0x0020 /* DSP1RX_MUTERATE */
+#define WM8915_DSP1RX_MUTERATE_MASK 0x0020 /* DSP1RX_MUTERATE */
+#define WM8915_DSP1RX_MUTERATE_SHIFT 5 /* DSP1RX_MUTERATE */
+#define WM8915_DSP1RX_MUTERATE_WIDTH 1 /* DSP1RX_MUTERATE */
+#define WM8915_DSP1RX_UNMUTE_RAMP 0x0010 /* DSP1RX_UNMUTE_RAMP */
+#define WM8915_DSP1RX_UNMUTE_RAMP_MASK 0x0010 /* DSP1RX_UNMUTE_RAMP */
+#define WM8915_DSP1RX_UNMUTE_RAMP_SHIFT 4 /* DSP1RX_UNMUTE_RAMP */
+#define WM8915_DSP1RX_UNMUTE_RAMP_WIDTH 1 /* DSP1RX_UNMUTE_RAMP */
+
+/*
+ * R1057 (0x421) - DSP1 RX Filters (2)
+ */
+#define WM8915_DSP1RX_3D_GAIN_MASK 0x3E00 /* DSP1RX_3D_GAIN - [13:9] */
+#define WM8915_DSP1RX_3D_GAIN_SHIFT 9 /* DSP1RX_3D_GAIN - [13:9] */
+#define WM8915_DSP1RX_3D_GAIN_WIDTH 5 /* DSP1RX_3D_GAIN - [13:9] */
+#define WM8915_DSP1RX_3D_ENA 0x0100 /* DSP1RX_3D_ENA */
+#define WM8915_DSP1RX_3D_ENA_MASK 0x0100 /* DSP1RX_3D_ENA */
+#define WM8915_DSP1RX_3D_ENA_SHIFT 8 /* DSP1RX_3D_ENA */
+#define WM8915_DSP1RX_3D_ENA_WIDTH 1 /* DSP1RX_3D_ENA */
+
+/*
+ * R1088 (0x440) - DSP1 DRC (1)
+ */
+#define WM8915_DSP1DRC_SIG_DET_RMS_MASK 0xF800 /* DSP1DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP1DRC_SIG_DET_RMS_SHIFT 11 /* DSP1DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP1DRC_SIG_DET_RMS_WIDTH 5 /* DSP1DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP1DRC_SIG_DET_PK_MASK 0x0600 /* DSP1DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP1DRC_SIG_DET_PK_SHIFT 9 /* DSP1DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP1DRC_SIG_DET_PK_WIDTH 2 /* DSP1DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP1DRC_NG_ENA 0x0100 /* DSP1DRC_NG_ENA */
+#define WM8915_DSP1DRC_NG_ENA_MASK 0x0100 /* DSP1DRC_NG_ENA */
+#define WM8915_DSP1DRC_NG_ENA_SHIFT 8 /* DSP1DRC_NG_ENA */
+#define WM8915_DSP1DRC_NG_ENA_WIDTH 1 /* DSP1DRC_NG_ENA */
+#define WM8915_DSP1DRC_SIG_DET_MODE 0x0080 /* DSP1DRC_SIG_DET_MODE */
+#define WM8915_DSP1DRC_SIG_DET_MODE_MASK 0x0080 /* DSP1DRC_SIG_DET_MODE */
+#define WM8915_DSP1DRC_SIG_DET_MODE_SHIFT 7 /* DSP1DRC_SIG_DET_MODE */
+#define WM8915_DSP1DRC_SIG_DET_MODE_WIDTH 1 /* DSP1DRC_SIG_DET_MODE */
+#define WM8915_DSP1DRC_SIG_DET 0x0040 /* DSP1DRC_SIG_DET */
+#define WM8915_DSP1DRC_SIG_DET_MASK 0x0040 /* DSP1DRC_SIG_DET */
+#define WM8915_DSP1DRC_SIG_DET_SHIFT 6 /* DSP1DRC_SIG_DET */
+#define WM8915_DSP1DRC_SIG_DET_WIDTH 1 /* DSP1DRC_SIG_DET */
+#define WM8915_DSP1DRC_KNEE2_OP_ENA 0x0020 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8915_DSP1DRC_KNEE2_OP_ENA_MASK 0x0020 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8915_DSP1DRC_KNEE2_OP_ENA_SHIFT 5 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8915_DSP1DRC_KNEE2_OP_ENA_WIDTH 1 /* DSP1DRC_KNEE2_OP_ENA */
+#define WM8915_DSP1DRC_QR 0x0010 /* DSP1DRC_QR */
+#define WM8915_DSP1DRC_QR_MASK 0x0010 /* DSP1DRC_QR */
+#define WM8915_DSP1DRC_QR_SHIFT 4 /* DSP1DRC_QR */
+#define WM8915_DSP1DRC_QR_WIDTH 1 /* DSP1DRC_QR */
+#define WM8915_DSP1DRC_ANTICLIP 0x0008 /* DSP1DRC_ANTICLIP */
+#define WM8915_DSP1DRC_ANTICLIP_MASK 0x0008 /* DSP1DRC_ANTICLIP */
+#define WM8915_DSP1DRC_ANTICLIP_SHIFT 3 /* DSP1DRC_ANTICLIP */
+#define WM8915_DSP1DRC_ANTICLIP_WIDTH 1 /* DSP1DRC_ANTICLIP */
+#define WM8915_DSP1RX_DRC_ENA 0x0004 /* DSP1RX_DRC_ENA */
+#define WM8915_DSP1RX_DRC_ENA_MASK 0x0004 /* DSP1RX_DRC_ENA */
+#define WM8915_DSP1RX_DRC_ENA_SHIFT 2 /* DSP1RX_DRC_ENA */
+#define WM8915_DSP1RX_DRC_ENA_WIDTH 1 /* DSP1RX_DRC_ENA */
+#define WM8915_DSP1TXL_DRC_ENA 0x0002 /* DSP1TXL_DRC_ENA */
+#define WM8915_DSP1TXL_DRC_ENA_MASK 0x0002 /* DSP1TXL_DRC_ENA */
+#define WM8915_DSP1TXL_DRC_ENA_SHIFT 1 /* DSP1TXL_DRC_ENA */
+#define WM8915_DSP1TXL_DRC_ENA_WIDTH 1 /* DSP1TXL_DRC_ENA */
+#define WM8915_DSP1TXR_DRC_ENA 0x0001 /* DSP1TXR_DRC_ENA */
+#define WM8915_DSP1TXR_DRC_ENA_MASK 0x0001 /* DSP1TXR_DRC_ENA */
+#define WM8915_DSP1TXR_DRC_ENA_SHIFT 0 /* DSP1TXR_DRC_ENA */
+#define WM8915_DSP1TXR_DRC_ENA_WIDTH 1 /* DSP1TXR_DRC_ENA */
+
+/*
+ * R1089 (0x441) - DSP1 DRC (2)
+ */
+#define WM8915_DSP1DRC_ATK_MASK 0x1E00 /* DSP1DRC_ATK - [12:9] */
+#define WM8915_DSP1DRC_ATK_SHIFT 9 /* DSP1DRC_ATK - [12:9] */
+#define WM8915_DSP1DRC_ATK_WIDTH 4 /* DSP1DRC_ATK - [12:9] */
+#define WM8915_DSP1DRC_DCY_MASK 0x01E0 /* DSP1DRC_DCY - [8:5] */
+#define WM8915_DSP1DRC_DCY_SHIFT 5 /* DSP1DRC_DCY - [8:5] */
+#define WM8915_DSP1DRC_DCY_WIDTH 4 /* DSP1DRC_DCY - [8:5] */
+#define WM8915_DSP1DRC_MINGAIN_MASK 0x001C /* DSP1DRC_MINGAIN - [4:2] */
+#define WM8915_DSP1DRC_MINGAIN_SHIFT 2 /* DSP1DRC_MINGAIN - [4:2] */
+#define WM8915_DSP1DRC_MINGAIN_WIDTH 3 /* DSP1DRC_MINGAIN - [4:2] */
+#define WM8915_DSP1DRC_MAXGAIN_MASK 0x0003 /* DSP1DRC_MAXGAIN - [1:0] */
+#define WM8915_DSP1DRC_MAXGAIN_SHIFT 0 /* DSP1DRC_MAXGAIN - [1:0] */
+#define WM8915_DSP1DRC_MAXGAIN_WIDTH 2 /* DSP1DRC_MAXGAIN - [1:0] */
+
+/*
+ * R1090 (0x442) - DSP1 DRC (3)
+ */
+#define WM8915_DSP1DRC_NG_MINGAIN_MASK 0xF000 /* DSP1DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP1DRC_NG_MINGAIN_SHIFT 12 /* DSP1DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP1DRC_NG_MINGAIN_WIDTH 4 /* DSP1DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP1DRC_NG_EXP_MASK 0x0C00 /* DSP1DRC_NG_EXP - [11:10] */
+#define WM8915_DSP1DRC_NG_EXP_SHIFT 10 /* DSP1DRC_NG_EXP - [11:10] */
+#define WM8915_DSP1DRC_NG_EXP_WIDTH 2 /* DSP1DRC_NG_EXP - [11:10] */
+#define WM8915_DSP1DRC_QR_THR_MASK 0x0300 /* DSP1DRC_QR_THR - [9:8] */
+#define WM8915_DSP1DRC_QR_THR_SHIFT 8 /* DSP1DRC_QR_THR - [9:8] */
+#define WM8915_DSP1DRC_QR_THR_WIDTH 2 /* DSP1DRC_QR_THR - [9:8] */
+#define WM8915_DSP1DRC_QR_DCY_MASK 0x00C0 /* DSP1DRC_QR_DCY - [7:6] */
+#define WM8915_DSP1DRC_QR_DCY_SHIFT 6 /* DSP1DRC_QR_DCY - [7:6] */
+#define WM8915_DSP1DRC_QR_DCY_WIDTH 2 /* DSP1DRC_QR_DCY - [7:6] */
+#define WM8915_DSP1DRC_HI_COMP_MASK 0x0038 /* DSP1DRC_HI_COMP - [5:3] */
+#define WM8915_DSP1DRC_HI_COMP_SHIFT 3 /* DSP1DRC_HI_COMP - [5:3] */
+#define WM8915_DSP1DRC_HI_COMP_WIDTH 3 /* DSP1DRC_HI_COMP - [5:3] */
+#define WM8915_DSP1DRC_LO_COMP_MASK 0x0007 /* DSP1DRC_LO_COMP - [2:0] */
+#define WM8915_DSP1DRC_LO_COMP_SHIFT 0 /* DSP1DRC_LO_COMP - [2:0] */
+#define WM8915_DSP1DRC_LO_COMP_WIDTH 3 /* DSP1DRC_LO_COMP - [2:0] */
+
+/*
+ * R1091 (0x443) - DSP1 DRC (4)
+ */
+#define WM8915_DSP1DRC_KNEE_IP_MASK 0x07E0 /* DSP1DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP1DRC_KNEE_IP_SHIFT 5 /* DSP1DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP1DRC_KNEE_IP_WIDTH 6 /* DSP1DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP1DRC_KNEE_OP_MASK 0x001F /* DSP1DRC_KNEE_OP - [4:0] */
+#define WM8915_DSP1DRC_KNEE_OP_SHIFT 0 /* DSP1DRC_KNEE_OP - [4:0] */
+#define WM8915_DSP1DRC_KNEE_OP_WIDTH 5 /* DSP1DRC_KNEE_OP - [4:0] */
+
+/*
+ * R1092 (0x444) - DSP1 DRC (5)
+ */
+#define WM8915_DSP1DRC_KNEE2_IP_MASK 0x03E0 /* DSP1DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP1DRC_KNEE2_IP_SHIFT 5 /* DSP1DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP1DRC_KNEE2_IP_WIDTH 5 /* DSP1DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP1DRC_KNEE2_OP_MASK 0x001F /* DSP1DRC_KNEE2_OP - [4:0] */
+#define WM8915_DSP1DRC_KNEE2_OP_SHIFT 0 /* DSP1DRC_KNEE2_OP - [4:0] */
+#define WM8915_DSP1DRC_KNEE2_OP_WIDTH 5 /* DSP1DRC_KNEE2_OP - [4:0] */
+
+/*
+ * R1152 (0x480) - DSP1 RX EQ Gains (1)
+ */
+#define WM8915_DSP1RX_EQ_B1_GAIN_MASK 0xF800 /* DSP1RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B1_GAIN_SHIFT 11 /* DSP1RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B1_GAIN_WIDTH 5 /* DSP1RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B2_GAIN_MASK 0x07C0 /* DSP1RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP1RX_EQ_B2_GAIN_SHIFT 6 /* DSP1RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP1RX_EQ_B2_GAIN_WIDTH 5 /* DSP1RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP1RX_EQ_B3_GAIN_MASK 0x003E /* DSP1RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP1RX_EQ_B3_GAIN_SHIFT 1 /* DSP1RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP1RX_EQ_B3_GAIN_WIDTH 5 /* DSP1RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP1RX_EQ_ENA 0x0001 /* DSP1RX_EQ_ENA */
+#define WM8915_DSP1RX_EQ_ENA_MASK 0x0001 /* DSP1RX_EQ_ENA */
+#define WM8915_DSP1RX_EQ_ENA_SHIFT 0 /* DSP1RX_EQ_ENA */
+#define WM8915_DSP1RX_EQ_ENA_WIDTH 1 /* DSP1RX_EQ_ENA */
+
+/*
+ * R1153 (0x481) - DSP1 RX EQ Gains (2)
+ */
+#define WM8915_DSP1RX_EQ_B4_GAIN_MASK 0xF800 /* DSP1RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B4_GAIN_SHIFT 11 /* DSP1RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B4_GAIN_WIDTH 5 /* DSP1RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP1RX_EQ_B5_GAIN_MASK 0x07C0 /* DSP1RX_EQ_B5_GAIN - [10:6] */
+#define WM8915_DSP1RX_EQ_B5_GAIN_SHIFT 6 /* DSP1RX_EQ_B5_GAIN - [10:6] */
+#define WM8915_DSP1RX_EQ_B5_GAIN_WIDTH 5 /* DSP1RX_EQ_B5_GAIN - [10:6] */
+
+/*
+ * R1154 (0x482) - DSP1 RX EQ Band 1 A
+ */
+#define WM8915_DSP1RX_EQ_B1_A_MASK 0xFFFF /* DSP1RX_EQ_B1_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_A_SHIFT 0 /* DSP1RX_EQ_B1_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_A_WIDTH 16 /* DSP1RX_EQ_B1_A - [15:0] */
+
+/*
+ * R1155 (0x483) - DSP1 RX EQ Band 1 B
+ */
+#define WM8915_DSP1RX_EQ_B1_B_MASK 0xFFFF /* DSP1RX_EQ_B1_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_B_SHIFT 0 /* DSP1RX_EQ_B1_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_B_WIDTH 16 /* DSP1RX_EQ_B1_B - [15:0] */
+
+/*
+ * R1156 (0x484) - DSP1 RX EQ Band 1 PG
+ */
+#define WM8915_DSP1RX_EQ_B1_PG_MASK 0xFFFF /* DSP1RX_EQ_B1_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_PG_SHIFT 0 /* DSP1RX_EQ_B1_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B1_PG_WIDTH 16 /* DSP1RX_EQ_B1_PG - [15:0] */
+
+/*
+ * R1157 (0x485) - DSP1 RX EQ Band 2 A
+ */
+#define WM8915_DSP1RX_EQ_B2_A_MASK 0xFFFF /* DSP1RX_EQ_B2_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_A_SHIFT 0 /* DSP1RX_EQ_B2_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_A_WIDTH 16 /* DSP1RX_EQ_B2_A - [15:0] */
+
+/*
+ * R1158 (0x486) - DSP1 RX EQ Band 2 B
+ */
+#define WM8915_DSP1RX_EQ_B2_B_MASK 0xFFFF /* DSP1RX_EQ_B2_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_B_SHIFT 0 /* DSP1RX_EQ_B2_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_B_WIDTH 16 /* DSP1RX_EQ_B2_B - [15:0] */
+
+/*
+ * R1159 (0x487) - DSP1 RX EQ Band 2 C
+ */
+#define WM8915_DSP1RX_EQ_B2_C_MASK 0xFFFF /* DSP1RX_EQ_B2_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_C_SHIFT 0 /* DSP1RX_EQ_B2_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_C_WIDTH 16 /* DSP1RX_EQ_B2_C - [15:0] */
+
+/*
+ * R1160 (0x488) - DSP1 RX EQ Band 2 PG
+ */
+#define WM8915_DSP1RX_EQ_B2_PG_MASK 0xFFFF /* DSP1RX_EQ_B2_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_PG_SHIFT 0 /* DSP1RX_EQ_B2_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B2_PG_WIDTH 16 /* DSP1RX_EQ_B2_PG - [15:0] */
+
+/*
+ * R1161 (0x489) - DSP1 RX EQ Band 3 A
+ */
+#define WM8915_DSP1RX_EQ_B3_A_MASK 0xFFFF /* DSP1RX_EQ_B3_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_A_SHIFT 0 /* DSP1RX_EQ_B3_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_A_WIDTH 16 /* DSP1RX_EQ_B3_A - [15:0] */
+
+/*
+ * R1162 (0x48A) - DSP1 RX EQ Band 3 B
+ */
+#define WM8915_DSP1RX_EQ_B3_B_MASK 0xFFFF /* DSP1RX_EQ_B3_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_B_SHIFT 0 /* DSP1RX_EQ_B3_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_B_WIDTH 16 /* DSP1RX_EQ_B3_B - [15:0] */
+
+/*
+ * R1163 (0x48B) - DSP1 RX EQ Band 3 C
+ */
+#define WM8915_DSP1RX_EQ_B3_C_MASK 0xFFFF /* DSP1RX_EQ_B3_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_C_SHIFT 0 /* DSP1RX_EQ_B3_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_C_WIDTH 16 /* DSP1RX_EQ_B3_C - [15:0] */
+
+/*
+ * R1164 (0x48C) - DSP1 RX EQ Band 3 PG
+ */
+#define WM8915_DSP1RX_EQ_B3_PG_MASK 0xFFFF /* DSP1RX_EQ_B3_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_PG_SHIFT 0 /* DSP1RX_EQ_B3_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B3_PG_WIDTH 16 /* DSP1RX_EQ_B3_PG - [15:0] */
+
+/*
+ * R1165 (0x48D) - DSP1 RX EQ Band 4 A
+ */
+#define WM8915_DSP1RX_EQ_B4_A_MASK 0xFFFF /* DSP1RX_EQ_B4_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_A_SHIFT 0 /* DSP1RX_EQ_B4_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_A_WIDTH 16 /* DSP1RX_EQ_B4_A - [15:0] */
+
+/*
+ * R1166 (0x48E) - DSP1 RX EQ Band 4 B
+ */
+#define WM8915_DSP1RX_EQ_B4_B_MASK 0xFFFF /* DSP1RX_EQ_B4_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_B_SHIFT 0 /* DSP1RX_EQ_B4_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_B_WIDTH 16 /* DSP1RX_EQ_B4_B - [15:0] */
+
+/*
+ * R1167 (0x48F) - DSP1 RX EQ Band 4 C
+ */
+#define WM8915_DSP1RX_EQ_B4_C_MASK 0xFFFF /* DSP1RX_EQ_B4_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_C_SHIFT 0 /* DSP1RX_EQ_B4_C - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_C_WIDTH 16 /* DSP1RX_EQ_B4_C - [15:0] */
+
+/*
+ * R1168 (0x490) - DSP1 RX EQ Band 4 PG
+ */
+#define WM8915_DSP1RX_EQ_B4_PG_MASK 0xFFFF /* DSP1RX_EQ_B4_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_PG_SHIFT 0 /* DSP1RX_EQ_B4_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B4_PG_WIDTH 16 /* DSP1RX_EQ_B4_PG - [15:0] */
+
+/*
+ * R1169 (0x491) - DSP1 RX EQ Band 5 A
+ */
+#define WM8915_DSP1RX_EQ_B5_A_MASK 0xFFFF /* DSP1RX_EQ_B5_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_A_SHIFT 0 /* DSP1RX_EQ_B5_A - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_A_WIDTH 16 /* DSP1RX_EQ_B5_A - [15:0] */
+
+/*
+ * R1170 (0x492) - DSP1 RX EQ Band 5 B
+ */
+#define WM8915_DSP1RX_EQ_B5_B_MASK 0xFFFF /* DSP1RX_EQ_B5_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_B_SHIFT 0 /* DSP1RX_EQ_B5_B - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_B_WIDTH 16 /* DSP1RX_EQ_B5_B - [15:0] */
+
+/*
+ * R1171 (0x493) - DSP1 RX EQ Band 5 PG
+ */
+#define WM8915_DSP1RX_EQ_B5_PG_MASK 0xFFFF /* DSP1RX_EQ_B5_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_PG_SHIFT 0 /* DSP1RX_EQ_B5_PG - [15:0] */
+#define WM8915_DSP1RX_EQ_B5_PG_WIDTH 16 /* DSP1RX_EQ_B5_PG - [15:0] */
+
+/*
+ * R1280 (0x500) - DSP2 TX Left Volume
+ */
+#define WM8915_DSP2TX_VU 0x0100 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_MASK 0x0100 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_SHIFT 8 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_WIDTH 1 /* DSP2TX_VU */
+#define WM8915_DSP2TXL_VOL_MASK 0x00FF /* DSP2TXL_VOL - [7:0] */
+#define WM8915_DSP2TXL_VOL_SHIFT 0 /* DSP2TXL_VOL - [7:0] */
+#define WM8915_DSP2TXL_VOL_WIDTH 8 /* DSP2TXL_VOL - [7:0] */
+
+/*
+ * R1281 (0x501) - DSP2 TX Right Volume
+ */
+#define WM8915_DSP2TX_VU 0x0100 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_MASK 0x0100 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_SHIFT 8 /* DSP2TX_VU */
+#define WM8915_DSP2TX_VU_WIDTH 1 /* DSP2TX_VU */
+#define WM8915_DSP2TXR_VOL_MASK 0x00FF /* DSP2TXR_VOL - [7:0] */
+#define WM8915_DSP2TXR_VOL_SHIFT 0 /* DSP2TXR_VOL - [7:0] */
+#define WM8915_DSP2TXR_VOL_WIDTH 8 /* DSP2TXR_VOL - [7:0] */
+
+/*
+ * R1282 (0x502) - DSP2 RX Left Volume
+ */
+#define WM8915_DSP2RX_VU 0x0100 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_MASK 0x0100 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_SHIFT 8 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_WIDTH 1 /* DSP2RX_VU */
+#define WM8915_DSP2RXL_VOL_MASK 0x00FF /* DSP2RXL_VOL - [7:0] */
+#define WM8915_DSP2RXL_VOL_SHIFT 0 /* DSP2RXL_VOL - [7:0] */
+#define WM8915_DSP2RXL_VOL_WIDTH 8 /* DSP2RXL_VOL - [7:0] */
+
+/*
+ * R1283 (0x503) - DSP2 RX Right Volume
+ */
+#define WM8915_DSP2RX_VU 0x0100 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_MASK 0x0100 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_SHIFT 8 /* DSP2RX_VU */
+#define WM8915_DSP2RX_VU_WIDTH 1 /* DSP2RX_VU */
+#define WM8915_DSP2RXR_VOL_MASK 0x00FF /* DSP2RXR_VOL - [7:0] */
+#define WM8915_DSP2RXR_VOL_SHIFT 0 /* DSP2RXR_VOL - [7:0] */
+#define WM8915_DSP2RXR_VOL_WIDTH 8 /* DSP2RXR_VOL - [7:0] */
+
+/*
+ * R1296 (0x510) - DSP2 TX Filters
+ */
+#define WM8915_DSP2TX_NF 0x2000 /* DSP2TX_NF */
+#define WM8915_DSP2TX_NF_MASK 0x2000 /* DSP2TX_NF */
+#define WM8915_DSP2TX_NF_SHIFT 13 /* DSP2TX_NF */
+#define WM8915_DSP2TX_NF_WIDTH 1 /* DSP2TX_NF */
+#define WM8915_DSP2TXL_HPF 0x1000 /* DSP2TXL_HPF */
+#define WM8915_DSP2TXL_HPF_MASK 0x1000 /* DSP2TXL_HPF */
+#define WM8915_DSP2TXL_HPF_SHIFT 12 /* DSP2TXL_HPF */
+#define WM8915_DSP2TXL_HPF_WIDTH 1 /* DSP2TXL_HPF */
+#define WM8915_DSP2TXR_HPF 0x0800 /* DSP2TXR_HPF */
+#define WM8915_DSP2TXR_HPF_MASK 0x0800 /* DSP2TXR_HPF */
+#define WM8915_DSP2TXR_HPF_SHIFT 11 /* DSP2TXR_HPF */
+#define WM8915_DSP2TXR_HPF_WIDTH 1 /* DSP2TXR_HPF */
+#define WM8915_DSP2TX_HPF_MODE_MASK 0x0018 /* DSP2TX_HPF_MODE - [4:3] */
+#define WM8915_DSP2TX_HPF_MODE_SHIFT 3 /* DSP2TX_HPF_MODE - [4:3] */
+#define WM8915_DSP2TX_HPF_MODE_WIDTH 2 /* DSP2TX_HPF_MODE - [4:3] */
+#define WM8915_DSP2TX_HPF_CUT_MASK 0x0007 /* DSP2TX_HPF_CUT - [2:0] */
+#define WM8915_DSP2TX_HPF_CUT_SHIFT 0 /* DSP2TX_HPF_CUT - [2:0] */
+#define WM8915_DSP2TX_HPF_CUT_WIDTH 3 /* DSP2TX_HPF_CUT - [2:0] */
+
+/*
+ * R1312 (0x520) - DSP2 RX Filters (1)
+ */
+#define WM8915_DSP2RX_MUTE 0x0200 /* DSP2RX_MUTE */
+#define WM8915_DSP2RX_MUTE_MASK 0x0200 /* DSP2RX_MUTE */
+#define WM8915_DSP2RX_MUTE_SHIFT 9 /* DSP2RX_MUTE */
+#define WM8915_DSP2RX_MUTE_WIDTH 1 /* DSP2RX_MUTE */
+#define WM8915_DSP2RX_MONO 0x0080 /* DSP2RX_MONO */
+#define WM8915_DSP2RX_MONO_MASK 0x0080 /* DSP2RX_MONO */
+#define WM8915_DSP2RX_MONO_SHIFT 7 /* DSP2RX_MONO */
+#define WM8915_DSP2RX_MONO_WIDTH 1 /* DSP2RX_MONO */
+#define WM8915_DSP2RX_MUTERATE 0x0020 /* DSP2RX_MUTERATE */
+#define WM8915_DSP2RX_MUTERATE_MASK 0x0020 /* DSP2RX_MUTERATE */
+#define WM8915_DSP2RX_MUTERATE_SHIFT 5 /* DSP2RX_MUTERATE */
+#define WM8915_DSP2RX_MUTERATE_WIDTH 1 /* DSP2RX_MUTERATE */
+#define WM8915_DSP2RX_UNMUTE_RAMP 0x0010 /* DSP2RX_UNMUTE_RAMP */
+#define WM8915_DSP2RX_UNMUTE_RAMP_MASK 0x0010 /* DSP2RX_UNMUTE_RAMP */
+#define WM8915_DSP2RX_UNMUTE_RAMP_SHIFT 4 /* DSP2RX_UNMUTE_RAMP */
+#define WM8915_DSP2RX_UNMUTE_RAMP_WIDTH 1 /* DSP2RX_UNMUTE_RAMP */
+
+/*
+ * R1313 (0x521) - DSP2 RX Filters (2)
+ */
+#define WM8915_DSP2RX_3D_GAIN_MASK 0x3E00 /* DSP2RX_3D_GAIN - [13:9] */
+#define WM8915_DSP2RX_3D_GAIN_SHIFT 9 /* DSP2RX_3D_GAIN - [13:9] */
+#define WM8915_DSP2RX_3D_GAIN_WIDTH 5 /* DSP2RX_3D_GAIN - [13:9] */
+#define WM8915_DSP2RX_3D_ENA 0x0100 /* DSP2RX_3D_ENA */
+#define WM8915_DSP2RX_3D_ENA_MASK 0x0100 /* DSP2RX_3D_ENA */
+#define WM8915_DSP2RX_3D_ENA_SHIFT 8 /* DSP2RX_3D_ENA */
+#define WM8915_DSP2RX_3D_ENA_WIDTH 1 /* DSP2RX_3D_ENA */
+
+/*
+ * R1344 (0x540) - DSP2 DRC (1)
+ */
+#define WM8915_DSP2DRC_SIG_DET_RMS_MASK 0xF800 /* DSP2DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP2DRC_SIG_DET_RMS_SHIFT 11 /* DSP2DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP2DRC_SIG_DET_RMS_WIDTH 5 /* DSP2DRC_SIG_DET_RMS - [15:11] */
+#define WM8915_DSP2DRC_SIG_DET_PK_MASK 0x0600 /* DSP2DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP2DRC_SIG_DET_PK_SHIFT 9 /* DSP2DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP2DRC_SIG_DET_PK_WIDTH 2 /* DSP2DRC_SIG_DET_PK - [10:9] */
+#define WM8915_DSP2DRC_NG_ENA 0x0100 /* DSP2DRC_NG_ENA */
+#define WM8915_DSP2DRC_NG_ENA_MASK 0x0100 /* DSP2DRC_NG_ENA */
+#define WM8915_DSP2DRC_NG_ENA_SHIFT 8 /* DSP2DRC_NG_ENA */
+#define WM8915_DSP2DRC_NG_ENA_WIDTH 1 /* DSP2DRC_NG_ENA */
+#define WM8915_DSP2DRC_SIG_DET_MODE 0x0080 /* DSP2DRC_SIG_DET_MODE */
+#define WM8915_DSP2DRC_SIG_DET_MODE_MASK 0x0080 /* DSP2DRC_SIG_DET_MODE */
+#define WM8915_DSP2DRC_SIG_DET_MODE_SHIFT 7 /* DSP2DRC_SIG_DET_MODE */
+#define WM8915_DSP2DRC_SIG_DET_MODE_WIDTH 1 /* DSP2DRC_SIG_DET_MODE */
+#define WM8915_DSP2DRC_SIG_DET 0x0040 /* DSP2DRC_SIG_DET */
+#define WM8915_DSP2DRC_SIG_DET_MASK 0x0040 /* DSP2DRC_SIG_DET */
+#define WM8915_DSP2DRC_SIG_DET_SHIFT 6 /* DSP2DRC_SIG_DET */
+#define WM8915_DSP2DRC_SIG_DET_WIDTH 1 /* DSP2DRC_SIG_DET */
+#define WM8915_DSP2DRC_KNEE2_OP_ENA 0x0020 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8915_DSP2DRC_KNEE2_OP_ENA_MASK 0x0020 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8915_DSP2DRC_KNEE2_OP_ENA_SHIFT 5 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8915_DSP2DRC_KNEE2_OP_ENA_WIDTH 1 /* DSP2DRC_KNEE2_OP_ENA */
+#define WM8915_DSP2DRC_QR 0x0010 /* DSP2DRC_QR */
+#define WM8915_DSP2DRC_QR_MASK 0x0010 /* DSP2DRC_QR */
+#define WM8915_DSP2DRC_QR_SHIFT 4 /* DSP2DRC_QR */
+#define WM8915_DSP2DRC_QR_WIDTH 1 /* DSP2DRC_QR */
+#define WM8915_DSP2DRC_ANTICLIP 0x0008 /* DSP2DRC_ANTICLIP */
+#define WM8915_DSP2DRC_ANTICLIP_MASK 0x0008 /* DSP2DRC_ANTICLIP */
+#define WM8915_DSP2DRC_ANTICLIP_SHIFT 3 /* DSP2DRC_ANTICLIP */
+#define WM8915_DSP2DRC_ANTICLIP_WIDTH 1 /* DSP2DRC_ANTICLIP */
+#define WM8915_DSP2RX_DRC_ENA 0x0004 /* DSP2RX_DRC_ENA */
+#define WM8915_DSP2RX_DRC_ENA_MASK 0x0004 /* DSP2RX_DRC_ENA */
+#define WM8915_DSP2RX_DRC_ENA_SHIFT 2 /* DSP2RX_DRC_ENA */
+#define WM8915_DSP2RX_DRC_ENA_WIDTH 1 /* DSP2RX_DRC_ENA */
+#define WM8915_DSP2TXL_DRC_ENA 0x0002 /* DSP2TXL_DRC_ENA */
+#define WM8915_DSP2TXL_DRC_ENA_MASK 0x0002 /* DSP2TXL_DRC_ENA */
+#define WM8915_DSP2TXL_DRC_ENA_SHIFT 1 /* DSP2TXL_DRC_ENA */
+#define WM8915_DSP2TXL_DRC_ENA_WIDTH 1 /* DSP2TXL_DRC_ENA */
+#define WM8915_DSP2TXR_DRC_ENA 0x0001 /* DSP2TXR_DRC_ENA */
+#define WM8915_DSP2TXR_DRC_ENA_MASK 0x0001 /* DSP2TXR_DRC_ENA */
+#define WM8915_DSP2TXR_DRC_ENA_SHIFT 0 /* DSP2TXR_DRC_ENA */
+#define WM8915_DSP2TXR_DRC_ENA_WIDTH 1 /* DSP2TXR_DRC_ENA */
+
+/*
+ * R1345 (0x541) - DSP2 DRC (2)
+ */
+#define WM8915_DSP2DRC_ATK_MASK 0x1E00 /* DSP2DRC_ATK - [12:9] */
+#define WM8915_DSP2DRC_ATK_SHIFT 9 /* DSP2DRC_ATK - [12:9] */
+#define WM8915_DSP2DRC_ATK_WIDTH 4 /* DSP2DRC_ATK - [12:9] */
+#define WM8915_DSP2DRC_DCY_MASK 0x01E0 /* DSP2DRC_DCY - [8:5] */
+#define WM8915_DSP2DRC_DCY_SHIFT 5 /* DSP2DRC_DCY - [8:5] */
+#define WM8915_DSP2DRC_DCY_WIDTH 4 /* DSP2DRC_DCY - [8:5] */
+#define WM8915_DSP2DRC_MINGAIN_MASK 0x001C /* DSP2DRC_MINGAIN - [4:2] */
+#define WM8915_DSP2DRC_MINGAIN_SHIFT 2 /* DSP2DRC_MINGAIN - [4:2] */
+#define WM8915_DSP2DRC_MINGAIN_WIDTH 3 /* DSP2DRC_MINGAIN - [4:2] */
+#define WM8915_DSP2DRC_MAXGAIN_MASK 0x0003 /* DSP2DRC_MAXGAIN - [1:0] */
+#define WM8915_DSP2DRC_MAXGAIN_SHIFT 0 /* DSP2DRC_MAXGAIN - [1:0] */
+#define WM8915_DSP2DRC_MAXGAIN_WIDTH 2 /* DSP2DRC_MAXGAIN - [1:0] */
+
+/*
+ * R1346 (0x542) - DSP2 DRC (3)
+ */
+#define WM8915_DSP2DRC_NG_MINGAIN_MASK 0xF000 /* DSP2DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP2DRC_NG_MINGAIN_SHIFT 12 /* DSP2DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP2DRC_NG_MINGAIN_WIDTH 4 /* DSP2DRC_NG_MINGAIN - [15:12] */
+#define WM8915_DSP2DRC_NG_EXP_MASK 0x0C00 /* DSP2DRC_NG_EXP - [11:10] */
+#define WM8915_DSP2DRC_NG_EXP_SHIFT 10 /* DSP2DRC_NG_EXP - [11:10] */
+#define WM8915_DSP2DRC_NG_EXP_WIDTH 2 /* DSP2DRC_NG_EXP - [11:10] */
+#define WM8915_DSP2DRC_QR_THR_MASK 0x0300 /* DSP2DRC_QR_THR - [9:8] */
+#define WM8915_DSP2DRC_QR_THR_SHIFT 8 /* DSP2DRC_QR_THR - [9:8] */
+#define WM8915_DSP2DRC_QR_THR_WIDTH 2 /* DSP2DRC_QR_THR - [9:8] */
+#define WM8915_DSP2DRC_QR_DCY_MASK 0x00C0 /* DSP2DRC_QR_DCY - [7:6] */
+#define WM8915_DSP2DRC_QR_DCY_SHIFT 6 /* DSP2DRC_QR_DCY - [7:6] */
+#define WM8915_DSP2DRC_QR_DCY_WIDTH 2 /* DSP2DRC_QR_DCY - [7:6] */
+#define WM8915_DSP2DRC_HI_COMP_MASK 0x0038 /* DSP2DRC_HI_COMP - [5:3] */
+#define WM8915_DSP2DRC_HI_COMP_SHIFT 3 /* DSP2DRC_HI_COMP - [5:3] */
+#define WM8915_DSP2DRC_HI_COMP_WIDTH 3 /* DSP2DRC_HI_COMP - [5:3] */
+#define WM8915_DSP2DRC_LO_COMP_MASK 0x0007 /* DSP2DRC_LO_COMP - [2:0] */
+#define WM8915_DSP2DRC_LO_COMP_SHIFT 0 /* DSP2DRC_LO_COMP - [2:0] */
+#define WM8915_DSP2DRC_LO_COMP_WIDTH 3 /* DSP2DRC_LO_COMP - [2:0] */
+
+/*
+ * R1347 (0x543) - DSP2 DRC (4)
+ */
+#define WM8915_DSP2DRC_KNEE_IP_MASK 0x07E0 /* DSP2DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP2DRC_KNEE_IP_SHIFT 5 /* DSP2DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP2DRC_KNEE_IP_WIDTH 6 /* DSP2DRC_KNEE_IP - [10:5] */
+#define WM8915_DSP2DRC_KNEE_OP_MASK 0x001F /* DSP2DRC_KNEE_OP - [4:0] */
+#define WM8915_DSP2DRC_KNEE_OP_SHIFT 0 /* DSP2DRC_KNEE_OP - [4:0] */
+#define WM8915_DSP2DRC_KNEE_OP_WIDTH 5 /* DSP2DRC_KNEE_OP - [4:0] */
+
+/*
+ * R1348 (0x544) - DSP2 DRC (5)
+ */
+#define WM8915_DSP2DRC_KNEE2_IP_MASK 0x03E0 /* DSP2DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP2DRC_KNEE2_IP_SHIFT 5 /* DSP2DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP2DRC_KNEE2_IP_WIDTH 5 /* DSP2DRC_KNEE2_IP - [9:5] */
+#define WM8915_DSP2DRC_KNEE2_OP_MASK 0x001F /* DSP2DRC_KNEE2_OP - [4:0] */
+#define WM8915_DSP2DRC_KNEE2_OP_SHIFT 0 /* DSP2DRC_KNEE2_OP - [4:0] */
+#define WM8915_DSP2DRC_KNEE2_OP_WIDTH 5 /* DSP2DRC_KNEE2_OP - [4:0] */
+
+/*
+ * R1408 (0x580) - DSP2 RX EQ Gains (1)
+ */
+#define WM8915_DSP2RX_EQ_B1_GAIN_MASK 0xF800 /* DSP2RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B1_GAIN_SHIFT 11 /* DSP2RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B1_GAIN_WIDTH 5 /* DSP2RX_EQ_B1_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B2_GAIN_MASK 0x07C0 /* DSP2RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP2RX_EQ_B2_GAIN_SHIFT 6 /* DSP2RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP2RX_EQ_B2_GAIN_WIDTH 5 /* DSP2RX_EQ_B2_GAIN - [10:6] */
+#define WM8915_DSP2RX_EQ_B3_GAIN_MASK 0x003E /* DSP2RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP2RX_EQ_B3_GAIN_SHIFT 1 /* DSP2RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP2RX_EQ_B3_GAIN_WIDTH 5 /* DSP2RX_EQ_B3_GAIN - [5:1] */
+#define WM8915_DSP2RX_EQ_ENA 0x0001 /* DSP2RX_EQ_ENA */
+#define WM8915_DSP2RX_EQ_ENA_MASK 0x0001 /* DSP2RX_EQ_ENA */
+#define WM8915_DSP2RX_EQ_ENA_SHIFT 0 /* DSP2RX_EQ_ENA */
+#define WM8915_DSP2RX_EQ_ENA_WIDTH 1 /* DSP2RX_EQ_ENA */
+
+/*
+ * R1409 (0x581) - DSP2 RX EQ Gains (2)
+ */
+#define WM8915_DSP2RX_EQ_B4_GAIN_MASK 0xF800 /* DSP2RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B4_GAIN_SHIFT 11 /* DSP2RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B4_GAIN_WIDTH 5 /* DSP2RX_EQ_B4_GAIN - [15:11] */
+#define WM8915_DSP2RX_EQ_B5_GAIN_MASK 0x07C0 /* DSP2RX_EQ_B5_GAIN - [10:6] */
+#define WM8915_DSP2RX_EQ_B5_GAIN_SHIFT 6 /* DSP2RX_EQ_B5_GAIN - [10:6] */
+#define WM8915_DSP2RX_EQ_B5_GAIN_WIDTH 5 /* DSP2RX_EQ_B5_GAIN - [10:6] */
+
+/*
+ * R1410 (0x582) - DSP2 RX EQ Band 1 A
+ */
+#define WM8915_DSP2RX_EQ_B1_A_MASK 0xFFFF /* DSP2RX_EQ_B1_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_A_SHIFT 0 /* DSP2RX_EQ_B1_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_A_WIDTH 16 /* DSP2RX_EQ_B1_A - [15:0] */
+
+/*
+ * R1411 (0x583) - DSP2 RX EQ Band 1 B
+ */
+#define WM8915_DSP2RX_EQ_B1_B_MASK 0xFFFF /* DSP2RX_EQ_B1_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_B_SHIFT 0 /* DSP2RX_EQ_B1_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_B_WIDTH 16 /* DSP2RX_EQ_B1_B - [15:0] */
+
+/*
+ * R1412 (0x584) - DSP2 RX EQ Band 1 PG
+ */
+#define WM8915_DSP2RX_EQ_B1_PG_MASK 0xFFFF /* DSP2RX_EQ_B1_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_PG_SHIFT 0 /* DSP2RX_EQ_B1_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B1_PG_WIDTH 16 /* DSP2RX_EQ_B1_PG - [15:0] */
+
+/*
+ * R1413 (0x585) - DSP2 RX EQ Band 2 A
+ */
+#define WM8915_DSP2RX_EQ_B2_A_MASK 0xFFFF /* DSP2RX_EQ_B2_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_A_SHIFT 0 /* DSP2RX_EQ_B2_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_A_WIDTH 16 /* DSP2RX_EQ_B2_A - [15:0] */
+
+/*
+ * R1414 (0x586) - DSP2 RX EQ Band 2 B
+ */
+#define WM8915_DSP2RX_EQ_B2_B_MASK 0xFFFF /* DSP2RX_EQ_B2_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_B_SHIFT 0 /* DSP2RX_EQ_B2_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_B_WIDTH 16 /* DSP2RX_EQ_B2_B - [15:0] */
+
+/*
+ * R1415 (0x587) - DSP2 RX EQ Band 2 C
+ */
+#define WM8915_DSP2RX_EQ_B2_C_MASK 0xFFFF /* DSP2RX_EQ_B2_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_C_SHIFT 0 /* DSP2RX_EQ_B2_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_C_WIDTH 16 /* DSP2RX_EQ_B2_C - [15:0] */
+
+/*
+ * R1416 (0x588) - DSP2 RX EQ Band 2 PG
+ */
+#define WM8915_DSP2RX_EQ_B2_PG_MASK 0xFFFF /* DSP2RX_EQ_B2_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_PG_SHIFT 0 /* DSP2RX_EQ_B2_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B2_PG_WIDTH 16 /* DSP2RX_EQ_B2_PG - [15:0] */
+
+/*
+ * R1417 (0x589) - DSP2 RX EQ Band 3 A
+ */
+#define WM8915_DSP2RX_EQ_B3_A_MASK 0xFFFF /* DSP2RX_EQ_B3_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_A_SHIFT 0 /* DSP2RX_EQ_B3_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_A_WIDTH 16 /* DSP2RX_EQ_B3_A - [15:0] */
+
+/*
+ * R1418 (0x58A) - DSP2 RX EQ Band 3 B
+ */
+#define WM8915_DSP2RX_EQ_B3_B_MASK 0xFFFF /* DSP2RX_EQ_B3_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_B_SHIFT 0 /* DSP2RX_EQ_B3_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_B_WIDTH 16 /* DSP2RX_EQ_B3_B - [15:0] */
+
+/*
+ * R1419 (0x58B) - DSP2 RX EQ Band 3 C
+ */
+#define WM8915_DSP2RX_EQ_B3_C_MASK 0xFFFF /* DSP2RX_EQ_B3_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_C_SHIFT 0 /* DSP2RX_EQ_B3_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_C_WIDTH 16 /* DSP2RX_EQ_B3_C - [15:0] */
+
+/*
+ * R1420 (0x58C) - DSP2 RX EQ Band 3 PG
+ */
+#define WM8915_DSP2RX_EQ_B3_PG_MASK 0xFFFF /* DSP2RX_EQ_B3_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_PG_SHIFT 0 /* DSP2RX_EQ_B3_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B3_PG_WIDTH 16 /* DSP2RX_EQ_B3_PG - [15:0] */
+
+/*
+ * R1421 (0x58D) - DSP2 RX EQ Band 4 A
+ */
+#define WM8915_DSP2RX_EQ_B4_A_MASK 0xFFFF /* DSP2RX_EQ_B4_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_A_SHIFT 0 /* DSP2RX_EQ_B4_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_A_WIDTH 16 /* DSP2RX_EQ_B4_A - [15:0] */
+
+/*
+ * R1422 (0x58E) - DSP2 RX EQ Band 4 B
+ */
+#define WM8915_DSP2RX_EQ_B4_B_MASK 0xFFFF /* DSP2RX_EQ_B4_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_B_SHIFT 0 /* DSP2RX_EQ_B4_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_B_WIDTH 16 /* DSP2RX_EQ_B4_B - [15:0] */
+
+/*
+ * R1423 (0x58F) - DSP2 RX EQ Band 4 C
+ */
+#define WM8915_DSP2RX_EQ_B4_C_MASK 0xFFFF /* DSP2RX_EQ_B4_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_C_SHIFT 0 /* DSP2RX_EQ_B4_C - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_C_WIDTH 16 /* DSP2RX_EQ_B4_C - [15:0] */
+
+/*
+ * R1424 (0x590) - DSP2 RX EQ Band 4 PG
+ */
+#define WM8915_DSP2RX_EQ_B4_PG_MASK 0xFFFF /* DSP2RX_EQ_B4_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_PG_SHIFT 0 /* DSP2RX_EQ_B4_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B4_PG_WIDTH 16 /* DSP2RX_EQ_B4_PG - [15:0] */
+
+/*
+ * R1425 (0x591) - DSP2 RX EQ Band 5 A
+ */
+#define WM8915_DSP2RX_EQ_B5_A_MASK 0xFFFF /* DSP2RX_EQ_B5_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_A_SHIFT 0 /* DSP2RX_EQ_B5_A - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_A_WIDTH 16 /* DSP2RX_EQ_B5_A - [15:0] */
+
+/*
+ * R1426 (0x592) - DSP2 RX EQ Band 5 B
+ */
+#define WM8915_DSP2RX_EQ_B5_B_MASK 0xFFFF /* DSP2RX_EQ_B5_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_B_SHIFT 0 /* DSP2RX_EQ_B5_B - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_B_WIDTH 16 /* DSP2RX_EQ_B5_B - [15:0] */
+
+/*
+ * R1427 (0x593) - DSP2 RX EQ Band 5 PG
+ */
+#define WM8915_DSP2RX_EQ_B5_PG_MASK 0xFFFF /* DSP2RX_EQ_B5_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_PG_SHIFT 0 /* DSP2RX_EQ_B5_PG - [15:0] */
+#define WM8915_DSP2RX_EQ_B5_PG_WIDTH 16 /* DSP2RX_EQ_B5_PG - [15:0] */
+
+/*
+ * R1536 (0x600) - DAC1 Mixer Volumes
+ */
+#define WM8915_ADCR_DAC1_VOL_MASK 0x03E0 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8915_ADCR_DAC1_VOL_SHIFT 5 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8915_ADCR_DAC1_VOL_WIDTH 5 /* ADCR_DAC1_VOL - [9:5] */
+#define WM8915_ADCL_DAC1_VOL_MASK 0x001F /* ADCL_DAC1_VOL - [4:0] */
+#define WM8915_ADCL_DAC1_VOL_SHIFT 0 /* ADCL_DAC1_VOL - [4:0] */
+#define WM8915_ADCL_DAC1_VOL_WIDTH 5 /* ADCL_DAC1_VOL - [4:0] */
+
+/*
+ * R1537 (0x601) - DAC1 Left Mixer Routing
+ */
+#define WM8915_ADCR_TO_DAC1L 0x0020 /* ADCR_TO_DAC1L */
+#define WM8915_ADCR_TO_DAC1L_MASK 0x0020 /* ADCR_TO_DAC1L */
+#define WM8915_ADCR_TO_DAC1L_SHIFT 5 /* ADCR_TO_DAC1L */
+#define WM8915_ADCR_TO_DAC1L_WIDTH 1 /* ADCR_TO_DAC1L */
+#define WM8915_ADCL_TO_DAC1L 0x0010 /* ADCL_TO_DAC1L */
+#define WM8915_ADCL_TO_DAC1L_MASK 0x0010 /* ADCL_TO_DAC1L */
+#define WM8915_ADCL_TO_DAC1L_SHIFT 4 /* ADCL_TO_DAC1L */
+#define WM8915_ADCL_TO_DAC1L_WIDTH 1 /* ADCL_TO_DAC1L */
+#define WM8915_DSP2RXL_TO_DAC1L 0x0002 /* DSP2RXL_TO_DAC1L */
+#define WM8915_DSP2RXL_TO_DAC1L_MASK 0x0002 /* DSP2RXL_TO_DAC1L */
+#define WM8915_DSP2RXL_TO_DAC1L_SHIFT 1 /* DSP2RXL_TO_DAC1L */
+#define WM8915_DSP2RXL_TO_DAC1L_WIDTH 1 /* DSP2RXL_TO_DAC1L */
+#define WM8915_DSP1RXL_TO_DAC1L 0x0001 /* DSP1RXL_TO_DAC1L */
+#define WM8915_DSP1RXL_TO_DAC1L_MASK 0x0001 /* DSP1RXL_TO_DAC1L */
+#define WM8915_DSP1RXL_TO_DAC1L_SHIFT 0 /* DSP1RXL_TO_DAC1L */
+#define WM8915_DSP1RXL_TO_DAC1L_WIDTH 1 /* DSP1RXL_TO_DAC1L */
+
+/*
+ * R1538 (0x602) - DAC1 Right Mixer Routing
+ */
+#define WM8915_ADCR_TO_DAC1R 0x0020 /* ADCR_TO_DAC1R */
+#define WM8915_ADCR_TO_DAC1R_MASK 0x0020 /* ADCR_TO_DAC1R */
+#define WM8915_ADCR_TO_DAC1R_SHIFT 5 /* ADCR_TO_DAC1R */
+#define WM8915_ADCR_TO_DAC1R_WIDTH 1 /* ADCR_TO_DAC1R */
+#define WM8915_ADCL_TO_DAC1R 0x0010 /* ADCL_TO_DAC1R */
+#define WM8915_ADCL_TO_DAC1R_MASK 0x0010 /* ADCL_TO_DAC1R */
+#define WM8915_ADCL_TO_DAC1R_SHIFT 4 /* ADCL_TO_DAC1R */
+#define WM8915_ADCL_TO_DAC1R_WIDTH 1 /* ADCL_TO_DAC1R */
+#define WM8915_DSP2RXR_TO_DAC1R 0x0002 /* DSP2RXR_TO_DAC1R */
+#define WM8915_DSP2RXR_TO_DAC1R_MASK 0x0002 /* DSP2RXR_TO_DAC1R */
+#define WM8915_DSP2RXR_TO_DAC1R_SHIFT 1 /* DSP2RXR_TO_DAC1R */
+#define WM8915_DSP2RXR_TO_DAC1R_WIDTH 1 /* DSP2RXR_TO_DAC1R */
+#define WM8915_DSP1RXR_TO_DAC1R 0x0001 /* DSP1RXR_TO_DAC1R */
+#define WM8915_DSP1RXR_TO_DAC1R_MASK 0x0001 /* DSP1RXR_TO_DAC1R */
+#define WM8915_DSP1RXR_TO_DAC1R_SHIFT 0 /* DSP1RXR_TO_DAC1R */
+#define WM8915_DSP1RXR_TO_DAC1R_WIDTH 1 /* DSP1RXR_TO_DAC1R */
+
+/*
+ * R1539 (0x603) - DAC2 Mixer Volumes
+ */
+#define WM8915_ADCR_DAC2_VOL_MASK 0x03E0 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8915_ADCR_DAC2_VOL_SHIFT 5 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8915_ADCR_DAC2_VOL_WIDTH 5 /* ADCR_DAC2_VOL - [9:5] */
+#define WM8915_ADCL_DAC2_VOL_MASK 0x001F /* ADCL_DAC2_VOL - [4:0] */
+#define WM8915_ADCL_DAC2_VOL_SHIFT 0 /* ADCL_DAC2_VOL - [4:0] */
+#define WM8915_ADCL_DAC2_VOL_WIDTH 5 /* ADCL_DAC2_VOL - [4:0] */
+
+/*
+ * R1540 (0x604) - DAC2 Left Mixer Routing
+ */
+#define WM8915_ADCR_TO_DAC2L 0x0020 /* ADCR_TO_DAC2L */
+#define WM8915_ADCR_TO_DAC2L_MASK 0x0020 /* ADCR_TO_DAC2L */
+#define WM8915_ADCR_TO_DAC2L_SHIFT 5 /* ADCR_TO_DAC2L */
+#define WM8915_ADCR_TO_DAC2L_WIDTH 1 /* ADCR_TO_DAC2L */
+#define WM8915_ADCL_TO_DAC2L 0x0010 /* ADCL_TO_DAC2L */
+#define WM8915_ADCL_TO_DAC2L_MASK 0x0010 /* ADCL_TO_DAC2L */
+#define WM8915_ADCL_TO_DAC2L_SHIFT 4 /* ADCL_TO_DAC2L */
+#define WM8915_ADCL_TO_DAC2L_WIDTH 1 /* ADCL_TO_DAC2L */
+#define WM8915_DSP2RXL_TO_DAC2L 0x0002 /* DSP2RXL_TO_DAC2L */
+#define WM8915_DSP2RXL_TO_DAC2L_MASK 0x0002 /* DSP2RXL_TO_DAC2L */
+#define WM8915_DSP2RXL_TO_DAC2L_SHIFT 1 /* DSP2RXL_TO_DAC2L */
+#define WM8915_DSP2RXL_TO_DAC2L_WIDTH 1 /* DSP2RXL_TO_DAC2L */
+#define WM8915_DSP1RXL_TO_DAC2L 0x0001 /* DSP1RXL_TO_DAC2L */
+#define WM8915_DSP1RXL_TO_DAC2L_MASK 0x0001 /* DSP1RXL_TO_DAC2L */
+#define WM8915_DSP1RXL_TO_DAC2L_SHIFT 0 /* DSP1RXL_TO_DAC2L */
+#define WM8915_DSP1RXL_TO_DAC2L_WIDTH 1 /* DSP1RXL_TO_DAC2L */
+
+/*
+ * R1541 (0x605) - DAC2 Right Mixer Routing
+ */
+#define WM8915_ADCR_TO_DAC2R 0x0020 /* ADCR_TO_DAC2R */
+#define WM8915_ADCR_TO_DAC2R_MASK 0x0020 /* ADCR_TO_DAC2R */
+#define WM8915_ADCR_TO_DAC2R_SHIFT 5 /* ADCR_TO_DAC2R */
+#define WM8915_ADCR_TO_DAC2R_WIDTH 1 /* ADCR_TO_DAC2R */
+#define WM8915_ADCL_TO_DAC2R 0x0010 /* ADCL_TO_DAC2R */
+#define WM8915_ADCL_TO_DAC2R_MASK 0x0010 /* ADCL_TO_DAC2R */
+#define WM8915_ADCL_TO_DAC2R_SHIFT 4 /* ADCL_TO_DAC2R */
+#define WM8915_ADCL_TO_DAC2R_WIDTH 1 /* ADCL_TO_DAC2R */
+#define WM8915_DSP2RXR_TO_DAC2R 0x0002 /* DSP2RXR_TO_DAC2R */
+#define WM8915_DSP2RXR_TO_DAC2R_MASK 0x0002 /* DSP2RXR_TO_DAC2R */
+#define WM8915_DSP2RXR_TO_DAC2R_SHIFT 1 /* DSP2RXR_TO_DAC2R */
+#define WM8915_DSP2RXR_TO_DAC2R_WIDTH 1 /* DSP2RXR_TO_DAC2R */
+#define WM8915_DSP1RXR_TO_DAC2R 0x0001 /* DSP1RXR_TO_DAC2R */
+#define WM8915_DSP1RXR_TO_DAC2R_MASK 0x0001 /* DSP1RXR_TO_DAC2R */
+#define WM8915_DSP1RXR_TO_DAC2R_SHIFT 0 /* DSP1RXR_TO_DAC2R */
+#define WM8915_DSP1RXR_TO_DAC2R_WIDTH 1 /* DSP1RXR_TO_DAC2R */
+
+/*
+ * R1542 (0x606) - DSP1 TX Left Mixer Routing
+ */
+#define WM8915_ADC1L_TO_DSP1TXL 0x0002 /* ADC1L_TO_DSP1TXL */
+#define WM8915_ADC1L_TO_DSP1TXL_MASK 0x0002 /* ADC1L_TO_DSP1TXL */
+#define WM8915_ADC1L_TO_DSP1TXL_SHIFT 1 /* ADC1L_TO_DSP1TXL */
+#define WM8915_ADC1L_TO_DSP1TXL_WIDTH 1 /* ADC1L_TO_DSP1TXL */
+#define WM8915_DACL_TO_DSP1TXL 0x0001 /* DACL_TO_DSP1TXL */
+#define WM8915_DACL_TO_DSP1TXL_MASK 0x0001 /* DACL_TO_DSP1TXL */
+#define WM8915_DACL_TO_DSP1TXL_SHIFT 0 /* DACL_TO_DSP1TXL */
+#define WM8915_DACL_TO_DSP1TXL_WIDTH 1 /* DACL_TO_DSP1TXL */
+
+/*
+ * R1543 (0x607) - DSP1 TX Right Mixer Routing
+ */
+#define WM8915_ADC1R_TO_DSP1TXR 0x0002 /* ADC1R_TO_DSP1TXR */
+#define WM8915_ADC1R_TO_DSP1TXR_MASK 0x0002 /* ADC1R_TO_DSP1TXR */
+#define WM8915_ADC1R_TO_DSP1TXR_SHIFT 1 /* ADC1R_TO_DSP1TXR */
+#define WM8915_ADC1R_TO_DSP1TXR_WIDTH 1 /* ADC1R_TO_DSP1TXR */
+#define WM8915_DACR_TO_DSP1TXR 0x0001 /* DACR_TO_DSP1TXR */
+#define WM8915_DACR_TO_DSP1TXR_MASK 0x0001 /* DACR_TO_DSP1TXR */
+#define WM8915_DACR_TO_DSP1TXR_SHIFT 0 /* DACR_TO_DSP1TXR */
+#define WM8915_DACR_TO_DSP1TXR_WIDTH 1 /* DACR_TO_DSP1TXR */
+
+/*
+ * R1544 (0x608) - DSP2 TX Left Mixer Routing
+ */
+#define WM8915_ADC2L_TO_DSP2TXL 0x0002 /* ADC2L_TO_DSP2TXL */
+#define WM8915_ADC2L_TO_DSP2TXL_MASK 0x0002 /* ADC2L_TO_DSP2TXL */
+#define WM8915_ADC2L_TO_DSP2TXL_SHIFT 1 /* ADC2L_TO_DSP2TXL */
+#define WM8915_ADC2L_TO_DSP2TXL_WIDTH 1 /* ADC2L_TO_DSP2TXL */
+#define WM8915_DACL_TO_DSP2TXL 0x0001 /* DACL_TO_DSP2TXL */
+#define WM8915_DACL_TO_DSP2TXL_MASK 0x0001 /* DACL_TO_DSP2TXL */
+#define WM8915_DACL_TO_DSP2TXL_SHIFT 0 /* DACL_TO_DSP2TXL */
+#define WM8915_DACL_TO_DSP2TXL_WIDTH 1 /* DACL_TO_DSP2TXL */
+
+/*
+ * R1545 (0x609) - DSP2 TX Right Mixer Routing
+ */
+#define WM8915_ADC2R_TO_DSP2TXR 0x0002 /* ADC2R_TO_DSP2TXR */
+#define WM8915_ADC2R_TO_DSP2TXR_MASK 0x0002 /* ADC2R_TO_DSP2TXR */
+#define WM8915_ADC2R_TO_DSP2TXR_SHIFT 1 /* ADC2R_TO_DSP2TXR */
+#define WM8915_ADC2R_TO_DSP2TXR_WIDTH 1 /* ADC2R_TO_DSP2TXR */
+#define WM8915_DACR_TO_DSP2TXR 0x0001 /* DACR_TO_DSP2TXR */
+#define WM8915_DACR_TO_DSP2TXR_MASK 0x0001 /* DACR_TO_DSP2TXR */
+#define WM8915_DACR_TO_DSP2TXR_SHIFT 0 /* DACR_TO_DSP2TXR */
+#define WM8915_DACR_TO_DSP2TXR_WIDTH 1 /* DACR_TO_DSP2TXR */
+
+/*
+ * R1546 (0x60A) - DSP TX Mixer Select
+ */
+#define WM8915_DAC_TO_DSPTX_SRC 0x0001 /* DAC_TO_DSPTX_SRC */
+#define WM8915_DAC_TO_DSPTX_SRC_MASK 0x0001 /* DAC_TO_DSPTX_SRC */
+#define WM8915_DAC_TO_DSPTX_SRC_SHIFT 0 /* DAC_TO_DSPTX_SRC */
+#define WM8915_DAC_TO_DSPTX_SRC_WIDTH 1 /* DAC_TO_DSPTX_SRC */
+
+/*
+ * R1552 (0x610) - DAC Softmute
+ */
+#define WM8915_DAC_SOFTMUTEMODE 0x0002 /* DAC_SOFTMUTEMODE */
+#define WM8915_DAC_SOFTMUTEMODE_MASK 0x0002 /* DAC_SOFTMUTEMODE */
+#define WM8915_DAC_SOFTMUTEMODE_SHIFT 1 /* DAC_SOFTMUTEMODE */
+#define WM8915_DAC_SOFTMUTEMODE_WIDTH 1 /* DAC_SOFTMUTEMODE */
+#define WM8915_DAC_MUTERATE 0x0001 /* DAC_MUTERATE */
+#define WM8915_DAC_MUTERATE_MASK 0x0001 /* DAC_MUTERATE */
+#define WM8915_DAC_MUTERATE_SHIFT 0 /* DAC_MUTERATE */
+#define WM8915_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */
+
+/*
+ * R1568 (0x620) - Oversampling
+ */
+#define WM8915_SPK_OSR128 0x0008 /* SPK_OSR128 */
+#define WM8915_SPK_OSR128_MASK 0x0008 /* SPK_OSR128 */
+#define WM8915_SPK_OSR128_SHIFT 3 /* SPK_OSR128 */
+#define WM8915_SPK_OSR128_WIDTH 1 /* SPK_OSR128 */
+#define WM8915_DMIC_OSR64 0x0004 /* DMIC_OSR64 */
+#define WM8915_DMIC_OSR64_MASK 0x0004 /* DMIC_OSR64 */
+#define WM8915_DMIC_OSR64_SHIFT 2 /* DMIC_OSR64 */
+#define WM8915_DMIC_OSR64_WIDTH 1 /* DMIC_OSR64 */
+#define WM8915_ADC_OSR128 0x0002 /* ADC_OSR128 */
+#define WM8915_ADC_OSR128_MASK 0x0002 /* ADC_OSR128 */
+#define WM8915_ADC_OSR128_SHIFT 1 /* ADC_OSR128 */
+#define WM8915_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */
+#define WM8915_DAC_OSR128 0x0001 /* DAC_OSR128 */
+#define WM8915_DAC_OSR128_MASK 0x0001 /* DAC_OSR128 */
+#define WM8915_DAC_OSR128_SHIFT 0 /* DAC_OSR128 */
+#define WM8915_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */
+
+/*
+ * R1569 (0x621) - Sidetone
+ */
+#define WM8915_ST_LPF 0x1000 /* ST_LPF */
+#define WM8915_ST_LPF_MASK 0x1000 /* ST_LPF */
+#define WM8915_ST_LPF_SHIFT 12 /* ST_LPF */
+#define WM8915_ST_LPF_WIDTH 1 /* ST_LPF */
+#define WM8915_ST_HPF_CUT_MASK 0x0380 /* ST_HPF_CUT - [9:7] */
+#define WM8915_ST_HPF_CUT_SHIFT 7 /* ST_HPF_CUT - [9:7] */
+#define WM8915_ST_HPF_CUT_WIDTH 3 /* ST_HPF_CUT - [9:7] */
+#define WM8915_ST_HPF 0x0040 /* ST_HPF */
+#define WM8915_ST_HPF_MASK 0x0040 /* ST_HPF */
+#define WM8915_ST_HPF_SHIFT 6 /* ST_HPF */
+#define WM8915_ST_HPF_WIDTH 1 /* ST_HPF */
+#define WM8915_STR_SEL 0x0002 /* STR_SEL */
+#define WM8915_STR_SEL_MASK 0x0002 /* STR_SEL */
+#define WM8915_STR_SEL_SHIFT 1 /* STR_SEL */
+#define WM8915_STR_SEL_WIDTH 1 /* STR_SEL */
+#define WM8915_STL_SEL 0x0001 /* STL_SEL */
+#define WM8915_STL_SEL_MASK 0x0001 /* STL_SEL */
+#define WM8915_STL_SEL_SHIFT 0 /* STL_SEL */
+#define WM8915_STL_SEL_WIDTH 1 /* STL_SEL */
+
+/*
+ * R1792 (0x700) - GPIO 1
+ */
+#define WM8915_GP1_DIR 0x8000 /* GP1_DIR */
+#define WM8915_GP1_DIR_MASK 0x8000 /* GP1_DIR */
+#define WM8915_GP1_DIR_SHIFT 15 /* GP1_DIR */
+#define WM8915_GP1_DIR_WIDTH 1 /* GP1_DIR */
+#define WM8915_GP1_PU 0x4000 /* GP1_PU */
+#define WM8915_GP1_PU_MASK 0x4000 /* GP1_PU */
+#define WM8915_GP1_PU_SHIFT 14 /* GP1_PU */
+#define WM8915_GP1_PU_WIDTH 1 /* GP1_PU */
+#define WM8915_GP1_PD 0x2000 /* GP1_PD */
+#define WM8915_GP1_PD_MASK 0x2000 /* GP1_PD */
+#define WM8915_GP1_PD_SHIFT 13 /* GP1_PD */
+#define WM8915_GP1_PD_WIDTH 1 /* GP1_PD */
+#define WM8915_GP1_POL 0x0400 /* GP1_POL */
+#define WM8915_GP1_POL_MASK 0x0400 /* GP1_POL */
+#define WM8915_GP1_POL_SHIFT 10 /* GP1_POL */
+#define WM8915_GP1_POL_WIDTH 1 /* GP1_POL */
+#define WM8915_GP1_OP_CFG 0x0200 /* GP1_OP_CFG */
+#define WM8915_GP1_OP_CFG_MASK 0x0200 /* GP1_OP_CFG */
+#define WM8915_GP1_OP_CFG_SHIFT 9 /* GP1_OP_CFG */
+#define WM8915_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */
+#define WM8915_GP1_DB 0x0100 /* GP1_DB */
+#define WM8915_GP1_DB_MASK 0x0100 /* GP1_DB */
+#define WM8915_GP1_DB_SHIFT 8 /* GP1_DB */
+#define WM8915_GP1_DB_WIDTH 1 /* GP1_DB */
+#define WM8915_GP1_LVL 0x0040 /* GP1_LVL */
+#define WM8915_GP1_LVL_MASK 0x0040 /* GP1_LVL */
+#define WM8915_GP1_LVL_SHIFT 6 /* GP1_LVL */
+#define WM8915_GP1_LVL_WIDTH 1 /* GP1_LVL */
+#define WM8915_GP1_FN_MASK 0x000F /* GP1_FN - [3:0] */
+#define WM8915_GP1_FN_SHIFT 0 /* GP1_FN - [3:0] */
+#define WM8915_GP1_FN_WIDTH 4 /* GP1_FN - [3:0] */
+
+/*
+ * R1793 (0x701) - GPIO 2
+ */
+#define WM8915_GP2_DIR 0x8000 /* GP2_DIR */
+#define WM8915_GP2_DIR_MASK 0x8000 /* GP2_DIR */
+#define WM8915_GP2_DIR_SHIFT 15 /* GP2_DIR */
+#define WM8915_GP2_DIR_WIDTH 1 /* GP2_DIR */
+#define WM8915_GP2_PU 0x4000 /* GP2_PU */
+#define WM8915_GP2_PU_MASK 0x4000 /* GP2_PU */
+#define WM8915_GP2_PU_SHIFT 14 /* GP2_PU */
+#define WM8915_GP2_PU_WIDTH 1 /* GP2_PU */
+#define WM8915_GP2_PD 0x2000 /* GP2_PD */
+#define WM8915_GP2_PD_MASK 0x2000 /* GP2_PD */
+#define WM8915_GP2_PD_SHIFT 13 /* GP2_PD */
+#define WM8915_GP2_PD_WIDTH 1 /* GP2_PD */
+#define WM8915_GP2_POL 0x0400 /* GP2_POL */
+#define WM8915_GP2_POL_MASK 0x0400 /* GP2_POL */
+#define WM8915_GP2_POL_SHIFT 10 /* GP2_POL */
+#define WM8915_GP2_POL_WIDTH 1 /* GP2_POL */
+#define WM8915_GP2_OP_CFG 0x0200 /* GP2_OP_CFG */
+#define WM8915_GP2_OP_CFG_MASK 0x0200 /* GP2_OP_CFG */
+#define WM8915_GP2_OP_CFG_SHIFT 9 /* GP2_OP_CFG */
+#define WM8915_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */
+#define WM8915_GP2_DB 0x0100 /* GP2_DB */
+#define WM8915_GP2_DB_MASK 0x0100 /* GP2_DB */
+#define WM8915_GP2_DB_SHIFT 8 /* GP2_DB */
+#define WM8915_GP2_DB_WIDTH 1 /* GP2_DB */
+#define WM8915_GP2_LVL 0x0040 /* GP2_LVL */
+#define WM8915_GP2_LVL_MASK 0x0040 /* GP2_LVL */
+#define WM8915_GP2_LVL_SHIFT 6 /* GP2_LVL */
+#define WM8915_GP2_LVL_WIDTH 1 /* GP2_LVL */
+#define WM8915_GP2_FN_MASK 0x000F /* GP2_FN - [3:0] */
+#define WM8915_GP2_FN_SHIFT 0 /* GP2_FN - [3:0] */
+#define WM8915_GP2_FN_WIDTH 4 /* GP2_FN - [3:0] */
+
+/*
+ * R1794 (0x702) - GPIO 3
+ */
+#define WM8915_GP3_DIR 0x8000 /* GP3_DIR */
+#define WM8915_GP3_DIR_MASK 0x8000 /* GP3_DIR */
+#define WM8915_GP3_DIR_SHIFT 15 /* GP3_DIR */
+#define WM8915_GP3_DIR_WIDTH 1 /* GP3_DIR */
+#define WM8915_GP3_PU 0x4000 /* GP3_PU */
+#define WM8915_GP3_PU_MASK 0x4000 /* GP3_PU */
+#define WM8915_GP3_PU_SHIFT 14 /* GP3_PU */
+#define WM8915_GP3_PU_WIDTH 1 /* GP3_PU */
+#define WM8915_GP3_PD 0x2000 /* GP3_PD */
+#define WM8915_GP3_PD_MASK 0x2000 /* GP3_PD */
+#define WM8915_GP3_PD_SHIFT 13 /* GP3_PD */
+#define WM8915_GP3_PD_WIDTH 1 /* GP3_PD */
+#define WM8915_GP3_POL 0x0400 /* GP3_POL */
+#define WM8915_GP3_POL_MASK 0x0400 /* GP3_POL */
+#define WM8915_GP3_POL_SHIFT 10 /* GP3_POL */
+#define WM8915_GP3_POL_WIDTH 1 /* GP3_POL */
+#define WM8915_GP3_OP_CFG 0x0200 /* GP3_OP_CFG */
+#define WM8915_GP3_OP_CFG_MASK 0x0200 /* GP3_OP_CFG */
+#define WM8915_GP3_OP_CFG_SHIFT 9 /* GP3_OP_CFG */
+#define WM8915_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */
+#define WM8915_GP3_DB 0x0100 /* GP3_DB */
+#define WM8915_GP3_DB_MASK 0x0100 /* GP3_DB */
+#define WM8915_GP3_DB_SHIFT 8 /* GP3_DB */
+#define WM8915_GP3_DB_WIDTH 1 /* GP3_DB */
+#define WM8915_GP3_LVL 0x0040 /* GP3_LVL */
+#define WM8915_GP3_LVL_MASK 0x0040 /* GP3_LVL */
+#define WM8915_GP3_LVL_SHIFT 6 /* GP3_LVL */
+#define WM8915_GP3_LVL_WIDTH 1 /* GP3_LVL */
+#define WM8915_GP3_FN_MASK 0x000F /* GP3_FN - [3:0] */
+#define WM8915_GP3_FN_SHIFT 0 /* GP3_FN - [3:0] */
+#define WM8915_GP3_FN_WIDTH 4 /* GP3_FN - [3:0] */
+
+/*
+ * R1795 (0x703) - GPIO 4
+ */
+#define WM8915_GP4_DIR 0x8000 /* GP4_DIR */
+#define WM8915_GP4_DIR_MASK 0x8000 /* GP4_DIR */
+#define WM8915_GP4_DIR_SHIFT 15 /* GP4_DIR */
+#define WM8915_GP4_DIR_WIDTH 1 /* GP4_DIR */
+#define WM8915_GP4_PU 0x4000 /* GP4_PU */
+#define WM8915_GP4_PU_MASK 0x4000 /* GP4_PU */
+#define WM8915_GP4_PU_SHIFT 14 /* GP4_PU */
+#define WM8915_GP4_PU_WIDTH 1 /* GP4_PU */
+#define WM8915_GP4_PD 0x2000 /* GP4_PD */
+#define WM8915_GP4_PD_MASK 0x2000 /* GP4_PD */
+#define WM8915_GP4_PD_SHIFT 13 /* GP4_PD */
+#define WM8915_GP4_PD_WIDTH 1 /* GP4_PD */
+#define WM8915_GP4_POL 0x0400 /* GP4_POL */
+#define WM8915_GP4_POL_MASK 0x0400 /* GP4_POL */
+#define WM8915_GP4_POL_SHIFT 10 /* GP4_POL */
+#define WM8915_GP4_POL_WIDTH 1 /* GP4_POL */
+#define WM8915_GP4_OP_CFG 0x0200 /* GP4_OP_CFG */
+#define WM8915_GP4_OP_CFG_MASK 0x0200 /* GP4_OP_CFG */
+#define WM8915_GP4_OP_CFG_SHIFT 9 /* GP4_OP_CFG */
+#define WM8915_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */
+#define WM8915_GP4_DB 0x0100 /* GP4_DB */
+#define WM8915_GP4_DB_MASK 0x0100 /* GP4_DB */
+#define WM8915_GP4_DB_SHIFT 8 /* GP4_DB */
+#define WM8915_GP4_DB_WIDTH 1 /* GP4_DB */
+#define WM8915_GP4_LVL 0x0040 /* GP4_LVL */
+#define WM8915_GP4_LVL_MASK 0x0040 /* GP4_LVL */
+#define WM8915_GP4_LVL_SHIFT 6 /* GP4_LVL */
+#define WM8915_GP4_LVL_WIDTH 1 /* GP4_LVL */
+#define WM8915_GP4_FN_MASK 0x000F /* GP4_FN - [3:0] */
+#define WM8915_GP4_FN_SHIFT 0 /* GP4_FN - [3:0] */
+#define WM8915_GP4_FN_WIDTH 4 /* GP4_FN - [3:0] */
+
+/*
+ * R1796 (0x704) - GPIO 5
+ */
+#define WM8915_GP5_DIR 0x8000 /* GP5_DIR */
+#define WM8915_GP5_DIR_MASK 0x8000 /* GP5_DIR */
+#define WM8915_GP5_DIR_SHIFT 15 /* GP5_DIR */
+#define WM8915_GP5_DIR_WIDTH 1 /* GP5_DIR */
+#define WM8915_GP5_PU 0x4000 /* GP5_PU */
+#define WM8915_GP5_PU_MASK 0x4000 /* GP5_PU */
+#define WM8915_GP5_PU_SHIFT 14 /* GP5_PU */
+#define WM8915_GP5_PU_WIDTH 1 /* GP5_PU */
+#define WM8915_GP5_PD 0x2000 /* GP5_PD */
+#define WM8915_GP5_PD_MASK 0x2000 /* GP5_PD */
+#define WM8915_GP5_PD_SHIFT 13 /* GP5_PD */
+#define WM8915_GP5_PD_WIDTH 1 /* GP5_PD */
+#define WM8915_GP5_POL 0x0400 /* GP5_POL */
+#define WM8915_GP5_POL_MASK 0x0400 /* GP5_POL */
+#define WM8915_GP5_POL_SHIFT 10 /* GP5_POL */
+#define WM8915_GP5_POL_WIDTH 1 /* GP5_POL */
+#define WM8915_GP5_OP_CFG 0x0200 /* GP5_OP_CFG */
+#define WM8915_GP5_OP_CFG_MASK 0x0200 /* GP5_OP_CFG */
+#define WM8915_GP5_OP_CFG_SHIFT 9 /* GP5_OP_CFG */
+#define WM8915_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */
+#define WM8915_GP5_DB 0x0100 /* GP5_DB */
+#define WM8915_GP5_DB_MASK 0x0100 /* GP5_DB */
+#define WM8915_GP5_DB_SHIFT 8 /* GP5_DB */
+#define WM8915_GP5_DB_WIDTH 1 /* GP5_DB */
+#define WM8915_GP5_LVL 0x0040 /* GP5_LVL */
+#define WM8915_GP5_LVL_MASK 0x0040 /* GP5_LVL */
+#define WM8915_GP5_LVL_SHIFT 6 /* GP5_LVL */
+#define WM8915_GP5_LVL_WIDTH 1 /* GP5_LVL */
+#define WM8915_GP5_FN_MASK 0x000F /* GP5_FN - [3:0] */
+#define WM8915_GP5_FN_SHIFT 0 /* GP5_FN - [3:0] */
+#define WM8915_GP5_FN_WIDTH 4 /* GP5_FN - [3:0] */
+
+/*
+ * R1824 (0x720) - Pull Control (1)
+ */
+#define WM8915_DMICDAT2_PD 0x1000 /* DMICDAT2_PD */
+#define WM8915_DMICDAT2_PD_MASK 0x1000 /* DMICDAT2_PD */
+#define WM8915_DMICDAT2_PD_SHIFT 12 /* DMICDAT2_PD */
+#define WM8915_DMICDAT2_PD_WIDTH 1 /* DMICDAT2_PD */
+#define WM8915_DMICDAT1_PD 0x0400 /* DMICDAT1_PD */
+#define WM8915_DMICDAT1_PD_MASK 0x0400 /* DMICDAT1_PD */
+#define WM8915_DMICDAT1_PD_SHIFT 10 /* DMICDAT1_PD */
+#define WM8915_DMICDAT1_PD_WIDTH 1 /* DMICDAT1_PD */
+#define WM8915_MCLK2_PU 0x0200 /* MCLK2_PU */
+#define WM8915_MCLK2_PU_MASK 0x0200 /* MCLK2_PU */
+#define WM8915_MCLK2_PU_SHIFT 9 /* MCLK2_PU */
+#define WM8915_MCLK2_PU_WIDTH 1 /* MCLK2_PU */
+#define WM8915_MCLK2_PD 0x0100 /* MCLK2_PD */
+#define WM8915_MCLK2_PD_MASK 0x0100 /* MCLK2_PD */
+#define WM8915_MCLK2_PD_SHIFT 8 /* MCLK2_PD */
+#define WM8915_MCLK2_PD_WIDTH 1 /* MCLK2_PD */
+#define WM8915_MCLK1_PU 0x0080 /* MCLK1_PU */
+#define WM8915_MCLK1_PU_MASK 0x0080 /* MCLK1_PU */
+#define WM8915_MCLK1_PU_SHIFT 7 /* MCLK1_PU */
+#define WM8915_MCLK1_PU_WIDTH 1 /* MCLK1_PU */
+#define WM8915_MCLK1_PD 0x0040 /* MCLK1_PD */
+#define WM8915_MCLK1_PD_MASK 0x0040 /* MCLK1_PD */
+#define WM8915_MCLK1_PD_SHIFT 6 /* MCLK1_PD */
+#define WM8915_MCLK1_PD_WIDTH 1 /* MCLK1_PD */
+#define WM8915_DACDAT1_PU 0x0020 /* DACDAT1_PU */
+#define WM8915_DACDAT1_PU_MASK 0x0020 /* DACDAT1_PU */
+#define WM8915_DACDAT1_PU_SHIFT 5 /* DACDAT1_PU */
+#define WM8915_DACDAT1_PU_WIDTH 1 /* DACDAT1_PU */
+#define WM8915_DACDAT1_PD 0x0010 /* DACDAT1_PD */
+#define WM8915_DACDAT1_PD_MASK 0x0010 /* DACDAT1_PD */
+#define WM8915_DACDAT1_PD_SHIFT 4 /* DACDAT1_PD */
+#define WM8915_DACDAT1_PD_WIDTH 1 /* DACDAT1_PD */
+#define WM8915_DACLRCLK1_PU 0x0008 /* DACLRCLK1_PU */
+#define WM8915_DACLRCLK1_PU_MASK 0x0008 /* DACLRCLK1_PU */
+#define WM8915_DACLRCLK1_PU_SHIFT 3 /* DACLRCLK1_PU */
+#define WM8915_DACLRCLK1_PU_WIDTH 1 /* DACLRCLK1_PU */
+#define WM8915_DACLRCLK1_PD 0x0004 /* DACLRCLK1_PD */
+#define WM8915_DACLRCLK1_PD_MASK 0x0004 /* DACLRCLK1_PD */
+#define WM8915_DACLRCLK1_PD_SHIFT 2 /* DACLRCLK1_PD */
+#define WM8915_DACLRCLK1_PD_WIDTH 1 /* DACLRCLK1_PD */
+#define WM8915_BCLK1_PU 0x0002 /* BCLK1_PU */
+#define WM8915_BCLK1_PU_MASK 0x0002 /* BCLK1_PU */
+#define WM8915_BCLK1_PU_SHIFT 1 /* BCLK1_PU */
+#define WM8915_BCLK1_PU_WIDTH 1 /* BCLK1_PU */
+#define WM8915_BCLK1_PD 0x0001 /* BCLK1_PD */
+#define WM8915_BCLK1_PD_MASK 0x0001 /* BCLK1_PD */
+#define WM8915_BCLK1_PD_SHIFT 0 /* BCLK1_PD */
+#define WM8915_BCLK1_PD_WIDTH 1 /* BCLK1_PD */
+
+/*
+ * R1825 (0x721) - Pull Control (2)
+ */
+#define WM8915_LDO1ENA_PD 0x0100 /* LDO1ENA_PD */
+#define WM8915_LDO1ENA_PD_MASK 0x0100 /* LDO1ENA_PD */
+#define WM8915_LDO1ENA_PD_SHIFT 8 /* LDO1ENA_PD */
+#define WM8915_LDO1ENA_PD_WIDTH 1 /* LDO1ENA_PD */
+#define WM8915_ADDR_PD 0x0040 /* ADDR_PD */
+#define WM8915_ADDR_PD_MASK 0x0040 /* ADDR_PD */
+#define WM8915_ADDR_PD_SHIFT 6 /* ADDR_PD */
+#define WM8915_ADDR_PD_WIDTH 1 /* ADDR_PD */
+#define WM8915_DACDAT2_PU 0x0020 /* DACDAT2_PU */
+#define WM8915_DACDAT2_PU_MASK 0x0020 /* DACDAT2_PU */
+#define WM8915_DACDAT2_PU_SHIFT 5 /* DACDAT2_PU */
+#define WM8915_DACDAT2_PU_WIDTH 1 /* DACDAT2_PU */
+#define WM8915_DACDAT2_PD 0x0010 /* DACDAT2_PD */
+#define WM8915_DACDAT2_PD_MASK 0x0010 /* DACDAT2_PD */
+#define WM8915_DACDAT2_PD_SHIFT 4 /* DACDAT2_PD */
+#define WM8915_DACDAT2_PD_WIDTH 1 /* DACDAT2_PD */
+#define WM8915_DACLRCLK2_PU 0x0008 /* DACLRCLK2_PU */
+#define WM8915_DACLRCLK2_PU_MASK 0x0008 /* DACLRCLK2_PU */
+#define WM8915_DACLRCLK2_PU_SHIFT 3 /* DACLRCLK2_PU */
+#define WM8915_DACLRCLK2_PU_WIDTH 1 /* DACLRCLK2_PU */
+#define WM8915_DACLRCLK2_PD 0x0004 /* DACLRCLK2_PD */
+#define WM8915_DACLRCLK2_PD_MASK 0x0004 /* DACLRCLK2_PD */
+#define WM8915_DACLRCLK2_PD_SHIFT 2 /* DACLRCLK2_PD */
+#define WM8915_DACLRCLK2_PD_WIDTH 1 /* DACLRCLK2_PD */
+#define WM8915_BCLK2_PU 0x0002 /* BCLK2_PU */
+#define WM8915_BCLK2_PU_MASK 0x0002 /* BCLK2_PU */
+#define WM8915_BCLK2_PU_SHIFT 1 /* BCLK2_PU */
+#define WM8915_BCLK2_PU_WIDTH 1 /* BCLK2_PU */
+#define WM8915_BCLK2_PD 0x0001 /* BCLK2_PD */
+#define WM8915_BCLK2_PD_MASK 0x0001 /* BCLK2_PD */
+#define WM8915_BCLK2_PD_SHIFT 0 /* BCLK2_PD */
+#define WM8915_BCLK2_PD_WIDTH 1 /* BCLK2_PD */
+
+/*
+ * R1840 (0x730) - Interrupt Status 1
+ */
+#define WM8915_GP5_EINT 0x0010 /* GP5_EINT */
+#define WM8915_GP5_EINT_MASK 0x0010 /* GP5_EINT */
+#define WM8915_GP5_EINT_SHIFT 4 /* GP5_EINT */
+#define WM8915_GP5_EINT_WIDTH 1 /* GP5_EINT */
+#define WM8915_GP4_EINT 0x0008 /* GP4_EINT */
+#define WM8915_GP4_EINT_MASK 0x0008 /* GP4_EINT */
+#define WM8915_GP4_EINT_SHIFT 3 /* GP4_EINT */
+#define WM8915_GP4_EINT_WIDTH 1 /* GP4_EINT */
+#define WM8915_GP3_EINT 0x0004 /* GP3_EINT */
+#define WM8915_GP3_EINT_MASK 0x0004 /* GP3_EINT */
+#define WM8915_GP3_EINT_SHIFT 2 /* GP3_EINT */
+#define WM8915_GP3_EINT_WIDTH 1 /* GP3_EINT */
+#define WM8915_GP2_EINT 0x0002 /* GP2_EINT */
+#define WM8915_GP2_EINT_MASK 0x0002 /* GP2_EINT */
+#define WM8915_GP2_EINT_SHIFT 1 /* GP2_EINT */
+#define WM8915_GP2_EINT_WIDTH 1 /* GP2_EINT */
+#define WM8915_GP1_EINT 0x0001 /* GP1_EINT */
+#define WM8915_GP1_EINT_MASK 0x0001 /* GP1_EINT */
+#define WM8915_GP1_EINT_SHIFT 0 /* GP1_EINT */
+#define WM8915_GP1_EINT_WIDTH 1 /* GP1_EINT */
+
+/*
+ * R1841 (0x731) - Interrupt Status 2
+ */
+#define WM8915_DCS_DONE_23_EINT 0x1000 /* DCS_DONE_23_EINT */
+#define WM8915_DCS_DONE_23_EINT_MASK 0x1000 /* DCS_DONE_23_EINT */
+#define WM8915_DCS_DONE_23_EINT_SHIFT 12 /* DCS_DONE_23_EINT */
+#define WM8915_DCS_DONE_23_EINT_WIDTH 1 /* DCS_DONE_23_EINT */
+#define WM8915_DCS_DONE_01_EINT 0x0800 /* DCS_DONE_01_EINT */
+#define WM8915_DCS_DONE_01_EINT_MASK 0x0800 /* DCS_DONE_01_EINT */
+#define WM8915_DCS_DONE_01_EINT_SHIFT 11 /* DCS_DONE_01_EINT */
+#define WM8915_DCS_DONE_01_EINT_WIDTH 1 /* DCS_DONE_01_EINT */
+#define WM8915_WSEQ_DONE_EINT 0x0400 /* WSEQ_DONE_EINT */
+#define WM8915_WSEQ_DONE_EINT_MASK 0x0400 /* WSEQ_DONE_EINT */
+#define WM8915_WSEQ_DONE_EINT_SHIFT 10 /* WSEQ_DONE_EINT */
+#define WM8915_WSEQ_DONE_EINT_WIDTH 1 /* WSEQ_DONE_EINT */
+#define WM8915_FIFOS_ERR_EINT 0x0200 /* FIFOS_ERR_EINT */
+#define WM8915_FIFOS_ERR_EINT_MASK 0x0200 /* FIFOS_ERR_EINT */
+#define WM8915_FIFOS_ERR_EINT_SHIFT 9 /* FIFOS_ERR_EINT */
+#define WM8915_FIFOS_ERR_EINT_WIDTH 1 /* FIFOS_ERR_EINT */
+#define WM8915_DSP2DRC_SIG_DET_EINT 0x0080 /* DSP2DRC_SIG_DET_EINT */
+#define WM8915_DSP2DRC_SIG_DET_EINT_MASK 0x0080 /* DSP2DRC_SIG_DET_EINT */
+#define WM8915_DSP2DRC_SIG_DET_EINT_SHIFT 7 /* DSP2DRC_SIG_DET_EINT */
+#define WM8915_DSP2DRC_SIG_DET_EINT_WIDTH 1 /* DSP2DRC_SIG_DET_EINT */
+#define WM8915_DSP1DRC_SIG_DET_EINT 0x0040 /* DSP1DRC_SIG_DET_EINT */
+#define WM8915_DSP1DRC_SIG_DET_EINT_MASK 0x0040 /* DSP1DRC_SIG_DET_EINT */
+#define WM8915_DSP1DRC_SIG_DET_EINT_SHIFT 6 /* DSP1DRC_SIG_DET_EINT */
+#define WM8915_DSP1DRC_SIG_DET_EINT_WIDTH 1 /* DSP1DRC_SIG_DET_EINT */
+#define WM8915_FLL_SW_CLK_DONE_EINT 0x0008 /* FLL_SW_CLK_DONE_EINT */
+#define WM8915_FLL_SW_CLK_DONE_EINT_MASK 0x0008 /* FLL_SW_CLK_DONE_EINT */
+#define WM8915_FLL_SW_CLK_DONE_EINT_SHIFT 3 /* FLL_SW_CLK_DONE_EINT */
+#define WM8915_FLL_SW_CLK_DONE_EINT_WIDTH 1 /* FLL_SW_CLK_DONE_EINT */
+#define WM8915_FLL_LOCK_EINT 0x0004 /* FLL_LOCK_EINT */
+#define WM8915_FLL_LOCK_EINT_MASK 0x0004 /* FLL_LOCK_EINT */
+#define WM8915_FLL_LOCK_EINT_SHIFT 2 /* FLL_LOCK_EINT */
+#define WM8915_FLL_LOCK_EINT_WIDTH 1 /* FLL_LOCK_EINT */
+#define WM8915_HP_DONE_EINT 0x0002 /* HP_DONE_EINT */
+#define WM8915_HP_DONE_EINT_MASK 0x0002 /* HP_DONE_EINT */
+#define WM8915_HP_DONE_EINT_SHIFT 1 /* HP_DONE_EINT */
+#define WM8915_HP_DONE_EINT_WIDTH 1 /* HP_DONE_EINT */
+#define WM8915_MICD_EINT 0x0001 /* MICD_EINT */
+#define WM8915_MICD_EINT_MASK 0x0001 /* MICD_EINT */
+#define WM8915_MICD_EINT_SHIFT 0 /* MICD_EINT */
+#define WM8915_MICD_EINT_WIDTH 1 /* MICD_EINT */
+
+/*
+ * R1842 (0x732) - Interrupt Raw Status 2
+ */
+#define WM8915_DCS_DONE_23_STS 0x1000 /* DCS_DONE_23_STS */
+#define WM8915_DCS_DONE_23_STS_MASK 0x1000 /* DCS_DONE_23_STS */
+#define WM8915_DCS_DONE_23_STS_SHIFT 12 /* DCS_DONE_23_STS */
+#define WM8915_DCS_DONE_23_STS_WIDTH 1 /* DCS_DONE_23_STS */
+#define WM8915_DCS_DONE_01_STS 0x0800 /* DCS_DONE_01_STS */
+#define WM8915_DCS_DONE_01_STS_MASK 0x0800 /* DCS_DONE_01_STS */
+#define WM8915_DCS_DONE_01_STS_SHIFT 11 /* DCS_DONE_01_STS */
+#define WM8915_DCS_DONE_01_STS_WIDTH 1 /* DCS_DONE_01_STS */
+#define WM8915_WSEQ_DONE_STS 0x0400 /* WSEQ_DONE_STS */
+#define WM8915_WSEQ_DONE_STS_MASK 0x0400 /* WSEQ_DONE_STS */
+#define WM8915_WSEQ_DONE_STS_SHIFT 10 /* WSEQ_DONE_STS */
+#define WM8915_WSEQ_DONE_STS_WIDTH 1 /* WSEQ_DONE_STS */
+#define WM8915_FIFOS_ERR_STS 0x0200 /* FIFOS_ERR_STS */
+#define WM8915_FIFOS_ERR_STS_MASK 0x0200 /* FIFOS_ERR_STS */
+#define WM8915_FIFOS_ERR_STS_SHIFT 9 /* FIFOS_ERR_STS */
+#define WM8915_FIFOS_ERR_STS_WIDTH 1 /* FIFOS_ERR_STS */
+#define WM8915_DSP2DRC_SIG_DET_STS 0x0080 /* DSP2DRC_SIG_DET_STS */
+#define WM8915_DSP2DRC_SIG_DET_STS_MASK 0x0080 /* DSP2DRC_SIG_DET_STS */
+#define WM8915_DSP2DRC_SIG_DET_STS_SHIFT 7 /* DSP2DRC_SIG_DET_STS */
+#define WM8915_DSP2DRC_SIG_DET_STS_WIDTH 1 /* DSP2DRC_SIG_DET_STS */
+#define WM8915_DSP1DRC_SIG_DET_STS 0x0040 /* DSP1DRC_SIG_DET_STS */
+#define WM8915_DSP1DRC_SIG_DET_STS_MASK 0x0040 /* DSP1DRC_SIG_DET_STS */
+#define WM8915_DSP1DRC_SIG_DET_STS_SHIFT 6 /* DSP1DRC_SIG_DET_STS */
+#define WM8915_DSP1DRC_SIG_DET_STS_WIDTH 1 /* DSP1DRC_SIG_DET_STS */
+#define WM8915_FLL_LOCK_STS 0x0004 /* FLL_LOCK_STS */
+#define WM8915_FLL_LOCK_STS_MASK 0x0004 /* FLL_LOCK_STS */
+#define WM8915_FLL_LOCK_STS_SHIFT 2 /* FLL_LOCK_STS */
+#define WM8915_FLL_LOCK_STS_WIDTH 1 /* FLL_LOCK_STS */
+
+/*
+ * R1848 (0x738) - Interrupt Status 1 Mask
+ */
+#define WM8915_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */
+#define WM8915_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */
+#define WM8915_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */
+#define WM8915_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */
+#define WM8915_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */
+#define WM8915_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */
+#define WM8915_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */
+#define WM8915_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */
+#define WM8915_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */
+#define WM8915_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */
+#define WM8915_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */
+#define WM8915_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */
+#define WM8915_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */
+#define WM8915_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */
+#define WM8915_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */
+#define WM8915_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */
+#define WM8915_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */
+#define WM8915_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */
+#define WM8915_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */
+#define WM8915_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */
+
+/*
+ * R1849 (0x739) - Interrupt Status 2 Mask
+ */
+#define WM8915_IM_DCS_DONE_23_EINT 0x1000 /* IM_DCS_DONE_23_EINT */
+#define WM8915_IM_DCS_DONE_23_EINT_MASK 0x1000 /* IM_DCS_DONE_23_EINT */
+#define WM8915_IM_DCS_DONE_23_EINT_SHIFT 12 /* IM_DCS_DONE_23_EINT */
+#define WM8915_IM_DCS_DONE_23_EINT_WIDTH 1 /* IM_DCS_DONE_23_EINT */
+#define WM8915_IM_DCS_DONE_01_EINT 0x0800 /* IM_DCS_DONE_01_EINT */
+#define WM8915_IM_DCS_DONE_01_EINT_MASK 0x0800 /* IM_DCS_DONE_01_EINT */
+#define WM8915_IM_DCS_DONE_01_EINT_SHIFT 11 /* IM_DCS_DONE_01_EINT */
+#define WM8915_IM_DCS_DONE_01_EINT_WIDTH 1 /* IM_DCS_DONE_01_EINT */
+#define WM8915_IM_WSEQ_DONE_EINT 0x0400 /* IM_WSEQ_DONE_EINT */
+#define WM8915_IM_WSEQ_DONE_EINT_MASK 0x0400 /* IM_WSEQ_DONE_EINT */
+#define WM8915_IM_WSEQ_DONE_EINT_SHIFT 10 /* IM_WSEQ_DONE_EINT */
+#define WM8915_IM_WSEQ_DONE_EINT_WIDTH 1 /* IM_WSEQ_DONE_EINT */
+#define WM8915_IM_FIFOS_ERR_EINT 0x0200 /* IM_FIFOS_ERR_EINT */
+#define WM8915_IM_FIFOS_ERR_EINT_MASK 0x0200 /* IM_FIFOS_ERR_EINT */
+#define WM8915_IM_FIFOS_ERR_EINT_SHIFT 9 /* IM_FIFOS_ERR_EINT */
+#define WM8915_IM_FIFOS_ERR_EINT_WIDTH 1 /* IM_FIFOS_ERR_EINT */
+#define WM8915_IM_DSP2DRC_SIG_DET_EINT 0x0080 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP2DRC_SIG_DET_EINT_MASK 0x0080 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP2DRC_SIG_DET_EINT_SHIFT 7 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP2DRC_SIG_DET_EINT_WIDTH 1 /* IM_DSP2DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP1DRC_SIG_DET_EINT 0x0040 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP1DRC_SIG_DET_EINT_MASK 0x0040 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP1DRC_SIG_DET_EINT_SHIFT 6 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8915_IM_DSP1DRC_SIG_DET_EINT_WIDTH 1 /* IM_DSP1DRC_SIG_DET_EINT */
+#define WM8915_IM_FLL_SW_CLK_DONE_EINT 0x0008 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8915_IM_FLL_SW_CLK_DONE_EINT_MASK 0x0008 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8915_IM_FLL_SW_CLK_DONE_EINT_SHIFT 3 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8915_IM_FLL_SW_CLK_DONE_EINT_WIDTH 1 /* IM_FLL_SW_CLK_DONE_EINT */
+#define WM8915_IM_FLL_LOCK_EINT 0x0004 /* IM_FLL_LOCK_EINT */
+#define WM8915_IM_FLL_LOCK_EINT_MASK 0x0004 /* IM_FLL_LOCK_EINT */
+#define WM8915_IM_FLL_LOCK_EINT_SHIFT 2 /* IM_FLL_LOCK_EINT */
+#define WM8915_IM_FLL_LOCK_EINT_WIDTH 1 /* IM_FLL_LOCK_EINT */
+#define WM8915_IM_HP_DONE_EINT 0x0002 /* IM_HP_DONE_EINT */
+#define WM8915_IM_HP_DONE_EINT_MASK 0x0002 /* IM_HP_DONE_EINT */
+#define WM8915_IM_HP_DONE_EINT_SHIFT 1 /* IM_HP_DONE_EINT */
+#define WM8915_IM_HP_DONE_EINT_WIDTH 1 /* IM_HP_DONE_EINT */
+#define WM8915_IM_MICD_EINT 0x0001 /* IM_MICD_EINT */
+#define WM8915_IM_MICD_EINT_MASK 0x0001 /* IM_MICD_EINT */
+#define WM8915_IM_MICD_EINT_SHIFT 0 /* IM_MICD_EINT */
+#define WM8915_IM_MICD_EINT_WIDTH 1 /* IM_MICD_EINT */
+
+/*
+ * R1856 (0x740) - Interrupt Control
+ */
+#define WM8915_IM_IRQ 0x0001 /* IM_IRQ */
+#define WM8915_IM_IRQ_MASK 0x0001 /* IM_IRQ */
+#define WM8915_IM_IRQ_SHIFT 0 /* IM_IRQ */
+#define WM8915_IM_IRQ_WIDTH 1 /* IM_IRQ */
+
+/*
+ * R2048 (0x800) - Left PDM Speaker
+ */
+#define WM8915_SPKL_ENA 0x0010 /* SPKL_ENA */
+#define WM8915_SPKL_ENA_MASK 0x0010 /* SPKL_ENA */
+#define WM8915_SPKL_ENA_SHIFT 4 /* SPKL_ENA */
+#define WM8915_SPKL_ENA_WIDTH 1 /* SPKL_ENA */
+#define WM8915_SPKL_MUTE 0x0008 /* SPKL_MUTE */
+#define WM8915_SPKL_MUTE_MASK 0x0008 /* SPKL_MUTE */
+#define WM8915_SPKL_MUTE_SHIFT 3 /* SPKL_MUTE */
+#define WM8915_SPKL_MUTE_WIDTH 1 /* SPKL_MUTE */
+#define WM8915_SPKL_MUTE_ZC 0x0004 /* SPKL_MUTE_ZC */
+#define WM8915_SPKL_MUTE_ZC_MASK 0x0004 /* SPKL_MUTE_ZC */
+#define WM8915_SPKL_MUTE_ZC_SHIFT 2 /* SPKL_MUTE_ZC */
+#define WM8915_SPKL_MUTE_ZC_WIDTH 1 /* SPKL_MUTE_ZC */
+#define WM8915_SPKL_SRC_MASK 0x0003 /* SPKL_SRC - [1:0] */
+#define WM8915_SPKL_SRC_SHIFT 0 /* SPKL_SRC - [1:0] */
+#define WM8915_SPKL_SRC_WIDTH 2 /* SPKL_SRC - [1:0] */
+
+/*
+ * R2049 (0x801) - Right PDM Speaker
+ */
+#define WM8915_SPKR_ENA 0x0010 /* SPKR_ENA */
+#define WM8915_SPKR_ENA_MASK 0x0010 /* SPKR_ENA */
+#define WM8915_SPKR_ENA_SHIFT 4 /* SPKR_ENA */
+#define WM8915_SPKR_ENA_WIDTH 1 /* SPKR_ENA */
+#define WM8915_SPKR_MUTE 0x0008 /* SPKR_MUTE */
+#define WM8915_SPKR_MUTE_MASK 0x0008 /* SPKR_MUTE */
+#define WM8915_SPKR_MUTE_SHIFT 3 /* SPKR_MUTE */
+#define WM8915_SPKR_MUTE_WIDTH 1 /* SPKR_MUTE */
+#define WM8915_SPKR_MUTE_ZC 0x0004 /* SPKR_MUTE_ZC */
+#define WM8915_SPKR_MUTE_ZC_MASK 0x0004 /* SPKR_MUTE_ZC */
+#define WM8915_SPKR_MUTE_ZC_SHIFT 2 /* SPKR_MUTE_ZC */
+#define WM8915_SPKR_MUTE_ZC_WIDTH 1 /* SPKR_MUTE_ZC */
+#define WM8915_SPKR_SRC_MASK 0x0003 /* SPKR_SRC - [1:0] */
+#define WM8915_SPKR_SRC_SHIFT 0 /* SPKR_SRC - [1:0] */
+#define WM8915_SPKR_SRC_WIDTH 2 /* SPKR_SRC - [1:0] */
+
+/*
+ * R2050 (0x802) - PDM Speaker Mute Sequence
+ */
+#define WM8915_SPK_MUTE_ENDIAN 0x0100 /* SPK_MUTE_ENDIAN */
+#define WM8915_SPK_MUTE_ENDIAN_MASK 0x0100 /* SPK_MUTE_ENDIAN */
+#define WM8915_SPK_MUTE_ENDIAN_SHIFT 8 /* SPK_MUTE_ENDIAN */
+#define WM8915_SPK_MUTE_ENDIAN_WIDTH 1 /* SPK_MUTE_ENDIAN */
+#define WM8915_SPK_MUTE_SEQ1_MASK 0x00FF /* SPK_MUTE_SEQ1 - [7:0] */
+#define WM8915_SPK_MUTE_SEQ1_SHIFT 0 /* SPK_MUTE_SEQ1 - [7:0] */
+#define WM8915_SPK_MUTE_SEQ1_WIDTH 8 /* SPK_MUTE_SEQ1 - [7:0] */
+
+/*
+ * R2051 (0x803) - PDM Speaker Volume
+ */
+#define WM8915_SPKR_VOL_MASK 0x00F0 /* SPKR_VOL - [7:4] */
+#define WM8915_SPKR_VOL_SHIFT 4 /* SPKR_VOL - [7:4] */
+#define WM8915_SPKR_VOL_WIDTH 4 /* SPKR_VOL - [7:4] */
+#define WM8915_SPKL_VOL_MASK 0x000F /* SPKL_VOL - [3:0] */
+#define WM8915_SPKL_VOL_SHIFT 0 /* SPKL_VOL - [3:0] */
+#define WM8915_SPKL_VOL_WIDTH 4 /* SPKL_VOL - [3:0] */
+
+#endif
--- /dev/null
+/*
+ * wm8958-dsp2.c -- WM8958 DSP2 support
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <trace/events/asoc.h>
+
+#include <linux/mfd/wm8994/core.h>
+#include <linux/mfd/wm8994/registers.h>
+#include <linux/mfd/wm8994/pdata.h>
+#include <linux/mfd/wm8994/gpio.h>
+
+#include "wm8994.h"
+
+#define WM_FW_BLOCK_INFO 0xff
+#define WM_FW_BLOCK_PM 0x00
+#define WM_FW_BLOCK_X 0x01
+#define WM_FW_BLOCK_Y 0x02
+#define WM_FW_BLOCK_Z 0x03
+#define WM_FW_BLOCK_I 0x06
+#define WM_FW_BLOCK_A 0x08
+#define WM_FW_BLOCK_C 0x0c
+
+static int wm8958_dsp2_fw(struct snd_soc_codec *codec, const char *name,
+ const struct firmware *fw, bool check)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ u64 data64;
+ u32 data32;
+ const u8 *data;
+ char *str;
+ size_t block_len, len;
+ int ret = 0;
+
+ /* Suppress unneeded downloads */
+ if (wm8994->cur_fw == fw)
+ return 0;
+
+ if (fw->size < 32) {
+ dev_err(codec->dev, "%s: firmware too short\n", name);
+ goto err;
+ }
+
+ if (memcmp(fw->data, "WMFW", 4) != 0) {
+ dev_err(codec->dev, "%s: firmware has bad file magic %08x\n",
+ name, data32);
+ goto err;
+ }
+
+ memcpy(&data32, fw->data + 4, sizeof(data32));
+ len = be32_to_cpu(data32);
+
+ memcpy(&data32, fw->data + 8, sizeof(data32));
+ data32 = be32_to_cpu(data32);
+ if ((data32 >> 24) & 0xff) {
+ dev_err(codec->dev, "%s: unsupported firmware version %d\n",
+ name, (data32 >> 24) & 0xff);
+ goto err;
+ }
+ if ((data32 & 0xffff) != 8958) {
+ dev_err(codec->dev, "%s: unsupported target device %d\n",
+ name, data32 & 0xffff);
+ goto err;
+ }
+ if (((data32 >> 16) & 0xff) != 0xc) {
+ dev_err(codec->dev, "%s: unsupported target core %d\n",
+ name, (data32 >> 16) & 0xff);
+ goto err;
+ }
+
+ if (check) {
+ memcpy(&data64, fw->data + 24, sizeof(u64));
+ dev_info(codec->dev, "%s timestamp %llx\n",
+ name, be64_to_cpu(data64));
+ } else {
+ snd_soc_write(codec, 0x102, 0x2);
+ snd_soc_write(codec, 0x900, 0x2);
+ }
+
+ data = fw->data + len;
+ len = fw->size - len;
+ while (len) {
+ if (len < 12) {
+ dev_err(codec->dev, "%s short data block of %zd\n",
+ name, len);
+ goto err;
+ }
+
+ memcpy(&data32, data + 4, sizeof(data32));
+ block_len = be32_to_cpu(data32);
+ if (block_len + 8 > len) {
+ dev_err(codec->dev, "%zd byte block longer than file\n",
+ block_len);
+ goto err;
+ }
+ if (block_len == 0) {
+ dev_err(codec->dev, "Zero length block\n");
+ goto err;
+ }
+
+ memcpy(&data32, data, sizeof(data32));
+ data32 = be32_to_cpu(data32);
+
+ switch ((data32 >> 24) & 0xff) {
+ case WM_FW_BLOCK_INFO:
+ /* Informational text */
+ if (!check)
+ break;
+
+ str = kzalloc(block_len + 1, GFP_KERNEL);
+ if (str) {
+ memcpy(str, data + 8, block_len);
+ dev_info(codec->dev, "%s: %s\n", name, str);
+ kfree(str);
+ } else {
+ dev_err(codec->dev, "Out of memory\n");
+ }
+ break;
+ case WM_FW_BLOCK_PM:
+ case WM_FW_BLOCK_X:
+ case WM_FW_BLOCK_Y:
+ case WM_FW_BLOCK_Z:
+ case WM_FW_BLOCK_I:
+ case WM_FW_BLOCK_A:
+ case WM_FW_BLOCK_C:
+ dev_dbg(codec->dev, "%s: %zd bytes of %x@%x\n", name,
+ block_len, (data32 >> 24) & 0xff,
+ data32 & 0xffffff);
+
+ if (check)
+ break;
+
+ data32 &= 0xffffff;
+
+ wm8994_bulk_write(codec->control_data,
+ data32 & 0xffffff,
+ block_len / 2,
+ (void *)(data + 8));
+
+ break;
+ default:
+ dev_warn(codec->dev, "%s: unknown block type %d\n",
+ name, (data32 >> 24) & 0xff);
+ break;
+ }
+
+ /* Round up to the next 32 bit word */
+ block_len += block_len % 4;
+
+ data += block_len + 8;
+ len -= block_len + 8;
+ }
+
+ if (!check) {
+ dev_dbg(codec->dev, "%s: download done\n", name);
+ wm8994->cur_fw = fw;
+ } else {
+ dev_info(codec->dev, "%s: got firmware\n", name);
+ }
+
+ goto ok;
+
+err:
+ ret = -EINVAL;
+ok:
+ if (!check) {
+ snd_soc_write(codec, 0x900, 0x0);
+ snd_soc_write(codec, 0x102, 0x0);
+ }
+
+ return ret;
+}
+
+static void wm8958_dsp_start_mbc(struct snd_soc_codec *codec, int path)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int i;
+
+ /* If the DSP is already running then noop */
+ if (snd_soc_read(codec, WM8958_DSP2_PROGRAM) & WM8958_DSP2_ENA)
+ return;
+
+ /* If we have MBC firmware download it */
+ if (wm8994->mbc)
+ wm8958_dsp2_fw(codec, "MBC", wm8994->mbc, false);
+
+ snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ WM8958_DSP2_ENA, WM8958_DSP2_ENA);
+
+ /* If we've got user supplied MBC settings use them */
+ if (pdata && pdata->num_mbc_cfgs) {
+ struct wm8958_mbc_cfg *cfg
+ = &pdata->mbc_cfgs[wm8994->mbc_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++)
+ snd_soc_write(codec, i + WM8958_MBC_BAND_1_K_1,
+ cfg->coeff_regs[i]);
+
+ for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++)
+ snd_soc_write(codec,
+ i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1,
+ cfg->cutoff_regs[i]);
+ }
+
+ /* Run the DSP */
+ snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ WM8958_DSP2_RUNR);
+
+ /* And we're off! */
+ snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ WM8958_MBC_ENA |
+ WM8958_MBC_SEL_MASK,
+ path << WM8958_MBC_SEL_SHIFT |
+ WM8958_MBC_ENA);
+}
+
+static void wm8958_dsp_start_vss(struct snd_soc_codec *codec, int path)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int i, ena;
+
+ if (wm8994->mbc_vss)
+ wm8958_dsp2_fw(codec, "MBC+VSS", wm8994->mbc_vss, false);
+
+ snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ WM8958_DSP2_ENA, WM8958_DSP2_ENA);
+
+ /* If we've got user supplied settings use them */
+ if (pdata && pdata->num_mbc_cfgs) {
+ struct wm8958_mbc_cfg *cfg
+ = &pdata->mbc_cfgs[wm8994->mbc_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->combined_regs); i++)
+ snd_soc_write(codec, i + 0x2800,
+ cfg->combined_regs[i]);
+ }
+
+ if (pdata && pdata->num_vss_cfgs) {
+ struct wm8958_vss_cfg *cfg
+ = &pdata->vss_cfgs[wm8994->vss_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
+ snd_soc_write(codec, i + 0x2600, cfg->regs[i]);
+ }
+
+ if (pdata && pdata->num_vss_hpf_cfgs) {
+ struct wm8958_vss_hpf_cfg *cfg
+ = &pdata->vss_hpf_cfgs[wm8994->vss_hpf_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
+ snd_soc_write(codec, i + 0x2400, cfg->regs[i]);
+ }
+
+ /* Run the DSP */
+ snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ WM8958_DSP2_RUNR);
+
+ /* Enable the algorithms we've selected */
+ ena = 0;
+ if (wm8994->mbc_ena[path])
+ ena |= 0x8;
+ if (wm8994->hpf2_ena[path])
+ ena |= 0x4;
+ if (wm8994->hpf1_ena[path])
+ ena |= 0x2;
+ if (wm8994->vss_ena[path])
+ ena |= 0x1;
+
+ snd_soc_write(codec, 0x2201, ena);
+
+ /* Switch the DSP into the data path */
+ snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ WM8958_MBC_SEL_MASK | WM8958_MBC_ENA,
+ path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA);
+}
+
+static void wm8958_dsp_start_enh_eq(struct snd_soc_codec *codec, int path)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int i;
+
+ wm8958_dsp2_fw(codec, "ENH_EQ", wm8994->enh_eq, false);
+
+ snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ WM8958_DSP2_ENA, WM8958_DSP2_ENA);
+
+ /* If we've got user supplied settings use them */
+ if (pdata && pdata->num_enh_eq_cfgs) {
+ struct wm8958_enh_eq_cfg *cfg
+ = &pdata->enh_eq_cfgs[wm8994->enh_eq_cfg];
+
+ for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
+ snd_soc_write(codec, i + 0x2200,
+ cfg->regs[i]);
+ }
+
+ /* Run the DSP */
+ snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ WM8958_DSP2_RUNR);
+
+ /* Switch the DSP into the data path */
+ snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ WM8958_MBC_SEL_MASK | WM8958_MBC_ENA,
+ path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA);
+}
+
+static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ int pwr_reg = snd_soc_read(codec, WM8994_POWER_MANAGEMENT_5);
+ int ena, reg, aif;
+
+ switch (path) {
+ case 0:
+ pwr_reg &= (WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA);
+ aif = 0;
+ break;
+ case 1:
+ pwr_reg &= (WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
+ aif = 0;
+ break;
+ case 2:
+ pwr_reg &= (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA);
+ aif = 1;
+ break;
+ default:
+ BUG();
+ return;
+ }
+
+ /* Do we have both an active AIF and an active algorithm? */
+ ena = wm8994->mbc_ena[path] || wm8994->vss_ena[path] ||
+ wm8994->hpf1_ena[path] || wm8994->hpf2_ena[path] ||
+ wm8994->enh_eq_ena[path];
+ if (!pwr_reg)
+ ena = 0;
+
+ reg = snd_soc_read(codec, WM8958_DSP2_PROGRAM);
+
+ dev_dbg(codec->dev, "DSP path %d %d startup: %d, power: %x, DSP: %x\n",
+ path, wm8994->dsp_active, start, pwr_reg, reg);
+
+ if (start && ena) {
+ /* If the DSP is already running then noop */
+ if (reg & WM8958_DSP2_ENA)
+ return;
+
+ /* If either AIFnCLK is not yet enabled postpone */
+ if (!(snd_soc_read(codec, WM8994_AIF1_CLOCKING_1)
+ & WM8994_AIF1CLK_ENA_MASK) &&
+ !(snd_soc_read(codec, WM8994_AIF2_CLOCKING_1)
+ & WM8994_AIF2CLK_ENA_MASK))
+ return;
+
+ /* Switch the clock over to the appropriate AIF */
+ snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA,
+ aif << WM8958_DSP2CLK_SRC_SHIFT |
+ WM8958_DSP2CLK_ENA);
+
+ if (wm8994->enh_eq_ena[path])
+ wm8958_dsp_start_enh_eq(codec, path);
+ else if (wm8994->vss_ena[path] || wm8994->hpf1_ena[path] ||
+ wm8994->hpf2_ena[path])
+ wm8958_dsp_start_vss(codec, path);
+ else if (wm8994->mbc_ena[path])
+ wm8958_dsp_start_mbc(codec, path);
+
+ wm8994->dsp_active = path;
+
+ dev_dbg(codec->dev, "DSP running in path %d\n", path);
+ }
+
+ if (!start && wm8994->dsp_active == path) {
+ /* If the DSP is already stopped then noop */
+ if (!(reg & WM8958_DSP2_ENA))
+ return;
+
+ snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
+ WM8958_MBC_ENA, 0);
+ snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
+ WM8958_DSP2_STOP);
+ snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
+ WM8958_DSP2_ENA, 0);
+ snd_soc_update_bits(codec, WM8994_CLOCKING_1,
+ WM8958_DSP2CLK_ENA, 0);
+
+ wm8994->dsp_active = -1;
+
+ dev_dbg(codec->dev, "DSP stopped\n");
+ }
+}
+
+int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ int i;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_PRE_PMU:
+ for (i = 0; i < 3; i++)
+ wm8958_dsp_apply(codec, i, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ case SND_SOC_DAPM_PRE_PMD:
+ for (i = 0; i < 3; i++)
+ wm8958_dsp_apply(codec, i, 0);
+ break;
+ }
+
+ return 0;
+}
+
+/* Check if DSP2 is in use on another AIF */
+static int wm8958_dsp2_busy(struct wm8994_priv *wm8994, int aif)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) {
+ if (i == aif)
+ continue;
+ if (wm8994->mbc_ena[i] || wm8994->vss_ena[i] ||
+ wm8994->hpf1_ena[i] || wm8994->hpf2_ena[i])
+ return 1;
+ }
+
+ return 0;
+}
+
+static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int value = ucontrol->value.integer.value[0];
+ int reg;
+
+ /* Don't allow on the fly reconfiguration */
+ reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
+ return -EBUSY;
+
+ if (value >= pdata->num_mbc_cfgs)
+ return -EINVAL;
+
+ wm8994->mbc_cfg = value;
+
+ return 0;
+}
+
+static int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg;
+
+ return 0;
+}
+
+static int wm8958_mbc_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm8958_mbc_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mbc = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc];
+
+ return 0;
+}
+
+static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mbc = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (wm8994->mbc_ena[mbc] == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (ucontrol->value.integer.value[0] > 1)
+ return -EINVAL;
+
+ if (wm8958_dsp2_busy(wm8994, mbc)) {
+ dev_dbg(codec->dev, "DSP2 active on %d already\n", mbc);
+ return -EBUSY;
+ }
+
+ if (wm8994->enh_eq_ena[mbc])
+ return -EBUSY;
+
+ wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0];
+
+ wm8958_dsp_apply(codec, mbc, wm8994->mbc_ena[mbc]);
+
+ return 0;
+}
+
+#define WM8958_MBC_SWITCH(xname, xval) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = wm8958_mbc_info, \
+ .get = wm8958_mbc_get, .put = wm8958_mbc_put, \
+ .private_value = xval }
+
+static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int value = ucontrol->value.integer.value[0];
+ int reg;
+
+ /* Don't allow on the fly reconfiguration */
+ reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
+ return -EBUSY;
+
+ if (value >= pdata->num_vss_cfgs)
+ return -EINVAL;
+
+ wm8994->vss_cfg = value;
+
+ return 0;
+}
+
+static int wm8958_get_vss_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8994->vss_cfg;
+
+ return 0;
+}
+
+static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int value = ucontrol->value.integer.value[0];
+ int reg;
+
+ /* Don't allow on the fly reconfiguration */
+ reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
+ return -EBUSY;
+
+ if (value >= pdata->num_vss_hpf_cfgs)
+ return -EINVAL;
+
+ wm8994->vss_hpf_cfg = value;
+
+ return 0;
+}
+
+static int wm8958_get_vss_hpf_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8994->vss_hpf_cfg;
+
+ return 0;
+}
+
+static int wm8958_vss_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm8958_vss_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int vss = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8994->vss_ena[vss];
+
+ return 0;
+}
+
+static int wm8958_vss_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int vss = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (wm8994->vss_ena[vss] == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (ucontrol->value.integer.value[0] > 1)
+ return -EINVAL;
+
+ if (!wm8994->mbc_vss)
+ return -ENODEV;
+
+ if (wm8958_dsp2_busy(wm8994, vss)) {
+ dev_dbg(codec->dev, "DSP2 active on %d already\n", vss);
+ return -EBUSY;
+ }
+
+ if (wm8994->enh_eq_ena[vss])
+ return -EBUSY;
+
+ wm8994->vss_ena[vss] = ucontrol->value.integer.value[0];
+
+ wm8958_dsp_apply(codec, vss, wm8994->vss_ena[vss]);
+
+ return 0;
+}
+
+
+#define WM8958_VSS_SWITCH(xname, xval) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = wm8958_vss_info, \
+ .get = wm8958_vss_get, .put = wm8958_vss_put, \
+ .private_value = xval }
+
+static int wm8958_hpf_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm8958_hpf_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int hpf = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (hpf < 3)
+ ucontrol->value.integer.value[0] = wm8994->hpf1_ena[hpf % 3];
+ else
+ ucontrol->value.integer.value[0] = wm8994->hpf2_ena[hpf % 3];
+
+ return 0;
+}
+
+static int wm8958_hpf_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int hpf = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (hpf < 3) {
+ if (wm8994->hpf1_ena[hpf % 3] ==
+ ucontrol->value.integer.value[0])
+ return 0;
+ } else {
+ if (wm8994->hpf2_ena[hpf % 3] ==
+ ucontrol->value.integer.value[0])
+ return 0;
+ }
+
+ if (ucontrol->value.integer.value[0] > 1)
+ return -EINVAL;
+
+ if (!wm8994->mbc_vss)
+ return -ENODEV;
+
+ if (wm8958_dsp2_busy(wm8994, hpf % 3)) {
+ dev_dbg(codec->dev, "DSP2 active on %d already\n", hpf);
+ return -EBUSY;
+ }
+
+ if (wm8994->enh_eq_ena[hpf % 3])
+ return -EBUSY;
+
+ if (hpf < 3)
+ wm8994->hpf1_ena[hpf % 3] = ucontrol->value.integer.value[0];
+ else
+ wm8994->hpf2_ena[hpf % 3] = ucontrol->value.integer.value[0];
+
+ wm8958_dsp_apply(codec, hpf % 3, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+#define WM8958_HPF_SWITCH(xname, xval) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = wm8958_hpf_info, \
+ .get = wm8958_hpf_get, .put = wm8958_hpf_put, \
+ .private_value = xval }
+
+static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int value = ucontrol->value.integer.value[0];
+ int reg;
+
+ /* Don't allow on the fly reconfiguration */
+ reg = snd_soc_read(codec, WM8994_CLOCKING_1);
+ if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
+ return -EBUSY;
+
+ if (value >= pdata->num_enh_eq_cfgs)
+ return -EINVAL;
+
+ wm8994->enh_eq_cfg = value;
+
+ return 0;
+}
+
+static int wm8958_get_enh_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = wm8994->enh_eq_cfg;
+
+ return 0;
+}
+
+static int wm8958_enh_eq_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int wm8958_enh_eq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wm8994->enh_eq_ena[eq];
+
+ return 0;
+}
+
+static int wm8958_enh_eq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int eq = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (wm8994->enh_eq_ena[eq] == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (ucontrol->value.integer.value[0] > 1)
+ return -EINVAL;
+
+ if (!wm8994->enh_eq)
+ return -ENODEV;
+
+ if (wm8958_dsp2_busy(wm8994, eq)) {
+ dev_dbg(codec->dev, "DSP2 active on %d already\n", eq);
+ return -EBUSY;
+ }
+
+ if (wm8994->mbc_ena[eq] || wm8994->vss_ena[eq] ||
+ wm8994->hpf1_ena[eq] || wm8994->hpf2_ena[eq])
+ return -EBUSY;
+
+ wm8994->enh_eq_ena[eq] = ucontrol->value.integer.value[0];
+
+ wm8958_dsp_apply(codec, eq, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+#define WM8958_ENH_EQ_SWITCH(xname, xval) {\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .info = wm8958_enh_eq_info, \
+ .get = wm8958_enh_eq_get, .put = wm8958_enh_eq_put, \
+ .private_value = xval }
+
+static const struct snd_kcontrol_new wm8958_mbc_snd_controls[] = {
+WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0),
+WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1),
+WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2),
+};
+
+static const struct snd_kcontrol_new wm8958_vss_snd_controls[] = {
+WM8958_VSS_SWITCH("AIF1DAC1 VSS Switch", 0),
+WM8958_VSS_SWITCH("AIF1DAC2 VSS Switch", 1),
+WM8958_VSS_SWITCH("AIF2DAC VSS Switch", 2),
+WM8958_HPF_SWITCH("AIF1DAC1 HPF1 Switch", 0),
+WM8958_HPF_SWITCH("AIF1DAC2 HPF1 Switch", 1),
+WM8958_HPF_SWITCH("AIF2DAC HPF1 Switch", 2),
+WM8958_HPF_SWITCH("AIF1DAC1 HPF2 Switch", 3),
+WM8958_HPF_SWITCH("AIF1DAC2 HPF2 Switch", 4),
+WM8958_HPF_SWITCH("AIF2DAC HPF2 Switch", 5),
+};
+
+static const struct snd_kcontrol_new wm8958_enh_eq_snd_controls[] = {
+WM8958_ENH_EQ_SWITCH("AIF1DAC1 Enhanced EQ Switch", 0),
+WM8958_ENH_EQ_SWITCH("AIF1DAC2 Enhanced EQ Switch", 1),
+WM8958_ENH_EQ_SWITCH("AIF2DAC Enhanced EQ Switch", 2),
+};
+
+static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context)
+{
+ struct snd_soc_codec *codec = context;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (fw && (wm8958_dsp2_fw(codec, "ENH_EQ", fw, true) == 0)) {
+ mutex_lock(&codec->mutex);
+ wm8994->enh_eq = fw;
+ mutex_unlock(&codec->mutex);
+ }
+}
+
+static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context)
+{
+ struct snd_soc_codec *codec = context;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (fw && (wm8958_dsp2_fw(codec, "MBC+VSS", fw, true) == 0)) {
+ mutex_lock(&codec->mutex);
+ wm8994->mbc_vss = fw;
+ mutex_unlock(&codec->mutex);
+ }
+
+ /* We can't have more than one request outstanding at once so
+ * we daisy chain.
+ */
+ request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ "wm8958_enh_eq.wfw", codec->dev, GFP_KERNEL,
+ codec, wm8958_enh_eq_loaded);
+}
+
+static void wm8958_mbc_loaded(const struct firmware *fw, void *context)
+{
+ struct snd_soc_codec *codec = context;
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+ if (wm8958_dsp2_fw(codec, "MBC", fw, true) != 0)
+ return;
+
+ mutex_lock(&codec->mutex);
+ wm8994->mbc = fw;
+ mutex_unlock(&codec->mutex);
+
+ /* We can't have more than one request outstanding at once so
+ * we daisy chain.
+ */
+ request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ "wm8958_mbc_vss.wfw", codec->dev, GFP_KERNEL,
+ codec, wm8958_mbc_vss_loaded);
+}
+
+void wm8958_dsp2_init(struct snd_soc_codec *codec)
+{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994_pdata *pdata = wm8994->pdata;
+ int ret, i;
+
+ wm8994->dsp_active = -1;
+
+ snd_soc_add_controls(codec, wm8958_mbc_snd_controls,
+ ARRAY_SIZE(wm8958_mbc_snd_controls));
+ snd_soc_add_controls(codec, wm8958_vss_snd_controls,
+ ARRAY_SIZE(wm8958_vss_snd_controls));
+ snd_soc_add_controls(codec, wm8958_enh_eq_snd_controls,
+ ARRAY_SIZE(wm8958_enh_eq_snd_controls));
+
+
+ /* We don't *require* firmware and don't want to delay boot */
+ request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ "wm8958_mbc.wfw", codec->dev, GFP_KERNEL,
+ codec, wm8958_mbc_loaded);
+
+ if (!pdata)
+ return;
+
+ if (pdata->num_mbc_cfgs) {
+ struct snd_kcontrol_new control[] = {
+ SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum,
+ wm8958_get_mbc_enum, wm8958_put_mbc_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->mbc_texts = kmalloc(sizeof(char *)
+ * pdata->num_mbc_cfgs, GFP_KERNEL);
+ if (!wm8994->mbc_texts) {
+ dev_err(wm8994->codec->dev,
+ "Failed to allocate %d MBC config texts\n",
+ pdata->num_mbc_cfgs);
+ return;
+ }
+
+ for (i = 0; i < pdata->num_mbc_cfgs; i++)
+ wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name;
+
+ wm8994->mbc_enum.max = pdata->num_mbc_cfgs;
+ wm8994->mbc_enum.texts = wm8994->mbc_texts;
+
+ ret = snd_soc_add_controls(wm8994->codec, control, 1);
+ if (ret != 0)
+ dev_err(wm8994->codec->dev,
+ "Failed to add MBC mode controls: %d\n", ret);
+ }
+
+ if (pdata->num_vss_cfgs) {
+ struct snd_kcontrol_new control[] = {
+ SOC_ENUM_EXT("VSS Mode", wm8994->vss_enum,
+ wm8958_get_vss_enum, wm8958_put_vss_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->vss_texts = kmalloc(sizeof(char *)
+ * pdata->num_vss_cfgs, GFP_KERNEL);
+ if (!wm8994->vss_texts) {
+ dev_err(wm8994->codec->dev,
+ "Failed to allocate %d VSS config texts\n",
+ pdata->num_vss_cfgs);
+ return;
+ }
+
+ for (i = 0; i < pdata->num_vss_cfgs; i++)
+ wm8994->vss_texts[i] = pdata->vss_cfgs[i].name;
+
+ wm8994->vss_enum.max = pdata->num_vss_cfgs;
+ wm8994->vss_enum.texts = wm8994->vss_texts;
+
+ ret = snd_soc_add_controls(wm8994->codec, control, 1);
+ if (ret != 0)
+ dev_err(wm8994->codec->dev,
+ "Failed to add VSS mode controls: %d\n", ret);
+ }
+
+ if (pdata->num_vss_hpf_cfgs) {
+ struct snd_kcontrol_new control[] = {
+ SOC_ENUM_EXT("VSS HPF Mode", wm8994->vss_hpf_enum,
+ wm8958_get_vss_hpf_enum,
+ wm8958_put_vss_hpf_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->vss_hpf_texts = kmalloc(sizeof(char *)
+ * pdata->num_vss_hpf_cfgs, GFP_KERNEL);
+ if (!wm8994->vss_hpf_texts) {
+ dev_err(wm8994->codec->dev,
+ "Failed to allocate %d VSS HPF config texts\n",
+ pdata->num_vss_hpf_cfgs);
+ return;
+ }
+
+ for (i = 0; i < pdata->num_vss_hpf_cfgs; i++)
+ wm8994->vss_hpf_texts[i] = pdata->vss_hpf_cfgs[i].name;
+
+ wm8994->vss_hpf_enum.max = pdata->num_vss_hpf_cfgs;
+ wm8994->vss_hpf_enum.texts = wm8994->vss_hpf_texts;
+
+ ret = snd_soc_add_controls(wm8994->codec, control, 1);
+ if (ret != 0)
+ dev_err(wm8994->codec->dev,
+ "Failed to add VSS HPFmode controls: %d\n",
+ ret);
+ }
+
+ if (pdata->num_enh_eq_cfgs) {
+ struct snd_kcontrol_new control[] = {
+ SOC_ENUM_EXT("Enhanced EQ Mode", wm8994->enh_eq_enum,
+ wm8958_get_enh_eq_enum,
+ wm8958_put_enh_eq_enum),
+ };
+
+ /* We need an array of texts for the enum API */
+ wm8994->enh_eq_texts = kmalloc(sizeof(char *)
+ * pdata->num_enh_eq_cfgs, GFP_KERNEL);
+ if (!wm8994->enh_eq_texts) {
+ dev_err(wm8994->codec->dev,
+ "Failed to allocate %d enhanced EQ config texts\n",
+ pdata->num_enh_eq_cfgs);
+ return;
+ }
+
+ for (i = 0; i < pdata->num_enh_eq_cfgs; i++)
+ wm8994->enh_eq_texts[i] = pdata->enh_eq_cfgs[i].name;
+
+ wm8994->enh_eq_enum.max = pdata->num_enh_eq_cfgs;
+ wm8994->enh_eq_enum.texts = wm8994->enh_eq_texts;
+
+ ret = snd_soc_add_controls(wm8994->codec, control, 1);
+ if (ret != 0)
+ dev_err(wm8994->codec->dev,
+ "Failed to add enhanced EQ controls: %d\n",
+ ret);
+ }
+}
int bclk; /* Desired BCLK */
int lrclk;
+ struct completion fll_lock;
int fll_src;
int fll_fref;
int fll_fout;
return 0;
}
+static const char *cap_hpf_mode_text[] = {
+ "Hi-fi", "Application"
+};
+
+static const struct soc_enum cap_hpf_mode =
+ SOC_ENUM_SINGLE(WM8962_ADC_DAC_CONTROL_2, 10, 2, cap_hpf_mode_text);
+
static const struct snd_kcontrol_new wm8962_snd_controls[] = {
SOC_DOUBLE("Input Mixer Switch", WM8962_INPUT_MIXER_CONTROL_1, 3, 2, 1, 1),
WM8962_RIGHT_INPUT_VOLUME, 7, 1, 1),
SOC_DOUBLE_R("Capture ZC Switch", WM8962_LEFT_INPUT_VOLUME,
WM8962_RIGHT_INPUT_VOLUME, 6, 1, 1),
+SOC_SINGLE("Capture HPF Switch", WM8962_ADC_DAC_CONTROL_1, 0, 1, 1),
+SOC_ENUM("Capture HPF Mode", cap_hpf_mode),
+SOC_SINGLE("Capture HPF Cutoff", WM8962_ADC_DAC_CONTROL_2, 7, 7, 0),
SOC_DOUBLE_R_TLV("Sidetone Volume", WM8962_DAC_DSP_MIXING_1,
WM8962_DAC_DSP_MIXING_2, 4, 12, 0, st_tlv),
SND_SOC_DAPM_INPUT("IN4L"),
SND_SOC_DAPM_INPUT("IN4R"),
SND_SOC_DAPM_INPUT("Beep"),
+SND_SOC_DAPM_INPUT("DMICDAT"),
SND_SOC_DAPM_MICBIAS("MICBIAS", WM8962_PWR_MGMT_1, 1, 0),
SND_SOC_DAPM_MIXER("MIXINR", WM8962_PWR_MGMT_1, 4, 0,
mixinr, ARRAY_SIZE(mixinr)),
+SND_SOC_DAPM_AIF_IN("DMIC", NULL, 0, WM8962_PWR_MGMT_1, 10, 0),
+
SND_SOC_DAPM_ADC("ADCL", "Capture", WM8962_PWR_MGMT_1, 3, 0),
SND_SOC_DAPM_ADC("ADCR", "Capture", WM8962_PWR_MGMT_1, 2, 0),
{ "MICBIAS", NULL, "SYSCLK" },
+ { "DMIC", NULL, "DMICDAT" },
+
{ "ADCL", NULL, "SYSCLK" },
{ "ADCL", NULL, "TOCLK" },
{ "ADCL", NULL, "MIXINL" },
+ { "ADCL", NULL, "DMIC" },
{ "ADCR", NULL, "SYSCLK" },
{ "ADCR", NULL, "TOCLK" },
{ "ADCR", NULL, "MIXINR" },
+ { "ADCR", NULL, "DMIC" },
{ "STL", "Left", "ADCL" },
{ "STL", "Right", "ADCR" },
case WM8962_SYSCLK_FLL:
wm8962->sysclk = WM8962_SYSCLK_FLL;
src = 1 << WM8962_SYSCLK_SRC_SHIFT;
- WARN_ON(freq != wm8962->fll_fout);
break;
default:
return -EINVAL;
return 0;
}
-static int wm8962_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
+static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
unsigned int Fref, unsigned int Fout)
{
- struct snd_soc_codec *codec = dai->codec;
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
struct _fll_div fll_div;
+ unsigned long timeout;
int ret;
int fll1 = snd_soc_read(codec, WM8962_FLL_CONTROL_1) & WM8962_FLL_ENA;
dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
+ /* This should be a massive overestimate */
+ timeout = msecs_to_jiffies(1);
+
+ wait_for_completion_timeout(&wm8962->fll_lock, timeout);
+
wm8962->fll_fref = Fref;
wm8962->fll_fout = Fout;
wm8962->fll_src = source;
.hw_params = wm8962_hw_params,
.set_sysclk = wm8962_set_dai_sysclk,
.set_fmt = wm8962_set_dai_fmt,
- .set_pll = wm8962_set_fll,
.digital_mute = wm8962_mute,
};
active = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2);
active &= ~mask;
+ if (active & WM8962_FLL_LOCK_EINT) {
+ dev_dbg(codec->dev, "FLL locked\n");
+ complete(&wm8962->fll_lock);
+ }
+
if (active & WM8962_FIFOS_ERR_EINT)
dev_err(codec->dev, "FIFO error\n");
dev);
u16 *reg_cache = codec->reg_cache;
int i, trigger, irq_pol;
+ bool dmicclk, dmicdat;
wm8962->codec = codec;
INIT_DELAYED_WORK(&wm8962->mic_work, wm8962_mic_work);
+ init_completion(&wm8962->fll_lock);
codec->cache_sync = 1;
codec->dapm.idle_bias_off = 1;
wm8962_add_widgets(codec);
+ /* Save boards having to disable DMIC when not in use */
+ dmicclk = false;
+ dmicdat = false;
+ for (i = 0; i < WM8962_MAX_GPIO; i++) {
+ switch (snd_soc_read(codec, WM8962_GPIO_BASE + i)
+ & WM8962_GP2_FN_MASK) {
+ case WM8962_GPIO_FN_DMICCLK:
+ dmicclk = true;
+ break;
+ case WM8962_GPIO_FN_DMICDAT:
+ dmicdat = true;
+ break;
+ default:
+ break;
+ }
+ }
+ if (!dmicclk || !dmicdat) {
+ dev_dbg(codec->dev, "DMIC not in use, disabling\n");
+ snd_soc_dapm_nc_pin(&codec->dapm, "DMICDAT");
+ }
+ if (dmicclk != dmicdat)
+ dev_warn(codec->dev, "DMIC GPIOs partially configured\n");
+
wm8962_init_beep(codec);
wm8962_init_gpio(codec);
i2c->irq, ret);
/* Non-fatal */
} else {
- /* Enable error reporting IRQs by default */
+ /* Enable some IRQs by default */
snd_soc_update_bits(codec,
WM8962_INTERRUPT_STATUS_2_MASK,
+ WM8962_FLL_LOCK_EINT |
WM8962_TEMP_SHUT_EINT |
WM8962_FIFOS_ERR_EINT, 0);
}
.reg_cache_default = wm8962_reg,
.volatile_register = wm8962_volatile_register,
.readable_register = wm8962_readable_register,
+ .set_pll = wm8962_set_fll,
};
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static int class_w_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec;
struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
int ret;
#include "wm8994.h"
#include "wm_hubs.h"
-struct fll_config {
- int src;
- int in;
- int out;
-};
-
#define WM8994_NUM_DRC 3
#define WM8994_NUM_EQ 3
WM8994_AIF2_EQ_GAINS_1,
};
-struct wm8994_micdet {
- struct snd_soc_jack *jack;
- int det;
- int shrt;
-};
-
-/* codec private data */
-struct wm8994_priv {
- struct wm_hubs_data hubs;
- enum snd_soc_control_type control_type;
- void *control_data;
- struct snd_soc_codec *codec;
- int sysclk[2];
- int sysclk_rate[2];
- int mclk[2];
- int aifclk[2];
- struct fll_config fll[2], fll_suspend[2];
-
- int dac_rates[2];
- int lrclk_shared[2];
-
- int mbc_ena[3];
-
- /* Platform dependent DRC configuration */
- const char **drc_texts;
- int drc_cfg[WM8994_NUM_DRC];
- struct soc_enum drc_enum;
-
- /* Platform dependent ReTune mobile configuration */
- int num_retune_mobile_texts;
- const char **retune_mobile_texts;
- int retune_mobile_cfg[WM8994_NUM_EQ];
- struct soc_enum retune_mobile_enum;
-
- /* Platform dependent MBC configuration */
- int mbc_cfg;
- const char **mbc_texts;
- struct soc_enum mbc_enum;
-
- struct wm8994_micdet micdet[2];
-
- wm8958_micdet_cb jack_cb;
- void *jack_cb_data;
- int micdet_irq;
-
- int revision;
- struct wm8994_pdata *pdata;
-
- unsigned int aif1clk_enable:1;
- unsigned int aif2clk_enable:1;
-
- unsigned int aif1clk_disable:1;
- unsigned int aif2clk_disable:1;
-};
-
static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg)
{
+ struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = wm8994->control_data;
+
switch (reg) {
case WM8994_GPIO_1:
case WM8994_GPIO_2:
case WM8994_INTERRUPT_STATUS_2:
case WM8994_INTERRUPT_RAW_STATUS_2:
return 1;
+
+ case WM8958_DSP2_PROGRAM:
+ case WM8958_DSP2_CONFIG:
+ case WM8958_DSP2_EXECCONTROL:
+ if (control->type == WM8958)
+ return 1;
+ else
+ return 0;
+
default:
break;
}
static const struct soc_enum adc_osr =
SOC_ENUM_SINGLE(WM8994_OVERSAMPLING, 1, 2, osr_text);
-static void wm8958_mbc_apply(struct snd_soc_codec *codec, int mbc, int start)
-{
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
- struct wm8994_pdata *pdata = wm8994->pdata;
- int pwr_reg = snd_soc_read(codec, WM8994_POWER_MANAGEMENT_5);
- int ena, reg, aif, i;
-
- switch (mbc) {
- case 0:
- pwr_reg &= (WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA);
- aif = 0;
- break;
- case 1:
- pwr_reg &= (WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
- aif = 0;
- break;
- case 2:
- pwr_reg &= (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA);
- aif = 1;
- break;
- default:
- BUG();
- return;
- }
-
- /* We can only enable the MBC if the AIF is enabled and we
- * want it to be enabled. */
- ena = pwr_reg && wm8994->mbc_ena[mbc];
-
- reg = snd_soc_read(codec, WM8958_DSP2_PROGRAM);
-
- dev_dbg(codec->dev, "MBC %d startup: %d, power: %x, DSP: %x\n",
- mbc, start, pwr_reg, reg);
-
- if (start && ena) {
- /* If the DSP is already running then noop */
- if (reg & WM8958_DSP2_ENA)
- return;
-
- /* Switch the clock over to the appropriate AIF */
- snd_soc_update_bits(codec, WM8994_CLOCKING_1,
- WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA,
- aif << WM8958_DSP2CLK_SRC_SHIFT |
- WM8958_DSP2CLK_ENA);
-
- snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
- WM8958_DSP2_ENA, WM8958_DSP2_ENA);
-
- /* If we've got user supplied MBC settings use them */
- if (pdata && pdata->num_mbc_cfgs) {
- struct wm8958_mbc_cfg *cfg
- = &pdata->mbc_cfgs[wm8994->mbc_cfg];
-
- for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++)
- snd_soc_write(codec, i + WM8958_MBC_BAND_1_K_1,
- cfg->coeff_regs[i]);
-
- for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++)
- snd_soc_write(codec,
- i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1,
- cfg->cutoff_regs[i]);
- }
-
- /* Run the DSP */
- snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
- WM8958_DSP2_RUNR);
-
- /* And we're off! */
- snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
- WM8958_MBC_ENA | WM8958_MBC_SEL_MASK,
- mbc << WM8958_MBC_SEL_SHIFT |
- WM8958_MBC_ENA);
- } else {
- /* If the DSP is already stopped then noop */
- if (!(reg & WM8958_DSP2_ENA))
- return;
-
- snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
- WM8958_MBC_ENA, 0);
- snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
- WM8958_DSP2_ENA, 0);
- snd_soc_update_bits(codec, WM8994_CLOCKING_1,
- WM8958_DSP2CLK_ENA, 0);
- }
-}
-
-static int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_codec *codec = w->codec;
- int mbc;
-
- switch (w->shift) {
- case 13:
- case 12:
- mbc = 2;
- break;
- case 11:
- case 10:
- mbc = 1;
- break;
- case 9:
- case 8:
- mbc = 0;
- break;
- default:
- BUG();
- return -EINVAL;
- }
-
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- wm8958_mbc_apply(codec, mbc, 1);
- break;
- case SND_SOC_DAPM_POST_PMD:
- wm8958_mbc_apply(codec, mbc, 0);
- break;
- }
-
- return 0;
-}
-
-static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
- struct wm8994_pdata *pdata = wm8994->pdata;
- int value = ucontrol->value.integer.value[0];
- int reg;
-
- /* Don't allow on the fly reconfiguration */
- reg = snd_soc_read(codec, WM8994_CLOCKING_1);
- if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
- return -EBUSY;
-
- if (value >= pdata->num_mbc_cfgs)
- return -EINVAL;
-
- wm8994->mbc_cfg = value;
-
- return 0;
-}
-
-static int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
-
- ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg;
-
- return 0;
-}
-
-static int wm8958_mbc_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 1;
- return 0;
-}
-
-static int wm8958_mbc_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int mbc = kcontrol->private_value;
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
-
- ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc];
-
- return 0;
-}
-
-static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int mbc = kcontrol->private_value;
- int i;
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
-
- if (ucontrol->value.integer.value[0] > 1)
- return -EINVAL;
-
- for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) {
- if (mbc != i && wm8994->mbc_ena[i]) {
- dev_dbg(codec->dev, "MBC %d active already\n", mbc);
- return -EBUSY;
- }
- }
-
- wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0];
-
- wm8958_mbc_apply(codec, mbc, wm8994->mbc_ena[mbc]);
-
- return 0;
-}
-
-#define WM8958_MBC_SWITCH(xname, xval) {\
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
- .info = wm8958_mbc_info, \
- .get = wm8958_mbc_get, .put = wm8958_mbc_put, \
- .private_value = xval }
-
static const struct snd_kcontrol_new wm8994_snd_controls[] = {
SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME,
WM8994_AIF1_ADC1_RIGHT_VOLUME,
static const struct snd_kcontrol_new wm8958_snd_controls[] = {
SOC_SINGLE_TLV("AIF3 Boost Volume", WM8958_AIF3_CONTROL_2, 10, 3, 0, aif_tlv),
-WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0),
-WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1),
-WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2),
};
static int clk_sys_event(struct snd_soc_dapm_widget *w,
break;
}
+ /* We may also have postponed startup of DSP, handle that. */
+ wm8958_aif_ev(w, kcontrol, event);
+
return 0;
}
static int wm8994_put_hp_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *w = wlist->widgets[0];
struct snd_soc_codec *codec = w->codec;
int ret;
static int wm8994_put_class_w(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *w = wlist->widgets[0];
struct snd_soc_codec *codec = w->codec;
int ret;
WM8994_VMID_BUF_ENA |
WM8994_VMID_RAMP_MASK, 0);
+ wm8994->cur_fw = NULL;
+
pm_runtime_put(codec->dev);
}
break;
static int wm8994_suspend(struct snd_soc_codec *codec, pm_message_t state)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = codec->control_data;
int i, ret;
+ switch (control->type) {
+ case WM8994:
+ snd_soc_update_bits(codec, WM8994_MICBIAS, WM8994_MICD_ENA, 0);
+ break;
+ case WM8958:
+ snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
+ WM8958_MICD_ENA, 0);
+ break;
+ }
+
for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
memcpy(&wm8994->fll_suspend[i], &wm8994->fll[i],
- sizeof(struct fll_config));
+ sizeof(struct wm8994_fll_config));
ret = _wm8994_set_fll(codec, i + 1, 0, 0, 0);
if (ret < 0)
dev_warn(codec->dev, "Failed to stop FLL%d: %d\n",
static int wm8994_resume(struct snd_soc_codec *codec)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+ struct wm8994 *control = codec->control_data;
int i, ret;
unsigned int val, mask;
i + 1, ret);
}
+ switch (control->type) {
+ case WM8994:
+ if (wm8994->micdet[0].jack || wm8994->micdet[1].jack)
+ snd_soc_update_bits(codec, WM8994_MICBIAS,
+ WM8994_MICD_ENA, WM8994_MICD_ENA);
+ break;
+ case WM8958:
+ if (wm8994->jack_cb)
+ snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
+ WM8958_MICD_ENA, WM8958_MICD_ENA);
+ break;
+ }
+
return 0;
}
#else
dev_dbg(codec->dev, "%d ReTune Mobile configurations\n",
pdata->num_retune_mobile_cfgs);
- if (pdata->num_mbc_cfgs) {
- struct snd_kcontrol_new control[] = {
- SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum,
- wm8958_get_mbc_enum, wm8958_put_mbc_enum),
- };
-
- /* We need an array of texts for the enum API */
- wm8994->mbc_texts = kmalloc(sizeof(char *)
- * pdata->num_mbc_cfgs, GFP_KERNEL);
- if (!wm8994->mbc_texts) {
- dev_err(wm8994->codec->dev,
- "Failed to allocate %d MBC config texts\n",
- pdata->num_mbc_cfgs);
- return;
- }
-
- for (i = 0; i < pdata->num_mbc_cfgs; i++)
- wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name;
-
- wm8994->mbc_enum.max = pdata->num_mbc_cfgs;
- wm8994->mbc_enum.texts = wm8994->mbc_texts;
-
- ret = snd_soc_add_controls(wm8994->codec, control, 1);
- if (ret != 0)
- dev_err(wm8994->codec->dev,
- "Failed to add MBC mode controls: %d\n", ret);
- }
-
if (pdata->num_retune_mobile_cfgs)
wm8994_handle_retune_mobile_pdata(wm8994);
else
case WM8958:
snd_soc_add_controls(codec, wm8958_snd_controls,
ARRAY_SIZE(wm8958_snd_controls));
- snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets,
- ARRAY_SIZE(wm8994_lateclk_widgets));
- snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets,
- ARRAY_SIZE(wm8994_adc_widgets));
- snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets,
- ARRAY_SIZE(wm8994_dac_widgets));
snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets,
ARRAY_SIZE(wm8958_dapm_widgets));
+ if (wm8994->revision < 1) {
+ snd_soc_dapm_new_controls(dapm, wm8994_lateclk_revd_widgets,
+ ARRAY_SIZE(wm8994_lateclk_revd_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_adc_revd_widgets,
+ ARRAY_SIZE(wm8994_adc_revd_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_dac_revd_widgets,
+ ARRAY_SIZE(wm8994_dac_revd_widgets));
+ } else {
+ snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets,
+ ARRAY_SIZE(wm8994_lateclk_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets,
+ ARRAY_SIZE(wm8994_adc_widgets));
+ snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets,
+ ARRAY_SIZE(wm8994_dac_widgets));
+ }
break;
}
}
break;
case WM8958:
- snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon,
- ARRAY_SIZE(wm8994_lateclk_intercon));
- snd_soc_dapm_add_routes(dapm, wm8958_intercon,
- ARRAY_SIZE(wm8958_intercon));
+ if (wm8994->revision < 1) {
+ snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon,
+ ARRAY_SIZE(wm8994_revd_intercon));
+ snd_soc_dapm_add_routes(dapm, wm8994_lateclk_revd_intercon,
+ ARRAY_SIZE(wm8994_lateclk_revd_intercon));
+ } else {
+ snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon,
+ ARRAY_SIZE(wm8994_lateclk_intercon));
+ snd_soc_dapm_add_routes(dapm, wm8958_intercon,
+ ARRAY_SIZE(wm8958_intercon));
+ }
+
+ wm8958_dsp2_init(codec);
break;
}
free_irq(wm8994->micdet_irq, wm8994);
break;
}
+ if (wm8994->mbc)
+ release_firmware(wm8994->mbc);
+ if (wm8994->mbc_vss)
+ release_firmware(wm8994->mbc_vss);
+ if (wm8994->enh_eq)
+ release_firmware(wm8994->enh_eq);
kfree(wm8994->retune_mobile_texts);
kfree(wm8994->drc_texts);
kfree(wm8994);
#define _WM8994_H
#include <sound/soc.h>
+#include <linux/firmware.h>
+
+#include "wm_hubs.h"
/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
#define WM8994_SYSCLK_MCLK1 1
extern const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE];
extern const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE];
+int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+void wm8958_dsp2_init(struct snd_soc_codec *codec);
+
+struct wm8994_micdet {
+ struct snd_soc_jack *jack;
+ int det;
+ int shrt;
+};
+
+/* codec private data */
+struct wm8994_fll_config {
+ int src;
+ int in;
+ int out;
+};
+
+#define WM8994_NUM_DRC 3
+#define WM8994_NUM_EQ 3
+
+struct wm8994_priv {
+ struct wm_hubs_data hubs;
+ enum snd_soc_control_type control_type;
+ void *control_data;
+ struct snd_soc_codec *codec;
+ int sysclk[2];
+ int sysclk_rate[2];
+ int mclk[2];
+ int aifclk[2];
+ struct wm8994_fll_config fll[2], fll_suspend[2];
+
+ int dac_rates[2];
+ int lrclk_shared[2];
+
+ int mbc_ena[3];
+ int hpf1_ena[3];
+ int hpf2_ena[3];
+ int vss_ena[3];
+ int enh_eq_ena[3];
+
+ /* Platform dependant DRC configuration */
+ const char **drc_texts;
+ int drc_cfg[WM8994_NUM_DRC];
+ struct soc_enum drc_enum;
+
+ /* Platform dependant ReTune mobile configuration */
+ int num_retune_mobile_texts;
+ const char **retune_mobile_texts;
+ int retune_mobile_cfg[WM8994_NUM_EQ];
+ struct soc_enum retune_mobile_enum;
+
+ /* Platform dependant MBC configuration */
+ int mbc_cfg;
+ const char **mbc_texts;
+ struct soc_enum mbc_enum;
+
+ /* Platform dependant VSS configuration */
+ int vss_cfg;
+ const char **vss_texts;
+ struct soc_enum vss_enum;
+
+ /* Platform dependant VSS HPF configuration */
+ int vss_hpf_cfg;
+ const char **vss_hpf_texts;
+ struct soc_enum vss_hpf_enum;
+
+ /* Platform dependant enhanced EQ configuration */
+ int enh_eq_cfg;
+ const char **enh_eq_texts;
+ struct soc_enum enh_eq_enum;
+
+ struct wm8994_micdet micdet[2];
+
+ wm8958_micdet_cb jack_cb;
+ void *jack_cb_data;
+ int micdet_irq;
+
+ int revision;
+ struct wm8994_pdata *pdata;
+
+ unsigned int aif1clk_enable:1;
+ unsigned int aif2clk_enable:1;
+
+ unsigned int aif1clk_disable:1;
+ unsigned int aif2clk_disable:1;
+
+ int dsp_active;
+ const struct firmware *cur_fw;
+ const struct firmware *mbc;
+ const struct firmware *mbc_vss;
+ const struct firmware *enh_eq;
+};
+
#endif
static int wm8995_put_class_w(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *w = wlist->widgets[0];
struct snd_soc_codec *codec;
int ret;
- w = snd_kcontrol_chip(kcontrol);
codec = w->codec;
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
wm8995_update_class_w(codec);
* constantly enabled, we use the mutes on those inputs to simulate such
* controls.
*/
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route wm9705_audio_map[] = {
/* HP mixer */
{"HP Mixer", "PCBeep Playback Switch", "PCBEEP PGA"},
{"HP Mixer", "CD Playback Switch", "CD PGA"},
{"Right ADC", NULL, "ADC PGA"},
};
-static int wm9705_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm9705_dapm_widgets,
- ARRAY_SIZE(wm9705_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- return 0;
-}
-
/* We use a register cache to enhance read performance. */
static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
{
snd_soc_add_controls(codec, wm9705_snd_ac97_controls,
ARRAY_SIZE(wm9705_snd_ac97_controls));
- wm9705_add_widgets(codec);
return 0;
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = wm9705_reg,
+ .dapm_widgets = wm9705_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets),
+ .dapm_routes = wm9705_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9705_audio_map),
};
static __devinit int wm9705_probe(struct platform_device *pdev)
SND_SOC_DAPM_INPUT("MIC2"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route wm9712_audio_map[] = {
/* virtual mixer - mixes left & right channels for spk and mono */
{"AC97 Mixer", NULL, "Left DAC"},
{"AC97 Mixer", NULL, "Right DAC"},
{"ROUT2", NULL, "Speaker PGA"},
};
-static int wm9712_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm9712_dapm_widgets,
- ARRAY_SIZE(wm9712_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- return 0;
-}
-
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
snd_soc_add_controls(codec, wm9712_snd_ac97_controls,
ARRAY_SIZE(wm9712_snd_ac97_controls));
- wm9712_add_widgets(codec);
return 0;
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = wm9712_reg,
+ .dapm_widgets = wm9712_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
+ .dapm_routes = wm9712_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9712_audio_map),
};
static __devinit int wm9712_probe(struct platform_device *pdev)
SND_SOC_DAPM_VMID("VMID"),
};
-static const struct snd_soc_dapm_route audio_map[] = {
+static const struct snd_soc_dapm_route wm9713_audio_map[] = {
/* left HP mixer */
{"Left HP Mixer", "Beep Playback Switch", "PCBEEP"},
{"Left HP Mixer", "Voice Playback Switch", "Voice DAC"},
{"Capture Mono Mux", "Right", "Right Capture Source"},
};
-static int wm9713_add_widgets(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- snd_soc_dapm_new_controls(dapm, wm9713_dapm_widgets,
- ARRAY_SIZE(wm9713_dapm_widgets));
-
- snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
- return 0;
-}
-
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
snd_soc_add_controls(codec, wm9713_snd_ac97_controls,
ARRAY_SIZE(wm9713_snd_ac97_controls));
- wm9713_add_widgets(codec);
return 0;
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = wm9713_reg,
+ .dapm_widgets = wm9713_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),
+ .dapm_routes = wm9713_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9713_audio_map),
};
static __devinit int wm9713_probe(struct platform_device *pdev)
static const struct snd_soc_dapm_route lineout1_diff_routes[] = {
{ "LINEOUT1 Mixer", "IN1L Switch", "IN1L PGA" },
{ "LINEOUT1 Mixer", "IN1R Switch", "IN1R PGA" },
- { "LINEOUT1 Mixer", "Output Switch", "Left Output Mixer" },
+ { "LINEOUT1 Mixer", "Output Switch", "Left Output PGA" },
{ "LINEOUT1N Driver", NULL, "LINEOUT1 Mixer" },
{ "LINEOUT1P Driver", NULL, "LINEOUT1 Mixer" },
};
static const struct snd_soc_dapm_route lineout1_se_routes[] = {
- { "LINEOUT1N Mixer", "Left Output Switch", "Left Output Mixer" },
- { "LINEOUT1N Mixer", "Right Output Switch", "Left Output Mixer" },
+ { "LINEOUT1N Mixer", "Left Output Switch", "Left Output PGA" },
+ { "LINEOUT1N Mixer", "Right Output Switch", "Right Output PGA" },
- { "LINEOUT1P Mixer", "Left Output Switch", "Left Output Mixer" },
+ { "LINEOUT1P Mixer", "Left Output Switch", "Left Output PGA" },
{ "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" },
{ "LINEOUT1P Driver", NULL, "LINEOUT1P Mixer" },
static const struct snd_soc_dapm_route lineout2_diff_routes[] = {
{ "LINEOUT2 Mixer", "IN2L Switch", "IN2L PGA" },
{ "LINEOUT2 Mixer", "IN2R Switch", "IN2R PGA" },
- { "LINEOUT2 Mixer", "Output Switch", "Right Output Mixer" },
+ { "LINEOUT2 Mixer", "Output Switch", "Right Output PGA" },
{ "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" },
{ "LINEOUT2P Driver", NULL, "LINEOUT2 Mixer" },
};
static const struct snd_soc_dapm_route lineout2_se_routes[] = {
- { "LINEOUT2N Mixer", "Left Output Switch", "Left Output Mixer" },
- { "LINEOUT2N Mixer", "Right Output Switch", "Left Output Mixer" },
+ { "LINEOUT2N Mixer", "Left Output Switch", "Left Output PGA" },
+ { "LINEOUT2N Mixer", "Right Output Switch", "Right Output PGA" },
- { "LINEOUT2P Mixer", "Right Output Switch", "Right Output Mixer" },
+ { "LINEOUT2P Mixer", "Right Output Switch", "Right Output PGA" },
{ "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" },
{ "LINEOUT2P Driver", NULL, "LINEOUT2P Mixer" },
snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_3_4_VOLUME,
WM8993_IN2_VU, WM8993_IN2_VU);
+ snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_LEFT,
+ WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_RIGHT,
WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
snd_soc_update_bits(codec, WM8993_LEFT_OUTPUT_VOLUME,
- WM8993_HPOUT1L_ZC, WM8993_HPOUT1L_ZC);
+ WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC,
+ WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC);
snd_soc_update_bits(codec, WM8993_RIGHT_OUTPUT_VOLUME,
WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC,
WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC);
snd_soc_update_bits(codec, WM8993_LEFT_OPGA_VOLUME,
- WM8993_MIXOUTL_ZC, WM8993_MIXOUTL_ZC);
+ WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU,
+ WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU);
snd_soc_update_bits(codec, WM8993_RIGHT_OPGA_VOLUME,
WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU,
WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU);
mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
- mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x7 << 26));
+ mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG,
+ ACLKX | AHCLKX | AFSX);
break;
case SND_SOC_DAIFMT_CBM_CFS:
/* codec is clock master and frame slave */
- mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+ mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
- mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+ mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
- mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x2d << 26));
+ mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG,
+ ACLKX | ACLKR);
+ mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG,
+ AFSX | AFSR);
break;
case SND_SOC_DAIFMT_CBM_CFM:
/* codec is clock and frame master */
mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
- mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG, (0x3f << 26));
+ mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG,
+ ACLKX | AHCLKX | AFSX | ACLKR | AHCLKR | AFSR);
break;
default:
mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, mask);
mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, TXORD);
- if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32))
+ if ((dev->tdm_slots >= 2) && (dev->tdm_slots <= 32))
mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG,
FSXMOD(dev->tdm_slots), FSXMOD(0x1FF));
else
AHCLKRE);
mcasp_set_reg(dev->base + DAVINCI_MCASP_RXTDM_REG, mask);
- if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32))
+ if ((dev->tdm_slots >= 2) && (dev->tdm_slots <= 32))
mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG,
FSRMOD(dev->tdm_slots), FSRMOD(0x1FF));
else
dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
dma_data->asp_chan_q = pdata->asp_chan_q;
dma_data->ram_chan_q = pdata->ram_chan_q;
+ dma_data->sram_size = pdata->sram_size_playback;
dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset +
mem->start);
dma_data = &dev->dma_params[SNDRV_PCM_STREAM_CAPTURE];
dma_data->asp_chan_q = pdata->asp_chan_q;
dma_data->ram_chan_q = pdata->ram_chan_q;
+ dma_data->sram_size = pdata->sram_size_capture;
dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset +
mem->start);
if (res)
ssi->dma_params_rx.dma = res->start;
- if ((cpu_is_mx27() || cpu_is_mx21()) &&
- !(ssi->flags & IMX_SSI_USE_AC97) &&
- (ssi->flags & IMX_SSI_DMA)) {
- ssi->flags |= IMX_SSI_DMA;
- }
-
platform_set_drvdata(pdev, ssi);
ret = snd_soc_register_dai(&pdev->dev, dai);
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
uint32_t conf;
- if (!dai->active)
+ if (dai->active)
return;
conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
static int qi_lb60_spk_event(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *ctrl, int event)
{
- int on = 0;
- if (event & SND_SOC_DAPM_POST_PMU)
- on = 1;
- else if (event & SND_SOC_DAPM_PRE_PMD)
- on = 0;
+ int on = !SND_SOC_DAPM_EVENT_OFF(event);
gpio_set_value(QI_LB60_SND_GPIO, on);
gpio_set_value(QI_LB60_AMP_GPIO, on);
return ret;
}
- snd_soc_dapm_new_controls(dapm, qi_lb60_widgets,
- ARRAY_SIZE(qi_lb60_widgets));
- snd_soc_dapm_add_routes(dapm, qi_lb60_routes,
- ARRAY_SIZE(qi_lb60_routes));
- snd_soc_dapm_sync(dapm);
-
return 0;
}
.name = "QI LB60",
.dai_link = &qi_lb60_dai,
.num_links = 1,
+
+ .dapm_widgets = qi_lb60_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(qi_lb60_widgets),
+ .dapm_routes = qi_lb60_routes,
+ .num_dapm_routes = ARRAY_SIZE(qi_lb60_routes),
};
static struct platform_device *qi_lb60_snd_device;
+static const struct gpio qi_lb60_gpios[] = {
+ { QI_LB60_SND_GPIO, GPIOF_OUT_INIT_LOW, "SND" },
+ { QI_LB60_AMP_GPIO, GPIOF_OUT_INIT_LOW, "AMP" },
+};
+
static int __init qi_lb60_init(void)
{
int ret;
if (!qi_lb60_snd_device)
return -ENOMEM;
- ret = gpio_request(QI_LB60_SND_GPIO, "SND");
+ ret = gpio_request_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
if (ret) {
- pr_err("qi_lb60 snd: Failed to request SND GPIO(%d): %d\n",
- QI_LB60_SND_GPIO, ret);
+ pr_err("qi_lb60 snd: Failed to request gpios: %d\n", ret);
goto err_device_put;
}
- ret = gpio_request(QI_LB60_AMP_GPIO, "AMP");
- if (ret) {
- pr_err("qi_lb60 snd: Failed to request AMP GPIO(%d): %d\n",
- QI_LB60_AMP_GPIO, ret);
- goto err_gpio_free_snd;
- }
-
- gpio_direction_output(QI_LB60_SND_GPIO, 0);
- gpio_direction_output(QI_LB60_AMP_GPIO, 0);
-
platform_set_drvdata(qi_lb60_snd_device, &qi_lb60);
ret = platform_device_add(qi_lb60_snd_device);
err_unset_pdata:
platform_set_drvdata(qi_lb60_snd_device, NULL);
-/*err_gpio_free_amp:*/
- gpio_free(QI_LB60_AMP_GPIO);
-err_gpio_free_snd:
- gpio_free(QI_LB60_SND_GPIO);
+/*err_gpio_free_array:*/
+ gpio_free_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
err_device_put:
platform_device_put(qi_lb60_snd_device);
static void __exit qi_lb60_exit(void)
{
- gpio_free(QI_LB60_AMP_GPIO);
- gpio_free(QI_LB60_SND_GPIO);
platform_device_unregister(qi_lb60_snd_device);
+ gpio_free_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
}
module_exit(qi_lb60_exit);
return -ENOMEM;
}
stream->sstdrv_ops->vendor_id = MSIC_VENDOR_ID;
+ stream->sstdrv_ops->module_name = SST_CARD_NAMES;
/* registering with SST driver to get access to SST APIs to use */
ret_val = register_sst_card(stream->sstdrv_ops);
if (ret_val) {
pr_err("sst: sst card registration failed\n");
+ kfree(stream->sstdrv_ops);
+ kfree(stream);
return ret_val;
}
runtime->private_data = stream;
str_id = stream->stream_info.str_id;
if (str_id)
ret_val = stream->sstdrv_ops->pcm_control->close(str_id);
+ unregister_sst_card(stream->sstdrv_ops);
kfree(stream->sstdrv_ops);
kfree(stream);
return ret_val;
return 0;
}
+static int sst_platform_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
static struct snd_pcm_ops sst_platform_ops = {
.open = sst_platform_open,
.close = sst_platform_close,
.trigger = sst_platform_pcm_trigger,
.pointer = sst_platform_pcm_pointer,
.hw_params = sst_platform_pcm_hw_params,
+ .hw_free = sst_platform_pcm_hw_free,
};
static void sst_pcm_free(struct snd_pcm *pcm)
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Jarkko Nikula <jhnikula@gmail.com>
- * Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words)
* 4 channels: size is 128 / 4 = 32 frames (4 * 32 words)
*/
- if (cpu_is_omap343x() || cpu_is_omap44xx()) {
+ if (cpu_is_omap34xx() || cpu_is_omap44xx()) {
/*
* Rule for the buffer size. We should not allow
* smaller buffer than the FIFO size to avoid underruns
default:
return -EINVAL;
}
- if (cpu_is_omap343x()) {
+ if (cpu_is_omap34xx()) {
dma_data->set_threshold = omap_mcbsp_set_threshold;
/* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
if (omap_mcbsp_get_dma_op_mode(bus_id) ==
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Jarkko Nikula <jhnikula@gmail.com>
- * Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Jarkko Nikula <jhnikula@gmail.com>
- * Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME,
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
.period_bytes_min = 32,
if ((cpu_is_omap1510()))
omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ |
OMAP_DMA_LAST_IRQ | OMAP_DMA_BLOCK_IRQ);
- else
+ else if (!substream->runtime->no_period_wakeup)
omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ);
if (!(cpu_class_is_omap1())) {
* Copyright (C) 2008 Nokia Corporation
*
* Contact: Jarkko Nikula <jhnikula@gmail.com>
- * Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
*
* Copyright (C) 2008 - 2009 Nokia Corporation
*
- * Contact: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Contact: Peter Ujfalusi <peter.ujfalusi@ti.com>
* Eduardo Valentin <eduardo.valentin@nokia.com>
* Jarkko Nikula <jhnikula@gmail.com>
*
help
Say Y if you want to add support for SoC audio on Raumfeld devices
+config SND_PXA2XX_SOC_HX4700
+ tristate "SoC Audio support for HP iPAQ hx4700"
+ depends on SND_PXA2XX_SOC && MACH_H4700
+ select SND_PXA2XX_SOC_I2S
+ select SND_SOC_AK4641
+ help
+ Say Y if you want to add support for SoC audio on the
+ HP iPAQ hx4700.
+
config SND_PXA2XX_SOC_MAGICIAN
tristate "SoC Audio support for HTC Magician"
depends on SND_PXA2XX_SOC && MACH_MAGICIAN
snd-soc-saarb-objs := saarb.o
snd-soc-tavorevb3-objs := tavorevb3.o
snd-soc-zylonite-objs := zylonite.o
+snd-soc-hx4700-objs := hx4700.o
snd-soc-magician-objs := magician.o
snd-soc-mioa701-objs := mioa701_wm9713.o
snd-soc-z2-objs := z2.o
obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o
obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o
+obj-$(CONFIG_SND_PXA2XX_SOC_HX4700) += snd-soc-hx4700.o
obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o
obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o
obj-$(CONFIG_SND_PXA2XX_SOC_Z2) += snd-soc-z2.o
.cpu_dai_name = "pxa2xx-i2s",
.codec_dai_name = "wm8731-hifi",
.platform_name = "pxa-pcm-audio",
- .codec_name = "wm8731-codec.0-001b",
+ .codec_name = "wm8731.0-001b",
.init = corgi_wm8731_init,
.ops = &corgi_ops,
};
--- /dev/null
+/*
+ * SoC audio for HP iPAQ hx4700
+ *
+ * Copyright (c) 2009 Philipp Zabel
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <mach/hx4700.h>
+#include <asm/mach-types.h>
+#include "pxa2xx-i2s.h"
+
+#include "../codecs/ak4641.h"
+
+static struct snd_soc_jack hs_jack;
+
+/* Headphones jack detection DAPM pin */
+static struct snd_soc_jack_pin hs_jack_pin[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Speaker",
+ /* disable speaker when hp jack is inserted */
+ .mask = SND_JACK_HEADPHONE,
+ .invert = 1,
+ },
+};
+
+/* Headphones jack detection GPIO */
+static struct snd_soc_jack_gpio hs_jack_gpio = {
+ .gpio = GPIO75_HX4700_EARPHONE_nDET,
+ .invert = true,
+ .name = "hp-gpio",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 200,
+};
+
+/*
+ * iPAQ hx4700 uses I2S for capture and playback.
+ */
+static int hx4700_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set the I2S system clock as output */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+ SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ return ret;
+
+ /* inform codec driver about clock freq *
+ * (PXA I2S always uses divider 256) */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 256 * params_rate(params),
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops hx4700_ops = {
+ .hw_params = hx4700_hw_params,
+};
+
+static int hx4700_spk_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ gpio_set_value(GPIO107_HX4700_SPK_nSD, !!SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+}
+
+static int hx4700_hp_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ gpio_set_value(GPIO92_HX4700_HP_DRIVER, !!SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+}
+
+/* hx4700 machine dapm widgets */
+static const struct snd_soc_dapm_widget hx4700_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", hx4700_hp_power),
+ SND_SOC_DAPM_SPK("Speaker", hx4700_spk_power),
+ SND_SOC_DAPM_MIC("Built-in Microphone", NULL),
+};
+
+/* hx4700 machine audio_map */
+static const struct snd_soc_dapm_route hx4700_audio_map[] = {
+
+ /* Headphone connected to LOUT, ROUT */
+ {"Headphone Jack", NULL, "LOUT"},
+ {"Headphone Jack", NULL, "ROUT"},
+
+ /* Speaker connected to MOUT2 */
+ {"Speaker", NULL, "MOUT2"},
+
+ /* Microphone connected to MICIN */
+ {"MICIN", NULL, "Built-in Microphone"},
+ {"AIN", NULL, "MICOUT"},
+};
+
+/*
+ * Logic for a ak4641 as connected on a HP iPAQ hx4700
+ */
+static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int err;
+
+ /* NC codec pins */
+ /* FIXME: is anything connected here? */
+ snd_soc_dapm_nc_pin(dapm, "MOUT1");
+ snd_soc_dapm_nc_pin(dapm, "MICEXT");
+ snd_soc_dapm_nc_pin(dapm, "AUX");
+
+ /* Jack detection API stuff */
+ err = snd_soc_jack_new(codec, "Headphone Jack",
+ SND_JACK_HEADPHONE, &hs_jack);
+ if (err)
+ return err;
+
+ err = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pin),
+ hs_jack_pin);
+ if (err)
+ return err;
+
+ err = snd_soc_jack_add_gpios(&hs_jack, 1, &hs_jack_gpio);
+
+ return err;
+}
+
+/* hx4700 digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link hx4700_dai = {
+ .name = "ak4641",
+ .stream_name = "AK4641",
+ .cpu_dai_name = "pxa2xx-i2s",
+ .codec_dai_name = "ak4641-hifi",
+ .platform_name = "pxa-pcm-audio",
+ .codec_name = "ak4641.0-0012",
+ .init = hx4700_ak4641_init,
+ .ops = &hx4700_ops,
+};
+
+/* hx4700 audio machine driver */
+static struct snd_soc_card snd_soc_card_hx4700 = {
+ .name = "iPAQ hx4700",
+ .dai_link = &hx4700_dai,
+ .num_links = 1,
+ .dapm_widgets = hx4700_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hx4700_dapm_widgets),
+ .dapm_routes = hx4700_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(hx4700_audio_map),
+};
+
+static struct gpio hx4700_audio_gpios[] = {
+ { GPIO107_HX4700_SPK_nSD, GPIOF_OUT_INIT_HIGH, "SPK_POWER" },
+ { GPIO92_HX4700_HP_DRIVER, GPIOF_OUT_INIT_LOW, "EP_POWER" },
+};
+
+static int __devinit hx4700_audio_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ if (!machine_is_h4700())
+ return -ENODEV;
+
+ ret = gpio_request_array(hx4700_audio_gpios,
+ ARRAY_SIZE(hx4700_audio_gpios));
+ if (ret)
+ return ret;
+
+ snd_soc_card_hx4700.dev = &pdev->dev;
+ ret = snd_soc_register_card(&snd_soc_card_hx4700);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int __devexit hx4700_audio_remove(struct platform_device *pdev)
+{
+ snd_soc_jack_free_gpios(&hs_jack, 1, &hs_jack_gpio);
+ snd_soc_unregister_card(&snd_soc_card_hx4700);
+
+ gpio_set_value(GPIO92_HX4700_HP_DRIVER, 0);
+ gpio_set_value(GPIO107_HX4700_SPK_nSD, 0);
+
+ gpio_free_array(hx4700_audio_gpios, ARRAY_SIZE(hx4700_audio_gpios));
+ return 0;
+}
+
+static struct platform_driver hx4700_audio_driver = {
+ .driver = {
+ .name = "hx4700-audio",
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = hx4700_audio_probe,
+ .remove = __devexit_p(hx4700_audio_remove),
+};
+
+static int __init hx4700_modinit(void)
+{
+ return platform_driver_register(&hx4700_audio_driver);
+}
+module_init(hx4700_modinit);
+
+static void __exit hx4700_modexit(void)
+{
+ platform_driver_unregister(&hx4700_audio_driver);
+}
+
+module_exit(hx4700_modexit);
+
+MODULE_AUTHOR("Philipp Zabel");
+MODULE_DESCRIPTION("ALSA SoC iPAQ hx4700");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:hx4700-audio");
.cpu_dai_name = "pxa2xx-i2s",
.codec_dai_name = "wm8731-hifi",
.platform_name = "pxa-pcm-audio",
- .codec_name = "wm8731-codec.0-001b",
+ .codec_name = "wm8731.0-001b",
.init = poodle_wm8731_init,
.ops = &poodle_ops,
};
static int spitz_jack_func;
static int spitz_spk_func;
+static int spitz_mic_gpio;
static void spitz_ext_control(struct snd_soc_codec *codec)
{
static int spitz_mic_bias(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- if (machine_is_borzoi() || machine_is_spitz())
- gpio_set_value(SPITZ_GPIO_MIC_BIAS,
- SND_SOC_DAPM_EVENT_ON(event));
-
- if (machine_is_akita())
- gpio_set_value(AKITA_GPIO_MIC_BIAS,
- SND_SOC_DAPM_EVENT_ON(event));
-
+ gpio_set_value_cansleep(spitz_mic_gpio, SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita()))
return -ENODEV;
+ if (machine_is_borzoi() || machine_is_spitz())
+ spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS;
+ else
+ spitz_mic_gpio = AKITA_GPIO_MIC_BIAS;
+
+ ret = gpio_request(spitz_mic_gpio, "MIC GPIO");
+ if (ret)
+ goto err1;
+
+ ret = gpio_direction_output(spitz_mic_gpio, 0);
+ if (ret)
+ goto err2;
+
spitz_snd_device = platform_device_alloc("soc-audio", -1);
- if (!spitz_snd_device)
- return -ENOMEM;
+ if (!spitz_snd_device) {
+ ret = -ENOMEM;
+ goto err2;
+ }
platform_set_drvdata(spitz_snd_device, &snd_soc_spitz);
- ret = platform_device_add(spitz_snd_device);
+ ret = platform_device_add(spitz_snd_device);
if (ret)
- platform_device_put(spitz_snd_device);
+ goto err3;
+
+ return 0;
+err3:
+ platform_device_put(spitz_snd_device);
+err2:
+ gpio_free(spitz_mic_gpio);
+err1:
return ret;
}
static void __exit spitz_exit(void)
{
platform_device_unregister(spitz_snd_device);
+ gpio_free(spitz_mic_gpio);
}
module_init(spitz_init);
select SND_SAMSUNG_SPDIF
help
Say Y if you want to add support for SoC S/PDIF audio on the SMDK.
+
+config SND_SOC_SMDK_WM8580_PCM
+ tristate "SoC PCM Audio support for WM8580 on SMDK"
+ depends on SND_SOC_SAMSUNG && (MACH_SMDK6450 || MACH_SMDKV210 || MACH_SMDKC110)
+ select SND_SOC_WM8580
+ select SND_SAMSUNG_PCM
+ help
+ Say Y if you want to add support for SoC audio on the SMDK.
+
+config SND_SOC_SPEYSIDE
+ tristate "Audio support for Wolfson Speyside"
+ depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410
+ select SND_SAMSUNG_I2S
+ select SND_SOC_WM8915
+ select SND_SOC_WM9081
snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o
snd-soc-goni-wm8994-objs := goni_wm8994.o
snd-soc-smdk-spdif-objs := smdk_spdif.o
+snd-soc-smdk-wm8580pcm-objs := smdk_wm8580pcm.o
+snd-soc-speyside-objs := speyside.o
obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
obj-$(CONFIG_SND_SOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.o
obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_SPDIF) += snd-soc-smdk-spdif.o
obj-$(CONFIG_SND_SOC_GONI_AQUILA_WM8994) += snd-soc-goni-wm8994.o
+obj-$(CONFIG_SND_SOC_SMDK_WM8580_PCM) += snd-soc-smdk-wm8580pcm.o
+obj-$(CONFIG_SND_SOC_SPEYSIDE) += snd-soc-speyside.o
.name = "WM8994",
.stream_name = "WM8994 HiFi",
.cpu_dai_name = "samsung-i2s.0",
- .codec_dai_name = "wm8994-hifi",
+ .codec_dai_name = "wm8994-aif1",
.platform_name = "samsung-audio",
- .codec_name = "wm8994-codec.0-0x1a",
+ .codec_name = "wm8994-codec.0-001a",
.init = goni_wm8994_init,
.ops = &goni_hifi_ops,
}, {
.name = "WM8994 Voice",
.stream_name = "Voice",
.cpu_dai_name = "goni-voice-dai",
- .codec_dai_name = "wm8994-voice",
- .platform_name = "samsung-audio",
- .codec_name = "wm8994-codec.0-0x1a",
+ .codec_dai_name = "wm8994-aif2",
+ .codec_name = "wm8994-codec.0-001a",
.ops = &goni_voice_ops,
},
};
{ /* Voice via BT */
.name = "Bluetooth",
.stream_name = "Voice",
- .platform_name = "samsung-audio",
.cpu_dai_name = "dfbmcs320-pcm",
.codec_dai_name = "wm8753-voice",
.codec_name = "wm8753-codec.0-001a",
--- /dev/null
+/*
+ * sound/soc/samsung/smdk_wm8580pcm.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co. Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/pcm.h>
+
+#include <asm/mach-types.h>
+
+#include "../codecs/wm8580.h"
+#include "dma.h"
+#include "pcm.h"
+
+/*
+ * Board Settings:
+ * o '1' means 'ON'
+ * o '0' means 'OFF'
+ * o 'X' means 'Don't care'
+ *
+ * SMDK6410, SMDK6440, SMDK6450 Base B/D: CFG1-0000, CFG2-1111
+ * SMDKC110, SMDKV210: CFGB11-100100, CFGB12-0000
+ */
+
+#define SMDK_WM8580_EXT_OSC 12000000
+#define SMDK_WM8580_EXT_MCLK 4096000
+#define SMDK_WM8580_EXT_VOICE 2048000
+
+static unsigned long mclk_freq;
+static unsigned long xtal_freq;
+
+/*
+ * If MCLK clock directly gets from XTAL, we don't have to use PLL
+ * to make MCLK, but if XTAL clock source connects with other codec
+ * pin (like XTI), we should have to set codec's PLL to make MCLK.
+ * Because Samsung SoC does not support pcmcdclk output like I2S.
+ */
+
+static int smdk_wm8580_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int rfs, ret;
+
+ switch (params_rate(params)) {
+ case 8000:
+ break;
+ default:
+ printk(KERN_ERR "%s:%d Sampling Rate %u not supported!\n",
+ __func__, __LINE__, params_rate(params));
+ return -EINVAL;
+ }
+
+ rfs = mclk_freq / params_rate(params) / 2;
+
+ /* Set the codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B
+ | SND_SOC_DAIFMT_IB_NF
+ | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* Set the cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_B
+ | SND_SOC_DAIFMT_IB_NF
+ | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ if (mclk_freq == xtal_freq) {
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_MCLK,
+ mclk_freq, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
+ WM8580_CLKSRC_MCLK);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_PLLA,
+ mclk_freq, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
+ WM8580_CLKSRC_PLLA);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0,
+ xtal_freq, mclk_freq);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Set PCM source clock on CPU */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, S3C_PCM_CLKSRC_MUX,
+ mclk_freq, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* Set SCLK_DIV for making bclk */
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_PCM_SCLK_PER_FS, rfs);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops smdk_wm8580_pcm_ops = {
+ .hw_params = smdk_wm8580_pcm_hw_params,
+};
+
+static struct snd_soc_dai_link smdk_dai[] = {
+ {
+ .name = "WM8580 PAIF PCM RX",
+ .stream_name = "Playback",
+ .cpu_dai_name = "samsung-pcm.0",
+ .codec_dai_name = "wm8580-hifi-playback",
+ .platform_name = "samsung-audio",
+ .codec_name = "wm8580-codec.0-001b",
+ .ops = &smdk_wm8580_pcm_ops,
+ }, {
+ .name = "WM8580 PAIF PCM TX",
+ .stream_name = "Capture",
+ .cpu_dai_name = "samsung-pcm.0",
+ .codec_dai_name = "wm8580-hifi-capture",
+ .platform_name = "samsung-audio",
+ .codec_name = "wm8580-codec.0-001b",
+ .ops = &smdk_wm8580_pcm_ops,
+ },
+};
+
+static struct snd_soc_card smdk_pcm = {
+ .name = "SMDK-PCM",
+ .dai_link = smdk_dai,
+ .num_links = 2,
+};
+
+/*
+ * After SMDKC110 Base Board's Rev is '0.1', 12MHz External OSC(X1)
+ * is absent (or not connected), so we connect EXT_VOICE_CLK(OSC4),
+ * 2.0484Mhz, directly with MCLK both Codec and SoC.
+ */
+static int __devinit snd_smdk_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ xtal_freq = SMDK_WM8580_EXT_OSC;
+ mclk_freq = SMDK_WM8580_EXT_MCLK;
+
+ if (machine_is_smdkc110() || machine_is_smdkv210())
+ xtal_freq = mclk_freq = SMDK_WM8580_EXT_VOICE;
+
+ smdk_pcm.dev = &pdev->dev;
+ ret = snd_soc_register_card(&smdk_pcm);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devexit snd_smdk_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_card(&smdk_pcm);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static struct platform_driver snd_smdk_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "samsung-smdk-pcm",
+ },
+ .probe = snd_smdk_probe,
+ .remove = __devexit_p(snd_smdk_remove),
+};
+
+static int __init smdk_audio_init(void)
+{
+ return platform_driver_register(&snd_smdk_driver);
+}
+
+module_init(smdk_audio_init);
+
+static void __exit smdk_audio_exit(void)
+{
+ platform_driver_unregister(&snd_smdk_driver);
+}
+
+module_exit(smdk_audio_exit);
+
+MODULE_AUTHOR("Sangbeom Kim, <sbkim73@samsung.com>");
+MODULE_DESCRIPTION("ALSA SoC SMDK WM8580 for PCM");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Speyside audio support
+ *
+ * Copyright 2011 Wolfson Microelectronics
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <linux/gpio.h>
+
+#include "../codecs/wm8915.h"
+#include "../codecs/wm9081.h"
+
+#define WM8915_HPSEL_GPIO 214
+
+static int speyside_set_bias_level(struct snd_soc_card *card,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_MCLK1,
+ 32768, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, WM8915_FLL_MCLK1,
+ 0, 0, 0);
+ if (ret < 0) {
+ pr_err("Failed to stop FLL\n");
+ return ret;
+ }
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int speyside_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, WM8915_FLL_MCLK1,
+ 32768, 256 * 48000);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_FLL,
+ 256 * 48000, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops speyside_ops = {
+ .hw_params = speyside_hw_params,
+};
+
+static struct snd_soc_jack speyside_headset;
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin speyside_headset_pins[] = {
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+/* Default the headphone selection to active high */
+static int speyside_jack_polarity;
+
+static int speyside_get_micbias(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ if (speyside_jack_polarity && (strcmp(source->name, "MICB1") == 0))
+ return 1;
+ if (!speyside_jack_polarity && (strcmp(source->name, "MICB2") == 0))
+ return 1;
+
+ return 0;
+}
+
+static void speyside_set_polarity(struct snd_soc_codec *codec,
+ int polarity)
+{
+ speyside_jack_polarity = !polarity;
+ gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity);
+
+ /* Re-run DAPM to make sure we're using the correct mic bias */
+ snd_soc_dapm_sync(&codec->dapm);
+}
+
+static int speyside_wm8915_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = rtd->codec_dai;
+ struct snd_soc_codec *codec = rtd->codec;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK1, 32768, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = gpio_request(WM8915_HPSEL_GPIO, "HP_SEL");
+ if (ret != 0)
+ pr_err("Failed to request HP_SEL GPIO: %d\n", ret);
+ gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity);
+
+ ret = snd_soc_jack_new(codec, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &speyside_headset);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_jack_add_pins(&speyside_headset,
+ ARRAY_SIZE(speyside_headset_pins),
+ speyside_headset_pins);
+ if (ret)
+ return ret;
+
+ wm8915_detect(codec, &speyside_headset, speyside_set_polarity);
+
+ return 0;
+}
+
+static int speyside_late_probe(struct snd_soc_card *card)
+{
+ snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "Headset Mic");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "Main AMIC");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "Main DMIC");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Output");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Input");
+
+ return 0;
+}
+
+static struct snd_soc_dai_link speyside_dai[] = {
+ {
+ .name = "CPU",
+ .stream_name = "CPU",
+ .cpu_dai_name = "samsung-i2s.0",
+ .codec_dai_name = "wm8915-aif1",
+ .platform_name = "samsung-audio",
+ .codec_name = "wm8915.1-001a",
+ .init = speyside_wm8915_init,
+ .ops = &speyside_ops,
+ },
+ {
+ .name = "Baseband",
+ .stream_name = "Baseband",
+ .cpu_dai_name = "wm8915-aif2",
+ .codec_dai_name = "wm1250-ev1",
+ .codec_name = "wm1250-ev1.1-0027",
+ .ops = &speyside_ops,
+ .ignore_suspend = 1,
+ },
+};
+
+static int speyside_wm9081_init(struct snd_soc_dapm_context *dapm)
+{
+ snd_soc_dapm_nc_pin(dapm, "LINEOUT");
+
+ /* At any time the WM9081 is active it will have this clock */
+ return snd_soc_codec_set_sysclk(dapm->codec, WM9081_SYSCLK_MCLK,
+ 48000 * 256, 0);
+}
+
+static struct snd_soc_aux_dev speyside_aux_dev[] = {
+ {
+ .name = "wm9081",
+ .codec_name = "wm9081.1-006c",
+ .init = speyside_wm9081_init,
+ },
+};
+
+static struct snd_soc_codec_conf speyside_codec_conf[] = {
+ {
+ .dev_name = "wm9081.1-006c",
+ .name_prefix = "Sub",
+ },
+};
+
+static const struct snd_kcontrol_new controls[] = {
+ SOC_DAPM_PIN_SWITCH("Main Speaker"),
+ SOC_DAPM_PIN_SWITCH("Main DMIC"),
+ SOC_DAPM_PIN_SWITCH("Main AMIC"),
+ SOC_DAPM_PIN_SWITCH("WM1250 Input"),
+ SOC_DAPM_PIN_SWITCH("WM1250 Output"),
+};
+
+static struct snd_soc_dapm_widget widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+
+ SND_SOC_DAPM_SPK("Main Speaker", NULL),
+
+ SND_SOC_DAPM_MIC("Main AMIC", NULL),
+ SND_SOC_DAPM_MIC("Main DMIC", NULL),
+};
+
+static struct snd_soc_dapm_route audio_paths[] = {
+ { "IN1RN", NULL, "MICB1" },
+ { "IN1RP", NULL, "MICB1" },
+ { "IN1RN", NULL, "MICB2" },
+ { "IN1RP", NULL, "MICB2" },
+ { "MICB1", NULL, "Headset Mic", speyside_get_micbias },
+ { "MICB2", NULL, "Headset Mic", speyside_get_micbias },
+
+ { "IN1LP", NULL, "MICB2" },
+ { "IN1RN", NULL, "MICB1" },
+ { "MICB2", NULL, "Main AMIC" },
+
+ { "DMIC1DAT", NULL, "MICB1" },
+ { "DMIC2DAT", NULL, "MICB1" },
+ { "MICB1", NULL, "Main DMIC" },
+
+ { "Headphone", NULL, "HPOUT1L" },
+ { "Headphone", NULL, "HPOUT1R" },
+
+ { "Sub IN1", NULL, "HPOUT2L" },
+ { "Sub IN2", NULL, "HPOUT2R" },
+
+ { "Main Speaker", NULL, "Sub SPKN" },
+ { "Main Speaker", NULL, "Sub SPKP" },
+ { "Main Speaker", NULL, "SPKDAT" },
+};
+
+static struct snd_soc_card speyside = {
+ .name = "Speyside",
+ .dai_link = speyside_dai,
+ .num_links = ARRAY_SIZE(speyside_dai),
+ .aux_dev = speyside_aux_dev,
+ .num_aux_devs = ARRAY_SIZE(speyside_aux_dev),
+ .codec_conf = speyside_codec_conf,
+ .num_configs = ARRAY_SIZE(speyside_codec_conf),
+
+ .set_bias_level = speyside_set_bias_level,
+
+ .controls = controls,
+ .num_controls = ARRAY_SIZE(controls),
+ .dapm_widgets = widgets,
+ .num_dapm_widgets = ARRAY_SIZE(widgets),
+ .dapm_routes = audio_paths,
+ .num_dapm_routes = ARRAY_SIZE(audio_paths),
+
+ .late_probe = speyside_late_probe,
+};
+
+static __devinit int speyside_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &speyside;
+ int ret;
+
+ card->dev = &pdev->dev;
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devexit speyside_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
+
+ return 0;
+}
+
+static struct platform_driver speyside_driver = {
+ .driver = {
+ .name = "speyside",
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = speyside_probe,
+ .remove = __devexit_p(speyside_remove),
+};
+
+static int __init speyside_audio_init(void)
+{
+ return platform_driver_register(&speyside_driver);
+}
+module_init(speyside_audio_init);
+
+static void __exit speyside_audio_exit(void)
+{
+ platform_driver_unregister(&speyside_driver);
+}
+module_exit(speyside_audio_exit);
+
+MODULE_DESCRIPTION("Speyside audio support");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:speyside");
#define SE (1 << 0) /* Fix the master clock */
/* CLK_RST */
-#define B_CLK 0x00000010
-#define A_CLK 0x00000001
+#define CRB (1 << 4)
+#define CRA (1 << 0)
/* IO SHIFT / MACRO */
#define BI_SHIFT 12
void __iomem *base;
struct fsi_master *master;
- int chan_num;
struct fsi_stream playback;
struct fsi_stream capture;
+ int chan_num:16;
+ int clk_master:1;
+
long rate;
+
+ /* for suspend/resume */
+ u32 saved_do_fmt;
+ u32 saved_di_fmt;
+ u32 saved_ckg1;
+ u32 saved_ckg2;
+ u32 saved_out_sel;
};
struct fsi_core {
struct fsi_core *core;
struct sh_fsi_platform_info *info;
spinlock_t lock;
+
+ /* for suspend/resume */
+ u32 saved_a_mclk;
+ u32 saved_b_mclk;
+ u32 saved_iemsk;
+ u32 saved_imsk;
+ u32 saved_clk_rst;
+ u32 saved_soft_rst;
};
/*
return fsi->master;
}
+static int fsi_is_clk_master(struct fsi_priv *fsi)
+{
+ return fsi->clk_master;
+}
+
static int fsi_is_port_a(struct fsi_priv *fsi)
{
return fsi->master->base == fsi->base;
}
/*
- * ctrl function
+ * clock function
*/
+#define fsi_module_init(m, d) __fsi_module_clk_ctrl(m, d, 1)
+#define fsi_module_kill(m, d) __fsi_module_clk_ctrl(m, d, 0)
+static void __fsi_module_clk_ctrl(struct fsi_master *master,
+ struct device *dev,
+ int enable)
+{
+ pm_runtime_get_sync(dev);
+
+ if (enable) {
+ /* enable only SR */
+ fsi_master_mask_set(master, SOFT_RST, FSISR, FSISR);
+ fsi_master_mask_set(master, SOFT_RST, PASR | PBSR, 0);
+ } else {
+ /* clear all registers */
+ fsi_master_mask_set(master, SOFT_RST, FSISR, 0);
+ }
-static void fsi_clk_ctrl(struct fsi_priv *fsi, int enable)
+ pm_runtime_put_sync(dev);
+}
+
+#define fsi_port_start(f) __fsi_port_clk_ctrl(f, 1)
+#define fsi_port_stop(f) __fsi_port_clk_ctrl(f, 0)
+static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int enable)
{
- u32 val = fsi_is_port_a(fsi) ? (1 << 0) : (1 << 4);
struct fsi_master *master = fsi_get_master(fsi);
+ u32 soft = fsi_is_port_a(fsi) ? PASR : PBSR;
+ u32 clk = fsi_is_port_a(fsi) ? CRA : CRB;
+ int is_master = fsi_is_clk_master(fsi);
- if (enable)
- fsi_master_mask_set(master, CLK_RST, val, val);
- else
- fsi_master_mask_set(master, CLK_RST, val, 0);
+ fsi_master_mask_set(master, SOFT_RST, soft, (enable) ? soft : 0);
+ if (is_master)
+ fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
}
+/*
+ * ctrl function
+ */
static void fsi_fifo_init(struct fsi_priv *fsi,
int is_play,
struct snd_soc_dai *dai)
}
}
-static void fsi_soft_all_reset(struct fsi_master *master)
-{
- /* port AB reset */
- fsi_master_mask_set(master, SOFT_RST, PASR | PBSR, 0);
- mdelay(10);
-
- /* soft reset */
- fsi_master_mask_set(master, SOFT_RST, FSISR, 0);
- fsi_master_mask_set(master, SOFT_RST, FSISR, FSISR);
- mdelay(10);
-}
-
static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
{
struct snd_pcm_runtime *runtime;
struct fsi_priv *fsi = fsi_get_priv(substream);
int is_play = fsi_is_play(substream);
struct fsi_master *master = fsi_get_master(fsi);
- set_rate_func set_rate;
+ set_rate_func set_rate = fsi_get_info_set_rate(master);
fsi_irq_disable(fsi, is_play);
- fsi_clk_ctrl(fsi, 0);
- set_rate = fsi_get_info_set_rate(master);
- if (set_rate && fsi->rate)
+ if (fsi_is_clk_master(fsi))
set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0);
+
fsi->rate = 0;
pm_runtime_put_sync(dai->dev);
frames_to_bytes(runtime, runtime->period_size));
ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi);
fsi_irq_enable(fsi, is_play);
+ fsi_port_start(fsi);
break;
case SNDRV_PCM_TRIGGER_STOP:
+ fsi_port_stop(fsi);
fsi_irq_disable(fsi, is_play);
fsi_stream_pop(fsi, is_play);
break;
static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai);
+ struct fsi_master *master = fsi_get_master(fsi);
+ set_rate_func set_rate = fsi_get_info_set_rate(master);
u32 flags = fsi_get_info_flags(fsi);
u32 data = 0;
int ret;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
data = DIMD | DOMD;
+ fsi->clk_master = 1;
break;
case SND_SOC_DAIFMT_CBS_CFS:
break;
ret = -EINVAL;
goto set_fmt_exit;
}
+
+ if (fsi_is_clk_master(fsi) && !set_rate) {
+ dev_err(dai->dev, "platform doesn't have set_rate\n");
+ ret = -EINVAL;
+ goto set_fmt_exit;
+ }
+
fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data);
/* set format */
{
struct fsi_priv *fsi = fsi_get_priv(substream);
struct fsi_master *master = fsi_get_master(fsi);
- set_rate_func set_rate;
+ set_rate_func set_rate = fsi_get_info_set_rate(master);
int fsi_ver = master->core->ver;
long rate = params_rate(params);
int ret;
- set_rate = fsi_get_info_set_rate(master);
- if (!set_rate)
+ if (!fsi_is_clk_master(fsi))
return 0;
ret = set_rate(dai->dev, fsi_is_port_a(fsi), rate, 1);
fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
udelay(10);
- fsi_clk_ctrl(fsi, 1);
ret = 0;
}
pm_runtime_enable(&pdev->dev);
dev_set_drvdata(&pdev->dev, master);
- pm_runtime_get_sync(&pdev->dev);
- fsi_soft_all_reset(master);
- pm_runtime_put_sync(&pdev->dev);
+ fsi_module_init(master, &pdev->dev);
ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED,
id_entry->name, master);
master = dev_get_drvdata(&pdev->dev);
+ fsi_module_kill(master, &pdev->dev);
+
free_irq(master->irq, master);
pm_runtime_disable(&pdev->dev);
return 0;
}
+static void __fsi_suspend(struct fsi_priv *fsi,
+ struct device *dev,
+ set_rate_func set_rate)
+{
+ fsi->saved_do_fmt = fsi_reg_read(fsi, DO_FMT);
+ fsi->saved_di_fmt = fsi_reg_read(fsi, DI_FMT);
+ fsi->saved_ckg1 = fsi_reg_read(fsi, CKG1);
+ fsi->saved_ckg2 = fsi_reg_read(fsi, CKG2);
+ fsi->saved_out_sel = fsi_reg_read(fsi, OUT_SEL);
+
+ if (fsi_is_clk_master(fsi))
+ set_rate(dev, fsi_is_port_a(fsi), fsi->rate, 0);
+}
+
+static void __fsi_resume(struct fsi_priv *fsi,
+ struct device *dev,
+ set_rate_func set_rate)
+{
+ fsi_reg_write(fsi, DO_FMT, fsi->saved_do_fmt);
+ fsi_reg_write(fsi, DI_FMT, fsi->saved_di_fmt);
+ fsi_reg_write(fsi, CKG1, fsi->saved_ckg1);
+ fsi_reg_write(fsi, CKG2, fsi->saved_ckg2);
+ fsi_reg_write(fsi, OUT_SEL, fsi->saved_out_sel);
+
+ if (fsi_is_clk_master(fsi))
+ set_rate(dev, fsi_is_port_a(fsi), fsi->rate, 1);
+}
+
+static int fsi_suspend(struct device *dev)
+{
+ struct fsi_master *master = dev_get_drvdata(dev);
+ set_rate_func set_rate = fsi_get_info_set_rate(master);
+
+ pm_runtime_get_sync(dev);
+
+ __fsi_suspend(&master->fsia, dev, set_rate);
+ __fsi_suspend(&master->fsib, dev, set_rate);
+
+ master->saved_a_mclk = fsi_core_read(master, a_mclk);
+ master->saved_b_mclk = fsi_core_read(master, b_mclk);
+ master->saved_iemsk = fsi_core_read(master, iemsk);
+ master->saved_imsk = fsi_core_read(master, imsk);
+ master->saved_clk_rst = fsi_master_read(master, CLK_RST);
+ master->saved_soft_rst = fsi_master_read(master, SOFT_RST);
+
+ fsi_module_kill(master, dev);
+
+ pm_runtime_put_sync(dev);
+
+ return 0;
+}
+
+static int fsi_resume(struct device *dev)
+{
+ struct fsi_master *master = dev_get_drvdata(dev);
+ set_rate_func set_rate = fsi_get_info_set_rate(master);
+
+ pm_runtime_get_sync(dev);
+
+ fsi_module_init(master, dev);
+
+ fsi_master_mask_set(master, SOFT_RST, 0xffff, master->saved_soft_rst);
+ fsi_master_mask_set(master, CLK_RST, 0xffff, master->saved_clk_rst);
+ fsi_core_mask_set(master, a_mclk, 0xffff, master->saved_a_mclk);
+ fsi_core_mask_set(master, b_mclk, 0xffff, master->saved_b_mclk);
+ fsi_core_mask_set(master, iemsk, 0xffff, master->saved_iemsk);
+ fsi_core_mask_set(master, imsk, 0xffff, master->saved_imsk);
+
+ __fsi_resume(&master->fsia, dev, set_rate);
+ __fsi_resume(&master->fsib, dev, set_rate);
+
+ pm_runtime_put_sync(dev);
+
+ return 0;
+}
+
static int fsi_runtime_nop(struct device *dev)
{
/* Runtime PM callback shared between ->runtime_suspend()
}
static struct dev_pm_ops fsi_pm_ops = {
+ .suspend = fsi_suspend,
+ .resume = fsi_resume,
.runtime_suspend = fsi_runtime_nop,
.runtime_resume = fsi_runtime_nop,
};
#include <trace/events/asoc.h>
-static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
- unsigned int reg)
+#ifdef CONFIG_SPI_MASTER
+static int do_spi_write(void *control, const char *data, int len)
{
+ struct spi_device *spi = control;
int ret;
- unsigned int val;
-
- if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
-
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
- }
- ret = snd_soc_cache_read(codec, reg, &val);
+ ret = spi_write(spi, data, len);
if (ret < 0)
- return -1;
- return val;
+ return ret;
+
+ return len;
}
+#endif
-static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
+static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value, const void *data, int len)
{
- u8 data[2];
int ret;
- data[0] = (reg << 4) | ((value >> 8) & 0x000f);
- data[1] = value & 0x00ff;
-
if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
+ reg < codec->driver->reg_cache_size &&
+ !codec->cache_bypass) {
ret = snd_soc_cache_write(codec, reg, value);
if (ret < 0)
return -1;
return 0;
}
- ret = codec->hw_write(codec->control_data, data, 2);
- if (ret == 2)
+ ret = codec->hw_write(codec->control_data, data, len);
+ if (ret == len)
return 0;
if (ret < 0)
return ret;
return -EIO;
}
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_4_12_spi_write(void *control_data, const char *data,
- int len)
-{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[2];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[1];
- msg[1] = data[0];
-
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
-}
-#else
-#define snd_soc_4_12_spi_write NULL
-#endif
-
-static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
- unsigned int reg)
+static unsigned int do_hw_read(struct snd_soc_codec *codec, unsigned int reg)
{
int ret;
unsigned int val;
if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
+ snd_soc_codec_volatile_register(codec, reg) ||
+ codec->cache_bypass) {
+ if (codec->cache_only)
+ return -1;
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
+ BUG_ON(!codec->hw_read);
+ return codec->hw_read(codec, reg);
}
ret = snd_soc_cache_read(codec, reg, &val);
return val;
}
-static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
+static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
+ unsigned int reg)
{
- u8 data[2];
- int ret;
-
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret < 0)
- return -1;
- }
-
- if (codec->cache_only) {
- codec->cache_sync = 1;
- return 0;
- }
-
- ret = codec->hw_write(codec->control_data, data, 2);
- if (ret == 2)
- return 0;
- if (ret < 0)
- return ret;
- else
- return -EIO;
+ return do_hw_read(codec, reg);
}
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_7_9_spi_write(void *control_data, const char *data,
- int len)
+static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[2];
+ u16 data;
- if (len <= 0)
- return 0;
+ data = cpu_to_be16((reg << 12) | (value & 0xffffff));
- msg[0] = data[0];
- msg[1] = data[1];
+ return do_hw_write(codec, reg, value, &data, 2);
+}
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
+static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ return do_hw_read(codec, reg);
+}
- t.tx_buf = &msg[0];
- t.len = len;
+static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+ data[1] = value & 0x00ff;
- return len;
+ return do_hw_write(codec, reg, value, data, 2);
}
-#else
-#define snd_soc_7_9_spi_write NULL
-#endif
static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[2];
- int ret;
reg &= 0xff;
data[0] = reg;
data[1] = value & 0xff;
- if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret < 0)
- return -1;
- }
-
- if (codec->cache_only) {
- codec->cache_sync = 1;
- return 0;
- }
-
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
+ return do_hw_write(codec, reg, value, data, 2);
}
static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec,
unsigned int reg)
{
- int ret;
- unsigned int val;
-
- reg &= 0xff;
- if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
-
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
- }
-
- ret = snd_soc_cache_read(codec, reg, &val);
- if (ret < 0)
- return -1;
- return val;
-}
-
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_8_8_spi_write(void *control_data, const char *data,
- int len)
-{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[2];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[0];
- msg[1] = data[1];
-
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
+ return do_hw_read(codec, reg);
}
-#else
-#define snd_soc_8_8_spi_write NULL
-#endif
static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[3];
- int ret;
data[0] = reg;
data[1] = (value >> 8) & 0xff;
data[2] = value & 0xff;
- if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret < 0)
- return -1;
- }
-
- if (codec->cache_only) {
- codec->cache_sync = 1;
- return 0;
- }
-
- if (codec->hw_write(codec->control_data, data, 3) == 3)
- return 0;
- else
- return -EIO;
+ return do_hw_write(codec, reg, value, data, 3);
}
static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec,
unsigned int reg)
{
- int ret;
- unsigned int val;
-
- if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
-
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
- }
-
- ret = snd_soc_cache_read(codec, reg, &val);
- if (ret < 0)
- return -1;
- return val;
-}
-
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_8_16_spi_write(void *control_data, const char *data,
- int len)
-{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[3];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[0];
- msg[1] = data[1];
- msg[2] = data[2];
-
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
+ return do_hw_read(codec, reg);
}
-#else
-#define snd_soc_8_16_spi_write NULL
-#endif
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec,
- unsigned int r)
+static unsigned int do_i2c_read(struct snd_soc_codec *codec,
+ void *reg, int reglen,
+ void *data, int datalen)
{
struct i2c_msg xfer[2];
- u8 reg = r;
- u8 data;
int ret;
struct i2c_client *client = codec->control_data;
/* Write register */
xfer[0].addr = client->addr;
xfer[0].flags = 0;
- xfer[0].len = 1;
- xfer[0].buf = ®
+ xfer[0].len = reglen;
+ xfer[0].buf = reg;
/* Read data */
xfer[1].addr = client->addr;
xfer[1].flags = I2C_M_RD;
- xfer[1].len = 1;
- xfer[1].buf = &data;
+ xfer[1].len = datalen;
+ xfer[1].buf = data;
ret = i2c_transfer(client->adapter, xfer, 2);
- if (ret != 2) {
- dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+ if (ret == 2)
return 0;
- }
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+#endif
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec,
+ unsigned int r)
+{
+ u8 reg = r;
+ u8 data;
+ int ret;
+
+ ret = do_i2c_read(codec, ®, 1, &data, 1);
+ if (ret < 0)
+ return 0;
return data;
}
#else
static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
- struct i2c_msg xfer[2];
u8 reg = r;
u16 data;
int ret;
- struct i2c_client *client = codec->control_data;
- /* Write register */
- xfer[0].addr = client->addr;
- xfer[0].flags = 0;
- xfer[0].len = 1;
- xfer[0].buf = ®
-
- /* Read data */
- xfer[1].addr = client->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = 2;
- xfer[1].buf = (u8 *)&data;
-
- ret = i2c_transfer(client->adapter, xfer, 2);
- if (ret != 2) {
- dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+ ret = do_i2c_read(codec, ®, 1, &data, 2);
+ if (ret < 0)
return 0;
- }
-
return (data >> 8) | ((data & 0xff) << 8);
}
#else
static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
- struct i2c_msg xfer[2];
u16 reg = r;
u8 data;
int ret;
- struct i2c_client *client = codec->control_data;
-
- /* Write register */
- xfer[0].addr = client->addr;
- xfer[0].flags = 0;
- xfer[0].len = 2;
- xfer[0].buf = (u8 *)®
-
- /* Read data */
- xfer[1].addr = client->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = 1;
- xfer[1].buf = &data;
- ret = i2c_transfer(client->adapter, xfer, 2);
- if (ret != 2) {
- dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+ ret = do_i2c_read(codec, ®, 2, &data, 1);
+ if (ret < 0)
return 0;
- }
-
return data;
}
#else
#endif
static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec,
- unsigned int reg)
+ unsigned int reg)
{
- int ret;
- unsigned int val;
-
- reg &= 0xff;
- if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
-
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
- }
-
- ret = snd_soc_cache_read(codec, reg, &val);
- if (ret < 0)
- return -1;
- return val;
+ return do_hw_read(codec, reg);
}
static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
+ unsigned int value)
{
u8 data[3];
- int ret;
data[0] = (reg >> 8) & 0xff;
data[1] = reg & 0xff;
data[2] = value;
- reg &= 0xff;
- if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret < 0)
- return -1;
- }
-
- if (codec->cache_only) {
- codec->cache_sync = 1;
- return 0;
- }
-
- ret = codec->hw_write(codec->control_data, data, 3);
- if (ret == 3)
- return 0;
- if (ret < 0)
- return ret;
- else
- return -EIO;
-}
-
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_16_8_spi_write(void *control_data, const char *data,
- int len)
-{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[3];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[0];
- msg[1] = data[1];
- msg[2] = data[2];
-
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
+ return do_hw_write(codec, reg, value, data, 3);
}
-#else
-#define snd_soc_16_8_spi_write NULL
-#endif
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
- struct i2c_msg xfer[2];
u16 reg = cpu_to_be16(r);
u16 data;
int ret;
- struct i2c_client *client = codec->control_data;
-
- /* Write register */
- xfer[0].addr = client->addr;
- xfer[0].flags = 0;
- xfer[0].len = 2;
- xfer[0].buf = (u8 *)®
- /* Read data */
- xfer[1].addr = client->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = 2;
- xfer[1].buf = (u8 *)&data;
-
- ret = i2c_transfer(client->adapter, xfer, 2);
- if (ret != 2) {
- dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+ ret = do_i2c_read(codec, ®, 2, &data, 2);
+ if (ret < 0)
return 0;
- }
-
return be16_to_cpu(data);
}
#else
static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec,
unsigned int reg)
{
- int ret;
- unsigned int val;
-
- if (reg >= codec->driver->reg_cache_size ||
- snd_soc_codec_volatile_register(codec, reg) ||
- codec->cache_bypass) {
- if (codec->cache_only)
- return -1;
-
- BUG_ON(!codec->hw_read);
- return codec->hw_read(codec, reg);
- }
-
- ret = snd_soc_cache_read(codec, reg, &val);
- if (ret < 0)
- return -1;
-
- return val;
+ return do_hw_read(codec, reg);
}
static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[4];
- int ret;
data[0] = (reg >> 8) & 0xff;
data[1] = reg & 0xff;
data[2] = (value >> 8) & 0xff;
data[3] = value & 0xff;
- if (!snd_soc_codec_volatile_register(codec, reg) &&
- reg < codec->driver->reg_cache_size &&
- !codec->cache_bypass) {
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret < 0)
- return -1;
- }
+ return do_hw_write(codec, reg, value, data, 4);
+}
- if (codec->cache_only) {
- codec->cache_sync = 1;
- return 0;
+/* Primitive bulk write support for soc-cache. The data pointed to by
+ * `data' needs to already be in the form the hardware expects
+ * including any leading register specific data. Any data written
+ * through this function will not go through the cache as it only
+ * handles writing to volatile or out of bounds registers.
+ */
+static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int reg,
+ const void *data, size_t len)
+{
+ int ret;
+
+ /* To ensure that we don't get out of sync with the cache, check
+ * whether the base register is volatile or if we've directly asked
+ * to bypass the cache. Out of bounds registers are considered
+ * volatile.
+ */
+ if (!codec->cache_bypass
+ && !snd_soc_codec_volatile_register(codec, reg)
+ && reg < codec->driver->reg_cache_size)
+ return -EINVAL;
+
+ switch (codec->control_type) {
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+ case SND_SOC_I2C:
+ ret = i2c_master_send(codec->control_data, data, len);
+ break;
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ case SND_SOC_SPI:
+ ret = spi_write(codec->control_data, data, len);
+ break;
+#endif
+ default:
+ BUG();
}
- ret = codec->hw_write(codec->control_data, data, 4);
- if (ret == 4)
+ if (ret == len)
return 0;
if (ret < 0)
return ret;
return -EIO;
}
-#if defined(CONFIG_SPI_MASTER)
-static int snd_soc_16_16_spi_write(void *control_data, const char *data,
- int len)
-{
- struct spi_device *spi = control_data;
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[4];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[0];
- msg[1] = data[1];
- msg[2] = data[2];
- msg[3] = data[3];
-
- spi_message_init(&m);
- memset(&t, 0, sizeof t);
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
-}
-#else
-#define snd_soc_16_16_spi_write NULL
-#endif
-
static struct {
int addr_bits;
int data_bits;
int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int);
- int (*spi_write)(void *, const char *, int);
unsigned int (*read)(struct snd_soc_codec *, unsigned int);
unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
} io_types[] = {
{
.addr_bits = 4, .data_bits = 12,
.write = snd_soc_4_12_write, .read = snd_soc_4_12_read,
- .spi_write = snd_soc_4_12_spi_write,
},
{
.addr_bits = 7, .data_bits = 9,
.write = snd_soc_7_9_write, .read = snd_soc_7_9_read,
- .spi_write = snd_soc_7_9_spi_write,
},
{
.addr_bits = 8, .data_bits = 8,
.write = snd_soc_8_8_write, .read = snd_soc_8_8_read,
.i2c_read = snd_soc_8_8_read_i2c,
- .spi_write = snd_soc_8_8_spi_write,
},
{
.addr_bits = 8, .data_bits = 16,
.write = snd_soc_8_16_write, .read = snd_soc_8_16_read,
.i2c_read = snd_soc_8_16_read_i2c,
- .spi_write = snd_soc_8_16_spi_write,
},
{
.addr_bits = 16, .data_bits = 8,
.write = snd_soc_16_8_write, .read = snd_soc_16_8_read,
.i2c_read = snd_soc_16_8_read_i2c,
- .spi_write = snd_soc_16_8_spi_write,
},
{
.addr_bits = 16, .data_bits = 16,
.write = snd_soc_16_16_write, .read = snd_soc_16_16_read,
.i2c_read = snd_soc_16_16_read_i2c,
- .spi_write = snd_soc_16_16_spi_write,
},
};
* snd_soc_codec_set_cache_io: Set up standard I/O functions.
*
* @codec: CODEC to configure.
- * @type: Type of cache.
* @addr_bits: Number of bits of register address data.
* @data_bits: Number of bits of data per register.
* @control: Control bus used.
codec->write = io_types[i].write;
codec->read = io_types[i].read;
+ codec->bulk_write_raw = snd_soc_hw_bulk_write_raw;
switch (control) {
case SND_SOC_CUSTOM:
break;
case SND_SOC_SPI:
- if (io_types[i].spi_write)
- codec->hw_write = io_types[i].spi_write;
+#ifdef CONFIG_SPI_MASTER
+ codec->hw_write = do_spi_write;
+#endif
codec->control_data = container_of(codec->dev,
struct spi_device,
rbnode = rb_entry(node, struct snd_soc_rbtree_node, node);
if (rbnode->value == rbnode->defval)
continue;
+ WARN_ON(codec->writable_register &&
+ codec->writable_register(codec, rbnode->reg));
ret = snd_soc_cache_read(codec, rbnode->reg, &val);
if (ret)
return ret;
lzo_blocks = codec->reg_cache;
for_each_set_bit(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) {
+ WARN_ON(codec->writable_register &&
+ codec->writable_register(codec, i));
ret = snd_soc_cache_read(codec, i, &val);
if (ret)
return ret;
codec_drv = codec->driver;
for (i = 0; i < codec_drv->reg_cache_size; ++i) {
+ WARN_ON(codec->writable_register &&
+ codec->writable_register(codec, i));
ret = snd_soc_cache_read(codec, i, &val);
if (ret)
return ret;
codec->cache_ops->name, codec->name);
return codec->cache_ops->init(codec);
}
- return -EINVAL;
+ return -ENOSYS;
}
/*
codec->cache_ops->name, codec->name);
return codec->cache_ops->exit(codec);
}
- return -EINVAL;
+ return -ENOSYS;
}
/**
}
mutex_unlock(&codec->cache_rw_mutex);
- return -EINVAL;
+ return -ENOSYS;
}
EXPORT_SYMBOL_GPL(snd_soc_cache_read);
}
mutex_unlock(&codec->cache_rw_mutex);
- return -EINVAL;
+ return -ENOSYS;
}
EXPORT_SYMBOL_GPL(snd_soc_cache_write);
}
if (!codec->cache_ops || !codec->cache_ops->sync)
- return -EINVAL;
+ return -ENOSYS;
if (codec->cache_ops->name)
name = codec->cache_ops->name;
return codec->driver->reg_access_default[index].read;
}
EXPORT_SYMBOL_GPL(snd_soc_default_readable_register);
+
+int snd_soc_default_writable_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ int index;
+
+ if (reg >= codec->driver->reg_cache_size)
+ return 1;
+ index = snd_soc_get_reg_access_index(codec, reg);
+ if (index < 0)
+ return 0;
+ return codec->driver->reg_access_default[index].write;
+}
+EXPORT_SYMBOL_GPL(snd_soc_default_writable_register);
const char __user *user_buf, size_t count, loff_t *ppos)
{
char buf[32];
- int buf_size;
+ size_t buf_size;
char *start = buf;
unsigned long reg, value;
int step = 1;
printk(KERN_WARNING
"ASoC: Failed to create codec register debugfs file\n");
- codec->dapm.debugfs_dapm = debugfs_create_dir("dapm",
- codec->debugfs_codec_root);
- if (!codec->dapm.debugfs_dapm)
- printk(KERN_WARNING
- "Failed to create DAPM debugfs directory\n");
-
- snd_soc_dapm_debugfs_init(&codec->dapm);
+ snd_soc_dapm_debugfs_init(&codec->dapm, codec->debugfs_codec_root);
}
static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
}
}
- if (platform->driver->ops->open) {
+ if (platform->driver->ops && platform->driver->ops->open) {
ret = platform->driver->ops->open(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open platform %s\n", platform->name);
codec_dai->driver->ops->shutdown(substream, codec_dai);
codec_dai_err:
- if (platform->driver->ops->close)
+ if (platform->driver->ops && platform->driver->ops->close)
platform->driver->ops->close(substream);
platform_err:
if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
rtd->dai_link->ops->shutdown(substream);
- if (platform->driver->ops->close)
+ if (platform->driver->ops && platform->driver->ops->close)
platform->driver->ops->close(substream);
cpu_dai->runtime = NULL;
}
}
- if (platform->driver->ops->prepare) {
+ if (platform->driver->ops && platform->driver->ops->prepare) {
ret = platform->driver->ops->prepare(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: platform prepare error\n");
}
}
- if (platform->driver->ops->hw_params) {
+ if (platform->driver->ops && platform->driver->ops->hw_params) {
ret = platform->driver->ops->hw_params(substream, params);
if (ret < 0) {
printk(KERN_ERR "asoc: platform %s hw params failed\n",
rtd->dai_link->ops->hw_free(substream);
/* free any DMA resources */
- if (platform->driver->ops->hw_free)
+ if (platform->driver->ops && platform->driver->ops->hw_free)
platform->driver->ops->hw_free(substream);
/* now free hw params for the DAIs */
return ret;
}
- if (platform->driver->ops->trigger) {
+ if (platform->driver->ops && platform->driver->ops->trigger) {
ret = platform->driver->ops->trigger(substream, cmd);
if (ret < 0)
return ret;
snd_pcm_uframes_t offset = 0;
snd_pcm_sframes_t delay = 0;
- if (platform->driver->ops->pointer)
+ if (platform->driver->ops && platform->driver->ops->pointer)
offset = platform->driver->ops->pointer(substream);
if (cpu_dai->driver->ops->delay)
struct snd_soc_codec *codec;
struct snd_soc_platform *platform;
struct snd_soc_dai *codec_dai, *cpu_dai;
+ const char *platform_name;
if (rtd->complete)
return 1;
dai_link->codec_name);
find_platform:
- /* do we already have the CODEC DAI for this link ? */
- if (rtd->platform) {
+ /* do we need a platform? */
+ if (rtd->platform)
goto out;
- }
- /* no, then find CPU DAI from registered DAIs*/
+
+ /* if there's no platform we match on the empty platform */
+ platform_name = dai_link->platform_name;
+ if (!platform_name)
+ platform_name = "snd-soc-dummy";
+
+ /* no, then find one from the set of registered platforms */
list_for_each_entry(platform, &platform_list, list) {
- if (!strcmp(platform->name, dai_link->platform_name)) {
+ if (!strcmp(platform->name, platform_name)) {
rtd->platform = platform;
goto out;
}
}
}
+static void soc_remove_dai_links(struct snd_soc_card *card)
+{
+ int i;
+
+ for (i = 0; i < card->num_rtd; i++)
+ soc_remove_dai_link(card, i);
+
+ card->num_rtd = 0;
+}
+
static void soc_set_name_prefix(struct snd_soc_card *card,
struct snd_soc_codec *codec)
{
if (!try_module_get(codec->dev->driver->owner))
return -ENODEV;
+ soc_init_codec_debugfs(codec);
+
+ if (driver->dapm_widgets)
+ snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,
+ driver->num_dapm_widgets);
+
if (driver->probe) {
ret = driver->probe(codec);
if (ret < 0) {
}
}
- if (driver->dapm_widgets)
- snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,
- driver->num_dapm_widgets);
+ if (driver->controls)
+ snd_soc_add_controls(codec, driver->controls,
+ driver->num_controls);
if (driver->dapm_routes)
snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes,
driver->num_dapm_routes);
- soc_init_codec_debugfs(codec);
-
/* mark codec as probed and add to card codec list */
codec->probed = 1;
list_add(&codec->card_list, &card->codec_dev_list);
return 0;
err_probe:
+ soc_cleanup_codec_debugfs(codec);
module_put(codec->dev->driver->owner);
return ret;
card->dapm.card = card;
list_add(&card->dapm.list, &card->dapm_list);
+#ifdef CONFIG_DEBUG_FS
+ snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root);
+#endif
+
#ifdef CONFIG_PM_SLEEP
/* deferred resume work */
INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif
+ if (card->dapm_widgets)
+ snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
+ card->num_dapm_widgets);
+
/* initialise the sound card only once */
if (card->probe) {
ret = card->probe(card);
}
}
- if (card->dapm_widgets)
- snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
- card->num_dapm_widgets);
+ /* We should have a non-codec control add function but we don't */
+ if (card->controls)
+ snd_soc_add_controls(list_first_entry(&card->codec_dev_list,
+ struct snd_soc_codec,
+ card_list),
+ card->controls,
+ card->num_controls);
+
if (card->dapm_routes)
snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
card->num_dapm_routes);
-#ifdef CONFIG_DEBUG_FS
- card->dapm.debugfs_dapm = debugfs_create_dir("dapm",
- card->debugfs_card_root);
- if (!card->dapm.debugfs_dapm)
- printk(KERN_WARNING
- "Failed to create card DAPM debugfs directory\n");
-
- snd_soc_dapm_debugfs_init(&card->dapm);
-#endif
-
snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
- "%s", card->name);
- snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
"%s", card->name);
+ snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
+ "%s", card->long_name ? card->long_name : card->name);
+ snprintf(card->snd_card->driver, sizeof(card->snd_card->driver),
+ "%s", card->driver_name ? card->driver_name : card->name);
if (card->late_probe) {
ret = card->late_probe(card);
soc_remove_aux_dev(card, i);
probe_dai_err:
- for (i = 0; i < card->num_links; i++)
- soc_remove_dai_link(card, i);
+ soc_remove_dai_links(card);
card_probe_error:
if (card->remove)
soc_remove_aux_dev(card, i);
/* remove and free each DAI */
- for (i = 0; i < card->num_rtd; i++)
- soc_remove_dai_link(card, i);
+ soc_remove_dai_links(card);
soc_cleanup_card_debugfs(card);
if (card->remove)
card->remove(card);
+ snd_soc_dapm_free(&card->dapm);
+
kfree(card->rtd);
snd_card_free(card->snd_card);
return 0;
rtd->pcm = pcm;
pcm->private_data = rtd;
- soc_pcm_ops.mmap = platform->driver->ops->mmap;
- soc_pcm_ops.pointer = platform->driver->ops->pointer;
- soc_pcm_ops.ioctl = platform->driver->ops->ioctl;
- soc_pcm_ops.copy = platform->driver->ops->copy;
- soc_pcm_ops.silence = platform->driver->ops->silence;
- soc_pcm_ops.ack = platform->driver->ops->ack;
- soc_pcm_ops.page = platform->driver->ops->page;
+ if (platform->driver->ops) {
+ soc_pcm_ops.mmap = platform->driver->ops->mmap;
+ soc_pcm_ops.pointer = platform->driver->ops->pointer;
+ soc_pcm_ops.ioctl = platform->driver->ops->ioctl;
+ soc_pcm_ops.copy = platform->driver->ops->copy;
+ soc_pcm_ops.silence = platform->driver->ops->silence;
+ soc_pcm_ops.ack = platform->driver->ops->ack;
+ soc_pcm_ops.page = platform->driver->ops->page;
+ }
if (playback)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
- ret = platform->driver->pcm_new(rtd->card->snd_card, codec_dai, pcm);
- if (ret < 0) {
- printk(KERN_ERR "asoc: platform pcm constructor failed\n");
- return ret;
+ if (platform->driver->pcm_new) {
+ ret = platform->driver->pcm_new(rtd->card->snd_card,
+ codec_dai, pcm);
+ if (ret < 0) {
+ pr_err("asoc: platform pcm constructor failed\n");
+ return ret;
+ }
}
pcm->private_free = platform->driver->pcm_free;
}
EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register);
+/**
+ * snd_soc_codec_readable_register: Report if a register is readable.
+ *
+ * @codec: CODEC to query.
+ * @reg: Register to query.
+ *
+ * Boolean function indicating if a CODEC register is readable.
+ */
+int snd_soc_codec_readable_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ if (codec->readable_register)
+ return codec->readable_register(codec, reg);
+ else
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_readable_register);
+
+/**
+ * snd_soc_codec_writable_register: Report if a register is writable.
+ *
+ * @codec: CODEC to query.
+ * @reg: Register to query.
+ *
+ * Boolean function indicating if a CODEC register is writable.
+ */
+int snd_soc_codec_writable_register(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ if (codec->writable_register)
+ return codec->writable_register(codec, reg);
+ else
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_writable_register);
+
/**
* snd_soc_new_ac97_codec - initailise AC97 device
* @codec: audio codec
}
EXPORT_SYMBOL_GPL(snd_soc_write);
+unsigned int snd_soc_bulk_write_raw(struct snd_soc_codec *codec,
+ unsigned int reg, const void *data, size_t len)
+{
+ return codec->bulk_write_raw(codec, reg, data, len);
+}
+EXPORT_SYMBOL_GPL(snd_soc_bulk_write_raw);
+
/**
* snd_soc_update_bits - update codec register bits
* @codec: audio codec
if (!card->name || !card->dev)
return -EINVAL;
+ dev_set_drvdata(card->dev, card);
+
snd_soc_initialize_card_lists(card);
soc_init_card_debugfs(card);
dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
if (dai == NULL)
- return -ENOMEM;
+ return -ENOMEM;
/* create DAI component name */
dai->name = fmt_single_name(dev, &dai->id);
platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL);
if (platform == NULL)
- return -ENOMEM;
+ return -ENOMEM;
/* create platform component name */
platform->name = fmt_single_name(dev, &platform->id);
codec->read = codec_drv->read;
codec->volatile_register = codec_drv->volatile_register;
codec->readable_register = codec_drv->readable_register;
+ codec->writable_register = codec_drv->writable_register;
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
codec->dapm.dev = dev;
codec->dapm.codec = codec;
codec->volatile_register = snd_soc_default_volatile_register;
if (!codec->readable_register)
codec->readable_register = snd_soc_default_readable_register;
+ if (!codec->writable_register)
+ codec->writable_register = snd_soc_default_writable_register;
}
for (i = 0; i < num_dai; i++) {
pr_warn("ASoC: Failed to create platform list debugfs file\n");
#endif
+ snd_soc_util_init();
+
return platform_driver_register(&soc_driver);
}
module_init(snd_soc_init);
static void __exit snd_soc_exit(void)
{
+ snd_soc_util_exit();
+
#ifdef CONFIG_DEBUG_FS
debugfs_remove_recursive(snd_soc_debugfs_root);
#endif
case snd_soc_dapm_mixer_named_ctl: {
int val;
struct soc_mixer_control *mc = (struct soc_mixer_control *)
- w->kcontrols[i].private_value;
+ w->kcontrol_news[i].private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
int max = mc->max;
}
break;
case snd_soc_dapm_mux: {
- struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;
+ struct soc_enum *e = (struct soc_enum *)
+ w->kcontrol_news[i].private_value;
int val, item, bitmask;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
}
break;
case snd_soc_dapm_virt_mux: {
- struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;
+ struct soc_enum *e = (struct soc_enum *)
+ w->kcontrol_news[i].private_value;
p->connect = 0;
/* since a virtual mux has no backing registers to
break;
case snd_soc_dapm_value_mux: {
struct soc_enum *e = (struct soc_enum *)
- w->kcontrols[i].private_value;
+ w->kcontrol_news[i].private_value;
int val, item;
val = snd_soc_read(w->codec, e->reg);
/* search for mixer kcontrol */
for (i = 0; i < dest->num_kcontrols; i++) {
- if (!strcmp(control_name, dest->kcontrols[i].name)) {
+ if (!strcmp(control_name, dest->kcontrol_news[i].name)) {
list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &dest->sources);
list_add(&path->list_source, &src->sinks);
- path->name = dest->kcontrols[i].name;
+ path->name = dest->kcontrol_news[i].name;
dapm_set_path_status(dest, path, i);
return 0;
}
return -ENODEV;
}
-/* update dapm codec register bits */
-static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
+static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
+ const struct snd_kcontrol_new *kcontrol_new,
+ struct snd_kcontrol **kcontrol)
{
- int change, power;
- unsigned int old, new;
- struct snd_soc_codec *codec = widget->codec;
- struct snd_soc_dapm_context *dapm = widget->dapm;
- struct snd_soc_card *card = dapm->card;
-
- /* check for valid widgets */
- if (widget->reg < 0 || widget->id == snd_soc_dapm_input ||
- widget->id == snd_soc_dapm_output ||
- widget->id == snd_soc_dapm_hp ||
- widget->id == snd_soc_dapm_mic ||
- widget->id == snd_soc_dapm_line ||
- widget->id == snd_soc_dapm_spk)
- return 0;
-
- power = widget->power;
- if (widget->invert)
- power = (power ? 0:1);
+ struct snd_soc_dapm_widget *w;
+ int i;
- old = snd_soc_read(codec, widget->reg);
- new = (old & ~(0x1 << widget->shift)) | (power << widget->shift);
+ *kcontrol = NULL;
- change = old != new;
- if (change) {
- pop_dbg(dapm->dev, card->pop_time,
- "pop test %s : %s in %d ms\n",
- widget->name, widget->power ? "on" : "off",
- card->pop_time);
- pop_wait(card->pop_time);
- snd_soc_write(codec, widget->reg, new);
+ list_for_each_entry(w, &dapm->card->widgets, list) {
+ for (i = 0; i < w->num_kcontrols; i++) {
+ if (&w->kcontrol_news[i] == kcontrol_new) {
+ if (w->kcontrols)
+ *kcontrol = w->kcontrols[i];
+ return 1;
+ }
+ }
}
- dev_dbg(dapm->dev, "reg %x old %x new %x change %d\n", widget->reg,
- old, new, change);
- return change;
+
+ return 0;
}
/* create new dapm mixer control */
struct snd_soc_dapm_path *path;
struct snd_card *card = dapm->card->snd_card;
const char *prefix;
+ struct snd_soc_dapm_widget_list *wlist;
+ size_t wlistsize;
if (dapm->codec)
prefix = dapm->codec->name_prefix;
list_for_each_entry(path, &w->sources, list_sink) {
/* mixer/mux paths name must match control name */
- if (path->name != (char*)w->kcontrols[i].name)
+ if (path->name != (char *)w->kcontrol_news[i].name)
continue;
+ wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
+ sizeof(struct snd_soc_dapm_widget *),
+ wlist = kzalloc(wlistsize, GFP_KERNEL);
+ if (wlist == NULL) {
+ dev_err(dapm->dev,
+ "asoc: can't allocate widget list for %s\n",
+ w->name);
+ return -ENOMEM;
+ }
+ wlist->num_widgets = 1;
+ wlist->widgets[0] = w;
+
/* add dapm control with long name.
* for dapm_mixer this is the concatenation of the
* mixer and kcontrol name.
* for dapm_mixer_named_ctl this is simply the
* kcontrol name.
*/
- name_len = strlen(w->kcontrols[i].name) + 1;
+ name_len = strlen(w->kcontrol_news[i].name) + 1;
if (w->id != snd_soc_dapm_mixer_named_ctl)
name_len += 1 + strlen(w->name);
path->long_name = kmalloc(name_len, GFP_KERNEL);
- if (path->long_name == NULL)
+ if (path->long_name == NULL) {
+ kfree(wlist);
return -ENOMEM;
+ }
switch (w->id) {
default:
*/
snprintf(path->long_name, name_len, "%s %s",
w->name + prefix_len,
- w->kcontrols[i].name);
+ w->kcontrol_news[i].name);
break;
case snd_soc_dapm_mixer_named_ctl:
snprintf(path->long_name, name_len, "%s",
- w->kcontrols[i].name);
+ w->kcontrol_news[i].name);
break;
}
path->long_name[name_len - 1] = '\0';
- path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
- path->long_name, prefix);
+ path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i],
+ wlist, path->long_name,
+ prefix);
ret = snd_ctl_add(card, path->kcontrol);
if (ret < 0) {
dev_err(dapm->dev,
"asoc: failed to add dapm kcontrol %s: %d\n",
path->long_name, ret);
+ kfree(wlist);
kfree(path->long_name);
path->long_name = NULL;
return ret;
}
+ w->kcontrols[i] = path->kcontrol;
}
}
return ret;
struct snd_card *card = dapm->card->snd_card;
const char *prefix;
size_t prefix_len;
- int ret = 0;
-
- if (!w->num_kcontrols) {
- dev_err(dapm->dev, "asoc: mux %s has no controls\n", w->name);
+ int ret;
+ struct snd_soc_dapm_widget_list *wlist;
+ int shared, wlistentries;
+ size_t wlistsize;
+ char *name;
+
+ if (w->num_kcontrols != 1) {
+ dev_err(dapm->dev,
+ "asoc: mux %s has incorrect number of controls\n",
+ w->name);
return -EINVAL;
}
- if (dapm->codec)
- prefix = dapm->codec->name_prefix;
- else
- prefix = NULL;
+ shared = dapm_is_shared_kcontrol(dapm, &w->kcontrol_news[0],
+ &kcontrol);
+ if (kcontrol) {
+ wlist = kcontrol->private_data;
+ wlistentries = wlist->num_widgets + 1;
+ } else {
+ wlist = NULL;
+ wlistentries = 1;
+ }
+ wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
+ wlistentries * sizeof(struct snd_soc_dapm_widget *),
+ wlist = krealloc(wlist, wlistsize, GFP_KERNEL);
+ if (wlist == NULL) {
+ dev_err(dapm->dev,
+ "asoc: can't allocate widget list for %s\n", w->name);
+ return -ENOMEM;
+ }
+ wlist->num_widgets = wlistentries;
+ wlist->widgets[wlistentries - 1] = w;
- if (prefix)
- prefix_len = strlen(prefix) + 1;
- else
- prefix_len = 0;
+ if (!kcontrol) {
+ if (dapm->codec)
+ prefix = dapm->codec->name_prefix;
+ else
+ prefix = NULL;
+
+ if (shared) {
+ name = w->kcontrol_news[0].name;
+ prefix_len = 0;
+ } else {
+ name = w->name;
+ if (prefix)
+ prefix_len = strlen(prefix) + 1;
+ else
+ prefix_len = 0;
+ }
- /* The control will get a prefix from the control creation
- * process but we're also using the same prefix for widgets so
- * cut the prefix off the front of the widget name.
- */
- kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name + prefix_len,
- prefix);
- ret = snd_ctl_add(card, kcontrol);
+ /*
+ * The control will get a prefix from the control creation
+ * process but we're also using the same prefix for widgets so
+ * cut the prefix off the front of the widget name.
+ */
+ kcontrol = snd_soc_cnew(&w->kcontrol_news[0], wlist,
+ name + prefix_len, prefix);
+ ret = snd_ctl_add(card, kcontrol);
+ if (ret < 0) {
+ dev_err(dapm->dev,
+ "asoc: failed to add kcontrol %s\n", w->name);
+ kfree(wlist);
+ return ret;
+ }
+ }
- if (ret < 0)
- goto err;
+ kcontrol->private_data = wlist;
+
+ w->kcontrols[0] = kcontrol;
list_for_each_entry(path, &w->sources, list_sink)
path->kcontrol = kcontrol;
- return ret;
-
-err:
- dev_err(dapm->dev, "asoc: failed to add kcontrol %s\n", w->name);
- return ret;
+ return 0;
}
/* create new dapm volume control */
}
EXPORT_SYMBOL_GPL(dapm_reg_event);
-/* Standard power change method, used to apply power changes to most
- * widgets.
- */
-static int dapm_generic_apply_power(struct snd_soc_dapm_widget *w)
-{
- int ret;
-
- /* call any power change event handlers */
- if (w->event)
- dev_dbg(w->dapm->dev, "power %s event for %s flags %x\n",
- w->power ? "on" : "off",
- w->name, w->event_flags);
-
- /* power up pre event */
- if (w->power && w->event &&
- (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
- ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
- if (ret < 0)
- return ret;
- }
-
- /* power down pre event */
- if (!w->power && w->event &&
- (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
- ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
- if (ret < 0)
- return ret;
- }
-
- dapm_update_bits(w);
-
- /* power up post event */
- if (w->power && w->event &&
- (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
- ret = w->event(w,
- NULL, SND_SOC_DAPM_POST_PMU);
- if (ret < 0)
- return ret;
- }
-
- /* power down post event */
- if (!w->power && w->event &&
- (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
- ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-
/* Generic check to see if a widget should be powered.
*/
static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
NULL, SND_SOC_DAPM_POST_PMD);
break;
- case snd_soc_dapm_input:
- case snd_soc_dapm_output:
- case snd_soc_dapm_hp:
- case snd_soc_dapm_mic:
- case snd_soc_dapm_line:
- case snd_soc_dapm_spk:
- /* No register support currently */
- ret = dapm_generic_apply_power(w);
- break;
-
default:
/* Queue it up for application */
cur_sort = sort[w->id];
}
}
+ /* Force all contexts in the card to the same bias state */
+ power = 0;
+ list_for_each_entry(d, &card->dapm_list, list)
+ if (d->dev_power)
+ power = 1;
+ list_for_each_entry(d, &card->dapm_list, list)
+ d->dev_power = power;
+
+
/* Run all the bias changes in parallel */
list_for_each_entry(d, &dapm->card->dapm_list, list)
async_schedule_domain(dapm_pre_sequence_async, d,
.llseek = default_llseek,
};
-void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm)
+static int dapm_bias_open_file(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t dapm_bias_read_file(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct snd_soc_dapm_context *dapm = file->private_data;
+ char *level;
+
+ switch (dapm->bias_level) {
+ case SND_SOC_BIAS_ON:
+ level = "On\n";
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ level = "Prepare\n";
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ level = "Standby\n";
+ break;
+ case SND_SOC_BIAS_OFF:
+ level = "Off\n";
+ break;
+ default:
+ BUG();
+ level = "Unknown\n";
+ break;
+ }
+
+ return simple_read_from_buffer(user_buf, count, ppos, level,
+ strlen(level));
+}
+
+static const struct file_operations dapm_bias_fops = {
+ .open = dapm_bias_open_file,
+ .read = dapm_bias_read_file,
+ .llseek = default_llseek,
+};
+
+void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,
+ struct dentry *parent)
{
- struct snd_soc_dapm_widget *w;
struct dentry *d;
- if (!dapm->debugfs_dapm)
+ dapm->debugfs_dapm = debugfs_create_dir("dapm", parent);
+
+ if (!dapm->debugfs_dapm) {
+ printk(KERN_WARNING
+ "Failed to create DAPM debugfs directory\n");
return;
+ }
- list_for_each_entry(w, &dapm->card->widgets, list) {
- if (!w->name || w->dapm != dapm)
- continue;
+ d = debugfs_create_file("bias_level", 0444,
+ dapm->debugfs_dapm, dapm,
+ &dapm_bias_fops);
+ if (!d)
+ dev_warn(dapm->dev,
+ "ASoC: Failed to create bias level debugfs file\n");
+}
- d = debugfs_create_file(w->name, 0444,
- dapm->debugfs_dapm, w,
- &dapm_widget_power_fops);
- if (!d)
- dev_warn(w->dapm->dev,
- "ASoC: Failed to create %s debugfs file\n",
- w->name);
- }
+static void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct dentry *d;
+
+ if (!dapm->debugfs_dapm || !w->name)
+ return;
+
+ d = debugfs_create_file(w->name, 0444,
+ dapm->debugfs_dapm, w,
+ &dapm_widget_power_fops);
+ if (!d)
+ dev_warn(w->dapm->dev,
+ "ASoC: Failed to create %s debugfs file\n",
+ w->name);
+}
+
+static void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
+{
+ debugfs_remove_recursive(dapm->debugfs_dapm);
}
+
#else
-void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm)
+void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,
+ struct dentry *parent)
+{
+}
+
+static inline void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w)
+{
+}
+
+static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
{
}
+
#endif
/* test and update the power status of a mux widget */
kfree(p->long_name);
kfree(p);
}
+ kfree(w->kcontrols);
kfree(w->name);
kfree(w);
}
}
-static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
- const char *pin, int status)
+static struct snd_soc_dapm_widget *dapm_find_widget(
+ struct snd_soc_dapm_context *dapm, const char *pin,
+ bool search_other_contexts)
{
struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *fallback = NULL;
list_for_each_entry(w, &dapm->card->widgets, list) {
- if (w->dapm != dapm)
- continue;
if (!strcmp(w->name, pin)) {
- dev_dbg(w->dapm->dev, "dapm: pin %s = %d\n",
- pin, status);
- w->connected = status;
- /* Allow disabling of forced pins */
- if (status == 0)
- w->force = 0;
- return 0;
+ if (w->dapm == dapm)
+ return w;
+ else
+ fallback = w;
}
}
- dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
- return -EINVAL;
+ if (search_other_contexts)
+ return fallback;
+
+ return NULL;
+}
+
+static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
+ const char *pin, int status)
+{
+ struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
+
+ if (!w) {
+ dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
+ return -EINVAL;
+ }
+
+ w->connected = status;
+ if (status == 0)
+ w->force = 0;
+
+ return 0;
}
/**
}
/* connect dynamic paths */
- switch(wsink->id) {
+ switch (wsink->id) {
case snd_soc_dapm_adc:
case snd_soc_dapm_dac:
case snd_soc_dapm_pga:
case snd_soc_dapm_virt_mux:
case snd_soc_dapm_value_mux:
ret = dapm_connect_mux(dapm, wsource, wsink, path, control,
- &wsink->kcontrols[0]);
+ &wsink->kcontrol_news[0]);
if (ret != 0)
goto err;
break;
if (w->new)
continue;
+ if (w->num_kcontrols) {
+ w->kcontrols = kzalloc(w->num_kcontrols *
+ sizeof(struct snd_kcontrol *),
+ GFP_KERNEL);
+ if (!w->kcontrols)
+ return -ENOMEM;
+ }
+
switch(w->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
}
w->new = 1;
+
+ dapm_debugfs_add_widget(w);
}
dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int val;
int connect, change;
struct snd_soc_dapm_update update;
+ int wi;
val = (ucontrol->value.integer.value[0] & mask);
mask = mask << shift;
val = val << shift;
- mutex_lock(&widget->codec->mutex);
- widget->value = val;
+ if (val)
+ /* new connection */
+ connect = invert ? 0 : 1;
+ else
+ /* old connection must be powered down */
+ connect = invert ? 1 : 0;
+
+ mutex_lock(&codec->mutex);
change = snd_soc_test_bits(widget->codec, reg, mask, val);
if (change) {
- if (val)
- /* new connection */
- connect = invert ? 0:1;
- else
- /* old connection must be powered down */
- connect = invert ? 1:0;
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ widget = wlist->widgets[wi];
- update.kcontrol = kcontrol;
- update.widget = widget;
- update.reg = reg;
- update.mask = mask;
- update.val = val;
- widget->dapm->update = &update;
+ widget->value = val;
- dapm_mixer_update_power(widget, kcontrol, connect);
+ update.kcontrol = kcontrol;
+ update.widget = widget;
+ update.reg = reg;
+ update.mask = mask;
+ update.val = val;
+ widget->dapm->update = &update;
- widget->dapm->update = NULL;
+ dapm_mixer_update_power(widget, kcontrol, connect);
+
+ widget->dapm->update = NULL;
+ }
}
- mutex_unlock(&widget->codec->mutex);
+ mutex_unlock(&codec->mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, bitmask;
int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, mux, change;
unsigned int mask, bitmask;
struct snd_soc_dapm_update update;
+ int wi;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
;
mask |= (bitmask - 1) << e->shift_r;
}
- mutex_lock(&widget->codec->mutex);
- widget->value = val;
+ mutex_lock(&codec->mutex);
+
change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+ if (change) {
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ widget = wlist->widgets[wi];
- update.kcontrol = kcontrol;
- update.widget = widget;
- update.reg = e->reg;
- update.mask = mask;
- update.val = val;
- widget->dapm->update = &update;
+ widget->value = val;
- dapm_mux_update_power(widget, kcontrol, change, mux, e);
+ update.kcontrol = kcontrol;
+ update.widget = widget;
+ update.reg = e->reg;
+ update.mask = mask;
+ update.val = val;
+ widget->dapm->update = &update;
- widget->dapm->update = NULL;
+ dapm_mux_update_power(widget, kcontrol, change, mux, e);
- mutex_unlock(&widget->codec->mutex);
+ widget->dapm->update = NULL;
+ }
+ }
+
+ mutex_unlock(&codec->mutex);
return change;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
ucontrol->value.enumerated.item[0] = widget->value;
int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
struct soc_enum *e =
(struct soc_enum *)kcontrol->private_value;
int change;
int ret = 0;
+ int wi;
if (ucontrol->value.enumerated.item[0] >= e->max)
return -EINVAL;
- mutex_lock(&widget->codec->mutex);
+ mutex_lock(&codec->mutex);
change = widget->value != ucontrol->value.enumerated.item[0];
- widget->value = ucontrol->value.enumerated.item[0];
- dapm_mux_update_power(widget, kcontrol, change, widget->value, e);
+ if (change) {
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ widget = wlist->widgets[wi];
- mutex_unlock(&widget->codec->mutex);
+ widget->value = ucontrol->value.enumerated.item[0];
+
+ dapm_mux_update_power(widget, kcontrol, change,
+ widget->value, e);
+ }
+ }
+
+ mutex_unlock(&codec->mutex);
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int reg_val, val, mux;
int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct snd_soc_codec *codec = widget->codec;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, mux, change;
unsigned int mask;
struct snd_soc_dapm_update update;
+ int wi;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
mask |= e->mask << e->shift_r;
}
- mutex_lock(&widget->codec->mutex);
- widget->value = val;
+ mutex_lock(&codec->mutex);
+
change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+ if (change) {
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ widget = wlist->widgets[wi];
- update.kcontrol = kcontrol;
- update.widget = widget;
- update.reg = e->reg;
- update.mask = mask;
- update.val = val;
- widget->dapm->update = &update;
+ widget->value = val;
- dapm_mux_update_power(widget, kcontrol, change, mux, e);
+ update.kcontrol = kcontrol;
+ update.widget = widget;
+ update.reg = e->reg;
+ update.mask = mask;
+ update.val = val;
+ widget->dapm->update = &update;
- widget->dapm->update = NULL;
+ dapm_mux_update_power(widget, kcontrol, change, mux, e);
+
+ widget->dapm->update = NULL;
+ }
+ }
- mutex_unlock(&widget->codec->mutex);
+ mutex_unlock(&codec->mutex);
return change;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double);
int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm,
const char *pin)
{
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
- list_for_each_entry(w, &dapm->card->widgets, list) {
- if (w->dapm != dapm)
- continue;
- if (!strcmp(w->name, pin)) {
- dev_dbg(w->dapm->dev,
- "dapm: force enable pin %s\n", pin);
- w->connected = 1;
- w->force = 1;
- return 0;
- }
+ if (!w) {
+ dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
+ return -EINVAL;
}
- dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
- return -EINVAL;
+ dev_dbg(w->dapm->dev, "dapm: force enable pin %s\n", pin);
+ w->connected = 1;
+ w->force = 1;
+
+ return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin);
int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm,
const char *pin)
{
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
- list_for_each_entry(w, &dapm->card->widgets, list) {
- if (w->dapm != dapm)
- continue;
- if (!strcmp(w->name, pin))
- return w->connected;
- }
+ if (w)
+ return w->connected;
return 0;
}
int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
const char *pin)
{
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, false);
- list_for_each_entry(w, &dapm->card->widgets, list) {
- if (w->dapm != dapm)
- continue;
- if (!strcmp(w->name, pin)) {
- w->ignore_suspend = 1;
- return 0;
- }
+ if (!w) {
+ dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
+ return -EINVAL;
}
- dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
- return -EINVAL;
+ w->ignore_suspend = 1;
+
+ return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm)
{
snd_soc_dapm_sys_remove(dapm->dev);
+ dapm_debugfs_cleanup(dapm);
dapm_free_widgets(dapm);
list_del(&dapm->list);
}
gpio_handler,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
- jack->codec->dev->driver->name,
+ gpios[i].name,
&gpios[i]);
if (ret)
goto err;
* option) any later version.
*/
+#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk);
+
+static struct snd_soc_platform_driver dummy_platform;
+
+static __devinit int snd_soc_dummy_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_platform(&pdev->dev, &dummy_platform);
+}
+
+static __devexit int snd_soc_dummy_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver soc_dummy_driver = {
+ .driver = {
+ .name = "snd-soc-dummy",
+ .owner = THIS_MODULE,
+ },
+ .probe = snd_soc_dummy_probe,
+ .remove = __devexit_p(snd_soc_dummy_remove),
+};
+
+static struct platform_device *soc_dummy_dev;
+
+int __init snd_soc_util_init(void)
+{
+ int ret;
+
+ soc_dummy_dev = platform_device_alloc("snd-soc-dummy", -1);
+ if (!soc_dummy_dev)
+ return -ENOMEM;
+
+ ret = platform_device_add(soc_dummy_dev);
+ if (ret != 0) {
+ platform_device_put(soc_dummy_dev);
+ return ret;
+ }
+
+ ret = platform_driver_register(&soc_dummy_driver);
+ if (ret != 0)
+ platform_device_unregister(soc_dummy_dev);
+
+ return ret;
+}
+
+void __exit snd_soc_util_exit(void)
+{
+ platform_device_unregister(soc_dummy_dev);
+ platform_driver_unregister(&soc_dummy_driver);
+}
-config SND_TEGRA_SOC
+config SND_SOC_TEGRA
tristate "SoC Audio for the Tegra System-on-Chip"
depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA
- default m
help
Say Y or M here if you want support for SoC audio on Tegra.
-config SND_TEGRA_SOC_I2S
+config SND_SOC_TEGRA_I2S
tristate
- depends on SND_TEGRA_SOC
- default m
+ depends on SND_SOC_TEGRA
help
Say Y or M if you want to add support for codecs attached to the
Tegra I2S interface. You will also need to select the individual
machine drivers to support below.
-config SND_TEGRA_SOC_HARMONY
- tristate "SoC Audio support for Tegra Harmony reference board"
- depends on SND_TEGRA_SOC && MACH_HARMONY && I2C
- default m
- select SND_TEGRA_SOC_I2S
+config MACH_HAS_SND_SOC_TEGRA_WM8903
+ bool
+ help
+ Machines that use the SND_SOC_TEGRA_WM8903 driver should select
+ this config option, in order to allow the user to enable
+ SND_SOC_TEGRA_WM8903.
+
+config SND_SOC_TEGRA_WM8903
+ tristate "SoC Audio support for Tegra boards using a WM8903 codec"
+ depends on SND_SOC_TEGRA && I2C
+ depends on MACH_HAS_SND_SOC_TEGRA_WM8903
+ select SND_SOC_TEGRA_I2S
select SND_SOC_WM8903
help
- Say Y or M here if you want to add support for SoC audio on the
- Tegra Harmony reference board.
+ Say Y or M here if you want to add support for SoC audio on Tegra
+ boards using the WM8093 codec. Currently, the supported boards are
+ Harmony, Ventana, Seaboard, Kaen, and Aebl.
+config SND_SOC_TEGRA_TRIMSLICE
+ tristate "SoC Audio support for TrimSlice board"
+ depends on SND_SOC_TEGRA && MACH_TRIMSLICE && I2C
+ select SND_SOC_TEGRA_I2S
+ select SND_SOC_TLV320AIC23
+ help
+ Say Y or M here if you want to add support for SoC audio on the
+ TrimSlice platform.
snd-soc-tegra-i2s-objs := tegra_i2s.o
snd-soc-tegra-utils-objs += tegra_asoc_utils.o
-obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-utils.o
-obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-das.o
-obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-pcm.o
-obj-$(CONFIG_SND_TEGRA_SOC_I2S) += snd-soc-tegra-i2s.o
+obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
+obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-das.o
+obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
+obj-$(CONFIG_SND_SOC_TEGRA_I2S) += snd-soc-tegra-i2s.o
# Tegra machine Support
-snd-soc-tegra-harmony-objs := harmony.o
+snd-soc-tegra-wm8903-objs := tegra_wm8903.o
+snd-soc-tegra-trimslice-objs := trimslice.o
-obj-$(CONFIG_SND_TEGRA_SOC_HARMONY) += snd-soc-tegra-harmony.o
+obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o
+obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o
+++ /dev/null
-/*
- * harmony.c - Harmony machine ASoC driver
- *
- * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010-2011 - NVIDIA, Inc.
- *
- * Based on code copyright/by:
- *
- * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
- *
- * Copyright 2007 Wolfson Microelectronics PLC.
- * Author: Graeme Gregory
- * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <asm/mach-types.h>
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/gpio.h>
-
-#include <mach/harmony_audio.h>
-
-#include <sound/core.h>
-#include <sound/jack.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include "../codecs/wm8903.h"
-
-#include "tegra_das.h"
-#include "tegra_i2s.h"
-#include "tegra_pcm.h"
-#include "tegra_asoc_utils.h"
-
-#define DRV_NAME "tegra-snd-harmony"
-
-#define GPIO_SPKR_EN BIT(0)
-#define GPIO_INT_MIC_EN BIT(1)
-#define GPIO_EXT_MIC_EN BIT(2)
-
-struct tegra_harmony {
- struct tegra_asoc_utils_data util_data;
- struct harmony_audio_platform_data *pdata;
- int gpio_requested;
-};
-
-static int harmony_asoc_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_card *card = codec->card;
- struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card);
- int srate, mclk, mclk_change;
- int err;
-
- srate = params_rate(params);
- switch (srate) {
- case 64000:
- case 88200:
- case 96000:
- mclk = 128 * srate;
- break;
- default:
- mclk = 256 * srate;
- break;
- }
- /* FIXME: Codec only requires >= 3MHz if OSR==0 */
- while (mclk < 6000000)
- mclk *= 2;
-
- err = tegra_asoc_utils_set_rate(&harmony->util_data, srate, mclk,
- &mclk_change);
- if (err < 0) {
- dev_err(card->dev, "Can't configure clocks\n");
- return err;
- }
-
- err = snd_soc_dai_set_fmt(codec_dai,
- SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
- if (err < 0) {
- dev_err(card->dev, "codec_dai fmt not set\n");
- return err;
- }
-
- err = snd_soc_dai_set_fmt(cpu_dai,
- SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
- if (err < 0) {
- dev_err(card->dev, "cpu_dai fmt not set\n");
- return err;
- }
-
- if (mclk_change) {
- err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
- SND_SOC_CLOCK_IN);
- if (err < 0) {
- dev_err(card->dev, "codec_dai clock not set\n");
- return err;
- }
- }
-
- return 0;
-}
-
-static struct snd_soc_ops harmony_asoc_ops = {
- .hw_params = harmony_asoc_hw_params,
-};
-
-static struct snd_soc_jack harmony_hp_jack;
-
-static struct snd_soc_jack_pin harmony_hp_jack_pins[] = {
- {
- .pin = "Headphone Jack",
- .mask = SND_JACK_HEADPHONE,
- },
-};
-
-static struct snd_soc_jack_gpio harmony_hp_jack_gpios[] = {
- {
- .name = "headphone detect",
- .report = SND_JACK_HEADPHONE,
- .debounce_time = 150,
- .invert = 1,
- }
-};
-
-static struct snd_soc_jack harmony_mic_jack;
-
-static struct snd_soc_jack_pin harmony_mic_jack_pins[] = {
- {
- .pin = "Mic Jack",
- .mask = SND_JACK_MICROPHONE,
- },
-};
-
-static int harmony_event_int_spk(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct snd_soc_codec *codec = w->codec;
- struct snd_soc_card *card = codec->card;
- struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card);
- struct harmony_audio_platform_data *pdata = harmony->pdata;
-
- gpio_set_value_cansleep(pdata->gpio_spkr_en,
- SND_SOC_DAPM_EVENT_ON(event));
-
- return 0;
-}
-
-static const struct snd_soc_dapm_widget harmony_dapm_widgets[] = {
- SND_SOC_DAPM_SPK("Int Spk", harmony_event_int_spk),
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
-};
-
-static const struct snd_soc_dapm_route harmony_audio_map[] = {
- {"Headphone Jack", NULL, "HPOUTR"},
- {"Headphone Jack", NULL, "HPOUTL"},
- {"Int Spk", NULL, "ROP"},
- {"Int Spk", NULL, "RON"},
- {"Int Spk", NULL, "LOP"},
- {"Int Spk", NULL, "LON"},
- {"Mic Bias", NULL, "Mic Jack"},
- {"IN1L", NULL, "Mic Bias"},
-};
-
-static const struct snd_kcontrol_new harmony_controls[] = {
- SOC_DAPM_PIN_SWITCH("Int Spk"),
-};
-
-static int harmony_asoc_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
- struct snd_soc_card *card = codec->card;
- struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card);
- struct harmony_audio_platform_data *pdata = harmony->pdata;
- int ret;
-
- ret = gpio_request(pdata->gpio_spkr_en, "spkr_en");
- if (ret) {
- dev_err(card->dev, "cannot get spkr_en gpio\n");
- return ret;
- }
- harmony->gpio_requested |= GPIO_SPKR_EN;
-
- gpio_direction_output(pdata->gpio_spkr_en, 0);
-
- ret = gpio_request(pdata->gpio_int_mic_en, "int_mic_en");
- if (ret) {
- dev_err(card->dev, "cannot get int_mic_en gpio\n");
- return ret;
- }
- harmony->gpio_requested |= GPIO_INT_MIC_EN;
-
- /* Disable int mic; enable signal is active-high */
- gpio_direction_output(pdata->gpio_int_mic_en, 0);
-
- ret = gpio_request(pdata->gpio_ext_mic_en, "ext_mic_en");
- if (ret) {
- dev_err(card->dev, "cannot get ext_mic_en gpio\n");
- return ret;
- }
- harmony->gpio_requested |= GPIO_EXT_MIC_EN;
-
- /* Enable ext mic; enable signal is active-low */
- gpio_direction_output(pdata->gpio_ext_mic_en, 0);
-
- ret = snd_soc_add_controls(codec, harmony_controls,
- ARRAY_SIZE(harmony_controls));
- if (ret < 0)
- return ret;
-
- snd_soc_dapm_new_controls(dapm, harmony_dapm_widgets,
- ARRAY_SIZE(harmony_dapm_widgets));
-
- snd_soc_dapm_add_routes(dapm, harmony_audio_map,
- ARRAY_SIZE(harmony_audio_map));
-
- harmony_hp_jack_gpios[0].gpio = pdata->gpio_hp_det;
- snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
- &harmony_hp_jack);
- snd_soc_jack_add_pins(&harmony_hp_jack,
- ARRAY_SIZE(harmony_hp_jack_pins),
- harmony_hp_jack_pins);
- snd_soc_jack_add_gpios(&harmony_hp_jack,
- ARRAY_SIZE(harmony_hp_jack_gpios),
- harmony_hp_jack_gpios);
-
- snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
- &harmony_mic_jack);
- snd_soc_jack_add_pins(&harmony_mic_jack,
- ARRAY_SIZE(harmony_mic_jack_pins),
- harmony_mic_jack_pins);
- wm8903_mic_detect(codec, &harmony_mic_jack, SND_JACK_MICROPHONE, 0);
-
- snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
-
- snd_soc_dapm_nc_pin(dapm, "IN3L");
- snd_soc_dapm_nc_pin(dapm, "IN3R");
- snd_soc_dapm_nc_pin(dapm, "LINEOUTL");
- snd_soc_dapm_nc_pin(dapm, "LINEOUTR");
-
- snd_soc_dapm_sync(dapm);
-
- return 0;
-}
-
-static struct snd_soc_dai_link harmony_wm8903_dai = {
- .name = "WM8903",
- .stream_name = "WM8903 PCM",
- .codec_name = "wm8903.0-001a",
- .platform_name = "tegra-pcm-audio",
- .cpu_dai_name = "tegra-i2s.0",
- .codec_dai_name = "wm8903-hifi",
- .init = harmony_asoc_init,
- .ops = &harmony_asoc_ops,
-};
-
-static struct snd_soc_card snd_soc_harmony = {
- .name = "tegra-harmony",
- .dai_link = &harmony_wm8903_dai,
- .num_links = 1,
-};
-
-static __devinit int tegra_snd_harmony_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &snd_soc_harmony;
- struct tegra_harmony *harmony;
- struct harmony_audio_platform_data *pdata;
- int ret;
-
- if (!machine_is_harmony()) {
- dev_err(&pdev->dev, "Not running on Tegra Harmony!\n");
- return -ENODEV;
- }
-
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data supplied\n");
- return -EINVAL;
- }
-
- harmony = kzalloc(sizeof(struct tegra_harmony), GFP_KERNEL);
- if (!harmony) {
- dev_err(&pdev->dev, "Can't allocate tegra_harmony\n");
- return -ENOMEM;
- }
-
- harmony->pdata = pdata;
-
- ret = tegra_asoc_utils_init(&harmony->util_data, &pdev->dev);
- if (ret)
- goto err_free_harmony;
-
- card->dev = &pdev->dev;
- platform_set_drvdata(pdev, card);
- snd_soc_card_set_drvdata(card, harmony);
-
- ret = snd_soc_register_card(card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
- ret);
- goto err_clear_drvdata;
- }
-
- return 0;
-
-err_clear_drvdata:
- snd_soc_card_set_drvdata(card, NULL);
- platform_set_drvdata(pdev, NULL);
- card->dev = NULL;
- tegra_asoc_utils_fini(&harmony->util_data);
-err_free_harmony:
- kfree(harmony);
- return ret;
-}
-
-static int __devexit tegra_snd_harmony_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card);
- struct harmony_audio_platform_data *pdata = harmony->pdata;
-
- snd_soc_unregister_card(card);
-
- snd_soc_card_set_drvdata(card, NULL);
- platform_set_drvdata(pdev, NULL);
- card->dev = NULL;
-
- tegra_asoc_utils_fini(&harmony->util_data);
-
- if (harmony->gpio_requested & GPIO_EXT_MIC_EN)
- gpio_free(pdata->gpio_ext_mic_en);
- if (harmony->gpio_requested & GPIO_INT_MIC_EN)
- gpio_free(pdata->gpio_int_mic_en);
- if (harmony->gpio_requested & GPIO_SPKR_EN)
- gpio_free(pdata->gpio_spkr_en);
-
- kfree(harmony);
-
- return 0;
-}
-
-static struct platform_driver tegra_snd_harmony_driver = {
- .driver = {
- .name = DRV_NAME,
- .owner = THIS_MODULE,
- .pm = &snd_soc_pm_ops,
- },
- .probe = tegra_snd_harmony_probe,
- .remove = __devexit_p(tegra_snd_harmony_remove),
-};
-
-static int __init snd_tegra_harmony_init(void)
-{
- return platform_driver_register(&tegra_snd_harmony_driver);
-}
-module_init(snd_tegra_harmony_init);
-
-static void __exit snd_tegra_harmony_exit(void)
-{
- platform_driver_unregister(&tegra_snd_harmony_driver);
-}
-module_exit(snd_tegra_harmony_exit);
-
-MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
-MODULE_DESCRIPTION("Harmony machine ASoC driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
#include "tegra_asoc_utils.h"
int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
- int mclk, int *mclk_change)
+ int mclk)
{
int new_baseclock;
+ bool clk_change;
int err;
switch (srate) {
return -EINVAL;
}
- *mclk_change = ((new_baseclock != data->set_baseclock) ||
+ clk_change = ((new_baseclock != data->set_baseclock) ||
(mclk != data->set_mclk));
- if (!*mclk_change)
- return 0;
+ if (!clk_change)
+ return 0;
data->set_baseclock = 0;
data->set_mclk = 0;
};
int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
- int mclk, int *mclk_change);
+ int mclk);
int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
struct device *dev);
void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data);
debugfs_remove(i2s->debug);
}
#else
-static inline void tegra_i2s_debug_add(struct tegra_i2s *i2s)
+static inline void tegra_i2s_debug_add(struct tegra_i2s *i2s, int id)
{
}
--- /dev/null
+/*
+ * tegra_wm8903.c - Tegra machine ASoC driver for boards using WM8903 codec.
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010-2011 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <asm/mach-types.h>
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+
+#include <mach/tegra_wm8903_pdata.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../codecs/wm8903.h"
+
+#include "tegra_das.h"
+#include "tegra_i2s.h"
+#include "tegra_pcm.h"
+#include "tegra_asoc_utils.h"
+
+#define DRV_NAME "tegra-snd-wm8903"
+
+#define GPIO_SPKR_EN BIT(0)
+#define GPIO_HP_MUTE BIT(1)
+#define GPIO_INT_MIC_EN BIT(2)
+#define GPIO_EXT_MIC_EN BIT(3)
+
+struct tegra_wm8903 {
+ struct tegra_asoc_utils_data util_data;
+ struct tegra_wm8903_platform_data *pdata;
+ int gpio_requested;
+};
+
+static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_card *card = codec->card;
+ struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+ int srate, mclk;
+ int err;
+
+ srate = params_rate(params);
+ switch (srate) {
+ case 64000:
+ case 88200:
+ case 96000:
+ mclk = 128 * srate;
+ break;
+ default:
+ mclk = 256 * srate;
+ break;
+ }
+ /* FIXME: Codec only requires >= 3MHz if OSR==0 */
+ while (mclk < 6000000)
+ mclk *= 2;
+
+ err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
+ if (err < 0) {
+ dev_err(card->dev, "Can't configure clocks\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai fmt not set\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (err < 0) {
+ dev_err(card->dev, "cpu_dai fmt not set\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops tegra_wm8903_ops = {
+ .hw_params = tegra_wm8903_hw_params,
+};
+
+static struct snd_soc_jack tegra_wm8903_hp_jack;
+
+static struct snd_soc_jack_pin tegra_wm8903_hp_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+static struct snd_soc_jack_gpio tegra_wm8903_hp_jack_gpio = {
+ .name = "headphone detect",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 150,
+ .invert = 1,
+};
+
+static struct snd_soc_jack tegra_wm8903_mic_jack;
+
+static struct snd_soc_jack_pin tegra_wm8903_mic_jack_pins[] = {
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int tegra_wm8903_event_int_spk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+ struct tegra_wm8903_platform_data *pdata = machine->pdata;
+
+ if (!(machine->gpio_requested & GPIO_SPKR_EN))
+ return 0;
+
+ gpio_set_value_cansleep(pdata->gpio_spkr_en,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static int tegra_wm8903_event_hp(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+ struct tegra_wm8903_platform_data *pdata = machine->pdata;
+
+ if (!(machine->gpio_requested & GPIO_HP_MUTE))
+ return 0;
+
+ gpio_set_value_cansleep(pdata->gpio_hp_mute,
+ !SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget tegra_wm8903_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Int Spk", tegra_wm8903_event_int_spk),
+ SND_SOC_DAPM_HP("Headphone Jack", tegra_wm8903_event_hp),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route harmony_audio_map[] = {
+ {"Headphone Jack", NULL, "HPOUTR"},
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Int Spk", NULL, "ROP"},
+ {"Int Spk", NULL, "RON"},
+ {"Int Spk", NULL, "LOP"},
+ {"Int Spk", NULL, "LON"},
+ {"Mic Bias", NULL, "Mic Jack"},
+ {"IN1L", NULL, "Mic Bias"},
+};
+
+static const struct snd_soc_dapm_route seaboard_audio_map[] = {
+ {"Headphone Jack", NULL, "HPOUTR"},
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Int Spk", NULL, "ROP"},
+ {"Int Spk", NULL, "RON"},
+ {"Int Spk", NULL, "LOP"},
+ {"Int Spk", NULL, "LON"},
+ {"Mic Bias", NULL, "Mic Jack"},
+ {"IN1R", NULL, "Mic Bias"},
+};
+
+static const struct snd_soc_dapm_route kaen_audio_map[] = {
+ {"Headphone Jack", NULL, "HPOUTR"},
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Int Spk", NULL, "ROP"},
+ {"Int Spk", NULL, "RON"},
+ {"Int Spk", NULL, "LOP"},
+ {"Int Spk", NULL, "LON"},
+ {"Mic Bias", NULL, "Mic Jack"},
+ {"IN2R", NULL, "Mic Bias"},
+};
+
+static const struct snd_soc_dapm_route aebl_audio_map[] = {
+ {"Headphone Jack", NULL, "HPOUTR"},
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Int Spk", NULL, "LINEOUTR"},
+ {"Int Spk", NULL, "LINEOUTL"},
+ {"Mic Bias", NULL, "Mic Jack"},
+ {"IN1R", NULL, "Mic Bias"},
+};
+
+static const struct snd_kcontrol_new tegra_wm8903_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Int Spk"),
+};
+
+static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_card *card = codec->card;
+ struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+ struct tegra_wm8903_platform_data *pdata = machine->pdata;
+ int ret;
+
+ if (gpio_is_valid(pdata->gpio_spkr_en)) {
+ ret = gpio_request(pdata->gpio_spkr_en, "spkr_en");
+ if (ret) {
+ dev_err(card->dev, "cannot get spkr_en gpio\n");
+ return ret;
+ }
+ machine->gpio_requested |= GPIO_SPKR_EN;
+
+ gpio_direction_output(pdata->gpio_spkr_en, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_hp_mute)) {
+ ret = gpio_request(pdata->gpio_hp_mute, "hp_mute");
+ if (ret) {
+ dev_err(card->dev, "cannot get hp_mute gpio\n");
+ return ret;
+ }
+ machine->gpio_requested |= GPIO_HP_MUTE;
+
+ gpio_direction_output(pdata->gpio_hp_mute, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_int_mic_en)) {
+ ret = gpio_request(pdata->gpio_int_mic_en, "int_mic_en");
+ if (ret) {
+ dev_err(card->dev, "cannot get int_mic_en gpio\n");
+ return ret;
+ }
+ machine->gpio_requested |= GPIO_INT_MIC_EN;
+
+ /* Disable int mic; enable signal is active-high */
+ gpio_direction_output(pdata->gpio_int_mic_en, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_ext_mic_en)) {
+ ret = gpio_request(pdata->gpio_ext_mic_en, "ext_mic_en");
+ if (ret) {
+ dev_err(card->dev, "cannot get ext_mic_en gpio\n");
+ return ret;
+ }
+ machine->gpio_requested |= GPIO_EXT_MIC_EN;
+
+ /* Enable ext mic; enable signal is active-low */
+ gpio_direction_output(pdata->gpio_ext_mic_en, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_hp_det)) {
+ tegra_wm8903_hp_jack_gpio.gpio = pdata->gpio_hp_det;
+ snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
+ &tegra_wm8903_hp_jack);
+ snd_soc_jack_add_pins(&tegra_wm8903_hp_jack,
+ ARRAY_SIZE(tegra_wm8903_hp_jack_pins),
+ tegra_wm8903_hp_jack_pins);
+ snd_soc_jack_add_gpios(&tegra_wm8903_hp_jack,
+ 1,
+ &tegra_wm8903_hp_jack_gpio);
+ }
+
+ snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
+ &tegra_wm8903_mic_jack);
+ snd_soc_jack_add_pins(&tegra_wm8903_mic_jack,
+ ARRAY_SIZE(tegra_wm8903_mic_jack_pins),
+ tegra_wm8903_mic_jack_pins);
+ wm8903_mic_detect(codec, &tegra_wm8903_mic_jack, SND_JACK_MICROPHONE,
+ 0);
+
+ snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
+
+ /* FIXME: Calculate automatically based on DAPM routes? */
+ if (!machine_is_harmony() && !machine_is_ventana())
+ snd_soc_dapm_nc_pin(dapm, "IN1L");
+ if (!machine_is_seaboard() && !machine_is_aebl())
+ snd_soc_dapm_nc_pin(dapm, "IN1R");
+ snd_soc_dapm_nc_pin(dapm, "IN2L");
+ if (!machine_is_kaen())
+ snd_soc_dapm_nc_pin(dapm, "IN2R");
+ snd_soc_dapm_nc_pin(dapm, "IN3L");
+ snd_soc_dapm_nc_pin(dapm, "IN3R");
+
+ if (machine_is_aebl()) {
+ snd_soc_dapm_nc_pin(dapm, "LON");
+ snd_soc_dapm_nc_pin(dapm, "RON");
+ snd_soc_dapm_nc_pin(dapm, "ROP");
+ snd_soc_dapm_nc_pin(dapm, "LOP");
+ } else {
+ snd_soc_dapm_nc_pin(dapm, "LINEOUTR");
+ snd_soc_dapm_nc_pin(dapm, "LINEOUTL");
+ }
+
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link tegra_wm8903_dai = {
+ .name = "WM8903",
+ .stream_name = "WM8903 PCM",
+ .codec_name = "wm8903.0-001a",
+ .platform_name = "tegra-pcm-audio",
+ .cpu_dai_name = "tegra-i2s.0",
+ .codec_dai_name = "wm8903-hifi",
+ .init = tegra_wm8903_init,
+ .ops = &tegra_wm8903_ops,
+};
+
+static struct snd_soc_card snd_soc_tegra_wm8903 = {
+ .name = "tegra-wm8903",
+ .dai_link = &tegra_wm8903_dai,
+ .num_links = 1,
+
+ .controls = tegra_wm8903_controls,
+ .num_controls = ARRAY_SIZE(tegra_wm8903_controls),
+ .dapm_widgets = tegra_wm8903_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra_wm8903_dapm_widgets),
+};
+
+static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_tegra_wm8903;
+ struct tegra_wm8903 *machine;
+ struct tegra_wm8903_platform_data *pdata;
+ int ret;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data supplied\n");
+ return -EINVAL;
+ }
+
+ machine = kzalloc(sizeof(struct tegra_wm8903), GFP_KERNEL);
+ if (!machine) {
+ dev_err(&pdev->dev, "Can't allocate tegra_wm8903 struct\n");
+ return -ENOMEM;
+ }
+
+ machine->pdata = pdata;
+
+ ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
+ if (ret)
+ goto err_free_machine;
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+
+ if (machine_is_harmony() || machine_is_ventana()) {
+ card->dapm_routes = harmony_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(harmony_audio_map);
+ } else if (machine_is_seaboard()) {
+ card->dapm_routes = seaboard_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(seaboard_audio_map);
+ } else if (machine_is_kaen()) {
+ card->dapm_routes = kaen_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(kaen_audio_map);
+ } else {
+ card->dapm_routes = aebl_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(aebl_audio_map);
+ }
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ goto err_fini_utils;
+ }
+
+ return 0;
+
+err_fini_utils:
+ tegra_asoc_utils_fini(&machine->util_data);
+err_free_machine:
+ kfree(machine);
+ return ret;
+}
+
+static int __devexit tegra_wm8903_driver_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+ struct tegra_wm8903_platform_data *pdata = machine->pdata;
+
+ snd_soc_unregister_card(card);
+
+ tegra_asoc_utils_fini(&machine->util_data);
+
+ if (machine->gpio_requested & GPIO_EXT_MIC_EN)
+ gpio_free(pdata->gpio_ext_mic_en);
+ if (machine->gpio_requested & GPIO_INT_MIC_EN)
+ gpio_free(pdata->gpio_int_mic_en);
+ if (machine->gpio_requested & GPIO_HP_MUTE)
+ gpio_free(pdata->gpio_hp_mute);
+ if (machine->gpio_requested & GPIO_SPKR_EN)
+ gpio_free(pdata->gpio_spkr_en);
+
+ kfree(machine);
+
+ return 0;
+}
+
+static struct platform_driver tegra_wm8903_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = tegra_wm8903_driver_probe,
+ .remove = __devexit_p(tegra_wm8903_driver_remove),
+};
+
+static int __init tegra_wm8903_modinit(void)
+{
+ return platform_driver_register(&tegra_wm8903_driver);
+}
+module_init(tegra_wm8903_modinit);
+
+static void __exit tegra_wm8903_modexit(void)
+{
+ platform_driver_unregister(&tegra_wm8903_driver);
+}
+module_exit(tegra_wm8903_modexit);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra+WM8903 machine ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
--- /dev/null
+/*
+ * trimslice.c - TrimSlice machine ASoC driver
+ *
+ * Copyright (C) 2011 - CompuLab, Ltd.
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * Based on code copyright/by:
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010-2011 - NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <asm/mach-types.h>
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../codecs/tlv320aic23.h"
+
+#include "tegra_das.h"
+#include "tegra_i2s.h"
+#include "tegra_pcm.h"
+#include "tegra_asoc_utils.h"
+
+#define DRV_NAME "tegra-snd-trimslice"
+
+struct tegra_trimslice {
+ struct tegra_asoc_utils_data util_data;
+};
+
+static int trimslice_asoc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_card *card = codec->card;
+ struct tegra_trimslice *trimslice = snd_soc_card_get_drvdata(card);
+ int srate, mclk;
+ int err;
+
+ srate = params_rate(params);
+ mclk = 128 * srate;
+
+ err = tegra_asoc_utils_set_rate(&trimslice->util_data, srate, mclk);
+ if (err < 0) {
+ dev_err(card->dev, "Can't configure clocks\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai fmt not set\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (err < 0) {
+ dev_err(card->dev, "cpu_dai fmt not set\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops trimslice_asoc_ops = {
+ .hw_params = trimslice_asoc_hw_params,
+};
+
+static const struct snd_soc_dapm_widget trimslice_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Line Out", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+};
+
+static const struct snd_soc_dapm_route trimslice_audio_map[] = {
+ {"Line Out", NULL, "LOUT"},
+ {"Line Out", NULL, "ROUT"},
+
+ {"LLINEIN", NULL, "Line In"},
+ {"RLINEIN", NULL, "Line In"},
+};
+
+static int trimslice_asoc_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ snd_soc_dapm_nc_pin(dapm, "LHPOUT");
+ snd_soc_dapm_nc_pin(dapm, "RHPOUT");
+ snd_soc_dapm_nc_pin(dapm, "MICIN");
+
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link trimslice_tlv320aic23_dai = {
+ .name = "TLV320AIC23",
+ .stream_name = "AIC23",
+ .codec_name = "tlv320aic23-codec.2-001a",
+ .platform_name = "tegra-pcm-audio",
+ .cpu_dai_name = "tegra-i2s.0",
+ .codec_dai_name = "tlv320aic23-hifi",
+ .init = trimslice_asoc_init,
+ .ops = &trimslice_asoc_ops,
+};
+
+static struct snd_soc_card snd_soc_trimslice = {
+ .name = "tegra-trimslice",
+ .dai_link = &trimslice_tlv320aic23_dai,
+ .num_links = 1,
+
+ .dapm_widgets = trimslice_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(trimslice_dapm_widgets),
+ .dapm_routes = trimslice_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(trimslice_audio_map),
+};
+
+static __devinit int tegra_snd_trimslice_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_trimslice;
+ struct tegra_trimslice *trimslice;
+ int ret;
+
+ trimslice = kzalloc(sizeof(struct tegra_trimslice), GFP_KERNEL);
+ if (!trimslice) {
+ dev_err(&pdev->dev, "Can't allocate tegra_trimslice\n");
+ return -ENOMEM;
+ }
+
+ ret = tegra_asoc_utils_init(&trimslice->util_data, &pdev->dev);
+ if (ret)
+ goto err_free_trimslice;
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, trimslice);
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ goto err_fini_utils;
+ }
+
+ return 0;
+
+err_fini_utils:
+ tegra_asoc_utils_fini(&trimslice->util_data);
+err_free_trimslice:
+ kfree(trimslice);
+ return ret;
+}
+
+static int __devexit tegra_snd_trimslice_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct tegra_trimslice *trimslice = snd_soc_card_get_drvdata(card);
+
+ snd_soc_unregister_card(card);
+
+ tegra_asoc_utils_fini(&trimslice->util_data);
+
+ kfree(trimslice);
+
+ return 0;
+}
+
+static struct platform_driver tegra_snd_trimslice_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = tegra_snd_trimslice_probe,
+ .remove = __devexit_p(tegra_snd_trimslice_remove),
+};
+
+static int __init snd_tegra_trimslice_init(void)
+{
+ return platform_driver_register(&tegra_snd_trimslice_driver);
+}
+module_init(snd_tegra_trimslice_init);
+
+static void __exit snd_tegra_trimslice_exit(void)
+{
+ platform_driver_unregister(&tegra_snd_trimslice_driver);
+}
+module_exit(snd_tegra_trimslice_exit);
+
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_DESCRIPTION("Trimslice machine ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
{ 0 } /* TERMINATING ENTRY */
};
+static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 };
+/* values to write to soundcard register for all samplerates */
+static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
+static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00};
+
+enum {
+ DIGITAL_THRU_ONLY_SAMPLERATE = 3
+};
+
static void usb6fire_control_master_vol_update(struct control_runtime *rt)
{
struct comm_runtime *comm_rt = rt->chip->comm;
}
}
+static int usb6fire_control_set_rate(struct control_runtime *rt, int rate)
+{
+ int ret;
+ struct usb_device *device = rt->chip->dev;
+ struct comm_runtime *comm_rt = rt->chip->comm;
+
+ if (rate < 0 || rate >= CONTROL_N_RATES)
+ return -EINVAL;
+
+ ret = usb_set_interface(device, 1, rates_altsetting[rate]);
+ if (ret < 0)
+ return ret;
+
+ /* set soundcard clock */
+ ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rate],
+ rates_6fire_vh[rate]);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int usb6fire_control_set_channels(
+ struct control_runtime *rt, int n_analog_out,
+ int n_analog_in, bool spdif_out, bool spdif_in)
+{
+ int ret;
+ struct comm_runtime *comm_rt = rt->chip->comm;
+
+ /* enable analog inputs and outputs
+ * (one bit per stereo-channel) */
+ ret = comm_rt->write16(comm_rt, 0x02, 0x02,
+ (1 << (n_analog_out / 2)) - 1,
+ (1 << (n_analog_in / 2)) - 1);
+ if (ret < 0)
+ return ret;
+
+ /* disable digital inputs and outputs */
+ /* TODO: use spdif_x to enable/disable digital channels */
+ ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int usb6fire_control_streaming_update(struct control_runtime *rt)
+{
+ struct comm_runtime *comm_rt = rt->chip->comm;
+
+ if (comm_rt) {
+ if (!rt->usb_streaming && rt->digital_thru_switch)
+ usb6fire_control_set_rate(rt,
+ DIGITAL_THRU_ONLY_SAMPLERATE);
+ return comm_rt->write16(comm_rt, 0x02, 0x00, 0x00,
+ (rt->usb_streaming ? 0x01 : 0x00) |
+ (rt->digital_thru_switch ? 0x08 : 0x00));
+ }
+ return -EINVAL;
+}
+
static int usb6fire_control_master_vol_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
return 0;
}
+static int usb6fire_control_digital_thru_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+
+ if (rt->digital_thru_switch != ucontrol->value.integer.value[0]) {
+ rt->digital_thru_switch = ucontrol->value.integer.value[0];
+ usb6fire_control_streaming_update(rt);
+ changed = 1;
+ }
+ return changed;
+}
+
+static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = rt->digital_thru_switch;
+ return 0;
+}
+
static struct __devinitdata snd_kcontrol_new elements[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.get = usb6fire_control_opt_coax_get,
.put = usb6fire_control_opt_coax_put
},
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Thru Playback Route",
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_ctl_boolean_mono_info,
+ .get = usb6fire_control_digital_thru_get,
+ .put = usb6fire_control_digital_thru_put
+ },
{}
};
return -ENOMEM;
rt->chip = chip;
+ rt->update_streaming = usb6fire_control_streaming_update;
+ rt->set_rate = usb6fire_control_set_rate;
+ rt->set_channels = usb6fire_control_set_channels;
i = 0;
while (init_data[i].type) {
usb6fire_control_opt_coax_update(rt);
usb6fire_control_line_phono_update(rt);
usb6fire_control_master_vol_update(rt);
+ usb6fire_control_streaming_update(rt);
i = 0;
while (elements[i].name) {
CONTROL_MAX_ELEMENTS = 32
};
+enum {
+ CONTROL_RATE_44KHZ,
+ CONTROL_RATE_48KHZ,
+ CONTROL_RATE_88KHZ,
+ CONTROL_RATE_96KHZ,
+ CONTROL_RATE_176KHZ,
+ CONTROL_RATE_192KHZ,
+ CONTROL_N_RATES
+};
+
struct control_runtime {
+ int (*update_streaming)(struct control_runtime *rt);
+ int (*set_rate)(struct control_runtime *rt, int rate);
+ int (*set_channels)(struct control_runtime *rt, int n_analog_out,
+ int n_analog_in, bool spdif_out, bool spdif_in);
+
struct sfire_chip *chip;
struct snd_kcontrol *element[CONTROL_MAX_ELEMENTS];
bool opt_coax_switch;
bool line_phono_switch;
+ bool digital_thru_switch;
+ bool usb_streaming;
u8 master_vol;
};
*
* Firmware loader
*
- * Currently not working for all devices. To be able to use the device
- * in linux, it is also possible to let the windows driver upload the firmware.
- * For that, start the computer in windows and reboot.
- * As long as the device is connected to the power supply, no firmware reload
- * needs to be performed.
- *
* Author: Torsten Schenk <torsten.schenk@zoho.com>
* Created: Jan 01, 2011
* Version: 0.3.0
*/
#include <linux/firmware.h>
+#include <linux/bitrev.h>
#include "firmware.h"
#include "chip.h"
FPGA_BUFSIZE = 512, FPGA_EP = 2
};
-static const u8 BIT_REVERSE_TABLE[256] = {
- 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50,
- 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8,
- 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04,
- 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4,
- 0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c,
- 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82,
- 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32,
- 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
- 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46,
- 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6,
- 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e,
- 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1,
- 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71,
- 0xf1, 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99,
- 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25,
- 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
- 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d,
- 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3,
- 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0x0b,
- 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb,
- 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67,
- 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 0x0f, 0x8f,
- 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f,
- 0xbf, 0x7f, 0xff };
-
/*
* wMaxPacketSize of pcm endpoints.
* keep synced with rates_in_packet_size and rates_out_packet_size in pcm.c
0x94, 0x01, 0x5c, 0x02 /* alt 3: 404 EP2 and 604 EP6 (25 fpp) */
};
+static const u8 known_fw_versions[][4] = {
+ { 0x03, 0x01, 0x0b, 0x00 }
+};
+
struct ihex_record {
u16 address;
u8 len;
while (c != end) {
for (i = 0; c != end && i < FPGA_BUFSIZE; i++, c++)
- buffer[i] = BIT_REVERSE_TABLE[(u8) *c];
+ buffer[i] = byte_rev_table[(u8) *c];
ret = usb6fire_fw_fpga_write(device, buffer, i);
if (ret < 0) {
return 0;
}
+/* check, if the firmware version the devices has currently loaded
+ * is known by this driver. 'version' needs to have 4 bytes version
+ * info data. */
+static int usb6fire_fw_check(u8 *version)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(known_fw_versions); i++)
+ if (!memcmp(version, known_fw_versions + i, 4))
+ return 0;
+
+ snd_printk(KERN_ERR PREFIX "invalid fimware version in device: "
+ "%02x %02x %02x %02x. "
+ "please reconnect to power. if this failure "
+ "still happens, check your firmware installation.",
+ version[0], version[1], version[2], version[3]);
+ return -EINVAL;
+}
+
int usb6fire_fw_init(struct usb_interface *intf)
{
int i;
"firmware state.\n");
return ret;
}
- if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55
- || buffer[4] != 0x03 || buffer[5] != 0x01 || buffer[7]
- != 0x00) {
+ if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55) {
snd_printk(KERN_ERR PREFIX "unknown device firmware state "
"received from device: ");
for (i = 0; i < 8; i++)
return -EIO;
}
/* do we need fpga loader ezusb firmware? */
- if (buffer[3] == 0x01 && buffer[6] == 0x19) {
+ if (buffer[3] == 0x01) {
ret = usb6fire_fw_ezusb_upload(intf,
"6fire/dmx6firel2.ihx", 0, NULL, 0);
if (ret < 0)
return FW_NOT_READY;
}
/* do we need fpga firmware and application ezusb firmware? */
- else if (buffer[3] == 0x02 && buffer[6] == 0x0b) {
+ else if (buffer[3] == 0x02) {
+ ret = usb6fire_fw_check(buffer + 4);
+ if (ret < 0)
+ return ret;
ret = usb6fire_fw_fpga_upload(intf, "6fire/dmx6firecf.bin");
if (ret < 0)
return ret;
return FW_NOT_READY;
}
/* all fw loaded? */
- else if (buffer[3] == 0x03 && buffer[6] == 0x0b)
- return 0;
+ else if (buffer[3] == 0x03)
+ return usb6fire_fw_check(buffer + 4);
/* unknown data? */
else {
snd_printk(KERN_ERR PREFIX "unknown device firmware state "
#include "pcm.h"
#include "chip.h"
#include "comm.h"
+#include "control.h"
enum {
OUT_N_CHANNELS = 6, IN_N_CHANNELS = 4
};
/* keep next two synced with
- * FW_EP_W_MAX_PACKET_SIZE[] and RATES_MAX_PACKET_SIZE */
+ * FW_EP_W_MAX_PACKET_SIZE[] and RATES_MAX_PACKET_SIZE
+ * and CONTROL_RATE_XXX in control.h */
static const int rates_in_packet_size[] = { 228, 228, 420, 420, 404, 404 };
static const int rates_out_packet_size[] = { 228, 228, 420, 420, 604, 604 };
static const int rates[] = { 44100, 48000, 88200, 96000, 176400, 192000 };
-static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 };
static const int rates_alsaid[] = {
SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_48000,
SNDRV_PCM_RATE_88200, SNDRV_PCM_RATE_96000,
SNDRV_PCM_RATE_176400, SNDRV_PCM_RATE_192000 };
-/* values to write to soundcard register for all samplerates */
-static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
-static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00};
-
enum { /* settings for pcm */
OUT_EP = 6, IN_EP = 2, MAX_BUFSIZE = 128 * 1024
};
STREAM_STOPPING
};
-enum { /* pcm sample rates (also index into RATES_XXX[]) */
- RATE_44KHZ,
- RATE_48KHZ,
- RATE_88KHZ,
- RATE_96KHZ,
- RATE_176KHZ,
- RATE_192KHZ
-};
-
static const struct snd_pcm_hardware pcm_hw = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BATCH,
- .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
static int usb6fire_pcm_set_rate(struct pcm_runtime *rt)
{
int ret;
- struct usb_device *device = rt->chip->dev;
- struct comm_runtime *comm_rt = rt->chip->comm;
+ struct control_runtime *ctrl_rt = rt->chip->control;
- if (rt->rate >= ARRAY_SIZE(rates))
- return -EINVAL;
- /* disable streaming */
- ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x00);
+ ctrl_rt->usb_streaming = false;
+ ret = ctrl_rt->update_streaming(ctrl_rt);
if (ret < 0) {
snd_printk(KERN_ERR PREFIX "error stopping streaming while "
"setting samplerate %d.\n", rates[rt->rate]);
return ret;
}
- ret = usb_set_interface(device, 1, rates_altsetting[rt->rate]);
- if (ret < 0) {
- snd_printk(KERN_ERR PREFIX "error setting interface "
- "altsetting %d for samplerate %d.\n",
- rates_altsetting[rt->rate], rates[rt->rate]);
- return ret;
- }
-
- /* set soundcard clock */
- ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rt->rate],
- rates_6fire_vh[rt->rate]);
+ ret = ctrl_rt->set_rate(ctrl_rt, rt->rate);
if (ret < 0) {
snd_printk(KERN_ERR PREFIX "error setting samplerate %d.\n",
rates[rt->rate]);
return ret;
}
- /* enable analog inputs and outputs
- * (one bit per stereo-channel) */
- ret = comm_rt->write16(comm_rt, 0x02, 0x02,
- (1 << (OUT_N_CHANNELS / 2)) - 1,
- (1 << (IN_N_CHANNELS / 2)) - 1);
+ ret = ctrl_rt->set_channels(ctrl_rt, OUT_N_CHANNELS, IN_N_CHANNELS,
+ false, false);
if (ret < 0) {
- snd_printk(KERN_ERR PREFIX "error initializing analog channels "
+ snd_printk(KERN_ERR PREFIX "error initializing channels "
"while setting samplerate %d.\n",
rates[rt->rate]);
return ret;
}
- /* disable digital inputs and outputs */
- ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00);
- if (ret < 0) {
- snd_printk(KERN_ERR PREFIX "error initializing digital "
- "channels while setting samplerate %d.\n",
- rates[rt->rate]);
- return ret;
- }
- ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x01);
+ ctrl_rt->usb_streaming = true;
+ ret = ctrl_rt->update_streaming(ctrl_rt);
if (ret < 0) {
snd_printk(KERN_ERR PREFIX "error starting streaming while "
"setting samplerate %d.\n", rates[rt->rate]);
static void usb6fire_pcm_stream_stop(struct pcm_runtime *rt)
{
int i;
+ struct control_runtime *ctrl_rt = rt->chip->control;
if (rt->stream_state != STREAM_DISABLED) {
for (i = 0; i < PCM_N_URBS; i++) {
usb_kill_urb(&rt->in_urbs[i].instance);
usb_kill_urb(&rt->out_urbs[i].instance);
}
+ ctrl_rt->usb_streaming = false;
+ ctrl_rt->update_streaming(ctrl_rt);
rt->stream_state = STREAM_DISABLED;
}
}
unsigned int total_length = 0;
struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance);
struct snd_pcm_runtime *alsa_rt = sub->instance->runtime;
- u32 *src = (u32 *) urb->buffer;
+ u32 *src = NULL;
u32 *dest = (u32 *) (alsa_rt->dma_area + sub->dma_off
* (alsa_rt->frame_bits >> 3));
u32 *dest_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size
else
frame_count = 0;
- src = (u32 *) (urb->buffer + total_length);
+ if (alsa_rt->format == SNDRV_PCM_FORMAT_S24_LE)
+ src = (u32 *) (urb->buffer + total_length);
+ else if (alsa_rt->format == SNDRV_PCM_FORMAT_S32_LE)
+ src = (u32 *) (urb->buffer - 1 + total_length);
+ else
+ return;
src++; /* skip leading 4 bytes of every packet */
total_length += urb->packets[i].length;
for (frame = 0; frame < frame_count; frame++) {
* (alsa_rt->frame_bits >> 3));
u32 *src_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size
* (alsa_rt->frame_bits >> 3));
- u32 *dest = (u32 *) urb->buffer;
+ u32 *dest;
int bytes_per_frame = alsa_rt->channels << 2;
+ if (alsa_rt->format == SNDRV_PCM_FORMAT_S32_LE)
+ dest = (u32 *) (urb->buffer - 1);
+ else if (alsa_rt->format == SNDRV_PCM_FORMAT_S24_LE)
+ dest = (u32 *) (urb->buffer);
+ else {
+ snd_printk(KERN_ERR PREFIX "Unknown sample format.");
+ return;
+ }
+
for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
/* at least 4 header bytes for valid packet.
* after that: 32 bits per sample for analog channels */
/* all substreams closed? if so, stop streaming */
if (!rt->playback.instance && !rt->capture.instance) {
usb6fire_pcm_stream_stop(rt);
- rt->rate = -1;
+ rt->rate = ARRAY_SIZE(rates);
}
}
mutex_unlock(&rt->stream_mutex);
struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
- int i;
int ret;
if (rt->panic)
sub->period_off = 0;
if (rt->stream_state == STREAM_DISABLED) {
- for (i = 0; i < ARRAY_SIZE(rates); i++)
- if (alsa_rt->rate == rates[i]) {
- rt->rate = i;
+ for (rt->rate = 0; rt->rate < ARRAY_SIZE(rates); rt->rate++)
+ if (alsa_rt->rate == rates[rt->rate])
break;
- }
- if (i == ARRAY_SIZE(rates)) {
+ if (rt->rate == ARRAY_SIZE(rates)) {
mutex_unlock(&rt->stream_mutex);
snd_printk("invalid rate %d in prepare.\n",
alsa_rt->rate);
rt->chip = chip;
rt->stream_state = STREAM_DISABLED;
- rt->rate = -1;
+ rt->rate = ARRAY_SIZE(rates);
init_waitqueue_head(&rt->stream_wait_queue);
mutex_init(&rt->stream_mutex);
config SND_USB_6FIRE
tristate "TerraTec DMX 6Fire USB"
- depends on EXPERIMENTAL
select FW_LOADER
+ select BITREVERSE
select SND_RAWMIDI
select SND_PCM
help
Say Y here to include support for TerraTec 6fire DMX USB interface.
You will need firmware files in order to be able to use the device
- after it has been coldstarted. This driver currently does not support
- firmware loading for all devices. If you own such a device,
- you could start windows and let the windows driver upload
- the firmware. As long as you do not unplug your device from power,
- it should be usable.
+ after it has been coldstarted. An install script for the firmware
+ and further help can be found at
+ http://sixfireusb.sourceforge.net
endif # SND_USB
int err;
unsigned char data;
struct usb_device *dev = chip->dev;
+ struct uac_clock_source_descriptor *cs_desc =
+ snd_usb_find_clock_source(chip->ctrl_intf, source_id);
+
+ if (!cs_desc)
+ return 0;
+
+ /* If a clock source can't tell us whether it's valid, we assume it is */
+ if (!uac2_control_is_readable(cs_desc->bmControls, UAC2_CS_CONTROL_CLOCK_VALID))
+ return 1;
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
if (err < 0) {
snd_printk(KERN_WARNING "%s(): cannot get clock validity for id %d\n",
__func__, source_id);
- return err;
+ return 0;
}
return !!data;
#ifdef HW_CONST_DEBUG
#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args)
#else
-#define hwc_debug(fmt, args...) /**/
+#define hwc_debug(fmt, args...) do { } while(0)
#endif
#endif /* __USBAUDIO_DEBUG_H */
#include "helper.h"
#include "debug.h"
#include "clock.h"
+#include "format.h"
/*
* parse the audio format type I descriptor
append_ctl_name(kctl, control == UAC_FU_MUTE ?
" Switch" : " Volume");
if (control == UAC_FU_VOLUME) {
- kctl->tlv.c = mixer_vol_tlv;
- kctl->vd[0].access |=
- SNDRV_CTL_ELEM_ACCESS_TLV_READ |
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
check_mapped_dB(map, cval);
+ if (cval->dBmin < cval->dBmax) {
+ kctl->tlv.c = mixer_vol_tlv;
+ kctl->vd[0].access |=
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ }
}
break;
{ USB_ID(0x041e, 0x3020), 2, 1, 6, 6, 18, 0x0013 }, /* Audigy 2 NX */
{ USB_ID(0x041e, 0x3040), 2, 2, 6, 6, 2, 0x6e91 }, /* Live! 24-bit */
{ USB_ID(0x041e, 0x3042), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 */
+ { USB_ID(0x041e, 0x30df), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 Pro */
{ USB_ID(0x041e, 0x3048), 2, 2, 6, 6, 2, 0x6e91 }, /* Toshiba SB0500 */
};
usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
!value, 0, NULL, 0, 100);
+ /* USB X-Fi S51 Pro */
+ if (mixer->chip->usb_id == USB_ID(0x041e, 0x30df))
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ !value, 0, NULL, 0, 100);
else
err = snd_usb_ctl_msg(mixer->chip->dev,
usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
/* USB X-Fi S51 doesn't have a CMSS LED */
if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0)
continue;
+ /* USB X-Fi S51 Pro doesn't have one either */
+ if ((mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) && i == 0)
+ continue;
if (i > 1 && /* Live24ext has 2 LEDs only */
(mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
mixer->chip->usb_id == USB_ID(0x041e, 0x3042) ||
+ mixer->chip->usb_id == USB_ID(0x041e, 0x30df) ||
mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
break;
err = snd_ctl_add(mixer->chip->card,
case USB_ID(0x041e, 0x3020):
case USB_ID(0x041e, 0x3040):
case USB_ID(0x041e, 0x3042):
+ case USB_ID(0x041e, 0x30df):
case USB_ID(0x041e, 0x3048):
err = snd_audigy2nx_controls_create(mixer);
if (err < 0)
}
}
},
+{
+ USB_DEVICE(0x0582, 0x0127),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "Roland", */
+ /* .product_name = "GR-55", */
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_MIDI_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
/* Guillemot devices */
{
}
},
{
- USB_DEVICE(0x0763, 0x2080),
+ USB_DEVICE_VENDOR_SPEC(0x0763, 0x2080),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
/* .vendor_name = "M-Audio", */
/* .product_name = "Fast Track Ultra", */
}
},
{
- USB_DEVICE(0x0763, 0x2081),
+ USB_DEVICE_VENDOR_SPEC(0x0763, 0x2081),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
/* .vendor_name = "M-Audio", */
/* .product_name = "Fast Track Ultra 8R", */
}
},
+/* KORG devices */
+{
+ USB_DEVICE_VENDOR_SPEC(0x0944, 0x0200),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "KORG, Inc.",
+ /* .product_name = "PANDORA PX5D", */
+ .ifnum = 3,
+ .type = QUIRK_MIDI_STANDARD_INTERFACE,
+ }
+},
+
/* AKAI devices */
{
USB_DEVICE(0x09e8, 0x0062),
},
/* Native Instruments MK2 series */
+{
+ /* Komplete Audio 6 */
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x17cc,
+ .idProduct = 0x1000,
+},
{
/* Traktor Audio 6 */
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
/* Access Music VirusTI Desktop */
return snd_usb_accessmusic_boot_quirk(dev);
+ case USB_ID(0x17cc, 0x1000): /* Komplete Audio 6 */
case USB_ID(0x17cc, 0x1010): /* Traktor Audio 6 */
case USB_ID(0x17cc, 0x1020): /* Traktor Audio 10 */
return snd_usb_nativeinstruments_boot_quirk(dev);
-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
-e s/sh[234].*/sh/ )
+CC = $(CROSS_COMPILE)gcc
+AR = $(CROSS_COMPILE)ar
+
# Additional ARCH settings for x86
ifeq ($(ARCH),i386)
ARCH := x86
endif
ifeq ($(ARCH),x86_64)
- RAW_ARCH := x86_64
- ARCH := x86
- ARCH_CFLAGS := -DARCH_X86_64
- ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S
+ ARCH := x86
+ IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -xc - | tail -n 1)
+ ifeq (${IS_X86_64}, 1)
+ RAW_ARCH := x86_64
+ ARCH_CFLAGS := -DARCH_X86_64
+ ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S
+ endif
endif
#
export prefix bindir sharedir sysconfdir
-CC = $(CROSS_COMPILE)gcc
-AR = $(CROSS_COMPILE)ar
RM = rm -f
MKDIR = mkdir
FIND = find
struct perf_event_attr *attr = &evsel->attr;
int track = !evsel->idx; /* only the first counter needs these */
+ attr->inherit = !no_inherit;
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING |
PERF_FORMAT_ID;
{
struct perf_evsel *pos;
+ if (evlist->cpus->map[0] < 0)
+ no_inherit = true;
+
list_for_each_entry(pos, &evlist->entries, node) {
struct perf_event_attr *attr = &pos->attr;
/*
retry_sample_id:
attr->sample_id_all = sample_id_all_avail ? 1 : 0;
try_again:
- if (perf_evsel__open(pos, evlist->cpus, evlist->threads, group,
- !no_inherit) < 0) {
+ if (perf_evsel__open(pos, evlist->cpus, evlist->threads, group) < 0) {
int err = errno;
if (err == EPERM || err == EACCES) {
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING;
+ attr->inherit = !no_inherit;
+
if (system_wide)
- return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, false, false);
+ return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, false);
- attr->inherit = !no_inherit;
if (target_pid == -1 && target_tid == -1) {
attr->disabled = 1;
attr->enable_on_exec = 1;
}
- return perf_evsel__open_per_thread(evsel, evsel_list->threads, false, false);
+ return perf_evsel__open_per_thread(evsel, evsel_list->threads, false);
}
/*
goto out_thread_map_delete;
}
- if (perf_evsel__open_per_thread(evsel, threads, false, false) < 0) {
+ if (perf_evsel__open_per_thread(evsel, threads, false) < 0) {
pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
strerror(errno));
}
if (perf_evsel__read_on_cpu(evsel, 0, 0) < 0) {
- pr_debug("perf_evsel__open_read_on_cpu\n");
+ pr_debug("perf_evsel__read_on_cpu\n");
goto out_close_fd;
}
goto out_thread_map_delete;
}
- if (perf_evsel__open(evsel, cpus, threads, false, false) < 0) {
+ if (perf_evsel__open(evsel, cpus, threads, false) < 0) {
pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
strerror(errno));
continue;
if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) {
- pr_debug("perf_evsel__open_read_on_cpu\n");
+ pr_debug("perf_evsel__read_on_cpu\n");
err = -1;
break;
}
perf_evlist__add(evlist, evsels[i]);
- if (perf_evsel__open(evsels[i], cpus, threads, false, false) < 0) {
+ if (perf_evsel__open(evsels[i], cpus, threads, false) < 0) {
pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
strerror(errno));
}
attr->mmap = 1;
+ attr->inherit = inherit;
try_again:
if (perf_evsel__open(counter, top.evlist->cpus,
- top.evlist->threads, group, inherit) < 0) {
+ top.evlist->threads, group) < 0) {
int err = errno;
if (err == EPERM || err == EACCES) {
{
FILE *fp;
char mountpoint[MAX_PATH+1], tokens[MAX_PATH+1], type[MAX_PATH+1];
- char *token, *saved_ptr;
+ char *token, *saved_ptr = NULL;
int found = 0;
fp = fopen("/proc/mounts", "r");
#include "evlist.h"
#include "evsel.h"
#include "util.h"
+#include "debug.h"
#include <sys/mman.h>
return evlist->mmap != NULL ? 0 : -ENOMEM;
}
-static int __perf_evlist__mmap(struct perf_evlist *evlist, int cpu, int prot,
- int mask, int fd)
+static int __perf_evlist__mmap(struct perf_evlist *evlist, struct perf_evsel *evsel,
+ int cpu, int prot, int mask, int fd)
{
evlist->mmap[cpu].prev = 0;
evlist->mmap[cpu].mask = mask;
evlist->mmap[cpu].base = mmap(NULL, evlist->mmap_len, prot,
MAP_SHARED, fd, 0);
- if (evlist->mmap[cpu].base == MAP_FAILED)
+ if (evlist->mmap[cpu].base == MAP_FAILED) {
+ if (evlist->cpus->map[cpu] == -1 && evsel->attr.inherit)
+ ui__warning("Inherit is not allowed on per-task "
+ "events using mmap.\n");
return -1;
+ }
perf_evlist__add_pollfd(evlist, fd);
return 0;
if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT,
FD(first_evsel, cpu, 0)) != 0)
goto out_unmap;
- } else if (__perf_evlist__mmap(evlist, cpu, prot, mask, fd) < 0)
+ } else if (__perf_evlist__mmap(evlist, evsel, cpu,
+ prot, mask, fd) < 0)
goto out_unmap;
if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
}
static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
- struct thread_map *threads, bool group, bool inherit)
+ struct thread_map *threads, bool group)
{
int cpu, thread;
unsigned long flags = 0;
for (cpu = 0; cpu < cpus->nr; cpu++) {
int group_fd = -1;
- /*
- * Don't allow mmap() of inherited per-task counters. This
- * would create a performance issue due to all children writing
- * to the same buffer.
- *
- * FIXME:
- * Proper fix is not to pass 'inherit' to perf_evsel__open*,
- * but a 'flags' parameter, with 'group' folded there as well,
- * then introduce a PERF_O_{MMAP,GROUP,INHERIT} enum, and if
- * O_MMAP is set, emit a warning if cpu < 0 and O_INHERIT is
- * set. Lets go for the minimal fix first tho.
- */
- evsel->attr.inherit = (cpus->map[cpu] >= 0) && inherit;
for (thread = 0; thread < threads->nr; thread++) {
};
int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
- struct thread_map *threads, bool group, bool inherit)
+ struct thread_map *threads, bool group)
{
if (cpus == NULL) {
/* Work around old compiler warnings about strict aliasing */
if (threads == NULL)
threads = &empty_thread_map.map;
- return __perf_evsel__open(evsel, cpus, threads, group, inherit);
+ return __perf_evsel__open(evsel, cpus, threads, group);
}
int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
- struct cpu_map *cpus, bool group, bool inherit)
+ struct cpu_map *cpus, bool group)
{
- return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, inherit);
+ return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group);
}
int perf_evsel__open_per_thread(struct perf_evsel *evsel,
- struct thread_map *threads, bool group, bool inherit)
+ struct thread_map *threads, bool group)
{
- return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, inherit);
+ return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group);
}
static int perf_event__parse_id_sample(const union perf_event *event, u64 type,
void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
- struct cpu_map *cpus, bool group, bool inherit);
+ struct cpu_map *cpus, bool group);
int perf_evsel__open_per_thread(struct perf_evsel *evsel,
- struct thread_map *threads, bool group, bool inherit);
+ struct thread_map *threads, bool group);
int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
- struct thread_map *threads, bool group, bool inherit);
+ struct thread_map *threads, bool group);
#define perf_evsel__match(evsel, t, c) \
(evsel->attr.type == PERF_TYPE_##t && \
struct cpu_map *cpus = NULL;
struct thread_map *threads = NULL;
PyObject *pcpus = NULL, *pthreads = NULL;
- int group = 0, overwrite = 0;
- static char *kwlist[] = {"cpus", "threads", "group", "overwrite", NULL, NULL};
+ int group = 0, inherit = 0;
+ static char *kwlist[] = {"cpus", "threads", "group", "inherit", NULL, NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist,
- &pcpus, &pthreads, &group, &overwrite))
+ &pcpus, &pthreads, &group, &inherit))
return NULL;
if (pthreads != NULL)
if (pcpus != NULL)
cpus = ((struct pyrf_cpu_map *)pcpus)->cpus;
- if (perf_evsel__open(evsel, cpus, threads, group, overwrite) < 0) {
+ evsel->attr.inherit = inherit;
+ if (perf_evsel__open(evsel, cpus, threads, group) < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
int refresh)
{
struct objdump_line *pos, *n;
- struct annotation *notes = symbol__annotation(sym);
+ struct annotation *notes;
struct annotate_browser browser = {
.b = {
- .entries = ¬es->src->source,
.refresh = ui_browser__list_head_refresh,
.seek = ui_browser__list_head_seek,
.write = annotate_browser__write,
ui_helpline__push("Press <- or ESC to exit");
+ notes = symbol__annotation(sym);
+
list_for_each_entry(pos, ¬es->src->source, node) {
struct objdump_line_rb_node *rbpos;
size_t line_len = strlen(pos->line);
rbpos->idx = browser.b.nr_entries++;
}
+ browser.b.entries = ¬es->src->source,
browser.b.width += 18; /* Percentage */
ret = annotate_browser__run(&browser, evidx, refresh);
list_for_each_entry_safe(pos, n, ¬es->src->source, node) {
goto out_free_stack;
case 'a':
if (browser->selection == NULL ||
- browser->selection->map == NULL ||
+ browser->selection->sym == NULL ||
browser->selection->map->dso->annotate_warned)
continue;
goto do_annotate;