Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6: (61 commits)
Dynamic debug: fix pr_fmt() build error
Dynamic debug: allow simple quoting of words
dynamic debug: update docs
dynamic debug: combine dprintk and dynamic printk
sysfs: fix some bin_vm_ops errors
kobject: don't block for each kobject_uevent
sysfs: only allow one scheduled removal callback per kobj
Driver core: Fix device_move() vs. dpm list ordering, v2
Driver core: some cleanup on drivers/base/sys.c
Driver core: implement uevent suppress in kobject
vcs: hook sysfs devices into object lifetime instead of "binding"
driver core: fix passing platform_data
driver core: move platform_data into platform_device
sysfs: don't block indefinitely for unmapped files.
driver core: move knode_bus into private structure
driver core: move knode_driver into private structure
driver core: move klist_children into private structure
driver core: create a private portion of struct device
driver core: remove polling for driver_probe_done(v5)
sysfs: reference sysfs_dirent from sysfs inodes
...
Fixed conflicts in drivers/sh/maple/maple.c manually
diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy
new file mode 100644
index 0000000..6434f0d
--- /dev/null
+++ b/Documentation/ABI/testing/ima_policy
@@ -0,0 +1,61 @@
+What: security/ima/policy
+Date: May 2008
+Contact: Mimi Zohar <zohar@us.ibm.com>
+Description:
+ The Trusted Computing Group(TCG) runtime Integrity
+ Measurement Architecture(IMA) maintains a list of hash
+ values of executables and other sensitive system files
+ loaded into the run-time of this system. At runtime,
+ the policy can be constrained based on LSM specific data.
+ Policies are loaded into the securityfs file ima/policy
+ by opening the file, writing the rules one at a time and
+ then closing the file. The new policy takes effect after
+ the file ima/policy is closed.
+
+ rule format: action [condition ...]
+
+ action: measure | dont_measure
+ condition:= base | lsm
+ base: [[func=] [mask=] [fsmagic=] [uid=]]
+ lsm: [[subj_user=] [subj_role=] [subj_type=]
+ [obj_user=] [obj_role=] [obj_type=]]
+
+ base: func:= [BPRM_CHECK][FILE_MMAP][INODE_PERMISSION]
+ mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC]
+ fsmagic:= hex value
+ uid:= decimal value
+ lsm: are LSM specific
+
+ default policy:
+ # PROC_SUPER_MAGIC
+ dont_measure fsmagic=0x9fa0
+ # SYSFS_MAGIC
+ dont_measure fsmagic=0x62656572
+ # DEBUGFS_MAGIC
+ dont_measure fsmagic=0x64626720
+ # TMPFS_MAGIC
+ dont_measure fsmagic=0x01021994
+ # SECURITYFS_MAGIC
+ dont_measure fsmagic=0x73636673
+
+ measure func=BPRM_CHECK
+ measure func=FILE_MMAP mask=MAY_EXEC
+ measure func=INODE_PERM mask=MAY_READ uid=0
+
+ The default policy measures all executables in bprm_check,
+ all files mmapped executable in file_mmap, and all files
+ open for read by root in inode_permission.
+
+ Examples of LSM specific definitions:
+
+ SELinux:
+ # SELINUX_MAGIC
+ dont_measure fsmagic=0xF97CFF8C
+
+ dont_measure obj_type=var_log_t
+ dont_measure obj_type=auditd_log_t
+ measure subj_user=system_u func=INODE_PERM mask=MAY_READ
+ measure subj_role=system_r func=INODE_PERM mask=MAY_READ
+
+ Smack:
+ measure subj_user=_ func=INODE_PERM mask=MAY_READ
diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile
index 1462ed8..a3a83d3 100644
--- a/Documentation/DocBook/Makefile
+++ b/Documentation/DocBook/Makefile
@@ -12,7 +12,8 @@
kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \
gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
- mac80211.xml debugobjects.xml sh.xml regulator.xml
+ mac80211.xml debugobjects.xml sh.xml regulator.xml \
+ alsa-driver-api.xml writing-an-alsa-driver.xml
###
# The build process is as follows (targets):
diff --git a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl b/Documentation/DocBook/alsa-driver-api.tmpl
similarity index 87%
rename from Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl
rename to Documentation/DocBook/alsa-driver-api.tmpl
index 9d644f7..0230a96 100644
--- a/Documentation/sound/alsa/DocBook/alsa-driver-api.tmpl
+++ b/Documentation/DocBook/alsa-driver-api.tmpl
@@ -1,11 +1,11 @@
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
-
-<book>
-<?dbhtml filename="index.html">
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
<!-- ****************************************************** -->
<!-- Header -->
<!-- ****************************************************** -->
+<book id="ALSA-Driver-API">
<bookinfo>
<title>The ALSA Driver API</title>
@@ -35,6 +35,8 @@
</bookinfo>
+<toc></toc>
+
<chapter><title>Management of Cards and Devices</title>
<sect1><title>Card Management</title>
!Esound/core/init.c
@@ -71,6 +73,10 @@
!Esound/pci/ac97/ac97_codec.c
!Esound/pci/ac97/ac97_pcm.c
</sect1>
+ <sect1><title>Virtual Master Control API</title>
+!Esound/core/vmaster.c
+!Iinclude/sound/control.h
+ </sect1>
</chapter>
<chapter><title>MIDI API</title>
<sect1><title>Raw MIDI API</title>
@@ -89,6 +95,9 @@
<sect1><title>Hardware-Dependent Devices API</title>
!Esound/core/hwdep.c
</sect1>
+ <sect1><title>Jack Abstraction Layer API</title>
+!Esound/core/jack.c
+ </sect1>
<sect1><title>ISA DMA Helpers</title>
!Esound/core/isadma.c
</sect1>
diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/DocBook/writing-an-alsa-driver.tmpl
similarity index 98%
rename from Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
rename to Documentation/DocBook/writing-an-alsa-driver.tmpl
index 87a7c07a..46b08fe 100644
--- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
+++ b/Documentation/DocBook/writing-an-alsa-driver.tmpl
@@ -1,11 +1,11 @@
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
-
-<book>
-<?dbhtml filename="index.html">
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
<!-- ****************************************************** -->
<!-- Header -->
<!-- ****************************************************** -->
+<book id="Writing-an-ALSA-Driver">
<bookinfo>
<title>Writing an ALSA Driver</title>
<author>
@@ -492,9 +492,9 @@
}
/* (2) */
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
/* (3) */
err = snd_mychip_create(card, pci, &chip);
@@ -590,8 +590,9 @@
<programlisting>
<![CDATA[
struct snd_card *card;
+ int err;
....
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
]]>
</programlisting>
</informalexample>
@@ -809,26 +810,28 @@
<para>
As mentioned above, to create a card instance, call
- <function>snd_card_new()</function>.
+ <function>snd_card_create()</function>.
<informalexample>
<programlisting>
<![CDATA[
struct snd_card *card;
- card = snd_card_new(index, id, module, extra_size);
+ int err;
+ err = snd_card_create(index, id, module, extra_size, &card);
]]>
</programlisting>
</informalexample>
</para>
<para>
- The function takes four arguments, the card-index number, the
+ The function takes five arguments, the card-index number, the
id string, the module pointer (usually
<constant>THIS_MODULE</constant>),
- and the size of extra-data space. The last argument is used to
+ the size of extra-data space, and the pointer to return the
+ card instance. The extra_size argument is used to
allocate card->private_data for the
chip-specific data. Note that these data
- are allocated by <function>snd_card_new()</function>.
+ are allocated by <function>snd_card_create()</function>.
</para>
</section>
@@ -915,15 +918,16 @@
</para>
<section id="card-management-chip-specific-snd-card-new">
- <title>1. Allocating via <function>snd_card_new()</function>.</title>
+ <title>1. Allocating via <function>snd_card_create()</function>.</title>
<para>
As mentioned above, you can pass the extra-data-length
- to the 4th argument of <function>snd_card_new()</function>, i.e.
+ to the 4th argument of <function>snd_card_create()</function>, i.e.
<informalexample>
<programlisting>
<![CDATA[
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(struct mychip));
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct mychip), &card);
]]>
</programlisting>
</informalexample>
@@ -952,8 +956,8 @@
<para>
After allocating a card instance via
- <function>snd_card_new()</function> (with
- <constant>NULL</constant> on the 4th arg), call
+ <function>snd_card_create()</function> (with
+ <constant>0</constant> on the 4th arg), call
<function>kzalloc()</function>.
<informalexample>
@@ -961,7 +965,7 @@
<![CDATA[
struct snd_card *card;
struct mychip *chip;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, NULL);
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
.....
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
]]>
@@ -5750,8 +5754,9 @@
....
struct snd_card *card;
struct mychip *chip;
+ int err;
....
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, NULL);
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
....
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
....
@@ -5763,7 +5768,7 @@
</informalexample>
When you created the chip data with
- <function>snd_card_new()</function>, it's anyway accessible
+ <function>snd_card_create()</function>, it's anyway accessible
via <structfield>private_data</structfield> field.
<informalexample>
@@ -5775,9 +5780,10 @@
....
struct snd_card *card;
struct mychip *chip;
+ int err;
....
- card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct mychip));
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct mychip), &card);
....
chip = card->private_data;
....
diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt
index 5b0cfa6..ce73f3eb 100644
--- a/Documentation/cpu-freq/governors.txt
+++ b/Documentation/cpu-freq/governors.txt
@@ -117,10 +117,28 @@
sampling_rate: measured in uS (10^-6 seconds), this is how often you
want the kernel to look at the CPU usage and to make decisions on
what to do about the frequency. Typically this is set to values of
-around '10000' or more.
+around '10000' or more. It's default value is (cmp. with users-guide.txt):
+transition_latency * 1000
+The lowest value you can set is:
+transition_latency * 100 or it may get restricted to a value where it
+makes not sense for the kernel anymore to poll that often which depends
+on your HZ config variable (HZ=1000: max=20000us, HZ=250: max=5000).
+Be aware that transition latency is in ns and sampling_rate is in us, so you
+get the same sysfs value by default.
+Sampling rate should always get adjusted considering the transition latency
+To set the sampling rate 750 times as high as the transition latency
+in the bash (as said, 1000 is default), do:
+echo `$(($(cat cpuinfo_transition_latency) * 750 / 1000)) \
+ >ondemand/sampling_rate
-show_sampling_rate_(min|max): the minimum and maximum sampling rates
-available that you may set 'sampling_rate' to.
+show_sampling_rate_(min|max): THIS INTERFACE IS DEPRECATED, DON'T USE IT.
+You can use wider ranges now and the general
+cpuinfo_transition_latency variable (cmp. with user-guide.txt) can be
+used to obtain exactly the same info:
+show_sampling_rate_min = transtition_latency * 500 / 1000
+show_sampling_rate_max = transtition_latency * 500000 / 1000
+(divided by 1000 is to illustrate that sampling rate is in us and
+transition latency is exported ns).
up_threshold: defines what the average CPU usage between the samplings
of 'sampling_rate' needs to be for the kernel to make a decision on
diff --git a/Documentation/cpu-freq/user-guide.txt b/Documentation/cpu-freq/user-guide.txt
index 917918f..75f4119 100644
--- a/Documentation/cpu-freq/user-guide.txt
+++ b/Documentation/cpu-freq/user-guide.txt
@@ -152,6 +152,18 @@
frequency the processor can run at(in kHz)
cpuinfo_max_freq : this file shows the maximum operating
frequency the processor can run at(in kHz)
+cpuinfo_transition_latency The time it takes on this CPU to
+ switch between two frequencies in nano
+ seconds. If unknown or known to be
+ that high that the driver does not
+ work with the ondemand governor, -1
+ (CPUFREQ_ETERNAL) will be returned.
+ Using this information can be useful
+ to choose an appropriate polling
+ frequency for a kernel governor or
+ userspace daemon. Make sure to not
+ switch the frequency too often
+ resulting in performance loss.
scaling_driver : this file shows what cpufreq driver is
used to set the frequency on this CPU
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 3a1aa8a..1a29ff3 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -44,6 +44,7 @@
FB The frame buffer device is enabled.
HW Appropriate hardware is enabled.
IA-64 IA-64 architecture is enabled.
+ IMA Integrity measurement architecture is enabled.
IOSCHED More than one I/O scheduler is enabled.
IP_PNP IP DHCP, BOOTP, or RARP is enabled.
ISAPNP ISA PnP code is enabled.
@@ -902,6 +903,15 @@
ihash_entries= [KNL]
Set number of hash buckets for inode cache.
+ ima_audit= [IMA]
+ Format: { "0" | "1" }
+ 0 -- integrity auditing messages. (Default)
+ 1 -- enable informational integrity auditing messages.
+
+ ima_hash= [IMA]
+ Formt: { "sha1" | "md5" }
+ default: "sha1"
+
in2000= [HW,SCSI]
See header of drivers/scsi/in2000.c.
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index 841a936..012858d 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -346,6 +346,9 @@
sbirq - IRQ # for CMI8330 chip (SB16)
sbdma8 - 8bit DMA # for CMI8330 chip (SB16)
sbdma16 - 16bit DMA # for CMI8330 chip (SB16)
+ fmport - (optional) OPL3 I/O port
+ mpuport - (optional) MPU401 I/O port
+ mpuirq - (optional) MPU401 irq #
This module supports multiple cards and autoprobe.
@@ -388,34 +391,11 @@
The power-management is supported.
- Module snd-cs4232
- -----------------
-
- Module for sound cards based on CS4232/CS4232A ISA chips.
-
- isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
-
- with isapnp=0, the following options are available:
-
- port - port # for CS4232 chip (PnP setup - 0x534)
- cport - control port # for CS4232 chip (PnP setup - 0x120,0x210,0xf00)
- mpu_port - port # for MPU-401 UART (PnP setup - 0x300), -1 = disable
- fm_port - FM port # for CS4232 chip (PnP setup - 0x388), -1 = disable
- irq - IRQ # for CS4232 chip (5,7,9,11,12,15)
- mpu_irq - IRQ # for MPU-401 UART (9,11,12,15)
- dma1 - first DMA # for CS4232 chip (0,1,3)
- dma2 - second DMA # for Yamaha CS4232 chip (0,1,3), -1 = disable
-
- This module supports multiple cards. This module does not support autoprobe
- (if ISA PnP is not used) thus main port must be specified!!! Other ports are
- optional.
-
- The power-management is supported.
-
Module snd-cs4236
-----------------
- Module for sound cards based on CS4235/CS4236/CS4236B/CS4237B/
+ Module for sound cards based on CS4232/CS4232A,
+ CS4235/CS4236/CS4236B/CS4237B/
CS4238B/CS4239 ISA chips.
isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
@@ -437,6 +417,9 @@
The power-management is supported.
+ This module is aliased as snd-cs4232 since it provides the old
+ snd-cs4232 functionality, too.
+
Module snd-cs4281
-----------------
@@ -606,6 +589,7 @@
Module for ESS AudioDrive ES-1688 and ES-688 sound cards.
port - port # for ES-1688 chip (0x220,0x240,0x260)
+ fm_port - port # for OPL3 (option; share the same port as default)
mpu_port - port # for MPU-401 port (0x300,0x310,0x320,0x330), -1 = disable (default)
irq - IRQ # for ES-1688 chip (5,7,9,10)
mpu_irq - IRQ # for MPU-401 port (5,7,9,10)
@@ -757,6 +741,9 @@
model - force the model name
position_fix - Fix DMA pointer (0 = auto, 1 = use LPIB, 2 = POSBUF)
probe_mask - Bitmask to probe codecs (default = -1, meaning all slots)
+ When the bit 8 (0x100) is set, the lower 8 bits are used
+ as the "fixed" codec slots; i.e. the driver probes the
+ slots regardless what hardware reports back
probe_only - Only probing and no codec initialization (default=off);
Useful to check the initial codec status for debugging
bdl_pos_adj - Specifies the DMA IRQ timing delay in samples.
@@ -1185,6 +1172,54 @@
This module supports multiple devices and PnP.
+ Module snd-msnd-classic
+ -----------------------
+
+ Module for Turtle Beach MultiSound Classic, Tahiti or Monterey
+ soundcards.
+
+ io - Port # for msnd-classic card
+ irq - IRQ # for msnd-classic card
+ mem - Memory address (0xb0000, 0xc8000, 0xd0000, 0xd8000,
+ 0xe0000 or 0xe8000)
+ write_ndelay - enable write ndelay (default = 1)
+ calibrate_signal - calibrate signal (default = 0)
+ isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
+ digital - Digital daughterboard present (default = 0)
+ cfg - Config port (0x250, 0x260 or 0x270) default = PnP
+ reset - Reset all devices
+ mpu_io - MPU401 I/O port
+ mpu_irq - MPU401 irq#
+ ide_io0 - IDE port #0
+ ide_io1 - IDE port #1
+ ide_irq - IDE irq#
+ joystick_io - Joystick I/O port
+
+ The driver requires firmware files "turtlebeach/msndinit.bin" and
+ "turtlebeach/msndperm.bin" in the proper firmware directory.
+
+ See Documentation/sound/oss/MultiSound for important information
+ about this driver. Note that it has been discontinued, but the
+ Voyetra Turtle Beach knowledge base entry for it is still available
+ at
+ http://www.turtlebeach.com/site/kb_ftp/790.asp
+
+ Module snd-msnd-pinnacle
+ ------------------------
+
+ Module for Turtle Beach MultiSound Pinnacle/Fiji soundcards.
+
+ io - Port # for pinnacle/fiji card
+ irq - IRQ # for pinnalce/fiji card
+ mem - Memory address (0xb0000, 0xc8000, 0xd0000, 0xd8000,
+ 0xe0000 or 0xe8000)
+ write_ndelay - enable write ndelay (default = 1)
+ calibrate_signal - calibrate signal (default = 0)
+ isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
+
+ The driver requires firmware files "turtlebeach/pndspini.bin" and
+ "turtlebeach/pndsperm.bin" in the proper firmware directory.
+
Module snd-mtpav
----------------
@@ -1824,7 +1859,7 @@
-------------------
Module for sound cards based on the Asus AV100/AV200 chips,
- i.e., Xonar D1, DX, D2, D2X and HDAV1.3 (Deluxe).
+ i.e., Xonar D1, DX, D2, D2X, HDAV1.3 (Deluxe), and Essence STX.
This module supports autoprobe and multiple cards.
diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt
index 0f5d26b..8eec05b 100644
--- a/Documentation/sound/alsa/HD-Audio-Models.txt
+++ b/Documentation/sound/alsa/HD-Audio-Models.txt
@@ -56,6 +56,7 @@
sony-assamd Sony ASSAMD
toshiba-s06 Toshiba S06
toshiba-rx1 Toshiba RX1
+ tyan Tyan Thunder n6650W (S2915-E)
ultra Samsung Q1 Ultra Vista model
lenovo-3000 Lenovo 3000 y410
nec NEC Versa S9100
@@ -261,6 +262,8 @@
=============
laptop Basic Laptop config (default)
hp HP Spartan laptop
+ hp-dv6736 HP dv6736
+ lenovo-x200 Lenovo X200 laptop
STAC9200
========
@@ -278,6 +281,7 @@
gateway-m4 Gateway laptops with EAPD control
gateway-m4-2 Gateway laptops with EAPD control
panasonic Panasonic CF-74
+ auto BIOS setup (default)
STAC9205/9254
=============
@@ -285,6 +289,8 @@
dell-m42 Dell (unknown)
dell-m43 Dell Precision
dell-m44 Dell Inspiron
+ eapd Keep EAPD on (e.g. Gateway T1616)
+ auto BIOS setup (default)
STAC9220/9221
=============
@@ -308,6 +314,7 @@
dell-d82 Dell (unknown)
dell-m81 Dell (unknown)
dell-m82 Dell XPS M1210
+ auto BIOS setup (default)
STAC9202/9250/9251
==================
@@ -319,6 +326,7 @@
m3 Some Gateway MX series laptops
m5 Some Gateway MX series laptops (MP6954)
m6 Some Gateway NX series laptops
+ auto BIOS setup (default)
STAC9227/9228/9229/927x
=======================
@@ -328,6 +336,7 @@
5stack D965 5stack + SPDIF
dell-3stack Dell Dimension E520
dell-bios Fixes with Dell BIOS setup
+ auto BIOS setup (default)
STAC92HD71B*
============
@@ -335,7 +344,10 @@
dell-m4-1 Dell desktops
dell-m4-2 Dell desktops
dell-m4-3 Dell desktops
- hp-m4 HP dv laptops
+ hp-m4 HP mini 1000
+ hp-dv5 HP dv series
+ hp-hdx HP HDX series
+ auto BIOS setup (default)
STAC92HD73*
===========
@@ -345,13 +357,16 @@
dell-m6-dmic Dell desktops/laptops with digital mics
dell-m6 Dell desktops/laptops with both type of mics
dell-eq Dell desktops/laptops
+ auto BIOS setup (default)
STAC92HD83*
===========
ref Reference board
mic-ref Reference board with power managment for ports
+ dell-s14 Dell laptop
+ auto BIOS setup (default)
STAC9872
========
- vaio Setup for VAIO FE550G/SZ110
- vaio-ar Setup for VAIO AR
+ vaio VAIO laptop without SPDIF
+ auto BIOS setup (default)
diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt
index 8d68fff..c5948f2 100644
--- a/Documentation/sound/alsa/HD-Audio.txt
+++ b/Documentation/sound/alsa/HD-Audio.txt
@@ -109,6 +109,13 @@
Since 2.6.29 kernel, the driver has a more robust probing method, so
this error might happen rarely, though.
+On a machine with a broken BIOS, sometimes you need to force the
+driver to probe the codec slots the hardware doesn't report for use.
+In such a case, turn the bit 8 (0x100) of `probe_mask` option on.
+Then the rest 8 bits are passed as the codec slots to probe
+unconditionally. For example, `probe_mask=0x103` will force to probe
+the codec slots 0 and 1 no matter what the hardware reports.
+
Interrupt Handling
~~~~~~~~~~~~~~~~~~
@@ -358,10 +365,26 @@
to this file.
init_verbs::
The extra verbs to execute at initialization. You can add a verb by
- writing to this file. Pass tree numbers, nid, verb and parameter.
+ writing to this file. Pass three numbers: nid, verb and parameter
+ (separated with a space).
hints::
- Shows hint strings for codec parsers for any use. Right now it's
- not used.
+ Shows / stores hint strings for codec parsers for any use.
+ Its format is `key = value`. For example, passing `hp_detect = yes`
+ to IDT/STAC codec parser will result in the disablement of the
+ headphone detection.
+init_pin_configs::
+ Shows the initial pin default config values set by BIOS.
+driver_pin_configs::
+ Shows the pin default values set by the codec parser explicitly.
+ This doesn't show all pin values but only the changed values by
+ the parser. That is, if the parser doesn't change the pin default
+ config values by itself, this will contain nothing.
+user_pin_configs::
+ Shows the pin default config values to override the BIOS setup.
+ Writing this (with two numbers, NID and value) appends the new
+ value. The given will be used instead of the initial BIOS value at
+ the next reconfiguration time. Note that this config will override
+ even the driver pin configs, too.
reconfig::
Triggers the codec re-configuration. When any value is written to
this file, the driver re-initialize and parses the codec tree
@@ -371,6 +394,14 @@
Resets the codec, removes the mixer elements and PCM stuff of the
specified codec, and clear all init verbs and hints.
+For example, when you want to change the pin default configuration
+value of the pin widget 0x14 to 0x9993013f, and let the driver
+re-configure based on that state, run like below:
+------------------------------------------------------------------------
+ # echo 0x14 0x9993013f > /sys/class/sound/hwC0D0/user_pin_configs
+ # echo 1 > /sys/class/sound/hwC0D0/reconfig
+------------------------------------------------------------------------
+
Power-Saving
~~~~~~~~~~~~
@@ -461,6 +492,16 @@
There are some other useful options. See `--help` option output for
details.
+When a probe error occurs or when the driver obviously assigns a
+mismatched model, it'd be helpful to load the driver with
+`probe_only=1` option (at best after the cold reboot) and run
+alsa-info at this state. With this option, the driver won't configure
+the mixer and PCM but just tries to probe the codec slot. After
+probing, the proc file is available, so you can get the raw codec
+information before modified by the driver. Of course, the driver
+isn't usable with `probe_only=1`. But you can continue the
+configuration via hwdep sysfs file if hda-reconfig option is enabled.
+
hda-verb
~~~~~~~~
diff --git a/Documentation/sound/alsa/soc/dapm.txt b/Documentation/sound/alsa/soc/dapm.txt
index 46f9684..9e67632 100644
--- a/Documentation/sound/alsa/soc/dapm.txt
+++ b/Documentation/sound/alsa/soc/dapm.txt
@@ -116,6 +116,9 @@
SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls,
ARRAY_SIZE(wm8731_output_mixer_controls)),
+If you dont want the mixer elements prefixed with the name of the mixer widget,
+you can use SND_SOC_DAPM_MIXER_NAMED_CTL instead. the parameters are the same
+as for SND_SOC_DAPM_MIXER.
2.3 Platform/Machine domain Widgets
-----------------------------------
diff --git a/Documentation/sound/oss/CS4232 b/Documentation/sound/oss/CS4232
deleted file mode 100644
index 7d6af7a..0000000
--- a/Documentation/sound/oss/CS4232
+++ /dev/null
@@ -1,23 +0,0 @@
-To configure the Crystal CS423x sound chip and activate its DSP functions,
-modules may be loaded in this order:
-
- modprobe sound
- insmod ad1848
- insmod uart401
- insmod cs4232 io=* irq=* dma=* dma2=*
-
-This is the meaning of the parameters:
-
- io--I/O address of the Windows Sound System (normally 0x534)
- irq--IRQ of this device
- dma and dma2--DMA channels (DMA2 may be 0)
-
-On some cards, the board attempts to do non-PnP setup, and fails. If you
-have problems, use Linux' PnP facilities.
-
-To get MIDI facilities add
-
- insmod opl3 io=*
-
-where "io" is the I/O address of the OPL3 synthesizer. This will be shown
-in /proc/sys/pnp and is normally 0x388.
diff --git a/Documentation/sound/oss/Introduction b/Documentation/sound/oss/Introduction
index f04ba6b..75d967f 100644
--- a/Documentation/sound/oss/Introduction
+++ b/Documentation/sound/oss/Introduction
@@ -80,7 +80,7 @@
additional features.
2. The commercial OSS driver may be obtained from the site:
- http://www/opensound.com. This may be used for cards that
+ http://www.opensound.com. This may be used for cards that
are unsupported by the kernel driver, or may be used
by other operating systems.
diff --git a/MAINTAINERS b/MAINTAINERS
index 5d460c9..1978fb2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1269,6 +1269,12 @@
T: git kernel.org:/pub/scm/linux/kernel/git/herbert/crypto-2.6.git
S: Maintained
+CRYPTOGRAPHIC RANDOM NUMBER GENERATOR
+P: Neil Horman
+M: nhorman@tuxdriver.com
+L: linux-crypto@vger.kernel.org
+S: Maintained
+
CS5535 Audio ALSA driver
P: Jaya Kumar
M: jayakumar.alsa@gmail.com
@@ -2216,6 +2222,11 @@
L: linux1394-devel@lists.sourceforge.net
S: Maintained
+INTEGRITY MEASUREMENT ARCHITECTURE (IMA)
+P: Mimi Zohar
+M: zohar@us.ibm.com
+S: Supported
+
IMS TWINTURBO FRAMEBUFFER DRIVER
L: linux-fbdev-devel@lists.sourceforge.net (moderated for non-subscribers)
S: Orphan
@@ -3844,6 +3855,7 @@
L: linux-kernel@vger.kernel.org
L: linux-security-module@vger.kernel.org (suggested Cc:)
T: git kernel.org:pub/scm/linux/kernel/git/jmorris/security-testing-2.6.git
+W: http://security.wiki.kernel.org/
S: Supported
SECURITY CONTACT
@@ -4285,6 +4297,19 @@
W: http://sourceforge.net/projects/tlan/
S: Maintained
+TOMOYO SECURITY MODULE
+P: Kentaro Takeda
+M: takedakn@nttdata.co.jp
+P: Tetsuo Handa
+M: penguin-kernel@I-love.SAKURA.ne.jp
+L: linux-kernel@vger.kernel.org (kernel issues)
+L: tomoyo-users-en@lists.sourceforge.jp (subscribers-only, for developers and users in English)
+L: tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese)
+L: tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese)
+W: http://tomoyo.sourceforge.jp/
+T: quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.2.x/tomoyo-lsm/patches/
+S: Maintained
+
TOSHIBA ACPI EXTRAS DRIVER
P: John Belmonte
M: toshiba_acpi@memebeam.org
diff --git a/arch/arm/mach-pxa/e740.c b/arch/arm/mach-pxa/e740.c
index 6d48e00..a6fff78 100644
--- a/arch/arm/mach-pxa/e740.c
+++ b/arch/arm/mach-pxa/e740.c
@@ -135,6 +135,11 @@
/* IrDA */
GPIO38_GPIO | MFP_LPM_DRIVE_HIGH,
+ /* Audio power control */
+ GPIO16_GPIO, /* AC97 codec AVDD2 supply (analogue power) */
+ GPIO40_GPIO, /* Mic amp power */
+ GPIO41_GPIO, /* Headphone amp power */
+
/* PC Card */
GPIO8_GPIO, /* CD0 */
GPIO44_GPIO, /* CD1 */
diff --git a/arch/arm/mach-pxa/e750.c b/arch/arm/mach-pxa/e750.c
index be1ab8e..665066f 100644
--- a/arch/arm/mach-pxa/e750.c
+++ b/arch/arm/mach-pxa/e750.c
@@ -133,6 +133,11 @@
/* IrDA */
GPIO38_GPIO | MFP_LPM_DRIVE_HIGH,
+ /* Audio power control */
+ GPIO4_GPIO, /* Headphone amp power */
+ GPIO7_GPIO, /* Speaker amp power */
+ GPIO37_GPIO, /* Headphone detect */
+
/* PC Card */
GPIO8_GPIO, /* CD0 */
GPIO44_GPIO, /* CD1 */
diff --git a/arch/arm/mach-pxa/h5000.c b/arch/arm/mach-pxa/h5000.c
index da6e442..295ec41 100644
--- a/arch/arm/mach-pxa/h5000.c
+++ b/arch/arm/mach-pxa/h5000.c
@@ -153,6 +153,13 @@
GPIO23_SSP1_SCLK,
GPIO25_SSP1_TXD,
GPIO26_SSP1_RXD,
+
+ /* I2S */
+ GPIO28_I2S_BITCLK_OUT,
+ GPIO29_I2S_SDATA_IN,
+ GPIO30_I2S_SDATA_OUT,
+ GPIO31_I2S_SYNC,
+ GPIO32_I2S_SYSCLK,
};
/*
diff --git a/arch/arm/mach-pxa/include/mach/eseries-gpio.h b/arch/arm/mach-pxa/include/mach/eseries-gpio.h
index efbd2aa..f3e5509 100644
--- a/arch/arm/mach-pxa/include/mach/eseries-gpio.h
+++ b/arch/arm/mach-pxa/include/mach/eseries-gpio.h
@@ -45,6 +45,21 @@
/* e7xx IrDA power control */
#define GPIO_E7XX_IR_OFF 38
+/* e740 audio control GPIOs */
+#define GPIO_E740_WM9705_nAVDD2 16
+#define GPIO_E740_MIC_ON 40
+#define GPIO_E740_AMP_ON 41
+
+/* e750 audio control GPIOs */
+#define GPIO_E750_HP_AMP_OFF 4
+#define GPIO_E750_SPK_AMP_OFF 7
+#define GPIO_E750_HP_DETECT 37
+
+/* e800 audio control GPIOs */
+#define GPIO_E800_HP_DETECT 81
+#define GPIO_E800_HP_AMP_OFF 82
+#define GPIO_E800_SPK_AMP_ON 83
+
/* ASIC related GPIOs */
#define GPIO_ESERIES_TMIO_IRQ 5
#define GPIO_ESERIES_TMIO_PCLR 19
diff --git a/arch/arm/mach-pxa/include/mach/regs-ssp.h b/arch/arm/mach-pxa/include/mach/regs-ssp.h
index cf31986..018f6d6 100644
--- a/arch/arm/mach-pxa/include/mach/regs-ssp.h
+++ b/arch/arm/mach-pxa/include/mach/regs-ssp.h
@@ -50,7 +50,7 @@
#define SSCR0_TUM (1 << 23) /* Transmit FIFO underrun interrupt mask */
#define SSCR0_FRDC (0x07000000) /* Frame rate divider control (mask) */
#define SSCR0_SlotsPerFrm(x) (((x) - 1) << 24) /* Time slots per frame [1..8] */
-#define SSCR0_ADC (1 << 30) /* Audio clock select */
+#define SSCR0_ACS (1 << 30) /* Audio clock select */
#define SSCR0_MOD (1 << 31) /* Mode (normal or network) */
#endif
@@ -109,6 +109,11 @@
#define SSSR_TINT (1 << 19) /* Receiver Time-out Interrupt */
#define SSSR_PINT (1 << 18) /* Peripheral Trailing Byte Interrupt */
+#if defined(CONFIG_PXA3xx)
+#define SSPSP_EDMYSTOP(x) ((x) << 28) /* Extended Dummy Stop */
+#define SSPSP_EDMYSTRT(x) ((x) << 26) /* Extended Dummy Start */
+#endif
+
#define SSPSP_FSRT (1 << 25) /* Frame Sync Relative Timing */
#define SSPSP_DMYSTOP(x) ((x) << 23) /* Dummy Stop */
#define SSPSP_SFRMWDTH(x) ((x) << 16) /* Serial Frame Width */
diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c
index 6d447c9..0d62d31 100644
--- a/arch/arm/mach-pxa/spitz.c
+++ b/arch/arm/mach-pxa/spitz.c
@@ -105,6 +105,12 @@
GPIO57_nIOIS16,
GPIO104_PSKTSEL,
+ /* I2S */
+ GPIO28_I2S_BITCLK_OUT,
+ GPIO29_I2S_SDATA_IN,
+ GPIO30_I2S_SDATA_OUT,
+ GPIO31_I2S_SYNC,
+
/* MMC */
GPIO32_MMC_CLK,
GPIO112_MMC_CMD,
diff --git a/arch/arm/mach-s3c2410/dma.c b/arch/arm/mach-s3c2410/dma.c
index 552b4c7..440c014 100644
--- a/arch/arm/mach-s3c2410/dma.c
+++ b/arch/arm/mach-s3c2410/dma.c
@@ -28,7 +28,7 @@
#include <mach/regs-mem.h>
#include <mach/regs-lcd.h>
#include <mach/regs-sdi.h>
-#include <asm/plat-s3c24xx/regs-iis.h>
+#include <plat/regs-iis.h>
#include <plat/regs-spi.h>
static struct s3c24xx_dma_map __initdata s3c2410_dma_mappings[] = {
diff --git a/arch/arm/mach-s3c2412/dma.c b/arch/arm/mach-s3c2412/dma.c
index 919856c..9e34785 100644
--- a/arch/arm/mach-s3c2412/dma.c
+++ b/arch/arm/mach-s3c2412/dma.c
@@ -29,8 +29,8 @@
#include <mach/regs-mem.h>
#include <mach/regs-lcd.h>
#include <mach/regs-sdi.h>
-#include <asm/plat-s3c24xx/regs-s3c2412-iis.h>
-#include <asm/plat-s3c24xx/regs-iis.h>
+#include <plat/regs-s3c2412-iis.h>
+#include <plat/regs-iis.h>
#include <plat/regs-spi.h>
#define MAP(x) { (x)| DMA_CH_VALID, (x)| DMA_CH_VALID, (x)| DMA_CH_VALID, (x)| DMA_CH_VALID }
diff --git a/arch/arm/mach-s3c2440/dma.c b/arch/arm/mach-s3c2440/dma.c
index 5b5ee0b..69b6cf3 100644
--- a/arch/arm/mach-s3c2440/dma.c
+++ b/arch/arm/mach-s3c2440/dma.c
@@ -28,7 +28,7 @@
#include <mach/regs-mem.h>
#include <mach/regs-lcd.h>
#include <mach/regs-sdi.h>
-#include <asm/plat-s3c24xx/regs-iis.h>
+#include <plat/regs-iis.h>
#include <plat/regs-spi.h>
static struct s3c24xx_dma_map __initdata s3c2440_dma_mappings[] = {
diff --git a/arch/arm/mach-s3c2443/dma.c b/arch/arm/mach-s3c2443/dma.c
index 2a58a4d..8430e58 100644
--- a/arch/arm/mach-s3c2443/dma.c
+++ b/arch/arm/mach-s3c2443/dma.c
@@ -29,7 +29,7 @@
#include <mach/regs-mem.h>
#include <mach/regs-lcd.h>
#include <mach/regs-sdi.h>
-#include <asm/plat-s3c24xx/regs-iis.h>
+#include <plat/regs-iis.h>
#include <plat/regs-spi.h>
#define MAP(x) { \
diff --git a/arch/arm/mach-s3c2410/include/mach/audio.h b/arch/arm/plat-s3c/include/plat/audio.h
similarity index 100%
rename from arch/arm/mach-s3c2410/include/mach/audio.h
rename to arch/arm/plat-s3c/include/plat/audio.h
diff --git a/include/asm-arm/plat-s3c24xx/regs-s3c2412-iis.h b/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
similarity index 93%
rename from include/asm-arm/plat-s3c24xx/regs-s3c2412-iis.h
rename to arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
index 25d4058..0fad757 100644
--- a/include/asm-arm/plat-s3c24xx/regs-s3c2412-iis.h
+++ b/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
@@ -33,6 +33,9 @@
#define S3C2412_IISCON_RXDMA_ACTIVE (1 << 1)
#define S3C2412_IISCON_IIS_ACTIVE (1 << 0)
+#define S3C64XX_IISMOD_IMS_PCLK (0 << 10)
+#define S3C64XX_IISMOD_IMS_SYSMUX (1 << 10)
+
#define S3C2412_IISMOD_MASTER_INTERNAL (0 << 10)
#define S3C2412_IISMOD_MASTER_EXTERNAL (1 << 10)
#define S3C2412_IISMOD_SLAVE (2 << 10)
@@ -44,8 +47,8 @@
#define S3C2412_IISMOD_LR_LLOW (0 << 7)
#define S3C2412_IISMOD_LR_RLOW (1 << 7)
#define S3C2412_IISMOD_SDF_IIS (0 << 5)
-#define S3C2412_IISMOD_SDF_MSB (0 << 5)
-#define S3C2412_IISMOD_SDF_LSB (0 << 5)
+#define S3C2412_IISMOD_SDF_MSB (1 << 5)
+#define S3C2412_IISMOD_SDF_LSB (2 << 5)
#define S3C2412_IISMOD_SDF_MASK (3 << 5)
#define S3C2412_IISMOD_RCLK_256FS (0 << 3)
#define S3C2412_IISMOD_RCLK_512FS (1 << 3)
diff --git a/include/asm-arm/plat-s3c24xx/regs-iis.h b/arch/arm/plat-s3c24xx/include/plat/regs-iis.h
similarity index 100%
rename from include/asm-arm/plat-s3c24xx/regs-iis.h
rename to arch/arm/plat-s3c24xx/include/plat/regs-iis.h
diff --git a/arch/avr32/boards/hammerhead/flash.c b/arch/avr32/boards/hammerhead/flash.c
index a98c6dd..559bbcb0 100644
--- a/arch/avr32/boards/hammerhead/flash.c
+++ b/arch/avr32/boards/hammerhead/flash.c
@@ -15,7 +15,6 @@
#include <linux/mtd/physmap.h>
#include <linux/usb/isp116x.h>
#include <linux/dma-mapping.h>
-#include <linux/platform_device.h>
#include <linux/delay.h>
#include <mach/portmux.h>
diff --git a/arch/avr32/include/asm/uaccess.h b/arch/avr32/include/asm/uaccess.h
index ed09239..245b2ee2 100644
--- a/arch/avr32/include/asm/uaccess.h
+++ b/arch/avr32/include/asm/uaccess.h
@@ -230,10 +230,10 @@
asm volatile( \
"1: ld." suffix " %1, %3 \n" \
"2: \n" \
- " .section .fixup, \"ax\" \n" \
+ " .subsection 1 \n" \
"3: mov %0, %4 \n" \
" rjmp 2b \n" \
- " .previous \n" \
+ " .subsection 0 \n" \
" .section __ex_table, \"a\" \n" \
" .long 1b, 3b \n" \
" .previous \n" \
@@ -295,10 +295,10 @@
asm volatile( \
"1: st." suffix " %1, %3 \n" \
"2: \n" \
- " .section .fixup, \"ax\" \n" \
+ " .subsection 1 \n" \
"3: mov %0, %4 \n" \
" rjmp 2b \n" \
- " .previous \n" \
+ " .subsection 0 \n" \
" .section __ex_table, \"a\" \n" \
" .long 1b, 3b \n" \
" .previous \n" \
diff --git a/arch/avr32/kernel/entry-avr32b.S b/arch/avr32/kernel/entry-avr32b.S
index 33d4937..009a801 100644
--- a/arch/avr32/kernel/entry-avr32b.S
+++ b/arch/avr32/kernel/entry-avr32b.S
@@ -150,10 +150,10 @@
tlbmiss_restore
sub sp, 4
stmts --sp, r0-lr
- rcall save_full_context_ex
+ call save_full_context_ex
mfsr r12, SYSREG_ECR
mov r11, sp
- rcall do_page_fault
+ call do_page_fault
rjmp ret_from_exception
.align 2
@@ -250,7 +250,7 @@
.global ret_from_fork
ret_from_fork:
- rcall schedule_tail
+ call schedule_tail
/* check for syscall tracing */
get_thread_info r0
@@ -261,7 +261,7 @@
syscall_trace_enter:
pushm r8-r12
- rcall syscall_trace
+ call syscall_trace
popm r8-r12
rjmp syscall_trace_cont
@@ -269,14 +269,14 @@
bld r1, TIF_SYSCALL_TRACE
brcc 1f
unmask_interrupts
- rcall syscall_trace
+ call syscall_trace
mask_interrupts
ld.w r1, r0[TI_flags]
1: bld r1, TIF_NEED_RESCHED
brcc 2f
unmask_interrupts
- rcall schedule
+ call schedule
mask_interrupts
ld.w r1, r0[TI_flags]
rjmp 1b
@@ -287,7 +287,7 @@
unmask_interrupts
mov r12, sp
mov r11, r0
- rcall do_notify_resume
+ call do_notify_resume
mask_interrupts
ld.w r1, r0[TI_flags]
rjmp 1b
@@ -394,7 +394,7 @@
mfsr r12, SYSREG_ECR
mov r11, sp
- rcall do_critical_exception
+ call do_critical_exception
/* We should never get here... */
bad_return:
@@ -407,18 +407,18 @@
do_bus_error_write:
sub sp, 4
stmts --sp, r0-lr
- rcall save_full_context_ex
+ call save_full_context_ex
mov r11, 1
rjmp 1f
do_bus_error_read:
sub sp, 4
stmts --sp, r0-lr
- rcall save_full_context_ex
+ call save_full_context_ex
mov r11, 0
1: mfsr r12, SYSREG_BEAR
mov r10, sp
- rcall do_bus_error
+ call do_bus_error
rjmp ret_from_exception
.align 1
@@ -433,7 +433,7 @@
1: pushm r8, r9 /* PC and SR */
mfsr r12, SYSREG_ECR
mov r11, sp
- rcall do_nmi
+ call do_nmi
popm r8-r9
mtsr SYSREG_RAR_NMI, r8
tst r0, r0
@@ -457,29 +457,29 @@
handle_address_fault:
sub sp, 4
stmts --sp, r0-lr
- rcall save_full_context_ex
+ call save_full_context_ex
mfsr r12, SYSREG_ECR
mov r11, sp
- rcall do_address_exception
+ call do_address_exception
rjmp ret_from_exception
handle_protection_fault:
sub sp, 4
stmts --sp, r0-lr
- rcall save_full_context_ex
+ call save_full_context_ex
mfsr r12, SYSREG_ECR
mov r11, sp
- rcall do_page_fault
+ call do_page_fault
rjmp ret_from_exception
.align 1
do_illegal_opcode_ll:
sub sp, 4
stmts --sp, r0-lr
- rcall save_full_context_ex
+ call save_full_context_ex
mfsr r12, SYSREG_ECR
mov r11, sp
- rcall do_illegal_opcode
+ call do_illegal_opcode
rjmp ret_from_exception
do_dtlb_modified:
@@ -513,11 +513,11 @@
do_fpe_ll:
sub sp, 4
stmts --sp, r0-lr
- rcall save_full_context_ex
+ call save_full_context_ex
unmask_interrupts
mov r12, 26
mov r11, sp
- rcall do_fpe
+ call do_fpe
rjmp ret_from_exception
ret_from_exception:
@@ -553,7 +553,7 @@
lddsp r4, sp[REG_SR]
bld r4, SYSREG_GM_OFFSET
brcs 1f
- rcall preempt_schedule_irq
+ call preempt_schedule_irq
1:
#endif
@@ -582,7 +582,7 @@
bld r1, TIF_NEED_RESCHED
brcc 1f
unmask_interrupts
- rcall schedule
+ call schedule
mask_interrupts
ld.w r1, r0[TI_flags]
rjmp fault_exit_work
@@ -593,7 +593,7 @@
unmask_interrupts
mov r12, sp
mov r11, r0
- rcall do_notify_resume
+ call do_notify_resume
mask_interrupts
ld.w r1, r0[TI_flags]
rjmp fault_exit_work
@@ -616,10 +616,10 @@
.Ldebug_fixup_cont:
#ifdef CONFIG_TRACE_IRQFLAGS
- rcall trace_hardirqs_off
+ call trace_hardirqs_off
#endif
mov r12, sp
- rcall do_debug
+ call do_debug
mov sp, r12
lddsp r2, sp[REG_SR]
@@ -643,7 +643,7 @@
mtsr SYSREG_RSR_DBG, r11
mtsr SYSREG_RAR_DBG, r10
#ifdef CONFIG_TRACE_IRQFLAGS
- rcall trace_hardirqs_on
+ call trace_hardirqs_on
1:
#endif
ldmts sp++, r0-lr
@@ -676,7 +676,7 @@
#ifdef CONFIG_TRACE_IRQFLAGS
bld r11, SYSREG_GM_OFFSET
brcc 1f
- rcall trace_hardirqs_on
+ call trace_hardirqs_on
1:
#endif
mfsr r2, SYSREG_SR
@@ -747,7 +747,7 @@
mov r11, sp
mov r12, \level
- rcall do_IRQ
+ call do_IRQ
lddsp r4, sp[REG_SR]
bfextu r4, r4, SYSREG_M0_OFFSET, 3
@@ -767,7 +767,7 @@
1:
#ifdef CONFIG_TRACE_IRQFLAGS
- rcall trace_hardirqs_on
+ call trace_hardirqs_on
#endif
popm r8-r9
mtsr rar_int\level, r8
@@ -807,7 +807,7 @@
lddsp r4, sp[REG_SR]
bld r4, SYSREG_GM_OFFSET
brcs 1b
- rcall preempt_schedule_irq
+ call preempt_schedule_irq
#endif
rjmp 1b
.endm
diff --git a/arch/avr32/kernel/syscall-stubs.S b/arch/avr32/kernel/syscall-stubs.S
index 673178e..f7244cd 100644
--- a/arch/avr32/kernel/syscall-stubs.S
+++ b/arch/avr32/kernel/syscall-stubs.S
@@ -61,7 +61,7 @@
__sys_mmap2:
pushm lr
st.w --sp, ARG6
- rcall sys_mmap2
+ call sys_mmap2
sub sp, -4
popm pc
@@ -70,7 +70,7 @@
__sys_sendto:
pushm lr
st.w --sp, ARG6
- rcall sys_sendto
+ call sys_sendto
sub sp, -4
popm pc
@@ -79,7 +79,7 @@
__sys_recvfrom:
pushm lr
st.w --sp, ARG6
- rcall sys_recvfrom
+ call sys_recvfrom
sub sp, -4
popm pc
@@ -88,7 +88,7 @@
__sys_pselect6:
pushm lr
st.w --sp, ARG6
- rcall sys_pselect6
+ call sys_pselect6
sub sp, -4
popm pc
@@ -97,7 +97,7 @@
__sys_splice:
pushm lr
st.w --sp, ARG6
- rcall sys_splice
+ call sys_splice
sub sp, -4
popm pc
@@ -106,7 +106,7 @@
__sys_epoll_pwait:
pushm lr
st.w --sp, ARG6
- rcall sys_epoll_pwait
+ call sys_epoll_pwait
sub sp, -4
popm pc
@@ -115,6 +115,6 @@
__sys_sync_file_range:
pushm lr
st.w --sp, ARG6
- rcall sys_sync_file_range
+ call sys_sync_file_range
sub sp, -4
popm pc
diff --git a/arch/avr32/lib/strnlen_user.S b/arch/avr32/lib/strnlen_user.S
index 65ce11a..e46f4724 100644
--- a/arch/avr32/lib/strnlen_user.S
+++ b/arch/avr32/lib/strnlen_user.S
@@ -48,7 +48,7 @@
lddpc lr, _task_size
sub r11, lr, r12
mov r9, r11
- rcall __strnlen_user
+ call __strnlen_user
cp.w r12, r9
brgt 1f
popm pc
diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h
index 0417516..526f327 100644
--- a/arch/mips/include/asm/mipsregs.h
+++ b/arch/mips/include/asm/mipsregs.h
@@ -1391,11 +1391,11 @@
static inline unsigned int \
set_c0_##name(unsigned int set) \
{ \
- unsigned int res; \
+ unsigned int res, new; \
\
res = read_c0_##name(); \
- res |= set; \
- write_c0_##name(res); \
+ new = res | set; \
+ write_c0_##name(new); \
\
return res; \
} \
@@ -1403,24 +1403,24 @@
static inline unsigned int \
clear_c0_##name(unsigned int clear) \
{ \
- unsigned int res; \
+ unsigned int res, new; \
\
res = read_c0_##name(); \
- res &= ~clear; \
- write_c0_##name(res); \
+ new = res & ~clear; \
+ write_c0_##name(new); \
\
return res; \
} \
\
static inline unsigned int \
-change_c0_##name(unsigned int change, unsigned int new) \
+change_c0_##name(unsigned int change, unsigned int val) \
{ \
- unsigned int res; \
+ unsigned int res, new; \
\
res = read_c0_##name(); \
- res &= ~change; \
- res |= (new & change); \
- write_c0_##name(res); \
+ new = res & ~change; \
+ new |= (val & change); \
+ write_c0_##name(new); \
\
return res; \
}
diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c
index 1a86f84..49aac6e 100644
--- a/arch/mips/kernel/linux32.c
+++ b/arch/mips/kernel/linux32.c
@@ -32,7 +32,6 @@
#include <linux/module.h>
#include <linux/binfmts.h>
#include <linux/security.h>
-#include <linux/syscalls.h>
#include <linux/compat.h>
#include <linux/vfs.h>
#include <linux/ipc.h>
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index b2d7041..29fadac 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -1520,7 +1520,9 @@
#endif /* CONFIG_MIPS_MT_SMTC */
if (cpu_has_veic || cpu_has_vint) {
+ unsigned long sr = set_c0_status(ST0_BEV);
write_c0_ebase(ebase);
+ write_c0_status(sr);
/* Setting vector spacing enables EI/VI mode */
change_c0_intctl(0x3e0, VECTORSPACING);
}
@@ -1602,8 +1604,6 @@
#ifdef CONFIG_64BIT
unsigned long uncached_ebase = TO_UNCAC(ebase);
#endif
- if (cpu_has_mips_r2)
- uncached_ebase += (read_c0_ebase() & 0x3ffff000);
if (!addr)
panic(panic_null_cerr);
@@ -1635,9 +1635,11 @@
return; /* Already done */
#endif
- if (cpu_has_veic || cpu_has_vint)
- ebase = (unsigned long) alloc_bootmem_low_pages(0x200 + VECTORSPACING*64);
- else {
+ if (cpu_has_veic || cpu_has_vint) {
+ unsigned long size = 0x200 + VECTORSPACING*64;
+ ebase = (unsigned long)
+ __alloc_bootmem(size, 1 << fls(size), 0);
+ } else {
ebase = CAC_BASE;
if (cpu_has_mips_r2)
ebase += (read_c0_ebase() & 0x3ffff000);
diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
index c43f4b2..871e828 100644
--- a/arch/mips/mm/c-r4k.c
+++ b/arch/mips/mm/c-r4k.c
@@ -780,7 +780,7 @@
c->dcache.ways = 2;
c->dcache.waybit = 0;
- c->options |= MIPS_CPU_CACHE_CDEX_P;
+ c->options |= MIPS_CPU_CACHE_CDEX_P | MIPS_CPU_PREFETCH;
break;
case CPU_TX49XX:
diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c
index 546e697..bed56f1 100644
--- a/arch/mips/mm/dma-default.c
+++ b/arch/mips/mm/dma-default.c
@@ -225,7 +225,7 @@
if (!plat_device_is_coherent(dev) && direction != DMA_TO_DEVICE) {
unsigned long addr;
- addr = plat_dma_addr_to_phys(dma_address);
+ addr = dma_addr_to_virt(dma_address);
dma_cache_wback_inv(addr, size);
}
diff --git a/arch/powerpc/boot/dts/canyonlands.dts b/arch/powerpc/boot/dts/canyonlands.dts
index 8b5ba82..4447def 100644
--- a/arch/powerpc/boot/dts/canyonlands.dts
+++ b/arch/powerpc/boot/dts/canyonlands.dts
@@ -127,6 +127,13 @@
dcr-reg = <0x010 0x002>;
};
+ CRYPTO: crypto@180000 {
+ compatible = "amcc,ppc460ex-crypto", "amcc,ppc4xx-crypto";
+ reg = <4 0x00180000 0x80400>;
+ interrupt-parent = <&UIC0>;
+ interrupts = <0x1d 0x4>;
+ };
+
MAL0: mcmal {
compatible = "ibm,mcmal-460ex", "ibm,mcmal2";
dcr-reg = <0x180 0x062>;
diff --git a/arch/powerpc/boot/dts/kilauea.dts b/arch/powerpc/boot/dts/kilauea.dts
index 2804444..5e6b08f 100644
--- a/arch/powerpc/boot/dts/kilauea.dts
+++ b/arch/powerpc/boot/dts/kilauea.dts
@@ -97,6 +97,13 @@
0x6 0x4>; /* ECC SEC Error */
};
+ CRYPTO: crypto@ef700000 {
+ compatible = "amcc,ppc405ex-crypto", "amcc,ppc4xx-crypto";
+ reg = <0xef700000 0x80400>;
+ interrupt-parent = <&UIC0>;
+ interrupts = <0x17 0x2>;
+ };
+
MAL0: mcmal {
compatible = "ibm,mcmal-405ex", "ibm,mcmal2";
dcr-reg = <0x180 0x062>;
diff --git a/arch/s390/crypto/sha.h b/arch/s390/crypto/sha.h
index 1ceafa5..f4e9dc7 100644
--- a/arch/s390/crypto/sha.h
+++ b/arch/s390/crypto/sha.h
@@ -29,7 +29,9 @@
int func; /* KIMD function to use */
};
-void s390_sha_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len);
-void s390_sha_final(struct crypto_tfm *tfm, u8 *out);
+struct shash_desc;
+
+int s390_sha_update(struct shash_desc *desc, const u8 *data, unsigned int len);
+int s390_sha_final(struct shash_desc *desc, u8 *out);
#endif
diff --git a/arch/s390/crypto/sha1_s390.c b/arch/s390/crypto/sha1_s390.c
index b3cb5a8..e85ba34 100644
--- a/arch/s390/crypto/sha1_s390.c
+++ b/arch/s390/crypto/sha1_s390.c
@@ -23,17 +23,17 @@
* any later version.
*
*/
+#include <crypto/internal/hash.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/crypto.h>
#include <crypto/sha.h>
#include "crypt_s390.h"
#include "sha.h"
-static void sha1_init(struct crypto_tfm *tfm)
+static int sha1_init(struct shash_desc *desc)
{
- struct s390_sha_ctx *sctx = crypto_tfm_ctx(tfm);
+ struct s390_sha_ctx *sctx = shash_desc_ctx(desc);
sctx->state[0] = SHA1_H0;
sctx->state[1] = SHA1_H1;
@@ -42,34 +42,36 @@
sctx->state[4] = SHA1_H4;
sctx->count = 0;
sctx->func = KIMD_SHA_1;
+
+ return 0;
}
-static struct crypto_alg alg = {
- .cra_name = "sha1",
- .cra_driver_name= "sha1-s390",
- .cra_priority = CRYPT_S390_PRIORITY,
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = SHA1_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct s390_sha_ctx),
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(alg.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = SHA1_DIGEST_SIZE,
- .dia_init = sha1_init,
- .dia_update = s390_sha_update,
- .dia_final = s390_sha_final } }
+static struct shash_alg alg = {
+ .digestsize = SHA1_DIGEST_SIZE,
+ .init = sha1_init,
+ .update = s390_sha_update,
+ .final = s390_sha_final,
+ .descsize = sizeof(struct s390_sha_ctx),
+ .base = {
+ .cra_name = "sha1",
+ .cra_driver_name= "sha1-s390",
+ .cra_priority = CRYPT_S390_PRIORITY,
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA1_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
static int __init sha1_s390_init(void)
{
if (!crypt_s390_func_available(KIMD_SHA_1))
return -EOPNOTSUPP;
- return crypto_register_alg(&alg);
+ return crypto_register_shash(&alg);
}
static void __exit sha1_s390_fini(void)
{
- crypto_unregister_alg(&alg);
+ crypto_unregister_shash(&alg);
}
module_init(sha1_s390_init);
diff --git a/arch/s390/crypto/sha256_s390.c b/arch/s390/crypto/sha256_s390.c
index 19c03fb..f9fefc5 100644
--- a/arch/s390/crypto/sha256_s390.c
+++ b/arch/s390/crypto/sha256_s390.c
@@ -16,17 +16,17 @@
* any later version.
*
*/
+#include <crypto/internal/hash.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/crypto.h>
#include <crypto/sha.h>
#include "crypt_s390.h"
#include "sha.h"
-static void sha256_init(struct crypto_tfm *tfm)
+static int sha256_init(struct shash_desc *desc)
{
- struct s390_sha_ctx *sctx = crypto_tfm_ctx(tfm);
+ struct s390_sha_ctx *sctx = shash_desc_ctx(desc);
sctx->state[0] = SHA256_H0;
sctx->state[1] = SHA256_H1;
@@ -38,22 +38,24 @@
sctx->state[7] = SHA256_H7;
sctx->count = 0;
sctx->func = KIMD_SHA_256;
+
+ return 0;
}
-static struct crypto_alg alg = {
- .cra_name = "sha256",
- .cra_driver_name = "sha256-s390",
- .cra_priority = CRYPT_S390_PRIORITY,
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = SHA256_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct s390_sha_ctx),
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(alg.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = SHA256_DIGEST_SIZE,
- .dia_init = sha256_init,
- .dia_update = s390_sha_update,
- .dia_final = s390_sha_final } }
+static struct shash_alg alg = {
+ .digestsize = SHA256_DIGEST_SIZE,
+ .init = sha256_init,
+ .update = s390_sha_update,
+ .final = s390_sha_final,
+ .descsize = sizeof(struct s390_sha_ctx),
+ .base = {
+ .cra_name = "sha256",
+ .cra_driver_name= "sha256-s390",
+ .cra_priority = CRYPT_S390_PRIORITY,
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA256_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
static int sha256_s390_init(void)
@@ -61,12 +63,12 @@
if (!crypt_s390_func_available(KIMD_SHA_256))
return -EOPNOTSUPP;
- return crypto_register_alg(&alg);
+ return crypto_register_shash(&alg);
}
static void __exit sha256_s390_fini(void)
{
- crypto_unregister_alg(&alg);
+ crypto_unregister_shash(&alg);
}
module_init(sha256_s390_init);
diff --git a/arch/s390/crypto/sha512_s390.c b/arch/s390/crypto/sha512_s390.c
index 23c7861..83192bf 100644
--- a/arch/s390/crypto/sha512_s390.c
+++ b/arch/s390/crypto/sha512_s390.c
@@ -12,16 +12,16 @@
* any later version.
*
*/
+#include <crypto/internal/hash.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/crypto.h>
#include "sha.h"
#include "crypt_s390.h"
-static void sha512_init(struct crypto_tfm *tfm)
+static int sha512_init(struct shash_desc *desc)
{
- struct s390_sha_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct s390_sha_ctx *ctx = shash_desc_ctx(desc);
*(__u64 *)&ctx->state[0] = 0x6a09e667f3bcc908ULL;
*(__u64 *)&ctx->state[2] = 0xbb67ae8584caa73bULL;
@@ -33,29 +33,31 @@
*(__u64 *)&ctx->state[14] = 0x5be0cd19137e2179ULL;
ctx->count = 0;
ctx->func = KIMD_SHA_512;
+
+ return 0;
}
-static struct crypto_alg sha512_alg = {
- .cra_name = "sha512",
- .cra_driver_name = "sha512-s390",
- .cra_priority = CRYPT_S390_PRIORITY,
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = SHA512_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct s390_sha_ctx),
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(sha512_alg.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = SHA512_DIGEST_SIZE,
- .dia_init = sha512_init,
- .dia_update = s390_sha_update,
- .dia_final = s390_sha_final } }
+static struct shash_alg sha512_alg = {
+ .digestsize = SHA512_DIGEST_SIZE,
+ .init = sha512_init,
+ .update = s390_sha_update,
+ .final = s390_sha_final,
+ .descsize = sizeof(struct s390_sha_ctx),
+ .base = {
+ .cra_name = "sha512",
+ .cra_driver_name= "sha512-s390",
+ .cra_priority = CRYPT_S390_PRIORITY,
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA512_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
MODULE_ALIAS("sha512");
-static void sha384_init(struct crypto_tfm *tfm)
+static int sha384_init(struct shash_desc *desc)
{
- struct s390_sha_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct s390_sha_ctx *ctx = shash_desc_ctx(desc);
*(__u64 *)&ctx->state[0] = 0xcbbb9d5dc1059ed8ULL;
*(__u64 *)&ctx->state[2] = 0x629a292a367cd507ULL;
@@ -67,22 +69,25 @@
*(__u64 *)&ctx->state[14] = 0x47b5481dbefa4fa4ULL;
ctx->count = 0;
ctx->func = KIMD_SHA_512;
+
+ return 0;
}
-static struct crypto_alg sha384_alg = {
- .cra_name = "sha384",
- .cra_driver_name = "sha384-s390",
- .cra_priority = CRYPT_S390_PRIORITY,
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = SHA384_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct s390_sha_ctx),
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(sha384_alg.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = SHA384_DIGEST_SIZE,
- .dia_init = sha384_init,
- .dia_update = s390_sha_update,
- .dia_final = s390_sha_final } }
+static struct shash_alg sha384_alg = {
+ .digestsize = SHA384_DIGEST_SIZE,
+ .init = sha384_init,
+ .update = s390_sha_update,
+ .final = s390_sha_final,
+ .descsize = sizeof(struct s390_sha_ctx),
+ .base = {
+ .cra_name = "sha384",
+ .cra_driver_name= "sha384-s390",
+ .cra_priority = CRYPT_S390_PRIORITY,
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA384_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct s390_sha_ctx),
+ .cra_module = THIS_MODULE,
+ }
};
MODULE_ALIAS("sha384");
@@ -93,18 +98,18 @@
if (!crypt_s390_func_available(KIMD_SHA_512))
return -EOPNOTSUPP;
- if ((ret = crypto_register_alg(&sha512_alg)) < 0)
+ if ((ret = crypto_register_shash(&sha512_alg)) < 0)
goto out;
- if ((ret = crypto_register_alg(&sha384_alg)) < 0)
- crypto_unregister_alg(&sha512_alg);
+ if ((ret = crypto_register_shash(&sha384_alg)) < 0)
+ crypto_unregister_shash(&sha512_alg);
out:
return ret;
}
static void __exit fini(void)
{
- crypto_unregister_alg(&sha512_alg);
- crypto_unregister_alg(&sha384_alg);
+ crypto_unregister_shash(&sha512_alg);
+ crypto_unregister_shash(&sha384_alg);
}
module_init(init);
diff --git a/arch/s390/crypto/sha_common.c b/arch/s390/crypto/sha_common.c
index 9d6eb8c..7903ec4 100644
--- a/arch/s390/crypto/sha_common.c
+++ b/arch/s390/crypto/sha_common.c
@@ -13,14 +13,14 @@
*
*/
-#include <linux/crypto.h>
+#include <crypto/internal/hash.h>
#include "sha.h"
#include "crypt_s390.h"
-void s390_sha_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
+int s390_sha_update(struct shash_desc *desc, const u8 *data, unsigned int len)
{
- struct s390_sha_ctx *ctx = crypto_tfm_ctx(tfm);
- unsigned int bsize = crypto_tfm_alg_blocksize(tfm);
+ struct s390_sha_ctx *ctx = shash_desc_ctx(desc);
+ unsigned int bsize = crypto_shash_blocksize(desc->tfm);
unsigned int index;
int ret;
@@ -51,13 +51,15 @@
store:
if (len)
memcpy(ctx->buf + index , data, len);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(s390_sha_update);
-void s390_sha_final(struct crypto_tfm *tfm, u8 *out)
+int s390_sha_final(struct shash_desc *desc, u8 *out)
{
- struct s390_sha_ctx *ctx = crypto_tfm_ctx(tfm);
- unsigned int bsize = crypto_tfm_alg_blocksize(tfm);
+ struct s390_sha_ctx *ctx = shash_desc_ctx(desc);
+ unsigned int bsize = crypto_shash_blocksize(desc->tfm);
u64 bits;
unsigned int index, end, plen;
int ret;
@@ -87,9 +89,11 @@
BUG_ON(ret != end);
/* copy digest to out */
- memcpy(out, ctx->state, crypto_hash_digestsize(crypto_hash_cast(tfm)));
+ memcpy(out, ctx->state, crypto_shash_digestsize(desc->tfm));
/* wipe context */
memset(ctx, 0, sizeof *ctx);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(s390_sha_final);
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index ebabe51..8d50d52 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -107,6 +107,9 @@
config SYS_SUPPORTS_PCI
bool
+config SYS_SUPPORTS_CMT
+ bool
+
config STACKTRACE_SUPPORT
def_bool y
@@ -176,6 +179,10 @@
config CPU_SHX3
bool
+config ARCH_SHMOBILE
+ bool
+ select ARCH_SUSPEND_POSSIBLE
+
choice
prompt "Processor sub-type selection"
@@ -188,6 +195,7 @@
config CPU_SUBTYPE_SH7619
bool "Support SH7619 processor"
select CPU_SH2
+ select SYS_SUPPORTS_CMT
# SH-2A Processor Support
@@ -200,15 +208,18 @@
bool "Support SH7203 processor"
select CPU_SH2A
select CPU_HAS_FPU
+ select SYS_SUPPORTS_CMT
config CPU_SUBTYPE_SH7206
bool "Support SH7206 processor"
select CPU_SH2A
+ select SYS_SUPPORTS_CMT
config CPU_SUBTYPE_SH7263
bool "Support SH7263 processor"
select CPU_SH2A
select CPU_HAS_FPU
+ select SYS_SUPPORTS_CMT
config CPU_SUBTYPE_MXG
bool "Support MX-G processor"
@@ -323,7 +334,9 @@
bool "Support SH7723 processor"
select CPU_SH4A
select CPU_SHX2
+ select ARCH_SHMOBILE
select ARCH_SPARSEMEM_ENABLE
+ select SYS_SUPPORTS_CMT
help
Select SH7723 if you have an SH-MobileR2 CPU.
@@ -348,6 +361,14 @@
select ARCH_SPARSEMEM_ENABLE
select SYS_SUPPORTS_NUMA
+config CPU_SUBTYPE_SH7786
+ bool "Support SH7786 processor"
+ select CPU_SH4A
+ select CPU_SHX3
+ select CPU_HAS_PTEAEX
+ select ARCH_SPARSEMEM_ENABLE
+ select SYS_SUPPORTS_NUMA
+
config CPU_SUBTYPE_SHX3
bool "Support SH-X3 processor"
select CPU_SH4A
@@ -362,20 +383,26 @@
config CPU_SUBTYPE_SH7343
bool "Support SH7343 processor"
select CPU_SH4AL_DSP
+ select ARCH_SHMOBILE
+ select SYS_SUPPORTS_CMT
config CPU_SUBTYPE_SH7722
bool "Support SH7722 processor"
select CPU_SH4AL_DSP
select CPU_SHX2
+ select ARCH_SHMOBILE
select ARCH_SPARSEMEM_ENABLE
select SYS_SUPPORTS_NUMA
+ select SYS_SUPPORTS_CMT
config CPU_SUBTYPE_SH7366
bool "Support SH7366 processor"
select CPU_SH4AL_DSP
select CPU_SHX2
+ select ARCH_SHMOBILE
select ARCH_SPARSEMEM_ENABLE
select SYS_SUPPORTS_NUMA
+ select SYS_SUPPORTS_CMT
# SH-5 Processor Support
@@ -398,25 +425,34 @@
menu "Timer and clock configuration"
config SH_TMU
- def_bool y
- prompt "TMU timer support"
+ bool "TMU timer support"
depends on CPU_SH3 || CPU_SH4
+ default y
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
help
This enables the use of the TMU as the system timer.
config SH_CMT
- def_bool y
- prompt "CMT timer support"
- depends on CPU_SH2 && !CPU_SUBTYPE_MXG
+ bool "CMT timer support"
+ depends on SYS_SUPPORTS_CMT && CPU_SH2
+ default y
help
This enables the use of the CMT as the system timer.
+#
+# Support for the new-style CMT driver. This will replace SH_CMT
+# once its other dependencies are merged.
+#
+config SH_TIMER_CMT
+ bool "CMT clockevents driver"
+ depends on SYS_SUPPORTS_CMT && !SH_CMT
+ select GENERIC_CLOCKEVENTS
+
config SH_MTU2
- def_bool n
- prompt "MTU2 timer support"
+ bool "MTU2 timer support"
depends on CPU_SH2A
+ default y
help
This enables the use of the MTU2 as the system timer.
@@ -426,7 +462,8 @@
CPU_SUBTYPE_SH7763
default "86" if CPU_SUBTYPE_SH7619
default "140" if CPU_SUBTYPE_SH7206
- default "142" if CPU_SUBTYPE_SH7203
+ default "142" if CPU_SUBTYPE_SH7203 && SH_CMT
+ default "153" if CPU_SUBTYPE_SH7203 && SH_MTU2
default "238" if CPU_SUBTYPE_MXG
default "16"
@@ -438,7 +475,8 @@
default "33333333" if CPU_SUBTYPE_SH7770 || CPU_SUBTYPE_SH7723 || \
CPU_SUBTYPE_SH7760 || CPU_SUBTYPE_SH7705 || \
CPU_SUBTYPE_SH7203 || CPU_SUBTYPE_SH7206 || \
- CPU_SUBTYPE_SH7263 || CPU_SUBTYPE_MXG
+ CPU_SUBTYPE_SH7263 || CPU_SUBTYPE_MXG || \
+ CPU_SUBTYPE_SH7786
default "60000000" if CPU_SUBTYPE_SH7751 || CPU_SUBTYPE_SH7751R
default "66000000" if CPU_SUBTYPE_SH4_202
default "50000000"
@@ -521,6 +559,13 @@
For more details see Documentation/kdump/kdump.txt
+config KEXEC_JUMP
+ bool "kexec jump (EXPERIMENTAL)"
+ depends on SUPERH32 && KEXEC && HIBERNATION && EXPERIMENTAL
+ help
+ Jump between original kernel and kexeced kernel and invoke
+ code via KEXEC
+
config SECCOMP
bool "Enable seccomp to safely compute untrusted bytecode"
depends on PROC_FS
diff --git a/arch/sh/Kconfig.cpu b/arch/sh/Kconfig.cpu
index 0e27fe3..c7d7043 100644
--- a/arch/sh/Kconfig.cpu
+++ b/arch/sh/Kconfig.cpu
@@ -104,6 +104,9 @@
config CPU_HAS_PTEA
bool
+config CPU_HAS_PTEAEX
+ bool
+
config CPU_HAS_DSP
bool
diff --git a/arch/sh/Makefile b/arch/sh/Makefile
index 4067b0d..bece1f7 100644
--- a/arch/sh/Makefile
+++ b/arch/sh/Makefile
@@ -80,6 +80,7 @@
defaultimage-$(CONFIG_SUPERH32) := zImage
defaultimage-$(CONFIG_SH_SH7785LCR) := uImage
defaultimage-$(CONFIG_SH_RSK) := uImage
+defaultimage-$(CONFIG_SH_URQUELL) := uImage
defaultimage-$(CONFIG_SH_7206_SOLUTION_ENGINE) := vmlinux
defaultimage-$(CONFIG_SH_7619_SOLUTION_ENGINE) := vmlinux
diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig
index 8619147..dcc1af8 100644
--- a/arch/sh/boards/Kconfig
+++ b/arch/sh/boards/Kconfig
@@ -155,17 +155,22 @@
config SH_SH7785LCR_29BIT_PHYSMAPS
bool "SH7785LCR 29bit physmaps"
- depends on SH_SH7785LCR
+ depends on SH_SH7785LCR && 29BIT
default y
help
This board has 2 physical memory maps. It can be changed with
DIP switch(S2-5). If you set the DIP switch for S2-5 = ON,
you can access all on-board device in 29bit address mode.
+config SH_URQUELL
+ bool "Urquell"
+ depends on CPU_SUBTYPE_SH7786
+ select ARCH_REQUIRE_GPIOLIB
+
config SH_MIGOR
bool "Migo-R"
depends on CPU_SUBTYPE_SH7722
- select GENERIC_GPIO
+ select ARCH_REQUIRE_GPIOLIB
help
Select Migo-R if configuring for the SH7722 Migo-R platform
by Renesas System Solutions Asia Pte. Ltd.
@@ -173,7 +178,7 @@
config SH_AP325RXA
bool "AP-325RXA"
depends on CPU_SUBTYPE_SH7723
- select GENERIC_GPIO
+ select ARCH_REQUIRE_GPIOLIB
help
Renesas "AP-325RXA" support.
Compatible with ALGO SYSTEM CO.,LTD. "AP-320A"
@@ -185,6 +190,13 @@
Select SH7763RDP if configuring for a Renesas SH7763
evaluation board.
+config SH_ESPT
+ bool "ESPT"
+ depends on CPU_SUBTYPE_SH7763
+ help
+ Select ESPT if configuring for a Renesas SH7763
+ with gigabit ether evaluation board.
+
config SH_EDOSK7705
bool "EDOSK7705"
depends on CPU_SUBTYPE_SH7705
@@ -240,7 +252,7 @@
config SH_MAGIC_PANEL_R2
bool "Magic Panel R2"
depends on CPU_SUBTYPE_SH7720
- select GENERIC_GPIO
+ select ARCH_REQUIRE_GPIOLIB
help
Select Magic Panel R2 if configuring for Magic Panel R2.
@@ -249,6 +261,13 @@
depends on CPU_SUBTYPE_SH5_101 || CPU_SUBTYPE_SH5_103
select SYS_SUPPORTS_PCI
+config SH_POLARIS
+ bool "SMSC Polaris"
+ select CPU_HAS_IPR_IRQ
+ depends on CPU_SUBTYPE_SH7709
+ help
+ Select if configuring for an SMSC Polaris development board
+
endmenu
source "arch/sh/boards/mach-r2d/Kconfig"
diff --git a/arch/sh/boards/Makefile b/arch/sh/boards/Makefile
index 269ae2b..7baa210 100644
--- a/arch/sh/boards/Makefile
+++ b/arch/sh/boards/Makefile
@@ -4,5 +4,8 @@
obj-$(CONFIG_SH_AP325RXA) += board-ap325rxa.o
obj-$(CONFIG_SH_MAGIC_PANEL_R2) += board-magicpanelr2.o
obj-$(CONFIG_SH_SH7785LCR) += board-sh7785lcr.o
+obj-$(CONFIG_SH_URQUELL) += board-urquell.o
obj-$(CONFIG_SH_SHMIN) += board-shmin.o
obj-$(CONFIG_SH_EDOSK7760) += board-edosk7760.o
+obj-$(CONFIG_SH_ESPT) += board-espt.o
+obj-$(CONFIG_SH_POLARIS) += board-polaris.o
diff --git a/arch/sh/boards/board-ap325rxa.c b/arch/sh/boards/board-ap325rxa.c
index 15b6d45..a64e388 100644
--- a/arch/sh/boards/board-ap325rxa.c
+++ b/arch/sh/boards/board-ap325rxa.c
@@ -166,6 +166,16 @@
ctrl_outw(0x100, FPGA_BKLREG);
}
+static void ap320_wvga_power_off(void *board_data)
+{
+ /* backlight */
+ ctrl_outw(0, FPGA_BKLREG);
+ gpio_set_value(GPIO_PTS3, 1);
+
+ /* ASD AP-320/325 LCD OFF */
+ ctrl_outw(0, FPGA_LCDREG);
+}
+
static struct sh_mobile_lcdc_info lcdc_info = {
.clock_source = LCDC_CLK_EXTERNAL,
.ch[0] = {
@@ -191,6 +201,7 @@
},
.board_cfg = {
.display_on = ap320_wvga_power_on,
+ .display_off = ap320_wvga_power_off,
},
}
};
diff --git a/arch/sh/boards/board-espt.c b/arch/sh/boards/board-espt.c
new file mode 100644
index 0000000..d5ce5e1
--- /dev/null
+++ b/arch/sh/boards/board-espt.c
@@ -0,0 +1,102 @@
+/*
+ * Data Technology Inc. ESPT-GIGA board suport
+ *
+ * Copyright (C) 2008, 2009 Renesas Solutions Corp.
+ * Copyright (C) 2008, 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/mtd/physmap.h>
+#include <linux/io.h>
+#include <asm/machvec.h>
+#include <asm/sizes.h>
+#include <asm/sh_eth.h>
+
+/* NOR Flash */
+static struct mtd_partition espt_nor_flash_partitions[] = {
+ {
+ .name = "U-Boot",
+ .offset = 0,
+ .size = (2 * SZ_128K),
+ .mask_flags = MTD_WRITEABLE, /* Read-only */
+ }, {
+ .name = "Linux-Kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = (20 * SZ_128K),
+ }, {
+ .name = "Root Filesystem",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+static struct physmap_flash_data espt_nor_flash_data = {
+ .width = 2,
+ .parts = espt_nor_flash_partitions,
+ .nr_parts = ARRAY_SIZE(espt_nor_flash_partitions),
+};
+
+static struct resource espt_nor_flash_resources[] = {
+ [0] = {
+ .name = "NOR Flash",
+ .start = 0,
+ .end = SZ_8M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device espt_nor_flash_device = {
+ .name = "physmap-flash",
+ .resource = espt_nor_flash_resources,
+ .num_resources = ARRAY_SIZE(espt_nor_flash_resources),
+ .dev = {
+ .platform_data = &espt_nor_flash_data,
+ },
+};
+
+/* SH-Ether */
+static struct resource sh_eth_resources[] = {
+ {
+ .start = 0xFEE00800, /* use eth1 */
+ .end = 0xFEE00F7C - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 57, /* irq number */
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct sh_eth_plat_data sh7763_eth_pdata = {
+ .phy = 0,
+ .edmac_endian = EDMAC_LITTLE_ENDIAN,
+};
+
+static struct platform_device espt_eth_device = {
+ .name = "sh-eth",
+ .resource = sh_eth_resources,
+ .num_resources = ARRAY_SIZE(sh_eth_resources),
+ .dev = {
+ .platform_data = &sh7763_eth_pdata,
+ },
+};
+
+static struct platform_device *espt_devices[] __initdata = {
+ &espt_nor_flash_device,
+ &espt_eth_device,
+};
+
+static int __init espt_devices_setup(void)
+{
+ return platform_add_devices(espt_devices,
+ ARRAY_SIZE(espt_devices));
+}
+device_initcall(espt_devices_setup);
+
+static struct sh_machine_vector mv_espt __initmv = {
+ .mv_name = "ESPT-GIGA",
+};
diff --git a/arch/sh/boards/board-polaris.c b/arch/sh/boards/board-polaris.c
new file mode 100644
index 0000000..62607eb
--- /dev/null
+++ b/arch/sh/boards/board-polaris.c
@@ -0,0 +1,149 @@
+/*
+ * June 2006 steve.glendinning@smsc.com
+ *
+ * Polaris-specific resource declaration
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/smsc911x.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <asm/machvec.h>
+#include <asm/heartbeat.h>
+#include <cpu/gpio.h>
+#include <mach-se/mach/se.h>
+
+#define BCR2 (0xFFFFFF62)
+#define WCR2 (0xFFFFFF66)
+#define AREA5_WAIT_CTRL (0x1C00)
+#define WAIT_STATES_10 (0x7)
+
+static struct resource smsc911x_resources[] = {
+ [0] = {
+ .name = "smsc911x-memory",
+ .start = PA_EXT5,
+ .end = PA_EXT5 + 0x1fff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .name = "smsc911x-irq",
+ .start = IRQ0_IRQ,
+ .end = IRQ0_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct smsc911x_platform_config smsc911x_config = {
+ .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW,
+ .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN,
+ .flags = SMSC911X_USE_32BIT,
+ .phy_interface = PHY_INTERFACE_MODE_MII,
+};
+
+static struct platform_device smsc911x_device = {
+ .name = "smsc911x",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(smsc911x_resources),
+ .resource = smsc911x_resources,
+ .dev = {
+ .platform_data = &smsc911x_config,
+ },
+};
+
+static unsigned char heartbeat_bit_pos[] = { 0, 1, 2, 3 };
+
+static struct heartbeat_data heartbeat_data = {
+ .bit_pos = heartbeat_bit_pos,
+ .nr_bits = ARRAY_SIZE(heartbeat_bit_pos),
+ .regsize = 8,
+};
+
+static struct resource heartbeat_resources[] = {
+ [0] = {
+ .start = PORT_PCDR,
+ .end = PORT_PCDR,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device heartbeat_device = {
+ .name = "heartbeat",
+ .id = -1,
+ .dev = {
+ .platform_data = &heartbeat_data,
+ },
+ .num_resources = ARRAY_SIZE(heartbeat_resources),
+ .resource = heartbeat_resources,
+};
+
+static struct platform_device *polaris_devices[] __initdata = {
+ &smsc911x_device,
+ &heartbeat_device,
+};
+
+static int __init polaris_initialise(void)
+{
+ u16 wcr, bcr_mask;
+
+ printk(KERN_INFO "Configuring Polaris external bus\n");
+
+ /* Configure area 5 with 2 wait states */
+ wcr = ctrl_inw(WCR2);
+ wcr &= (~AREA5_WAIT_CTRL);
+ wcr |= (WAIT_STATES_10 << 10);
+ ctrl_outw(wcr, WCR2);
+
+ /* Configure area 5 for 32-bit access */
+ bcr_mask = ctrl_inw(BCR2);
+ bcr_mask |= 1 << 10;
+ ctrl_outw(bcr_mask, BCR2);
+
+ return platform_add_devices(polaris_devices,
+ ARRAY_SIZE(polaris_devices));
+}
+arch_initcall(polaris_initialise);
+
+static struct ipr_data ipr_irq_table[] = {
+ /* External IRQs */
+ { IRQ0_IRQ, 0, 0, 1, }, /* IRQ0 */
+ { IRQ1_IRQ, 0, 4, 1, }, /* IRQ1 */
+};
+
+static unsigned long ipr_offsets[] = {
+ INTC_IPRC
+};
+
+static struct ipr_desc ipr_irq_desc = {
+ .ipr_offsets = ipr_offsets,
+ .nr_offsets = ARRAY_SIZE(ipr_offsets),
+
+ .ipr_data = ipr_irq_table,
+ .nr_irqs = ARRAY_SIZE(ipr_irq_table),
+ .chip = {
+ .name = "sh7709-ext",
+ },
+};
+
+static void __init init_polaris_irq(void)
+{
+ /* Disable all interrupts */
+ ctrl_outw(0, BCR_ILCRA);
+ ctrl_outw(0, BCR_ILCRB);
+ ctrl_outw(0, BCR_ILCRC);
+ ctrl_outw(0, BCR_ILCRD);
+ ctrl_outw(0, BCR_ILCRE);
+ ctrl_outw(0, BCR_ILCRF);
+ ctrl_outw(0, BCR_ILCRG);
+
+ register_ipr_controller(&ipr_irq_desc);
+}
+
+static struct sh_machine_vector mv_polaris __initmv = {
+ .mv_name = "Polaris",
+ .mv_nr_irqs = 61,
+ .mv_init_irq = init_polaris_irq,
+};
diff --git a/arch/sh/boards/board-sh7785lcr.c b/arch/sh/boards/board-sh7785lcr.c
index 38a6496..94c0296 100644
--- a/arch/sh/boards/board-sh7785lcr.c
+++ b/arch/sh/boards/board-sh7785lcr.c
@@ -275,7 +275,18 @@
static void sh7785lcr_power_off(void)
{
- ctrl_outb(0x01, P2SEGADDR(PLD_POFCR));
+ unsigned char *p;
+
+ p = ioremap(PLD_POFCR, PLD_POFCR + 1);
+ if (!p) {
+ printk(KERN_ERR "%s: ioremap error.\n", __func__);
+ return;
+ }
+ *p = 0x01;
+ iounmap(p);
+ set_bl_bit();
+ while (1)
+ cpu_relax();
}
/* Initialize the board */
diff --git a/arch/sh/boards/board-urquell.c b/arch/sh/boards/board-urquell.c
new file mode 100644
index 0000000..17036ce
--- /dev/null
+++ b/arch/sh/boards/board-urquell.c
@@ -0,0 +1,162 @@
+/*
+ * Renesas Technology Corp. SH7786 Urquell Support.
+ *
+ * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ * Copyright (C) 2008 Yoshihiro Shimoda
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/smc91x.h>
+#include <linux/mtd/physmap.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <mach/urquell.h>
+#include <cpu/sh7786.h>
+#include <asm/heartbeat.h>
+#include <asm/sizes.h>
+
+static struct resource heartbeat_resources[] = {
+ [0] = {
+ .start = BOARDREG(SLEDR),
+ .end = BOARDREG(SLEDR),
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct heartbeat_data heartbeat_data = {
+ .regsize = 16,
+};
+
+static struct platform_device heartbeat_device = {
+ .name = "heartbeat",
+ .id = -1,
+ .dev = {
+ .platform_data = &heartbeat_data,
+ },
+ .num_resources = ARRAY_SIZE(heartbeat_resources),
+ .resource = heartbeat_resources,
+};
+
+static struct smc91x_platdata smc91x_info = {
+ .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT,
+};
+
+static struct resource smc91x_eth_resources[] = {
+ [0] = {
+ .name = "SMC91C111" ,
+ .start = 0x05800300,
+ .end = 0x0580030f,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = 11,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device smc91x_eth_device = {
+ .name = "smc91x",
+ .num_resources = ARRAY_SIZE(smc91x_eth_resources),
+ .resource = smc91x_eth_resources,
+ .dev = {
+ .platform_data = &smc91x_info,
+ },
+};
+
+static struct mtd_partition nor_flash_partitions[] = {
+ {
+ .name = "loader",
+ .offset = 0x00000000,
+ .size = SZ_512K,
+ .mask_flags = MTD_WRITEABLE, /* Read-only */
+ },
+ {
+ .name = "bootenv",
+ .offset = MTDPART_OFS_APPEND,
+ .size = SZ_512K,
+ .mask_flags = MTD_WRITEABLE, /* Read-only */
+ },
+ {
+ .name = "kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = SZ_4M,
+ },
+ {
+ .name = "data",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+static struct physmap_flash_data nor_flash_data = {
+ .width = 2,
+ .parts = nor_flash_partitions,
+ .nr_parts = ARRAY_SIZE(nor_flash_partitions),
+};
+
+static struct resource nor_flash_resources[] = {
+ [0] = {
+ .start = NOR_FLASH_ADDR,
+ .end = NOR_FLASH_ADDR + NOR_FLASH_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device nor_flash_device = {
+ .name = "physmap-flash",
+ .dev = {
+ .platform_data = &nor_flash_data,
+ },
+ .num_resources = ARRAY_SIZE(nor_flash_resources),
+ .resource = nor_flash_resources,
+};
+
+static struct platform_device *urquell_devices[] __initdata = {
+ &heartbeat_device,
+ &smc91x_eth_device,
+ &nor_flash_device,
+};
+
+static int __init urquell_devices_setup(void)
+{
+ /* USB */
+ gpio_request(GPIO_FN_USB_OVC0, NULL);
+ gpio_request(GPIO_FN_USB_PENC0, NULL);
+
+ return platform_add_devices(urquell_devices,
+ ARRAY_SIZE(urquell_devices));
+}
+device_initcall(urquell_devices_setup);
+
+static void urquell_power_off(void)
+{
+ __raw_writew(0xa5a5, UBOARDREG(SRSTR));
+}
+
+static void __init urquell_init_irq(void)
+{
+ plat_irq_setup_pins(IRQ_MODE_IRL3210_MASK);
+}
+
+/* Initialize the board */
+static void __init urquell_setup(char **cmdline_p)
+{
+ printk(KERN_INFO "Renesas Technology Corp. Urquell support.\n");
+
+ pm_power_off = urquell_power_off;
+}
+
+/*
+ * The Machine Vector
+ */
+static struct sh_machine_vector mv_urquell __initmv = {
+ .mv_name = "Urquell",
+ .mv_setup = urquell_setup,
+ .mv_init_irq = urquell_init_irq,
+};
diff --git a/arch/sh/boards/mach-highlander/Kconfig b/arch/sh/boards/mach-highlander/Kconfig
index 08057f6..def49cc 100644
--- a/arch/sh/boards/mach-highlander/Kconfig
+++ b/arch/sh/boards/mach-highlander/Kconfig
@@ -18,7 +18,7 @@
config SH_R7785RP
bool "R7785RP board support"
depends on CPU_SUBTYPE_SH7785
- select GENERIC_GPIO
+ select ARCH_REQUIRE_GPIOLIB
endchoice
diff --git a/arch/sh/boards/mach-hp6xx/pm_wakeup.S b/arch/sh/boards/mach-hp6xx/pm_wakeup.S
index 44b648c..4f18d44 100644
--- a/arch/sh/boards/mach-hp6xx/pm_wakeup.S
+++ b/arch/sh/boards/mach-hp6xx/pm_wakeup.S
@@ -10,47 +10,32 @@
#include <linux/linkage.h>
#include <cpu/mmu_context.h>
-#define k0 r0
-#define k1 r1
-#define k2 r2
-#define k3 r3
-#define k4 r4
-
/*
* Kernel mode register usage:
* k0 scratch
* k1 scratch
- * k2 scratch (Exception code)
- * k3 scratch (Return address)
- * k4 scratch
- * k5 reserved
- * k6 Global Interrupt Mask (0--15 << 4)
- * k7 CURRENT_THREAD_INFO (pointer to current thread info)
+ * For more details, please have a look at entry.S
*/
+#define k0 r0
+#define k1 r1
+
ENTRY(wakeup_start)
! clear STBY bit
- mov #-126, k2
+ mov #-126, k1
and #127, k0
- mov.b k0, @k2
+ mov.b k0, @k1
! enable refresh
mov.l 5f, k1
mov.w 6f, k0
mov.w k0, @k1
! jump to handler
- mov.l 2f, k2
- mov.l 3f, k3
- mov.l @k2, k2
-
mov.l 4f, k1
jmp @k1
- nop
+ nop
.align 2
-1: .long EXPEVT
-2: .long INTEVT
-3: .long ret_from_irq
-4: .long handle_exception
+4: .long handle_interrupt
5: .long 0xffffff68
6: .word 0x0524
diff --git a/arch/sh/boards/mach-hp6xx/setup.c b/arch/sh/boards/mach-hp6xx/setup.c
index 746742bd..8f305b3 100644
--- a/arch/sh/boards/mach-hp6xx/setup.c
+++ b/arch/sh/boards/mach-hp6xx/setup.c
@@ -115,7 +115,6 @@
.mv_setup = hp6xx_setup,
/* IRQ's : CPU(64) + CCHIP(16) + FREE_TO_USE(6) */
.mv_nr_irqs = HD64461_IRQBASE + HD64461_IRQ_NUM + 6,
- .mv_irq_demux = hd64461_irq_demux,
/* Enable IRQ0 -> IRQ3 in IRQ_MODE */
.mv_init_irq = hp6xx_init_irq,
};
diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c
index 28e56c5..bc35b4c 100644
--- a/arch/sh/boards/mach-migor/setup.c
+++ b/arch/sh/boards/mach-migor/setup.c
@@ -450,6 +450,14 @@
static int __init migor_devices_setup(void)
{
+
+#ifdef CONFIG_PM
+ /* Let D11 LED show STATUS0 */
+ gpio_request(GPIO_FN_STATUS0, NULL);
+
+ /* Lit D12 LED show PDSTATUS */
+ gpio_request(GPIO_FN_PDSTATUS, NULL);
+#else
/* Lit D11 LED */
gpio_request(GPIO_PTJ7, NULL);
gpio_direction_output(GPIO_PTJ7, 1);
@@ -459,6 +467,7 @@
gpio_request(GPIO_PTJ5, NULL);
gpio_direction_output(GPIO_PTJ5, 1);
gpio_export(GPIO_PTJ5, 0);
+#endif
/* SMC91C111 - Enable IRQ0, Setup CS4 for 16-bit fast access */
gpio_request(GPIO_FN_IRQ0, NULL);
diff --git a/arch/sh/boards/mach-rsk/Kconfig b/arch/sh/boards/mach-rsk/Kconfig
index bff095d..aeff3b0 100644
--- a/arch/sh/boards/mach-rsk/Kconfig
+++ b/arch/sh/boards/mach-rsk/Kconfig
@@ -10,7 +10,7 @@
config SH_RSK7203
bool "RSK7203"
- select GENERIC_GPIO
+ select ARCH_REQUIRE_GPIOLIB
depends on CPU_SUBTYPE_SH7203
endchoice
diff --git a/arch/sh/boards/mach-sh7763rdp/setup.c b/arch/sh/boards/mach-sh7763rdp/setup.c
index 6f926fd..390534a 100644
--- a/arch/sh/boards/mach-sh7763rdp/setup.c
+++ b/arch/sh/boards/mach-sh7763rdp/setup.c
@@ -63,15 +63,19 @@
},
};
-/* SH-Ether */
+/*
+ * SH-Ether
+ *
+ * SH Ether of SH7763 has multi IRQ handling.
+ * (57,58,59 -> 57)
+ */
static struct resource sh_eth_resources[] = {
{
.start = 0xFEE00800, /* use eth1 */
.end = 0xFEE00F7C - 1,
.flags = IORESOURCE_MEM,
}, {
- .start = 58, /* irq number */
- .end = 58,
+ .start = 57, /* irq number */
.flags = IORESOURCE_IRQ,
},
};
diff --git a/arch/sh/boot/Makefile b/arch/sh/boot/Makefile
index c16ccd4..95483d1 100644
--- a/arch/sh/boot/Makefile
+++ b/arch/sh/boot/Makefile
@@ -33,20 +33,24 @@
$(obj)/compressed/vmlinux: FORCE
$(Q)$(MAKE) $(build)=$(obj)/compressed $@
-ifeq ($(CONFIG_32BIT),y)
-KERNEL_LOAD := $(shell /bin/bash -c 'printf "0x%08x" \
- $$[$(CONFIG_PAGE_OFFSET) + \
- $(CONFIG_ZERO_PAGE_OFFSET)]')
-else
-KERNEL_LOAD := $(shell /bin/bash -c 'printf "0x%08x" \
- $$[$(CONFIG_PAGE_OFFSET) + \
- $(CONFIG_MEMORY_START) + \
- $(CONFIG_ZERO_PAGE_OFFSET)]')
+KERNEL_MEMORY := 0x00000000
+ifeq ($(CONFIG_PMB_FIXED),y)
+KERNEL_MEMORY := $(shell /bin/bash -c 'printf "0x%08x" \
+ $$[$(CONFIG_MEMORY_START) & 0x1fffffff]')
endif
+ifeq ($(CONFIG_29BIT),y)
+KERNEL_MEMORY := $(shell /bin/bash -c 'printf "0x%08x" \
+ $$[$(CONFIG_MEMORY_START)]')
+endif
+
+KERNEL_LOAD := $(shell /bin/bash -c 'printf "0x%08x" \
+ $$[$(CONFIG_PAGE_OFFSET) + \
+ $(KERNEL_MEMORY) + \
+ $(CONFIG_ZERO_PAGE_OFFSET)]')
KERNEL_ENTRY := $(shell /bin/bash -c 'printf "0x%08x" \
$$[$(CONFIG_PAGE_OFFSET) + \
- $(CONFIG_MEMORY_START) + \
+ $(KERNEL_MEMORY) + \
$(CONFIG_ZERO_PAGE_OFFSET) + $(CONFIG_ENTRY_OFFSET)]')
quiet_cmd_uimage = UIMAGE $@
diff --git a/arch/sh/cchips/hd6446x/hd64461.c b/arch/sh/cchips/hd6446x/hd64461.c
index 27ceeb9..25ef910 100644
--- a/arch/sh/cchips/hd6446x/hd64461.c
+++ b/arch/sh/cchips/hd6446x/hd64461.c
@@ -53,21 +53,22 @@
.unmask = hd64461_unmask_irq,
};
-int hd64461_irq_demux(int irq)
+static void hd64461_irq_demux(unsigned int irq, struct irq_desc *desc)
{
- if (irq == CONFIG_HD64461_IRQ) {
- unsigned short bit;
- unsigned short nirr = inw(HD64461_NIRR);
- unsigned short nimr = inw(HD64461_NIMR);
- int i;
+ unsigned short intv = ctrl_inw(HD64461_NIRR);
+ struct irq_desc *ext_desc;
+ unsigned int ext_irq = HD64461_IRQBASE;
- nirr &= ~nimr;
- for (bit = 1, i = 0; i < 16; bit <<= 1, i++)
- if (nirr & bit)
- break;
- irq = HD64461_IRQBASE + i;
+ intv &= (1 << HD64461_IRQ_NUM) - 1;
+
+ while (intv) {
+ if (intv & 1) {
+ ext_desc = irq_desc + ext_irq;
+ handle_level_irq(ext_irq, ext_desc);
+ }
+ intv >>= 1;
+ ext_irq++;
}
- return irq;
}
int __init setup_hd64461(void)
@@ -93,6 +94,9 @@
set_irq_chip_and_handler(i, &hd64461_irq_chip,
handle_level_irq);
+ set_irq_chained_handler(CONFIG_HD64461_IRQ, hd64461_irq_demux);
+ set_irq_type(CONFIG_HD64461_IRQ, IRQ_TYPE_LEVEL_LOW);
+
#ifdef CONFIG_HD64461_ENABLER
printk(KERN_INFO "HD64461: enabling PCMCIA devices\n");
__raw_writeb(0x4c, HD64461_PCC1CSCIER);
diff --git a/arch/sh/configs/espt_defconfig b/arch/sh/configs/espt_defconfig
new file mode 100644
index 0000000..873ec42
--- /dev/null
+++ b/arch/sh/configs/espt_defconfig
@@ -0,0 +1,1190 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.29-rc7
+# Tue Mar 17 13:25:58 2009
+#
+CONFIG_SUPERH=y
+CONFIG_SUPERH32=y
+CONFIG_ARCH_DEFCONFIG="arch/sh/configs/shx3_defconfig"
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_FIND_NEXT_BIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_GENERIC_IRQ_PROBE=y
+# CONFIG_GENERIC_GPIO is not set
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_ARCH_SUSPEND_POSSIBLE is not set
+# CONFIG_ARCH_HIBERNATION_POSSIBLE is not set
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_ARCH_NO_VIRT_TO_BUS=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_CLASSIC_RCU=y
+# CONFIG_TREE_RCU is not set
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_PREEMPT_RCU_TRACE is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_GROUP_SCHED=y
+CONFIG_FAIR_GROUP_SCHED=y
+# CONFIG_RT_GROUP_SCHED is not set
+CONFIG_USER_SCHED=y
+# CONFIG_CGROUP_SCHED is not set
+# CONFIG_CGROUPS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+CONFIG_NAMESPACES=y
+CONFIG_UTS_NS=y
+CONFIG_IPC_NS=y
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_NET_NS is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_PROFILING=y
+CONFIG_TRACEPOINTS=y
+# CONFIG_MARKERS is not set
+CONFIG_OPROFILE=y
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_IOREMAP_PROT=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+# CONFIG_MODULE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+# CONFIG_FREEZER is not set
+
+#
+# System type
+#
+CONFIG_CPU_SH4=y
+CONFIG_CPU_SH4A=y
+# CONFIG_CPU_SUBTYPE_SH7619 is not set
+# CONFIG_CPU_SUBTYPE_SH7201 is not set
+# CONFIG_CPU_SUBTYPE_SH7203 is not set
+# CONFIG_CPU_SUBTYPE_SH7206 is not set
+# CONFIG_CPU_SUBTYPE_SH7263 is not set
+# CONFIG_CPU_SUBTYPE_MXG is not set
+# CONFIG_CPU_SUBTYPE_SH7705 is not set
+# CONFIG_CPU_SUBTYPE_SH7706 is not set
+# CONFIG_CPU_SUBTYPE_SH7707 is not set
+# CONFIG_CPU_SUBTYPE_SH7708 is not set
+# CONFIG_CPU_SUBTYPE_SH7709 is not set
+# CONFIG_CPU_SUBTYPE_SH7710 is not set
+# CONFIG_CPU_SUBTYPE_SH7712 is not set
+# CONFIG_CPU_SUBTYPE_SH7720 is not set
+# CONFIG_CPU_SUBTYPE_SH7721 is not set
+# CONFIG_CPU_SUBTYPE_SH7750 is not set
+# CONFIG_CPU_SUBTYPE_SH7091 is not set
+# CONFIG_CPU_SUBTYPE_SH7750R is not set
+# CONFIG_CPU_SUBTYPE_SH7750S is not set
+# CONFIG_CPU_SUBTYPE_SH7751 is not set
+# CONFIG_CPU_SUBTYPE_SH7751R is not set
+# CONFIG_CPU_SUBTYPE_SH7760 is not set
+# CONFIG_CPU_SUBTYPE_SH4_202 is not set
+# CONFIG_CPU_SUBTYPE_SH7723 is not set
+CONFIG_CPU_SUBTYPE_SH7763=y
+# CONFIG_CPU_SUBTYPE_SH7770 is not set
+# CONFIG_CPU_SUBTYPE_SH7780 is not set
+# CONFIG_CPU_SUBTYPE_SH7785 is not set
+# CONFIG_CPU_SUBTYPE_SH7786 is not set
+# CONFIG_CPU_SUBTYPE_SHX3 is not set
+# CONFIG_CPU_SUBTYPE_SH7343 is not set
+# CONFIG_CPU_SUBTYPE_SH7722 is not set
+# CONFIG_CPU_SUBTYPE_SH7366 is not set
+# CONFIG_CPU_SUBTYPE_SH5_101 is not set
+# CONFIG_CPU_SUBTYPE_SH5_103 is not set
+
+#
+# Memory management options
+#
+CONFIG_QUICKLIST=y
+CONFIG_MMU=y
+CONFIG_PAGE_OFFSET=0x80000000
+CONFIG_MEMORY_START=0x0c000000
+CONFIG_MEMORY_SIZE=0x04000000
+CONFIG_29BIT=y
+CONFIG_VSYSCALL=y
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_DEFAULT=y
+CONFIG_MAX_ACTIVE_REGIONS=1
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_8KB is not set
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_64KB is not set
+CONFIG_ENTRY_OFFSET=0x00001000
+CONFIG_SELECT_MEMORY_MODEL=y
+# CONFIG_FLATMEM_MANUAL is not set
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+CONFIG_SPARSEMEM_MANUAL=y
+CONFIG_SPARSEMEM=y
+CONFIG_HAVE_MEMORY_PRESENT=y
+CONFIG_SPARSEMEM_STATIC=y
+# CONFIG_MEMORY_HOTPLUG is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+CONFIG_MIGRATION=y
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_NR_QUICK=2
+CONFIG_UNEVICTABLE_LRU=y
+
+#
+# Cache configuration
+#
+# CONFIG_SH_DIRECT_MAPPED is not set
+CONFIG_CACHE_WRITEBACK=y
+# CONFIG_CACHE_WRITETHROUGH is not set
+# CONFIG_CACHE_OFF is not set
+
+#
+# Processor features
+#
+CONFIG_CPU_LITTLE_ENDIAN=y
+# CONFIG_CPU_BIG_ENDIAN is not set
+CONFIG_SH_FPU=y
+# CONFIG_SH_STORE_QUEUES is not set
+CONFIG_CPU_HAS_INTEVT=y
+CONFIG_CPU_HAS_SR_RB=y
+CONFIG_CPU_HAS_FPU=y
+
+#
+# Board support
+#
+# CONFIG_SH_SH7763RDP is not set
+CONFIG_SH_ESPT=y
+
+#
+# Timer and clock configuration
+#
+CONFIG_SH_TMU=y
+CONFIG_SH_TIMER_IRQ=28
+CONFIG_SH_PCLK_FREQ=66666666
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# DMA support
+#
+# CONFIG_SH_DMA is not set
+
+#
+# Companion Chips
+#
+
+#
+# Additional SuperH Device Drivers
+#
+# CONFIG_HEARTBEAT is not set
+# CONFIG_PUSH_SWITCH is not set
+
+#
+# Kernel features
+#
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+# CONFIG_SCHED_HRTICK is not set
+# CONFIG_KEXEC is not set
+# CONFIG_CRASH_DUMP is not set
+CONFIG_SECCOMP=y
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_GUSA=y
+
+#
+# Boot options
+#
+CONFIG_ZERO_PAGE_OFFSET=0x00001000
+CONFIG_BOOT_LINK_OFFSET=0x00800000
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttySC0,115200 root=/dev/nfs ip=bootp"
+
+#
+# Bus options
+#
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_HAVE_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options (EXPERIMENTAL)
+#
+# CONFIG_PM is not set
+# CONFIG_CPU_IDLE is not set
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_COMPAT_NET_DEV_OPS=y
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_PHONET is not set
+# CONFIG_WIRELESS is not set
+# CONFIG_WIMAX is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_GEN_PROBE=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_NOSWAP=y
+# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
+# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
+CONFIG_MTD_CFI_GEOMETRY=y
+# CONFIG_MTD_MAP_BANK_WIDTH_1 is not set
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+# CONFIG_MTD_MAP_BANK_WIDTH_4 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_OTP is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+CONFIG_MTD_PHYSMAP=y
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_UB is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_BLK_DEV_HD is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_LIBFC is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_DH is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+CONFIG_PHYLIB=y
+
+#
+# MII PHY device drivers
+#
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_VITESSE_PHY is not set
+# CONFIG_SMSC_PHY is not set
+# CONFIG_BROADCOM_PHY is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_NATIONAL_PHY is not set
+# CONFIG_STE10XP is not set
+# CONFIG_LSI_ET1011C_PHY is not set
+# CONFIG_FIXED_PHY is not set
+CONFIG_MDIO_BITBANG=y
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
+# CONFIG_STNIC is not set
+CONFIG_SH_ETH=y
+# CONFIG_SMC91X is not set
+# CONFIG_SMC911X is not set
+# CONFIG_SMSC911X is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_NR_UARTS=3
+CONFIG_SERIAL_SH_SCI_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_REGULATOR is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+CONFIG_FB_FOREIGN_ENDIAN=y
+CONFIG_FB_BOTH_ENDIAN=y
+# CONFIG_FB_BIG_ENDIAN is not set
+# CONFIG_FB_LITTLE_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_SH_MOBILE_LCDC is not set
+CONFIG_FB_SH7760=y
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOGO_LINUX_CLUT224=y
+CONFIG_LOGO_SUPERH_MONO=y
+CONFIG_LOGO_SUPERH_VGA16=y
+CONFIG_LOGO_SUPERH_CLUT224=y
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+# CONFIG_USB_DEVICEFS is not set
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+CONFIG_USB_MON=y
+# CONFIG_USB_WUSB is not set
+# CONFIG_USB_WUSB_CBAF is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_OXU210HP_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+CONFIG_USB_OHCI_HCD=y
+# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_TMC is not set
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed;
+#
+
+#
+# see USB_STORAGE Help for more information
+#
+CONFIG_USB_STORAGE=y
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+
+#
+# USB port drivers
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_BERRY_CHARGE is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_VST is not set
+# CONFIG_USB_GADGET is not set
+
+#
+# OTG and related infrastructure
+#
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# CONFIG_EXT3_FS_SECURITY is not set
+# CONFIG_EXT4_FS is not set
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_FILE_LOCKING=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+CONFIG_AUTOFS_FS=y
+CONFIG_AUTOFS4_FS=y
+# CONFIG_FUSE_FS is not set
+CONFIG_GENERIC_ACL=y
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS2_FS is not set
+CONFIG_CRAMFS=y
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+CONFIG_ROMFS_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+# CONFIG_NFS_V3 is not set
+# CONFIG_NFS_V4 is not set
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+CONFIG_LOCKD=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_SUNRPC_REGISTER_V4 is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=y
+CONFIG_NLS_CODEPAGE_775=y
+CONFIG_NLS_CODEPAGE_850=y
+CONFIG_NLS_CODEPAGE_852=y
+CONFIG_NLS_CODEPAGE_855=y
+CONFIG_NLS_CODEPAGE_857=y
+CONFIG_NLS_CODEPAGE_860=y
+CONFIG_NLS_CODEPAGE_861=y
+CONFIG_NLS_CODEPAGE_862=y
+CONFIG_NLS_CODEPAGE_863=y
+CONFIG_NLS_CODEPAGE_864=y
+CONFIG_NLS_CODEPAGE_865=y
+CONFIG_NLS_CODEPAGE_866=y
+CONFIG_NLS_CODEPAGE_869=y
+CONFIG_NLS_CODEPAGE_936=y
+CONFIG_NLS_CODEPAGE_950=y
+CONFIG_NLS_CODEPAGE_932=y
+CONFIG_NLS_CODEPAGE_949=y
+CONFIG_NLS_CODEPAGE_874=y
+CONFIG_NLS_ISO8859_8=y
+CONFIG_NLS_CODEPAGE_1250=y
+CONFIG_NLS_CODEPAGE_1251=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=y
+CONFIG_NLS_ISO8859_3=y
+CONFIG_NLS_ISO8859_4=y
+CONFIG_NLS_ISO8859_5=y
+CONFIG_NLS_ISO8859_6=y
+CONFIG_NLS_ISO8859_7=y
+CONFIG_NLS_ISO8859_9=y
+CONFIG_NLS_ISO8859_13=y
+CONFIG_NLS_ISO8859_14=y
+CONFIG_NLS_ISO8859_15=y
+CONFIG_NLS_KOI8_R=y
+CONFIG_NLS_KOI8_U=y
+CONFIG_NLS_UTF8=y
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_PRINTK_TIME is not set
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+CONFIG_STACKTRACE=y
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_LATENCYTOP is not set
+CONFIG_NOP_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_RING_BUFFER=y
+CONFIG_TRACING=y
+
+#
+# Tracers
+#
+# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_SH_STANDARD_BIOS is not set
+# CONFIG_EARLY_SCIF_CONSOLE is not set
+# CONFIG_MORE_COMPILE_OPTIONS is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_FIPS is not set
+# CONFIG_CRYPTO_MANAGER is not set
+# CONFIG_CRYPTO_MANAGER2 is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+# CONFIG_CRYPTO_CBC is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_HW=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_FIND_LAST_BIT=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+CONFIG_CRC_T10DIF=y
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/sh/configs/polaris_defconfig b/arch/sh/configs/polaris_defconfig
new file mode 100644
index 0000000..320def2
--- /dev/null
+++ b/arch/sh/configs/polaris_defconfig
@@ -0,0 +1,969 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.29-rc4
+# Wed Feb 11 18:41:59 2009
+#
+CONFIG_SUPERH=y
+CONFIG_SUPERH32=y
+CONFIG_ARCH_DEFCONFIG="arch/sh/configs/shx3_defconfig"
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_FIND_NEXT_BIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_GENERIC_IRQ_PROBE=y
+# CONFIG_GENERIC_GPIO is not set
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_ARCH_SUSPEND_POSSIBLE is not set
+# CONFIG_ARCH_HIBERNATION_POSSIBLE is not set
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_ARCH_NO_VIRT_TO_BUS=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+# CONFIG_TASKSTATS is not set
+CONFIG_AUDIT=y
+# CONFIG_AUDITSYSCALL is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_CLASSIC_RCU=y
+# CONFIG_TREE_RCU is not set
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_PREEMPT_RCU_TRACE is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_GROUP_SCHED=y
+CONFIG_FAIR_GROUP_SCHED=y
+# CONFIG_RT_GROUP_SCHED is not set
+CONFIG_USER_SCHED=y
+# CONFIG_CGROUP_SCHED is not set
+# CONFIG_CGROUPS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_ALL=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_IOREMAP_PROT=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_MODVERSIONS=y
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_AS is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+# CONFIG_FREEZER is not set
+
+#
+# System type
+#
+CONFIG_CPU_SH3=y
+# CONFIG_CPU_SUBTYPE_SH7619 is not set
+# CONFIG_CPU_SUBTYPE_SH7201 is not set
+# CONFIG_CPU_SUBTYPE_SH7203 is not set
+# CONFIG_CPU_SUBTYPE_SH7206 is not set
+# CONFIG_CPU_SUBTYPE_SH7263 is not set
+# CONFIG_CPU_SUBTYPE_MXG is not set
+# CONFIG_CPU_SUBTYPE_SH7705 is not set
+# CONFIG_CPU_SUBTYPE_SH7706 is not set
+# CONFIG_CPU_SUBTYPE_SH7707 is not set
+# CONFIG_CPU_SUBTYPE_SH7708 is not set
+CONFIG_CPU_SUBTYPE_SH7709=y
+# CONFIG_CPU_SUBTYPE_SH7710 is not set
+# CONFIG_CPU_SUBTYPE_SH7712 is not set
+# CONFIG_CPU_SUBTYPE_SH7720 is not set
+# CONFIG_CPU_SUBTYPE_SH7721 is not set
+# CONFIG_CPU_SUBTYPE_SH7750 is not set
+# CONFIG_CPU_SUBTYPE_SH7091 is not set
+# CONFIG_CPU_SUBTYPE_SH7750R is not set
+# CONFIG_CPU_SUBTYPE_SH7750S is not set
+# CONFIG_CPU_SUBTYPE_SH7751 is not set
+# CONFIG_CPU_SUBTYPE_SH7751R is not set
+# CONFIG_CPU_SUBTYPE_SH7760 is not set
+# CONFIG_CPU_SUBTYPE_SH4_202 is not set
+# CONFIG_CPU_SUBTYPE_SH7723 is not set
+# CONFIG_CPU_SUBTYPE_SH7763 is not set
+# CONFIG_CPU_SUBTYPE_SH7770 is not set
+# CONFIG_CPU_SUBTYPE_SH7780 is not set
+# CONFIG_CPU_SUBTYPE_SH7785 is not set
+# CONFIG_CPU_SUBTYPE_SHX3 is not set
+# CONFIG_CPU_SUBTYPE_SH7343 is not set
+# CONFIG_CPU_SUBTYPE_SH7722 is not set
+# CONFIG_CPU_SUBTYPE_SH7366 is not set
+# CONFIG_CPU_SUBTYPE_SH5_101 is not set
+# CONFIG_CPU_SUBTYPE_SH5_103 is not set
+
+#
+# Memory management options
+#
+CONFIG_QUICKLIST=y
+CONFIG_MMU=y
+CONFIG_PAGE_OFFSET=0x80000000
+CONFIG_MEMORY_START=0x0C000000
+CONFIG_MEMORY_SIZE=0x04000000
+CONFIG_29BIT=y
+CONFIG_VSYSCALL=y
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_DEFAULT=y
+CONFIG_MAX_ACTIVE_REGIONS=1
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_8KB is not set
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_64KB is not set
+CONFIG_ENTRY_OFFSET=0x00001000
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_SPARSEMEM_STATIC=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_NR_QUICK=2
+CONFIG_UNEVICTABLE_LRU=y
+
+#
+# Cache configuration
+#
+# CONFIG_SH_DIRECT_MAPPED is not set
+CONFIG_CACHE_WRITEBACK=y
+# CONFIG_CACHE_WRITETHROUGH is not set
+# CONFIG_CACHE_OFF is not set
+
+#
+# Processor features
+#
+CONFIG_CPU_LITTLE_ENDIAN=y
+# CONFIG_CPU_BIG_ENDIAN is not set
+CONFIG_SH_FPU_EMU=y
+CONFIG_SH_ADC=y
+CONFIG_CPU_HAS_INTEVT=y
+CONFIG_CPU_HAS_IPR_IRQ=y
+CONFIG_CPU_HAS_SR_RB=y
+
+#
+# Board support
+#
+# CONFIG_SH_SOLUTION_ENGINE is not set
+# CONFIG_SH_HP6XX is not set
+CONFIG_SH_POLARIS=y
+
+#
+# Timer and clock configuration
+#
+CONFIG_SH_TMU=y
+CONFIG_SH_TIMER_IRQ=16
+CONFIG_SH_PCLK_FREQ=33000000
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# DMA support
+#
+CONFIG_SH_DMA_API=y
+CONFIG_SH_DMA=y
+CONFIG_NR_ONCHIP_DMA_CHANNELS=4
+# CONFIG_NR_DMA_CHANNELS_BOOL is not set
+
+#
+# Companion Chips
+#
+
+#
+# Additional SuperH Device Drivers
+#
+CONFIG_HEARTBEAT=y
+# CONFIG_PUSH_SWITCH is not set
+
+#
+# Kernel features
+#
+CONFIG_HZ_100=y
+# CONFIG_HZ_250 is not set
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=100
+CONFIG_SCHED_HRTICK=y
+# CONFIG_KEXEC is not set
+# CONFIG_CRASH_DUMP is not set
+# CONFIG_SECCOMP is not set
+# CONFIG_PREEMPT_NONE is not set
+# CONFIG_PREEMPT_VOLUNTARY is not set
+CONFIG_PREEMPT=y
+CONFIG_GUSA=y
+# CONFIG_GUSA_RB is not set
+
+#
+# Boot options
+#
+CONFIG_ZERO_PAGE_OFFSET=0x00001000
+CONFIG_BOOT_LINK_OFFSET=0x00800000
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttySC1,115200 root=/dev/mtdblock2 rootfstype=jffs2 mem=63M mtdparts=physmap-flash.0:0x00100000(bootloader)ro,0x00500000(Kernel)ro,0x00A00000(Filesystem)"
+
+#
+# Bus options
+#
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_HAVE_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options (EXPERIMENTAL)
+#
+# CONFIG_PM is not set
+# CONFIG_CPU_IDLE is not set
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_COMPAT_NET_DEV_OPS=y
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+# CONFIG_IP_PNP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_PHONET is not set
+# CONFIG_WIRELESS is not set
+# CONFIG_WIMAX is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_NOSWAP=y
+# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
+# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
+# CONFIG_MTD_CFI_GEOMETRY is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_OTP is not set
+CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_COMPAT=y
+CONFIG_MTD_PHYSMAP_START=0x00000000
+CONFIG_MTD_PHYSMAP_LEN=0x01000000
+CONFIG_MTD_PHYSMAP_BANKWIDTH=2
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+# CONFIG_MTD_QINFO_PROBE is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_BLK_DEV_HD is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_C2PORT is not set
+
+#
+# EEPROM support
+#
+# CONFIG_EEPROM_93CX6 is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+CONFIG_PHYLIB=y
+
+#
+# MII PHY device drivers
+#
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_VITESSE_PHY is not set
+CONFIG_SMSC_PHY=y
+# CONFIG_BROADCOM_PHY is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_NATIONAL_PHY is not set
+# CONFIG_STE10XP is not set
+# CONFIG_LSI_ET1011C_PHY is not set
+# CONFIG_FIXED_PHY is not set
+# CONFIG_MDIO_BITBANG is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
+# CONFIG_STNIC is not set
+# CONFIG_SMC91X is not set
+# CONFIG_SMC911X is not set
+CONFIG_SMSC911X=y
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_DEVKMEM=y
+CONFIG_SERIAL_NONSTANDARD=y
+# CONFIG_N_HDLC is not set
+# CONFIG_RISCOM8 is not set
+# CONFIG_SPECIALIX is not set
+# CONFIG_RIO is not set
+# CONFIG_STALDRV is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_NR_UARTS=3
+CONFIG_SERIAL_SH_SCI_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_REGULATOR is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_DRV_SH=y
+# CONFIG_DMADEVICES is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+
+#
+# File systems
+#
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+# CONFIG_JFFS2_FS_WRITEBUFFER is not set
+# CONFIG_JFFS2_SUMMARY is not set
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFSD is not set
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_SUNRPC_REGISTER_V4 is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DEBUG_SHIRQ=y
+CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_DEBUG_SLAB is not set
+CONFIG_DEBUG_PREEMPT=y
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_PI_LIST=y
+# CONFIG_RT_MUTEX_TESTER is not set
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_LOCK_ALLOC=y
+# CONFIG_PROVE_LOCKING is not set
+CONFIG_LOCKDEP=y
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_LOCKDEP is not set
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+CONFIG_STACKTRACE=y
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_DEBUG_LIST is not set
+CONFIG_DEBUG_SG=y
+# CONFIG_DEBUG_NOTIFIERS is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_LATENCYTOP is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+
+#
+# Tracers
+#
+# CONFIG_FUNCTION_TRACER is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_PREEMPT_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_CONTEXT_SWITCH_TRACER is not set
+# CONFIG_BOOT_TRACER is not set
+# CONFIG_TRACE_BRANCH_PROFILING is not set
+# CONFIG_STACK_TRACER is not set
+# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+# CONFIG_SH_STANDARD_BIOS is not set
+CONFIG_EARLY_SCIF_CONSOLE=y
+CONFIG_EARLY_SCIF_CONSOLE_PORT=0x00000000
+CONFIG_EARLY_PRINTK=y
+# CONFIG_DEBUG_BOOTMEM is not set
+# CONFIG_DEBUG_STACKOVERFLOW is not set
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_4KSTACKS is not set
+# CONFIG_IRQSTACKS is not set
+CONFIG_DUMP_CODE=y
+# CONFIG_SH_NO_BSS_INIT is not set
+# CONFIG_MORE_COMPILE_OPTIONS is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+# CONFIG_CRYPTO is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_FIND_LAST_BIT=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_AUDIT_GENERIC=y
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/sh/configs/sh7785lcr_32bit_defconfig b/arch/sh/configs/sh7785lcr_32bit_defconfig
new file mode 100644
index 0000000..54e1dee
--- /dev/null
+++ b/arch/sh/configs/sh7785lcr_32bit_defconfig
@@ -0,0 +1,1553 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.29-rc4
+# Fri Feb 20 18:25:29 2009
+#
+CONFIG_SUPERH=y
+CONFIG_SUPERH32=y
+CONFIG_ARCH_DEFCONFIG="arch/sh/configs/shx3_defconfig"
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_FIND_NEXT_BIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_GENERIC_IRQ_PROBE=y
+# CONFIG_GENERIC_GPIO is not set
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_ARCH_SUSPEND_POSSIBLE is not set
+# CONFIG_ARCH_HIBERNATION_POSSIBLE is not set
+CONFIG_SYS_SUPPORTS_NUMA=y
+CONFIG_SYS_SUPPORTS_PCI=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_ARCH_NO_VIRT_TO_BUS=y
+CONFIG_IO_TRAPPED=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_CLASSIC_RCU=y
+# CONFIG_TREE_RCU is not set
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_PREEMPT_RCU_TRACE is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_GROUP_SCHED=y
+CONFIG_FAIR_GROUP_SCHED=y
+# CONFIG_RT_GROUP_SCHED is not set
+CONFIG_USER_SCHED=y
+# CONFIG_CGROUP_SCHED is not set
+# CONFIG_CGROUPS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_PCI_QUIRKS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_PROFILING=y
+# CONFIG_OPROFILE is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_IOREMAP_PROT=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+# CONFIG_FREEZER is not set
+
+#
+# System type
+#
+CONFIG_CPU_SH4=y
+CONFIG_CPU_SH4A=y
+CONFIG_CPU_SHX2=y
+# CONFIG_CPU_SUBTYPE_SH7619 is not set
+# CONFIG_CPU_SUBTYPE_SH7201 is not set
+# CONFIG_CPU_SUBTYPE_SH7203 is not set
+# CONFIG_CPU_SUBTYPE_SH7206 is not set
+# CONFIG_CPU_SUBTYPE_SH7263 is not set
+# CONFIG_CPU_SUBTYPE_MXG is not set
+# CONFIG_CPU_SUBTYPE_SH7705 is not set
+# CONFIG_CPU_SUBTYPE_SH7706 is not set
+# CONFIG_CPU_SUBTYPE_SH7707 is not set
+# CONFIG_CPU_SUBTYPE_SH7708 is not set
+# CONFIG_CPU_SUBTYPE_SH7709 is not set
+# CONFIG_CPU_SUBTYPE_SH7710 is not set
+# CONFIG_CPU_SUBTYPE_SH7712 is not set
+# CONFIG_CPU_SUBTYPE_SH7720 is not set
+# CONFIG_CPU_SUBTYPE_SH7721 is not set
+# CONFIG_CPU_SUBTYPE_SH7750 is not set
+# CONFIG_CPU_SUBTYPE_SH7091 is not set
+# CONFIG_CPU_SUBTYPE_SH7750R is not set
+# CONFIG_CPU_SUBTYPE_SH7750S is not set
+# CONFIG_CPU_SUBTYPE_SH7751 is not set
+# CONFIG_CPU_SUBTYPE_SH7751R is not set
+# CONFIG_CPU_SUBTYPE_SH7760 is not set
+# CONFIG_CPU_SUBTYPE_SH4_202 is not set
+# CONFIG_CPU_SUBTYPE_SH7723 is not set
+# CONFIG_CPU_SUBTYPE_SH7763 is not set
+# CONFIG_CPU_SUBTYPE_SH7770 is not set
+# CONFIG_CPU_SUBTYPE_SH7780 is not set
+CONFIG_CPU_SUBTYPE_SH7785=y
+# CONFIG_CPU_SUBTYPE_SHX3 is not set
+# CONFIG_CPU_SUBTYPE_SH7343 is not set
+# CONFIG_CPU_SUBTYPE_SH7722 is not set
+# CONFIG_CPU_SUBTYPE_SH7366 is not set
+# CONFIG_CPU_SUBTYPE_SH5_101 is not set
+# CONFIG_CPU_SUBTYPE_SH5_103 is not set
+
+#
+# Memory management options
+#
+CONFIG_QUICKLIST=y
+CONFIG_MMU=y
+CONFIG_PAGE_OFFSET=0x80000000
+CONFIG_MEMORY_START=0x40000000
+CONFIG_MEMORY_SIZE=0x20000000
+# CONFIG_29BIT is not set
+CONFIG_32BIT=y
+CONFIG_PMB_ENABLE=y
+# CONFIG_PMB is not set
+CONFIG_PMB_FIXED=y
+# CONFIG_X2TLB is not set
+CONFIG_VSYSCALL=y
+# CONFIG_NUMA is not set
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_DEFAULT=y
+CONFIG_MAX_ACTIVE_REGIONS=2
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_8KB is not set
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_64KB is not set
+CONFIG_ENTRY_OFFSET=0x00001000
+CONFIG_SELECT_MEMORY_MODEL=y
+# CONFIG_FLATMEM_MANUAL is not set
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+CONFIG_SPARSEMEM_MANUAL=y
+CONFIG_SPARSEMEM=y
+CONFIG_HAVE_MEMORY_PRESENT=y
+CONFIG_SPARSEMEM_STATIC=y
+# CONFIG_MEMORY_HOTPLUG is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+CONFIG_MIGRATION=y
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_NR_QUICK=2
+CONFIG_UNEVICTABLE_LRU=y
+
+#
+# Cache configuration
+#
+# CONFIG_SH_DIRECT_MAPPED is not set
+CONFIG_CACHE_WRITEBACK=y
+# CONFIG_CACHE_WRITETHROUGH is not set
+# CONFIG_CACHE_OFF is not set
+
+#
+# Processor features
+#
+CONFIG_CPU_LITTLE_ENDIAN=y
+# CONFIG_CPU_BIG_ENDIAN is not set
+CONFIG_SH_FPU=y
+CONFIG_SH_STORE_QUEUES=y
+CONFIG_CPU_HAS_INTEVT=y
+CONFIG_CPU_HAS_SR_RB=y
+CONFIG_CPU_HAS_PTEA=y
+CONFIG_CPU_HAS_FPU=y
+
+#
+# Board support
+#
+# CONFIG_SH_HIGHLANDER is not set
+CONFIG_SH_SH7785LCR=y
+
+#
+# Timer and clock configuration
+#
+CONFIG_SH_TMU=y
+CONFIG_SH_TIMER_IRQ=28
+CONFIG_SH_PCLK_FREQ=50000000
+CONFIG_TICK_ONESHOT=y
+# CONFIG_NO_HZ is not set
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# DMA support
+#
+# CONFIG_SH_DMA is not set
+
+#
+# Companion Chips
+#
+
+#
+# Additional SuperH Device Drivers
+#
+CONFIG_HEARTBEAT=y
+# CONFIG_PUSH_SWITCH is not set
+
+#
+# Kernel features
+#
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+CONFIG_SCHED_HRTICK=y
+CONFIG_KEXEC=y
+# CONFIG_CRASH_DUMP is not set
+# CONFIG_SECCOMP is not set
+# CONFIG_PREEMPT_NONE is not set
+# CONFIG_PREEMPT_VOLUNTARY is not set
+CONFIG_PREEMPT=y
+CONFIG_GUSA=y
+
+#
+# Boot options
+#
+CONFIG_ZERO_PAGE_OFFSET=0x00001000
+CONFIG_BOOT_LINK_OFFSET=0x00800000
+# CONFIG_CMDLINE_BOOL is not set
+
+#
+# Bus options
+#
+CONFIG_PCI=y
+CONFIG_SH_PCIDMA_NONCOHERENT=y
+CONFIG_PCI_AUTO=y
+CONFIG_PCI_AUTO_UPDATE_RESOURCES=y
+# CONFIG_PCIEPORTBUS is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+CONFIG_PCI_LEGACY=y
+# CONFIG_PCI_DEBUG is not set
+# CONFIG_PCI_STUB is not set
+# CONFIG_PCCARD is not set
+# CONFIG_HOTPLUG_PCI is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_HAVE_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options (EXPERIMENTAL)
+#
+# CONFIG_PM is not set
+# CONFIG_CPU_IDLE is not set
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_COMPAT_NET_DEV_OPS=y
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_ASK_IP_FIB_HASH=y
+# CONFIG_IP_FIB_TRIE is not set
+CONFIG_IP_FIB_HASH=y
+# CONFIG_IP_MULTIPLE_TABLES is not set
+# CONFIG_IP_ROUTE_MULTIPATH is not set
+# CONFIG_IP_ROUTE_VERBOSE is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_PHONET is not set
+CONFIG_WIRELESS=y
+# CONFIG_CFG80211 is not set
+# CONFIG_WIRELESS_OLD_REGULATORY is not set
+CONFIG_WIRELESS_EXT=y
+CONFIG_WIRELESS_EXT_SYSFS=y
+# CONFIG_LIB80211 is not set
+# CONFIG_MAC80211 is not set
+# CONFIG_WIMAX is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER is not set
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+CONFIG_MTD_CONCAT=y
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+# CONFIG_MTD_CMDLINE_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
+# CONFIG_MTD_INTEL_VR_NOR is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_PMC551 is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+# CONFIG_MTD_QINFO_PROBE is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_SX8 is not set
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_BLK_DEV_HD is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+# CONFIG_SCSI_LOWLEVEL is not set
+# CONFIG_SCSI_DH is not set
+CONFIG_ATA=y
+# CONFIG_ATA_NONSTANDARD is not set
+CONFIG_SATA_PMP=y
+# CONFIG_SATA_AHCI is not set
+# CONFIG_SATA_SIL24 is not set
+CONFIG_ATA_SFF=y
+# CONFIG_SATA_SVW is not set
+# CONFIG_ATA_PIIX is not set
+# CONFIG_SATA_MV is not set
+# CONFIG_SATA_NV is not set
+# CONFIG_PDC_ADMA is not set
+# CONFIG_SATA_QSTOR is not set
+# CONFIG_SATA_PROMISE is not set
+# CONFIG_SATA_SX4 is not set
+CONFIG_SATA_SIL=y
+# CONFIG_SATA_SIS is not set
+# CONFIG_SATA_ULI is not set
+# CONFIG_SATA_VIA is not set
+# CONFIG_SATA_VITESSE is not set
+# CONFIG_SATA_INIC162X is not set
+# CONFIG_PATA_ALI is not set
+# CONFIG_PATA_AMD is not set
+# CONFIG_PATA_ARTOP is not set
+# CONFIG_PATA_ATIIXP is not set
+# CONFIG_PATA_CMD640_PCI is not set
+# CONFIG_PATA_CMD64X is not set
+# CONFIG_PATA_CS5520 is not set
+# CONFIG_PATA_CS5530 is not set
+# CONFIG_PATA_CYPRESS is not set
+# CONFIG_PATA_EFAR is not set
+# CONFIG_ATA_GENERIC is not set
+# CONFIG_PATA_HPT366 is not set
+# CONFIG_PATA_HPT37X is not set
+# CONFIG_PATA_HPT3X2N is not set
+# CONFIG_PATA_HPT3X3 is not set
+# CONFIG_PATA_IT821X is not set
+# CONFIG_PATA_IT8213 is not set
+# CONFIG_PATA_JMICRON is not set
+# CONFIG_PATA_TRIFLEX is not set
+# CONFIG_PATA_MARVELL is not set
+# CONFIG_PATA_MPIIX is not set
+# CONFIG_PATA_OLDPIIX is not set
+# CONFIG_PATA_NETCELL is not set
+# CONFIG_PATA_NINJA32 is not set
+# CONFIG_PATA_NS87410 is not set
+# CONFIG_PATA_NS87415 is not set
+# CONFIG_PATA_OPTI is not set
+# CONFIG_PATA_OPTIDMA is not set
+# CONFIG_PATA_PDC_OLD is not set
+# CONFIG_PATA_RADISYS is not set
+# CONFIG_PATA_RZ1000 is not set
+# CONFIG_PATA_SC1200 is not set
+# CONFIG_PATA_SERVERWORKS is not set
+# CONFIG_PATA_PDC2027X is not set
+# CONFIG_PATA_SIL680 is not set
+# CONFIG_PATA_SIS is not set
+# CONFIG_PATA_VIA is not set
+# CONFIG_PATA_WINBOND is not set
+# CONFIG_PATA_PLATFORM is not set
+# CONFIG_PATA_SCH is not set
+# CONFIG_MD is not set
+# CONFIG_FUSION is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# Enable only one of the two stacks, unless you know what you are doing
+#
+# CONFIG_FIREWIRE is not set
+# CONFIG_IEEE1394 is not set
+# CONFIG_I2O is not set
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+# CONFIG_ARCNET is not set
+# CONFIG_NET_ETHERNET is not set
+CONFIG_MII=y
+CONFIG_NETDEV_1000=y
+# CONFIG_ACENIC is not set
+# CONFIG_DL2K is not set
+# CONFIG_E1000 is not set
+# CONFIG_E1000E is not set
+# CONFIG_IP1000 is not set
+# CONFIG_IGB is not set
+# CONFIG_NS83820 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_YELLOWFIN is not set
+CONFIG_R8169=y
+# CONFIG_SIS190 is not set
+# CONFIG_SKGE is not set
+# CONFIG_SKY2 is not set
+# CONFIG_VIA_VELOCITY is not set
+# CONFIG_TIGON3 is not set
+# CONFIG_BNX2 is not set
+# CONFIG_QLA3XXX is not set
+# CONFIG_ATL1 is not set
+# CONFIG_ATL1E is not set
+# CONFIG_JME is not set
+# CONFIG_NETDEV_10000 is not set
+# CONFIG_TR is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_FC is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+CONFIG_INPUT_FF_MEMLESS=m
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SH_KEYSC is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_NOZOMI is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_NR_UARTS=6
+CONFIG_SERIAL_SH_SCI_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_JSM is not set
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_DEVPORT=y
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_CHARDEV is not set
+CONFIG_I2C_HELPER_AUTO=y
+CONFIG_I2C_ALGOPCA=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# PC SMBus host controller drivers
+#
+# CONFIG_I2C_ALI1535 is not set
+# CONFIG_I2C_ALI1563 is not set
+# CONFIG_I2C_ALI15X3 is not set
+# CONFIG_I2C_AMD756 is not set
+# CONFIG_I2C_AMD8111 is not set
+# CONFIG_I2C_I801 is not set
+# CONFIG_I2C_ISCH is not set
+# CONFIG_I2C_PIIX4 is not set
+# CONFIG_I2C_NFORCE2 is not set
+# CONFIG_I2C_SIS5595 is not set
+# CONFIG_I2C_SIS630 is not set
+# CONFIG_I2C_SIS96X is not set
+# CONFIG_I2C_VIA is not set
+# CONFIG_I2C_VIAPRO is not set
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_SH_MOBILE is not set
+# CONFIG_I2C_SIMTEC is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Graphics adapter I2C/DDC channel drivers
+#
+# CONFIG_I2C_VOODOO3 is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+CONFIG_I2C_PCA_PLATFORM=y
+# CONFIG_I2C_STUB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+# CONFIG_SPI is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+CONFIG_MFD_SM501=y
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_REGULATOR is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_DRM is not set
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+CONFIG_FB_SYS_FILLRECT=m
+CONFIG_FB_SYS_COPYAREA=m
+CONFIG_FB_SYS_IMAGEBLIT=m
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+CONFIG_FB_SYS_FOPS=m
+CONFIG_FB_DEFERRED_IO=y
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_CIRRUS is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_CYBER2000 is not set
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_IMSTT is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_NVIDIA is not set
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_S3 is not set
+# CONFIG_FB_SAVAGE is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_VIA is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_KYRO is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_VT8623 is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_ARK is not set
+# CONFIG_FB_PM3 is not set
+# CONFIG_FB_CARMINE is not set
+CONFIG_FB_SH_MOBILE_LCDC=m
+CONFIG_FB_SM501=y
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_LOGO_LINUX_CLUT224=y
+# CONFIG_LOGO_SUPERH_MONO is not set
+# CONFIG_LOGO_SUPERH_VGA16 is not set
+# CONFIG_LOGO_SUPERH_CLUT224 is not set
+# CONFIG_SOUND is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=y
+# CONFIG_HID_PID is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# Special HID drivers
+#
+CONFIG_HID_COMPAT=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_LOGITECH=y
+# CONFIG_LOGITECH_FF is not set
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+# CONFIG_HID_NTRIG is not set
+CONFIG_HID_PANTHERLORD=y
+# CONFIG_PANTHERLORD_FF is not set
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SUNPLUS=y
+# CONFIG_GREENASIA_FF is not set
+# CONFIG_HID_TOPSEED is not set
+CONFIG_THRUSTMASTER_FF=m
+CONFIG_ZEROPLUS_FF=m
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+CONFIG_USB_ARCH_HAS_EHCI=y
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+CONFIG_USB_MON=y
+# CONFIG_USB_WUSB is not set
+# CONFIG_USB_WUSB_CBAF is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+CONFIG_USB_EHCI_HCD=m
+# CONFIG_USB_EHCI_ROOT_HUB_TT is not set
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+# CONFIG_USB_OXU210HP_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+CONFIG_USB_OHCI_HCD=m
+# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+# CONFIG_USB_UHCI_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+CONFIG_USB_R8A66597_HCD=y
+# CONFIG_USB_WHCI_HCD is not set
+# CONFIG_USB_HWA_HCD is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_TMC is not set
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed;
+#
+
+#
+# see USB_STORAGE Help for more information
+#
+CONFIG_USB_STORAGE=y
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+
+#
+# USB port drivers
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_BERRY_CHARGE is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+CONFIG_USB_TEST=m
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_VST is not set
+# CONFIG_USB_GADGET is not set
+
+#
+# OTG and related infrastructure
+#
+# CONFIG_UWB is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_INFINIBAND is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+CONFIG_RTC_DRV_RS5C372=y
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+# CONFIG_RTC_DRV_SH is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# CONFIG_EXT3_FS_SECURITY is not set
+# CONFIG_EXT4_FS is not set
+CONFIG_JBD=y
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_FILE_LOCKING=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+CONFIG_NTFS_FS=y
+# CONFIG_NTFS_DEBUG is not set
+CONFIG_NTFS_RW=y
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS2_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+CONFIG_MINIX_FS=y
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFSD=y
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V3_ACL is not set
+CONFIG_NFSD_V4=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+# CONFIG_SUNRPC_REGISTER_V4 is not set
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+CONFIG_NLS_CODEPAGE_932=y
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_PRINTK_TIME is not set
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+CONFIG_SCHED_DEBUG=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_DEBUG_SLAB is not set
+CONFIG_DEBUG_PREEMPT=y
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
+# CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_FRAME_POINTER is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_LATENCYTOP is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+
+#
+# Tracers
+#
+# CONFIG_FUNCTION_TRACER is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_PREEMPT_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_CONTEXT_SWITCH_TRACER is not set
+# CONFIG_BOOT_TRACER is not set
+# CONFIG_TRACE_BRANCH_PROFILING is not set
+# CONFIG_STACK_TRACER is not set
+# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+# CONFIG_SH_STANDARD_BIOS is not set
+# CONFIG_EARLY_SCIF_CONSOLE is not set
+# CONFIG_DEBUG_BOOTMEM is not set
+# CONFIG_DEBUG_STACKOVERFLOW is not set
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_4KSTACKS is not set
+# CONFIG_IRQSTACKS is not set
+# CONFIG_DUMP_CODE is not set
+# CONFIG_SH_NO_BSS_INIT is not set
+# CONFIG_MORE_COMPILE_OPTIONS is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_FIPS is not set
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_BLKCIPHER2=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=y
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+CONFIG_CRYPTO_HMAC=y
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_HW is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_FIND_LAST_BIT=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/sh/configs/urquell_defconfig b/arch/sh/configs/urquell_defconfig
new file mode 100644
index 0000000..be726c7
--- /dev/null
+++ b/arch/sh/configs/urquell_defconfig
@@ -0,0 +1,1332 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.29-rc4
+# Thu Mar 5 17:28:13 2009
+#
+CONFIG_SUPERH=y
+CONFIG_SUPERH32=y
+CONFIG_ARCH_DEFCONFIG="arch/sh/configs/shx3_defconfig"
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_FIND_NEXT_BIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_ARCH_SUSPEND_POSSIBLE is not set
+# CONFIG_ARCH_HIBERNATION_POSSIBLE is not set
+CONFIG_SYS_SUPPORTS_NUMA=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_ARCH_NO_VIRT_TO_BUS=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_CLASSIC_RCU=y
+# CONFIG_TREE_RCU is not set
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_PREEMPT_RCU_TRACE is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_GROUP_SCHED=y
+CONFIG_FAIR_GROUP_SCHED=y
+# CONFIG_RT_GROUP_SCHED is not set
+CONFIG_USER_SCHED=y
+# CONFIG_CGROUP_SCHED is not set
+# CONFIG_CGROUPS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_PROFILING=y
+# CONFIG_OPROFILE is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_IOREMAP_PROT=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+# CONFIG_FREEZER is not set
+
+#
+# System type
+#
+CONFIG_CPU_SH4=y
+CONFIG_CPU_SH4A=y
+CONFIG_CPU_SHX3=y
+# CONFIG_CPU_SUBTYPE_SH7619 is not set
+# CONFIG_CPU_SUBTYPE_SH7201 is not set
+# CONFIG_CPU_SUBTYPE_SH7203 is not set
+# CONFIG_CPU_SUBTYPE_SH7206 is not set
+# CONFIG_CPU_SUBTYPE_SH7263 is not set
+# CONFIG_CPU_SUBTYPE_MXG is not set
+# CONFIG_CPU_SUBTYPE_SH7705 is not set
+# CONFIG_CPU_SUBTYPE_SH7706 is not set
+# CONFIG_CPU_SUBTYPE_SH7707 is not set
+# CONFIG_CPU_SUBTYPE_SH7708 is not set
+# CONFIG_CPU_SUBTYPE_SH7709 is not set
+# CONFIG_CPU_SUBTYPE_SH7710 is not set
+# CONFIG_CPU_SUBTYPE_SH7712 is not set
+# CONFIG_CPU_SUBTYPE_SH7720 is not set
+# CONFIG_CPU_SUBTYPE_SH7721 is not set
+# CONFIG_CPU_SUBTYPE_SH7750 is not set
+# CONFIG_CPU_SUBTYPE_SH7091 is not set
+# CONFIG_CPU_SUBTYPE_SH7750R is not set
+# CONFIG_CPU_SUBTYPE_SH7750S is not set
+# CONFIG_CPU_SUBTYPE_SH7751 is not set
+# CONFIG_CPU_SUBTYPE_SH7751R is not set
+# CONFIG_CPU_SUBTYPE_SH7760 is not set
+# CONFIG_CPU_SUBTYPE_SH4_202 is not set
+# CONFIG_CPU_SUBTYPE_SH7723 is not set
+# CONFIG_CPU_SUBTYPE_SH7763 is not set
+# CONFIG_CPU_SUBTYPE_SH7770 is not set
+# CONFIG_CPU_SUBTYPE_SH7780 is not set
+# CONFIG_CPU_SUBTYPE_SH7785 is not set
+CONFIG_CPU_SUBTYPE_SH7786=y
+# CONFIG_CPU_SUBTYPE_SHX3 is not set
+# CONFIG_CPU_SUBTYPE_SH7343 is not set
+# CONFIG_CPU_SUBTYPE_SH7722 is not set
+# CONFIG_CPU_SUBTYPE_SH7366 is not set
+# CONFIG_CPU_SUBTYPE_SH5_101 is not set
+# CONFIG_CPU_SUBTYPE_SH5_103 is not set
+
+#
+# Memory management options
+#
+CONFIG_QUICKLIST=y
+CONFIG_MMU=y
+CONFIG_PAGE_OFFSET=0x80000000
+CONFIG_MEMORY_START=0x08000000
+CONFIG_MEMORY_SIZE=0x08000000
+CONFIG_29BIT=y
+# CONFIG_X2TLB is not set
+CONFIG_VSYSCALL=y
+# CONFIG_NUMA is not set
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_DEFAULT=y
+CONFIG_MAX_ACTIVE_REGIONS=1
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_8KB is not set
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_64KB is not set
+CONFIG_ENTRY_OFFSET=0x00001000
+CONFIG_SELECT_MEMORY_MODEL=y
+# CONFIG_FLATMEM_MANUAL is not set
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+CONFIG_SPARSEMEM_MANUAL=y
+CONFIG_SPARSEMEM=y
+CONFIG_HAVE_MEMORY_PRESENT=y
+CONFIG_SPARSEMEM_STATIC=y
+# CONFIG_MEMORY_HOTPLUG is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+CONFIG_MIGRATION=y
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_NR_QUICK=2
+CONFIG_UNEVICTABLE_LRU=y
+
+#
+# Cache configuration
+#
+# CONFIG_SH_DIRECT_MAPPED is not set
+CONFIG_CACHE_WRITEBACK=y
+# CONFIG_CACHE_WRITETHROUGH is not set
+# CONFIG_CACHE_OFF is not set
+
+#
+# Processor features
+#
+CONFIG_CPU_LITTLE_ENDIAN=y
+# CONFIG_CPU_BIG_ENDIAN is not set
+CONFIG_SH_FPU=y
+CONFIG_SH_STORE_QUEUES=y
+CONFIG_CPU_HAS_INTEVT=y
+CONFIG_CPU_HAS_SR_RB=y
+CONFIG_CPU_HAS_FPU=y
+
+#
+# Board support
+#
+CONFIG_SH_URQUELL=y
+
+#
+# Timer and clock configuration
+#
+CONFIG_SH_TMU=y
+CONFIG_SH_TIMER_IRQ=16
+CONFIG_SH_PCLK_FREQ=33333333
+CONFIG_TICK_ONESHOT=y
+# CONFIG_NO_HZ is not set
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# DMA support
+#
+# CONFIG_SH_DMA is not set
+
+#
+# Companion Chips
+#
+
+#
+# Additional SuperH Device Drivers
+#
+CONFIG_HEARTBEAT=y
+# CONFIG_PUSH_SWITCH is not set
+
+#
+# Kernel features
+#
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+CONFIG_SCHED_HRTICK=y
+CONFIG_KEXEC=y
+# CONFIG_CRASH_DUMP is not set
+# CONFIG_SECCOMP is not set
+# CONFIG_PREEMPT_NONE is not set
+# CONFIG_PREEMPT_VOLUNTARY is not set
+CONFIG_PREEMPT=y
+CONFIG_GUSA=y
+
+#
+# Boot options
+#
+CONFIG_ZERO_PAGE_OFFSET=0x00001000
+CONFIG_BOOT_LINK_OFFSET=0x00800000
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttySC1, 38400 earlyprintk=serial ip=on ignore_loglevel root=/dev/nfs ip=dhcp memchunk.vpu=4m"
+
+#
+# Bus options
+#
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_HAVE_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options (EXPERIMENTAL)
+#
+# CONFIG_PM is not set
+# CONFIG_CPU_IDLE is not set
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_COMPAT_NET_DEV_OPS=y
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_ASK_IP_FIB_HASH=y
+# CONFIG_IP_FIB_TRIE is not set
+CONFIG_IP_FIB_HASH=y
+# CONFIG_IP_MULTIPLE_TABLES is not set
+# CONFIG_IP_ROUTE_MULTIPATH is not set
+# CONFIG_IP_ROUTE_VERBOSE is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_PHONET is not set
+CONFIG_WIRELESS=y
+# CONFIG_CFG80211 is not set
+# CONFIG_WIRELESS_OLD_REGULATORY is not set
+CONFIG_WIRELESS_EXT=y
+CONFIG_WIRELESS_EXT_SYSFS=y
+# CONFIG_LIB80211 is not set
+# CONFIG_MAC80211 is not set
+# CONFIG_WIMAX is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+CONFIG_MTD_CONCAT=y
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+# CONFIG_MTD_CMDLINE_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+# CONFIG_MTD_QINFO_PROBE is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_BLK_DEV_HD is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+# CONFIG_SCSI_LOWLEVEL is not set
+# CONFIG_SCSI_DH is not set
+CONFIG_ATA=y
+# CONFIG_ATA_NONSTANDARD is not set
+CONFIG_SATA_PMP=y
+CONFIG_ATA_SFF=y
+# CONFIG_SATA_MV is not set
+# CONFIG_PATA_PLATFORM is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+CONFIG_PHYLIB=y
+
+#
+# MII PHY device drivers
+#
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_VITESSE_PHY is not set
+# CONFIG_SMSC_PHY is not set
+# CONFIG_BROADCOM_PHY is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_NATIONAL_PHY is not set
+# CONFIG_STE10XP is not set
+# CONFIG_LSI_ET1011C_PHY is not set
+# CONFIG_FIXED_PHY is not set
+# CONFIG_MDIO_BITBANG is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
+# CONFIG_STNIC is not set
+CONFIG_SMC91X=y
+# CONFIG_SMC911X is not set
+# CONFIG_SMSC911X is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+CONFIG_INPUT_FF_MEMLESS=m
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_GPIO is not set
+# CONFIG_KEYBOARD_SH_KEYSC is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_NR_UARTS=6
+CONFIG_SERIAL_SH_SCI_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_CHARDEV is not set
+CONFIG_I2C_HELPER_AUTO=y
+CONFIG_I2C_ALGOPCA=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_GPIO is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_SH_MOBILE is not set
+# CONFIG_I2C_SIMTEC is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+CONFIG_I2C_PCA_PLATFORM=y
+# CONFIG_I2C_STUB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+# CONFIG_SPI is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+# CONFIG_GPIO_SYSFS is not set
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+CONFIG_MFD_SM501=y
+# CONFIG_MFD_SM501_GPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_REGULATOR is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+CONFIG_FB_SYS_FILLRECT=m
+CONFIG_FB_SYS_COPYAREA=m
+CONFIG_FB_SYS_IMAGEBLIT=m
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+CONFIG_FB_SYS_FOPS=m
+CONFIG_FB_DEFERRED_IO=y
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_S1D13XXX is not set
+CONFIG_FB_SH_MOBILE_LCDC=m
+CONFIG_FB_SM501=y
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_LOGO_LINUX_CLUT224=y
+# CONFIG_LOGO_SUPERH_MONO is not set
+# CONFIG_LOGO_SUPERH_VGA16 is not set
+# CONFIG_LOGO_SUPERH_CLUT224 is not set
+# CONFIG_SOUND is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=y
+# CONFIG_HID_PID is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# Special HID drivers
+#
+CONFIG_HID_COMPAT=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_LOGITECH=y
+# CONFIG_LOGITECH_FF is not set
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+# CONFIG_HID_NTRIG is not set
+CONFIG_HID_PANTHERLORD=y
+# CONFIG_PANTHERLORD_FF is not set
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SUNPLUS=y
+# CONFIG_GREENASIA_FF is not set
+# CONFIG_HID_TOPSEED is not set
+CONFIG_THRUSTMASTER_FF=m
+CONFIG_ZEROPLUS_FF=m
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+CONFIG_USB_MON=y
+# CONFIG_USB_WUSB is not set
+# CONFIG_USB_WUSB_CBAF is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_OXU210HP_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_TMC is not set
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed;
+#
+
+#
+# see USB_STORAGE Help for more information
+#
+CONFIG_USB_STORAGE=y
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+
+#
+# USB port drivers
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_BERRY_CHARGE is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_VST is not set
+# CONFIG_USB_GADGET is not set
+
+#
+# OTG and related infrastructure
+#
+# CONFIG_USB_GPIO_VBUS is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# CONFIG_EXT3_FS_SECURITY is not set
+# CONFIG_EXT4_FS is not set
+CONFIG_JBD=y
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_FILE_LOCKING=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+CONFIG_NTFS_FS=y
+# CONFIG_NTFS_DEBUG is not set
+CONFIG_NTFS_RW=y
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS2_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+CONFIG_MINIX_FS=y
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFSD=y
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V3_ACL is not set
+CONFIG_NFSD_V4=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+# CONFIG_SUNRPC_REGISTER_V4 is not set
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+CONFIG_NLS_CODEPAGE_932=y
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_PRINTK_TIME is not set
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_LATENCYTOP is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+
+#
+# Tracers
+#
+# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_SH_STANDARD_BIOS is not set
+# CONFIG_EARLY_SCIF_CONSOLE is not set
+# CONFIG_MORE_COMPILE_OPTIONS is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_FIPS is not set
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_BLKCIPHER2=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=y
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+CONFIG_CRYPTO_HMAC=y
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_HW is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_FIND_LAST_BIT=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/sh/drivers/dma/Kconfig b/arch/sh/drivers/dma/Kconfig
index 0193636..f13a052 100644
--- a/arch/sh/drivers/dma/Kconfig
+++ b/arch/sh/drivers/dma/Kconfig
@@ -9,13 +9,21 @@
select SH_DMA_API
default n
+config SH_DMA_IRQ_MULTI
+ bool
+ depends on SH_DMA
+ default y if CPU_SUBTYPE_SH7750 || CPU_SUBTYPE_SH7751 || \
+ CPU_SUBTYPE_SH7750S || CPU_SUBTYPE_SH7750R || CPU_SUBTYPE_SH7751R || \
+ CPU_SUBTYPE_SH7091 || CPU_SUBTYPE_SH7763 || CPU_SUBTYPE_SH7764 || \
+ CPU_SUBTYPE_SH7780 || CPU_SUBTYPE_SH7785
+
config NR_ONCHIP_DMA_CHANNELS
int
depends on SH_DMA
- default "6" if CPU_SUBTYPE_SH7720 || CPU_SUBTYPE_SH7721
- default "8" if CPU_SUBTYPE_SH7750R || CPU_SUBTYPE_SH7751R
- default "12" if CPU_SUBTYPE_SH7780
- default "4"
+ default "4" if CPU_SUBTYPE_SH7750 || CPU_SUBTYPE_SH7751 || CPU_SUBTYPE_SH7750S
+ default "8" if CPU_SUBTYPE_SH7750R || CPU_SUBTYPE_SH7751R || CPU_SUBTYPE_SH7760
+ default "12" if CPU_SUBTYPE_SH7723 || CPU_SUBTYPE_SH7780 || CPU_SUBTYPE_SH7785
+ default "6"
help
This allows you to specify the number of channels that the on-chip
DMAC supports. This will be 4 for SH7750/SH7751 and 8 for the
@@ -46,4 +54,28 @@
of the SH7760.
Say Y if you want to use Audio/USB DMA on your SH7760 board.
+config PVR2_DMA
+ tristate "PowerVR 2 DMAC support"
+ depends on SH_DREAMCAST && SH_DMA
+ help
+ Selecting this will enable support for the PVR2 DMA controller.
+ As this chains off of the on-chip DMAC, that must also be
+ enabled by default.
+
+ This is primarily used by the pvr2fb framebuffer driver for
+ certain optimizations, but is not necessary for functionality.
+
+ If in doubt, say N.
+
+config G2_DMA
+ tristate "G2 Bus DMA support"
+ depends on SH_DREAMCAST
+ select SH_DMA_API
+ help
+ This enables support for the DMA controller for the Dreamcast's
+ G2 bus. Drivers that want this will generally enable this on
+ their own.
+
+ If in doubt, say N.
+
endmenu
diff --git a/arch/sh/drivers/dma/Makefile b/arch/sh/drivers/dma/Makefile
index ab956ad..c606813 100644
--- a/arch/sh/drivers/dma/Makefile
+++ b/arch/sh/drivers/dma/Makefile
@@ -4,5 +4,6 @@
obj-$(CONFIG_SH_DMA_API) += dma-api.o dma-sysfs.o
obj-$(CONFIG_SH_DMA) += dma-sh.o
-obj-$(CONFIG_SH_DREAMCAST) += dma-pvr2.o dma-g2.o
+obj-$(CONFIG_PVR2_DMA) += dma-pvr2.o
+obj-$(CONFIG_G2_DMA) += dma-g2.o
obj-$(CONFIG_SH_DMABRG) += dmabrg.o
diff --git a/arch/sh/drivers/dma/dma-sh.c b/arch/sh/drivers/dma/dma-sh.c
index 50887a5..37fb5b8 100644
--- a/arch/sh/drivers/dma/dma-sh.c
+++ b/arch/sh/drivers/dma/dma-sh.c
@@ -17,28 +17,16 @@
#include <mach-dreamcast/mach/dma.h>
#include <asm/dma.h>
#include <asm/io.h>
-#include "dma-sh.h"
+#include <asm/dma-sh.h>
-static int dmte_irq_map[] = {
- DMTE0_IRQ,
- DMTE1_IRQ,
- DMTE2_IRQ,
- DMTE3_IRQ,
-#if defined(CONFIG_CPU_SUBTYPE_SH7720) || \
- defined(CONFIG_CPU_SUBTYPE_SH7721) || \
- defined(CONFIG_CPU_SUBTYPE_SH7751R) || \
- defined(CONFIG_CPU_SUBTYPE_SH7760) || \
- defined(CONFIG_CPU_SUBTYPE_SH7709) || \
- defined(CONFIG_CPU_SUBTYPE_SH7780)
- DMTE4_IRQ,
- DMTE5_IRQ,
+#if defined(DMAE1_IRQ)
+#define NR_DMAE 2
+#else
+#define NR_DMAE 1
#endif
-#if defined(CONFIG_CPU_SUBTYPE_SH7751R) || \
- defined(CONFIG_CPU_SUBTYPE_SH7760) || \
- defined(CONFIG_CPU_SUBTYPE_SH7780)
- DMTE6_IRQ,
- DMTE7_IRQ,
-#endif
+
+static const char *dmae_name[] = {
+ "DMAC Address Error0", "DMAC Address Error1"
};
static inline unsigned int get_dmte_irq(unsigned int chan)
@@ -46,7 +34,14 @@
unsigned int irq = 0;
if (chan < ARRAY_SIZE(dmte_irq_map))
irq = dmte_irq_map[chan];
+
+#if defined(CONFIG_SH_DMA_IRQ_MULTI)
+ if (irq > DMTE6_IRQ)
+ return DMTE6_IRQ;
+ return DMTE0_IRQ;
+#else
return irq;
+#endif
}
/*
@@ -59,7 +54,7 @@
*/
static inline unsigned int calc_xmit_shift(struct dma_channel *chan)
{
- u32 chcr = ctrl_inl(CHCR[chan->chan]);
+ u32 chcr = ctrl_inl(dma_base_addr[chan->chan] + CHCR);
return ts_shift[(chcr & CHCR_TS_MASK)>>CHCR_TS_SHIFT];
}
@@ -75,13 +70,13 @@
struct dma_channel *chan = dev_id;
u32 chcr;
- chcr = ctrl_inl(CHCR[chan->chan]);
+ chcr = ctrl_inl(dma_base_addr[chan->chan] + CHCR);
if (!(chcr & CHCR_TE))
return IRQ_NONE;
chcr &= ~(CHCR_IE | CHCR_DE);
- ctrl_outl(chcr, CHCR[chan->chan]);
+ ctrl_outl(chcr, (dma_base_addr[chan->chan] + CHCR));
wake_up(&chan->wait_queue);
@@ -94,7 +89,12 @@
return 0;
return request_irq(get_dmte_irq(chan->chan), dma_tei,
- IRQF_DISABLED, chan->dev_id, chan);
+#if defined(CONFIG_SH_DMA_IRQ_MULTI)
+ IRQF_SHARED,
+#else
+ IRQF_DISABLED,
+#endif
+ chan->dev_id, chan);
}
static void sh_dmac_free_dma(struct dma_channel *chan)
@@ -115,7 +115,7 @@
chan->flags &= ~DMA_TEI_CAPABLE;
}
- ctrl_outl(chcr, CHCR[chan->chan]);
+ ctrl_outl(chcr, (dma_base_addr[chan->chan] + CHCR));
chan->flags |= DMA_CONFIGURED;
return 0;
@@ -126,13 +126,13 @@
int irq;
u32 chcr;
- chcr = ctrl_inl(CHCR[chan->chan]);
+ chcr = ctrl_inl(dma_base_addr[chan->chan] + CHCR);
chcr |= CHCR_DE;
if (chan->flags & DMA_TEI_CAPABLE)
chcr |= CHCR_IE;
- ctrl_outl(chcr, CHCR[chan->chan]);
+ ctrl_outl(chcr, (dma_base_addr[chan->chan] + CHCR));
if (chan->flags & DMA_TEI_CAPABLE) {
irq = get_dmte_irq(chan->chan);
@@ -150,9 +150,9 @@
disable_irq(irq);
}
- chcr = ctrl_inl(CHCR[chan->chan]);
+ chcr = ctrl_inl(dma_base_addr[chan->chan] + CHCR);
chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE);
- ctrl_outl(chcr, CHCR[chan->chan]);
+ ctrl_outl(chcr, (dma_base_addr[chan->chan] + CHCR));
}
static int sh_dmac_xfer_dma(struct dma_channel *chan)
@@ -183,12 +183,13 @@
*/
if (chan->sar || (mach_is_dreamcast() &&
chan->chan == PVR2_CASCADE_CHAN))
- ctrl_outl(chan->sar, SAR[chan->chan]);
+ ctrl_outl(chan->sar, (dma_base_addr[chan->chan]+SAR));
if (chan->dar || (mach_is_dreamcast() &&
chan->chan == PVR2_CASCADE_CHAN))
- ctrl_outl(chan->dar, DAR[chan->chan]);
+ ctrl_outl(chan->dar, (dma_base_addr[chan->chan] + DAR));
- ctrl_outl(chan->count >> calc_xmit_shift(chan), DMATCR[chan->chan]);
+ ctrl_outl(chan->count >> calc_xmit_shift(chan),
+ (dma_base_addr[chan->chan] + TCR));
sh_dmac_enable_dma(chan);
@@ -197,36 +198,26 @@
static int sh_dmac_get_dma_residue(struct dma_channel *chan)
{
- if (!(ctrl_inl(CHCR[chan->chan]) & CHCR_DE))
+ if (!(ctrl_inl(dma_base_addr[chan->chan] + CHCR) & CHCR_DE))
return 0;
- return ctrl_inl(DMATCR[chan->chan]) << calc_xmit_shift(chan);
+ return ctrl_inl(dma_base_addr[chan->chan] + TCR)
+ << calc_xmit_shift(chan);
}
-#if defined(CONFIG_CPU_SUBTYPE_SH7720) || \
- defined(CONFIG_CPU_SUBTYPE_SH7721) || \
- defined(CONFIG_CPU_SUBTYPE_SH7780) || \
- defined(CONFIG_CPU_SUBTYPE_SH7709)
-#define dmaor_read_reg() ctrl_inw(DMAOR)
-#define dmaor_write_reg(data) ctrl_outw(data, DMAOR)
-#else
-#define dmaor_read_reg() ctrl_inl(DMAOR)
-#define dmaor_write_reg(data) ctrl_outl(data, DMAOR)
-#endif
-
-static inline int dmaor_reset(void)
+static inline int dmaor_reset(int no)
{
- unsigned long dmaor = dmaor_read_reg();
+ unsigned long dmaor = dmaor_read_reg(no);
/* Try to clear the error flags first, incase they are set */
dmaor &= ~(DMAOR_NMIF | DMAOR_AE);
- dmaor_write_reg(dmaor);
+ dmaor_write_reg(no, dmaor);
dmaor |= DMAOR_INIT;
- dmaor_write_reg(dmaor);
+ dmaor_write_reg(no, dmaor);
/* See if we got an error again */
- if ((dmaor_read_reg() & (DMAOR_AE | DMAOR_NMIF))) {
+ if ((dmaor_read_reg(no) & (DMAOR_AE | DMAOR_NMIF))) {
printk(KERN_ERR "dma-sh: Can't initialize DMAOR.\n");
return -EINVAL;
}
@@ -237,10 +228,33 @@
#if defined(CONFIG_CPU_SH4)
static irqreturn_t dma_err(int irq, void *dummy)
{
- dmaor_reset();
+#if defined(CONFIG_SH_DMA_IRQ_MULTI)
+ int cnt = 0;
+ switch (irq) {
+#if defined(DMTE6_IRQ) && defined(DMAE1_IRQ)
+ case DMTE6_IRQ:
+ cnt++;
+#endif
+ case DMTE0_IRQ:
+ if (dmaor_read_reg(cnt) & (DMAOR_NMIF | DMAOR_AE)) {
+ disable_irq(irq);
+ /* DMA multi and error IRQ */
+ return IRQ_HANDLED;
+ }
+ default:
+ return IRQ_NONE;
+ }
+#else
+ dmaor_reset(0);
+#if defined(CONFIG_CPU_SUBTYPE_SH7723) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7780) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7785)
+ dmaor_reset(1);
+#endif
disable_irq(irq);
return IRQ_HANDLED;
+#endif
}
#endif
@@ -259,24 +273,59 @@
.flags = DMAC_CHANNELS_TEI_CAPABLE,
};
+#ifdef CONFIG_CPU_SH4
+static unsigned int get_dma_error_irq(int n)
+{
+#if defined(CONFIG_SH_DMA_IRQ_MULTI)
+ return (n == 0) ? get_dmte_irq(0) : get_dmte_irq(6);
+#else
+ return (n == 0) ? DMAE0_IRQ :
+#if defined(DMAE1_IRQ)
+ DMAE1_IRQ;
+#else
+ -1;
+#endif
+#endif
+}
+#endif
+
static int __init sh_dmac_init(void)
{
struct dma_info *info = &sh_dmac_info;
int i;
#ifdef CONFIG_CPU_SH4
- i = request_irq(DMAE_IRQ, dma_err, IRQF_DISABLED, "DMAC Address Error", 0);
- if (unlikely(i < 0))
- return i;
+ int n;
+
+ for (n = 0; n < NR_DMAE; n++) {
+ i = request_irq(get_dma_error_irq(n), dma_err,
+#if defined(CONFIG_SH_DMA_IRQ_MULTI)
+ IRQF_SHARED,
+#else
+ IRQF_DISABLED,
#endif
+ dmae_name[n], (void *)dmae_name[n]);
+ if (unlikely(i < 0)) {
+ printk(KERN_ERR "%s request_irq fail\n", dmae_name[n]);
+ return i;
+ }
+ }
+#endif /* CONFIG_CPU_SH4 */
/*
* Initialize DMAOR, and clean up any error flags that may have
* been set.
*/
- i = dmaor_reset();
+ i = dmaor_reset(0);
if (unlikely(i != 0))
return i;
+#if defined(CONFIG_CPU_SUBTYPE_SH7723) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7780) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7785)
+ i = dmaor_reset(1);
+ if (unlikely(i != 0))
+ return i;
+#endif
return register_dmac(info);
}
@@ -284,8 +333,12 @@
static void __exit sh_dmac_exit(void)
{
#ifdef CONFIG_CPU_SH4
- free_irq(DMAE_IRQ, 0);
-#endif
+ int n;
+
+ for (n = 0; n < NR_DMAE; n++) {
+ free_irq(get_dma_error_irq(n), (void *)dmae_name[n]);
+ }
+#endif /* CONFIG_CPU_SH4 */
unregister_dmac(&sh_dmac_info);
}
diff --git a/arch/sh/drivers/dma/dma-sh.h b/arch/sh/drivers/dma/dma-sh.h
deleted file mode 100644
index 05fecd5..0000000
--- a/arch/sh/drivers/dma/dma-sh.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * arch/sh/drivers/dma/dma-sh.h
- *
- * Copyright (C) 2000 Takashi YOSHII
- * Copyright (C) 2003 Paul Mundt
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#ifndef __DMA_SH_H
-#define __DMA_SH_H
-
-#include <cpu/dma.h>
-
-/* Definitions for the SuperH DMAC */
-#define REQ_L 0x00000000
-#define REQ_E 0x00080000
-#define RACK_H 0x00000000
-#define RACK_L 0x00040000
-#define ACK_R 0x00000000
-#define ACK_W 0x00020000
-#define ACK_H 0x00000000
-#define ACK_L 0x00010000
-#define DM_INC 0x00004000
-#define DM_DEC 0x00008000
-#define SM_INC 0x00001000
-#define SM_DEC 0x00002000
-#define RS_IN 0x00000200
-#define RS_OUT 0x00000300
-#define TS_BLK 0x00000040
-#define TM_BUR 0x00000020
-#define CHCR_DE 0x00000001
-#define CHCR_TE 0x00000002
-#define CHCR_IE 0x00000004
-
-/* DMAOR definitions */
-#define DMAOR_AE 0x00000004
-#define DMAOR_NMIF 0x00000002
-#define DMAOR_DME 0x00000001
-
-/*
- * Define the default configuration for dual address memory-memory transfer.
- * The 0x400 value represents auto-request, external->external.
- */
-#define RS_DUAL (DM_INC | SM_INC | 0x400 | TS_32)
-
-#define MAX_DMAC_CHANNELS (CONFIG_NR_ONCHIP_DMA_CHANNELS)
-
-/*
- * Subtypes that have fewer channels than this simply need to change
- * CONFIG_NR_ONCHIP_DMA_CHANNELS. Likewise, subtypes with a larger number
- * of channels should expand on this.
- *
- * For most subtypes we can easily figure these values out with some
- * basic calculation, unfortunately on other subtypes these are more
- * scattered, so we just leave it unrolled for simplicity.
- */
-#define SAR ((unsigned long[]){SH_DMAC_BASE + 0x00, SH_DMAC_BASE + 0x10, \
- SH_DMAC_BASE + 0x20, SH_DMAC_BASE + 0x30, \
- SH_DMAC_BASE + 0x50, SH_DMAC_BASE + 0x60})
-#define DAR ((unsigned long[]){SH_DMAC_BASE + 0x04, SH_DMAC_BASE + 0x14, \
- SH_DMAC_BASE + 0x24, SH_DMAC_BASE + 0x34, \
- SH_DMAC_BASE + 0x54, SH_DMAC_BASE + 0x64})
-#define DMATCR ((unsigned long[]){SH_DMAC_BASE + 0x08, SH_DMAC_BASE + 0x18, \
- SH_DMAC_BASE + 0x28, SH_DMAC_BASE + 0x38, \
- SH_DMAC_BASE + 0x58, SH_DMAC_BASE + 0x68})
-#define CHCR ((unsigned long[]){SH_DMAC_BASE + 0x0c, SH_DMAC_BASE + 0x1c, \
- SH_DMAC_BASE + 0x2c, SH_DMAC_BASE + 0x3c, \
- SH_DMAC_BASE + 0x5c, SH_DMAC_BASE + 0x6c})
-
-#define DMAOR (SH_DMAC_BASE + 0x40)
-
-#endif /* __DMA_SH_H */
-
diff --git a/arch/sh/drivers/pci/pci-sh7780.c b/arch/sh/drivers/pci/pci-sh7780.c
index 078dc44..773d575 100644
--- a/arch/sh/drivers/pci/pci-sh7780.c
+++ b/arch/sh/drivers/pci/pci-sh7780.c
@@ -127,8 +127,8 @@
pci_write_reg(word, SH4_PCILSR0);
pci_write_reg(0x00000001, SH4_PCILSR1);
/* Set the values on window 0 PCI config registers */
- word = (CONFIG_MEMORY_SIZE > 0x08000000) ? 0x10000000 : 0x08000000;
- pci_write_reg(word | 0xa0000000, SH4_PCILAR0);
+ word = CONFIG_MEMORY_START | (CONFIG_MEMORY_SIZE - 0x01000000);
+ pci_write_reg(word, SH4_PCILAR0);
pci_write_reg(word, SH7780_PCIMBAR0);
/* Set the values on window 1 PCI config registers */
pci_write_reg(0x00000000, SH4_PCILAR1);
diff --git a/arch/sh/include/asm/addrspace.h b/arch/sh/include/asm/addrspace.h
index 36736c7..80d4081 100644
--- a/arch/sh/include/asm/addrspace.h
+++ b/arch/sh/include/asm/addrspace.h
@@ -31,7 +31,7 @@
/* Returns the physical address of a PnSEG (n=1,2) address */
#define PHYSADDR(a) (((unsigned long)(a)) & 0x1fffffff)
-#ifdef CONFIG_29BIT
+#if defined(CONFIG_29BIT) || defined(CONFIG_PMB_FIXED)
/*
* Map an address to a certain privileged segment
*/
@@ -43,7 +43,7 @@
((__typeof__(a))(((unsigned long)(a) & 0x1fffffff) | P3SEG))
#define P4SEGADDR(a) \
((__typeof__(a))(((unsigned long)(a) & 0x1fffffff) | P4SEG))
-#endif /* 29BIT */
+#endif /* 29BIT || PMB_FIXED */
#endif /* P1SEG */
/* Check if an address can be reached in 29 bits */
diff --git a/arch/sh/include/asm/atomic-irq.h b/arch/sh/include/asm/atomic-irq.h
index 74f7943..a0b3480 100644
--- a/arch/sh/include/asm/atomic-irq.h
+++ b/arch/sh/include/asm/atomic-irq.h
@@ -11,7 +11,7 @@
unsigned long flags;
local_irq_save(flags);
- *(long *)v += i;
+ v->counter += i;
local_irq_restore(flags);
}
@@ -20,7 +20,7 @@
unsigned long flags;
local_irq_save(flags);
- *(long *)v -= i;
+ v->counter -= i;
local_irq_restore(flags);
}
@@ -29,9 +29,9 @@
unsigned long temp, flags;
local_irq_save(flags);
- temp = *(long *)v;
+ temp = v->counter;
temp += i;
- *(long *)v = temp;
+ v->counter = temp;
local_irq_restore(flags);
return temp;
@@ -42,9 +42,9 @@
unsigned long temp, flags;
local_irq_save(flags);
- temp = *(long *)v;
+ temp = v->counter;
temp -= i;
- *(long *)v = temp;
+ v->counter = temp;
local_irq_restore(flags);
return temp;
@@ -55,7 +55,7 @@
unsigned long flags;
local_irq_save(flags);
- *(long *)v &= ~mask;
+ v->counter &= ~mask;
local_irq_restore(flags);
}
@@ -64,7 +64,7 @@
unsigned long flags;
local_irq_save(flags);
- *(long *)v |= mask;
+ v->counter |= mask;
local_irq_restore(flags);
}
diff --git a/arch/sh/include/asm/bitops-llsc.h b/arch/sh/include/asm/bitops-llsc.h
index 1d2fc0b..d8328be 100644
--- a/arch/sh/include/asm/bitops-llsc.h
+++ b/arch/sh/include/asm/bitops-llsc.h
@@ -1,7 +1,7 @@
#ifndef __ASM_SH_BITOPS_LLSC_H
#define __ASM_SH_BITOPS_LLSC_H
-static inline void set_bit(int nr, volatile void * addr)
+static inline void set_bit(int nr, volatile void *addr)
{
int mask;
volatile unsigned int *a = addr;
@@ -13,16 +13,16 @@
__asm__ __volatile__ (
"1: \n\t"
"movli.l @%1, %0 ! set_bit \n\t"
- "or %3, %0 \n\t"
+ "or %2, %0 \n\t"
"movco.l %0, @%1 \n\t"
"bf 1b \n\t"
- : "=&z" (tmp), "=r" (a)
- : "1" (a), "r" (mask)
+ : "=&z" (tmp)
+ : "r" (a), "r" (mask)
: "t", "memory"
);
}
-static inline void clear_bit(int nr, volatile void * addr)
+static inline void clear_bit(int nr, volatile void *addr)
{
int mask;
volatile unsigned int *a = addr;
@@ -34,16 +34,16 @@
__asm__ __volatile__ (
"1: \n\t"
"movli.l @%1, %0 ! clear_bit \n\t"
- "and %3, %0 \n\t"
+ "and %2, %0 \n\t"
"movco.l %0, @%1 \n\t"
"bf 1b \n\t"
- : "=&z" (tmp), "=r" (a)
- : "1" (a), "r" (~mask)
+ : "=&z" (tmp)
+ : "r" (a), "r" (~mask)
: "t", "memory"
);
}
-static inline void change_bit(int nr, volatile void * addr)
+static inline void change_bit(int nr, volatile void *addr)
{
int mask;
volatile unsigned int *a = addr;
@@ -55,86 +55,86 @@
__asm__ __volatile__ (
"1: \n\t"
"movli.l @%1, %0 ! change_bit \n\t"
+ "xor %2, %0 \n\t"
+ "movco.l %0, @%1 \n\t"
+ "bf 1b \n\t"
+ : "=&z" (tmp)
+ : "r" (a), "r" (mask)
+ : "t", "memory"
+ );
+}
+
+static inline int test_and_set_bit(int nr, volatile void *addr)
+{
+ int mask, retval;
+ volatile unsigned int *a = addr;
+ unsigned long tmp;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+
+ __asm__ __volatile__ (
+ "1: \n\t"
+ "movli.l @%2, %0 ! test_and_set_bit \n\t"
+ "mov %0, %1 \n\t"
+ "or %3, %0 \n\t"
+ "movco.l %0, @%2 \n\t"
+ "bf 1b \n\t"
+ "and %3, %1 \n\t"
+ : "=&z" (tmp), "=&r" (retval)
+ : "r" (a), "r" (mask)
+ : "t", "memory"
+ );
+
+ return retval != 0;
+}
+
+static inline int test_and_clear_bit(int nr, volatile void *addr)
+{
+ int mask, retval;
+ volatile unsigned int *a = addr;
+ unsigned long tmp;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+
+ __asm__ __volatile__ (
+ "1: \n\t"
+ "movli.l @%2, %0 ! test_and_clear_bit \n\t"
+ "mov %0, %1 \n\t"
+ "and %4, %0 \n\t"
+ "movco.l %0, @%2 \n\t"
+ "bf 1b \n\t"
+ "and %3, %1 \n\t"
+ "synco \n\t"
+ : "=&z" (tmp), "=&r" (retval)
+ : "r" (a), "r" (mask), "r" (~mask)
+ : "t", "memory"
+ );
+
+ return retval != 0;
+}
+
+static inline int test_and_change_bit(int nr, volatile void *addr)
+{
+ int mask, retval;
+ volatile unsigned int *a = addr;
+ unsigned long tmp;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+
+ __asm__ __volatile__ (
+ "1: \n\t"
+ "movli.l @%2, %0 ! test_and_change_bit \n\t"
+ "mov %0, %1 \n\t"
"xor %3, %0 \n\t"
- "movco.l %0, @%1 \n\t"
+ "movco.l %0, @%2 \n\t"
"bf 1b \n\t"
- : "=&z" (tmp), "=r" (a)
- : "1" (a), "r" (mask)
- : "t", "memory"
- );
-}
-
-static inline int test_and_set_bit(int nr, volatile void * addr)
-{
- int mask, retval;
- volatile unsigned int *a = addr;
- unsigned long tmp;
-
- a += nr >> 5;
- mask = 1 << (nr & 0x1f);
-
- __asm__ __volatile__ (
- "1: \n\t"
- "movli.l @%1, %0 ! test_and_set_bit \n\t"
- "mov %0, %2 \n\t"
- "or %4, %0 \n\t"
- "movco.l %0, @%1 \n\t"
- "bf 1b \n\t"
- "and %4, %2 \n\t"
- : "=&z" (tmp), "=r" (a), "=&r" (retval)
- : "1" (a), "r" (mask)
- : "t", "memory"
- );
-
- return retval != 0;
-}
-
-static inline int test_and_clear_bit(int nr, volatile void * addr)
-{
- int mask, retval;
- volatile unsigned int *a = addr;
- unsigned long tmp;
-
- a += nr >> 5;
- mask = 1 << (nr & 0x1f);
-
- __asm__ __volatile__ (
- "1: \n\t"
- "movli.l @%1, %0 ! test_and_clear_bit \n\t"
- "mov %0, %2 \n\t"
- "and %5, %0 \n\t"
- "movco.l %0, @%1 \n\t"
- "bf 1b \n\t"
- "and %4, %2 \n\t"
+ "and %3, %1 \n\t"
"synco \n\t"
- : "=&z" (tmp), "=r" (a), "=&r" (retval)
- : "1" (a), "r" (mask), "r" (~mask)
- : "t", "memory"
- );
-
- return retval != 0;
-}
-
-static inline int test_and_change_bit(int nr, volatile void * addr)
-{
- int mask, retval;
- volatile unsigned int *a = addr;
- unsigned long tmp;
-
- a += nr >> 5;
- mask = 1 << (nr & 0x1f);
-
- __asm__ __volatile__ (
- "1: \n\t"
- "movli.l @%1, %0 ! test_and_change_bit \n\t"
- "mov %0, %2 \n\t"
- "xor %4, %0 \n\t"
- "movco.l %0, @%1 \n\t"
- "bf 1b \n\t"
- "and %4, %2 \n\t"
- "synco \n\t"
- : "=&z" (tmp), "=r" (a), "=&r" (retval)
- : "1" (a), "r" (mask)
+ : "=&z" (tmp), "=&r" (retval)
+ : "r" (a), "r" (mask)
: "t", "memory"
);
diff --git a/arch/sh/include/asm/clock.h b/arch/sh/include/asm/clock.h
index f9c8858..2f6c962 100644
--- a/arch/sh/include/asm/clock.h
+++ b/arch/sh/include/asm/clock.h
@@ -15,6 +15,7 @@
void (*disable)(struct clk *clk);
void (*recalc)(struct clk *clk);
int (*set_rate)(struct clk *clk, unsigned long rate, int algo_id);
+ int (*set_parent)(struct clk *clk, struct clk *parent);
long (*round_rate)(struct clk *clk, unsigned long rate);
};
diff --git a/arch/sh/include/asm/cmpxchg-llsc.h b/arch/sh/include/asm/cmpxchg-llsc.h
index aee3bf2..0fac3da 100644
--- a/arch/sh/include/asm/cmpxchg-llsc.h
+++ b/arch/sh/include/asm/cmpxchg-llsc.h
@@ -8,14 +8,14 @@
__asm__ __volatile__ (
"1: \n\t"
- "movli.l @%1, %0 ! xchg_u32 \n\t"
- "mov %0, %2 \n\t"
- "mov %4, %0 \n\t"
- "movco.l %0, @%1 \n\t"
+ "movli.l @%2, %0 ! xchg_u32 \n\t"
+ "mov %0, %1 \n\t"
+ "mov %3, %0 \n\t"
+ "movco.l %0, @%2 \n\t"
"bf 1b \n\t"
"synco \n\t"
- : "=&z"(tmp), "=r" (m), "=&r" (retval)
- : "1" (m), "r" (val)
+ : "=&z"(tmp), "=&r" (retval)
+ : "r" (m), "r" (val)
: "t", "memory"
);
@@ -29,14 +29,14 @@
__asm__ __volatile__ (
"1: \n\t"
- "movli.l @%1, %0 ! xchg_u8 \n\t"
- "mov %0, %2 \n\t"
- "mov %4, %0 \n\t"
- "movco.l %0, @%1 \n\t"
+ "movli.l @%2, %0 ! xchg_u8 \n\t"
+ "mov %0, %1 \n\t"
+ "mov %3, %0 \n\t"
+ "movco.l %0, @%2 \n\t"
"bf 1b \n\t"
"synco \n\t"
- : "=&z"(tmp), "=r" (m), "=&r" (retval)
- : "1" (m), "r" (val & 0xff)
+ : "=&z"(tmp), "=&r" (retval)
+ : "r" (m), "r" (val & 0xff)
: "t", "memory"
);
@@ -51,17 +51,17 @@
__asm__ __volatile__ (
"1: \n\t"
- "movli.l @%1, %0 ! __cmpxchg_u32 \n\t"
- "mov %0, %2 \n\t"
- "cmp/eq %2, %4 \n\t"
+ "movli.l @%2, %0 ! __cmpxchg_u32 \n\t"
+ "mov %0, %1 \n\t"
+ "cmp/eq %1, %3 \n\t"
"bf 2f \n\t"
- "mov %5, %0 \n\t"
+ "mov %3, %0 \n\t"
"2: \n\t"
- "movco.l %0, @%1 \n\t"
+ "movco.l %0, @%2 \n\t"
"bf 1b \n\t"
"synco \n\t"
- : "=&z" (tmp), "=r" (m), "=&r" (retval)
- : "1" (m), "r" (old), "r" (new)
+ : "=&z" (tmp), "=&r" (retval)
+ : "r" (m), "r" (old), "r" (new)
: "t", "memory"
);
diff --git a/arch/sh/include/asm/cpu-features.h b/arch/sh/include/asm/cpu-features.h
index 86308aa..694abe4 100644
--- a/arch/sh/include/asm/cpu-features.h
+++ b/arch/sh/include/asm/cpu-features.h
@@ -21,5 +21,6 @@
#define CPU_HAS_LLSC 0x0040 /* movli.l/movco.l */
#define CPU_HAS_L2_CACHE 0x0080 /* Secondary cache / URAM */
#define CPU_HAS_OP32 0x0100 /* 32-bit instruction support */
+#define CPU_HAS_PTEAEX 0x0200 /* PTE ASID Extension support */
#endif /* __ASM_SH_CPU_FEATURES_H */
diff --git a/arch/sh/include/asm/dma-sh.h b/arch/sh/include/asm/dma-sh.h
new file mode 100644
index 0000000..0c8f8e1
--- /dev/null
+++ b/arch/sh/include/asm/dma-sh.h
@@ -0,0 +1,118 @@
+/*
+ * arch/sh/include/asm/dma-sh.h
+ *
+ * Copyright (C) 2000 Takashi YOSHII
+ * Copyright (C) 2003 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#ifndef __DMA_SH_H
+#define __DMA_SH_H
+
+#include <asm/dma.h>
+#include <cpu/dma.h>
+
+/* DMAOR contorl: The DMAOR access size is different by CPU.*/
+#if defined(CONFIG_CPU_SUBTYPE_SH7723) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7780) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7785)
+#define dmaor_read_reg(n) \
+ (n ? ctrl_inw(SH_DMAC_BASE1 + DMAOR) \
+ : ctrl_inw(SH_DMAC_BASE0 + DMAOR))
+#define dmaor_write_reg(n, data) \
+ (n ? ctrl_outw(data, SH_DMAC_BASE1 + DMAOR) \
+ : ctrl_outw(data, SH_DMAC_BASE0 + DMAOR))
+#else /* Other CPU */
+#define dmaor_read_reg(n) ctrl_inw(SH_DMAC_BASE0 + DMAOR)
+#define dmaor_write_reg(n, data) ctrl_outw(data, SH_DMAC_BASE0 + DMAOR)
+#endif
+
+static int dmte_irq_map[] __maybe_unused = {
+#if (MAX_DMA_CHANNELS >= 4)
+ DMTE0_IRQ,
+ DMTE0_IRQ + 1,
+ DMTE0_IRQ + 2,
+ DMTE0_IRQ + 3,
+#endif
+#if (MAX_DMA_CHANNELS >= 6)
+ DMTE4_IRQ,
+ DMTE4_IRQ + 1,
+#endif
+#if (MAX_DMA_CHANNELS >= 8)
+ DMTE6_IRQ,
+ DMTE6_IRQ + 1,
+#endif
+#if (MAX_DMA_CHANNELS >= 12)
+ DMTE8_IRQ,
+ DMTE9_IRQ,
+ DMTE10_IRQ,
+ DMTE11_IRQ,
+#endif
+};
+
+/* Definitions for the SuperH DMAC */
+#define REQ_L 0x00000000
+#define REQ_E 0x00080000
+#define RACK_H 0x00000000
+#define RACK_L 0x00040000
+#define ACK_R 0x00000000
+#define ACK_W 0x00020000
+#define ACK_H 0x00000000
+#define ACK_L 0x00010000
+#define DM_INC 0x00004000
+#define DM_DEC 0x00008000
+#define SM_INC 0x00001000
+#define SM_DEC 0x00002000
+#define RS_IN 0x00000200
+#define RS_OUT 0x00000300
+#define TS_BLK 0x00000040
+#define TM_BUR 0x00000020
+#define CHCR_DE 0x00000001
+#define CHCR_TE 0x00000002
+#define CHCR_IE 0x00000004
+
+/* DMAOR definitions */
+#define DMAOR_AE 0x00000004
+#define DMAOR_NMIF 0x00000002
+#define DMAOR_DME 0x00000001
+
+/*
+ * Define the default configuration for dual address memory-memory transfer.
+ * The 0x400 value represents auto-request, external->external.
+ */
+#define RS_DUAL (DM_INC | SM_INC | 0x400 | TS_32)
+
+/* DMA base address */
+static u32 dma_base_addr[] __maybe_unused = {
+#if (MAX_DMA_CHANNELS >= 4)
+ SH_DMAC_BASE0 + 0x00, /* channel 0 */
+ SH_DMAC_BASE0 + 0x10,
+ SH_DMAC_BASE0 + 0x20,
+ SH_DMAC_BASE0 + 0x30,
+#endif
+#if (MAX_DMA_CHANNELS >= 6)
+ SH_DMAC_BASE0 + 0x50,
+ SH_DMAC_BASE0 + 0x60,
+#endif
+#if (MAX_DMA_CHANNELS >= 8)
+ SH_DMAC_BASE1 + 0x00,
+ SH_DMAC_BASE1 + 0x10,
+#endif
+#if (MAX_DMA_CHANNELS >= 12)
+ SH_DMAC_BASE1 + 0x20,
+ SH_DMAC_BASE1 + 0x30,
+ SH_DMAC_BASE1 + 0x50,
+ SH_DMAC_BASE1 + 0x60, /* channel 11 */
+#endif
+};
+
+/* DMA register */
+#define SAR 0x00
+#define DAR 0x04
+#define TCR 0x08
+#define CHCR 0x0C
+#define DMAOR 0x40
+
+#endif /* __DMA_SH_H */
diff --git a/arch/sh/include/asm/dma.h b/arch/sh/include/asm/dma.h
index beca712..6bd1784 100644
--- a/arch/sh/include/asm/dma.h
+++ b/arch/sh/include/asm/dma.h
@@ -25,9 +25,9 @@
#define MAX_DMA_ADDRESS (PAGE_OFFSET+0x10000000)
#ifdef CONFIG_NR_DMA_CHANNELS
-# define MAX_DMA_CHANNELS (CONFIG_NR_DMA_CHANNELS)
+# define MAX_DMA_CHANNELS (CONFIG_NR_DMA_CHANNELS)
#else
-# define MAX_DMA_CHANNELS (CONFIG_NR_ONCHIP_DMA_CHANNELS)
+# define MAX_DMA_CHANNELS (CONFIG_NR_ONCHIP_DMA_CHANNELS)
#endif
/*
diff --git a/arch/sh/include/asm/entry-macros.S b/arch/sh/include/asm/entry-macros.S
index 2dab0b8..3a4752a 100644
--- a/arch/sh/include/asm/entry-macros.S
+++ b/arch/sh/include/asm/entry-macros.S
@@ -31,3 +31,8 @@
#endif
.endm
+#if defined(CONFIG_CPU_SH2A) || defined(CONFIG_CPU_SH4)
+# define PREF(x) pref @x
+#else
+# define PREF(x) nop
+#endif
diff --git a/arch/sh/include/asm/gpio.h b/arch/sh/include/asm/gpio.h
index 9067365..61f93da 100644
--- a/arch/sh/include/asm/gpio.h
+++ b/arch/sh/include/asm/gpio.h
@@ -19,8 +19,42 @@
#include <cpu/gpio.h>
#endif
+#define ARCH_NR_GPIOS 512
+#include <asm-generic/gpio.h>
+
+#ifdef CONFIG_GPIOLIB
+
+static inline int gpio_get_value(unsigned gpio)
+{
+ return __gpio_get_value(gpio);
+}
+
+static inline void gpio_set_value(unsigned gpio, int value)
+{
+ __gpio_set_value(gpio, value);
+}
+
+static inline int gpio_cansleep(unsigned gpio)
+{
+ return __gpio_cansleep(gpio);
+}
+
+static inline int gpio_to_irq(unsigned gpio)
+{
+ WARN_ON(1);
+ return -ENOSYS;
+}
+
+static inline int irq_to_gpio(unsigned int irq)
+{
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+#endif /* CONFIG_GPIOLIB */
+
typedef unsigned short pinmux_enum_t;
-typedef unsigned char pinmux_flag_t;
+typedef unsigned short pinmux_flag_t;
#define PINMUX_TYPE_NONE 0
#define PINMUX_TYPE_FUNCTION 1
@@ -34,6 +68,11 @@
#define PINMUX_FLAG_WANT_PULLUP (1 << 3)
#define PINMUX_FLAG_WANT_PULLDOWN (1 << 4)
+#define PINMUX_FLAG_DBIT_SHIFT 5
+#define PINMUX_FLAG_DBIT (0x1f << PINMUX_FLAG_DBIT_SHIFT)
+#define PINMUX_FLAG_DREG_SHIFT 10
+#define PINMUX_FLAG_DREG (0x3f << PINMUX_FLAG_DREG_SHIFT)
+
struct pinmux_gpio {
pinmux_enum_t enum_id;
pinmux_flag_t flags;
@@ -54,7 +93,7 @@
.enum_ids = (pinmux_enum_t [(r_width / f_width) * (1 << f_width)]) \
struct pinmux_data_reg {
- unsigned long reg, reg_width;
+ unsigned long reg, reg_width, reg_shadow;
pinmux_enum_t *enum_ids;
};
@@ -89,34 +128,9 @@
unsigned int gpio_data_size;
unsigned long *gpio_in_use;
+ struct gpio_chip chip;
};
int register_pinmux(struct pinmux_info *pip);
-int __gpio_request(unsigned gpio);
-static inline int gpio_request(unsigned gpio, const char *label)
-{
- return __gpio_request(gpio);
-}
-void gpio_free(unsigned gpio);
-int gpio_direction_input(unsigned gpio);
-int gpio_direction_output(unsigned gpio, int value);
-int gpio_get_value(unsigned gpio);
-void gpio_set_value(unsigned gpio, int value);
-
-/* IRQ modes are unspported */
-static inline int gpio_to_irq(unsigned gpio)
-{
- WARN_ON(1);
- return -EINVAL;
-}
-
-static inline int irq_to_gpio(unsigned irq)
-{
- WARN_ON(1);
- return -EINVAL;
-}
-
-#include <asm-generic/gpio.h>
-
#endif /* __ASM_SH_GPIO_H */
diff --git a/arch/sh/include/asm/hd64461.h b/arch/sh/include/asm/hd64461.h
index 8c1353b..52b4b62 100644
--- a/arch/sh/include/asm/hd64461.h
+++ b/arch/sh/include/asm/hd64461.h
@@ -242,7 +242,6 @@
#include <asm/io_generic.h>
/* arch/sh/cchips/hd6446x/hd64461/setup.c */
-int hd64461_irq_demux(int irq);
void hd64461_register_irq_demux(int irq,
int (*demux) (int irq, void *dev), void *dev);
void hd64461_unregister_irq_demux(int irq);
diff --git a/arch/sh/include/asm/io.h b/arch/sh/include/asm/io.h
index 61f6dae..0454f8d 100644
--- a/arch/sh/include/asm/io.h
+++ b/arch/sh/include/asm/io.h
@@ -238,7 +238,7 @@
static inline void __iomem *
__ioremap_mode(unsigned long offset, unsigned long size, unsigned long flags)
{
-#ifdef CONFIG_SUPERH32
+#if defined(CONFIG_SUPERH32) && !defined(CONFIG_PMB_FIXED)
unsigned long last_addr = offset + size - 1;
#endif
void __iomem *ret;
@@ -247,7 +247,7 @@
if (ret)
return ret;
-#ifdef CONFIG_SUPERH32
+#if defined(CONFIG_SUPERH32) && !defined(CONFIG_PMB_FIXED)
/*
* For P1 and P2 space this is trivial, as everything is already
* mapped. Uncached access for P1 addresses are done through P2.
diff --git a/arch/sh/include/asm/kprobes.h b/arch/sh/include/asm/kprobes.h
index 6078d8e..613644a 100644
--- a/arch/sh/include/asm/kprobes.h
+++ b/arch/sh/include/asm/kprobes.h
@@ -16,7 +16,7 @@
? (MAX_STACK_SIZE) \
: (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR)))
-#define regs_return_value(regs) ((regs)->regs[0])
+#define regs_return_value(_regs) ((_regs)->regs[0])
#define flush_insn_slot(p) do { } while (0)
#define kretprobe_blacklist_size 0
diff --git a/arch/sh/include/asm/mmu_context.h b/arch/sh/include/asm/mmu_context.h
index 5d9157b..2a9c55f 100644
--- a/arch/sh/include/asm/mmu_context.h
+++ b/arch/sh/include/asm/mmu_context.h
@@ -19,13 +19,18 @@
* (a) TLB cache version (or round, cycle whatever expression you like)
* (b) ASID (Address Space IDentifier)
*/
+#ifdef CONFIG_CPU_HAS_PTEAEX
+#define MMU_CONTEXT_ASID_MASK 0x0000ffff
+#else
#define MMU_CONTEXT_ASID_MASK 0x000000ff
-#define MMU_CONTEXT_VERSION_MASK 0xffffff00
-#define MMU_CONTEXT_FIRST_VERSION 0x00000100
-#define NO_CONTEXT 0UL
+#endif
-/* ASID is 8-bit value, so it can't be 0x100 */
-#define MMU_NO_ASID 0x100
+#define MMU_CONTEXT_VERSION_MASK (~0UL & ~MMU_CONTEXT_ASID_MASK)
+#define MMU_CONTEXT_FIRST_VERSION (MMU_CONTEXT_ASID_MASK + 1)
+
+/* Impossible ASID value, to differentiate from NO_CONTEXT. */
+#define MMU_NO_ASID MMU_CONTEXT_FIRST_VERSION
+#define NO_CONTEXT 0UL
#define asid_cache(cpu) (cpu_data[cpu].asid_cache)
diff --git a/arch/sh/include/asm/mmu_context_32.h b/arch/sh/include/asm/mmu_context_32.h
index f4f9aeb..8ef800c 100644
--- a/arch/sh/include/asm/mmu_context_32.h
+++ b/arch/sh/include/asm/mmu_context_32.h
@@ -10,6 +10,17 @@
/* Do nothing */
}
+#ifdef CONFIG_CPU_HAS_PTEAEX
+static inline void set_asid(unsigned long asid)
+{
+ __raw_writel(asid, MMU_PTEAEX);
+}
+
+static inline unsigned long get_asid(void)
+{
+ return __raw_readl(MMU_PTEAEX) & MMU_CONTEXT_ASID_MASK;
+}
+#else
static inline void set_asid(unsigned long asid)
{
unsigned long __dummy;
@@ -33,6 +44,7 @@
asid &= MMU_CONTEXT_ASID_MASK;
return asid;
}
+#endif /* CONFIG_CPU_HAS_PTEAEX */
/* MMU_TTB is used for optimizing the fault handling. */
static inline void set_TTB(pgd_t *pgd)
diff --git a/arch/sh/include/asm/page.h b/arch/sh/include/asm/page.h
index 5871d78..9c6d21e 100644
--- a/arch/sh/include/asm/page.h
+++ b/arch/sh/include/asm/page.h
@@ -129,7 +129,12 @@
* is not visible (it is part of the PMB mapping) and so needs to be
* added or subtracted as required.
*/
-#ifdef CONFIG_32BIT
+#if defined(CONFIG_PMB_FIXED)
+/* phys = virt - PAGE_OFFSET - (__MEMORY_START & 0xe0000000) */
+#define PMB_OFFSET (PAGE_OFFSET - PXSEG(__MEMORY_START))
+#define __pa(x) ((unsigned long)(x) - PMB_OFFSET)
+#define __va(x) ((void *)((unsigned long)(x) + PMB_OFFSET))
+#elif defined(CONFIG_32BIT)
#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET+__MEMORY_START)
#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET-__MEMORY_START))
#else
diff --git a/arch/sh/include/asm/processor.h b/arch/sh/include/asm/processor.h
index 1ef4b24..1fd58b4 100644
--- a/arch/sh/include/asm/processor.h
+++ b/arch/sh/include/asm/processor.h
@@ -31,7 +31,7 @@
CPU_SH7760, CPU_SH4_202, CPU_SH4_501,
/* SH-4A types */
- CPU_SH7763, CPU_SH7770, CPU_SH7780, CPU_SH7781, CPU_SH7785,
+ CPU_SH7763, CPU_SH7770, CPU_SH7780, CPU_SH7781, CPU_SH7785, CPU_SH7786,
CPU_SH7723, CPU_SHX3,
/* SH4AL-DSP types */
diff --git a/arch/sh/include/asm/processor_32.h b/arch/sh/include/asm/processor_32.h
index d79063c..efdd78a 100644
--- a/arch/sh/include/asm/processor_32.h
+++ b/arch/sh/include/asm/processor_32.h
@@ -108,12 +108,12 @@
/*
* Do necessary setup to start up a newly executed thread.
*/
-#define start_thread(regs, new_pc, new_sp) \
+#define start_thread(_regs, new_pc, new_sp) \
set_fs(USER_DS); \
- regs->pr = 0; \
- regs->sr = SR_FD; /* User mode. */ \
- regs->pc = new_pc; \
- regs->regs[15] = new_sp
+ _regs->pr = 0; \
+ _regs->sr = SR_FD; /* User mode. */ \
+ _regs->pc = new_pc; \
+ _regs->regs[15] = new_sp
/* Forward declaration, a strange C thing */
struct task_struct;
@@ -189,10 +189,9 @@
#define KSTK_EIP(tsk) (task_pt_regs(tsk)->pc)
#define KSTK_ESP(tsk) (task_pt_regs(tsk)->regs[15])
-#define user_stack_pointer(regs) ((regs)->regs[15])
+#define user_stack_pointer(_regs) ((_regs)->regs[15])
-#if defined(CONFIG_CPU_SH2A) || defined(CONFIG_CPU_SH3) || \
- defined(CONFIG_CPU_SH4)
+#if defined(CONFIG_CPU_SH2A) || defined(CONFIG_CPU_SH4)
#define PREFETCH_STRIDE L1_CACHE_BYTES
#define ARCH_HAS_PREFETCH
#define ARCH_HAS_PREFETCHW
diff --git a/arch/sh/include/asm/processor_64.h b/arch/sh/include/asm/processor_64.h
index 803177f..5727d31 100644
--- a/arch/sh/include/asm/processor_64.h
+++ b/arch/sh/include/asm/processor_64.h
@@ -145,13 +145,13 @@
*/
#define SR_USER (SR_MMU | SR_FD)
-#define start_thread(regs, new_pc, new_sp) \
+#define start_thread(_regs, new_pc, new_sp) \
set_fs(USER_DS); \
- regs->sr = SR_USER; /* User mode. */ \
- regs->pc = new_pc - 4; /* Compensate syscall exit */ \
- regs->pc |= 1; /* Set SHmedia ! */ \
- regs->regs[18] = 0; \
- regs->regs[15] = new_sp
+ _regs->sr = SR_USER; /* User mode. */ \
+ _regs->pc = new_pc - 4; /* Compensate syscall exit */ \
+ _regs->pc |= 1; /* Set SHmedia ! */ \
+ _regs->regs[18] = 0; \
+ _regs->regs[15] = new_sp
/* Forward declaration, a strange C thing */
struct task_struct;
@@ -226,7 +226,7 @@
#define KSTK_EIP(tsk) ((tsk)->thread.pc)
#define KSTK_ESP(tsk) ((tsk)->thread.sp)
-#define user_stack_pointer(regs) ((regs)->regs[15])
+#define user_stack_pointer(_regs) ((_regs)->regs[15])
#endif /* __ASSEMBLY__ */
#endif /* __ASM_SH_PROCESSOR_64_H */
diff --git a/arch/sh/include/asm/ptrace.h b/arch/sh/include/asm/ptrace.h
index 12912ab..81c6568 100644
--- a/arch/sh/include/asm/ptrace.h
+++ b/arch/sh/include/asm/ptrace.h
@@ -122,14 +122,12 @@
#ifdef CONFIG_SH_DSP
#define task_pt_regs(task) \
((struct pt_regs *) (task_stack_page(task) + THREAD_SIZE \
- - sizeof(struct pt_dspregs) - sizeof(unsigned long)) - 1)
+ - sizeof(struct pt_dspregs)) - 1)
#define task_pt_dspregs(task) \
- ((struct pt_dspregs *) (task_stack_page(task) + THREAD_SIZE \
- - sizeof(unsigned long)) - 1)
+ ((struct pt_dspregs *) (task_stack_page(task) + THREAD_SIZE) - 1)
#else
#define task_pt_regs(task) \
- ((struct pt_regs *) (task_stack_page(task) + THREAD_SIZE \
- - sizeof(unsigned long)) - 1)
+ ((struct pt_regs *) (task_stack_page(task) + THREAD_SIZE) - 1)
#endif
static inline unsigned long profile_pc(struct pt_regs *regs)
diff --git a/arch/sh/include/asm/sections.h b/arch/sh/include/asm/sections.h
index 8f8f4ad..01a4076a37 100644
--- a/arch/sh/include/asm/sections.h
+++ b/arch/sh/include/asm/sections.h
@@ -3,6 +3,7 @@
#include <asm-generic/sections.h>
+extern void __nosave_begin, __nosave_end;
extern long __machvec_start, __machvec_end;
extern char __uncached_start, __uncached_end;
extern char _ebss[];
diff --git a/arch/sh/include/asm/suspend.h b/arch/sh/include/asm/suspend.h
new file mode 100644
index 0000000..b1b9953
--- /dev/null
+++ b/arch/sh/include/asm/suspend.h
@@ -0,0 +1,22 @@
+#ifndef _ASM_SH_SUSPEND_H
+#define _ASM_SH_SUSPEND_H
+
+#ifndef __ASSEMBLY__
+static inline int arch_prepare_suspend(void) { return 0; }
+
+#include <asm/ptrace.h>
+
+struct swsusp_arch_regs {
+ struct pt_regs user_regs;
+ unsigned long bank1_regs[8];
+};
+#endif
+
+/* flags passed to assembly suspend code */
+#define SUSP_SH_SLEEP (1 << 0) /* Regular sleep mode */
+#define SUSP_SH_STANDBY (1 << 1) /* SH-Mobile Software standby mode */
+#define SUSP_SH_RSTANDBY (1 << 2) /* SH-Mobile R-standby mode */
+#define SUSP_SH_USTANDBY (1 << 3) /* SH-Mobile U-standby mode */
+#define SUSP_SH_SF (1 << 4) /* Enable self-refresh */
+
+#endif /* _ASM_SH_SUSPEND_H */
diff --git a/arch/sh/include/asm/timer.h b/arch/sh/include/asm/timer.h
index a7ca3a1..4c3b66e3 100644
--- a/arch/sh/include/asm/timer.h
+++ b/arch/sh/include/asm/timer.h
@@ -9,7 +9,6 @@
int (*init)(void);
int (*start)(void);
int (*stop)(void);
- cycle_t (*read)(void);
#ifndef CONFIG_GENERIC_TIME
unsigned long (*get_offset)(void);
#endif
@@ -39,6 +38,7 @@
/* arch/sh/kernel/time.c */
void handle_timer_tick(void);
-extern unsigned long sh_hpt_frequency;
+
+extern struct clocksource clocksource_sh;
#endif /* __ASM_SH_TIMER_H */
diff --git a/arch/sh/include/asm/tlb.h b/arch/sh/include/asm/tlb.h
index 88ff1ae..9c16f73 100644
--- a/arch/sh/include/asm/tlb.h
+++ b/arch/sh/include/asm/tlb.h
@@ -6,22 +6,106 @@
#endif
#ifndef __ASSEMBLY__
+#include <linux/pagemap.h>
-#define tlb_start_vma(tlb, vma) \
- flush_cache_range(vma, vma->vm_start, vma->vm_end)
-
-#define tlb_end_vma(tlb, vma) \
- flush_tlb_range(vma, vma->vm_start, vma->vm_end)
-
-#define __tlb_remove_tlb_entry(tlb, pte, address) do { } while (0)
+#ifdef CONFIG_MMU
+#include <asm/pgalloc.h>
+#include <asm/tlbflush.h>
/*
- * Flush whole TLBs for MM
+ * TLB handling. This allows us to remove pages from the page
+ * tables, and efficiently handle the TLB issues.
*/
-#define tlb_flush(tlb) flush_tlb_mm((tlb)->mm)
+struct mmu_gather {
+ struct mm_struct *mm;
+ unsigned int fullmm;
+ unsigned long start, end;
+};
-#include <linux/pagemap.h>
+DECLARE_PER_CPU(struct mmu_gather, mmu_gathers);
+
+static inline void init_tlb_gather(struct mmu_gather *tlb)
+{
+ tlb->start = TASK_SIZE;
+ tlb->end = 0;
+
+ if (tlb->fullmm) {
+ tlb->start = 0;
+ tlb->end = TASK_SIZE;
+ }
+}
+
+static inline struct mmu_gather *
+tlb_gather_mmu(struct mm_struct *mm, unsigned int full_mm_flush)
+{
+ struct mmu_gather *tlb = &get_cpu_var(mmu_gathers);
+
+ tlb->mm = mm;
+ tlb->fullmm = full_mm_flush;
+
+ init_tlb_gather(tlb);
+
+ return tlb;
+}
+
+static inline void
+tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end)
+{
+ if (tlb->fullmm)
+ flush_tlb_mm(tlb->mm);
+
+ /* keep the page table cache within bounds */
+ check_pgt_cache();
+
+ put_cpu_var(mmu_gathers);
+}
+
+static inline void
+tlb_remove_tlb_entry(struct mmu_gather *tlb, pte_t *ptep, unsigned long address)
+{
+ if (tlb->start > address)
+ tlb->start = address;
+ if (tlb->end < address + PAGE_SIZE)
+ tlb->end = address + PAGE_SIZE;
+}
+
+/*
+ * In the case of tlb vma handling, we can optimise these away in the
+ * case where we're doing a full MM flush. When we're doing a munmap,
+ * the vmas are adjusted to only cover the region to be torn down.
+ */
+static inline void
+tlb_start_vma(struct mmu_gather *tlb, struct vm_area_struct *vma)
+{
+ if (!tlb->fullmm)
+ flush_cache_range(vma, vma->vm_start, vma->vm_end);
+}
+
+static inline void
+tlb_end_vma(struct mmu_gather *tlb, struct vm_area_struct *vma)
+{
+ if (!tlb->fullmm && tlb->end) {
+ flush_tlb_range(vma, tlb->start, tlb->end);
+ init_tlb_gather(tlb);
+ }
+}
+
+#define tlb_remove_page(tlb,page) free_page_and_swap_cache(page)
+#define pte_free_tlb(tlb, ptep) pte_free((tlb)->mm, ptep)
+#define pmd_free_tlb(tlb, pmdp) pmd_free((tlb)->mm, pmdp)
+#define pud_free_tlb(tlb, pudp) pud_free((tlb)->mm, pudp)
+
+#define tlb_migrate_finish(mm) do { } while (0)
+
+#else /* CONFIG_MMU */
+
+#define tlb_start_vma(tlb, vma) do { } while (0)
+#define tlb_end_vma(tlb, vma) do { } while (0)
+#define __tlb_remove_tlb_entry(tlb, pte, address) do { } while (0)
+#define tlb_flush(tlb) do { } while (0)
+
#include <asm-generic/tlb.h>
+#endif /* CONFIG_MMU */
#endif /* __ASSEMBLY__ */
#endif /* __ASM_SH_TLB_H */
diff --git a/arch/sh/include/cpu-sh3/cpu/dma.h b/arch/sh/include/cpu-sh3/cpu/dma.h
index 6813c32..0ea15f3 100644
--- a/arch/sh/include/cpu-sh3/cpu/dma.h
+++ b/arch/sh/include/cpu-sh3/cpu/dma.h
@@ -1,22 +1,17 @@
#ifndef __ASM_CPU_SH3_DMA_H
#define __ASM_CPU_SH3_DMA_H
-
#if defined(CONFIG_CPU_SUBTYPE_SH7720) || \
- defined(CONFIG_CPU_SUBTYPE_SH7721)
-#define SH_DMAC_BASE 0xa4010020
-#else
-#define SH_DMAC_BASE 0xa4000020
+ defined(CONFIG_CPU_SUBTYPE_SH7721) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7710) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7712)
+#define SH_DMAC_BASE0 0xa4010020
+#else /* SH7705/06/07/09 */
+#define SH_DMAC_BASE0 0xa4000020
#endif
-#if defined(CONFIG_CPU_SUBTYPE_SH7720) || defined(CONFIG_CPU_SUBTYPE_SH7709)
#define DMTE0_IRQ 48
-#define DMTE1_IRQ 49
-#define DMTE2_IRQ 50
-#define DMTE3_IRQ 51
#define DMTE4_IRQ 76
-#define DMTE5_IRQ 77
-#endif
/* Definitions for the SuperH DMAC */
#define TM_BURST 0x00000020
diff --git a/arch/sh/include/cpu-sh4/cpu/dma-sh4a.h b/arch/sh/include/cpu-sh4/cpu/dma-sh4a.h
new file mode 100644
index 0000000..0ed5178
--- /dev/null
+++ b/arch/sh/include/cpu-sh4/cpu/dma-sh4a.h
@@ -0,0 +1,94 @@
+#ifndef __ASM_SH_CPU_SH4_DMA_SH7780_H
+#define __ASM_SH_CPU_SH4_DMA_SH7780_H
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7343) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7722) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7730)
+#define DMTE0_IRQ 48
+#define DMTE4_IRQ 76
+#define DMAE0_IRQ 78 /* DMA Error IRQ*/
+#define SH_DMAC_BASE0 0xFE008020
+#define SH_DMARS_BASE 0xFE009000
+#elif defined(CONFIG_CPU_SUBTYPE_SH7763) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7764)
+#define DMTE0_IRQ 34
+#define DMTE4_IRQ 44
+#define DMAE0_IRQ 38
+#define SH_DMAC_BASE0 0xFF608020
+#define SH_DMARS_BASE 0xFF609000
+#elif defined(CONFIG_CPU_SUBTYPE_SH7723)
+#define DMTE0_IRQ 48 /* DMAC0A*/
+#define DMTE4_IRQ 40 /* DMAC0B */
+#define DMTE6_IRQ 42
+#define DMTE8_IRQ 76 /* DMAC1A */
+#define DMTE9_IRQ 77
+#define DMTE10_IRQ 72 /* DMAC1B */
+#define DMTE11_IRQ 73
+#define DMAE0_IRQ 78 /* DMA Error IRQ*/
+#define DMAE1_IRQ 74 /* DMA Error IRQ*/
+#define SH_DMAC_BASE0 0xFE008020
+#define SH_DMAC_BASE1 0xFDC08020
+#define SH_DMARS_BASE 0xFDC09000
+#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
+#define DMTE0_IRQ 34
+#define DMTE4_IRQ 44
+#define DMTE6_IRQ 46
+#define DMTE8_IRQ 92
+#define DMTE9_IRQ 93
+#define DMTE10_IRQ 94
+#define DMTE11_IRQ 95
+#define DMAE0_IRQ 38 /* DMA Error IRQ */
+#define SH_DMAC_BASE0 0xFC808020
+#define SH_DMAC_BASE1 0xFC818020
+#define SH_DMARS_BASE 0xFC809000
+#else /* SH7785 */
+#define DMTE0_IRQ 33
+#define DMTE4_IRQ 37
+#define DMTE6_IRQ 52
+#define DMTE8_IRQ 54
+#define DMTE9_IRQ 55
+#define DMTE10_IRQ 56
+#define DMTE11_IRQ 57
+#define DMAE0_IRQ 39 /* DMA Error IRQ0 */
+#define DMAE1_IRQ 58 /* DMA Error IRQ1 */
+#define SH_DMAC_BASE0 0xFC808020
+#define SH_DMAC_BASE1 0xFCC08020
+#define SH_DMARS_BASE 0xFC809000
+#endif
+
+#define REQ_HE 0x000000C0
+#define REQ_H 0x00000080
+#define REQ_LE 0x00000040
+#define TM_BURST 0x0000020
+#define TS_8 0x00000000
+#define TS_16 0x00000008
+#define TS_32 0x00000010
+#define TS_16BLK 0x00000018
+#define TS_32BLK 0x00100000
+
+/*
+ * The SuperH DMAC supports a number of transmit sizes, we list them here,
+ * with their respective values as they appear in the CHCR registers.
+ *
+ * Defaults to a 64-bit transfer size.
+ */
+enum {
+ XMIT_SZ_8BIT,
+ XMIT_SZ_16BIT,
+ XMIT_SZ_32BIT,
+ XMIT_SZ_128BIT,
+ XMIT_SZ_256BIT,
+};
+
+/*
+ * The DMA count is defined as the number of bytes to transfer.
+ */
+static unsigned int ts_shift[] __maybe_unused = {
+ [XMIT_SZ_8BIT] = 0,
+ [XMIT_SZ_16BIT] = 1,
+ [XMIT_SZ_32BIT] = 2,
+ [XMIT_SZ_128BIT] = 4,
+ [XMIT_SZ_256BIT] = 5,
+};
+
+#endif /* __ASM_SH_CPU_SH4_DMA_SH7780_H */
diff --git a/arch/sh/include/cpu-sh4/cpu/dma-sh7780.h b/arch/sh/include/cpu-sh4/cpu/dma-sh7780.h
deleted file mode 100644
index 71b426a..0000000
--- a/arch/sh/include/cpu-sh4/cpu/dma-sh7780.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#ifndef __ASM_SH_CPU_SH4_DMA_SH7780_H
-#define __ASM_SH_CPU_SH4_DMA_SH7780_H
-
-#define REQ_HE 0x000000C0
-#define REQ_H 0x00000080
-#define REQ_LE 0x00000040
-#define TM_BURST 0x0000020
-#define TS_8 0x00000000
-#define TS_16 0x00000008
-#define TS_32 0x00000010
-#define TS_16BLK 0x00000018
-#define TS_32BLK 0x00100000
-
-/*
- * The SuperH DMAC supports a number of transmit sizes, we list them here,
- * with their respective values as they appear in the CHCR registers.
- *
- * Defaults to a 64-bit transfer size.
- */
-enum {
- XMIT_SZ_8BIT,
- XMIT_SZ_16BIT,
- XMIT_SZ_32BIT,
- XMIT_SZ_128BIT,
- XMIT_SZ_256BIT,
-};
-
-/*
- * The DMA count is defined as the number of bytes to transfer.
- */
-static unsigned int ts_shift[] __maybe_unused = {
- [XMIT_SZ_8BIT] = 0,
- [XMIT_SZ_16BIT] = 1,
- [XMIT_SZ_32BIT] = 2,
- [XMIT_SZ_128BIT] = 4,
- [XMIT_SZ_256BIT] = 5,
-};
-
-#endif /* __ASM_SH_CPU_SH4_DMA_SH7780_H */
diff --git a/arch/sh/include/cpu-sh4/cpu/dma.h b/arch/sh/include/cpu-sh4/cpu/dma.h
index 235b7cd..bcb3024 100644
--- a/arch/sh/include/cpu-sh4/cpu/dma.h
+++ b/arch/sh/include/cpu-sh4/cpu/dma.h
@@ -1,31 +1,29 @@
#ifndef __ASM_CPU_SH4_DMA_H
#define __ASM_CPU_SH4_DMA_H
-#define DMAOR_INIT ( 0x8000 | DMAOR_DME )
-
/* SH7751/7760/7780 DMA IRQ sources */
-#define DMTE0_IRQ 34
-#define DMTE1_IRQ 35
-#define DMTE2_IRQ 36
-#define DMTE3_IRQ 37
-#define DMTE4_IRQ 44
-#define DMTE5_IRQ 45
-#define DMTE6_IRQ 46
-#define DMTE7_IRQ 47
-#define DMAE_IRQ 38
#ifdef CONFIG_CPU_SH4A
-#define SH_DMAC_BASE 0xfc808020
+#define DMAOR_INIT (DMAOR_DME)
#define CHCR_TS_MASK 0x18
#define CHCR_TS_SHIFT 3
-#include <cpu/dma-sh7780.h>
-#else
-#define SH_DMAC_BASE 0xffa00000
+#include <cpu/dma-sh4a.h>
+#else /* CONFIG_CPU_SH4A */
+/*
+ * SH7750/SH7751/SH7760
+ */
+#define DMTE0_IRQ 34
+#define DMTE4_IRQ 44
+#define DMTE6_IRQ 46
+#define DMAE0_IRQ 38
+#define DMAOR_INIT (0x8000|DMAOR_DME)
+#define SH_DMAC_BASE0 0xffa00000
+#define SH_DMAC_BASE1 0xffa00070
/* Definitions for the SuperH DMAC */
-#define TM_BURST 0x0000080
+#define TM_BURST 0x00000080
#define TS_8 0x00000010
#define TS_16 0x00000020
#define TS_32 0x00000030
diff --git a/arch/sh/include/cpu-sh4/cpu/freq.h b/arch/sh/include/cpu-sh4/cpu/freq.h
index c23af81..749d1c4 100644
--- a/arch/sh/include/cpu-sh4/cpu/freq.h
+++ b/arch/sh/include/cpu-sh4/cpu/freq.h
@@ -29,6 +29,10 @@
#define FRQCR0 0xffc80000
#define FRQCR1 0xffc80004
#define FRQMR1 0xffc80014
+#elif defined(CONFIG_CPU_SUBTYPE_SH7786)
+#define FRQCR0 0xffc40000
+#define FRQCR1 0xffc40004
+#define FRQMR1 0xffc40014
#elif defined(CONFIG_CPU_SUBTYPE_SHX3)
#define FRQCR 0xffc00014
#else
diff --git a/arch/sh/include/cpu-sh4/cpu/mmu_context.h b/arch/sh/include/cpu-sh4/cpu/mmu_context.h
index 9ea8eb2..3ce7ef6 100644
--- a/arch/sh/include/cpu-sh4/cpu/mmu_context.h
+++ b/arch/sh/include/cpu-sh4/cpu/mmu_context.h
@@ -14,28 +14,35 @@
#define MMU_PTEL 0xFF000004 /* Page table entry register LOW */
#define MMU_TTB 0xFF000008 /* Translation table base register */
#define MMU_TEA 0xFF00000C /* TLB Exception Address */
-#define MMU_PTEA 0xFF000034 /* Page table entry assistance register */
+#define MMU_PTEA 0xFF000034 /* PTE assistance register */
+#define MMU_PTEAEX 0xFF00007C /* PTE ASID extension register */
#define MMUCR 0xFF000010 /* MMU Control Register */
-#define MMU_ITLB_ADDRESS_ARRAY 0xF2000000
#define MMU_UTLB_ADDRESS_ARRAY 0xF6000000
+#define MMU_UTLB_ADDRESS_ARRAY2 0xF6800000
#define MMU_PAGE_ASSOC_BIT 0x80
#define MMUCR_TI (1<<2)
-#ifdef CONFIG_X2TLB
-#define MMUCR_ME (1 << 7)
-#else
-#define MMUCR_ME (0)
-#endif
-
#if defined(CONFIG_32BIT) && defined(CONFIG_CPU_SUBTYPE_ST40)
#define MMUCR_SE (1 << 4)
#else
#define MMUCR_SE (0)
#endif
+#ifdef CONFIG_CPU_HAS_PTEAEX
+#define MMUCR_AEX (1 << 6)
+#else
+#define MMUCR_AEX (0)
+#endif
+
+#ifdef CONFIG_X2TLB
+#define MMUCR_ME (1 << 7)
+#else
+#define MMUCR_ME (0)
+#endif
+
#ifdef CONFIG_SH_STORE_QUEUES
#define MMUCR_SQMD (1 << 9)
#else
@@ -43,17 +50,7 @@
#endif
#define MMU_NTLB_ENTRIES 64
-#define MMU_CONTROL_INIT (0x05|MMUCR_SQMD|MMUCR_ME|MMUCR_SE)
-
-#define MMU_ITLB_DATA_ARRAY 0xF3000000
-#define MMU_UTLB_DATA_ARRAY 0xF7000000
-
-#define MMU_UTLB_ENTRIES 64
-#define MMU_U_ENTRY_SHIFT 8
-#define MMU_UTLB_VALID 0x100
-#define MMU_ITLB_ENTRIES 4
-#define MMU_I_ENTRY_SHIFT 8
-#define MMU_ITLB_VALID 0x100
+#define MMU_CONTROL_INIT (0x05|MMUCR_SQMD|MMUCR_ME|MMUCR_SE|MMUCR_AEX)
#define TRA 0xff000020
#define EXPEVT 0xff000024
diff --git a/arch/sh/include/cpu-sh4/cpu/sh7786.h b/arch/sh/include/cpu-sh4/cpu/sh7786.h
new file mode 100644
index 0000000..48688ad
--- /dev/null
+++ b/arch/sh/include/cpu-sh4/cpu/sh7786.h
@@ -0,0 +1,192 @@
+/*
+ * SH7786 Pinmux
+ *
+ * Copyright (C) 2008, 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on sh7785.h
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef __CPU_SH7786_H__
+#define __CPU_SH7786_H__
+
+enum {
+ /* PA */
+ GPIO_PA7, GPIO_PA6, GPIO_PA5, GPIO_PA4,
+ GPIO_PA3, GPIO_PA2, GPIO_PA1, GPIO_PA0,
+
+ /* PB */
+ GPIO_PB7, GPIO_PB6, GPIO_PB5, GPIO_PB4,
+ GPIO_PB3, GPIO_PB2, GPIO_PB1, GPIO_PB0,
+
+ /* PC */
+ GPIO_PC7, GPIO_PC6, GPIO_PC5, GPIO_PC4,
+ GPIO_PC3, GPIO_PC2, GPIO_PC1, GPIO_PC0,
+
+ /* PD */
+ GPIO_PD7, GPIO_PD6, GPIO_PD5, GPIO_PD4,
+ GPIO_PD3, GPIO_PD2, GPIO_PD1, GPIO_PD0,
+
+ /* PE */
+ GPIO_PE5, GPIO_PE4, GPIO_PE3, GPIO_PE2,
+ GPIO_PE1, GPIO_PE0,
+
+ /* PF */
+ GPIO_PF7, GPIO_PF6, GPIO_PF5, GPIO_PF4,
+ GPIO_PF3, GPIO_PF2, GPIO_PF1, GPIO_PF0,
+
+ /* PG */
+ GPIO_PG7, GPIO_PG6, GPIO_PG5, GPIO_PG4,
+ GPIO_PG3, GPIO_PG2, GPIO_PG1, GPIO_PG0,
+
+ /* PH */
+ GPIO_PH7, GPIO_PH6, GPIO_PH5, GPIO_PH4,
+ GPIO_PH3, GPIO_PH2, GPIO_PH1, GPIO_PH0,
+
+ /* PJ */
+ GPIO_PJ7, GPIO_PJ6, GPIO_PJ5, GPIO_PJ4,
+ GPIO_PJ3, GPIO_PJ2, GPIO_PJ1, GPIO_PJ0,
+
+ GPIO_FN_CDE,
+ GPIO_FN_ETH_MAGIC,
+ GPIO_FN_DISP,
+ GPIO_FN_ETH_LINK,
+ GPIO_FN_DR5,
+ GPIO_FN_ETH_TX_ER,
+ GPIO_FN_DR4,
+ GPIO_FN_ETH_TX_EN,
+ GPIO_FN_DR3,
+ GPIO_FN_ETH_TXD3,
+ GPIO_FN_DR2,
+ GPIO_FN_ETH_TXD2,
+ GPIO_FN_DR1,
+ GPIO_FN_ETH_TXD1,
+ GPIO_FN_DR0,
+ GPIO_FN_ETH_TXD0,
+ GPIO_FN_VSYNC,
+ GPIO_FN_HSPI_CLK,
+ GPIO_FN_ODDF,
+ GPIO_FN_HSPI_CS,
+ GPIO_FN_DG5,
+ GPIO_FN_ETH_MDIO,
+ GPIO_FN_DG4,
+ GPIO_FN_ETH_RX_CLK,
+ GPIO_FN_DG3,
+ GPIO_FN_ETH_MDC,
+ GPIO_FN_DG2,
+ GPIO_FN_ETH_COL,
+ GPIO_FN_DG1,
+ GPIO_FN_ETH_TX_CLK,
+ GPIO_FN_DG0,
+ GPIO_FN_ETH_CRS,
+ GPIO_FN_DCLKIN,
+ GPIO_FN_HSPI_RX,
+ GPIO_FN_HSYNC,
+ GPIO_FN_HSPI_TX,
+ GPIO_FN_DB5,
+ GPIO_FN_ETH_RXD3,
+ GPIO_FN_DB4,
+ GPIO_FN_ETH_RXD2,
+ GPIO_FN_DB3,
+ GPIO_FN_ETH_RXD1,
+ GPIO_FN_DB2,
+ GPIO_FN_ETH_RXD0,
+ GPIO_FN_DB1,
+ GPIO_FN_ETH_RX_DV,
+ GPIO_FN_DB0,
+ GPIO_FN_ETH_RX_ER,
+ GPIO_FN_DCLKOUT,
+ GPIO_FN_SCIF1_SLK,
+ GPIO_FN_SCIF1_RXD,
+ GPIO_FN_SCIF1_TXD,
+ GPIO_FN_DACK1,
+ GPIO_FN_BACK,
+ GPIO_FN_FALE,
+ GPIO_FN_DACK0,
+ GPIO_FN_FCLE,
+ GPIO_FN_DREQ1,
+ GPIO_FN_BREQ,
+ GPIO_FN_USB_OVC1,
+ GPIO_FN_DREQ0,
+ GPIO_FN_USB_OVC0,
+ GPIO_FN_USB_PENC1,
+ GPIO_FN_USB_PENC0,
+ GPIO_FN_HAC1_SDOUT,
+ GPIO_FN_SSI1_SDATA,
+ GPIO_FN_SDIF1CMD,
+ GPIO_FN_HAC1_SDIN,
+ GPIO_FN_SSI1_SCK,
+ GPIO_FN_SDIF1CD,
+ GPIO_FN_HAC1_SYNC,
+ GPIO_FN_SSI1_WS,
+ GPIO_FN_SDIF1WP,
+ GPIO_FN_HAC1_BITCLK,
+ GPIO_FN_SSI1_CLK,
+ GPIO_FN_SDIF1CLK,
+ GPIO_FN_HAC0_SDOUT,
+ GPIO_FN_SSI0_SDATA,
+ GPIO_FN_SDIF1D3,
+ GPIO_FN_HAC0_SDIN,
+ GPIO_FN_SSI0_SCK,
+ GPIO_FN_SDIF1D2,
+ GPIO_FN_HAC0_SYNC,
+ GPIO_FN_SSI0_WS,
+ GPIO_FN_SDIF1D1,
+ GPIO_FN_HAC0_BITCLK,
+ GPIO_FN_SSI0_CLK,
+ GPIO_FN_SDIF1D0,
+ GPIO_FN_SCIF3_SCK,
+ GPIO_FN_SSI2_SDATA,
+ GPIO_FN_SCIF3_RXD,
+ GPIO_FN_TCLK,
+ GPIO_FN_SSI2_SCK,
+ GPIO_FN_SCIF3_TXD,
+ GPIO_FN_HAC_RES,
+ GPIO_FN_SSI2_WS,
+ GPIO_FN_DACK3,
+ GPIO_FN_SDIF0CMD,
+ GPIO_FN_DACK2,
+ GPIO_FN_SDIF0CD,
+ GPIO_FN_DREQ3,
+ GPIO_FN_SDIF0WP,
+ GPIO_FN_SCIF0_CTS,
+ GPIO_FN_DREQ2,
+ GPIO_FN_SDIF0CLK,
+ GPIO_FN_SCIF0_RTS,
+ GPIO_FN_IRL7,
+ GPIO_FN_SDIF0D3,
+ GPIO_FN_SCIF0_SCK,
+ GPIO_FN_IRL6,
+ GPIO_FN_SDIF0D2,
+ GPIO_FN_SCIF0_RXD,
+ GPIO_FN_IRL5,
+ GPIO_FN_SDIF0D1,
+ GPIO_FN_SCIF0_TXD,
+ GPIO_FN_IRL4,
+ GPIO_FN_SDIF0D0,
+ GPIO_FN_SCIF5_SCK,
+ GPIO_FN_FRB,
+ GPIO_FN_SCIF5_RXD,
+ GPIO_FN_IOIS16,
+ GPIO_FN_SCIF5_TXD,
+ GPIO_FN_CE2B,
+ GPIO_FN_DRAK3,
+ GPIO_FN_CE2A,
+ GPIO_FN_SCIF4_SCK,
+ GPIO_FN_DRAK2,
+ GPIO_FN_SSI3_WS,
+ GPIO_FN_SCIF4_RXD,
+ GPIO_FN_DRAK1,
+ GPIO_FN_SSI3_SDATA,
+ GPIO_FN_FSTATUS,
+ GPIO_FN_SCIF4_TXD,
+ GPIO_FN_DRAK0,
+ GPIO_FN_SSI3_SCK,
+ GPIO_FN_FSE,
+};
+
+#endif /* __CPU_SH7786_H__ */
diff --git a/arch/sh/include/mach-common/mach/urquell.h b/arch/sh/include/mach-common/mach/urquell.h
new file mode 100644
index 0000000..14b3e1d
--- /dev/null
+++ b/arch/sh/include/mach-common/mach/urquell.h
@@ -0,0 +1,68 @@
+#ifndef __MACH_URQUELL_H
+#define __MACH_URQUELL_H
+
+/*
+ * ------ 0x00000000 ------------------------------------
+ * CS0 | (SW1,SW47) EEPROM, SRAM, NOR FLASH
+ * -----+ 0x04000000 ------------------------------------
+ * CS1 | (SW47) SRAM, SRAM-LAN-PCMCIA, NOR FLASH
+ * -----+ 0x08000000 ------------------------------------
+ * CS2 | DDR3
+ * CS3 |
+ * -----+ 0x10000000 ------------------------------------
+ * CS4 | PCIe
+ * -----+ 0x14000000 ------------------------------------
+ * CS5 | (SW47) LRAM/URAM, SRAM-LAN-PCMCIA
+ * -----+ 0x18000000 ------------------------------------
+ * CS6 | ATA, NAND FLASH
+ * -----+ 0x1c000000 ------------------------------------
+ * CS7 | SH7786 register
+ * -----+------------------------------------------------
+ */
+
+#define NOR_FLASH_ADDR 0x00000000
+#define NOR_FLASH_SIZE 0x04000000
+
+#define CS1_BASE 0x05000000
+#define CS5_BASE 0x15000000
+#define FPGA_BASE CS1_BASE
+
+#define BOARDREG(ofs) (FPGA_BASE + ofs##_OFS)
+#define UBOARDREG(ofs) (0xa0000000 + FPGA_BASE + ofs##_OFS)
+
+#define SRSTR_OFS 0x0000 /* System reset register */
+#define BDMR_OFS 0x0010 /* Board operating mode resister */
+#define IRL0SR_OFS 0x0020 /* IRL0 Status register */
+#define IRL0MSKR_OFS 0x0030 /* IRL0 Mask register */
+#define IRL1SR_OFS 0x0040 /* IRL1 Status register */
+#define IRL1MSKR_OFS 0x0050 /* IRL1 Mask register */
+#define IRL2SR_OFS 0x0060 /* IRL2 Status register */
+#define IRL2MSKR_OFS 0x0070 /* IRL2 Mask register */
+#define IRL3SR_OFS 0x0080 /* IRL3 Status register */
+#define IRL3MSKR_OFS 0x0090 /* IRL3 Mask register */
+#define SOFTINTR_OFS 0x0120 /* Softwear Interrupt register */
+#define SLEDR_OFS 0x0130 /* LED control resister */
+#define MAPSCIFSWR_OFS 0x0140 /* Map/SCIF Switch register */
+#define FPVERR_OFS 0x0150 /* FPGA Version register */
+#define FPDATER_OFS 0x0160 /* FPGA Date register */
+#define FPYEARR_OFS 0x0170 /* FPGA Year register */
+#define TCLKCR_OFS 0x0180 /* TCLK Control register */
+#define DIPSWMR_OFS 0x1000 /* DIPSW monitor register */
+#define FPODR_OFS 0x1010 /* Output port data register */
+#define ATACNR_OFS 0x1020 /* ATA-CN Control/status register */
+#define FPINDR_OFS 0x1030 /* Input port data register */
+#define MDSWMR_OFS 0x1040 /* MODE SW monitor register */
+#define DDR3BUPCR_OFS 0x1050 /* DDR3 Backup control register */
+#define SSICODECCR_OFS 0x1060 /* SSI-CODEC control register */
+#define PCIESLOTSR_OFS 0x1070 /* PCIexpress Slot status register */
+#define ETHERPORTSR_OFS 0x1080 /* EtherPhy Port status register */
+#define LATCHCR_OFS 0x3000 /* Latch control register */
+#define LATCUAR_OFS 0x3010 /* Latch upper address register */
+#define LATCLAR_OFS 0x3012 /* Latch lower address register */
+#define LATCLUDR_OFS 0x3024 /* Latch D31-16 register */
+#define LATCLLDR_OFS 0x3026 /* Latch D15-0 register */
+
+#define CHARLED_OFS 0x2000 /* Character LED */
+
+#endif /* __MACH_URQUELL_H */
+
diff --git a/arch/sh/kernel/Makefile_32 b/arch/sh/kernel/Makefile_32
index 2e1b86e..82a3a15 100644
--- a/arch/sh/kernel/Makefile_32
+++ b/arch/sh/kernel/Makefile_32
@@ -30,5 +30,6 @@
obj-$(CONFIG_GENERIC_GPIO) += gpio.o
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_DUMP_CODE) += disassemble.o
+obj-$(CONFIG_HIBERNATION) += swsusp.o
EXTRA_CFLAGS += -Werror
diff --git a/arch/sh/kernel/asm-offsets.c b/arch/sh/kernel/asm-offsets.c
index 57cf0e0..99aceb28 100644
--- a/arch/sh/kernel/asm-offsets.c
+++ b/arch/sh/kernel/asm-offsets.c
@@ -12,8 +12,10 @@
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/kbuild.h>
+#include <linux/suspend.h>
#include <asm/thread_info.h>
+#include <asm/suspend.h>
int main(void)
{
@@ -25,5 +27,11 @@
DEFINE(TI_PRE_COUNT, offsetof(struct thread_info, preempt_count));
DEFINE(TI_RESTART_BLOCK,offsetof(struct thread_info, restart_block));
+#ifdef CONFIG_HIBERNATION
+ DEFINE(PBE_ADDRESS, offsetof(struct pbe, address));
+ DEFINE(PBE_ORIG_ADDRESS, offsetof(struct pbe, orig_address));
+ DEFINE(PBE_NEXT, offsetof(struct pbe, next));
+ DEFINE(SWSUSP_ARCH_REGS_SIZE, sizeof(struct swsusp_arch_regs));
+#endif
return 0;
}
diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile
index f471d24..2600641 100644
--- a/arch/sh/kernel/cpu/Makefile
+++ b/arch/sh/kernel/cpu/Makefile
@@ -11,6 +11,7 @@
# Special cases for family ancestry.
obj-$(CONFIG_CPU_SH4A) += sh4a/
+obj-$(CONFIG_ARCH_SHMOBILE) += shmobile/
# Common interfaces.
diff --git a/arch/sh/kernel/cpu/clock.c b/arch/sh/kernel/cpu/clock.c
index 7b17137..1dc8964 100644
--- a/arch/sh/kernel/cpu/clock.c
+++ b/arch/sh/kernel/cpu/clock.c
@@ -20,6 +20,8 @@
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/kref.h>
+#include <linux/kobject.h>
+#include <linux/sysdev.h>
#include <linux/seq_file.h>
#include <linux/err.h>
#include <linux/platform_device.h>
@@ -239,6 +241,35 @@
}
EXPORT_SYMBOL_GPL(clk_recalc_rate);
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ int ret = -EINVAL;
+ struct clk *old;
+
+ if (!parent || !clk)
+ return ret;
+
+ old = clk->parent;
+ if (likely(clk->ops && clk->ops->set_parent)) {
+ unsigned long flags;
+ spin_lock_irqsave(&clock_lock, flags);
+ ret = clk->ops->set_parent(clk, parent);
+ spin_unlock_irqrestore(&clock_lock, flags);
+ clk->parent = (ret ? old : parent);
+ }
+
+ if (unlikely(clk->flags & CLK_RATE_PROPAGATES))
+ propagate_rate(clk);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_parent);
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+ return clk->parent;
+}
+EXPORT_SYMBOL_GPL(clk_get_parent);
+
long clk_round_rate(struct clk *clk, unsigned long rate)
{
if (likely(clk->ops && clk->ops->round_rate)) {
@@ -329,6 +360,70 @@
return p - buf;
}
+#ifdef CONFIG_PM
+static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state)
+{
+ static pm_message_t prev_state;
+ struct clk *clkp;
+
+ switch (state.event) {
+ case PM_EVENT_ON:
+ /* Resumeing from hibernation */
+ if (prev_state.event == PM_EVENT_FREEZE) {
+ list_for_each_entry(clkp, &clock_list, node)
+ if (likely(clkp->ops)) {
+ unsigned long rate = clkp->rate;
+
+ if (likely(clkp->ops->set_parent))
+ clkp->ops->set_parent(clkp,
+ clkp->parent);
+ if (likely(clkp->ops->set_rate))
+ clkp->ops->set_rate(clkp,
+ rate, NO_CHANGE);
+ else if (likely(clkp->ops->recalc))
+ clkp->ops->recalc(clkp);
+ }
+ }
+ break;
+ case PM_EVENT_FREEZE:
+ break;
+ case PM_EVENT_SUSPEND:
+ break;
+ }
+
+ prev_state = state;
+ return 0;
+}
+
+static int clks_sysdev_resume(struct sys_device *dev)
+{
+ return clks_sysdev_suspend(dev, PMSG_ON);
+}
+
+static struct sysdev_class clks_sysdev_class = {
+ .name = "clks",
+};
+
+static struct sysdev_driver clks_sysdev_driver = {
+ .suspend = clks_sysdev_suspend,
+ .resume = clks_sysdev_resume,
+};
+
+static struct sys_device clks_sysdev_dev = {
+ .cls = &clks_sysdev_class,
+};
+
+static int __init clk_sysdev_init(void)
+{
+ sysdev_class_register(&clks_sysdev_class);
+ sysdev_driver_register(&clks_sysdev_class, &clks_sysdev_driver);
+ sysdev_register(&clks_sysdev_dev);
+
+ return 0;
+}
+subsys_initcall(clk_sysdev_init);
+#endif
+
int __init clk_init(void)
{
int i, ret = 0;
diff --git a/arch/sh/kernel/cpu/sh2/setup-sh7619.c b/arch/sh/kernel/cpu/sh2/setup-sh7619.c
index 56e5878..0e32d8e 100644
--- a/arch/sh/kernel/cpu/sh2/setup-sh7619.c
+++ b/arch/sh/kernel/cpu/sh2/setup-sh7619.c
@@ -2,6 +2,7 @@
* SH7619 Setup
*
* Copyright (C) 2006 Yoshinori Sato
+ * Copyright (C) 2009 Paul Mundt
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
@@ -18,15 +19,10 @@
/* interrupt sources */
IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7,
WDT, EDMAC, CMT0, CMT1,
- SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI,
- SCIF1_ERI, SCIF1_RXI, SCIF1_BRI, SCIF1_TXI,
- SCIF2_ERI, SCIF2_RXI, SCIF2_BRI, SCIF2_TXI,
+ SCIF0, SCIF1, SCIF2,
HIF_HIFI, HIF_HIFBI,
DMAC0, DMAC1, DMAC2, DMAC3,
SIOF,
-
- /* interrupt groups */
- SCIF0, SCIF1, SCIF2,
};
static struct intc_vect vectors[] __initdata = {
@@ -36,24 +32,18 @@
INTC_IRQ(IRQ6, 82), INTC_IRQ(IRQ7, 83),
INTC_IRQ(WDT, 84), INTC_IRQ(EDMAC, 85),
INTC_IRQ(CMT0, 86), INTC_IRQ(CMT1, 87),
- INTC_IRQ(SCIF0_ERI, 88), INTC_IRQ(SCIF0_RXI, 89),
- INTC_IRQ(SCIF0_BRI, 90), INTC_IRQ(SCIF0_TXI, 91),
- INTC_IRQ(SCIF1_ERI, 92), INTC_IRQ(SCIF1_RXI, 93),
- INTC_IRQ(SCIF1_BRI, 94), INTC_IRQ(SCIF1_TXI, 95),
- INTC_IRQ(SCIF2_ERI, 96), INTC_IRQ(SCIF2_RXI, 97),
- INTC_IRQ(SCIF2_BRI, 98), INTC_IRQ(SCIF2_TXI, 99),
+ INTC_IRQ(SCIF0, 88), INTC_IRQ(SCIF0, 89),
+ INTC_IRQ(SCIF0, 90), INTC_IRQ(SCIF0, 91),
+ INTC_IRQ(SCIF1, 92), INTC_IRQ(SCIF1, 93),
+ INTC_IRQ(SCIF1, 94), INTC_IRQ(SCIF1, 95),
+ INTC_IRQ(SCIF2, 96), INTC_IRQ(SCIF2, 97),
+ INTC_IRQ(SCIF2, 98), INTC_IRQ(SCIF2, 99),
INTC_IRQ(HIF_HIFI, 100), INTC_IRQ(HIF_HIFBI, 101),
INTC_IRQ(DMAC0, 104), INTC_IRQ(DMAC1, 105),
INTC_IRQ(DMAC2, 106), INTC_IRQ(DMAC3, 107),
INTC_IRQ(SIOF, 108),
};
-static struct intc_group groups[] __initdata = {
- INTC_GROUP(SCIF0, SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI),
- INTC_GROUP(SCIF1, SCIF1_ERI, SCIF1_RXI, SCIF1_BRI, SCIF1_TXI),
- INTC_GROUP(SCIF2, SCIF2_ERI, SCIF2_RXI, SCIF2_BRI, SCIF2_TXI),
-};
-
static struct intc_prio_reg prio_registers[] __initdata = {
{ 0xf8140006, 0, 16, 4, /* IPRA */ { IRQ0, IRQ1, IRQ2, IRQ3 } },
{ 0xf8140008, 0, 16, 4, /* IPRB */ { IRQ4, IRQ5, IRQ6, IRQ7 } },
@@ -64,7 +54,7 @@
{ 0xf8080008, 0, 16, 4, /* IPRG */ { SIOF } },
};
-static DECLARE_INTC_DESC(intc_desc, "sh7619", vectors, groups,
+static DECLARE_INTC_DESC(intc_desc, "sh7619", vectors, NULL,
NULL, prio_registers, NULL);
static struct plat_sci_port sci_platform_data[] = {
@@ -72,17 +62,17 @@
.mapbase = 0xf8400000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 88, 89, 91, 90},
+ .irqs = { 88, 88, 88, 88 },
}, {
.mapbase = 0xf8410000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 92, 93, 95, 94},
+ .irqs = { 92, 92, 92, 92 },
}, {
.mapbase = 0xf8420000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 96, 97, 99, 98},
+ .irqs = { 96, 96, 96, 96 },
}, {
.flags = 0,
}
diff --git a/arch/sh/kernel/cpu/sh2a/setup-mxg.c b/arch/sh/kernel/cpu/sh2a/setup-mxg.c
index e611d79..8442937 100644
--- a/arch/sh/kernel/cpu/sh2a/setup-mxg.c
+++ b/arch/sh/kernel/cpu/sh2a/setup-mxg.c
@@ -1,7 +1,7 @@
/*
* Renesas MX-G (R8A03022BG) Setup
*
- * Copyright (C) 2008 Paul Mundt
+ * Copyright (C) 2008, 2009 Paul Mundt
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
@@ -20,23 +20,15 @@
IRQ8, IRQ9, IRQ10, IRQ11, IRQ12, IRQ13, IRQ14, IRQ15,
PINT0, PINT1, PINT2, PINT3, PINT4, PINT5, PINT6, PINT7,
-
SINT8, SINT7, SINT6, SINT5, SINT4, SINT3, SINT2, SINT1,
- SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI,
- SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI,
+ SCIF0, SCIF1,
- MTU2_TGI0A, MTU2_TGI0B, MTU2_TGI0C, MTU2_TGI0D,
- MTU2_TCI0V, MTU2_TGI0E, MTU2_TGI0F,
- MTU2_TGI1A, MTU2_TGI1B, MTU2_TCI1V, MTU2_TCI1U,
- MTU2_TGI2A, MTU2_TGI2B, MTU2_TCI2V, MTU2_TCI2U,
- MTU2_TGI3A, MTU2_TGI3B, MTU2_TGI3C, MTU2_TGI3D, MTU2_TCI3V,
- MTU2_TGI4A, MTU2_TGI4B, MTU2_TGI4C, MTU2_TGI4D, MTU2_TCI4V,
- MTU2_TGI5U, MTU2_TGI5V, MTU2_TGI5W,
+ MTU2_GROUP1, MTU2_GROUP2, MTU2_GROUP3, MTU2_GROUP4, MTU2_GROUP5
+ MTU2_TGI3B, MTU2_TGI3C,
/* interrupt groups */
- PINT, SCIF0, SCIF1,
- MTU2_GROUP1, MTU2_GROUP2, MTU2_GROUP3, MTU2_GROUP4, MTU2_GROUP5
+ PINT,
};
static struct intc_vect vectors[] __initdata = {
@@ -59,47 +51,36 @@
INTC_IRQ(SINT4, 98), INTC_IRQ(SINT3, 99),
INTC_IRQ(SINT2, 100), INTC_IRQ(SINT1, 101),
- INTC_IRQ(SCIF0_RXI, 220), INTC_IRQ(SCIF0_TXI, 221),
- INTC_IRQ(SCIF0_BRI, 222), INTC_IRQ(SCIF0_ERI, 223),
- INTC_IRQ(SCIF1_RXI, 224), INTC_IRQ(SCIF1_TXI, 225),
- INTC_IRQ(SCIF1_BRI, 226), INTC_IRQ(SCIF1_ERI, 227),
+ INTC_IRQ(SCIF0, 220), INTC_IRQ(SCIF0, 221),
+ INTC_IRQ(SCIF0, 222), INTC_IRQ(SCIF0, 223),
+ INTC_IRQ(SCIF1, 224), INTC_IRQ(SCIF1, 225),
+ INTC_IRQ(SCIF1, 226), INTC_IRQ(SCIF1, 227),
- INTC_IRQ(MTU2_TGI0A, 228), INTC_IRQ(MTU2_TGI0B, 229),
- INTC_IRQ(MTU2_TGI0C, 230), INTC_IRQ(MTU2_TGI0D, 231),
- INTC_IRQ(MTU2_TCI0V, 232), INTC_IRQ(MTU2_TGI0E, 233),
+ INTC_IRQ(MTU2_GROUP1, 228), INTC_IRQ(MTU2_GROUP1, 229),
+ INTC_IRQ(MTU2_GROUP1, 230), INTC_IRQ(MTU2_GROUP1, 231),
+ INTC_IRQ(MTU2_GROUP1, 232), INTC_IRQ(MTU2_GROUP1, 233),
- INTC_IRQ(MTU2_TGI0F, 234), INTC_IRQ(MTU2_TGI1A, 235),
- INTC_IRQ(MTU2_TGI1B, 236), INTC_IRQ(MTU2_TCI1V, 237),
- INTC_IRQ(MTU2_TCI1U, 238), INTC_IRQ(MTU2_TGI2A, 239),
+ INTC_IRQ(MTU2_GROUP2, 234), INTC_IRQ(MTU2_GROUP2, 235),
+ INTC_IRQ(MTU2_GROUP2, 236), INTC_IRQ(MTU2_GROUP2, 237),
+ INTC_IRQ(MTU2_GROUP2, 238), INTC_IRQ(MTU2_GROUP2, 239),
- INTC_IRQ(MTU2_TGI2B, 240), INTC_IRQ(MTU2_TCI2V, 241),
- INTC_IRQ(MTU2_TCI2U, 242), INTC_IRQ(MTU2_TGI3A, 243),
+ INTC_IRQ(MTU2_GROUP3, 240), INTC_IRQ(MTU2_GROUP3, 241),
+ INTC_IRQ(MTU2_GROUP3, 242), INTC_IRQ(MTU2_GROUP3, 243),
INTC_IRQ(MTU2_TGI3B, 244),
INTC_IRQ(MTU2_TGI3C, 245),
- INTC_IRQ(MTU2_TGI3D, 246), INTC_IRQ(MTU2_TCI3V, 247),
- INTC_IRQ(MTU2_TGI4A, 248), INTC_IRQ(MTU2_TGI4B, 249),
- INTC_IRQ(MTU2_TGI4C, 250), INTC_IRQ(MTU2_TGI4D, 251),
+ INTC_IRQ(MTU2_GROUP4, 246), INTC_IRQ(MTU2_GROUP4, 247),
+ INTC_IRQ(MTU2_GROUP4, 248), INTC_IRQ(MTU2_GROUP4, 249),
+ INTC_IRQ(MTU2_GROUP4, 250), INTC_IRQ(MTU2_GROUP4, 251),
- INTC_IRQ(MTU2_TCI4V, 252), INTC_IRQ(MTU2_TGI5U, 253),
- INTC_IRQ(MTU2_TGI5V, 254), INTC_IRQ(MTU2_TGI5W, 255),
+ INTC_IRQ(MTU2_GROUP5, 252), INTC_IRQ(MTU2_GROUP5, 253),
+ INTC_IRQ(MTU2_GROUP5, 254), INTC_IRQ(MTU2_GROUP5, 255),
};
static struct intc_group groups[] __initdata = {
INTC_GROUP(PINT, PINT0, PINT1, PINT2, PINT3,
PINT4, PINT5, PINT6, PINT7),
- INTC_GROUP(MTU2_GROUP1, MTU2_TGI0A, MTU2_TGI0B, MTU2_TGI0C, MTU2_TGI0D,
- MTU2_TCI0V, MTU2_TGI0E),
- INTC_GROUP(MTU2_GROUP2, MTU2_TGI0F, MTU2_TGI1A, MTU2_TGI1B,
- MTU2_TCI1V, MTU2_TCI1U, MTU2_TGI2A),
- INTC_GROUP(MTU2_GROUP3, MTU2_TGI2B, MTU2_TCI2V, MTU2_TCI2U,
- MTU2_TGI3A),
- INTC_GROUP(MTU2_GROUP4, MTU2_TGI3D, MTU2_TCI3V, MTU2_TGI4A,
- MTU2_TGI4B, MTU2_TGI4C, MTU2_TGI4D),
- INTC_GROUP(MTU2_GROUP5, MTU2_TCI4V, MTU2_TGI5U, MTU2_TGI5V, MTU2_TGI5W),
- INTC_GROUP(SCIF0, SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI),
- INTC_GROUP(SCIF1, SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI),
};
static struct intc_prio_reg prio_registers[] __initdata = {
@@ -137,7 +118,7 @@
.mapbase = 0xff804000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 223, 220, 221, 222 },
+ .irqs = { 220, 220, 220, 220 },
}, {
.flags = 0,
}
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7201.c b/arch/sh/kernel/cpu/sh2a/setup-sh7201.c
index 0631e42..00f42f9 100644
--- a/arch/sh/kernel/cpu/sh2a/setup-sh7201.c
+++ b/arch/sh/kernel/cpu/sh2a/setup-sh7201.c
@@ -2,6 +2,7 @@
* SH7201 setup
*
* Copyright (C) 2008 Peter Griffin pgriffin@mpc-data.co.uk
+ * Copyright (C) 2009 Paul Mundt
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
@@ -18,57 +19,32 @@
/* interrupt sources */
IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7,
PINT0, PINT1, PINT2, PINT3, PINT4, PINT5, PINT6, PINT7,
+
ADC_ADI,
- MTU2_TGI0A, MTU2_TGI0B, MTU2_TGI0C, MTU2_TGI0D,
- MTU2_TCI0V, MTU2_TGI0E, MTU2_TGI0F,
- MTU2_TGI1A, MTU2_TGI1B, MTU2_TCI1V, MTU2_TCI1U,
- MTU2_TGI2A, MTU2_TGI2B, MTU2_TCI2V, MTU2_TCI2U,
- MTU2_TGI3A, MTU2_TGI3B, MTU2_TGI3C, MTU2_TGI3D, MTU2_TCI3V,
- MTU2_TGI4A, MTU2_TGI4B, MTU2_TGI4C, MTU2_TGI4D, MTU2_TCI4V,
- MTU2_TGI5U, MTU2_TGI5V, MTU2_TGI5W,
- RTC_ARM, RTC_PRD, RTC_CUP,
- WDT,
- IIC30_STPI, IIC30_NAKI, IIC30_RXI, IIC30_TXI, IIC30_TEI,
- IIC31_STPI, IIC31_NAKI, IIC31_RXI, IIC31_TXI, IIC31_TEI,
- IIC32_STPI, IIC32_NAKI, IIC32_RXI, IIC32_TXI, IIC32_TEI,
+
+ MTU20_ABCD, MTU20_VEF, MTU21_AB, MTU21_VU, MTU22_AB, MTU22_VU,
+ MTU23_ABCD, MTU24_ABCD, MTU25_UVW, MTU2_TCI3V, MTU2_TCI4V,
+
+ RTC, WDT,
+
+ IIC30, IIC31, IIC32,
DMAC0_DMINT0, DMAC1_DMINT1,
DMAC2_DMINT2, DMAC3_DMINT3,
- SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI,
- SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI,
- SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI,
- SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI,
- SCIF4_BRI, SCIF4_ERI, SCIF4_RXI, SCIF4_TXI,
- SCIF5_BRI, SCIF5_ERI, SCIF5_RXI, SCIF5_TXI,
- SCIF6_BRI, SCIF6_ERI, SCIF6_RXI, SCIF6_TXI,
- SCIF7_BRI, SCIF7_ERI, SCIF7_RXI, SCIF7_TXI,
+ SCIF0, SCIF1, SCIF2, SCIF3, SCIF4, SCIF5, SCIF6, SCIF7,
DMAC0_DMINTA, DMAC4_DMINT4, DMAC5_DMINT5, DMAC6_DMINT6,
DMAC7_DMINT7,
- RCAN0_ERS, RCAN0_OVR,
- RCAN0_SLE,
- RCAN0_RM0, RCAN0_RM1,
-
- RCAN1_ERS, RCAN1_OVR,
- RCAN1_SLE,
- RCAN1_RM0, RCAN1_RM1,
+ RCAN0, RCAN1,
SSI0_SSII, SSI1_SSII,
- TMR0_CMIA0, TMR0_CMIB0, TMR0_OVI0,
- TMR1_CMIA1, TMR1_CMIB1, TMR1_OVI1,
+ TMR0, TMR1,
/* interrupt groups */
-
- IRQ, PINT, ADC,
- MTU20_ABCD, MTU20_VEF, MTU21_AB, MTU21_VU, MTU22_AB, MTU22_VU,
- MTU23_ABCD, MTU24_ABCD, MTU25_UVW,
- RTC, IIC30, IIC31, IIC32,
- SCIF0, SCIF1, SCIF2, SCIF3, SCIF4, SCIF5, SCIF6, SCIF7,
- RCAN0, RCAN1, TMR0, TMR1
-
+ PINT,
};
static struct intc_vect vectors[] __initdata = {
@@ -76,6 +52,7 @@
INTC_IRQ(IRQ2, 66), INTC_IRQ(IRQ3, 67),
INTC_IRQ(IRQ4, 68), INTC_IRQ(IRQ5, 69),
INTC_IRQ(IRQ6, 70), INTC_IRQ(IRQ7, 71),
+
INTC_IRQ(PINT0, 80), INTC_IRQ(PINT1, 81),
INTC_IRQ(PINT2, 82), INTC_IRQ(PINT3, 83),
INTC_IRQ(PINT4, 84), INTC_IRQ(PINT5, 85),
@@ -83,123 +60,92 @@
INTC_IRQ(ADC_ADI, 92),
- INTC_IRQ(MTU2_TGI0A, 108), INTC_IRQ(MTU2_TGI0B, 109),
- INTC_IRQ(MTU2_TGI0C, 110), INTC_IRQ(MTU2_TGI0D, 111),
- INTC_IRQ(MTU2_TCI0V, 112),
- INTC_IRQ(MTU2_TGI0E, 113), INTC_IRQ(MTU2_TGI0F, 114),
+ INTC_IRQ(MTU20_ABCD, 108), INTC_IRQ(MTU20_ABCD, 109),
+ INTC_IRQ(MTU20_ABCD, 110), INTC_IRQ(MTU20_ABCD, 111),
- INTC_IRQ(MTU2_TGI1A, 116), INTC_IRQ(MTU2_TGI1B, 117),
- INTC_IRQ(MTU2_TCI1V, 120), INTC_IRQ(MTU2_TCI1U, 121),
+ INTC_IRQ(MTU20_VEF, 112), INTC_IRQ(MTU20_VEF, 113),
+ INTC_IRQ(MTU20_VEF, 114),
- INTC_IRQ(MTU2_TGI2A, 124), INTC_IRQ(MTU2_TGI2B, 125),
- INTC_IRQ(MTU2_TCI2V, 128), INTC_IRQ(MTU2_TCI2U, 129),
+ INTC_IRQ(MTU21_AB, 116), INTC_IRQ(MTU21_AB, 117),
+ INTC_IRQ(MTU21_VU, 120), INTC_IRQ(MTU21_VU, 121),
- INTC_IRQ(MTU2_TGI3A, 132), INTC_IRQ(MTU2_TGI3B, 133),
- INTC_IRQ(MTU2_TGI3C, 134), INTC_IRQ(MTU2_TGI3D, 135),
+ INTC_IRQ(MTU22_AB, 124), INTC_IRQ(MTU22_AB, 125),
+ INTC_IRQ(MTU22_VU, 128), INTC_IRQ(MTU22_VU, 129),
+
+ INTC_IRQ(MTU23_ABCD, 132), INTC_IRQ(MTU23_ABCD, 133),
+ INTC_IRQ(MTU23_ABCD, 134), INTC_IRQ(MTU23_ABCD, 135),
+
INTC_IRQ(MTU2_TCI3V, 136),
- INTC_IRQ(MTU2_TGI4A, 140), INTC_IRQ(MTU2_TGI4B, 141),
- INTC_IRQ(MTU2_TGI4C, 142), INTC_IRQ(MTU2_TGI4D, 143),
+ INTC_IRQ(MTU24_ABCD, 140), INTC_IRQ(MTU24_ABCD, 141),
+ INTC_IRQ(MTU24_ABCD, 142), INTC_IRQ(MTU24_ABCD, 143),
+
INTC_IRQ(MTU2_TCI4V, 144),
- INTC_IRQ(MTU2_TGI5U, 148), INTC_IRQ(MTU2_TGI5V, 149),
- INTC_IRQ(MTU2_TGI5W, 150),
+ INTC_IRQ(MTU25_UVW, 148), INTC_IRQ(MTU25_UVW, 149),
+ INTC_IRQ(MTU25_UVW, 150),
- INTC_IRQ(RTC_ARM, 152), INTC_IRQ(RTC_PRD, 153),
- INTC_IRQ(RTC_CUP, 154), INTC_IRQ(WDT, 156),
+ INTC_IRQ(RTC, 152), INTC_IRQ(RTC, 153),
+ INTC_IRQ(RTC, 154),
- INTC_IRQ(IIC30_STPI, 157), INTC_IRQ(IIC30_NAKI, 158),
- INTC_IRQ(IIC30_RXI, 159), INTC_IRQ(IIC30_TXI, 160),
- INTC_IRQ(IIC30_TEI, 161),
+ INTC_IRQ(WDT, 156),
- INTC_IRQ(IIC31_STPI, 164), INTC_IRQ(IIC31_NAKI, 165),
- INTC_IRQ(IIC31_RXI, 166), INTC_IRQ(IIC31_TXI, 167),
- INTC_IRQ(IIC31_TEI, 168),
+ INTC_IRQ(IIC30, 157), INTC_IRQ(IIC30, 158),
+ INTC_IRQ(IIC30, 159), INTC_IRQ(IIC30, 160),
+ INTC_IRQ(IIC30, 161),
- INTC_IRQ(IIC32_STPI, 170), INTC_IRQ(IIC32_NAKI, 171),
- INTC_IRQ(IIC32_RXI, 172), INTC_IRQ(IIC32_TXI, 173),
- INTC_IRQ(IIC32_TEI, 174),
+ INTC_IRQ(IIC31, 164), INTC_IRQ(IIC31, 165),
+ INTC_IRQ(IIC31, 166), INTC_IRQ(IIC31, 167),
+ INTC_IRQ(IIC31, 168),
+
+ INTC_IRQ(IIC32, 170), INTC_IRQ(IIC32, 171),
+ INTC_IRQ(IIC32, 172), INTC_IRQ(IIC32, 173),
+ INTC_IRQ(IIC32, 174),
INTC_IRQ(DMAC0_DMINT0, 176), INTC_IRQ(DMAC1_DMINT1, 177),
INTC_IRQ(DMAC2_DMINT2, 178), INTC_IRQ(DMAC3_DMINT3, 179),
- INTC_IRQ(SCIF0_BRI, 180), INTC_IRQ(SCIF0_ERI, 181),
- INTC_IRQ(SCIF0_RXI, 182), INTC_IRQ(SCIF0_TXI, 183),
- INTC_IRQ(SCIF1_BRI, 184), INTC_IRQ(SCIF1_ERI, 185),
- INTC_IRQ(SCIF1_RXI, 186), INTC_IRQ(SCIF1_TXI, 187),
- INTC_IRQ(SCIF2_BRI, 188), INTC_IRQ(SCIF2_ERI, 189),
- INTC_IRQ(SCIF2_RXI, 190), INTC_IRQ(SCIF2_TXI, 191),
- INTC_IRQ(SCIF3_BRI, 192), INTC_IRQ(SCIF3_ERI, 193),
- INTC_IRQ(SCIF3_RXI, 194), INTC_IRQ(SCIF3_TXI, 195),
- INTC_IRQ(SCIF4_BRI, 196), INTC_IRQ(SCIF4_ERI, 197),
- INTC_IRQ(SCIF4_RXI, 198), INTC_IRQ(SCIF4_TXI, 199),
- INTC_IRQ(SCIF5_BRI, 200), INTC_IRQ(SCIF5_ERI, 201),
- INTC_IRQ(SCIF5_RXI, 202), INTC_IRQ(SCIF5_TXI, 203),
- INTC_IRQ(SCIF6_BRI, 204), INTC_IRQ(SCIF6_ERI, 205),
- INTC_IRQ(SCIF6_RXI, 206), INTC_IRQ(SCIF6_TXI, 207),
- INTC_IRQ(SCIF7_BRI, 208), INTC_IRQ(SCIF7_ERI, 209),
- INTC_IRQ(SCIF7_RXI, 210), INTC_IRQ(SCIF7_TXI, 211),
+ INTC_IRQ(SCIF0, 180), INTC_IRQ(SCIF0, 181),
+ INTC_IRQ(SCIF0, 182), INTC_IRQ(SCIF0, 183),
+ INTC_IRQ(SCIF1, 184), INTC_IRQ(SCIF1, 185),
+ INTC_IRQ(SCIF1, 186), INTC_IRQ(SCIF1, 187),
+ INTC_IRQ(SCIF2, 188), INTC_IRQ(SCIF2, 189),
+ INTC_IRQ(SCIF2, 190), INTC_IRQ(SCIF2, 191),
+ INTC_IRQ(SCIF3, 192), INTC_IRQ(SCIF3, 193),
+ INTC_IRQ(SCIF3, 194), INTC_IRQ(SCIF3, 195),
+ INTC_IRQ(SCIF4, 196), INTC_IRQ(SCIF4, 197),
+ INTC_IRQ(SCIF4, 198), INTC_IRQ(SCIF4, 199),
+ INTC_IRQ(SCIF5, 200), INTC_IRQ(SCIF5, 201),
+ INTC_IRQ(SCIF5, 202), INTC_IRQ(SCIF5, 203),
+ INTC_IRQ(SCIF6, 204), INTC_IRQ(SCIF6, 205),
+ INTC_IRQ(SCIF6, 206), INTC_IRQ(SCIF6, 207),
+ INTC_IRQ(SCIF7, 208), INTC_IRQ(SCIF7, 209),
+ INTC_IRQ(SCIF7, 210), INTC_IRQ(SCIF7, 211),
INTC_IRQ(DMAC0_DMINTA, 212), INTC_IRQ(DMAC4_DMINT4, 216),
INTC_IRQ(DMAC5_DMINT5, 217), INTC_IRQ(DMAC6_DMINT6, 218),
INTC_IRQ(DMAC7_DMINT7, 219),
- INTC_IRQ(RCAN0_ERS, 228), INTC_IRQ(RCAN0_OVR, 229),
- INTC_IRQ(RCAN0_SLE, 230),
- INTC_IRQ(RCAN0_RM0, 231), INTC_IRQ(RCAN0_RM1, 232),
+ INTC_IRQ(RCAN0, 228), INTC_IRQ(RCAN0, 229),
+ INTC_IRQ(RCAN0, 230),
+ INTC_IRQ(RCAN0, 231), INTC_IRQ(RCAN0, 232),
- INTC_IRQ(RCAN1_ERS, 234), INTC_IRQ(RCAN1_OVR, 235),
- INTC_IRQ(RCAN1_SLE, 236),
- INTC_IRQ(RCAN1_RM0, 237), INTC_IRQ(RCAN1_RM1, 238),
+ INTC_IRQ(RCAN1, 234), INTC_IRQ(RCAN1, 235),
+ INTC_IRQ(RCAN1, 236),
+ INTC_IRQ(RCAN1, 237), INTC_IRQ(RCAN1, 238),
INTC_IRQ(SSI0_SSII, 244), INTC_IRQ(SSI1_SSII, 245),
- INTC_IRQ(TMR0_CMIA0, 246), INTC_IRQ(TMR0_CMIB0, 247),
- INTC_IRQ(TMR0_OVI0, 248),
+ INTC_IRQ(TMR0, 246), INTC_IRQ(TMR0, 247),
+ INTC_IRQ(TMR0, 248),
- INTC_IRQ(TMR1_CMIA1, 252), INTC_IRQ(TMR1_CMIB1, 253),
- INTC_IRQ(TMR1_OVI1, 254),
-
+ INTC_IRQ(TMR1, 252), INTC_IRQ(TMR1, 253),
+ INTC_IRQ(TMR1, 254),
};
static struct intc_group groups[] __initdata = {
INTC_GROUP(PINT, PINT0, PINT1, PINT2, PINT3,
PINT4, PINT5, PINT6, PINT7),
- INTC_GROUP(MTU20_ABCD, MTU2_TGI0A, MTU2_TGI0B, MTU2_TGI0C, MTU2_TGI0D),
- INTC_GROUP(MTU20_VEF, MTU2_TCI0V, MTU2_TGI0E, MTU2_TGI0F),
-
- INTC_GROUP(MTU21_AB, MTU2_TGI1A, MTU2_TGI1B),
- INTC_GROUP(MTU21_VU, MTU2_TCI1V, MTU2_TCI1U),
- INTC_GROUP(MTU22_AB, MTU2_TGI2A, MTU2_TGI2B),
- INTC_GROUP(MTU22_VU, MTU2_TCI2V, MTU2_TCI2U),
- INTC_GROUP(MTU23_ABCD, MTU2_TGI3A, MTU2_TGI3B, MTU2_TGI3C, MTU2_TGI3D),
- INTC_GROUP(MTU24_ABCD, MTU2_TGI4A, MTU2_TGI4B, MTU2_TGI4C, MTU2_TGI4D),
- INTC_GROUP(MTU25_UVW, MTU2_TGI5U, MTU2_TGI5V, MTU2_TGI5W),
- INTC_GROUP(RTC, RTC_ARM, RTC_PRD, RTC_CUP ),
-
- INTC_GROUP(IIC30, IIC30_STPI, IIC30_NAKI, IIC30_RXI, IIC30_TXI,
- IIC30_TEI),
- INTC_GROUP(IIC31, IIC31_STPI, IIC31_NAKI, IIC31_RXI, IIC31_TXI,
- IIC31_TEI),
- INTC_GROUP(IIC32, IIC32_STPI, IIC32_NAKI, IIC32_RXI, IIC32_TXI,
- IIC32_TEI),
-
- INTC_GROUP(SCIF0, SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI),
- INTC_GROUP(SCIF1, SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI),
- INTC_GROUP(SCIF2, SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI),
- INTC_GROUP(SCIF3, SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI),
- INTC_GROUP(SCIF4, SCIF4_BRI, SCIF4_ERI, SCIF4_RXI, SCIF4_TXI),
- INTC_GROUP(SCIF5, SCIF5_BRI, SCIF5_ERI, SCIF5_RXI, SCIF5_TXI),
- INTC_GROUP(SCIF6, SCIF6_BRI, SCIF6_ERI, SCIF6_RXI, SCIF6_TXI),
- INTC_GROUP(SCIF7, SCIF7_BRI, SCIF7_ERI, SCIF7_RXI, SCIF7_TXI),
-
- INTC_GROUP(RCAN0, RCAN0_ERS, RCAN0_OVR, RCAN0_RM0, RCAN0_RM1,
- RCAN0_SLE),
- INTC_GROUP(RCAN1, RCAN1_ERS, RCAN1_OVR, RCAN1_RM0, RCAN1_RM1,
- RCAN1_SLE),
-
- INTC_GROUP(TMR0, TMR0_CMIA0, TMR0_CMIB0, TMR0_OVI0),
- INTC_GROUP(TMR1, TMR1_CMIA1, TMR1_CMIB1, TMR1_OVI1),
};
static struct intc_prio_reg prio_registers[] __initdata = {
@@ -212,7 +158,7 @@
{ 0xfffe9806, 0, 16, 4, /* IPR09 */ { RTC, WDT, IIC30, 0 } },
{ 0xfffe9808, 0, 16, 4, /* IPR10 */ { IIC31, IIC32, DMAC0_DMINT0, DMAC1_DMINT1 } },
- { 0xfffe980a, 0, 16, 4, /* IPR11 */ { DMAC2_DMINT2, DMAC3_DMINT3, SCIF0 , SCIF1 } },
+ { 0xfffe980a, 0, 16, 4, /* IPR11 */ { DMAC2_DMINT2, DMAC3_DMINT3, SCIF0, SCIF1 } },
{ 0xfffe980c, 0, 16, 4, /* IPR12 */ { SCIF2, SCIF3, SCIF4, SCIF5 } },
{ 0xfffe980e, 0, 16, 4, /* IPR13 */ { SCIF6, SCIF7, DMAC0_DMINTA, DMAC4_DMINT4 } },
{ 0xfffe9810, 0, 16, 4, /* IPR14 */ { DMAC5_DMINT5, DMAC6_DMINT6, DMAC7_DMINT7, 0 } },
@@ -234,42 +180,42 @@
.mapbase = 0xfffe8000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 181, 182, 183, 180}
+ .irqs = { 180, 180, 180, 180 }
}, {
.mapbase = 0xfffe8800,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 185, 186, 187, 184}
+ .irqs = { 184, 184, 184, 184 }
}, {
.mapbase = 0xfffe9000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 189, 186, 187, 188}
+ .irqs = { 188, 188, 188, 188 }
}, {
.mapbase = 0xfffe9800,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 193, 194, 195, 192}
+ .irqs = { 192, 192, 192, 192 }
}, {
.mapbase = 0xfffea000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 196, 198, 199, 196}
+ .irqs = { 196, 196, 196, 196 }
}, {
.mapbase = 0xfffea800,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 201, 202, 203, 200}
+ .irqs = { 200, 200, 200, 200 }
}, {
.mapbase = 0xfffeb000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 205, 206, 207, 204}
+ .irqs = { 204, 204, 204, 204 }
}, {
.mapbase = 0xfffeb800,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 209, 210, 211, 208}
+ .irqs = { 208, 208, 208, 208 }
}, {
.flags = 0,
}
@@ -290,17 +236,7 @@
.flags = IORESOURCE_IO,
},
[1] = {
- /* Period IRQ */
- .start = 153,
- .flags = IORESOURCE_IRQ,
- },
- [2] = {
- /* Carry IRQ */
- .start = 154,
- .flags = IORESOURCE_IRQ,
- },
- [3] = {
- /* Alarm IRQ */
+ /* Shared Period/Carry/Alarm IRQ */
.start = 152,
.flags = IORESOURCE_IRQ,
},
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7203.c b/arch/sh/kernel/cpu/sh2a/setup-sh7203.c
index e98dc44..820dfb2 100644
--- a/arch/sh/kernel/cpu/sh2a/setup-sh7203.c
+++ b/arch/sh/kernel/cpu/sh2a/setup-sh7203.c
@@ -1,7 +1,7 @@
/*
* SH7203 and SH7263 Setup
*
- * Copyright (C) 2007 Paul Mundt
+ * Copyright (C) 2007 - 2009 Paul Mundt
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
@@ -18,50 +18,27 @@
/* interrupt sources */
IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7,
PINT0, PINT1, PINT2, PINT3, PINT4, PINT5, PINT6, PINT7,
- DMAC0_DEI, DMAC0_HEI, DMAC1_DEI, DMAC1_HEI,
- DMAC2_DEI, DMAC2_HEI, DMAC3_DEI, DMAC3_HEI,
- DMAC4_DEI, DMAC4_HEI, DMAC5_DEI, DMAC5_HEI,
- DMAC6_DEI, DMAC6_HEI, DMAC7_DEI, DMAC7_HEI,
+ DMAC0, DMAC1, DMAC2, DMAC3, DMAC4, DMAC5, DMAC6, DMAC7,
USB, LCDC, CMT0, CMT1, BSC, WDT,
- MTU2_TGI0A, MTU2_TGI0B, MTU2_TGI0C, MTU2_TGI0D,
- MTU2_TCI0V, MTU2_TGI0E, MTU2_TGI0F,
- MTU2_TGI1A, MTU2_TGI1B, MTU2_TCI1V, MTU2_TCI1U,
- MTU2_TGI2A, MTU2_TGI2B, MTU2_TCI2V, MTU2_TCI2U,
- MTU2_TGI3A, MTU2_TGI3B, MTU2_TGI3C, MTU2_TGI3D, MTU2_TCI3V,
- MTU2_TGI4A, MTU2_TGI4B, MTU2_TGI4C, MTU2_TGI4D, MTU2_TCI4V,
+
+ MTU0_ABCD, MTU0_VEF, MTU1_AB, MTU1_VU, MTU2_AB, MTU2_VU,
+ MTU3_ABCD, MTU4_ABCD, MTU2_TCI3V, MTU2_TCI4V,
+
ADC_ADI,
- IIC30_STPI, IIC30_NAKI, IIC30_RXI, IIC30_TXI, IIC30_TEI,
- IIC31_STPI, IIC31_NAKI, IIC31_RXI, IIC31_TXI, IIC31_TEI,
- IIC32_STPI, IIC32_NAKI, IIC32_RXI, IIC32_TXI, IIC32_TEI,
- IIC33_STPI, IIC33_NAKI, IIC33_RXI, IIC33_TXI, IIC33_TEI,
- SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI,
- SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI,
- SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI,
- SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI,
- SSU0_SSERI, SSU0_SSRXI, SSU0_SSTXI,
- SSU1_SSERI, SSU1_SSRXI, SSU1_SSTXI,
+
+ IIC30, IIC31, IIC32, IIC33,
+ SCIF0, SCIF1, SCIF2, SCIF3,
+
+ SSU0, SSU1,
+
SSI0_SSII, SSI1_SSII, SSI2_SSII, SSI3_SSII,
/* ROM-DEC, SDHI, SRC, and IEB are SH7263 specific */
- ROMDEC_ISY, ROMDEC_IERR, ROMDEC_IARG, ROMDEC_ISEC, ROMDEC_IBUF,
- ROMDEC_IREADY,
-
- FLCTL_FLSTEI, FLCTL_FLTENDI, FLCTL_FLTREQ0I, FLCTL_FLTREQ1I,
-
- SDHI3, SDHI0, SDHI1,
-
- RTC_ARM, RTC_PRD, RTC_CUP,
- RCAN0_ERS, RCAN0_OVR, RCAN0_RM0, RCAN0_RM1, RCAN0_SLE,
- RCAN1_ERS, RCAN1_OVR, RCAN1_RM0, RCAN1_RM1, RCAN1_SLE,
-
- SRC_OVF, SRC_ODFI, SRC_IDEI, IEBI,
+ ROMDEC, FLCTL, SDHI, RTC, RCAN0, RCAN1,
+ SRC, IEBI,
/* interrupt groups */
- PINT, DMAC0, DMAC1, DMAC2, DMAC3, DMAC4, DMAC5, DMAC6, DMAC7,
- MTU0_ABCD, MTU0_VEF, MTU1_AB, MTU1_VU, MTU2_AB, MTU2_VU,
- MTU3_ABCD, MTU4_ABCD,
- IIC30, IIC31, IIC32, IIC33, SCIF0, SCIF1, SCIF2, SCIF3,
- SSU0, SSU1, ROMDEC, SDHI, FLCTL, RTC, RCAN0, RCAN1, SRC
+ PINT,
};
static struct intc_vect vectors[] __initdata = {
@@ -73,79 +50,80 @@
INTC_IRQ(PINT2, 82), INTC_IRQ(PINT3, 83),
INTC_IRQ(PINT4, 84), INTC_IRQ(PINT5, 85),
INTC_IRQ(PINT6, 86), INTC_IRQ(PINT7, 87),
- INTC_IRQ(DMAC0_DEI, 108), INTC_IRQ(DMAC0_HEI, 109),
- INTC_IRQ(DMAC1_DEI, 112), INTC_IRQ(DMAC1_HEI, 113),
- INTC_IRQ(DMAC2_DEI, 116), INTC_IRQ(DMAC2_HEI, 117),
- INTC_IRQ(DMAC3_DEI, 120), INTC_IRQ(DMAC3_HEI, 121),
- INTC_IRQ(DMAC4_DEI, 124), INTC_IRQ(DMAC4_HEI, 125),
- INTC_IRQ(DMAC5_DEI, 128), INTC_IRQ(DMAC5_HEI, 129),
- INTC_IRQ(DMAC6_DEI, 132), INTC_IRQ(DMAC6_HEI, 133),
- INTC_IRQ(DMAC7_DEI, 136), INTC_IRQ(DMAC7_HEI, 137),
+ INTC_IRQ(DMAC0, 108), INTC_IRQ(DMAC0, 109),
+ INTC_IRQ(DMAC1, 112), INTC_IRQ(DMAC1, 113),
+ INTC_IRQ(DMAC2, 116), INTC_IRQ(DMAC2, 117),
+ INTC_IRQ(DMAC3, 120), INTC_IRQ(DMAC3, 121),
+ INTC_IRQ(DMAC4, 124), INTC_IRQ(DMAC4, 125),
+ INTC_IRQ(DMAC5, 128), INTC_IRQ(DMAC5, 129),
+ INTC_IRQ(DMAC6, 132), INTC_IRQ(DMAC6, 133),
+ INTC_IRQ(DMAC7, 136), INTC_IRQ(DMAC7, 137),
INTC_IRQ(USB, 140), INTC_IRQ(LCDC, 141),
INTC_IRQ(CMT0, 142), INTC_IRQ(CMT1, 143),
INTC_IRQ(BSC, 144), INTC_IRQ(WDT, 145),
- INTC_IRQ(MTU2_TGI0A, 146), INTC_IRQ(MTU2_TGI0B, 147),
- INTC_IRQ(MTU2_TGI0C, 148), INTC_IRQ(MTU2_TGI0D, 149),
- INTC_IRQ(MTU2_TCI0V, 150),
- INTC_IRQ(MTU2_TGI0E, 151), INTC_IRQ(MTU2_TGI0F, 152),
- INTC_IRQ(MTU2_TGI1A, 153), INTC_IRQ(MTU2_TGI1B, 154),
- INTC_IRQ(MTU2_TCI1V, 155), INTC_IRQ(MTU2_TCI1U, 156),
- INTC_IRQ(MTU2_TGI2A, 157), INTC_IRQ(MTU2_TGI2B, 158),
- INTC_IRQ(MTU2_TCI2V, 159), INTC_IRQ(MTU2_TCI2U, 160),
- INTC_IRQ(MTU2_TGI3A, 161), INTC_IRQ(MTU2_TGI3B, 162),
- INTC_IRQ(MTU2_TGI3C, 163), INTC_IRQ(MTU2_TGI3D, 164),
+ INTC_IRQ(MTU0_ABCD, 146), INTC_IRQ(MTU0_ABCD, 147),
+ INTC_IRQ(MTU0_ABCD, 148), INTC_IRQ(MTU0_ABCD, 149),
+ INTC_IRQ(MTU0_VEF, 150),
+ INTC_IRQ(MTU0_VEF, 151), INTC_IRQ(MTU0_VEF, 152),
+ INTC_IRQ(MTU1_AB, 153), INTC_IRQ(MTU1_AB, 154),
+ INTC_IRQ(MTU1_VU, 155), INTC_IRQ(MTU1_VU, 156),
+ INTC_IRQ(MTU2_AB, 157), INTC_IRQ(MTU2_AB, 158),
+ INTC_IRQ(MTU2_VU, 159), INTC_IRQ(MTU2_VU, 160),
+ INTC_IRQ(MTU3_ABCD, 161), INTC_IRQ(MTU3_ABCD, 162),
+ INTC_IRQ(MTU3_ABCD, 163), INTC_IRQ(MTU3_ABCD, 164),
INTC_IRQ(MTU2_TCI3V, 165),
- INTC_IRQ(MTU2_TGI4A, 166), INTC_IRQ(MTU2_TGI4B, 167),
- INTC_IRQ(MTU2_TGI4C, 168), INTC_IRQ(MTU2_TGI4D, 169),
+ INTC_IRQ(MTU4_ABCD, 166), INTC_IRQ(MTU4_ABCD, 167),
+ INTC_IRQ(MTU4_ABCD, 168), INTC_IRQ(MTU4_ABCD, 169),
INTC_IRQ(MTU2_TCI4V, 170),
INTC_IRQ(ADC_ADI, 171),
- INTC_IRQ(IIC30_STPI, 172), INTC_IRQ(IIC30_NAKI, 173),
- INTC_IRQ(IIC30_RXI, 174), INTC_IRQ(IIC30_TXI, 175),
- INTC_IRQ(IIC30_TEI, 176),
- INTC_IRQ(IIC31_STPI, 177), INTC_IRQ(IIC31_NAKI, 178),
- INTC_IRQ(IIC31_RXI, 179), INTC_IRQ(IIC31_TXI, 180),
- INTC_IRQ(IIC31_TEI, 181),
- INTC_IRQ(IIC32_STPI, 182), INTC_IRQ(IIC32_NAKI, 183),
- INTC_IRQ(IIC32_RXI, 184), INTC_IRQ(IIC32_TXI, 185),
- INTC_IRQ(IIC32_TEI, 186),
- INTC_IRQ(IIC33_STPI, 187), INTC_IRQ(IIC33_NAKI, 188),
- INTC_IRQ(IIC33_RXI, 189), INTC_IRQ(IIC33_TXI, 190),
- INTC_IRQ(IIC33_TEI, 191),
- INTC_IRQ(SCIF0_BRI, 192), INTC_IRQ(SCIF0_ERI, 193),
- INTC_IRQ(SCIF0_RXI, 194), INTC_IRQ(SCIF0_TXI, 195),
- INTC_IRQ(SCIF1_BRI, 196), INTC_IRQ(SCIF1_ERI, 197),
- INTC_IRQ(SCIF1_RXI, 198), INTC_IRQ(SCIF1_TXI, 199),
- INTC_IRQ(SCIF2_BRI, 200), INTC_IRQ(SCIF2_ERI, 201),
- INTC_IRQ(SCIF2_RXI, 202), INTC_IRQ(SCIF2_TXI, 203),
- INTC_IRQ(SCIF3_BRI, 204), INTC_IRQ(SCIF3_ERI, 205),
- INTC_IRQ(SCIF3_RXI, 206), INTC_IRQ(SCIF3_TXI, 207),
- INTC_IRQ(SSU0_SSERI, 208), INTC_IRQ(SSU0_SSRXI, 209),
- INTC_IRQ(SSU0_SSTXI, 210),
- INTC_IRQ(SSU1_SSERI, 211), INTC_IRQ(SSU1_SSRXI, 212),
- INTC_IRQ(SSU1_SSTXI, 213),
+ INTC_IRQ(IIC30, 172), INTC_IRQ(IIC30, 173),
+ INTC_IRQ(IIC30, 174), INTC_IRQ(IIC30, 175),
+ INTC_IRQ(IIC30, 176),
+ INTC_IRQ(IIC31, 177), INTC_IRQ(IIC31, 178),
+ INTC_IRQ(IIC31, 179), INTC_IRQ(IIC31, 180),
+ INTC_IRQ(IIC31, 181),
+ INTC_IRQ(IIC32, 182), INTC_IRQ(IIC32, 183),
+ INTC_IRQ(IIC32, 184), INTC_IRQ(IIC32, 185),
+ INTC_IRQ(IIC32, 186),
+ INTC_IRQ(IIC33, 187), INTC_IRQ(IIC33, 188),
+ INTC_IRQ(IIC33, 189), INTC_IRQ(IIC33, 190),
+ INTC_IRQ(IIC33, 191),
+ INTC_IRQ(SCIF0, 192), INTC_IRQ(SCIF0, 193),
+ INTC_IRQ(SCIF0, 194), INTC_IRQ(SCIF0, 195),
+ INTC_IRQ(SCIF1, 196), INTC_IRQ(SCIF1, 197),
+ INTC_IRQ(SCIF1, 198), INTC_IRQ(SCIF1, 199),
+ INTC_IRQ(SCIF2, 200), INTC_IRQ(SCIF2, 201),
+ INTC_IRQ(SCIF2, 202), INTC_IRQ(SCIF2, 203),
+ INTC_IRQ(SCIF3, 204), INTC_IRQ(SCIF3, 205),
+ INTC_IRQ(SCIF3, 206), INTC_IRQ(SCIF3, 207),
+ INTC_IRQ(SSU0, 208), INTC_IRQ(SSU0, 209),
+ INTC_IRQ(SSU0, 210),
+ INTC_IRQ(SSU1, 211), INTC_IRQ(SSU1, 212),
+ INTC_IRQ(SSU1, 213),
INTC_IRQ(SSI0_SSII, 214), INTC_IRQ(SSI1_SSII, 215),
INTC_IRQ(SSI2_SSII, 216), INTC_IRQ(SSI3_SSII, 217),
- INTC_IRQ(FLCTL_FLSTEI, 224), INTC_IRQ(FLCTL_FLTENDI, 225),
- INTC_IRQ(FLCTL_FLTREQ0I, 226), INTC_IRQ(FLCTL_FLTREQ1I, 227),
- INTC_IRQ(RTC_ARM, 231), INTC_IRQ(RTC_PRD, 232),
- INTC_IRQ(RTC_CUP, 233),
- INTC_IRQ(RCAN0_ERS, 234), INTC_IRQ(RCAN0_OVR, 235),
- INTC_IRQ(RCAN0_RM0, 236), INTC_IRQ(RCAN0_RM1, 237),
- INTC_IRQ(RCAN0_SLE, 238),
- INTC_IRQ(RCAN1_ERS, 239), INTC_IRQ(RCAN1_OVR, 240),
- INTC_IRQ(RCAN1_RM0, 241), INTC_IRQ(RCAN1_RM1, 242),
- INTC_IRQ(RCAN1_SLE, 243),
+ INTC_IRQ(FLCTL, 224), INTC_IRQ(FLCTL, 225),
+ INTC_IRQ(FLCTL, 226), INTC_IRQ(FLCTL, 227),
+ INTC_IRQ(RTC, 231), INTC_IRQ(RTC, 232),
+ INTC_IRQ(RTC, 233),
+ INTC_IRQ(RCAN0, 234), INTC_IRQ(RCAN0, 235),
+ INTC_IRQ(RCAN0, 236), INTC_IRQ(RCAN0, 237),
+ INTC_IRQ(RCAN0, 238),
+ INTC_IRQ(RCAN1, 239), INTC_IRQ(RCAN1, 240),
+ INTC_IRQ(RCAN1, 241), INTC_IRQ(RCAN1, 242),
+ INTC_IRQ(RCAN1, 243),
/* SH7263-specific trash */
#ifdef CONFIG_CPU_SUBTYPE_SH7263
- INTC_IRQ(ROMDEC_ISY, 218), INTC_IRQ(ROMDEC_IERR, 219),
- INTC_IRQ(ROMDEC_IARG, 220), INTC_IRQ(ROMDEC_ISEC, 221),
- INTC_IRQ(ROMDEC_IBUF, 222), INTC_IRQ(ROMDEC_IREADY, 223),
+ INTC_IRQ(ROMDEC, 218), INTC_IRQ(ROMDEC, 219),
+ INTC_IRQ(ROMDEC, 220), INTC_IRQ(ROMDEC, 221),
+ INTC_IRQ(ROMDEC, 222), INTC_IRQ(ROMDEC, 223),
- INTC_IRQ(SDHI3, 228), INTC_IRQ(SDHI0, 229), INTC_IRQ(SDHI1, 230),
+ INTC_IRQ(SDHI, 228), INTC_IRQ(SDHI, 229),
+ INTC_IRQ(SDHI, 230),
- INTC_IRQ(SRC_OVF, 244), INTC_IRQ(SRC_ODFI, 245),
- INTC_IRQ(SRC_IDEI, 246),
+ INTC_IRQ(SRC, 244), INTC_IRQ(SRC, 245),
+ INTC_IRQ(SRC, 246),
INTC_IRQ(IEBI, 247),
#endif
@@ -154,50 +132,6 @@
static struct intc_group groups[] __initdata = {
INTC_GROUP(PINT, PINT0, PINT1, PINT2, PINT3,
PINT4, PINT5, PINT6, PINT7),
- INTC_GROUP(DMAC0, DMAC0_DEI, DMAC0_HEI),
- INTC_GROUP(DMAC1, DMAC1_DEI, DMAC1_HEI),
- INTC_GROUP(DMAC2, DMAC2_DEI, DMAC2_HEI),
- INTC_GROUP(DMAC3, DMAC3_DEI, DMAC3_HEI),
- INTC_GROUP(DMAC4, DMAC4_DEI, DMAC4_HEI),
- INTC_GROUP(DMAC5, DMAC5_DEI, DMAC5_HEI),
- INTC_GROUP(DMAC6, DMAC6_DEI, DMAC6_HEI),
- INTC_GROUP(DMAC7, DMAC7_DEI, DMAC7_HEI),
- INTC_GROUP(MTU0_ABCD, MTU2_TGI0A, MTU2_TGI0B, MTU2_TGI0C, MTU2_TGI0D),
- INTC_GROUP(MTU0_VEF, MTU2_TCI0V, MTU2_TGI0E, MTU2_TGI0F),
- INTC_GROUP(MTU1_AB, MTU2_TGI1A, MTU2_TGI1B),
- INTC_GROUP(MTU1_VU, MTU2_TCI1V, MTU2_TCI1U),
- INTC_GROUP(MTU2_AB, MTU2_TGI2A, MTU2_TGI2B),
- INTC_GROUP(MTU2_VU, MTU2_TCI2V, MTU2_TCI2U),
- INTC_GROUP(MTU3_ABCD, MTU2_TGI3A, MTU2_TGI3B, MTU2_TGI3C, MTU2_TGI3D),
- INTC_GROUP(MTU4_ABCD, MTU2_TGI4A, MTU2_TGI4B, MTU2_TGI4C, MTU2_TGI4D),
- INTC_GROUP(IIC30, IIC30_STPI, IIC30_NAKI, IIC30_RXI, IIC30_TXI,
- IIC30_TEI),
- INTC_GROUP(IIC31, IIC31_STPI, IIC31_NAKI, IIC31_RXI, IIC31_TXI,
- IIC31_TEI),
- INTC_GROUP(IIC32, IIC32_STPI, IIC32_NAKI, IIC32_RXI, IIC32_TXI,
- IIC32_TEI),
- INTC_GROUP(IIC33, IIC33_STPI, IIC33_NAKI, IIC33_RXI, IIC33_TXI,
- IIC33_TEI),
- INTC_GROUP(SCIF0, SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI),
- INTC_GROUP(SCIF1, SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI),
- INTC_GROUP(SCIF2, SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI),
- INTC_GROUP(SCIF3, SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI),
- INTC_GROUP(SSU0, SSU0_SSERI, SSU0_SSRXI, SSU0_SSTXI),
- INTC_GROUP(SSU1, SSU1_SSERI, SSU1_SSRXI, SSU1_SSTXI),
- INTC_GROUP(FLCTL, FLCTL_FLSTEI, FLCTL_FLTENDI, FLCTL_FLTREQ0I,
- FLCTL_FLTREQ1I),
- INTC_GROUP(RTC, RTC_ARM, RTC_PRD, RTC_CUP),
- INTC_GROUP(RCAN0, RCAN0_ERS, RCAN0_OVR, RCAN0_RM0, RCAN0_RM1,
- RCAN0_SLE),
- INTC_GROUP(RCAN1, RCAN1_ERS, RCAN1_OVR, RCAN1_RM0, RCAN1_RM1,
- RCAN1_SLE),
-
-#ifdef CONFIG_CPU_SUBTYPE_SH7263
- INTC_GROUP(ROMDEC, ROMDEC_ISY, ROMDEC_IERR, ROMDEC_IARG,
- ROMDEC_ISEC, ROMDEC_IBUF, ROMDEC_IREADY),
- INTC_GROUP(SDHI, SDHI3, SDHI0, SDHI1),
- INTC_GROUP(SRC, SRC_OVF, SRC_ODFI, SRC_IDEI),
-#endif
};
static struct intc_prio_reg prio_registers[] __initdata = {
@@ -242,22 +176,22 @@
.mapbase = 0xfffe8000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 193, 194, 195, 192 },
+ .irqs = { 192, 192, 192, 192 },
}, {
.mapbase = 0xfffe8800,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 197, 198, 199, 196 },
+ .irqs = { 196, 196, 196, 196 },
}, {
.mapbase = 0xfffe9000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 201, 202, 203, 200 },
+ .irqs = { 200, 200, 200, 200 },
}, {
.mapbase = 0xfffe9800,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 205, 206, 207, 204 },
+ .irqs = { 204, 204, 204, 204 },
}, {
.flags = 0,
}
@@ -278,17 +212,7 @@
.flags = IORESOURCE_IO,
},
[1] = {
- /* Period IRQ */
- .start = 232,
- .flags = IORESOURCE_IRQ,
- },
- [2] = {
- /* Carry IRQ */
- .start = 233,
- .flags = IORESOURCE_IRQ,
- },
- [3] = {
- /* Alarm IRQ */
+ /* Shared Period/Carry/Alarm IRQ */
.start = 231,
.flags = IORESOURCE_IRQ,
},
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7206.c b/arch/sh/kernel/cpu/sh2a/setup-sh7206.c
index e6d4ec4..c46a835 100644
--- a/arch/sh/kernel/cpu/sh2a/setup-sh7206.c
+++ b/arch/sh/kernel/cpu/sh2a/setup-sh7206.c
@@ -2,6 +2,7 @@
* SH7206 Setup
*
* Copyright (C) 2006 Yoshinori Sato
+ * Copyright (C) 2009 Paul Mundt
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
@@ -19,34 +20,23 @@
IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7,
PINT0, PINT1, PINT2, PINT3, PINT4, PINT5, PINT6, PINT7,
ADC_ADI0, ADC_ADI1,
- DMAC0_DEI, DMAC0_HEI, DMAC1_DEI, DMAC1_HEI,
- DMAC2_DEI, DMAC2_HEI, DMAC3_DEI, DMAC3_HEI,
- DMAC4_DEI, DMAC4_HEI, DMAC5_DEI, DMAC5_HEI,
- DMAC6_DEI, DMAC6_HEI, DMAC7_DEI, DMAC7_HEI,
- CMT0, CMT1, BSC, WDT,
- MTU2_TGI0A, MTU2_TGI0B, MTU2_TGI0C, MTU2_TGI0D,
- MTU2_TCI0V, MTU2_TGI0E, MTU2_TGI0F,
- MTU2_TGI1A, MTU2_TGI1B, MTU2_TCI1V, MTU2_TCI1U,
- MTU2_TGI2A, MTU2_TGI2B, MTU2_TCI2V, MTU2_TCI2U,
- MTU2_TGI3A, MTU2_TGI3B, MTU2_TGI3C, MTU2_TGI3D, MTU2_TCI3V,
- MTU2_TGI4A, MTU2_TGI4B, MTU2_TGI4C, MTU2_TGI4D, MTU2_TCI4V,
- MTU2_TGI5U, MTU2_TGI5V, MTU2_TGI5W,
- POE2_OEI1, POE2_OEI2,
- MTU2S_TGI3A, MTU2S_TGI3B, MTU2S_TGI3C, MTU2S_TGI3D, MTU2S_TCI3V,
- MTU2S_TGI4A, MTU2S_TGI4B, MTU2S_TGI4C, MTU2S_TGI4D, MTU2S_TCI4V,
- MTU2S_TGI5U, MTU2S_TGI5V, MTU2S_TGI5W,
- POE2_OEI3,
- IIC3_STPI, IIC3_NAKI, IIC3_RXI, IIC3_TXI, IIC3_TEI,
- SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI,
- SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI,
- SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI,
- SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI,
- /* interrupt groups */
- PINT, DMAC0, DMAC1, DMAC2, DMAC3, DMAC4, DMAC5, DMAC6, DMAC7,
+ DMAC0, DMAC1, DMAC2, DMAC3, DMAC4, DMAC5, DMAC6, DMAC7,
+
MTU0_ABCD, MTU0_VEF, MTU1_AB, MTU1_VU, MTU2_AB, MTU2_VU,
MTU3_ABCD, MTU4_ABCD, MTU5, POE2_12, MTU3S_ABCD, MTU4S_ABCD, MTU5S,
- IIC3, SCIF0, SCIF1, SCIF2, SCIF3,
+ IIC3,
+
+ CMT0, CMT1, BSC, WDT,
+
+ MTU2_TCI3V, MTU2_TCI4V, MTU2S_TCI3V, MTU2S_TCI4V,
+
+ POE2_OEI3,
+
+ SCIF0, SCIF1, SCIF2, SCIF3,
+
+ /* interrupt groups */
+ PINT,
};
static struct intc_vect vectors[] __initdata = {
@@ -59,86 +49,58 @@
INTC_IRQ(PINT4, 84), INTC_IRQ(PINT5, 85),
INTC_IRQ(PINT6, 86), INTC_IRQ(PINT7, 87),
INTC_IRQ(ADC_ADI0, 92), INTC_IRQ(ADC_ADI1, 96),
- INTC_IRQ(DMAC0_DEI, 108), INTC_IRQ(DMAC0_HEI, 109),
- INTC_IRQ(DMAC1_DEI, 112), INTC_IRQ(DMAC1_HEI, 113),
- INTC_IRQ(DMAC2_DEI, 116), INTC_IRQ(DMAC2_HEI, 117),
- INTC_IRQ(DMAC3_DEI, 120), INTC_IRQ(DMAC3_HEI, 121),
- INTC_IRQ(DMAC4_DEI, 124), INTC_IRQ(DMAC4_HEI, 125),
- INTC_IRQ(DMAC5_DEI, 128), INTC_IRQ(DMAC5_HEI, 129),
- INTC_IRQ(DMAC6_DEI, 132), INTC_IRQ(DMAC6_HEI, 133),
- INTC_IRQ(DMAC7_DEI, 136), INTC_IRQ(DMAC7_HEI, 137),
+ INTC_IRQ(DMAC0, 108), INTC_IRQ(DMAC0, 109),
+ INTC_IRQ(DMAC1, 112), INTC_IRQ(DMAC1, 113),
+ INTC_IRQ(DMAC2, 116), INTC_IRQ(DMAC2, 117),
+ INTC_IRQ(DMAC3, 120), INTC_IRQ(DMAC3, 121),
+ INTC_IRQ(DMAC4, 124), INTC_IRQ(DMAC4, 125),
+ INTC_IRQ(DMAC5, 128), INTC_IRQ(DMAC5, 129),
+ INTC_IRQ(DMAC6, 132), INTC_IRQ(DMAC6, 133),
+ INTC_IRQ(DMAC7, 136), INTC_IRQ(DMAC7, 137),
INTC_IRQ(CMT0, 140), INTC_IRQ(CMT1, 144),
INTC_IRQ(BSC, 148), INTC_IRQ(WDT, 152),
- INTC_IRQ(MTU2_TGI0A, 156), INTC_IRQ(MTU2_TGI0B, 157),
- INTC_IRQ(MTU2_TGI0C, 158), INTC_IRQ(MTU2_TGI0D, 159),
- INTC_IRQ(MTU2_TCI0V, 160),
- INTC_IRQ(MTU2_TGI0E, 161), INTC_IRQ(MTU2_TGI0F, 162),
- INTC_IRQ(MTU2_TGI1A, 164), INTC_IRQ(MTU2_TGI1B, 165),
- INTC_IRQ(MTU2_TCI1V, 168), INTC_IRQ(MTU2_TCI1U, 169),
- INTC_IRQ(MTU2_TGI2A, 172), INTC_IRQ(MTU2_TGI2B, 173),
- INTC_IRQ(MTU2_TCI2V, 176), INTC_IRQ(MTU2_TCI2U, 177),
- INTC_IRQ(MTU2_TGI3A, 180), INTC_IRQ(MTU2_TGI3B, 181),
- INTC_IRQ(MTU2_TGI3C, 182), INTC_IRQ(MTU2_TGI3D, 183),
+ INTC_IRQ(MTU0_ABCD, 156), INTC_IRQ(MTU0_ABCD, 157),
+ INTC_IRQ(MTU0_ABCD, 158), INTC_IRQ(MTU0_ABCD, 159),
+ INTC_IRQ(MTU0_VEF, 160), INTC_IRQ(MTU0_VEF, 161),
+ INTC_IRQ(MTU0_VEF, 162),
+ INTC_IRQ(MTU1_AB, 164), INTC_IRQ(MTU1_AB, 165),
+ INTC_IRQ(MTU1_VU, 168), INTC_IRQ(MTU1_VU, 169),
+ INTC_IRQ(MTU2_AB, 172), INTC_IRQ(MTU2_AB, 173),
+ INTC_IRQ(MTU2_VU, 176), INTC_IRQ(MTU2_VU, 177),
+ INTC_IRQ(MTU3_ABCD, 180), INTC_IRQ(MTU3_ABCD, 181),
+ INTC_IRQ(MTU3_ABCD, 182), INTC_IRQ(MTU3_ABCD, 183),
INTC_IRQ(MTU2_TCI3V, 184),
- INTC_IRQ(MTU2_TGI4A, 188), INTC_IRQ(MTU2_TGI4B, 189),
- INTC_IRQ(MTU2_TGI4C, 190), INTC_IRQ(MTU2_TGI4D, 191),
+ INTC_IRQ(MTU4_ABCD, 188), INTC_IRQ(MTU4_ABCD, 189),
+ INTC_IRQ(MTU4_ABCD, 190), INTC_IRQ(MTU4_ABCD, 191),
INTC_IRQ(MTU2_TCI4V, 192),
- INTC_IRQ(MTU2_TGI5U, 196), INTC_IRQ(MTU2_TGI5V, 197),
- INTC_IRQ(MTU2_TGI5W, 198),
- INTC_IRQ(POE2_OEI1, 200), INTC_IRQ(POE2_OEI2, 201),
- INTC_IRQ(MTU2S_TGI3A, 204), INTC_IRQ(MTU2S_TGI3B, 205),
- INTC_IRQ(MTU2S_TGI3C, 206), INTC_IRQ(MTU2S_TGI3D, 207),
+ INTC_IRQ(MTU5, 196), INTC_IRQ(MTU5, 197),
+ INTC_IRQ(MTU5, 198),
+ INTC_IRQ(POE2_12, 200), INTC_IRQ(POE2_12, 201),
+ INTC_IRQ(MTU3S_ABCD, 204), INTC_IRQ(MTU3S_ABCD, 205),
+ INTC_IRQ(MTU3S_ABCD, 206), INTC_IRQ(MTU3S_ABCD, 207),
INTC_IRQ(MTU2S_TCI3V, 208),
- INTC_IRQ(MTU2S_TGI4A, 212), INTC_IRQ(MTU2S_TGI4B, 213),
- INTC_IRQ(MTU2S_TGI4C, 214), INTC_IRQ(MTU2S_TGI4D, 215),
+ INTC_IRQ(MTU4S_ABCD, 212), INTC_IRQ(MTU4S_ABCD, 213),
+ INTC_IRQ(MTU4S_ABCD, 214), INTC_IRQ(MTU4S_ABCD, 215),
INTC_IRQ(MTU2S_TCI4V, 216),
- INTC_IRQ(MTU2S_TGI5U, 220), INTC_IRQ(MTU2S_TGI5V, 221),
- INTC_IRQ(MTU2S_TGI5W, 222),
+ INTC_IRQ(MTU5S, 220), INTC_IRQ(MTU5S, 221),
+ INTC_IRQ(MTU5S, 222),
INTC_IRQ(POE2_OEI3, 224),
- INTC_IRQ(IIC3_STPI, 228), INTC_IRQ(IIC3_NAKI, 229),
- INTC_IRQ(IIC3_RXI, 230), INTC_IRQ(IIC3_TXI, 231),
- INTC_IRQ(IIC3_TEI, 232),
- INTC_IRQ(SCIF0_BRI, 240), INTC_IRQ(SCIF0_ERI, 241),
- INTC_IRQ(SCIF0_RXI, 242), INTC_IRQ(SCIF0_TXI, 243),
- INTC_IRQ(SCIF1_BRI, 244), INTC_IRQ(SCIF1_ERI, 245),
- INTC_IRQ(SCIF1_RXI, 246), INTC_IRQ(SCIF1_TXI, 247),
- INTC_IRQ(SCIF2_BRI, 248), INTC_IRQ(SCIF2_ERI, 249),
- INTC_IRQ(SCIF2_RXI, 250), INTC_IRQ(SCIF2_TXI, 251),
- INTC_IRQ(SCIF3_BRI, 252), INTC_IRQ(SCIF3_ERI, 253),
- INTC_IRQ(SCIF3_RXI, 254), INTC_IRQ(SCIF3_TXI, 255),
+ INTC_IRQ(IIC3, 228), INTC_IRQ(IIC3, 229),
+ INTC_IRQ(IIC3, 230), INTC_IRQ(IIC3, 231),
+ INTC_IRQ(IIC3, 232),
+ INTC_IRQ(SCIF0, 240), INTC_IRQ(SCIF0, 241),
+ INTC_IRQ(SCIF0, 242), INTC_IRQ(SCIF0, 243),
+ INTC_IRQ(SCIF1, 244), INTC_IRQ(SCIF1, 245),
+ INTC_IRQ(SCIF1, 246), INTC_IRQ(SCIF1, 247),
+ INTC_IRQ(SCIF2, 248), INTC_IRQ(SCIF2, 249),
+ INTC_IRQ(SCIF2, 250), INTC_IRQ(SCIF2, 251),
+ INTC_IRQ(SCIF3, 252), INTC_IRQ(SCIF3, 253),
+ INTC_IRQ(SCIF3, 254), INTC_IRQ(SCIF3, 255),
};
static struct intc_group groups[] __initdata = {
INTC_GROUP(PINT, PINT0, PINT1, PINT2, PINT3,
PINT4, PINT5, PINT6, PINT7),
- INTC_GROUP(DMAC0, DMAC0_DEI, DMAC0_HEI),
- INTC_GROUP(DMAC1, DMAC1_DEI, DMAC1_HEI),
- INTC_GROUP(DMAC2, DMAC2_DEI, DMAC2_HEI),
- INTC_GROUP(DMAC3, DMAC3_DEI, DMAC3_HEI),
- INTC_GROUP(DMAC4, DMAC4_DEI, DMAC4_HEI),
- INTC_GROUP(DMAC5, DMAC5_DEI, DMAC5_HEI),
- INTC_GROUP(DMAC6, DMAC6_DEI, DMAC6_HEI),
- INTC_GROUP(DMAC7, DMAC7_DEI, DMAC7_HEI),
- INTC_GROUP(MTU0_ABCD, MTU2_TGI0A, MTU2_TGI0B, MTU2_TGI0C, MTU2_TGI0D),
- INTC_GROUP(MTU0_VEF, MTU2_TCI0V, MTU2_TGI0E, MTU2_TGI0F),
- INTC_GROUP(MTU1_AB, MTU2_TGI1A, MTU2_TGI1B),
- INTC_GROUP(MTU1_VU, MTU2_TCI1V, MTU2_TCI1U),
- INTC_GROUP(MTU2_AB, MTU2_TGI2A, MTU2_TGI2B),
- INTC_GROUP(MTU2_VU, MTU2_TCI2V, MTU2_TCI2U),
- INTC_GROUP(MTU3_ABCD, MTU2_TGI3A, MTU2_TGI3B, MTU2_TGI3C, MTU2_TGI3D),
- INTC_GROUP(MTU4_ABCD, MTU2_TGI4A, MTU2_TGI4B, MTU2_TGI4C, MTU2_TGI4D),
- INTC_GROUP(MTU5, MTU2_TGI5U, MTU2_TGI5V, MTU2_TGI5W),
- INTC_GROUP(POE2_12, POE2_OEI1, POE2_OEI2),
- INTC_GROUP(MTU3S_ABCD, MTU2S_TGI3A, MTU2S_TGI3B,
- MTU2S_TGI3C, MTU2S_TGI3D),
- INTC_GROUP(MTU4S_ABCD, MTU2S_TGI4A, MTU2S_TGI4B,
- MTU2S_TGI4C, MTU2S_TGI4D),
- INTC_GROUP(MTU5S, MTU2S_TGI5U, MTU2S_TGI5V, MTU2S_TGI5W),
- INTC_GROUP(IIC3, IIC3_STPI, IIC3_NAKI, IIC3_RXI, IIC3_TXI, IIC3_TEI),
- INTC_GROUP(SCIF0, SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI),
- INTC_GROUP(SCIF1, SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI),
- INTC_GROUP(SCIF2, SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI),
- INTC_GROUP(SCIF3, SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI),
};
static struct intc_prio_reg prio_registers[] __initdata = {
@@ -174,22 +136,22 @@
.mapbase = 0xfffe8000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 241, 242, 243, 240 },
+ .irqs = { 240, 240, 240, 240 },
}, {
.mapbase = 0xfffe8800,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 245, 246, 247, 244 },
+ .irqs = { 244, 244, 244, 244 },
}, {
.mapbase = 0xfffe9000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 249, 250, 251, 248 },
+ .irqs = { 248, 248, 248, 248 },
}, {
.mapbase = 0xfffe9800,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 253, 254, 255, 252 },
+ .irqs = { 252, 252, 252, 252 },
}, {
.flags = 0,
}
diff --git a/arch/sh/kernel/cpu/sh3/Makefile b/arch/sh/kernel/cpu/sh3/Makefile
index e07c69e..ecab274 100644
--- a/arch/sh/kernel/cpu/sh3/Makefile
+++ b/arch/sh/kernel/cpu/sh3/Makefile
@@ -4,6 +4,8 @@
obj-y := ex.o probe.o entry.o setup-sh3.o
+obj-$(CONFIG_HIBERNATION) += swsusp.o
+
# CPU subtype setup
obj-$(CONFIG_CPU_SUBTYPE_SH7705) += setup-sh7705.o
obj-$(CONFIG_CPU_SUBTYPE_SH7706) += setup-sh770x.o
diff --git a/arch/sh/kernel/cpu/sh3/entry.S b/arch/sh/kernel/cpu/sh3/entry.S
index b4106d0..55da0ff 100644
--- a/arch/sh/kernel/cpu/sh3/entry.S
+++ b/arch/sh/kernel/cpu/sh3/entry.S
@@ -16,6 +16,7 @@
#include <asm/unistd.h>
#include <cpu/mmu_context.h>
#include <asm/page.h>
+#include <asm/cache.h>
! NOTE:
! GNU as (as of 2.9.1) changes bf/s into bt/s and bra, when the address
@@ -187,44 +188,35 @@
#if defined(CONFIG_SH_STANDARD_BIOS)
/* Unwind the stack and jmp to the debug entry */
ENTRY(sh_bios_handler)
- mov.l @r15+, r0
- mov.l @r15+, r1
- mov.l @r15+, r2
- mov.l @r15+, r3
- mov.l @r15+, r4
- mov.l @r15+, r5
- mov.l @r15+, r6
- mov.l @r15+, r7
- stc sr, r8
- mov.l 1f, r9 ! BL =1, RB=1, IMASK=0x0F
- or r9, r8
- ldc r8, sr ! here, change the register bank
- mov.l @r15+, r8
- mov.l @r15+, r9
- mov.l @r15+, r10
- mov.l @r15+, r11
- mov.l @r15+, r12
- mov.l @r15+, r13
- mov.l @r15+, r14
- mov.l @r15+, k0
- ldc.l @r15+, spc
- lds.l @r15+, pr
- mov.l @r15+, k1
- ldc.l @r15+, gbr
- lds.l @r15+, mach
- lds.l @r15+, macl
- mov k0, r15
+ mov.l 1f, r8
+ bsr restore_regs
+ nop
+
+ lds k2, pr ! restore pr
+ mov k4, r15
!
mov.l 2f, k0
mov.l @k0, k0
jmp @k0
- ldc k1, ssr
+ ldc k3, ssr
.align 2
1: .long 0x300000f0
2: .long gdb_vbr_vector
#endif /* CONFIG_SH_STANDARD_BIOS */
-restore_all:
+! restore_regs()
+! - restore r0, r1, r2, r3, r4, r5, r6, r7 from the stack
+! - switch bank
+! - restore r8, r9, r10, r11, r12, r13, r14, r15 from the stack
+! - restore spc, pr*, ssr, gbr, mach, macl, skip default tra
+! k2 returns original pr
+! k3 returns original sr
+! k4 returns original stack pointer
+! r8 passes SR bitmask, overwritten with restored data on return
+! r9 trashed
+! BL=0 on entry, on exit BL=1 (depending on r8).
+
+ENTRY(restore_regs)
mov.l @r15+, r0
mov.l @r15+, r1
mov.l @r15+, r2
@@ -234,10 +226,9 @@
mov.l @r15+, r6
mov.l @r15+, r7
!
- stc sr, r8
- mov.l 7f, r9
- or r9, r8 ! BL =1, RB=1
- ldc r8, sr ! here, change the register bank
+ stc sr, r9
+ or r8, r9
+ ldc r9, sr
!
mov.l @r15+, r8
mov.l @r15+, r9
@@ -248,12 +239,20 @@
mov.l @r15+, r14
mov.l @r15+, k4 ! original stack pointer
ldc.l @r15+, spc
- lds.l @r15+, pr
+ mov.l @r15+, k2 ! original PR
mov.l @r15+, k3 ! original SR
ldc.l @r15+, gbr
lds.l @r15+, mach
lds.l @r15+, macl
- add #4, r15 ! Skip syscall number
+ rts
+ add #4, r15 ! Skip syscall number
+
+restore_all:
+ mov.l 7f, r8
+ bsr restore_regs
+ nop
+
+ lds k2, pr ! restore pr
!
#ifdef CONFIG_SH_DSP
mov.l @r15+, k0 ! DSP mode marker
@@ -294,7 +293,7 @@
mov #0xf0, k1
extu.b k1, k1
not k1, k1
- and k1, k2 ! Mask orignal SR value
+ and k1, k2 ! Mask original SR value
!
mov k3, k0 ! Calculate IMASK-bits
shlr2 k0
@@ -313,7 +312,6 @@
mov #0, k1
mov.b k1, @k0
#endif
- mov.l @r15+, k2 ! restore EXPEVT
mov k4, r15
rte
nop
@@ -336,81 +334,55 @@
ENTRY(vbr_base)
.long 0
!
+! 0x100: General exception vector
+!
.balign 256,0,256
general_exception:
- mov.l 1f, k2
- mov.l 2f, k3
-#ifdef CONFIG_CPU_SUBTYPE_SHX3
- mov.l @k2, k2
+#ifndef CONFIG_CPU_SUBTYPE_SHX3
+ bra handle_exception
+ sts pr, k3 ! save original pr value in k3
+#else
+ mov.l 1f, k4
+ mov.l @k4, k4
! Is EXPEVT larger than 0x800?
mov #0x8, k0
shll8 k0
- cmp/hs k0, k2
+ cmp/hs k0, k4
bf 0f
! then add 0x580 (k2 is 0xd80 or 0xda0)
mov #0x58, k0
shll2 k0
shll2 k0
- add k0, k2
+ add k0, k4
0:
- bra handle_exception
+ ! Setup stack and save DSP context (k0 contains original r15 on return)
+ bsr prepare_stack_save_dsp
nop
-#else
- bra handle_exception
- mov.l @k2, k2
+
+ ! Save registers / Switch to bank 0
+ mov k4, k2 ! keep vector in k2
+ mov.l 1f, k4 ! SR bits to clear in k4
+ bsr save_regs ! needs original pr value in k3
+ nop
+
+ bra handle_exception_special
+ nop
+
+ .align 2
+1: .long EXPEVT
#endif
- .align 2
-1: .long EXPEVT
-2: .long ret_from_exception
-!
-!
- .balign 1024,0,1024
-tlb_miss:
- mov.l 1f, k2
- mov.l 4f, k3
- bra handle_exception
- mov.l @k2, k2
-!
- .balign 512,0,512
-interrupt:
- mov.l 3f, k3
-#if defined(CONFIG_KGDB)
- mov.l 2f, k2
- ! Debounce (filter nested NMI)
- mov.l @k2, k0
- mov.l 5f, k1
- cmp/eq k1, k0
- bf 0f
- mov.l 6f, k1
- tas.b @k1
- bt 0f
- rte
- nop
- .align 2
-2: .long INTEVT
-5: .long NMI_VEC
-6: .long in_nmi
-0:
-#endif /* defined(CONFIG_KGDB) */
- bra handle_exception
- mov #-1, k2 ! interrupt exception marker
+! prepare_stack_save_dsp()
+! - roll back gRB
+! - switch to kernel stack
+! - save DSP
+! k0 returns original sp (after roll back)
+! k1 trashed
+! k2 trashed
- .align 2
-1: .long EXPEVT
-3: .long ret_from_irq
-4: .long ret_from_exception
-
-!
-!
- .align 2
-ENTRY(handle_exception)
- ! Using k0, k1 for scratch registers (r0_bank1, r1_bank),
- ! save all registers onto stack.
- !
-
+prepare_stack_save_dsp:
#ifdef CONFIG_GUSA
! Check for roll back gRB (User and Kernel)
mov r15, k0
@@ -430,7 +402,7 @@
2: mov k1, r15 ! SP = r1
1:
#endif
-
+ ! Switch to kernel stack if needed
stc ssr, k0 ! Is it from kernel space?
shll k0 ! Check MD bit (bit30) by shifting it into...
shll k0 ! ...the T bit
@@ -443,18 +415,17 @@
add current, k1
mov k1, r15 ! change to kernel stack
!
-1: mov.l 2f, k1
- !
+1:
#ifdef CONFIG_SH_DSP
- mov.l r2, @-r15 ! Save r2, we need another reg
- stc sr, k4
- mov.l 1f, r2
- tst r2, k4 ! Check if in DSP mode
- mov.l @r15+, r2 ! Restore r2 now
+ ! Save DSP context if needed
+ stc sr, k1
+ mov #0x10, k2
+ shll8 k2 ! DSP=1 (0x00001000)
+ tst k2, k1 ! Check if in DSP mode (passed in k2)
bt/s skip_save
- mov #0, k4 ! Set marker for no stack frame
+ mov #0, k1 ! Set marker for no stack frame
- mov r2, k4 ! Backup r2 (in k4) for later
+ mov k2, k1 ! Save has-frame marker
! Save DSP registers on stack
stc.l mod, @-r15
@@ -473,35 +444,74 @@
! as we're not at all interested in supporting ancient toolchains at
! this point. -- PFM.
- mov r15, r2
+ mov r15, k2
.word 0xf653 ! movs.l a1, @-r2
.word 0xf6f3 ! movs.l a0g, @-r2
.word 0xf6d3 ! movs.l a1g, @-r2
.word 0xf6c3 ! movs.l m0, @-r2
.word 0xf6e3 ! movs.l m1, @-r2
- mov r2, r15
+ mov k2, r15
- mov k4, r2 ! Restore r2
- mov.l 1f, k4 ! Force DSP stack frame
skip_save:
- mov.l k4, @-r15 ! Push DSP mode marker onto stack
+ mov.l k1, @-r15 ! Push DSP mode marker onto stack
#endif
- ! Save the user registers on the stack.
- mov.l k2, @-r15 ! EXPEVT
+ rts
+ nop
+!
+! 0x400: Instruction and Data TLB miss exception vector
+!
+ .balign 1024,0,1024
+tlb_miss:
+ sts pr, k3 ! save original pr value in k3
- mov #-1, k4
- mov.l k4, @-r15 ! set TRA (default: -1)
- !
+handle_exception:
+ mova exception_data, k0
+
+ ! Setup stack and save DSP context (k0 contains original r15 on return)
+ bsr prepare_stack_save_dsp
+ PREF(k0)
+
+ ! Save registers / Switch to bank 0
+ mov.l 5f, k2 ! vector register address
+ mov.l 1f, k4 ! SR bits to clear in k4
+ bsr save_regs ! needs original pr value in k3
+ mov.l @k2, k2 ! read out vector and keep in k2
+
+handle_exception_special:
+ ! Setup return address and jump to exception handler
+ mov.l 7f, r9 ! fetch return address
+ stc r2_bank, r0 ! k2 (vector)
+ mov.l 6f, r10
+ shlr2 r0
+ shlr r0
+ mov.l @(r0, r10), r10
+ jmp @r10
+ lds r9, pr ! put return address in pr
+
+ .align L1_CACHE_SHIFT
+
+! save_regs()
+! - save default tra, macl, mach, gbr, ssr, pr* and spc on the stack
+! - save r15*, r14, r13, r12, r11, r10, r9, r8 on the stack
+! - switch bank
+! - save r7, r6, r5, r4, r3, r2, r1, r0 on the stack
+! k0 contains original stack pointer*
+! k1 trashed
+! k3 passes original pr*
+! k4 passes SR bitmask
+! BL=1 on entry, on exit BL=0.
+
+ENTRY(save_regs)
+ mov #-1, r1
+ mov.l k1, @-r15 ! set TRA (default: -1)
sts.l macl, @-r15
sts.l mach, @-r15
stc.l gbr, @-r15
stc.l ssr, @-r15
- sts.l pr, @-r15
+ mov.l k3, @-r15 ! original pr in k3
stc.l spc, @-r15
- !
- lds k3, pr ! Set the return address to pr
- !
- mov.l k0, @-r15 ! save orignal stack
+
+ mov.l k0, @-r15 ! original stack pointer in k0
mov.l r14, @-r15
mov.l r13, @-r15
mov.l r12, @-r15
@@ -509,13 +519,23 @@
mov.l r10, @-r15
mov.l r9, @-r15
mov.l r8, @-r15
- !
- stc sr, r8 ! Back to normal register bank, and
- or k1, r8 ! Block all interrupts
- mov.l 3f, k1
- and k1, r8 ! ...
- ldc r8, sr ! ...changed here.
- !
+
+ mov.l 0f, k3 ! SR bits to set in k3
+
+ ! fall-through
+
+! save_low_regs()
+! - modify SR for bank switch
+! - save r7, r6, r5, r4, r3, r2, r1, r0 on the stack
+! k3 passes bits to set in SR
+! k4 passes bits to clear in SR
+
+ENTRY(save_low_regs)
+ stc sr, r8
+ or k3, r8
+ and k4, r8
+ ldc r8, sr
+
mov.l r7, @-r15
mov.l r6, @-r15
mov.l r5, @-r15
@@ -523,52 +543,63 @@
mov.l r3, @-r15
mov.l r2, @-r15
mov.l r1, @-r15
- mov.l r0, @-r15
-
- /*
- * This gets a bit tricky.. in the INTEVT case we don't want to use
- * the VBR offset as a destination in the jump call table, since all
- * of the destinations are the same. In this case, (interrupt) sets
- * a marker in r2 (now r2_bank since SR.RB changed), which we check
- * to determine the exception type. For all other exceptions, we
- * forcibly read EXPEVT from memory and fix up the jump address, in
- * the interrupt exception case we jump to do_IRQ() and defer the
- * INTEVT read until there. As a bonus, we can also clean up the SR.RB
- * checks that do_IRQ() was doing..
- */
- stc r2_bank, r8
- cmp/pz r8
- bf interrupt_exception
- shlr2 r8
- shlr r8
- mov.l 4f, r9
- add r8, r9
- mov.l @r9, r9
- jmp @r9
- nop
rts
+ mov.l r0, @-r15
+
+!
+! 0x600: Interrupt / NMI vector
+!
+ .balign 512,0,512
+ENTRY(handle_interrupt)
+#if defined(CONFIG_KGDB)
+ mov.l 2f, k2
+ ! Debounce (filter nested NMI)
+ mov.l @k2, k0
+ mov.l 9f, k1
+ cmp/eq k1, k0
+ bf 11f
+ mov.l 10f, k1
+ tas.b @k1
+ bt 11f
+ rte
nop
-
.align 2
-1: .long 0x00001000 ! DSP=1
-2: .long 0x000080f0 ! FD=1, IMASK=15
-3: .long 0xcfffffff ! RB=0, BL=0
-4: .long exception_handling_table
+9: .long NMI_VEC
+10: .long in_nmi
+11:
+#endif /* defined(CONFIG_KGDB) */
+ sts pr, k3 ! save original pr value in k3
+ mova exception_data, k0
-interrupt_exception:
- mov.l 1f, r9
+ ! Setup stack and save DSP context (k0 contains original r15 on return)
+ bsr prepare_stack_save_dsp
+ PREF(k0)
+
+ ! Save registers / Switch to bank 0
+ mov.l 1f, k4 ! SR bits to clear in k4
+ bsr save_regs ! needs original pr value in k3
+ mov #-1, k2 ! default vector kept in k2
+
+ ! Setup return address and jump to do_IRQ
+ mov.l 4f, r9 ! fetch return address
+ lds r9, pr ! put return address in pr
mov.l 2f, r4
- mov.l @r4, r4
+ mov.l 3f, r9
+ mov.l @r4, r4 ! pass INTEVT vector as arg0
jmp @r9
- mov r15, r5
- rts
- nop
+ mov r15, r5 ! pass saved registers as arg1
- .align 2
-1: .long do_IRQ
-2: .long INTEVT
-
- .align 2
ENTRY(exception_none)
rts
nop
+
+ .align L1_CACHE_SHIFT
+exception_data:
+0: .long 0x000080f0 ! FD=1, IMASK=15
+1: .long 0xcfffffff ! RB=0, BL=0
+2: .long INTEVT
+3: .long do_IRQ
+4: .long ret_from_irq
+5: .long EXPEVT
+6: .long exception_handling_table
+7: .long ret_from_exception
diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7705.c b/arch/sh/kernel/cpu/sh3/setup-sh7705.c
index 6468ae8..63b67ba 100644
--- a/arch/sh/kernel/cpu/sh3/setup-sh7705.c
+++ b/arch/sh/kernel/cpu/sh3/setup-sh7705.c
@@ -1,7 +1,7 @@
/*
* SH7705 Setup
*
- * Copyright (C) 2006, 2007 Paul Mundt
+ * Copyright (C) 2006 - 2009 Paul Mundt
* Copyright (C) 2007 Nobuhiro Iwamatsu
*
* This file is subject to the terms and conditions of the GNU General Public
@@ -21,51 +21,36 @@
/* interrupt sources */
IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5,
PINT07, PINT815,
- DMAC_DEI0, DMAC_DEI1, DMAC_DEI2, DMAC_DEI3,
- SCIF0_ERI, SCIF0_RXI, SCIF0_TXI,
- SCIF2_ERI, SCIF2_RXI, SCIF2_TXI,
- ADC_ADI,
- USB_USI0, USB_USI1,
- TPU0, TPU1, TPU2, TPU3,
- TMU0, TMU1, TMU2_TUNI, TMU2_TICPI,
- RTC_ATI, RTC_PRI, RTC_CUI,
- WDT,
- REF_RCMI,
- /* interrupt groups */
- RTC, TMU2, DMAC, USB, SCIF2, SCIF0,
+ DMAC, SCIF0, SCIF2, ADC_ADI, USB,
+
+ TPU0, TPU1, TPU2, TPU3,
+ TMU0, TMU1, TMU2,
+
+ RTC, WDT, REF_RCMI,
};
static struct intc_vect vectors[] __initdata = {
/* IRQ0->5 are handled in setup-sh3.c */
INTC_VECT(PINT07, 0x700), INTC_VECT(PINT815, 0x720),
- INTC_VECT(DMAC_DEI0, 0x800), INTC_VECT(DMAC_DEI1, 0x820),
- INTC_VECT(DMAC_DEI2, 0x840), INTC_VECT(DMAC_DEI3, 0x860),
- INTC_VECT(SCIF0_ERI, 0x880), INTC_VECT(SCIF0_RXI, 0x8a0),
- INTC_VECT(SCIF0_TXI, 0x8e0),
- INTC_VECT(SCIF2_ERI, 0x900), INTC_VECT(SCIF2_RXI, 0x920),
- INTC_VECT(SCIF2_TXI, 0x960),
+ INTC_VECT(DMAC, 0x800), INTC_VECT(DMAC, 0x820),
+ INTC_VECT(DMAC, 0x840), INTC_VECT(DMAC, 0x860),
+ INTC_VECT(SCIF0, 0x880), INTC_VECT(SCIF0, 0x8a0),
+ INTC_VECT(SCIF0, 0x8e0),
+ INTC_VECT(SCIF2, 0x900), INTC_VECT(SCIF2, 0x920),
+ INTC_VECT(SCIF2, 0x960),
INTC_VECT(ADC_ADI, 0x980),
- INTC_VECT(USB_USI0, 0xa20), INTC_VECT(USB_USI1, 0xa40),
+ INTC_VECT(USB, 0xa20), INTC_VECT(USB, 0xa40),
INTC_VECT(TPU0, 0xc00), INTC_VECT(TPU1, 0xc20),
INTC_VECT(TPU2, 0xc80), INTC_VECT(TPU3, 0xca0),
INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420),
- INTC_VECT(TMU2_TUNI, 0x440), INTC_VECT(TMU2_TICPI, 0x460),
- INTC_VECT(RTC_ATI, 0x480), INTC_VECT(RTC_PRI, 0x4a0),
- INTC_VECT(RTC_CUI, 0x4c0),
+ INTC_VECT(TMU2, 0x440), INTC_VECT(TMU2, 0x460),
+ INTC_VECT(RTC, 0x480), INTC_VECT(RTC, 0x4a0),
+ INTC_VECT(RTC, 0x4c0),
INTC_VECT(WDT, 0x560),
INTC_VECT(REF_RCMI, 0x580),
};
-static struct intc_group groups[] __initdata = {
- INTC_GROUP(RTC, RTC_ATI, RTC_PRI, RTC_CUI),
- INTC_GROUP(TMU2, TMU2_TUNI, TMU2_TICPI),
- INTC_GROUP(DMAC, DMAC_DEI0, DMAC_DEI1, DMAC_DEI2, DMAC_DEI3),
- INTC_GROUP(USB, USB_USI0, USB_USI1),
- INTC_GROUP(SCIF0, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI),
- INTC_GROUP(SCIF2, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI),
-};
-
static struct intc_prio_reg prio_registers[] __initdata = {
{ 0xfffffee2, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } },
{ 0xfffffee4, 0, 16, 4, /* IPRB */ { WDT, REF_RCMI, 0, 0 } },
@@ -78,7 +63,7 @@
};
-static DECLARE_INTC_DESC(intc_desc, "sh7705", vectors, groups,
+static DECLARE_INTC_DESC(intc_desc, "sh7705", vectors, NULL,
NULL, prio_registers, NULL);
static struct plat_sci_port sci_platform_data[] = {
@@ -86,12 +71,12 @@
.mapbase = 0xa4410000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 56, 57, 59 },
+ .irqs = { 56, 56, 56 },
}, {
.mapbase = 0xa4400000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 52, 53, 55 },
+ .irqs = { 52, 52, 52 },
}, {
.flags = 0,
}
@@ -115,14 +100,6 @@
.start = 20,
.flags = IORESOURCE_IRQ,
},
- [2] = {
- .start = 21,
- .flags = IORESOURCE_IRQ,
- },
- [3] = {
- .start = 22,
- .flags = IORESOURCE_IRQ,
- },
};
static struct sh_rtc_platform_info rtc_info = {
diff --git a/arch/sh/kernel/cpu/sh3/setup-sh770x.c b/arch/sh/kernel/cpu/sh3/setup-sh770x.c
index 93c55e2..a74f960 100644
--- a/arch/sh/kernel/cpu/sh3/setup-sh770x.c
+++ b/arch/sh/kernel/cpu/sh3/setup-sh770x.c
@@ -2,6 +2,7 @@
* SH3 Setup code for SH7706, SH7707, SH7708, SH7709
*
* Copyright (C) 2007 Magnus Damm
+ * Copyright (C) 2009 Paul Mundt
*
* Based on setup-sh7709.c
*
@@ -24,46 +25,37 @@
/* interrupt sources */
IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5,
PINT07, PINT815,
- DMAC_DEI0, DMAC_DEI1, DMAC_DEI2, DMAC_DEI3,
- SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI,
- SCIF2_ERI, SCIF2_RXI, SCIF2_BRI, SCIF2_TXI,
- SCI_ERI, SCI_RXI, SCI_TXI, SCI_TEI,
- ADC_ADI,
+ DMAC, SCIF0, SCIF2, SCI, ADC_ADI,
LCDC, PCC0, PCC1,
- TMU0, TMU1, TMU2_TUNI, TMU2_TICPI,
- RTC_ATI, RTC_PRI, RTC_CUI,
- WDT,
- REF_RCMI, REF_ROVI,
-
- /* interrupt groups */
- RTC, REF, TMU2, DMAC, SCI, SCIF2, SCIF0,
+ TMU0, TMU1, TMU2,
+ RTC, WDT, REF,
};
static struct intc_vect vectors[] __initdata = {
INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420),
- INTC_VECT(TMU2_TUNI, 0x440), INTC_VECT(TMU2_TICPI, 0x460),
- INTC_VECT(RTC_ATI, 0x480), INTC_VECT(RTC_PRI, 0x4a0),
- INTC_VECT(RTC_CUI, 0x4c0),
- INTC_VECT(SCI_ERI, 0x4e0), INTC_VECT(SCI_RXI, 0x500),
- INTC_VECT(SCI_TXI, 0x520), INTC_VECT(SCI_TEI, 0x540),
+ INTC_VECT(TMU2, 0x440), INTC_VECT(TMU2, 0x460),
+ INTC_VECT(RTC, 0x480), INTC_VECT(RTC, 0x4a0),
+ INTC_VECT(RTC, 0x4c0),
+ INTC_VECT(SCI, 0x4e0), INTC_VECT(SCI, 0x500),
+ INTC_VECT(SCI, 0x520), INTC_VECT(SCI, 0x540),
INTC_VECT(WDT, 0x560),
- INTC_VECT(REF_RCMI, 0x580),
- INTC_VECT(REF_ROVI, 0x5a0),
+ INTC_VECT(REF, 0x580),
+ INTC_VECT(REF, 0x5a0),
#if defined(CONFIG_CPU_SUBTYPE_SH7706) || \
defined(CONFIG_CPU_SUBTYPE_SH7707) || \
defined(CONFIG_CPU_SUBTYPE_SH7709)
/* IRQ0->5 are handled in setup-sh3.c */
- INTC_VECT(DMAC_DEI0, 0x800), INTC_VECT(DMAC_DEI1, 0x820),
- INTC_VECT(DMAC_DEI2, 0x840), INTC_VECT(DMAC_DEI3, 0x860),
+ INTC_VECT(DMAC, 0x800), INTC_VECT(DMAC, 0x820),
+ INTC_VECT(DMAC, 0x840), INTC_VECT(DMAC, 0x860),
INTC_VECT(ADC_ADI, 0x980),
- INTC_VECT(SCIF2_ERI, 0x900), INTC_VECT(SCIF2_RXI, 0x920),
- INTC_VECT(SCIF2_BRI, 0x940), INTC_VECT(SCIF2_TXI, 0x960),
+ INTC_VECT(SCIF2, 0x900), INTC_VECT(SCIF2, 0x920),
+ INTC_VECT(SCIF2, 0x940), INTC_VECT(SCIF2, 0x960),
#endif
#if defined(CONFIG_CPU_SUBTYPE_SH7707) || \
defined(CONFIG_CPU_SUBTYPE_SH7709)
INTC_VECT(PINT07, 0x700), INTC_VECT(PINT815, 0x720),
- INTC_VECT(SCIF0_ERI, 0x880), INTC_VECT(SCIF0_RXI, 0x8a0),
- INTC_VECT(SCIF0_BRI, 0x8c0), INTC_VECT(SCIF0_TXI, 0x8e0),
+ INTC_VECT(SCIF0, 0x880), INTC_VECT(SCIF0, 0x8a0),
+ INTC_VECT(SCIF0, 0x8c0), INTC_VECT(SCIF0, 0x8e0),
#endif
#if defined(CONFIG_CPU_SUBTYPE_SH7707)
INTC_VECT(LCDC, 0x9a0),
@@ -71,16 +63,6 @@
#endif
};
-static struct intc_group groups[] __initdata = {
- INTC_GROUP(RTC, RTC_ATI, RTC_PRI, RTC_CUI),
- INTC_GROUP(TMU2, TMU2_TUNI, TMU2_TICPI),
- INTC_GROUP(REF, REF_RCMI, REF_ROVI),
- INTC_GROUP(DMAC, DMAC_DEI0, DMAC_DEI1, DMAC_DEI2, DMAC_DEI3),
- INTC_GROUP(SCI, SCI_ERI, SCI_RXI, SCI_TXI, SCI_TEI),
- INTC_GROUP(SCIF0, SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI),
- INTC_GROUP(SCIF2, SCIF2_ERI, SCIF2_RXI, SCIF2_BRI, SCIF2_TXI),
-};
-
static struct intc_prio_reg prio_registers[] __initdata = {
{ 0xfffffee2, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } },
{ 0xfffffee4, 0, 16, 4, /* IPRB */ { WDT, REF, SCI, 0 } },
@@ -101,7 +83,7 @@
#endif
};
-static DECLARE_INTC_DESC(intc_desc, "sh770x", vectors, groups,
+static DECLARE_INTC_DESC(intc_desc, "sh770x", vectors, NULL,
NULL, prio_registers, NULL);
static struct resource rtc_resources[] = {
@@ -111,14 +93,6 @@
.flags = IORESOURCE_IO,
},
[1] = {
- .start = 21,
- .flags = IORESOURCE_IRQ,
- },
- [2] = {
- .start = 22,
- .flags = IORESOURCE_IRQ,
- },
- [3] = {
.start = 20,
.flags = IORESOURCE_IRQ,
},
@@ -136,7 +110,7 @@
.mapbase = 0xfffffe80,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCI,
- .irqs = { 23, 24, 25, 0 },
+ .irqs = { 23, 23, 23, 0 },
},
#if defined(CONFIG_CPU_SUBTYPE_SH7706) || \
defined(CONFIG_CPU_SUBTYPE_SH7707) || \
@@ -145,7 +119,7 @@
.mapbase = 0xa4000150,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 56, 57, 59, 58 },
+ .irqs = { 56, 56, 56, 56 },
},
#endif
#if defined(CONFIG_CPU_SUBTYPE_SH7707) || \
@@ -154,7 +128,7 @@
.mapbase = 0xa4000140,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_IRDA,
- .irqs = { 52, 53, 55, 54 },
+ .irqs = { 52, 52, 52, 52 },
},
#endif
{
diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7710.c b/arch/sh/kernel/cpu/sh3/setup-sh7710.c
index 77eee48..335098b 100644
--- a/arch/sh/kernel/cpu/sh3/setup-sh7710.c
+++ b/arch/sh/kernel/cpu/sh3/setup-sh7710.c
@@ -1,7 +1,7 @@
/*
* SH3 Setup code for SH7710, SH7712
*
- * Copyright (C) 2006, 2007 Paul Mundt
+ * Copyright (C) 2006 - 2009 Paul Mundt
* Copyright (C) 2007 Nobuhiro Iwamatsu
*
* This file is subject to the terms and conditions of the GNU General Public
@@ -20,59 +20,40 @@
/* interrupt sources */
IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5,
- DMAC_DEI0, DMAC_DEI1, DMAC_DEI2, DMAC_DEI3,
- SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI,
- SCIF1_ERI, SCIF1_RXI, SCIF1_BRI, SCIF1_TXI,
- DMAC_DEI4, DMAC_DEI5,
- IPSEC,
+ DMAC1, SCIF0, SCIF1, DMAC2, IPSEC,
EDMAC0, EDMAC1, EDMAC2,
- SIOF0_ERI, SIOF0_TXI, SIOF0_RXI, SIOF0_CCI,
- SIOF1_ERI, SIOF1_TXI, SIOF1_RXI, SIOF1_CCI,
- TMU0, TMU1, TMU2,
- RTC_ATI, RTC_PRI, RTC_CUI,
- WDT,
- REF,
+ SIOF0, SIOF1,
- /* interrupt groups */
- RTC, DMAC1, SCIF0, SCIF1, DMAC2, SIOF0, SIOF1,
+ TMU0, TMU1, TMU2,
+ RTC, WDT, REF,
};
static struct intc_vect vectors[] __initdata = {
/* IRQ0->5 are handled in setup-sh3.c */
- INTC_VECT(DMAC_DEI0, 0x800), INTC_VECT(DMAC_DEI1, 0x820),
- INTC_VECT(DMAC_DEI2, 0x840), INTC_VECT(DMAC_DEI3, 0x860),
- INTC_VECT(SCIF0_ERI, 0x880), INTC_VECT(SCIF0_RXI, 0x8a0),
- INTC_VECT(SCIF0_BRI, 0x8c0), INTC_VECT(SCIF0_TXI, 0x8e0),
- INTC_VECT(SCIF1_ERI, 0x900), INTC_VECT(SCIF1_RXI, 0x920),
- INTC_VECT(SCIF1_BRI, 0x940), INTC_VECT(SCIF1_TXI, 0x960),
- INTC_VECT(DMAC_DEI4, 0xb80), INTC_VECT(DMAC_DEI5, 0xba0),
+ INTC_VECT(DMAC1, 0x800), INTC_VECT(DMAC1, 0x820),
+ INTC_VECT(DMAC1, 0x840), INTC_VECT(DMAC1, 0x860),
+ INTC_VECT(SCIF0, 0x880), INTC_VECT(SCIF0, 0x8a0),
+ INTC_VECT(SCIF0, 0x8c0), INTC_VECT(SCIF0, 0x8e0),
+ INTC_VECT(SCIF1, 0x900), INTC_VECT(SCIF1, 0x920),
+ INTC_VECT(SCIF1, 0x940), INTC_VECT(SCIF1, 0x960),
+ INTC_VECT(DMAC2, 0xb80), INTC_VECT(DMAC2, 0xba0),
#ifdef CONFIG_CPU_SUBTYPE_SH7710
INTC_VECT(IPSEC, 0xbe0),
#endif
INTC_VECT(EDMAC0, 0xc00), INTC_VECT(EDMAC1, 0xc20),
INTC_VECT(EDMAC2, 0xc40),
- INTC_VECT(SIOF0_ERI, 0xe00), INTC_VECT(SIOF0_TXI, 0xe20),
- INTC_VECT(SIOF0_RXI, 0xe40), INTC_VECT(SIOF0_CCI, 0xe60),
- INTC_VECT(SIOF1_ERI, 0xe80), INTC_VECT(SIOF1_TXI, 0xea0),
- INTC_VECT(SIOF1_RXI, 0xec0), INTC_VECT(SIOF1_CCI, 0xee0),
+ INTC_VECT(SIOF0, 0xe00), INTC_VECT(SIOF0, 0xe20),
+ INTC_VECT(SIOF0, 0xe40), INTC_VECT(SIOF0, 0xe60),
+ INTC_VECT(SIOF1, 0xe80), INTC_VECT(SIOF1, 0xea0),
+ INTC_VECT(SIOF1, 0xec0), INTC_VECT(SIOF1, 0xee0),
INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420),
INTC_VECT(TMU2, 0x440),
- INTC_VECT(RTC_ATI, 0x480), INTC_VECT(RTC_PRI, 0x4a0),
- INTC_VECT(RTC_CUI, 0x4c0),
+ INTC_VECT(RTC, 0x480), INTC_VECT(RTC, 0x4a0),
+ INTC_VECT(RTC, 0x4c0),
INTC_VECT(WDT, 0x560),
INTC_VECT(REF, 0x580),
};
-static struct intc_group groups[] __initdata = {
- INTC_GROUP(RTC, RTC_ATI, RTC_PRI, RTC_CUI),
- INTC_GROUP(DMAC1, DMAC_DEI0, DMAC_DEI1, DMAC_DEI2, DMAC_DEI3),
- INTC_GROUP(SCIF0, SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI),
- INTC_GROUP(SCIF1, SCIF1_ERI, SCIF1_RXI, SCIF1_BRI, SCIF1_TXI),
- INTC_GROUP(DMAC2, DMAC_DEI4, DMAC_DEI5),
- INTC_GROUP(SIOF0, SIOF0_ERI, SIOF0_TXI, SIOF0_RXI, SIOF0_CCI),
- INTC_GROUP(SIOF1, SIOF1_ERI, SIOF1_TXI, SIOF1_RXI, SIOF1_CCI),
-};
-
static struct intc_prio_reg prio_registers[] __initdata = {
{ 0xfffffee2, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } },
{ 0xfffffee4, 0, 16, 4, /* IPRB */ { WDT, REF, 0, 0 } },
@@ -85,7 +66,7 @@
{ 0xa4080006, 0, 16, 4, /* IPRI */ { 0, 0, SIOF1 } },
};
-static DECLARE_INTC_DESC(intc_desc, "sh7710", vectors, groups,
+static DECLARE_INTC_DESC(intc_desc, "sh7710", vectors, NULL,
NULL, prio_registers, NULL);
static struct resource rtc_resources[] = {
@@ -98,14 +79,6 @@
.start = 20,
.flags = IORESOURCE_IRQ,
},
- [2] = {
- .start = 21,
- .flags = IORESOURCE_IRQ,
- },
- [3] = {
- .start = 22,
- .flags = IORESOURCE_IRQ,
- },
};
static struct sh_rtc_platform_info rtc_info = {
@@ -127,12 +100,12 @@
.mapbase = 0xa4400000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 52, 53, 55, 54 },
+ .irqs = { 52, 52, 52, 52 },
}, {
.mapbase = 0xa4410000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 56, 57, 59, 58 },
+ .irqs = { 56, 56, 56, 56 },
}, {
.flags = 0,
diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7720.c b/arch/sh/kernel/cpu/sh3/setup-sh7720.c
index f807a21..003874a 100644
--- a/arch/sh/kernel/cpu/sh3/setup-sh7720.c
+++ b/arch/sh/kernel/cpu/sh3/setup-sh7720.c
@@ -2,6 +2,7 @@
* SH7720 Setup
*
* Copyright (C) 2007 Markus Brunner, Mark Jonas
+ * Copyright (C) 2009 Paul Mundt
*
* Based on arch/sh/kernel/cpu/sh4/setup-sh7750.c:
*
@@ -26,17 +27,7 @@
.flags = IORESOURCE_IO,
},
[1] = {
- /* Period IRQ */
- .start = 21,
- .flags = IORESOURCE_IRQ,
- },
- [2] = {
- /* Carry IRQ */
- .start = 22,
- .flags = IORESOURCE_IRQ,
- },
- [3] = {
- /* Alarm IRQ */
+ /* Shared Period/Carry/Alarm IRQ */
.start = 20,
.flags = IORESOURCE_IRQ,
},
@@ -150,62 +141,49 @@
UNUSED = 0,
/* interrupt sources */
- TMU0, TMU1, TMU2, RTC_ATI, RTC_PRI, RTC_CUI,
- WDT, REF_RCMI, SIM_ERI, SIM_RXI, SIM_TXI, SIM_TEND,
+ TMU0, TMU1, TMU2, RTC,
+ WDT, REF_RCMI, SIM,
IRQ0, IRQ1, IRQ2, IRQ3,
USBF_SPD, TMU_SUNI, IRQ5, IRQ4,
- DMAC1_DEI0, DMAC1_DEI1, DMAC1_DEI2, DMAC1_DEI3, LCDC, SSL,
- ADC, DMAC2_DEI4, DMAC2_DEI5, USBFI0, USBFI1, CMT,
+ DMAC1, LCDC, SSL,
+ ADC, DMAC2, USBFI, CMT,
SCIF0, SCIF1,
- PINT07, PINT815, TPU0, TPU1, TPU2, TPU3, IIC,
- SIOF0, SIOF1, MMCI0, MMCI1, MMCI2, MMCI3, PCC,
+ PINT07, PINT815, TPU, IIC,
+ SIOF0, SIOF1, MMC, PCC,
USBHI, AFEIF,
H_UDI,
- /* interrupt groups */
- TMU, RTC, SIM, DMAC1, USBFI, DMAC2, USB, TPU, MMC,
};
static struct intc_vect vectors[] __initdata = {
/* IRQ0->5 are handled in setup-sh3.c */
INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420),
- INTC_VECT(TMU2, 0x440), INTC_VECT(RTC_ATI, 0x480),
- INTC_VECT(RTC_PRI, 0x4a0), INTC_VECT(RTC_CUI, 0x4c0),
- INTC_VECT(SIM_ERI, 0x4e0), INTC_VECT(SIM_RXI, 0x500),
- INTC_VECT(SIM_TXI, 0x520), INTC_VECT(SIM_TEND, 0x540),
+ INTC_VECT(TMU2, 0x440), INTC_VECT(RTC, 0x480),
+ INTC_VECT(RTC, 0x4a0), INTC_VECT(RTC, 0x4c0),
+ INTC_VECT(SIM, 0x4e0), INTC_VECT(SIM, 0x500),
+ INTC_VECT(SIM, 0x520), INTC_VECT(SIM, 0x540),
INTC_VECT(WDT, 0x560), INTC_VECT(REF_RCMI, 0x580),
/* H_UDI cannot be masked */ INTC_VECT(TMU_SUNI, 0x6c0),
- INTC_VECT(USBF_SPD, 0x6e0), INTC_VECT(DMAC1_DEI0, 0x800),
- INTC_VECT(DMAC1_DEI1, 0x820), INTC_VECT(DMAC1_DEI2, 0x840),
- INTC_VECT(DMAC1_DEI3, 0x860), INTC_VECT(LCDC, 0x900),
+ INTC_VECT(USBF_SPD, 0x6e0), INTC_VECT(DMAC1, 0x800),
+ INTC_VECT(DMAC1, 0x820), INTC_VECT(DMAC1, 0x840),
+ INTC_VECT(DMAC1, 0x860), INTC_VECT(LCDC, 0x900),
#if defined(CONFIG_CPU_SUBTYPE_SH7720)
INTC_VECT(SSL, 0x980),
#endif
- INTC_VECT(USBFI0, 0xa20), INTC_VECT(USBFI1, 0xa40),
+ INTC_VECT(USBFI, 0xa20), INTC_VECT(USBFI, 0xa40),
INTC_VECT(USBHI, 0xa60),
- INTC_VECT(DMAC2_DEI4, 0xb80), INTC_VECT(DMAC2_DEI5, 0xba0),
+ INTC_VECT(DMAC2, 0xb80), INTC_VECT(DMAC2, 0xba0),
INTC_VECT(ADC, 0xbe0), INTC_VECT(SCIF0, 0xc00),
INTC_VECT(SCIF1, 0xc20), INTC_VECT(PINT07, 0xc80),
INTC_VECT(PINT815, 0xca0), INTC_VECT(SIOF0, 0xd00),
- INTC_VECT(SIOF1, 0xd20), INTC_VECT(TPU0, 0xd80),
- INTC_VECT(TPU1, 0xda0), INTC_VECT(TPU2, 0xdc0),
- INTC_VECT(TPU3, 0xde0), INTC_VECT(IIC, 0xe00),
- INTC_VECT(MMCI0, 0xe80), INTC_VECT(MMCI1, 0xea0),
- INTC_VECT(MMCI2, 0xec0), INTC_VECT(MMCI3, 0xee0),
+ INTC_VECT(SIOF1, 0xd20), INTC_VECT(TPU, 0xd80),
+ INTC_VECT(TPU, 0xda0), INTC_VECT(TPU, 0xdc0),
+ INTC_VECT(TPU, 0xde0), INTC_VECT(IIC, 0xe00),
+ INTC_VECT(MMC, 0xe80), INTC_VECT(MMC, 0xea0),
+ INTC_VECT(MMC, 0xec0), INTC_VECT(MMC, 0xee0),
INTC_VECT(CMT, 0xf00), INTC_VECT(PCC, 0xf60),
INTC_VECT(AFEIF, 0xfe0),
};
-static struct intc_group groups[] __initdata = {
- INTC_GROUP(TMU, TMU0, TMU1, TMU2),
- INTC_GROUP(RTC, RTC_ATI, RTC_PRI, RTC_CUI),
- INTC_GROUP(SIM, SIM_ERI, SIM_RXI, SIM_TXI, SIM_TEND),
- INTC_GROUP(DMAC1, DMAC1_DEI0, DMAC1_DEI1, DMAC1_DEI2, DMAC1_DEI3),
- INTC_GROUP(USBFI, USBFI0, USBFI1),
- INTC_GROUP(DMAC2, DMAC2_DEI4, DMAC2_DEI5),
- INTC_GROUP(TPU, TPU0, TPU1, TPU2, TPU3),
- INTC_GROUP(MMC, MMCI0, MMCI1, MMCI2, MMCI3),
-};
-
static struct intc_prio_reg prio_registers[] __initdata = {
{ 0xA414FEE2UL, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } },
{ 0xA414FEE4UL, 0, 16, 4, /* IPRB */ { WDT, REF_RCMI, SIM, 0 } },
@@ -219,7 +197,7 @@
{ 0xA4080008UL, 0, 16, 4, /* IPRJ */ { 0, USBHI, 0, AFEIF } },
};
-static DECLARE_INTC_DESC(intc_desc, "sh7720", vectors, groups,
+static DECLARE_INTC_DESC(intc_desc, "sh7720", vectors, NULL,
NULL, prio_registers, NULL);
void __init plat_irq_setup(void)
diff --git a/arch/sh/kernel/cpu/sh3/swsusp.S b/arch/sh/kernel/cpu/sh3/swsusp.S
new file mode 100644
index 0000000..0114542
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh3/swsusp.S
@@ -0,0 +1,147 @@
+/*
+ * arch/sh/kernel/cpu/sh3/swsusp.S
+ *
+ * Copyright (C) 2009 Magnus Damm
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/sys.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/page.h>
+
+#define k0 r0
+#define k1 r1
+#define k2 r2
+#define k3 r3
+#define k4 r4
+
+! swsusp_arch_resume()
+! - copy restore_pblist pages
+! - restore registers from swsusp_arch_regs_cpu0
+
+ENTRY(swsusp_arch_resume)
+ mov.l 1f, r15
+ mov.l 2f, r4
+ mov.l @r4, r4
+
+swsusp_copy_loop:
+ mov r4, r0
+ cmp/eq #0, r0
+ bt swsusp_restore_regs
+
+ mov.l @(PBE_ADDRESS, r4), r2
+ mov.l @(PBE_ORIG_ADDRESS, r4), r5
+
+ mov #(PAGE_SIZE >> 10), r3
+ shll8 r3
+ shlr2 r3 /* PAGE_SIZE / 16 */
+swsusp_copy_page:
+ dt r3
+ mov.l @r2+,r1 /* 16n+0 */
+ mov.l r1,@r5
+ add #4,r5
+ mov.l @r2+,r1 /* 16n+4 */
+ mov.l r1,@r5
+ add #4,r5
+ mov.l @r2+,r1 /* 16n+8 */
+ mov.l r1,@r5
+ add #4,r5
+ mov.l @r2+,r1 /* 16n+12 */
+ mov.l r1,@r5
+ bf/s swsusp_copy_page
+ add #4,r5
+
+ bra swsusp_copy_loop
+ mov.l @(PBE_NEXT, r4), r4
+
+swsusp_restore_regs:
+ ! BL=0: R7->R0 is bank0
+ mov.l 3f, r8
+ mov.l 4f, r5
+ jsr @r5
+ nop
+
+ ! BL=1: R7->R0 is bank1
+ lds k2, pr
+ ldc k3, ssr
+
+ mov.l @r15+, r0
+ mov.l @r15+, r1
+ mov.l @r15+, r2
+ mov.l @r15+, r3
+ mov.l @r15+, r4
+ mov.l @r15+, r5
+ mov.l @r15+, r6
+ mov.l @r15+, r7
+
+ rte
+ nop
+ ! BL=0: R7->R0 is bank0
+
+ .align 2
+1: .long swsusp_arch_regs_cpu0
+2: .long restore_pblist
+3: .long 0x20000000 ! RB=1
+4: .long restore_regs
+
+! swsusp_arch_suspend()
+! - prepare pc for resume, return from function without swsusp_save on resume
+! - save registers in swsusp_arch_regs_cpu0
+! - call swsusp_save write suspend image
+
+ENTRY(swsusp_arch_suspend)
+ sts pr, r0 ! save pr in r0
+ mov r15, r2 ! save sp in r2
+ mov r8, r5 ! save r8 in r5
+ stc sr, r1
+ ldc r1, ssr ! save sr in ssr
+ mov.l 1f, r1
+ ldc r1, spc ! setup pc value for resuming
+ mov.l 5f, r15 ! use swsusp_arch_regs_cpu0 as stack
+ mov.l 6f, r3
+ add r3, r15 ! save from top of structure
+
+ ! BL=0: R7->R0 is bank0
+ mov.l 2f, r3 ! get new SR value for bank1
+ mov #0, r4
+ mov.l 7f, r1
+ jsr @r1 ! switch to bank1 and save bank1 r7->r0
+ not r4, r4
+
+ ! BL=1: R7->R0 is bank1
+ stc r2_bank, k0 ! fetch old sp from r2_bank0
+ mov.l 3f, k4 ! SR bits to clear in k4
+ mov.l 8f, k1
+ jsr @k1 ! switch to bank0 and save all regs
+ stc r0_bank, k3 ! fetch old pr from r0_bank0
+
+ ! BL=0: R7->R0 is bank0
+ mov r2, r15 ! restore old sp
+ mov r5, r8 ! restore old r8
+ stc ssr, r1
+ ldc r1, sr ! restore old sr
+ lds r0, pr ! restore old pr
+ mov.l 4f, r0
+ jmp @r0
+ nop
+
+swsusp_call_save:
+ mov r2, r15 ! restore old sp
+ mov r5, r8 ! restore old r8
+ lds r0, pr ! restore old pr
+ rts
+ mov #0, r0
+
+ .align 2
+1: .long swsusp_call_save
+2: .long 0x20000000 ! RB=1
+3: .long 0xdfffffff ! RB=0
+4: .long swsusp_save
+5: .long swsusp_arch_regs_cpu0
+6: .long SWSUSP_ARCH_REGS_SIZE
+7: .long save_low_regs
+8: .long save_regs
diff --git a/arch/sh/kernel/cpu/sh4/Makefile b/arch/sh/kernel/cpu/sh4/Makefile
index d608557..203b183 100644
--- a/arch/sh/kernel/cpu/sh4/Makefile
+++ b/arch/sh/kernel/cpu/sh4/Makefile
@@ -5,6 +5,7 @@
obj-y := probe.o common.o
common-y += $(addprefix ../sh3/, entry.o ex.o)
+obj-$(CONFIG_HIBERNATION) += $(addprefix ../sh3/, swsusp.o)
obj-$(CONFIG_SH_FPU) += fpu.o softfloat.o
obj-$(CONFIG_SH_STORE_QUEUES) += sq.o
diff --git a/arch/sh/kernel/cpu/sh4/probe.c b/arch/sh/kernel/cpu/sh4/probe.c
index 2e42572..3d3a3c4 100644
--- a/arch/sh/kernel/cpu/sh4/probe.c
+++ b/arch/sh/kernel/cpu/sh4/probe.c
@@ -129,6 +129,13 @@
boot_cpu_data.flags |= CPU_HAS_FPU | CPU_HAS_PERF_COUNTER |
CPU_HAS_LLSC;
break;
+ case 0x4004:
+ boot_cpu_data.type = CPU_SH7786;
+ boot_cpu_data.icache.ways = 4;
+ boot_cpu_data.dcache.ways = 4;
+ boot_cpu_data.flags |= CPU_HAS_FPU | CPU_HAS_PERF_COUNTER |
+ CPU_HAS_LLSC | CPU_HAS_PTEAEX;
+ break;
case 0x3008:
boot_cpu_data.icache.ways = 4;
boot_cpu_data.dcache.ways = 4;
diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7750.c b/arch/sh/kernel/cpu/sh4/setup-sh7750.c
index ec88403..a1c80d9 100644
--- a/arch/sh/kernel/cpu/sh4/setup-sh7750.c
+++ b/arch/sh/kernel/cpu/sh4/setup-sh7750.c
@@ -21,17 +21,7 @@
.flags = IORESOURCE_IO,
},
[1] = {
- /* Period IRQ */
- .start = 21,
- .flags = IORESOURCE_IRQ,
- },
- [2] = {
- /* Carry IRQ */
- .start = 22,
- .flags = IORESOURCE_IRQ,
- },
- [3] = {
- /* Alarm IRQ */
+ /* Shared Period/Carry/Alarm IRQ */
.start = 20,
.flags = IORESOURCE_IRQ,
},
@@ -50,13 +40,13 @@
.mapbase = 0xffe00000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCI,
- .irqs = { 23, 24, 25, 0 },
+ .irqs = { 23, 23, 23, 0 },
}, {
#endif
.mapbase = 0xffe80000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 40, 41, 43, 42 },
+ .irqs = { 40, 40, 40, 40 },
}, {
.flags = 0,
}
@@ -87,43 +77,27 @@
/* interrupt sources */
IRL0, IRL1, IRL2, IRL3, /* only IRLM mode supported */
- HUDI, GPIOI,
- DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, DMAC_DMTE3,
- DMAC_DMTE4, DMAC_DMTE5, DMAC_DMTE6, DMAC_DMTE7,
- DMAC_DMAE,
+ HUDI, GPIOI, DMAC,
PCIC0_PCISERR, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON,
PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3,
- TMU3, TMU4, TMU0, TMU1, TMU2_TUNI, TMU2_TICPI,
- RTC_ATI, RTC_PRI, RTC_CUI,
- SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI,
- SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI,
- WDT,
- REF_RCMI, REF_ROVI,
+ TMU3, TMU4, TMU0, TMU1, TMU2, RTC, SCI1, SCIF, WDT, REF,
/* interrupt groups */
- DMAC, PCIC1, TMU2, RTC, SCI1, SCIF, REF,
+ PCIC1,
};
static struct intc_vect vectors[] __initdata = {
INTC_VECT(HUDI, 0x600), INTC_VECT(GPIOI, 0x620),
INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420),
- INTC_VECT(TMU2_TUNI, 0x440), INTC_VECT(TMU2_TICPI, 0x460),
- INTC_VECT(RTC_ATI, 0x480), INTC_VECT(RTC_PRI, 0x4a0),
- INTC_VECT(RTC_CUI, 0x4c0),
- INTC_VECT(SCI1_ERI, 0x4e0), INTC_VECT(SCI1_RXI, 0x500),
- INTC_VECT(SCI1_TXI, 0x520), INTC_VECT(SCI1_TEI, 0x540),
- INTC_VECT(SCIF_ERI, 0x700), INTC_VECT(SCIF_RXI, 0x720),
- INTC_VECT(SCIF_BRI, 0x740), INTC_VECT(SCIF_TXI, 0x760),
+ INTC_VECT(TMU2, 0x440), INTC_VECT(TMU2, 0x460),
+ INTC_VECT(RTC, 0x480), INTC_VECT(RTC, 0x4a0),
+ INTC_VECT(RTC, 0x4c0),
+ INTC_VECT(SCI1, 0x4e0), INTC_VECT(SCI1, 0x500),
+ INTC_VECT(SCI1, 0x520), INTC_VECT(SCI1, 0x540),
+ INTC_VECT(SCIF, 0x700), INTC_VECT(SCIF, 0x720),
+ INTC_VECT(SCIF, 0x740), INTC_VECT(SCIF, 0x760),
INTC_VECT(WDT, 0x560),
- INTC_VECT(REF_RCMI, 0x580), INTC_VECT(REF_ROVI, 0x5a0),
-};
-
-static struct intc_group groups[] __initdata = {
- INTC_GROUP(TMU2, TMU2_TUNI, TMU2_TICPI),
- INTC_GROUP(RTC, RTC_ATI, RTC_PRI, RTC_CUI),
- INTC_GROUP(SCI1, SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI),
- INTC_GROUP(SCIF, SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI),
- INTC_GROUP(REF, REF_RCMI, REF_ROVI),
+ INTC_VECT(REF, 0x580), INTC_VECT(REF, 0x5a0),
};
static struct intc_prio_reg prio_registers[] __initdata = {
@@ -136,7 +110,7 @@
PCIC1, PCIC0_PCISERR } },
};
-static DECLARE_INTC_DESC(intc_desc, "sh7750", vectors, groups,
+static DECLARE_INTC_DESC(intc_desc, "sh7750", vectors, NULL,
NULL, prio_registers, NULL);
/* SH7750, SH7750S, SH7751 and SH7091 all have 4-channel DMA controllers */
@@ -145,39 +119,28 @@
defined(CONFIG_CPU_SUBTYPE_SH7751) || \
defined(CONFIG_CPU_SUBTYPE_SH7091)
static struct intc_vect vectors_dma4[] __initdata = {
- INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660),
- INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0),
- INTC_VECT(DMAC_DMAE, 0x6c0),
-};
-
-static struct intc_group groups_dma4[] __initdata = {
- INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2,
- DMAC_DMTE3, DMAC_DMAE),
+ INTC_VECT(DMAC, 0x640), INTC_VECT(DMAC, 0x660),
+ INTC_VECT(DMAC, 0x680), INTC_VECT(DMAC, 0x6a0),
+ INTC_VECT(DMAC, 0x6c0),
};
static DECLARE_INTC_DESC(intc_desc_dma4, "sh7750_dma4",
- vectors_dma4, groups_dma4,
+ vectors_dma4, NULL,
NULL, prio_registers, NULL);
#endif
/* SH7750R and SH7751R both have 8-channel DMA controllers */
#if defined(CONFIG_CPU_SUBTYPE_SH7750R) || defined(CONFIG_CPU_SUBTYPE_SH7751R)
static struct intc_vect vectors_dma8[] __initdata = {
- INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660),
- INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0),
- INTC_VECT(DMAC_DMTE4, 0x780), INTC_VECT(DMAC_DMTE5, 0x7a0),
- INTC_VECT(DMAC_DMTE6, 0x7c0), INTC_VECT(DMAC_DMTE7, 0x7e0),
- INTC_VECT(DMAC_DMAE, 0x6c0),
-};
-
-static struct intc_group groups_dma8[] __initdata = {
- INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2,
- DMAC_DMTE3, DMAC_DMTE4, DMAC_DMTE5,
- DMAC_DMTE6, DMAC_DMTE7, DMAC_DMAE),
+ INTC_VECT(DMAC, 0x640), INTC_VECT(DMAC, 0x660),
+ INTC_VECT(DMAC, 0x680), INTC_VECT(DMAC, 0x6a0),
+ INTC_VECT(DMAC, 0x780), INTC_VECT(DMAC, 0x7a0),
+ INTC_VECT(DMAC, 0x7c0), INTC_VECT(DMAC, 0x7e0),
+ INTC_VECT(DMAC, 0x6c0),
};
static DECLARE_INTC_DESC(intc_desc_dma8, "sh7750_dma8",
- vectors_dma8, groups_dma8,
+ vectors_dma8, NULL,
NULL, prio_registers, NULL);
#endif
diff --git a/arch/sh/kernel/cpu/sh4a/Makefile b/arch/sh/kernel/cpu/sh4a/Makefile
index 8e344ec..1a92361 100644
--- a/arch/sh/kernel/cpu/sh4a/Makefile
+++ b/arch/sh/kernel/cpu/sh4a/Makefile
@@ -7,6 +7,7 @@
obj-$(CONFIG_CPU_SUBTYPE_SH7770) += setup-sh7770.o
obj-$(CONFIG_CPU_SUBTYPE_SH7780) += setup-sh7780.o
obj-$(CONFIG_CPU_SUBTYPE_SH7785) += setup-sh7785.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7786) += setup-sh7786.o
obj-$(CONFIG_CPU_SUBTYPE_SH7343) += setup-sh7343.o
obj-$(CONFIG_CPU_SUBTYPE_SH7722) += setup-sh7722.o
obj-$(CONFIG_CPU_SUBTYPE_SH7723) += setup-sh7723.o
@@ -21,6 +22,7 @@
clock-$(CONFIG_CPU_SUBTYPE_SH7770) := clock-sh7770.o
clock-$(CONFIG_CPU_SUBTYPE_SH7780) := clock-sh7780.o
clock-$(CONFIG_CPU_SUBTYPE_SH7785) := clock-sh7785.o
+clock-$(CONFIG_CPU_SUBTYPE_SH7786) := clock-sh7786.o
clock-$(CONFIG_CPU_SUBTYPE_SH7343) := clock-sh7722.o
clock-$(CONFIG_CPU_SUBTYPE_SH7722) := clock-sh7722.o
clock-$(CONFIG_CPU_SUBTYPE_SH7723) := clock-sh7722.o
@@ -31,6 +33,7 @@
pinmux-$(CONFIG_CPU_SUBTYPE_SH7722) := pinmux-sh7722.o
pinmux-$(CONFIG_CPU_SUBTYPE_SH7723) := pinmux-sh7723.o
pinmux-$(CONFIG_CPU_SUBTYPE_SH7785) := pinmux-sh7785.o
+pinmux-$(CONFIG_CPU_SUBTYPE_SH7786) := pinmux-sh7786.o
obj-y += $(clock-y)
obj-$(CONFIG_SMP) += $(smp-y)
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7786.c b/arch/sh/kernel/cpu/sh4a/clock-sh7786.c
new file mode 100644
index 0000000..f84a9c1
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7786.c
@@ -0,0 +1,148 @@
+/*
+ * arch/sh/kernel/cpu/sh4a/clock-sh7786.c
+ *
+ * SH7786 support for the clock framework
+ *
+ * Copyright (C) 2008, 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on SH7785
+ * Copyright (C) 2007 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <asm/clock.h>
+#include <asm/freq.h>
+#include <asm/io.h>
+
+static int ifc_divisors[] = { 1, 2, 4, 1 };
+static int sfc_divisors[] = { 1, 1, 4, 1 };
+static int bfc_divisors[] = { 1, 1, 1, 1, 1, 12, 16, 1,
+ 24, 32, 1, 1, 1, 1, 1, 1 };
+static int mfc_divisors[] = { 1, 1, 4, 1 };
+static int pfc_divisors[] = { 1, 1, 1, 1, 1, 1, 16, 1,
+ 24, 32, 1, 48, 1, 1, 1, 1 };
+
+static void master_clk_init(struct clk *clk)
+{
+ clk->rate *= pfc_divisors[ctrl_inl(FRQMR1) & 0x000f];
+}
+
+static struct clk_ops sh7786_master_clk_ops = {
+ .init = master_clk_init,
+};
+
+static void module_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inl(FRQMR1) & 0x000f);
+ clk->rate = clk->parent->rate / pfc_divisors[idx];
+}
+
+static struct clk_ops sh7786_module_clk_ops = {
+ .recalc = module_clk_recalc,
+};
+
+static void bus_clk_recalc(struct clk *clk)
+{
+ int idx = ((ctrl_inl(FRQMR1) >> 16) & 0x000f);
+ clk->rate = clk->parent->rate / bfc_divisors[idx];
+}
+
+static struct clk_ops sh7786_bus_clk_ops = {
+ .recalc = bus_clk_recalc,
+};
+
+static void cpu_clk_recalc(struct clk *clk)
+{
+ int idx = ((ctrl_inl(FRQMR1) >> 28) & 0x0003);
+ clk->rate = clk->parent->rate / ifc_divisors[idx];
+}
+
+static struct clk_ops sh7786_cpu_clk_ops = {
+ .recalc = cpu_clk_recalc,
+};
+
+static struct clk_ops *sh7786_clk_ops[] = {
+ &sh7786_master_clk_ops,
+ &sh7786_module_clk_ops,
+ &sh7786_bus_clk_ops,
+ &sh7786_cpu_clk_ops,
+};
+
+void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
+{
+ if (idx < ARRAY_SIZE(sh7786_clk_ops))
+ *ops = sh7786_clk_ops[idx];
+}
+
+static void shyway_clk_recalc(struct clk *clk)
+{
+ int idx = ((ctrl_inl(FRQMR1) >> 20) & 0x0003);
+ clk->rate = clk->parent->rate / sfc_divisors[idx];
+}
+
+static struct clk_ops sh7786_shyway_clk_ops = {
+ .recalc = shyway_clk_recalc,
+};
+
+static struct clk sh7786_shyway_clk = {
+ .name = "shyway_clk",
+ .flags = CLK_ALWAYS_ENABLED,
+ .ops = &sh7786_shyway_clk_ops,
+};
+
+static void ddr_clk_recalc(struct clk *clk)
+{
+ int idx = ((ctrl_inl(FRQMR1) >> 12) & 0x0003);
+ clk->rate = clk->parent->rate / mfc_divisors[idx];
+}
+
+static struct clk_ops sh7786_ddr_clk_ops = {
+ .recalc = ddr_clk_recalc,
+};
+
+static struct clk sh7786_ddr_clk = {
+ .name = "ddr_clk",
+ .flags = CLK_ALWAYS_ENABLED,
+ .ops = &sh7786_ddr_clk_ops,
+};
+
+/*
+ * Additional SH7786-specific on-chip clocks that aren't already part of the
+ * clock framework
+ */
+static struct clk *sh7786_onchip_clocks[] = {
+ &sh7786_shyway_clk,
+ &sh7786_ddr_clk,
+};
+
+static int __init sh7786_clk_init(void)
+{
+ struct clk *clk = clk_get(NULL, "master_clk");
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sh7786_onchip_clocks); i++) {
+ struct clk *clkp = sh7786_onchip_clocks[i];
+
+ clkp->parent = clk;
+ clk_register(clkp);
+ clk_enable(clkp);
+ }
+
+ /*
+ * Now that we have the rest of the clocks registered, we need to
+ * force the parent clock to propagate so that these clocks will
+ * automatically figure out their rate. We cheat by handing the
+ * parent clock its current rate and forcing child propagation.
+ */
+ clk_set_rate(clk, clk_get_rate(clk));
+
+ clk_put(clk);
+
+ return 0;
+}
+arch_initcall(sh7786_clk_init);
diff --git a/arch/sh/kernel/cpu/sh4a/pinmux-sh7786.c b/arch/sh/kernel/cpu/sh4a/pinmux-sh7786.c
new file mode 100644
index 0000000..373b344
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4a/pinmux-sh7786.c
@@ -0,0 +1,950 @@
+/*
+ * SH7786 Pinmux
+ *
+ * Copyright (C) 2008, 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on SH7785 pinmux
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <cpu/sh7786.h>
+
+enum {
+ PINMUX_RESERVED = 0,
+
+ PINMUX_DATA_BEGIN,
+ PA7_DATA, PA6_DATA, PA5_DATA, PA4_DATA,
+ PA3_DATA, PA2_DATA, PA1_DATA, PA0_DATA,
+ PB7_DATA, PB6_DATA, PB5_DATA, PB4_DATA,
+ PB3_DATA, PB2_DATA, PB1_DATA, PB0_DATA,
+ PC7_DATA, PC6_DATA, PC5_DATA, PC4_DATA,
+ PC3_DATA, PC2_DATA, PC1_DATA, PC0_DATA,
+ PD7_DATA, PD6_DATA, PD5_DATA, PD4_DATA,
+ PD3_DATA, PD2_DATA, PD1_DATA, PD0_DATA,
+ PE7_DATA, PE6_DATA,
+ PF7_DATA, PF6_DATA, PF5_DATA, PF4_DATA,
+ PF3_DATA, PF2_DATA, PF1_DATA, PF0_DATA,
+ PG7_DATA, PG6_DATA, PG5_DATA,
+ PH7_DATA, PH6_DATA, PH5_DATA, PH4_DATA,
+ PH3_DATA, PH2_DATA, PH1_DATA, PH0_DATA,
+ PJ7_DATA, PJ6_DATA, PJ5_DATA, PJ4_DATA,
+ PJ3_DATA, PJ2_DATA, PJ1_DATA,
+ PINMUX_DATA_END,
+
+ PINMUX_INPUT_BEGIN,
+ PA7_IN, PA6_IN, PA5_IN, PA4_IN,
+ PA3_IN, PA2_IN, PA1_IN, PA0_IN,
+ PB7_IN, PB6_IN, PB5_IN, PB4_IN,
+ PB3_IN, PB2_IN, PB1_IN, PB0_IN,
+ PC7_IN, PC6_IN, PC5_IN, PC4_IN,
+ PC3_IN, PC2_IN, PC1_IN, PC0_IN,
+ PD7_IN, PD6_IN, PD5_IN, PD4_IN,
+ PD3_IN, PD2_IN, PD1_IN, PD0_IN,
+ PE7_IN, PE6_IN,
+ PF7_IN, PF6_IN, PF5_IN, PF4_IN,
+ PF3_IN, PF2_IN, PF1_IN, PF0_IN,
+ PG7_IN, PG6_IN, PG5_IN,
+ PH7_IN, PH6_IN, PH5_IN, PH4_IN,
+ PH3_IN, PH2_IN, PH1_IN, PH0_IN,
+ PJ7_IN, PJ6_IN, PJ5_IN, PJ4_IN,
+ PJ3_IN, PJ2_IN, PJ1_IN,
+ PINMUX_INPUT_END,
+
+ PINMUX_INPUT_PULLUP_BEGIN,
+ PA7_IN_PU, PA6_IN_PU, PA5_IN_PU, PA4_IN_PU,
+ PA3_IN_PU, PA2_IN_PU, PA1_IN_PU, PA0_IN_PU,
+ PB7_IN_PU, PB6_IN_PU, PB5_IN_PU, PB4_IN_PU,
+ PB3_IN_PU, PB2_IN_PU, PB1_IN_PU, PB0_IN_PU,
+ PC7_IN_PU, PC6_IN_PU, PC5_IN_PU, PC4_IN_PU,
+ PC3_IN_PU, PC2_IN_PU, PC1_IN_PU, PC0_IN_PU,
+ PD7_IN_PU, PD6_IN_PU, PD5_IN_PU, PD4_IN_PU,
+ PD3_IN_PU, PD2_IN_PU, PD1_IN_PU, PD0_IN_PU,
+ PE7_IN_PU, PE6_IN_PU,
+ PF7_IN_PU, PF6_IN_PU, PF5_IN_PU, PF4_IN_PU,
+ PF3_IN_PU, PF2_IN_PU, PF1_IN_PU, PF0_IN_PU,
+ PG7_IN_PU, PG6_IN_PU, PG5_IN_PU,
+ PH7_IN_PU, PH6_IN_PU, PH5_IN_PU, PH4_IN_PU,
+ PH3_IN_PU, PH2_IN_PU, PH1_IN_PU, PH0_IN_PU,
+ PJ7_IN_PU, PJ6_IN_PU, PJ5_IN_PU, PJ4_IN_PU,
+ PJ3_IN_PU, PJ2_IN_PU, PJ1_IN_PU,
+ PINMUX_INPUT_PULLUP_END,
+
+ PINMUX_OUTPUT_BEGIN,
+ PA7_OUT, PA6_OUT, PA5_OUT, PA4_OUT,
+ PA3_OUT, PA2_OUT, PA1_OUT, PA0_OUT,
+ PB7_OUT, PB6_OUT, PB5_OUT, PB4_OUT,
+ PB3_OUT, PB2_OUT, PB1_OUT, PB0_OUT,
+ PC7_OUT, PC6_OUT, PC5_OUT, PC4_OUT,
+ PC3_OUT, PC2_OUT, PC1_OUT, PC0_OUT,
+ PD7_OUT, PD6_OUT, PD5_OUT, PD4_OUT,
+ PD3_OUT, PD2_OUT, PD1_OUT, PD0_OUT,
+ PE7_OUT, PE6_OUT,
+ PF7_OUT, PF6_OUT, PF5_OUT, PF4_OUT,
+ PF3_OUT, PF2_OUT, PF1_OUT, PF0_OUT,
+ PG7_OUT, PG6_OUT, PG5_OUT,
+ PH7_OUT, PH6_OUT, PH5_OUT, PH4_OUT,
+ PH3_OUT, PH2_OUT, PH1_OUT, PH0_OUT,
+ PJ7_OUT, PJ6_OUT, PJ5_OUT, PJ4_OUT,
+ PJ3_OUT, PJ2_OUT, PJ1_OUT,
+ PINMUX_OUTPUT_END,
+
+ PINMUX_FUNCTION_BEGIN,
+ PA7_FN, PA6_FN, PA5_FN, PA4_FN,
+ PA3_FN, PA2_FN, PA1_FN, PA0_FN,
+ PB7_FN, PB6_FN, PB5_FN, PB4_FN,
+ PB3_FN, PB2_FN, PB1_FN, PB0_FN,
+ PC7_FN, PC6_FN, PC5_FN, PC4_FN,
+ PC3_FN, PC2_FN, PC1_FN, PC0_FN,
+ PD7_FN, PD6_FN, PD5_FN, PD4_FN,
+ PD3_FN, PD2_FN, PD1_FN, PD0_FN,
+ PE7_FN, PE6_FN,
+ PF7_FN, PF6_FN, PF5_FN, PF4_FN,
+ PF3_FN, PF2_FN, PF1_FN, PF0_FN,
+ PG7_FN, PG6_FN, PG5_FN,
+ PH7_FN, PH6_FN, PH5_FN, PH4_FN,
+ PH3_FN, PH2_FN, PH1_FN, PH0_FN,
+ PJ7_FN, PJ6_FN, PJ5_FN, PJ4_FN,
+ PJ3_FN, PJ2_FN, PJ1_FN,
+ P1MSEL14_0, P1MSEL14_1,
+ P1MSEL13_0, P1MSEL13_1,
+ P1MSEL12_0, P1MSEL12_1,
+ P1MSEL11_0, P1MSEL11_1,
+ P1MSEL10_0, P1MSEL10_1,
+ P1MSEL9_0, P1MSEL9_1,
+ P1MSEL8_0, P1MSEL8_1,
+ P1MSEL7_0, P1MSEL7_1,
+ P1MSEL6_0, P1MSEL6_1,
+ P1MSEL5_0, P1MSEL5_1,
+ P1MSEL4_0, P1MSEL4_1,
+ P1MSEL3_0, P1MSEL3_1,
+ P1MSEL2_0, P1MSEL2_1,
+ P1MSEL1_0, P1MSEL1_1,
+ P1MSEL0_0, P1MSEL0_1,
+
+ P2MSEL15_0, P2MSEL15_1,
+ P2MSEL14_0, P2MSEL14_1,
+ P2MSEL13_0, P2MSEL13_1,
+ P2MSEL12_0, P2MSEL12_1,
+ P2MSEL11_0, P2MSEL11_1,
+ P2MSEL10_0, P2MSEL10_1,
+ P2MSEL9_0, P2MSEL9_1,
+ P2MSEL8_0, P2MSEL8_1,
+ P2MSEL7_0, P2MSEL7_1,
+ P2MSEL6_0, P2MSEL6_1,
+ P2MSEL5_0, P2MSEL5_1,
+ P2MSEL4_0, P2MSEL4_1,
+ P2MSEL3_0, P2MSEL3_1,
+ P2MSEL2_0, P2MSEL2_1,
+ P2MSEL1_0, P2MSEL1_1,
+ P2MSEL0_0, P2MSEL0_1,
+ PINMUX_FUNCTION_END,
+
+ PINMUX_MARK_BEGIN,
+ CDE_MARK,
+ ETH_MAGIC_MARK,
+ DISP_MARK,
+ ETH_LINK_MARK,
+ DR5_MARK,
+ ETH_TX_ER_MARK,
+ DR4_MARK,
+ ETH_TX_EN_MARK,
+ DR3_MARK,
+ ETH_TXD3_MARK,
+ DR2_MARK,
+ ETH_TXD2_MARK,
+ DR1_MARK,
+ ETH_TXD1_MARK,
+ DR0_MARK,
+ ETH_TXD0_MARK,
+
+ VSYNC_MARK,
+ HSPI_CLK_MARK,
+ ODDF_MARK,
+ HSPI_CS_MARK,
+ DG5_MARK,
+ ETH_MDIO_MARK,
+ DG4_MARK,
+ ETH_RX_CLK_MARK,
+ DG3_MARK,
+ ETH_MDC_MARK,
+ DG2_MARK,
+ ETH_COL_MARK,
+ DG1_MARK,
+ ETH_TX_CLK_MARK,
+ DG0_MARK,
+ ETH_CRS_MARK,
+
+ DCLKIN_MARK,
+ HSPI_RX_MARK,
+ HSYNC_MARK,
+ HSPI_TX_MARK,
+ DB5_MARK,
+ ETH_RXD3_MARK,
+ DB4_MARK,
+ ETH_RXD2_MARK,
+ DB3_MARK,
+ ETH_RXD1_MARK,
+ DB2_MARK,
+ ETH_RXD0_MARK,
+ DB1_MARK,
+ ETH_RX_DV_MARK,
+ DB0_MARK,
+ ETH_RX_ER_MARK,
+
+ DCLKOUT_MARK,
+ SCIF1_SLK_MARK,
+ SCIF1_RXD_MARK,
+ SCIF1_TXD_MARK,
+ DACK1_MARK,
+ BACK_MARK,
+ FALE_MARK,
+ DACK0_MARK,
+ FCLE_MARK,
+ DREQ1_MARK,
+ BREQ_MARK,
+ USB_OVC1_MARK,
+ DREQ0_MARK,
+ USB_OVC0_MARK,
+
+ USB_PENC1_MARK,
+ USB_PENC0_MARK,
+
+ HAC1_SDOUT_MARK,
+ SSI1_SDATA_MARK,
+ SDIF1CMD_MARK,
+ HAC1_SDIN_MARK,
+ SSI1_SCK_MARK,
+ SDIF1CD_MARK,
+ HAC1_SYNC_MARK,
+ SSI1_WS_MARK,
+ SDIF1WP_MARK,
+ HAC1_BITCLK_MARK,
+ SSI1_CLK_MARK,
+ SDIF1CLK_MARK,
+ HAC0_SDOUT_MARK,
+ SSI0_SDATA_MARK,
+ SDIF1D3_MARK,
+ HAC0_SDIN_MARK,
+ SSI0_SCK_MARK,
+ SDIF1D2_MARK,
+ HAC0_SYNC_MARK,
+ SSI0_WS_MARK,
+ SDIF1D1_MARK,
+ HAC0_BITCLK_MARK,
+ SSI0_CLK_MARK,
+ SDIF1D0_MARK,
+
+ SCIF3_SCK_MARK,
+ SSI2_SDATA_MARK,
+ SCIF3_RXD_MARK,
+ TCLK_MARK,
+ SSI2_SCK_MARK,
+ SCIF3_TXD_MARK,
+ HAC_RES_MARK,
+ SSI2_WS_MARK,
+
+ DACK3_MARK,
+ SDIF0CMD_MARK,
+ DACK2_MARK,
+ SDIF0CD_MARK,
+ DREQ3_MARK,
+ SDIF0WP_MARK,
+ SCIF0_CTS_MARK,
+ DREQ2_MARK,
+ SDIF0CLK_MARK,
+ SCIF0_RTS_MARK,
+ IRL7_MARK,
+ SDIF0D3_MARK,
+ SCIF0_SCK_MARK,
+ IRL6_MARK,
+ SDIF0D2_MARK,
+ SCIF0_RXD_MARK,
+ IRL5_MARK,
+ SDIF0D1_MARK,
+ SCIF0_TXD_MARK,
+ IRL4_MARK,
+ SDIF0D0_MARK,
+
+ SCIF5_SCK_MARK,
+ FRB_MARK,
+ SCIF5_RXD_MARK,
+ IOIS16_MARK,
+ SCIF5_TXD_MARK,
+ CE2B_MARK,
+ DRAK3_MARK,
+ CE2A_MARK,
+ SCIF4_SCK_MARK,
+ DRAK2_MARK,
+ SSI3_WS_MARK,
+ SCIF4_RXD_MARK,
+ DRAK1_MARK,
+ SSI3_SDATA_MARK,
+ FSTATUS_MARK,
+ SCIF4_TXD_MARK,
+ DRAK0_MARK,
+ SSI3_SCK_MARK,
+ FSE_MARK,
+ PINMUX_MARK_END,
+};
+
+static pinmux_enum_t pinmux_data[] = {
+
+ /* PA GPIO */
+ PINMUX_DATA(PA7_DATA, PA7_IN, PA7_OUT, PA7_IN_PU),
+ PINMUX_DATA(PA6_DATA, PA6_IN, PA6_OUT, PA6_IN_PU),
+ PINMUX_DATA(PA5_DATA, PA5_IN, PA5_OUT, PA5_IN_PU),
+ PINMUX_DATA(PA4_DATA, PA4_IN, PA4_OUT, PA4_IN_PU),
+ PINMUX_DATA(PA3_DATA, PA3_IN, PA3_OUT, PA3_IN_PU),
+ PINMUX_DATA(PA2_DATA, PA2_IN, PA2_OUT, PA2_IN_PU),
+ PINMUX_DATA(PA1_DATA, PA1_IN, PA1_OUT, PA1_IN_PU),
+ PINMUX_DATA(PA0_DATA, PA0_IN, PA0_OUT, PA0_IN_PU),
+
+ /* PB GPIO */
+ PINMUX_DATA(PB7_DATA, PB7_IN, PB7_OUT, PB7_IN_PU),
+ PINMUX_DATA(PB6_DATA, PB6_IN, PB6_OUT, PB6_IN_PU),
+ PINMUX_DATA(PB5_DATA, PB5_IN, PB5_OUT, PB5_IN_PU),
+ PINMUX_DATA(PB4_DATA, PB4_IN, PB4_OUT, PB4_IN_PU),
+ PINMUX_DATA(PB3_DATA, PB3_IN, PB3_OUT, PB3_IN_PU),
+ PINMUX_DATA(PB2_DATA, PB2_IN, PB2_OUT, PB2_IN_PU),
+ PINMUX_DATA(PB1_DATA, PB1_IN, PB1_OUT, PB1_IN_PU),
+ PINMUX_DATA(PB0_DATA, PB0_IN, PB0_OUT, PB0_IN_PU),
+
+ /* PC GPIO */
+ PINMUX_DATA(PC7_DATA, PC7_IN, PC7_OUT, PC7_IN_PU),
+ PINMUX_DATA(PC6_DATA, PC6_IN, PC6_OUT, PC6_IN_PU),
+ PINMUX_DATA(PC5_DATA, PC5_IN, PC5_OUT, PC5_IN_PU),
+ PINMUX_DATA(PC4_DATA, PC4_IN, PC4_OUT, PC4_IN_PU),
+ PINMUX_DATA(PC3_DATA, PC3_IN, PC3_OUT, PC3_IN_PU),
+ PINMUX_DATA(PC2_DATA, PC2_IN, PC2_OUT, PC2_IN_PU),
+ PINMUX_DATA(PC1_DATA, PC1_IN, PC1_OUT, PC1_IN_PU),
+ PINMUX_DATA(PC0_DATA, PC0_IN, PC0_OUT, PC0_IN_PU),
+
+ /* PD GPIO */
+ PINMUX_DATA(PD7_DATA, PD7_IN, PD7_OUT, PD7_IN_PU),
+ PINMUX_DATA(PD6_DATA, PD6_IN, PD6_OUT, PD6_IN_PU),
+ PINMUX_DATA(PD5_DATA, PD5_IN, PD5_OUT, PD5_IN_PU),
+ PINMUX_DATA(PD4_DATA, PD4_IN, PD4_OUT, PD4_IN_PU),
+ PINMUX_DATA(PD3_DATA, PD3_IN, PD3_OUT, PD3_IN_PU),
+ PINMUX_DATA(PD2_DATA, PD2_IN, PD2_OUT, PD2_IN_PU),
+ PINMUX_DATA(PD1_DATA, PD1_IN, PD1_OUT, PD1_IN_PU),
+ PINMUX_DATA(PD0_DATA, PD0_IN, PD0_OUT, PD0_IN_PU),
+
+ /* PE GPIO */
+ PINMUX_DATA(PE7_DATA, PE7_IN, PE7_OUT, PE7_IN_PU),
+ PINMUX_DATA(PE6_DATA, PE6_IN, PE6_OUT, PE6_IN_PU),
+
+ /* PF GPIO */
+ PINMUX_DATA(PF7_DATA, PF7_IN, PF7_OUT, PF7_IN_PU),
+ PINMUX_DATA(PF6_DATA, PF6_IN, PF6_OUT, PF6_IN_PU),
+ PINMUX_DATA(PF5_DATA, PF5_IN, PF5_OUT, PF5_IN_PU),
+ PINMUX_DATA(PF4_DATA, PF4_IN, PF4_OUT, PF4_IN_PU),
+ PINMUX_DATA(PF3_DATA, PF3_IN, PF3_OUT, PF3_IN_PU),
+ PINMUX_DATA(PF2_DATA, PF2_IN, PF2_OUT, PF2_IN_PU),
+ PINMUX_DATA(PF1_DATA, PF1_IN, PF1_OUT, PF1_IN_PU),
+ PINMUX_DATA(PF0_DATA, PF0_IN, PF0_OUT, PF0_IN_PU),
+
+ /* PG GPIO */
+ PINMUX_DATA(PG7_DATA, PG7_IN, PG7_OUT, PG7_IN_PU),
+ PINMUX_DATA(PG6_DATA, PG6_IN, PG6_OUT, PG6_IN_PU),
+ PINMUX_DATA(PG5_DATA, PG5_IN, PG5_OUT, PG5_IN_PU),
+
+ /* PH GPIO */
+ PINMUX_DATA(PH7_DATA, PH7_IN, PH7_OUT, PH7_IN_PU),
+ PINMUX_DATA(PH6_DATA, PH6_IN, PH6_OUT, PH6_IN_PU),
+ PINMUX_DATA(PH5_DATA, PH5_IN, PH5_OUT, PH5_IN_PU),
+ PINMUX_DATA(PH4_DATA, PH4_IN, PH4_OUT, PH4_IN_PU),
+ PINMUX_DATA(PH3_DATA, PH3_IN, PH3_OUT, PH3_IN_PU),
+ PINMUX_DATA(PH2_DATA, PH2_IN, PH2_OUT, PH2_IN_PU),
+ PINMUX_DATA(PH1_DATA, PH1_IN, PH1_OUT, PH1_IN_PU),
+ PINMUX_DATA(PH0_DATA, PH0_IN, PH0_OUT, PH0_IN_PU),
+
+ /* PJ GPIO */
+ PINMUX_DATA(PJ7_DATA, PJ7_IN, PJ7_OUT, PJ7_IN_PU),
+ PINMUX_DATA(PJ6_DATA, PJ6_IN, PJ6_OUT, PJ6_IN_PU),
+ PINMUX_DATA(PJ5_DATA, PJ5_IN, PJ5_OUT, PJ5_IN_PU),
+ PINMUX_DATA(PJ4_DATA, PJ4_IN, PJ4_OUT, PJ4_IN_PU),
+ PINMUX_DATA(PJ3_DATA, PJ3_IN, PJ3_OUT, PJ3_IN_PU),
+ PINMUX_DATA(PJ2_DATA, PJ2_IN, PJ2_OUT, PJ2_IN_PU),
+ PINMUX_DATA(PJ1_DATA, PJ1_IN, PJ1_OUT, PJ1_IN_PU),
+
+ /* PA FN */
+ PINMUX_MARK_BEGIN,
+ PINMUX_DATA(CDE_MARK, P1MSEL2_0, PA7_FN),
+ PINMUX_DATA(DISP_MARK, P1MSEL2_0, PA6_FN),
+ PINMUX_DATA(DR5_MARK, P1MSEL2_0, PA5_FN),
+ PINMUX_DATA(DR4_MARK, P1MSEL2_0, PA4_FN),
+ PINMUX_DATA(DR3_MARK, P1MSEL2_0, PA3_FN),
+ PINMUX_DATA(DR2_MARK, P1MSEL2_0, PA2_FN),
+ PINMUX_DATA(DR1_MARK, P1MSEL2_0, PA1_FN),
+ PINMUX_DATA(DR0_MARK, P1MSEL2_0, PA0_FN),
+ PINMUX_DATA(ETH_MAGIC_MARK, P1MSEL2_1, PA7_FN),
+ PINMUX_DATA(ETH_LINK_MARK, P1MSEL2_1, PA6_FN),
+ PINMUX_DATA(ETH_TX_ER_MARK, P1MSEL2_1, PA5_FN),
+ PINMUX_DATA(ETH_TX_EN_MARK, P1MSEL2_1, PA4_FN),
+ PINMUX_DATA(ETH_TXD3_MARK, P1MSEL2_1, PA3_FN),
+ PINMUX_DATA(ETH_TXD2_MARK, P1MSEL2_1, PA2_FN),
+ PINMUX_DATA(ETH_TXD1_MARK, P1MSEL2_1, PA1_FN),
+ PINMUX_DATA(ETH_TXD0_MARK, P1MSEL2_1, PA0_FN),
+
+ /* PB FN */
+ PINMUX_DATA(VSYNC_MARK, P1MSEL3_0, PB7_FN),
+ PINMUX_DATA(ODDF_MARK, P1MSEL3_0, PB6_FN),
+ PINMUX_DATA(DG5_MARK, P1MSEL2_0, PB5_FN),
+ PINMUX_DATA(DG4_MARK, P1MSEL2_0, PB4_FN),
+ PINMUX_DATA(DG3_MARK, P1MSEL2_0, PB3_FN),
+ PINMUX_DATA(DG2_MARK, P1MSEL2_0, PB2_FN),
+ PINMUX_DATA(DG1_MARK, P1MSEL2_0, PB1_FN),
+ PINMUX_DATA(DG0_MARK, P1MSEL2_0, PB0_FN),
+ PINMUX_DATA(HSPI_CLK_MARK, P1MSEL3_1, PB7_FN),
+ PINMUX_DATA(HSPI_CS_MARK, P1MSEL3_1, PB6_FN),
+ PINMUX_DATA(ETH_MDIO_MARK, P1MSEL2_1, PB5_FN),
+ PINMUX_DATA(ETH_RX_CLK_MARK, P1MSEL2_1, PB4_FN),
+ PINMUX_DATA(ETH_MDC_MARK, P1MSEL2_1, PB3_FN),
+ PINMUX_DATA(ETH_COL_MARK, P1MSEL2_1, PB2_FN),
+ PINMUX_DATA(ETH_TX_CLK_MARK, P1MSEL2_1, PB1_FN),
+ PINMUX_DATA(ETH_CRS_MARK, P1MSEL2_1, PB0_FN),
+
+ /* PC FN */
+ PINMUX_DATA(DCLKIN_MARK, P1MSEL3_0, PC7_FN),
+ PINMUX_DATA(HSYNC_MARK, P1MSEL3_0, PC6_FN),
+ PINMUX_DATA(DB5_MARK, P1MSEL2_0, PC5_FN),
+ PINMUX_DATA(DB4_MARK, P1MSEL2_0, PC4_FN),
+ PINMUX_DATA(DB3_MARK, P1MSEL2_0, PC3_FN),
+ PINMUX_DATA(DB2_MARK, P1MSEL2_0, PC2_FN),
+ PINMUX_DATA(DB1_MARK, P1MSEL2_0, PC1_FN),
+ PINMUX_DATA(DB0_MARK, P1MSEL2_0, PC0_FN),
+
+ PINMUX_DATA(HSPI_RX_MARK, P1MSEL3_1, PC7_FN),
+ PINMUX_DATA(HSPI_TX_MARK, P1MSEL3_1, PC6_FN),
+ PINMUX_DATA(ETH_RXD3_MARK, P1MSEL2_1, PC5_FN),
+ PINMUX_DATA(ETH_RXD2_MARK, P1MSEL2_1, PC4_FN),
+ PINMUX_DATA(ETH_RXD1_MARK, P1MSEL2_1, PC3_FN),
+ PINMUX_DATA(ETH_RXD0_MARK, P1MSEL2_1, PC2_FN),
+ PINMUX_DATA(ETH_RX_DV_MARK, P1MSEL2_1, PC1_FN),
+ PINMUX_DATA(ETH_RX_ER_MARK, P1MSEL2_1, PC0_FN),
+
+ /* PD FN */
+ PINMUX_DATA(DCLKOUT_MARK, PD7_FN),
+ PINMUX_DATA(SCIF1_SLK_MARK, PD6_FN),
+ PINMUX_DATA(SCIF1_RXD_MARK, PD5_FN),
+ PINMUX_DATA(SCIF1_TXD_MARK, PD4_FN),
+ PINMUX_DATA(DACK1_MARK, P1MSEL13_1, P1MSEL12_0, PD3_FN),
+ PINMUX_DATA(BACK_MARK, P1MSEL13_0, P1MSEL12_1, PD3_FN),
+ PINMUX_DATA(FALE_MARK, P1MSEL13_0, P1MSEL12_0, PD3_FN),
+ PINMUX_DATA(DACK0_MARK, P1MSEL14_1, PD2_FN),
+ PINMUX_DATA(FCLE_MARK, P1MSEL14_0, PD2_FN),
+ PINMUX_DATA(DREQ1_MARK, P1MSEL10_0, P1MSEL9_1, PD1_FN),
+ PINMUX_DATA(BREQ_MARK, P1MSEL10_1, P1MSEL9_0, PD1_FN),
+ PINMUX_DATA(USB_OVC1_MARK, P1MSEL10_0, P1MSEL9_0, PD1_FN),
+ PINMUX_DATA(DREQ0_MARK, P1MSEL11_1, PD0_FN),
+ PINMUX_DATA(USB_OVC0_MARK, P1MSEL11_0, PD0_FN),
+
+ /* PE FN */
+ PINMUX_DATA(USB_PENC1_MARK, PE7_FN),
+ PINMUX_DATA(USB_PENC0_MARK, PE6_FN),
+
+ /* PF FN */
+ PINMUX_DATA(HAC1_SDOUT_MARK, P2MSEL15_0, P2MSEL14_0, PF7_FN),
+ PINMUX_DATA(HAC1_SDIN_MARK, P2MSEL15_0, P2MSEL14_0, PF6_FN),
+ PINMUX_DATA(HAC1_SYNC_MARK, P2MSEL15_0, P2MSEL14_0, PF5_FN),
+ PINMUX_DATA(HAC1_BITCLK_MARK, P2MSEL15_0, P2MSEL14_0, PF4_FN),
+ PINMUX_DATA(HAC0_SDOUT_MARK, P2MSEL13_0, P2MSEL12_0, PF3_FN),
+ PINMUX_DATA(HAC0_SDIN_MARK, P2MSEL13_0, P2MSEL12_0, PF2_FN),
+ PINMUX_DATA(HAC0_SYNC_MARK, P2MSEL13_0, P2MSEL12_0, PF1_FN),
+ PINMUX_DATA(HAC0_BITCLK_MARK, P2MSEL13_0, P2MSEL12_0, PF0_FN),
+ PINMUX_DATA(SSI1_SDATA_MARK, P2MSEL15_0, P2MSEL14_1, PF7_FN),
+ PINMUX_DATA(SSI1_SCK_MARK, P2MSEL15_0, P2MSEL14_1, PF6_FN),
+ PINMUX_DATA(SSI1_WS_MARK, P2MSEL15_0, P2MSEL14_1, PF5_FN),
+ PINMUX_DATA(SSI1_CLK_MARK, P2MSEL15_0, P2MSEL14_1, PF4_FN),
+ PINMUX_DATA(SSI0_SDATA_MARK, P2MSEL13_0, P2MSEL12_1, PF3_FN),
+ PINMUX_DATA(SSI0_SCK_MARK, P2MSEL13_0, P2MSEL12_1, PF2_FN),
+ PINMUX_DATA(SSI0_WS_MARK, P2MSEL13_0, P2MSEL12_1, PF1_FN),
+ PINMUX_DATA(SSI0_CLK_MARK, P2MSEL13_0, P2MSEL12_1, PF0_FN),
+ PINMUX_DATA(SDIF1CMD_MARK, P2MSEL15_1, P2MSEL14_0, PF7_FN),
+ PINMUX_DATA(SDIF1CD_MARK, P2MSEL15_1, P2MSEL14_0, PF6_FN),
+ PINMUX_DATA(SDIF1WP_MARK, P2MSEL15_1, P2MSEL14_0, PF5_FN),
+ PINMUX_DATA(SDIF1CLK_MARK, P2MSEL15_1, P2MSEL14_0, PF4_FN),
+ PINMUX_DATA(SDIF1D3_MARK, P2MSEL13_1, P2MSEL12_0, PF3_FN),
+ PINMUX_DATA(SDIF1D2_MARK, P2MSEL13_1, P2MSEL12_0, PF2_FN),
+ PINMUX_DATA(SDIF1D1_MARK, P2MSEL13_1, P2MSEL12_0, PF1_FN),
+ PINMUX_DATA(SDIF1D0_MARK, P2MSEL13_1, P2MSEL12_0, PF0_FN),
+
+ /* PG FN */
+ PINMUX_DATA(SCIF3_SCK_MARK, P1MSEL8_0, PG7_FN),
+ PINMUX_DATA(SSI2_SDATA_MARK, P1MSEL8_1, PG7_FN),
+ PINMUX_DATA(SCIF3_RXD_MARK, P1MSEL7_0, P1MSEL6_0, PG6_FN),
+ PINMUX_DATA(SSI2_SCK_MARK, P1MSEL7_1, P1MSEL6_0, PG6_FN),
+ PINMUX_DATA(TCLK_MARK, P1MSEL7_0, P1MSEL6_1, PG6_FN),
+ PINMUX_DATA(SCIF3_TXD_MARK, P1MSEL5_0, P1MSEL4_0, PG5_FN),
+ PINMUX_DATA(SSI2_WS_MARK, P1MSEL5_1, P1MSEL4_0, PG5_FN),
+ PINMUX_DATA(HAC_RES_MARK, P1MSEL5_0, P1MSEL4_1, PG5_FN),
+
+ /* PH FN */
+ PINMUX_DATA(DACK3_MARK, P2MSEL4_0, PH7_FN),
+ PINMUX_DATA(SDIF0CMD_MARK, P2MSEL4_1, PH7_FN),
+ PINMUX_DATA(DACK2_MARK, P2MSEL4_0, PH6_FN),
+ PINMUX_DATA(SDIF0CD_MARK, P2MSEL4_1, PH6_FN),
+ PINMUX_DATA(DREQ3_MARK, P2MSEL4_0, PH5_FN),
+ PINMUX_DATA(SDIF0WP_MARK, P2MSEL4_1, PH5_FN),
+ PINMUX_DATA(DREQ2_MARK, P2MSEL3_0, P2MSEL2_1, PH4_FN),
+ PINMUX_DATA(SDIF0CLK_MARK, P2MSEL3_1, P2MSEL2_0, PH4_FN),
+ PINMUX_DATA(SCIF0_CTS_MARK, P2MSEL3_0, P2MSEL2_0, PH4_FN),
+ PINMUX_DATA(SDIF0D3_MARK, P2MSEL1_1, P2MSEL0_0, PH3_FN),
+ PINMUX_DATA(SCIF0_RTS_MARK, P2MSEL1_0, P2MSEL0_0, PH3_FN),
+ PINMUX_DATA(IRL7_MARK, P2MSEL1_0, P2MSEL0_1, PH3_FN),
+ PINMUX_DATA(SDIF0D2_MARK, P2MSEL1_1, P2MSEL0_0, PH2_FN),
+ PINMUX_DATA(SCIF0_SCK_MARK, P2MSEL1_0, P2MSEL0_0, PH2_FN),
+ PINMUX_DATA(IRL6_MARK, P2MSEL1_0, P2MSEL0_1, PH2_FN),
+ PINMUX_DATA(SDIF0D1_MARK, P2MSEL1_1, P2MSEL0_0, PH1_FN),
+ PINMUX_DATA(SCIF0_RXD_MARK, P2MSEL1_0, P2MSEL0_0, PH1_FN),
+ PINMUX_DATA(IRL5_MARK, P2MSEL1_0, P2MSEL0_1, PH1_FN),
+ PINMUX_DATA(SDIF0D0_MARK, P2MSEL1_1, P2MSEL0_0, PH0_FN),
+ PINMUX_DATA(SCIF0_TXD_MARK, P2MSEL1_0, P2MSEL0_0, PH0_FN),
+ PINMUX_DATA(IRL4_MARK, P2MSEL1_0, P2MSEL0_1, PH0_FN),
+
+ /* PJ FN */
+ PINMUX_DATA(SCIF5_SCK_MARK, P2MSEL11_1, PJ7_FN),
+ PINMUX_DATA(FRB_MARK, P2MSEL11_0, PJ7_FN),
+ PINMUX_DATA(SCIF5_RXD_MARK, P2MSEL10_0, PJ6_FN),
+ PINMUX_DATA(IOIS16_MARK, P2MSEL10_1, PJ6_FN),
+ PINMUX_DATA(SCIF5_TXD_MARK, P2MSEL10_0, PJ5_FN),
+ PINMUX_DATA(CE2B_MARK, P2MSEL10_1, PJ5_FN),
+ PINMUX_DATA(DRAK3_MARK, P2MSEL7_0, PJ4_FN),
+ PINMUX_DATA(CE2A_MARK, P2MSEL7_1, PJ4_FN),
+ PINMUX_DATA(SCIF4_SCK_MARK, P2MSEL9_0, P2MSEL8_0, PJ3_FN),
+ PINMUX_DATA(DRAK2_MARK, P2MSEL9_0, P2MSEL8_1, PJ3_FN),
+ PINMUX_DATA(SSI3_WS_MARK, P2MSEL9_1, P2MSEL8_0, PJ3_FN),
+ PINMUX_DATA(SCIF4_RXD_MARK, P2MSEL6_1, P2MSEL5_0, PJ2_FN),
+ PINMUX_DATA(DRAK1_MARK, P2MSEL6_0, P2MSEL5_1, PJ2_FN),
+ PINMUX_DATA(FSTATUS_MARK, P2MSEL6_0, P2MSEL5_0, PJ2_FN),
+ PINMUX_DATA(SSI3_SDATA_MARK, P2MSEL6_1, P2MSEL5_1, PJ2_FN),
+ PINMUX_DATA(SCIF4_TXD_MARK, P2MSEL6_1, P2MSEL5_0, PJ1_FN),
+ PINMUX_DATA(DRAK0_MARK, P2MSEL6_0, P2MSEL5_1, PJ1_FN),
+ PINMUX_DATA(FSE_MARK, P2MSEL6_0, P2MSEL5_0, PJ1_FN),
+ PINMUX_DATA(SSI3_SCK_MARK, P2MSEL6_1, P2MSEL5_1, PJ1_FN),
+};
+
+static struct pinmux_gpio pinmux_gpios[] = {
+ /* PA */
+ PINMUX_GPIO(GPIO_PA7, PA7_DATA),
+ PINMUX_GPIO(GPIO_PA6, PA6_DATA),
+ PINMUX_GPIO(GPIO_PA5, PA5_DATA),
+ PINMUX_GPIO(GPIO_PA4, PA4_DATA),
+ PINMUX_GPIO(GPIO_PA3, PA3_DATA),
+ PINMUX_GPIO(GPIO_PA2, PA2_DATA),
+ PINMUX_GPIO(GPIO_PA1, PA1_DATA),
+ PINMUX_GPIO(GPIO_PA0, PA0_DATA),
+
+ /* PB */
+ PINMUX_GPIO(GPIO_PB7, PB7_DATA),
+ PINMUX_GPIO(GPIO_PB6, PB6_DATA),
+ PINMUX_GPIO(GPIO_PB5, PB5_DATA),
+ PINMUX_GPIO(GPIO_PB4, PB4_DATA),
+ PINMUX_GPIO(GPIO_PB3, PB3_DATA),
+ PINMUX_GPIO(GPIO_PB2, PB2_DATA),
+ PINMUX_GPIO(GPIO_PB1, PB1_DATA),
+ PINMUX_GPIO(GPIO_PB0, PB0_DATA),
+
+ /* PC */
+ PINMUX_GPIO(GPIO_PC7, PC7_DATA),
+ PINMUX_GPIO(GPIO_PC6, PC6_DATA),
+ PINMUX_GPIO(GPIO_PC5, PC5_DATA),
+ PINMUX_GPIO(GPIO_PC4, PC4_DATA),
+ PINMUX_GPIO(GPIO_PC3, PC3_DATA),
+ PINMUX_GPIO(GPIO_PC2, PC2_DATA),
+ PINMUX_GPIO(GPIO_PC1, PC1_DATA),
+ PINMUX_GPIO(GPIO_PC0, PC0_DATA),
+
+ /* PD */
+ PINMUX_GPIO(GPIO_PD7, PD7_DATA),
+ PINMUX_GPIO(GPIO_PD6, PD6_DATA),
+ PINMUX_GPIO(GPIO_PD5, PD5_DATA),
+ PINMUX_GPIO(GPIO_PD4, PD4_DATA),
+ PINMUX_GPIO(GPIO_PD3, PD3_DATA),
+ PINMUX_GPIO(GPIO_PD2, PD2_DATA),
+ PINMUX_GPIO(GPIO_PD1, PD1_DATA),
+ PINMUX_GPIO(GPIO_PD0, PD0_DATA),
+
+ /* PE */
+ PINMUX_GPIO(GPIO_PE5, PE7_DATA),
+ PINMUX_GPIO(GPIO_PE4, PE6_DATA),
+
+ /* PF */
+ PINMUX_GPIO(GPIO_PF7, PF7_DATA),
+ PINMUX_GPIO(GPIO_PF6, PF6_DATA),
+ PINMUX_GPIO(GPIO_PF5, PF5_DATA),
+ PINMUX_GPIO(GPIO_PF4, PF4_DATA),
+ PINMUX_GPIO(GPIO_PF3, PF3_DATA),
+ PINMUX_GPIO(GPIO_PF2, PF2_DATA),
+ PINMUX_GPIO(GPIO_PF1, PF1_DATA),
+ PINMUX_GPIO(GPIO_PF0, PF0_DATA),
+
+ /* PG */
+ PINMUX_GPIO(GPIO_PG7, PG7_DATA),
+ PINMUX_GPIO(GPIO_PG6, PG6_DATA),
+ PINMUX_GPIO(GPIO_PG5, PG5_DATA),
+
+ /* PH */
+ PINMUX_GPIO(GPIO_PH7, PH7_DATA),
+ PINMUX_GPIO(GPIO_PH6, PH6_DATA),
+ PINMUX_GPIO(GPIO_PH5, PH5_DATA),
+ PINMUX_GPIO(GPIO_PH4, PH4_DATA),
+ PINMUX_GPIO(GPIO_PH3, PH3_DATA),
+ PINMUX_GPIO(GPIO_PH2, PH2_DATA),
+ PINMUX_GPIO(GPIO_PH1, PH1_DATA),
+ PINMUX_GPIO(GPIO_PH0, PH0_DATA),
+
+ /* PJ */
+ PINMUX_GPIO(GPIO_PJ7, PJ7_DATA),
+ PINMUX_GPIO(GPIO_PJ6, PJ6_DATA),
+ PINMUX_GPIO(GPIO_PJ5, PJ5_DATA),
+ PINMUX_GPIO(GPIO_PJ4, PJ4_DATA),
+ PINMUX_GPIO(GPIO_PJ3, PJ3_DATA),
+ PINMUX_GPIO(GPIO_PJ2, PJ2_DATA),
+ PINMUX_GPIO(GPIO_PJ1, PJ1_DATA),
+
+ /* FN */
+ PINMUX_GPIO(GPIO_FN_CDE, CDE_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_MAGIC, ETH_MAGIC_MARK),
+ PINMUX_GPIO(GPIO_FN_DISP, DISP_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_LINK, ETH_LINK_MARK),
+ PINMUX_GPIO(GPIO_FN_DR5, DR5_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_TX_ER, ETH_TX_ER_MARK),
+ PINMUX_GPIO(GPIO_FN_DR4, DR4_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_TX_EN, ETH_TX_EN_MARK),
+ PINMUX_GPIO(GPIO_FN_DR3, DR3_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_TXD3, ETH_TXD3_MARK),
+ PINMUX_GPIO(GPIO_FN_DR2, DR2_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_TXD2, ETH_TXD2_MARK),
+ PINMUX_GPIO(GPIO_FN_DR1, DR1_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_TXD1, ETH_TXD1_MARK),
+ PINMUX_GPIO(GPIO_FN_DR0, DR0_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_TXD0, ETH_TXD0_MARK),
+ PINMUX_GPIO(GPIO_FN_VSYNC, VSYNC_MARK),
+ PINMUX_GPIO(GPIO_FN_HSPI_CLK, HSPI_CLK_MARK),
+ PINMUX_GPIO(GPIO_FN_ODDF, ODDF_MARK),
+ PINMUX_GPIO(GPIO_FN_HSPI_CS, HSPI_CS_MARK),
+ PINMUX_GPIO(GPIO_FN_DG5, DG5_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_MDIO, ETH_MDIO_MARK),
+ PINMUX_GPIO(GPIO_FN_DG4, DG4_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_RX_CLK, ETH_RX_CLK_MARK),
+ PINMUX_GPIO(GPIO_FN_DG3, DG3_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_MDC, ETH_MDC_MARK),
+ PINMUX_GPIO(GPIO_FN_DG2, DG2_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_COL, ETH_COL_MARK),
+ PINMUX_GPIO(GPIO_FN_DG1, DG1_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_TX_CLK, ETH_TX_CLK_MARK),
+ PINMUX_GPIO(GPIO_FN_DG0, DG0_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_CRS, ETH_CRS_MARK),
+ PINMUX_GPIO(GPIO_FN_DCLKIN, DCLKIN_MARK),
+ PINMUX_GPIO(GPIO_FN_HSPI_RX, HSPI_RX_MARK),
+ PINMUX_GPIO(GPIO_FN_HSYNC, HSYNC_MARK),
+ PINMUX_GPIO(GPIO_FN_HSPI_TX, HSPI_TX_MARK),
+ PINMUX_GPIO(GPIO_FN_DB5, DB5_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_RXD3, ETH_RXD3_MARK),
+ PINMUX_GPIO(GPIO_FN_DB4, DB4_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_RXD2, ETH_RXD2_MARK),
+ PINMUX_GPIO(GPIO_FN_DB3, DB3_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_RXD1, ETH_RXD1_MARK),
+ PINMUX_GPIO(GPIO_FN_DB2, DB2_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_RXD0, ETH_RXD0_MARK),
+ PINMUX_GPIO(GPIO_FN_DB1, DB1_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_RX_DV, ETH_RX_DV_MARK),
+ PINMUX_GPIO(GPIO_FN_DB0, DB0_MARK),
+ PINMUX_GPIO(GPIO_FN_ETH_RX_ER, ETH_RX_ER_MARK),
+ PINMUX_GPIO(GPIO_FN_DCLKOUT, DCLKOUT_MARK),
+ PINMUX_GPIO(GPIO_FN_SCIF1_SLK, SCIF1_SLK_MARK),
+ PINMUX_GPIO(GPIO_FN_SCIF1_RXD, SCIF1_RXD_MARK),
+ PINMUX_GPIO(GPIO_FN_SCIF1_TXD, SCIF1_TXD_MARK),
+ PINMUX_GPIO(GPIO_FN_DACK1, DACK1_MARK),
+ PINMUX_GPIO(GPIO_FN_BACK, BACK_MARK),
+ PINMUX_GPIO(GPIO_FN_FALE, FALE_MARK),
+ PINMUX_GPIO(GPIO_FN_DACK0, DACK0_MARK),
+ PINMUX_GPIO(GPIO_FN_FCLE, FCLE_MARK),
+ PINMUX_GPIO(GPIO_FN_DREQ1, DREQ1_MARK),
+ PINMUX_GPIO(GPIO_FN_BREQ, BREQ_MARK),
+ PINMUX_GPIO(GPIO_FN_USB_OVC1, USB_OVC1_MARK),
+ PINMUX_GPIO(GPIO_FN_DREQ0, DREQ0_MARK),
+ PINMUX_GPIO(GPIO_FN_USB_OVC0, USB_OVC0_MARK),
+ PINMUX_GPIO(GPIO_FN_USB_PENC1, USB_PENC1_MARK),
+ PINMUX_GPIO(GPIO_FN_USB_PENC0, USB_PENC0_MARK),
+ PINMUX_GPIO(GPIO_FN_HAC1_SDOUT, HAC1_SDOUT_MARK),
+ PINMUX_GPIO(GPIO_FN_SSI1_SDATA, SSI1_SDATA_MARK),
+ PINMUX_GPIO(GPIO_FN_SDIF1CMD, SDIF1CMD_MARK),
+ PINMUX_GPIO(GPIO_FN_HAC1_SDIN, HAC1_SDIN_MARK),
+ PINMUX_GPIO(GPIO_FN_SSI1_SCK, SSI1_SCK_MARK),
+ PINMUX_GPIO(GPIO_FN_SDIF1CD, SDIF1CD_MARK),
+ PINMUX_GPIO(GPIO_FN_HAC1_SYNC, HAC1_SYNC_MARK),
+ PINMUX_GPIO(GPIO_FN_SSI1_WS, SSI1_WS_MARK),
+ PINMUX_GPIO(GPIO_FN_SDIF1WP, SDIF1WP_MARK),
+ PINMUX_GPIO(GPIO_FN_HAC1_BITCLK, HAC1_BITCLK_MARK),
+ PINMUX_GPIO(GPIO_FN_SSI1_CLK, SSI1_CLK_MARK),
+ PINMUX_GPIO(GPIO_FN_SDIF1CLK, SDIF1CLK_MARK),
+ PINMUX_GPIO(GPIO_FN_HAC0_SDOUT, HAC0_SDOUT_MARK),
+ PINMUX_GPIO(GPIO_FN_SSI0_SDATA, SSI0_SDATA_MARK),
+ PINMUX_GPIO(GPIO_FN_SDIF1D3, SDIF1D3_MARK),
+ PINMUX_GPIO(GPIO_FN_HAC0_SDIN, HAC0_SDIN_MARK),
+ PINMUX_GPIO(GPIO_FN_SSI0_SCK, SSI0_SCK_MARK),
+ PINMUX_GPIO(GPIO_FN_SDIF1D2, SDIF1D2_MARK),
+ PINMUX_GPIO(GPIO_FN_HAC0_SYNC, HAC0_SYNC_MARK),
+ PINMUX_GPIO(GPIO_FN_SSI0_WS, SSI0_WS_MARK),
+ PINMUX_GPIO(GPIO_FN_SDIF1D1, SDIF1D1_MARK),
+ PINMUX_GPIO(GPIO_FN_HAC0_BITCLK, HAC0_BITCLK_MARK),
+ PINMUX_GPIO(GPIO_FN_SSI0_CLK, SSI0_CLK_MARK),
+ PINMUX_GPIO(GPIO_FN_SDIF1D0, SDIF1D0_MARK),
+ PINMUX_GPIO(GPIO_FN_SCIF3_SCK, SCIF3_SCK_MARK),
+ PINMUX_GPIO(GPIO_FN_SSI2_SDATA, SSI2_SDATA_MARK),
+ PINMUX_GPIO(GPIO_FN_SCIF3_RXD, SCIF3_RXD_MARK),
+ PINMUX_GPIO(GPIO_FN_TCLK, TCLK_MARK),
+ PINMUX_GPIO(GPIO_FN_SSI2_SCK, SSI2_SCK_MARK),
+ PINMUX_GPIO(GPIO_FN_SCIF3_TXD, SCIF3_TXD_MARK),
+ PINMUX_GPIO(GPIO_FN_HAC_RES, HAC_RES_MARK),
+ PINMUX_GPIO(GPIO_FN_SSI2_WS, SSI2_WS_MARK),
+ PINMUX_GPIO(GPIO_FN_DACK3, DACK3_MARK),
+ PINMUX_GPIO(GPIO_FN_SDIF0CMD, SDIF0CMD_MARK),
+ PINMUX_GPIO(GPIO_FN_DACK2, DACK2_MARK),
+ PINMUX_GPIO(GPIO_FN_SDIF0CD, SDIF0CD_MARK),
+ PINMUX_GPIO(GPIO_FN_DREQ3, DREQ3_MARK),
+ PINMUX_GPIO(GPIO_FN_SDIF0WP, SDIF0WP_MARK),
+ PINMUX_GPIO(GPIO_FN_SCIF0_CTS, SCIF0_CTS_MARK),
+ PINMUX_GPIO(GPIO_FN_DREQ2, DREQ2_MARK),
+ PINMUX_GPIO(GPIO_FN_SDIF0CLK, SDIF0CLK_MARK),
+ PINMUX_GPIO(GPIO_FN_SCIF0_RTS, SCIF0_RTS_MARK),
+ PINMUX_GPIO(GPIO_FN_IRL7, IRL7_MARK),
+ PINMUX_GPIO(GPIO_FN_SDIF0D3, SDIF0D3_MARK),
+ PINMUX_GPIO(GPIO_FN_SCIF0_SCK, SCIF0_SCK_MARK),
+ PINMUX_GPIO(GPIO_FN_IRL6, IRL6_MARK),
+ PINMUX_GPIO(GPIO_FN_SDIF0D2, SDIF0D2_MARK),
+ PINMUX_GPIO(GPIO_FN_SCIF0_RXD, SCIF0_RXD_MARK),
+ PINMUX_GPIO(GPIO_FN_IRL5, IRL5_MARK),
+ PINMUX_GPIO(GPIO_FN_SDIF0D1, SDIF0D1_MARK),
+ PINMUX_GPIO(GPIO_FN_SCIF0_TXD, SCIF0_TXD_MARK),
+ PINMUX_GPIO(GPIO_FN_IRL4, IRL4_MARK),
+ PINMUX_GPIO(GPIO_FN_SDIF0D0, SDIF0D0_MARK),
+ PINMUX_GPIO(GPIO_FN_SCIF5_SCK, SCIF5_SCK_MARK),
+ PINMUX_GPIO(GPIO_FN_FRB, FRB_MARK),
+ PINMUX_GPIO(GPIO_FN_SCIF5_RXD, SCIF5_RXD_MARK),
+ PINMUX_GPIO(GPIO_FN_IOIS16, IOIS16_MARK),
+ PINMUX_GPIO(GPIO_FN_SCIF5_TXD, SCIF5_TXD_MARK),
+ PINMUX_GPIO(GPIO_FN_CE2B, CE2B_MARK),
+ PINMUX_GPIO(GPIO_FN_DRAK3, DRAK3_MARK),
+ PINMUX_GPIO(GPIO_FN_CE2A, CE2A_MARK),
+ PINMUX_GPIO(GPIO_FN_SCIF4_SCK, SCIF4_SCK_MARK),
+ PINMUX_GPIO(GPIO_FN_DRAK2, DRAK2_MARK),
+ PINMUX_GPIO(GPIO_FN_SSI3_WS, SSI3_WS_MARK),
+ PINMUX_GPIO(GPIO_FN_SCIF4_RXD, SCIF4_RXD_MARK),
+ PINMUX_GPIO(GPIO_FN_DRAK1, DRAK1_MARK),
+ PINMUX_GPIO(GPIO_FN_SSI3_SDATA, SSI3_SDATA_MARK),
+ PINMUX_GPIO(GPIO_FN_FSTATUS, FSTATUS_MARK),
+ PINMUX_GPIO(GPIO_FN_SCIF4_TXD, SCIF4_TXD_MARK),
+ PINMUX_GPIO(GPIO_FN_DRAK0, DRAK0_MARK),
+ PINMUX_GPIO(GPIO_FN_SSI3_SCK, SSI3_SCK_MARK),
+ PINMUX_GPIO(GPIO_FN_FSE, FSE_MARK),
+};
+
+static struct pinmux_cfg_reg pinmux_config_regs[] = {
+ { PINMUX_CFG_REG("PACR", 0xffcc0000, 16, 2) {
+ PA7_FN, PA7_OUT, PA7_IN, PA7_IN_PU,
+ PA6_FN, PA6_OUT, PA6_IN, PA6_IN_PU,
+ PA5_FN, PA5_OUT, PA5_IN, PA5_IN_PU,
+ PA4_FN, PA4_OUT, PA4_IN, PA4_IN_PU,
+ PA3_FN, PA3_OUT, PA3_IN, PA3_IN_PU,
+ PA2_FN, PA2_OUT, PA2_IN, PA2_IN_PU,
+ PA1_FN, PA1_OUT, PA1_IN, PA1_IN_PU,
+ PA0_FN, PA0_OUT, PA0_IN, PA0_IN_PU }
+ },
+ { PINMUX_CFG_REG("PBCR", 0xffcc0002, 16, 2) {
+ PB7_FN, PB7_OUT, PB7_IN, PB7_IN_PU,
+ PB6_FN, PB6_OUT, PB6_IN, PB6_IN_PU,
+ PB5_FN, PB5_OUT, PB5_IN, PB5_IN_PU,
+ PB4_FN, PB4_OUT, PB4_IN, PB4_IN_PU,
+ PB3_FN, PB3_OUT, PB3_IN, PB3_IN_PU,
+ PB2_FN, PB2_OUT, PB2_IN, PB2_IN_PU,
+ PB1_FN, PB1_OUT, PB1_IN, PB1_IN_PU,
+ PB0_FN, PB0_OUT, PB0_IN, PB0_IN_PU }
+ },
+ { PINMUX_CFG_REG("PCCR", 0xffcc0004, 16, 2) {
+ PC7_FN, PC7_OUT, PC7_IN, PC7_IN_PU,
+ PC6_FN, PC6_OUT, PC6_IN, PC6_IN_PU,
+ PC5_FN, PC5_OUT, PC5_IN, PC5_IN_PU,
+ PC4_FN, PC4_OUT, PC4_IN, PC4_IN_PU,
+ PC3_FN, PC3_OUT, PC3_IN, PC3_IN_PU,
+ PC2_FN, PC2_OUT, PC2_IN, PC2_IN_PU,
+ PC1_FN, PC1_OUT, PC1_IN, PC1_IN_PU,
+ PC0_FN, PC0_OUT, PC0_IN, PC0_IN_PU }
+ },
+ { PINMUX_CFG_REG("PDCR", 0xffcc0006, 16, 2) {
+ PD7_FN, PD7_OUT, PD7_IN, PD7_IN_PU,
+ PD6_FN, PD6_OUT, PD6_IN, PD6_IN_PU,
+ PD5_FN, PD5_OUT, PD5_IN, PD5_IN_PU,
+ PD4_FN, PD4_OUT, PD4_IN, PD4_IN_PU,
+ PD3_FN, PD3_OUT, PD3_IN, PD3_IN_PU,
+ PD2_FN, PD2_OUT, PD2_IN, PD2_IN_PU,
+ PD1_FN, PD1_OUT, PD1_IN, PD1_IN_PU,
+ PD0_FN, PD0_OUT, PD0_IN, PD0_IN_PU }
+ },
+ { PINMUX_CFG_REG("PECR", 0xffcc0008, 16, 2) {
+ PE7_FN, PE7_OUT, PE7_IN, PE7_IN_PU,
+ PE6_FN, PE6_OUT, PE6_IN, PE6_IN_PU,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0, }
+ },
+ { PINMUX_CFG_REG("PFCR", 0xffcc000a, 16, 2) {
+ PF7_FN, PF7_OUT, PF7_IN, PF7_IN_PU,
+ PF6_FN, PF6_OUT, PF6_IN, PF6_IN_PU,
+ PF5_FN, PF5_OUT, PF5_IN, PF5_IN_PU,
+ PF4_FN, PF4_OUT, PF4_IN, PF4_IN_PU,
+ PF3_FN, PF3_OUT, PF3_IN, PF3_IN_PU,
+ PF2_FN, PF2_OUT, PF2_IN, PF2_IN_PU,
+ PF1_FN, PF1_OUT, PF1_IN, PF1_IN_PU,
+ PF0_FN, PF0_OUT, PF0_IN, PF0_IN_PU }
+ },
+ { PINMUX_CFG_REG("PGCR", 0xffcc000c, 16, 2) {
+ PG7_FN, PG7_OUT, PG7_IN, PG7_IN_PU,
+ PG6_FN, PG6_OUT, PG6_IN, PG6_IN_PU,
+ PG5_FN, PG5_OUT, PG5_IN, PG5_IN_PU,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0, }
+ },
+ { PINMUX_CFG_REG("PHCR", 0xffcc000e, 16, 2) {
+ PH7_FN, PH7_OUT, PH7_IN, PH7_IN_PU,
+ PH6_FN, PH6_OUT, PH6_IN, PH6_IN_PU,
+ PH5_FN, PH5_OUT, PH5_IN, PH5_IN_PU,
+ PH4_FN, PH4_OUT, PH4_IN, PH4_IN_PU,
+ PH3_FN, PH3_OUT, PH3_IN, PH3_IN_PU,
+ PH2_FN, PH2_OUT, PH2_IN, PH2_IN_PU,
+ PH1_FN, PH1_OUT, PH1_IN, PH1_IN_PU,
+ PH0_FN, PH0_OUT, PH0_IN, PH0_IN_PU }
+ },
+ { PINMUX_CFG_REG("PJCR", 0xffcc0010, 16, 2) {
+ PJ7_FN, PJ7_OUT, PJ7_IN, PJ7_IN_PU,
+ PJ6_FN, PJ6_OUT, PJ6_IN, PJ6_IN_PU,
+ PJ5_FN, PJ5_OUT, PJ5_IN, PJ5_IN_PU,
+ PJ4_FN, PJ4_OUT, PJ4_IN, PJ4_IN_PU,
+ PJ3_FN, PJ3_OUT, PJ3_IN, PJ3_IN_PU,
+ PJ2_FN, PJ2_OUT, PJ2_IN, PJ2_IN_PU,
+ PJ1_FN, PJ1_OUT, PJ1_IN, PJ1_IN_PU,
+ 0, 0, 0, 0, }
+ },
+ { PINMUX_CFG_REG("P1MSELR", 0xffcc0080, 16, 1) {
+ 0, 0,
+ P1MSEL14_0, P1MSEL14_1,
+ P1MSEL13_0, P1MSEL13_1,
+ P1MSEL12_0, P1MSEL12_1,
+ P1MSEL11_0, P1MSEL11_1,
+ P1MSEL10_0, P1MSEL10_1,
+ P1MSEL9_0, P1MSEL9_1,
+ P1MSEL8_0, P1MSEL8_1,
+ P1MSEL7_0, P1MSEL7_1,
+ P1MSEL6_0, P1MSEL6_1,
+ P1MSEL5_0, P1MSEL5_1,
+ P1MSEL4_0, P1MSEL4_1,
+ P1MSEL3_0, P1MSEL3_1,
+ P1MSEL2_0, P1MSEL2_1,
+ P1MSEL1_0, P1MSEL1_1,
+ P1MSEL0_0, P1MSEL0_1 }
+ },
+ { PINMUX_CFG_REG("P2MSELR", 0xffcc0082, 16, 1) {
+ P2MSEL15_0, P2MSEL15_1,
+ P2MSEL14_0, P2MSEL14_1,
+ P2MSEL13_0, P2MSEL13_1,
+ P2MSEL12_0, P2MSEL12_1,
+ P2MSEL11_0, P2MSEL11_1,
+ P2MSEL10_0, P2MSEL10_1,
+ P2MSEL9_0, P2MSEL9_1,
+ P2MSEL8_0, P2MSEL8_1,
+ P2MSEL7_0, P2MSEL7_1,
+ P2MSEL6_0, P2MSEL6_1,
+ P2MSEL5_0, P2MSEL5_1,
+ P2MSEL4_0, P2MSEL4_1,
+ P2MSEL3_0, P2MSEL3_1,
+ P2MSEL2_0, P2MSEL2_1,
+ P2MSEL1_0, P2MSEL1_1,
+ P2MSEL0_0, P2MSEL0_1 }
+ },
+ {}
+};
+
+static struct pinmux_data_reg pinmux_data_regs[] = {
+ { PINMUX_DATA_REG("PADR", 0xffcc0020, 8) {
+ PA7_DATA, PA6_DATA, PA5_DATA, PA4_DATA,
+ PA3_DATA, PA2_DATA, PA1_DATA, PA0_DATA }
+ },
+ { PINMUX_DATA_REG("PBDR", 0xffcc0022, 8) {
+ PB7_DATA, PB6_DATA, PB5_DATA, PB4_DATA,
+ PB3_DATA, PB2_DATA, PB1_DATA, PB0_DATA }
+ },
+ { PINMUX_DATA_REG("PCDR", 0xffcc0024, 8) {
+ PC7_DATA, PC6_DATA, PC5_DATA, PC4_DATA,
+ PC3_DATA, PC2_DATA, PC1_DATA, PC0_DATA }
+ },
+ { PINMUX_DATA_REG("PDDR", 0xffcc0026, 8) {
+ PD7_DATA, PD6_DATA, PD5_DATA, PD4_DATA,
+ PD3_DATA, PD2_DATA, PD1_DATA, PD0_DATA }
+ },
+ { PINMUX_DATA_REG("PEDR", 0xffcc0028, 8) {
+ PE7_DATA, PE6_DATA,
+ 0, 0, 0, 0, 0, 0 }
+ },
+ { PINMUX_DATA_REG("PFDR", 0xffcc002a, 8) {
+ PF7_DATA, PF6_DATA, PF5_DATA, PF4_DATA,
+ PF3_DATA, PF2_DATA, PF1_DATA, PF0_DATA }
+ },
+ { PINMUX_DATA_REG("PGDR", 0xffcc002c, 8) {
+ PG7_DATA, PG6_DATA, PG5_DATA, 0,
+ 0, 0, 0, 0 }
+ },
+ { PINMUX_DATA_REG("PHDR", 0xffcc002e, 8) {
+ PH7_DATA, PH6_DATA, PH5_DATA, PH4_DATA,
+ PH3_DATA, PH2_DATA, PH1_DATA, PH0_DATA }
+ },
+ { PINMUX_DATA_REG("PJDR", 0xffcc0030, 8) {
+ PJ7_DATA, PJ6_DATA, PJ5_DATA, PJ4_DATA,
+ PJ3_DATA, PJ2_DATA, PJ1_DATA, 0 }
+ },
+ { },
+};
+
+static struct pinmux_info sh7786_pinmux_info = {
+ .name = "sh7786_pfc",
+ .reserved_id = PINMUX_RESERVED,
+ .data = { PINMUX_DATA_BEGIN, PINMUX_DATA_END },
+ .input = { PINMUX_INPUT_BEGIN, PINMUX_INPUT_END },
+ .input_pu = { PINMUX_INPUT_PULLUP_BEGIN, PINMUX_INPUT_PULLUP_END },
+ .output = { PINMUX_OUTPUT_BEGIN, PINMUX_OUTPUT_END },
+ .mark = { PINMUX_MARK_BEGIN, PINMUX_MARK_END },
+ .function = { PINMUX_FUNCTION_BEGIN, PINMUX_FUNCTION_END },
+
+ .first_gpio = GPIO_PA7,
+ .last_gpio = GPIO_FN_FSE,
+
+ .gpios = pinmux_gpios,
+ .cfg_regs = pinmux_config_regs,
+ .data_regs = pinmux_data_regs,
+
+ .gpio_data = pinmux_data,
+ .gpio_data_size = ARRAY_SIZE(pinmux_data),
+};
+
+static int __init plat_pinmux_setup(void)
+{
+ return register_pinmux(&sh7786_pinmux_info);
+}
+
+arch_initcall(plat_pinmux_setup);
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7343.c b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c
index 4ff4dc6..c154938 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7343.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c
@@ -12,6 +12,7 @@
#include <linux/serial.h>
#include <linux/serial_sci.h>
#include <linux/uio_driver.h>
+#include <linux/sh_cmt.h>
#include <asm/clock.h>
static struct resource iic0_resources[] = {
@@ -140,6 +141,38 @@
.num_resources = ARRAY_SIZE(jpu_resources),
};
+static struct sh_cmt_config cmt_platform_data = {
+ .name = "CMT",
+ .channel_offset = 0x60,
+ .timer_bit = 5,
+ .clk = "cmt0",
+ .clockevent_rating = 125,
+ .clocksource_rating = 200,
+};
+
+static struct resource cmt_resources[] = {
+ [0] = {
+ .name = "CMT",
+ .start = 0x044a0060,
+ .end = 0x044a006b,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = 104,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device cmt_device = {
+ .name = "sh_cmt",
+ .id = 0,
+ .dev = {
+ .platform_data = &cmt_platform_data,
+ },
+ .resource = cmt_resources,
+ .num_resources = ARRAY_SIZE(cmt_resources),
+};
+
static struct plat_sci_port sci_platform_data[] = {
{
.mapbase = 0xffe00000,
@@ -175,6 +208,7 @@
};
static struct platform_device *sh7343_devices[] __initdata = {
+ &cmt_device,
&iic0_device,
&iic1_device,
&sci_device,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
index 839ae97..93ecf8e 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
@@ -14,6 +14,7 @@
#include <linux/serial.h>
#include <linux/serial_sci.h>
#include <linux/uio_driver.h>
+#include <linux/sh_cmt.h>
#include <asm/clock.h>
static struct resource iic_resources[] = {
@@ -147,6 +148,38 @@
.num_resources = ARRAY_SIZE(veu1_resources),
};
+static struct sh_cmt_config cmt_platform_data = {
+ .name = "CMT",
+ .channel_offset = 0x60,
+ .timer_bit = 5,
+ .clk = "cmt0",
+ .clockevent_rating = 125,
+ .clocksource_rating = 200,
+};
+
+static struct resource cmt_resources[] = {
+ [0] = {
+ .name = "CMT",
+ .start = 0x044a0060,
+ .end = 0x044a006b,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = 104,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device cmt_device = {
+ .name = "sh_cmt",
+ .id = 0,
+ .dev = {
+ .platform_data = &cmt_platform_data,
+ },
+ .resource = cmt_resources,
+ .num_resources = ARRAY_SIZE(cmt_resources),
+};
+
static struct plat_sci_port sci_platform_data[] = {
{
.mapbase = 0xffe00000,
@@ -167,6 +200,7 @@
};
static struct platform_device *sh7366_devices[] __initdata = {
+ &cmt_device,
&iic_device,
&sci_device,
&usb_host_device,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
index 5146afc..0e5d204 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
@@ -13,6 +13,7 @@
#include <linux/serial_sci.h>
#include <linux/mm.h>
#include <linux/uio_driver.h>
+#include <linux/sh_cmt.h>
#include <asm/clock.h>
#include <asm/mmzone.h>
@@ -176,6 +177,38 @@
.num_resources = ARRAY_SIZE(jpu_resources),
};
+static struct sh_cmt_config cmt_platform_data = {
+ .name = "CMT",
+ .channel_offset = 0x60,
+ .timer_bit = 5,
+ .clk = "cmt0",
+ .clockevent_rating = 125,
+ .clocksource_rating = 200,
+};
+
+static struct resource cmt_resources[] = {
+ [0] = {
+ .name = "CMT",
+ .start = 0x044a0060,
+ .end = 0x044a006b,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = 104,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device cmt_device = {
+ .name = "sh_cmt",
+ .id = 0,
+ .dev = {
+ .platform_data = &cmt_platform_data,
+ },
+ .resource = cmt_resources,
+ .num_resources = ARRAY_SIZE(cmt_resources),
+};
+
static struct plat_sci_port sci_platform_data[] = {
{
.mapbase = 0xffe00000,
@@ -209,6 +242,7 @@
};
static struct platform_device *sh7722_devices[] __initdata = {
+ &cmt_device,
&rtc_device,
&usbf_device,
&iic_device,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
index 849770d..5338dac 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
@@ -13,6 +13,7 @@
#include <linux/mm.h>
#include <linux/serial_sci.h>
#include <linux/uio_driver.h>
+#include <linux/sh_cmt.h>
#include <asm/clock.h>
#include <asm/mmzone.h>
@@ -100,6 +101,38 @@
.num_resources = ARRAY_SIZE(veu1_resources),
};
+static struct sh_cmt_config cmt_platform_data = {
+ .name = "CMT",
+ .channel_offset = 0x60,
+ .timer_bit = 5,
+ .clk = "cmt0",
+ .clockevent_rating = 125,
+ .clocksource_rating = 200,
+};
+
+static struct resource cmt_resources[] = {
+ [0] = {
+ .name = "CMT",
+ .start = 0x044a0060,
+ .end = 0x044a006b,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = 104,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device cmt_device = {
+ .name = "sh_cmt",
+ .id = 0,
+ .dev = {
+ .platform_data = &cmt_platform_data,
+ },
+ .resource = cmt_resources,
+ .num_resources = ARRAY_SIZE(cmt_resources),
+};
+
static struct plat_sci_port sci_platform_data[] = {
{
.mapbase = 0xffe00000,
@@ -221,6 +254,7 @@
};
static struct platform_device *sh7723_devices[] __initdata = {
+ &cmt_device,
&sci_device,
&rtc_device,
&iic_device,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c
index 3c5b629..bdf0f61 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2006 Paul Mundt
* Copyright (C) 2007 Yoshihiro Shimoda
- * Copyright (C) 2008 Nobuhiro Iwamatsu
+ * Copyright (C) 2008, 2009 Nobuhiro Iwamatsu
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
@@ -22,18 +22,8 @@
.flags = IORESOURCE_IO,
},
[1] = {
- /* Period IRQ */
- .start = 21,
- .flags = IORESOURCE_IRQ,
- },
- [2] = {
- /* Carry IRQ */
- .start = 22,
- .flags = IORESOURCE_IRQ,
- },
- [3] = {
- /* Alarm IRQ */
- .start = 20,
+ /* Shared Period/Carry/Alarm IRQ */
+ .start = 20,
.flags = IORESOURCE_IRQ,
},
};
@@ -50,17 +40,17 @@
.mapbase = 0xffe00000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 40, 41, 43, 42 },
+ .irqs = { 40, 40, 40, 40 },
}, {
.mapbase = 0xffe08000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 76, 77, 79, 78 },
+ .irqs = { 76, 76, 76, 76 },
}, {
.mapbase = 0xffe10000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 104, 105, 107, 106 },
+ .irqs = { 104, 104, 104, 104 },
}, {
.flags = 0,
}
@@ -148,93 +138,65 @@
IRL_HHLL, IRL_HHLH, IRL_HHHL,
IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7,
- RTC_ATI, RTC_PRI, RTC_CUI,
- WDT, TMU0, TMU1, TMU2, TMU2_TICPI,
- HUDI, LCDC,
- DMAC0_DMINT0, DMAC0_DMINT1, DMAC0_DMINT2, DMAC0_DMINT3, DMAC0_DMAE,
- SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI,
- DMAC0_DMINT4, DMAC0_DMINT5,
- IIC0, IIC1,
- CMT,
- GEINT0, GEINT1, GEINT2,
- HAC,
- PCISERR, PCIINTA, PCIINTB, PCIINTC, PCIINTD,
- PCIERR, PCIPWD3, PCIPWD2, PCIPWD1, PCIPWD0,
- STIF0, STIF1,
- SCIF1_ERI, SCIF1_RXI, SCIF1_BRI, SCIF1_TXI,
- SIOF0, SIOF1, SIOF2,
- USBH, USBFI0, USBFI1,
- TPU, PCC,
- MMCIF_FSTAT, MMCIF_TRAN, MMCIF_ERR, MMCIF_FRDY,
- SIM_ERI, SIM_RXI, SIM_TXI, SIM_TEND,
+ RTC, WDT, TMU0, TMU1, TMU2, TMU2_TICPI,
+ HUDI, LCDC, DMAC, SCIF0, IIC0, IIC1, CMT, GETHER, HAC,
+ PCISERR, PCIINTA, PCIINTB, PCIINTC, PCIINTD, PCIC5,
+ STIF0, STIF1, SCIF1, SIOF0, SIOF1, SIOF2,
+ USBH, USBF, TPU, PCC, MMCIF, SIM,
TMU3, TMU4, TMU5, ADC, SSI0, SSI1, SSI2, SSI3,
- SCIF2_ERI, SCIF2_RXI, SCIF2_BRI, SCIF2_TXI,
- GPIO_CH0, GPIO_CH1, GPIO_CH2, GPIO_CH3,
+ SCIF2, GPIO,
/* interrupt groups */
- TMU012, TMU345, RTC, DMAC, SCIF0, GETHER, PCIC5,
- SCIF1, USBF, MMCIF, SIM, SCIF2, GPIO,
+ TMU012, TMU345,
};
static struct intc_vect vectors[] __initdata = {
- INTC_VECT(RTC_ATI, 0x480), INTC_VECT(RTC_PRI, 0x4a0),
- INTC_VECT(RTC_CUI, 0x4c0),
+ INTC_VECT(RTC, 0x480), INTC_VECT(RTC, 0x4a0),
+ INTC_VECT(RTC, 0x4c0),
INTC_VECT(WDT, 0x560), INTC_VECT(TMU0, 0x580),
INTC_VECT(TMU1, 0x5a0), INTC_VECT(TMU2, 0x5c0),
INTC_VECT(TMU2_TICPI, 0x5e0), INTC_VECT(HUDI, 0x600),
INTC_VECT(LCDC, 0x620),
- INTC_VECT(DMAC0_DMINT0, 0x640), INTC_VECT(DMAC0_DMINT1, 0x660),
- INTC_VECT(DMAC0_DMINT2, 0x680), INTC_VECT(DMAC0_DMINT3, 0x6a0),
- INTC_VECT(DMAC0_DMAE, 0x6c0),
- INTC_VECT(SCIF0_ERI, 0x700), INTC_VECT(SCIF0_RXI, 0x720),
- INTC_VECT(SCIF0_BRI, 0x740), INTC_VECT(SCIF0_TXI, 0x760),
- INTC_VECT(DMAC0_DMINT4, 0x780), INTC_VECT(DMAC0_DMINT5, 0x7a0),
+ INTC_VECT(DMAC, 0x640), INTC_VECT(DMAC, 0x660),
+ INTC_VECT(DMAC, 0x680), INTC_VECT(DMAC, 0x6a0),
+ INTC_VECT(DMAC, 0x6c0),
+ INTC_VECT(SCIF0, 0x700), INTC_VECT(SCIF0, 0x720),
+ INTC_VECT(SCIF0, 0x740), INTC_VECT(SCIF0, 0x760),
+ INTC_VECT(DMAC, 0x780), INTC_VECT(DMAC, 0x7a0),
INTC_VECT(IIC0, 0x8A0), INTC_VECT(IIC1, 0x8C0),
- INTC_VECT(CMT, 0x900), INTC_VECT(GEINT0, 0x920),
- INTC_VECT(GEINT1, 0x940), INTC_VECT(GEINT2, 0x960),
+ INTC_VECT(CMT, 0x900), INTC_VECT(GETHER, 0x920),
+ INTC_VECT(GETHER, 0x940), INTC_VECT(GETHER, 0x960),
INTC_VECT(HAC, 0x980),
INTC_VECT(PCISERR, 0xa00), INTC_VECT(PCIINTA, 0xa20),
INTC_VECT(PCIINTB, 0xa40), INTC_VECT(PCIINTC, 0xa60),
- INTC_VECT(PCIINTD, 0xa80), INTC_VECT(PCIERR, 0xaa0),
- INTC_VECT(PCIPWD3, 0xac0), INTC_VECT(PCIPWD2, 0xae0),
- INTC_VECT(PCIPWD1, 0xb00), INTC_VECT(PCIPWD0, 0xb20),
+ INTC_VECT(PCIINTD, 0xa80), INTC_VECT(PCIC5, 0xaa0),
+ INTC_VECT(PCIC5, 0xac0), INTC_VECT(PCIC5, 0xae0),
+ INTC_VECT(PCIC5, 0xb00), INTC_VECT(PCIC5, 0xb20),
INTC_VECT(STIF0, 0xb40), INTC_VECT(STIF1, 0xb60),
- INTC_VECT(SCIF1_ERI, 0xb80), INTC_VECT(SCIF1_RXI, 0xba0),
- INTC_VECT(SCIF1_BRI, 0xbc0), INTC_VECT(SCIF1_TXI, 0xbe0),
+ INTC_VECT(SCIF1, 0xb80), INTC_VECT(SCIF1, 0xba0),
+ INTC_VECT(SCIF1, 0xbc0), INTC_VECT(SCIF1, 0xbe0),
INTC_VECT(SIOF0, 0xc00), INTC_VECT(SIOF1, 0xc20),
- INTC_VECT(USBH, 0xc60), INTC_VECT(USBFI0, 0xc80),
- INTC_VECT(USBFI1, 0xca0),
+ INTC_VECT(USBH, 0xc60), INTC_VECT(USBF, 0xc80),
+ INTC_VECT(USBF, 0xca0),
INTC_VECT(TPU, 0xcc0), INTC_VECT(PCC, 0xce0),
- INTC_VECT(MMCIF_FSTAT, 0xd00), INTC_VECT(MMCIF_TRAN, 0xd20),
- INTC_VECT(MMCIF_ERR, 0xd40), INTC_VECT(MMCIF_FRDY, 0xd60),
- INTC_VECT(SIM_ERI, 0xd80), INTC_VECT(SIM_RXI, 0xda0),
- INTC_VECT(SIM_TXI, 0xdc0), INTC_VECT(SIM_TEND, 0xde0),
+ INTC_VECT(MMCIF, 0xd00), INTC_VECT(MMCIF, 0xd20),
+ INTC_VECT(MMCIF, 0xd40), INTC_VECT(MMCIF, 0xd60),
+ INTC_VECT(SIM, 0xd80), INTC_VECT(SIM, 0xda0),
+ INTC_VECT(SIM, 0xdc0), INTC_VECT(SIM, 0xde0),
INTC_VECT(TMU3, 0xe00), INTC_VECT(TMU4, 0xe20),
INTC_VECT(TMU5, 0xe40), INTC_VECT(ADC, 0xe60),
INTC_VECT(SSI0, 0xe80), INTC_VECT(SSI1, 0xea0),
INTC_VECT(SSI2, 0xec0), INTC_VECT(SSI3, 0xee0),
- INTC_VECT(SCIF2_ERI, 0xf00), INTC_VECT(SCIF2_RXI, 0xf20),
- INTC_VECT(SCIF2_BRI, 0xf40), INTC_VECT(SCIF2_TXI, 0xf60),
- INTC_VECT(GPIO_CH0, 0xf80), INTC_VECT(GPIO_CH1, 0xfa0),
- INTC_VECT(GPIO_CH2, 0xfc0), INTC_VECT(GPIO_CH3, 0xfe0),
+ INTC_VECT(SCIF2, 0xf00), INTC_VECT(SCIF2, 0xf20),
+ INTC_VECT(SCIF2, 0xf40), INTC_VECT(SCIF2, 0xf60),
+ INTC_VECT(GPIO, 0xf80), INTC_VECT(GPIO, 0xfa0),
+ INTC_VECT(GPIO, 0xfc0), INTC_VECT(GPIO, 0xfe0),
};
static struct intc_group groups[] __initdata = {
INTC_GROUP(TMU012, TMU0, TMU1, TMU2, TMU2_TICPI),
INTC_GROUP(TMU345, TMU3, TMU4, TMU5),
- INTC_GROUP(RTC, RTC_ATI, RTC_PRI, RTC_CUI),
- INTC_GROUP(DMAC, DMAC0_DMINT0, DMAC0_DMINT1, DMAC0_DMINT2,
- DMAC0_DMINT3, DMAC0_DMINT4, DMAC0_DMINT5, DMAC0_DMAE),
- INTC_GROUP(SCIF0, SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI),
- INTC_GROUP(GETHER, GEINT0, GEINT1, GEINT2),
- INTC_GROUP(PCIC5, PCIERR, PCIPWD3, PCIPWD2, PCIPWD1, PCIPWD0),
- INTC_GROUP(SCIF1, SCIF1_ERI, SCIF1_RXI, SCIF1_BRI, SCIF1_TXI),
- INTC_GROUP(USBF, USBFI0, USBFI1),
- INTC_GROUP(MMCIF, MMCIF_FSTAT, MMCIF_TRAN, MMCIF_ERR, MMCIF_FRDY),
- INTC_GROUP(SIM, SIM_ERI, SIM_RXI, SIM_TXI, SIM_TEND),
- INTC_GROUP(SCIF2, SCIF2_ERI, SCIF2_RXI, SCIF2_BRI, SCIF2_TXI),
- INTC_GROUP(GPIO, GPIO_CH0, GPIO_CH1, GPIO_CH2, GPIO_CH3),
};
static struct intc_mask_reg mask_registers[] __initdata = {
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c
index fb8200c..6f7227c 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c
@@ -20,17 +20,7 @@
.flags = IORESOURCE_IO,
},
[1] = {
- /* Period IRQ */
- .start = 21,
- .flags = IORESOURCE_IRQ,
- },
- [2] = {
- /* Carry IRQ */
- .start = 22,
- .flags = IORESOURCE_IRQ,
- },
- [3] = {
- /* Alarm IRQ */
+ /* Shared Period/Carry/Alarm IRQ */
.start = 20,
.flags = IORESOURCE_IRQ,
},
@@ -48,12 +38,12 @@
.mapbase = 0xffe00000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 40, 41, 43, 42 },
+ .irqs = { 40, 40, 40, 40 },
}, {
.mapbase = 0xffe10000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 76, 77, 79, 78 },
+ .irqs = { 76, 76, 76, 76 },
}, {
.flags = 0,
}
@@ -90,82 +80,55 @@
IRL_HHLL, IRL_HHLH, IRL_HHHL,
IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7,
- RTC_ATI, RTC_PRI, RTC_CUI,
- WDT,
- TMU0, TMU1, TMU2, TMU2_TICPI,
- HUDI,
- DMAC0_DMINT0, DMAC0_DMINT1, DMAC0_DMINT2, DMAC0_DMINT3, DMAC0_DMAE,
- SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI,
- DMAC0_DMINT4, DMAC0_DMINT5, DMAC1_DMINT6, DMAC1_DMINT7,
- CMT, HAC,
- PCISERR, PCIINTA, PCIINTB, PCIINTC, PCIINTD,
- PCIERR, PCIPWD3, PCIPWD2, PCIPWD1, PCIPWD0,
- SCIF1_ERI, SCIF1_RXI, SCIF1_BRI, SCIF1_TXI,
- SIOF, HSPI,
- MMCIF_FSTAT, MMCIF_TRAN, MMCIF_ERR, MMCIF_FRDY,
- DMAC1_DMINT8, DMAC1_DMINT9, DMAC1_DMINT10, DMAC1_DMINT11,
- TMU3, TMU4, TMU5,
- SSI,
- FLCTL_FLSTE, FLCTL_FLEND, FLCTL_FLTRQ0, FLCTL_FLTRQ1,
- GPIOI0, GPIOI1, GPIOI2, GPIOI3,
+ RTC, WDT, TMU0, TMU1, TMU2, TMU2_TICPI,
+ HUDI, DMAC0, SCIF0, DMAC1, CMT, HAC,
+ PCISERR, PCIINTA, PCIINTB, PCIINTC, PCIINTD, PCIC5,
+ SCIF1, SIOF, HSPI, MMCIF, TMU3, TMU4, TMU5, SSI, FLCTL, GPIO,
/* interrupt groups */
- RTC, TMU012, DMAC0, SCIF0, DMAC45, DMAC1,
- PCIC5, SCIF1, MMCIF, TMU345, FLCTL, GPIO,
+ TMU012, TMU345,
};
static struct intc_vect vectors[] __initdata = {
- INTC_VECT(RTC_ATI, 0x480), INTC_VECT(RTC_PRI, 0x4a0),
- INTC_VECT(RTC_CUI, 0x4c0),
+ INTC_VECT(RTC, 0x480), INTC_VECT(RTC, 0x4a0),
+ INTC_VECT(RTC, 0x4c0),
INTC_VECT(WDT, 0x560),
INTC_VECT(TMU0, 0x580), INTC_VECT(TMU1, 0x5a0),
INTC_VECT(TMU2, 0x5c0), INTC_VECT(TMU2_TICPI, 0x5e0),
INTC_VECT(HUDI, 0x600),
- INTC_VECT(DMAC0_DMINT0, 0x640), INTC_VECT(DMAC0_DMINT1, 0x660),
- INTC_VECT(DMAC0_DMINT2, 0x680), INTC_VECT(DMAC0_DMINT3, 0x6a0),
- INTC_VECT(DMAC0_DMAE, 0x6c0),
- INTC_VECT(SCIF0_ERI, 0x700), INTC_VECT(SCIF0_RXI, 0x720),
- INTC_VECT(SCIF0_BRI, 0x740), INTC_VECT(SCIF0_TXI, 0x760),
- INTC_VECT(DMAC0_DMINT4, 0x780), INTC_VECT(DMAC0_DMINT5, 0x7a0),
- INTC_VECT(DMAC1_DMINT6, 0x7c0), INTC_VECT(DMAC1_DMINT7, 0x7e0),
+ INTC_VECT(DMAC0, 0x640), INTC_VECT(DMAC0, 0x660),
+ INTC_VECT(DMAC0, 0x680), INTC_VECT(DMAC0, 0x6a0),
+ INTC_VECT(DMAC0, 0x6c0),
+ INTC_VECT(SCIF0, 0x700), INTC_VECT(SCIF0, 0x720),
+ INTC_VECT(SCIF0, 0x740), INTC_VECT(SCIF0, 0x760),
+ INTC_VECT(DMAC0, 0x780), INTC_VECT(DMAC0, 0x7a0),
+ INTC_VECT(DMAC1, 0x7c0), INTC_VECT(DMAC1, 0x7e0),
INTC_VECT(CMT, 0x900), INTC_VECT(HAC, 0x980),
INTC_VECT(PCISERR, 0xa00), INTC_VECT(PCIINTA, 0xa20),
INTC_VECT(PCIINTB, 0xa40), INTC_VECT(PCIINTC, 0xa60),
- INTC_VECT(PCIINTD, 0xa80), INTC_VECT(PCIERR, 0xaa0),
- INTC_VECT(PCIPWD3, 0xac0), INTC_VECT(PCIPWD2, 0xae0),
- INTC_VECT(PCIPWD1, 0xb00), INTC_VECT(PCIPWD0, 0xb20),
- INTC_VECT(SCIF1_ERI, 0xb80), INTC_VECT(SCIF1_RXI, 0xba0),
- INTC_VECT(SCIF1_BRI, 0xbc0), INTC_VECT(SCIF1_TXI, 0xbe0),
+ INTC_VECT(PCIINTD, 0xa80), INTC_VECT(PCIC5, 0xaa0),
+ INTC_VECT(PCIC5, 0xac0), INTC_VECT(PCIC5, 0xae0),
+ INTC_VECT(PCIC5, 0xb00), INTC_VECT(PCIC5, 0xb20),
+ INTC_VECT(SCIF1, 0xb80), INTC_VECT(SCIF1, 0xba0),
+ INTC_VECT(SCIF1, 0xbc0), INTC_VECT(SCIF1, 0xbe0),
INTC_VECT(SIOF, 0xc00), INTC_VECT(HSPI, 0xc80),
- INTC_VECT(MMCIF_FSTAT, 0xd00), INTC_VECT(MMCIF_TRAN, 0xd20),
- INTC_VECT(MMCIF_ERR, 0xd40), INTC_VECT(MMCIF_FRDY, 0xd60),
- INTC_VECT(DMAC1_DMINT8, 0xd80), INTC_VECT(DMAC1_DMINT9, 0xda0),
- INTC_VECT(DMAC1_DMINT10, 0xdc0), INTC_VECT(DMAC1_DMINT11, 0xde0),
+ INTC_VECT(MMCIF, 0xd00), INTC_VECT(MMCIF, 0xd20),
+ INTC_VECT(MMCIF, 0xd40), INTC_VECT(MMCIF, 0xd60),
+ INTC_VECT(DMAC1, 0xd80), INTC_VECT(DMAC1, 0xda0),
+ INTC_VECT(DMAC1, 0xdc0), INTC_VECT(DMAC1, 0xde0),
INTC_VECT(TMU3, 0xe00), INTC_VECT(TMU4, 0xe20),
INTC_VECT(TMU5, 0xe40),
INTC_VECT(SSI, 0xe80),
- INTC_VECT(FLCTL_FLSTE, 0xf00), INTC_VECT(FLCTL_FLEND, 0xf20),
- INTC_VECT(FLCTL_FLTRQ0, 0xf40), INTC_VECT(FLCTL_FLTRQ1, 0xf60),
- INTC_VECT(GPIOI0, 0xf80), INTC_VECT(GPIOI1, 0xfa0),
- INTC_VECT(GPIOI2, 0xfc0), INTC_VECT(GPIOI3, 0xfe0),
+ INTC_VECT(FLCTL, 0xf00), INTC_VECT(FLCTL, 0xf20),
+ INTC_VECT(FLCTL, 0xf40), INTC_VECT(FLCTL, 0xf60),
+ INTC_VECT(GPIO, 0xf80), INTC_VECT(GPIO, 0xfa0),
+ INTC_VECT(GPIO, 0xfc0), INTC_VECT(GPIO, 0xfe0),
};
static struct intc_group groups[] __initdata = {
- INTC_GROUP(RTC, RTC_ATI, RTC_PRI, RTC_CUI),
INTC_GROUP(TMU012, TMU0, TMU1, TMU2, TMU2_TICPI),
- INTC_GROUP(DMAC0, DMAC0_DMINT0, DMAC0_DMINT1, DMAC0_DMINT2,
- DMAC0_DMINT3, DMAC0_DMINT4, DMAC0_DMINT5, DMAC0_DMAE),
- INTC_GROUP(SCIF0, SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI),
- INTC_GROUP(DMAC1, DMAC1_DMINT6, DMAC1_DMINT7, DMAC1_DMINT8,
- DMAC1_DMINT9, DMAC1_DMINT10, DMAC1_DMINT11),
- INTC_GROUP(PCIC5, PCIERR, PCIPWD3, PCIPWD2, PCIPWD1, PCIPWD0),
- INTC_GROUP(SCIF1, SCIF1_ERI, SCIF1_RXI, SCIF1_BRI, SCIF1_TXI),
- INTC_GROUP(MMCIF, MMCIF_FSTAT, MMCIF_TRAN, MMCIF_ERR, MMCIF_FRDY),
INTC_GROUP(TMU345, TMU3, TMU4, TMU5),
- INTC_GROUP(FLCTL, FLCTL_FLSTE, FLCTL_FLEND,
- FLCTL_FLTRQ0, FLCTL_FLTRQ1),
- INTC_GROUP(GPIO, GPIOI0, GPIOI1, GPIOI2, GPIOI3),
};
static struct intc_mask_reg mask_registers[] __initdata = {
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c
index 30baa63..d80802a 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c
@@ -20,18 +20,13 @@
.mapbase = 0xffea0000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 40, 41, 43, 42 },
+ .irqs = { 40, 40, 40, 40 },
}, {
.mapbase = 0xffeb0000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 44, 45, 47, 46 },
- },
-
- /*
- * The rest of these all have multiplexed IRQs
- */
- {
+ .irqs = { 44, 44, 44, 44 },
+ }, {
.mapbase = 0xffec0000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
@@ -91,33 +86,19 @@
IRL4_HHLL, IRL4_HHLH, IRL4_HHHL,
IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7,
- WDT,
- TMU0, TMU1, TMU2, TMU2_TICPI,
- HUDI,
- DMAC0_DMINT0, DMAC0_DMINT1, DMAC0_DMINT2, DMAC0_DMINT3,
- DMAC0_DMINT4, DMAC0_DMINT5, DMAC0_DMAE,
- SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI,
- SCIF1_ERI, SCIF1_RXI, SCIF1_BRI, SCIF1_TXI,
- DMAC1_DMINT6, DMAC1_DMINT7, DMAC1_DMINT8, DMAC1_DMINT9,
- DMAC1_DMINT10, DMAC1_DMINT11, DMAC1_DMAE,
- HSPI,
+ WDT, TMU0, TMU1, TMU2, TMU2_TICPI,
+ HUDI, DMAC0, SCIF0, SCIF1, DMAC1, HSPI,
SCIF2, SCIF3, SCIF4, SCIF5,
- PCISERR, PCIINTA, PCIINTB, PCIINTC, PCIINTD,
- PCIERR, PCIPWD3, PCIPWD2, PCIPWD1, PCIPWD0,
- SIOF,
- MMCIF_FSTAT, MMCIF_TRAN, MMCIF_ERR, MMCIF_FRDY,
- DU,
- GDTA_GACLI, GDTA_GAMCI, GDTA_GAERI,
+ PCISERR, PCIINTA, PCIINTB, PCIINTC, PCIINTD, PCIC5,
+ SIOF, MMCIF, DU, GDTA,
TMU3, TMU4, TMU5,
SSI0, SSI1,
HAC0, HAC1,
- FLCTL_FLSTE, FLCTL_FLEND, FLCTL_FLTRQ0, FLCTL_FLTRQ1,
- GPIOI0, GPIOI1, GPIOI2, GPIOI3,
+ FLCTL, GPIO,
/* interrupt groups */
- TMU012, DMAC0, SCIF0, SCIF1, DMAC1,
- PCIC5, MMCIF, GDTA, TMU345, FLCTL, GPIO
+ TMU012, TMU345
};
static struct intc_vect vectors[] __initdata = {
@@ -125,57 +106,45 @@
INTC_VECT(TMU0, 0x580), INTC_VECT(TMU1, 0x5a0),
INTC_VECT(TMU2, 0x5c0), INTC_VECT(TMU2_TICPI, 0x5e0),
INTC_VECT(HUDI, 0x600),
- INTC_VECT(DMAC0_DMINT0, 0x620), INTC_VECT(DMAC0_DMINT1, 0x640),
- INTC_VECT(DMAC0_DMINT2, 0x660), INTC_VECT(DMAC0_DMINT3, 0x680),
- INTC_VECT(DMAC0_DMINT4, 0x6a0), INTC_VECT(DMAC0_DMINT5, 0x6c0),
- INTC_VECT(DMAC0_DMAE, 0x6e0),
- INTC_VECT(SCIF0_ERI, 0x700), INTC_VECT(SCIF0_RXI, 0x720),
- INTC_VECT(SCIF0_BRI, 0x740), INTC_VECT(SCIF0_TXI, 0x760),
- INTC_VECT(SCIF1_ERI, 0x780), INTC_VECT(SCIF1_RXI, 0x7a0),
- INTC_VECT(SCIF1_BRI, 0x7c0), INTC_VECT(SCIF1_TXI, 0x7e0),
- INTC_VECT(DMAC1_DMINT6, 0x880), INTC_VECT(DMAC1_DMINT7, 0x8a0),
- INTC_VECT(DMAC1_DMINT8, 0x8c0), INTC_VECT(DMAC1_DMINT9, 0x8e0),
- INTC_VECT(DMAC1_DMINT10, 0x900), INTC_VECT(DMAC1_DMINT11, 0x920),
- INTC_VECT(DMAC1_DMAE, 0x940),
+ INTC_VECT(DMAC0, 0x620), INTC_VECT(DMAC0, 0x640),
+ INTC_VECT(DMAC0, 0x660), INTC_VECT(DMAC0, 0x680),
+ INTC_VECT(DMAC0, 0x6a0), INTC_VECT(DMAC0, 0x6c0),
+ INTC_VECT(DMAC0, 0x6e0),
+ INTC_VECT(SCIF0, 0x700), INTC_VECT(SCIF0, 0x720),
+ INTC_VECT(SCIF0, 0x740), INTC_VECT(SCIF0, 0x760),
+ INTC_VECT(SCIF1, 0x780), INTC_VECT(SCIF1, 0x7a0),
+ INTC_VECT(SCIF1, 0x7c0), INTC_VECT(SCIF1, 0x7e0),
+ INTC_VECT(DMAC1, 0x880), INTC_VECT(DMAC1, 0x8a0),
+ INTC_VECT(DMAC1, 0x8c0), INTC_VECT(DMAC1, 0x8e0),
+ INTC_VECT(DMAC1, 0x900), INTC_VECT(DMAC1, 0x920),
+ INTC_VECT(DMAC1, 0x940),
INTC_VECT(HSPI, 0x960),
INTC_VECT(SCIF2, 0x980), INTC_VECT(SCIF3, 0x9a0),
INTC_VECT(SCIF4, 0x9c0), INTC_VECT(SCIF5, 0x9e0),
INTC_VECT(PCISERR, 0xa00), INTC_VECT(PCIINTA, 0xa20),
INTC_VECT(PCIINTB, 0xa40), INTC_VECT(PCIINTC, 0xa60),
- INTC_VECT(PCIINTD, 0xa80), INTC_VECT(PCIERR, 0xaa0),
- INTC_VECT(PCIPWD3, 0xac0), INTC_VECT(PCIPWD2, 0xae0),
- INTC_VECT(PCIPWD1, 0xb00), INTC_VECT(PCIPWD0, 0xb20),
+ INTC_VECT(PCIINTD, 0xa80), INTC_VECT(PCIC5, 0xaa0),
+ INTC_VECT(PCIC5, 0xac0), INTC_VECT(PCIC5, 0xae0),
+ INTC_VECT(PCIC5, 0xb00), INTC_VECT(PCIC5, 0xb20),
INTC_VECT(SIOF, 0xc00),
- INTC_VECT(MMCIF_FSTAT, 0xd00), INTC_VECT(MMCIF_TRAN, 0xd20),
- INTC_VECT(MMCIF_ERR, 0xd40), INTC_VECT(MMCIF_FRDY, 0xd60),
+ INTC_VECT(MMCIF, 0xd00), INTC_VECT(MMCIF, 0xd20),
+ INTC_VECT(MMCIF, 0xd40), INTC_VECT(MMCIF, 0xd60),
INTC_VECT(DU, 0xd80),
- INTC_VECT(GDTA_GACLI, 0xda0), INTC_VECT(GDTA_GAMCI, 0xdc0),
- INTC_VECT(GDTA_GAERI, 0xde0),
+ INTC_VECT(GDTA, 0xda0), INTC_VECT(GDTA, 0xdc0),
+ INTC_VECT(GDTA, 0xde0),
INTC_VECT(TMU3, 0xe00), INTC_VECT(TMU4, 0xe20),
INTC_VECT(TMU5, 0xe40),
INTC_VECT(SSI0, 0xe80), INTC_VECT(SSI1, 0xea0),
INTC_VECT(HAC0, 0xec0), INTC_VECT(HAC1, 0xee0),
- INTC_VECT(FLCTL_FLSTE, 0xf00), INTC_VECT(FLCTL_FLEND, 0xf20),
- INTC_VECT(FLCTL_FLTRQ0, 0xf40), INTC_VECT(FLCTL_FLTRQ1, 0xf60),
- INTC_VECT(GPIOI0, 0xf80), INTC_VECT(GPIOI1, 0xfa0),
- INTC_VECT(GPIOI2, 0xfc0), INTC_VECT(GPIOI3, 0xfe0),
+ INTC_VECT(FLCTL, 0xf00), INTC_VECT(FLCTL, 0xf20),
+ INTC_VECT(FLCTL, 0xf40), INTC_VECT(FLCTL, 0xf60),
+ INTC_VECT(GPIO, 0xf80), INTC_VECT(GPIO, 0xfa0),
+ INTC_VECT(GPIO, 0xfc0), INTC_VECT(GPIO, 0xfe0),
};
static struct intc_group groups[] __initdata = {
INTC_GROUP(TMU012, TMU0, TMU1, TMU2, TMU2_TICPI),
- INTC_GROUP(DMAC0, DMAC0_DMINT0, DMAC0_DMINT1, DMAC0_DMINT2,
- DMAC0_DMINT3, DMAC0_DMINT4, DMAC0_DMINT5, DMAC0_DMAE),
- INTC_GROUP(SCIF0, SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI),
- INTC_GROUP(SCIF1, SCIF1_ERI, SCIF1_RXI, SCIF1_BRI, SCIF1_TXI),
- INTC_GROUP(DMAC1, DMAC1_DMINT6, DMAC1_DMINT7, DMAC1_DMINT8,
- DMAC1_DMINT9, DMAC1_DMINT10, DMAC1_DMINT11, DMAC1_DMAE),
- INTC_GROUP(PCIC5, PCIERR, PCIPWD3, PCIPWD2, PCIPWD1, PCIPWD0),
- INTC_GROUP(MMCIF, MMCIF_FSTAT, MMCIF_TRAN, MMCIF_ERR, MMCIF_FRDY),
- INTC_GROUP(GDTA, GDTA_GACLI, GDTA_GAMCI, GDTA_GAERI),
INTC_GROUP(TMU345, TMU3, TMU4, TMU5),
- INTC_GROUP(FLCTL, FLCTL_FLSTE, FLCTL_FLEND,
- FLCTL_FLTRQ0, FLCTL_FLTRQ1),
- INTC_GROUP(GPIO, GPIOI0, GPIOI1, GPIOI2, GPIOI3),
};
static struct intc_mask_reg mask_registers[] __initdata = {
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c
new file mode 100644
index 0000000..5a47e1c
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c
@@ -0,0 +1,490 @@
+/*
+ * SH7786 Setup
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on SH7785 Setup
+ *
+ * Copyright (C) 2007 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/serial_sci.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <asm/mmzone.h>
+
+static struct plat_sci_port sci_platform_data[] = {
+ {
+ .mapbase = 0xffea0000,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 40, 41, 43, 42 },
+ },
+ /*
+ * The rest of these all have multiplexed IRQs
+ */
+ {
+ .mapbase = 0xffeb0000,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 44, 44, 44, 44 },
+ }, {
+ .mapbase = 0xffec0000,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 50, 50, 50, 50 },
+ }, {
+ .mapbase = 0xffed0000,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 51, 51, 51, 51 },
+ }, {
+ .mapbase = 0xffee0000,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 52, 52, 52, 52 },
+ }, {
+ .mapbase = 0xffef0000,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 53, 53, 53, 53 },
+ }, {
+ .flags = 0,
+ }
+};
+
+static struct platform_device sci_device = {
+ .name = "sh-sci",
+ .id = -1,
+ .dev = {
+ .platform_data = sci_platform_data,
+ },
+};
+
+static struct resource usb_ohci_resources[] = {
+ [0] = {
+ .start = 0xffe70400,
+ .end = 0xffe704ff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = 77,
+ .end = 77,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static u64 usb_ohci_dma_mask = DMA_BIT_MASK(32);
+static struct platform_device usb_ohci_device = {
+ .name = "sh_ohci",
+ .id = -1,
+ .dev = {
+ .dma_mask = &usb_ohci_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .num_resources = ARRAY_SIZE(usb_ohci_resources),
+ .resource = usb_ohci_resources,
+};
+
+static struct platform_device *sh7786_devices[] __initdata = {
+ &sci_device,
+ &usb_ohci_device,
+};
+
+
+/*
+ * Please call this function if your platform board
+ * use external clock for USB
+ * */
+#define USBCTL0 0xffe70858
+#define CLOCK_MODE_MASK 0xffffff7f
+#define EXT_CLOCK_MODE 0x00000080
+void __init sh7786_usb_use_exclock(void)
+{
+ u32 val = __raw_readl(USBCTL0) & CLOCK_MODE_MASK;
+ __raw_writel(val | EXT_CLOCK_MODE, USBCTL0);
+}
+
+#define USBINITREG1 0xffe70094
+#define USBINITREG2 0xffe7009c
+#define USBINITVAL1 0x00ff0040
+#define USBINITVAL2 0x00000001
+
+#define USBPCTL1 0xffe70804
+#define USBST 0xffe70808
+#define PHY_ENB 0x00000001
+#define PLL_ENB 0x00000002
+#define PHY_RST 0x00000004
+#define ACT_PLL_STATUS 0xc0000000
+static void __init sh7786_usb_setup(void)
+{
+ int i = 1000000;
+
+ /*
+ * USB initial settings
+ *
+ * The following settings are necessary
+ * for using the USB modules.
+ *
+ * see "USB Inital Settings" for detail
+ */
+ __raw_writel(USBINITVAL1, USBINITREG1);
+ __raw_writel(USBINITVAL2, USBINITREG2);
+
+ /*
+ * Set the PHY and PLL enable bit
+ */
+ __raw_writel(PHY_ENB | PLL_ENB, USBPCTL1);
+ while (i-- &&
+ ((__raw_readl(USBST) & ACT_PLL_STATUS) != ACT_PLL_STATUS))
+ cpu_relax();
+
+ if (i) {
+ /* Set the PHY RST bit */
+ __raw_writel(PHY_ENB | PLL_ENB | PHY_RST, USBPCTL1);
+ printk(KERN_INFO "sh7786 usb setup done\n");
+ }
+}
+
+static int __init sh7786_devices_setup(void)
+{
+ sh7786_usb_setup();
+ return platform_add_devices(sh7786_devices,
+ ARRAY_SIZE(sh7786_devices));
+}
+device_initcall(sh7786_devices_setup);
+
+enum {
+ UNUSED = 0,
+
+ /* interrupt sources */
+
+ IRL0_LLLL, IRL0_LLLH, IRL0_LLHL, IRL0_LLHH,
+ IRL0_LHLL, IRL0_LHLH, IRL0_LHHL, IRL0_LHHH,
+ IRL0_HLLL, IRL0_HLLH, IRL0_HLHL, IRL0_HLHH,
+ IRL0_HHLL, IRL0_HHLH, IRL0_HHHL,
+
+ IRL4_LLLL, IRL4_LLLH, IRL4_LLHL, IRL4_LLHH,
+ IRL4_LHLL, IRL4_LHLH, IRL4_LHHL, IRL4_LHHH,
+ IRL4_HLLL, IRL4_HLLH, IRL4_HLHL, IRL4_HLHH,
+ IRL4_HHLL, IRL4_HHLH, IRL4_HHHL,
+
+ IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7,
+ WDT,
+ TMU0_0, TMU0_1, TMU0_2, TMU0_3,
+ TMU1_0, TMU1_1, TMU1_2,
+ DMAC0_0, DMAC0_1, DMAC0_2, DMAC0_3, DMAC0_4, DMAC0_5, DMAC0_6,
+ HUDI1, HUDI0,
+ DMAC1_0, DMAC1_1, DMAC1_2, DMAC1_3,
+ HPB_0, HPB_1, HPB_2,
+ SCIF0_0, SCIF0_1, SCIF0_2, SCIF0_3,
+ SCIF1,
+ TMU2, TMU3,
+ SCIF2, SCIF3, SCIF4, SCIF5,
+ Eth_0, Eth_1,
+ PCIeC0_0, PCIeC0_1, PCIeC0_2,
+ PCIeC1_0, PCIeC1_1, PCIeC1_2,
+ USB,
+ I2C0, I2C1,
+ DU,
+ SSI0, SSI1, SSI2, SSI3,
+ PCIeC2_0, PCIeC2_1, PCIeC2_2,
+ HAC0, HAC1,
+ FLCTL,
+ HSPI,
+ GPIO0, GPIO1,
+ Thermal,
+ INTC0, INTC1, INTC2, INTC3, INTC4, INTC5, INTC6, INTC7,
+
+ /* interrupt groups */
+};
+
+static struct intc_vect vectors[] __initdata = {
+ INTC_VECT(WDT, 0x3e0),
+ INTC_VECT(TMU0_0, 0x400), INTC_VECT(TMU0_1, 0x420),
+ INTC_VECT(TMU0_2, 0x440), INTC_VECT(TMU0_3, 0x460),
+ INTC_VECT(TMU1_0, 0x480), INTC_VECT(TMU1_1, 0x4a0),
+ INTC_VECT(TMU1_2, 0x4c0),
+ INTC_VECT(DMAC0_0, 0x500), INTC_VECT(DMAC0_1, 0x520),
+ INTC_VECT(DMAC0_2, 0x540), INTC_VECT(DMAC0_3, 0x560),
+ INTC_VECT(DMAC0_4, 0x580), INTC_VECT(DMAC0_5, 0x5a0),
+ INTC_VECT(DMAC0_6, 0x5c0),
+ INTC_VECT(HUDI1, 0x5e0), INTC_VECT(HUDI0, 0x600),
+ INTC_VECT(DMAC1_0, 0x620), INTC_VECT(DMAC1_1, 0x640),
+ INTC_VECT(DMAC1_2, 0x660), INTC_VECT(DMAC1_3, 0x680),
+ INTC_VECT(HPB_0, 0x6a0), INTC_VECT(HPB_1, 0x6c0),
+ INTC_VECT(HPB_2, 0x6e0),
+ INTC_VECT(SCIF0_0, 0x700), INTC_VECT(SCIF0_1, 0x720),
+ INTC_VECT(SCIF0_2, 0x740), INTC_VECT(SCIF0_3, 0x760),
+ INTC_VECT(SCIF1, 0x780),
+ INTC_VECT(TMU2, 0x7a0), INTC_VECT(TMU3, 0x7c0),
+ INTC_VECT(SCIF2, 0x840), INTC_VECT(SCIF3, 0x860),
+ INTC_VECT(SCIF4, 0x880), INTC_VECT(SCIF5, 0x8a0),
+ INTC_VECT(Eth_0, 0x8c0), INTC_VECT(Eth_1, 0x8e0),
+ INTC_VECT(PCIeC0_0, 0xae0), INTC_VECT(PCIeC0_1, 0xb00),
+ INTC_VECT(PCIeC0_2, 0xb20),
+ INTC_VECT(PCIeC1_0, 0xb40), INTC_VECT(PCIeC1_1, 0xb60),
+ INTC_VECT(PCIeC1_2, 0xb80),
+ INTC_VECT(USB, 0xba0),
+ INTC_VECT(I2C0, 0xcc0), INTC_VECT(I2C1, 0xce0),
+ INTC_VECT(DU, 0xd00),
+ INTC_VECT(SSI0, 0xd20), INTC_VECT(SSI1, 0xd40),
+ INTC_VECT(SSI2, 0xd60), INTC_VECT(SSI3, 0xd80),
+ INTC_VECT(PCIeC2_0, 0xda0), INTC_VECT(PCIeC2_1, 0xdc0),
+ INTC_VECT(PCIeC2_2, 0xde0),
+ INTC_VECT(HAC0, 0xe00), INTC_VECT(HAC1, 0xe20),
+ INTC_VECT(FLCTL, 0xe40),
+ INTC_VECT(HSPI, 0xe80),
+ INTC_VECT(GPIO0, 0xea0), INTC_VECT(GPIO1, 0xec0),
+ INTC_VECT(Thermal, 0xee0),
+};
+
+/* FIXME: Main CPU support only now */
+#if 1 /* Main CPU */
+#define CnINTMSK0 0xfe410030
+#define CnINTMSK1 0xfe410040
+#define CnINTMSKCLR0 0xfe410050
+#define CnINTMSKCLR1 0xfe410060
+#define CnINT2MSKR0 0xfe410a20
+#define CnINT2MSKR1 0xfe410a24
+#define CnINT2MSKR2 0xfe410a28
+#define CnINT2MSKR3 0xfe410a2c
+#define CnINT2MSKCR0 0xfe410a30
+#define CnINT2MSKCR1 0xfe410a34
+#define CnINT2MSKCR2 0xfe410a38
+#define CnINT2MSKCR3 0xfe410a3c
+#else /* Sub CPU */
+#define CnINTMSK0 0xfe410034
+#define CnINTMSK1 0xfe410044
+#define CnINTMSKCLR0 0xfe410054
+#define CnINTMSKCLR1 0xfe410064
+#define CnINT2MSKR0 0xfe410b20
+#define CnINT2MSKR1 0xfe410b24
+#define CnINT2MSKR2 0xfe410b28
+#define CnINT2MSKR3 0xfe410b2c
+#define CnINT2MSKCR0 0xfe410b30
+#define CnINT2MSKCR1 0xfe410b34
+#define CnINT2MSKCR2 0xfe410b38
+#define CnINT2MSKCR3 0xfe410b3c
+#endif
+
+#define INTMSK2 0xfe410068
+#define INTMSKCLR2 0xfe41006c
+
+static struct intc_mask_reg mask_registers[] __initdata = {
+ { CnINTMSK0, CnINTMSKCLR0, 32,
+ { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } },
+ { INTMSK2, INTMSKCLR2, 32,
+ { IRL0_LLLL, IRL0_LLLH, IRL0_LLHL, IRL0_LLHH,
+ IRL0_LHLL, IRL0_LHLH, IRL0_LHHL, IRL0_LHHH,
+ IRL0_HLLL, IRL0_HLLH, IRL0_HLHL, IRL0_HLHH,
+ IRL0_HHLL, IRL0_HHLH, IRL0_HHHL, 0,
+ IRL4_LLLL, IRL4_LLLH, IRL4_LLHL, IRL4_LLHH,
+ IRL4_LHLL, IRL4_LHLH, IRL4_LHHL, IRL4_LHHH,
+ IRL4_HLLL, IRL4_HLLH, IRL4_HLHL, IRL4_HLHH,
+ IRL4_HHLL, IRL4_HHLH, IRL4_HHHL, 0, } },
+ { CnINT2MSKR0, CnINT2MSKCR0 , 32,
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, WDT } },
+ { CnINT2MSKR1, CnINT2MSKCR1, 32,
+ { TMU0_0, TMU0_1, TMU0_2, TMU0_3, TMU1_0, TMU1_1, TMU1_2, 0,
+ DMAC0_0, DMAC0_1, DMAC0_2, DMAC0_3, DMAC0_4, DMAC0_5, DMAC0_6,
+ HUDI1, HUDI0,
+ DMAC1_0, DMAC1_1, DMAC1_2, DMAC1_3,
+ HPB_0, HPB_1, HPB_2,
+ SCIF0_0, SCIF0_1, SCIF0_2, SCIF0_3,
+ SCIF1,
+ TMU2, TMU3, 0, } },
+ { CnINT2MSKR2, CnINT2MSKCR2, 32,
+ { 0, 0, SCIF2, SCIF3, SCIF4, SCIF5,
+ Eth_0, Eth_1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ PCIeC0_0, PCIeC0_1, PCIeC0_2,
+ PCIeC1_0, PCIeC1_1, PCIeC1_2,
+ USB, 0, 0 } },
+ { CnINT2MSKR3, CnINT2MSKCR3, 32,
+ { 0, 0, 0, 0, 0, 0,
+ I2C0, I2C1,
+ DU, SSI0, SSI1, SSI2, SSI3,
+ PCIeC2_0, PCIeC2_1, PCIeC2_2,
+ HAC0, HAC1,
+ FLCTL, 0,
+ HSPI, GPIO0, GPIO1, Thermal,
+ 0, 0, 0, 0, 0, 0, 0, 0 } },
+};
+
+static struct intc_prio_reg prio_registers[] __initdata = {
+ { 0xfe410010, 0, 32, 4, /* INTPRI */ { IRQ0, IRQ1, IRQ2, IRQ3,
+ IRQ4, IRQ5, IRQ6, IRQ7 } },
+ { 0xfe410800, 0, 32, 8, /* INT2PRI0 */ { 0, 0, 0, WDT } },
+ { 0xfe410804, 0, 32, 8, /* INT2PRI1 */ { TMU0_0, TMU0_1,
+ TMU0_2, TMU0_3 } },
+ { 0xfe410808, 0, 32, 8, /* INT2PRI2 */ { TMU1_0, TMU1_1,
+ TMU1_2, 0 } },
+ { 0xfe41080c, 0, 32, 8, /* INT2PRI3 */ { DMAC0_0, DMAC0_1,
+ DMAC0_2, DMAC0_3 } },
+ { 0xfe410810, 0, 32, 8, /* INT2PRI4 */ { DMAC0_4, DMAC0_5,
+ DMAC0_6, HUDI1 } },
+ { 0xfe410814, 0, 32, 8, /* INT2PRI5 */ { HUDI0, DMAC1_0,
+ DMAC1_1, DMAC1_2 } },
+ { 0xfe410818, 0, 32, 8, /* INT2PRI6 */ { DMAC1_3, HPB_0,
+ HPB_1, HPB_2 } },
+ { 0xfe41081c, 0, 32, 8, /* INT2PRI7 */ { SCIF0_0, SCIF0_1,
+ SCIF0_2, SCIF0_3 } },
+ { 0xfe410820, 0, 32, 8, /* INT2PRI8 */ { SCIF1, TMU2, TMU3, 0 } },
+ { 0xfe410824, 0, 32, 8, /* INT2PRI9 */ { 0, 0, SCIF2, SCIF3 } },
+ { 0xfe410828, 0, 32, 8, /* INT2PRI10 */ { SCIF4, SCIF5,
+ Eth_0, Eth_1 } },
+ { 0xfe41082c, 0, 32, 8, /* INT2PRI11 */ { 0, 0, 0, 0 } },
+ { 0xfe410830, 0, 32, 8, /* INT2PRI12 */ { 0, 0, 0, 0 } },
+ { 0xfe410834, 0, 32, 8, /* INT2PRI13 */ { 0, 0, 0, 0 } },
+ { 0xfe410838, 0, 32, 8, /* INT2PRI14 */ { 0, 0, 0, PCIeC0_0 } },
+ { 0xfe41083c, 0, 32, 8, /* INT2PRI15 */ { PCIeC0_1, PCIeC0_2,
+ PCIeC1_0, PCIeC1_1 } },
+ { 0xfe410840, 0, 32, 8, /* INT2PRI16 */ { PCIeC1_2, USB, 0, 0 } },
+ { 0xfe410844, 0, 32, 8, /* INT2PRI17 */ { 0, 0, 0, 0 } },
+ { 0xfe410848, 0, 32, 8, /* INT2PRI18 */ { 0, 0, I2C0, I2C1 } },
+ { 0xfe41084c, 0, 32, 8, /* INT2PRI19 */ { DU, SSI0, SSI1, SSI2 } },
+ { 0xfe410850, 0, 32, 8, /* INT2PRI20 */ { SSI3, PCIeC2_0,
+ PCIeC2_1, PCIeC2_2 } },
+ { 0xfe410854, 0, 32, 8, /* INT2PRI21 */ { HAC0, HAC1, FLCTL, 0 } },
+ { 0xfe410858, 0, 32, 8, /* INT2PRI22 */ { HSPI, GPIO0,
+ GPIO1, Thermal } },
+ { 0xfe41085c, 0, 32, 8, /* INT2PRI23 */ { 0, 0, 0, 0 } },
+ { 0xfe410860, 0, 32, 8, /* INT2PRI24 */ { 0, 0, 0, 0 } },
+};
+
+static DECLARE_INTC_DESC(intc_desc, "sh7786", vectors, NULL,
+ mask_registers, prio_registers, NULL);
+
+/* Support for external interrupt pins in IRQ mode */
+
+static struct intc_vect vectors_irq0123[] __initdata = {
+ INTC_VECT(IRQ0, 0x200), INTC_VECT(IRQ1, 0x240),
+ INTC_VECT(IRQ2, 0x280), INTC_VECT(IRQ3, 0x2c0),
+};
+
+static struct intc_vect vectors_irq4567[] __initdata = {
+ INTC_VECT(IRQ4, 0x300), INTC_VECT(IRQ5, 0x340),
+ INTC_VECT(IRQ6, 0x380), INTC_VECT(IRQ7, 0x3c0),
+};
+
+static struct intc_sense_reg sense_registers[] __initdata = {
+ { 0xfe41001c, 32, 2, /* ICR1 */ { IRQ0, IRQ1, IRQ2, IRQ3,
+ IRQ4, IRQ5, IRQ6, IRQ7 } },
+};
+
+static struct intc_mask_reg ack_registers[] __initdata = {
+ { 0xfe410024, 0, 32, /* INTREQ */
+ { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } },
+};
+
+static DECLARE_INTC_DESC_ACK(intc_desc_irq0123, "sh7786-irq0123",
+ vectors_irq0123, NULL, mask_registers,
+ prio_registers, sense_registers, ack_registers);
+
+static DECLARE_INTC_DESC_ACK(intc_desc_irq4567, "sh7786-irq4567",
+ vectors_irq4567, NULL, mask_registers,
+ prio_registers, sense_registers, ack_registers);
+
+/* External interrupt pins in IRL mode */
+
+static struct intc_vect vectors_irl0123[] __initdata = {
+ INTC_VECT(IRL0_LLLL, 0x200), INTC_VECT(IRL0_LLLH, 0x220),
+ INTC_VECT(IRL0_LLHL, 0x240), INTC_VECT(IRL0_LLHH, 0x260),
+ INTC_VECT(IRL0_LHLL, 0x280), INTC_VECT(IRL0_LHLH, 0x2a0),
+ INTC_VECT(IRL0_LHHL, 0x2c0), INTC_VECT(IRL0_LHHH, 0x2e0),
+ INTC_VECT(IRL0_HLLL, 0x300), INTC_VECT(IRL0_HLLH, 0x320),
+ INTC_VECT(IRL0_HLHL, 0x340), INTC_VECT(IRL0_HLHH, 0x360),
+ INTC_VECT(IRL0_HHLL, 0x380), INTC_VECT(IRL0_HHLH, 0x3a0),
+ INTC_VECT(IRL0_HHHL, 0x3c0),
+};
+
+static struct intc_vect vectors_irl4567[] __initdata = {
+ INTC_VECT(IRL4_LLLL, 0x900), INTC_VECT(IRL4_LLLH, 0x920),
+ INTC_VECT(IRL4_LLHL, 0x940), INTC_VECT(IRL4_LLHH, 0x960),
+ INTC_VECT(IRL4_LHLL, 0x980), INTC_VECT(IRL4_LHLH, 0x9a0),
+ INTC_VECT(IRL4_LHHL, 0x9c0), INTC_VECT(IRL4_LHHH, 0x9e0),
+ INTC_VECT(IRL4_HLLL, 0xa00), INTC_VECT(IRL4_HLLH, 0xa20),
+ INTC_VECT(IRL4_HLHL, 0xa40), INTC_VECT(IRL4_HLHH, 0xa60),
+ INTC_VECT(IRL4_HHLL, 0xa80), INTC_VECT(IRL4_HHLH, 0xaa0),
+ INTC_VECT(IRL4_HHHL, 0xac0),
+};
+
+static DECLARE_INTC_DESC(intc_desc_irl0123, "sh7786-irl0123", vectors_irl0123,
+ NULL, mask_registers, NULL, NULL);
+
+static DECLARE_INTC_DESC(intc_desc_irl4567, "sh7786-irl4567", vectors_irl4567,
+ NULL, mask_registers, NULL, NULL);
+
+#define INTC_ICR0 0xfe410000
+#define INTC_INTMSK0 CnINTMSK0
+#define INTC_INTMSK1 CnINTMSK1
+#define INTC_INTMSK2 INTMSK2
+#define INTC_INTMSKCLR1 CnINTMSKCLR1
+#define INTC_INTMSKCLR2 INTMSKCLR2
+
+void __init plat_irq_setup(void)
+{
+ /* disable IRQ3-0 + IRQ7-4 */
+ ctrl_outl(0xff000000, INTC_INTMSK0);
+
+ /* disable IRL3-0 + IRL7-4 */
+ ctrl_outl(0xc0000000, INTC_INTMSK1);
+ ctrl_outl(0xfffefffe, INTC_INTMSK2);
+
+ /* select IRL mode for IRL3-0 + IRL7-4 */
+ ctrl_outl(ctrl_inl(INTC_ICR0) & ~0x00c00000, INTC_ICR0);
+
+ register_intc_controller(&intc_desc);
+}
+
+void __init plat_irq_setup_pins(int mode)
+{
+ switch (mode) {
+ case IRQ_MODE_IRQ7654:
+ /* select IRQ mode for IRL7-4 */
+ ctrl_outl(ctrl_inl(INTC_ICR0) | 0x00400000, INTC_ICR0);
+ register_intc_controller(&intc_desc_irq4567);
+ break;
+ case IRQ_MODE_IRQ3210:
+ /* select IRQ mode for IRL3-0 */
+ ctrl_outl(ctrl_inl(INTC_ICR0) | 0x00800000, INTC_ICR0);
+ register_intc_controller(&intc_desc_irq0123);
+ break;
+ case IRQ_MODE_IRL7654:
+ /* enable IRL7-4 but don't provide any masking */
+ ctrl_outl(0x40000000, INTC_INTMSKCLR1);
+ ctrl_outl(0x0000fffe, INTC_INTMSKCLR2);
+ break;
+ case IRQ_MODE_IRL3210:
+ /* enable IRL0-3 but don't provide any masking */
+ ctrl_outl(0x80000000, INTC_INTMSKCLR1);
+ ctrl_outl(0xfffe0000, INTC_INTMSKCLR2);
+ break;
+ case IRQ_MODE_IRL7654_MASK:
+ /* enable IRL7-4 and mask using cpu intc controller */
+ ctrl_outl(0x40000000, INTC_INTMSKCLR1);
+ register_intc_controller(&intc_desc_irl4567);
+ break;
+ case IRQ_MODE_IRL3210_MASK:
+ /* enable IRL0-3 and mask using cpu intc controller */
+ ctrl_outl(0x80000000, INTC_INTMSKCLR1);
+ register_intc_controller(&intc_desc_irl0123);
+ break;
+ default:
+ BUG();
+ }
+}
+
+void __init plat_mem_setup(void)
+{
+}
diff --git a/arch/sh/kernel/cpu/shmobile/Makefile b/arch/sh/kernel/cpu/shmobile/Makefile
new file mode 100644
index 0000000..08bfa7c
--- /dev/null
+++ b/arch/sh/kernel/cpu/shmobile/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Linux/SuperH SH-Mobile backends.
+#
+
+# Power Management & Sleep mode
+obj-$(CONFIG_PM) += pm.o sleep.o
diff --git a/arch/sh/kernel/cpu/shmobile/pm.c b/arch/sh/kernel/cpu/shmobile/pm.c
new file mode 100644
index 0000000..8c067ad
--- /dev/null
+++ b/arch/sh/kernel/cpu/shmobile/pm.c
@@ -0,0 +1,92 @@
+/*
+ * arch/sh/kernel/cpu/sh4a/pm-sh_mobile.c
+ *
+ * Power management support code for SuperH Mobile
+ *
+ * Copyright (C) 2009 Magnus Damm
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/suspend.h>
+#include <asm/suspend.h>
+#include <asm/uaccess.h>
+
+/*
+ * Sleep modes available on SuperH Mobile:
+ *
+ * Sleep mode is just plain "sleep" instruction
+ * Sleep Self-Refresh mode is above plus RAM put in Self-Refresh
+ * Standby Self-Refresh mode is above plus stopped clocks
+ */
+#define SUSP_MODE_SLEEP (SUSP_SH_SLEEP)
+#define SUSP_MODE_SLEEP_SF (SUSP_SH_SLEEP | SUSP_SH_SF)
+#define SUSP_MODE_STANDBY_SF (SUSP_SH_STANDBY | SUSP_SH_SF)
+
+/*
+ * The following modes are not there yet:
+ *
+ * R-standby mode is unsupported, but will be added in the future
+ * U-standby mode is low priority since it needs bootloader hacks
+ *
+ * All modes should be tied in with cpuidle. But before that can
+ * happen we need to keep track of enabled hardware blocks so we
+ * can avoid entering sleep modes that stop clocks to hardware
+ * blocks that are in use even though the cpu core is idle.
+ */
+
+extern const unsigned char sh_mobile_standby[];
+extern const unsigned int sh_mobile_standby_size;
+
+static void sh_mobile_call_standby(unsigned long mode)
+{
+ extern void *vbr_base;
+ void *onchip_mem = (void *)0xe5200000; /* ILRAM */
+ void (*standby_onchip_mem)(unsigned long) = onchip_mem;
+
+ /* Note: Wake up from sleep may generate exceptions!
+ * Setup VBR to point to on-chip ram if self-refresh is
+ * going to be used.
+ */
+ if (mode & SUSP_SH_SF)
+ asm volatile("ldc %0, vbr" : : "r" (onchip_mem) : "memory");
+
+ /* Copy the assembly snippet to the otherwise ununsed ILRAM */
+ memcpy(onchip_mem, sh_mobile_standby, sh_mobile_standby_size);
+ wmb();
+ ctrl_barrier();
+
+ /* Let assembly snippet in on-chip memory handle the rest */
+ standby_onchip_mem(mode);
+
+ /* Put VBR back in System RAM again */
+ if (mode & SUSP_SH_SF)
+ asm volatile("ldc %0, vbr" : : "r" (&vbr_base) : "memory");
+}
+
+static int sh_pm_enter(suspend_state_t state)
+{
+ local_irq_disable();
+ set_bl_bit();
+ sh_mobile_call_standby(SUSP_MODE_STANDBY_SF);
+ local_irq_disable();
+ clear_bl_bit();
+ return 0;
+}
+
+static struct platform_suspend_ops sh_pm_ops = {
+ .enter = sh_pm_enter,
+ .valid = suspend_valid_only_mem,
+};
+
+static int __init sh_pm_init(void)
+{
+ suspend_set_ops(&sh_pm_ops);
+ return 0;
+}
+
+late_initcall(sh_pm_init);
diff --git a/arch/sh/kernel/cpu/shmobile/sleep.S b/arch/sh/kernel/cpu/shmobile/sleep.S
new file mode 100644
index 0000000..5d888ef
--- /dev/null
+++ b/arch/sh/kernel/cpu/shmobile/sleep.S
@@ -0,0 +1,125 @@
+/*
+ * arch/sh/kernel/cpu/sh4a/sleep-sh_mobile.S
+ *
+ * Sleep mode and Standby modes support for SuperH Mobile
+ *
+ * Copyright (C) 2009 Magnus Damm
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/sys.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/suspend.h>
+
+/* manage self-refresh and enter standby mode.
+ * this code will be copied to on-chip memory and executed from there.
+ */
+
+ .balign 4096,0,4096
+ENTRY(sh_mobile_standby)
+ mov r4, r0
+
+ tst #SUSP_SH_SF, r0
+ bt skip_set_sf
+
+ /* SDRAM: disable power down and put in self-refresh mode */
+ mov.l 1f, r4
+ mov.l 2f, r1
+ mov.l @r4, r2
+ or r1, r2
+ mov.l 3f, r3
+ and r3, r2
+ mov.l r2, @r4
+
+skip_set_sf:
+ tst #SUSP_SH_SLEEP, r0
+ bt test_standby
+
+ /* set mode to "sleep mode" */
+ bra do_sleep
+ mov #0x00, r1
+
+test_standby:
+ tst #SUSP_SH_STANDBY, r0
+ bt test_rstandby
+
+ /* set mode to "software standby mode" */
+ bra do_sleep
+ mov #0x80, r1
+
+test_rstandby:
+ tst #SUSP_SH_RSTANDBY, r0
+ bt test_ustandby
+
+ /* set mode to "r-standby mode" */
+ bra do_sleep
+ mov #0x20, r1
+
+test_ustandby:
+ tst #SUSP_SH_USTANDBY, r0
+ bt done_sleep
+
+ /* set mode to "u-standby mode" */
+ mov #0x10, r1
+
+ /* fall-through */
+
+do_sleep:
+ /* setup and enter selected standby mode */
+ mov.l 5f, r4
+ mov.l r1, @r4
+ sleep
+
+done_sleep:
+ /* reset standby mode to sleep mode */
+ mov.l 5f, r4
+ mov #0x00, r1
+ mov.l r1, @r4
+
+ tst #SUSP_SH_SF, r0
+ bt skip_restore_sf
+
+ /* SDRAM: set auto-refresh mode */
+ mov.l 1f, r4
+ mov.l @r4, r2
+ mov.l 4f, r3
+ and r3, r2
+ mov.l r2, @r4
+ mov.l 6f, r4
+ mov.l 7f, r1
+ mov.l 8f, r2
+ mov.l @r4, r3
+ mov #-1, r4
+ add r4, r3
+ or r2, r3
+ mov.l r3, @r1
+skip_restore_sf:
+ rts
+ nop
+
+ .balign 4
+1: .long 0xfe400008 /* SDCR0 */
+2: .long 0x00000400
+3: .long 0xffff7fff
+4: .long 0xfffffbff
+5: .long 0xa4150020 /* STBCR */
+6: .long 0xfe40001c /* RTCOR */
+7: .long 0xfe400018 /* RTCNT */
+8: .long 0xa55a0000
+
+/* interrupt vector @ 0x600 */
+ .balign 0x400,0,0x400
+ .long 0xdeadbeef
+ .balign 0x200,0,0x200
+ /* sh7722 will end up here in sleep mode */
+ rte
+ nop
+sh_mobile_standby_end:
+
+ENTRY(sh_mobile_standby_size)
+ .long sh_mobile_standby_end - sh_mobile_standby
diff --git a/arch/sh/kernel/gpio.c b/arch/sh/kernel/gpio.c
index d371653..d22e5af 100644
--- a/arch/sh/kernel/gpio.c
+++ b/arch/sh/kernel/gpio.c
@@ -19,22 +19,6 @@
#include <linux/bitops.h>
#include <linux/gpio.h>
-static struct pinmux_info *registered_gpio;
-
-static struct pinmux_info *gpio_controller(unsigned gpio)
-{
- if (!registered_gpio)
- return NULL;
-
- if (gpio < registered_gpio->first_gpio)
- return NULL;
-
- if (gpio > registered_gpio->last_gpio)
- return NULL;
-
- return registered_gpio;
-}
-
static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r)
{
if (enum_id < r->begin)
@@ -46,9 +30,64 @@
return 1;
}
-static int read_write_reg(unsigned long reg, unsigned long reg_width,
- unsigned long field_width, unsigned long in_pos,
- unsigned long value, int do_write)
+static unsigned long gpio_read_raw_reg(unsigned long reg,
+ unsigned long reg_width)
+{
+ switch (reg_width) {
+ case 8:
+ return ctrl_inb(reg);
+ case 16:
+ return ctrl_inw(reg);
+ case 32:
+ return ctrl_inl(reg);
+ }
+
+ BUG();
+ return 0;
+}
+
+static void gpio_write_raw_reg(unsigned long reg,
+ unsigned long reg_width,
+ unsigned long data)
+{
+ switch (reg_width) {
+ case 8:
+ ctrl_outb(data, reg);
+ return;
+ case 16:
+ ctrl_outw(data, reg);
+ return;
+ case 32:
+ ctrl_outl(data, reg);
+ return;
+ }
+
+ BUG();
+}
+
+static void gpio_write_bit(struct pinmux_data_reg *dr,
+ unsigned long in_pos, unsigned long value)
+{
+ unsigned long pos;
+
+ pos = dr->reg_width - (in_pos + 1);
+
+#ifdef DEBUG
+ pr_info("write_bit addr = %lx, value = %ld, pos = %ld, "
+ "r_width = %ld\n",
+ dr->reg, !!value, pos, dr->reg_width);
+#endif
+
+ if (value)
+ set_bit(pos, &dr->reg_shadow);
+ else
+ clear_bit(pos, &dr->reg_shadow);
+
+ gpio_write_raw_reg(dr->reg, dr->reg_width, dr->reg_shadow);
+}
+
+static int gpio_read_reg(unsigned long reg, unsigned long reg_width,
+ unsigned long field_width, unsigned long in_pos)
{
unsigned long data, mask, pos;
@@ -57,52 +96,53 @@
pos = reg_width - ((in_pos + 1) * field_width);
#ifdef DEBUG
- pr_info("%s, addr = %lx, value = %ld, pos = %ld, "
+ pr_info("read_reg: addr = %lx, pos = %ld, "
"r_width = %ld, f_width = %ld\n",
- do_write ? "write" : "read", reg, value, pos,
- reg_width, field_width);
+ reg, pos, reg_width, field_width);
#endif
- switch (reg_width) {
- case 8:
- data = ctrl_inb(reg);
- break;
- case 16:
- data = ctrl_inw(reg);
- break;
- case 32:
- data = ctrl_inl(reg);
- break;
- }
-
- if (!do_write)
- return (data >> pos) & mask;
-
- data &= ~(mask << pos);
- data |= value << pos;
-
- switch (reg_width) {
- case 8:
- ctrl_outb(data, reg);
- break;
- case 16:
- ctrl_outw(data, reg);
- break;
- case 32:
- ctrl_outl(data, reg);
- break;
- }
- return 0;
+ data = gpio_read_raw_reg(reg, reg_width);
+ return (data >> pos) & mask;
}
-static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio,
- struct pinmux_data_reg **drp, int *bitp)
+static void gpio_write_reg(unsigned long reg, unsigned long reg_width,
+ unsigned long field_width, unsigned long in_pos,
+ unsigned long value)
{
- pinmux_enum_t enum_id = gpioc->gpios[gpio].enum_id;
+ unsigned long mask, pos;
+
+ mask = (1 << field_width) - 1;
+ pos = reg_width - ((in_pos + 1) * field_width);
+
+#ifdef DEBUG
+ pr_info("write_reg addr = %lx, value = %ld, pos = %ld, "
+ "r_width = %ld, f_width = %ld\n",
+ reg, value, pos, reg_width, field_width);
+#endif
+
+ mask = ~(mask << pos);
+ value = value << pos;
+
+ switch (reg_width) {
+ case 8:
+ ctrl_outb((ctrl_inb(reg) & mask) | value, reg);
+ break;
+ case 16:
+ ctrl_outw((ctrl_inw(reg) & mask) | value, reg);
+ break;
+ case 32:
+ ctrl_outl((ctrl_inl(reg) & mask) | value, reg);
+ break;
+ }
+}
+
+static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio)
+{
+ struct pinmux_gpio *gpiop = &gpioc->gpios[gpio];
struct pinmux_data_reg *data_reg;
int k, n;
- if (!enum_in_range(enum_id, &gpioc->data))
+ if (!enum_in_range(gpiop->enum_id, &gpioc->data))
return -1;
k = 0;
@@ -113,19 +153,58 @@
break;
for (n = 0; n < data_reg->reg_width; n++) {
- if (data_reg->enum_ids[n] == enum_id) {
- *drp = data_reg;
- *bitp = n;
+ if (data_reg->enum_ids[n] == gpiop->enum_id) {
+ gpiop->flags &= ~PINMUX_FLAG_DREG;
+ gpiop->flags |= (k << PINMUX_FLAG_DREG_SHIFT);
+ gpiop->flags &= ~PINMUX_FLAG_DBIT;
+ gpiop->flags |= (n << PINMUX_FLAG_DBIT_SHIFT);
return 0;
-
}
}
k++;
}
+ BUG();
+
return -1;
}
+static void setup_data_regs(struct pinmux_info *gpioc)
+{
+ struct pinmux_data_reg *drp;
+ int k;
+
+ for (k = gpioc->first_gpio; k <= gpioc->last_gpio; k++)
+ setup_data_reg(gpioc, k);
+
+ k = 0;
+ while (1) {
+ drp = gpioc->data_regs + k;
+
+ if (!drp->reg_width)
+ break;
+
+ drp->reg_shadow = gpio_read_raw_reg(drp->reg, drp->reg_width);
+ k++;
+ }
+}
+
+static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio,
+ struct pinmux_data_reg **drp, int *bitp)
+{
+ struct pinmux_gpio *gpiop = &gpioc->gpios[gpio];
+ int k, n;
+
+ if (!enum_in_range(gpiop->enum_id, &gpioc->data))
+ return -1;
+
+ k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT;
+ n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT;
+ *drp = gpioc->data_regs + k;
+ *bitp = n;
+ return 0;
+}
+
static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id,
struct pinmux_cfg_reg **crp, int *indexp,
unsigned long **cntp)
@@ -187,9 +266,9 @@
return -1;
}
-static int write_config_reg(struct pinmux_info *gpioc,
- struct pinmux_cfg_reg *crp,
- int index)
+static void write_config_reg(struct pinmux_info *gpioc,
+ struct pinmux_cfg_reg *crp,
+ int index)
{
unsigned long ncomb, pos, value;
@@ -197,8 +276,7 @@
pos = index / ncomb;
value = index % ncomb;
- return read_write_reg(crp->reg, crp->reg_width,
- crp->field_width, pos, value, 1);
+ gpio_write_reg(crp->reg, crp->reg_width, crp->field_width, pos, value);
}
static int check_config_reg(struct pinmux_info *gpioc,
@@ -211,8 +289,8 @@
pos = index / ncomb;
value = index % ncomb;
- if (read_write_reg(crp->reg, crp->reg_width,
- crp->field_width, pos, 0, 0) == value)
+ if (gpio_read_reg(crp->reg, crp->reg_width,
+ crp->field_width, pos) == value)
return 0;
return -1;
@@ -220,8 +298,8 @@
enum { GPIO_CFG_DRYRUN, GPIO_CFG_REQ, GPIO_CFG_FREE };
-int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio,
- int pinmux_type, int cfg_mode)
+static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio,
+ int pinmux_type, int cfg_mode)
{
struct pinmux_cfg_reg *cr = NULL;
pinmux_enum_t enum_id;
@@ -287,8 +365,7 @@
break;
case GPIO_CFG_REQ:
- if (write_config_reg(gpioc, cr, index) != 0)
- goto out_err;
+ write_config_reg(gpioc, cr, index);
*cntp = *cntp + 1;
break;
@@ -305,9 +382,14 @@
static DEFINE_SPINLOCK(gpio_lock);
-int __gpio_request(unsigned gpio)
+static struct pinmux_info *chip_to_pinmux(struct gpio_chip *chip)
{
- struct pinmux_info *gpioc = gpio_controller(gpio);
+ return container_of(chip, struct pinmux_info, chip);
+}
+
+static int sh_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct pinmux_info *gpioc = chip_to_pinmux(chip);
struct pinmux_data_reg *dummy;
unsigned long flags;
int i, ret, pinmux_type;
@@ -319,29 +401,30 @@
spin_lock_irqsave(&gpio_lock, flags);
- if ((gpioc->gpios[gpio].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE)
+ if ((gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE)
goto err_unlock;
/* setup pin function here if no data is associated with pin */
- if (get_data_reg(gpioc, gpio, &dummy, &i) != 0)
+ if (get_data_reg(gpioc, offset, &dummy, &i) != 0)
pinmux_type = PINMUX_TYPE_FUNCTION;
else
pinmux_type = PINMUX_TYPE_GPIO;
if (pinmux_type == PINMUX_TYPE_FUNCTION) {
- if (pinmux_config_gpio(gpioc, gpio,
+ if (pinmux_config_gpio(gpioc, offset,
pinmux_type,
GPIO_CFG_DRYRUN) != 0)
goto err_unlock;
- if (pinmux_config_gpio(gpioc, gpio,
+ if (pinmux_config_gpio(gpioc, offset,
pinmux_type,
GPIO_CFG_REQ) != 0)
BUG();
}
- gpioc->gpios[gpio].flags = pinmux_type;
+ gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE;
+ gpioc->gpios[offset].flags |= pinmux_type;
ret = 0;
err_unlock:
@@ -349,11 +432,10 @@
err_out:
return ret;
}
-EXPORT_SYMBOL(__gpio_request);
-void gpio_free(unsigned gpio)
+static void sh_gpio_free(struct gpio_chip *chip, unsigned offset)
{
- struct pinmux_info *gpioc = gpio_controller(gpio);
+ struct pinmux_info *gpioc = chip_to_pinmux(chip);
unsigned long flags;
int pinmux_type;
@@ -362,20 +444,23 @@
spin_lock_irqsave(&gpio_lock, flags);
- pinmux_type = gpioc->gpios[gpio].flags & PINMUX_FLAG_TYPE;
- pinmux_config_gpio(gpioc, gpio, pinmux_type, GPIO_CFG_FREE);
- gpioc->gpios[gpio].flags = PINMUX_TYPE_NONE;
+ pinmux_type = gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE;
+ pinmux_config_gpio(gpioc, offset, pinmux_type, GPIO_CFG_FREE);
+ gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE;
+ gpioc->gpios[offset].flags |= PINMUX_TYPE_NONE;
spin_unlock_irqrestore(&gpio_lock, flags);
}
-EXPORT_SYMBOL(gpio_free);
static int pinmux_direction(struct pinmux_info *gpioc,
unsigned gpio, int new_pinmux_type)
{
- int ret, pinmux_type;
+ int pinmux_type;
+ int ret = -EINVAL;
- ret = -EINVAL;
+ if (!gpioc)
+ goto err_out;
+
pinmux_type = gpioc->gpios[gpio].flags & PINMUX_FLAG_TYPE;
switch (pinmux_type) {
@@ -401,102 +486,99 @@
GPIO_CFG_REQ) != 0)
BUG();
- gpioc->gpios[gpio].flags = new_pinmux_type;
+ gpioc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE;
+ gpioc->gpios[gpio].flags |= new_pinmux_type;
ret = 0;
err_out:
return ret;
}
-int gpio_direction_input(unsigned gpio)
+static int sh_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
- struct pinmux_info *gpioc = gpio_controller(gpio);
+ struct pinmux_info *gpioc = chip_to_pinmux(chip);
unsigned long flags;
- int ret = -EINVAL;
-
- if (!gpioc)
- goto err_out;
+ int ret;
spin_lock_irqsave(&gpio_lock, flags);
- ret = pinmux_direction(gpioc, gpio, PINMUX_TYPE_INPUT);
+ ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_INPUT);
spin_unlock_irqrestore(&gpio_lock, flags);
- err_out:
+
return ret;
}
-EXPORT_SYMBOL(gpio_direction_input);
-static int __gpio_get_set_value(struct pinmux_info *gpioc,
- unsigned gpio, int value,
- int do_write)
+static void sh_gpio_set_value(struct pinmux_info *gpioc,
+ unsigned gpio, int value)
{
struct pinmux_data_reg *dr = NULL;
int bit = 0;
- if (get_data_reg(gpioc, gpio, &dr, &bit) != 0)
+ if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0)
BUG();
else
- value = read_write_reg(dr->reg, dr->reg_width,
- 1, bit, !!value, do_write);
-
- return value;
+ gpio_write_bit(dr, bit, value);
}
-int gpio_direction_output(unsigned gpio, int value)
+static int sh_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
{
- struct pinmux_info *gpioc = gpio_controller(gpio);
+ struct pinmux_info *gpioc = chip_to_pinmux(chip);
unsigned long flags;
- int ret = -EINVAL;
+ int ret;
- if (!gpioc)
- goto err_out;
-
+ sh_gpio_set_value(gpioc, offset, value);
spin_lock_irqsave(&gpio_lock, flags);
- __gpio_get_set_value(gpioc, gpio, value, 1);
- ret = pinmux_direction(gpioc, gpio, PINMUX_TYPE_OUTPUT);
+ ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_OUTPUT);
spin_unlock_irqrestore(&gpio_lock, flags);
- err_out:
+
return ret;
}
-EXPORT_SYMBOL(gpio_direction_output);
-int gpio_get_value(unsigned gpio)
+static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio)
{
- struct pinmux_info *gpioc = gpio_controller(gpio);
- unsigned long flags;
- int value = 0;
+ struct pinmux_data_reg *dr = NULL;
+ int bit = 0;
- if (!gpioc)
+ if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) {
BUG();
- else {
- spin_lock_irqsave(&gpio_lock, flags);
- value = __gpio_get_set_value(gpioc, gpio, 0, 0);
- spin_unlock_irqrestore(&gpio_lock, flags);
+ return 0;
}
- return value;
+ return gpio_read_reg(dr->reg, dr->reg_width, 1, bit);
}
-EXPORT_SYMBOL(gpio_get_value);
-void gpio_set_value(unsigned gpio, int value)
+static int sh_gpio_get(struct gpio_chip *chip, unsigned offset)
{
- struct pinmux_info *gpioc = gpio_controller(gpio);
- unsigned long flags;
-
- if (!gpioc)
- BUG();
- else {
- spin_lock_irqsave(&gpio_lock, flags);
- __gpio_get_set_value(gpioc, gpio, value, 1);
- spin_unlock_irqrestore(&gpio_lock, flags);
- }
+ return sh_gpio_get_value(chip_to_pinmux(chip), offset);
}
-EXPORT_SYMBOL(gpio_set_value);
+
+static void sh_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ sh_gpio_set_value(chip_to_pinmux(chip), offset, value);
+}
int register_pinmux(struct pinmux_info *pip)
{
- registered_gpio = pip;
- pr_info("pinmux: %s handling gpio %d -> %d\n",
+ struct gpio_chip *chip = &pip->chip;
+
+ pr_info("sh pinmux: %s handling gpio %d -> %d\n",
pip->name, pip->first_gpio, pip->last_gpio);
- return 0;
+ setup_data_regs(pip);
+
+ chip->request = sh_gpio_request;
+ chip->free = sh_gpio_free;
+ chip->direction_input = sh_gpio_direction_input;
+ chip->get = sh_gpio_get;
+ chip->direction_output = sh_gpio_direction_output;
+ chip->set = sh_gpio_set;
+
+ WARN_ON(pip->first_gpio != 0); /* needs testing */
+
+ chip->label = pip->name;
+ chip->owner = THIS_MODULE;
+ chip->base = pip->first_gpio;
+ chip->ngpio = (pip->last_gpio - pip->first_gpio) + 1;
+
+ return gpiochip_add(chip);
}
diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c
index 64b7690..90d63ae 100644
--- a/arch/sh/kernel/irq.c
+++ b/arch/sh/kernel/irq.c
@@ -106,7 +106,7 @@
}
#endif
- irq = irq_demux(evt2irq(irq));
+ irq = irq_demux(intc_evt2irq(irq));
#ifdef CONFIG_IRQSTACKS
curctx = (union irq_ctx *)current_thread_info();
diff --git a/arch/sh/kernel/machine_kexec.c b/arch/sh/kernel/machine_kexec.c
index 94df56b..7ea2704 100644
--- a/arch/sh/kernel/machine_kexec.c
+++ b/arch/sh/kernel/machine_kexec.c
@@ -14,21 +14,22 @@
#include <linux/delay.h>
#include <linux/reboot.h>
#include <linux/numa.h>
+#include <linux/ftrace.h>
+#include <linux/suspend.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/mmu_context.h>
#include <asm/io.h>
#include <asm/cacheflush.h>
-typedef NORET_TYPE void (*relocate_new_kernel_t)(
- unsigned long indirection_page,
- unsigned long reboot_code_buffer,
- unsigned long start_address,
- unsigned long vbr_reg) ATTRIB_NORET;
+typedef void (*relocate_new_kernel_t)(unsigned long indirection_page,
+ unsigned long reboot_code_buffer,
+ unsigned long start_address);
extern const unsigned char relocate_new_kernel[];
extern const unsigned int relocate_new_kernel_size;
extern void *gdb_vbr_vector;
+extern void *vbr_base;
void machine_shutdown(void)
{
@@ -45,6 +46,12 @@
*/
int machine_kexec_prepare(struct kimage *image)
{
+ /* older versions of kexec-tools are passing
+ * the zImage entry point as a virtual address.
+ */
+ if (image->start != PHYSADDR(image->start))
+ return -EINVAL; /* upgrade your kexec-tools */
+
return 0;
}
@@ -73,17 +80,33 @@
*/
void machine_kexec(struct kimage *image)
{
-
unsigned long page_list;
unsigned long reboot_code_buffer;
- unsigned long vbr_reg;
relocate_new_kernel_t rnk;
+ unsigned long entry;
+ unsigned long *ptr;
+ int save_ftrace_enabled;
-#if defined(CONFIG_SH_STANDARD_BIOS)
- vbr_reg = ((unsigned long )gdb_vbr_vector) - 0x100;
-#else
- vbr_reg = 0x80000000; // dummy
+ /*
+ * Nicked from the mips version of machine_kexec():
+ * The generic kexec code builds a page list with physical
+ * addresses. Use phys_to_virt() to convert them to virtual.
+ */
+ for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE);
+ ptr = (entry & IND_INDIRECTION) ?
+ phys_to_virt(entry & PAGE_MASK) : ptr + 1) {
+ if (*ptr & IND_SOURCE || *ptr & IND_INDIRECTION ||
+ *ptr & IND_DESTINATION)
+ *ptr = (unsigned long) phys_to_virt(*ptr);
+ }
+
+#ifdef CONFIG_KEXEC_JUMP
+ if (image->preserve_context)
+ save_processor_state();
#endif
+
+ save_ftrace_enabled = __ftrace_enabled_save();
+
/* Interrupts aren't acceptable while we reboot */
local_irq_disable();
@@ -97,12 +120,37 @@
memcpy((void *)reboot_code_buffer, relocate_new_kernel,
relocate_new_kernel_size);
- kexec_info(image);
+ kexec_info(image);
flush_cache_all();
+#if defined(CONFIG_SH_STANDARD_BIOS)
+ asm volatile("ldc %0, vbr" :
+ : "r" (((unsigned long) gdb_vbr_vector) - 0x100)
+ : "memory");
+#endif
+
/* now call it */
rnk = (relocate_new_kernel_t) reboot_code_buffer;
- (*rnk)(page_list, reboot_code_buffer, P2SEGADDR(image->start), vbr_reg);
+ (*rnk)(page_list, reboot_code_buffer,
+ (unsigned long)phys_to_virt(image->start));
+
+#ifdef CONFIG_KEXEC_JUMP
+ asm volatile("ldc %0, vbr" : : "r" (&vbr_base) : "memory");
+
+ if (image->preserve_context)
+ restore_processor_state();
+
+ /* Convert page list back to physical addresses, what a mess. */
+ for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE);
+ ptr = (*ptr & IND_INDIRECTION) ?
+ phys_to_virt(*ptr & PAGE_MASK) : ptr + 1) {
+ if (*ptr & IND_SOURCE || *ptr & IND_INDIRECTION ||
+ *ptr & IND_DESTINATION)
+ *ptr = virt_to_phys(*ptr);
+ }
+#endif
+
+ __ftrace_enabled_restore(save_ftrace_enabled);
}
void arch_crash_save_vmcoreinfo(void)
diff --git a/arch/sh/kernel/relocate_kernel.S b/arch/sh/kernel/relocate_kernel.S
index c66cb32..fcc9934 100644
--- a/arch/sh/kernel/relocate_kernel.S
+++ b/arch/sh/kernel/relocate_kernel.S
@@ -4,6 +4,8 @@
*
* LANDISK/sh4 is supported. Maybe, SH archtecture works well.
*
+ * 2009-03-18 Magnus Damm - Added Kexec Jump support
+ *
* This source code is licensed under the GNU General Public License,
* Version 2. See the file COPYING for more details.
*/
@@ -16,23 +18,141 @@
/* r4 = indirection_page */
/* r5 = reboot_code_buffer */
/* r6 = start_address */
- /* r7 = vbr_reg */
- mov.l 10f,r8 /* PAGE_SIZE */
- mov.l 11f,r9 /* P2SEG */
+ mov.l 10f, r0 /* PAGE_SIZE */
+ add r5, r0 /* setup new stack at end of control page */
- /* stack setting */
- add r8,r5
- mov r5,r15
+ /* save r15->r8 to new stack */
+ mov.l r15, @-r0
+ mov r0, r15
+ mov.l r14, @-r15
+ mov.l r13, @-r15
+ mov.l r12, @-r15
+ mov.l r11, @-r15
+ mov.l r10, @-r15
+ mov.l r9, @-r15
+ mov.l r8, @-r15
+ /* save other random registers */
+ sts.l macl, @-r15
+ sts.l mach, @-r15
+ stc.l gbr, @-r15
+ stc.l ssr, @-r15
+ stc.l sr, @-r15
+ sts.l pr, @-r15
+ stc.l spc, @-r15
+
+ /* switch to bank1 and save r7->r0 */
+ mov.l 12f, r9
+ stc sr, r8
+ or r9, r8
+ ldc r8, sr
+ mov.l r7, @-r15
+ mov.l r6, @-r15
+ mov.l r5, @-r15
+ mov.l r4, @-r15
+ mov.l r3, @-r15
+ mov.l r2, @-r15
+ mov.l r1, @-r15
+ mov.l r0, @-r15
+
+ /* switch to bank0 and save r7->r0 */
+ mov.l 12f, r9
+ not r9, r9
+ stc sr, r8
+ and r9, r8
+ ldc r8, sr
+ mov.l r7, @-r15
+ mov.l r6, @-r15
+ mov.l r5, @-r15
+ mov.l r4, @-r15
+ mov.l r3, @-r15
+ mov.l r2, @-r15
+ mov.l r1, @-r15
+ mov.l r0, @-r15
+
+ mov.l r4, @-r15 /* save indirection page again */
+
+ bsr swap_pages /* swap pages before jumping to new kernel */
+ nop
+
+ mova 11f, r0
+ mov.l r15, @r0 /* save pointer to stack */
+
+ jsr @r6 /* hand over control to new kernel */
+ nop
+
+ mov.l 11f, r15 /* get pointer to stack */
+ mov.l @r15+, r4 /* restore r4 to get indirection page */
+
+ bsr swap_pages /* swap pages back to previous state */
+ nop
+
+ /* make sure bank0 is active and restore r0->r7 */
+ mov.l 12f, r9
+ not r9, r9
+ stc sr, r8
+ and r9, r8
+ ldc r8, sr
+ mov.l @r15+, r0
+ mov.l @r15+, r1
+ mov.l @r15+, r2
+ mov.l @r15+, r3
+ mov.l @r15+, r4
+ mov.l @r15+, r5
+ mov.l @r15+, r6
+ mov.l @r15+, r7
+
+ /* switch to bank1 and restore r0->r7 */
+ mov.l 12f, r9
+ stc sr, r8
+ or r9, r8
+ ldc r8, sr
+ mov.l @r15+, r0
+ mov.l @r15+, r1
+ mov.l @r15+, r2
+ mov.l @r15+, r3
+ mov.l @r15+, r4
+ mov.l @r15+, r5
+ mov.l @r15+, r6
+ mov.l @r15+, r7
+
+ /* switch back to bank0 */
+ mov.l 12f, r9
+ not r9, r9
+ stc sr, r8
+ and r9, r8
+ ldc r8, sr
+
+ /* restore other random registers */
+ ldc.l @r15+, spc
+ lds.l @r15+, pr
+ ldc.l @r15+, sr
+ ldc.l @r15+, ssr
+ ldc.l @r15+, gbr
+ lds.l @r15+, mach
+ lds.l @r15+, macl
+
+ /* restore r8->r15 */
+ mov.l @r15+, r8
+ mov.l @r15+, r9
+ mov.l @r15+, r10
+ mov.l @r15+, r11
+ mov.l @r15+, r12
+ mov.l @r15+, r13
+ mov.l @r15+, r14
+ mov.l @r15+, r15
+ rts
+ nop
+
+swap_pages:
bra 1f
- mov r4,r0 /* cmd = indirection_page */
+ mov r4,r0 /* cmd = indirection_page */
0:
mov.l @r4+,r0 /* cmd = *ind++ */
-1: /* addr = (cmd | P2SEG) & 0xfffffff0 */
+1: /* addr = cmd & 0xfffffff0 */
mov r0,r2
- or r9,r2
mov #-16,r1
and r1,r2
@@ -40,57 +160,70 @@
tst #1,r0
bt 2f
bra 0b
- mov r2,r5
+ mov r2,r5
2: /* else if(cmd & IND_INDIRECTION) ind = addr */
tst #2,r0
bt 3f
bra 0b
- mov r2,r4
+ mov r2,r4
-3: /* else if(cmd & IND_DONE) goto 6 */
+3: /* else if(cmd & IND_DONE) return */
tst #4,r0
bt 4f
- bra 6f
- nop
+ rts
+ nop
4: /* else if(cmd & IND_SOURCE) memcpy(dst,addr,PAGE_SIZE) */
tst #8,r0
bt 0b
- mov r8,r3
+ mov.l 10f,r3 /* PAGE_SIZE */
shlr2 r3
shlr2 r3
5:
dt r3
- mov.l @r2+,r1 /* 16n+0 */
- mov.l r1,@r5
- add #4,r5
- mov.l @r2+,r1 /* 16n+4 */
- mov.l r1,@r5
- add #4,r5
- mov.l @r2+,r1 /* 16n+8 */
- mov.l r1,@r5
- add #4,r5
- mov.l @r2+,r1 /* 16n+12 */
- mov.l r1,@r5
- add #4,r5
+
+ /* regular kexec just overwrites the destination page
+ * with the contents of the source page.
+ * for the kexec jump case we need to swap the contents
+ * of the pages.
+ * to keep it simple swap the contents for both cases.
+ */
+ mov.l @(0, r2), r8
+ mov.l @(0, r5), r1
+ mov.l r8, @(0, r5)
+ mov.l r1, @(0, r2)
+
+ mov.l @(4, r2), r8
+ mov.l @(4, r5), r1
+ mov.l r8, @(4, r5)
+ mov.l r1, @(4, r2)
+
+ mov.l @(8, r2), r8
+ mov.l @(8, r5), r1
+ mov.l r8, @(8, r5)
+ mov.l r1, @(8, r2)
+
+ mov.l @(12, r2), r8
+ mov.l @(12, r5), r1
+ mov.l r8, @(12, r5)
+ mov.l r1, @(12, r2)
+
+ add #16,r5
+ add #16,r2
bf 5b
bra 0b
- nop
-6:
-#ifdef CONFIG_SH_STANDARD_BIOS
- ldc r7, vbr
-#endif
- jmp @r6
- nop
+ nop
.align 2
10:
.long PAGE_SIZE
11:
- .long P2SEG
+ .long 0
+12:
+ .long 0x20000000 ! RB=1
relocate_new_kernel_end:
diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c
index 370d2cf..24c6025 100644
--- a/arch/sh/kernel/setup.c
+++ b/arch/sh/kernel/setup.c
@@ -432,6 +432,7 @@
[CPU_SH7763] = "SH7763", [CPU_SH7770] = "SH7770",
[CPU_SH7780] = "SH7780", [CPU_SH7781] = "SH7781",
[CPU_SH7343] = "SH7343", [CPU_SH7785] = "SH7785",
+ [CPU_SH7786] = "SH7786",
[CPU_SH7722] = "SH7722", [CPU_SHX3] = "SH-X3",
[CPU_SH5_101] = "SH5-101", [CPU_SH5_103] = "SH5-103",
[CPU_MXG] = "MX-G", [CPU_SH7723] = "SH7723",
@@ -448,7 +449,7 @@
/* Symbolic CPU flags, keep in sync with asm/cpu-features.h */
static const char *cpu_flags[] = {
"none", "fpu", "p2flush", "mmuassoc", "dsp", "perfctr",
- "ptea", "llsc", "l2", "op32", NULL
+ "ptea", "llsc", "l2", "op32", "pteaex", NULL
};
static void show_cpuflags(struct seq_file *m, struct sh_cpuinfo *c)
diff --git a/arch/sh/kernel/swsusp.c b/arch/sh/kernel/swsusp.c
new file mode 100644
index 0000000..12b64a0
--- /dev/null
+++ b/arch/sh/kernel/swsusp.c
@@ -0,0 +1,38 @@
+/*
+ * swsusp.c - SuperH hibernation support
+ *
+ * Copyright (C) 2009 Magnus Damm
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/suspend.h>
+#include <asm/suspend.h>
+#include <asm/sections.h>
+#include <asm/tlbflush.h>
+#include <asm/page.h>
+#include <asm/fpu.h>
+
+struct swsusp_arch_regs swsusp_arch_regs_cpu0;
+
+int pfn_is_nosave(unsigned long pfn)
+{
+ unsigned long begin_pfn = __pa(&__nosave_begin) >> PAGE_SHIFT;
+ unsigned long end_pfn = PAGE_ALIGN(__pa(&__nosave_end)) >> PAGE_SHIFT;
+
+ return (pfn >= begin_pfn) && (pfn < end_pfn);
+}
+
+void save_processor_state(void)
+{
+ init_fpu(current);
+}
+
+void restore_processor_state(void)
+{
+ local_flush_tlb_all();
+}
diff --git a/arch/sh/kernel/time_32.c b/arch/sh/kernel/time_32.c
index 8457f83..c34e1e0 100644
--- a/arch/sh/kernel/time_32.c
+++ b/arch/sh/kernel/time_32.c
@@ -41,14 +41,6 @@
return 0;
}
-/*
- * Null high precision timer functions for systems lacking one.
- */
-static cycle_t null_hpt_read(void)
-{
- return 0;
-}
-
void (*rtc_sh_get_time)(struct timespec *) = null_rtc_get_time;
int (*rtc_sh_set_time)(const time_t) = null_rtc_set_time;
@@ -112,7 +104,6 @@
EXPORT_SYMBOL(do_settimeofday);
#endif /* !CONFIG_GENERIC_TIME */
-#ifndef CONFIG_GENERIC_CLOCKEVENTS
/* last time the RTC clock got updated */
static long last_rtc_update;
@@ -156,7 +147,6 @@
update_process_times(user_mode(get_irq_regs()));
#endif
}
-#endif /* !CONFIG_GENERIC_CLOCKEVENTS */
#ifdef CONFIG_PM
int timer_suspend(struct sys_device *dev, pm_message_t state)
@@ -189,7 +179,12 @@
static int __init timer_init_sysfs(void)
{
- int ret = sysdev_class_register(&timer_sysclass);
+ int ret;
+
+ if (!sys_timer)
+ return 0;
+
+ ret = sysdev_class_register(&timer_sysclass);
if (ret != 0)
return ret;
@@ -200,42 +195,21 @@
void (*board_time_init)(void);
-/*
- * Shamelessly based on the MIPS and Sparc64 work.
- */
-static unsigned long timer_ticks_per_nsec_quotient __read_mostly;
-unsigned long sh_hpt_frequency = 0;
-
-#define NSEC_PER_CYC_SHIFT 10
-
-static struct clocksource clocksource_sh = {
+struct clocksource clocksource_sh = {
.name = "SuperH",
- .rating = 200,
- .mask = CLOCKSOURCE_MASK(32),
- .read = null_hpt_read,
- .shift = 16,
- .flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
-static void __init init_sh_clocksource(void)
-{
- if (!sh_hpt_frequency || clocksource_sh.read == null_hpt_read)
- return;
-
- clocksource_sh.mult = clocksource_hz2mult(sh_hpt_frequency,
- clocksource_sh.shift);
-
- timer_ticks_per_nsec_quotient =
- clocksource_hz2mult(sh_hpt_frequency, NSEC_PER_CYC_SHIFT);
-
- clocksource_register(&clocksource_sh);
-}
-
#ifdef CONFIG_GENERIC_TIME
unsigned long long sched_clock(void)
{
- unsigned long long ticks = clocksource_sh.read();
- return (ticks * timer_ticks_per_nsec_quotient) >> NSEC_PER_CYC_SHIFT;
+ unsigned long long cycles;
+
+ /* jiffies based sched_clock if no clocksource is installed */
+ if (!clocksource_sh.rating)
+ return (unsigned long long)jiffies * (NSEC_PER_SEC / HZ);
+
+ cycles = clocksource_sh.read();
+ return cyc2ns(&clocksource_sh, cycles);
}
#endif
@@ -259,17 +233,8 @@
* initialized for us.
*/
sys_timer = get_sys_timer();
+ if (unlikely(!sys_timer))
+ panic("System timer missing.\n");
+
printk(KERN_INFO "Using %s for system timer\n", sys_timer->name);
-
-
- if (sys_timer->ops->read)
- clocksource_sh.read = sys_timer->ops->read;
-
- init_sh_clocksource();
-
- if (sh_hpt_frequency)
- printk("Using %lu.%03lu MHz high precision timer.\n",
- ((sh_hpt_frequency + 500) / 1000) / 1000,
- ((sh_hpt_frequency + 500) / 1000) % 1000);
-
}
diff --git a/arch/sh/kernel/timers/timer-mtu2.c b/arch/sh/kernel/timers/timer-mtu2.c
index c3d237e..9a77ae8 100644
--- a/arch/sh/kernel/timers/timer-mtu2.c
+++ b/arch/sh/kernel/timers/timer-mtu2.c
@@ -35,7 +35,8 @@
#define MTU2_TSR_1 0xfffe4385
#define MTU2_TCNT_1 0xfffe4386 /* 16-bit counter */
-#if defined(CONFIG_CPU_SUBTYPE_SH7201)
+#if defined(CONFIG_CPU_SUBTYPE_SH7201) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7203)
#define MTU2_TGRA_1 0xfffe4388
#else
#define MTU2_TGRA_1 0xfffe438a
diff --git a/arch/sh/kernel/timers/timer-tmu.c b/arch/sh/kernel/timers/timer-tmu.c
index 0db3f95..10b5a6f 100644
--- a/arch/sh/kernel/timers/timer-tmu.c
+++ b/arch/sh/kernel/timers/timer-tmu.c
@@ -146,7 +146,14 @@
_tmu_clear_status(TMU0);
_tmu_set_irq(TMU0,tmu0_clockevent.mode != CLOCK_EVT_MODE_ONESHOT);
- evt->event_handler(evt);
+ switch (tmu0_clockevent.mode) {
+ case CLOCK_EVT_MODE_ONESHOT:
+ case CLOCK_EVT_MODE_PERIODIC:
+ evt->event_handler(evt);
+ break;
+ default:
+ break;
+ }
return IRQ_HANDLED;
}
@@ -237,6 +244,7 @@
!defined(CONFIG_CPU_SUBTYPE_SH7721) && \
!defined(CONFIG_CPU_SUBTYPE_SH7760) && \
!defined(CONFIG_CPU_SUBTYPE_SH7785) && \
+ !defined(CONFIG_CPU_SUBTYPE_SH7786) && \
!defined(CONFIG_CPU_SUBTYPE_SHX3)
ctrl_outb(TMU_TOCR_INIT, TMU_TOCR);
#endif
@@ -254,7 +262,14 @@
_tmu_start(TMU1);
- sh_hpt_frequency = clk_get_rate(&tmu1_clk);
+ clocksource_sh.rating = 200;
+ clocksource_sh.mask = CLOCKSOURCE_MASK(32);
+ clocksource_sh.read = tmu_timer_read;
+ clocksource_sh.shift = 10;
+ clocksource_sh.mult = clocksource_hz2mult(clk_get_rate(&tmu1_clk),
+ clocksource_sh.shift);
+ clocksource_sh.flags = CLOCK_SOURCE_IS_CONTINUOUS;
+ clocksource_register(&clocksource_sh);
tmu0_clockevent.mult = div_sc(frequency, NSEC_PER_SEC,
tmu0_clockevent.shift);
@@ -264,6 +279,7 @@
clockevent_delta2ns(1, &tmu0_clockevent);
tmu0_clockevent.cpumask = cpumask_of(0);
+ tmu0_clockevent.rating = 100;
clockevents_register_device(&tmu0_clockevent);
@@ -274,7 +290,6 @@
.init = tmu_timer_init,
.start = tmu_timer_start,
.stop = tmu_timer_stop,
- .read = tmu_timer_read,
};
struct sys_timer tmu_timer = {
diff --git a/arch/sh/kernel/vmlinux_32.lds.S b/arch/sh/kernel/vmlinux_32.lds.S
index 7b4b82bd..d0b2a71 100644
--- a/arch/sh/kernel/vmlinux_32.lds.S
+++ b/arch/sh/kernel/vmlinux_32.lds.S
@@ -15,7 +15,10 @@
ENTRY(_start)
SECTIONS
{
-#ifdef CONFIG_32BIT
+#ifdef CONFIG_PMB_FIXED
+ . = CONFIG_PAGE_OFFSET + (CONFIG_MEMORY_START & 0x1fffffff) +
+ CONFIG_ZERO_PAGE_OFFSET;
+#elif defined(CONFIG_32BIT)
. = CONFIG_PAGE_OFFSET + CONFIG_ZERO_PAGE_OFFSET;
#else
. = CONFIG_PAGE_OFFSET + CONFIG_MEMORY_START + CONFIG_ZERO_PAGE_OFFSET;
diff --git a/arch/sh/mm/Kconfig b/arch/sh/mm/Kconfig
index 555ec97..10c2435 100644
--- a/arch/sh/mm/Kconfig
+++ b/arch/sh/mm/Kconfig
@@ -57,7 +57,7 @@
bool
default y if CPU_SH5
-config PMB
+config PMB_ENABLE
bool "Support 32-bit physical addressing through PMB"
depends on MMU && EXPERIMENTAL && (CPU_SUBTYPE_SH7780 || CPU_SUBTYPE_SH7785)
select 32BIT
@@ -67,6 +67,33 @@
32-bits through the SH-4A PMB. If this is not set, legacy
29-bit physical addressing will be used.
+choice
+ prompt "PMB handling type"
+ depends on PMB_ENABLE
+ default PMB_FIXED
+
+config PMB
+ bool "PMB"
+ depends on MMU && EXPERIMENTAL && (CPU_SUBTYPE_SH7780 || CPU_SUBTYPE_SH7785)
+ select 32BIT
+ help
+ If you say Y here, physical addressing will be extended to
+ 32-bits through the SH-4A PMB. If this is not set, legacy
+ 29-bit physical addressing will be used.
+
+config PMB_FIXED
+ bool "fixed PMB"
+ depends on MMU && EXPERIMENTAL && (CPU_SUBTYPE_SH7780 || \
+ CPU_SUBTYPE_SH7785)
+ select 32BIT
+ help
+ If this option is enabled, fixed PMB mappings are inherited
+ from the boot loader, and the kernel does not attempt dynamic
+ management. This is the closest to legacy 29-bit physical mode,
+ and allows systems to support up to 512MiB of system memory.
+
+endchoice
+
config X2TLB
bool "Enable extended TLB mode"
depends on (CPU_SHX2 || CPU_SHX3) && MMU && EXPERIMENTAL
diff --git a/arch/sh/mm/Makefile_32 b/arch/sh/mm/Makefile_32
index cb2f3f2..986a1e0 100644
--- a/arch/sh/mm/Makefile_32
+++ b/arch/sh/mm/Makefile_32
@@ -25,8 +25,10 @@
endif
ifdef CONFIG_MMU
-obj-$(CONFIG_CPU_SH3) += tlb-sh3.o
-obj-$(CONFIG_CPU_SH4) += tlb-sh4.o
+tlb-$(CONFIG_CPU_SH3) := tlb-sh3.o
+tlb-$(CONFIG_CPU_SH4) := tlb-sh4.o
+tlb-$(CONFIG_CPU_HAS_PTEAEX) := tlb-pteaex.o
+obj-y += $(tlb-y)
ifndef CONFIG_CACHE_OFF
obj-$(CONFIG_CPU_SH4) += pg-sh4.o
obj-$(CONFIG_SH7705_CACHE_32KB) += pg-sh7705.o
@@ -35,6 +37,7 @@
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
obj-$(CONFIG_PMB) += pmb.o
+obj-$(CONFIG_PMB_FIXED) += pmb-fixed.o
obj-$(CONFIG_NUMA) += numa.o
EXTRA_CFLAGS += -Werror
diff --git a/arch/sh/mm/asids-debugfs.c b/arch/sh/mm/asids-debugfs.c
index 8e912a1..cd8c3bf3 100644
--- a/arch/sh/mm/asids-debugfs.c
+++ b/arch/sh/mm/asids-debugfs.c
@@ -37,10 +37,8 @@
continue;
if (p->mm)
- seq_printf(file, "%5d : %02lx\n", pid,
+ seq_printf(file, "%5d : %04lx\n", pid,
cpu_asid(smp_processor_id(), p->mm));
- else
- seq_printf(file, "%5d : (none)\n", pid);
}
read_unlock(&tasklist_lock);
diff --git a/arch/sh/mm/ioremap_32.c b/arch/sh/mm/ioremap_32.c
index 32946fb..60cc486 100644
--- a/arch/sh/mm/ioremap_32.c
+++ b/arch/sh/mm/ioremap_32.c
@@ -59,11 +59,13 @@
if (is_pci_memaddr(phys_addr) && is_pci_memaddr(last_addr))
return (void __iomem *)phys_addr;
+#if !defined(CONFIG_PMB_FIXED)
/*
* Don't allow anybody to remap normal RAM that we're using..
*/
if (phys_addr < virt_to_phys(high_memory))
return NULL;
+#endif
/*
* Mappings have to be page-aligned
@@ -81,7 +83,7 @@
area->phys_addr = phys_addr;
orig_addr = addr = (unsigned long)area->addr;
-#ifdef CONFIG_32BIT
+#ifdef CONFIG_PMB
/*
* First try to remap through the PMB once a valid VMA has been
* established. Smaller allocations (or the rest of the size
@@ -119,10 +121,10 @@
unsigned long seg = PXSEG(vaddr);
struct vm_struct *p;
- if (seg < P3SEG || seg >= P3_ADDR_MAX || is_pci_memaddr(vaddr))
+ if (seg < P3SEG || vaddr >= P3_ADDR_MAX || is_pci_memaddr(vaddr))
return;
-#ifdef CONFIG_32BIT
+#ifdef CONFIG_PMB
/*
* Purge any PMB entries that may have been established for this
* mapping, then proceed with conventional VMA teardown.
diff --git a/arch/sh/mm/pmb-fixed.c b/arch/sh/mm/pmb-fixed.c
new file mode 100644
index 0000000..43c8eac
--- /dev/null
+++ b/arch/sh/mm/pmb-fixed.c
@@ -0,0 +1,45 @@
+/*
+ * arch/sh/mm/fixed_pmb.c
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <asm/mmu.h>
+#include <asm/mmu_context.h>
+
+static int __uses_jump_to_uncached fixed_pmb_init(void)
+{
+ int i;
+ unsigned long addr, data;
+
+ jump_to_uncached();
+
+ for (i = 0; i < PMB_ENTRY_MAX; i++) {
+ addr = PMB_DATA + (i << PMB_E_SHIFT);
+ data = ctrl_inl(addr);
+ if (!(data & PMB_V))
+ continue;
+
+ if (data & PMB_C) {
+#if defined(CONFIG_CACHE_WRITETHROUGH)
+ data |= PMB_WT;
+#elif defined(CONFIG_CACHE_WRITEBACK)
+ data &= ~PMB_WT;
+#else
+ data &= ~(PMB_C | PMB_WT);
+#endif
+ }
+ ctrl_outl(data, addr);
+ }
+
+ back_to_cached();
+
+ return 0;
+}
+arch_initcall(fixed_pmb_init);
diff --git a/arch/sh/mm/pmb.c b/arch/sh/mm/pmb.c
index 8424167..b1a714a 100644
--- a/arch/sh/mm/pmb.c
+++ b/arch/sh/mm/pmb.c
@@ -15,6 +15,8 @@
*/
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/sysdev.h>
+#include <linux/cpu.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/bitops.h>
@@ -402,3 +404,39 @@
return 0;
}
postcore_initcall(pmb_debugfs_init);
+
+#ifdef CONFIG_PM
+static int pmb_sysdev_suspend(struct sys_device *dev, pm_message_t state)
+{
+ static pm_message_t prev_state;
+
+ /* Restore the PMB after a resume from hibernation */
+ if (state.event == PM_EVENT_ON &&
+ prev_state.event == PM_EVENT_FREEZE) {
+ struct pmb_entry *pmbe;
+ spin_lock_irq(&pmb_list_lock);
+ for (pmbe = pmb_list; pmbe; pmbe = pmbe->next)
+ set_pmb_entry(pmbe);
+ spin_unlock_irq(&pmb_list_lock);
+ }
+ prev_state = state;
+ return 0;
+}
+
+static int pmb_sysdev_resume(struct sys_device *dev)
+{
+ return pmb_sysdev_suspend(dev, PMSG_ON);
+}
+
+static struct sysdev_driver pmb_sysdev_driver = {
+ .suspend = pmb_sysdev_suspend,
+ .resume = pmb_sysdev_resume,
+};
+
+static int __init pmb_sysdev_init(void)
+{
+ return sysdev_driver_register(&cpu_sysdev_class, &pmb_sysdev_driver);
+}
+
+subsys_initcall(pmb_sysdev_init);
+#endif
diff --git a/arch/sh/mm/tlb-pteaex.c b/arch/sh/mm/tlb-pteaex.c
new file mode 100644
index 0000000..2aab3ea
--- /dev/null
+++ b/arch/sh/mm/tlb-pteaex.c
@@ -0,0 +1,96 @@
+/*
+ * arch/sh/mm/tlb-pteaex.c
+ *
+ * TLB operations for SH-X3 CPUs featuring PTE ASID Extensions.
+ *
+ * Copyright (C) 2009 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <asm/system.h>
+#include <asm/mmu_context.h>
+#include <asm/cacheflush.h>
+
+void update_mmu_cache(struct vm_area_struct * vma,
+ unsigned long address, pte_t pte)
+{
+ unsigned long flags;
+ unsigned long pteval;
+ unsigned long vpn;
+
+ /* Ptrace may call this routine. */
+ if (vma && current->active_mm != vma->vm_mm)
+ return;
+
+#ifndef CONFIG_CACHE_OFF
+ {
+ unsigned long pfn = pte_pfn(pte);
+
+ if (pfn_valid(pfn)) {
+ struct page *page = pfn_to_page(pfn);
+
+ if (!test_bit(PG_mapped, &page->flags)) {
+ unsigned long phys = pte_val(pte) & PTE_PHYS_MASK;
+ __flush_wback_region((void *)P1SEGADDR(phys),
+ PAGE_SIZE);
+ __set_bit(PG_mapped, &page->flags);
+ }
+ }
+ }
+#endif
+
+ local_irq_save(flags);
+
+ /* Set PTEH register */
+ vpn = address & MMU_VPN_MASK;
+ __raw_writel(vpn, MMU_PTEH);
+
+ /* Set PTEAEX */
+ __raw_writel(get_asid(), MMU_PTEAEX);
+
+ pteval = pte.pte_low;
+
+ /* Set PTEA register */
+#ifdef CONFIG_X2TLB
+ /*
+ * For the extended mode TLB this is trivial, only the ESZ and
+ * EPR bits need to be written out to PTEA, with the remainder of
+ * the protection bits (with the exception of the compat-mode SZ
+ * and PR bits, which are cleared) being written out in PTEL.
+ */
+ __raw_writel(pte.pte_high, MMU_PTEA);
+#endif
+
+ /* Set PTEL register */
+ pteval &= _PAGE_FLAGS_HARDWARE_MASK; /* drop software flags */
+#ifdef CONFIG_CACHE_WRITETHROUGH
+ pteval |= _PAGE_WT;
+#endif
+ /* conveniently, we want all the software flags to be 0 anyway */
+ __raw_writel(pteval, MMU_PTEL);
+
+ /* Load the TLB */
+ asm volatile("ldtlb": /* no output */ : /* no input */ : "memory");
+ local_irq_restore(flags);
+}
+
+/*
+ * While SH-X2 extended TLB mode splits out the memory-mapped I/UTLB
+ * data arrays, SH-X3 cores with PTEAEX split out the memory-mapped
+ * address arrays. In compat mode the second array is inaccessible, while
+ * in extended mode, the legacy 8-bit ASID field in address array 1 has
+ * undefined behaviour.
+ */
+void __uses_jump_to_uncached local_flush_tlb_one(unsigned long asid,
+ unsigned long page)
+{
+ jump_to_uncached();
+ __raw_writel(page, MMU_UTLB_ADDRESS_ARRAY | MMU_PAGE_ASSOC_BIT);
+ __raw_writel(asid, MMU_UTLB_ADDRESS_ARRAY2 | MMU_PAGE_ASSOC_BIT);
+ back_to_cached();
+}
diff --git a/arch/sh/oprofile/common.c b/arch/sh/oprofile/common.c
index 1d97d64..1b9d430 100644
--- a/arch/sh/oprofile/common.c
+++ b/arch/sh/oprofile/common.c
@@ -107,6 +107,7 @@
case CPU_SH7780:
case CPU_SH7781:
case CPU_SH7785:
+ case CPU_SH7786:
case CPU_SH7723:
case CPU_SHX3:
lmodel = &op_model_sh4a_ops;
diff --git a/arch/sh/tools/mach-types b/arch/sh/tools/mach-types
index 284b7e8..8477b5d 100644
--- a/arch/sh/tools/mach-types
+++ b/arch/sh/tools/mach-types
@@ -52,3 +52,6 @@
AP325RXA SH_AP325RXA
SH7763RDP SH_SH7763RDP
SH7785LCR SH_SH7785LCR
+URQUELL SH_URQUELL
+ESPT SH_ESPT
+POLARIS SH_POLARIS
diff --git a/arch/x86/crypto/Makefile b/arch/x86/crypto/Makefile
index 903de4a..ebe7dee 100644
--- a/arch/x86/crypto/Makefile
+++ b/arch/x86/crypto/Makefile
@@ -9,6 +9,7 @@
obj-$(CONFIG_CRYPTO_AES_X86_64) += aes-x86_64.o
obj-$(CONFIG_CRYPTO_TWOFISH_X86_64) += twofish-x86_64.o
obj-$(CONFIG_CRYPTO_SALSA20_X86_64) += salsa20-x86_64.o
+obj-$(CONFIG_CRYPTO_AES_NI_INTEL) += aesni-intel.o
obj-$(CONFIG_CRYPTO_CRC32C_INTEL) += crc32c-intel.o
@@ -19,3 +20,5 @@
aes-x86_64-y := aes-x86_64-asm_64.o aes_glue.o
twofish-x86_64-y := twofish-x86_64-asm_64.o twofish_glue.o
salsa20-x86_64-y := salsa20-x86_64-asm_64.o salsa20_glue.o
+
+aesni-intel-y := aesni-intel_asm.o aesni-intel_glue.o
diff --git a/arch/x86/crypto/aes-i586-asm_32.S b/arch/x86/crypto/aes-i586-asm_32.S
index e41b147..b949ec2 100644
--- a/arch/x86/crypto/aes-i586-asm_32.S
+++ b/arch/x86/crypto/aes-i586-asm_32.S
@@ -41,14 +41,14 @@
#define tlen 1024 // length of each of 4 'xor' arrays (256 32-bit words)
/* offsets to parameters with one register pushed onto stack */
-#define tfm 8
+#define ctx 8
#define out_blk 12
#define in_blk 16
-/* offsets in crypto_tfm structure */
-#define klen (crypto_tfm_ctx_offset + 0)
-#define ekey (crypto_tfm_ctx_offset + 4)
-#define dkey (crypto_tfm_ctx_offset + 244)
+/* offsets in crypto_aes_ctx structure */
+#define klen (480)
+#define ekey (0)
+#define dkey (240)
// register mapping for encrypt and decrypt subroutines
@@ -217,7 +217,7 @@
do_col (table, r5,r0,r1,r4, r2,r3); /* idx=r5 */
// AES (Rijndael) Encryption Subroutine
-/* void aes_enc_blk(struct crypto_tfm *tfm, u8 *out_blk, const u8 *in_blk) */
+/* void aes_enc_blk(struct crypto_aes_ctx *ctx, u8 *out_blk, const u8 *in_blk) */
.global aes_enc_blk
@@ -228,7 +228,7 @@
aes_enc_blk:
push %ebp
- mov tfm(%esp),%ebp
+ mov ctx(%esp),%ebp
// CAUTION: the order and the values used in these assigns
// rely on the register mappings
@@ -292,7 +292,7 @@
ret
// AES (Rijndael) Decryption Subroutine
-/* void aes_dec_blk(struct crypto_tfm *tfm, u8 *out_blk, const u8 *in_blk) */
+/* void aes_dec_blk(struct crypto_aes_ctx *ctx, u8 *out_blk, const u8 *in_blk) */
.global aes_dec_blk
@@ -303,7 +303,7 @@
aes_dec_blk:
push %ebp
- mov tfm(%esp),%ebp
+ mov ctx(%esp),%ebp
// CAUTION: the order and the values used in these assigns
// rely on the register mappings
diff --git a/arch/x86/crypto/aes-x86_64-asm_64.S b/arch/x86/crypto/aes-x86_64-asm_64.S
index a120f52..5b577d5 100644
--- a/arch/x86/crypto/aes-x86_64-asm_64.S
+++ b/arch/x86/crypto/aes-x86_64-asm_64.S
@@ -17,8 +17,6 @@
#include <asm/asm-offsets.h>
-#define BASE crypto_tfm_ctx_offset
-
#define R1 %rax
#define R1E %eax
#define R1X %ax
@@ -56,13 +54,13 @@
.align 8; \
FUNC: movq r1,r2; \
movq r3,r4; \
- leaq BASE+KEY+48+4(r8),r9; \
+ leaq KEY+48(r8),r9; \
movq r10,r11; \
movl (r7),r5 ## E; \
movl 4(r7),r1 ## E; \
movl 8(r7),r6 ## E; \
movl 12(r7),r7 ## E; \
- movl BASE+0(r8),r10 ## E; \
+ movl 480(r8),r10 ## E; \
xorl -48(r9),r5 ## E; \
xorl -44(r9),r1 ## E; \
xorl -40(r9),r6 ## E; \
diff --git a/arch/x86/crypto/aes_glue.c b/arch/x86/crypto/aes_glue.c
index 71f4578..49ae9fe 100644
--- a/arch/x86/crypto/aes_glue.c
+++ b/arch/x86/crypto/aes_glue.c
@@ -5,17 +5,29 @@
#include <crypto/aes.h>
-asmlinkage void aes_enc_blk(struct crypto_tfm *tfm, u8 *out, const u8 *in);
-asmlinkage void aes_dec_blk(struct crypto_tfm *tfm, u8 *out, const u8 *in);
+asmlinkage void aes_enc_blk(struct crypto_aes_ctx *ctx, u8 *out, const u8 *in);
+asmlinkage void aes_dec_blk(struct crypto_aes_ctx *ctx, u8 *out, const u8 *in);
+
+void crypto_aes_encrypt_x86(struct crypto_aes_ctx *ctx, u8 *dst, const u8 *src)
+{
+ aes_enc_blk(ctx, dst, src);
+}
+EXPORT_SYMBOL_GPL(crypto_aes_encrypt_x86);
+
+void crypto_aes_decrypt_x86(struct crypto_aes_ctx *ctx, u8 *dst, const u8 *src)
+{
+ aes_dec_blk(ctx, dst, src);
+}
+EXPORT_SYMBOL_GPL(crypto_aes_decrypt_x86);
static void aes_encrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src)
{
- aes_enc_blk(tfm, dst, src);
+ aes_enc_blk(crypto_tfm_ctx(tfm), dst, src);
}
static void aes_decrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src)
{
- aes_dec_blk(tfm, dst, src);
+ aes_dec_blk(crypto_tfm_ctx(tfm), dst, src);
}
static struct crypto_alg aes_alg = {
diff --git a/arch/x86/crypto/aesni-intel_asm.S b/arch/x86/crypto/aesni-intel_asm.S
new file mode 100644
index 0000000..caba996
--- /dev/null
+++ b/arch/x86/crypto/aesni-intel_asm.S
@@ -0,0 +1,896 @@
+/*
+ * Implement AES algorithm in Intel AES-NI instructions.
+ *
+ * The white paper of AES-NI instructions can be downloaded from:
+ * http://softwarecommunity.intel.com/isn/downloads/intelavx/AES-Instructions-Set_WP.pdf
+ *
+ * Copyright (C) 2008, Intel Corp.
+ * Author: Huang Ying <ying.huang@intel.com>
+ * Vinodh Gopal <vinodh.gopal@intel.com>
+ * Kahraman Akdemir
+ *
+ * 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>
+
+.text
+
+#define STATE1 %xmm0
+#define STATE2 %xmm4
+#define STATE3 %xmm5
+#define STATE4 %xmm6
+#define STATE STATE1
+#define IN1 %xmm1
+#define IN2 %xmm7
+#define IN3 %xmm8
+#define IN4 %xmm9
+#define IN IN1
+#define KEY %xmm2
+#define IV %xmm3
+
+#define KEYP %rdi
+#define OUTP %rsi
+#define INP %rdx
+#define LEN %rcx
+#define IVP %r8
+#define KLEN %r9d
+#define T1 %r10
+#define TKEYP T1
+#define T2 %r11
+
+_key_expansion_128:
+_key_expansion_256a:
+ pshufd $0b11111111, %xmm1, %xmm1
+ shufps $0b00010000, %xmm0, %xmm4
+ pxor %xmm4, %xmm0
+ shufps $0b10001100, %xmm0, %xmm4
+ pxor %xmm4, %xmm0
+ pxor %xmm1, %xmm0
+ movaps %xmm0, (%rcx)
+ add $0x10, %rcx
+ ret
+
+_key_expansion_192a:
+ pshufd $0b01010101, %xmm1, %xmm1
+ shufps $0b00010000, %xmm0, %xmm4
+ pxor %xmm4, %xmm0
+ shufps $0b10001100, %xmm0, %xmm4
+ pxor %xmm4, %xmm0
+ pxor %xmm1, %xmm0
+
+ movaps %xmm2, %xmm5
+ movaps %xmm2, %xmm6
+ pslldq $4, %xmm5
+ pshufd $0b11111111, %xmm0, %xmm3
+ pxor %xmm3, %xmm2
+ pxor %xmm5, %xmm2
+
+ movaps %xmm0, %xmm1
+ shufps $0b01000100, %xmm0, %xmm6
+ movaps %xmm6, (%rcx)
+ shufps $0b01001110, %xmm2, %xmm1
+ movaps %xmm1, 16(%rcx)
+ add $0x20, %rcx
+ ret
+
+_key_expansion_192b:
+ pshufd $0b01010101, %xmm1, %xmm1
+ shufps $0b00010000, %xmm0, %xmm4
+ pxor %xmm4, %xmm0
+ shufps $0b10001100, %xmm0, %xmm4
+ pxor %xmm4, %xmm0
+ pxor %xmm1, %xmm0
+
+ movaps %xmm2, %xmm5
+ pslldq $4, %xmm5
+ pshufd $0b11111111, %xmm0, %xmm3
+ pxor %xmm3, %xmm2
+ pxor %xmm5, %xmm2
+
+ movaps %xmm0, (%rcx)
+ add $0x10, %rcx
+ ret
+
+_key_expansion_256b:
+ pshufd $0b10101010, %xmm1, %xmm1
+ shufps $0b00010000, %xmm2, %xmm4
+ pxor %xmm4, %xmm2
+ shufps $0b10001100, %xmm2, %xmm4
+ pxor %xmm4, %xmm2
+ pxor %xmm1, %xmm2
+ movaps %xmm2, (%rcx)
+ add $0x10, %rcx
+ ret
+
+/*
+ * int aesni_set_key(struct crypto_aes_ctx *ctx, const u8 *in_key,
+ * unsigned int key_len)
+ */
+ENTRY(aesni_set_key)
+ movups (%rsi), %xmm0 # user key (first 16 bytes)
+ movaps %xmm0, (%rdi)
+ lea 0x10(%rdi), %rcx # key addr
+ movl %edx, 480(%rdi)
+ pxor %xmm4, %xmm4 # xmm4 is assumed 0 in _key_expansion_x
+ cmp $24, %dl
+ jb .Lenc_key128
+ je .Lenc_key192
+ movups 0x10(%rsi), %xmm2 # other user key
+ movaps %xmm2, (%rcx)
+ add $0x10, %rcx
+ # aeskeygenassist $0x1, %xmm2, %xmm1 # round 1
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x01
+ call _key_expansion_256a
+ # aeskeygenassist $0x1, %xmm0, %xmm1
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x01
+ call _key_expansion_256b
+ # aeskeygenassist $0x2, %xmm2, %xmm1 # round 2
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x02
+ call _key_expansion_256a
+ # aeskeygenassist $0x2, %xmm0, %xmm1
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x02
+ call _key_expansion_256b
+ # aeskeygenassist $0x4, %xmm2, %xmm1 # round 3
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x04
+ call _key_expansion_256a
+ # aeskeygenassist $0x4, %xmm0, %xmm1
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x04
+ call _key_expansion_256b
+ # aeskeygenassist $0x8, %xmm2, %xmm1 # round 4
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x08
+ call _key_expansion_256a
+ # aeskeygenassist $0x8, %xmm0, %xmm1
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x08
+ call _key_expansion_256b
+ # aeskeygenassist $0x10, %xmm2, %xmm1 # round 5
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x10
+ call _key_expansion_256a
+ # aeskeygenassist $0x10, %xmm0, %xmm1
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x10
+ call _key_expansion_256b
+ # aeskeygenassist $0x20, %xmm2, %xmm1 # round 6
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x20
+ call _key_expansion_256a
+ # aeskeygenassist $0x20, %xmm0, %xmm1
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x20
+ call _key_expansion_256b
+ # aeskeygenassist $0x40, %xmm2, %xmm1 # round 7
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x40
+ call _key_expansion_256a
+ jmp .Ldec_key
+.Lenc_key192:
+ movq 0x10(%rsi), %xmm2 # other user key
+ # aeskeygenassist $0x1, %xmm2, %xmm1 # round 1
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x01
+ call _key_expansion_192a
+ # aeskeygenassist $0x2, %xmm2, %xmm1 # round 2
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x02
+ call _key_expansion_192b
+ # aeskeygenassist $0x4, %xmm2, %xmm1 # round 3
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x04
+ call _key_expansion_192a
+ # aeskeygenassist $0x8, %xmm2, %xmm1 # round 4
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x08
+ call _key_expansion_192b
+ # aeskeygenassist $0x10, %xmm2, %xmm1 # round 5
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x10
+ call _key_expansion_192a
+ # aeskeygenassist $0x20, %xmm2, %xmm1 # round 6
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x20
+ call _key_expansion_192b
+ # aeskeygenassist $0x40, %xmm2, %xmm1 # round 7
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x40
+ call _key_expansion_192a
+ # aeskeygenassist $0x80, %xmm2, %xmm1 # round 8
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xca, 0x80
+ call _key_expansion_192b
+ jmp .Ldec_key
+.Lenc_key128:
+ # aeskeygenassist $0x1, %xmm0, %xmm1 # round 1
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x01
+ call _key_expansion_128
+ # aeskeygenassist $0x2, %xmm0, %xmm1 # round 2
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x02
+ call _key_expansion_128
+ # aeskeygenassist $0x4, %xmm0, %xmm1 # round 3
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x04
+ call _key_expansion_128
+ # aeskeygenassist $0x8, %xmm0, %xmm1 # round 4
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x08
+ call _key_expansion_128
+ # aeskeygenassist $0x10, %xmm0, %xmm1 # round 5
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x10
+ call _key_expansion_128
+ # aeskeygenassist $0x20, %xmm0, %xmm1 # round 6
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x20
+ call _key_expansion_128
+ # aeskeygenassist $0x40, %xmm0, %xmm1 # round 7
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x40
+ call _key_expansion_128
+ # aeskeygenassist $0x80, %xmm0, %xmm1 # round 8
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x80
+ call _key_expansion_128
+ # aeskeygenassist $0x1b, %xmm0, %xmm1 # round 9
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x1b
+ call _key_expansion_128
+ # aeskeygenassist $0x36, %xmm0, %xmm1 # round 10
+ .byte 0x66, 0x0f, 0x3a, 0xdf, 0xc8, 0x36
+ call _key_expansion_128
+.Ldec_key:
+ sub $0x10, %rcx
+ movaps (%rdi), %xmm0
+ movaps (%rcx), %xmm1
+ movaps %xmm0, 240(%rcx)
+ movaps %xmm1, 240(%rdi)
+ add $0x10, %rdi
+ lea 240-16(%rcx), %rsi
+.align 4
+.Ldec_key_loop:
+ movaps (%rdi), %xmm0
+ # aesimc %xmm0, %xmm1
+ .byte 0x66, 0x0f, 0x38, 0xdb, 0xc8
+ movaps %xmm1, (%rsi)
+ add $0x10, %rdi
+ sub $0x10, %rsi
+ cmp %rcx, %rdi
+ jb .Ldec_key_loop
+ xor %rax, %rax
+ ret
+
+/*
+ * void aesni_enc(struct crypto_aes_ctx *ctx, u8 *dst, const u8 *src)
+ */
+ENTRY(aesni_enc)
+ movl 480(KEYP), KLEN # key length
+ movups (INP), STATE # input
+ call _aesni_enc1
+ movups STATE, (OUTP) # output
+ ret
+
+/*
+ * _aesni_enc1: internal ABI
+ * input:
+ * KEYP: key struct pointer
+ * KLEN: round count
+ * STATE: initial state (input)
+ * output:
+ * STATE: finial state (output)
+ * changed:
+ * KEY
+ * TKEYP (T1)
+ */
+_aesni_enc1:
+ movaps (KEYP), KEY # key
+ mov KEYP, TKEYP
+ pxor KEY, STATE # round 0
+ add $0x30, TKEYP
+ cmp $24, KLEN
+ jb .Lenc128
+ lea 0x20(TKEYP), TKEYP
+ je .Lenc192
+ add $0x20, TKEYP
+ movaps -0x60(TKEYP), KEY
+ # aesenc KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ movaps -0x50(TKEYP), KEY
+ # aesenc KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+.align 4
+.Lenc192:
+ movaps -0x40(TKEYP), KEY
+ # aesenc KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ movaps -0x30(TKEYP), KEY
+ # aesenc KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+.align 4
+.Lenc128:
+ movaps -0x20(TKEYP), KEY
+ # aesenc KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ movaps -0x10(TKEYP), KEY
+ # aesenc KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ movaps (TKEYP), KEY
+ # aesenc KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ movaps 0x10(TKEYP), KEY
+ # aesenc KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ movaps 0x20(TKEYP), KEY
+ # aesenc KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ movaps 0x30(TKEYP), KEY
+ # aesenc KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ movaps 0x40(TKEYP), KEY
+ # aesenc KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ movaps 0x50(TKEYP), KEY
+ # aesenc KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ movaps 0x60(TKEYP), KEY
+ # aesenc KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ movaps 0x70(TKEYP), KEY
+ # aesenclast KEY, STATE # last round
+ .byte 0x66, 0x0f, 0x38, 0xdd, 0xc2
+ ret
+
+/*
+ * _aesni_enc4: internal ABI
+ * input:
+ * KEYP: key struct pointer
+ * KLEN: round count
+ * STATE1: initial state (input)
+ * STATE2
+ * STATE3
+ * STATE4
+ * output:
+ * STATE1: finial state (output)
+ * STATE2
+ * STATE3
+ * STATE4
+ * changed:
+ * KEY
+ * TKEYP (T1)
+ */
+_aesni_enc4:
+ movaps (KEYP), KEY # key
+ mov KEYP, TKEYP
+ pxor KEY, STATE1 # round 0
+ pxor KEY, STATE2
+ pxor KEY, STATE3
+ pxor KEY, STATE4
+ add $0x30, TKEYP
+ cmp $24, KLEN
+ jb .L4enc128
+ lea 0x20(TKEYP), TKEYP
+ je .L4enc192
+ add $0x20, TKEYP
+ movaps -0x60(TKEYP), KEY
+ # aesenc KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ # aesenc KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+ # aesenc KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+ # aesenc KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+ movaps -0x50(TKEYP), KEY
+ # aesenc KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ # aesenc KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+ # aesenc KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+ # aesenc KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+#.align 4
+.L4enc192:
+ movaps -0x40(TKEYP), KEY
+ # aesenc KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ # aesenc KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+ # aesenc KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+ # aesenc KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+ movaps -0x30(TKEYP), KEY
+ # aesenc KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ # aesenc KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+ # aesenc KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+ # aesenc KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+#.align 4
+.L4enc128:
+ movaps -0x20(TKEYP), KEY
+ # aesenc KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ # aesenc KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+ # aesenc KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+ # aesenc KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+ movaps -0x10(TKEYP), KEY
+ # aesenc KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ # aesenc KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+ # aesenc KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+ # aesenc KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+ movaps (TKEYP), KEY
+ # aesenc KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ # aesenc KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+ # aesenc KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+ # aesenc KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+ movaps 0x10(TKEYP), KEY
+ # aesenc KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ # aesenc KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+ # aesenc KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+ # aesenc KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+ movaps 0x20(TKEYP), KEY
+ # aesenc KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ # aesenc KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+ # aesenc KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+ # aesenc KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+ movaps 0x30(TKEYP), KEY
+ # aesenc KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ # aesenc KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+ # aesenc KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+ # aesenc KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+ movaps 0x40(TKEYP), KEY
+ # aesenc KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ # aesenc KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+ # aesenc KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+ # aesenc KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+ movaps 0x50(TKEYP), KEY
+ # aesenc KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ # aesenc KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+ # aesenc KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+ # aesenc KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+ movaps 0x60(TKEYP), KEY
+ # aesenc KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xc2
+ # aesenc KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xe2
+ # aesenc KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xea
+ # aesenc KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xdc, 0xf2
+ movaps 0x70(TKEYP), KEY
+ # aesenclast KEY, STATE1 # last round
+ .byte 0x66, 0x0f, 0x38, 0xdd, 0xc2
+ # aesenclast KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xdd, 0xe2
+ # aesenclast KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xdd, 0xea
+ # aesenclast KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xdd, 0xf2
+ ret
+
+/*
+ * void aesni_dec (struct crypto_aes_ctx *ctx, u8 *dst, const u8 *src)
+ */
+ENTRY(aesni_dec)
+ mov 480(KEYP), KLEN # key length
+ add $240, KEYP
+ movups (INP), STATE # input
+ call _aesni_dec1
+ movups STATE, (OUTP) #output
+ ret
+
+/*
+ * _aesni_dec1: internal ABI
+ * input:
+ * KEYP: key struct pointer
+ * KLEN: key length
+ * STATE: initial state (input)
+ * output:
+ * STATE: finial state (output)
+ * changed:
+ * KEY
+ * TKEYP (T1)
+ */
+_aesni_dec1:
+ movaps (KEYP), KEY # key
+ mov KEYP, TKEYP
+ pxor KEY, STATE # round 0
+ add $0x30, TKEYP
+ cmp $24, KLEN
+ jb .Ldec128
+ lea 0x20(TKEYP), TKEYP
+ je .Ldec192
+ add $0x20, TKEYP
+ movaps -0x60(TKEYP), KEY
+ # aesdec KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ movaps -0x50(TKEYP), KEY
+ # aesdec KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+.align 4
+.Ldec192:
+ movaps -0x40(TKEYP), KEY
+ # aesdec KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ movaps -0x30(TKEYP), KEY
+ # aesdec KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+.align 4
+.Ldec128:
+ movaps -0x20(TKEYP), KEY
+ # aesdec KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ movaps -0x10(TKEYP), KEY
+ # aesdec KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ movaps (TKEYP), KEY
+ # aesdec KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ movaps 0x10(TKEYP), KEY
+ # aesdec KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ movaps 0x20(TKEYP), KEY
+ # aesdec KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ movaps 0x30(TKEYP), KEY
+ # aesdec KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ movaps 0x40(TKEYP), KEY
+ # aesdec KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ movaps 0x50(TKEYP), KEY
+ # aesdec KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ movaps 0x60(TKEYP), KEY
+ # aesdec KEY, STATE
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ movaps 0x70(TKEYP), KEY
+ # aesdeclast KEY, STATE # last round
+ .byte 0x66, 0x0f, 0x38, 0xdf, 0xc2
+ ret
+
+/*
+ * _aesni_dec4: internal ABI
+ * input:
+ * KEYP: key struct pointer
+ * KLEN: key length
+ * STATE1: initial state (input)
+ * STATE2
+ * STATE3
+ * STATE4
+ * output:
+ * STATE1: finial state (output)
+ * STATE2
+ * STATE3
+ * STATE4
+ * changed:
+ * KEY
+ * TKEYP (T1)
+ */
+_aesni_dec4:
+ movaps (KEYP), KEY # key
+ mov KEYP, TKEYP
+ pxor KEY, STATE1 # round 0
+ pxor KEY, STATE2
+ pxor KEY, STATE3
+ pxor KEY, STATE4
+ add $0x30, TKEYP
+ cmp $24, KLEN
+ jb .L4dec128
+ lea 0x20(TKEYP), TKEYP
+ je .L4dec192
+ add $0x20, TKEYP
+ movaps -0x60(TKEYP), KEY
+ # aesdec KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ # aesdec KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+ # aesdec KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+ # aesdec KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+ movaps -0x50(TKEYP), KEY
+ # aesdec KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ # aesdec KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+ # aesdec KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+ # aesdec KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+.align 4
+.L4dec192:
+ movaps -0x40(TKEYP), KEY
+ # aesdec KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ # aesdec KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+ # aesdec KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+ # aesdec KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+ movaps -0x30(TKEYP), KEY
+ # aesdec KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ # aesdec KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+ # aesdec KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+ # aesdec KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+.align 4
+.L4dec128:
+ movaps -0x20(TKEYP), KEY
+ # aesdec KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ # aesdec KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+ # aesdec KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+ # aesdec KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+ movaps -0x10(TKEYP), KEY
+ # aesdec KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ # aesdec KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+ # aesdec KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+ # aesdec KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+ movaps (TKEYP), KEY
+ # aesdec KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ # aesdec KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+ # aesdec KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+ # aesdec KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+ movaps 0x10(TKEYP), KEY
+ # aesdec KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ # aesdec KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+ # aesdec KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+ # aesdec KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+ movaps 0x20(TKEYP), KEY
+ # aesdec KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ # aesdec KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+ # aesdec KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+ # aesdec KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+ movaps 0x30(TKEYP), KEY
+ # aesdec KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ # aesdec KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+ # aesdec KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+ # aesdec KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+ movaps 0x40(TKEYP), KEY
+ # aesdec KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ # aesdec KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+ # aesdec KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+ # aesdec KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+ movaps 0x50(TKEYP), KEY
+ # aesdec KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ # aesdec KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+ # aesdec KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+ # aesdec KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+ movaps 0x60(TKEYP), KEY
+ # aesdec KEY, STATE1
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xc2
+ # aesdec KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xe2
+ # aesdec KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xea
+ # aesdec KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xde, 0xf2
+ movaps 0x70(TKEYP), KEY
+ # aesdeclast KEY, STATE1 # last round
+ .byte 0x66, 0x0f, 0x38, 0xdf, 0xc2
+ # aesdeclast KEY, STATE2
+ .byte 0x66, 0x0f, 0x38, 0xdf, 0xe2
+ # aesdeclast KEY, STATE3
+ .byte 0x66, 0x0f, 0x38, 0xdf, 0xea
+ # aesdeclast KEY, STATE4
+ .byte 0x66, 0x0f, 0x38, 0xdf, 0xf2
+ ret
+
+/*
+ * void aesni_ecb_enc(struct crypto_aes_ctx *ctx, const u8 *dst, u8 *src,
+ * size_t len)
+ */
+ENTRY(aesni_ecb_enc)
+ test LEN, LEN # check length
+ jz .Lecb_enc_ret
+ mov 480(KEYP), KLEN
+ cmp $16, LEN
+ jb .Lecb_enc_ret
+ cmp $64, LEN
+ jb .Lecb_enc_loop1
+.align 4
+.Lecb_enc_loop4:
+ movups (INP), STATE1
+ movups 0x10(INP), STATE2
+ movups 0x20(INP), STATE3
+ movups 0x30(INP), STATE4
+ call _aesni_enc4
+ movups STATE1, (OUTP)
+ movups STATE2, 0x10(OUTP)
+ movups STATE3, 0x20(OUTP)
+ movups STATE4, 0x30(OUTP)
+ sub $64, LEN
+ add $64, INP
+ add $64, OUTP
+ cmp $64, LEN
+ jge .Lecb_enc_loop4
+ cmp $16, LEN
+ jb .Lecb_enc_ret
+.align 4
+.Lecb_enc_loop1:
+ movups (INP), STATE1
+ call _aesni_enc1
+ movups STATE1, (OUTP)
+ sub $16, LEN
+ add $16, INP
+ add $16, OUTP
+ cmp $16, LEN
+ jge .Lecb_enc_loop1
+.Lecb_enc_ret:
+ ret
+
+/*
+ * void aesni_ecb_dec(struct crypto_aes_ctx *ctx, const u8 *dst, u8 *src,
+ * size_t len);
+ */
+ENTRY(aesni_ecb_dec)
+ test LEN, LEN
+ jz .Lecb_dec_ret
+ mov 480(KEYP), KLEN
+ add $240, KEYP
+ cmp $16, LEN
+ jb .Lecb_dec_ret
+ cmp $64, LEN
+ jb .Lecb_dec_loop1
+.align 4
+.Lecb_dec_loop4:
+ movups (INP), STATE1
+ movups 0x10(INP), STATE2
+ movups 0x20(INP), STATE3
+ movups 0x30(INP), STATE4
+ call _aesni_dec4
+ movups STATE1, (OUTP)
+ movups STATE2, 0x10(OUTP)
+ movups STATE3, 0x20(OUTP)
+ movups STATE4, 0x30(OUTP)
+ sub $64, LEN
+ add $64, INP
+ add $64, OUTP
+ cmp $64, LEN
+ jge .Lecb_dec_loop4
+ cmp $16, LEN
+ jb .Lecb_dec_ret
+.align 4
+.Lecb_dec_loop1:
+ movups (INP), STATE1
+ call _aesni_dec1
+ movups STATE1, (OUTP)
+ sub $16, LEN
+ add $16, INP
+ add $16, OUTP
+ cmp $16, LEN
+ jge .Lecb_dec_loop1
+.Lecb_dec_ret:
+ ret
+
+/*
+ * void aesni_cbc_enc(struct crypto_aes_ctx *ctx, const u8 *dst, u8 *src,
+ * size_t len, u8 *iv)
+ */
+ENTRY(aesni_cbc_enc)
+ cmp $16, LEN
+ jb .Lcbc_enc_ret
+ mov 480(KEYP), KLEN
+ movups (IVP), STATE # load iv as initial state
+.align 4
+.Lcbc_enc_loop:
+ movups (INP), IN # load input
+ pxor IN, STATE
+ call _aesni_enc1
+ movups STATE, (OUTP) # store output
+ sub $16, LEN
+ add $16, INP
+ add $16, OUTP
+ cmp $16, LEN
+ jge .Lcbc_enc_loop
+ movups STATE, (IVP)
+.Lcbc_enc_ret:
+ ret
+
+/*
+ * void aesni_cbc_dec(struct crypto_aes_ctx *ctx, const u8 *dst, u8 *src,
+ * size_t len, u8 *iv)
+ */
+ENTRY(aesni_cbc_dec)
+ cmp $16, LEN
+ jb .Lcbc_dec_ret
+ mov 480(KEYP), KLEN
+ add $240, KEYP
+ movups (IVP), IV
+ cmp $64, LEN
+ jb .Lcbc_dec_loop1
+.align 4
+.Lcbc_dec_loop4:
+ movups (INP), IN1
+ movaps IN1, STATE1
+ movups 0x10(INP), IN2
+ movaps IN2, STATE2
+ movups 0x20(INP), IN3
+ movaps IN3, STATE3
+ movups 0x30(INP), IN4
+ movaps IN4, STATE4
+ call _aesni_dec4
+ pxor IV, STATE1
+ pxor IN1, STATE2
+ pxor IN2, STATE3
+ pxor IN3, STATE4
+ movaps IN4, IV
+ movups STATE1, (OUTP)
+ movups STATE2, 0x10(OUTP)
+ movups STATE3, 0x20(OUTP)
+ movups STATE4, 0x30(OUTP)
+ sub $64, LEN
+ add $64, INP
+ add $64, OUTP
+ cmp $64, LEN
+ jge .Lcbc_dec_loop4
+ cmp $16, LEN
+ jb .Lcbc_dec_ret
+.align 4
+.Lcbc_dec_loop1:
+ movups (INP), IN
+ movaps IN, STATE
+ call _aesni_dec1
+ pxor IV, STATE
+ movups STATE, (OUTP)
+ movaps IN, IV
+ sub $16, LEN
+ add $16, INP
+ add $16, OUTP
+ cmp $16, LEN
+ jge .Lcbc_dec_loop1
+ movups IV, (IVP)
+.Lcbc_dec_ret:
+ ret
diff --git a/arch/x86/crypto/aesni-intel_glue.c b/arch/x86/crypto/aesni-intel_glue.c
new file mode 100644
index 0000000..02af0af
--- /dev/null
+++ b/arch/x86/crypto/aesni-intel_glue.c
@@ -0,0 +1,461 @@
+/*
+ * Support for Intel AES-NI instructions. This file contains glue
+ * code, the real AES implementation is in intel-aes_asm.S.
+ *
+ * Copyright (C) 2008, Intel Corp.
+ * Author: Huang Ying <ying.huang@intel.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/hardirq.h>
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <linux/err.h>
+#include <crypto/algapi.h>
+#include <crypto/aes.h>
+#include <crypto/cryptd.h>
+#include <asm/i387.h>
+#include <asm/aes.h>
+
+struct async_aes_ctx {
+ struct cryptd_ablkcipher *cryptd_tfm;
+};
+
+#define AESNI_ALIGN 16
+#define AES_BLOCK_MASK (~(AES_BLOCK_SIZE-1))
+
+asmlinkage int aesni_set_key(struct crypto_aes_ctx *ctx, const u8 *in_key,
+ unsigned int key_len);
+asmlinkage void aesni_enc(struct crypto_aes_ctx *ctx, u8 *out,
+ const u8 *in);
+asmlinkage void aesni_dec(struct crypto_aes_ctx *ctx, u8 *out,
+ const u8 *in);
+asmlinkage void aesni_ecb_enc(struct crypto_aes_ctx *ctx, u8 *out,
+ const u8 *in, unsigned int len);
+asmlinkage void aesni_ecb_dec(struct crypto_aes_ctx *ctx, u8 *out,
+ const u8 *in, unsigned int len);
+asmlinkage void aesni_cbc_enc(struct crypto_aes_ctx *ctx, u8 *out,
+ const u8 *in, unsigned int len, u8 *iv);
+asmlinkage void aesni_cbc_dec(struct crypto_aes_ctx *ctx, u8 *out,
+ const u8 *in, unsigned int len, u8 *iv);
+
+static inline int kernel_fpu_using(void)
+{
+ if (in_interrupt() && !(read_cr0() & X86_CR0_TS))
+ return 1;
+ return 0;
+}
+
+static inline struct crypto_aes_ctx *aes_ctx(void *raw_ctx)
+{
+ unsigned long addr = (unsigned long)raw_ctx;
+ unsigned long align = AESNI_ALIGN;
+
+ if (align <= crypto_tfm_ctx_alignment())
+ align = 1;
+ return (struct crypto_aes_ctx *)ALIGN(addr, align);
+}
+
+static int aes_set_key_common(struct crypto_tfm *tfm, void *raw_ctx,
+ const u8 *in_key, unsigned int key_len)
+{
+ struct crypto_aes_ctx *ctx = aes_ctx(raw_ctx);
+ u32 *flags = &tfm->crt_flags;
+ int err;
+
+ if (key_len != AES_KEYSIZE_128 && key_len != AES_KEYSIZE_192 &&
+ key_len != AES_KEYSIZE_256) {
+ *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ return -EINVAL;
+ }
+
+ if (kernel_fpu_using())
+ err = crypto_aes_expand_key(ctx, in_key, key_len);
+ else {
+ kernel_fpu_begin();
+ err = aesni_set_key(ctx, in_key, key_len);
+ kernel_fpu_end();
+ }
+
+ return err;
+}
+
+static int aes_set_key(struct crypto_tfm *tfm, const u8 *in_key,
+ unsigned int key_len)
+{
+ return aes_set_key_common(tfm, crypto_tfm_ctx(tfm), in_key, key_len);
+}
+
+static void aes_encrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src)
+{
+ struct crypto_aes_ctx *ctx = aes_ctx(crypto_tfm_ctx(tfm));
+
+ if (kernel_fpu_using())
+ crypto_aes_encrypt_x86(ctx, dst, src);
+ else {
+ kernel_fpu_begin();
+ aesni_enc(ctx, dst, src);
+ kernel_fpu_end();
+ }
+}
+
+static void aes_decrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src)
+{
+ struct crypto_aes_ctx *ctx = aes_ctx(crypto_tfm_ctx(tfm));
+
+ if (kernel_fpu_using())
+ crypto_aes_decrypt_x86(ctx, dst, src);
+ else {
+ kernel_fpu_begin();
+ aesni_dec(ctx, dst, src);
+ kernel_fpu_end();
+ }
+}
+
+static struct crypto_alg aesni_alg = {
+ .cra_name = "aes",
+ .cra_driver_name = "aes-aesni",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_CIPHER,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct crypto_aes_ctx)+AESNI_ALIGN-1,
+ .cra_alignmask = 0,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(aesni_alg.cra_list),
+ .cra_u = {
+ .cipher = {
+ .cia_min_keysize = AES_MIN_KEY_SIZE,
+ .cia_max_keysize = AES_MAX_KEY_SIZE,
+ .cia_setkey = aes_set_key,
+ .cia_encrypt = aes_encrypt,
+ .cia_decrypt = aes_decrypt
+ }
+ }
+};
+
+static int ecb_encrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ struct crypto_aes_ctx *ctx = aes_ctx(crypto_blkcipher_ctx(desc->tfm));
+ struct blkcipher_walk walk;
+ int err;
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+ err = blkcipher_walk_virt(desc, &walk);
+
+ kernel_fpu_begin();
+ while ((nbytes = walk.nbytes)) {
+ aesni_ecb_enc(ctx, walk.dst.virt.addr, walk.src.virt.addr,
+ nbytes & AES_BLOCK_MASK);
+ nbytes &= AES_BLOCK_SIZE - 1;
+ err = blkcipher_walk_done(desc, &walk, nbytes);
+ }
+ kernel_fpu_end();
+
+ return err;
+}
+
+static int ecb_decrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ struct crypto_aes_ctx *ctx = aes_ctx(crypto_blkcipher_ctx(desc->tfm));
+ struct blkcipher_walk walk;
+ int err;
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+ err = blkcipher_walk_virt(desc, &walk);
+
+ kernel_fpu_begin();
+ while ((nbytes = walk.nbytes)) {
+ aesni_ecb_dec(ctx, walk.dst.virt.addr, walk.src.virt.addr,
+ nbytes & AES_BLOCK_MASK);
+ nbytes &= AES_BLOCK_SIZE - 1;
+ err = blkcipher_walk_done(desc, &walk, nbytes);
+ }
+ kernel_fpu_end();
+
+ return err;
+}
+
+static struct crypto_alg blk_ecb_alg = {
+ .cra_name = "__ecb-aes-aesni",
+ .cra_driver_name = "__driver-ecb-aes-aesni",
+ .cra_priority = 0,
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct crypto_aes_ctx)+AESNI_ALIGN-1,
+ .cra_alignmask = 0,
+ .cra_type = &crypto_blkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(blk_ecb_alg.cra_list),
+ .cra_u = {
+ .blkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = aes_set_key,
+ .encrypt = ecb_encrypt,
+ .decrypt = ecb_decrypt,
+ },
+ },
+};
+
+static int cbc_encrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ struct crypto_aes_ctx *ctx = aes_ctx(crypto_blkcipher_ctx(desc->tfm));
+ struct blkcipher_walk walk;
+ int err;
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+ err = blkcipher_walk_virt(desc, &walk);
+
+ kernel_fpu_begin();
+ while ((nbytes = walk.nbytes)) {
+ aesni_cbc_enc(ctx, walk.dst.virt.addr, walk.src.virt.addr,
+ nbytes & AES_BLOCK_MASK, walk.iv);
+ nbytes &= AES_BLOCK_SIZE - 1;
+ err = blkcipher_walk_done(desc, &walk, nbytes);
+ }
+ kernel_fpu_end();
+
+ return err;
+}
+
+static int cbc_decrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ struct crypto_aes_ctx *ctx = aes_ctx(crypto_blkcipher_ctx(desc->tfm));
+ struct blkcipher_walk walk;
+ int err;
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+ err = blkcipher_walk_virt(desc, &walk);
+
+ kernel_fpu_begin();
+ while ((nbytes = walk.nbytes)) {
+ aesni_cbc_dec(ctx, walk.dst.virt.addr, walk.src.virt.addr,
+ nbytes & AES_BLOCK_MASK, walk.iv);
+ nbytes &= AES_BLOCK_SIZE - 1;
+ err = blkcipher_walk_done(desc, &walk, nbytes);
+ }
+ kernel_fpu_end();
+
+ return err;
+}
+
+static struct crypto_alg blk_cbc_alg = {
+ .cra_name = "__cbc-aes-aesni",
+ .cra_driver_name = "__driver-cbc-aes-aesni",
+ .cra_priority = 0,
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct crypto_aes_ctx)+AESNI_ALIGN-1,
+ .cra_alignmask = 0,
+ .cra_type = &crypto_blkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(blk_cbc_alg.cra_list),
+ .cra_u = {
+ .blkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = aes_set_key,
+ .encrypt = cbc_encrypt,
+ .decrypt = cbc_decrypt,
+ },
+ },
+};
+
+static int ablk_set_key(struct crypto_ablkcipher *tfm, const u8 *key,
+ unsigned int key_len)
+{
+ struct async_aes_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+
+ return crypto_ablkcipher_setkey(&ctx->cryptd_tfm->base, key, key_len);
+}
+
+static int ablk_encrypt(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct async_aes_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+
+ if (kernel_fpu_using()) {
+ struct ablkcipher_request *cryptd_req =
+ ablkcipher_request_ctx(req);
+ memcpy(cryptd_req, req, sizeof(*req));
+ ablkcipher_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base);
+ return crypto_ablkcipher_encrypt(cryptd_req);
+ } else {
+ struct blkcipher_desc desc;
+ desc.tfm = cryptd_ablkcipher_child(ctx->cryptd_tfm);
+ desc.info = req->info;
+ desc.flags = 0;
+ return crypto_blkcipher_crt(desc.tfm)->encrypt(
+ &desc, req->dst, req->src, req->nbytes);
+ }
+}
+
+static int ablk_decrypt(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct async_aes_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+
+ if (kernel_fpu_using()) {
+ struct ablkcipher_request *cryptd_req =
+ ablkcipher_request_ctx(req);
+ memcpy(cryptd_req, req, sizeof(*req));
+ ablkcipher_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base);
+ return crypto_ablkcipher_decrypt(cryptd_req);
+ } else {
+ struct blkcipher_desc desc;
+ desc.tfm = cryptd_ablkcipher_child(ctx->cryptd_tfm);
+ desc.info = req->info;
+ desc.flags = 0;
+ return crypto_blkcipher_crt(desc.tfm)->decrypt(
+ &desc, req->dst, req->src, req->nbytes);
+ }
+}
+
+static void ablk_exit(struct crypto_tfm *tfm)
+{
+ struct async_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ cryptd_free_ablkcipher(ctx->cryptd_tfm);
+}
+
+static void ablk_init_common(struct crypto_tfm *tfm,
+ struct cryptd_ablkcipher *cryptd_tfm)
+{
+ struct async_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ ctx->cryptd_tfm = cryptd_tfm;
+ tfm->crt_ablkcipher.reqsize = sizeof(struct ablkcipher_request) +
+ crypto_ablkcipher_reqsize(&cryptd_tfm->base);
+}
+
+static int ablk_ecb_init(struct crypto_tfm *tfm)
+{
+ struct cryptd_ablkcipher *cryptd_tfm;
+
+ cryptd_tfm = cryptd_alloc_ablkcipher("__driver-ecb-aes-aesni", 0, 0);
+ if (IS_ERR(cryptd_tfm))
+ return PTR_ERR(cryptd_tfm);
+ ablk_init_common(tfm, cryptd_tfm);
+ return 0;
+}
+
+static struct crypto_alg ablk_ecb_alg = {
+ .cra_name = "ecb(aes)",
+ .cra_driver_name = "ecb-aes-aesni",
+ .cra_priority = 400,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct async_aes_ctx),
+ .cra_alignmask = 0,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(ablk_ecb_alg.cra_list),
+ .cra_init = ablk_ecb_init,
+ .cra_exit = ablk_exit,
+ .cra_u = {
+ .ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = ablk_set_key,
+ .encrypt = ablk_encrypt,
+ .decrypt = ablk_decrypt,
+ },
+ },
+};
+
+static int ablk_cbc_init(struct crypto_tfm *tfm)
+{
+ struct cryptd_ablkcipher *cryptd_tfm;
+
+ cryptd_tfm = cryptd_alloc_ablkcipher("__driver-cbc-aes-aesni", 0, 0);
+ if (IS_ERR(cryptd_tfm))
+ return PTR_ERR(cryptd_tfm);
+ ablk_init_common(tfm, cryptd_tfm);
+ return 0;
+}
+
+static struct crypto_alg ablk_cbc_alg = {
+ .cra_name = "cbc(aes)",
+ .cra_driver_name = "cbc-aes-aesni",
+ .cra_priority = 400,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct async_aes_ctx),
+ .cra_alignmask = 0,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(ablk_cbc_alg.cra_list),
+ .cra_init = ablk_cbc_init,
+ .cra_exit = ablk_exit,
+ .cra_u = {
+ .ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = AES_BLOCK_SIZE,
+ .setkey = ablk_set_key,
+ .encrypt = ablk_encrypt,
+ .decrypt = ablk_decrypt,
+ },
+ },
+};
+
+static int __init aesni_init(void)
+{
+ int err;
+
+ if (!cpu_has_aes) {
+ printk(KERN_ERR "Intel AES-NI instructions are not detected.\n");
+ return -ENODEV;
+ }
+ if ((err = crypto_register_alg(&aesni_alg)))
+ goto aes_err;
+ if ((err = crypto_register_alg(&blk_ecb_alg)))
+ goto blk_ecb_err;
+ if ((err = crypto_register_alg(&blk_cbc_alg)))
+ goto blk_cbc_err;
+ if ((err = crypto_register_alg(&ablk_ecb_alg)))
+ goto ablk_ecb_err;
+ if ((err = crypto_register_alg(&ablk_cbc_alg)))
+ goto ablk_cbc_err;
+
+ return err;
+
+ablk_cbc_err:
+ crypto_unregister_alg(&ablk_ecb_alg);
+ablk_ecb_err:
+ crypto_unregister_alg(&blk_cbc_alg);
+blk_cbc_err:
+ crypto_unregister_alg(&blk_ecb_alg);
+blk_ecb_err:
+ crypto_unregister_alg(&aesni_alg);
+aes_err:
+ return err;
+}
+
+static void __exit aesni_exit(void)
+{
+ crypto_unregister_alg(&ablk_cbc_alg);
+ crypto_unregister_alg(&ablk_ecb_alg);
+ crypto_unregister_alg(&blk_cbc_alg);
+ crypto_unregister_alg(&blk_ecb_alg);
+ crypto_unregister_alg(&aesni_alg);
+}
+
+module_init(aesni_init);
+module_exit(aesni_exit);
+
+MODULE_DESCRIPTION("Rijndael (AES) Cipher Algorithm, Intel AES-NI instructions optimized");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("aes");
diff --git a/arch/x86/include/asm/aes.h b/arch/x86/include/asm/aes.h
new file mode 100644
index 0000000..80545a1
--- /dev/null
+++ b/arch/x86/include/asm/aes.h
@@ -0,0 +1,11 @@
+#ifndef ASM_X86_AES_H
+#define ASM_X86_AES_H
+
+#include <linux/crypto.h>
+#include <crypto/aes.h>
+
+void crypto_aes_encrypt_x86(struct crypto_aes_ctx *ctx, u8 *dst,
+ const u8 *src);
+void crypto_aes_decrypt_x86(struct crypto_aes_ctx *ctx, u8 *dst,
+ const u8 *src);
+#endif
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h
index 7301e60..0beba0d 100644
--- a/arch/x86/include/asm/cpufeature.h
+++ b/arch/x86/include/asm/cpufeature.h
@@ -213,6 +213,7 @@
#define cpu_has_xmm boot_cpu_has(X86_FEATURE_XMM)
#define cpu_has_xmm2 boot_cpu_has(X86_FEATURE_XMM2)
#define cpu_has_xmm3 boot_cpu_has(X86_FEATURE_XMM3)
+#define cpu_has_aes boot_cpu_has(X86_FEATURE_AES)
#define cpu_has_ht boot_cpu_has(X86_FEATURE_HT)
#define cpu_has_mp boot_cpu_has(X86_FEATURE_MP)
#define cpu_has_nx boot_cpu_has(X86_FEATURE_NX)
diff --git a/arch/x86/include/asm/timer.h b/arch/x86/include/asm/timer.h
index 2bb6a83..4f5c247 100644
--- a/arch/x86/include/asm/timer.h
+++ b/arch/x86/include/asm/timer.h
@@ -11,8 +11,8 @@
#ifdef CONFIG_X86_32
extern int timer_ack;
+#endif
extern int recalibrate_cpu_khz(void);
-#endif /* CONFIG_X86_32 */
extern int no_timer_check;
diff --git a/arch/x86/kernel/cpu/cpufreq/Kconfig b/arch/x86/kernel/cpu/cpufreq/Kconfig
index 65792c2..52c8398 100644
--- a/arch/x86/kernel/cpu/cpufreq/Kconfig
+++ b/arch/x86/kernel/cpu/cpufreq/Kconfig
@@ -87,30 +87,15 @@
config X86_POWERNOW_K8
tristate "AMD Opteron/Athlon64 PowerNow!"
select CPU_FREQ_TABLE
+ depends on ACPI && ACPI_PROCESSOR
help
- This adds the CPUFreq driver for mobile AMD Opteron/Athlon64 processors.
+ This adds the CPUFreq driver for K8/K10 Opteron/Athlon64 processors.
To compile this driver as a module, choose M here: the
module will be called powernow-k8.
For details, take a look at <file:Documentation/cpu-freq/>.
- If in doubt, say N.
-
-config X86_POWERNOW_K8_ACPI
- bool
- prompt "ACPI Support" if X86_32
- depends on ACPI && X86_POWERNOW_K8 && ACPI_PROCESSOR
- depends on !(X86_POWERNOW_K8 = y && ACPI_PROCESSOR = m)
- default y
- help
- This provides access to the K8s Processor Performance States via ACPI.
- This driver is probably required for CPUFreq to work with multi-socket and
- SMP systems. It is not required on at least some single-socket yet
- multi-core systems, even if SMP is enabled.
-
- It is safe to say Y here.
-
config X86_GX_SUSPMOD
tristate "Cyrix MediaGX/NatSemi Geode Suspend Modulation"
depends on X86_32 && PCI
diff --git a/arch/x86/kernel/cpu/cpufreq/Makefile b/arch/x86/kernel/cpu/cpufreq/Makefile
index 560f776..509296d 100644
--- a/arch/x86/kernel/cpu/cpufreq/Makefile
+++ b/arch/x86/kernel/cpu/cpufreq/Makefile
@@ -1,6 +1,11 @@
+# Link order matters. K8 is preferred to ACPI because of firmware bugs in early
+# K8 systems. ACPI is preferred to all other hardware-specific drivers.
+# speedstep-* is preferred over p4-clockmod.
+
+obj-$(CONFIG_X86_POWERNOW_K8) += powernow-k8.o
+obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi-cpufreq.o
obj-$(CONFIG_X86_POWERNOW_K6) += powernow-k6.o
obj-$(CONFIG_X86_POWERNOW_K7) += powernow-k7.o
-obj-$(CONFIG_X86_POWERNOW_K8) += powernow-k8.o
obj-$(CONFIG_X86_LONGHAUL) += longhaul.o
obj-$(CONFIG_X86_E_POWERSAVER) += e_powersaver.o
obj-$(CONFIG_ELAN_CPUFREQ) += elanfreq.o
@@ -10,7 +15,6 @@
obj-$(CONFIG_X86_SPEEDSTEP_ICH) += speedstep-ich.o
obj-$(CONFIG_X86_SPEEDSTEP_LIB) += speedstep-lib.o
obj-$(CONFIG_X86_SPEEDSTEP_SMI) += speedstep-smi.o
-obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi-cpufreq.o
obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO) += speedstep-centrino.o
obj-$(CONFIG_X86_P4_CLOCKMOD) += p4-clockmod.o
obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o
diff --git a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c
index 4b1c319..3babe1f 100644
--- a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c
+++ b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c
@@ -1,5 +1,5 @@
/*
- * acpi-cpufreq.c - ACPI Processor P-States Driver ($Revision: 1.4 $)
+ * acpi-cpufreq.c - ACPI Processor P-States Driver
*
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
@@ -36,16 +36,18 @@
#include <linux/ftrace.h>
#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+
#include <acpi/processor.h>
-#include <asm/io.h>
#include <asm/msr.h>
#include <asm/processor.h>
#include <asm/cpufeature.h>
-#include <asm/delay.h>
-#include <asm/uaccess.h>
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "acpi-cpufreq", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+ "acpi-cpufreq", msg)
MODULE_AUTHOR("Paul Diefenbaugh, Dominik Brodowski");
MODULE_DESCRIPTION("ACPI Processor P-States Driver");
@@ -95,7 +97,7 @@
perf = data->acpi_data;
- for (i=0; i<perf->state_count; i++) {
+ for (i = 0; i < perf->state_count; i++) {
if (value == perf->states[i].status)
return data->freq_table[i].frequency;
}
@@ -110,7 +112,7 @@
msr &= INTEL_MSR_RANGE;
perf = data->acpi_data;
- for (i=0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
+ for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
if (msr == perf->states[data->freq_table[i].index].status)
return data->freq_table[i].frequency;
}
@@ -138,15 +140,13 @@
u8 bit_width;
};
-typedef union {
- struct msr_addr msr;
- struct io_addr io;
-} drv_addr_union;
-
struct drv_cmd {
unsigned int type;
const struct cpumask *mask;
- drv_addr_union addr;
+ union {
+ struct msr_addr msr;
+ struct io_addr io;
+ } addr;
u32 val;
};
@@ -369,7 +369,7 @@
unsigned int cur_freq;
unsigned int i;
- for (i=0; i<100; i++) {
+ for (i = 0; i < 100; i++) {
cur_freq = extract_freq(get_cur_val(mask), data);
if (cur_freq == freq)
return 1;
@@ -494,7 +494,7 @@
unsigned long freq;
unsigned long freqn = perf->states[0].core_frequency * 1000;
- for (i=0; i<(perf->state_count-1); i++) {
+ for (i = 0; i < (perf->state_count-1); i++) {
freq = freqn;
freqn = perf->states[i+1].core_frequency * 1000;
if ((2 * cpu_khz) > (freqn + freq)) {
@@ -673,7 +673,7 @@
/* detect transition latency */
policy->cpuinfo.transition_latency = 0;
- for (i=0; i<perf->state_count; i++) {
+ for (i = 0; i < perf->state_count; i++) {
if ((perf->states[i].transition_latency * 1000) >
policy->cpuinfo.transition_latency)
policy->cpuinfo.transition_latency =
@@ -682,8 +682,8 @@
data->max_freq = perf->states[0].core_frequency * 1000;
/* table init */
- for (i=0; i<perf->state_count; i++) {
- if (i>0 && perf->states[i].core_frequency >=
+ for (i = 0; i < perf->state_count; i++) {
+ if (i > 0 && perf->states[i].core_frequency >=
data->freq_table[valid_states-1].frequency / 1000)
continue;
diff --git a/arch/x86/kernel/cpu/cpufreq/cpufreq-nforce2.c b/arch/x86/kernel/cpu/cpufreq/cpufreq-nforce2.c
index 965ea52..733093d 100644
--- a/arch/x86/kernel/cpu/cpufreq/cpufreq-nforce2.c
+++ b/arch/x86/kernel/cpu/cpufreq/cpufreq-nforce2.c
@@ -32,7 +32,7 @@
* nforce2_chipset:
* FSB is changed using the chipset
*/
-static struct pci_dev *nforce2_chipset_dev;
+static struct pci_dev *nforce2_dev;
/* fid:
* multiplier * 10
@@ -56,7 +56,9 @@
MODULE_PARM_DESC(min_fsb,
"Minimum FSB to use, if not defined: current FSB - 50");
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "cpufreq-nforce2", msg)
+#define PFX "cpufreq-nforce2: "
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+ "cpufreq-nforce2", msg)
/**
* nforce2_calc_fsb - calculate FSB
@@ -118,11 +120,11 @@
int temp;
/* Set the pll addr. to 0x00 */
- pci_write_config_dword(nforce2_chipset_dev, NFORCE2_PLLADR, 0);
+ pci_write_config_dword(nforce2_dev, NFORCE2_PLLADR, 0);
/* Now write the value in all 64 registers */
for (temp = 0; temp <= 0x3f; temp++)
- pci_write_config_dword(nforce2_chipset_dev, NFORCE2_PLLREG, pll);
+ pci_write_config_dword(nforce2_dev, NFORCE2_PLLREG, pll);
return;
}
@@ -139,8 +141,8 @@
u32 fsb, temp = 0;
/* Get chipset boot FSB from subdevice 5 (FSB at boot-time) */
- nforce2_sub5 = pci_get_subsys(PCI_VENDOR_ID_NVIDIA,
- 0x01EF, PCI_ANY_ID, PCI_ANY_ID, NULL);
+ nforce2_sub5 = pci_get_subsys(PCI_VENDOR_ID_NVIDIA, 0x01EF,
+ PCI_ANY_ID, PCI_ANY_ID, NULL);
if (!nforce2_sub5)
return 0;
@@ -148,13 +150,13 @@
fsb /= 1000000;
/* Check if PLL register is already set */
- pci_read_config_byte(nforce2_chipset_dev, NFORCE2_PLLENABLE, (u8 *)&temp);
+ pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp);
if (bootfsb || !temp)
return fsb;
/* Use PLL register FSB value */
- pci_read_config_dword(nforce2_chipset_dev, NFORCE2_PLLREG, &temp);
+ pci_read_config_dword(nforce2_dev, NFORCE2_PLLREG, &temp);
fsb = nforce2_calc_fsb(temp);
return fsb;
@@ -174,18 +176,18 @@
int pll = 0;
if ((fsb > max_fsb) || (fsb < NFORCE2_MIN_FSB)) {
- printk(KERN_ERR "cpufreq: FSB %d is out of range!\n", fsb);
+ printk(KERN_ERR PFX "FSB %d is out of range!\n", fsb);
return -EINVAL;
}
tfsb = nforce2_fsb_read(0);
if (!tfsb) {
- printk(KERN_ERR "cpufreq: Error while reading the FSB\n");
+ printk(KERN_ERR PFX "Error while reading the FSB\n");
return -EINVAL;
}
/* First write? Then set actual value */
- pci_read_config_byte(nforce2_chipset_dev, NFORCE2_PLLENABLE, (u8 *)&temp);
+ pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp);
if (!temp) {
pll = nforce2_calc_pll(tfsb);
@@ -197,7 +199,7 @@
/* Enable write access */
temp = 0x01;
- pci_write_config_byte(nforce2_chipset_dev, NFORCE2_PLLENABLE, (u8)temp);
+ pci_write_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8)temp);
diff = tfsb - fsb;
@@ -222,7 +224,7 @@
}
temp = 0x40;
- pci_write_config_byte(nforce2_chipset_dev, NFORCE2_PLLADR, (u8)temp);
+ pci_write_config_byte(nforce2_dev, NFORCE2_PLLADR, (u8)temp);
return 0;
}
@@ -244,7 +246,8 @@
* nforce2_target - set a new CPUFreq policy
* @policy: new policy
* @target_freq: the target frequency
- * @relation: how that frequency relates to achieved frequency (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
+ * @relation: how that frequency relates to achieved frequency
+ * (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
*
* Sets a new CPUFreq policy.
*/
@@ -276,7 +279,7 @@
/* local_irq_save(flags); */
if (nforce2_set_fsb(target_fsb) < 0)
- printk(KERN_ERR "cpufreq: Changing FSB to %d failed\n",
+ printk(KERN_ERR PFX "Changing FSB to %d failed\n",
target_fsb);
else
dprintk("Changed FSB successfully to %d\n",
@@ -327,8 +330,8 @@
/* FIX: Get FID from CPU */
if (!fid) {
if (!cpu_khz) {
- printk(KERN_WARNING
- "cpufreq: cpu_khz not set, can't calculate multiplier!\n");
+ printk(KERN_WARNING PFX
+ "cpu_khz not set, can't calculate multiplier!\n");
return -ENODEV;
}
@@ -343,7 +346,7 @@
}
}
- printk(KERN_INFO "cpufreq: FSB currently at %i MHz, FID %d.%d\n", fsb,
+ printk(KERN_INFO PFX "FSB currently at %i MHz, FID %d.%d\n", fsb,
fid / 10, fid % 10);
/* Set maximum FSB to FSB at boot time */
@@ -392,17 +395,18 @@
*/
static unsigned int nforce2_detect_chipset(void)
{
- nforce2_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA,
+ nforce2_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA,
PCI_DEVICE_ID_NVIDIA_NFORCE2,
PCI_ANY_ID, PCI_ANY_ID, NULL);
- if (nforce2_chipset_dev == NULL)
+ if (nforce2_dev == NULL)
return -ENODEV;
- printk(KERN_INFO "cpufreq: Detected nForce2 chipset revision %X\n",
- nforce2_chipset_dev->revision);
- printk(KERN_INFO
- "cpufreq: FSB changing is maybe unstable and can lead to crashes and data loss.\n");
+ printk(KERN_INFO PFX "Detected nForce2 chipset revision %X\n",
+ nforce2_dev->revision);
+ printk(KERN_INFO PFX
+ "FSB changing is maybe unstable and can lead to "
+ "crashes and data loss.\n");
return 0;
}
@@ -420,7 +424,7 @@
/* detect chipset */
if (nforce2_detect_chipset()) {
- printk(KERN_ERR "cpufreq: No nForce2 chipset.\n");
+ printk(KERN_INFO PFX "No nForce2 chipset.\n");
return -ENODEV;
}
diff --git a/arch/x86/kernel/cpu/cpufreq/e_powersaver.c b/arch/x86/kernel/cpu/cpufreq/e_powersaver.c
index c2f930d..3f83ea1 100644
--- a/arch/x86/kernel/cpu/cpufreq/e_powersaver.c
+++ b/arch/x86/kernel/cpu/cpufreq/e_powersaver.c
@@ -12,12 +12,12 @@
#include <linux/cpufreq.h>
#include <linux/ioport.h>
#include <linux/slab.h>
+#include <linux/timex.h>
+#include <linux/io.h>
+#include <linux/delay.h>
#include <asm/msr.h>
#include <asm/tsc.h>
-#include <asm/timex.h>
-#include <asm/io.h>
-#include <asm/delay.h>
#define EPS_BRAND_C7M 0
#define EPS_BRAND_C7 1
@@ -184,7 +184,7 @@
break;
}
- switch(brand) {
+ switch (brand) {
case EPS_BRAND_C7M:
printk(KERN_CONT "C7-M\n");
break;
@@ -218,17 +218,20 @@
/* Print voltage and multiplier */
rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
current_voltage = lo & 0xff;
- printk(KERN_INFO "eps: Current voltage = %dmV\n", current_voltage * 16 + 700);
+ printk(KERN_INFO "eps: Current voltage = %dmV\n",
+ current_voltage * 16 + 700);
current_multiplier = (lo >> 8) & 0xff;
printk(KERN_INFO "eps: Current multiplier = %d\n", current_multiplier);
/* Print limits */
max_voltage = hi & 0xff;
- printk(KERN_INFO "eps: Highest voltage = %dmV\n", max_voltage * 16 + 700);
+ printk(KERN_INFO "eps: Highest voltage = %dmV\n",
+ max_voltage * 16 + 700);
max_multiplier = (hi >> 8) & 0xff;
printk(KERN_INFO "eps: Highest multiplier = %d\n", max_multiplier);
min_voltage = (hi >> 16) & 0xff;
- printk(KERN_INFO "eps: Lowest voltage = %dmV\n", min_voltage * 16 + 700);
+ printk(KERN_INFO "eps: Lowest voltage = %dmV\n",
+ min_voltage * 16 + 700);
min_multiplier = (hi >> 24) & 0xff;
printk(KERN_INFO "eps: Lowest multiplier = %d\n", min_multiplier);
@@ -318,7 +321,7 @@
return 0;
}
-static struct freq_attr* eps_attr[] = {
+static struct freq_attr *eps_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};
@@ -356,7 +359,7 @@
cpufreq_unregister_driver(&eps_driver);
}
-MODULE_AUTHOR("Rafa³ Bilski <rafalbilski@interia.pl>");
+MODULE_AUTHOR("Rafal Bilski <rafalbilski@interia.pl>");
MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's.");
MODULE_LICENSE("GPL");
diff --git a/arch/x86/kernel/cpu/cpufreq/elanfreq.c b/arch/x86/kernel/cpu/cpufreq/elanfreq.c
index fe613c9..006b278 100644
--- a/arch/x86/kernel/cpu/cpufreq/elanfreq.c
+++ b/arch/x86/kernel/cpu/cpufreq/elanfreq.c
@@ -184,7 +184,8 @@
{
unsigned int newstate = 0;
- if (cpufreq_frequency_table_target(policy, &elanfreq_table[0], target_freq, relation, &newstate))
+ if (cpufreq_frequency_table_target(policy, &elanfreq_table[0],
+ target_freq, relation, &newstate))
return -EINVAL;
elanfreq_set_cpu_state(newstate);
@@ -301,7 +302,8 @@
module_param(max_freq, int, 0444);
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Robert Schwebel <r.schwebel@pengutronix.de>, Sven Geggus <sven@geggus.net>");
+MODULE_AUTHOR("Robert Schwebel <r.schwebel@pengutronix.de>, "
+ "Sven Geggus <sven@geggus.net>");
MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs");
module_init(elanfreq_init);
diff --git a/arch/x86/kernel/cpu/cpufreq/gx-suspmod.c b/arch/x86/kernel/cpu/cpufreq/gx-suspmod.c
index 9d9eae8..ac27ec2 100644
--- a/arch/x86/kernel/cpu/cpufreq/gx-suspmod.c
+++ b/arch/x86/kernel/cpu/cpufreq/gx-suspmod.c
@@ -79,8 +79,9 @@
#include <linux/smp.h>
#include <linux/cpufreq.h>
#include <linux/pci.h>
+#include <linux/errno.h>
+
#include <asm/processor-cyrix.h>
-#include <asm/errno.h>
/* PCI config registers, all at F0 */
#define PCI_PMER1 0x80 /* power management enable register 1 */
@@ -122,8 +123,8 @@
static int stock_freq;
/* PCI bus clock - defaults to 30.000 if cpu_khz is not available */
-static int pci_busclk = 0;
-module_param (pci_busclk, int, 0444);
+static int pci_busclk;
+module_param(pci_busclk, int, 0444);
/* maximum duration for which the cpu may be suspended
* (32us * MAX_DURATION). If no parameter is given, this defaults
@@ -132,7 +133,7 @@
* is suspended -- processing power is just 0.39% of what it used to be,
* though. 781.25 kHz(!) for a 200 MHz processor -- wow. */
static int max_duration = 255;
-module_param (max_duration, int, 0444);
+module_param(max_duration, int, 0444);
/* For the default policy, we want at least some processing power
* - let's say 5%. (min = maxfreq / POLICY_MIN_DIV)
@@ -140,7 +141,8 @@
#define POLICY_MIN_DIV 20
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "gx-suspmod", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+ "gx-suspmod", msg)
/**
* we can detect a core multipiler from dir0_lsb
@@ -166,12 +168,20 @@
* Low Level chipset interface *
****************************************************************/
static struct pci_device_id gx_chipset_tbl[] __initdata = {
- { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY, PCI_ANY_ID, PCI_ANY_ID },
- { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, PCI_ANY_ID, PCI_ANY_ID },
- { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5510, PCI_ANY_ID, PCI_ANY_ID },
+ { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5510,
+ PCI_ANY_ID, PCI_ANY_ID },
{ 0, },
};
+static void gx_write_byte(int reg, int value)
+{
+ pci_write_config_byte(gx_params->cs55x0, reg, value);
+}
+
/**
* gx_detect_chipset:
*
@@ -200,7 +210,8 @@
/**
* gx_get_cpuspeed:
*
- * Finds out at which efficient frequency the Cyrix MediaGX/NatSemi Geode CPU runs.
+ * Finds out at which efficient frequency the Cyrix MediaGX/NatSemi
+ * Geode CPU runs.
*/
static unsigned int gx_get_cpuspeed(unsigned int cpu)
{
@@ -217,17 +228,18 @@
*
**/
-static unsigned int gx_validate_speed(unsigned int khz, u8 *on_duration, u8 *off_duration)
+static unsigned int gx_validate_speed(unsigned int khz, u8 *on_duration,
+ u8 *off_duration)
{
unsigned int i;
u8 tmp_on, tmp_off;
int old_tmp_freq = stock_freq;
int tmp_freq;
- *off_duration=1;
- *on_duration=0;
+ *off_duration = 1;
+ *on_duration = 0;
- for (i=max_duration; i>0; i--) {
+ for (i = max_duration; i > 0; i--) {
tmp_off = ((khz * i) / stock_freq) & 0xff;
tmp_on = i - tmp_off;
tmp_freq = (stock_freq * tmp_off) / i;
@@ -259,26 +271,34 @@
freqs.cpu = 0;
freqs.old = gx_get_cpuspeed(0);
- new_khz = gx_validate_speed(khz, &gx_params->on_duration, &gx_params->off_duration);
+ new_khz = gx_validate_speed(khz, &gx_params->on_duration,
+ &gx_params->off_duration);
freqs.new = new_khz;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
local_irq_save(flags);
- if (new_khz != stock_freq) { /* if new khz == 100% of CPU speed, it is special case */
+
+
+ if (new_khz != stock_freq) {
+ /* if new khz == 100% of CPU speed, it is special case */
switch (gx_params->cs55x0->device) {
case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
pmer1 = gx_params->pci_pmer1 | IRQ_SPDUP | VID_SPDUP;
/* FIXME: need to test other values -- Zwane,Miura */
- pci_write_config_byte(gx_params->cs55x0, PCI_IRQTC, 4); /* typical 2 to 4ms */
- pci_write_config_byte(gx_params->cs55x0, PCI_VIDTC, 100);/* typical 50 to 100ms */
- pci_write_config_byte(gx_params->cs55x0, PCI_PMER1, pmer1);
+ /* typical 2 to 4ms */
+ gx_write_byte(PCI_IRQTC, 4);
+ /* typical 50 to 100ms */
+ gx_write_byte(PCI_VIDTC, 100);
+ gx_write_byte(PCI_PMER1, pmer1);
- if (gx_params->cs55x0->revision < 0x10) { /* CS5530(rev 1.2, 1.3) */
- suscfg = gx_params->pci_suscfg | SUSMOD;
- } else { /* CS5530A,B.. */
- suscfg = gx_params->pci_suscfg | SUSMOD | PWRSVE;
+ if (gx_params->cs55x0->revision < 0x10) {
+ /* CS5530(rev 1.2, 1.3) */
+ suscfg = gx_params->pci_suscfg|SUSMOD;
+ } else {
+ /* CS5530A,B.. */
+ suscfg = gx_params->pci_suscfg|SUSMOD|PWRSVE;
}
break;
case PCI_DEVICE_ID_CYRIX_5520:
@@ -294,13 +314,13 @@
suscfg = gx_params->pci_suscfg & ~(SUSMOD);
gx_params->off_duration = 0;
gx_params->on_duration = 0;
- dprintk("suspend modulation disabled: cpu runs 100 percent speed.\n");
+ dprintk("suspend modulation disabled: cpu runs 100%% speed.\n");
}
- pci_write_config_byte(gx_params->cs55x0, PCI_MODOFF, gx_params->off_duration);
- pci_write_config_byte(gx_params->cs55x0, PCI_MODON, gx_params->on_duration);
+ gx_write_byte(PCI_MODOFF, gx_params->off_duration);
+ gx_write_byte(PCI_MODON, gx_params->on_duration);
- pci_write_config_byte(gx_params->cs55x0, PCI_SUSCFG, suscfg);
+ gx_write_byte(PCI_SUSCFG, suscfg);
pci_read_config_byte(gx_params->cs55x0, PCI_SUSCFG, &suscfg);
local_irq_restore(flags);
@@ -334,7 +354,8 @@
return -EINVAL;
policy->cpu = 0;
- cpufreq_verify_within_limits(policy, (stock_freq / max_duration), stock_freq);
+ cpufreq_verify_within_limits(policy, (stock_freq / max_duration),
+ stock_freq);
/* it needs to be assured that at least one supported frequency is
* within policy->min and policy->max. If it is not, policy->max
@@ -354,7 +375,8 @@
policy->max = tmp_freq;
if (policy->max < policy->min)
policy->max = policy->min;
- cpufreq_verify_within_limits(policy, (stock_freq / max_duration), stock_freq);
+ cpufreq_verify_within_limits(policy, (stock_freq / max_duration),
+ stock_freq);
return 0;
}
@@ -398,18 +420,18 @@
return -ENODEV;
/* determine maximum frequency */
- if (pci_busclk) {
+ if (pci_busclk)
maxfreq = pci_busclk * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
- } else if (cpu_khz) {
+ else if (cpu_khz)
maxfreq = cpu_khz;
- } else {
+ else
maxfreq = 30000 * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
- }
+
stock_freq = maxfreq;
curfreq = gx_get_cpuspeed(0);
dprintk("cpu max frequency is %d.\n", maxfreq);
- dprintk("cpu current frequency is %dkHz.\n",curfreq);
+ dprintk("cpu current frequency is %dkHz.\n", curfreq);
/* setup basic struct for cpufreq API */
policy->cpu = 0;
@@ -447,7 +469,8 @@
struct pci_dev *gx_pci;
/* Test if we have the right hardware */
- if ((gx_pci = gx_detect_chipset()) == NULL)
+ gx_pci = gx_detect_chipset();
+ if (gx_pci == NULL)
return -ENODEV;
/* check whether module parameters are sane */
@@ -468,9 +491,11 @@
pci_read_config_byte(params->cs55x0, PCI_PMER1, &(params->pci_pmer1));
pci_read_config_byte(params->cs55x0, PCI_PMER2, &(params->pci_pmer2));
pci_read_config_byte(params->cs55x0, PCI_MODON, &(params->on_duration));
- pci_read_config_byte(params->cs55x0, PCI_MODOFF, &(params->off_duration));
+ pci_read_config_byte(params->cs55x0, PCI_MODOFF,
+ &(params->off_duration));
- if ((ret = cpufreq_register_driver(&gx_suspmod_driver))) {
+ ret = cpufreq_register_driver(&gx_suspmod_driver);
+ if (ret) {
kfree(params);
return ret; /* register error! */
}
@@ -485,9 +510,9 @@
kfree(gx_params);
}
-MODULE_AUTHOR ("Hiroshi Miura <miura@da-cha.org>");
-MODULE_DESCRIPTION ("Cpufreq driver for Cyrix MediaGX and NatSemi Geode");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Hiroshi Miura <miura@da-cha.org>");
+MODULE_DESCRIPTION("Cpufreq driver for Cyrix MediaGX and NatSemi Geode");
+MODULE_LICENSE("GPL");
module_init(cpufreq_gx_init);
module_exit(cpufreq_gx_exit);
diff --git a/arch/x86/kernel/cpu/cpufreq/longhaul.c b/arch/x86/kernel/cpu/cpufreq/longhaul.c
index a4cff5d..f1c51ae 100644
--- a/arch/x86/kernel/cpu/cpufreq/longhaul.c
+++ b/arch/x86/kernel/cpu/cpufreq/longhaul.c
@@ -30,12 +30,12 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/delay.h>
+#include <linux/timex.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+#include <linux/kernel.h>
#include <asm/msr.h>
-#include <asm/timex.h>
-#include <asm/io.h>
-#include <asm/acpi.h>
-#include <linux/acpi.h>
#include <acpi/processor.h>
#include "longhaul.h"
@@ -58,7 +58,7 @@
#define USE_NORTHBRIDGE (1 << 2)
static int cpu_model;
-static unsigned int numscales=16;
+static unsigned int numscales = 16;
static unsigned int fsb;
static const struct mV_pos *vrm_mV_table;
@@ -67,8 +67,8 @@
static unsigned int highest_speed, lowest_speed; /* kHz */
static unsigned int minmult, maxmult;
static int can_scale_voltage;
-static struct acpi_processor *pr = NULL;
-static struct acpi_processor_cx *cx = NULL;
+static struct acpi_processor *pr;
+static struct acpi_processor_cx *cx;
static u32 acpi_regs_addr;
static u8 longhaul_flags;
static unsigned int longhaul_index;
@@ -78,12 +78,13 @@
static int disable_acpi_c3;
static int revid_errata;
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "longhaul", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+ "longhaul", msg)
/* Clock ratios multiplied by 10 */
-static int clock_ratio[32];
-static int eblcr_table[32];
+static int mults[32];
+static int eblcr[32];
static int longhaul_version;
static struct cpufreq_frequency_table *longhaul_table;
@@ -93,7 +94,7 @@
static char *print_speed(int speed)
{
if (speed < 1000) {
- snprintf(speedbuffer, sizeof(speedbuffer),"%dMHz", speed);
+ snprintf(speedbuffer, sizeof(speedbuffer), "%dMHz", speed);
return speedbuffer;
}
@@ -122,27 +123,28 @@
static int longhaul_get_cpu_mult(void)
{
- unsigned long invalue=0,lo, hi;
+ unsigned long invalue = 0, lo, hi;
- rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi);
- invalue = (lo & (1<<22|1<<23|1<<24|1<<25)) >>22;
- if (longhaul_version==TYPE_LONGHAUL_V2 || longhaul_version==TYPE_POWERSAVER) {
+ rdmsr(MSR_IA32_EBL_CR_POWERON, lo, hi);
+ invalue = (lo & (1<<22|1<<23|1<<24|1<<25))>>22;
+ if (longhaul_version == TYPE_LONGHAUL_V2 ||
+ longhaul_version == TYPE_POWERSAVER) {
if (lo & (1<<27))
- invalue+=16;
+ invalue += 16;
}
- return eblcr_table[invalue];
+ return eblcr[invalue];
}
/* For processor with BCR2 MSR */
-static void do_longhaul1(unsigned int clock_ratio_index)
+static void do_longhaul1(unsigned int mults_index)
{
union msr_bcr2 bcr2;
rdmsrl(MSR_VIA_BCR2, bcr2.val);
/* Enable software clock multiplier */
bcr2.bits.ESOFTBF = 1;
- bcr2.bits.CLOCKMUL = clock_ratio_index & 0xff;
+ bcr2.bits.CLOCKMUL = mults_index & 0xff;
/* Sync to timer tick */
safe_halt();
@@ -161,7 +163,7 @@
/* For processor with Longhaul MSR */
-static void do_powersaver(int cx_address, unsigned int clock_ratio_index,
+static void do_powersaver(int cx_address, unsigned int mults_index,
unsigned int dir)
{
union msr_longhaul longhaul;
@@ -173,11 +175,11 @@
longhaul.bits.RevisionKey = longhaul.bits.RevisionID;
else
longhaul.bits.RevisionKey = 0;
- longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf;
- longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4;
+ longhaul.bits.SoftBusRatio = mults_index & 0xf;
+ longhaul.bits.SoftBusRatio4 = (mults_index & 0x10) >> 4;
/* Setup new voltage */
if (can_scale_voltage)
- longhaul.bits.SoftVID = (clock_ratio_index >> 8) & 0x1f;
+ longhaul.bits.SoftVID = (mults_index >> 8) & 0x1f;
/* Sync to timer tick */
safe_halt();
/* Raise voltage if necessary */
@@ -240,14 +242,14 @@
/**
* longhaul_set_cpu_frequency()
- * @clock_ratio_index : bitpattern of the new multiplier.
+ * @mults_index : bitpattern of the new multiplier.
*
* Sets a new clock ratio.
*/
static void longhaul_setstate(unsigned int table_index)
{
- unsigned int clock_ratio_index;
+ unsigned int mults_index;
int speed, mult;
struct cpufreq_freqs freqs;
unsigned long flags;
@@ -256,9 +258,9 @@
u32 bm_timeout = 1000;
unsigned int dir = 0;
- clock_ratio_index = longhaul_table[table_index].index;
+ mults_index = longhaul_table[table_index].index;
/* Safety precautions */
- mult = clock_ratio[clock_ratio_index & 0x1f];
+ mult = mults[mults_index & 0x1f];
if (mult == -1)
return;
speed = calc_speed(mult);
@@ -274,7 +276,7 @@
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
- dprintk ("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n",
+ dprintk("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n",
fsb, mult/10, mult%10, print_speed(speed/1000));
retry_loop:
preempt_disable();
@@ -282,8 +284,8 @@
pic2_mask = inb(0xA1);
pic1_mask = inb(0x21); /* works on C3. save mask. */
- outb(0xFF,0xA1); /* Overkill */
- outb(0xFE,0x21); /* TMR0 only */
+ outb(0xFF, 0xA1); /* Overkill */
+ outb(0xFE, 0x21); /* TMR0 only */
/* Wait while PCI bus is busy. */
if (acpi_regs_addr && (longhaul_flags & USE_NORTHBRIDGE
@@ -312,7 +314,7 @@
* Software controlled multipliers only.
*/
case TYPE_LONGHAUL_V1:
- do_longhaul1(clock_ratio_index);
+ do_longhaul1(mults_index);
break;
/*
@@ -327,9 +329,9 @@
if (longhaul_flags & USE_ACPI_C3) {
/* Don't allow wakeup */
acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0);
- do_powersaver(cx->address, clock_ratio_index, dir);
+ do_powersaver(cx->address, mults_index, dir);
} else {
- do_powersaver(0, clock_ratio_index, dir);
+ do_powersaver(0, mults_index, dir);
}
break;
}
@@ -341,8 +343,8 @@
/* Enable bus master arbitration */
acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0);
}
- outb(pic2_mask,0xA1); /* restore mask */
- outb(pic1_mask,0x21);
+ outb(pic2_mask, 0xA1); /* restore mask */
+ outb(pic1_mask, 0x21);
local_irq_restore(flags);
preempt_enable();
@@ -392,7 +394,8 @@
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
if (!bm_timeout)
- printk(KERN_INFO PFX "Warning: Timeout while waiting for idle PCI bus.\n");
+ printk(KERN_INFO PFX "Warning: Timeout while waiting for "
+ "idle PCI bus.\n");
}
/*
@@ -458,31 +461,32 @@
break;
}
- dprintk ("MinMult:%d.%dx MaxMult:%d.%dx\n",
+ dprintk("MinMult:%d.%dx MaxMult:%d.%dx\n",
minmult/10, minmult%10, maxmult/10, maxmult%10);
highest_speed = calc_speed(maxmult);
lowest_speed = calc_speed(minmult);
- dprintk ("FSB:%dMHz Lowest speed: %s Highest speed:%s\n", fsb,
+ dprintk("FSB:%dMHz Lowest speed: %s Highest speed:%s\n", fsb,
print_speed(lowest_speed/1000),
print_speed(highest_speed/1000));
if (lowest_speed == highest_speed) {
- printk (KERN_INFO PFX "highestspeed == lowest, aborting.\n");
+ printk(KERN_INFO PFX "highestspeed == lowest, aborting.\n");
return -EINVAL;
}
if (lowest_speed > highest_speed) {
- printk (KERN_INFO PFX "nonsense! lowest (%d > %d) !\n",
+ printk(KERN_INFO PFX "nonsense! lowest (%d > %d) !\n",
lowest_speed, highest_speed);
return -EINVAL;
}
- longhaul_table = kmalloc((numscales + 1) * sizeof(struct cpufreq_frequency_table), GFP_KERNEL);
- if(!longhaul_table)
+ longhaul_table = kmalloc((numscales + 1) * sizeof(*longhaul_table),
+ GFP_KERNEL);
+ if (!longhaul_table)
return -ENOMEM;
for (j = 0; j < numscales; j++) {
- ratio = clock_ratio[j];
+ ratio = mults[j];
if (ratio == -1)
continue;
if (ratio > maxmult || ratio < minmult)
@@ -507,13 +511,10 @@
}
}
if (min_i != j) {
- unsigned int temp;
- temp = longhaul_table[j].frequency;
- longhaul_table[j].frequency = longhaul_table[min_i].frequency;
- longhaul_table[min_i].frequency = temp;
- temp = longhaul_table[j].index;
- longhaul_table[j].index = longhaul_table[min_i].index;
- longhaul_table[min_i].index = temp;
+ swap(longhaul_table[j].frequency,
+ longhaul_table[min_i].frequency);
+ swap(longhaul_table[j].index,
+ longhaul_table[min_i].index);
}
}
@@ -521,7 +522,7 @@
/* Find index we are running on */
for (j = 0; j < k; j++) {
- if (clock_ratio[longhaul_table[j].index & 0x1f] == mult) {
+ if (mults[longhaul_table[j].index & 0x1f] == mult) {
longhaul_index = j;
break;
}
@@ -559,20 +560,22 @@
maxvid = vrm_mV_table[longhaul.bits.MaximumVID];
if (minvid.mV == 0 || maxvid.mV == 0 || minvid.mV > maxvid.mV) {
- printk (KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. "
+ printk(KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. "
"Voltage scaling disabled.\n",
- minvid.mV/1000, minvid.mV%1000, maxvid.mV/1000, maxvid.mV%1000);
+ minvid.mV/1000, minvid.mV%1000,
+ maxvid.mV/1000, maxvid.mV%1000);
return;
}
if (minvid.mV == maxvid.mV) {
- printk (KERN_INFO PFX "Claims to support voltage scaling but min & max are "
- "both %d.%03d. Voltage scaling disabled\n",
+ printk(KERN_INFO PFX "Claims to support voltage scaling but "
+ "min & max are both %d.%03d. "
+ "Voltage scaling disabled\n",
maxvid.mV/1000, maxvid.mV%1000);
return;
}
- /* How many voltage steps */
+ /* How many voltage steps*/
numvscales = maxvid.pos - minvid.pos + 1;
printk(KERN_INFO PFX
"Max VID=%d.%03d "
@@ -586,7 +589,7 @@
j = longhaul.bits.MinMHzBR;
if (longhaul.bits.MinMHzBR4)
j += 16;
- min_vid_speed = eblcr_table[j];
+ min_vid_speed = eblcr[j];
if (min_vid_speed == -1)
return;
switch (longhaul.bits.MinMHzFSB) {
@@ -617,7 +620,8 @@
pos = minvid.pos;
longhaul_table[j].index |= mV_vrm_table[pos] << 8;
vid = vrm_mV_table[mV_vrm_table[pos]];
- printk(KERN_INFO PFX "f: %d kHz, index: %d, vid: %d mV\n", speed, j, vid.mV);
+ printk(KERN_INFO PFX "f: %d kHz, index: %d, vid: %d mV\n",
+ speed, j, vid.mV);
j++;
}
@@ -640,7 +644,8 @@
unsigned int dir = 0;
u8 vid, current_vid;
- if (cpufreq_frequency_table_target(policy, longhaul_table, target_freq, relation, &table_index))
+ if (cpufreq_frequency_table_target(policy, longhaul_table, target_freq,
+ relation, &table_index))
return -EINVAL;
/* Don't set same frequency again */
@@ -656,7 +661,8 @@
* this in hardware, C3 is old and we need to do this
* in software. */
i = longhaul_index;
- current_vid = (longhaul_table[longhaul_index].index >> 8) & 0x1f;
+ current_vid = (longhaul_table[longhaul_index].index >> 8);
+ current_vid &= 0x1f;
if (table_index > longhaul_index)
dir = 1;
while (i != table_index) {
@@ -691,9 +697,9 @@
{
struct acpi_device *d;
- if ( acpi_bus_get_device(obj_handle, &d) ) {
+ if (acpi_bus_get_device(obj_handle, &d))
return 0;
- }
+
*return_value = acpi_driver_data(d);
return 1;
}
@@ -750,7 +756,7 @@
/* Find VT8235 southbridge */
dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, NULL);
if (dev == NULL)
- /* Find VT8237 southbridge */
+ /* Find VT8237 southbridge */
dev = pci_get_device(PCI_VENDOR_ID_VIA,
PCI_DEVICE_ID_VIA_8237, NULL);
if (dev != NULL) {
@@ -769,7 +775,8 @@
if (pci_cmd & 1 << 7) {
pci_read_config_dword(dev, 0x88, &acpi_regs_addr);
acpi_regs_addr &= 0xff00;
- printk(KERN_INFO PFX "ACPI I/O at 0x%x\n", acpi_regs_addr);
+ printk(KERN_INFO PFX "ACPI I/O at 0x%x\n",
+ acpi_regs_addr);
}
pci_dev_put(dev);
@@ -781,7 +788,7 @@
static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
{
struct cpuinfo_x86 *c = &cpu_data(0);
- char *cpuname=NULL;
+ char *cpuname = NULL;
int ret;
u32 lo, hi;
@@ -791,8 +798,8 @@
cpu_model = CPU_SAMUEL;
cpuname = "C3 'Samuel' [C5A]";
longhaul_version = TYPE_LONGHAUL_V1;
- memcpy (clock_ratio, samuel1_clock_ratio, sizeof(samuel1_clock_ratio));
- memcpy (eblcr_table, samuel1_eblcr, sizeof(samuel1_eblcr));
+ memcpy(mults, samuel1_mults, sizeof(samuel1_mults));
+ memcpy(eblcr, samuel1_eblcr, sizeof(samuel1_eblcr));
break;
case 7:
@@ -803,10 +810,8 @@
cpuname = "C3 'Samuel 2' [C5B]";
/* Note, this is not a typo, early Samuel2's had
* Samuel1 ratios. */
- memcpy(clock_ratio, samuel1_clock_ratio,
- sizeof(samuel1_clock_ratio));
- memcpy(eblcr_table, samuel2_eblcr,
- sizeof(samuel2_eblcr));
+ memcpy(mults, samuel1_mults, sizeof(samuel1_mults));
+ memcpy(eblcr, samuel2_eblcr, sizeof(samuel2_eblcr));
break;
case 1 ... 15:
longhaul_version = TYPE_LONGHAUL_V1;
@@ -817,10 +822,8 @@
cpu_model = CPU_EZRA;
cpuname = "C3 'Ezra' [C5C]";
}
- memcpy(clock_ratio, ezra_clock_ratio,
- sizeof(ezra_clock_ratio));
- memcpy(eblcr_table, ezra_eblcr,
- sizeof(ezra_eblcr));
+ memcpy(mults, ezra_mults, sizeof(ezra_mults));
+ memcpy(eblcr, ezra_eblcr, sizeof(ezra_eblcr));
break;
}
break;
@@ -829,18 +832,16 @@
cpu_model = CPU_EZRA_T;
cpuname = "C3 'Ezra-T' [C5M]";
longhaul_version = TYPE_POWERSAVER;
- numscales=32;
- memcpy (clock_ratio, ezrat_clock_ratio, sizeof(ezrat_clock_ratio));
- memcpy (eblcr_table, ezrat_eblcr, sizeof(ezrat_eblcr));
+ numscales = 32;
+ memcpy(mults, ezrat_mults, sizeof(ezrat_mults));
+ memcpy(eblcr, ezrat_eblcr, sizeof(ezrat_eblcr));
break;
case 9:
longhaul_version = TYPE_POWERSAVER;
numscales = 32;
- memcpy(clock_ratio,
- nehemiah_clock_ratio,
- sizeof(nehemiah_clock_ratio));
- memcpy(eblcr_table, nehemiah_eblcr, sizeof(nehemiah_eblcr));
+ memcpy(mults, nehemiah_mults, sizeof(nehemiah_mults));
+ memcpy(eblcr, nehemiah_eblcr, sizeof(nehemiah_eblcr));
switch (c->x86_mask) {
case 0 ... 1:
cpu_model = CPU_NEHEMIAH;
@@ -869,14 +870,14 @@
longhaul_version = TYPE_LONGHAUL_V1;
}
- printk (KERN_INFO PFX "VIA %s CPU detected. ", cpuname);
+ printk(KERN_INFO PFX "VIA %s CPU detected. ", cpuname);
switch (longhaul_version) {
case TYPE_LONGHAUL_V1:
case TYPE_LONGHAUL_V2:
- printk ("Longhaul v%d supported.\n", longhaul_version);
+ printk(KERN_CONT "Longhaul v%d supported.\n", longhaul_version);
break;
case TYPE_POWERSAVER:
- printk ("Powersaver supported.\n");
+ printk(KERN_CONT "Powersaver supported.\n");
break;
};
@@ -940,7 +941,7 @@
return 0;
}
-static struct freq_attr* longhaul_attr[] = {
+static struct freq_attr *longhaul_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};
@@ -966,13 +967,15 @@
#ifdef CONFIG_SMP
if (num_online_cpus() > 1) {
- printk(KERN_ERR PFX "More than 1 CPU detected, longhaul disabled.\n");
+ printk(KERN_ERR PFX "More than 1 CPU detected, "
+ "longhaul disabled.\n");
return -ENODEV;
}
#endif
#ifdef CONFIG_X86_IO_APIC
if (cpu_has_apic) {
- printk(KERN_ERR PFX "APIC detected. Longhaul is currently broken in this configuration.\n");
+ printk(KERN_ERR PFX "APIC detected. Longhaul is currently "
+ "broken in this configuration.\n");
return -ENODEV;
}
#endif
@@ -993,8 +996,8 @@
{
int i;
- for (i=0; i < numscales; i++) {
- if (clock_ratio[i] == maxmult) {
+ for (i = 0; i < numscales; i++) {
+ if (mults[i] == maxmult) {
longhaul_setstate(i);
break;
}
@@ -1007,11 +1010,11 @@
/* Even if BIOS is exporting ACPI C3 state, and it is used
* with success when CPU is idle, this state doesn't
* trigger frequency transition in some cases. */
-module_param (disable_acpi_c3, int, 0644);
+module_param(disable_acpi_c3, int, 0644);
MODULE_PARM_DESC(disable_acpi_c3, "Don't use ACPI C3 support");
/* Change CPU voltage with frequency. Very usefull to save
* power, but most VIA C3 processors aren't supporting it. */
-module_param (scale_voltage, int, 0644);
+module_param(scale_voltage, int, 0644);
MODULE_PARM_DESC(scale_voltage, "Scale voltage of processor");
/* Force revision key to 0 for processors which doesn't
* support voltage scaling, but are introducing itself as
@@ -1019,9 +1022,9 @@
module_param(revid_errata, int, 0644);
MODULE_PARM_DESC(revid_errata, "Ignore CPU Revision ID");
-MODULE_AUTHOR ("Dave Jones <davej@redhat.com>");
-MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors.");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
+MODULE_DESCRIPTION("Longhaul driver for VIA Cyrix processors.");
+MODULE_LICENSE("GPL");
late_initcall(longhaul_init);
module_exit(longhaul_exit);
diff --git a/arch/x86/kernel/cpu/cpufreq/longhaul.h b/arch/x86/kernel/cpu/cpufreq/longhaul.h
index 4fcc320..e2360a4 100644
--- a/arch/x86/kernel/cpu/cpufreq/longhaul.h
+++ b/arch/x86/kernel/cpu/cpufreq/longhaul.h
@@ -49,14 +49,14 @@
/*
* Clock ratio tables. Div/Mod by 10 to get ratio.
- * The eblcr ones specify the ratio read from the CPU.
- * The clock_ratio ones specify what to write to the CPU.
+ * The eblcr values specify the ratio read from the CPU.
+ * The mults values specify what to write to the CPU.
*/
/*
* VIA C3 Samuel 1 & Samuel 2 (stepping 0)
*/
-static const int __initdata samuel1_clock_ratio[16] = {
+static const int __initdata samuel1_mults[16] = {
-1, /* 0000 -> RESERVED */
30, /* 0001 -> 3.0x */
40, /* 0010 -> 4.0x */
@@ -119,7 +119,7 @@
/*
* VIA C3 Ezra
*/
-static const int __initdata ezra_clock_ratio[16] = {
+static const int __initdata ezra_mults[16] = {
100, /* 0000 -> 10.0x */
30, /* 0001 -> 3.0x */
40, /* 0010 -> 4.0x */
@@ -160,7 +160,7 @@
/*
* VIA C3 (Ezra-T) [C5M].
*/
-static const int __initdata ezrat_clock_ratio[32] = {
+static const int __initdata ezrat_mults[32] = {
100, /* 0000 -> 10.0x */
30, /* 0001 -> 3.0x */
40, /* 0010 -> 4.0x */
@@ -235,7 +235,7 @@
/*
* VIA C3 Nehemiah */
-static const int __initdata nehemiah_clock_ratio[32] = {
+static const int __initdata nehemiah_mults[32] = {
100, /* 0000 -> 10.0x */
-1, /* 0001 -> 16.0x */
40, /* 0010 -> 4.0x */
diff --git a/arch/x86/kernel/cpu/cpufreq/longrun.c b/arch/x86/kernel/cpu/cpufreq/longrun.c
index 777a7ff..da5f70f 100644
--- a/arch/x86/kernel/cpu/cpufreq/longrun.c
+++ b/arch/x86/kernel/cpu/cpufreq/longrun.c
@@ -11,12 +11,13 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/cpufreq.h>
+#include <linux/timex.h>
#include <asm/msr.h>
#include <asm/processor.h>
-#include <asm/timex.h>
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "longrun", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+ "longrun", msg)
static struct cpufreq_driver longrun_driver;
@@ -51,7 +52,7 @@
msr_lo &= 0x0000007F;
msr_hi &= 0x0000007F;
- if ( longrun_high_freq <= longrun_low_freq ) {
+ if (longrun_high_freq <= longrun_low_freq) {
/* Assume degenerate Longrun table */
policy->min = policy->max = longrun_high_freq;
} else {
@@ -79,7 +80,7 @@
if (!policy)
return -EINVAL;
- if ( longrun_high_freq <= longrun_low_freq ) {
+ if (longrun_high_freq <= longrun_low_freq) {
/* Assume degenerate Longrun table */
pctg_lo = pctg_hi = 100;
} else {
@@ -152,7 +153,7 @@
cpuid(0x80860007, &eax, &ebx, &ecx, &edx);
dprintk("cpuid eax is %u\n", eax);
- return (eax * 1000);
+ return eax * 1000;
}
/**
@@ -196,7 +197,8 @@
rdmsr(MSR_TMTA_LRTI_VOLT_MHZ, msr_lo, msr_hi);
*high_freq = msr_lo * 1000; /* to kHz */
- dprintk("longrun table interface told %u - %u kHz\n", *low_freq, *high_freq);
+ dprintk("longrun table interface told %u - %u kHz\n",
+ *low_freq, *high_freq);
if (*low_freq > *high_freq)
*low_freq = *high_freq;
@@ -219,7 +221,7 @@
cpuid(0x80860007, &eax, &ebx, &ecx, &edx);
/* try decreasing in 10% steps, some processors react only
* on some barrier values */
- for (try_hi = 80; try_hi > 0 && ecx > 90; try_hi -=10) {
+ for (try_hi = 80; try_hi > 0 && ecx > 90; try_hi -= 10) {
/* set to 0 to try_hi perf_pctg */
msr_lo &= 0xFFFFFF80;
msr_hi &= 0xFFFFFF80;
@@ -236,7 +238,7 @@
/* performance_pctg = (current_freq - low_freq)/(high_freq - low_freq)
* eqals
- * low_freq * ( 1 - perf_pctg) = (cur_freq - high_freq * perf_pctg)
+ * low_freq * (1 - perf_pctg) = (cur_freq - high_freq * perf_pctg)
*
* high_freq * perf_pctg is stored tempoarily into "ebx".
*/
@@ -317,9 +319,10 @@
}
-MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>");
-MODULE_DESCRIPTION ("LongRun driver for Transmeta Crusoe and Efficeon processors.");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
+MODULE_DESCRIPTION("LongRun driver for Transmeta Crusoe and "
+ "Efficeon processors.");
+MODULE_LICENSE("GPL");
module_init(longrun_init);
module_exit(longrun_exit);
diff --git a/arch/x86/kernel/cpu/cpufreq/p4-clockmod.c b/arch/x86/kernel/cpu/cpufreq/p4-clockmod.c
index 3178c3a..41ed949 100644
--- a/arch/x86/kernel/cpu/cpufreq/p4-clockmod.c
+++ b/arch/x86/kernel/cpu/cpufreq/p4-clockmod.c
@@ -27,15 +27,17 @@
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/cpumask.h>
+#include <linux/timex.h>
#include <asm/processor.h>
#include <asm/msr.h>
-#include <asm/timex.h>
+#include <asm/timer.h>
#include "speedstep-lib.h"
#define PFX "p4-clockmod: "
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "p4-clockmod", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+ "p4-clockmod", msg)
/*
* Duty Cycle (3bits), note DC_DISABLE is not specified in
@@ -58,7 +60,8 @@
{
u32 l, h;
- if (!cpu_online(cpu) || (newstate > DC_DISABLE) || (newstate == DC_RESV))
+ if (!cpu_online(cpu) ||
+ (newstate > DC_DISABLE) || (newstate == DC_RESV))
return -EINVAL;
rdmsr_on_cpu(cpu, MSR_IA32_THERM_STATUS, &l, &h);
@@ -66,7 +69,8 @@
if (l & 0x01)
dprintk("CPU#%d currently thermal throttled\n", cpu);
- if (has_N44_O17_errata[cpu] && (newstate == DC_25PT || newstate == DC_DFLT))
+ if (has_N44_O17_errata[cpu] &&
+ (newstate == DC_25PT || newstate == DC_DFLT))
newstate = DC_38PT;
rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h);
@@ -112,7 +116,8 @@
struct cpufreq_freqs freqs;
int i;
- if (cpufreq_frequency_table_target(policy, &p4clockmod_table[0], target_freq, relation, &newstate))
+ if (cpufreq_frequency_table_target(policy, &p4clockmod_table[0],
+ target_freq, relation, &newstate))
return -EINVAL;
freqs.old = cpufreq_p4_get(policy->cpu);
@@ -127,7 +132,8 @@
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
}
- /* run on each logical CPU, see section 13.15.3 of IA32 Intel Architecture Software
+ /* run on each logical CPU,
+ * see section 13.15.3 of IA32 Intel Architecture Software
* Developer's Manual, Volume 3
*/
for_each_cpu(i, policy->cpus)
@@ -153,28 +159,30 @@
{
if (c->x86 == 0x06) {
if (cpu_has(c, X86_FEATURE_EST))
- printk(KERN_WARNING PFX "Warning: EST-capable CPU detected. "
- "The acpi-cpufreq module offers voltage scaling"
- " in addition of frequency scaling. You should use "
- "that instead of p4-clockmod, if possible.\n");
+ printk(KERN_WARNING PFX "Warning: EST-capable CPU "
+ "detected. The acpi-cpufreq module offers "
+ "voltage scaling in addition of frequency "
+ "scaling. You should use that instead of "
+ "p4-clockmod, if possible.\n");
switch (c->x86_model) {
case 0x0E: /* Core */
case 0x0F: /* Core Duo */
case 0x16: /* Celeron Core */
p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
- return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_PCORE);
+ return speedstep_get_frequency(SPEEDSTEP_CPU_PCORE);
case 0x0D: /* Pentium M (Dothan) */
p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
/* fall through */
case 0x09: /* Pentium M (Banias) */
- return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_PM);
+ return speedstep_get_frequency(SPEEDSTEP_CPU_PM);
}
}
if (c->x86 != 0xF) {
if (!cpu_has(c, X86_FEATURE_EST))
- printk(KERN_WARNING PFX "Unknown p4-clockmod-capable CPU. "
- "Please send an e-mail to <cpufreq@vger.kernel.org>\n");
+ printk(KERN_WARNING PFX "Unknown CPU. "
+ "Please send an e-mail to "
+ "<cpufreq@vger.kernel.org>\n");
return 0;
}
@@ -182,16 +190,16 @@
* throttling is active or not. */
p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
- if (speedstep_detect_processor() == SPEEDSTEP_PROCESSOR_P4M) {
+ if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4M) {
printk(KERN_WARNING PFX "Warning: Pentium 4-M detected. "
"The speedstep-ich or acpi cpufreq modules offer "
"voltage scaling in addition of frequency scaling. "
"You should use either one instead of p4-clockmod, "
"if possible.\n");
- return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_P4M);
+ return speedstep_get_frequency(SPEEDSTEP_CPU_P4M);
}
- return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_P4D);
+ return speedstep_get_frequency(SPEEDSTEP_CPU_P4D);
}
@@ -217,14 +225,20 @@
dprintk("has errata -- disabling low frequencies\n");
}
+ if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4D &&
+ c->x86_model < 2) {
+ /* switch to maximum frequency and measure result */
+ cpufreq_p4_setdc(policy->cpu, DC_DISABLE);
+ recalibrate_cpu_khz();
+ }
/* get max frequency */
stock_freq = cpufreq_p4_get_frequency(c);
if (!stock_freq)
return -EINVAL;
/* table init */
- for (i=1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) {
- if ((i<2) && (has_N44_O17_errata[policy->cpu]))
+ for (i = 1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) {
+ if ((i < 2) && (has_N44_O17_errata[policy->cpu]))
p4clockmod_table[i].frequency = CPUFREQ_ENTRY_INVALID;
else
p4clockmod_table[i].frequency = (stock_freq * i)/8;
@@ -232,7 +246,10 @@
cpufreq_frequency_table_get_attr(p4clockmod_table, policy->cpu);
/* cpuinfo and default policy values */
- policy->cpuinfo.transition_latency = 1000000; /* assumed */
+
+ /* the transition latency is set to be 1 higher than the maximum
+ * transition latency of the ondemand governor */
+ policy->cpuinfo.transition_latency = 10000001;
policy->cur = stock_freq;
return cpufreq_frequency_table_cpuinfo(policy, &p4clockmod_table[0]);
@@ -258,12 +275,12 @@
l = DC_DISABLE;
if (l != DC_DISABLE)
- return (stock_freq * l / 8);
+ return stock_freq * l / 8;
return stock_freq;
}
-static struct freq_attr* p4clockmod_attr[] = {
+static struct freq_attr *p4clockmod_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};
@@ -298,9 +315,10 @@
ret = cpufreq_register_driver(&p4clockmod_driver);
if (!ret)
- printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock Modulation available\n");
+ printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock "
+ "Modulation available\n");
- return (ret);
+ return ret;
}
@@ -310,9 +328,9 @@
}
-MODULE_AUTHOR ("Zwane Mwaikambo <zwane@commfireservices.com>");
-MODULE_DESCRIPTION ("cpufreq driver for Pentium(TM) 4/Xeon(TM)");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
+MODULE_DESCRIPTION("cpufreq driver for Pentium(TM) 4/Xeon(TM)");
+MODULE_LICENSE("GPL");
late_initcall(cpufreq_p4_init);
module_exit(cpufreq_p4_exit);
diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k6.c b/arch/x86/kernel/cpu/cpufreq/powernow-k6.c
index c1ac579..f10dea4 100644
--- a/arch/x86/kernel/cpu/cpufreq/powernow-k6.c
+++ b/arch/x86/kernel/cpu/cpufreq/powernow-k6.c
@@ -1,6 +1,7 @@
/*
* This file was based upon code in Powertweak Linux (http://powertweak.sf.net)
- * (C) 2000-2003 Dave Jones, Arjan van de Ven, Janne Pänkälä, Dominik Brodowski.
+ * (C) 2000-2003 Dave Jones, Arjan van de Ven, Janne Pänkälä,
+ * Dominik Brodowski.
*
* Licensed under the terms of the GNU GPL License version 2.
*
@@ -13,14 +14,15 @@
#include <linux/cpufreq.h>
#include <linux/ioport.h>
#include <linux/slab.h>
-
-#include <asm/msr.h>
#include <linux/timex.h>
#include <linux/io.h>
+#include <asm/msr.h>
+
#define POWERNOW_IOPORT 0xfff0 /* it doesn't matter where, as long
as it is unused */
+#define PFX "powernow-k6: "
static unsigned int busfreq; /* FSB, in 10 kHz */
static unsigned int max_multiplier;
@@ -47,8 +49,8 @@
*/
static int powernow_k6_get_cpu_multiplier(void)
{
- u64 invalue = 0;
- u32 msrval;
+ u64 invalue = 0;
+ u32 msrval;
msrval = POWERNOW_IOPORT + 0x1;
wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
@@ -68,12 +70,12 @@
*/
static void powernow_k6_set_state(unsigned int best_i)
{
- unsigned long outvalue = 0, invalue = 0;
- unsigned long msrval;
- struct cpufreq_freqs freqs;
+ unsigned long outvalue = 0, invalue = 0;
+ unsigned long msrval;
+ struct cpufreq_freqs freqs;
if (clock_ratio[best_i].index > max_multiplier) {
- printk(KERN_ERR "cpufreq: invalid target frequency\n");
+ printk(KERN_ERR PFX "invalid target frequency\n");
return;
}
@@ -119,7 +121,8 @@
* powernow_k6_setpolicy - sets a new CPUFreq policy
* @policy: new policy
* @target_freq: the target frequency
- * @relation: how that frequency relates to achieved frequency (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
+ * @relation: how that frequency relates to achieved frequency
+ * (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
*
* sets a new CPUFreq policy
*/
@@ -127,9 +130,10 @@
unsigned int target_freq,
unsigned int relation)
{
- unsigned int newstate = 0;
+ unsigned int newstate = 0;
- if (cpufreq_frequency_table_target(policy, &clock_ratio[0], target_freq, relation, &newstate))
+ if (cpufreq_frequency_table_target(policy, &clock_ratio[0],
+ target_freq, relation, &newstate))
return -EINVAL;
powernow_k6_set_state(newstate);
@@ -140,7 +144,7 @@
static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
{
- unsigned int i;
+ unsigned int i, f;
int result;
if (policy->cpu != 0)
@@ -152,10 +156,11 @@
/* table init */
for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {
- if (clock_ratio[i].index > max_multiplier)
+ f = clock_ratio[i].index;
+ if (f > max_multiplier)
clock_ratio[i].frequency = CPUFREQ_ENTRY_INVALID;
else
- clock_ratio[i].frequency = busfreq * clock_ratio[i].index;
+ clock_ratio[i].frequency = busfreq * f;
}
/* cpuinfo and default policy values */
@@ -185,7 +190,9 @@
static unsigned int powernow_k6_get(unsigned int cpu)
{
- return busfreq * powernow_k6_get_cpu_multiplier();
+ unsigned int ret;
+ ret = (busfreq * powernow_k6_get_cpu_multiplier());
+ return ret;
}
static struct freq_attr *powernow_k6_attr[] = {
@@ -221,7 +228,7 @@
return -ENODEV;
if (!request_region(POWERNOW_IOPORT, 16, "PowerNow!")) {
- printk("cpufreq: PowerNow IOPORT region already used.\n");
+ printk(KERN_INFO PFX "PowerNow IOPORT region already used.\n");
return -EIO;
}
@@ -246,7 +253,8 @@
}
-MODULE_AUTHOR("Arjan van de Ven, Dave Jones <davej@redhat.com>, Dominik Brodowski <linux@brodo.de>");
+MODULE_AUTHOR("Arjan van de Ven, Dave Jones <davej@redhat.com>, "
+ "Dominik Brodowski <linux@brodo.de>");
MODULE_DESCRIPTION("PowerNow! driver for AMD K6-2+ / K6-3+ processors.");
MODULE_LICENSE("GPL");
diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k7.c b/arch/x86/kernel/cpu/cpufreq/powernow-k7.c
index 1b446d7..3c28ccd 100644
--- a/arch/x86/kernel/cpu/cpufreq/powernow-k7.c
+++ b/arch/x86/kernel/cpu/cpufreq/powernow-k7.c
@@ -6,10 +6,12 @@
* Licensed under the terms of the GNU GPL License version 2.
* Based upon datasheets & sample CPUs kindly provided by AMD.
*
- * Errata 5: Processor may fail to execute a FID/VID change in presence of interrupt.
- * - We cli/sti on stepping A0 CPUs around the FID/VID transition.
- * Errata 15: Processors with half frequency multipliers may hang upon wakeup from disconnect.
- * - We disable half multipliers if ACPI is used on A0 stepping CPUs.
+ * Errata 5:
+ * CPU may fail to execute a FID/VID change in presence of interrupt.
+ * - We cli/sti on stepping A0 CPUs around the FID/VID transition.
+ * Errata 15:
+ * CPU with half frequency multipliers may hang upon wakeup from disconnect.
+ * - We disable half multipliers if ACPI is used on A0 stepping CPUs.
*/
#include <linux/kernel.h>
@@ -20,11 +22,11 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/dmi.h>
+#include <linux/timex.h>
+#include <linux/io.h>
+#include <asm/timer.h> /* Needed for recalibrate_cpu_khz() */
#include <asm/msr.h>
-#include <asm/timer.h>
-#include <asm/timex.h>
-#include <asm/io.h>
#include <asm/system.h>
#ifdef CONFIG_X86_POWERNOW_K7_ACPI
@@ -58,9 +60,9 @@
union powernow_acpi_control_t {
struct {
unsigned long fid:5,
- vid:5,
- sgtc:20,
- res1:2;
+ vid:5,
+ sgtc:20,
+ res1:2;
} bits;
unsigned long val;
};
@@ -94,14 +96,15 @@
static unsigned int can_scale_bus;
static unsigned int can_scale_vid;
-static unsigned int minimum_speed=-1;
+static unsigned int minimum_speed = -1;
static unsigned int maximum_speed;
static unsigned int number_scales;
static unsigned int fsb;
static unsigned int latency;
static char have_a0;
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "powernow-k7", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+ "powernow-k7", msg)
static int check_fsb(unsigned int fsbspeed)
{
@@ -109,7 +112,7 @@
unsigned int f = fsb / 1000;
delta = (fsbspeed > f) ? fsbspeed - f : f - fsbspeed;
- return (delta < 5);
+ return delta < 5;
}
static int check_powernow(void)
@@ -117,24 +120,26 @@
struct cpuinfo_x86 *c = &cpu_data(0);
unsigned int maxei, eax, ebx, ecx, edx;
- if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 !=6)) {
+ if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 6)) {
#ifdef MODULE
- printk (KERN_INFO PFX "This module only works with AMD K7 CPUs\n");
+ printk(KERN_INFO PFX "This module only works with "
+ "AMD K7 CPUs\n");
#endif
return 0;
}
/* Get maximum capabilities */
- maxei = cpuid_eax (0x80000000);
+ maxei = cpuid_eax(0x80000000);
if (maxei < 0x80000007) { /* Any powernow info ? */
#ifdef MODULE
- printk (KERN_INFO PFX "No powernow capabilities detected\n");
+ printk(KERN_INFO PFX "No powernow capabilities detected\n");
#endif
return 0;
}
if ((c->x86_model == 6) && (c->x86_mask == 0)) {
- printk (KERN_INFO PFX "K7 660[A0] core detected, enabling errata workarounds\n");
+ printk(KERN_INFO PFX "K7 660[A0] core detected, "
+ "enabling errata workarounds\n");
have_a0 = 1;
}
@@ -144,37 +149,42 @@
if (!(edx & (1 << 1 | 1 << 2)))
return 0;
- printk (KERN_INFO PFX "PowerNOW! Technology present. Can scale: ");
+ printk(KERN_INFO PFX "PowerNOW! Technology present. Can scale: ");
if (edx & 1 << 1) {
- printk ("frequency");
- can_scale_bus=1;
+ printk("frequency");
+ can_scale_bus = 1;
}
if ((edx & (1 << 1 | 1 << 2)) == 0x6)
- printk (" and ");
+ printk(" and ");
if (edx & 1 << 2) {
- printk ("voltage");
- can_scale_vid=1;
+ printk("voltage");
+ can_scale_vid = 1;
}
- printk (".\n");
+ printk(".\n");
return 1;
}
+static void invalidate_entry(unsigned int entry)
+{
+ powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID;
+}
-static int get_ranges (unsigned char *pst)
+static int get_ranges(unsigned char *pst)
{
unsigned int j;
unsigned int speed;
u8 fid, vid;
- powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) * (number_scales + 1)), GFP_KERNEL);
+ powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) *
+ (number_scales + 1)), GFP_KERNEL);
if (!powernow_table)
return -ENOMEM;
- for (j=0 ; j < number_scales; j++) {
+ for (j = 0 ; j < number_scales; j++) {
fid = *pst++;
powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;
@@ -182,10 +192,10 @@
speed = powernow_table[j].frequency;
- if ((fid_codes[fid] % 10)==5) {
+ if ((fid_codes[fid] % 10) == 5) {
#ifdef CONFIG_X86_POWERNOW_K7_ACPI
if (have_a0 == 1)
- powernow_table[j].frequency = CPUFREQ_ENTRY_INVALID;
+ invalidate_entry(j);
#endif
}
@@ -197,7 +207,7 @@
vid = *pst++;
powernow_table[j].index |= (vid << 8); /* upper 8 bits */
- dprintk (" FID: 0x%x (%d.%dx [%dMHz]) "
+ dprintk(" FID: 0x%x (%d.%dx [%dMHz]) "
"VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
fid_codes[fid] % 10, speed/1000, vid,
mobile_vid_table[vid]/1000,
@@ -214,13 +224,13 @@
{
union msr_fidvidctl fidvidctl;
- rdmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
+ rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
if (fidvidctl.bits.FID != fid) {
fidvidctl.bits.SGTC = latency;
fidvidctl.bits.FID = fid;
fidvidctl.bits.VIDC = 0;
fidvidctl.bits.FIDC = 1;
- wrmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
+ wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
}
}
@@ -229,18 +239,18 @@
{
union msr_fidvidctl fidvidctl;
- rdmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
+ rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
if (fidvidctl.bits.VID != vid) {
fidvidctl.bits.SGTC = latency;
fidvidctl.bits.VID = vid;
fidvidctl.bits.FIDC = 0;
fidvidctl.bits.VIDC = 1;
- wrmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
+ wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
}
}
-static void change_speed (unsigned int index)
+static void change_speed(unsigned int index)
{
u8 fid, vid;
struct cpufreq_freqs freqs;
@@ -257,7 +267,7 @@
freqs.cpu = 0;
- rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
+ rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
cfid = fidvidstatus.bits.CFID;
freqs.old = fsb * fid_codes[cfid] / 10;
@@ -321,12 +331,14 @@
goto err1;
}
- if (acpi_processor_perf->control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) {
+ if (acpi_processor_perf->control_register.space_id !=
+ ACPI_ADR_SPACE_FIXED_HARDWARE) {
retval = -ENODEV;
goto err2;
}
- if (acpi_processor_perf->status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) {
+ if (acpi_processor_perf->status_register.space_id !=
+ ACPI_ADR_SPACE_FIXED_HARDWARE) {
retval = -ENODEV;
goto err2;
}
@@ -338,7 +350,8 @@
goto err2;
}
- powernow_table = kzalloc((number_scales + 1) * (sizeof(struct cpufreq_frequency_table)), GFP_KERNEL);
+ powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) *
+ (number_scales + 1)), GFP_KERNEL);
if (!powernow_table) {
retval = -ENOMEM;
goto err2;
@@ -352,7 +365,7 @@
unsigned int speed, speed_mhz;
pc.val = (unsigned long) state->control;
- dprintk ("acpi: P%d: %d MHz %d mW %d uS control %08x SGTC %d\n",
+ dprintk("acpi: P%d: %d MHz %d mW %d uS control %08x SGTC %d\n",
i,
(u32) state->core_frequency,
(u32) state->power,
@@ -381,12 +394,12 @@
if (speed % 1000 > 0)
speed_mhz++;
- if ((fid_codes[fid] % 10)==5) {
+ if ((fid_codes[fid] % 10) == 5) {
if (have_a0 == 1)
- powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+ invalidate_entry(i);
}
- dprintk (" FID: 0x%x (%d.%dx [%dMHz]) "
+ dprintk(" FID: 0x%x (%d.%dx [%dMHz]) "
"VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
fid_codes[fid] % 10, speed_mhz, vid,
mobile_vid_table[vid]/1000,
@@ -422,7 +435,8 @@
err05:
kfree(acpi_processor_perf);
err0:
- printk(KERN_WARNING PFX "ACPI perflib can not be used in this platform\n");
+ printk(KERN_WARNING PFX "ACPI perflib can not be used on "
+ "this platform\n");
acpi_processor_perf = NULL;
return retval;
}
@@ -435,7 +449,14 @@
}
#endif
-static int powernow_decode_bios (int maxfid, int startvid)
+static void print_pst_entry(struct pst_s *pst, unsigned int j)
+{
+ dprintk("PST:%d (@%p)\n", j, pst);
+ dprintk(" cpuid: 0x%x fsb: %d maxFID: 0x%x startvid: 0x%x\n",
+ pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid);
+}
+
+static int powernow_decode_bios(int maxfid, int startvid)
{
struct psb_s *psb;
struct pst_s *pst;
@@ -446,61 +467,67 @@
etuple = cpuid_eax(0x80000001);
- for (i=0xC0000; i < 0xffff0 ; i+=16) {
+ for (i = 0xC0000; i < 0xffff0 ; i += 16) {
p = phys_to_virt(i);
- if (memcmp(p, "AMDK7PNOW!", 10) == 0){
- dprintk ("Found PSB header at %p\n", p);
+ if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
+ dprintk("Found PSB header at %p\n", p);
psb = (struct psb_s *) p;
- dprintk ("Table version: 0x%x\n", psb->tableversion);
+ dprintk("Table version: 0x%x\n", psb->tableversion);
if (psb->tableversion != 0x12) {
- printk (KERN_INFO PFX "Sorry, only v1.2 tables supported right now\n");
+ printk(KERN_INFO PFX "Sorry, only v1.2 tables"
+ " supported right now\n");
return -ENODEV;
}
- dprintk ("Flags: 0x%x\n", psb->flags);
- if ((psb->flags & 1)==0) {
- dprintk ("Mobile voltage regulator\n");
- } else {
- dprintk ("Desktop voltage regulator\n");
- }
+ dprintk("Flags: 0x%x\n", psb->flags);
+ if ((psb->flags & 1) == 0)
+ dprintk("Mobile voltage regulator\n");
+ else
+ dprintk("Desktop voltage regulator\n");
latency = psb->settlingtime;
if (latency < 100) {
- printk(KERN_INFO PFX "BIOS set settling time to %d microseconds. "
- "Should be at least 100. Correcting.\n", latency);
+ printk(KERN_INFO PFX "BIOS set settling time "
+ "to %d microseconds. "
+ "Should be at least 100. "
+ "Correcting.\n", latency);
latency = 100;
}
- dprintk ("Settling Time: %d microseconds.\n", psb->settlingtime);
- dprintk ("Has %d PST tables. (Only dumping ones relevant to this CPU).\n", psb->numpst);
+ dprintk("Settling Time: %d microseconds.\n",
+ psb->settlingtime);
+ dprintk("Has %d PST tables. (Only dumping ones "
+ "relevant to this CPU).\n",
+ psb->numpst);
- p += sizeof (struct psb_s);
+ p += sizeof(struct psb_s);
pst = (struct pst_s *) p;
- for (j=0; j<psb->numpst; j++) {
+ for (j = 0; j < psb->numpst; j++) {
pst = (struct pst_s *) p;
number_scales = pst->numpstates;
- if ((etuple == pst->cpuid) && check_fsb(pst->fsbspeed) &&
- (maxfid==pst->maxfid) && (startvid==pst->startvid))
- {
- dprintk ("PST:%d (@%p)\n", j, pst);
- dprintk (" cpuid: 0x%x fsb: %d maxFID: 0x%x startvid: 0x%x\n",
- pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid);
-
- ret = get_ranges ((char *) pst + sizeof (struct pst_s));
+ if ((etuple == pst->cpuid) &&
+ check_fsb(pst->fsbspeed) &&
+ (maxfid == pst->maxfid) &&
+ (startvid == pst->startvid)) {
+ print_pst_entry(pst, j);
+ p = (char *)pst + sizeof(struct pst_s);
+ ret = get_ranges(p);
return ret;
} else {
unsigned int k;
- p = (char *) pst + sizeof (struct pst_s);
- for (k=0; k<number_scales; k++)
- p+=2;
+ p = (char *)pst + sizeof(struct pst_s);
+ for (k = 0; k < number_scales; k++)
+ p += 2;
}
}
- printk (KERN_INFO PFX "No PST tables match this cpuid (0x%x)\n", etuple);
- printk (KERN_INFO PFX "This is indicative of a broken BIOS.\n");
+ printk(KERN_INFO PFX "No PST tables match this cpuid "
+ "(0x%x)\n", etuple);
+ printk(KERN_INFO PFX "This is indicative of a broken "
+ "BIOS.\n");
return -EINVAL;
}
@@ -511,13 +538,14 @@
}
-static int powernow_target (struct cpufreq_policy *policy,
+static int powernow_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
unsigned int newstate;
- if (cpufreq_frequency_table_target(policy, powernow_table, target_freq, relation, &newstate))
+ if (cpufreq_frequency_table_target(policy, powernow_table, target_freq,
+ relation, &newstate))
return -EINVAL;
change_speed(newstate);
@@ -526,7 +554,7 @@
}
-static int powernow_verify (struct cpufreq_policy *policy)
+static int powernow_verify(struct cpufreq_policy *policy)
{
return cpufreq_frequency_table_verify(policy, powernow_table);
}
@@ -566,18 +594,23 @@
if (cpu)
return 0;
- rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
+ rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
cfid = fidvidstatus.bits.CFID;
- return (fsb * fid_codes[cfid] / 10);
+ return fsb * fid_codes[cfid] / 10;
}
static int __init acer_cpufreq_pst(const struct dmi_system_id *d)
{
- printk(KERN_WARNING "%s laptop with broken PST tables in BIOS detected.\n", d->ident);
- printk(KERN_WARNING "You need to downgrade to 3A21 (09/09/2002), or try a newer BIOS than 3A71 (01/20/2003)\n");
- printk(KERN_WARNING "cpufreq scaling has been disabled as a result of this.\n");
+ printk(KERN_WARNING PFX
+ "%s laptop with broken PST tables in BIOS detected.\n",
+ d->ident);
+ printk(KERN_WARNING PFX
+ "You need to downgrade to 3A21 (09/09/2002), or try a newer "
+ "BIOS than 3A71 (01/20/2003)\n");
+ printk(KERN_WARNING PFX
+ "cpufreq scaling has been disabled as a result of this.\n");
return 0;
}
@@ -598,7 +631,7 @@
{ }
};
-static int __init powernow_cpu_init (struct cpufreq_policy *policy)
+static int __init powernow_cpu_init(struct cpufreq_policy *policy)
{
union msr_fidvidstatus fidvidstatus;
int result;
@@ -606,7 +639,7 @@
if (policy->cpu != 0)
return -ENODEV;
- rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
+ rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
recalibrate_cpu_khz();
@@ -618,19 +651,21 @@
dprintk("FSB: %3dMHz\n", fsb/1000);
if (dmi_check_system(powernow_dmi_table) || acpi_force) {
- printk (KERN_INFO PFX "PSB/PST known to be broken. Trying ACPI instead\n");
+ printk(KERN_INFO PFX "PSB/PST known to be broken. "
+ "Trying ACPI instead\n");
result = powernow_acpi_init();
} else {
- result = powernow_decode_bios(fidvidstatus.bits.MFID, fidvidstatus.bits.SVID);
+ result = powernow_decode_bios(fidvidstatus.bits.MFID,
+ fidvidstatus.bits.SVID);
if (result) {
- printk (KERN_INFO PFX "Trying ACPI perflib\n");
+ printk(KERN_INFO PFX "Trying ACPI perflib\n");
maximum_speed = 0;
minimum_speed = -1;
latency = 0;
result = powernow_acpi_init();
if (result) {
- printk (KERN_INFO PFX "ACPI and legacy methods failed\n");
- printk (KERN_INFO PFX "See http://www.codemonkey.org.uk/projects/cpufreq/powernow-k7.html\n");
+ printk(KERN_INFO PFX
+ "ACPI and legacy methods failed\n");
}
} else {
/* SGTC use the bus clock as timer */
@@ -642,10 +677,11 @@
if (result)
return result;
- printk (KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n",
+ printk(KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n",
minimum_speed/1000, maximum_speed/1000);
- policy->cpuinfo.transition_latency = cpufreq_scale(2000000UL, fsb, latency);
+ policy->cpuinfo.transition_latency =
+ cpufreq_scale(2000000UL, fsb, latency);
policy->cur = powernow_get(0);
@@ -654,7 +690,8 @@
return cpufreq_frequency_table_cpuinfo(policy, powernow_table);
}
-static int powernow_cpu_exit (struct cpufreq_policy *policy) {
+static int powernow_cpu_exit(struct cpufreq_policy *policy)
+{
cpufreq_frequency_table_put_attr(policy->cpu);
#ifdef CONFIG_X86_POWERNOW_K7_ACPI
@@ -669,7 +706,7 @@
return 0;
}
-static struct freq_attr* powernow_table_attr[] = {
+static struct freq_attr *powernow_table_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};
@@ -685,15 +722,15 @@
.attr = powernow_table_attr,
};
-static int __init powernow_init (void)
+static int __init powernow_init(void)
{
- if (check_powernow()==0)
+ if (check_powernow() == 0)
return -ENODEV;
return cpufreq_register_driver(&powernow_driver);
}
-static void __exit powernow_exit (void)
+static void __exit powernow_exit(void)
{
cpufreq_unregister_driver(&powernow_driver);
}
@@ -701,9 +738,9 @@
module_param(acpi_force, int, 0444);
MODULE_PARM_DESC(acpi_force, "Force ACPI to be used.");
-MODULE_AUTHOR ("Dave Jones <davej@redhat.com>");
-MODULE_DESCRIPTION ("Powernow driver for AMD K7 processors.");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
+MODULE_DESCRIPTION("Powernow driver for AMD K7 processors.");
+MODULE_LICENSE("GPL");
late_initcall(powernow_init);
module_exit(powernow_exit);
diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c
index 6428aa1..a15ac94 100644
--- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c
+++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c
@@ -33,16 +33,14 @@
#include <linux/string.h>
#include <linux/cpumask.h>
#include <linux/sched.h> /* for current / set_cpus_allowed() */
+#include <linux/io.h>
+#include <linux/delay.h>
#include <asm/msr.h>
-#include <asm/io.h>
-#include <asm/delay.h>
-#ifdef CONFIG_X86_POWERNOW_K8_ACPI
#include <linux/acpi.h>
#include <linux/mutex.h>
#include <acpi/processor.h>
-#endif
#define PFX "powernow-k8: "
#define VERSION "version 2.20.00"
@@ -71,7 +69,8 @@
return 1000 * find_freq_from_fid(fid);
}
-static u32 find_khz_freq_from_pstate(struct cpufreq_frequency_table *data, u32 pstate)
+static u32 find_khz_freq_from_pstate(struct cpufreq_frequency_table *data,
+ u32 pstate)
{
return data[pstate].frequency;
}
@@ -186,7 +185,9 @@
return 1;
}
- lo = fid | (data->currvid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
+ lo = fid;
+ lo |= (data->currvid << MSR_C_LO_VID_SHIFT);
+ lo |= MSR_C_LO_INIT_FID_VID;
dprintk("writing fid 0x%x, lo 0x%x, hi 0x%x\n",
fid, lo, data->plllock * PLL_LOCK_CONVERSION);
@@ -194,7 +195,9 @@
do {
wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);
if (i++ > 100) {
- printk(KERN_ERR PFX "Hardware error - pending bit very stuck - no further pstate changes possible\n");
+ printk(KERN_ERR PFX
+ "Hardware error - pending bit very stuck - "
+ "no further pstate changes possible\n");
return 1;
}
} while (query_current_values_with_pending_wait(data));
@@ -202,14 +205,16 @@
count_off_irt(data);
if (savevid != data->currvid) {
- printk(KERN_ERR PFX "vid change on fid trans, old 0x%x, new 0x%x\n",
- savevid, data->currvid);
+ printk(KERN_ERR PFX
+ "vid change on fid trans, old 0x%x, new 0x%x\n",
+ savevid, data->currvid);
return 1;
}
if (fid != data->currfid) {
- printk(KERN_ERR PFX "fid trans failed, fid 0x%x, curr 0x%x\n", fid,
- data->currfid);
+ printk(KERN_ERR PFX
+ "fid trans failed, fid 0x%x, curr 0x%x\n", fid,
+ data->currfid);
return 1;
}
@@ -228,7 +233,9 @@
return 1;
}
- lo = data->currfid | (vid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;
+ lo = data->currfid;
+ lo |= (vid << MSR_C_LO_VID_SHIFT);
+ lo |= MSR_C_LO_INIT_FID_VID;
dprintk("writing vid 0x%x, lo 0x%x, hi 0x%x\n",
vid, lo, STOP_GRANT_5NS);
@@ -236,20 +243,24 @@
do {
wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);
if (i++ > 100) {
- printk(KERN_ERR PFX "internal error - pending bit very stuck - no further pstate changes possible\n");
+ printk(KERN_ERR PFX "internal error - pending bit "
+ "very stuck - no further pstate "
+ "changes possible\n");
return 1;
}
} while (query_current_values_with_pending_wait(data));
if (savefid != data->currfid) {
- printk(KERN_ERR PFX "fid changed on vid trans, old 0x%x new 0x%x\n",
+ printk(KERN_ERR PFX "fid changed on vid trans, old "
+ "0x%x new 0x%x\n",
savefid, data->currfid);
return 1;
}
if (vid != data->currvid) {
- printk(KERN_ERR PFX "vid trans failed, vid 0x%x, curr 0x%x\n", vid,
- data->currvid);
+ printk(KERN_ERR PFX "vid trans failed, vid 0x%x, "
+ "curr 0x%x\n",
+ vid, data->currvid);
return 1;
}
@@ -261,7 +272,8 @@
* Decreasing vid codes represent increasing voltages:
* vid of 0 is 1.550V, vid of 0x1e is 0.800V, vid of VID_OFF is off.
*/
-static int decrease_vid_code_by_step(struct powernow_k8_data *data, u32 reqvid, u32 step)
+static int decrease_vid_code_by_step(struct powernow_k8_data *data,
+ u32 reqvid, u32 step)
{
if ((data->currvid - reqvid) > step)
reqvid = data->currvid - step;
@@ -283,7 +295,8 @@
}
/* Change Opteron/Athlon64 fid and vid, by the 3 phases. */
-static int transition_fid_vid(struct powernow_k8_data *data, u32 reqfid, u32 reqvid)
+static int transition_fid_vid(struct powernow_k8_data *data,
+ u32 reqfid, u32 reqvid)
{
if (core_voltage_pre_transition(data, reqvid))
return 1;
@@ -298,7 +311,8 @@
return 1;
if ((reqfid != data->currfid) || (reqvid != data->currvid)) {
- printk(KERN_ERR PFX "failed (cpu%d): req 0x%x 0x%x, curr 0x%x 0x%x\n",
+ printk(KERN_ERR PFX "failed (cpu%d): req 0x%x 0x%x, "
+ "curr 0x%x 0x%x\n",
smp_processor_id(),
reqfid, reqvid, data->currfid, data->currvid);
return 1;
@@ -311,13 +325,15 @@
}
/* Phase 1 - core voltage transition ... setup voltage */
-static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid)
+static int core_voltage_pre_transition(struct powernow_k8_data *data,
+ u32 reqvid)
{
u32 rvosteps = data->rvo;
u32 savefid = data->currfid;
u32 maxvid, lo;
- dprintk("ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo 0x%x\n",
+ dprintk("ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, "
+ "reqvid 0x%x, rvo 0x%x\n",
smp_processor_id(),
data->currfid, data->currvid, reqvid, data->rvo);
@@ -340,7 +356,7 @@
} else {
dprintk("ph1: changing vid for rvo, req 0x%x\n",
data->currvid - 1);
- if (decrease_vid_code_by_step(data, data->currvid - 1, 1))
+ if (decrease_vid_code_by_step(data, data->currvid-1, 1))
return 1;
rvosteps--;
}
@@ -350,7 +366,8 @@
return 1;
if (savefid != data->currfid) {
- printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n", data->currfid);
+ printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n",
+ data->currfid);
return 1;
}
@@ -363,20 +380,24 @@
/* Phase 2 - core frequency transition */
static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
{
- u32 vcoreqfid, vcocurrfid, vcofiddiff, fid_interval, savevid = data->currvid;
+ u32 vcoreqfid, vcocurrfid, vcofiddiff;
+ u32 fid_interval, savevid = data->currvid;
- if ((reqfid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
- printk(KERN_ERR PFX "ph2: illegal lo-lo transition 0x%x 0x%x\n",
- reqfid, data->currfid);
+ if ((reqfid < HI_FID_TABLE_BOTTOM) &&
+ (data->currfid < HI_FID_TABLE_BOTTOM)) {
+ printk(KERN_ERR PFX "ph2: illegal lo-lo transition "
+ "0x%x 0x%x\n", reqfid, data->currfid);
return 1;
}
if (data->currfid == reqfid) {
- printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n", data->currfid);
+ printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n",
+ data->currfid);
return 0;
}
- dprintk("ph2 (cpu%d): starting, currfid 0x%x, currvid 0x%x, reqfid 0x%x\n",
+ dprintk("ph2 (cpu%d): starting, currfid 0x%x, currvid 0x%x, "
+ "reqfid 0x%x\n",
smp_processor_id(),
data->currfid, data->currvid, reqfid);
@@ -390,14 +411,14 @@
if (reqfid > data->currfid) {
if (data->currfid > LO_FID_TABLE_TOP) {
- if (write_new_fid(data, data->currfid + fid_interval)) {
+ if (write_new_fid(data,
+ data->currfid + fid_interval))
return 1;
- }
} else {
if (write_new_fid
- (data, 2 + convert_fid_to_vco_fid(data->currfid))) {
+ (data,
+ 2 + convert_fid_to_vco_fid(data->currfid)))
return 1;
- }
}
} else {
if (write_new_fid(data, data->currfid - fid_interval))
@@ -417,7 +438,8 @@
if (data->currfid != reqfid) {
printk(KERN_ERR PFX
- "ph2: mismatch, failed fid transition, curr 0x%x, req 0x%x\n",
+ "ph2: mismatch, failed fid transition, "
+ "curr 0x%x, req 0x%x\n",
data->currfid, reqfid);
return 1;
}
@@ -435,7 +457,8 @@
}
/* Phase 3 - core voltage transition flow ... jump to the final vid. */
-static int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid)
+static int core_voltage_post_transition(struct powernow_k8_data *data,
+ u32 reqvid)
{
u32 savefid = data->currfid;
u32 savereqvid = reqvid;
@@ -457,7 +480,8 @@
if (data->currvid != reqvid) {
printk(KERN_ERR PFX
- "ph3: failed vid transition\n, req 0x%x, curr 0x%x",
+ "ph3: failed vid transition\n, "
+ "req 0x%x, curr 0x%x",
reqvid, data->currvid);
return 1;
}
@@ -508,7 +532,8 @@
if ((eax & CPUID_XFAM) == CPUID_XFAM_K8) {
if (((eax & CPUID_USE_XFAM_XMOD) != CPUID_USE_XFAM_XMOD) ||
((eax & CPUID_XMOD) > CPUID_XMOD_REV_MASK)) {
- printk(KERN_INFO PFX "Processor cpuid %x not supported\n", eax);
+ printk(KERN_INFO PFX
+ "Processor cpuid %x not supported\n", eax);
goto out;
}
@@ -520,8 +545,10 @@
}
cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
- if ((edx & P_STATE_TRANSITION_CAPABLE) != P_STATE_TRANSITION_CAPABLE) {
- printk(KERN_INFO PFX "Power state transitions not supported\n");
+ if ((edx & P_STATE_TRANSITION_CAPABLE)
+ != P_STATE_TRANSITION_CAPABLE) {
+ printk(KERN_INFO PFX
+ "Power state transitions not supported\n");
goto out;
}
} else { /* must be a HW Pstate capable processor */
@@ -539,7 +566,8 @@
return rc;
}
-static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
+static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst,
+ u8 maxvid)
{
unsigned int j;
u8 lastfid = 0xff;
@@ -550,12 +578,14 @@
j, pst[j].vid);
return -EINVAL;
}
- if (pst[j].vid < data->rvo) { /* vid + rvo >= 0 */
+ if (pst[j].vid < data->rvo) {
+ /* vid + rvo >= 0 */
printk(KERN_ERR FW_BUG PFX "0 vid exceeded with pstate"
" %d\n", j);
return -ENODEV;
}
- if (pst[j].vid < maxvid + data->rvo) { /* vid + rvo >= maxvid */
+ if (pst[j].vid < maxvid + data->rvo) {
+ /* vid + rvo >= maxvid */
printk(KERN_ERR FW_BUG PFX "maxvid exceeded with pstate"
" %d\n", j);
return -ENODEV;
@@ -579,23 +609,31 @@
return -EINVAL;
}
if (lastfid > LO_FID_TABLE_TOP)
- printk(KERN_INFO FW_BUG PFX "first fid not from lo freq table\n");
+ printk(KERN_INFO FW_BUG PFX
+ "first fid not from lo freq table\n");
return 0;
}
+static void invalidate_entry(struct powernow_k8_data *data, unsigned int entry)
+{
+ data->powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID;
+}
+
static void print_basics(struct powernow_k8_data *data)
{
int j;
for (j = 0; j < data->numps; j++) {
- if (data->powernow_table[j].frequency != CPUFREQ_ENTRY_INVALID) {
+ if (data->powernow_table[j].frequency !=
+ CPUFREQ_ENTRY_INVALID) {
if (cpu_family == CPU_HW_PSTATE) {
- printk(KERN_INFO PFX " %d : pstate %d (%d MHz)\n",
- j,
+ printk(KERN_INFO PFX
+ " %d : pstate %d (%d MHz)\n", j,
data->powernow_table[j].index,
data->powernow_table[j].frequency/1000);
} else {
- printk(KERN_INFO PFX " %d : fid 0x%x (%d MHz), vid 0x%x\n",
+ printk(KERN_INFO PFX
+ " %d : fid 0x%x (%d MHz), vid 0x%x\n",
j,
data->powernow_table[j].index & 0xff,
data->powernow_table[j].frequency/1000,
@@ -604,20 +642,25 @@
}
}
if (data->batps)
- printk(KERN_INFO PFX "Only %d pstates on battery\n", data->batps);
+ printk(KERN_INFO PFX "Only %d pstates on battery\n",
+ data->batps);
}
-static int fill_powernow_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid)
+static int fill_powernow_table(struct powernow_k8_data *data,
+ struct pst_s *pst, u8 maxvid)
{
struct cpufreq_frequency_table *powernow_table;
unsigned int j;
- if (data->batps) { /* use ACPI support to get full speed on mains power */
- printk(KERN_WARNING PFX "Only %d pstates usable (use ACPI driver for full range\n", data->batps);
+ if (data->batps) {
+ /* use ACPI support to get full speed on mains power */
+ printk(KERN_WARNING PFX
+ "Only %d pstates usable (use ACPI driver for full "
+ "range\n", data->batps);
data->numps = data->batps;
}
- for ( j=1; j<data->numps; j++ ) {
+ for (j = 1; j < data->numps; j++) {
if (pst[j-1].fid >= pst[j].fid) {
printk(KERN_ERR PFX "PST out of sequence\n");
return -EINVAL;
@@ -640,9 +683,11 @@
}
for (j = 0; j < data->numps; j++) {
+ int freq;
powernow_table[j].index = pst[j].fid; /* lower 8 bits */
powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */
- powernow_table[j].frequency = find_khz_freq_from_fid(pst[j].fid);
+ freq = find_khz_freq_from_fid(pst[j].fid);
+ powernow_table[j].frequency = freq;
}
powernow_table[data->numps].frequency = CPUFREQ_TABLE_END;
powernow_table[data->numps].index = 0;
@@ -658,7 +703,8 @@
print_basics(data);
for (j = 0; j < data->numps; j++)
- if ((pst[j].fid==data->currfid) && (pst[j].vid==data->currvid))
+ if ((pst[j].fid == data->currfid) &&
+ (pst[j].vid == data->currvid))
return 0;
dprintk("currfid/vid do not match PST, ignoring\n");
@@ -698,7 +744,8 @@
}
data->vstable = psb->vstable;
- dprintk("voltage stabilization time: %d(*20us)\n", data->vstable);
+ dprintk("voltage stabilization time: %d(*20us)\n",
+ data->vstable);
dprintk("flags2: 0x%x\n", psb->flags2);
data->rvo = psb->flags2 & 3;
@@ -713,11 +760,12 @@
dprintk("numpst: 0x%x\n", psb->num_tables);
cpst = psb->num_tables;
- if ((psb->cpuid == 0x00000fc0) || (psb->cpuid == 0x00000fe0) ){
+ if ((psb->cpuid == 0x00000fc0) ||
+ (psb->cpuid == 0x00000fe0)) {
thiscpuid = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
- if ((thiscpuid == 0x00000fc0) || (thiscpuid == 0x00000fe0) ) {
+ if ((thiscpuid == 0x00000fc0) ||
+ (thiscpuid == 0x00000fe0))
cpst = 1;
- }
}
if (cpst != 1) {
printk(KERN_ERR FW_BUG PFX "numpst must be 1\n");
@@ -732,7 +780,8 @@
data->numps = psb->numps;
dprintk("numpstates: 0x%x\n", data->numps);
- return fill_powernow_table(data, (struct pst_s *)(psb+1), maxvid);
+ return fill_powernow_table(data,
+ (struct pst_s *)(psb+1), maxvid);
}
/*
* If you see this message, complain to BIOS manufacturer. If
@@ -745,28 +794,31 @@
* BIOS and Kernel Developer's Guide, which is available on
* www.amd.com
*/
- printk(KERN_ERR PFX "BIOS error - no PSB or ACPI _PSS objects\n");
+ printk(KERN_ERR FW_BUG PFX "No PSB or ACPI _PSS objects\n");
return -ENODEV;
}
-#ifdef CONFIG_X86_POWERNOW_K8_ACPI
-static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index)
+static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data,
+ unsigned int index)
{
+ acpi_integer control;
+
if (!data->acpi_data.state_count || (cpu_family == CPU_HW_PSTATE))
return;
- data->irt = (data->acpi_data.states[index].control >> IRT_SHIFT) & IRT_MASK;
- data->rvo = (data->acpi_data.states[index].control >> RVO_SHIFT) & RVO_MASK;
- data->exttype = (data->acpi_data.states[index].control >> EXT_TYPE_SHIFT) & EXT_TYPE_MASK;
- data->plllock = (data->acpi_data.states[index].control >> PLL_L_SHIFT) & PLL_L_MASK;
- data->vidmvs = 1 << ((data->acpi_data.states[index].control >> MVS_SHIFT) & MVS_MASK);
- data->vstable = (data->acpi_data.states[index].control >> VST_SHIFT) & VST_MASK;
-}
+ control = data->acpi_data.states[index].control; data->irt = (control
+ >> IRT_SHIFT) & IRT_MASK; data->rvo = (control >>
+ RVO_SHIFT) & RVO_MASK; data->exttype = (control
+ >> EXT_TYPE_SHIFT) & EXT_TYPE_MASK;
+ data->plllock = (control >> PLL_L_SHIFT) & PLL_L_MASK; data->vidmvs = 1
+ << ((control >> MVS_SHIFT) & MVS_MASK); data->vstable =
+ (control >> VST_SHIFT) & VST_MASK; }
static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
{
struct cpufreq_frequency_table *powernow_table;
int ret_val = -ENODEV;
+ acpi_integer space_id;
if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) {
dprintk("register performance failed: bad ACPI data\n");
@@ -779,11 +831,12 @@
goto err_out;
}
- if ((data->acpi_data.control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) ||
- (data->acpi_data.status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) {
+ space_id = data->acpi_data.control_register.space_id;
+ if ((space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) ||
+ (space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) {
dprintk("Invalid control/status registers (%x - %x)\n",
data->acpi_data.control_register.space_id,
- data->acpi_data.status_register.space_id);
+ space_id);
goto err_out;
}
@@ -802,7 +855,8 @@
if (ret_val)
goto err_out_mem;
- powernow_table[data->acpi_data.state_count].frequency = CPUFREQ_TABLE_END;
+ powernow_table[data->acpi_data.state_count].frequency =
+ CPUFREQ_TABLE_END;
powernow_table[data->acpi_data.state_count].index = 0;
data->powernow_table = powernow_table;
@@ -830,13 +884,15 @@
err_out:
acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
- /* data->acpi_data.state_count informs us at ->exit() whether ACPI was used */
+ /* data->acpi_data.state_count informs us at ->exit()
+ * whether ACPI was used */
data->acpi_data.state_count = 0;
return ret_val;
}
-static int fill_powernow_table_pstate(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table)
+static int fill_powernow_table_pstate(struct powernow_k8_data *data,
+ struct cpufreq_frequency_table *powernow_table)
{
int i;
u32 hi = 0, lo = 0;
@@ -848,84 +904,101 @@
index = data->acpi_data.states[i].control & HW_PSTATE_MASK;
if (index > data->max_hw_pstate) {
- printk(KERN_ERR PFX "invalid pstate %d - bad value %d.\n", i, index);
- printk(KERN_ERR PFX "Please report to BIOS manufacturer\n");
- powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+ printk(KERN_ERR PFX "invalid pstate %d - "
+ "bad value %d.\n", i, index);
+ printk(KERN_ERR PFX "Please report to BIOS "
+ "manufacturer\n");
+ invalidate_entry(data, i);
continue;
}
rdmsr(MSR_PSTATE_DEF_BASE + index, lo, hi);
if (!(hi & HW_PSTATE_VALID_MASK)) {
dprintk("invalid pstate %d, ignoring\n", index);
- powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+ invalidate_entry(data, i);
continue;
}
powernow_table[i].index = index;
- powernow_table[i].frequency = data->acpi_data.states[i].core_frequency * 1000;
+ powernow_table[i].frequency =
+ data->acpi_data.states[i].core_frequency * 1000;
}
return 0;
}
-static int fill_powernow_table_fidvid(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table)
+static int fill_powernow_table_fidvid(struct powernow_k8_data *data,
+ struct cpufreq_frequency_table *powernow_table)
{
int i;
int cntlofreq = 0;
+
for (i = 0; i < data->acpi_data.state_count; i++) {
u32 fid;
u32 vid;
+ u32 freq, index;
+ acpi_integer status, control;
if (data->exttype) {
- fid = data->acpi_data.states[i].status & EXT_FID_MASK;
- vid = (data->acpi_data.states[i].status >> VID_SHIFT) & EXT_VID_MASK;
+ status = data->acpi_data.states[i].status;
+ fid = status & EXT_FID_MASK;
+ vid = (status >> VID_SHIFT) & EXT_VID_MASK;
} else {
- fid = data->acpi_data.states[i].control & FID_MASK;
- vid = (data->acpi_data.states[i].control >> VID_SHIFT) & VID_MASK;
+ control = data->acpi_data.states[i].control;
+ fid = control & FID_MASK;
+ vid = (control >> VID_SHIFT) & VID_MASK;
}
dprintk(" %d : fid 0x%x, vid 0x%x\n", i, fid, vid);
- powernow_table[i].index = fid; /* lower 8 bits */
- powernow_table[i].index |= (vid << 8); /* upper 8 bits */
- powernow_table[i].frequency = find_khz_freq_from_fid(fid);
+ index = fid | (vid<<8);
+ powernow_table[i].index = index;
+
+ freq = find_khz_freq_from_fid(fid);
+ powernow_table[i].frequency = freq;
/* verify frequency is OK */
- if ((powernow_table[i].frequency > (MAX_FREQ * 1000)) ||
- (powernow_table[i].frequency < (MIN_FREQ * 1000))) {
- dprintk("invalid freq %u kHz, ignoring\n", powernow_table[i].frequency);
- powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+ if ((freq > (MAX_FREQ * 1000)) || (freq < (MIN_FREQ * 1000))) {
+ dprintk("invalid freq %u kHz, ignoring\n", freq);
+ invalidate_entry(data, i);
continue;
}
- /* verify voltage is OK - BIOSs are using "off" to indicate invalid */
+ /* verify voltage is OK -
+ * BIOSs are using "off" to indicate invalid */
if (vid == VID_OFF) {
dprintk("invalid vid %u, ignoring\n", vid);
- powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+ invalidate_entry(data, i);
continue;
}
/* verify only 1 entry from the lo frequency table */
if (fid < HI_FID_TABLE_BOTTOM) {
if (cntlofreq) {
- /* if both entries are the same, ignore this one ... */
- if ((powernow_table[i].frequency != powernow_table[cntlofreq].frequency) ||
- (powernow_table[i].index != powernow_table[cntlofreq].index)) {
- printk(KERN_ERR PFX "Too many lo freq table entries\n");
+ /* if both entries are the same,
+ * ignore this one ... */
+ if ((freq != powernow_table[cntlofreq].frequency) ||
+ (index != powernow_table[cntlofreq].index)) {
+ printk(KERN_ERR PFX
+ "Too many lo freq table "
+ "entries\n");
return 1;
}
- dprintk("double low frequency table entry, ignoring it.\n");
- powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+ dprintk("double low frequency table entry, "
+ "ignoring it.\n");
+ invalidate_entry(data, i);
continue;
} else
cntlofreq = i;
}
- if (powernow_table[i].frequency != (data->acpi_data.states[i].core_frequency * 1000)) {
- printk(KERN_INFO PFX "invalid freq entries %u kHz vs. %u kHz\n",
- powernow_table[i].frequency,
- (unsigned int) (data->acpi_data.states[i].core_frequency * 1000));
- powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+ if (freq != (data->acpi_data.states[i].core_frequency * 1000)) {
+ printk(KERN_INFO PFX "invalid freq entries "
+ "%u kHz vs. %u kHz\n", freq,
+ (unsigned int)
+ (data->acpi_data.states[i].core_frequency
+ * 1000));
+ invalidate_entry(data, i);
continue;
}
}
@@ -935,7 +1008,8 @@
static void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data)
{
if (data->acpi_data.state_count)
- acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
+ acpi_processor_unregister_performance(&data->acpi_data,
+ data->cpu);
free_cpumask_var(data->acpi_data.shared_cpu_map);
}
@@ -953,15 +1027,9 @@
return 1000 * max_latency;
}
-#else
-static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) { return -ENODEV; }
-static void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data) { return; }
-static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index) { return; }
-static int get_transition_latency(struct powernow_k8_data *data) { return 0; }
-#endif /* CONFIG_X86_POWERNOW_K8_ACPI */
-
/* Take a frequency, and issue the fid/vid transition command */
-static int transition_frequency_fidvid(struct powernow_k8_data *data, unsigned int index)
+static int transition_frequency_fidvid(struct powernow_k8_data *data,
+ unsigned int index)
{
u32 fid = 0;
u32 vid = 0;
@@ -989,7 +1057,8 @@
return 0;
}
- if ((fid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
+ if ((fid < HI_FID_TABLE_BOTTOM) &&
+ (data->currfid < HI_FID_TABLE_BOTTOM)) {
printk(KERN_ERR PFX
"ignoring illegal change in lo freq table-%x to 0x%x\n",
data->currfid, fid);
@@ -1017,7 +1086,8 @@
}
/* Take a frequency, and issue the hardware pstate transition command */
-static int transition_frequency_pstate(struct powernow_k8_data *data, unsigned int index)
+static int transition_frequency_pstate(struct powernow_k8_data *data,
+ unsigned int index)
{
u32 pstate = 0;
int res, i;
@@ -1029,7 +1099,8 @@
pstate = index & HW_PSTATE_MASK;
if (pstate > data->max_hw_pstate)
return 0;
- freqs.old = find_khz_freq_from_pstate(data->powernow_table, data->currpstate);
+ freqs.old = find_khz_freq_from_pstate(data->powernow_table,
+ data->currpstate);
freqs.new = find_khz_freq_from_pstate(data->powernow_table, pstate);
for_each_cpu_mask_nr(i, *(data->available_cores)) {
@@ -1048,7 +1119,8 @@
}
/* Driver entry point to switch to the target frequency */
-static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned relation)
+static int powernowk8_target(struct cpufreq_policy *pol,
+ unsigned targfreq, unsigned relation)
{
cpumask_t oldmask;
struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu);
@@ -1087,14 +1159,18 @@
dprintk("targ: curr fid 0x%x, vid 0x%x\n",
data->currfid, data->currvid);
- if ((checkvid != data->currvid) || (checkfid != data->currfid)) {
+ if ((checkvid != data->currvid) ||
+ (checkfid != data->currfid)) {
printk(KERN_INFO PFX
- "error - out of sync, fix 0x%x 0x%x, vid 0x%x 0x%x\n",
- checkfid, data->currfid, checkvid, data->currvid);
+ "error - out of sync, fix 0x%x 0x%x, "
+ "vid 0x%x 0x%x\n",
+ checkfid, data->currfid,
+ checkvid, data->currvid);
}
}
- if (cpufreq_frequency_table_target(pol, data->powernow_table, targfreq, relation, &newstate))
+ if (cpufreq_frequency_table_target(pol, data->powernow_table,
+ targfreq, relation, &newstate))
goto err_out;
mutex_lock(&fidvid_mutex);
@@ -1114,7 +1190,8 @@
mutex_unlock(&fidvid_mutex);
if (cpu_family == CPU_HW_PSTATE)
- pol->cur = find_khz_freq_from_pstate(data->powernow_table, newstate);
+ pol->cur = find_khz_freq_from_pstate(data->powernow_table,
+ newstate);
else
pol->cur = find_khz_freq_from_fid(data->currfid);
ret = 0;
@@ -1141,6 +1218,7 @@
struct powernow_k8_data *data;
cpumask_t oldmask;
int rc;
+ static int print_once;
if (!cpu_online(pol->cpu))
return -ENODEV;
@@ -1163,33 +1241,31 @@
* an UP version, and is deprecated by AMD.
*/
if (num_online_cpus() != 1) {
-#ifndef CONFIG_ACPI_PROCESSOR
- printk(KERN_ERR PFX "ACPI Processor support is required "
- "for SMP systems but is absent. Please load the "
- "ACPI Processor module before starting this "
- "driver.\n");
-#else
- printk(KERN_ERR FW_BUG PFX "Your BIOS does not provide"
- " ACPI _PSS objects in a way that Linux "
- "understands. Please report this to the Linux "
- "ACPI maintainers and complain to your BIOS "
- "vendor.\n");
-#endif
- kfree(data);
- return -ENODEV;
+ /*
+ * Replace this one with print_once as soon as such a
+ * thing gets introduced
+ */
+ if (!print_once) {
+ WARN_ONCE(1, KERN_ERR FW_BUG PFX "Your BIOS "
+ "does not provide ACPI _PSS objects "
+ "in a way that Linux understands. "
+ "Please report this to the Linux ACPI"
+ " maintainers and complain to your "
+ "BIOS vendor.\n");
+ print_once++;
+ }
+ goto err_out;
}
if (pol->cpu != 0) {
printk(KERN_ERR FW_BUG PFX "No ACPI _PSS objects for "
"CPU other than CPU0. Complain to your BIOS "
"vendor.\n");
- kfree(data);
- return -ENODEV;
+ goto err_out;
}
rc = find_psb_table(data);
- if (rc) {
- kfree(data);
- return -ENODEV;
- }
+ if (rc)
+ goto err_out;
+
/* Take a crude guess here.
* That guess was in microseconds, so multiply with 1000 */
pol->cpuinfo.transition_latency = (
@@ -1204,16 +1280,16 @@
if (smp_processor_id() != pol->cpu) {
printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu);
- goto err_out;
+ goto err_out_unmask;
}
if (pending_bit_stuck()) {
printk(KERN_ERR PFX "failing init, change pending bit set\n");
- goto err_out;
+ goto err_out_unmask;
}
if (query_current_values_with_pending_wait(data))
- goto err_out;
+ goto err_out_unmask;
if (cpu_family == CPU_OPTERON)
fidvid_msr_init();
@@ -1228,7 +1304,8 @@
data->available_cores = pol->cpus;
if (cpu_family == CPU_HW_PSTATE)
- pol->cur = find_khz_freq_from_pstate(data->powernow_table, data->currpstate);
+ pol->cur = find_khz_freq_from_pstate(data->powernow_table,
+ data->currpstate);
else
pol->cur = find_khz_freq_from_fid(data->currfid);
dprintk("policy current frequency %d kHz\n", pol->cur);
@@ -1245,7 +1322,8 @@
cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu);
if (cpu_family == CPU_HW_PSTATE)
- dprintk("cpu_init done, current pstate 0x%x\n", data->currpstate);
+ dprintk("cpu_init done, current pstate 0x%x\n",
+ data->currpstate);
else
dprintk("cpu_init done, current fid 0x%x, vid 0x%x\n",
data->currfid, data->currvid);
@@ -1254,15 +1332,16 @@
return 0;
-err_out:
+err_out_unmask:
set_cpus_allowed_ptr(current, &oldmask);
powernow_k8_cpu_exit_acpi(data);
+err_out:
kfree(data);
return -ENODEV;
}
-static int __devexit powernowk8_cpu_exit (struct cpufreq_policy *pol)
+static int __devexit powernowk8_cpu_exit(struct cpufreq_policy *pol)
{
struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu);
@@ -1279,7 +1358,7 @@
return 0;
}
-static unsigned int powernowk8_get (unsigned int cpu)
+static unsigned int powernowk8_get(unsigned int cpu)
{
struct powernow_k8_data *data;
cpumask_t oldmask = current->cpus_allowed;
@@ -1315,7 +1394,7 @@
return khz;
}
-static struct freq_attr* powernow_k8_attr[] = {
+static struct freq_attr *powernow_k8_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};
@@ -1360,7 +1439,8 @@
cpufreq_unregister_driver(&cpufreq_amd64_driver);
}
-MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com> and Mark Langsdorf <mark.langsdorf@amd.com>");
+MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com> and "
+ "Mark Langsdorf <mark.langsdorf@amd.com>");
MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver.");
MODULE_LICENSE("GPL");
diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k8.h b/arch/x86/kernel/cpu/cpufreq/powernow-k8.h
index 8ecc75b..6c6698f 100644
--- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.h
+++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.h
@@ -45,11 +45,10 @@
* frequency is in kHz */
struct cpufreq_frequency_table *powernow_table;
-#ifdef CONFIG_X86_POWERNOW_K8_ACPI
/* the acpi table needs to be kept. it's only available if ACPI was
* used to determine valid frequency/vid/fid states */
struct acpi_processor_performance acpi_data;
-#endif
+
/* we need to keep track of associated cores, but let cpufreq
* handle hotplug events - so just point at cpufreq pol->cpus
* structure */
@@ -222,10 +221,8 @@
static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index);
-#ifdef CONFIG_X86_POWERNOW_K8_ACPI
static int fill_powernow_table_pstate(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table);
static int fill_powernow_table_fidvid(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table);
-#endif
#ifdef CONFIG_SMP
static inline void define_siblings(int cpu, cpumask_t cpu_sharedcore_mask[])
diff --git a/arch/x86/kernel/cpu/cpufreq/sc520_freq.c b/arch/x86/kernel/cpu/cpufreq/sc520_freq.c
index 42da9bd..435a996 100644
--- a/arch/x86/kernel/cpu/cpufreq/sc520_freq.c
+++ b/arch/x86/kernel/cpu/cpufreq/sc520_freq.c
@@ -19,17 +19,19 @@
#include <linux/delay.h>
#include <linux/cpufreq.h>
+#include <linux/timex.h>
+#include <linux/io.h>
#include <asm/msr.h>
-#include <asm/timex.h>
-#include <asm/io.h>
#define MMCR_BASE 0xfffef000 /* The default base address */
#define OFFS_CPUCTL 0x2 /* CPU Control Register */
static __u8 __iomem *cpuctl;
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "sc520_freq", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+ "sc520_freq", msg)
+#define PFX "sc520_freq: "
static struct cpufreq_frequency_table sc520_freq_table[] = {
{0x01, 100000},
@@ -43,7 +45,8 @@
switch (clockspeed_reg & 0x03) {
default:
- printk(KERN_ERR "sc520_freq: error: cpuctl register has unexpected value %02x\n", clockspeed_reg);
+ printk(KERN_ERR PFX "error: cpuctl register has unexpected "
+ "value %02x\n", clockspeed_reg);
case 0x01:
return 100000;
case 0x02:
@@ -51,7 +54,7 @@
}
}
-static void sc520_freq_set_cpu_state (unsigned int state)
+static void sc520_freq_set_cpu_state(unsigned int state)
{
struct cpufreq_freqs freqs;
@@ -76,18 +79,19 @@
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
};
-static int sc520_freq_verify (struct cpufreq_policy *policy)
+static int sc520_freq_verify(struct cpufreq_policy *policy)
{
return cpufreq_frequency_table_verify(policy, &sc520_freq_table[0]);
}
-static int sc520_freq_target (struct cpufreq_policy *policy,
+static int sc520_freq_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
unsigned int newstate = 0;
- if (cpufreq_frequency_table_target(policy, sc520_freq_table, target_freq, relation, &newstate))
+ if (cpufreq_frequency_table_target(policy, sc520_freq_table,
+ target_freq, relation, &newstate))
return -EINVAL;
sc520_freq_set_cpu_state(newstate);
@@ -116,7 +120,7 @@
result = cpufreq_frequency_table_cpuinfo(policy, sc520_freq_table);
if (result)
- return (result);
+ return result;
cpufreq_frequency_table_get_attr(sc520_freq_table, policy->cpu);
@@ -131,7 +135,7 @@
}
-static struct freq_attr* sc520_freq_attr[] = {
+static struct freq_attr *sc520_freq_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};
@@ -155,13 +159,13 @@
int err;
/* Test if we have the right hardware */
- if(c->x86_vendor != X86_VENDOR_AMD ||
- c->x86 != 4 || c->x86_model != 9) {
+ if (c->x86_vendor != X86_VENDOR_AMD ||
+ c->x86 != 4 || c->x86_model != 9) {
dprintk("no Elan SC520 processor found!\n");
return -ENODEV;
}
cpuctl = ioremap((unsigned long)(MMCR_BASE + OFFS_CPUCTL), 1);
- if(!cpuctl) {
+ if (!cpuctl) {
printk(KERN_ERR "sc520_freq: error: failed to remap memory\n");
return -ENOMEM;
}
diff --git a/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c b/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c
index dedc1e9..8bbb11a 100644
--- a/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c
+++ b/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c
@@ -39,7 +39,7 @@
/* speedstep_processor
*/
-static unsigned int speedstep_processor = 0;
+static unsigned int speedstep_processor;
static u32 pmbase;
@@ -54,7 +54,8 @@
};
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "speedstep-ich", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+ "speedstep-ich", msg)
/**
@@ -62,7 +63,7 @@
*
* Returns: -ENODEV if no register could be found
*/
-static int speedstep_find_register (void)
+static int speedstep_find_register(void)
{
if (!speedstep_chipset_dev)
return -ENODEV;
@@ -90,7 +91,7 @@
*
* Tries to change the SpeedStep state.
*/
-static void speedstep_set_state (unsigned int state)
+static void speedstep_set_state(unsigned int state)
{
u8 pm2_blk;
u8 value;
@@ -133,11 +134,11 @@
dprintk("read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value);
- if (state == (value & 0x1)) {
- dprintk("change to %u MHz succeeded\n", (speedstep_get_processor_frequency(speedstep_processor) / 1000));
- } else {
- printk (KERN_ERR "cpufreq: change failed - I/O error\n");
- }
+ if (state == (value & 0x1))
+ dprintk("change to %u MHz succeeded\n",
+ speedstep_get_frequency(speedstep_processor) / 1000);
+ else
+ printk(KERN_ERR "cpufreq: change failed - I/O error\n");
return;
}
@@ -149,7 +150,7 @@
* Tries to activate the SpeedStep status and control registers.
* Returns -EINVAL on an unsupported chipset, and zero on success.
*/
-static int speedstep_activate (void)
+static int speedstep_activate(void)
{
u16 value = 0;
@@ -175,20 +176,18 @@
* functions. Returns the SPEEDSTEP_CHIPSET_-number for the detected
* chipset, or zero on failure.
*/
-static unsigned int speedstep_detect_chipset (void)
+static unsigned int speedstep_detect_chipset(void)
{
speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82801DB_12,
- PCI_ANY_ID,
- PCI_ANY_ID,
+ PCI_ANY_ID, PCI_ANY_ID,
NULL);
if (speedstep_chipset_dev)
return 4; /* 4-M */
speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82801CA_12,
- PCI_ANY_ID,
- PCI_ANY_ID,
+ PCI_ANY_ID, PCI_ANY_ID,
NULL);
if (speedstep_chipset_dev)
return 3; /* 3-M */
@@ -196,8 +195,7 @@
speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82801BA_10,
- PCI_ANY_ID,
- PCI_ANY_ID,
+ PCI_ANY_ID, PCI_ANY_ID,
NULL);
if (speedstep_chipset_dev) {
/* speedstep.c causes lockups on Dell Inspirons 8000 and
@@ -208,8 +206,7 @@
hostbridge = pci_get_subsys(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82815_MC,
- PCI_ANY_ID,
- PCI_ANY_ID,
+ PCI_ANY_ID, PCI_ANY_ID,
NULL);
if (!hostbridge)
@@ -236,7 +233,7 @@
cpus_allowed = current->cpus_allowed;
set_cpus_allowed_ptr(current, cpus);
- speed = speedstep_get_processor_frequency(speedstep_processor);
+ speed = speedstep_get_frequency(speedstep_processor);
set_cpus_allowed_ptr(current, &cpus_allowed);
dprintk("detected %u kHz as current frequency\n", speed);
return speed;
@@ -251,11 +248,12 @@
* speedstep_target - set a new CPUFreq policy
* @policy: new policy
* @target_freq: the target frequency
- * @relation: how that frequency relates to achieved frequency (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
+ * @relation: how that frequency relates to achieved frequency
+ * (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
*
* Sets a new CPUFreq policy.
*/
-static int speedstep_target (struct cpufreq_policy *policy,
+static int speedstep_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
@@ -264,7 +262,8 @@
cpumask_t cpus_allowed;
int i;
- if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0], target_freq, relation, &newstate))
+ if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0],
+ target_freq, relation, &newstate))
return -EINVAL;
freqs.old = _speedstep_get(policy->cpus);
@@ -308,7 +307,7 @@
* Limit must be within speedstep_low_freq and speedstep_high_freq, with
* at least one border included.
*/
-static int speedstep_verify (struct cpufreq_policy *policy)
+static int speedstep_verify(struct cpufreq_policy *policy)
{
return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
}
@@ -344,7 +343,8 @@
return -EIO;
dprintk("currently at %s speed setting - %i MHz\n",
- (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low" : "high",
+ (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency)
+ ? "low" : "high",
(speed / 1000));
/* cpuinfo and default policy values */
@@ -352,9 +352,9 @@
result = cpufreq_frequency_table_cpuinfo(policy, speedstep_freqs);
if (result)
- return (result);
+ return result;
- cpufreq_frequency_table_get_attr(speedstep_freqs, policy->cpu);
+ cpufreq_frequency_table_get_attr(speedstep_freqs, policy->cpu);
return 0;
}
@@ -366,7 +366,7 @@
return 0;
}
-static struct freq_attr* speedstep_attr[] = {
+static struct freq_attr *speedstep_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};
@@ -396,13 +396,15 @@
/* detect processor */
speedstep_processor = speedstep_detect_processor();
if (!speedstep_processor) {
- dprintk("Intel(R) SpeedStep(TM) capable processor not found\n");
+ dprintk("Intel(R) SpeedStep(TM) capable processor "
+ "not found\n");
return -ENODEV;
}
/* detect chipset */
if (!speedstep_detect_chipset()) {
- dprintk("Intel(R) SpeedStep(TM) for this chipset not (yet) available.\n");
+ dprintk("Intel(R) SpeedStep(TM) for this chipset not "
+ "(yet) available.\n");
return -ENODEV;
}
@@ -431,9 +433,11 @@
}
-MODULE_AUTHOR ("Dave Jones <davej@redhat.com>, Dominik Brodowski <linux@brodo.de>");
-MODULE_DESCRIPTION ("Speedstep driver for Intel mobile processors on chipsets with ICH-M southbridges.");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Dave Jones <davej@redhat.com>, "
+ "Dominik Brodowski <linux@brodo.de>");
+MODULE_DESCRIPTION("Speedstep driver for Intel mobile processors on chipsets "
+ "with ICH-M southbridges.");
+MODULE_LICENSE("GPL");
module_init(speedstep_init);
module_exit(speedstep_exit);
diff --git a/arch/x86/kernel/cpu/cpufreq/speedstep-lib.c b/arch/x86/kernel/cpu/cpufreq/speedstep-lib.c
index cdac7d6..2e3c686 100644
--- a/arch/x86/kernel/cpu/cpufreq/speedstep-lib.c
+++ b/arch/x86/kernel/cpu/cpufreq/speedstep-lib.c
@@ -16,12 +16,16 @@
#include <linux/slab.h>
#include <asm/msr.h>
+#include <asm/tsc.h>
#include "speedstep-lib.h"
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "speedstep-lib", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+ "speedstep-lib", msg)
+
+#define PFX "speedstep-lib: "
#ifdef CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK
-static int relaxed_check = 0;
+static int relaxed_check;
#else
#define relaxed_check 0
#endif
@@ -30,14 +34,14 @@
* GET PROCESSOR CORE SPEED IN KHZ *
*********************************************************************/
-static unsigned int pentium3_get_frequency (unsigned int processor)
+static unsigned int pentium3_get_frequency(unsigned int processor)
{
- /* See table 14 of p3_ds.pdf and table 22 of 29834003.pdf */
+ /* See table 14 of p3_ds.pdf and table 22 of 29834003.pdf */
struct {
unsigned int ratio; /* Frequency Multiplier (x10) */
u8 bitmap; /* power on configuration bits
[27, 25:22] (in MSR 0x2a) */
- } msr_decode_mult [] = {
+ } msr_decode_mult[] = {
{ 30, 0x01 },
{ 35, 0x05 },
{ 40, 0x02 },
@@ -52,7 +56,7 @@
{ 85, 0x26 },
{ 90, 0x20 },
{ 100, 0x2b },
- { 0, 0xff } /* error or unknown value */
+ { 0, 0xff } /* error or unknown value */
};
/* PIII(-M) FSB settings: see table b1-b of 24547206.pdf */
@@ -60,7 +64,7 @@
unsigned int value; /* Front Side Bus speed in MHz */
u8 bitmap; /* power on configuration bits [18: 19]
(in MSR 0x2a) */
- } msr_decode_fsb [] = {
+ } msr_decode_fsb[] = {
{ 66, 0x0 },
{ 100, 0x2 },
{ 133, 0x1 },
@@ -85,7 +89,7 @@
}
/* decode the multiplier */
- if (processor == SPEEDSTEP_PROCESSOR_PIII_C_EARLY) {
+ if (processor == SPEEDSTEP_CPU_PIII_C_EARLY) {
dprintk("workaround for early PIIIs\n");
msr_lo &= 0x03c00000;
} else
@@ -97,9 +101,10 @@
j++;
}
- dprintk("speed is %u\n", (msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100));
+ dprintk("speed is %u\n",
+ (msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100));
- return (msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100);
+ return msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100;
}
@@ -112,20 +117,23 @@
/* see table B-2 of 24547212.pdf */
if (msr_lo & 0x00040000) {
- printk(KERN_DEBUG "speedstep-lib: PM - invalid FSB: 0x%x 0x%x\n", msr_lo, msr_tmp);
+ printk(KERN_DEBUG PFX "PM - invalid FSB: 0x%x 0x%x\n",
+ msr_lo, msr_tmp);
return 0;
}
msr_tmp = (msr_lo >> 22) & 0x1f;
- dprintk("bits 22-26 are 0x%x, speed is %u\n", msr_tmp, (msr_tmp * 100 * 1000));
+ dprintk("bits 22-26 are 0x%x, speed is %u\n",
+ msr_tmp, (msr_tmp * 100 * 1000));
- return (msr_tmp * 100 * 1000);
+ return msr_tmp * 100 * 1000;
}
static unsigned int pentium_core_get_frequency(void)
{
u32 fsb = 0;
u32 msr_lo, msr_tmp;
+ int ret;
rdmsr(MSR_FSB_FREQ, msr_lo, msr_tmp);
/* see table B-2 of 25366920.pdf */
@@ -153,12 +161,15 @@
}
rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp);
- dprintk("PCORE - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n", msr_lo, msr_tmp);
+ dprintk("PCORE - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n",
+ msr_lo, msr_tmp);
msr_tmp = (msr_lo >> 22) & 0x1f;
- dprintk("bits 22-26 are 0x%x, speed is %u\n", msr_tmp, (msr_tmp * fsb));
+ dprintk("bits 22-26 are 0x%x, speed is %u\n",
+ msr_tmp, (msr_tmp * fsb));
- return (msr_tmp * fsb);
+ ret = (msr_tmp * fsb);
+ return ret;
}
@@ -167,6 +178,16 @@
struct cpuinfo_x86 *c = &boot_cpu_data;
u32 msr_lo, msr_hi, mult;
unsigned int fsb = 0;
+ unsigned int ret;
+ u8 fsb_code;
+
+ /* Pentium 4 Model 0 and 1 do not have the Core Clock Frequency
+ * to System Bus Frequency Ratio Field in the Processor Frequency
+ * Configuration Register of the MSR. Therefore the current
+ * frequency cannot be calculated and has to be measured.
+ */
+ if (c->x86_model < 2)
+ return cpu_khz;
rdmsr(0x2c, msr_lo, msr_hi);
@@ -177,62 +198,61 @@
* revision #12 in Table B-1: MSRs in the Pentium 4 and
* Intel Xeon Processors, on page B-4 and B-5.
*/
- if (c->x86_model < 2)
+ fsb_code = (msr_lo >> 16) & 0x7;
+ switch (fsb_code) {
+ case 0:
fsb = 100 * 1000;
- else {
- u8 fsb_code = (msr_lo >> 16) & 0x7;
- switch (fsb_code) {
- case 0:
- fsb = 100 * 1000;
- break;
- case 1:
- fsb = 13333 * 10;
- break;
- case 2:
- fsb = 200 * 1000;
- break;
- }
+ break;
+ case 1:
+ fsb = 13333 * 10;
+ break;
+ case 2:
+ fsb = 200 * 1000;
+ break;
}
if (!fsb)
- printk(KERN_DEBUG "speedstep-lib: couldn't detect FSB speed. Please send an e-mail to <linux@brodo.de>\n");
+ printk(KERN_DEBUG PFX "couldn't detect FSB speed. "
+ "Please send an e-mail to <linux@brodo.de>\n");
/* Multiplier. */
mult = msr_lo >> 24;
- dprintk("P4 - FSB %u kHz; Multiplier %u; Speed %u kHz\n", fsb, mult, (fsb * mult));
+ dprintk("P4 - FSB %u kHz; Multiplier %u; Speed %u kHz\n",
+ fsb, mult, (fsb * mult));
- return (fsb * mult);
+ ret = (fsb * mult);
+ return ret;
}
-unsigned int speedstep_get_processor_frequency(unsigned int processor)
+unsigned int speedstep_get_frequency(unsigned int processor)
{
switch (processor) {
- case SPEEDSTEP_PROCESSOR_PCORE:
+ case SPEEDSTEP_CPU_PCORE:
return pentium_core_get_frequency();
- case SPEEDSTEP_PROCESSOR_PM:
+ case SPEEDSTEP_CPU_PM:
return pentiumM_get_frequency();
- case SPEEDSTEP_PROCESSOR_P4D:
- case SPEEDSTEP_PROCESSOR_P4M:
+ case SPEEDSTEP_CPU_P4D:
+ case SPEEDSTEP_CPU_P4M:
return pentium4_get_frequency();
- case SPEEDSTEP_PROCESSOR_PIII_T:
- case SPEEDSTEP_PROCESSOR_PIII_C:
- case SPEEDSTEP_PROCESSOR_PIII_C_EARLY:
+ case SPEEDSTEP_CPU_PIII_T:
+ case SPEEDSTEP_CPU_PIII_C:
+ case SPEEDSTEP_CPU_PIII_C_EARLY:
return pentium3_get_frequency(processor);
default:
return 0;
};
return 0;
}
-EXPORT_SYMBOL_GPL(speedstep_get_processor_frequency);
+EXPORT_SYMBOL_GPL(speedstep_get_frequency);
/*********************************************************************
* DETECT SPEEDSTEP-CAPABLE PROCESSOR *
*********************************************************************/
-unsigned int speedstep_detect_processor (void)
+unsigned int speedstep_detect_processor(void)
{
struct cpuinfo_x86 *c = &cpu_data(0);
u32 ebx, msr_lo, msr_hi;
@@ -261,7 +281,7 @@
* sample has ebx = 0x0f, production has 0x0e.
*/
if ((ebx == 0x0e) || (ebx == 0x0f))
- return SPEEDSTEP_PROCESSOR_P4M;
+ return SPEEDSTEP_CPU_P4M;
break;
case 7:
/*
@@ -272,7 +292,7 @@
* samples are only of B-stepping...
*/
if (ebx == 0x0e)
- return SPEEDSTEP_PROCESSOR_P4M;
+ return SPEEDSTEP_CPU_P4M;
break;
case 9:
/*
@@ -288,10 +308,13 @@
* M-P4-Ms may have either ebx=0xe or 0xf [see above]
* M-P4/533 have either ebx=0xe or 0xf. [25317607.pdf]
* also, M-P4M HTs have ebx=0x8, too
- * For now, they are distinguished by the model_id string
+ * For now, they are distinguished by the model_id
+ * string
*/
- if ((ebx == 0x0e) || (strstr(c->x86_model_id,"Mobile Intel(R) Pentium(R) 4") != NULL))
- return SPEEDSTEP_PROCESSOR_P4M;
+ if ((ebx == 0x0e) ||
+ (strstr(c->x86_model_id,
+ "Mobile Intel(R) Pentium(R) 4") != NULL))
+ return SPEEDSTEP_CPU_P4M;
break;
default:
break;
@@ -301,7 +324,8 @@
switch (c->x86_model) {
case 0x0B: /* Intel PIII [Tualatin] */
- /* cpuid_ebx(1) is 0x04 for desktop PIII, 0x06 for mobile PIII-M */
+ /* cpuid_ebx(1) is 0x04 for desktop PIII,
+ * 0x06 for mobile PIII-M */
ebx = cpuid_ebx(0x00000001);
dprintk("ebx is %x\n", ebx);
@@ -313,14 +337,15 @@
/* So far all PIII-M processors support SpeedStep. See
* Intel's 24540640.pdf of June 2003
*/
- return SPEEDSTEP_PROCESSOR_PIII_T;
+ return SPEEDSTEP_CPU_PIII_T;
case 0x08: /* Intel PIII [Coppermine] */
/* all mobile PIII Coppermines have FSB 100 MHz
* ==> sort out a few desktop PIIIs. */
rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_hi);
- dprintk("Coppermine: MSR_IA32_EBL_CR_POWERON is 0x%x, 0x%x\n", msr_lo, msr_hi);
+ dprintk("Coppermine: MSR_IA32_EBL_CR_POWERON is 0x%x, 0x%x\n",
+ msr_lo, msr_hi);
msr_lo &= 0x00c0000;
if (msr_lo != 0x0080000)
return 0;
@@ -332,13 +357,15 @@
* bit 56 or 57 is set
*/
rdmsr(MSR_IA32_PLATFORM_ID, msr_lo, msr_hi);
- dprintk("Coppermine: MSR_IA32_PLATFORM ID is 0x%x, 0x%x\n", msr_lo, msr_hi);
- if ((msr_hi & (1<<18)) && (relaxed_check ? 1 : (msr_hi & (3<<24)))) {
+ dprintk("Coppermine: MSR_IA32_PLATFORM ID is 0x%x, 0x%x\n",
+ msr_lo, msr_hi);
+ if ((msr_hi & (1<<18)) &&
+ (relaxed_check ? 1 : (msr_hi & (3<<24)))) {
if (c->x86_mask == 0x01) {
dprintk("early PIII version\n");
- return SPEEDSTEP_PROCESSOR_PIII_C_EARLY;
+ return SPEEDSTEP_CPU_PIII_C_EARLY;
} else
- return SPEEDSTEP_PROCESSOR_PIII_C;
+ return SPEEDSTEP_CPU_PIII_C;
}
default:
@@ -369,7 +396,7 @@
dprintk("trying to determine both speeds\n");
/* get current speed */
- prev_speed = speedstep_get_processor_frequency(processor);
+ prev_speed = speedstep_get_frequency(processor);
if (!prev_speed)
return -EIO;
@@ -379,7 +406,7 @@
/* switch to low state */
set_state(SPEEDSTEP_LOW);
- *low_speed = speedstep_get_processor_frequency(processor);
+ *low_speed = speedstep_get_frequency(processor);
if (!*low_speed) {
ret = -EIO;
goto out;
@@ -398,7 +425,7 @@
if (transition_latency)
do_gettimeofday(&tv2);
- *high_speed = speedstep_get_processor_frequency(processor);
+ *high_speed = speedstep_get_frequency(processor);
if (!*high_speed) {
ret = -EIO;
goto out;
@@ -426,9 +453,12 @@
/* check if the latency measurement is too high or too low
* and set it to a safe value (500uSec) in that case
*/
- if (*transition_latency > 10000000 || *transition_latency < 50000) {
- printk (KERN_WARNING "speedstep: frequency transition measured seems out of "
- "range (%u nSec), falling back to a safe one of %u nSec.\n",
+ if (*transition_latency > 10000000 ||
+ *transition_latency < 50000) {
+ printk(KERN_WARNING PFX "frequency transition "
+ "measured seems out of range (%u "
+ "nSec), falling back to a safe one of"
+ "%u nSec.\n",
*transition_latency, 500000);
*transition_latency = 500000;
}
@@ -436,15 +466,16 @@
out:
local_irq_restore(flags);
- return (ret);
+ return ret;
}
EXPORT_SYMBOL_GPL(speedstep_get_freqs);
#ifdef CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK
module_param(relaxed_check, int, 0444);
-MODULE_PARM_DESC(relaxed_check, "Don't do all checks for speedstep capability.");
+MODULE_PARM_DESC(relaxed_check,
+ "Don't do all checks for speedstep capability.");
#endif
-MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>");
-MODULE_DESCRIPTION ("Library for Intel SpeedStep 1 or 2 cpufreq drivers.");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
+MODULE_DESCRIPTION("Library for Intel SpeedStep 1 or 2 cpufreq drivers.");
+MODULE_LICENSE("GPL");
diff --git a/arch/x86/kernel/cpu/cpufreq/speedstep-lib.h b/arch/x86/kernel/cpu/cpufreq/speedstep-lib.h
index b11bcc6..2b6c04e 100644
--- a/arch/x86/kernel/cpu/cpufreq/speedstep-lib.h
+++ b/arch/x86/kernel/cpu/cpufreq/speedstep-lib.h
@@ -12,17 +12,17 @@
/* processors */
-#define SPEEDSTEP_PROCESSOR_PIII_C_EARLY 0x00000001 /* Coppermine core */
-#define SPEEDSTEP_PROCESSOR_PIII_C 0x00000002 /* Coppermine core */
-#define SPEEDSTEP_PROCESSOR_PIII_T 0x00000003 /* Tualatin core */
-#define SPEEDSTEP_PROCESSOR_P4M 0x00000004 /* P4-M */
+#define SPEEDSTEP_CPU_PIII_C_EARLY 0x00000001 /* Coppermine core */
+#define SPEEDSTEP_CPU_PIII_C 0x00000002 /* Coppermine core */
+#define SPEEDSTEP_CPU_PIII_T 0x00000003 /* Tualatin core */
+#define SPEEDSTEP_CPU_P4M 0x00000004 /* P4-M */
/* the following processors are not speedstep-capable and are not auto-detected
* in speedstep_detect_processor(). However, their speed can be detected using
- * the speedstep_get_processor_frequency() call. */
-#define SPEEDSTEP_PROCESSOR_PM 0xFFFFFF03 /* Pentium M */
-#define SPEEDSTEP_PROCESSOR_P4D 0xFFFFFF04 /* desktop P4 */
-#define SPEEDSTEP_PROCESSOR_PCORE 0xFFFFFF05 /* Core */
+ * the speedstep_get_frequency() call. */
+#define SPEEDSTEP_CPU_PM 0xFFFFFF03 /* Pentium M */
+#define SPEEDSTEP_CPU_P4D 0xFFFFFF04 /* desktop P4 */
+#define SPEEDSTEP_CPU_PCORE 0xFFFFFF05 /* Core */
/* speedstep states -- only two of them */
@@ -34,7 +34,7 @@
extern unsigned int speedstep_detect_processor (void);
/* detect the current speed (in khz) of the processor */
-extern unsigned int speedstep_get_processor_frequency(unsigned int processor);
+extern unsigned int speedstep_get_frequency(unsigned int processor);
/* detect the low and high speeds of the processor. The callback
diff --git a/arch/x86/kernel/cpu/cpufreq/speedstep-smi.c b/arch/x86/kernel/cpu/cpufreq/speedstep-smi.c
index 8a85c93..befea08 100644
--- a/arch/x86/kernel/cpu/cpufreq/speedstep-smi.c
+++ b/arch/x86/kernel/cpu/cpufreq/speedstep-smi.c
@@ -19,8 +19,8 @@
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/io.h>
#include <asm/ist.h>
-#include <asm/io.h>
#include "speedstep-lib.h"
@@ -30,12 +30,12 @@
* If user gives it, these are used.
*
*/
-static int smi_port = 0;
-static int smi_cmd = 0;
-static unsigned int smi_sig = 0;
+static int smi_port;
+static int smi_cmd;
+static unsigned int smi_sig;
/* info about the processor */
-static unsigned int speedstep_processor = 0;
+static unsigned int speedstep_processor;
/*
* There are only two frequency states for each processor. Values
@@ -56,12 +56,13 @@
* of DMA activity going on? */
#define SMI_TRIES 5
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "speedstep-smi", msg)
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
+ "speedstep-smi", msg)
/**
* speedstep_smi_ownership
*/
-static int speedstep_smi_ownership (void)
+static int speedstep_smi_ownership(void)
{
u32 command, result, magic, dummy;
u32 function = GET_SPEEDSTEP_OWNER;
@@ -70,16 +71,18 @@
command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
magic = virt_to_phys(magic_data);
- dprintk("trying to obtain ownership with command %x at port %x\n", command, smi_port);
+ dprintk("trying to obtain ownership with command %x at port %x\n",
+ command, smi_port);
__asm__ __volatile__(
"push %%ebp\n"
"out %%al, (%%dx)\n"
"pop %%ebp\n"
- : "=D" (result), "=a" (dummy), "=b" (dummy), "=c" (dummy), "=d" (dummy),
- "=S" (dummy)
+ : "=D" (result),
+ "=a" (dummy), "=b" (dummy), "=c" (dummy), "=d" (dummy),
+ "=S" (dummy)
: "a" (command), "b" (function), "c" (0), "d" (smi_port),
- "D" (0), "S" (magic)
+ "D" (0), "S" (magic)
: "memory"
);
@@ -97,10 +100,10 @@
* even hangs [cf. bugme.osdl.org # 1422] on earlier systems. Empirical testing
* shows that the latter occurs if !(ist_info.event & 0xFFFF).
*/
-static int speedstep_smi_get_freqs (unsigned int *low, unsigned int *high)
+static int speedstep_smi_get_freqs(unsigned int *low, unsigned int *high)
{
u32 command, result = 0, edi, high_mhz, low_mhz, dummy;
- u32 state=0;
+ u32 state = 0;
u32 function = GET_SPEEDSTEP_FREQS;
if (!(ist_info.event & 0xFFFF)) {
@@ -110,17 +113,25 @@
command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
- dprintk("trying to determine frequencies with command %x at port %x\n", command, smi_port);
+ dprintk("trying to determine frequencies with command %x at port %x\n",
+ command, smi_port);
__asm__ __volatile__(
"push %%ebp\n"
"out %%al, (%%dx)\n"
"pop %%ebp"
- : "=a" (result), "=b" (high_mhz), "=c" (low_mhz), "=d" (state), "=D" (edi), "=S" (dummy)
- : "a" (command), "b" (function), "c" (state), "d" (smi_port), "S" (0), "D" (0)
+ : "=a" (result),
+ "=b" (high_mhz),
+ "=c" (low_mhz),
+ "=d" (state), "=D" (edi), "=S" (dummy)
+ : "a" (command),
+ "b" (function),
+ "c" (state),
+ "d" (smi_port), "S" (0), "D" (0)
);
- dprintk("result %x, low_freq %u, high_freq %u\n", result, low_mhz, high_mhz);
+ dprintk("result %x, low_freq %u, high_freq %u\n",
+ result, low_mhz, high_mhz);
/* abort if results are obviously incorrect... */
if ((high_mhz + low_mhz) < 600)
@@ -137,26 +148,30 @@
* @state: processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
*
*/
-static int speedstep_get_state (void)
+static int speedstep_get_state(void)
{
- u32 function=GET_SPEEDSTEP_STATE;
+ u32 function = GET_SPEEDSTEP_STATE;
u32 result, state, edi, command, dummy;
command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
- dprintk("trying to determine current setting with command %x at port %x\n", command, smi_port);
+ dprintk("trying to determine current setting with command %x "
+ "at port %x\n", command, smi_port);
__asm__ __volatile__(
"push %%ebp\n"
"out %%al, (%%dx)\n"
"pop %%ebp\n"
- : "=a" (result), "=b" (state), "=D" (edi), "=c" (dummy), "=d" (dummy), "=S" (dummy)
- : "a" (command), "b" (function), "c" (0), "d" (smi_port), "S" (0), "D" (0)
+ : "=a" (result),
+ "=b" (state), "=D" (edi),
+ "=c" (dummy), "=d" (dummy), "=S" (dummy)
+ : "a" (command), "b" (function), "c" (0),
+ "d" (smi_port), "S" (0), "D" (0)
);
dprintk("state is %x, result is %x\n", state, result);
- return (state & 1);
+ return state & 1;
}
@@ -165,11 +180,11 @@
* @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
*
*/
-static void speedstep_set_state (unsigned int state)
+static void speedstep_set_state(unsigned int state)
{
unsigned int result = 0, command, new_state, dummy;
unsigned long flags;
- unsigned int function=SET_SPEEDSTEP_STATE;
+ unsigned int function = SET_SPEEDSTEP_STATE;
unsigned int retry = 0;
if (state > 0x1)
@@ -180,11 +195,14 @@
command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
- dprintk("trying to set frequency to state %u with command %x at port %x\n", state, command, smi_port);
+ dprintk("trying to set frequency to state %u "
+ "with command %x at port %x\n",
+ state, command, smi_port);
do {
if (retry) {
- dprintk("retry %u, previous result %u, waiting...\n", retry, result);
+ dprintk("retry %u, previous result %u, waiting...\n",
+ retry, result);
mdelay(retry * 50);
}
retry++;
@@ -192,20 +210,26 @@
"push %%ebp\n"
"out %%al, (%%dx)\n"
"pop %%ebp"
- : "=b" (new_state), "=D" (result), "=c" (dummy), "=a" (dummy),
- "=d" (dummy), "=S" (dummy)
- : "a" (command), "b" (function), "c" (state), "d" (smi_port), "S" (0), "D" (0)
+ : "=b" (new_state), "=D" (result),
+ "=c" (dummy), "=a" (dummy),
+ "=d" (dummy), "=S" (dummy)
+ : "a" (command), "b" (function), "c" (state),
+ "d" (smi_port), "S" (0), "D" (0)
);
} while ((new_state != state) && (retry <= SMI_TRIES));
/* enable IRQs */
local_irq_restore(flags);
- if (new_state == state) {
- dprintk("change to %u MHz succeeded after %u tries with result %u\n", (speedstep_freqs[new_state].frequency / 1000), retry, result);
- } else {
- printk(KERN_ERR "cpufreq: change to state %u failed with new_state %u and result %u\n", state, new_state, result);
- }
+ if (new_state == state)
+ dprintk("change to %u MHz succeeded after %u tries "
+ "with result %u\n",
+ (speedstep_freqs[new_state].frequency / 1000),
+ retry, result);
+ else
+ printk(KERN_ERR "cpufreq: change to state %u "
+ "failed with new_state %u and result %u\n",
+ state, new_state, result);
return;
}
@@ -219,13 +243,14 @@
*
* Sets a new CPUFreq policy/freq.
*/
-static int speedstep_target (struct cpufreq_policy *policy,
+static int speedstep_target(struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int relation)
{
unsigned int newstate = 0;
struct cpufreq_freqs freqs;
- if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0], target_freq, relation, &newstate))
+ if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0],
+ target_freq, relation, &newstate))
return -EINVAL;
freqs.old = speedstep_freqs[speedstep_get_state()].frequency;
@@ -250,7 +275,7 @@
* Limit must be within speedstep_low_freq and speedstep_high_freq, with
* at least one border included.
*/
-static int speedstep_verify (struct cpufreq_policy *policy)
+static int speedstep_verify(struct cpufreq_policy *policy)
{
return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
}
@@ -259,7 +284,8 @@
static int speedstep_cpu_init(struct cpufreq_policy *policy)
{
int result;
- unsigned int speed,state;
+ unsigned int speed, state;
+ unsigned int *low, *high;
/* capability check */
if (policy->cpu != 0)
@@ -272,19 +298,23 @@
}
/* detect low and high frequency */
- result = speedstep_smi_get_freqs(&speedstep_freqs[SPEEDSTEP_LOW].frequency,
- &speedstep_freqs[SPEEDSTEP_HIGH].frequency);
+ low = &speedstep_freqs[SPEEDSTEP_LOW].frequency;
+ high = &speedstep_freqs[SPEEDSTEP_HIGH].frequency;
+
+ result = speedstep_smi_get_freqs(low, high);
if (result) {
- /* fall back to speedstep_lib.c dection mechanism: try both states out */
- dprintk("could not detect low and high frequencies by SMI call.\n");
+ /* fall back to speedstep_lib.c dection mechanism:
+ * try both states out */
+ dprintk("could not detect low and high frequencies "
+ "by SMI call.\n");
result = speedstep_get_freqs(speedstep_processor,
- &speedstep_freqs[SPEEDSTEP_LOW].frequency,
- &speedstep_freqs[SPEEDSTEP_HIGH].frequency,
+ low, high,
NULL,
&speedstep_set_state);
if (result) {
- dprintk("could not detect two different speeds -- aborting.\n");
+ dprintk("could not detect two different speeds"
+ " -- aborting.\n");
return result;
} else
dprintk("workaround worked.\n");
@@ -295,7 +325,8 @@
speed = speedstep_freqs[state].frequency;
dprintk("currently at %s speed setting - %i MHz\n",
- (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low" : "high",
+ (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency)
+ ? "low" : "high",
(speed / 1000));
/* cpuinfo and default policy values */
@@ -304,7 +335,7 @@
result = cpufreq_frequency_table_cpuinfo(policy, speedstep_freqs);
if (result)
- return (result);
+ return result;
cpufreq_frequency_table_get_attr(speedstep_freqs, policy->cpu);
@@ -321,7 +352,7 @@
{
if (cpu)
return -ENODEV;
- return speedstep_get_processor_frequency(speedstep_processor);
+ return speedstep_get_frequency(speedstep_processor);
}
@@ -335,7 +366,7 @@
return result;
}
-static struct freq_attr* speedstep_attr[] = {
+static struct freq_attr *speedstep_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};
@@ -364,21 +395,23 @@
speedstep_processor = speedstep_detect_processor();
switch (speedstep_processor) {
- case SPEEDSTEP_PROCESSOR_PIII_T:
- case SPEEDSTEP_PROCESSOR_PIII_C:
- case SPEEDSTEP_PROCESSOR_PIII_C_EARLY:
+ case SPEEDSTEP_CPU_PIII_T:
+ case SPEEDSTEP_CPU_PIII_C:
+ case SPEEDSTEP_CPU_PIII_C_EARLY:
break;
default:
speedstep_processor = 0;
}
if (!speedstep_processor) {
- dprintk ("No supported Intel CPU detected.\n");
+ dprintk("No supported Intel CPU detected.\n");
return -ENODEV;
}
- dprintk("signature:0x%.8lx, command:0x%.8lx, event:0x%.8lx, perf_level:0x%.8lx.\n",
- ist_info.signature, ist_info.command, ist_info.event, ist_info.perf_level);
+ dprintk("signature:0x%.8lx, command:0x%.8lx, "
+ "event:0x%.8lx, perf_level:0x%.8lx.\n",
+ ist_info.signature, ist_info.command,
+ ist_info.event, ist_info.perf_level);
/* Error if no IST-SMI BIOS or no PARM
sig= 'ISGE' aka 'Intel Speedstep Gate E' */
@@ -416,17 +449,20 @@
cpufreq_unregister_driver(&speedstep_driver);
}
-module_param(smi_port, int, 0444);
-module_param(smi_cmd, int, 0444);
-module_param(smi_sig, uint, 0444);
+module_param(smi_port, int, 0444);
+module_param(smi_cmd, int, 0444);
+module_param(smi_sig, uint, 0444);
-MODULE_PARM_DESC(smi_port, "Override the BIOS-given IST port with this value -- Intel's default setting is 0xb2");
-MODULE_PARM_DESC(smi_cmd, "Override the BIOS-given IST command with this value -- Intel's default setting is 0x82");
-MODULE_PARM_DESC(smi_sig, "Set to 1 to fake the IST signature when using the SMI interface.");
+MODULE_PARM_DESC(smi_port, "Override the BIOS-given IST port with this value "
+ "-- Intel's default setting is 0xb2");
+MODULE_PARM_DESC(smi_cmd, "Override the BIOS-given IST command with this value "
+ "-- Intel's default setting is 0x82");
+MODULE_PARM_DESC(smi_sig, "Set to 1 to fake the IST signature when using the "
+ "SMI interface.");
-MODULE_AUTHOR ("Hiroshi Miura");
-MODULE_DESCRIPTION ("Speedstep driver for IST applet SMI interface.");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Hiroshi Miura");
+MODULE_DESCRIPTION("Speedstep driver for IST applet SMI interface.");
+MODULE_LICENSE("GPL");
module_init(speedstep_init);
module_exit(speedstep_exit);
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index d5cebb5..b8e7aaf 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -543,8 +543,6 @@
return tsc_pit_min;
}
-#ifdef CONFIG_X86_32
-/* Only called from the Powernow K7 cpu freq driver */
int recalibrate_cpu_khz(void)
{
#ifndef CONFIG_SMP
@@ -566,7 +564,6 @@
EXPORT_SYMBOL(recalibrate_cpu_khz);
-#endif /* CONFIG_X86_32 */
/* Accelerators for sched_clock()
* convert from cycles(64bits) => nanoseconds (64bits)
diff --git a/crypto/Kconfig b/crypto/Kconfig
index 8dde4fc..74d0e62 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -56,6 +56,7 @@
tristate
select CRYPTO_ALGAPI2
select CRYPTO_RNG2
+ select CRYPTO_WORKQUEUE
config CRYPTO_HASH
tristate
@@ -75,6 +76,10 @@
tristate
select CRYPTO_ALGAPI2
+config CRYPTO_PCOMP
+ tristate
+ select CRYPTO_ALGAPI2
+
config CRYPTO_MANAGER
tristate "Cryptographic algorithm manager"
select CRYPTO_MANAGER2
@@ -87,6 +92,7 @@
select CRYPTO_AEAD2
select CRYPTO_HASH2
select CRYPTO_BLKCIPHER2
+ select CRYPTO_PCOMP
config CRYPTO_GF128MUL
tristate "GF(2^128) multiplication functions (EXPERIMENTAL)"
@@ -106,11 +112,15 @@
help
These are 'Null' algorithms, used by IPsec, which do nothing.
+config CRYPTO_WORKQUEUE
+ tristate
+
config CRYPTO_CRYPTD
tristate "Software async crypto daemon"
select CRYPTO_BLKCIPHER
select CRYPTO_HASH
select CRYPTO_MANAGER
+ select CRYPTO_WORKQUEUE
help
This is a generic software asynchronous crypto daemon that
converts an arbitrary synchronous software crypto algorithm
@@ -470,6 +480,31 @@
See <http://csrc.nist.gov/encryption/aes/> for more information.
+config CRYPTO_AES_NI_INTEL
+ tristate "AES cipher algorithms (AES-NI)"
+ depends on (X86 || UML_X86) && 64BIT
+ select CRYPTO_AES_X86_64
+ select CRYPTO_CRYPTD
+ select CRYPTO_ALGAPI
+ help
+ Use Intel AES-NI instructions for AES algorithm.
+
+ AES cipher algorithms (FIPS-197). AES uses the Rijndael
+ algorithm.
+
+ Rijndael appears to be consistently a very good performer in
+ both hardware and software across a wide range of computing
+ environments regardless of its use in feedback or non-feedback
+ modes. Its key setup time is excellent, and its key agility is
+ good. Rijndael's very low memory requirements make it very well
+ suited for restricted-space environments, in which it also
+ demonstrates excellent performance. Rijndael's operations are
+ among the easiest to defend against power and timing attacks.
+
+ The AES specifies three key sizes: 128, 192 and 256 bits
+
+ See <http://csrc.nist.gov/encryption/aes/> for more information.
+
config CRYPTO_ANUBIS
tristate "Anubis cipher algorithm"
select CRYPTO_ALGAPI
@@ -714,6 +749,15 @@
You will most probably want this if using IPSec.
+config CRYPTO_ZLIB
+ tristate "Zlib compression algorithm"
+ select CRYPTO_PCOMP
+ select ZLIB_INFLATE
+ select ZLIB_DEFLATE
+ select NLATTR
+ help
+ This is the zlib algorithm.
+
config CRYPTO_LZO
tristate "LZO compression algorithm"
select CRYPTO_ALGAPI
diff --git a/crypto/Makefile b/crypto/Makefile
index 46b08bf..673d9f7 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -5,6 +5,8 @@
obj-$(CONFIG_CRYPTO) += crypto.o
crypto-objs := api.o cipher.o digest.o compress.o
+obj-$(CONFIG_CRYPTO_WORKQUEUE) += crypto_wq.o
+
obj-$(CONFIG_CRYPTO_FIPS) += fips.o
crypto_algapi-$(CONFIG_PROC_FS) += proc.o
@@ -25,6 +27,8 @@
crypto_hash-objs += shash.o
obj-$(CONFIG_CRYPTO_HASH2) += crypto_hash.o
+obj-$(CONFIG_CRYPTO_PCOMP) += pcompress.o
+
cryptomgr-objs := algboss.o testmgr.o
obj-$(CONFIG_CRYPTO_MANAGER2) += cryptomgr.o
@@ -70,6 +74,7 @@
obj-$(CONFIG_CRYPTO_SEED) += seed.o
obj-$(CONFIG_CRYPTO_SALSA20) += salsa20_generic.o
obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o
+obj-$(CONFIG_CRYPTO_ZLIB) += zlib.o
obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o
obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o
obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o
diff --git a/crypto/ablkcipher.c b/crypto/ablkcipher.c
index 94140b3..e11ce37 100644
--- a/crypto/ablkcipher.c
+++ b/crypto/ablkcipher.c
@@ -282,6 +282,25 @@
alg->cra_ablkcipher.ivsize))
return alg;
+ crypto_mod_put(alg);
+ alg = crypto_alg_mod_lookup(name, type | CRYPTO_ALG_TESTED,
+ mask & ~CRYPTO_ALG_TESTED);
+ if (IS_ERR(alg))
+ return alg;
+
+ if ((alg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
+ CRYPTO_ALG_TYPE_GIVCIPHER) {
+ if ((alg->cra_flags ^ type ^ ~mask) & CRYPTO_ALG_TESTED) {
+ crypto_mod_put(alg);
+ alg = ERR_PTR(-ENOENT);
+ }
+ return alg;
+ }
+
+ BUG_ON(!((alg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
+ CRYPTO_ALG_TYPE_BLKCIPHER ? alg->cra_blkcipher.ivsize :
+ alg->cra_ablkcipher.ivsize));
+
return ERR_PTR(crypto_givcipher_default(alg, type, mask));
}
diff --git a/crypto/aead.c b/crypto/aead.c
index 3a6f3f5..d9aa733 100644
--- a/crypto/aead.c
+++ b/crypto/aead.c
@@ -422,6 +422,22 @@
if (!alg->cra_aead.ivsize)
return alg;
+ crypto_mod_put(alg);
+ alg = crypto_alg_mod_lookup(name, type | CRYPTO_ALG_TESTED,
+ mask & ~CRYPTO_ALG_TESTED);
+ if (IS_ERR(alg))
+ return alg;
+
+ if (alg->cra_type == &crypto_aead_type) {
+ if ((alg->cra_flags ^ type ^ ~mask) & CRYPTO_ALG_TESTED) {
+ crypto_mod_put(alg);
+ alg = ERR_PTR(-ENOENT);
+ }
+ return alg;
+ }
+
+ BUG_ON(!alg->cra_aead.ivsize);
+
return ERR_PTR(crypto_nivaead_default(alg, type, mask));
}
diff --git a/crypto/algboss.c b/crypto/algboss.c
index 4601e426..6906f92 100644
--- a/crypto/algboss.c
+++ b/crypto/algboss.c
@@ -10,7 +10,7 @@
*
*/
-#include <linux/crypto.h>
+#include <crypto/internal/aead.h>
#include <linux/ctype.h>
#include <linux/err.h>
#include <linux/init.h>
@@ -206,8 +206,7 @@
u32 type = param->type;
int err = 0;
- if (!((type ^ CRYPTO_ALG_TYPE_BLKCIPHER) &
- CRYPTO_ALG_TYPE_BLKCIPHER_MASK) && !(type & CRYPTO_ALG_GENIV))
+ if (type & CRYPTO_ALG_TESTED)
goto skiptest;
err = alg_test(param->driver, param->alg, type, CRYPTO_ALG_TESTED);
@@ -223,6 +222,7 @@
{
struct task_struct *thread;
struct crypto_test_param *param;
+ u32 type;
if (!try_module_get(THIS_MODULE))
goto err;
@@ -233,7 +233,19 @@
memcpy(param->driver, alg->cra_driver_name, sizeof(param->driver));
memcpy(param->alg, alg->cra_name, sizeof(param->alg));
- param->type = alg->cra_flags;
+ type = alg->cra_flags;
+
+ /* This piece of crap needs to disappear into per-type test hooks. */
+ if ((!((type ^ CRYPTO_ALG_TYPE_BLKCIPHER) &
+ CRYPTO_ALG_TYPE_BLKCIPHER_MASK) && !(type & CRYPTO_ALG_GENIV) &&
+ ((alg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
+ CRYPTO_ALG_TYPE_BLKCIPHER ? alg->cra_blkcipher.ivsize :
+ alg->cra_ablkcipher.ivsize)) ||
+ (!((type ^ CRYPTO_ALG_TYPE_AEAD) & CRYPTO_ALG_TYPE_MASK) &&
+ alg->cra_type == &crypto_nivaead_type && alg->cra_aead.ivsize))
+ type |= CRYPTO_ALG_TESTED;
+
+ param->type = type;
thread = kthread_run(cryptomgr_test, param, "cryptomgr_test");
if (IS_ERR(thread))
diff --git a/crypto/ansi_cprng.c b/crypto/ansi_cprng.c
index 0fac8ff..d80ed4c 100644
--- a/crypto/ansi_cprng.c
+++ b/crypto/ansi_cprng.c
@@ -132,9 +132,15 @@
*/
if (!memcmp(ctx->rand_data, ctx->last_rand_data,
DEFAULT_BLK_SZ)) {
+ if (fips_enabled) {
+ panic("cprng %p Failed repetition check!\n",
+ ctx);
+ }
+
printk(KERN_ERR
"ctx %p Failed repetition check!\n",
ctx);
+
ctx->flags |= PRNG_NEED_RESET;
return -EINVAL;
}
@@ -338,7 +344,16 @@
spin_lock_init(&ctx->prng_lock);
- return reset_prng_context(ctx, NULL, DEFAULT_PRNG_KSZ, NULL, NULL);
+ if (reset_prng_context(ctx, NULL, DEFAULT_PRNG_KSZ, NULL, NULL) < 0)
+ return -EINVAL;
+
+ /*
+ * after allocation, we should always force the user to reset
+ * so they don't inadvertently use the insecure default values
+ * without specifying them intentially
+ */
+ ctx->flags |= PRNG_NEED_RESET;
+ return 0;
}
static void cprng_exit(struct crypto_tfm *tfm)
diff --git a/crypto/api.c b/crypto/api.c
index 38a2bc0..314dab9 100644
--- a/crypto/api.c
+++ b/crypto/api.c
@@ -255,7 +255,7 @@
struct crypto_alg *larval;
int ok;
- if (!(mask & CRYPTO_ALG_TESTED)) {
+ if (!((type | mask) & CRYPTO_ALG_TESTED)) {
type |= CRYPTO_ALG_TESTED;
mask |= CRYPTO_ALG_TESTED;
}
@@ -464,8 +464,8 @@
}
EXPORT_SYMBOL_GPL(crypto_alloc_base);
-struct crypto_tfm *crypto_create_tfm(struct crypto_alg *alg,
- const struct crypto_type *frontend)
+void *crypto_create_tfm(struct crypto_alg *alg,
+ const struct crypto_type *frontend)
{
char *mem;
struct crypto_tfm *tfm = NULL;
@@ -499,9 +499,9 @@
crypto_shoot_alg(alg);
kfree(mem);
out_err:
- tfm = ERR_PTR(err);
+ mem = ERR_PTR(err);
out:
- return tfm;
+ return mem;
}
EXPORT_SYMBOL_GPL(crypto_create_tfm);
@@ -525,12 +525,11 @@
*
* In case of error the return value is an error pointer.
*/
-struct crypto_tfm *crypto_alloc_tfm(const char *alg_name,
- const struct crypto_type *frontend,
- u32 type, u32 mask)
+void *crypto_alloc_tfm(const char *alg_name,
+ const struct crypto_type *frontend, u32 type, u32 mask)
{
struct crypto_alg *(*lookup)(const char *name, u32 type, u32 mask);
- struct crypto_tfm *tfm;
+ void *tfm;
int err;
type &= frontend->maskclear;
diff --git a/crypto/blkcipher.c b/crypto/blkcipher.c
index d70a41c..90d26c9 100644
--- a/crypto/blkcipher.c
+++ b/crypto/blkcipher.c
@@ -521,7 +521,7 @@
int err;
type = crypto_skcipher_type(type);
- mask = crypto_skcipher_mask(mask) | CRYPTO_ALG_GENIV;
+ mask = crypto_skcipher_mask(mask)| CRYPTO_ALG_GENIV;
alg = crypto_alg_mod_lookup(name, type, mask);
if (IS_ERR(alg))
diff --git a/crypto/chainiv.c b/crypto/chainiv.c
index 7c37a49..ba200b0 100644
--- a/crypto/chainiv.c
+++ b/crypto/chainiv.c
@@ -15,6 +15,7 @@
#include <crypto/internal/skcipher.h>
#include <crypto/rng.h>
+#include <crypto/crypto_wq.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -133,7 +134,7 @@
goto out;
}
- queued = schedule_work(&ctx->postponed);
+ queued = queue_work(kcrypto_wq, &ctx->postponed);
BUG_ON(!queued);
out:
diff --git a/crypto/cryptd.c b/crypto/cryptd.c
index d29e06b..d14b226 100644
--- a/crypto/cryptd.c
+++ b/crypto/cryptd.c
@@ -12,30 +12,31 @@
#include <crypto/algapi.h>
#include <crypto/internal/hash.h>
+#include <crypto/cryptd.h>
+#include <crypto/crypto_wq.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
-#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/scatterlist.h>
#include <linux/sched.h>
#include <linux/slab.h>
-#include <linux/spinlock.h>
-#define CRYPTD_MAX_QLEN 100
+#define CRYPTD_MAX_CPU_QLEN 100
-struct cryptd_state {
- spinlock_t lock;
- struct mutex mutex;
+struct cryptd_cpu_queue {
struct crypto_queue queue;
- struct task_struct *task;
+ struct work_struct work;
+};
+
+struct cryptd_queue {
+ struct cryptd_cpu_queue *cpu_queue;
};
struct cryptd_instance_ctx {
struct crypto_spawn spawn;
- struct cryptd_state *state;
+ struct cryptd_queue *queue;
};
struct cryptd_blkcipher_ctx {
@@ -54,11 +55,85 @@
crypto_completion_t complete;
};
-static inline struct cryptd_state *cryptd_get_state(struct crypto_tfm *tfm)
+static void cryptd_queue_worker(struct work_struct *work);
+
+static int cryptd_init_queue(struct cryptd_queue *queue,
+ unsigned int max_cpu_qlen)
+{
+ int cpu;
+ struct cryptd_cpu_queue *cpu_queue;
+
+ queue->cpu_queue = alloc_percpu(struct cryptd_cpu_queue);
+ if (!queue->cpu_queue)
+ return -ENOMEM;
+ for_each_possible_cpu(cpu) {
+ cpu_queue = per_cpu_ptr(queue->cpu_queue, cpu);
+ crypto_init_queue(&cpu_queue->queue, max_cpu_qlen);
+ INIT_WORK(&cpu_queue->work, cryptd_queue_worker);
+ }
+ return 0;
+}
+
+static void cryptd_fini_queue(struct cryptd_queue *queue)
+{
+ int cpu;
+ struct cryptd_cpu_queue *cpu_queue;
+
+ for_each_possible_cpu(cpu) {
+ cpu_queue = per_cpu_ptr(queue->cpu_queue, cpu);
+ BUG_ON(cpu_queue->queue.qlen);
+ }
+ free_percpu(queue->cpu_queue);
+}
+
+static int cryptd_enqueue_request(struct cryptd_queue *queue,
+ struct crypto_async_request *request)
+{
+ int cpu, err;
+ struct cryptd_cpu_queue *cpu_queue;
+
+ cpu = get_cpu();
+ cpu_queue = per_cpu_ptr(queue->cpu_queue, cpu);
+ err = crypto_enqueue_request(&cpu_queue->queue, request);
+ queue_work_on(cpu, kcrypto_wq, &cpu_queue->work);
+ put_cpu();
+
+ return err;
+}
+
+/* Called in workqueue context, do one real cryption work (via
+ * req->complete) and reschedule itself if there are more work to
+ * do. */
+static void cryptd_queue_worker(struct work_struct *work)
+{
+ struct cryptd_cpu_queue *cpu_queue;
+ struct crypto_async_request *req, *backlog;
+
+ cpu_queue = container_of(work, struct cryptd_cpu_queue, work);
+ /* Only handle one request at a time to avoid hogging crypto
+ * workqueue. preempt_disable/enable is used to prevent
+ * being preempted by cryptd_enqueue_request() */
+ preempt_disable();
+ backlog = crypto_get_backlog(&cpu_queue->queue);
+ req = crypto_dequeue_request(&cpu_queue->queue);
+ preempt_enable();
+
+ if (!req)
+ return;
+
+ if (backlog)
+ backlog->complete(backlog, -EINPROGRESS);
+ req->complete(req, 0);
+
+ if (cpu_queue->queue.qlen)
+ queue_work(kcrypto_wq, &cpu_queue->work);
+}
+
+static inline struct cryptd_queue *cryptd_get_queue(struct crypto_tfm *tfm)
{
struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
struct cryptd_instance_ctx *ictx = crypto_instance_ctx(inst);
- return ictx->state;
+ return ictx->queue;
}
static int cryptd_blkcipher_setkey(struct crypto_ablkcipher *parent,
@@ -130,19 +205,13 @@
{
struct cryptd_blkcipher_request_ctx *rctx = ablkcipher_request_ctx(req);
struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
- struct cryptd_state *state =
- cryptd_get_state(crypto_ablkcipher_tfm(tfm));
- int err;
+ struct cryptd_queue *queue;
+ queue = cryptd_get_queue(crypto_ablkcipher_tfm(tfm));
rctx->complete = req->base.complete;
req->base.complete = complete;
- spin_lock_bh(&state->lock);
- err = ablkcipher_enqueue_request(&state->queue, req);
- spin_unlock_bh(&state->lock);
-
- wake_up_process(state->task);
- return err;
+ return cryptd_enqueue_request(queue, &req->base);
}
static int cryptd_blkcipher_encrypt_enqueue(struct ablkcipher_request *req)
@@ -176,21 +245,12 @@
static void cryptd_blkcipher_exit_tfm(struct crypto_tfm *tfm)
{
struct cryptd_blkcipher_ctx *ctx = crypto_tfm_ctx(tfm);
- struct cryptd_state *state = cryptd_get_state(tfm);
- int active;
-
- mutex_lock(&state->mutex);
- active = ablkcipher_tfm_in_queue(&state->queue,
- __crypto_ablkcipher_cast(tfm));
- mutex_unlock(&state->mutex);
-
- BUG_ON(active);
crypto_free_blkcipher(ctx->child);
}
static struct crypto_instance *cryptd_alloc_instance(struct crypto_alg *alg,
- struct cryptd_state *state)
+ struct cryptd_queue *queue)
{
struct crypto_instance *inst;
struct cryptd_instance_ctx *ctx;
@@ -213,7 +273,7 @@
if (err)
goto out_free_inst;
- ctx->state = state;
+ ctx->queue = queue;
memcpy(inst->alg.cra_name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
@@ -231,7 +291,7 @@
}
static struct crypto_instance *cryptd_alloc_blkcipher(
- struct rtattr **tb, struct cryptd_state *state)
+ struct rtattr **tb, struct cryptd_queue *queue)
{
struct crypto_instance *inst;
struct crypto_alg *alg;
@@ -241,7 +301,7 @@
if (IS_ERR(alg))
return ERR_CAST(alg);
- inst = cryptd_alloc_instance(alg, state);
+ inst = cryptd_alloc_instance(alg, queue);
if (IS_ERR(inst))
goto out_put_alg;
@@ -289,15 +349,6 @@
static void cryptd_hash_exit_tfm(struct crypto_tfm *tfm)
{
struct cryptd_hash_ctx *ctx = crypto_tfm_ctx(tfm);
- struct cryptd_state *state = cryptd_get_state(tfm);
- int active;
-
- mutex_lock(&state->mutex);
- active = ahash_tfm_in_queue(&state->queue,
- __crypto_ahash_cast(tfm));
- mutex_unlock(&state->mutex);
-
- BUG_ON(active);
crypto_free_hash(ctx->child);
}
@@ -323,19 +374,13 @@
{
struct cryptd_hash_request_ctx *rctx = ahash_request_ctx(req);
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
- struct cryptd_state *state =
- cryptd_get_state(crypto_ahash_tfm(tfm));
- int err;
+ struct cryptd_queue *queue =
+ cryptd_get_queue(crypto_ahash_tfm(tfm));
rctx->complete = req->base.complete;
req->base.complete = complete;
- spin_lock_bh(&state->lock);
- err = ahash_enqueue_request(&state->queue, req);
- spin_unlock_bh(&state->lock);
-
- wake_up_process(state->task);
- return err;
+ return cryptd_enqueue_request(queue, &req->base);
}
static void cryptd_hash_init(struct crypto_async_request *req_async, int err)
@@ -468,7 +513,7 @@
}
static struct crypto_instance *cryptd_alloc_hash(
- struct rtattr **tb, struct cryptd_state *state)
+ struct rtattr **tb, struct cryptd_queue *queue)
{
struct crypto_instance *inst;
struct crypto_alg *alg;
@@ -478,7 +523,7 @@
if (IS_ERR(alg))
return ERR_PTR(PTR_ERR(alg));
- inst = cryptd_alloc_instance(alg, state);
+ inst = cryptd_alloc_instance(alg, queue);
if (IS_ERR(inst))
goto out_put_alg;
@@ -502,7 +547,7 @@
return inst;
}
-static struct cryptd_state state;
+static struct cryptd_queue queue;
static struct crypto_instance *cryptd_alloc(struct rtattr **tb)
{
@@ -514,9 +559,9 @@
switch (algt->type & algt->mask & CRYPTO_ALG_TYPE_MASK) {
case CRYPTO_ALG_TYPE_BLKCIPHER:
- return cryptd_alloc_blkcipher(tb, &state);
+ return cryptd_alloc_blkcipher(tb, &queue);
case CRYPTO_ALG_TYPE_DIGEST:
- return cryptd_alloc_hash(tb, &state);
+ return cryptd_alloc_hash(tb, &queue);
}
return ERR_PTR(-EINVAL);
@@ -537,82 +582,58 @@
.module = THIS_MODULE,
};
-static inline int cryptd_create_thread(struct cryptd_state *state,
- int (*fn)(void *data), const char *name)
+struct cryptd_ablkcipher *cryptd_alloc_ablkcipher(const char *alg_name,
+ u32 type, u32 mask)
{
- spin_lock_init(&state->lock);
- mutex_init(&state->mutex);
- crypto_init_queue(&state->queue, CRYPTD_MAX_QLEN);
+ char cryptd_alg_name[CRYPTO_MAX_ALG_NAME];
+ struct crypto_ablkcipher *tfm;
- state->task = kthread_run(fn, state, name);
- if (IS_ERR(state->task))
- return PTR_ERR(state->task);
+ if (snprintf(cryptd_alg_name, CRYPTO_MAX_ALG_NAME,
+ "cryptd(%s)", alg_name) >= CRYPTO_MAX_ALG_NAME)
+ return ERR_PTR(-EINVAL);
+ tfm = crypto_alloc_ablkcipher(cryptd_alg_name, type, mask);
+ if (IS_ERR(tfm))
+ return ERR_CAST(tfm);
+ if (crypto_ablkcipher_tfm(tfm)->__crt_alg->cra_module != THIS_MODULE) {
+ crypto_free_ablkcipher(tfm);
+ return ERR_PTR(-EINVAL);
+ }
- return 0;
+ return __cryptd_ablkcipher_cast(tfm);
}
+EXPORT_SYMBOL_GPL(cryptd_alloc_ablkcipher);
-static inline void cryptd_stop_thread(struct cryptd_state *state)
+struct crypto_blkcipher *cryptd_ablkcipher_child(struct cryptd_ablkcipher *tfm)
{
- BUG_ON(state->queue.qlen);
- kthread_stop(state->task);
+ struct cryptd_blkcipher_ctx *ctx = crypto_ablkcipher_ctx(&tfm->base);
+ return ctx->child;
}
+EXPORT_SYMBOL_GPL(cryptd_ablkcipher_child);
-static int cryptd_thread(void *data)
+void cryptd_free_ablkcipher(struct cryptd_ablkcipher *tfm)
{
- struct cryptd_state *state = data;
- int stop;
-
- current->flags |= PF_NOFREEZE;
-
- do {
- struct crypto_async_request *req, *backlog;
-
- mutex_lock(&state->mutex);
- __set_current_state(TASK_INTERRUPTIBLE);
-
- spin_lock_bh(&state->lock);
- backlog = crypto_get_backlog(&state->queue);
- req = crypto_dequeue_request(&state->queue);
- spin_unlock_bh(&state->lock);
-
- stop = kthread_should_stop();
-
- if (stop || req) {
- __set_current_state(TASK_RUNNING);
- if (req) {
- if (backlog)
- backlog->complete(backlog,
- -EINPROGRESS);
- req->complete(req, 0);
- }
- }
-
- mutex_unlock(&state->mutex);
-
- schedule();
- } while (!stop);
-
- return 0;
+ crypto_free_ablkcipher(&tfm->base);
}
+EXPORT_SYMBOL_GPL(cryptd_free_ablkcipher);
static int __init cryptd_init(void)
{
int err;
- err = cryptd_create_thread(&state, cryptd_thread, "cryptd");
+ err = cryptd_init_queue(&queue, CRYPTD_MAX_CPU_QLEN);
if (err)
return err;
err = crypto_register_template(&cryptd_tmpl);
if (err)
- kthread_stop(state.task);
+ cryptd_fini_queue(&queue);
return err;
}
static void __exit cryptd_exit(void)
{
- cryptd_stop_thread(&state);
+ cryptd_fini_queue(&queue);
crypto_unregister_template(&cryptd_tmpl);
}
diff --git a/crypto/crypto_wq.c b/crypto/crypto_wq.c
new file mode 100644
index 0000000..fdcf624
--- /dev/null
+++ b/crypto/crypto_wq.c
@@ -0,0 +1,38 @@
+/*
+ * Workqueue for crypto subsystem
+ *
+ * Copyright (c) 2009 Intel Corp.
+ * Author: Huang Ying <ying.huang@intel.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/workqueue.h>
+#include <crypto/algapi.h>
+#include <crypto/crypto_wq.h>
+
+struct workqueue_struct *kcrypto_wq;
+EXPORT_SYMBOL_GPL(kcrypto_wq);
+
+static int __init crypto_wq_init(void)
+{
+ kcrypto_wq = create_workqueue("crypto");
+ if (unlikely(!kcrypto_wq))
+ return -ENOMEM;
+ return 0;
+}
+
+static void __exit crypto_wq_exit(void)
+{
+ destroy_workqueue(kcrypto_wq);
+}
+
+module_init(crypto_wq_init);
+module_exit(crypto_wq_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Workqueue for crypto subsystem");
diff --git a/crypto/gf128mul.c b/crypto/gf128mul.c
index ecbeaa1..a90d260 100644
--- a/crypto/gf128mul.c
+++ b/crypto/gf128mul.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006, Rik Snel <rsnel@cube.dyndns.org>
*
* Based on Dr Brian Gladman's (GPL'd) work published at
- * http://fp.gladman.plus.com/cryptography_technology/index.htm
+ * http://gladman.plushost.co.uk/oldsite/cryptography_technology/index.php
* See the original copyright notice below.
*
* This program is free software; you can redistribute it and/or modify it
diff --git a/crypto/internal.h b/crypto/internal.h
index 3c19a27..fc76e1f 100644
--- a/crypto/internal.h
+++ b/crypto/internal.h
@@ -109,8 +109,10 @@
void crypto_shoot_alg(struct crypto_alg *alg);
struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type,
u32 mask);
-struct crypto_tfm *crypto_create_tfm(struct crypto_alg *alg,
- const struct crypto_type *frontend);
+void *crypto_create_tfm(struct crypto_alg *alg,
+ const struct crypto_type *frontend);
+void *crypto_alloc_tfm(const char *alg_name,
+ const struct crypto_type *frontend, u32 type, u32 mask);
int crypto_register_instance(struct crypto_template *tmpl,
struct crypto_instance *inst);
diff --git a/crypto/pcompress.c b/crypto/pcompress.c
new file mode 100644
index 0000000..ca9a4af
--- /dev/null
+++ b/crypto/pcompress.c
@@ -0,0 +1,97 @@
+/*
+ * Cryptographic API.
+ *
+ * Partial (de)compression operations.
+ *
+ * Copyright 2008 Sony 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; version 2 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/crypto.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/string.h>
+
+#include <crypto/compress.h>
+
+#include "internal.h"
+
+
+static int crypto_pcomp_init(struct crypto_tfm *tfm, u32 type, u32 mask)
+{
+ return 0;
+}
+
+static unsigned int crypto_pcomp_extsize(struct crypto_alg *alg,
+ const struct crypto_type *frontend)
+{
+ return alg->cra_ctxsize;
+}
+
+static int crypto_pcomp_init_tfm(struct crypto_tfm *tfm,
+ const struct crypto_type *frontend)
+{
+ return 0;
+}
+
+static void crypto_pcomp_show(struct seq_file *m, struct crypto_alg *alg)
+ __attribute__ ((unused));
+static void crypto_pcomp_show(struct seq_file *m, struct crypto_alg *alg)
+{
+ seq_printf(m, "type : pcomp\n");
+}
+
+static const struct crypto_type crypto_pcomp_type = {
+ .extsize = crypto_pcomp_extsize,
+ .init = crypto_pcomp_init,
+ .init_tfm = crypto_pcomp_init_tfm,
+#ifdef CONFIG_PROC_FS
+ .show = crypto_pcomp_show,
+#endif
+ .maskclear = ~CRYPTO_ALG_TYPE_MASK,
+ .maskset = CRYPTO_ALG_TYPE_MASK,
+ .type = CRYPTO_ALG_TYPE_PCOMPRESS,
+ .tfmsize = offsetof(struct crypto_pcomp, base),
+};
+
+struct crypto_pcomp *crypto_alloc_pcomp(const char *alg_name, u32 type,
+ u32 mask)
+{
+ return crypto_alloc_tfm(alg_name, &crypto_pcomp_type, type, mask);
+}
+EXPORT_SYMBOL_GPL(crypto_alloc_pcomp);
+
+int crypto_register_pcomp(struct pcomp_alg *alg)
+{
+ struct crypto_alg *base = &alg->base;
+
+ base->cra_type = &crypto_pcomp_type;
+ base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
+ base->cra_flags |= CRYPTO_ALG_TYPE_PCOMPRESS;
+
+ return crypto_register_alg(base);
+}
+EXPORT_SYMBOL_GPL(crypto_register_pcomp);
+
+int crypto_unregister_pcomp(struct pcomp_alg *alg)
+{
+ return crypto_unregister_alg(&alg->base);
+}
+EXPORT_SYMBOL_GPL(crypto_unregister_pcomp);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Partial (de)compression type");
+MODULE_AUTHOR("Sony Corporation");
diff --git a/crypto/sha256_generic.c b/crypto/sha256_generic.c
index caa3542..6349d83 100644
--- a/crypto/sha256_generic.c
+++ b/crypto/sha256_generic.c
@@ -2,7 +2,7 @@
* Cryptographic API.
*
* SHA-256, as specified in
- * http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf
+ * http://csrc.nist.gov/groups/STM/cavp/documents/shs/sha256-384-512.pdf
*
* SHA-256 code by Jean-Luc Cooke <jlcooke@certainkey.com>.
*
diff --git a/crypto/shash.c b/crypto/shash.c
index d5a2b61..7a65973 100644
--- a/crypto/shash.c
+++ b/crypto/shash.c
@@ -18,15 +18,10 @@
#include <linux/slab.h>
#include <linux/seq_file.h>
-static const struct crypto_type crypto_shash_type;
-
-static inline struct crypto_shash *__crypto_shash_cast(struct crypto_tfm *tfm)
-{
- return container_of(tfm, struct crypto_shash, base);
-}
-
#include "internal.h"
+static const struct crypto_type crypto_shash_type;
+
static int shash_setkey_unaligned(struct crypto_shash *tfm, const u8 *key,
unsigned int keylen)
{
@@ -282,8 +277,7 @@
if (!crypto_mod_get(calg))
return -EAGAIN;
- shash = __crypto_shash_cast(crypto_create_tfm(
- calg, &crypto_shash_type));
+ shash = crypto_create_tfm(calg, &crypto_shash_type);
if (IS_ERR(shash)) {
crypto_mod_put(calg);
return PTR_ERR(shash);
@@ -391,8 +385,7 @@
if (!crypto_mod_get(calg))
return -EAGAIN;
- shash = __crypto_shash_cast(crypto_create_tfm(
- calg, &crypto_shash_type));
+ shash = crypto_create_tfm(calg, &crypto_shash_type);
if (IS_ERR(shash)) {
crypto_mod_put(calg);
return PTR_ERR(shash);
@@ -442,8 +435,6 @@
static int crypto_shash_init_tfm(struct crypto_tfm *tfm,
const struct crypto_type *frontend)
{
- if (frontend->type != CRYPTO_ALG_TYPE_SHASH)
- return -EINVAL;
return 0;
}
@@ -482,8 +473,7 @@
struct crypto_shash *crypto_alloc_shash(const char *alg_name, u32 type,
u32 mask)
{
- return __crypto_shash_cast(
- crypto_alloc_tfm(alg_name, &crypto_shash_type, type, mask));
+ return crypto_alloc_tfm(alg_name, &crypto_shash_type, type, mask);
}
EXPORT_SYMBOL_GPL(crypto_alloc_shash);
diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c
index 28a45a1..c3c9124 100644
--- a/crypto/tcrypt.c
+++ b/crypto/tcrypt.c
@@ -53,7 +53,7 @@
"cast6", "arc4", "michael_mic", "deflate", "crc32c", "tea", "xtea",
"khazad", "wp512", "wp384", "wp256", "tnepres", "xeta", "fcrypt",
"camellia", "seed", "salsa20", "rmd128", "rmd160", "rmd256", "rmd320",
- "lzo", "cts", NULL
+ "lzo", "cts", "zlib", NULL
};
static int test_cipher_jiffies(struct blkcipher_desc *desc, int enc,
@@ -661,6 +661,10 @@
tcrypt_test("ecb(seed)");
break;
+ case 44:
+ tcrypt_test("zlib");
+ break;
+
case 100:
tcrypt_test("hmac(md5)");
break;
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index a75f11f..b50c3c6 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -72,6 +72,13 @@
} comp, decomp;
};
+struct pcomp_test_suite {
+ struct {
+ struct pcomp_testvec *vecs;
+ unsigned int count;
+ } comp, decomp;
+};
+
struct hash_test_suite {
struct hash_testvec *vecs;
unsigned int count;
@@ -86,6 +93,7 @@
struct aead_test_suite aead;
struct cipher_test_suite cipher;
struct comp_test_suite comp;
+ struct pcomp_test_suite pcomp;
struct hash_test_suite hash;
} suite;
};
@@ -898,6 +906,159 @@
return ret;
}
+static int test_pcomp(struct crypto_pcomp *tfm,
+ struct pcomp_testvec *ctemplate,
+ struct pcomp_testvec *dtemplate, int ctcount,
+ int dtcount)
+{
+ const char *algo = crypto_tfm_alg_driver_name(crypto_pcomp_tfm(tfm));
+ unsigned int i;
+ char result[COMP_BUF_SIZE];
+ int error;
+
+ for (i = 0; i < ctcount; i++) {
+ struct comp_request req;
+
+ error = crypto_compress_setup(tfm, ctemplate[i].params,
+ ctemplate[i].paramsize);
+ if (error) {
+ pr_err("alg: pcomp: compression setup failed on test "
+ "%d for %s: error=%d\n", i + 1, algo, error);
+ return error;
+ }
+
+ error = crypto_compress_init(tfm);
+ if (error) {
+ pr_err("alg: pcomp: compression init failed on test "
+ "%d for %s: error=%d\n", i + 1, algo, error);
+ return error;
+ }
+
+ memset(result, 0, sizeof(result));
+
+ req.next_in = ctemplate[i].input;
+ req.avail_in = ctemplate[i].inlen / 2;
+ req.next_out = result;
+ req.avail_out = ctemplate[i].outlen / 2;
+
+ error = crypto_compress_update(tfm, &req);
+ if (error && (error != -EAGAIN || req.avail_in)) {
+ pr_err("alg: pcomp: compression update failed on test "
+ "%d for %s: error=%d\n", i + 1, algo, error);
+ return error;
+ }
+
+ /* Add remaining input data */
+ req.avail_in += (ctemplate[i].inlen + 1) / 2;
+
+ error = crypto_compress_update(tfm, &req);
+ if (error && (error != -EAGAIN || req.avail_in)) {
+ pr_err("alg: pcomp: compression update failed on test "
+ "%d for %s: error=%d\n", i + 1, algo, error);
+ return error;
+ }
+
+ /* Provide remaining output space */
+ req.avail_out += COMP_BUF_SIZE - ctemplate[i].outlen / 2;
+
+ error = crypto_compress_final(tfm, &req);
+ if (error) {
+ pr_err("alg: pcomp: compression final failed on test "
+ "%d for %s: error=%d\n", i + 1, algo, error);
+ return error;
+ }
+
+ if (COMP_BUF_SIZE - req.avail_out != ctemplate[i].outlen) {
+ pr_err("alg: comp: Compression test %d failed for %s: "
+ "output len = %d (expected %d)\n", i + 1, algo,
+ COMP_BUF_SIZE - req.avail_out,
+ ctemplate[i].outlen);
+ return -EINVAL;
+ }
+
+ if (memcmp(result, ctemplate[i].output, ctemplate[i].outlen)) {
+ pr_err("alg: pcomp: Compression test %d failed for "
+ "%s\n", i + 1, algo);
+ hexdump(result, ctemplate[i].outlen);
+ return -EINVAL;
+ }
+ }
+
+ for (i = 0; i < dtcount; i++) {
+ struct comp_request req;
+
+ error = crypto_decompress_setup(tfm, dtemplate[i].params,
+ dtemplate[i].paramsize);
+ if (error) {
+ pr_err("alg: pcomp: decompression setup failed on "
+ "test %d for %s: error=%d\n", i + 1, algo,
+ error);
+ return error;
+ }
+
+ error = crypto_decompress_init(tfm);
+ if (error) {
+ pr_err("alg: pcomp: decompression init failed on test "
+ "%d for %s: error=%d\n", i + 1, algo, error);
+ return error;
+ }
+
+ memset(result, 0, sizeof(result));
+
+ req.next_in = dtemplate[i].input;
+ req.avail_in = dtemplate[i].inlen / 2;
+ req.next_out = result;
+ req.avail_out = dtemplate[i].outlen / 2;
+
+ error = crypto_decompress_update(tfm, &req);
+ if (error && (error != -EAGAIN || req.avail_in)) {
+ pr_err("alg: pcomp: decompression update failed on "
+ "test %d for %s: error=%d\n", i + 1, algo,
+ error);
+ return error;
+ }
+
+ /* Add remaining input data */
+ req.avail_in += (dtemplate[i].inlen + 1) / 2;
+
+ error = crypto_decompress_update(tfm, &req);
+ if (error && (error != -EAGAIN || req.avail_in)) {
+ pr_err("alg: pcomp: decompression update failed on "
+ "test %d for %s: error=%d\n", i + 1, algo,
+ error);
+ return error;
+ }
+
+ /* Provide remaining output space */
+ req.avail_out += COMP_BUF_SIZE - dtemplate[i].outlen / 2;
+
+ error = crypto_decompress_final(tfm, &req);
+ if (error && (error != -EAGAIN || req.avail_in)) {
+ pr_err("alg: pcomp: decompression final failed on "
+ "test %d for %s: error=%d\n", i + 1, algo,
+ error);
+ return error;
+ }
+
+ if (COMP_BUF_SIZE - req.avail_out != dtemplate[i].outlen) {
+ pr_err("alg: comp: Decompression test %d failed for "
+ "%s: output len = %d (expected %d)\n", i + 1,
+ algo, COMP_BUF_SIZE - req.avail_out,
+ dtemplate[i].outlen);
+ return -EINVAL;
+ }
+
+ if (memcmp(result, dtemplate[i].output, dtemplate[i].outlen)) {
+ pr_err("alg: pcomp: Decompression test %d failed for "
+ "%s\n", i + 1, algo);
+ hexdump(result, dtemplate[i].outlen);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
static int alg_test_aead(const struct alg_test_desc *desc, const char *driver,
u32 type, u32 mask)
{
@@ -1007,6 +1168,28 @@
return err;
}
+static int alg_test_pcomp(const struct alg_test_desc *desc, const char *driver,
+ u32 type, u32 mask)
+{
+ struct crypto_pcomp *tfm;
+ int err;
+
+ tfm = crypto_alloc_pcomp(driver, type, mask);
+ if (IS_ERR(tfm)) {
+ pr_err("alg: pcomp: Failed to load transform for %s: %ld\n",
+ driver, PTR_ERR(tfm));
+ return PTR_ERR(tfm);
+ }
+
+ err = test_pcomp(tfm, desc->suite.pcomp.comp.vecs,
+ desc->suite.pcomp.decomp.vecs,
+ desc->suite.pcomp.comp.count,
+ desc->suite.pcomp.decomp.count);
+
+ crypto_free_pcomp(tfm);
+ return err;
+}
+
static int alg_test_hash(const struct alg_test_desc *desc, const char *driver,
u32 type, u32 mask)
{
@@ -1835,6 +2018,21 @@
}
}
}
+ }, {
+ .alg = "zlib",
+ .test = alg_test_pcomp,
+ .suite = {
+ .pcomp = {
+ .comp = {
+ .vecs = zlib_comp_tv_template,
+ .count = ZLIB_COMP_TEST_VECTORS
+ },
+ .decomp = {
+ .vecs = zlib_decomp_tv_template,
+ .count = ZLIB_DECOMP_TEST_VECTORS
+ }
+ }
+ }
}
};
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index 132953e..526f00a 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -15,6 +15,11 @@
#ifndef _CRYPTO_TESTMGR_H
#define _CRYPTO_TESTMGR_H
+#include <linux/netlink.h>
+#include <linux/zlib.h>
+
+#include <crypto/compress.h>
+
#define MAX_DIGEST_SIZE 64
#define MAX_TAP 8
@@ -8347,10 +8352,19 @@
char output[COMP_BUF_SIZE];
};
+struct pcomp_testvec {
+ void *params;
+ unsigned int paramsize;
+ int inlen, outlen;
+ char input[COMP_BUF_SIZE];
+ char output[COMP_BUF_SIZE];
+};
+
/*
* Deflate test vectors (null-terminated strings).
* Params: winbits=-11, Z_DEFAULT_COMPRESSION, MAX_MEM_LEVEL.
*/
+
#define DEFLATE_COMP_TEST_VECTORS 2
#define DEFLATE_DECOMP_TEST_VECTORS 2
@@ -8426,6 +8440,139 @@
},
};
+#define ZLIB_COMP_TEST_VECTORS 2
+#define ZLIB_DECOMP_TEST_VECTORS 2
+
+static const struct {
+ struct nlattr nla;
+ int val;
+} deflate_comp_params[] = {
+ {
+ .nla = {
+ .nla_len = NLA_HDRLEN + sizeof(int),
+ .nla_type = ZLIB_COMP_LEVEL,
+ },
+ .val = Z_DEFAULT_COMPRESSION,
+ }, {
+ .nla = {
+ .nla_len = NLA_HDRLEN + sizeof(int),
+ .nla_type = ZLIB_COMP_METHOD,
+ },
+ .val = Z_DEFLATED,
+ }, {
+ .nla = {
+ .nla_len = NLA_HDRLEN + sizeof(int),
+ .nla_type = ZLIB_COMP_WINDOWBITS,
+ },
+ .val = -11,
+ }, {
+ .nla = {
+ .nla_len = NLA_HDRLEN + sizeof(int),
+ .nla_type = ZLIB_COMP_MEMLEVEL,
+ },
+ .val = MAX_MEM_LEVEL,
+ }, {
+ .nla = {
+ .nla_len = NLA_HDRLEN + sizeof(int),
+ .nla_type = ZLIB_COMP_STRATEGY,
+ },
+ .val = Z_DEFAULT_STRATEGY,
+ }
+};
+
+static const struct {
+ struct nlattr nla;
+ int val;
+} deflate_decomp_params[] = {
+ {
+ .nla = {
+ .nla_len = NLA_HDRLEN + sizeof(int),
+ .nla_type = ZLIB_DECOMP_WINDOWBITS,
+ },
+ .val = -11,
+ }
+};
+
+static struct pcomp_testvec zlib_comp_tv_template[] = {
+ {
+ .params = &deflate_comp_params,
+ .paramsize = sizeof(deflate_comp_params),
+ .inlen = 70,
+ .outlen = 38,
+ .input = "Join us now and share the software "
+ "Join us now and share the software ",
+ .output = "\xf3\xca\xcf\xcc\x53\x28\x2d\x56"
+ "\xc8\xcb\x2f\x57\x48\xcc\x4b\x51"
+ "\x28\xce\x48\x2c\x4a\x55\x28\xc9"
+ "\x48\x55\x28\xce\x4f\x2b\x29\x07"
+ "\x71\xbc\x08\x2b\x01\x00",
+ }, {
+ .params = &deflate_comp_params,
+ .paramsize = sizeof(deflate_comp_params),
+ .inlen = 191,
+ .outlen = 122,
+ .input = "This document describes a compression method based on the DEFLATE"
+ "compression algorithm. This document defines the application of "
+ "the DEFLATE algorithm to the IP Payload Compression Protocol.",
+ .output = "\x5d\x8d\x31\x0e\xc2\x30\x10\x04"
+ "\xbf\xb2\x2f\xc8\x1f\x10\x04\x09"
+ "\x89\xc2\x85\x3f\x70\xb1\x2f\xf8"
+ "\x24\xdb\x67\xd9\x47\xc1\xef\x49"
+ "\x68\x12\x51\xae\x76\x67\xd6\x27"
+ "\x19\x88\x1a\xde\x85\xab\x21\xf2"
+ "\x08\x5d\x16\x1e\x20\x04\x2d\xad"
+ "\xf3\x18\xa2\x15\x85\x2d\x69\xc4"
+ "\x42\x83\x23\xb6\x6c\x89\x71\x9b"
+ "\xef\xcf\x8b\x9f\xcf\x33\xca\x2f"
+ "\xed\x62\xa9\x4c\x80\xff\x13\xaf"
+ "\x52\x37\xed\x0e\x52\x6b\x59\x02"
+ "\xd9\x4e\xe8\x7a\x76\x1d\x02\x98"
+ "\xfe\x8a\x87\x83\xa3\x4f\x56\x8a"
+ "\xb8\x9e\x8e\x5c\x57\xd3\xa0\x79"
+ "\xfa\x02",
+ },
+};
+
+static struct pcomp_testvec zlib_decomp_tv_template[] = {
+ {
+ .params = &deflate_decomp_params,
+ .paramsize = sizeof(deflate_decomp_params),
+ .inlen = 122,
+ .outlen = 191,
+ .input = "\x5d\x8d\x31\x0e\xc2\x30\x10\x04"
+ "\xbf\xb2\x2f\xc8\x1f\x10\x04\x09"
+ "\x89\xc2\x85\x3f\x70\xb1\x2f\xf8"
+ "\x24\xdb\x67\xd9\x47\xc1\xef\x49"
+ "\x68\x12\x51\xae\x76\x67\xd6\x27"
+ "\x19\x88\x1a\xde\x85\xab\x21\xf2"
+ "\x08\x5d\x16\x1e\x20\x04\x2d\xad"
+ "\xf3\x18\xa2\x15\x85\x2d\x69\xc4"
+ "\x42\x83\x23\xb6\x6c\x89\x71\x9b"
+ "\xef\xcf\x8b\x9f\xcf\x33\xca\x2f"
+ "\xed\x62\xa9\x4c\x80\xff\x13\xaf"
+ "\x52\x37\xed\x0e\x52\x6b\x59\x02"
+ "\xd9\x4e\xe8\x7a\x76\x1d\x02\x98"
+ "\xfe\x8a\x87\x83\xa3\x4f\x56\x8a"
+ "\xb8\x9e\x8e\x5c\x57\xd3\xa0\x79"
+ "\xfa\x02",
+ .output = "This document describes a compression method based on the DEFLATE"
+ "compression algorithm. This document defines the application of "
+ "the DEFLATE algorithm to the IP Payload Compression Protocol.",
+ }, {
+ .params = &deflate_decomp_params,
+ .paramsize = sizeof(deflate_decomp_params),
+ .inlen = 38,
+ .outlen = 70,
+ .input = "\xf3\xca\xcf\xcc\x53\x28\x2d\x56"
+ "\xc8\xcb\x2f\x57\x48\xcc\x4b\x51"
+ "\x28\xce\x48\x2c\x4a\x55\x28\xc9"
+ "\x48\x55\x28\xce\x4f\x2b\x29\x07"
+ "\x71\xbc\x08\x2b\x01\x00",
+ .output = "Join us now and share the software "
+ "Join us now and share the software ",
+ },
+};
+
/*
* LZO test vectors (null-terminated strings).
*/
diff --git a/crypto/zlib.c b/crypto/zlib.c
new file mode 100644
index 0000000..33609ba
--- /dev/null
+++ b/crypto/zlib.c
@@ -0,0 +1,378 @@
+/*
+ * Cryptographic API.
+ *
+ * Zlib algorithm
+ *
+ * Copyright 2008 Sony Corporation
+ *
+ * Based on deflate.c, which is
+ * Copyright (c) 2003 James Morris <jmorris@intercode.com.au>
+ *
+ * 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.
+ *
+ * FIXME: deflate transforms will require up to a total of about 436k of kernel
+ * memory on i386 (390k for compression, the rest for decompression), as the
+ * current zlib kernel code uses a worst case pre-allocation system by default.
+ * This needs to be fixed so that the amount of memory required is properly
+ * related to the winbits and memlevel parameters.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/zlib.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/net.h>
+#include <linux/slab.h>
+
+#include <crypto/internal/compress.h>
+
+#include <net/netlink.h>
+
+
+struct zlib_ctx {
+ struct z_stream_s comp_stream;
+ struct z_stream_s decomp_stream;
+ int decomp_windowBits;
+};
+
+
+static void zlib_comp_exit(struct zlib_ctx *ctx)
+{
+ struct z_stream_s *stream = &ctx->comp_stream;
+
+ if (stream->workspace) {
+ zlib_deflateEnd(stream);
+ vfree(stream->workspace);
+ stream->workspace = NULL;
+ }
+}
+
+static void zlib_decomp_exit(struct zlib_ctx *ctx)
+{
+ struct z_stream_s *stream = &ctx->decomp_stream;
+
+ if (stream->workspace) {
+ zlib_inflateEnd(stream);
+ kfree(stream->workspace);
+ stream->workspace = NULL;
+ }
+}
+
+static int zlib_init(struct crypto_tfm *tfm)
+{
+ return 0;
+}
+
+static void zlib_exit(struct crypto_tfm *tfm)
+{
+ struct zlib_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ zlib_comp_exit(ctx);
+ zlib_decomp_exit(ctx);
+}
+
+
+static int zlib_compress_setup(struct crypto_pcomp *tfm, void *params,
+ unsigned int len)
+{
+ struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
+ struct z_stream_s *stream = &ctx->comp_stream;
+ struct nlattr *tb[ZLIB_COMP_MAX + 1];
+ size_t workspacesize;
+ int ret;
+
+ ret = nla_parse(tb, ZLIB_COMP_MAX, params, len, NULL);
+ if (ret)
+ return ret;
+
+ zlib_comp_exit(ctx);
+
+ workspacesize = zlib_deflate_workspacesize();
+ stream->workspace = vmalloc(workspacesize);
+ if (!stream->workspace)
+ return -ENOMEM;
+
+ memset(stream->workspace, 0, workspacesize);
+ ret = zlib_deflateInit2(stream,
+ tb[ZLIB_COMP_LEVEL]
+ ? nla_get_u32(tb[ZLIB_COMP_LEVEL])
+ : Z_DEFAULT_COMPRESSION,
+ tb[ZLIB_COMP_METHOD]
+ ? nla_get_u32(tb[ZLIB_COMP_METHOD])
+ : Z_DEFLATED,
+ tb[ZLIB_COMP_WINDOWBITS]
+ ? nla_get_u32(tb[ZLIB_COMP_WINDOWBITS])
+ : MAX_WBITS,
+ tb[ZLIB_COMP_MEMLEVEL]
+ ? nla_get_u32(tb[ZLIB_COMP_MEMLEVEL])
+ : DEF_MEM_LEVEL,
+ tb[ZLIB_COMP_STRATEGY]
+ ? nla_get_u32(tb[ZLIB_COMP_STRATEGY])
+ : Z_DEFAULT_STRATEGY);
+ if (ret != Z_OK) {
+ vfree(stream->workspace);
+ stream->workspace = NULL;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int zlib_compress_init(struct crypto_pcomp *tfm)
+{
+ int ret;
+ struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
+ struct z_stream_s *stream = &dctx->comp_stream;
+
+ ret = zlib_deflateReset(stream);
+ if (ret != Z_OK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int zlib_compress_update(struct crypto_pcomp *tfm,
+ struct comp_request *req)
+{
+ int ret;
+ struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
+ struct z_stream_s *stream = &dctx->comp_stream;
+
+ pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
+ stream->next_in = req->next_in;
+ stream->avail_in = req->avail_in;
+ stream->next_out = req->next_out;
+ stream->avail_out = req->avail_out;
+
+ ret = zlib_deflate(stream, Z_NO_FLUSH);
+ switch (ret) {
+ case Z_OK:
+ break;
+
+ case Z_BUF_ERROR:
+ pr_debug("zlib_deflate could not make progress\n");
+ return -EAGAIN;
+
+ default:
+ pr_debug("zlib_deflate failed %d\n", ret);
+ return -EINVAL;
+ }
+
+ pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
+ stream->avail_in, stream->avail_out,
+ req->avail_in - stream->avail_in,
+ req->avail_out - stream->avail_out);
+ req->next_in = stream->next_in;
+ req->avail_in = stream->avail_in;
+ req->next_out = stream->next_out;
+ req->avail_out = stream->avail_out;
+ return 0;
+}
+
+static int zlib_compress_final(struct crypto_pcomp *tfm,
+ struct comp_request *req)
+{
+ int ret;
+ struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
+ struct z_stream_s *stream = &dctx->comp_stream;
+
+ pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
+ stream->next_in = req->next_in;
+ stream->avail_in = req->avail_in;
+ stream->next_out = req->next_out;
+ stream->avail_out = req->avail_out;
+
+ ret = zlib_deflate(stream, Z_FINISH);
+ if (ret != Z_STREAM_END) {
+ pr_debug("zlib_deflate failed %d\n", ret);
+ return -EINVAL;
+ }
+
+ pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
+ stream->avail_in, stream->avail_out,
+ req->avail_in - stream->avail_in,
+ req->avail_out - stream->avail_out);
+ req->next_in = stream->next_in;
+ req->avail_in = stream->avail_in;
+ req->next_out = stream->next_out;
+ req->avail_out = stream->avail_out;
+ return 0;
+}
+
+
+static int zlib_decompress_setup(struct crypto_pcomp *tfm, void *params,
+ unsigned int len)
+{
+ struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
+ struct z_stream_s *stream = &ctx->decomp_stream;
+ struct nlattr *tb[ZLIB_DECOMP_MAX + 1];
+ int ret = 0;
+
+ ret = nla_parse(tb, ZLIB_DECOMP_MAX, params, len, NULL);
+ if (ret)
+ return ret;
+
+ zlib_decomp_exit(ctx);
+
+ ctx->decomp_windowBits = tb[ZLIB_DECOMP_WINDOWBITS]
+ ? nla_get_u32(tb[ZLIB_DECOMP_WINDOWBITS])
+ : DEF_WBITS;
+
+ stream->workspace = kzalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
+ if (!stream->workspace)
+ return -ENOMEM;
+
+ ret = zlib_inflateInit2(stream, ctx->decomp_windowBits);
+ if (ret != Z_OK) {
+ kfree(stream->workspace);
+ stream->workspace = NULL;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int zlib_decompress_init(struct crypto_pcomp *tfm)
+{
+ int ret;
+ struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
+ struct z_stream_s *stream = &dctx->decomp_stream;
+
+ ret = zlib_inflateReset(stream);
+ if (ret != Z_OK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int zlib_decompress_update(struct crypto_pcomp *tfm,
+ struct comp_request *req)
+{
+ int ret;
+ struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
+ struct z_stream_s *stream = &dctx->decomp_stream;
+
+ pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
+ stream->next_in = req->next_in;
+ stream->avail_in = req->avail_in;
+ stream->next_out = req->next_out;
+ stream->avail_out = req->avail_out;
+
+ ret = zlib_inflate(stream, Z_SYNC_FLUSH);
+ switch (ret) {
+ case Z_OK:
+ case Z_STREAM_END:
+ break;
+
+ case Z_BUF_ERROR:
+ pr_debug("zlib_inflate could not make progress\n");
+ return -EAGAIN;
+
+ default:
+ pr_debug("zlib_inflate failed %d\n", ret);
+ return -EINVAL;
+ }
+
+ pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
+ stream->avail_in, stream->avail_out,
+ req->avail_in - stream->avail_in,
+ req->avail_out - stream->avail_out);
+ req->next_in = stream->next_in;
+ req->avail_in = stream->avail_in;
+ req->next_out = stream->next_out;
+ req->avail_out = stream->avail_out;
+ return 0;
+}
+
+static int zlib_decompress_final(struct crypto_pcomp *tfm,
+ struct comp_request *req)
+{
+ int ret;
+ struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
+ struct z_stream_s *stream = &dctx->decomp_stream;
+
+ pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
+ stream->next_in = req->next_in;
+ stream->avail_in = req->avail_in;
+ stream->next_out = req->next_out;
+ stream->avail_out = req->avail_out;
+
+ if (dctx->decomp_windowBits < 0) {
+ ret = zlib_inflate(stream, Z_SYNC_FLUSH);
+ /*
+ * Work around a bug in zlib, which sometimes wants to taste an
+ * extra byte when being used in the (undocumented) raw deflate
+ * mode. (From USAGI).
+ */
+ if (ret == Z_OK && !stream->avail_in && stream->avail_out) {
+ const void *saved_next_in = stream->next_in;
+ u8 zerostuff = 0;
+
+ stream->next_in = &zerostuff;
+ stream->avail_in = 1;
+ ret = zlib_inflate(stream, Z_FINISH);
+ stream->next_in = saved_next_in;
+ stream->avail_in = 0;
+ }
+ } else
+ ret = zlib_inflate(stream, Z_FINISH);
+ if (ret != Z_STREAM_END) {
+ pr_debug("zlib_inflate failed %d\n", ret);
+ return -EINVAL;
+ }
+
+ pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
+ stream->avail_in, stream->avail_out,
+ req->avail_in - stream->avail_in,
+ req->avail_out - stream->avail_out);
+ req->next_in = stream->next_in;
+ req->avail_in = stream->avail_in;
+ req->next_out = stream->next_out;
+ req->avail_out = stream->avail_out;
+ return 0;
+}
+
+
+static struct pcomp_alg zlib_alg = {
+ .compress_setup = zlib_compress_setup,
+ .compress_init = zlib_compress_init,
+ .compress_update = zlib_compress_update,
+ .compress_final = zlib_compress_final,
+ .decompress_setup = zlib_decompress_setup,
+ .decompress_init = zlib_decompress_init,
+ .decompress_update = zlib_decompress_update,
+ .decompress_final = zlib_decompress_final,
+
+ .base = {
+ .cra_name = "zlib",
+ .cra_flags = CRYPTO_ALG_TYPE_PCOMPRESS,
+ .cra_ctxsize = sizeof(struct zlib_ctx),
+ .cra_module = THIS_MODULE,
+ .cra_init = zlib_init,
+ .cra_exit = zlib_exit,
+ }
+};
+
+static int __init zlib_mod_init(void)
+{
+ return crypto_register_pcomp(&zlib_alg);
+}
+
+static void __exit zlib_mod_fini(void)
+{
+ crypto_unregister_pcomp(&zlib_alg);
+}
+
+module_init(zlib_mod_init);
+module_exit(zlib_mod_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Zlib Compression Algorithm");
+MODULE_AUTHOR("Sony Corporation");
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index 8822eca..5fab647 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -20,6 +20,20 @@
If unsure, say Y.
+config HW_RANDOM_TIMERIOMEM
+ tristate "Timer IOMEM HW Random Number Generator support"
+ depends on HW_RANDOM && HAS_IOMEM
+ ---help---
+ This driver provides kernel-side support for a generic Random
+ Number Generator used by reading a 'dumb' iomem address that
+ is to be read no faster than, for example, once a second;
+ the default FPGA bitstream on the TS-7800 has such functionality.
+
+ To compile this driver as a module, choose M here: the
+ module will be called timeriomem-rng.
+
+ If unsure, say Y.
+
config HW_RANDOM_INTEL
tristate "Intel HW Random Number Generator support"
depends on HW_RANDOM && (X86 || IA64) && PCI
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index b6effb7..e81d21a 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_HW_RANDOM) += rng-core.o
rng-core-y := core.o
+obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o
obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o
obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o
obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o
diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c
new file mode 100644
index 0000000..10ad41b
--- /dev/null
+++ b/drivers/char/hw_random/timeriomem-rng.c
@@ -0,0 +1,151 @@
+/*
+ * drivers/char/hw_random/timeriomem-rng.c
+ *
+ * Copyright (C) 2009 Alexander Clouter <alex@digriz.org.uk>
+ *
+ * Derived from drivers/char/hw_random/omap-rng.c
+ * Copyright 2005 (c) MontaVista Software, Inc.
+ * Author: Deepak Saxena <dsaxena@plexity.net>
+ *
+ * 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.
+ *
+ * Overview:
+ * This driver is useful for platforms that have an IO range that provides
+ * periodic random data from a single IO memory address. All the platform
+ * has to do is provide the address and 'wait time' that new data becomes
+ * available.
+ *
+ * TODO: add support for reading sizes other than 32bits and masking
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/timeriomem-rng.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/completion.h>
+
+static struct timeriomem_rng_data *timeriomem_rng_data;
+
+static void timeriomem_rng_trigger(unsigned long);
+static DEFINE_TIMER(timeriomem_rng_timer, timeriomem_rng_trigger, 0, 0);
+
+/*
+ * have data return 1, however return 0 if we have nothing
+ */
+static int timeriomem_rng_data_present(struct hwrng *rng, int wait)
+{
+ if (rng->priv == 0)
+ return 1;
+
+ if (!wait || timeriomem_rng_data->present)
+ return timeriomem_rng_data->present;
+
+ wait_for_completion(&timeriomem_rng_data->completion);
+
+ return 1;
+}
+
+static int timeriomem_rng_data_read(struct hwrng *rng, u32 *data)
+{
+ unsigned long cur;
+ s32 delay;
+
+ *data = readl(timeriomem_rng_data->address);
+
+ if (rng->priv != 0) {
+ cur = jiffies;
+
+ delay = cur - timeriomem_rng_timer.expires;
+ delay = rng->priv - (delay % rng->priv);
+
+ timeriomem_rng_timer.expires = cur + delay;
+ timeriomem_rng_data->present = 0;
+
+ init_completion(&timeriomem_rng_data->completion);
+ add_timer(&timeriomem_rng_timer);
+ }
+
+ return 4;
+}
+
+static void timeriomem_rng_trigger(unsigned long dummy)
+{
+ timeriomem_rng_data->present = 1;
+ complete(&timeriomem_rng_data->completion);
+}
+
+static struct hwrng timeriomem_rng_ops = {
+ .name = "timeriomem",
+ .data_present = timeriomem_rng_data_present,
+ .data_read = timeriomem_rng_data_read,
+ .priv = 0,
+};
+
+static int __init timeriomem_rng_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ timeriomem_rng_data = pdev->dev.platform_data;
+
+ if (timeriomem_rng_data->period != 0
+ && usecs_to_jiffies(timeriomem_rng_data->period) > 0) {
+ timeriomem_rng_timer.expires = jiffies;
+
+ timeriomem_rng_ops.priv = usecs_to_jiffies(
+ timeriomem_rng_data->period);
+ }
+ timeriomem_rng_data->present = 1;
+
+ ret = hwrng_register(&timeriomem_rng_ops);
+ if (ret) {
+ dev_err(&pdev->dev, "problem registering\n");
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n",
+ timeriomem_rng_data->address,
+ timeriomem_rng_data->period);
+
+ return 0;
+}
+
+static int __devexit timeriomem_rng_remove(struct platform_device *pdev)
+{
+ del_timer_sync(&timeriomem_rng_timer);
+ hwrng_unregister(&timeriomem_rng_ops);
+
+ return 0;
+}
+
+static struct platform_driver timeriomem_rng_driver = {
+ .driver = {
+ .name = "timeriomem_rng",
+ .owner = THIS_MODULE,
+ },
+ .probe = timeriomem_rng_probe,
+ .remove = __devexit_p(timeriomem_rng_remove),
+};
+
+static int __init timeriomem_rng_init(void)
+{
+ return platform_driver_register(&timeriomem_rng_driver);
+}
+
+static void __exit timeriomem_rng_exit(void)
+{
+ platform_driver_unregister(&timeriomem_rng_driver);
+}
+
+module_init(timeriomem_rng_init);
+module_exit(timeriomem_rng_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>");
+MODULE_DESCRIPTION("Timer IOMEM H/W RNG driver");
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c
index 9c47dc4..ccdd828 100644
--- a/drivers/char/tpm/tpm.c
+++ b/drivers/char/tpm/tpm.c
@@ -429,134 +429,148 @@
#define TPM_DIGEST_SIZE 20
#define TPM_ERROR_SIZE 10
#define TPM_RET_CODE_IDX 6
-#define TPM_GET_CAP_RET_SIZE_IDX 10
-#define TPM_GET_CAP_RET_UINT32_1_IDX 14
-#define TPM_GET_CAP_RET_UINT32_2_IDX 18
-#define TPM_GET_CAP_RET_UINT32_3_IDX 22
-#define TPM_GET_CAP_RET_UINT32_4_IDX 26
-#define TPM_GET_CAP_PERM_DISABLE_IDX 16
-#define TPM_GET_CAP_PERM_INACTIVE_IDX 18
-#define TPM_GET_CAP_RET_BOOL_1_IDX 14
-#define TPM_GET_CAP_TEMP_INACTIVE_IDX 16
-
-#define TPM_CAP_IDX 13
-#define TPM_CAP_SUBCAP_IDX 21
enum tpm_capabilities {
- TPM_CAP_FLAG = 4,
- TPM_CAP_PROP = 5,
+ TPM_CAP_FLAG = cpu_to_be32(4),
+ TPM_CAP_PROP = cpu_to_be32(5),
+ CAP_VERSION_1_1 = cpu_to_be32(0x06),
+ CAP_VERSION_1_2 = cpu_to_be32(0x1A)
};
enum tpm_sub_capabilities {
- TPM_CAP_PROP_PCR = 0x1,
- TPM_CAP_PROP_MANUFACTURER = 0x3,
- TPM_CAP_FLAG_PERM = 0x8,
- TPM_CAP_FLAG_VOL = 0x9,
- TPM_CAP_PROP_OWNER = 0x11,
- TPM_CAP_PROP_TIS_TIMEOUT = 0x15,
- TPM_CAP_PROP_TIS_DURATION = 0x20,
+ TPM_CAP_PROP_PCR = cpu_to_be32(0x101),
+ TPM_CAP_PROP_MANUFACTURER = cpu_to_be32(0x103),
+ TPM_CAP_FLAG_PERM = cpu_to_be32(0x108),
+ TPM_CAP_FLAG_VOL = cpu_to_be32(0x109),
+ TPM_CAP_PROP_OWNER = cpu_to_be32(0x111),
+ TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115),
+ TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120),
+
};
-/*
- * This is a semi generic GetCapability command for use
- * with the capability type TPM_CAP_PROP or TPM_CAP_FLAG
- * and their associated sub_capabilities.
- */
-
-static const u8 tpm_cap[] = {
- 0, 193, /* TPM_TAG_RQU_COMMAND */
- 0, 0, 0, 22, /* length */
- 0, 0, 0, 101, /* TPM_ORD_GetCapability */
- 0, 0, 0, 0, /* TPM_CAP_<TYPE> */
- 0, 0, 0, 4, /* TPM_CAP_SUB_<TYPE> size */
- 0, 0, 1, 0 /* TPM_CAP_SUB_<TYPE> */
-};
-
-static ssize_t transmit_cmd(struct tpm_chip *chip, u8 *data, int len,
- char *desc)
+static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
+ int len, const char *desc)
{
int err;
- len = tpm_transmit(chip, data, len);
+ len = tpm_transmit(chip,(u8 *) cmd, len);
if (len < 0)
return len;
if (len == TPM_ERROR_SIZE) {
- err = be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX)));
+ err = be32_to_cpu(cmd->header.out.return_code);
dev_dbg(chip->dev, "A TPM error (%d) occurred %s\n", err, desc);
return err;
}
return 0;
}
+#define TPM_INTERNAL_RESULT_SIZE 200
+#define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
+#define TPM_ORD_GET_CAP cpu_to_be32(101)
+
+static const struct tpm_input_header tpm_getcap_header = {
+ .tag = TPM_TAG_RQU_COMMAND,
+ .length = cpu_to_be32(22),
+ .ordinal = TPM_ORD_GET_CAP
+};
+
+ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap,
+ const char *desc)
+{
+ struct tpm_cmd_t tpm_cmd;
+ int rc;
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+
+ tpm_cmd.header.in = tpm_getcap_header;
+ if (subcap_id == CAP_VERSION_1_1 || subcap_id == CAP_VERSION_1_2) {
+ tpm_cmd.params.getcap_in.cap = subcap_id;
+ /*subcap field not necessary */
+ tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(0);
+ tpm_cmd.header.in.length -= cpu_to_be32(sizeof(__be32));
+ } else {
+ if (subcap_id == TPM_CAP_FLAG_PERM ||
+ subcap_id == TPM_CAP_FLAG_VOL)
+ tpm_cmd.params.getcap_in.cap = TPM_CAP_FLAG;
+ else
+ tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+ tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+ tpm_cmd.params.getcap_in.subcap = subcap_id;
+ }
+ rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc);
+ if (!rc)
+ *cap = tpm_cmd.params.getcap_out.cap;
+ return rc;
+}
+
void tpm_gen_interrupt(struct tpm_chip *chip)
{
- u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)];
+ struct tpm_cmd_t tpm_cmd;
ssize_t rc;
- memcpy(data, tpm_cap, sizeof(tpm_cap));
- data[TPM_CAP_IDX] = TPM_CAP_PROP;
- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT;
+ tpm_cmd.header.in = tpm_getcap_header;
+ tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+ tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+ tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
- rc = transmit_cmd(chip, data, sizeof(data),
+ rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
"attempting to determine the timeouts");
}
EXPORT_SYMBOL_GPL(tpm_gen_interrupt);
void tpm_get_timeouts(struct tpm_chip *chip)
{
- u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)];
+ struct tpm_cmd_t tpm_cmd;
+ struct timeout_t *timeout_cap;
+ struct duration_t *duration_cap;
ssize_t rc;
u32 timeout;
- memcpy(data, tpm_cap, sizeof(tpm_cap));
- data[TPM_CAP_IDX] = TPM_CAP_PROP;
- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT;
+ tpm_cmd.header.in = tpm_getcap_header;
+ tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+ tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+ tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
- rc = transmit_cmd(chip, data, sizeof(data),
+ rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
"attempting to determine the timeouts");
if (rc)
goto duration;
- if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX)))
+ if (be32_to_cpu(tpm_cmd.header.out.length)
!= 4 * sizeof(u32))
goto duration;
+ timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
/* Don't overwrite default if value is 0 */
- timeout =
- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX)));
+ timeout = be32_to_cpu(timeout_cap->a);
if (timeout)
chip->vendor.timeout_a = usecs_to_jiffies(timeout);
- timeout =
- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX)));
+ timeout = be32_to_cpu(timeout_cap->b);
if (timeout)
chip->vendor.timeout_b = usecs_to_jiffies(timeout);
- timeout =
- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX)));
+ timeout = be32_to_cpu(timeout_cap->c);
if (timeout)
chip->vendor.timeout_c = usecs_to_jiffies(timeout);
- timeout =
- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_4_IDX)));
+ timeout = be32_to_cpu(timeout_cap->d);
if (timeout)
chip->vendor.timeout_d = usecs_to_jiffies(timeout);
duration:
- memcpy(data, tpm_cap, sizeof(tpm_cap));
- data[TPM_CAP_IDX] = TPM_CAP_PROP;
- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_DURATION;
+ tpm_cmd.header.in = tpm_getcap_header;
+ tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+ tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+ tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
- rc = transmit_cmd(chip, data, sizeof(data),
+ rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
"attempting to determine the durations");
if (rc)
return;
- if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX)))
+ if (be32_to_cpu(tpm_cmd.header.out.return_code)
!= 3 * sizeof(u32))
return;
-
+ duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
chip->vendor.duration[TPM_SHORT] =
- usecs_to_jiffies(be32_to_cpu
- (*((__be32 *) (data +
- TPM_GET_CAP_RET_UINT32_1_IDX))));
+ usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
* value wrong and apparently reports msecs rather than usecs. So we
* fix up the resulting too-small TPM_SHORT value to make things work.
@@ -565,13 +579,9 @@
chip->vendor.duration[TPM_SHORT] = HZ;
chip->vendor.duration[TPM_MEDIUM] =
- usecs_to_jiffies(be32_to_cpu
- (*((__be32 *) (data +
- TPM_GET_CAP_RET_UINT32_2_IDX))));
+ usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
chip->vendor.duration[TPM_LONG] =
- usecs_to_jiffies(be32_to_cpu
- (*((__be32 *) (data +
- TPM_GET_CAP_RET_UINT32_3_IDX))));
+ usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
}
EXPORT_SYMBOL_GPL(tpm_get_timeouts);
@@ -587,36 +597,18 @@
}
EXPORT_SYMBOL_GPL(tpm_continue_selftest);
-#define TPM_INTERNAL_RESULT_SIZE 200
-
ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr,
char *buf)
{
- u8 *data;
+ cap_t cap;
ssize_t rc;
- struct tpm_chip *chip = dev_get_drvdata(dev);
- if (chip == NULL)
- return -ENODEV;
-
- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- memcpy(data, tpm_cap, sizeof(tpm_cap));
- data[TPM_CAP_IDX] = TPM_CAP_FLAG;
- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM;
-
- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
- "attemtping to determine the permanent enabled state");
- if (rc) {
- kfree(data);
+ rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
+ "attempting to determine the permanent enabled state");
+ if (rc)
return 0;
- }
- rc = sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_DISABLE_IDX]);
-
- kfree(data);
+ rc = sprintf(buf, "%d\n", !cap.perm_flags.disable);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_show_enabled);
@@ -624,31 +616,15 @@
ssize_t tpm_show_active(struct device * dev, struct device_attribute * attr,
char *buf)
{
- u8 *data;
+ cap_t cap;
ssize_t rc;
- struct tpm_chip *chip = dev_get_drvdata(dev);
- if (chip == NULL)
- return -ENODEV;
-
- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- memcpy(data, tpm_cap, sizeof(tpm_cap));
- data[TPM_CAP_IDX] = TPM_CAP_FLAG;
- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM;
-
- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
- "attemtping to determine the permanent active state");
- if (rc) {
- kfree(data);
+ rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
+ "attempting to determine the permanent active state");
+ if (rc)
return 0;
- }
- rc = sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_INACTIVE_IDX]);
-
- kfree(data);
+ rc = sprintf(buf, "%d\n", !cap.perm_flags.deactivated);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_show_active);
@@ -656,31 +632,15 @@
ssize_t tpm_show_owned(struct device * dev, struct device_attribute * attr,
char *buf)
{
- u8 *data;
+ cap_t cap;
ssize_t rc;
- struct tpm_chip *chip = dev_get_drvdata(dev);
- if (chip == NULL)
- return -ENODEV;
-
- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- memcpy(data, tpm_cap, sizeof(tpm_cap));
- data[TPM_CAP_IDX] = TPM_CAP_PROP;
- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_OWNER;
-
- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
- "attempting to determine the owner state");
- if (rc) {
- kfree(data);
+ rc = tpm_getcap(dev, TPM_CAP_PROP_OWNER, &cap,
+ "attempting to determine the owner state");
+ if (rc)
return 0;
- }
- rc = sprintf(buf, "%d\n", data[TPM_GET_CAP_RET_BOOL_1_IDX]);
-
- kfree(data);
+ rc = sprintf(buf, "%d\n", cap.owned);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_show_owned);
@@ -688,116 +648,180 @@
ssize_t tpm_show_temp_deactivated(struct device * dev,
struct device_attribute * attr, char *buf)
{
- u8 *data;
+ cap_t cap;
ssize_t rc;
- struct tpm_chip *chip = dev_get_drvdata(dev);
- if (chip == NULL)
- return -ENODEV;
-
- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- memcpy(data, tpm_cap, sizeof(tpm_cap));
- data[TPM_CAP_IDX] = TPM_CAP_FLAG;
- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_VOL;
-
- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
- "attempting to determine the temporary state");
- if (rc) {
- kfree(data);
+ rc = tpm_getcap(dev, TPM_CAP_FLAG_VOL, &cap,
+ "attempting to determine the temporary state");
+ if (rc)
return 0;
- }
- rc = sprintf(buf, "%d\n", data[TPM_GET_CAP_TEMP_INACTIVE_IDX]);
-
- kfree(data);
+ rc = sprintf(buf, "%d\n", cap.stclear_flags.deactivated);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated);
-static const u8 pcrread[] = {
- 0, 193, /* TPM_TAG_RQU_COMMAND */
- 0, 0, 0, 14, /* length */
- 0, 0, 0, 21, /* TPM_ORD_PcrRead */
- 0, 0, 0, 0 /* PCR index */
+/*
+ * tpm_chip_find_get - return tpm_chip for given chip number
+ */
+static struct tpm_chip *tpm_chip_find_get(int chip_num)
+{
+ struct tpm_chip *pos, *chip = NULL;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
+ if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num)
+ continue;
+
+ if (try_module_get(pos->dev->driver->owner)) {
+ chip = pos;
+ break;
+ }
+ }
+ rcu_read_unlock();
+ return chip;
+}
+
+#define TPM_ORDINAL_PCRREAD cpu_to_be32(21)
+#define READ_PCR_RESULT_SIZE 30
+static struct tpm_input_header pcrread_header = {
+ .tag = TPM_TAG_RQU_COMMAND,
+ .length = cpu_to_be32(14),
+ .ordinal = TPM_ORDINAL_PCRREAD
};
+int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
+{
+ int rc;
+ struct tpm_cmd_t cmd;
+
+ cmd.header.in = pcrread_header;
+ cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx);
+ BUILD_BUG_ON(cmd.header.in.length > READ_PCR_RESULT_SIZE);
+ rc = transmit_cmd(chip, &cmd, cmd.header.in.length,
+ "attempting to read a pcr value");
+
+ if (rc == 0)
+ memcpy(res_buf, cmd.params.pcrread_out.pcr_result,
+ TPM_DIGEST_SIZE);
+ return rc;
+}
+
+/**
+ * tpm_pcr_read - read a pcr value
+ * @chip_num: tpm idx # or ANY
+ * @pcr_idx: pcr idx to retrieve
+ * @res_buf: TPM_PCR value
+ * size of res_buf is 20 bytes (or NULL if you don't care)
+ *
+ * The TPM driver should be built-in, but for whatever reason it
+ * isn't, protect against the chip disappearing, by incrementing
+ * the module usage count.
+ */
+int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf)
+{
+ struct tpm_chip *chip;
+ int rc;
+
+ chip = tpm_chip_find_get(chip_num);
+ if (chip == NULL)
+ return -ENODEV;
+ rc = __tpm_pcr_read(chip, pcr_idx, res_buf);
+ module_put(chip->dev->driver->owner);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_pcr_read);
+
+/**
+ * tpm_pcr_extend - extend pcr value with hash
+ * @chip_num: tpm idx # or AN&
+ * @pcr_idx: pcr idx to extend
+ * @hash: hash value used to extend pcr value
+ *
+ * The TPM driver should be built-in, but for whatever reason it
+ * isn't, protect against the chip disappearing, by incrementing
+ * the module usage count.
+ */
+#define TPM_ORD_PCR_EXTEND cpu_to_be32(20)
+#define EXTEND_PCR_SIZE 34
+static struct tpm_input_header pcrextend_header = {
+ .tag = TPM_TAG_RQU_COMMAND,
+ .length = cpu_to_be32(34),
+ .ordinal = TPM_ORD_PCR_EXTEND
+};
+
+int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
+{
+ struct tpm_cmd_t cmd;
+ int rc;
+ struct tpm_chip *chip;
+
+ chip = tpm_chip_find_get(chip_num);
+ if (chip == NULL)
+ return -ENODEV;
+
+ cmd.header.in = pcrextend_header;
+ BUILD_BUG_ON(be32_to_cpu(cmd.header.in.length) > EXTEND_PCR_SIZE);
+ cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx);
+ memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE);
+ rc = transmit_cmd(chip, &cmd, cmd.header.in.length,
+ "attempting extend a PCR value");
+
+ module_put(chip->dev->driver->owner);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_pcr_extend);
+
ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr,
char *buf)
{
- u8 *data;
+ cap_t cap;
+ u8 digest[TPM_DIGEST_SIZE];
ssize_t rc;
int i, j, num_pcrs;
- __be32 index;
char *str = buf;
-
struct tpm_chip *chip = dev_get_drvdata(dev);
- if (chip == NULL)
- return -ENODEV;
- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- memcpy(data, tpm_cap, sizeof(tpm_cap));
- data[TPM_CAP_IDX] = TPM_CAP_PROP;
- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_PCR;
-
- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
+ rc = tpm_getcap(dev, TPM_CAP_PROP_PCR, &cap,
"attempting to determine the number of PCRS");
- if (rc) {
- kfree(data);
+ if (rc)
return 0;
- }
- num_pcrs = be32_to_cpu(*((__be32 *) (data + 14)));
+ num_pcrs = be32_to_cpu(cap.num_pcrs);
for (i = 0; i < num_pcrs; i++) {
- memcpy(data, pcrread, sizeof(pcrread));
- index = cpu_to_be32(i);
- memcpy(data + 10, &index, 4);
- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
- "attempting to read a PCR");
+ rc = __tpm_pcr_read(chip, i, digest);
if (rc)
- goto out;
+ break;
str += sprintf(str, "PCR-%02d: ", i);
for (j = 0; j < TPM_DIGEST_SIZE; j++)
- str += sprintf(str, "%02X ", *(data + 10 + j));
+ str += sprintf(str, "%02X ", digest[j]);
str += sprintf(str, "\n");
}
-out:
- kfree(data);
return str - buf;
}
EXPORT_SYMBOL_GPL(tpm_show_pcrs);
#define READ_PUBEK_RESULT_SIZE 314
-static const u8 readpubek[] = {
- 0, 193, /* TPM_TAG_RQU_COMMAND */
- 0, 0, 0, 30, /* length */
- 0, 0, 0, 124, /* TPM_ORD_ReadPubek */
+#define TPM_ORD_READPUBEK cpu_to_be32(124)
+struct tpm_input_header tpm_readpubek_header = {
+ .tag = TPM_TAG_RQU_COMMAND,
+ .length = cpu_to_be32(30),
+ .ordinal = TPM_ORD_READPUBEK
};
ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
char *buf)
{
u8 *data;
+ struct tpm_cmd_t tpm_cmd;
ssize_t err;
int i, rc;
char *str = buf;
struct tpm_chip *chip = dev_get_drvdata(dev);
- if (chip == NULL)
- return -ENODEV;
- data = kzalloc(READ_PUBEK_RESULT_SIZE, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- memcpy(data, readpubek, sizeof(readpubek));
-
- err = transmit_cmd(chip, data, READ_PUBEK_RESULT_SIZE,
+ tpm_cmd.header.in = tpm_readpubek_header;
+ err = transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
"attempting to read the PUBEK");
if (err)
goto out;
@@ -812,7 +836,7 @@
256 byte modulus
ignore checksum 20 bytes
*/
-
+ data = tpm_cmd.params.readpubek_out_buffer;
str +=
sprintf(str,
"Algorithm: %02X %02X %02X %02X\nEncscheme: %02X %02X\n"
@@ -832,65 +856,33 @@
}
out:
rc = str - buf;
- kfree(data);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_show_pubek);
-#define CAP_VERSION_1_1 6
-#define CAP_VERSION_1_2 0x1A
-#define CAP_VERSION_IDX 13
-static const u8 cap_version[] = {
- 0, 193, /* TPM_TAG_RQU_COMMAND */
- 0, 0, 0, 18, /* length */
- 0, 0, 0, 101, /* TPM_ORD_GetCapability */
- 0, 0, 0, 0,
- 0, 0, 0, 0
-};
ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr,
char *buf)
{
- u8 *data;
+ cap_t cap;
ssize_t rc;
char *str = buf;
- struct tpm_chip *chip = dev_get_drvdata(dev);
- if (chip == NULL)
- return -ENODEV;
-
- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- memcpy(data, tpm_cap, sizeof(tpm_cap));
- data[TPM_CAP_IDX] = TPM_CAP_PROP;
- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER;
-
- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
+ rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap,
"attempting to determine the manufacturer");
- if (rc) {
- kfree(data);
- return 0;
- }
-
- str += sprintf(str, "Manufacturer: 0x%x\n",
- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))));
-
- memcpy(data, cap_version, sizeof(cap_version));
- data[CAP_VERSION_IDX] = CAP_VERSION_1_1;
- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
- "attempting to determine the 1.1 version");
if (rc)
- goto out;
+ return 0;
+ str += sprintf(str, "Manufacturer: 0x%x\n",
+ be32_to_cpu(cap.manufacturer_id));
+ rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap,
+ "attempting to determine the 1.1 version");
+ if (rc)
+ return 0;
str += sprintf(str,
"TCG version: %d.%d\nFirmware version: %d.%d\n",
- (int) data[14], (int) data[15], (int) data[16],
- (int) data[17]);
-
-out:
- kfree(data);
+ cap.tpm_version.Major, cap.tpm_version.Minor,
+ cap.tpm_version.revMajor, cap.tpm_version.revMinor);
return str - buf;
}
EXPORT_SYMBOL_GPL(tpm_show_caps);
@@ -898,51 +890,25 @@
ssize_t tpm_show_caps_1_2(struct device * dev,
struct device_attribute * attr, char *buf)
{
- u8 *data;
- ssize_t len;
+ cap_t cap;
+ ssize_t rc;
char *str = buf;
- struct tpm_chip *chip = dev_get_drvdata(dev);
- if (chip == NULL)
- return -ENODEV;
-
- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- memcpy(data, tpm_cap, sizeof(tpm_cap));
- data[TPM_CAP_IDX] = TPM_CAP_PROP;
- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER;
-
- len = tpm_transmit(chip, data, TPM_INTERNAL_RESULT_SIZE);
- if (len <= TPM_ERROR_SIZE) {
- dev_dbg(chip->dev, "A TPM error (%d) occurred "
- "attempting to determine the manufacturer\n",
- be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX))));
- kfree(data);
+ rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap,
+ "attempting to determine the manufacturer");
+ if (rc)
return 0;
- }
-
str += sprintf(str, "Manufacturer: 0x%x\n",
- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))));
-
- memcpy(data, cap_version, sizeof(cap_version));
- data[CAP_VERSION_IDX] = CAP_VERSION_1_2;
-
- len = tpm_transmit(chip, data, TPM_INTERNAL_RESULT_SIZE);
- if (len <= TPM_ERROR_SIZE) {
- dev_err(chip->dev, "A TPM error (%d) occurred "
- "attempting to determine the 1.2 version\n",
- be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX))));
- goto out;
- }
+ be32_to_cpu(cap.manufacturer_id));
+ rc = tpm_getcap(dev, CAP_VERSION_1_2, &cap,
+ "attempting to determine the 1.2 version");
+ if (rc)
+ return 0;
str += sprintf(str,
"TCG version: %d.%d\nFirmware version: %d.%d\n",
- (int) data[16], (int) data[17], (int) data[18],
- (int) data[19]);
-
-out:
- kfree(data);
+ cap.tpm_version_1_2.Major, cap.tpm_version_1_2.Minor,
+ cap.tpm_version_1_2.revMajor,
+ cap.tpm_version_1_2.revMinor);
return str - buf;
}
EXPORT_SYMBOL_GPL(tpm_show_caps_1_2);
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 8e30df4..8e00b4d 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -26,6 +26,7 @@
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/tpm.h>
enum tpm_timeout {
TPM_TIMEOUT = 5, /* msecs */
@@ -123,6 +124,147 @@
outb(index, base);
outb(value & 0xFF, base+1);
}
+struct tpm_input_header {
+ __be16 tag;
+ __be32 length;
+ __be32 ordinal;
+}__attribute__((packed));
+
+struct tpm_output_header {
+ __be16 tag;
+ __be32 length;
+ __be32 return_code;
+}__attribute__((packed));
+
+struct stclear_flags_t {
+ __be16 tag;
+ u8 deactivated;
+ u8 disableForceClear;
+ u8 physicalPresence;
+ u8 physicalPresenceLock;
+ u8 bGlobalLock;
+}__attribute__((packed));
+
+struct tpm_version_t {
+ u8 Major;
+ u8 Minor;
+ u8 revMajor;
+ u8 revMinor;
+}__attribute__((packed));
+
+struct tpm_version_1_2_t {
+ __be16 tag;
+ u8 Major;
+ u8 Minor;
+ u8 revMajor;
+ u8 revMinor;
+}__attribute__((packed));
+
+struct timeout_t {
+ __be32 a;
+ __be32 b;
+ __be32 c;
+ __be32 d;
+}__attribute__((packed));
+
+struct duration_t {
+ __be32 tpm_short;
+ __be32 tpm_medium;
+ __be32 tpm_long;
+}__attribute__((packed));
+
+struct permanent_flags_t {
+ __be16 tag;
+ u8 disable;
+ u8 ownership;
+ u8 deactivated;
+ u8 readPubek;
+ u8 disableOwnerClear;
+ u8 allowMaintenance;
+ u8 physicalPresenceLifetimeLock;
+ u8 physicalPresenceHWEnable;
+ u8 physicalPresenceCMDEnable;
+ u8 CEKPUsed;
+ u8 TPMpost;
+ u8 TPMpostLock;
+ u8 FIPS;
+ u8 operator;
+ u8 enableRevokeEK;
+ u8 nvLocked;
+ u8 readSRKPub;
+ u8 tpmEstablished;
+ u8 maintenanceDone;
+ u8 disableFullDALogicInfo;
+}__attribute__((packed));
+
+typedef union {
+ struct permanent_flags_t perm_flags;
+ struct stclear_flags_t stclear_flags;
+ bool owned;
+ __be32 num_pcrs;
+ struct tpm_version_t tpm_version;
+ struct tpm_version_1_2_t tpm_version_1_2;
+ __be32 manufacturer_id;
+ struct timeout_t timeout;
+ struct duration_t duration;
+} cap_t;
+
+struct tpm_getcap_params_in {
+ __be32 cap;
+ __be32 subcap_size;
+ __be32 subcap;
+}__attribute__((packed));
+
+struct tpm_getcap_params_out {
+ __be32 cap_size;
+ cap_t cap;
+}__attribute__((packed));
+
+struct tpm_readpubek_params_out {
+ u8 algorithm[4];
+ u8 encscheme[2];
+ u8 sigscheme[2];
+ u8 parameters[12]; /*assuming RSA*/
+ __be32 keysize;
+ u8 modulus[256];
+ u8 checksum[20];
+}__attribute__((packed));
+
+typedef union {
+ struct tpm_input_header in;
+ struct tpm_output_header out;
+} tpm_cmd_header;
+
+#define TPM_DIGEST_SIZE 20
+struct tpm_pcrread_out {
+ u8 pcr_result[TPM_DIGEST_SIZE];
+}__attribute__((packed));
+
+struct tpm_pcrread_in {
+ __be32 pcr_idx;
+}__attribute__((packed));
+
+struct tpm_pcrextend_in {
+ __be32 pcr_idx;
+ u8 hash[TPM_DIGEST_SIZE];
+}__attribute__((packed));
+
+typedef union {
+ struct tpm_getcap_params_out getcap_out;
+ struct tpm_readpubek_params_out readpubek_out;
+ u8 readpubek_out_buffer[sizeof(struct tpm_readpubek_params_out)];
+ struct tpm_getcap_params_in getcap_in;
+ struct tpm_pcrread_in pcrread_in;
+ struct tpm_pcrread_out pcrread_out;
+ struct tpm_pcrextend_in pcrextend_in;
+} tpm_cmd_params;
+
+struct tpm_cmd_t {
+ tpm_cmd_header header;
+ tpm_cmd_params params;
+}__attribute__((packed));
+
+ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *);
extern void tpm_get_timeouts(struct tpm_chip *);
extern void tpm_gen_interrupt(struct tpm_chip *);
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 1525882..1efb287 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o
obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o
obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o
+obj-$(CONFIG_SH_TIMER_CMT) += sh_cmt.o
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
new file mode 100644
index 0000000..7783b42
--- /dev/null
+++ b/drivers/clocksource/sh_cmt.c
@@ -0,0 +1,615 @@
+/*
+ * SuperH Timer Support - CMT
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * 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
+ *
+ * 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/init.h>
+#include <linux/bootmem.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/sh_cmt.h>
+
+struct sh_cmt_priv {
+ void __iomem *mapbase;
+ struct clk *clk;
+ unsigned long width; /* 16 or 32 bit version of hardware block */
+ unsigned long overflow_bit;
+ unsigned long clear_bits;
+ struct irqaction irqaction;
+ struct platform_device *pdev;
+
+ unsigned long flags;
+ unsigned long match_value;
+ unsigned long next_match_value;
+ unsigned long max_match_value;
+ unsigned long rate;
+ spinlock_t lock;
+ struct clock_event_device ced;
+ unsigned long total_cycles;
+};
+
+static DEFINE_SPINLOCK(sh_cmt_lock);
+
+#define CMSTR -1 /* shared register */
+#define CMCSR 0 /* channel register */
+#define CMCNT 1 /* channel register */
+#define CMCOR 2 /* channel register */
+
+static inline unsigned long sh_cmt_read(struct sh_cmt_priv *p, int reg_nr)
+{
+ struct sh_cmt_config *cfg = p->pdev->dev.platform_data;
+ void __iomem *base = p->mapbase;
+ unsigned long offs;
+
+ if (reg_nr == CMSTR) {
+ offs = 0;
+ base -= cfg->channel_offset;
+ } else
+ offs = reg_nr;
+
+ if (p->width == 16)
+ offs <<= 1;
+ else {
+ offs <<= 2;
+ if ((reg_nr == CMCNT) || (reg_nr == CMCOR))
+ return ioread32(base + offs);
+ }
+
+ return ioread16(base + offs);
+}
+
+static inline void sh_cmt_write(struct sh_cmt_priv *p, int reg_nr,
+ unsigned long value)
+{
+ struct sh_cmt_config *cfg = p->pdev->dev.platform_data;
+ void __iomem *base = p->mapbase;
+ unsigned long offs;
+
+ if (reg_nr == CMSTR) {
+ offs = 0;
+ base -= cfg->channel_offset;
+ } else
+ offs = reg_nr;
+
+ if (p->width == 16)
+ offs <<= 1;
+ else {
+ offs <<= 2;
+ if ((reg_nr == CMCNT) || (reg_nr == CMCOR)) {
+ iowrite32(value, base + offs);
+ return;
+ }
+ }
+
+ iowrite16(value, base + offs);
+}
+
+static unsigned long sh_cmt_get_counter(struct sh_cmt_priv *p,
+ int *has_wrapped)
+{
+ unsigned long v1, v2, v3;
+
+ /* Make sure the timer value is stable. Stolen from acpi_pm.c */
+ do {
+ v1 = sh_cmt_read(p, CMCNT);
+ v2 = sh_cmt_read(p, CMCNT);
+ v3 = sh_cmt_read(p, CMCNT);
+ } while (unlikely((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1)
+ || (v3 > v1 && v3 < v2)));
+
+ *has_wrapped = sh_cmt_read(p, CMCSR) & p->overflow_bit;
+ return v2;
+}
+
+
+static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start)
+{
+ struct sh_cmt_config *cfg = p->pdev->dev.platform_data;
+ unsigned long flags, value;
+
+ /* start stop register shared by multiple timer channels */
+ spin_lock_irqsave(&sh_cmt_lock, flags);
+ value = sh_cmt_read(p, CMSTR);
+
+ if (start)
+ value |= 1 << cfg->timer_bit;
+ else
+ value &= ~(1 << cfg->timer_bit);
+
+ sh_cmt_write(p, CMSTR, value);
+ spin_unlock_irqrestore(&sh_cmt_lock, flags);
+}
+
+static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate)
+{
+ struct sh_cmt_config *cfg = p->pdev->dev.platform_data;
+ int ret;
+
+ /* enable clock */
+ ret = clk_enable(p->clk);
+ if (ret) {
+ pr_err("sh_cmt: cannot enable clock \"%s\"\n", cfg->clk);
+ return ret;
+ }
+ *rate = clk_get_rate(p->clk) / 8;
+
+ /* make sure channel is disabled */
+ sh_cmt_start_stop_ch(p, 0);
+
+ /* configure channel, periodic mode and maximum timeout */
+ if (p->width == 16)
+ sh_cmt_write(p, CMCSR, 0);
+ else
+ sh_cmt_write(p, CMCSR, 0x01a4);
+
+ sh_cmt_write(p, CMCOR, 0xffffffff);
+ sh_cmt_write(p, CMCNT, 0);
+
+ /* enable channel */
+ sh_cmt_start_stop_ch(p, 1);
+ return 0;
+}
+
+static void sh_cmt_disable(struct sh_cmt_priv *p)
+{
+ /* disable channel */
+ sh_cmt_start_stop_ch(p, 0);
+
+ /* stop clock */
+ clk_disable(p->clk);
+}
+
+/* private flags */
+#define FLAG_CLOCKEVENT (1 << 0)
+#define FLAG_CLOCKSOURCE (1 << 1)
+#define FLAG_REPROGRAM (1 << 2)
+#define FLAG_SKIPEVENT (1 << 3)
+#define FLAG_IRQCONTEXT (1 << 4)
+
+static void sh_cmt_clock_event_program_verify(struct sh_cmt_priv *p,
+ int absolute)
+{
+ unsigned long new_match;
+ unsigned long value = p->next_match_value;
+ unsigned long delay = 0;
+ unsigned long now = 0;
+ int has_wrapped;
+
+ now = sh_cmt_get_counter(p, &has_wrapped);
+ p->flags |= FLAG_REPROGRAM; /* force reprogram */
+
+ if (has_wrapped) {
+ /* we're competing with the interrupt handler.
+ * -> let the interrupt handler reprogram the timer.
+ * -> interrupt number two handles the event.
+ */
+ p->flags |= FLAG_SKIPEVENT;
+ return;
+ }
+
+ if (absolute)
+ now = 0;
+
+ do {
+ /* reprogram the timer hardware,
+ * but don't save the new match value yet.
+ */
+ new_match = now + value + delay;
+ if (new_match > p->max_match_value)
+ new_match = p->max_match_value;
+
+ sh_cmt_write(p, CMCOR, new_match);
+
+ now = sh_cmt_get_counter(p, &has_wrapped);
+ if (has_wrapped && (new_match > p->match_value)) {
+ /* we are changing to a greater match value,
+ * so this wrap must be caused by the counter
+ * matching the old value.
+ * -> first interrupt reprograms the timer.
+ * -> interrupt number two handles the event.
+ */
+ p->flags |= FLAG_SKIPEVENT;
+ break;
+ }
+
+ if (has_wrapped) {
+ /* we are changing to a smaller match value,
+ * so the wrap must be caused by the counter
+ * matching the new value.
+ * -> save programmed match value.
+ * -> let isr handle the event.
+ */
+ p->match_value = new_match;
+ break;
+ }
+
+ /* be safe: verify hardware settings */
+ if (now < new_match) {
+ /* timer value is below match value, all good.
+ * this makes sure we won't miss any match events.
+ * -> save programmed match value.
+ * -> let isr handle the event.
+ */
+ p->match_value = new_match;
+ break;
+ }
+
+ /* the counter has reached a value greater
+ * than our new match value. and since the
+ * has_wrapped flag isn't set we must have
+ * programmed a too close event.
+ * -> increase delay and retry.
+ */
+ if (delay)
+ delay <<= 1;
+ else
+ delay = 1;
+
+ if (!delay)
+ pr_warning("sh_cmt: too long delay\n");
+
+ } while (delay);
+}
+
+static void sh_cmt_set_next(struct sh_cmt_priv *p, unsigned long delta)
+{
+ unsigned long flags;
+
+ if (delta > p->max_match_value)
+ pr_warning("sh_cmt: delta out of range\n");
+
+ spin_lock_irqsave(&p->lock, flags);
+ p->next_match_value = delta;
+ sh_cmt_clock_event_program_verify(p, 0);
+ spin_unlock_irqrestore(&p->lock, flags);
+}
+
+static irqreturn_t sh_cmt_interrupt(int irq, void *dev_id)
+{
+ struct sh_cmt_priv *p = dev_id;
+
+ /* clear flags */
+ sh_cmt_write(p, CMCSR, sh_cmt_read(p, CMCSR) & p->clear_bits);
+
+ /* update clock source counter to begin with if enabled
+ * the wrap flag should be cleared by the timer specific
+ * isr before we end up here.
+ */
+ if (p->flags & FLAG_CLOCKSOURCE)
+ p->total_cycles += p->match_value;
+
+ if (!(p->flags & FLAG_REPROGRAM))
+ p->next_match_value = p->max_match_value;
+
+ p->flags |= FLAG_IRQCONTEXT;
+
+ if (p->flags & FLAG_CLOCKEVENT) {
+ if (!(p->flags & FLAG_SKIPEVENT)) {
+ if (p->ced.mode == CLOCK_EVT_MODE_ONESHOT) {
+ p->next_match_value = p->max_match_value;
+ p->flags |= FLAG_REPROGRAM;
+ }
+
+ p->ced.event_handler(&p->ced);
+ }
+ }
+
+ p->flags &= ~FLAG_SKIPEVENT;
+
+ if (p->flags & FLAG_REPROGRAM) {
+ p->flags &= ~FLAG_REPROGRAM;
+ sh_cmt_clock_event_program_verify(p, 1);
+
+ if (p->flags & FLAG_CLOCKEVENT)
+ if ((p->ced.mode == CLOCK_EVT_MODE_SHUTDOWN)
+ || (p->match_value == p->next_match_value))
+ p->flags &= ~FLAG_REPROGRAM;
+ }
+
+ p->flags &= ~FLAG_IRQCONTEXT;
+
+ return IRQ_HANDLED;
+}
+
+static int sh_cmt_start(struct sh_cmt_priv *p, unsigned long flag)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&p->lock, flags);
+
+ if (!(p->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
+ ret = sh_cmt_enable(p, &p->rate);
+
+ if (ret)
+ goto out;
+ p->flags |= flag;
+
+ /* setup timeout if no clockevent */
+ if ((flag == FLAG_CLOCKSOURCE) && (!(p->flags & FLAG_CLOCKEVENT)))
+ sh_cmt_set_next(p, p->max_match_value);
+ out:
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ return ret;
+}
+
+static void sh_cmt_stop(struct sh_cmt_priv *p, unsigned long flag)
+{
+ unsigned long flags;
+ unsigned long f;
+
+ spin_lock_irqsave(&p->lock, flags);
+
+ f = p->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE);
+ p->flags &= ~flag;
+
+ if (f && !(p->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
+ sh_cmt_disable(p);
+
+ /* adjust the timeout to maximum if only clocksource left */
+ if ((flag == FLAG_CLOCKEVENT) && (p->flags & FLAG_CLOCKSOURCE))
+ sh_cmt_set_next(p, p->max_match_value);
+
+ spin_unlock_irqrestore(&p->lock, flags);
+}
+
+static struct sh_cmt_priv *ced_to_sh_cmt(struct clock_event_device *ced)
+{
+ return container_of(ced, struct sh_cmt_priv, ced);
+}
+
+static void sh_cmt_clock_event_start(struct sh_cmt_priv *p, int periodic)
+{
+ struct clock_event_device *ced = &p->ced;
+
+ sh_cmt_start(p, FLAG_CLOCKEVENT);
+
+ /* TODO: calculate good shift from rate and counter bit width */
+
+ ced->shift = 32;
+ ced->mult = div_sc(p->rate, NSEC_PER_SEC, ced->shift);
+ ced->max_delta_ns = clockevent_delta2ns(p->max_match_value, ced);
+ ced->min_delta_ns = clockevent_delta2ns(0x1f, ced);
+
+ if (periodic)
+ sh_cmt_set_next(p, (p->rate + HZ/2) / HZ);
+ else
+ sh_cmt_set_next(p, p->max_match_value);
+}
+
+static void sh_cmt_clock_event_mode(enum clock_event_mode mode,
+ struct clock_event_device *ced)
+{
+ struct sh_cmt_priv *p = ced_to_sh_cmt(ced);
+
+ /* deal with old setting first */
+ switch (ced->mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ case CLOCK_EVT_MODE_ONESHOT:
+ sh_cmt_stop(p, FLAG_CLOCKEVENT);
+ break;
+ default:
+ break;
+ }
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ pr_info("sh_cmt: %s used for periodic clock events\n",
+ ced->name);
+ sh_cmt_clock_event_start(p, 1);
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ pr_info("sh_cmt: %s used for oneshot clock events\n",
+ ced->name);
+ sh_cmt_clock_event_start(p, 0);
+ break;
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_UNUSED:
+ sh_cmt_stop(p, FLAG_CLOCKEVENT);
+ break;
+ default:
+ break;
+ }
+}
+
+static int sh_cmt_clock_event_next(unsigned long delta,
+ struct clock_event_device *ced)
+{
+ struct sh_cmt_priv *p = ced_to_sh_cmt(ced);
+
+ BUG_ON(ced->mode != CLOCK_EVT_MODE_ONESHOT);
+ if (likely(p->flags & FLAG_IRQCONTEXT))
+ p->next_match_value = delta;
+ else
+ sh_cmt_set_next(p, delta);
+
+ return 0;
+}
+
+static void sh_cmt_register_clockevent(struct sh_cmt_priv *p,
+ char *name, unsigned long rating)
+{
+ struct clock_event_device *ced = &p->ced;
+
+ memset(ced, 0, sizeof(*ced));
+
+ ced->name = name;
+ ced->features = CLOCK_EVT_FEAT_PERIODIC;
+ ced->features |= CLOCK_EVT_FEAT_ONESHOT;
+ ced->rating = rating;
+ ced->cpumask = cpumask_of(0);
+ ced->set_next_event = sh_cmt_clock_event_next;
+ ced->set_mode = sh_cmt_clock_event_mode;
+
+ pr_info("sh_cmt: %s used for clock events\n", ced->name);
+ ced->mult = 1; /* work around misplaced WARN_ON() in clockevents.c */
+ clockevents_register_device(ced);
+}
+
+int sh_cmt_register(struct sh_cmt_priv *p, char *name,
+ unsigned long clockevent_rating,
+ unsigned long clocksource_rating)
+{
+ if (p->width == (sizeof(p->max_match_value) * 8))
+ p->max_match_value = ~0;
+ else
+ p->max_match_value = (1 << p->width) - 1;
+
+ p->match_value = p->max_match_value;
+ spin_lock_init(&p->lock);
+
+ if (clockevent_rating)
+ sh_cmt_register_clockevent(p, name, clockevent_rating);
+
+ return 0;
+}
+
+static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
+{
+ struct sh_cmt_config *cfg = pdev->dev.platform_data;
+ struct resource *res;
+ int irq, ret;
+ ret = -ENXIO;
+
+ memset(p, 0, sizeof(*p));
+ p->pdev = pdev;
+
+ if (!cfg) {
+ dev_err(&p->pdev->dev, "missing platform data\n");
+ goto err0;
+ }
+
+ platform_set_drvdata(pdev, p);
+
+ res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&p->pdev->dev, "failed to get I/O memory\n");
+ goto err0;
+ }
+
+ irq = platform_get_irq(p->pdev, 0);
+ if (irq < 0) {
+ dev_err(&p->pdev->dev, "failed to get irq\n");
+ goto err0;
+ }
+
+ /* map memory, let mapbase point to our channel */
+ p->mapbase = ioremap_nocache(res->start, resource_size(res));
+ if (p->mapbase == NULL) {
+ pr_err("sh_cmt: failed to remap I/O memory\n");
+ goto err0;
+ }
+
+ /* request irq using setup_irq() (too early for request_irq()) */
+ p->irqaction.name = cfg->name;
+ p->irqaction.handler = sh_cmt_interrupt;
+ p->irqaction.dev_id = p;
+ p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL;
+ p->irqaction.mask = CPU_MASK_NONE;
+ ret = setup_irq(irq, &p->irqaction);
+ if (ret) {
+ pr_err("sh_cmt: failed to request irq %d\n", irq);
+ goto err1;
+ }
+
+ /* get hold of clock */
+ p->clk = clk_get(&p->pdev->dev, cfg->clk);
+ if (IS_ERR(p->clk)) {
+ pr_err("sh_cmt: cannot get clock \"%s\"\n", cfg->clk);
+ ret = PTR_ERR(p->clk);
+ goto err2;
+ }
+
+ if (resource_size(res) == 6) {
+ p->width = 16;
+ p->overflow_bit = 0x80;
+ p->clear_bits = ~0xc0;
+ } else {
+ p->width = 32;
+ p->overflow_bit = 0x8000;
+ p->clear_bits = ~0xc000;
+ }
+
+ return sh_cmt_register(p, cfg->name,
+ cfg->clockevent_rating,
+ cfg->clocksource_rating);
+ err2:
+ free_irq(irq, p);
+ err1:
+ iounmap(p->mapbase);
+ err0:
+ return ret;
+}
+
+static int __devinit sh_cmt_probe(struct platform_device *pdev)
+{
+ struct sh_cmt_priv *p = platform_get_drvdata(pdev);
+ int ret;
+
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (p == NULL) {
+ dev_err(&pdev->dev, "failed to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ ret = sh_cmt_setup(p, pdev);
+ if (ret) {
+ kfree(p);
+
+ platform_set_drvdata(pdev, NULL);
+ }
+ return ret;
+}
+
+static int __devexit sh_cmt_remove(struct platform_device *pdev)
+{
+ return -EBUSY; /* cannot unregister clockevent and clocksource */
+}
+
+static struct platform_driver sh_cmt_device_driver = {
+ .probe = sh_cmt_probe,
+ .remove = __devexit_p(sh_cmt_remove),
+ .driver = {
+ .name = "sh_cmt",
+ }
+};
+
+static int __init sh_cmt_init(void)
+{
+ return platform_driver_register(&sh_cmt_device_driver);
+}
+
+static void __exit sh_cmt_exit(void)
+{
+ platform_driver_unregister(&sh_cmt_device_driver);
+}
+
+module_init(sh_cmt_init);
+module_exit(sh_cmt_exit);
+
+MODULE_AUTHOR("Magnus Damm");
+MODULE_DESCRIPTION("SuperH CMT Timer Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index d6daf3c..d270e8e 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -104,7 +104,8 @@
/* internal prototypes */
-static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event);
+static int __cpufreq_governor(struct cpufreq_policy *policy,
+ unsigned int event);
static unsigned int __cpufreq_get(unsigned int cpu);
static void handle_update(struct work_struct *work);
@@ -128,7 +129,7 @@
pure_initcall(init_cpufreq_transition_notifier_list);
static LIST_HEAD(cpufreq_governor_list);
-static DEFINE_MUTEX (cpufreq_governor_mutex);
+static DEFINE_MUTEX(cpufreq_governor_mutex);
struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
{
@@ -371,7 +372,7 @@
struct cpufreq_governor *t;
list_for_each_entry(t, &cpufreq_governor_list, governor_list)
- if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN))
+ if (!strnicmp(str_governor, t->name, CPUFREQ_NAME_LEN))
return t;
return NULL;
@@ -429,15 +430,11 @@
mutex_unlock(&cpufreq_governor_mutex);
}
- out:
+out:
return err;
}
-/* drivers/base/cpu.c */
-extern struct sysdev_class cpu_sysdev_class;
-
-
/**
* cpufreq_per_cpu_attr_read() / show_##file_name() -
* print out cpufreq information
@@ -450,11 +447,12 @@
static ssize_t show_##file_name \
(struct cpufreq_policy *policy, char *buf) \
{ \
- return sprintf (buf, "%u\n", policy->object); \
+ return sprintf(buf, "%u\n", policy->object); \
}
show_one(cpuinfo_min_freq, cpuinfo.min_freq);
show_one(cpuinfo_max_freq, cpuinfo.max_freq);
+show_one(cpuinfo_transition_latency, cpuinfo.transition_latency);
show_one(scaling_min_freq, min);
show_one(scaling_max_freq, max);
show_one(scaling_cur_freq, cur);
@@ -476,7 +474,7 @@
if (ret) \
return -EINVAL; \
\
- ret = sscanf (buf, "%u", &new_policy.object); \
+ ret = sscanf(buf, "%u", &new_policy.object); \
if (ret != 1) \
return -EINVAL; \
\
@@ -486,8 +484,8 @@
return ret ? ret : count; \
}
-store_one(scaling_min_freq,min);
-store_one(scaling_max_freq,max);
+store_one(scaling_min_freq, min);
+store_one(scaling_max_freq, max);
/**
* show_cpuinfo_cur_freq - current CPU frequency as detected by hardware
@@ -507,12 +505,13 @@
*/
static ssize_t show_scaling_governor(struct cpufreq_policy *policy, char *buf)
{
- if(policy->policy == CPUFREQ_POLICY_POWERSAVE)
+ if (policy->policy == CPUFREQ_POLICY_POWERSAVE)
return sprintf(buf, "powersave\n");
else if (policy->policy == CPUFREQ_POLICY_PERFORMANCE)
return sprintf(buf, "performance\n");
else if (policy->governor)
- return scnprintf(buf, CPUFREQ_NAME_LEN, "%s\n", policy->governor->name);
+ return scnprintf(buf, CPUFREQ_NAME_LEN, "%s\n",
+ policy->governor->name);
return -EINVAL;
}
@@ -531,7 +530,7 @@
if (ret)
return ret;
- ret = sscanf (buf, "%15s", str_governor);
+ ret = sscanf(buf, "%15s", str_governor);
if (ret != 1)
return -EINVAL;
@@ -575,7 +574,8 @@
}
list_for_each_entry(t, &cpufreq_governor_list, governor_list) {
- if (i >= (ssize_t) ((PAGE_SIZE / sizeof(char)) - (CPUFREQ_NAME_LEN + 2)))
+ if (i >= (ssize_t) ((PAGE_SIZE / sizeof(char))
+ - (CPUFREQ_NAME_LEN + 2)))
goto out;
i += scnprintf(&buf[i], CPUFREQ_NAME_LEN, "%s ", t->name);
}
@@ -594,7 +594,7 @@
i += scnprintf(&buf[i], (PAGE_SIZE - i - 2), " ");
i += scnprintf(&buf[i], (PAGE_SIZE - i - 2), "%u", cpu);
if (i >= (PAGE_SIZE - 5))
- break;
+ break;
}
i += sprintf(&buf[i], "\n");
return i;
@@ -660,6 +660,7 @@
define_one_ro0400(cpuinfo_cur_freq);
define_one_ro(cpuinfo_min_freq);
define_one_ro(cpuinfo_max_freq);
+define_one_ro(cpuinfo_transition_latency);
define_one_ro(scaling_available_governors);
define_one_ro(scaling_driver);
define_one_ro(scaling_cur_freq);
@@ -673,6 +674,7 @@
static struct attribute *default_attrs[] = {
&cpuinfo_min_freq.attr,
&cpuinfo_max_freq.attr,
+ &cpuinfo_transition_latency.attr,
&scaling_min_freq.attr,
&scaling_max_freq.attr,
&affected_cpus.attr,
@@ -684,10 +686,10 @@
NULL
};
-#define to_policy(k) container_of(k,struct cpufreq_policy,kobj)
-#define to_attr(a) container_of(a,struct freq_attr,attr)
+#define to_policy(k) container_of(k, struct cpufreq_policy, kobj)
+#define to_attr(a) container_of(a, struct freq_attr, attr)
-static ssize_t show(struct kobject *kobj, struct attribute *attr ,char *buf)
+static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
{
struct cpufreq_policy *policy = to_policy(kobj);
struct freq_attr *fattr = to_attr(attr);
@@ -853,10 +855,10 @@
if (cpu == j)
continue;
- /* check for existing affected CPUs. They may not be aware
- * of it due to CPU Hotplug.
+ /* Check for existing affected CPUs.
+ * They may not be aware of it due to CPU Hotplug.
*/
- managed_policy = cpufreq_cpu_get(j); // FIXME: Where is this released? What about error paths?
+ managed_policy = cpufreq_cpu_get(j); /* FIXME: Where is this released? What about error paths? */
if (unlikely(managed_policy)) {
/* Set proper policy_cpu */
@@ -1127,8 +1129,8 @@
* @old_freq: CPU frequency the kernel thinks the CPU runs at
* @new_freq: CPU frequency the CPU actually runs at
*
- * We adjust to current frequency first, and need to clean up later. So either call
- * to cpufreq_update_policy() or schedule handle_update()).
+ * We adjust to current frequency first, and need to clean up later.
+ * So either call to cpufreq_update_policy() or schedule handle_update()).
*/
static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq,
unsigned int new_freq)
@@ -1610,7 +1612,8 @@
/**
* cpufreq_get_policy - get the current cpufreq_policy
- * @policy: struct cpufreq_policy into which the current cpufreq_policy is written
+ * @policy: struct cpufreq_policy into which the current cpufreq_policy
+ * is written
*
* Reads the current cpufreq policy.
*/
diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c
index 0320962..2ecd95e 100644
--- a/drivers/cpufreq/cpufreq_conservative.c
+++ b/drivers/cpufreq/cpufreq_conservative.c
@@ -4,7 +4,7 @@
* Copyright (C) 2001 Russell King
* (C) 2003 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
* Jun Nakajima <jun.nakajima@intel.com>
- * (C) 2004 Alexander Clouter <alex-kernel@digriz.org.uk>
+ * (C) 2009 Alexander Clouter <alex@digriz.org.uk>
*
* 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
@@ -13,22 +13,17 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/smp.h>
#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/ctype.h>
#include <linux/cpufreq.h>
-#include <linux/sysctl.h>
-#include <linux/types.h>
-#include <linux/fs.h>
-#include <linux/sysfs.h>
#include <linux/cpu.h>
-#include <linux/kmod.h>
-#include <linux/workqueue.h>
#include <linux/jiffies.h>
#include <linux/kernel_stat.h>
-#include <linux/percpu.h>
#include <linux/mutex.h>
+#include <linux/hrtimer.h>
+#include <linux/tick.h>
+#include <linux/ktime.h>
+#include <linux/sched.h>
+
/*
* dbs is used in this file as a shortform for demandbased switching
* It helps to keep variable names smaller, simpler
@@ -43,19 +38,31 @@
* latency of the processor. The governor will work on any processor with
* transition latency <= 10mS, using appropriate sampling
* rate.
- * For CPUs with transition latency > 10mS (mostly drivers
- * with CPUFREQ_ETERNAL), this governor will not work.
+ * For CPUs with transition latency > 10mS (mostly drivers with CPUFREQ_ETERNAL)
+ * this governor will not work.
* All times here are in uS.
*/
static unsigned int def_sampling_rate;
#define MIN_SAMPLING_RATE_RATIO (2)
/* for correct statistics, we need at least 10 ticks between each measure */
-#define MIN_STAT_SAMPLING_RATE \
+#define MIN_STAT_SAMPLING_RATE \
(MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10))
#define MIN_SAMPLING_RATE \
(def_sampling_rate / MIN_SAMPLING_RATE_RATIO)
+/* Above MIN_SAMPLING_RATE will vanish with its sysfs file soon
+ * Define the minimal settable sampling rate to the greater of:
+ * - "HW transition latency" * 100 (same as default sampling / 10)
+ * - MIN_STAT_SAMPLING_RATE
+ * To avoid that userspace shoots itself.
+*/
+static unsigned int minimum_sampling_rate(void)
+{
+ return max(def_sampling_rate / 10, MIN_STAT_SAMPLING_RATE);
+}
+
+/* This will also vanish soon with removing sampling_rate_max */
#define MAX_SAMPLING_RATE (500 * def_sampling_rate)
-#define DEF_SAMPLING_RATE_LATENCY_MULTIPLIER (1000)
+#define LATENCY_MULTIPLIER (1000)
#define DEF_SAMPLING_DOWN_FACTOR (1)
#define MAX_SAMPLING_DOWN_FACTOR (10)
#define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000)
@@ -63,12 +70,15 @@
static void do_dbs_timer(struct work_struct *work);
struct cpu_dbs_info_s {
+ cputime64_t prev_cpu_idle;
+ cputime64_t prev_cpu_wall;
+ cputime64_t prev_cpu_nice;
struct cpufreq_policy *cur_policy;
- unsigned int prev_cpu_idle_up;
- unsigned int prev_cpu_idle_down;
- unsigned int enable;
+ struct delayed_work work;
unsigned int down_skip;
unsigned int requested_freq;
+ int cpu;
+ unsigned int enable:1;
};
static DEFINE_PER_CPU(struct cpu_dbs_info_s, cpu_dbs_info);
@@ -82,19 +92,18 @@
* cpu_hotplug lock should be taken before that. Note that cpu_hotplug lock
* is recursive for the same process. -Venki
*/
-static DEFINE_MUTEX (dbs_mutex);
-static DECLARE_DELAYED_WORK(dbs_work, do_dbs_timer);
+static DEFINE_MUTEX(dbs_mutex);
-struct dbs_tuners {
+static struct workqueue_struct *kconservative_wq;
+
+static struct dbs_tuners {
unsigned int sampling_rate;
unsigned int sampling_down_factor;
unsigned int up_threshold;
unsigned int down_threshold;
unsigned int ignore_nice;
unsigned int freq_step;
-};
-
-static struct dbs_tuners dbs_tuners_ins = {
+} dbs_tuners_ins = {
.up_threshold = DEF_FREQUENCY_UP_THRESHOLD,
.down_threshold = DEF_FREQUENCY_DOWN_THRESHOLD,
.sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR,
@@ -102,18 +111,37 @@
.freq_step = 5,
};
-static inline unsigned int get_cpu_idle_time(unsigned int cpu)
+static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu,
+ cputime64_t *wall)
{
- unsigned int add_nice = 0, ret;
+ cputime64_t idle_time;
+ cputime64_t cur_wall_time;
+ cputime64_t busy_time;
- if (dbs_tuners_ins.ignore_nice)
- add_nice = kstat_cpu(cpu).cpustat.nice;
+ cur_wall_time = jiffies64_to_cputime64(get_jiffies_64());
+ busy_time = cputime64_add(kstat_cpu(cpu).cpustat.user,
+ kstat_cpu(cpu).cpustat.system);
- ret = kstat_cpu(cpu).cpustat.idle +
- kstat_cpu(cpu).cpustat.iowait +
- add_nice;
+ busy_time = cputime64_add(busy_time, kstat_cpu(cpu).cpustat.irq);
+ busy_time = cputime64_add(busy_time, kstat_cpu(cpu).cpustat.softirq);
+ busy_time = cputime64_add(busy_time, kstat_cpu(cpu).cpustat.steal);
+ busy_time = cputime64_add(busy_time, kstat_cpu(cpu).cpustat.nice);
- return ret;
+ idle_time = cputime64_sub(cur_wall_time, busy_time);
+ if (wall)
+ *wall = cur_wall_time;
+
+ return idle_time;
+}
+
+static inline cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall)
+{
+ u64 idle_time = get_cpu_idle_time_us(cpu, wall);
+
+ if (idle_time == -1ULL)
+ return get_cpu_idle_time_jiffy(cpu, wall);
+
+ return idle_time;
}
/* keep track of frequency transitions */
@@ -125,10 +153,21 @@
struct cpu_dbs_info_s *this_dbs_info = &per_cpu(cpu_dbs_info,
freq->cpu);
+ struct cpufreq_policy *policy;
+
if (!this_dbs_info->enable)
return 0;
- this_dbs_info->requested_freq = freq->new;
+ policy = this_dbs_info->cur_policy;
+
+ /*
+ * we only care if our internally tracked freq moves outside
+ * the 'valid' ranges of freqency available to us otherwise
+ * we do not change it
+ */
+ if (this_dbs_info->requested_freq > policy->max
+ || this_dbs_info->requested_freq < policy->min)
+ this_dbs_info->requested_freq = freq->new;
return 0;
}
@@ -140,16 +179,31 @@
/************************** sysfs interface ************************/
static ssize_t show_sampling_rate_max(struct cpufreq_policy *policy, char *buf)
{
- return sprintf (buf, "%u\n", MAX_SAMPLING_RATE);
+ static int print_once;
+
+ if (!print_once) {
+ printk(KERN_INFO "CPUFREQ: conservative sampling_rate_max "
+ "sysfs file is deprecated - used by: %s\n",
+ current->comm);
+ print_once = 1;
+ }
+ return sprintf(buf, "%u\n", MAX_SAMPLING_RATE);
}
static ssize_t show_sampling_rate_min(struct cpufreq_policy *policy, char *buf)
{
- return sprintf (buf, "%u\n", MIN_SAMPLING_RATE);
+ static int print_once;
+
+ if (!print_once) {
+ printk(KERN_INFO "CPUFREQ: conservative sampling_rate_max "
+ "sysfs file is deprecated - used by: %s\n", current->comm);
+ print_once = 1;
+ }
+ return sprintf(buf, "%u\n", MIN_SAMPLING_RATE);
}
-#define define_one_ro(_name) \
-static struct freq_attr _name = \
+#define define_one_ro(_name) \
+static struct freq_attr _name = \
__ATTR(_name, 0444, show_##_name, NULL)
define_one_ro(sampling_rate_max);
@@ -174,7 +228,8 @@
{
unsigned int input;
int ret;
- ret = sscanf (buf, "%u", &input);
+ ret = sscanf(buf, "%u", &input);
+
if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1)
return -EINVAL;
@@ -190,15 +245,13 @@
{
unsigned int input;
int ret;
- ret = sscanf (buf, "%u", &input);
+ ret = sscanf(buf, "%u", &input);
+
+ if (ret != 1)
+ return -EINVAL;
mutex_lock(&dbs_mutex);
- if (ret != 1 || input > MAX_SAMPLING_RATE || input < MIN_SAMPLING_RATE) {
- mutex_unlock(&dbs_mutex);
- return -EINVAL;
- }
-
- dbs_tuners_ins.sampling_rate = input;
+ dbs_tuners_ins.sampling_rate = max(input, minimum_sampling_rate());
mutex_unlock(&dbs_mutex);
return count;
@@ -209,10 +262,11 @@
{
unsigned int input;
int ret;
- ret = sscanf (buf, "%u", &input);
+ ret = sscanf(buf, "%u", &input);
mutex_lock(&dbs_mutex);
- if (ret != 1 || input > 100 || input <= dbs_tuners_ins.down_threshold) {
+ if (ret != 1 || input > 100 ||
+ input <= dbs_tuners_ins.down_threshold) {
mutex_unlock(&dbs_mutex);
return -EINVAL;
}
@@ -228,10 +282,12 @@
{
unsigned int input;
int ret;
- ret = sscanf (buf, "%u", &input);
+ ret = sscanf(buf, "%u", &input);
mutex_lock(&dbs_mutex);
- if (ret != 1 || input > 100 || input >= dbs_tuners_ins.up_threshold) {
+ /* cannot be lower than 11 otherwise freq will not fall */
+ if (ret != 1 || input < 11 || input > 100 ||
+ input >= dbs_tuners_ins.up_threshold) {
mutex_unlock(&dbs_mutex);
return -EINVAL;
}
@@ -264,12 +320,14 @@
}
dbs_tuners_ins.ignore_nice = input;
- /* we need to re-evaluate prev_cpu_idle_up and prev_cpu_idle_down */
+ /* we need to re-evaluate prev_cpu_idle */
for_each_online_cpu(j) {
- struct cpu_dbs_info_s *j_dbs_info;
- j_dbs_info = &per_cpu(cpu_dbs_info, j);
- j_dbs_info->prev_cpu_idle_up = get_cpu_idle_time(j);
- j_dbs_info->prev_cpu_idle_down = j_dbs_info->prev_cpu_idle_up;
+ struct cpu_dbs_info_s *dbs_info;
+ dbs_info = &per_cpu(cpu_dbs_info, j);
+ dbs_info->prev_cpu_idle = get_cpu_idle_time(j,
+ &dbs_info->prev_cpu_wall);
+ if (dbs_tuners_ins.ignore_nice)
+ dbs_info->prev_cpu_nice = kstat_cpu(j).cpustat.nice;
}
mutex_unlock(&dbs_mutex);
@@ -281,7 +339,6 @@
{
unsigned int input;
int ret;
-
ret = sscanf(buf, "%u", &input);
if (ret != 1)
@@ -310,7 +367,7 @@
define_one_rw(ignore_nice_load);
define_one_rw(freq_step);
-static struct attribute * dbs_attributes[] = {
+static struct attribute *dbs_attributes[] = {
&sampling_rate_max.attr,
&sampling_rate_min.attr,
&sampling_rate.attr,
@@ -329,55 +386,78 @@
/************************** sysfs end ************************/
-static void dbs_check_cpu(int cpu)
+static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
{
- unsigned int idle_ticks, up_idle_ticks, down_idle_ticks;
- unsigned int tmp_idle_ticks, total_idle_ticks;
+ unsigned int load = 0;
unsigned int freq_target;
- unsigned int freq_down_sampling_rate;
- struct cpu_dbs_info_s *this_dbs_info = &per_cpu(cpu_dbs_info, cpu);
- struct cpufreq_policy *policy;
- if (!this_dbs_info->enable)
- return;
+ struct cpufreq_policy *policy;
+ unsigned int j;
policy = this_dbs_info->cur_policy;
/*
- * The default safe range is 20% to 80%
- * Every sampling_rate, we check
- * - If current idle time is less than 20%, then we try to
- * increase frequency
- * Every sampling_rate*sampling_down_factor, we check
- * - If current idle time is more than 80%, then we try to
- * decrease frequency
+ * Every sampling_rate, we check, if current idle time is less
+ * than 20% (default), then we try to increase frequency
+ * Every sampling_rate*sampling_down_factor, we check, if current
+ * idle time is more than 80%, then we try to decrease frequency
*
* Any frequency increase takes it to the maximum frequency.
* Frequency reduction happens at minimum steps of
- * 5% (default) of max_frequency
+ * 5% (default) of maximum frequency
*/
- /* Check for frequency increase */
- idle_ticks = UINT_MAX;
+ /* Get Absolute Load */
+ for_each_cpu(j, policy->cpus) {
+ struct cpu_dbs_info_s *j_dbs_info;
+ cputime64_t cur_wall_time, cur_idle_time;
+ unsigned int idle_time, wall_time;
+
+ j_dbs_info = &per_cpu(cpu_dbs_info, j);
+
+ cur_idle_time = get_cpu_idle_time(j, &cur_wall_time);
+
+ wall_time = (unsigned int) cputime64_sub(cur_wall_time,
+ j_dbs_info->prev_cpu_wall);
+ j_dbs_info->prev_cpu_wall = cur_wall_time;
+
+ idle_time = (unsigned int) cputime64_sub(cur_idle_time,
+ j_dbs_info->prev_cpu_idle);
+ j_dbs_info->prev_cpu_idle = cur_idle_time;
+
+ if (dbs_tuners_ins.ignore_nice) {
+ cputime64_t cur_nice;
+ unsigned long cur_nice_jiffies;
+
+ cur_nice = cputime64_sub(kstat_cpu(j).cpustat.nice,
+ j_dbs_info->prev_cpu_nice);
+ /*
+ * Assumption: nice time between sampling periods will
+ * be less than 2^32 jiffies for 32 bit sys
+ */
+ cur_nice_jiffies = (unsigned long)
+ cputime64_to_jiffies64(cur_nice);
+
+ j_dbs_info->prev_cpu_nice = kstat_cpu(j).cpustat.nice;
+ idle_time += jiffies_to_usecs(cur_nice_jiffies);
+ }
+
+ if (unlikely(!wall_time || wall_time < idle_time))
+ continue;
+
+ load = 100 * (wall_time - idle_time) / wall_time;
+ }
+
+ /*
+ * break out if we 'cannot' reduce the speed as the user might
+ * want freq_step to be zero
+ */
+ if (dbs_tuners_ins.freq_step == 0)
+ return;
/* Check for frequency increase */
- total_idle_ticks = get_cpu_idle_time(cpu);
- tmp_idle_ticks = total_idle_ticks -
- this_dbs_info->prev_cpu_idle_up;
- this_dbs_info->prev_cpu_idle_up = total_idle_ticks;
-
- if (tmp_idle_ticks < idle_ticks)
- idle_ticks = tmp_idle_ticks;
-
- /* Scale idle ticks by 100 and compare with up and down ticks */
- idle_ticks *= 100;
- up_idle_ticks = (100 - dbs_tuners_ins.up_threshold) *
- usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
-
- if (idle_ticks < up_idle_ticks) {
+ if (load > dbs_tuners_ins.up_threshold) {
this_dbs_info->down_skip = 0;
- this_dbs_info->prev_cpu_idle_down =
- this_dbs_info->prev_cpu_idle_up;
/* if we are already at full speed then break out early */
if (this_dbs_info->requested_freq == policy->max)
@@ -398,49 +478,24 @@
return;
}
- /* Check for frequency decrease */
- this_dbs_info->down_skip++;
- if (this_dbs_info->down_skip < dbs_tuners_ins.sampling_down_factor)
- return;
-
- /* Check for frequency decrease */
- total_idle_ticks = this_dbs_info->prev_cpu_idle_up;
- tmp_idle_ticks = total_idle_ticks -
- this_dbs_info->prev_cpu_idle_down;
- this_dbs_info->prev_cpu_idle_down = total_idle_ticks;
-
- if (tmp_idle_ticks < idle_ticks)
- idle_ticks = tmp_idle_ticks;
-
- /* Scale idle ticks by 100 and compare with up and down ticks */
- idle_ticks *= 100;
- this_dbs_info->down_skip = 0;
-
- freq_down_sampling_rate = dbs_tuners_ins.sampling_rate *
- dbs_tuners_ins.sampling_down_factor;
- down_idle_ticks = (100 - dbs_tuners_ins.down_threshold) *
- usecs_to_jiffies(freq_down_sampling_rate);
-
- if (idle_ticks > down_idle_ticks) {
- /*
- * if we are already at the lowest speed then break out early
- * or if we 'cannot' reduce the speed as the user might want
- * freq_target to be zero
- */
- if (this_dbs_info->requested_freq == policy->min
- || dbs_tuners_ins.freq_step == 0)
- return;
-
+ /*
+ * The optimal frequency is the frequency that is the lowest that
+ * can support the current CPU usage without triggering the up
+ * policy. To be safe, we focus 10 points under the threshold.
+ */
+ if (load < (dbs_tuners_ins.down_threshold - 10)) {
freq_target = (dbs_tuners_ins.freq_step * policy->max) / 100;
- /* max freq cannot be less than 100. But who knows.... */
- if (unlikely(freq_target == 0))
- freq_target = 5;
-
this_dbs_info->requested_freq -= freq_target;
if (this_dbs_info->requested_freq < policy->min)
this_dbs_info->requested_freq = policy->min;
+ /*
+ * if we cannot reduce the frequency anymore, break out early
+ */
+ if (policy->cur == policy->min)
+ return;
+
__cpufreq_driver_target(policy, this_dbs_info->requested_freq,
CPUFREQ_RELATION_H);
return;
@@ -449,27 +504,45 @@
static void do_dbs_timer(struct work_struct *work)
{
- int i;
- mutex_lock(&dbs_mutex);
- for_each_online_cpu(i)
- dbs_check_cpu(i);
- schedule_delayed_work(&dbs_work,
- usecs_to_jiffies(dbs_tuners_ins.sampling_rate));
- mutex_unlock(&dbs_mutex);
+ struct cpu_dbs_info_s *dbs_info =
+ container_of(work, struct cpu_dbs_info_s, work.work);
+ unsigned int cpu = dbs_info->cpu;
+
+ /* We want all CPUs to do sampling nearly on same jiffy */
+ int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
+
+ delay -= jiffies % delay;
+
+ if (lock_policy_rwsem_write(cpu) < 0)
+ return;
+
+ if (!dbs_info->enable) {
+ unlock_policy_rwsem_write(cpu);
+ return;
+ }
+
+ dbs_check_cpu(dbs_info);
+
+ queue_delayed_work_on(cpu, kconservative_wq, &dbs_info->work, delay);
+ unlock_policy_rwsem_write(cpu);
}
-static inline void dbs_timer_init(void)
+static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info)
{
- init_timer_deferrable(&dbs_work.timer);
- schedule_delayed_work(&dbs_work,
- usecs_to_jiffies(dbs_tuners_ins.sampling_rate));
- return;
+ /* We want all CPUs to do sampling nearly on same jiffy */
+ int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
+ delay -= jiffies % delay;
+
+ dbs_info->enable = 1;
+ INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer);
+ queue_delayed_work_on(dbs_info->cpu, kconservative_wq, &dbs_info->work,
+ delay);
}
-static inline void dbs_timer_exit(void)
+static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info)
{
- cancel_delayed_work(&dbs_work);
- return;
+ dbs_info->enable = 0;
+ cancel_delayed_work(&dbs_info->work);
}
static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
@@ -503,11 +576,13 @@
j_dbs_info = &per_cpu(cpu_dbs_info, j);
j_dbs_info->cur_policy = policy;
- j_dbs_info->prev_cpu_idle_up = get_cpu_idle_time(cpu);
- j_dbs_info->prev_cpu_idle_down
- = j_dbs_info->prev_cpu_idle_up;
+ j_dbs_info->prev_cpu_idle = get_cpu_idle_time(j,
+ &j_dbs_info->prev_cpu_wall);
+ if (dbs_tuners_ins.ignore_nice) {
+ j_dbs_info->prev_cpu_nice =
+ kstat_cpu(j).cpustat.nice;
+ }
}
- this_dbs_info->enable = 1;
this_dbs_info->down_skip = 0;
this_dbs_info->requested_freq = policy->cur;
@@ -523,38 +598,36 @@
if (latency == 0)
latency = 1;
- def_sampling_rate = 10 * latency *
- DEF_SAMPLING_RATE_LATENCY_MULTIPLIER;
-
- if (def_sampling_rate < MIN_STAT_SAMPLING_RATE)
- def_sampling_rate = MIN_STAT_SAMPLING_RATE;
+ def_sampling_rate =
+ max(latency * LATENCY_MULTIPLIER,
+ MIN_STAT_SAMPLING_RATE);
dbs_tuners_ins.sampling_rate = def_sampling_rate;
- dbs_timer_init();
cpufreq_register_notifier(
&dbs_cpufreq_notifier_block,
CPUFREQ_TRANSITION_NOTIFIER);
}
+ dbs_timer_init(this_dbs_info);
mutex_unlock(&dbs_mutex);
+
break;
case CPUFREQ_GOV_STOP:
mutex_lock(&dbs_mutex);
- this_dbs_info->enable = 0;
+ dbs_timer_exit(this_dbs_info);
sysfs_remove_group(&policy->kobj, &dbs_attr_group);
dbs_enable--;
+
/*
* Stop the timerschedule work, when this governor
* is used for first time
*/
- if (dbs_enable == 0) {
- dbs_timer_exit();
+ if (dbs_enable == 0)
cpufreq_unregister_notifier(
&dbs_cpufreq_notifier_block,
CPUFREQ_TRANSITION_NOTIFIER);
- }
mutex_unlock(&dbs_mutex);
@@ -571,6 +644,7 @@
this_dbs_info->cur_policy,
policy->min, CPUFREQ_RELATION_L);
mutex_unlock(&dbs_mutex);
+
break;
}
return 0;
@@ -588,23 +662,33 @@
static int __init cpufreq_gov_dbs_init(void)
{
- return cpufreq_register_governor(&cpufreq_gov_conservative);
+ int err;
+
+ kconservative_wq = create_workqueue("kconservative");
+ if (!kconservative_wq) {
+ printk(KERN_ERR "Creation of kconservative failed\n");
+ return -EFAULT;
+ }
+
+ err = cpufreq_register_governor(&cpufreq_gov_conservative);
+ if (err)
+ destroy_workqueue(kconservative_wq);
+
+ return err;
}
static void __exit cpufreq_gov_dbs_exit(void)
{
- /* Make sure that the scheduled work is indeed not running */
- flush_scheduled_work();
-
cpufreq_unregister_governor(&cpufreq_gov_conservative);
+ destroy_workqueue(kconservative_wq);
}
-MODULE_AUTHOR ("Alexander Clouter <alex-kernel@digriz.org.uk>");
-MODULE_DESCRIPTION ("'cpufreq_conservative' - A dynamic cpufreq governor for "
+MODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>");
+MODULE_DESCRIPTION("'cpufreq_conservative' - A dynamic cpufreq governor for "
"Low Latency Frequency Transition capable processors "
"optimised for use in a battery environment");
-MODULE_LICENSE ("GPL");
+MODULE_LICENSE("GPL");
#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE
fs_initcall(cpufreq_gov_dbs_init);
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 6f45b16..338f428 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -21,6 +21,7 @@
#include <linux/hrtimer.h>
#include <linux/tick.h>
#include <linux/ktime.h>
+#include <linux/sched.h>
/*
* dbs is used in this file as a shortform for demandbased switching
@@ -51,8 +52,20 @@
(MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10))
#define MIN_SAMPLING_RATE \
(def_sampling_rate / MIN_SAMPLING_RATE_RATIO)
+/* Above MIN_SAMPLING_RATE will vanish with its sysfs file soon
+ * Define the minimal settable sampling rate to the greater of:
+ * - "HW transition latency" * 100 (same as default sampling / 10)
+ * - MIN_STAT_SAMPLING_RATE
+ * To avoid that userspace shoots itself.
+*/
+static unsigned int minimum_sampling_rate(void)
+{
+ return max(def_sampling_rate / 10, MIN_STAT_SAMPLING_RATE);
+}
+
+/* This will also vanish soon with removing sampling_rate_max */
#define MAX_SAMPLING_RATE (500 * def_sampling_rate)
-#define DEF_SAMPLING_RATE_LATENCY_MULTIPLIER (1000)
+#define LATENCY_MULTIPLIER (1000)
#define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000)
static void do_dbs_timer(struct work_struct *work);
@@ -65,14 +78,14 @@
cputime64_t prev_cpu_wall;
cputime64_t prev_cpu_nice;
struct cpufreq_policy *cur_policy;
- struct delayed_work work;
+ struct delayed_work work;
struct cpufreq_frequency_table *freq_table;
unsigned int freq_lo;
unsigned int freq_lo_jiffies;
unsigned int freq_hi_jiffies;
int cpu;
unsigned int enable:1,
- sample_type:1;
+ sample_type:1;
};
static DEFINE_PER_CPU(struct cpu_dbs_info_s, cpu_dbs_info);
@@ -203,12 +216,28 @@
/************************** sysfs interface ************************/
static ssize_t show_sampling_rate_max(struct cpufreq_policy *policy, char *buf)
{
- return sprintf (buf, "%u\n", MAX_SAMPLING_RATE);
+ static int print_once;
+
+ if (!print_once) {
+ printk(KERN_INFO "CPUFREQ: ondemand sampling_rate_max "
+ "sysfs file is deprecated - used by: %s\n",
+ current->comm);
+ print_once = 1;
+ }
+ return sprintf(buf, "%u\n", MAX_SAMPLING_RATE);
}
static ssize_t show_sampling_rate_min(struct cpufreq_policy *policy, char *buf)
{
- return sprintf (buf, "%u\n", MIN_SAMPLING_RATE);
+ static int print_once;
+
+ if (!print_once) {
+ printk(KERN_INFO "CPUFREQ: ondemand sampling_rate_min "
+ "sysfs file is deprecated - used by: %s\n",
+ current->comm);
+ print_once = 1;
+ }
+ return sprintf(buf, "%u\n", MIN_SAMPLING_RATE);
}
#define define_one_ro(_name) \
@@ -238,13 +267,11 @@
ret = sscanf(buf, "%u", &input);
mutex_lock(&dbs_mutex);
- if (ret != 1 || input > MAX_SAMPLING_RATE
- || input < MIN_SAMPLING_RATE) {
+ if (ret != 1) {
mutex_unlock(&dbs_mutex);
return -EINVAL;
}
-
- dbs_tuners_ins.sampling_rate = input;
+ dbs_tuners_ins.sampling_rate = max(input, minimum_sampling_rate());
mutex_unlock(&dbs_mutex);
return count;
@@ -279,14 +306,14 @@
unsigned int j;
ret = sscanf(buf, "%u", &input);
- if ( ret != 1 )
+ if (ret != 1)
return -EINVAL;
- if ( input > 1 )
+ if (input > 1)
input = 1;
mutex_lock(&dbs_mutex);
- if ( input == dbs_tuners_ins.ignore_nice ) { /* nothing to do */
+ if (input == dbs_tuners_ins.ignore_nice) { /* nothing to do */
mutex_unlock(&dbs_mutex);
return count;
}
@@ -337,7 +364,7 @@
define_one_rw(ignore_nice_load);
define_one_rw(powersave_bias);
-static struct attribute * dbs_attributes[] = {
+static struct attribute *dbs_attributes[] = {
&sampling_rate_max.attr,
&sampling_rate_min.attr,
&sampling_rate.attr,
@@ -512,8 +539,7 @@
}
} else {
__cpufreq_driver_target(dbs_info->cur_policy,
- dbs_info->freq_lo,
- CPUFREQ_RELATION_H);
+ dbs_info->freq_lo, CPUFREQ_RELATION_H);
}
queue_delayed_work_on(cpu, kondemand_wq, &dbs_info->work, delay);
unlock_policy_rwsem_write(cpu);
@@ -530,7 +556,7 @@
dbs_info->sample_type = DBS_NORMAL_SAMPLE;
INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer);
queue_delayed_work_on(dbs_info->cpu, kondemand_wq, &dbs_info->work,
- delay);
+ delay);
}
static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info)
@@ -591,11 +617,9 @@
if (latency == 0)
latency = 1;
- def_sampling_rate = latency *
- DEF_SAMPLING_RATE_LATENCY_MULTIPLIER;
-
- if (def_sampling_rate < MIN_STAT_SAMPLING_RATE)
- def_sampling_rate = MIN_STAT_SAMPLING_RATE;
+ def_sampling_rate =
+ max(latency * LATENCY_MULTIPLIER,
+ MIN_STAT_SAMPLING_RATE);
dbs_tuners_ins.sampling_rate = def_sampling_rate;
}
@@ -617,12 +641,10 @@
mutex_lock(&dbs_mutex);
if (policy->max < this_dbs_info->cur_policy->cur)
__cpufreq_driver_target(this_dbs_info->cur_policy,
- policy->max,
- CPUFREQ_RELATION_H);
+ policy->max, CPUFREQ_RELATION_H);
else if (policy->min > this_dbs_info->cur_policy->cur)
__cpufreq_driver_target(this_dbs_info->cur_policy,
- policy->min,
- CPUFREQ_RELATION_L);
+ policy->min, CPUFREQ_RELATION_L);
mutex_unlock(&dbs_mutex);
break;
}
@@ -677,7 +699,7 @@
MODULE_AUTHOR("Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>");
MODULE_AUTHOR("Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>");
MODULE_DESCRIPTION("'cpufreq_ondemand' - A dynamic cpufreq governor for "
- "Low Latency Frequency Transition capable processors");
+ "Low Latency Frequency Transition capable processors");
MODULE_LICENSE("GPL");
#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c
index c0ff97d..5a62d67 100644
--- a/drivers/cpufreq/cpufreq_stats.c
+++ b/drivers/cpufreq/cpufreq_stats.c
@@ -2,7 +2,7 @@
* drivers/cpufreq/cpufreq_stats.c
*
* Copyright (C) 2003-2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
- * (C) 2004 Zou Nan hai <nanhai.zou@intel.com>.
+ * (C) 2004 Zou Nan hai <nanhai.zou@intel.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
@@ -23,7 +23,7 @@
static spinlock_t cpufreq_stats_lock;
-#define CPUFREQ_STATDEVICE_ATTR(_name,_mode,_show) \
+#define CPUFREQ_STATDEVICE_ATTR(_name, _mode, _show) \
static struct freq_attr _attr_##_name = {\
.attr = {.name = __stringify(_name), .mode = _mode, }, \
.show = _show,\
@@ -50,8 +50,7 @@
ssize_t(*show) (struct cpufreq_stats *, char *);
};
-static int
-cpufreq_stats_update (unsigned int cpu)
+static int cpufreq_stats_update(unsigned int cpu)
{
struct cpufreq_stats *stat;
unsigned long long cur_time;
@@ -68,8 +67,7 @@
return 0;
}
-static ssize_t
-show_total_trans(struct cpufreq_policy *policy, char *buf)
+static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf)
{
struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu);
if (!stat)
@@ -78,8 +76,7 @@
per_cpu(cpufreq_stats_table, stat->cpu)->total_trans);
}
-static ssize_t
-show_time_in_state(struct cpufreq_policy *policy, char *buf)
+static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
{
ssize_t len = 0;
int i;
@@ -89,14 +86,14 @@
cpufreq_stats_update(stat->cpu);
for (i = 0; i < stat->state_num; i++) {
len += sprintf(buf + len, "%u %llu\n", stat->freq_table[i],
- (unsigned long long)cputime64_to_clock_t(stat->time_in_state[i]));
+ (unsigned long long)
+ cputime64_to_clock_t(stat->time_in_state[i]));
}
return len;
}
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
-static ssize_t
-show_trans_table(struct cpufreq_policy *policy, char *buf)
+static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
{
ssize_t len = 0;
int i, j;
@@ -139,11 +136,11 @@
return PAGE_SIZE;
return len;
}
-CPUFREQ_STATDEVICE_ATTR(trans_table,0444,show_trans_table);
+CPUFREQ_STATDEVICE_ATTR(trans_table, 0444, show_trans_table);
#endif
-CPUFREQ_STATDEVICE_ATTR(total_trans,0444,show_total_trans);
-CPUFREQ_STATDEVICE_ATTR(time_in_state,0444,show_time_in_state);
+CPUFREQ_STATDEVICE_ATTR(total_trans, 0444, show_total_trans);
+CPUFREQ_STATDEVICE_ATTR(time_in_state, 0444, show_time_in_state);
static struct attribute *default_attrs[] = {
&_attr_total_trans.attr,
@@ -158,8 +155,7 @@
.name = "stats"
};
-static int
-freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq)
+static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq)
{
int index;
for (index = 0; index < stat->max_state; index++)
@@ -183,8 +179,7 @@
cpufreq_cpu_put(policy);
}
-static int
-cpufreq_stats_create_table (struct cpufreq_policy *policy,
+static int cpufreq_stats_create_table(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table)
{
unsigned int i, j, count = 0, ret = 0;
@@ -194,7 +189,8 @@
unsigned int cpu = policy->cpu;
if (per_cpu(cpufreq_stats_table, cpu))
return -EBUSY;
- if ((stat = kzalloc(sizeof(struct cpufreq_stats), GFP_KERNEL)) == NULL)
+ stat = kzalloc(sizeof(struct cpufreq_stats), GFP_KERNEL);
+ if ((stat) == NULL)
return -ENOMEM;
data = cpufreq_cpu_get(cpu);
@@ -203,13 +199,14 @@
goto error_get_fail;
}
- if ((ret = sysfs_create_group(&data->kobj, &stats_attr_group)))
+ ret = sysfs_create_group(&data->kobj, &stats_attr_group);
+ if (ret)
goto error_out;
stat->cpu = cpu;
per_cpu(cpufreq_stats_table, cpu) = stat;
- for (i=0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
+ for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
unsigned int freq = table[i].frequency;
if (freq == CPUFREQ_ENTRY_INVALID)
continue;
@@ -255,9 +252,8 @@
return ret;
}
-static int
-cpufreq_stat_notifier_policy (struct notifier_block *nb, unsigned long val,
- void *data)
+static int cpufreq_stat_notifier_policy(struct notifier_block *nb,
+ unsigned long val, void *data)
{
int ret;
struct cpufreq_policy *policy = data;
@@ -268,14 +264,14 @@
table = cpufreq_frequency_get_table(cpu);
if (!table)
return 0;
- if ((ret = cpufreq_stats_create_table(policy, table)))
+ ret = cpufreq_stats_create_table(policy, table);
+ if (ret)
return ret;
return 0;
}
-static int
-cpufreq_stat_notifier_trans (struct notifier_block *nb, unsigned long val,
- void *data)
+static int cpufreq_stat_notifier_trans(struct notifier_block *nb,
+ unsigned long val, void *data)
{
struct cpufreq_freqs *freq = data;
struct cpufreq_stats *stat;
@@ -340,19 +336,20 @@
.notifier_call = cpufreq_stat_notifier_trans
};
-static int
-__init cpufreq_stats_init(void)
+static int __init cpufreq_stats_init(void)
{
int ret;
unsigned int cpu;
spin_lock_init(&cpufreq_stats_lock);
- if ((ret = cpufreq_register_notifier(¬ifier_policy_block,
- CPUFREQ_POLICY_NOTIFIER)))
+ ret = cpufreq_register_notifier(¬ifier_policy_block,
+ CPUFREQ_POLICY_NOTIFIER);
+ if (ret)
return ret;
- if ((ret = cpufreq_register_notifier(¬ifier_trans_block,
- CPUFREQ_TRANSITION_NOTIFIER))) {
+ ret = cpufreq_register_notifier(¬ifier_trans_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+ if (ret) {
cpufreq_unregister_notifier(¬ifier_policy_block,
CPUFREQ_POLICY_NOTIFIER);
return ret;
@@ -364,8 +361,7 @@
}
return 0;
}
-static void
-__exit cpufreq_stats_exit(void)
+static void __exit cpufreq_stats_exit(void)
{
unsigned int cpu;
@@ -379,10 +375,10 @@
}
}
-MODULE_AUTHOR ("Zou Nan hai <nanhai.zou@intel.com>");
-MODULE_DESCRIPTION ("'cpufreq_stats' - A driver to export cpufreq stats "
+MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>");
+MODULE_DESCRIPTION("'cpufreq_stats' - A driver to export cpufreq stats "
"through sysfs filesystem");
-MODULE_LICENSE ("GPL");
+MODULE_LICENSE("GPL");
module_init(cpufreq_stats_init);
module_exit(cpufreq_stats_exit);
diff --git a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c
index 1442bba..66d2d1d 100644
--- a/drivers/cpufreq/cpufreq_userspace.c
+++ b/drivers/cpufreq/cpufreq_userspace.c
@@ -24,9 +24,6 @@
#include <linux/sysfs.h>
#include <linux/mutex.h>
-#include <asm/uaccess.h>
-
-
/**
* A few values needed by the userspace governor
*/
@@ -37,7 +34,7 @@
userspace */
static DEFINE_PER_CPU(unsigned int, cpu_is_managed);
-static DEFINE_MUTEX (userspace_mutex);
+static DEFINE_MUTEX(userspace_mutex);
static int cpus_using_userspace_governor;
#define dprintk(msg...) \
@@ -46,9 +43,9 @@
/* keep track of frequency transitions */
static int
userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
- void *data)
+ void *data)
{
- struct cpufreq_freqs *freq = data;
+ struct cpufreq_freqs *freq = data;
if (!per_cpu(cpu_is_managed, freq->cpu))
return 0;
@@ -57,11 +54,11 @@
freq->cpu, freq->new);
per_cpu(cpu_cur_freq, freq->cpu) = freq->new;
- return 0;
+ return 0;
}
static struct notifier_block userspace_cpufreq_notifier_block = {
- .notifier_call = userspace_cpufreq_notifier
+ .notifier_call = userspace_cpufreq_notifier
};
@@ -93,8 +90,11 @@
* We're safe from concurrent calls to ->target() here
* as we hold the userspace_mutex lock. If we were calling
* cpufreq_driver_target, a deadlock situation might occur:
- * A: cpufreq_set (lock userspace_mutex) -> cpufreq_driver_target(lock policy->lock)
- * B: cpufreq_set_policy(lock policy->lock) -> __cpufreq_governor -> cpufreq_governor_userspace (lock userspace_mutex)
+ * A: cpufreq_set (lock userspace_mutex) ->
+ * cpufreq_driver_target(lock policy->lock)
+ * B: cpufreq_set_policy(lock policy->lock) ->
+ * __cpufreq_governor ->
+ * cpufreq_governor_userspace (lock userspace_mutex)
*/
ret = __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L);
@@ -210,9 +210,10 @@
}
-MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>, Russell King <rmk@arm.linux.org.uk>");
-MODULE_DESCRIPTION ("CPUfreq policy governor 'userspace'");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>, "
+ "Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("CPUfreq policy governor 'userspace'");
+MODULE_LICENSE("GPL");
#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE
fs_initcall(cpufreq_gov_userspace_init);
diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c
index 9071d80..a9bd3a0 100644
--- a/drivers/cpufreq/freq_table.c
+++ b/drivers/cpufreq/freq_table.c
@@ -28,7 +28,7 @@
unsigned int max_freq = 0;
unsigned int i;
- for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
+ for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
unsigned int freq = table[i].frequency;
if (freq == CPUFREQ_ENTRY_INVALID) {
dprintk("table entry %u is invalid, skipping\n", i);
@@ -70,7 +70,7 @@
cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
policy->cpuinfo.max_freq);
- for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
+ for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
unsigned int freq = table[i].frequency;
if (freq == CPUFREQ_ENTRY_INVALID)
continue;
@@ -125,13 +125,13 @@
if (!cpu_online(policy->cpu))
return -EINVAL;
- for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
+ for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
unsigned int freq = table[i].frequency;
if (freq == CPUFREQ_ENTRY_INVALID)
continue;
if ((freq < policy->min) || (freq > policy->max))
continue;
- switch(relation) {
+ switch (relation) {
case CPUFREQ_RELATION_H:
if (freq <= target_freq) {
if (freq >= optimal.frequency) {
@@ -178,7 +178,7 @@
/**
* show_available_freqs - show available frequencies for the specified CPU
*/
-static ssize_t show_available_freqs (struct cpufreq_policy *policy, char *buf)
+static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf)
{
unsigned int i = 0;
unsigned int cpu = policy->cpu;
@@ -190,7 +190,7 @@
table = per_cpu(show_table, cpu);
- for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
+ for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
continue;
count += sprintf(&buf[count], "%d ", table[i].frequency);
@@ -234,6 +234,6 @@
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_get_table);
-MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>");
-MODULE_DESCRIPTION ("CPUfreq frequency table helpers");
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
+MODULE_DESCRIPTION("CPUfreq frequency table helpers");
+MODULE_LICENSE("GPL");
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index e522144..01afd75 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -86,7 +86,7 @@
config CRYPTO_SHA1_S390
tristate "SHA1 digest algorithm"
depends on S390
- select CRYPTO_ALGAPI
+ select CRYPTO_HASH
help
This is the s390 hardware accelerated implementation of the
SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2).
@@ -94,7 +94,7 @@
config CRYPTO_SHA256_S390
tristate "SHA256 digest algorithm"
depends on S390
- select CRYPTO_ALGAPI
+ select CRYPTO_HASH
help
This is the s390 hardware accelerated implementation of the
SHA256 secure hash standard (DFIPS 180-2).
@@ -105,7 +105,7 @@
config CRYPTO_SHA512_S390
tristate "SHA384 and SHA512 digest algorithm"
depends on S390
- select CRYPTO_ALGAPI
+ select CRYPTO_HASH
help
This is the s390 hardware accelerated implementation of the
SHA512 secure hash standard.
@@ -200,4 +200,13 @@
help
Driver for the IXP4xx NPE crypto engine.
+config CRYPTO_DEV_PPC4XX
+ tristate "Driver AMCC PPC4xx crypto accelerator"
+ depends on PPC && 4xx
+ select CRYPTO_HASH
+ select CRYPTO_ALGAPI
+ select CRYPTO_BLKCIPHER
+ help
+ This option allows you to have support for AMCC crypto acceleration.
+
endif # CRYPTO_HW
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index 73557b2..9bf4a2b 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -4,3 +4,4 @@
obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o
obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o
obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
+obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += amcc/
diff --git a/drivers/crypto/amcc/Makefile b/drivers/crypto/amcc/Makefile
new file mode 100644
index 0000000..aa376e8
--- /dev/null
+++ b/drivers/crypto/amcc/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += crypto4xx.o
+crypto4xx-objs := crypto4xx_core.o crypto4xx_alg.o crypto4xx_sa.o
diff --git a/drivers/crypto/amcc/crypto4xx_alg.c b/drivers/crypto/amcc/crypto4xx_alg.c
new file mode 100644
index 0000000..61b6e1b
--- /dev/null
+++ b/drivers/crypto/amcc/crypto4xx_alg.c
@@ -0,0 +1,293 @@
+/**
+ * AMCC SoC PPC4xx Crypto Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <jhsiao@amcc.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.
+ *
+ * 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.
+ *
+ * This file implements the Linux crypto algorithms.
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock_types.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <linux/hash.h>
+#include <crypto/internal/hash.h>
+#include <linux/dma-mapping.h>
+#include <crypto/algapi.h>
+#include <crypto/aes.h>
+#include <crypto/sha.h>
+#include "crypto4xx_reg_def.h"
+#include "crypto4xx_sa.h"
+#include "crypto4xx_core.h"
+
+void set_dynamic_sa_command_0(struct dynamic_sa_ctl *sa, u32 save_h,
+ u32 save_iv, u32 ld_h, u32 ld_iv, u32 hdr_proc,
+ u32 h, u32 c, u32 pad_type, u32 op_grp, u32 op,
+ u32 dir)
+{
+ sa->sa_command_0.w = 0;
+ sa->sa_command_0.bf.save_hash_state = save_h;
+ sa->sa_command_0.bf.save_iv = save_iv;
+ sa->sa_command_0.bf.load_hash_state = ld_h;
+ sa->sa_command_0.bf.load_iv = ld_iv;
+ sa->sa_command_0.bf.hdr_proc = hdr_proc;
+ sa->sa_command_0.bf.hash_alg = h;
+ sa->sa_command_0.bf.cipher_alg = c;
+ sa->sa_command_0.bf.pad_type = pad_type & 3;
+ sa->sa_command_0.bf.extend_pad = pad_type >> 2;
+ sa->sa_command_0.bf.op_group = op_grp;
+ sa->sa_command_0.bf.opcode = op;
+ sa->sa_command_0.bf.dir = dir;
+}
+
+void set_dynamic_sa_command_1(struct dynamic_sa_ctl *sa, u32 cm, u32 hmac_mc,
+ u32 cfb, u32 esn, u32 sn_mask, u32 mute,
+ u32 cp_pad, u32 cp_pay, u32 cp_hdr)
+{
+ sa->sa_command_1.w = 0;
+ sa->sa_command_1.bf.crypto_mode31 = (cm & 4) >> 2;
+ sa->sa_command_1.bf.crypto_mode9_8 = cm & 3;
+ sa->sa_command_1.bf.feedback_mode = cfb,
+ sa->sa_command_1.bf.sa_rev = 1;
+ sa->sa_command_1.bf.extended_seq_num = esn;
+ sa->sa_command_1.bf.seq_num_mask = sn_mask;
+ sa->sa_command_1.bf.mutable_bit_proc = mute;
+ sa->sa_command_1.bf.copy_pad = cp_pad;
+ sa->sa_command_1.bf.copy_payload = cp_pay;
+ sa->sa_command_1.bf.copy_hdr = cp_hdr;
+}
+
+int crypto4xx_encrypt(struct ablkcipher_request *req)
+{
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+
+ ctx->direction = DIR_OUTBOUND;
+ ctx->hash_final = 0;
+ ctx->is_hash = 0;
+ ctx->pd_ctl = 0x1;
+
+ return crypto4xx_build_pd(&req->base, ctx, req->src, req->dst,
+ req->nbytes, req->info,
+ get_dynamic_sa_iv_size(ctx));
+}
+
+int crypto4xx_decrypt(struct ablkcipher_request *req)
+{
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+
+ ctx->direction = DIR_INBOUND;
+ ctx->hash_final = 0;
+ ctx->is_hash = 0;
+ ctx->pd_ctl = 1;
+
+ return crypto4xx_build_pd(&req->base, ctx, req->src, req->dst,
+ req->nbytes, req->info,
+ get_dynamic_sa_iv_size(ctx));
+}
+
+/**
+ * AES Functions
+ */
+static int crypto4xx_setkey_aes(struct crypto_ablkcipher *cipher,
+ const u8 *key,
+ unsigned int keylen,
+ unsigned char cm,
+ u8 fb)
+{
+ struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct dynamic_sa_ctl *sa;
+ int rc;
+
+ if (keylen != AES_KEYSIZE_256 &&
+ keylen != AES_KEYSIZE_192 && keylen != AES_KEYSIZE_128) {
+ crypto_ablkcipher_set_flags(cipher,
+ CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+
+ /* Create SA */
+ if (ctx->sa_in_dma_addr || ctx->sa_out_dma_addr)
+ crypto4xx_free_sa(ctx);
+
+ rc = crypto4xx_alloc_sa(ctx, SA_AES128_LEN + (keylen-16) / 4);
+ if (rc)
+ return rc;
+
+ if (ctx->state_record_dma_addr == 0) {
+ rc = crypto4xx_alloc_state_record(ctx);
+ if (rc) {
+ crypto4xx_free_sa(ctx);
+ return rc;
+ }
+ }
+ /* Setup SA */
+ sa = (struct dynamic_sa_ctl *) ctx->sa_in;
+ ctx->hash_final = 0;
+
+ set_dynamic_sa_command_0(sa, SA_NOT_SAVE_HASH, SA_NOT_SAVE_IV,
+ SA_LOAD_HASH_FROM_SA, SA_LOAD_IV_FROM_STATE,
+ SA_NO_HEADER_PROC, SA_HASH_ALG_NULL,
+ SA_CIPHER_ALG_AES, SA_PAD_TYPE_ZERO,
+ SA_OP_GROUP_BASIC, SA_OPCODE_DECRYPT,
+ DIR_INBOUND);
+
+ set_dynamic_sa_command_1(sa, cm, SA_HASH_MODE_HASH,
+ fb, SA_EXTENDED_SN_OFF,
+ SA_SEQ_MASK_OFF, SA_MC_ENABLE,
+ SA_NOT_COPY_PAD, SA_NOT_COPY_PAYLOAD,
+ SA_NOT_COPY_HDR);
+ crypto4xx_memcpy_le(ctx->sa_in + get_dynamic_sa_offset_key_field(ctx),
+ key, keylen);
+ sa->sa_contents = SA_AES_CONTENTS | (keylen << 2);
+ sa->sa_command_1.bf.key_len = keylen >> 3;
+ ctx->is_hash = 0;
+ ctx->direction = DIR_INBOUND;
+ memcpy(ctx->sa_in + get_dynamic_sa_offset_state_ptr_field(ctx),
+ (void *)&ctx->state_record_dma_addr, 4);
+ ctx->offset_to_sr_ptr = get_dynamic_sa_offset_state_ptr_field(ctx);
+
+ memcpy(ctx->sa_out, ctx->sa_in, ctx->sa_len * 4);
+ sa = (struct dynamic_sa_ctl *) ctx->sa_out;
+ sa->sa_command_0.bf.dir = DIR_OUTBOUND;
+
+ return 0;
+}
+
+int crypto4xx_setkey_aes_cbc(struct crypto_ablkcipher *cipher,
+ const u8 *key, unsigned int keylen)
+{
+ return crypto4xx_setkey_aes(cipher, key, keylen, CRYPTO_MODE_CBC,
+ CRYPTO_FEEDBACK_MODE_NO_FB);
+}
+
+/**
+ * HASH SHA1 Functions
+ */
+static int crypto4xx_hash_alg_init(struct crypto_tfm *tfm,
+ unsigned int sa_len,
+ unsigned char ha,
+ unsigned char hm)
+{
+ struct crypto_alg *alg = tfm->__crt_alg;
+ struct crypto4xx_alg *my_alg = crypto_alg_to_crypto4xx_alg(alg);
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct dynamic_sa_ctl *sa;
+ struct dynamic_sa_hash160 *sa_in;
+ int rc;
+
+ ctx->dev = my_alg->dev;
+ ctx->is_hash = 1;
+ ctx->hash_final = 0;
+
+ /* Create SA */
+ if (ctx->sa_in_dma_addr || ctx->sa_out_dma_addr)
+ crypto4xx_free_sa(ctx);
+
+ rc = crypto4xx_alloc_sa(ctx, sa_len);
+ if (rc)
+ return rc;
+
+ if (ctx->state_record_dma_addr == 0) {
+ crypto4xx_alloc_state_record(ctx);
+ if (!ctx->state_record_dma_addr) {
+ crypto4xx_free_sa(ctx);
+ return -ENOMEM;
+ }
+ }
+
+ tfm->crt_ahash.reqsize = sizeof(struct crypto4xx_ctx);
+ sa = (struct dynamic_sa_ctl *) ctx->sa_in;
+ set_dynamic_sa_command_0(sa, SA_SAVE_HASH, SA_NOT_SAVE_IV,
+ SA_NOT_LOAD_HASH, SA_LOAD_IV_FROM_SA,
+ SA_NO_HEADER_PROC, ha, SA_CIPHER_ALG_NULL,
+ SA_PAD_TYPE_ZERO, SA_OP_GROUP_BASIC,
+ SA_OPCODE_HASH, DIR_INBOUND);
+ set_dynamic_sa_command_1(sa, 0, SA_HASH_MODE_HASH,
+ CRYPTO_FEEDBACK_MODE_NO_FB, SA_EXTENDED_SN_OFF,
+ SA_SEQ_MASK_OFF, SA_MC_ENABLE,
+ SA_NOT_COPY_PAD, SA_NOT_COPY_PAYLOAD,
+ SA_NOT_COPY_HDR);
+ ctx->direction = DIR_INBOUND;
+ sa->sa_contents = SA_HASH160_CONTENTS;
+ sa_in = (struct dynamic_sa_hash160 *) ctx->sa_in;
+ /* Need to zero hash digest in SA */
+ memset(sa_in->inner_digest, 0, sizeof(sa_in->inner_digest));
+ memset(sa_in->outer_digest, 0, sizeof(sa_in->outer_digest));
+ sa_in->state_ptr = ctx->state_record_dma_addr;
+ ctx->offset_to_sr_ptr = get_dynamic_sa_offset_state_ptr_field(ctx);
+
+ return 0;
+}
+
+int crypto4xx_hash_init(struct ahash_request *req)
+{
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ int ds;
+ struct dynamic_sa_ctl *sa;
+
+ sa = (struct dynamic_sa_ctl *) ctx->sa_in;
+ ds = crypto_ahash_digestsize(
+ __crypto_ahash_cast(req->base.tfm));
+ sa->sa_command_0.bf.digest_len = ds >> 2;
+ sa->sa_command_0.bf.load_hash_state = SA_LOAD_HASH_FROM_SA;
+ ctx->is_hash = 1;
+ ctx->direction = DIR_INBOUND;
+
+ return 0;
+}
+
+int crypto4xx_hash_update(struct ahash_request *req)
+{
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+
+ ctx->is_hash = 1;
+ ctx->hash_final = 0;
+ ctx->pd_ctl = 0x11;
+ ctx->direction = DIR_INBOUND;
+
+ return crypto4xx_build_pd(&req->base, ctx, req->src,
+ (struct scatterlist *) req->result,
+ req->nbytes, NULL, 0);
+}
+
+int crypto4xx_hash_final(struct ahash_request *req)
+{
+ return 0;
+}
+
+int crypto4xx_hash_digest(struct ahash_request *req)
+{
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+
+ ctx->hash_final = 1;
+ ctx->pd_ctl = 0x11;
+ ctx->direction = DIR_INBOUND;
+
+ return crypto4xx_build_pd(&req->base, ctx, req->src,
+ (struct scatterlist *) req->result,
+ req->nbytes, NULL, 0);
+}
+
+/**
+ * SHA1 Algorithm
+ */
+int crypto4xx_sha1_alg_init(struct crypto_tfm *tfm)
+{
+ return crypto4xx_hash_alg_init(tfm, SA_HASH160_LEN, SA_HASH_ALG_SHA1,
+ SA_HASH_MODE_HASH);
+}
+
+
diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c
new file mode 100644
index 0000000..4c0dfb2
--- /dev/null
+++ b/drivers/crypto/amcc/crypto4xx_core.c
@@ -0,0 +1,1310 @@
+/**
+ * AMCC SoC PPC4xx Crypto Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <jhsiao@amcc.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.
+ *
+ * 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.
+ *
+ * This file implements AMCC crypto offload Linux device driver for use with
+ * Linux CryptoAPI.
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock_types.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/of_platform.h>
+#include <asm/dcr.h>
+#include <asm/dcr-regs.h>
+#include <asm/cacheflush.h>
+#include <crypto/internal/hash.h>
+#include <crypto/algapi.h>
+#include <crypto/aes.h>
+#include <crypto/sha.h>
+#include "crypto4xx_reg_def.h"
+#include "crypto4xx_core.h"
+#include "crypto4xx_sa.h"
+
+#define PPC4XX_SEC_VERSION_STR "0.5"
+
+/**
+ * PPC4xx Crypto Engine Initialization Routine
+ */
+static void crypto4xx_hw_init(struct crypto4xx_device *dev)
+{
+ union ce_ring_size ring_size;
+ union ce_ring_contol ring_ctrl;
+ union ce_part_ring_size part_ring_size;
+ union ce_io_threshold io_threshold;
+ u32 rand_num;
+ union ce_pe_dma_cfg pe_dma_cfg;
+
+ writel(PPC4XX_BYTE_ORDER, dev->ce_base + CRYPTO4XX_BYTE_ORDER_CFG);
+ /* setup pe dma, include reset sg, pdr and pe, then release reset */
+ pe_dma_cfg.w = 0;
+ pe_dma_cfg.bf.bo_sgpd_en = 1;
+ pe_dma_cfg.bf.bo_data_en = 0;
+ pe_dma_cfg.bf.bo_sa_en = 1;
+ pe_dma_cfg.bf.bo_pd_en = 1;
+ pe_dma_cfg.bf.dynamic_sa_en = 1;
+ pe_dma_cfg.bf.reset_sg = 1;
+ pe_dma_cfg.bf.reset_pdr = 1;
+ pe_dma_cfg.bf.reset_pe = 1;
+ writel(pe_dma_cfg.w, dev->ce_base + CRYPTO4XX_PE_DMA_CFG);
+ /* un reset pe,sg and pdr */
+ pe_dma_cfg.bf.pe_mode = 0;
+ pe_dma_cfg.bf.reset_sg = 0;
+ pe_dma_cfg.bf.reset_pdr = 0;
+ pe_dma_cfg.bf.reset_pe = 0;
+ pe_dma_cfg.bf.bo_td_en = 0;
+ writel(pe_dma_cfg.w, dev->ce_base + CRYPTO4XX_PE_DMA_CFG);
+ writel(dev->pdr_pa, dev->ce_base + CRYPTO4XX_PDR_BASE);
+ writel(dev->pdr_pa, dev->ce_base + CRYPTO4XX_RDR_BASE);
+ writel(PPC4XX_PRNG_CTRL_AUTO_EN, dev->ce_base + CRYPTO4XX_PRNG_CTRL);
+ get_random_bytes(&rand_num, sizeof(rand_num));
+ writel(rand_num, dev->ce_base + CRYPTO4XX_PRNG_SEED_L);
+ get_random_bytes(&rand_num, sizeof(rand_num));
+ writel(rand_num, dev->ce_base + CRYPTO4XX_PRNG_SEED_H);
+ ring_size.w = 0;
+ ring_size.bf.ring_offset = PPC4XX_PD_SIZE;
+ ring_size.bf.ring_size = PPC4XX_NUM_PD;
+ writel(ring_size.w, dev->ce_base + CRYPTO4XX_RING_SIZE);
+ ring_ctrl.w = 0;
+ writel(ring_ctrl.w, dev->ce_base + CRYPTO4XX_RING_CTRL);
+ writel(PPC4XX_DC_3DES_EN, dev->ce_base + CRYPTO4XX_DEVICE_CTRL);
+ writel(dev->gdr_pa, dev->ce_base + CRYPTO4XX_GATH_RING_BASE);
+ writel(dev->sdr_pa, dev->ce_base + CRYPTO4XX_SCAT_RING_BASE);
+ part_ring_size.w = 0;
+ part_ring_size.bf.sdr_size = PPC4XX_SDR_SIZE;
+ part_ring_size.bf.gdr_size = PPC4XX_GDR_SIZE;
+ writel(part_ring_size.w, dev->ce_base + CRYPTO4XX_PART_RING_SIZE);
+ writel(PPC4XX_SD_BUFFER_SIZE, dev->ce_base + CRYPTO4XX_PART_RING_CFG);
+ io_threshold.w = 0;
+ io_threshold.bf.output_threshold = PPC4XX_OUTPUT_THRESHOLD;
+ io_threshold.bf.input_threshold = PPC4XX_INPUT_THRESHOLD;
+ writel(io_threshold.w, dev->ce_base + CRYPTO4XX_IO_THRESHOLD);
+ writel(0, dev->ce_base + CRYPTO4XX_PDR_BASE_UADDR);
+ writel(0, dev->ce_base + CRYPTO4XX_RDR_BASE_UADDR);
+ writel(0, dev->ce_base + CRYPTO4XX_PKT_SRC_UADDR);
+ writel(0, dev->ce_base + CRYPTO4XX_PKT_DEST_UADDR);
+ writel(0, dev->ce_base + CRYPTO4XX_SA_UADDR);
+ writel(0, dev->ce_base + CRYPTO4XX_GATH_RING_BASE_UADDR);
+ writel(0, dev->ce_base + CRYPTO4XX_SCAT_RING_BASE_UADDR);
+ /* un reset pe,sg and pdr */
+ pe_dma_cfg.bf.pe_mode = 1;
+ pe_dma_cfg.bf.reset_sg = 0;
+ pe_dma_cfg.bf.reset_pdr = 0;
+ pe_dma_cfg.bf.reset_pe = 0;
+ pe_dma_cfg.bf.bo_td_en = 0;
+ writel(pe_dma_cfg.w, dev->ce_base + CRYPTO4XX_PE_DMA_CFG);
+ /*clear all pending interrupt*/
+ writel(PPC4XX_INTERRUPT_CLR, dev->ce_base + CRYPTO4XX_INT_CLR);
+ writel(PPC4XX_INT_DESCR_CNT, dev->ce_base + CRYPTO4XX_INT_DESCR_CNT);
+ writel(PPC4XX_INT_DESCR_CNT, dev->ce_base + CRYPTO4XX_INT_DESCR_CNT);
+ writel(PPC4XX_INT_CFG, dev->ce_base + CRYPTO4XX_INT_CFG);
+ writel(PPC4XX_PD_DONE_INT, dev->ce_base + CRYPTO4XX_INT_EN);
+}
+
+int crypto4xx_alloc_sa(struct crypto4xx_ctx *ctx, u32 size)
+{
+ ctx->sa_in = dma_alloc_coherent(ctx->dev->core_dev->device, size * 4,
+ &ctx->sa_in_dma_addr, GFP_ATOMIC);
+ if (ctx->sa_in == NULL)
+ return -ENOMEM;
+
+ ctx->sa_out = dma_alloc_coherent(ctx->dev->core_dev->device, size * 4,
+ &ctx->sa_out_dma_addr, GFP_ATOMIC);
+ if (ctx->sa_out == NULL) {
+ dma_free_coherent(ctx->dev->core_dev->device,
+ ctx->sa_len * 4,
+ ctx->sa_in, ctx->sa_in_dma_addr);
+ return -ENOMEM;
+ }
+
+ memset(ctx->sa_in, 0, size * 4);
+ memset(ctx->sa_out, 0, size * 4);
+ ctx->sa_len = size;
+
+ return 0;
+}
+
+void crypto4xx_free_sa(struct crypto4xx_ctx *ctx)
+{
+ if (ctx->sa_in != NULL)
+ dma_free_coherent(ctx->dev->core_dev->device, ctx->sa_len * 4,
+ ctx->sa_in, ctx->sa_in_dma_addr);
+ if (ctx->sa_out != NULL)
+ dma_free_coherent(ctx->dev->core_dev->device, ctx->sa_len * 4,
+ ctx->sa_out, ctx->sa_out_dma_addr);
+
+ ctx->sa_in_dma_addr = 0;
+ ctx->sa_out_dma_addr = 0;
+ ctx->sa_len = 0;
+}
+
+u32 crypto4xx_alloc_state_record(struct crypto4xx_ctx *ctx)
+{
+ ctx->state_record = dma_alloc_coherent(ctx->dev->core_dev->device,
+ sizeof(struct sa_state_record),
+ &ctx->state_record_dma_addr, GFP_ATOMIC);
+ if (!ctx->state_record_dma_addr)
+ return -ENOMEM;
+ memset(ctx->state_record, 0, sizeof(struct sa_state_record));
+
+ return 0;
+}
+
+void crypto4xx_free_state_record(struct crypto4xx_ctx *ctx)
+{
+ if (ctx->state_record != NULL)
+ dma_free_coherent(ctx->dev->core_dev->device,
+ sizeof(struct sa_state_record),
+ ctx->state_record,
+ ctx->state_record_dma_addr);
+ ctx->state_record_dma_addr = 0;
+}
+
+/**
+ * alloc memory for the gather ring
+ * no need to alloc buf for the ring
+ * gdr_tail, gdr_head and gdr_count are initialized by this function
+ */
+static u32 crypto4xx_build_pdr(struct crypto4xx_device *dev)
+{
+ int i;
+ struct pd_uinfo *pd_uinfo;
+ dev->pdr = dma_alloc_coherent(dev->core_dev->device,
+ sizeof(struct ce_pd) * PPC4XX_NUM_PD,
+ &dev->pdr_pa, GFP_ATOMIC);
+ if (!dev->pdr)
+ return -ENOMEM;
+
+ dev->pdr_uinfo = kzalloc(sizeof(struct pd_uinfo) * PPC4XX_NUM_PD,
+ GFP_KERNEL);
+ if (!dev->pdr_uinfo) {
+ dma_free_coherent(dev->core_dev->device,
+ sizeof(struct ce_pd) * PPC4XX_NUM_PD,
+ dev->pdr,
+ dev->pdr_pa);
+ return -ENOMEM;
+ }
+ memset(dev->pdr, 0, sizeof(struct ce_pd) * PPC4XX_NUM_PD);
+ dev->shadow_sa_pool = dma_alloc_coherent(dev->core_dev->device,
+ 256 * PPC4XX_NUM_PD,
+ &dev->shadow_sa_pool_pa,
+ GFP_ATOMIC);
+ if (!dev->shadow_sa_pool)
+ return -ENOMEM;
+
+ dev->shadow_sr_pool = dma_alloc_coherent(dev->core_dev->device,
+ sizeof(struct sa_state_record) * PPC4XX_NUM_PD,
+ &dev->shadow_sr_pool_pa, GFP_ATOMIC);
+ if (!dev->shadow_sr_pool)
+ return -ENOMEM;
+ for (i = 0; i < PPC4XX_NUM_PD; i++) {
+ pd_uinfo = (struct pd_uinfo *) (dev->pdr_uinfo +
+ sizeof(struct pd_uinfo) * i);
+
+ /* alloc 256 bytes which is enough for any kind of dynamic sa */
+ pd_uinfo->sa_va = dev->shadow_sa_pool + 256 * i;
+ pd_uinfo->sa_pa = dev->shadow_sa_pool_pa + 256 * i;
+
+ /* alloc state record */
+ pd_uinfo->sr_va = dev->shadow_sr_pool +
+ sizeof(struct sa_state_record) * i;
+ pd_uinfo->sr_pa = dev->shadow_sr_pool_pa +
+ sizeof(struct sa_state_record) * i;
+ }
+
+ return 0;
+}
+
+static void crypto4xx_destroy_pdr(struct crypto4xx_device *dev)
+{
+ if (dev->pdr != NULL)
+ dma_free_coherent(dev->core_dev->device,
+ sizeof(struct ce_pd) * PPC4XX_NUM_PD,
+ dev->pdr, dev->pdr_pa);
+ if (dev->shadow_sa_pool)
+ dma_free_coherent(dev->core_dev->device, 256 * PPC4XX_NUM_PD,
+ dev->shadow_sa_pool, dev->shadow_sa_pool_pa);
+ if (dev->shadow_sr_pool)
+ dma_free_coherent(dev->core_dev->device,
+ sizeof(struct sa_state_record) * PPC4XX_NUM_PD,
+ dev->shadow_sr_pool, dev->shadow_sr_pool_pa);
+
+ kfree(dev->pdr_uinfo);
+}
+
+static u32 crypto4xx_get_pd_from_pdr_nolock(struct crypto4xx_device *dev)
+{
+ u32 retval;
+ u32 tmp;
+
+ retval = dev->pdr_head;
+ tmp = (dev->pdr_head + 1) % PPC4XX_NUM_PD;
+
+ if (tmp == dev->pdr_tail)
+ return ERING_WAS_FULL;
+
+ dev->pdr_head = tmp;
+
+ return retval;
+}
+
+static u32 crypto4xx_put_pd_to_pdr(struct crypto4xx_device *dev, u32 idx)
+{
+ struct pd_uinfo *pd_uinfo;
+ unsigned long flags;
+
+ pd_uinfo = (struct pd_uinfo *)(dev->pdr_uinfo +
+ sizeof(struct pd_uinfo) * idx);
+ spin_lock_irqsave(&dev->core_dev->lock, flags);
+ if (dev->pdr_tail != PPC4XX_LAST_PD)
+ dev->pdr_tail++;
+ else
+ dev->pdr_tail = 0;
+ pd_uinfo->state = PD_ENTRY_FREE;
+ spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+
+ return 0;
+}
+
+static struct ce_pd *crypto4xx_get_pdp(struct crypto4xx_device *dev,
+ dma_addr_t *pd_dma, u32 idx)
+{
+ *pd_dma = dev->pdr_pa + sizeof(struct ce_pd) * idx;
+
+ return dev->pdr + sizeof(struct ce_pd) * idx;
+}
+
+/**
+ * alloc memory for the gather ring
+ * no need to alloc buf for the ring
+ * gdr_tail, gdr_head and gdr_count are initialized by this function
+ */
+static u32 crypto4xx_build_gdr(struct crypto4xx_device *dev)
+{
+ dev->gdr = dma_alloc_coherent(dev->core_dev->device,
+ sizeof(struct ce_gd) * PPC4XX_NUM_GD,
+ &dev->gdr_pa, GFP_ATOMIC);
+ if (!dev->gdr)
+ return -ENOMEM;
+
+ memset(dev->gdr, 0, sizeof(struct ce_gd) * PPC4XX_NUM_GD);
+
+ return 0;
+}
+
+static inline void crypto4xx_destroy_gdr(struct crypto4xx_device *dev)
+{
+ dma_free_coherent(dev->core_dev->device,
+ sizeof(struct ce_gd) * PPC4XX_NUM_GD,
+ dev->gdr, dev->gdr_pa);
+}
+
+/*
+ * when this function is called.
+ * preemption or interrupt must be disabled
+ */
+u32 crypto4xx_get_n_gd(struct crypto4xx_device *dev, int n)
+{
+ u32 retval;
+ u32 tmp;
+ if (n >= PPC4XX_NUM_GD)
+ return ERING_WAS_FULL;
+
+ retval = dev->gdr_head;
+ tmp = (dev->gdr_head + n) % PPC4XX_NUM_GD;
+ if (dev->gdr_head > dev->gdr_tail) {
+ if (tmp < dev->gdr_head && tmp >= dev->gdr_tail)
+ return ERING_WAS_FULL;
+ } else if (dev->gdr_head < dev->gdr_tail) {
+ if (tmp < dev->gdr_head || tmp >= dev->gdr_tail)
+ return ERING_WAS_FULL;
+ }
+ dev->gdr_head = tmp;
+
+ return retval;
+}
+
+static u32 crypto4xx_put_gd_to_gdr(struct crypto4xx_device *dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->core_dev->lock, flags);
+ if (dev->gdr_tail == dev->gdr_head) {
+ spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+ return 0;
+ }
+
+ if (dev->gdr_tail != PPC4XX_LAST_GD)
+ dev->gdr_tail++;
+ else
+ dev->gdr_tail = 0;
+
+ spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+
+ return 0;
+}
+
+static inline struct ce_gd *crypto4xx_get_gdp(struct crypto4xx_device *dev,
+ dma_addr_t *gd_dma, u32 idx)
+{
+ *gd_dma = dev->gdr_pa + sizeof(struct ce_gd) * idx;
+
+ return (struct ce_gd *) (dev->gdr + sizeof(struct ce_gd) * idx);
+}
+
+/**
+ * alloc memory for the scatter ring
+ * need to alloc buf for the ring
+ * sdr_tail, sdr_head and sdr_count are initialized by this function
+ */
+static u32 crypto4xx_build_sdr(struct crypto4xx_device *dev)
+{
+ int i;
+ struct ce_sd *sd_array;
+
+ /* alloc memory for scatter descriptor ring */
+ dev->sdr = dma_alloc_coherent(dev->core_dev->device,
+ sizeof(struct ce_sd) * PPC4XX_NUM_SD,
+ &dev->sdr_pa, GFP_ATOMIC);
+ if (!dev->sdr)
+ return -ENOMEM;
+
+ dev->scatter_buffer_size = PPC4XX_SD_BUFFER_SIZE;
+ dev->scatter_buffer_va =
+ dma_alloc_coherent(dev->core_dev->device,
+ dev->scatter_buffer_size * PPC4XX_NUM_SD,
+ &dev->scatter_buffer_pa, GFP_ATOMIC);
+ if (!dev->scatter_buffer_va) {
+ dma_free_coherent(dev->core_dev->device,
+ sizeof(struct ce_sd) * PPC4XX_NUM_SD,
+ dev->sdr, dev->sdr_pa);
+ return -ENOMEM;
+ }
+
+ sd_array = dev->sdr;
+
+ for (i = 0; i < PPC4XX_NUM_SD; i++) {
+ sd_array[i].ptr = dev->scatter_buffer_pa +
+ dev->scatter_buffer_size * i;
+ }
+
+ return 0;
+}
+
+static void crypto4xx_destroy_sdr(struct crypto4xx_device *dev)
+{
+ if (dev->sdr != NULL)
+ dma_free_coherent(dev->core_dev->device,
+ sizeof(struct ce_sd) * PPC4XX_NUM_SD,
+ dev->sdr, dev->sdr_pa);
+
+ if (dev->scatter_buffer_va != NULL)
+ dma_free_coherent(dev->core_dev->device,
+ dev->scatter_buffer_size * PPC4XX_NUM_SD,
+ dev->scatter_buffer_va,
+ dev->scatter_buffer_pa);
+}
+
+/*
+ * when this function is called.
+ * preemption or interrupt must be disabled
+ */
+static u32 crypto4xx_get_n_sd(struct crypto4xx_device *dev, int n)
+{
+ u32 retval;
+ u32 tmp;
+
+ if (n >= PPC4XX_NUM_SD)
+ return ERING_WAS_FULL;
+
+ retval = dev->sdr_head;
+ tmp = (dev->sdr_head + n) % PPC4XX_NUM_SD;
+ if (dev->sdr_head > dev->gdr_tail) {
+ if (tmp < dev->sdr_head && tmp >= dev->sdr_tail)
+ return ERING_WAS_FULL;
+ } else if (dev->sdr_head < dev->sdr_tail) {
+ if (tmp < dev->sdr_head || tmp >= dev->sdr_tail)
+ return ERING_WAS_FULL;
+ } /* the head = tail, or empty case is already take cared */
+ dev->sdr_head = tmp;
+
+ return retval;
+}
+
+static u32 crypto4xx_put_sd_to_sdr(struct crypto4xx_device *dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->core_dev->lock, flags);
+ if (dev->sdr_tail == dev->sdr_head) {
+ spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+ return 0;
+ }
+ if (dev->sdr_tail != PPC4XX_LAST_SD)
+ dev->sdr_tail++;
+ else
+ dev->sdr_tail = 0;
+ spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+
+ return 0;
+}
+
+static inline struct ce_sd *crypto4xx_get_sdp(struct crypto4xx_device *dev,
+ dma_addr_t *sd_dma, u32 idx)
+{
+ *sd_dma = dev->sdr_pa + sizeof(struct ce_sd) * idx;
+
+ return (struct ce_sd *)(dev->sdr + sizeof(struct ce_sd) * idx);
+}
+
+static u32 crypto4xx_fill_one_page(struct crypto4xx_device *dev,
+ dma_addr_t *addr, u32 *length,
+ u32 *idx, u32 *offset, u32 *nbytes)
+{
+ u32 len;
+
+ if (*length > dev->scatter_buffer_size) {
+ memcpy(phys_to_virt(*addr),
+ dev->scatter_buffer_va +
+ *idx * dev->scatter_buffer_size + *offset,
+ dev->scatter_buffer_size);
+ *offset = 0;
+ *length -= dev->scatter_buffer_size;
+ *nbytes -= dev->scatter_buffer_size;
+ if (*idx == PPC4XX_LAST_SD)
+ *idx = 0;
+ else
+ (*idx)++;
+ *addr = *addr + dev->scatter_buffer_size;
+ return 1;
+ } else if (*length < dev->scatter_buffer_size) {
+ memcpy(phys_to_virt(*addr),
+ dev->scatter_buffer_va +
+ *idx * dev->scatter_buffer_size + *offset, *length);
+ if ((*offset + *length) == dev->scatter_buffer_size) {
+ if (*idx == PPC4XX_LAST_SD)
+ *idx = 0;
+ else
+ (*idx)++;
+ *nbytes -= *length;
+ *offset = 0;
+ } else {
+ *nbytes -= *length;
+ *offset += *length;
+ }
+
+ return 0;
+ } else {
+ len = (*nbytes <= dev->scatter_buffer_size) ?
+ (*nbytes) : dev->scatter_buffer_size;
+ memcpy(phys_to_virt(*addr),
+ dev->scatter_buffer_va +
+ *idx * dev->scatter_buffer_size + *offset,
+ len);
+ *offset = 0;
+ *nbytes -= len;
+
+ if (*idx == PPC4XX_LAST_SD)
+ *idx = 0;
+ else
+ (*idx)++;
+
+ return 0;
+ }
+}
+
+static void crypto4xx_copy_pkt_to_dst(struct crypto4xx_device *dev,
+ struct ce_pd *pd,
+ struct pd_uinfo *pd_uinfo,
+ u32 nbytes,
+ struct scatterlist *dst)
+{
+ dma_addr_t addr;
+ u32 this_sd;
+ u32 offset;
+ u32 len;
+ u32 i;
+ u32 sg_len;
+ struct scatterlist *sg;
+
+ this_sd = pd_uinfo->first_sd;
+ offset = 0;
+ i = 0;
+
+ while (nbytes) {
+ sg = &dst[i];
+ sg_len = sg->length;
+ addr = dma_map_page(dev->core_dev->device, sg_page(sg),
+ sg->offset, sg->length, DMA_TO_DEVICE);
+
+ if (offset == 0) {
+ len = (nbytes <= sg->length) ? nbytes : sg->length;
+ while (crypto4xx_fill_one_page(dev, &addr, &len,
+ &this_sd, &offset, &nbytes))
+ ;
+ if (!nbytes)
+ return;
+ i++;
+ } else {
+ len = (nbytes <= (dev->scatter_buffer_size - offset)) ?
+ nbytes : (dev->scatter_buffer_size - offset);
+ len = (sg->length < len) ? sg->length : len;
+ while (crypto4xx_fill_one_page(dev, &addr, &len,
+ &this_sd, &offset, &nbytes))
+ ;
+ if (!nbytes)
+ return;
+ sg_len -= len;
+ if (sg_len) {
+ addr += len;
+ while (crypto4xx_fill_one_page(dev, &addr,
+ &sg_len, &this_sd, &offset, &nbytes))
+ ;
+ }
+ i++;
+ }
+ }
+}
+
+static u32 crypto4xx_copy_digest_to_dst(struct pd_uinfo *pd_uinfo,
+ struct crypto4xx_ctx *ctx)
+{
+ struct dynamic_sa_ctl *sa = (struct dynamic_sa_ctl *) ctx->sa_in;
+ struct sa_state_record *state_record =
+ (struct sa_state_record *) pd_uinfo->sr_va;
+
+ if (sa->sa_command_0.bf.hash_alg == SA_HASH_ALG_SHA1) {
+ memcpy((void *) pd_uinfo->dest_va, state_record->save_digest,
+ SA_HASH_ALG_SHA1_DIGEST_SIZE);
+ }
+
+ return 0;
+}
+
+static void crypto4xx_ret_sg_desc(struct crypto4xx_device *dev,
+ struct pd_uinfo *pd_uinfo)
+{
+ int i;
+ if (pd_uinfo->num_gd) {
+ for (i = 0; i < pd_uinfo->num_gd; i++)
+ crypto4xx_put_gd_to_gdr(dev);
+ pd_uinfo->first_gd = 0xffffffff;
+ pd_uinfo->num_gd = 0;
+ }
+ if (pd_uinfo->num_sd) {
+ for (i = 0; i < pd_uinfo->num_sd; i++)
+ crypto4xx_put_sd_to_sdr(dev);
+
+ pd_uinfo->first_sd = 0xffffffff;
+ pd_uinfo->num_sd = 0;
+ }
+}
+
+static u32 crypto4xx_ablkcipher_done(struct crypto4xx_device *dev,
+ struct pd_uinfo *pd_uinfo,
+ struct ce_pd *pd)
+{
+ struct crypto4xx_ctx *ctx;
+ struct ablkcipher_request *ablk_req;
+ struct scatterlist *dst;
+ dma_addr_t addr;
+
+ ablk_req = ablkcipher_request_cast(pd_uinfo->async_req);
+ ctx = crypto_tfm_ctx(ablk_req->base.tfm);
+
+ if (pd_uinfo->using_sd) {
+ crypto4xx_copy_pkt_to_dst(dev, pd, pd_uinfo, ablk_req->nbytes,
+ ablk_req->dst);
+ } else {
+ dst = pd_uinfo->dest_va;
+ addr = dma_map_page(dev->core_dev->device, sg_page(dst),
+ dst->offset, dst->length, DMA_FROM_DEVICE);
+ }
+ crypto4xx_ret_sg_desc(dev, pd_uinfo);
+ if (ablk_req->base.complete != NULL)
+ ablk_req->base.complete(&ablk_req->base, 0);
+
+ return 0;
+}
+
+static u32 crypto4xx_ahash_done(struct crypto4xx_device *dev,
+ struct pd_uinfo *pd_uinfo)
+{
+ struct crypto4xx_ctx *ctx;
+ struct ahash_request *ahash_req;
+
+ ahash_req = ahash_request_cast(pd_uinfo->async_req);
+ ctx = crypto_tfm_ctx(ahash_req->base.tfm);
+
+ crypto4xx_copy_digest_to_dst(pd_uinfo,
+ crypto_tfm_ctx(ahash_req->base.tfm));
+ crypto4xx_ret_sg_desc(dev, pd_uinfo);
+ /* call user provided callback function x */
+ if (ahash_req->base.complete != NULL)
+ ahash_req->base.complete(&ahash_req->base, 0);
+
+ return 0;
+}
+
+static u32 crypto4xx_pd_done(struct crypto4xx_device *dev, u32 idx)
+{
+ struct ce_pd *pd;
+ struct pd_uinfo *pd_uinfo;
+
+ pd = dev->pdr + sizeof(struct ce_pd)*idx;
+ pd_uinfo = dev->pdr_uinfo + sizeof(struct pd_uinfo)*idx;
+ if (crypto_tfm_alg_type(pd_uinfo->async_req->tfm) ==
+ CRYPTO_ALG_TYPE_ABLKCIPHER)
+ return crypto4xx_ablkcipher_done(dev, pd_uinfo, pd);
+ else
+ return crypto4xx_ahash_done(dev, pd_uinfo);
+}
+
+/**
+ * Note: Only use this function to copy items that is word aligned.
+ */
+void crypto4xx_memcpy_le(unsigned int *dst,
+ const unsigned char *buf,
+ int len)
+{
+ u8 *tmp;
+ for (; len >= 4; buf += 4, len -= 4)
+ *dst++ = cpu_to_le32(*(unsigned int *) buf);
+
+ tmp = (u8 *)dst;
+ switch (len) {
+ case 3:
+ *tmp++ = 0;
+ *tmp++ = *(buf+2);
+ *tmp++ = *(buf+1);
+ *tmp++ = *buf;
+ break;
+ case 2:
+ *tmp++ = 0;
+ *tmp++ = 0;
+ *tmp++ = *(buf+1);
+ *tmp++ = *buf;
+ break;
+ case 1:
+ *tmp++ = 0;
+ *tmp++ = 0;
+ *tmp++ = 0;
+ *tmp++ = *buf;
+ break;
+ default:
+ break;
+ }
+}
+
+static void crypto4xx_stop_all(struct crypto4xx_core_device *core_dev)
+{
+ crypto4xx_destroy_pdr(core_dev->dev);
+ crypto4xx_destroy_gdr(core_dev->dev);
+ crypto4xx_destroy_sdr(core_dev->dev);
+ dev_set_drvdata(core_dev->device, NULL);
+ iounmap(core_dev->dev->ce_base);
+ kfree(core_dev->dev);
+ kfree(core_dev);
+}
+
+void crypto4xx_return_pd(struct crypto4xx_device *dev,
+ u32 pd_entry, struct ce_pd *pd,
+ struct pd_uinfo *pd_uinfo)
+{
+ /* irq should be already disabled */
+ dev->pdr_head = pd_entry;
+ pd->pd_ctl.w = 0;
+ pd->pd_ctl_len.w = 0;
+ pd_uinfo->state = PD_ENTRY_FREE;
+}
+
+/*
+ * derive number of elements in scatterlist
+ * Shamlessly copy from talitos.c
+ */
+static int get_sg_count(struct scatterlist *sg_list, int nbytes)
+{
+ struct scatterlist *sg = sg_list;
+ int sg_nents = 0;
+
+ while (nbytes) {
+ sg_nents++;
+ if (sg->length > nbytes)
+ break;
+ nbytes -= sg->length;
+ sg = sg_next(sg);
+ }
+
+ return sg_nents;
+}
+
+static u32 get_next_gd(u32 current)
+{
+ if (current != PPC4XX_LAST_GD)
+ return current + 1;
+ else
+ return 0;
+}
+
+static u32 get_next_sd(u32 current)
+{
+ if (current != PPC4XX_LAST_SD)
+ return current + 1;
+ else
+ return 0;
+}
+
+u32 crypto4xx_build_pd(struct crypto_async_request *req,
+ struct crypto4xx_ctx *ctx,
+ struct scatterlist *src,
+ struct scatterlist *dst,
+ unsigned int datalen,
+ void *iv, u32 iv_len)
+{
+ struct crypto4xx_device *dev = ctx->dev;
+ dma_addr_t addr, pd_dma, sd_dma, gd_dma;
+ struct dynamic_sa_ctl *sa;
+ struct scatterlist *sg;
+ struct ce_gd *gd;
+ struct ce_pd *pd;
+ u32 num_gd, num_sd;
+ u32 fst_gd = 0xffffffff;
+ u32 fst_sd = 0xffffffff;
+ u32 pd_entry;
+ unsigned long flags;
+ struct pd_uinfo *pd_uinfo = NULL;
+ unsigned int nbytes = datalen, idx;
+ unsigned int ivlen = 0;
+ u32 gd_idx = 0;
+
+ /* figure how many gd is needed */
+ num_gd = get_sg_count(src, datalen);
+ if (num_gd == 1)
+ num_gd = 0;
+
+ /* figure how many sd is needed */
+ if (sg_is_last(dst) || ctx->is_hash) {
+ num_sd = 0;
+ } else {
+ if (datalen > PPC4XX_SD_BUFFER_SIZE) {
+ num_sd = datalen / PPC4XX_SD_BUFFER_SIZE;
+ if (datalen % PPC4XX_SD_BUFFER_SIZE)
+ num_sd++;
+ } else {
+ num_sd = 1;
+ }
+ }
+
+ /*
+ * The follow section of code needs to be protected
+ * The gather ring and scatter ring needs to be consecutive
+ * In case of run out of any kind of descriptor, the descriptor
+ * already got must be return the original place.
+ */
+ spin_lock_irqsave(&dev->core_dev->lock, flags);
+ if (num_gd) {
+ fst_gd = crypto4xx_get_n_gd(dev, num_gd);
+ if (fst_gd == ERING_WAS_FULL) {
+ spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+ return -EAGAIN;
+ }
+ }
+ if (num_sd) {
+ fst_sd = crypto4xx_get_n_sd(dev, num_sd);
+ if (fst_sd == ERING_WAS_FULL) {
+ if (num_gd)
+ dev->gdr_head = fst_gd;
+ spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+ return -EAGAIN;
+ }
+ }
+ pd_entry = crypto4xx_get_pd_from_pdr_nolock(dev);
+ if (pd_entry == ERING_WAS_FULL) {
+ if (num_gd)
+ dev->gdr_head = fst_gd;
+ if (num_sd)
+ dev->sdr_head = fst_sd;
+ spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+ return -EAGAIN;
+ }
+ spin_unlock_irqrestore(&dev->core_dev->lock, flags);
+
+ pd_uinfo = (struct pd_uinfo *)(dev->pdr_uinfo +
+ sizeof(struct pd_uinfo) * pd_entry);
+ pd = crypto4xx_get_pdp(dev, &pd_dma, pd_entry);
+ pd_uinfo->async_req = req;
+ pd_uinfo->num_gd = num_gd;
+ pd_uinfo->num_sd = num_sd;
+
+ if (iv_len || ctx->is_hash) {
+ ivlen = iv_len;
+ pd->sa = pd_uinfo->sa_pa;
+ sa = (struct dynamic_sa_ctl *) pd_uinfo->sa_va;
+ if (ctx->direction == DIR_INBOUND)
+ memcpy(sa, ctx->sa_in, ctx->sa_len * 4);
+ else
+ memcpy(sa, ctx->sa_out, ctx->sa_len * 4);
+
+ memcpy((void *) sa + ctx->offset_to_sr_ptr,
+ &pd_uinfo->sr_pa, 4);
+
+ if (iv_len)
+ crypto4xx_memcpy_le(pd_uinfo->sr_va, iv, iv_len);
+ } else {
+ if (ctx->direction == DIR_INBOUND) {
+ pd->sa = ctx->sa_in_dma_addr;
+ sa = (struct dynamic_sa_ctl *) ctx->sa_in;
+ } else {
+ pd->sa = ctx->sa_out_dma_addr;
+ sa = (struct dynamic_sa_ctl *) ctx->sa_out;
+ }
+ }
+ pd->sa_len = ctx->sa_len;
+ if (num_gd) {
+ /* get first gd we are going to use */
+ gd_idx = fst_gd;
+ pd_uinfo->first_gd = fst_gd;
+ pd_uinfo->num_gd = num_gd;
+ gd = crypto4xx_get_gdp(dev, &gd_dma, gd_idx);
+ pd->src = gd_dma;
+ /* enable gather */
+ sa->sa_command_0.bf.gather = 1;
+ idx = 0;
+ src = &src[0];
+ /* walk the sg, and setup gather array */
+ while (nbytes) {
+ sg = &src[idx];
+ addr = dma_map_page(dev->core_dev->device, sg_page(sg),
+ sg->offset, sg->length, DMA_TO_DEVICE);
+ gd->ptr = addr;
+ gd->ctl_len.len = sg->length;
+ gd->ctl_len.done = 0;
+ gd->ctl_len.ready = 1;
+ if (sg->length >= nbytes)
+ break;
+ nbytes -= sg->length;
+ gd_idx = get_next_gd(gd_idx);
+ gd = crypto4xx_get_gdp(dev, &gd_dma, gd_idx);
+ idx++;
+ }
+ } else {
+ pd->src = (u32)dma_map_page(dev->core_dev->device, sg_page(src),
+ src->offset, src->length, DMA_TO_DEVICE);
+ /*
+ * Disable gather in sa command
+ */
+ sa->sa_command_0.bf.gather = 0;
+ /*
+ * Indicate gather array is not used
+ */
+ pd_uinfo->first_gd = 0xffffffff;
+ pd_uinfo->num_gd = 0;
+ }
+ if (ctx->is_hash || sg_is_last(dst)) {
+ /*
+ * we know application give us dst a whole piece of memory
+ * no need to use scatter ring.
+ * In case of is_hash, the icv is always at end of src data.
+ */
+ pd_uinfo->using_sd = 0;
+ pd_uinfo->first_sd = 0xffffffff;
+ pd_uinfo->num_sd = 0;
+ pd_uinfo->dest_va = dst;
+ sa->sa_command_0.bf.scatter = 0;
+ if (ctx->is_hash)
+ pd->dest = virt_to_phys((void *)dst);
+ else
+ pd->dest = (u32)dma_map_page(dev->core_dev->device,
+ sg_page(dst), dst->offset,
+ dst->length, DMA_TO_DEVICE);
+ } else {
+ struct ce_sd *sd = NULL;
+ u32 sd_idx = fst_sd;
+ nbytes = datalen;
+ sa->sa_command_0.bf.scatter = 1;
+ pd_uinfo->using_sd = 1;
+ pd_uinfo->dest_va = dst;
+ pd_uinfo->first_sd = fst_sd;
+ pd_uinfo->num_sd = num_sd;
+ sd = crypto4xx_get_sdp(dev, &sd_dma, sd_idx);
+ pd->dest = sd_dma;
+ /* setup scatter descriptor */
+ sd->ctl.done = 0;
+ sd->ctl.rdy = 1;
+ /* sd->ptr should be setup by sd_init routine*/
+ idx = 0;
+ if (nbytes >= PPC4XX_SD_BUFFER_SIZE)
+ nbytes -= PPC4XX_SD_BUFFER_SIZE;
+ else
+ nbytes = 0;
+ while (nbytes) {
+ sd_idx = get_next_sd(sd_idx);
+ sd = crypto4xx_get_sdp(dev, &sd_dma, sd_idx);
+ /* setup scatter descriptor */
+ sd->ctl.done = 0;
+ sd->ctl.rdy = 1;
+ if (nbytes >= PPC4XX_SD_BUFFER_SIZE)
+ nbytes -= PPC4XX_SD_BUFFER_SIZE;
+ else
+ /*
+ * SD entry can hold PPC4XX_SD_BUFFER_SIZE,
+ * which is more than nbytes, so done.
+ */
+ nbytes = 0;
+ }
+ }
+
+ sa->sa_command_1.bf.hash_crypto_offset = 0;
+ pd->pd_ctl.w = ctx->pd_ctl;
+ pd->pd_ctl_len.w = 0x00400000 | (ctx->bypass << 24) | datalen;
+ pd_uinfo->state = PD_ENTRY_INUSE;
+ wmb();
+ /* write any value to push engine to read a pd */
+ writel(1, dev->ce_base + CRYPTO4XX_INT_DESCR_RD);
+ return -EINPROGRESS;
+}
+
+/**
+ * Algorithm Registration Functions
+ */
+static int crypto4xx_alg_init(struct crypto_tfm *tfm)
+{
+ struct crypto_alg *alg = tfm->__crt_alg;
+ struct crypto4xx_alg *amcc_alg = crypto_alg_to_crypto4xx_alg(alg);
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ ctx->dev = amcc_alg->dev;
+ ctx->sa_in = NULL;
+ ctx->sa_out = NULL;
+ ctx->sa_in_dma_addr = 0;
+ ctx->sa_out_dma_addr = 0;
+ ctx->sa_len = 0;
+
+ if (alg->cra_type == &crypto_ablkcipher_type)
+ tfm->crt_ablkcipher.reqsize = sizeof(struct crypto4xx_ctx);
+ else if (alg->cra_type == &crypto_ahash_type)
+ tfm->crt_ahash.reqsize = sizeof(struct crypto4xx_ctx);
+
+ return 0;
+}
+
+static void crypto4xx_alg_exit(struct crypto_tfm *tfm)
+{
+ struct crypto4xx_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ crypto4xx_free_sa(ctx);
+ crypto4xx_free_state_record(ctx);
+}
+
+int crypto4xx_register_alg(struct crypto4xx_device *sec_dev,
+ struct crypto_alg *crypto_alg, int array_size)
+{
+ struct crypto4xx_alg *alg;
+ int i;
+ int rc = 0;
+
+ for (i = 0; i < array_size; i++) {
+ alg = kzalloc(sizeof(struct crypto4xx_alg), GFP_KERNEL);
+ if (!alg)
+ return -ENOMEM;
+
+ alg->alg = crypto_alg[i];
+ INIT_LIST_HEAD(&alg->alg.cra_list);
+ if (alg->alg.cra_init == NULL)
+ alg->alg.cra_init = crypto4xx_alg_init;
+ if (alg->alg.cra_exit == NULL)
+ alg->alg.cra_exit = crypto4xx_alg_exit;
+ alg->dev = sec_dev;
+ rc = crypto_register_alg(&alg->alg);
+ if (rc) {
+ list_del(&alg->entry);
+ kfree(alg);
+ } else {
+ list_add_tail(&alg->entry, &sec_dev->alg_list);
+ }
+ }
+
+ return 0;
+}
+
+static void crypto4xx_unregister_alg(struct crypto4xx_device *sec_dev)
+{
+ struct crypto4xx_alg *alg, *tmp;
+
+ list_for_each_entry_safe(alg, tmp, &sec_dev->alg_list, entry) {
+ list_del(&alg->entry);
+ crypto_unregister_alg(&alg->alg);
+ kfree(alg);
+ }
+}
+
+static void crypto4xx_bh_tasklet_cb(unsigned long data)
+{
+ struct device *dev = (struct device *)data;
+ struct crypto4xx_core_device *core_dev = dev_get_drvdata(dev);
+ struct pd_uinfo *pd_uinfo;
+ struct ce_pd *pd;
+ u32 tail;
+
+ while (core_dev->dev->pdr_head != core_dev->dev->pdr_tail) {
+ tail = core_dev->dev->pdr_tail;
+ pd_uinfo = core_dev->dev->pdr_uinfo +
+ sizeof(struct pd_uinfo)*tail;
+ pd = core_dev->dev->pdr + sizeof(struct ce_pd) * tail;
+ if ((pd_uinfo->state == PD_ENTRY_INUSE) &&
+ pd->pd_ctl.bf.pe_done &&
+ !pd->pd_ctl.bf.host_ready) {
+ pd->pd_ctl.bf.pe_done = 0;
+ crypto4xx_pd_done(core_dev->dev, tail);
+ crypto4xx_put_pd_to_pdr(core_dev->dev, tail);
+ pd_uinfo->state = PD_ENTRY_FREE;
+ } else {
+ /* if tail not done, break */
+ break;
+ }
+ }
+}
+
+/**
+ * Top Half of isr.
+ */
+static irqreturn_t crypto4xx_ce_interrupt_handler(int irq, void *data)
+{
+ struct device *dev = (struct device *)data;
+ struct crypto4xx_core_device *core_dev = dev_get_drvdata(dev);
+
+ if (core_dev->dev->ce_base == 0)
+ return 0;
+
+ writel(PPC4XX_INTERRUPT_CLR,
+ core_dev->dev->ce_base + CRYPTO4XX_INT_CLR);
+ tasklet_schedule(&core_dev->tasklet);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * Supported Crypto Algorithms
+ */
+struct crypto_alg crypto4xx_alg[] = {
+ /* Crypto AES modes */
+ {
+ .cra_name = "cbc(aes)",
+ .cra_driver_name = "cbc-aes-ppc4xx",
+ .cra_priority = CRYPTO4XX_CRYPTO_PRIORITY,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct crypto4xx_ctx),
+ .cra_alignmask = 0,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_u = {
+ .ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = AES_IV_SIZE,
+ .setkey = crypto4xx_setkey_aes_cbc,
+ .encrypt = crypto4xx_encrypt,
+ .decrypt = crypto4xx_decrypt,
+ }
+ }
+ },
+ /* Hash SHA1 */
+ {
+ .cra_name = "sha1",
+ .cra_driver_name = "sha1-ppc4xx",
+ .cra_priority = CRYPTO4XX_CRYPTO_PRIORITY,
+ .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC,
+ .cra_blocksize = SHA1_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct crypto4xx_ctx),
+ .cra_alignmask = 0,
+ .cra_type = &crypto_ahash_type,
+ .cra_init = crypto4xx_sha1_alg_init,
+ .cra_module = THIS_MODULE,
+ .cra_u = {
+ .ahash = {
+ .digestsize = SHA1_DIGEST_SIZE,
+ .init = crypto4xx_hash_init,
+ .update = crypto4xx_hash_update,
+ .final = crypto4xx_hash_final,
+ .digest = crypto4xx_hash_digest,
+ }
+ }
+ },
+};
+
+/**
+ * Module Initialization Routine
+ */
+static int __init crypto4xx_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
+{
+ int rc;
+ struct resource res;
+ struct device *dev = &ofdev->dev;
+ struct crypto4xx_core_device *core_dev;
+
+ rc = of_address_to_resource(ofdev->node, 0, &res);
+ if (rc)
+ return -ENODEV;
+
+ if (of_find_compatible_node(NULL, NULL, "amcc,ppc460ex-crypto")) {
+ mtdcri(SDR0, PPC460EX_SDR0_SRST,
+ mfdcri(SDR0, PPC460EX_SDR0_SRST) | PPC460EX_CE_RESET);
+ mtdcri(SDR0, PPC460EX_SDR0_SRST,
+ mfdcri(SDR0, PPC460EX_SDR0_SRST) & ~PPC460EX_CE_RESET);
+ } else if (of_find_compatible_node(NULL, NULL,
+ "amcc,ppc405ex-crypto")) {
+ mtdcri(SDR0, PPC405EX_SDR0_SRST,
+ mfdcri(SDR0, PPC405EX_SDR0_SRST) | PPC405EX_CE_RESET);
+ mtdcri(SDR0, PPC405EX_SDR0_SRST,
+ mfdcri(SDR0, PPC405EX_SDR0_SRST) & ~PPC405EX_CE_RESET);
+ } else if (of_find_compatible_node(NULL, NULL,
+ "amcc,ppc460sx-crypto")) {
+ mtdcri(SDR0, PPC460SX_SDR0_SRST,
+ mfdcri(SDR0, PPC460SX_SDR0_SRST) | PPC460SX_CE_RESET);
+ mtdcri(SDR0, PPC460SX_SDR0_SRST,
+ mfdcri(SDR0, PPC460SX_SDR0_SRST) & ~PPC460SX_CE_RESET);
+ } else {
+ printk(KERN_ERR "Crypto Function Not supported!\n");
+ return -EINVAL;
+ }
+
+ core_dev = kzalloc(sizeof(struct crypto4xx_core_device), GFP_KERNEL);
+ if (!core_dev)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, core_dev);
+ core_dev->ofdev = ofdev;
+ core_dev->dev = kzalloc(sizeof(struct crypto4xx_device), GFP_KERNEL);
+ if (!core_dev->dev)
+ goto err_alloc_dev;
+
+ core_dev->dev->core_dev = core_dev;
+ core_dev->device = dev;
+ spin_lock_init(&core_dev->lock);
+ INIT_LIST_HEAD(&core_dev->dev->alg_list);
+ rc = crypto4xx_build_pdr(core_dev->dev);
+ if (rc)
+ goto err_build_pdr;
+
+ rc = crypto4xx_build_gdr(core_dev->dev);
+ if (rc)
+ goto err_build_gdr;
+
+ rc = crypto4xx_build_sdr(core_dev->dev);
+ if (rc)
+ goto err_build_sdr;
+
+ /* Init tasklet for bottom half processing */
+ tasklet_init(&core_dev->tasklet, crypto4xx_bh_tasklet_cb,
+ (unsigned long) dev);
+
+ /* Register for Crypto isr, Crypto Engine IRQ */
+ core_dev->irq = irq_of_parse_and_map(ofdev->node, 0);
+ rc = request_irq(core_dev->irq, crypto4xx_ce_interrupt_handler, 0,
+ core_dev->dev->name, dev);
+ if (rc)
+ goto err_request_irq;
+
+ core_dev->dev->ce_base = of_iomap(ofdev->node, 0);
+ if (!core_dev->dev->ce_base) {
+ dev_err(dev, "failed to of_iomap\n");
+ goto err_iomap;
+ }
+
+ /* need to setup pdr, rdr, gdr and sdr before this */
+ crypto4xx_hw_init(core_dev->dev);
+
+ /* Register security algorithms with Linux CryptoAPI */
+ rc = crypto4xx_register_alg(core_dev->dev, crypto4xx_alg,
+ ARRAY_SIZE(crypto4xx_alg));
+ if (rc)
+ goto err_start_dev;
+
+ return 0;
+
+err_start_dev:
+ iounmap(core_dev->dev->ce_base);
+err_iomap:
+ free_irq(core_dev->irq, dev);
+ irq_dispose_mapping(core_dev->irq);
+ tasklet_kill(&core_dev->tasklet);
+err_request_irq:
+ crypto4xx_destroy_sdr(core_dev->dev);
+err_build_sdr:
+ crypto4xx_destroy_gdr(core_dev->dev);
+err_build_gdr:
+ crypto4xx_destroy_pdr(core_dev->dev);
+err_build_pdr:
+ kfree(core_dev->dev);
+err_alloc_dev:
+ kfree(core_dev);
+
+ return rc;
+}
+
+static int __exit crypto4xx_remove(struct of_device *ofdev)
+{
+ struct device *dev = &ofdev->dev;
+ struct crypto4xx_core_device *core_dev = dev_get_drvdata(dev);
+
+ free_irq(core_dev->irq, dev);
+ irq_dispose_mapping(core_dev->irq);
+
+ tasklet_kill(&core_dev->tasklet);
+ /* Un-register with Linux CryptoAPI */
+ crypto4xx_unregister_alg(core_dev->dev);
+ /* Free all allocated memory */
+ crypto4xx_stop_all(core_dev);
+
+ return 0;
+}
+
+static struct of_device_id crypto4xx_match[] = {
+ { .compatible = "amcc,ppc4xx-crypto",},
+ { },
+};
+
+static struct of_platform_driver crypto4xx_driver = {
+ .name = "crypto4xx",
+ .match_table = crypto4xx_match,
+ .probe = crypto4xx_probe,
+ .remove = crypto4xx_remove,
+};
+
+static int __init crypto4xx_init(void)
+{
+ return of_register_platform_driver(&crypto4xx_driver);
+}
+
+static void __exit crypto4xx_exit(void)
+{
+ of_unregister_platform_driver(&crypto4xx_driver);
+}
+
+module_init(crypto4xx_init);
+module_exit(crypto4xx_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("James Hsiao <jhsiao@amcc.com>");
+MODULE_DESCRIPTION("Driver for AMCC PPC4xx crypto accelerator");
+
diff --git a/drivers/crypto/amcc/crypto4xx_core.h b/drivers/crypto/amcc/crypto4xx_core.h
new file mode 100644
index 0000000..1ef1034
--- /dev/null
+++ b/drivers/crypto/amcc/crypto4xx_core.h
@@ -0,0 +1,177 @@
+/**
+ * AMCC SoC PPC4xx Crypto Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <jhsiao@amcc.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.
+ *
+ * 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.
+ *
+ * This is the header file for AMCC Crypto offload Linux device driver for
+ * use with Linux CryptoAPI.
+
+ */
+
+#ifndef __CRYPTO4XX_CORE_H__
+#define __CRYPTO4XX_CORE_H__
+
+#define PPC460SX_SDR0_SRST 0x201
+#define PPC405EX_SDR0_SRST 0x200
+#define PPC460EX_SDR0_SRST 0x201
+#define PPC460EX_CE_RESET 0x08000000
+#define PPC460SX_CE_RESET 0x20000000
+#define PPC405EX_CE_RESET 0x00000008
+
+#define CRYPTO4XX_CRYPTO_PRIORITY 300
+#define PPC4XX_LAST_PD 63
+#define PPC4XX_NUM_PD 64
+#define PPC4XX_LAST_GD 1023
+#define PPC4XX_NUM_GD 1024
+#define PPC4XX_LAST_SD 63
+#define PPC4XX_NUM_SD 64
+#define PPC4XX_SD_BUFFER_SIZE 2048
+
+#define PD_ENTRY_INUSE 1
+#define PD_ENTRY_FREE 0
+#define ERING_WAS_FULL 0xffffffff
+
+struct crypto4xx_device;
+
+struct pd_uinfo {
+ struct crypto4xx_device *dev;
+ u32 state;
+ u32 using_sd;
+ u32 first_gd; /* first gather discriptor
+ used by this packet */
+ u32 num_gd; /* number of gather discriptor
+ used by this packet */
+ u32 first_sd; /* first scatter discriptor
+ used by this packet */
+ u32 num_sd; /* number of scatter discriptors
+ used by this packet */
+ void *sa_va; /* shadow sa, when using cp from ctx->sa */
+ u32 sa_pa;
+ void *sr_va; /* state record for shadow sa */
+ u32 sr_pa;
+ struct scatterlist *dest_va;
+ struct crypto_async_request *async_req; /* base crypto request
+ for this packet */
+};
+
+struct crypto4xx_device {
+ struct crypto4xx_core_device *core_dev;
+ char *name;
+ u64 ce_phy_address;
+ void __iomem *ce_base;
+
+ void *pdr; /* base address of packet
+ descriptor ring */
+ dma_addr_t pdr_pa; /* physical address used to
+ program ce pdr_base_register */
+ void *gdr; /* gather descriptor ring */
+ dma_addr_t gdr_pa; /* physical address used to
+ program ce gdr_base_register */
+ void *sdr; /* scatter descriptor ring */
+ dma_addr_t sdr_pa; /* physical address used to
+ program ce sdr_base_register */
+ void *scatter_buffer_va;
+ dma_addr_t scatter_buffer_pa;
+ u32 scatter_buffer_size;
+
+ void *shadow_sa_pool; /* pool of memory for sa in pd_uinfo */
+ dma_addr_t shadow_sa_pool_pa;
+ void *shadow_sr_pool; /* pool of memory for sr in pd_uinfo */
+ dma_addr_t shadow_sr_pool_pa;
+ u32 pdr_tail;
+ u32 pdr_head;
+ u32 gdr_tail;
+ u32 gdr_head;
+ u32 sdr_tail;
+ u32 sdr_head;
+ void *pdr_uinfo;
+ struct list_head alg_list; /* List of algorithm supported
+ by this device */
+};
+
+struct crypto4xx_core_device {
+ struct device *device;
+ struct of_device *ofdev;
+ struct crypto4xx_device *dev;
+ u32 int_status;
+ u32 irq;
+ struct tasklet_struct tasklet;
+ spinlock_t lock;
+};
+
+struct crypto4xx_ctx {
+ struct crypto4xx_device *dev;
+ void *sa_in;
+ dma_addr_t sa_in_dma_addr;
+ void *sa_out;
+ dma_addr_t sa_out_dma_addr;
+ void *state_record;
+ dma_addr_t state_record_dma_addr;
+ u32 sa_len;
+ u32 offset_to_sr_ptr; /* offset to state ptr, in dynamic sa */
+ u32 direction;
+ u32 next_hdr;
+ u32 save_iv;
+ u32 pd_ctl_len;
+ u32 pd_ctl;
+ u32 bypass;
+ u32 is_hash;
+ u32 hash_final;
+};
+
+struct crypto4xx_req_ctx {
+ struct crypto4xx_device *dev; /* Device in which
+ operation to send to */
+ void *sa;
+ u32 sa_dma_addr;
+ u16 sa_len;
+};
+
+struct crypto4xx_alg {
+ struct list_head entry;
+ struct crypto_alg alg;
+ struct crypto4xx_device *dev;
+};
+
+#define crypto_alg_to_crypto4xx_alg(x) \
+ container_of(x, struct crypto4xx_alg, alg)
+
+extern int crypto4xx_alloc_sa(struct crypto4xx_ctx *ctx, u32 size);
+extern void crypto4xx_free_sa(struct crypto4xx_ctx *ctx);
+extern u32 crypto4xx_alloc_sa_rctx(struct crypto4xx_ctx *ctx,
+ struct crypto4xx_ctx *rctx);
+extern void crypto4xx_free_sa_rctx(struct crypto4xx_ctx *rctx);
+extern void crypto4xx_free_ctx(struct crypto4xx_ctx *ctx);
+extern u32 crypto4xx_alloc_state_record(struct crypto4xx_ctx *ctx);
+extern u32 get_dynamic_sa_offset_state_ptr_field(struct crypto4xx_ctx *ctx);
+extern u32 get_dynamic_sa_offset_key_field(struct crypto4xx_ctx *ctx);
+extern u32 get_dynamic_sa_iv_size(struct crypto4xx_ctx *ctx);
+extern void crypto4xx_memcpy_le(unsigned int *dst,
+ const unsigned char *buf, int len);
+extern u32 crypto4xx_build_pd(struct crypto_async_request *req,
+ struct crypto4xx_ctx *ctx,
+ struct scatterlist *src,
+ struct scatterlist *dst,
+ unsigned int datalen,
+ void *iv, u32 iv_len);
+extern int crypto4xx_setkey_aes_cbc(struct crypto_ablkcipher *cipher,
+ const u8 *key, unsigned int keylen);
+extern int crypto4xx_encrypt(struct ablkcipher_request *req);
+extern int crypto4xx_decrypt(struct ablkcipher_request *req);
+extern int crypto4xx_sha1_alg_init(struct crypto_tfm *tfm);
+extern int crypto4xx_hash_digest(struct ahash_request *req);
+extern int crypto4xx_hash_final(struct ahash_request *req);
+extern int crypto4xx_hash_update(struct ahash_request *req);
+extern int crypto4xx_hash_init(struct ahash_request *req);
+#endif
diff --git a/drivers/crypto/amcc/crypto4xx_reg_def.h b/drivers/crypto/amcc/crypto4xx_reg_def.h
new file mode 100644
index 0000000..7d4edb0
--- /dev/null
+++ b/drivers/crypto/amcc/crypto4xx_reg_def.h
@@ -0,0 +1,284 @@
+/**
+ * AMCC SoC PPC4xx Crypto Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <jhsiao@amcc.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.
+ *
+ * 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.
+ *
+ * This filr defines the register set for Security Subsystem
+ */
+
+#ifndef __CRYPTO4XX_REG_DEF_H__
+#define __CRYPTO4XX_REG_DEF_H__
+
+/* CRYPTO4XX Register offset */
+#define CRYPTO4XX_DESCRIPTOR 0x00000000
+#define CRYPTO4XX_CTRL_STAT 0x00000000
+#define CRYPTO4XX_SOURCE 0x00000004
+#define CRYPTO4XX_DEST 0x00000008
+#define CRYPTO4XX_SA 0x0000000C
+#define CRYPTO4XX_SA_LENGTH 0x00000010
+#define CRYPTO4XX_LENGTH 0x00000014
+
+#define CRYPTO4XX_PE_DMA_CFG 0x00000040
+#define CRYPTO4XX_PE_DMA_STAT 0x00000044
+#define CRYPTO4XX_PDR_BASE 0x00000048
+#define CRYPTO4XX_RDR_BASE 0x0000004c
+#define CRYPTO4XX_RING_SIZE 0x00000050
+#define CRYPTO4XX_RING_CTRL 0x00000054
+#define CRYPTO4XX_INT_RING_STAT 0x00000058
+#define CRYPTO4XX_EXT_RING_STAT 0x0000005c
+#define CRYPTO4XX_IO_THRESHOLD 0x00000060
+#define CRYPTO4XX_GATH_RING_BASE 0x00000064
+#define CRYPTO4XX_SCAT_RING_BASE 0x00000068
+#define CRYPTO4XX_PART_RING_SIZE 0x0000006c
+#define CRYPTO4XX_PART_RING_CFG 0x00000070
+
+#define CRYPTO4XX_PDR_BASE_UADDR 0x00000080
+#define CRYPTO4XX_RDR_BASE_UADDR 0x00000084
+#define CRYPTO4XX_PKT_SRC_UADDR 0x00000088
+#define CRYPTO4XX_PKT_DEST_UADDR 0x0000008c
+#define CRYPTO4XX_SA_UADDR 0x00000090
+#define CRYPTO4XX_GATH_RING_BASE_UADDR 0x000000A0
+#define CRYPTO4XX_SCAT_RING_BASE_UADDR 0x000000A4
+
+#define CRYPTO4XX_SEQ_RD 0x00000408
+#define CRYPTO4XX_SEQ_MASK_RD 0x0000040C
+
+#define CRYPTO4XX_SA_CMD_0 0x00010600
+#define CRYPTO4XX_SA_CMD_1 0x00010604
+
+#define CRYPTO4XX_STATE_PTR 0x000106dc
+#define CRYPTO4XX_STATE_IV 0x00010700
+#define CRYPTO4XX_STATE_HASH_BYTE_CNT_0 0x00010710
+#define CRYPTO4XX_STATE_HASH_BYTE_CNT_1 0x00010714
+
+#define CRYPTO4XX_STATE_IDIGEST_0 0x00010718
+#define CRYPTO4XX_STATE_IDIGEST_1 0x0001071c
+
+#define CRYPTO4XX_DATA_IN 0x00018000
+#define CRYPTO4XX_DATA_OUT 0x0001c000
+
+#define CRYPTO4XX_INT_UNMASK_STAT 0x000500a0
+#define CRYPTO4XX_INT_MASK_STAT 0x000500a4
+#define CRYPTO4XX_INT_CLR 0x000500a4
+#define CRYPTO4XX_INT_EN 0x000500a8
+
+#define CRYPTO4XX_INT_PKA 0x00000002
+#define CRYPTO4XX_INT_PDR_DONE 0x00008000
+#define CRYPTO4XX_INT_MA_WR_ERR 0x00020000
+#define CRYPTO4XX_INT_MA_RD_ERR 0x00010000
+#define CRYPTO4XX_INT_PE_ERR 0x00000200
+#define CRYPTO4XX_INT_USER_DMA_ERR 0x00000040
+#define CRYPTO4XX_INT_SLAVE_ERR 0x00000010
+#define CRYPTO4XX_INT_MASTER_ERR 0x00000008
+#define CRYPTO4XX_INT_ERROR 0x00030258
+
+#define CRYPTO4XX_INT_CFG 0x000500ac
+#define CRYPTO4XX_INT_DESCR_RD 0x000500b0
+#define CRYPTO4XX_INT_DESCR_CNT 0x000500b4
+#define CRYPTO4XX_INT_TIMEOUT_CNT 0x000500b8
+
+#define CRYPTO4XX_DEVICE_CTRL 0x00060080
+#define CRYPTO4XX_DEVICE_ID 0x00060084
+#define CRYPTO4XX_DEVICE_INFO 0x00060088
+#define CRYPTO4XX_DMA_USER_SRC 0x00060094
+#define CRYPTO4XX_DMA_USER_DEST 0x00060098
+#define CRYPTO4XX_DMA_USER_CMD 0x0006009C
+
+#define CRYPTO4XX_DMA_CFG 0x000600d4
+#define CRYPTO4XX_BYTE_ORDER_CFG 0x000600d8
+#define CRYPTO4XX_ENDIAN_CFG 0x000600d8
+
+#define CRYPTO4XX_PRNG_STAT 0x00070000
+#define CRYPTO4XX_PRNG_CTRL 0x00070004
+#define CRYPTO4XX_PRNG_SEED_L 0x00070008
+#define CRYPTO4XX_PRNG_SEED_H 0x0007000c
+
+#define CRYPTO4XX_PRNG_RES_0 0x00070020
+#define CRYPTO4XX_PRNG_RES_1 0x00070024
+#define CRYPTO4XX_PRNG_RES_2 0x00070028
+#define CRYPTO4XX_PRNG_RES_3 0x0007002C
+
+#define CRYPTO4XX_PRNG_LFSR_L 0x00070030
+#define CRYPTO4XX_PRNG_LFSR_H 0x00070034
+
+/**
+ * Initilize CRYPTO ENGINE registers, and memory bases.
+ */
+#define PPC4XX_PDR_POLL 0x3ff
+#define PPC4XX_OUTPUT_THRESHOLD 2
+#define PPC4XX_INPUT_THRESHOLD 2
+#define PPC4XX_PD_SIZE 6
+#define PPC4XX_CTX_DONE_INT 0x2000
+#define PPC4XX_PD_DONE_INT 0x8000
+#define PPC4XX_BYTE_ORDER 0x22222
+#define PPC4XX_INTERRUPT_CLR 0x3ffff
+#define PPC4XX_PRNG_CTRL_AUTO_EN 0x3
+#define PPC4XX_DC_3DES_EN 1
+#define PPC4XX_INT_DESCR_CNT 4
+#define PPC4XX_INT_TIMEOUT_CNT 0
+#define PPC4XX_INT_CFG 1
+/**
+ * all follow define are ad hoc
+ */
+#define PPC4XX_RING_RETRY 100
+#define PPC4XX_RING_POLL 100
+#define PPC4XX_SDR_SIZE PPC4XX_NUM_SD
+#define PPC4XX_GDR_SIZE PPC4XX_NUM_GD
+
+/**
+ * Generic Security Association (SA) with all possible fields. These will
+ * never likely used except for reference purpose. These structure format
+ * can be not changed as the hardware expects them to be layout as defined.
+ * Field can be removed or reduced but ordering can not be changed.
+ */
+#define CRYPTO4XX_DMA_CFG_OFFSET 0x40
+union ce_pe_dma_cfg {
+ struct {
+ u32 rsv:7;
+ u32 dir_host:1;
+ u32 rsv1:2;
+ u32 bo_td_en:1;
+ u32 dis_pdr_upd:1;
+ u32 bo_sgpd_en:1;
+ u32 bo_data_en:1;
+ u32 bo_sa_en:1;
+ u32 bo_pd_en:1;
+ u32 rsv2:4;
+ u32 dynamic_sa_en:1;
+ u32 pdr_mode:2;
+ u32 pe_mode:1;
+ u32 rsv3:5;
+ u32 reset_sg:1;
+ u32 reset_pdr:1;
+ u32 reset_pe:1;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+#define CRYPTO4XX_PDR_BASE_OFFSET 0x48
+#define CRYPTO4XX_RDR_BASE_OFFSET 0x4c
+#define CRYPTO4XX_RING_SIZE_OFFSET 0x50
+union ce_ring_size {
+ struct {
+ u32 ring_offset:16;
+ u32 rsv:6;
+ u32 ring_size:10;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+#define CRYPTO4XX_RING_CONTROL_OFFSET 0x54
+union ce_ring_contol {
+ struct {
+ u32 continuous:1;
+ u32 rsv:5;
+ u32 ring_retry_divisor:10;
+ u32 rsv1:4;
+ u32 ring_poll_divisor:10;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+#define CRYPTO4XX_IO_THRESHOLD_OFFSET 0x60
+union ce_io_threshold {
+ struct {
+ u32 rsv:6;
+ u32 output_threshold:10;
+ u32 rsv1:6;
+ u32 input_threshold:10;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+#define CRYPTO4XX_GATHER_RING_BASE_OFFSET 0x64
+#define CRYPTO4XX_SCATTER_RING_BASE_OFFSET 0x68
+
+union ce_part_ring_size {
+ struct {
+ u32 sdr_size:16;
+ u32 gdr_size:16;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+#define MAX_BURST_SIZE_32 0
+#define MAX_BURST_SIZE_64 1
+#define MAX_BURST_SIZE_128 2
+#define MAX_BURST_SIZE_256 3
+
+/* gather descriptor control length */
+struct gd_ctl_len {
+ u32 len:16;
+ u32 rsv:14;
+ u32 done:1;
+ u32 ready:1;
+} __attribute__((packed));
+
+struct ce_gd {
+ u32 ptr;
+ struct gd_ctl_len ctl_len;
+} __attribute__((packed));
+
+struct sd_ctl {
+ u32 ctl:30;
+ u32 done:1;
+ u32 rdy:1;
+} __attribute__((packed));
+
+struct ce_sd {
+ u32 ptr;
+ struct sd_ctl ctl;
+} __attribute__((packed));
+
+#define PD_PAD_CTL_32 0x10
+#define PD_PAD_CTL_64 0x20
+#define PD_PAD_CTL_128 0x40
+#define PD_PAD_CTL_256 0x80
+union ce_pd_ctl {
+ struct {
+ u32 pd_pad_ctl:8;
+ u32 status:8;
+ u32 next_hdr:8;
+ u32 rsv:2;
+ u32 cached_sa:1;
+ u32 hash_final:1;
+ u32 init_arc4:1;
+ u32 rsv1:1;
+ u32 pe_done:1;
+ u32 host_ready:1;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+union ce_pd_ctl_len {
+ struct {
+ u32 bypass:8;
+ u32 pe_done:1;
+ u32 host_ready:1;
+ u32 rsv:2;
+ u32 pkt_len:20;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+struct ce_pd {
+ union ce_pd_ctl pd_ctl;
+ u32 src;
+ u32 dest;
+ u32 sa; /* get from ctx->sa_dma_addr */
+ u32 sa_len; /* only if dynamic sa is used */
+ union ce_pd_ctl_len pd_ctl_len;
+
+} __attribute__((packed));
+#endif
diff --git a/drivers/crypto/amcc/crypto4xx_sa.c b/drivers/crypto/amcc/crypto4xx_sa.c
new file mode 100644
index 0000000..466fd94
--- /dev/null
+++ b/drivers/crypto/amcc/crypto4xx_sa.c
@@ -0,0 +1,108 @@
+/**
+ * AMCC SoC PPC4xx Crypto Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <jhsiao@amcc.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.
+ *
+ * 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.
+ *
+ * @file crypto4xx_sa.c
+ *
+ * This file implements the security context
+ * assoicate format.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mod_devicetable.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock_types.h>
+#include <linux/highmem.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <crypto/algapi.h>
+#include <crypto/des.h>
+#include "crypto4xx_reg_def.h"
+#include "crypto4xx_sa.h"
+#include "crypto4xx_core.h"
+
+u32 get_dynamic_sa_offset_iv_field(struct crypto4xx_ctx *ctx)
+{
+ u32 offset;
+ union dynamic_sa_contents cts;
+
+ if (ctx->direction == DIR_INBOUND)
+ cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_in))->sa_contents;
+ else
+ cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_out))->sa_contents;
+ offset = cts.bf.key_size
+ + cts.bf.inner_size
+ + cts.bf.outer_size
+ + cts.bf.spi
+ + cts.bf.seq_num0
+ + cts.bf.seq_num1
+ + cts.bf.seq_num_mask0
+ + cts.bf.seq_num_mask1
+ + cts.bf.seq_num_mask2
+ + cts.bf.seq_num_mask3;
+
+ return sizeof(struct dynamic_sa_ctl) + offset * 4;
+}
+
+u32 get_dynamic_sa_offset_state_ptr_field(struct crypto4xx_ctx *ctx)
+{
+ u32 offset;
+ union dynamic_sa_contents cts;
+
+ if (ctx->direction == DIR_INBOUND)
+ cts.w = ((struct dynamic_sa_ctl *) ctx->sa_in)->sa_contents;
+ else
+ cts.w = ((struct dynamic_sa_ctl *) ctx->sa_out)->sa_contents;
+ offset = cts.bf.key_size
+ + cts.bf.inner_size
+ + cts.bf.outer_size
+ + cts.bf.spi
+ + cts.bf.seq_num0
+ + cts.bf.seq_num1
+ + cts.bf.seq_num_mask0
+ + cts.bf.seq_num_mask1
+ + cts.bf.seq_num_mask2
+ + cts.bf.seq_num_mask3
+ + cts.bf.iv0
+ + cts.bf.iv1
+ + cts.bf.iv2
+ + cts.bf.iv3;
+
+ return sizeof(struct dynamic_sa_ctl) + offset * 4;
+}
+
+u32 get_dynamic_sa_iv_size(struct crypto4xx_ctx *ctx)
+{
+ union dynamic_sa_contents cts;
+
+ if (ctx->direction == DIR_INBOUND)
+ cts.w = ((struct dynamic_sa_ctl *) ctx->sa_in)->sa_contents;
+ else
+ cts.w = ((struct dynamic_sa_ctl *) ctx->sa_out)->sa_contents;
+ return (cts.bf.iv0 + cts.bf.iv1 + cts.bf.iv2 + cts.bf.iv3) * 4;
+}
+
+u32 get_dynamic_sa_offset_key_field(struct crypto4xx_ctx *ctx)
+{
+ union dynamic_sa_contents cts;
+
+ if (ctx->direction == DIR_INBOUND)
+ cts.w = ((struct dynamic_sa_ctl *) ctx->sa_in)->sa_contents;
+ else
+ cts.w = ((struct dynamic_sa_ctl *) ctx->sa_out)->sa_contents;
+
+ return sizeof(struct dynamic_sa_ctl);
+}
diff --git a/drivers/crypto/amcc/crypto4xx_sa.h b/drivers/crypto/amcc/crypto4xx_sa.h
new file mode 100644
index 0000000..4b83ed7
--- /dev/null
+++ b/drivers/crypto/amcc/crypto4xx_sa.h
@@ -0,0 +1,243 @@
+/**
+ * AMCC SoC PPC4xx Crypto Driver
+ *
+ * Copyright (c) 2008 Applied Micro Circuits Corporation.
+ * All rights reserved. James Hsiao <jhsiao@amcc.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.
+ *
+ * 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.
+ *
+ * This file defines the security context
+ * assoicate format.
+ */
+
+#ifndef __CRYPTO4XX_SA_H__
+#define __CRYPTO4XX_SA_H__
+
+#define AES_IV_SIZE 16
+
+/**
+ * Contents of Dynamic Security Association (SA) with all possible fields
+ */
+union dynamic_sa_contents {
+ struct {
+ u32 arc4_state_ptr:1;
+ u32 arc4_ij_ptr:1;
+ u32 state_ptr:1;
+ u32 iv3:1;
+ u32 iv2:1;
+ u32 iv1:1;
+ u32 iv0:1;
+ u32 seq_num_mask3:1;
+ u32 seq_num_mask2:1;
+ u32 seq_num_mask1:1;
+ u32 seq_num_mask0:1;
+ u32 seq_num1:1;
+ u32 seq_num0:1;
+ u32 spi:1;
+ u32 outer_size:5;
+ u32 inner_size:5;
+ u32 key_size:4;
+ u32 cmd_size:4;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+#define DIR_OUTBOUND 0
+#define DIR_INBOUND 1
+#define SA_OP_GROUP_BASIC 0
+#define SA_OPCODE_ENCRYPT 0
+#define SA_OPCODE_DECRYPT 0
+#define SA_OPCODE_HASH 3
+#define SA_CIPHER_ALG_DES 0
+#define SA_CIPHER_ALG_3DES 1
+#define SA_CIPHER_ALG_ARC4 2
+#define SA_CIPHER_ALG_AES 3
+#define SA_CIPHER_ALG_KASUMI 4
+#define SA_CIPHER_ALG_NULL 15
+
+#define SA_HASH_ALG_MD5 0
+#define SA_HASH_ALG_SHA1 1
+#define SA_HASH_ALG_NULL 15
+#define SA_HASH_ALG_SHA1_DIGEST_SIZE 20
+
+#define SA_LOAD_HASH_FROM_SA 0
+#define SA_LOAD_HASH_FROM_STATE 2
+#define SA_NOT_LOAD_HASH 3
+#define SA_LOAD_IV_FROM_SA 0
+#define SA_LOAD_IV_FROM_INPUT 1
+#define SA_LOAD_IV_FROM_STATE 2
+#define SA_LOAD_IV_GEN_IV 3
+
+#define SA_PAD_TYPE_CONSTANT 2
+#define SA_PAD_TYPE_ZERO 3
+#define SA_PAD_TYPE_TLS 5
+#define SA_PAD_TYPE_DTLS 5
+#define SA_NOT_SAVE_HASH 0
+#define SA_SAVE_HASH 1
+#define SA_NOT_SAVE_IV 0
+#define SA_SAVE_IV 1
+#define SA_HEADER_PROC 1
+#define SA_NO_HEADER_PROC 0
+
+union sa_command_0 {
+ struct {
+ u32 scatter:1;
+ u32 gather:1;
+ u32 save_hash_state:1;
+ u32 save_iv:1;
+ u32 load_hash_state:2;
+ u32 load_iv:2;
+ u32 digest_len:4;
+ u32 hdr_proc:1;
+ u32 extend_pad:1;
+ u32 stream_cipher_pad:1;
+ u32 rsv:1;
+ u32 hash_alg:4;
+ u32 cipher_alg:4;
+ u32 pad_type:2;
+ u32 op_group:2;
+ u32 dir:1;
+ u32 opcode:3;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+#define CRYPTO_MODE_ECB 0
+#define CRYPTO_MODE_CBC 1
+
+#define CRYPTO_FEEDBACK_MODE_NO_FB 0
+#define CRYPTO_FEEDBACK_MODE_64BIT_OFB 0
+#define CRYPTO_FEEDBACK_MODE_8BIT_CFB 1
+#define CRYPTO_FEEDBACK_MODE_1BIT_CFB 2
+#define CRYPTO_FEEDBACK_MODE_128BIT_CFB 3
+
+#define SA_AES_KEY_LEN_128 2
+#define SA_AES_KEY_LEN_192 3
+#define SA_AES_KEY_LEN_256 4
+
+#define SA_REV2 1
+/**
+ * The follow defines bits sa_command_1
+ * In Basic hash mode this bit define simple hash or hmac.
+ * In IPsec mode, this bit define muting control.
+ */
+#define SA_HASH_MODE_HASH 0
+#define SA_HASH_MODE_HMAC 1
+#define SA_MC_ENABLE 0
+#define SA_MC_DISABLE 1
+#define SA_NOT_COPY_HDR 0
+#define SA_COPY_HDR 1
+#define SA_NOT_COPY_PAD 0
+#define SA_COPY_PAD 1
+#define SA_NOT_COPY_PAYLOAD 0
+#define SA_COPY_PAYLOAD 1
+#define SA_EXTENDED_SN_OFF 0
+#define SA_EXTENDED_SN_ON 1
+#define SA_SEQ_MASK_OFF 0
+#define SA_SEQ_MASK_ON 1
+
+union sa_command_1 {
+ struct {
+ u32 crypto_mode31:1;
+ u32 save_arc4_state:1;
+ u32 arc4_stateful:1;
+ u32 key_len:5;
+ u32 hash_crypto_offset:8;
+ u32 sa_rev:2;
+ u32 byte_offset:1;
+ u32 hmac_muting:1;
+ u32 feedback_mode:2;
+ u32 crypto_mode9_8:2;
+ u32 extended_seq_num:1;
+ u32 seq_num_mask:1;
+ u32 mutable_bit_proc:1;
+ u32 ip_version:1;
+ u32 copy_pad:1;
+ u32 copy_payload:1;
+ u32 copy_hdr:1;
+ u32 rsv1:1;
+ } bf;
+ u32 w;
+} __attribute__((packed));
+
+struct dynamic_sa_ctl {
+ u32 sa_contents;
+ union sa_command_0 sa_command_0;
+ union sa_command_1 sa_command_1;
+} __attribute__((packed));
+
+/**
+ * State Record for Security Association (SA)
+ */
+struct sa_state_record {
+ u32 save_iv[4];
+ u32 save_hash_byte_cnt[2];
+ u32 save_digest[16];
+} __attribute__((packed));
+
+/**
+ * Security Association (SA) for AES128
+ *
+ */
+struct dynamic_sa_aes128 {
+ struct dynamic_sa_ctl ctrl;
+ u32 key[4];
+ u32 iv[4]; /* for CBC, OFC, and CFB mode */
+ u32 state_ptr;
+ u32 reserved;
+} __attribute__((packed));
+
+#define SA_AES128_LEN (sizeof(struct dynamic_sa_aes128)/4)
+#define SA_AES128_CONTENTS 0x3e000042
+
+/*
+ * Security Association (SA) for AES192
+ */
+struct dynamic_sa_aes192 {
+ struct dynamic_sa_ctl ctrl;
+ u32 key[6];
+ u32 iv[4]; /* for CBC, OFC, and CFB mode */
+ u32 state_ptr;
+ u32 reserved;
+} __attribute__((packed));
+
+#define SA_AES192_LEN (sizeof(struct dynamic_sa_aes192)/4)
+#define SA_AES192_CONTENTS 0x3e000062
+
+/**
+ * Security Association (SA) for AES256
+ */
+struct dynamic_sa_aes256 {
+ struct dynamic_sa_ctl ctrl;
+ u32 key[8];
+ u32 iv[4]; /* for CBC, OFC, and CFB mode */
+ u32 state_ptr;
+ u32 reserved;
+} __attribute__((packed));
+
+#define SA_AES256_LEN (sizeof(struct dynamic_sa_aes256)/4)
+#define SA_AES256_CONTENTS 0x3e000082
+#define SA_AES_CONTENTS 0x3e000002
+
+/**
+ * Security Association (SA) for HASH160: HMAC-SHA1
+ */
+struct dynamic_sa_hash160 {
+ struct dynamic_sa_ctl ctrl;
+ u32 inner_digest[5];
+ u32 outer_digest[5];
+ u32 state_ptr;
+ u32 reserved;
+} __attribute__((packed));
+#define SA_HASH160_LEN (sizeof(struct dynamic_sa_hash160)/4)
+#define SA_HASH160_CONTENTS 0x2000a502
+
+#endif
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index 5ea3bfa..640c992 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -56,8 +56,12 @@
comment "Please see Documentation/ide/ide.txt for help/info on IDE drives"
+config IDE_XFER_MODE
+ bool
+
config IDE_TIMINGS
bool
+ select IDE_XFER_MODE
config IDE_ATAPI
bool
@@ -698,6 +702,7 @@
config BLK_DEV_IDE_AU1XXX
bool "IDE for AMD Alchemy Au1200"
depends on SOC_AU1200
+ select IDE_XFER_MODE
choice
prompt "IDE Mode for AMD Alchemy Au1200"
default CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA
@@ -871,6 +876,7 @@
config BLK_DEV_DTC2278
tristate "DTC-2278 support"
+ select IDE_XFER_MODE
select IDE_LEGACY
help
This driver is enabled at runtime using the "dtc2278.probe" kernel
@@ -902,6 +908,7 @@
config BLK_DEV_UMC8672
tristate "UMC-8672 support"
+ select IDE_XFER_MODE
select IDE_LEGACY
help
This driver is enabled at runtime using the "umc8672.probe" kernel
@@ -915,5 +922,6 @@
config BLK_DEV_IDEDMA
def_bool BLK_DEV_IDEDMA_SFF || \
BLK_DEV_IDEDMA_ICS || BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA
+ select IDE_XFER_MODE
endif # IDE
diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile
index 1c326d9..9b4bbe1 100644
--- a/drivers/ide/Makefile
+++ b/drivers/ide/Makefile
@@ -5,9 +5,11 @@
EXTRA_CFLAGS += -Idrivers/ide
ide-core-y += ide.o ide-ioctls.o ide-io.o ide-iops.o ide-lib.o ide-probe.o \
- ide-taskfile.o ide-pm.o ide-park.o ide-pio-blacklist.o ide-sysfs.o
+ ide-taskfile.o ide-pm.o ide-park.o ide-sysfs.o ide-devsets.o \
+ ide-io-std.o ide-eh.o
# core IDE code
+ide-core-$(CONFIG_IDE_XFER_MODE) += ide-pio-blacklist.o ide-xfer-mode.o
ide-core-$(CONFIG_IDE_TIMINGS) += ide-timings.o
ide-core-$(CONFIG_IDE_ATAPI) += ide-atapi.o
ide-core-$(CONFIG_BLK_DEV_IDEPCI) += setup-pci.o
diff --git a/drivers/ide/aec62xx.c b/drivers/ide/aec62xx.c
index 4485b9c..878f8ec 100644
--- a/drivers/ide/aec62xx.c
+++ b/drivers/ide/aec62xx.c
@@ -139,7 +139,7 @@
drive->hwif->port_ops->set_dma_mode(drive, pio + XFER_PIO_0);
}
-static unsigned int init_chipset_aec62xx(struct pci_dev *dev)
+static int init_chipset_aec62xx(struct pci_dev *dev)
{
/* These are necessary to get AEC6280 Macintosh cards to work */
if ((dev->device == PCI_DEVICE_ID_ARTOP_ATP865) ||
@@ -156,7 +156,7 @@
pci_write_config_byte(dev, 0x4a, reg4ah | 0x80);
}
- return dev->irq;
+ return 0;
}
static u8 atp86x_cable_detect(ide_hwif_t *hwif)
diff --git a/drivers/ide/alim15x3.c b/drivers/ide/alim15x3.c
index 66f4308..d3513b6 100644
--- a/drivers/ide/alim15x3.c
+++ b/drivers/ide/alim15x3.c
@@ -212,7 +212,7 @@
* appropriate also sets up the 1533 southbridge.
*/
-static unsigned int init_chipset_ali15x3(struct pci_dev *dev)
+static int init_chipset_ali15x3(struct pci_dev *dev)
{
unsigned long flags;
u8 tmpbyte;
diff --git a/drivers/ide/amd74xx.c b/drivers/ide/amd74xx.c
index 77267c8..628cd2e 100644
--- a/drivers/ide/amd74xx.c
+++ b/drivers/ide/amd74xx.c
@@ -140,7 +140,7 @@
* The initialization callback. Initialize drive independent registers.
*/
-static unsigned int init_chipset_amd74xx(struct pci_dev *dev)
+static int init_chipset_amd74xx(struct pci_dev *dev)
{
u8 t = 0, offset = amd_offset(dev);
@@ -172,7 +172,7 @@
t |= 0xf0;
pci_write_config_byte(dev, AMD_IDE_CONFIG + offset, t);
- return dev->irq;
+ return 0;
}
static u8 amd_cable_detect(ide_hwif_t *hwif)
@@ -183,14 +183,6 @@
return ATA_CBL_PATA40;
}
-static void __devinit init_hwif_amd74xx(ide_hwif_t *hwif)
-{
- struct pci_dev *dev = to_pci_dev(hwif->dev);
-
- if (hwif->irq == 0) /* 0 is bogus but will do for now */
- hwif->irq = pci_get_legacy_ide_irq(dev, hwif->channel);
-}
-
static const struct ide_port_ops amd_port_ops = {
.set_pio_mode = amd_set_pio_mode,
.set_dma_mode = amd_set_drive,
@@ -207,7 +199,6 @@
{ \
.name = DRV_NAME, \
.init_chipset = init_chipset_amd74xx, \
- .init_hwif = init_hwif_amd74xx, \
.enablebits = {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, \
.port_ops = &amd_port_ops, \
.host_flags = IDE_HFLAGS_AMD, \
@@ -221,7 +212,6 @@
{ \
.name = DRV_NAME, \
.init_chipset = init_chipset_amd74xx, \
- .init_hwif = init_hwif_amd74xx, \
.enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, \
.port_ops = &amd_port_ops, \
.host_flags = IDE_HFLAGS_AMD, \
diff --git a/drivers/ide/atiixp.c b/drivers/ide/atiixp.c
index ecd1e62..923cbfe 100644
--- a/drivers/ide/atiixp.c
+++ b/drivers/ide/atiixp.c
@@ -142,7 +142,6 @@
.name = DRV_NAME,
.enablebits = {{0x48,0x01,0x00}, {0x48,0x08,0x00}},
.port_ops = &atiixp_port_ops,
- .host_flags = IDE_HFLAG_LEGACY_IRQS,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
@@ -151,7 +150,7 @@
.name = DRV_NAME,
.enablebits = {{0x48,0x01,0x00}, {0x00,0x00,0x00}},
.port_ops = &atiixp_port_ops,
- .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_LEGACY_IRQS,
+ .host_flags = IDE_HFLAG_SINGLE,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c
index 2f9688d..aeee036 100644
--- a/drivers/ide/cmd64x.c
+++ b/drivers/ide/cmd64x.c
@@ -333,7 +333,7 @@
return (dma_stat & 7) != 4;
}
-static unsigned int init_chipset_cmd64x(struct pci_dev *dev)
+static int init_chipset_cmd64x(struct pci_dev *dev)
{
u8 mrdmode = 0;
diff --git a/drivers/ide/cs5520.c b/drivers/ide/cs5520.c
index d003bec..58fb90e 100644
--- a/drivers/ide/cs5520.c
+++ b/drivers/ide/cs5520.c
@@ -133,7 +133,8 @@
* do all the device setup for us
*/
- ide_pci_setup_ports(dev, d, 14, &hw[0], &hws[0]);
+ ide_pci_setup_ports(dev, d, &hw[0], &hws[0]);
+ hw[0].irq = 14;
return ide_host_add(d, hws, NULL);
}
diff --git a/drivers/ide/cs5530.c b/drivers/ide/cs5530.c
index d8ede85..8e8b35a 100644
--- a/drivers/ide/cs5530.c
+++ b/drivers/ide/cs5530.c
@@ -135,7 +135,7 @@
* Initialize the cs5530 bridge for reliable IDE DMA operation.
*/
-static unsigned int init_chipset_cs5530(struct pci_dev *dev)
+static int init_chipset_cs5530(struct pci_dev *dev)
{
struct pci_dev *master_0 = NULL, *cs5530_0 = NULL;
diff --git a/drivers/ide/delkin_cb.c b/drivers/ide/delkin_cb.c
index 8f1b2d9..bacb119 100644
--- a/drivers/ide/delkin_cb.c
+++ b/drivers/ide/delkin_cb.c
@@ -46,7 +46,7 @@
.quirkproc = ide_undecoded_slave,
};
-static unsigned int delkin_cb_init_chipset(struct pci_dev *dev)
+static int delkin_cb_init_chipset(struct pci_dev *dev)
{
unsigned long base = pci_resource_start(dev, 0);
int i;
diff --git a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c
index 3eb9b5c..d3b3e82 100644
--- a/drivers/ide/hpt366.c
+++ b/drivers/ide/hpt366.c
@@ -995,7 +995,7 @@
pci_write_config_byte(dev, mcr_addr + 1, new_mcr);
}
-static unsigned int init_chipset_hpt366(struct pci_dev *dev)
+static int init_chipset_hpt366(struct pci_dev *dev)
{
unsigned long io_base = pci_resource_start(dev, 4);
struct hpt_info *info = hpt3xx_get_info(&dev->dev);
@@ -1237,7 +1237,7 @@
hpt3xx_disable_fast_irq(dev, 0x50);
hpt3xx_disable_fast_irq(dev, 0x54);
- return dev->irq;
+ return 0;
}
static u8 hpt3xx_cable_detect(ide_hwif_t *hwif)
diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c
index ec7d07f..5b704f1 100644
--- a/drivers/ide/ide-acpi.c
+++ b/drivers/ide/ide-acpi.c
@@ -20,9 +20,6 @@
#include <acpi/acpi_bus.h>
#define REGS_PER_GTF 7
-struct taskfile_array {
- u8 tfa[REGS_PER_GTF]; /* regs. 0x1f1 - 0x1f7 */
-};
struct GTM_buffer {
u32 PIO_speed0;
@@ -89,12 +86,8 @@
{ } /* terminate list */
};
-static int ide_acpi_blacklist(void)
+int ide_acpi_init(void)
{
- static int done;
- if (done)
- return 0;
- done = 1;
dmi_check_system(ide_acpi_dmi_table);
return 0;
}
@@ -202,40 +195,6 @@
}
/**
- * ide_acpi_drive_get_handle - Get ACPI object handle for a given drive
- * @drive: device to locate
- *
- * Retrieves the object handle of a given drive. According to the ACPI
- * spec the drive is a child of the hwif.
- *
- * Returns handle on success, 0 on error.
- */
-static acpi_handle ide_acpi_drive_get_handle(ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- int port;
- acpi_handle drive_handle;
-
- if (!hwif->acpidata)
- return NULL;
-
- if (!hwif->acpidata->obj_handle)
- return NULL;
-
- port = hwif->channel ? drive->dn - 2: drive->dn;
-
- DEBPRINT("ENTER: %s at channel#: %d port#: %d\n",
- drive->name, hwif->channel, port);
-
-
- /* TBD: could also check ACPI object VALID bits */
- drive_handle = acpi_get_child(hwif->acpidata->obj_handle, port);
- DEBPRINT("drive %s handle 0x%p\n", drive->name, drive_handle);
-
- return drive_handle;
-}
-
-/**
* do_drive_get_GTF - get the drive bootup default taskfile settings
* @drive: the drive for which the taskfile settings should be retrieved
* @gtf_length: number of bytes of _GTF data returned at @gtf_address
@@ -257,47 +216,15 @@
acpi_status status;
struct acpi_buffer output;
union acpi_object *out_obj;
- ide_hwif_t *hwif = drive->hwif;
- struct device *dev = hwif->gendev.parent;
int err = -ENODEV;
- int port;
*gtf_length = 0;
*gtf_address = 0UL;
*obj_loc = 0UL;
- if (ide_noacpi)
- return 0;
-
- if (!dev) {
- DEBPRINT("no PCI device for %s\n", hwif->name);
- goto out;
- }
-
- if (!hwif->acpidata) {
- DEBPRINT("no ACPI data for %s\n", hwif->name);
- goto out;
- }
-
- port = hwif->channel ? drive->dn - 2: drive->dn;
-
- DEBPRINT("ENTER: %s at %s, port#: %d, hard_port#: %d\n",
- hwif->name, dev_name(dev), port, hwif->channel);
-
- if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) {
- DEBPRINT("%s drive %d:%d not present\n",
- hwif->name, hwif->channel, port);
- goto out;
- }
-
- /* Get this drive's _ADR info. if not already known. */
if (!drive->acpidata->obj_handle) {
- drive->acpidata->obj_handle = ide_acpi_drive_get_handle(drive);
- if (!drive->acpidata->obj_handle) {
- DEBPRINT("No ACPI object found for %s\n",
- drive->name);
- goto out;
- }
+ DEBPRINT("No ACPI object found for %s\n", drive->name);
+ goto out;
}
/* Setting up output buffer */
@@ -355,43 +282,6 @@
}
/**
- * taskfile_load_raw - send taskfile registers to drive
- * @drive: drive to which output is sent
- * @gtf: raw ATA taskfile register set (0x1f1 - 0x1f7)
- *
- * Outputs IDE taskfile to the drive.
- */
-static int taskfile_load_raw(ide_drive_t *drive,
- const struct taskfile_array *gtf)
-{
- ide_task_t args;
- int err = 0;
-
- DEBPRINT("(0x1f1-1f7): hex: "
- "%02x %02x %02x %02x %02x %02x %02x\n",
- gtf->tfa[0], gtf->tfa[1], gtf->tfa[2],
- gtf->tfa[3], gtf->tfa[4], gtf->tfa[5], gtf->tfa[6]);
-
- memset(&args, 0, sizeof(ide_task_t));
-
- /* convert gtf to IDE Taskfile */
- memcpy(&args.tf_array[7], >f->tfa, 7);
- args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
-
- if (!ide_acpigtf) {
- DEBPRINT("_GTF execution disabled\n");
- return err;
- }
-
- err = ide_no_data_taskfile(drive, &args);
- if (err)
- printk(KERN_ERR "%s: ide_no_data_taskfile failed: %u\n",
- __func__, err);
-
- return err;
-}
-
-/**
* do_drive_set_taskfiles - write the drive taskfile settings from _GTF
* @drive: the drive to which the taskfile command should be sent
* @gtf_length: total number of bytes of _GTF taskfiles
@@ -404,43 +294,41 @@
unsigned int gtf_length,
unsigned long gtf_address)
{
- int rc = -ENODEV, err;
+ int rc = 0, err;
int gtf_count = gtf_length / REGS_PER_GTF;
int ix;
- struct taskfile_array *gtf;
-
- if (ide_noacpi)
- return 0;
-
- DEBPRINT("ENTER: %s, hard_port#: %d\n", drive->name, drive->dn);
-
- if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
- goto out;
-
- if (!gtf_count) /* shouldn't be here */
- goto out;
DEBPRINT("total GTF bytes=%u (0x%x), gtf_count=%d, addr=0x%lx\n",
gtf_length, gtf_length, gtf_count, gtf_address);
- if (gtf_length % REGS_PER_GTF) {
- printk(KERN_ERR "%s: unexpected GTF length (%d)\n",
- __func__, gtf_length);
- goto out;
- }
-
- rc = 0;
+ /* send all taskfile registers (0x1f1-0x1f7) *in*that*order* */
for (ix = 0; ix < gtf_count; ix++) {
- gtf = (struct taskfile_array *)
- (gtf_address + ix * REGS_PER_GTF);
+ u8 *gtf = (u8 *)(gtf_address + ix * REGS_PER_GTF);
+ ide_task_t task;
- /* send all TaskFile registers (0x1f1-0x1f7) *in*that*order* */
- err = taskfile_load_raw(drive, gtf);
- if (err)
+ DEBPRINT("(0x1f1-1f7): "
+ "hex: %02x %02x %02x %02x %02x %02x %02x\n",
+ gtf[0], gtf[1], gtf[2],
+ gtf[3], gtf[4], gtf[5], gtf[6]);
+
+ if (!ide_acpigtf) {
+ DEBPRINT("_GTF execution disabled\n");
+ continue;
+ }
+
+ /* convert GTF to taskfile */
+ memset(&task, 0, sizeof(ide_task_t));
+ memcpy(&task.tf_array[7], gtf, REGS_PER_GTF);
+ task.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
+
+ err = ide_no_data_taskfile(drive, &task);
+ if (err) {
+ printk(KERN_ERR "%s: ide_no_data_taskfile failed: %u\n",
+ __func__, err);
rc = err;
+ }
}
-out:
return rc;
}
@@ -647,26 +535,23 @@
DEBPRINT("no ACPI data for %s\n", hwif->name);
return;
}
+
/* channel first and then drives for power on and verse versa for power off */
if (on)
acpi_bus_set_power(hwif->acpidata->obj_handle, ACPI_STATE_D0);
- ide_port_for_each_dev(i, drive, hwif) {
- if (!drive->acpidata->obj_handle)
- drive->acpidata->obj_handle = ide_acpi_drive_get_handle(drive);
-
- if (drive->acpidata->obj_handle &&
- (drive->dev_flags & IDE_DFLAG_PRESENT)) {
+ ide_port_for_each_present_dev(i, drive, hwif) {
+ if (drive->acpidata->obj_handle)
acpi_bus_set_power(drive->acpidata->obj_handle,
- on? ACPI_STATE_D0: ACPI_STATE_D3);
- }
+ on ? ACPI_STATE_D0 : ACPI_STATE_D3);
}
+
if (!on)
acpi_bus_set_power(hwif->acpidata->obj_handle, ACPI_STATE_D3);
}
/**
- * ide_acpi_init - initialize the ACPI link for an IDE interface
+ * ide_acpi_init_port - initialize the ACPI link for an IDE interface
* @hwif: target IDE interface (channel)
*
* The ACPI spec is not quite clear when the drive identify buffer
@@ -676,10 +561,8 @@
* So we get the information during startup; but this means that
* any changes during run-time will be lost after resume.
*/
-void ide_acpi_init(ide_hwif_t *hwif)
+void ide_acpi_init_port(ide_hwif_t *hwif)
{
- ide_acpi_blacklist();
-
hwif->acpidata = kzalloc(sizeof(struct ide_acpi_hwif_link), GFP_KERNEL);
if (!hwif->acpidata)
return;
@@ -708,15 +591,24 @@
hwif->devices[0]->acpidata = &hwif->acpidata->master;
hwif->devices[1]->acpidata = &hwif->acpidata->slave;
- /*
- * Send IDENTIFY for each drive
- */
- ide_port_for_each_dev(i, drive, hwif) {
- memset(drive->acpidata, 0, sizeof(*drive->acpidata));
+ /* get _ADR info for each device */
+ ide_port_for_each_present_dev(i, drive, hwif) {
+ acpi_handle dev_handle;
- if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
- continue;
+ DEBPRINT("ENTER: %s at channel#: %d port#: %d\n",
+ drive->name, hwif->channel, drive->dn & 1);
+ /* TBD: could also check ACPI object VALID bits */
+ dev_handle = acpi_get_child(hwif->acpidata->obj_handle,
+ drive->dn & 1);
+
+ DEBPRINT("drive %s handle 0x%p\n", drive->name, dev_handle);
+
+ drive->acpidata->obj_handle = dev_handle;
+ }
+
+ /* send IDENTIFY for each device */
+ ide_port_for_each_present_dev(i, drive, hwif) {
err = taskfile_lib_get_identify(drive, drive->acpidata->idbuff);
if (err)
DEBPRINT("identify device %s failed (%d)\n",
@@ -736,9 +628,7 @@
ide_acpi_get_timing(hwif);
ide_acpi_push_timing(hwif);
- ide_port_for_each_dev(i, drive, hwif) {
- if (drive->dev_flags & IDE_DFLAG_PRESENT)
- /* Execute ACPI startup code */
- ide_acpi_exec_tfs(drive);
+ ide_port_for_each_present_dev(i, drive, hwif) {
+ ide_acpi_exec_tfs(drive);
}
}
diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c
index e9d042db..6adc5b4 100644
--- a/drivers/ide/ide-atapi.c
+++ b/drivers/ide/ide-atapi.c
@@ -149,7 +149,10 @@
memcpy(rq->cmd, pc->c, 12);
if (drive->media == ide_tape)
rq->cmd[13] = REQ_IDETAPE_PC1;
- ide_do_drive_cmd(drive, rq);
+
+ drive->hwif->rq = NULL;
+
+ elv_add_request(drive->queue, rq, ELEVATOR_INSERT_FRONT, 0);
}
/*
@@ -297,6 +300,21 @@
}
EXPORT_SYMBOL_GPL(ide_cd_get_xferlen);
+void ide_read_bcount_and_ireason(ide_drive_t *drive, u16 *bcount, u8 *ireason)
+{
+ ide_task_t task;
+
+ memset(&task, 0, sizeof(task));
+ task.tf_flags = IDE_TFLAG_IN_LBAH | IDE_TFLAG_IN_LBAM |
+ IDE_TFLAG_IN_NSECT;
+
+ drive->hwif->tp_ops->tf_read(drive, &task);
+
+ *bcount = (task.tf.lbah << 8) | task.tf.lbam;
+ *ireason = task.tf.nsect & 3;
+}
+EXPORT_SYMBOL_GPL(ide_read_bcount_and_ireason);
+
/*
* This is the usual interrupt handler which will be called during a packet
* command. We will transfer some of the data (as requested by the drive)
@@ -456,6 +474,25 @@
return ide_started;
}
+static void ide_pktcmd_tf_load(ide_drive_t *drive, u32 tf_flags, u16 bcount)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ ide_task_t task;
+ u8 dma = drive->dma;
+
+ memset(&task, 0, sizeof(task));
+ task.tf_flags = IDE_TFLAG_OUT_LBAH | IDE_TFLAG_OUT_LBAM |
+ IDE_TFLAG_OUT_FEATURE | tf_flags;
+ task.tf.feature = dma; /* Use PIO/DMA */
+ task.tf.lbam = bcount & 0xff;
+ task.tf.lbah = (bcount >> 8) & 0xff;
+
+ ide_tf_dump(drive->name, &task.tf);
+ hwif->tp_ops->set_irq(hwif, 1);
+ SELECT_MASK(drive, 0);
+ hwif->tp_ops->tf_load(drive, &task);
+}
+
static u8 ide_read_ireason(ide_drive_t *drive)
{
ide_task_t task;
@@ -629,7 +666,7 @@
: WAIT_TAPE_CMD;
}
- ide_pktcmd_tf_load(drive, tf_flags, bcount, drive->dma);
+ ide_pktcmd_tf_load(drive, tf_flags, bcount);
/* Issue the packet command */
if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) {
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index ddfbea4..2177cd1 100644
--- a/drivers/ide/ide-cd.c
+++ b/drivers/ide/ide-cd.c
@@ -242,7 +242,9 @@
ide_debug_log(IDE_DBG_SENSE, "failed_cmd: 0x%x\n",
failed_command->cmd[0]);
- ide_do_drive_cmd(drive, rq);
+ drive->hwif->rq = NULL;
+
+ elv_add_request(drive->queue, rq, ELEVATOR_INSERT_FRONT, 0);
}
static void cdrom_end_request(ide_drive_t *drive, int uptodate)
diff --git a/drivers/ide/ide-devsets.c b/drivers/ide/ide-devsets.c
new file mode 100644
index 0000000..7c39534
--- /dev/null
+++ b/drivers/ide/ide-devsets.c
@@ -0,0 +1,190 @@
+
+#include <linux/kernel.h>
+#include <linux/ide.h>
+
+DEFINE_MUTEX(ide_setting_mtx);
+
+ide_devset_get(io_32bit, io_32bit);
+
+static int set_io_32bit(ide_drive_t *drive, int arg)
+{
+ if (drive->dev_flags & IDE_DFLAG_NO_IO_32BIT)
+ return -EPERM;
+
+ if (arg < 0 || arg > 1 + (SUPPORT_VLB_SYNC << 1))
+ return -EINVAL;
+
+ drive->io_32bit = arg;
+
+ return 0;
+}
+
+ide_devset_get_flag(ksettings, IDE_DFLAG_KEEP_SETTINGS);
+
+static int set_ksettings(ide_drive_t *drive, int arg)
+{
+ if (arg < 0 || arg > 1)
+ return -EINVAL;
+
+ if (arg)
+ drive->dev_flags |= IDE_DFLAG_KEEP_SETTINGS;
+ else
+ drive->dev_flags &= ~IDE_DFLAG_KEEP_SETTINGS;
+
+ return 0;
+}
+
+ide_devset_get_flag(using_dma, IDE_DFLAG_USING_DMA);
+
+static int set_using_dma(ide_drive_t *drive, int arg)
+{
+#ifdef CONFIG_BLK_DEV_IDEDMA
+ int err = -EPERM;
+
+ if (arg < 0 || arg > 1)
+ return -EINVAL;
+
+ if (ata_id_has_dma(drive->id) == 0)
+ goto out;
+
+ if (drive->hwif->dma_ops == NULL)
+ goto out;
+
+ err = 0;
+
+ if (arg) {
+ if (ide_set_dma(drive))
+ err = -EIO;
+ } else
+ ide_dma_off(drive);
+
+out:
+ return err;
+#else
+ if (arg < 0 || arg > 1)
+ return -EINVAL;
+
+ return -EPERM;
+#endif
+}
+
+/*
+ * handle HDIO_SET_PIO_MODE ioctl abusers here, eventually it will go away
+ */
+static int set_pio_mode_abuse(ide_hwif_t *hwif, u8 req_pio)
+{
+ switch (req_pio) {
+ case 202:
+ case 201:
+ case 200:
+ case 102:
+ case 101:
+ case 100:
+ return (hwif->host_flags & IDE_HFLAG_ABUSE_DMA_MODES) ? 1 : 0;
+ case 9:
+ case 8:
+ return (hwif->host_flags & IDE_HFLAG_ABUSE_PREFETCH) ? 1 : 0;
+ case 7:
+ case 6:
+ return (hwif->host_flags & IDE_HFLAG_ABUSE_FAST_DEVSEL) ? 1 : 0;
+ default:
+ return 0;
+ }
+}
+
+static int set_pio_mode(ide_drive_t *drive, int arg)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ const struct ide_port_ops *port_ops = hwif->port_ops;
+
+ if (arg < 0 || arg > 255)
+ return -EINVAL;
+
+ if (port_ops == NULL || port_ops->set_pio_mode == NULL ||
+ (hwif->host_flags & IDE_HFLAG_NO_SET_MODE))
+ return -ENOSYS;
+
+ if (set_pio_mode_abuse(drive->hwif, arg)) {
+ if (arg == 8 || arg == 9) {
+ unsigned long flags;
+
+ /* take lock for IDE_DFLAG_[NO_]UNMASK/[NO_]IO_32BIT */
+ spin_lock_irqsave(&hwif->lock, flags);
+ port_ops->set_pio_mode(drive, arg);
+ spin_unlock_irqrestore(&hwif->lock, flags);
+ } else
+ port_ops->set_pio_mode(drive, arg);
+ } else {
+ int keep_dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA);
+
+ ide_set_pio(drive, arg);
+
+ if (hwif->host_flags & IDE_HFLAG_SET_PIO_MODE_KEEP_DMA) {
+ if (keep_dma)
+ ide_dma_on(drive);
+ }
+ }
+
+ return 0;
+}
+
+ide_devset_get_flag(unmaskirq, IDE_DFLAG_UNMASK);
+
+static int set_unmaskirq(ide_drive_t *drive, int arg)
+{
+ if (drive->dev_flags & IDE_DFLAG_NO_UNMASK)
+ return -EPERM;
+
+ if (arg < 0 || arg > 1)
+ return -EINVAL;
+
+ if (arg)
+ drive->dev_flags |= IDE_DFLAG_UNMASK;
+ else
+ drive->dev_flags &= ~IDE_DFLAG_UNMASK;
+
+ return 0;
+}
+
+ide_ext_devset_rw_sync(io_32bit, io_32bit);
+ide_ext_devset_rw_sync(keepsettings, ksettings);
+ide_ext_devset_rw_sync(unmaskirq, unmaskirq);
+ide_ext_devset_rw_sync(using_dma, using_dma);
+__IDE_DEVSET(pio_mode, DS_SYNC, NULL, set_pio_mode);
+
+int ide_devset_execute(ide_drive_t *drive, const struct ide_devset *setting,
+ int arg)
+{
+ struct request_queue *q = drive->queue;
+ struct request *rq;
+ int ret = 0;
+
+ if (!(setting->flags & DS_SYNC))
+ return setting->set(drive, arg);
+
+ rq = blk_get_request(q, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->cmd_len = 5;
+ rq->cmd[0] = REQ_DEVSET_EXEC;
+ *(int *)&rq->cmd[1] = arg;
+ rq->special = setting->set;
+
+ if (blk_execute_rq(q, NULL, rq, 0))
+ ret = rq->errors;
+ blk_put_request(rq);
+
+ return ret;
+}
+
+ide_startstop_t ide_do_devset(ide_drive_t *drive, struct request *rq)
+{
+ int err, (*setfunc)(ide_drive_t *, int) = rq->special;
+
+ err = setfunc(drive, *(int *)&rq->cmd[1]);
+ if (err)
+ rq->errors = err;
+ else
+ err = 1;
+ ide_end_request(drive, err, 0);
+ return ide_stopped;
+}
diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c
index 059c90b..a878f47 100644
--- a/drivers/ide/ide-dma.c
+++ b/drivers/ide/ide-dma.c
@@ -470,6 +470,63 @@
}
EXPORT_SYMBOL_GPL(ide_dma_timeout);
+/*
+ * un-busy the port etc, and clear any pending DMA status. we want to
+ * retry the current request in pio mode instead of risking tossing it
+ * all away
+ */
+ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct request *rq;
+ ide_startstop_t ret = ide_stopped;
+
+ /*
+ * end current dma transaction
+ */
+
+ if (error < 0) {
+ printk(KERN_WARNING "%s: DMA timeout error\n", drive->name);
+ (void)hwif->dma_ops->dma_end(drive);
+ ret = ide_error(drive, "dma timeout error",
+ hwif->tp_ops->read_status(hwif));
+ } else {
+ printk(KERN_WARNING "%s: DMA timeout retry\n", drive->name);
+ hwif->dma_ops->dma_timeout(drive);
+ }
+
+ /*
+ * disable dma for now, but remember that we did so because of
+ * a timeout -- we'll reenable after we finish this next request
+ * (or rather the first chunk of it) in pio.
+ */
+ drive->dev_flags |= IDE_DFLAG_DMA_PIO_RETRY;
+ drive->retry_pio++;
+ ide_dma_off_quietly(drive);
+
+ /*
+ * un-busy drive etc and make sure request is sane
+ */
+
+ rq = hwif->rq;
+ if (!rq)
+ goto out;
+
+ hwif->rq = NULL;
+
+ rq->errors = 0;
+
+ if (!rq->bio)
+ goto out;
+
+ rq->sector = rq->bio->bi_sector;
+ rq->current_nr_sectors = bio_iovec(rq->bio)->bv_len >> 9;
+ rq->hard_cur_sectors = rq->current_nr_sectors;
+ rq->buffer = bio_data(rq->bio);
+out:
+ return ret;
+}
+
void ide_release_dma_engine(ide_hwif_t *hwif)
{
if (hwif->dmatable_cpu) {
diff --git a/drivers/ide/ide-eh.c b/drivers/ide/ide-eh.c
new file mode 100644
index 0000000..1231b5e
--- /dev/null
+++ b/drivers/ide/ide-eh.c
@@ -0,0 +1,428 @@
+
+#include <linux/kernel.h>
+#include <linux/ide.h>
+#include <linux/delay.h>
+
+static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq,
+ u8 stat, u8 err)
+{
+ ide_hwif_t *hwif = drive->hwif;
+
+ if ((stat & ATA_BUSY) ||
+ ((stat & ATA_DF) && (drive->dev_flags & IDE_DFLAG_NOWERR) == 0)) {
+ /* other bits are useless when BUSY */
+ rq->errors |= ERROR_RESET;
+ } else if (stat & ATA_ERR) {
+ /* err has different meaning on cdrom and tape */
+ if (err == ATA_ABORTED) {
+ if ((drive->dev_flags & IDE_DFLAG_LBA) &&
+ /* some newer drives don't support ATA_CMD_INIT_DEV_PARAMS */
+ hwif->tp_ops->read_status(hwif) == ATA_CMD_INIT_DEV_PARAMS)
+ return ide_stopped;
+ } else if ((err & BAD_CRC) == BAD_CRC) {
+ /* UDMA crc error, just retry the operation */
+ drive->crc_count++;
+ } else if (err & (ATA_BBK | ATA_UNC)) {
+ /* retries won't help these */
+ rq->errors = ERROR_MAX;
+ } else if (err & ATA_TRK0NF) {
+ /* help it find track zero */
+ rq->errors |= ERROR_RECAL;
+ }
+ }
+
+ if ((stat & ATA_DRQ) && rq_data_dir(rq) == READ &&
+ (hwif->host_flags & IDE_HFLAG_ERROR_STOPS_FIFO) == 0) {
+ int nsect = drive->mult_count ? drive->mult_count : 1;
+
+ ide_pad_transfer(drive, READ, nsect * SECTOR_SIZE);
+ }
+
+ if (rq->errors >= ERROR_MAX || blk_noretry_request(rq)) {
+ ide_kill_rq(drive, rq);
+ return ide_stopped;
+ }
+
+ if (hwif->tp_ops->read_status(hwif) & (ATA_BUSY | ATA_DRQ))
+ rq->errors |= ERROR_RESET;
+
+ if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
+ ++rq->errors;
+ return ide_do_reset(drive);
+ }
+
+ if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
+ drive->special.b.recalibrate = 1;
+
+ ++rq->errors;
+
+ return ide_stopped;
+}
+
+static ide_startstop_t ide_atapi_error(ide_drive_t *drive, struct request *rq,
+ u8 stat, u8 err)
+{
+ ide_hwif_t *hwif = drive->hwif;
+
+ if ((stat & ATA_BUSY) ||
+ ((stat & ATA_DF) && (drive->dev_flags & IDE_DFLAG_NOWERR) == 0)) {
+ /* other bits are useless when BUSY */
+ rq->errors |= ERROR_RESET;
+ } else {
+ /* add decoding error stuff */
+ }
+
+ if (hwif->tp_ops->read_status(hwif) & (ATA_BUSY | ATA_DRQ))
+ /* force an abort */
+ hwif->tp_ops->exec_command(hwif, ATA_CMD_IDLEIMMEDIATE);
+
+ if (rq->errors >= ERROR_MAX) {
+ ide_kill_rq(drive, rq);
+ } else {
+ if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
+ ++rq->errors;
+ return ide_do_reset(drive);
+ }
+ ++rq->errors;
+ }
+
+ return ide_stopped;
+}
+
+static ide_startstop_t __ide_error(ide_drive_t *drive, struct request *rq,
+ u8 stat, u8 err)
+{
+ if (drive->media == ide_disk)
+ return ide_ata_error(drive, rq, stat, err);
+ return ide_atapi_error(drive, rq, stat, err);
+}
+
+/**
+ * ide_error - handle an error on the IDE
+ * @drive: drive the error occurred on
+ * @msg: message to report
+ * @stat: status bits
+ *
+ * ide_error() takes action based on the error returned by the drive.
+ * For normal I/O that may well include retries. We deal with
+ * both new-style (taskfile) and old style command handling here.
+ * In the case of taskfile command handling there is work left to
+ * do
+ */
+
+ide_startstop_t ide_error(ide_drive_t *drive, const char *msg, u8 stat)
+{
+ struct request *rq;
+ u8 err;
+
+ err = ide_dump_status(drive, msg, stat);
+
+ rq = drive->hwif->rq;
+ if (rq == NULL)
+ return ide_stopped;
+
+ /* retry only "normal" I/O: */
+ if (!blk_fs_request(rq)) {
+ rq->errors = 1;
+ ide_end_drive_cmd(drive, stat, err);
+ return ide_stopped;
+ }
+
+ return __ide_error(drive, rq, stat, err);
+}
+EXPORT_SYMBOL_GPL(ide_error);
+
+static inline void ide_complete_drive_reset(ide_drive_t *drive, int err)
+{
+ struct request *rq = drive->hwif->rq;
+
+ if (rq && blk_special_request(rq) && rq->cmd[0] == REQ_DRIVE_RESET)
+ ide_end_request(drive, err ? err : 1, 0);
+}
+
+/* needed below */
+static ide_startstop_t do_reset1(ide_drive_t *, int);
+
+/*
+ * atapi_reset_pollfunc() gets invoked to poll the interface for completion
+ * every 50ms during an atapi drive reset operation. If the drive has not yet
+ * responded, and we have not yet hit our maximum waiting time, then the timer
+ * is restarted for another 50ms.
+ */
+static ide_startstop_t atapi_reset_pollfunc(ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ u8 stat;
+
+ SELECT_DRIVE(drive);
+ udelay(10);
+ stat = hwif->tp_ops->read_status(hwif);
+
+ if (OK_STAT(stat, 0, ATA_BUSY))
+ printk(KERN_INFO "%s: ATAPI reset complete\n", drive->name);
+ else {
+ if (time_before(jiffies, hwif->poll_timeout)) {
+ ide_set_handler(drive, &atapi_reset_pollfunc, HZ/20,
+ NULL);
+ /* continue polling */
+ return ide_started;
+ }
+ /* end of polling */
+ hwif->polling = 0;
+ printk(KERN_ERR "%s: ATAPI reset timed-out, status=0x%02x\n",
+ drive->name, stat);
+ /* do it the old fashioned way */
+ return do_reset1(drive, 1);
+ }
+ /* done polling */
+ hwif->polling = 0;
+ ide_complete_drive_reset(drive, 0);
+ return ide_stopped;
+}
+
+static void ide_reset_report_error(ide_hwif_t *hwif, u8 err)
+{
+ static const char *err_master_vals[] =
+ { NULL, "passed", "formatter device error",
+ "sector buffer error", "ECC circuitry error",
+ "controlling MPU error" };
+
+ u8 err_master = err & 0x7f;
+
+ printk(KERN_ERR "%s: reset: master: ", hwif->name);
+ if (err_master && err_master < 6)
+ printk(KERN_CONT "%s", err_master_vals[err_master]);
+ else
+ printk(KERN_CONT "error (0x%02x?)", err);
+ if (err & 0x80)
+ printk(KERN_CONT "; slave: failed");
+ printk(KERN_CONT "\n");
+}
+
+/*
+ * reset_pollfunc() gets invoked to poll the interface for completion every 50ms
+ * during an ide reset operation. If the drives have not yet responded,
+ * and we have not yet hit our maximum waiting time, then the timer is restarted
+ * for another 50ms.
+ */
+static ide_startstop_t reset_pollfunc(ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ const struct ide_port_ops *port_ops = hwif->port_ops;
+ u8 tmp;
+ int err = 0;
+
+ if (port_ops && port_ops->reset_poll) {
+ err = port_ops->reset_poll(drive);
+ if (err) {
+ printk(KERN_ERR "%s: host reset_poll failure for %s.\n",
+ hwif->name, drive->name);
+ goto out;
+ }
+ }
+
+ tmp = hwif->tp_ops->read_status(hwif);
+
+ if (!OK_STAT(tmp, 0, ATA_BUSY)) {
+ if (time_before(jiffies, hwif->poll_timeout)) {
+ ide_set_handler(drive, &reset_pollfunc, HZ/20, NULL);
+ /* continue polling */
+ return ide_started;
+ }
+ printk(KERN_ERR "%s: reset timed-out, status=0x%02x\n",
+ hwif->name, tmp);
+ drive->failures++;
+ err = -EIO;
+ } else {
+ tmp = ide_read_error(drive);
+
+ if (tmp == 1) {
+ printk(KERN_INFO "%s: reset: success\n", hwif->name);
+ drive->failures = 0;
+ } else {
+ ide_reset_report_error(hwif, tmp);
+ drive->failures++;
+ err = -EIO;
+ }
+ }
+out:
+ hwif->polling = 0; /* done polling */
+ ide_complete_drive_reset(drive, err);
+ return ide_stopped;
+}
+
+static void ide_disk_pre_reset(ide_drive_t *drive)
+{
+ int legacy = (drive->id[ATA_ID_CFS_ENABLE_2] & 0x0400) ? 0 : 1;
+
+ drive->special.all = 0;
+ drive->special.b.set_geometry = legacy;
+ drive->special.b.recalibrate = legacy;
+
+ drive->mult_count = 0;
+ drive->dev_flags &= ~IDE_DFLAG_PARKED;
+
+ if ((drive->dev_flags & IDE_DFLAG_KEEP_SETTINGS) == 0 &&
+ (drive->dev_flags & IDE_DFLAG_USING_DMA) == 0)
+ drive->mult_req = 0;
+
+ if (drive->mult_req != drive->mult_count)
+ drive->special.b.set_multmode = 1;
+}
+
+static void pre_reset(ide_drive_t *drive)
+{
+ const struct ide_port_ops *port_ops = drive->hwif->port_ops;
+
+ if (drive->media == ide_disk)
+ ide_disk_pre_reset(drive);
+ else
+ drive->dev_flags |= IDE_DFLAG_POST_RESET;
+
+ if (drive->dev_flags & IDE_DFLAG_USING_DMA) {
+ if (drive->crc_count)
+ ide_check_dma_crc(drive);
+ else
+ ide_dma_off(drive);
+ }
+
+ if ((drive->dev_flags & IDE_DFLAG_KEEP_SETTINGS) == 0) {
+ if ((drive->dev_flags & IDE_DFLAG_USING_DMA) == 0) {
+ drive->dev_flags &= ~IDE_DFLAG_UNMASK;
+ drive->io_32bit = 0;
+ }
+ return;
+ }
+
+ if (port_ops && port_ops->pre_reset)
+ port_ops->pre_reset(drive);
+
+ if (drive->current_speed != 0xff)
+ drive->desired_speed = drive->current_speed;
+ drive->current_speed = 0xff;
+}
+
+/*
+ * do_reset1() attempts to recover a confused drive by resetting it.
+ * Unfortunately, resetting a disk drive actually resets all devices on
+ * the same interface, so it can really be thought of as resetting the
+ * interface rather than resetting the drive.
+ *
+ * ATAPI devices have their own reset mechanism which allows them to be
+ * individually reset without clobbering other devices on the same interface.
+ *
+ * Unfortunately, the IDE interface does not generate an interrupt to let
+ * us know when the reset operation has finished, so we must poll for this.
+ * Equally poor, though, is the fact that this may a very long time to complete,
+ * (up to 30 seconds worstcase). So, instead of busy-waiting here for it,
+ * we set a timer to poll at 50ms intervals.
+ */
+static ide_startstop_t do_reset1(ide_drive_t *drive, int do_not_try_atapi)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ const struct ide_tp_ops *tp_ops = hwif->tp_ops;
+ const struct ide_port_ops *port_ops;
+ ide_drive_t *tdrive;
+ unsigned long flags, timeout;
+ int i;
+ DEFINE_WAIT(wait);
+
+ spin_lock_irqsave(&hwif->lock, flags);
+
+ /* We must not reset with running handlers */
+ BUG_ON(hwif->handler != NULL);
+
+ /* For an ATAPI device, first try an ATAPI SRST. */
+ if (drive->media != ide_disk && !do_not_try_atapi) {
+ pre_reset(drive);
+ SELECT_DRIVE(drive);
+ udelay(20);
+ tp_ops->exec_command(hwif, ATA_CMD_DEV_RESET);
+ ndelay(400);
+ hwif->poll_timeout = jiffies + WAIT_WORSTCASE;
+ hwif->polling = 1;
+ __ide_set_handler(drive, &atapi_reset_pollfunc, HZ/20, NULL);
+ spin_unlock_irqrestore(&hwif->lock, flags);
+ return ide_started;
+ }
+
+ /* We must not disturb devices in the IDE_DFLAG_PARKED state. */
+ do {
+ unsigned long now;
+
+ prepare_to_wait(&ide_park_wq, &wait, TASK_UNINTERRUPTIBLE);
+ timeout = jiffies;
+ ide_port_for_each_present_dev(i, tdrive, hwif) {
+ if ((tdrive->dev_flags & IDE_DFLAG_PARKED) &&
+ time_after(tdrive->sleep, timeout))
+ timeout = tdrive->sleep;
+ }
+
+ now = jiffies;
+ if (time_before_eq(timeout, now))
+ break;
+
+ spin_unlock_irqrestore(&hwif->lock, flags);
+ timeout = schedule_timeout_uninterruptible(timeout - now);
+ spin_lock_irqsave(&hwif->lock, flags);
+ } while (timeout);
+ finish_wait(&ide_park_wq, &wait);
+
+ /*
+ * First, reset any device state data we were maintaining
+ * for any of the drives on this interface.
+ */
+ ide_port_for_each_dev(i, tdrive, hwif)
+ pre_reset(tdrive);
+
+ if (io_ports->ctl_addr == 0) {
+ spin_unlock_irqrestore(&hwif->lock, flags);
+ ide_complete_drive_reset(drive, -ENXIO);
+ return ide_stopped;
+ }
+
+ /*
+ * Note that we also set nIEN while resetting the device,
+ * to mask unwanted interrupts from the interface during the reset.
+ * However, due to the design of PC hardware, this will cause an
+ * immediate interrupt due to the edge transition it produces.
+ * This single interrupt gives us a "fast poll" for drives that
+ * recover from reset very quickly, saving us the first 50ms wait time.
+ *
+ * TODO: add ->softreset method and stop abusing ->set_irq
+ */
+ /* set SRST and nIEN */
+ tp_ops->set_irq(hwif, 4);
+ /* more than enough time */
+ udelay(10);
+ /* clear SRST, leave nIEN (unless device is on the quirk list) */
+ tp_ops->set_irq(hwif, drive->quirk_list == 2);
+ /* more than enough time */
+ udelay(10);
+ hwif->poll_timeout = jiffies + WAIT_WORSTCASE;
+ hwif->polling = 1;
+ __ide_set_handler(drive, &reset_pollfunc, HZ/20, NULL);
+
+ /*
+ * Some weird controller like resetting themselves to a strange
+ * state when the disks are reset this way. At least, the Winbond
+ * 553 documentation says that
+ */
+ port_ops = hwif->port_ops;
+ if (port_ops && port_ops->resetproc)
+ port_ops->resetproc(drive);
+
+ spin_unlock_irqrestore(&hwif->lock, flags);
+ return ide_started;
+}
+
+/*
+ * ide_do_reset() is the entry point to the drive/interface reset code.
+ */
+
+ide_startstop_t ide_do_reset(ide_drive_t *drive)
+{
+ return do_reset1(drive, 0);
+}
+EXPORT_SYMBOL(ide_do_reset);
diff --git a/drivers/ide/ide-io-std.c b/drivers/ide/ide-io-std.c
new file mode 100644
index 0000000..45b43dd
--- /dev/null
+++ b/drivers/ide/ide-io-std.c
@@ -0,0 +1,316 @@
+
+#include <linux/kernel.h>
+#include <linux/ide.h>
+
+/*
+ * Conventional PIO operations for ATA devices
+ */
+
+static u8 ide_inb(unsigned long port)
+{
+ return (u8) inb(port);
+}
+
+static void ide_outb(u8 val, unsigned long port)
+{
+ outb(val, port);
+}
+
+/*
+ * MMIO operations, typically used for SATA controllers
+ */
+
+static u8 ide_mm_inb(unsigned long port)
+{
+ return (u8) readb((void __iomem *) port);
+}
+
+static void ide_mm_outb(u8 value, unsigned long port)
+{
+ writeb(value, (void __iomem *) port);
+}
+
+void ide_exec_command(ide_hwif_t *hwif, u8 cmd)
+{
+ if (hwif->host_flags & IDE_HFLAG_MMIO)
+ writeb(cmd, (void __iomem *)hwif->io_ports.command_addr);
+ else
+ outb(cmd, hwif->io_ports.command_addr);
+}
+EXPORT_SYMBOL_GPL(ide_exec_command);
+
+u8 ide_read_status(ide_hwif_t *hwif)
+{
+ if (hwif->host_flags & IDE_HFLAG_MMIO)
+ return readb((void __iomem *)hwif->io_ports.status_addr);
+ else
+ return inb(hwif->io_ports.status_addr);
+}
+EXPORT_SYMBOL_GPL(ide_read_status);
+
+u8 ide_read_altstatus(ide_hwif_t *hwif)
+{
+ if (hwif->host_flags & IDE_HFLAG_MMIO)
+ return readb((void __iomem *)hwif->io_ports.ctl_addr);
+ else
+ return inb(hwif->io_ports.ctl_addr);
+}
+EXPORT_SYMBOL_GPL(ide_read_altstatus);
+
+void ide_set_irq(ide_hwif_t *hwif, int on)
+{
+ u8 ctl = ATA_DEVCTL_OBS;
+
+ if (on == 4) { /* hack for SRST */
+ ctl |= 4;
+ on &= ~4;
+ }
+
+ ctl |= on ? 0 : 2;
+
+ if (hwif->host_flags & IDE_HFLAG_MMIO)
+ writeb(ctl, (void __iomem *)hwif->io_ports.ctl_addr);
+ else
+ outb(ctl, hwif->io_ports.ctl_addr);
+}
+EXPORT_SYMBOL_GPL(ide_set_irq);
+
+void ide_tf_load(ide_drive_t *drive, ide_task_t *task)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ struct ide_taskfile *tf = &task->tf;
+ void (*tf_outb)(u8 addr, unsigned long port);
+ u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
+ u8 HIHI = (task->tf_flags & IDE_TFLAG_LBA48) ? 0xE0 : 0xEF;
+
+ if (mmio)
+ tf_outb = ide_mm_outb;
+ else
+ tf_outb = ide_outb;
+
+ if (task->tf_flags & IDE_TFLAG_FLAGGED)
+ HIHI = 0xFF;
+
+ if (task->tf_flags & IDE_TFLAG_OUT_DATA) {
+ u16 data = (tf->hob_data << 8) | tf->data;
+
+ if (mmio)
+ writew(data, (void __iomem *)io_ports->data_addr);
+ else
+ outw(data, io_ports->data_addr);
+ }
+
+ if (task->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE)
+ tf_outb(tf->hob_feature, io_ports->feature_addr);
+ if (task->tf_flags & IDE_TFLAG_OUT_HOB_NSECT)
+ tf_outb(tf->hob_nsect, io_ports->nsect_addr);
+ if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAL)
+ tf_outb(tf->hob_lbal, io_ports->lbal_addr);
+ if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAM)
+ tf_outb(tf->hob_lbam, io_ports->lbam_addr);
+ if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAH)
+ tf_outb(tf->hob_lbah, io_ports->lbah_addr);
+
+ if (task->tf_flags & IDE_TFLAG_OUT_FEATURE)
+ tf_outb(tf->feature, io_ports->feature_addr);
+ if (task->tf_flags & IDE_TFLAG_OUT_NSECT)
+ tf_outb(tf->nsect, io_ports->nsect_addr);
+ if (task->tf_flags & IDE_TFLAG_OUT_LBAL)
+ tf_outb(tf->lbal, io_ports->lbal_addr);
+ if (task->tf_flags & IDE_TFLAG_OUT_LBAM)
+ tf_outb(tf->lbam, io_ports->lbam_addr);
+ if (task->tf_flags & IDE_TFLAG_OUT_LBAH)
+ tf_outb(tf->lbah, io_ports->lbah_addr);
+
+ if (task->tf_flags & IDE_TFLAG_OUT_DEVICE)
+ tf_outb((tf->device & HIHI) | drive->select,
+ io_ports->device_addr);
+}
+EXPORT_SYMBOL_GPL(ide_tf_load);
+
+void ide_tf_read(ide_drive_t *drive, ide_task_t *task)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ struct ide_taskfile *tf = &task->tf;
+ void (*tf_outb)(u8 addr, unsigned long port);
+ u8 (*tf_inb)(unsigned long port);
+ u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
+
+ if (mmio) {
+ tf_outb = ide_mm_outb;
+ tf_inb = ide_mm_inb;
+ } else {
+ tf_outb = ide_outb;
+ tf_inb = ide_inb;
+ }
+
+ if (task->tf_flags & IDE_TFLAG_IN_DATA) {
+ u16 data;
+
+ if (mmio)
+ data = readw((void __iomem *)io_ports->data_addr);
+ else
+ data = inw(io_ports->data_addr);
+
+ tf->data = data & 0xff;
+ tf->hob_data = (data >> 8) & 0xff;
+ }
+
+ /* be sure we're looking at the low order bits */
+ tf_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr);
+
+ if (task->tf_flags & IDE_TFLAG_IN_FEATURE)
+ tf->feature = tf_inb(io_ports->feature_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_NSECT)
+ tf->nsect = tf_inb(io_ports->nsect_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_LBAL)
+ tf->lbal = tf_inb(io_ports->lbal_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_LBAM)
+ tf->lbam = tf_inb(io_ports->lbam_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_LBAH)
+ tf->lbah = tf_inb(io_ports->lbah_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_DEVICE)
+ tf->device = tf_inb(io_ports->device_addr);
+
+ if (task->tf_flags & IDE_TFLAG_LBA48) {
+ tf_outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr);
+
+ if (task->tf_flags & IDE_TFLAG_IN_HOB_FEATURE)
+ tf->hob_feature = tf_inb(io_ports->feature_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_HOB_NSECT)
+ tf->hob_nsect = tf_inb(io_ports->nsect_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAL)
+ tf->hob_lbal = tf_inb(io_ports->lbal_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAM)
+ tf->hob_lbam = tf_inb(io_ports->lbam_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAH)
+ tf->hob_lbah = tf_inb(io_ports->lbah_addr);
+ }
+}
+EXPORT_SYMBOL_GPL(ide_tf_read);
+
+/*
+ * Some localbus EIDE interfaces require a special access sequence
+ * when using 32-bit I/O instructions to transfer data. We call this
+ * the "vlb_sync" sequence, which consists of three successive reads
+ * of the sector count register location, with interrupts disabled
+ * to ensure that the reads all happen together.
+ */
+static void ata_vlb_sync(unsigned long port)
+{
+ (void)inb(port);
+ (void)inb(port);
+ (void)inb(port);
+}
+
+/*
+ * This is used for most PIO data transfers *from* the IDE interface
+ *
+ * These routines will round up any request for an odd number of bytes,
+ * so if an odd len is specified, be sure that there's at least one
+ * extra byte allocated for the buffer.
+ */
+void ide_input_data(ide_drive_t *drive, struct request *rq, void *buf,
+ unsigned int len)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ unsigned long data_addr = io_ports->data_addr;
+ u8 io_32bit = drive->io_32bit;
+ u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
+
+ len++;
+
+ if (io_32bit) {
+ unsigned long uninitialized_var(flags);
+
+ if ((io_32bit & 2) && !mmio) {
+ local_irq_save(flags);
+ ata_vlb_sync(io_ports->nsect_addr);
+ }
+
+ if (mmio)
+ __ide_mm_insl((void __iomem *)data_addr, buf, len / 4);
+ else
+ insl(data_addr, buf, len / 4);
+
+ if ((io_32bit & 2) && !mmio)
+ local_irq_restore(flags);
+
+ if ((len & 3) >= 2) {
+ if (mmio)
+ __ide_mm_insw((void __iomem *)data_addr,
+ (u8 *)buf + (len & ~3), 1);
+ else
+ insw(data_addr, (u8 *)buf + (len & ~3), 1);
+ }
+ } else {
+ if (mmio)
+ __ide_mm_insw((void __iomem *)data_addr, buf, len / 2);
+ else
+ insw(data_addr, buf, len / 2);
+ }
+}
+EXPORT_SYMBOL_GPL(ide_input_data);
+
+/*
+ * This is used for most PIO data transfers *to* the IDE interface
+ */
+void ide_output_data(ide_drive_t *drive, struct request *rq, void *buf,
+ unsigned int len)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ unsigned long data_addr = io_ports->data_addr;
+ u8 io_32bit = drive->io_32bit;
+ u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
+
+ len++;
+
+ if (io_32bit) {
+ unsigned long uninitialized_var(flags);
+
+ if ((io_32bit & 2) && !mmio) {
+ local_irq_save(flags);
+ ata_vlb_sync(io_ports->nsect_addr);
+ }
+
+ if (mmio)
+ __ide_mm_outsl((void __iomem *)data_addr, buf, len / 4);
+ else
+ outsl(data_addr, buf, len / 4);
+
+ if ((io_32bit & 2) && !mmio)
+ local_irq_restore(flags);
+
+ if ((len & 3) >= 2) {
+ if (mmio)
+ __ide_mm_outsw((void __iomem *)data_addr,
+ (u8 *)buf + (len & ~3), 1);
+ else
+ outsw(data_addr, (u8 *)buf + (len & ~3), 1);
+ }
+ } else {
+ if (mmio)
+ __ide_mm_outsw((void __iomem *)data_addr, buf, len / 2);
+ else
+ outsw(data_addr, buf, len / 2);
+ }
+}
+EXPORT_SYMBOL_GPL(ide_output_data);
+
+const struct ide_tp_ops default_tp_ops = {
+ .exec_command = ide_exec_command,
+ .read_status = ide_read_status,
+ .read_altstatus = ide_read_altstatus,
+
+ .set_irq = ide_set_irq,
+
+ .tf_load = ide_tf_load,
+ .tf_read = ide_tf_read,
+
+ .input_data = ide_input_data,
+ .output_data = ide_output_data,
+};
diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c
index a9a6c20..2e92497 100644
--- a/drivers/ide/ide-io.c
+++ b/drivers/ide/ide-io.c
@@ -196,7 +196,7 @@
}
EXPORT_SYMBOL(ide_end_drive_cmd);
-static void ide_kill_rq(ide_drive_t *drive, struct request *rq)
+void ide_kill_rq(ide_drive_t *drive, struct request *rq)
{
if (rq->rq_disk) {
struct ide_driver *drv;
@@ -207,133 +207,6 @@
ide_end_request(drive, 0, 0);
}
-static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err)
-{
- ide_hwif_t *hwif = drive->hwif;
-
- if ((stat & ATA_BUSY) ||
- ((stat & ATA_DF) && (drive->dev_flags & IDE_DFLAG_NOWERR) == 0)) {
- /* other bits are useless when BUSY */
- rq->errors |= ERROR_RESET;
- } else if (stat & ATA_ERR) {
- /* err has different meaning on cdrom and tape */
- if (err == ATA_ABORTED) {
- if ((drive->dev_flags & IDE_DFLAG_LBA) &&
- /* some newer drives don't support ATA_CMD_INIT_DEV_PARAMS */
- hwif->tp_ops->read_status(hwif) == ATA_CMD_INIT_DEV_PARAMS)
- return ide_stopped;
- } else if ((err & BAD_CRC) == BAD_CRC) {
- /* UDMA crc error, just retry the operation */
- drive->crc_count++;
- } else if (err & (ATA_BBK | ATA_UNC)) {
- /* retries won't help these */
- rq->errors = ERROR_MAX;
- } else if (err & ATA_TRK0NF) {
- /* help it find track zero */
- rq->errors |= ERROR_RECAL;
- }
- }
-
- if ((stat & ATA_DRQ) && rq_data_dir(rq) == READ &&
- (hwif->host_flags & IDE_HFLAG_ERROR_STOPS_FIFO) == 0) {
- int nsect = drive->mult_count ? drive->mult_count : 1;
-
- ide_pad_transfer(drive, READ, nsect * SECTOR_SIZE);
- }
-
- if (rq->errors >= ERROR_MAX || blk_noretry_request(rq)) {
- ide_kill_rq(drive, rq);
- return ide_stopped;
- }
-
- if (hwif->tp_ops->read_status(hwif) & (ATA_BUSY | ATA_DRQ))
- rq->errors |= ERROR_RESET;
-
- if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
- ++rq->errors;
- return ide_do_reset(drive);
- }
-
- if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
- drive->special.b.recalibrate = 1;
-
- ++rq->errors;
-
- return ide_stopped;
-}
-
-static ide_startstop_t ide_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err)
-{
- ide_hwif_t *hwif = drive->hwif;
-
- if ((stat & ATA_BUSY) ||
- ((stat & ATA_DF) && (drive->dev_flags & IDE_DFLAG_NOWERR) == 0)) {
- /* other bits are useless when BUSY */
- rq->errors |= ERROR_RESET;
- } else {
- /* add decoding error stuff */
- }
-
- if (hwif->tp_ops->read_status(hwif) & (ATA_BUSY | ATA_DRQ))
- /* force an abort */
- hwif->tp_ops->exec_command(hwif, ATA_CMD_IDLEIMMEDIATE);
-
- if (rq->errors >= ERROR_MAX) {
- ide_kill_rq(drive, rq);
- } else {
- if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
- ++rq->errors;
- return ide_do_reset(drive);
- }
- ++rq->errors;
- }
-
- return ide_stopped;
-}
-
-static ide_startstop_t
-__ide_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err)
-{
- if (drive->media == ide_disk)
- return ide_ata_error(drive, rq, stat, err);
- return ide_atapi_error(drive, rq, stat, err);
-}
-
-/**
- * ide_error - handle an error on the IDE
- * @drive: drive the error occurred on
- * @msg: message to report
- * @stat: status bits
- *
- * ide_error() takes action based on the error returned by the drive.
- * For normal I/O that may well include retries. We deal with
- * both new-style (taskfile) and old style command handling here.
- * In the case of taskfile command handling there is work left to
- * do
- */
-
-ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, u8 stat)
-{
- struct request *rq;
- u8 err;
-
- err = ide_dump_status(drive, msg, stat);
-
- rq = drive->hwif->rq;
- if (rq == NULL)
- return ide_stopped;
-
- /* retry only "normal" I/O: */
- if (!blk_fs_request(rq)) {
- rq->errors = 1;
- ide_end_drive_cmd(drive, stat, err);
- return ide_stopped;
- }
-
- return __ide_error(drive, rq, stat, err);
-}
-EXPORT_SYMBOL_GPL(ide_error);
-
static void ide_tf_set_specify_cmd(ide_drive_t *drive, struct ide_taskfile *tf)
{
tf->nsect = drive->sect;
@@ -490,71 +363,16 @@
return ide_stopped;
}
-int ide_devset_execute(ide_drive_t *drive, const struct ide_devset *setting,
- int arg)
-{
- struct request_queue *q = drive->queue;
- struct request *rq;
- int ret = 0;
-
- if (!(setting->flags & DS_SYNC))
- return setting->set(drive, arg);
-
- rq = blk_get_request(q, READ, __GFP_WAIT);
- rq->cmd_type = REQ_TYPE_SPECIAL;
- rq->cmd_len = 5;
- rq->cmd[0] = REQ_DEVSET_EXEC;
- *(int *)&rq->cmd[1] = arg;
- rq->special = setting->set;
-
- if (blk_execute_rq(q, NULL, rq, 0))
- ret = rq->errors;
- blk_put_request(rq);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(ide_devset_execute);
-
static ide_startstop_t ide_special_rq(ide_drive_t *drive, struct request *rq)
{
u8 cmd = rq->cmd[0];
- if (cmd == REQ_PARK_HEADS || cmd == REQ_UNPARK_HEADS) {
- ide_task_t task;
- struct ide_taskfile *tf = &task.tf;
-
- memset(&task, 0, sizeof(task));
- if (cmd == REQ_PARK_HEADS) {
- drive->sleep = *(unsigned long *)rq->special;
- drive->dev_flags |= IDE_DFLAG_SLEEPING;
- tf->command = ATA_CMD_IDLEIMMEDIATE;
- tf->feature = 0x44;
- tf->lbal = 0x4c;
- tf->lbam = 0x4e;
- tf->lbah = 0x55;
- task.tf_flags |= IDE_TFLAG_CUSTOM_HANDLER;
- } else /* cmd == REQ_UNPARK_HEADS */
- tf->command = ATA_CMD_CHK_POWER;
-
- task.tf_flags |= IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
- task.rq = rq;
- drive->hwif->data_phase = task.data_phase = TASKFILE_NO_DATA;
- return do_rw_taskfile(drive, &task);
- }
-
switch (cmd) {
+ case REQ_PARK_HEADS:
+ case REQ_UNPARK_HEADS:
+ return ide_do_park_unpark(drive, rq);
case REQ_DEVSET_EXEC:
- {
- int err, (*setfunc)(ide_drive_t *, int) = rq->special;
-
- err = setfunc(drive, *(int *)&rq->cmd[1]);
- if (err)
- rq->errors = err;
- else
- err = 1;
- ide_end_request(drive, err, 0);
- return ide_stopped;
- }
+ return ide_do_devset(drive, rq);
case REQ_DRIVE_RESET:
return ide_do_reset(drive);
default:
@@ -820,63 +638,6 @@
blk_plug_device(q);
}
-/*
- * un-busy the port etc, and clear any pending DMA status. we want to
- * retry the current request in pio mode instead of risking tossing it
- * all away
- */
-static ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error)
-{
- ide_hwif_t *hwif = drive->hwif;
- struct request *rq;
- ide_startstop_t ret = ide_stopped;
-
- /*
- * end current dma transaction
- */
-
- if (error < 0) {
- printk(KERN_WARNING "%s: DMA timeout error\n", drive->name);
- (void)hwif->dma_ops->dma_end(drive);
- ret = ide_error(drive, "dma timeout error",
- hwif->tp_ops->read_status(hwif));
- } else {
- printk(KERN_WARNING "%s: DMA timeout retry\n", drive->name);
- hwif->dma_ops->dma_timeout(drive);
- }
-
- /*
- * disable dma for now, but remember that we did so because of
- * a timeout -- we'll reenable after we finish this next request
- * (or rather the first chunk of it) in pio.
- */
- drive->dev_flags |= IDE_DFLAG_DMA_PIO_RETRY;
- drive->retry_pio++;
- ide_dma_off_quietly(drive);
-
- /*
- * un-busy drive etc and make sure request is sane
- */
-
- rq = hwif->rq;
- if (!rq)
- goto out;
-
- hwif->rq = NULL;
-
- rq->errors = 0;
-
- if (!rq->bio)
- goto out;
-
- rq->sector = rq->bio->bi_sector;
- rq->current_nr_sectors = bio_iovec(rq->bio)->bv_len >> 9;
- rq->hard_cur_sectors = rq->current_nr_sectors;
- rq->buffer = bio_data(rq->bio);
-out:
- return ret;
-}
-
static void ide_plug_device(ide_drive_t *drive)
{
struct request_queue *q = drive->queue;
@@ -888,6 +649,29 @@
spin_unlock_irqrestore(q->queue_lock, flags);
}
+static int drive_is_ready(ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ u8 stat = 0;
+
+ if (drive->waiting_for_dma)
+ return hwif->dma_ops->dma_test_irq(drive);
+
+ if (hwif->io_ports.ctl_addr &&
+ (hwif->host_flags & IDE_HFLAG_BROKEN_ALTSTATUS) == 0)
+ stat = hwif->tp_ops->read_altstatus(hwif);
+ else
+ /* Note: this may clear a pending IRQ!! */
+ stat = hwif->tp_ops->read_status(hwif);
+
+ if (stat & ATA_BUSY)
+ /* drive busy: definitely not interrupting */
+ return 0;
+
+ /* drive ready: *might* be interrupting */
+ return 1;
+}
+
/**
* ide_timer_expiry - handle lack of an IDE interrupt
* @data: timer callback magic (hwif)
@@ -1164,54 +948,6 @@
}
EXPORT_SYMBOL_GPL(ide_intr);
-/**
- * ide_do_drive_cmd - issue IDE special command
- * @drive: device to issue command
- * @rq: request to issue
- *
- * This function issues a special IDE device request
- * onto the request queue.
- *
- * the rq is queued at the head of the request queue, displacing
- * the currently-being-processed request and this function
- * returns immediately without waiting for the new rq to be
- * completed. This is VERY DANGEROUS, and is intended for
- * careful use by the ATAPI tape/cdrom driver code.
- */
-
-void ide_do_drive_cmd(ide_drive_t *drive, struct request *rq)
-{
- struct request_queue *q = drive->queue;
- unsigned long flags;
-
- drive->hwif->rq = NULL;
-
- spin_lock_irqsave(q->queue_lock, flags);
- __elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 0);
- spin_unlock_irqrestore(q->queue_lock, flags);
-}
-EXPORT_SYMBOL(ide_do_drive_cmd);
-
-void ide_pktcmd_tf_load(ide_drive_t *drive, u32 tf_flags, u16 bcount, u8 dma)
-{
- ide_hwif_t *hwif = drive->hwif;
- ide_task_t task;
-
- memset(&task, 0, sizeof(task));
- task.tf_flags = IDE_TFLAG_OUT_LBAH | IDE_TFLAG_OUT_LBAM |
- IDE_TFLAG_OUT_FEATURE | tf_flags;
- task.tf.feature = dma; /* Use PIO/DMA */
- task.tf.lbam = bcount & 0xff;
- task.tf.lbah = (bcount >> 8) & 0xff;
-
- ide_tf_dump(drive->name, &task.tf);
- hwif->tp_ops->set_irq(hwif, 1);
- SELECT_MASK(drive, 0);
- hwif->tp_ops->tf_load(drive, &task);
-}
-
-EXPORT_SYMBOL_GPL(ide_pktcmd_tf_load);
-
void ide_pad_transfer(ide_drive_t *drive, int write, int len)
{
ide_hwif_t *hwif = drive->hwif;
diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c
index b1892bd..317c5da 100644
--- a/drivers/ide/ide-iops.c
+++ b/drivers/ide/ide-iops.c
@@ -27,35 +27,7 @@
#include <asm/uaccess.h>
#include <asm/io.h>
-/*
- * Conventional PIO operations for ATA devices
- */
-
-static u8 ide_inb (unsigned long port)
-{
- return (u8) inb(port);
-}
-
-static void ide_outb (u8 val, unsigned long port)
-{
- outb(val, port);
-}
-
-/*
- * MMIO operations, typically used for SATA controllers
- */
-
-static u8 ide_mm_inb (unsigned long port)
-{
- return (u8) readb((void __iomem *) port);
-}
-
-static void ide_mm_outb (u8 value, unsigned long port)
-{
- writeb(value, (void __iomem *) port);
-}
-
-void SELECT_DRIVE (ide_drive_t *drive)
+void SELECT_DRIVE(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
const struct ide_port_ops *port_ops = hwif->port_ops;
@@ -78,277 +50,6 @@
port_ops->maskproc(drive, mask);
}
-void ide_exec_command(ide_hwif_t *hwif, u8 cmd)
-{
- if (hwif->host_flags & IDE_HFLAG_MMIO)
- writeb(cmd, (void __iomem *)hwif->io_ports.command_addr);
- else
- outb(cmd, hwif->io_ports.command_addr);
-}
-EXPORT_SYMBOL_GPL(ide_exec_command);
-
-u8 ide_read_status(ide_hwif_t *hwif)
-{
- if (hwif->host_flags & IDE_HFLAG_MMIO)
- return readb((void __iomem *)hwif->io_ports.status_addr);
- else
- return inb(hwif->io_ports.status_addr);
-}
-EXPORT_SYMBOL_GPL(ide_read_status);
-
-u8 ide_read_altstatus(ide_hwif_t *hwif)
-{
- if (hwif->host_flags & IDE_HFLAG_MMIO)
- return readb((void __iomem *)hwif->io_ports.ctl_addr);
- else
- return inb(hwif->io_ports.ctl_addr);
-}
-EXPORT_SYMBOL_GPL(ide_read_altstatus);
-
-void ide_set_irq(ide_hwif_t *hwif, int on)
-{
- u8 ctl = ATA_DEVCTL_OBS;
-
- if (on == 4) { /* hack for SRST */
- ctl |= 4;
- on &= ~4;
- }
-
- ctl |= on ? 0 : 2;
-
- if (hwif->host_flags & IDE_HFLAG_MMIO)
- writeb(ctl, (void __iomem *)hwif->io_ports.ctl_addr);
- else
- outb(ctl, hwif->io_ports.ctl_addr);
-}
-EXPORT_SYMBOL_GPL(ide_set_irq);
-
-void ide_tf_load(ide_drive_t *drive, ide_task_t *task)
-{
- ide_hwif_t *hwif = drive->hwif;
- struct ide_io_ports *io_ports = &hwif->io_ports;
- struct ide_taskfile *tf = &task->tf;
- void (*tf_outb)(u8 addr, unsigned long port);
- u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
- u8 HIHI = (task->tf_flags & IDE_TFLAG_LBA48) ? 0xE0 : 0xEF;
-
- if (mmio)
- tf_outb = ide_mm_outb;
- else
- tf_outb = ide_outb;
-
- if (task->tf_flags & IDE_TFLAG_FLAGGED)
- HIHI = 0xFF;
-
- if (task->tf_flags & IDE_TFLAG_OUT_DATA) {
- u16 data = (tf->hob_data << 8) | tf->data;
-
- if (mmio)
- writew(data, (void __iomem *)io_ports->data_addr);
- else
- outw(data, io_ports->data_addr);
- }
-
- if (task->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE)
- tf_outb(tf->hob_feature, io_ports->feature_addr);
- if (task->tf_flags & IDE_TFLAG_OUT_HOB_NSECT)
- tf_outb(tf->hob_nsect, io_ports->nsect_addr);
- if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAL)
- tf_outb(tf->hob_lbal, io_ports->lbal_addr);
- if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAM)
- tf_outb(tf->hob_lbam, io_ports->lbam_addr);
- if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAH)
- tf_outb(tf->hob_lbah, io_ports->lbah_addr);
-
- if (task->tf_flags & IDE_TFLAG_OUT_FEATURE)
- tf_outb(tf->feature, io_ports->feature_addr);
- if (task->tf_flags & IDE_TFLAG_OUT_NSECT)
- tf_outb(tf->nsect, io_ports->nsect_addr);
- if (task->tf_flags & IDE_TFLAG_OUT_LBAL)
- tf_outb(tf->lbal, io_ports->lbal_addr);
- if (task->tf_flags & IDE_TFLAG_OUT_LBAM)
- tf_outb(tf->lbam, io_ports->lbam_addr);
- if (task->tf_flags & IDE_TFLAG_OUT_LBAH)
- tf_outb(tf->lbah, io_ports->lbah_addr);
-
- if (task->tf_flags & IDE_TFLAG_OUT_DEVICE)
- tf_outb((tf->device & HIHI) | drive->select,
- io_ports->device_addr);
-}
-EXPORT_SYMBOL_GPL(ide_tf_load);
-
-void ide_tf_read(ide_drive_t *drive, ide_task_t *task)
-{
- ide_hwif_t *hwif = drive->hwif;
- struct ide_io_ports *io_ports = &hwif->io_ports;
- struct ide_taskfile *tf = &task->tf;
- void (*tf_outb)(u8 addr, unsigned long port);
- u8 (*tf_inb)(unsigned long port);
- u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
-
- if (mmio) {
- tf_outb = ide_mm_outb;
- tf_inb = ide_mm_inb;
- } else {
- tf_outb = ide_outb;
- tf_inb = ide_inb;
- }
-
- if (task->tf_flags & IDE_TFLAG_IN_DATA) {
- u16 data;
-
- if (mmio)
- data = readw((void __iomem *)io_ports->data_addr);
- else
- data = inw(io_ports->data_addr);
-
- tf->data = data & 0xff;
- tf->hob_data = (data >> 8) & 0xff;
- }
-
- /* be sure we're looking at the low order bits */
- tf_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr);
-
- if (task->tf_flags & IDE_TFLAG_IN_FEATURE)
- tf->feature = tf_inb(io_ports->feature_addr);
- if (task->tf_flags & IDE_TFLAG_IN_NSECT)
- tf->nsect = tf_inb(io_ports->nsect_addr);
- if (task->tf_flags & IDE_TFLAG_IN_LBAL)
- tf->lbal = tf_inb(io_ports->lbal_addr);
- if (task->tf_flags & IDE_TFLAG_IN_LBAM)
- tf->lbam = tf_inb(io_ports->lbam_addr);
- if (task->tf_flags & IDE_TFLAG_IN_LBAH)
- tf->lbah = tf_inb(io_ports->lbah_addr);
- if (task->tf_flags & IDE_TFLAG_IN_DEVICE)
- tf->device = tf_inb(io_ports->device_addr);
-
- if (task->tf_flags & IDE_TFLAG_LBA48) {
- tf_outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr);
-
- if (task->tf_flags & IDE_TFLAG_IN_HOB_FEATURE)
- tf->hob_feature = tf_inb(io_ports->feature_addr);
- if (task->tf_flags & IDE_TFLAG_IN_HOB_NSECT)
- tf->hob_nsect = tf_inb(io_ports->nsect_addr);
- if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAL)
- tf->hob_lbal = tf_inb(io_ports->lbal_addr);
- if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAM)
- tf->hob_lbam = tf_inb(io_ports->lbam_addr);
- if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAH)
- tf->hob_lbah = tf_inb(io_ports->lbah_addr);
- }
-}
-EXPORT_SYMBOL_GPL(ide_tf_read);
-
-/*
- * Some localbus EIDE interfaces require a special access sequence
- * when using 32-bit I/O instructions to transfer data. We call this
- * the "vlb_sync" sequence, which consists of three successive reads
- * of the sector count register location, with interrupts disabled
- * to ensure that the reads all happen together.
- */
-static void ata_vlb_sync(unsigned long port)
-{
- (void)inb(port);
- (void)inb(port);
- (void)inb(port);
-}
-
-/*
- * This is used for most PIO data transfers *from* the IDE interface
- *
- * These routines will round up any request for an odd number of bytes,
- * so if an odd len is specified, be sure that there's at least one
- * extra byte allocated for the buffer.
- */
-void ide_input_data(ide_drive_t *drive, struct request *rq, void *buf,
- unsigned int len)
-{
- ide_hwif_t *hwif = drive->hwif;
- struct ide_io_ports *io_ports = &hwif->io_ports;
- unsigned long data_addr = io_ports->data_addr;
- u8 io_32bit = drive->io_32bit;
- u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
-
- len++;
-
- if (io_32bit) {
- unsigned long uninitialized_var(flags);
-
- if ((io_32bit & 2) && !mmio) {
- local_irq_save(flags);
- ata_vlb_sync(io_ports->nsect_addr);
- }
-
- if (mmio)
- __ide_mm_insl((void __iomem *)data_addr, buf, len / 4);
- else
- insl(data_addr, buf, len / 4);
-
- if ((io_32bit & 2) && !mmio)
- local_irq_restore(flags);
-
- if ((len & 3) >= 2) {
- if (mmio)
- __ide_mm_insw((void __iomem *)data_addr,
- (u8 *)buf + (len & ~3), 1);
- else
- insw(data_addr, (u8 *)buf + (len & ~3), 1);
- }
- } else {
- if (mmio)
- __ide_mm_insw((void __iomem *)data_addr, buf, len / 2);
- else
- insw(data_addr, buf, len / 2);
- }
-}
-EXPORT_SYMBOL_GPL(ide_input_data);
-
-/*
- * This is used for most PIO data transfers *to* the IDE interface
- */
-void ide_output_data(ide_drive_t *drive, struct request *rq, void *buf,
- unsigned int len)
-{
- ide_hwif_t *hwif = drive->hwif;
- struct ide_io_ports *io_ports = &hwif->io_ports;
- unsigned long data_addr = io_ports->data_addr;
- u8 io_32bit = drive->io_32bit;
- u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
-
- len++;
-
- if (io_32bit) {
- unsigned long uninitialized_var(flags);
-
- if ((io_32bit & 2) && !mmio) {
- local_irq_save(flags);
- ata_vlb_sync(io_ports->nsect_addr);
- }
-
- if (mmio)
- __ide_mm_outsl((void __iomem *)data_addr, buf, len / 4);
- else
- outsl(data_addr, buf, len / 4);
-
- if ((io_32bit & 2) && !mmio)
- local_irq_restore(flags);
-
- if ((len & 3) >= 2) {
- if (mmio)
- __ide_mm_outsw((void __iomem *)data_addr,
- (u8 *)buf + (len & ~3), 1);
- else
- outsw(data_addr, (u8 *)buf + (len & ~3), 1);
- }
- } else {
- if (mmio)
- __ide_mm_outsw((void __iomem *)data_addr, buf, len / 2);
- else
- outsw(data_addr, buf, len / 2);
- }
-}
-EXPORT_SYMBOL_GPL(ide_output_data);
-
u8 ide_read_error(ide_drive_t *drive)
{
ide_task_t task;
@@ -362,35 +63,6 @@
}
EXPORT_SYMBOL_GPL(ide_read_error);
-void ide_read_bcount_and_ireason(ide_drive_t *drive, u16 *bcount, u8 *ireason)
-{
- ide_task_t task;
-
- memset(&task, 0, sizeof(task));
- task.tf_flags = IDE_TFLAG_IN_LBAH | IDE_TFLAG_IN_LBAM |
- IDE_TFLAG_IN_NSECT;
-
- drive->hwif->tp_ops->tf_read(drive, &task);
-
- *bcount = (task.tf.lbah << 8) | task.tf.lbam;
- *ireason = task.tf.nsect & 3;
-}
-EXPORT_SYMBOL_GPL(ide_read_bcount_and_ireason);
-
-const struct ide_tp_ops default_tp_ops = {
- .exec_command = ide_exec_command,
- .read_status = ide_read_status,
- .read_altstatus = ide_read_altstatus,
-
- .set_irq = ide_set_irq,
-
- .tf_load = ide_tf_load,
- .tf_read = ide_tf_read,
-
- .input_data = ide_input_data,
- .output_data = ide_output_data,
-};
-
void ide_fix_driveid(u16 *id)
{
#ifndef __LITTLE_ENDIAN
@@ -412,7 +84,7 @@
* returned by the ATA_CMD_ID_ATA[PI] commands.
*/
-void ide_fixstring (u8 *s, const int bytecount, const int byteswap)
+void ide_fixstring(u8 *s, const int bytecount, const int byteswap)
{
u8 *p, *end = &s[bytecount & ~1]; /* bytecount must be even */
@@ -435,44 +107,9 @@
while (p != end)
*p++ = '\0';
}
-
EXPORT_SYMBOL(ide_fixstring);
/*
- * Needed for PCI irq sharing
- */
-int drive_is_ready (ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- u8 stat = 0;
-
- if (drive->waiting_for_dma)
- return hwif->dma_ops->dma_test_irq(drive);
-
- /*
- * We do a passive status test under shared PCI interrupts on
- * cards that truly share the ATA side interrupt, but may also share
- * an interrupt with another pci card/device. We make no assumptions
- * about possible isa-pnp and pci-pnp issues yet.
- */
- if (hwif->io_ports.ctl_addr &&
- (hwif->host_flags & IDE_HFLAG_BROKEN_ALTSTATUS) == 0)
- stat = hwif->tp_ops->read_altstatus(hwif);
- else
- /* Note: this may clear a pending IRQ!! */
- stat = hwif->tp_ops->read_status(hwif);
-
- if (stat & ATA_BUSY)
- /* drive busy: definitely not interrupting */
- return 0;
-
- /* drive ready: *might* be interrupting */
- return 1;
-}
-
-EXPORT_SYMBOL(drive_is_ready);
-
-/*
* This routine busy-waits for the drive status to be not "busy".
* It then checks the status for all of the "good" bits and none
* of the "bad" bits, and if all is okay it returns 0. All other
@@ -483,7 +120,8 @@
* setting a timer to wake up at half second intervals thereafter,
* until timeout is achieved, before timing out.
*/
-static int __ide_wait_stat(ide_drive_t *drive, u8 good, u8 bad, unsigned long timeout, u8 *rstat)
+static int __ide_wait_stat(ide_drive_t *drive, u8 good, u8 bad,
+ unsigned long timeout, u8 *rstat)
{
ide_hwif_t *hwif = drive->hwif;
const struct ide_tp_ops *tp_ops = hwif->tp_ops;
@@ -541,7 +179,8 @@
* The caller should return the updated value of "startstop" in this case,
* "startstop" is unchanged when the function returns 0.
*/
-int ide_wait_stat(ide_startstop_t *startstop, ide_drive_t *drive, u8 good, u8 bad, unsigned long timeout)
+int ide_wait_stat(ide_startstop_t *startstop, ide_drive_t *drive, u8 good,
+ u8 bad, unsigned long timeout)
{
int err;
u8 stat;
@@ -561,7 +200,6 @@
return err;
}
-
EXPORT_SYMBOL(ide_wait_stat);
/**
@@ -582,7 +220,6 @@
return 1;
return 0;
}
-
EXPORT_SYMBOL_GPL(ide_in_drive_list);
/*
@@ -607,7 +244,7 @@
* All hosts that use the 80c ribbon must use!
* The name is derived from upper byte of word 93 and the 80c ribbon.
*/
-u8 eighty_ninty_three (ide_drive_t *drive)
+u8 eighty_ninty_three(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
u16 *id = drive->id;
@@ -652,47 +289,19 @@
int ide_driveid_update(ide_drive_t *drive)
{
- ide_hwif_t *hwif = drive->hwif;
- const struct ide_tp_ops *tp_ops = hwif->tp_ops;
u16 *id;
- unsigned long flags;
- u8 stat;
+ int rc;
- /*
- * Re-read drive->id for possible DMA mode
- * change (copied from ide-probe.c)
- */
+ id = kmalloc(SECTOR_SIZE, GFP_ATOMIC);
+ if (id == NULL)
+ return 0;
SELECT_MASK(drive, 1);
- tp_ops->set_irq(hwif, 0);
- msleep(50);
- tp_ops->exec_command(hwif, ATA_CMD_ID_ATA);
-
- if (ide_busy_sleep(hwif, WAIT_WORSTCASE, 1)) {
- SELECT_MASK(drive, 0);
- return 0;
- }
-
- msleep(50); /* wait for IRQ and ATA_DRQ */
- stat = tp_ops->read_status(hwif);
-
- if (!OK_STAT(stat, ATA_DRQ, BAD_R_STAT)) {
- SELECT_MASK(drive, 0);
- printk("%s: CHECK for good STATUS\n", drive->name);
- return 0;
- }
- local_irq_save(flags);
+ rc = ide_dev_read_id(drive, ATA_CMD_ID_ATA, id);
SELECT_MASK(drive, 0);
- id = kmalloc(SECTOR_SIZE, GFP_ATOMIC);
- if (!id) {
- local_irq_restore(flags);
- return 0;
- }
- tp_ops->input_data(drive, NULL, id, SECTOR_SIZE);
- (void)tp_ops->read_status(hwif); /* clear drive IRQ */
- local_irq_enable();
- local_irq_restore(flags);
- ide_fix_driveid(id);
+
+ if (rc)
+ goto out_err;
drive->id[ATA_ID_UDMA_MODES] = id[ATA_ID_UDMA_MODES];
drive->id[ATA_ID_MWDMA_MODES] = id[ATA_ID_MWDMA_MODES];
@@ -705,6 +314,12 @@
ide_dma_off(drive);
return 1;
+out_err:
+ SELECT_MASK(drive, 0);
+ if (rc == 2)
+ printk(KERN_ERR "%s: %s: bad status\n", drive->name, __func__);
+ kfree(id);
+ return 0;
}
int ide_config_drive_speed(ide_drive_t *drive, u8 speed)
@@ -731,18 +346,15 @@
* but for some reason these don't work at
* this point (lost interrupt).
*/
- /*
- * Select the drive, and issue the SETFEATURES command
- */
- disable_irq_nosync(hwif->irq);
-
+
/*
* FIXME: we race against the running IRQ here if
* this is called from non IRQ context. If we use
* disable_irq() we hang on the error path. Work
* is needed.
*/
-
+ disable_irq_nosync(hwif->irq);
+
udelay(1);
SELECT_DRIVE(drive);
SELECT_MASK(drive, 1);
@@ -812,8 +424,8 @@
*
* See also ide_execute_command
*/
-static void __ide_set_handler (ide_drive_t *drive, ide_handler_t *handler,
- unsigned int timeout, ide_expiry_t *expiry)
+void __ide_set_handler(ide_drive_t *drive, ide_handler_t *handler,
+ unsigned int timeout, ide_expiry_t *expiry)
{
ide_hwif_t *hwif = drive->hwif;
@@ -835,9 +447,8 @@
__ide_set_handler(drive, handler, timeout, expiry);
spin_unlock_irqrestore(&hwif->lock, flags);
}
-
EXPORT_SYMBOL(ide_set_handler);
-
+
/**
* ide_execute_command - execute an IDE command
* @drive: IDE drive to issue the command against
@@ -847,7 +458,7 @@
* @expiry: handler to run on timeout
*
* Helper function to issue an IDE command. This handles the
- * atomicity requirements, command timing and ensures that the
+ * atomicity requirements, command timing and ensures that the
* handler and IRQ setup do not race. All IDE command kick off
* should go via this function or do equivalent locking.
*/
@@ -884,301 +495,6 @@
}
EXPORT_SYMBOL_GPL(ide_execute_pkt_cmd);
-static inline void ide_complete_drive_reset(ide_drive_t *drive, int err)
-{
- struct request *rq = drive->hwif->rq;
-
- if (rq && blk_special_request(rq) && rq->cmd[0] == REQ_DRIVE_RESET)
- ide_end_request(drive, err ? err : 1, 0);
-}
-
-/* needed below */
-static ide_startstop_t do_reset1 (ide_drive_t *, int);
-
-/*
- * atapi_reset_pollfunc() gets invoked to poll the interface for completion every 50ms
- * during an atapi drive reset operation. If the drive has not yet responded,
- * and we have not yet hit our maximum waiting time, then the timer is restarted
- * for another 50ms.
- */
-static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- u8 stat;
-
- SELECT_DRIVE(drive);
- udelay (10);
- stat = hwif->tp_ops->read_status(hwif);
-
- if (OK_STAT(stat, 0, ATA_BUSY))
- printk("%s: ATAPI reset complete\n", drive->name);
- else {
- if (time_before(jiffies, hwif->poll_timeout)) {
- ide_set_handler(drive, &atapi_reset_pollfunc, HZ/20, NULL);
- /* continue polling */
- return ide_started;
- }
- /* end of polling */
- hwif->polling = 0;
- printk("%s: ATAPI reset timed-out, status=0x%02x\n",
- drive->name, stat);
- /* do it the old fashioned way */
- return do_reset1(drive, 1);
- }
- /* done polling */
- hwif->polling = 0;
- ide_complete_drive_reset(drive, 0);
- return ide_stopped;
-}
-
-static void ide_reset_report_error(ide_hwif_t *hwif, u8 err)
-{
- static const char *err_master_vals[] =
- { NULL, "passed", "formatter device error",
- "sector buffer error", "ECC circuitry error",
- "controlling MPU error" };
-
- u8 err_master = err & 0x7f;
-
- printk(KERN_ERR "%s: reset: master: ", hwif->name);
- if (err_master && err_master < 6)
- printk(KERN_CONT "%s", err_master_vals[err_master]);
- else
- printk(KERN_CONT "error (0x%02x?)", err);
- if (err & 0x80)
- printk(KERN_CONT "; slave: failed");
- printk(KERN_CONT "\n");
-}
-
-/*
- * reset_pollfunc() gets invoked to poll the interface for completion every 50ms
- * during an ide reset operation. If the drives have not yet responded,
- * and we have not yet hit our maximum waiting time, then the timer is restarted
- * for another 50ms.
- */
-static ide_startstop_t reset_pollfunc (ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- const struct ide_port_ops *port_ops = hwif->port_ops;
- u8 tmp;
- int err = 0;
-
- if (port_ops && port_ops->reset_poll) {
- err = port_ops->reset_poll(drive);
- if (err) {
- printk(KERN_ERR "%s: host reset_poll failure for %s.\n",
- hwif->name, drive->name);
- goto out;
- }
- }
-
- tmp = hwif->tp_ops->read_status(hwif);
-
- if (!OK_STAT(tmp, 0, ATA_BUSY)) {
- if (time_before(jiffies, hwif->poll_timeout)) {
- ide_set_handler(drive, &reset_pollfunc, HZ/20, NULL);
- /* continue polling */
- return ide_started;
- }
- printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp);
- drive->failures++;
- err = -EIO;
- } else {
- tmp = ide_read_error(drive);
-
- if (tmp == 1) {
- printk(KERN_INFO "%s: reset: success\n", hwif->name);
- drive->failures = 0;
- } else {
- ide_reset_report_error(hwif, tmp);
- drive->failures++;
- err = -EIO;
- }
- }
-out:
- hwif->polling = 0; /* done polling */
- ide_complete_drive_reset(drive, err);
- return ide_stopped;
-}
-
-static void ide_disk_pre_reset(ide_drive_t *drive)
-{
- int legacy = (drive->id[ATA_ID_CFS_ENABLE_2] & 0x0400) ? 0 : 1;
-
- drive->special.all = 0;
- drive->special.b.set_geometry = legacy;
- drive->special.b.recalibrate = legacy;
-
- drive->mult_count = 0;
- drive->dev_flags &= ~IDE_DFLAG_PARKED;
-
- if ((drive->dev_flags & IDE_DFLAG_KEEP_SETTINGS) == 0 &&
- (drive->dev_flags & IDE_DFLAG_USING_DMA) == 0)
- drive->mult_req = 0;
-
- if (drive->mult_req != drive->mult_count)
- drive->special.b.set_multmode = 1;
-}
-
-static void pre_reset(ide_drive_t *drive)
-{
- const struct ide_port_ops *port_ops = drive->hwif->port_ops;
-
- if (drive->media == ide_disk)
- ide_disk_pre_reset(drive);
- else
- drive->dev_flags |= IDE_DFLAG_POST_RESET;
-
- if (drive->dev_flags & IDE_DFLAG_USING_DMA) {
- if (drive->crc_count)
- ide_check_dma_crc(drive);
- else
- ide_dma_off(drive);
- }
-
- if ((drive->dev_flags & IDE_DFLAG_KEEP_SETTINGS) == 0) {
- if ((drive->dev_flags & IDE_DFLAG_USING_DMA) == 0) {
- drive->dev_flags &= ~IDE_DFLAG_UNMASK;
- drive->io_32bit = 0;
- }
- return;
- }
-
- if (port_ops && port_ops->pre_reset)
- port_ops->pre_reset(drive);
-
- if (drive->current_speed != 0xff)
- drive->desired_speed = drive->current_speed;
- drive->current_speed = 0xff;
-}
-
-/*
- * do_reset1() attempts to recover a confused drive by resetting it.
- * Unfortunately, resetting a disk drive actually resets all devices on
- * the same interface, so it can really be thought of as resetting the
- * interface rather than resetting the drive.
- *
- * ATAPI devices have their own reset mechanism which allows them to be
- * individually reset without clobbering other devices on the same interface.
- *
- * Unfortunately, the IDE interface does not generate an interrupt to let
- * us know when the reset operation has finished, so we must poll for this.
- * Equally poor, though, is the fact that this may a very long time to complete,
- * (up to 30 seconds worstcase). So, instead of busy-waiting here for it,
- * we set a timer to poll at 50ms intervals.
- */
-static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
-{
- ide_hwif_t *hwif = drive->hwif;
- struct ide_io_ports *io_ports = &hwif->io_ports;
- const struct ide_tp_ops *tp_ops = hwif->tp_ops;
- const struct ide_port_ops *port_ops;
- ide_drive_t *tdrive;
- unsigned long flags, timeout;
- int i;
- DEFINE_WAIT(wait);
-
- spin_lock_irqsave(&hwif->lock, flags);
-
- /* We must not reset with running handlers */
- BUG_ON(hwif->handler != NULL);
-
- /* For an ATAPI device, first try an ATAPI SRST. */
- if (drive->media != ide_disk && !do_not_try_atapi) {
- pre_reset(drive);
- SELECT_DRIVE(drive);
- udelay (20);
- tp_ops->exec_command(hwif, ATA_CMD_DEV_RESET);
- ndelay(400);
- hwif->poll_timeout = jiffies + WAIT_WORSTCASE;
- hwif->polling = 1;
- __ide_set_handler(drive, &atapi_reset_pollfunc, HZ/20, NULL);
- spin_unlock_irqrestore(&hwif->lock, flags);
- return ide_started;
- }
-
- /* We must not disturb devices in the IDE_DFLAG_PARKED state. */
- do {
- unsigned long now;
-
- prepare_to_wait(&ide_park_wq, &wait, TASK_UNINTERRUPTIBLE);
- timeout = jiffies;
- ide_port_for_each_dev(i, tdrive, hwif) {
- if (tdrive->dev_flags & IDE_DFLAG_PRESENT &&
- tdrive->dev_flags & IDE_DFLAG_PARKED &&
- time_after(tdrive->sleep, timeout))
- timeout = tdrive->sleep;
- }
-
- now = jiffies;
- if (time_before_eq(timeout, now))
- break;
-
- spin_unlock_irqrestore(&hwif->lock, flags);
- timeout = schedule_timeout_uninterruptible(timeout - now);
- spin_lock_irqsave(&hwif->lock, flags);
- } while (timeout);
- finish_wait(&ide_park_wq, &wait);
-
- /*
- * First, reset any device state data we were maintaining
- * for any of the drives on this interface.
- */
- ide_port_for_each_dev(i, tdrive, hwif)
- pre_reset(tdrive);
-
- if (io_ports->ctl_addr == 0) {
- spin_unlock_irqrestore(&hwif->lock, flags);
- ide_complete_drive_reset(drive, -ENXIO);
- return ide_stopped;
- }
-
- /*
- * Note that we also set nIEN while resetting the device,
- * to mask unwanted interrupts from the interface during the reset.
- * However, due to the design of PC hardware, this will cause an
- * immediate interrupt due to the edge transition it produces.
- * This single interrupt gives us a "fast poll" for drives that
- * recover from reset very quickly, saving us the first 50ms wait time.
- *
- * TODO: add ->softreset method and stop abusing ->set_irq
- */
- /* set SRST and nIEN */
- tp_ops->set_irq(hwif, 4);
- /* more than enough time */
- udelay(10);
- /* clear SRST, leave nIEN (unless device is on the quirk list) */
- tp_ops->set_irq(hwif, drive->quirk_list == 2);
- /* more than enough time */
- udelay(10);
- hwif->poll_timeout = jiffies + WAIT_WORSTCASE;
- hwif->polling = 1;
- __ide_set_handler(drive, &reset_pollfunc, HZ/20, NULL);
-
- /*
- * Some weird controller like resetting themselves to a strange
- * state when the disks are reset this way. At least, the Winbond
- * 553 documentation says that
- */
- port_ops = hwif->port_ops;
- if (port_ops && port_ops->resetproc)
- port_ops->resetproc(drive);
-
- spin_unlock_irqrestore(&hwif->lock, flags);
- return ide_started;
-}
-
-/*
- * ide_do_reset() is the entry point to the drive/interface reset code.
- */
-
-ide_startstop_t ide_do_reset (ide_drive_t *drive)
-{
- return do_reset1(drive, 0);
-}
-
-EXPORT_SYMBOL(ide_do_reset);
-
/*
* ide_wait_not_busy() waits for the currently selected device on the hwif
* to report a non-busy status, see comments in ide_probe_port().
@@ -1187,7 +503,7 @@
{
u8 stat = 0;
- while(timeout--) {
+ while (timeout--) {
/*
* Turn this into a schedule() sleep once I'm sure
* about locking issues (2.5 work ?).
diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c
index 09526a0..f6c683d 100644
--- a/drivers/ide/ide-lib.c
+++ b/drivers/ide/ide-lib.c
@@ -5,163 +5,6 @@
#include <linux/ide.h>
#include <linux/bitops.h>
-static const char *udma_str[] =
- { "UDMA/16", "UDMA/25", "UDMA/33", "UDMA/44",
- "UDMA/66", "UDMA/100", "UDMA/133", "UDMA7" };
-static const char *mwdma_str[] =
- { "MWDMA0", "MWDMA1", "MWDMA2" };
-static const char *swdma_str[] =
- { "SWDMA0", "SWDMA1", "SWDMA2" };
-static const char *pio_str[] =
- { "PIO0", "PIO1", "PIO2", "PIO3", "PIO4", "PIO5" };
-
-/**
- * ide_xfer_verbose - return IDE mode names
- * @mode: transfer mode
- *
- * Returns a constant string giving the name of the mode
- * requested.
- */
-
-const char *ide_xfer_verbose(u8 mode)
-{
- const char *s;
- u8 i = mode & 0xf;
-
- if (mode >= XFER_UDMA_0 && mode <= XFER_UDMA_7)
- s = udma_str[i];
- else if (mode >= XFER_MW_DMA_0 && mode <= XFER_MW_DMA_2)
- s = mwdma_str[i];
- else if (mode >= XFER_SW_DMA_0 && mode <= XFER_SW_DMA_2)
- s = swdma_str[i];
- else if (mode >= XFER_PIO_0 && mode <= XFER_PIO_5)
- s = pio_str[i & 0x7];
- else if (mode == XFER_PIO_SLOW)
- s = "PIO SLOW";
- else
- s = "XFER ERROR";
-
- return s;
-}
-EXPORT_SYMBOL(ide_xfer_verbose);
-
-/**
- * ide_rate_filter - filter transfer mode
- * @drive: IDE device
- * @speed: desired speed
- *
- * Given the available transfer modes this function returns
- * the best available speed at or below the speed requested.
- *
- * TODO: check device PIO capabilities
- */
-
-static u8 ide_rate_filter(ide_drive_t *drive, u8 speed)
-{
- ide_hwif_t *hwif = drive->hwif;
- u8 mode = ide_find_dma_mode(drive, speed);
-
- if (mode == 0) {
- if (hwif->pio_mask)
- mode = fls(hwif->pio_mask) - 1 + XFER_PIO_0;
- else
- mode = XFER_PIO_4;
- }
-
-/* printk("%s: mode 0x%02x, speed 0x%02x\n", __func__, mode, speed); */
-
- return min(speed, mode);
-}
-
-/**
- * ide_get_best_pio_mode - get PIO mode from drive
- * @drive: drive to consider
- * @mode_wanted: preferred mode
- * @max_mode: highest allowed mode
- *
- * This routine returns the recommended PIO settings for a given drive,
- * based on the drive->id information and the ide_pio_blacklist[].
- *
- * Drive PIO mode is auto-selected if 255 is passed as mode_wanted.
- * This is used by most chipset support modules when "auto-tuning".
- */
-
-u8 ide_get_best_pio_mode(ide_drive_t *drive, u8 mode_wanted, u8 max_mode)
-{
- u16 *id = drive->id;
- int pio_mode = -1, overridden = 0;
-
- if (mode_wanted != 255)
- return min_t(u8, mode_wanted, max_mode);
-
- if ((drive->hwif->host_flags & IDE_HFLAG_PIO_NO_BLACKLIST) == 0)
- pio_mode = ide_scan_pio_blacklist((char *)&id[ATA_ID_PROD]);
-
- if (pio_mode != -1) {
- printk(KERN_INFO "%s: is on PIO blacklist\n", drive->name);
- } else {
- pio_mode = id[ATA_ID_OLD_PIO_MODES] >> 8;
- if (pio_mode > 2) { /* 2 is maximum allowed tPIO value */
- pio_mode = 2;
- overridden = 1;
- }
-
- if (id[ATA_ID_FIELD_VALID] & 2) { /* ATA2? */
- if (ata_id_has_iordy(id)) {
- if (id[ATA_ID_PIO_MODES] & 7) {
- overridden = 0;
- if (id[ATA_ID_PIO_MODES] & 4)
- pio_mode = 5;
- else if (id[ATA_ID_PIO_MODES] & 2)
- pio_mode = 4;
- else
- pio_mode = 3;
- }
- }
- }
-
- if (overridden)
- printk(KERN_INFO "%s: tPIO > 2, assuming tPIO = 2\n",
- drive->name);
- }
-
- if (pio_mode > max_mode)
- pio_mode = max_mode;
-
- return pio_mode;
-}
-EXPORT_SYMBOL_GPL(ide_get_best_pio_mode);
-
-/* req_pio == "255" for auto-tune */
-void ide_set_pio(ide_drive_t *drive, u8 req_pio)
-{
- ide_hwif_t *hwif = drive->hwif;
- const struct ide_port_ops *port_ops = hwif->port_ops;
- u8 host_pio, pio;
-
- if (port_ops == NULL || port_ops->set_pio_mode == NULL ||
- (hwif->host_flags & IDE_HFLAG_NO_SET_MODE))
- return;
-
- BUG_ON(hwif->pio_mask == 0x00);
-
- host_pio = fls(hwif->pio_mask) - 1;
-
- pio = ide_get_best_pio_mode(drive, req_pio, host_pio);
-
- /*
- * TODO:
- * - report device max PIO mode
- * - check req_pio != 255 against device max PIO mode
- */
- printk(KERN_DEBUG "%s: host max PIO%d wanted PIO%d%s selected PIO%d\n",
- drive->name, host_pio, req_pio,
- req_pio == 255 ? "(auto-tune)" : "", pio);
-
- (void)ide_set_pio_mode(drive, XFER_PIO_0 + pio);
-}
-EXPORT_SYMBOL_GPL(ide_set_pio);
-
/**
* ide_toggle_bounce - handle bounce buffering
* @drive: drive to update
@@ -188,89 +31,6 @@
blk_queue_bounce_limit(drive->queue, addr);
}
-int ide_set_pio_mode(ide_drive_t *drive, const u8 mode)
-{
- ide_hwif_t *hwif = drive->hwif;
- const struct ide_port_ops *port_ops = hwif->port_ops;
-
- if (hwif->host_flags & IDE_HFLAG_NO_SET_MODE)
- return 0;
-
- if (port_ops == NULL || port_ops->set_pio_mode == NULL)
- return -1;
-
- /*
- * TODO: temporary hack for some legacy host drivers that didn't
- * set transfer mode on the device in ->set_pio_mode method...
- */
- if (port_ops->set_dma_mode == NULL) {
- port_ops->set_pio_mode(drive, mode - XFER_PIO_0);
- return 0;
- }
-
- if (hwif->host_flags & IDE_HFLAG_POST_SET_MODE) {
- if (ide_config_drive_speed(drive, mode))
- return -1;
- port_ops->set_pio_mode(drive, mode - XFER_PIO_0);
- return 0;
- } else {
- port_ops->set_pio_mode(drive, mode - XFER_PIO_0);
- return ide_config_drive_speed(drive, mode);
- }
-}
-
-int ide_set_dma_mode(ide_drive_t *drive, const u8 mode)
-{
- ide_hwif_t *hwif = drive->hwif;
- const struct ide_port_ops *port_ops = hwif->port_ops;
-
- if (hwif->host_flags & IDE_HFLAG_NO_SET_MODE)
- return 0;
-
- if (port_ops == NULL || port_ops->set_dma_mode == NULL)
- return -1;
-
- if (hwif->host_flags & IDE_HFLAG_POST_SET_MODE) {
- if (ide_config_drive_speed(drive, mode))
- return -1;
- port_ops->set_dma_mode(drive, mode);
- return 0;
- } else {
- port_ops->set_dma_mode(drive, mode);
- return ide_config_drive_speed(drive, mode);
- }
-}
-EXPORT_SYMBOL_GPL(ide_set_dma_mode);
-
-/**
- * ide_set_xfer_rate - set transfer rate
- * @drive: drive to set
- * @rate: speed to attempt to set
- *
- * General helper for setting the speed of an IDE device. This
- * function knows about user enforced limits from the configuration
- * which ->set_pio_mode/->set_dma_mode does not.
- */
-
-int ide_set_xfer_rate(ide_drive_t *drive, u8 rate)
-{
- ide_hwif_t *hwif = drive->hwif;
- const struct ide_port_ops *port_ops = hwif->port_ops;
-
- if (port_ops == NULL || port_ops->set_dma_mode == NULL ||
- (hwif->host_flags & IDE_HFLAG_NO_SET_MODE))
- return -1;
-
- rate = ide_rate_filter(drive, rate);
-
- BUG_ON(rate < XFER_PIO_0);
-
- if (rate >= XFER_PIO_0 && rate <= XFER_PIO_5)
- return ide_set_pio_mode(drive, rate);
-
- return ide_set_dma_mode(drive, rate);
-}
-
static void ide_dump_opcode(ide_drive_t *drive)
{
struct request *rq = drive->hwif->rq;
diff --git a/drivers/ide/ide-park.c b/drivers/ide/ide-park.c
index c875a95..f30e521 100644
--- a/drivers/ide/ide-park.c
+++ b/drivers/ide/ide-park.c
@@ -1,5 +1,6 @@
#include <linux/kernel.h>
#include <linux/ide.h>
+#include <linux/hdreg.h>
#include <linux/jiffies.h>
#include <linux/blkdev.h>
@@ -60,6 +61,30 @@
return;
}
+ide_startstop_t ide_do_park_unpark(ide_drive_t *drive, struct request *rq)
+{
+ ide_task_t task;
+ struct ide_taskfile *tf = &task.tf;
+
+ memset(&task, 0, sizeof(task));
+ if (rq->cmd[0] == REQ_PARK_HEADS) {
+ drive->sleep = *(unsigned long *)rq->special;
+ drive->dev_flags |= IDE_DFLAG_SLEEPING;
+ tf->command = ATA_CMD_IDLEIMMEDIATE;
+ tf->feature = 0x44;
+ tf->lbal = 0x4c;
+ tf->lbam = 0x4e;
+ tf->lbah = 0x55;
+ task.tf_flags |= IDE_TFLAG_CUSTOM_HANDLER;
+ } else /* cmd == REQ_UNPARK_HEADS */
+ tf->command = ATA_CMD_CHK_POWER;
+
+ task.tf_flags |= IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
+ task.rq = rq;
+ drive->hwif->data_phase = task.data_phase = TASKFILE_NO_DATA;
+ return do_rw_taskfile(drive, &task);
+}
+
ssize_t ide_park_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
diff --git a/drivers/ide/ide-pci-generic.c b/drivers/ide/ide-pci-generic.c
index bddae2b..61111fd 100644
--- a/drivers/ide/ide-pci-generic.c
+++ b/drivers/ide/ide-pci-generic.c
@@ -33,8 +33,6 @@
module_param_named(all_generic_ide, ide_generic_all, bool, 0444);
MODULE_PARM_DESC(all_generic_ide, "IDE generic will claim all unknown PCI IDE storage controllers.");
-#define IDE_HFLAGS_UMC (IDE_HFLAG_NO_DMA | IDE_HFLAG_FORCE_LEGACY_IRQS)
-
#define DECLARE_GENERIC_PCI_DEV(extra_flags) \
{ \
.name = DRV_NAME, \
@@ -61,7 +59,7 @@
/* 2: SAMURAI / HT6565 / HINT_IDE */
DECLARE_GENERIC_PCI_DEV(0),
/* 3: UM8673F / UM8886A / UM8886BF */
- DECLARE_GENERIC_PCI_DEV(IDE_HFLAGS_UMC),
+ DECLARE_GENERIC_PCI_DEV(IDE_HFLAG_NO_DMA),
/* 4: VIA_IDE / OPTI621V / Piccolo010{2,3,5} */
DECLARE_GENERIC_PCI_DEV(IDE_HFLAG_NO_AUTODMA),
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index ee8e3e7..9740670 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -181,16 +181,16 @@
* do_identify - identify a drive
* @drive: drive to identify
* @cmd: command used
+ * @id: buffer for IDENTIFY data
*
* Called when we have issued a drive identify command to
* read and parse the results. This function is run with
* interrupts disabled.
*/
-static void do_identify(ide_drive_t *drive, u8 cmd)
+static void do_identify(ide_drive_t *drive, u8 cmd, u16 *id)
{
ide_hwif_t *hwif = drive->hwif;
- u16 *id = drive->id;
char *m = (char *)&id[ATA_ID_PROD];
unsigned long flags;
int bswap = 1;
@@ -233,16 +233,6 @@
drive->dev_flags |= IDE_DFLAG_PRESENT;
drive->dev_flags &= ~IDE_DFLAG_DEAD;
- /*
- * Check for an ATAPI device
- */
- if (cmd == ATA_CMD_ID_ATAPI)
- ide_classify_atapi_dev(drive);
- else
- /*
- * Not an ATAPI device: looks like a "regular" hard disk
- */
- ide_classify_ata_dev(drive);
return;
err_misc:
kfree(id);
@@ -250,21 +240,19 @@
}
/**
- * actual_try_to_identify - send ata/atapi identify
+ * ide_dev_read_id - send ATA/ATAPI IDENTIFY command
* @drive: drive to identify
* @cmd: command to use
+ * @id: buffer for IDENTIFY data
*
- * try_to_identify() sends an ATA(PI) IDENTIFY request to a drive
- * and waits for a response. It also monitors irqs while this is
- * happening, in hope of automatically determining which one is
- * being used by the interface.
+ * Sends an ATA(PI) IDENTIFY request to a drive and waits for a response.
*
* Returns: 0 device was identified
* 1 device timed-out (no response to identify request)
* 2 device aborted the command (refused to identify itself)
*/
-static int actual_try_to_identify (ide_drive_t *drive, u8 cmd)
+int ide_dev_read_id(ide_drive_t *drive, u8 cmd, u16 *id)
{
ide_hwif_t *hwif = drive->hwif;
struct ide_io_ports *io_ports = &hwif->io_ports;
@@ -273,6 +261,13 @@
unsigned long timeout;
u8 s = 0, a = 0;
+ /*
+ * Disable device IRQ. Otherwise we'll get spurious interrupts
+ * during the identify phase that the IRQ handler isn't expecting.
+ */
+ if (io_ports->ctl_addr)
+ tp_ops->set_irq(hwif, 0);
+
/* take a deep breath */
msleep(50);
@@ -317,7 +312,7 @@
if (OK_STAT(s, ATA_DRQ, BAD_R_STAT)) {
/* drive returned ID */
- do_identify(drive, cmd);
+ do_identify(drive, cmd, id);
/* drive responded with ID */
rc = 0;
/* clear drive IRQ */
@@ -329,63 +324,6 @@
return rc;
}
-/**
- * try_to_identify - try to identify a drive
- * @drive: drive to probe
- * @cmd: command to use
- *
- * Issue the identify command and then do IRQ probing to
- * complete the identification when needed by finding the
- * IRQ the drive is attached to
- */
-
-static int try_to_identify (ide_drive_t *drive, u8 cmd)
-{
- ide_hwif_t *hwif = drive->hwif;
- const struct ide_tp_ops *tp_ops = hwif->tp_ops;
- int retval;
- int autoprobe = 0;
- unsigned long cookie = 0;
-
- /*
- * Disable device irq unless we need to
- * probe for it. Otherwise we'll get spurious
- * interrupts during the identify-phase that
- * the irq handler isn't expecting.
- */
- if (hwif->io_ports.ctl_addr) {
- if (!hwif->irq) {
- autoprobe = 1;
- cookie = probe_irq_on();
- }
- tp_ops->set_irq(hwif, autoprobe);
- }
-
- retval = actual_try_to_identify(drive, cmd);
-
- if (autoprobe) {
- int irq;
-
- tp_ops->set_irq(hwif, 0);
- /* clear drive IRQ */
- (void)tp_ops->read_status(hwif);
- udelay(5);
- irq = probe_irq_off(cookie);
- if (!hwif->irq) {
- if (irq > 0) {
- hwif->irq = irq;
- } else {
- /* Mmmm.. multiple IRQs..
- * don't know which was ours
- */
- printk(KERN_ERR "%s: IRQ probe failed (0x%lx)\n",
- drive->name, cookie);
- }
- }
- }
- return retval;
-}
-
int ide_busy_sleep(ide_hwif_t *hwif, unsigned long timeout, int altstatus)
{
u8 stat;
@@ -440,6 +378,7 @@
{
ide_hwif_t *hwif = drive->hwif;
const struct ide_tp_ops *tp_ops = hwif->tp_ops;
+ u16 *id = drive->id;
int rc;
u8 present = !!(drive->dev_flags & IDE_DFLAG_PRESENT), stat;
@@ -475,11 +414,10 @@
if (OK_STAT(stat, ATA_DRDY, ATA_BUSY) ||
present || cmd == ATA_CMD_ID_ATAPI) {
- /* send cmd and wait */
- if ((rc = try_to_identify(drive, cmd))) {
+ rc = ide_dev_read_id(drive, cmd, id);
+ if (rc)
/* failed: try again */
- rc = try_to_identify(drive,cmd);
- }
+ rc = ide_dev_read_id(drive, cmd, id);
stat = tp_ops->read_status(hwif);
@@ -494,7 +432,7 @@
msleep(50);
tp_ops->exec_command(hwif, ATA_CMD_DEV_RESET);
(void)ide_busy_sleep(hwif, WAIT_WORSTCASE, 0);
- rc = try_to_identify(drive, cmd);
+ rc = ide_dev_read_id(drive, cmd, id);
}
/* ensure drive IRQ is clear */
@@ -517,37 +455,6 @@
return rc;
}
-/*
- *
- */
-static void enable_nest (ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- const struct ide_tp_ops *tp_ops = hwif->tp_ops;
- u8 stat;
-
- printk(KERN_INFO "%s: enabling %s -- ",
- hwif->name, (char *)&drive->id[ATA_ID_PROD]);
-
- SELECT_DRIVE(drive);
- msleep(50);
- tp_ops->exec_command(hwif, ATA_EXABYTE_ENABLE_NEST);
-
- if (ide_busy_sleep(hwif, WAIT_WORSTCASE, 0)) {
- printk(KERN_CONT "failed (timeout)\n");
- return;
- }
-
- msleep(50);
-
- stat = tp_ops->read_status(hwif);
-
- if (!OK_STAT(stat, 0, BAD_STAT))
- printk(KERN_CONT "failed (status = 0x%02x)\n", stat);
- else
- printk(KERN_CONT "success\n");
-}
-
/**
* probe_for_drives - upper level drive probe
* @drive: drive to probe for
@@ -563,6 +470,8 @@
static u8 probe_for_drive(ide_drive_t *drive)
{
char *m;
+ int rc;
+ u8 cmd;
/*
* In order to keep things simple we have an id
@@ -586,21 +495,19 @@
/* skip probing? */
if ((drive->dev_flags & IDE_DFLAG_NOPROBE) == 0) {
-retry:
/* if !(success||timed-out) */
- if (do_probe(drive, ATA_CMD_ID_ATA) >= 2)
+ cmd = ATA_CMD_ID_ATA;
+ rc = do_probe(drive, cmd);
+ if (rc >= 2) {
/* look for ATAPI device */
- (void)do_probe(drive, ATA_CMD_ID_ATAPI);
+ cmd = ATA_CMD_ID_ATAPI;
+ rc = do_probe(drive, cmd);
+ }
if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
/* drive not found */
return 0;
- if (strstr(m, "E X A B Y T E N E S T")) {
- enable_nest(drive);
- goto retry;
- }
-
/* identification failed? */
if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) {
if (drive->media == ide_disk) {
@@ -614,8 +521,12 @@
printk(KERN_WARNING "%s: Unknown device on bus refused identification. Ignoring.\n", drive->name);
drive->dev_flags &= ~IDE_DFLAG_PRESENT;
}
+ } else {
+ if (cmd == ATA_CMD_ID_ATAPI)
+ ide_classify_atapi_dev(drive);
+ else
+ ide_classify_ata_dev(drive);
}
- /* drive was found */
}
if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
@@ -779,7 +690,6 @@
static int ide_probe_port(ide_hwif_t *hwif)
{
ide_drive_t *drive;
- unsigned long flags;
unsigned int irqd;
int i, rc = -ENODEV;
@@ -797,9 +707,6 @@
if (irqd)
disable_irq(hwif->irq);
- local_save_flags(flags);
- local_irq_enable_in_hardirq();
-
if (ide_port_wait_ready(hwif) == -EBUSY)
printk(KERN_DEBUG "%s: Wait for ready failed before probe !\n", hwif->name);
@@ -813,8 +720,6 @@
rc = 0;
}
- local_irq_restore(flags);
-
/*
* Use cached IRQ number. It might be (and is...) changed by probe
* code above
@@ -831,29 +736,18 @@
ide_drive_t *drive;
int i;
- ide_port_for_each_dev(i, drive, hwif) {
- if (drive->dev_flags & IDE_DFLAG_PRESENT) {
- if (port_ops && port_ops->quirkproc)
- port_ops->quirkproc(drive);
- }
+ ide_port_for_each_present_dev(i, drive, hwif) {
+ if (port_ops && port_ops->quirkproc)
+ port_ops->quirkproc(drive);
}
- ide_port_for_each_dev(i, drive, hwif) {
- if (drive->dev_flags & IDE_DFLAG_PRESENT) {
- ide_set_max_pio(drive);
+ ide_port_for_each_present_dev(i, drive, hwif) {
+ ide_set_max_pio(drive);
- drive->dev_flags |= IDE_DFLAG_NICE1;
+ drive->dev_flags |= IDE_DFLAG_NICE1;
- if (hwif->dma_ops)
- ide_set_dma(drive);
- }
- }
-
- ide_port_for_each_dev(i, drive, hwif) {
- if (hwif->host_flags & IDE_HFLAG_NO_IO_32BIT)
- drive->dev_flags |= IDE_DFLAG_NO_IO_32BIT;
- else
- drive->dev_flags &= ~IDE_DFLAG_NO_IO_32BIT;
+ if (hwif->dma_ops)
+ ide_set_dma(drive);
}
}
@@ -924,10 +818,7 @@
int i, j = 0;
mutex_lock(&ide_cfg_mtx);
- ide_port_for_each_dev(i, drive, hwif) {
- if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
- continue;
-
+ ide_port_for_each_present_dev(i, drive, hwif) {
if (ide_init_queue(drive)) {
printk(KERN_ERR "ide: failed to init %s\n",
drive->name);
@@ -953,13 +844,6 @@
irq_handler_t irq_handler;
int sa = 0;
- mutex_lock(&ide_cfg_mtx);
- spin_lock_init(&hwif->lock);
-
- init_timer(&hwif->timer);
- hwif->timer.function = &ide_timer_expiry;
- hwif->timer.data = (unsigned long)hwif;
-
irq_handler = hwif->host->irq_handler;
if (irq_handler == NULL)
irq_handler = ide_intr;
@@ -997,10 +881,8 @@
printk(KERN_CONT " (serialized)");
printk(KERN_CONT "\n");
- mutex_unlock(&ide_cfg_mtx);
return 0;
out_up:
- mutex_unlock(&ide_cfg_mtx);
return 1;
}
@@ -1099,14 +981,9 @@
static int hwif_init(ide_hwif_t *hwif)
{
- int old_irq;
-
if (!hwif->irq) {
- hwif->irq = __ide_default_irq(hwif->io_ports.data_addr);
- if (!hwif->irq) {
- printk(KERN_ERR "%s: disabled, no IRQ\n", hwif->name);
- return 0;
- }
+ printk(KERN_ERR "%s: disabled, no IRQ\n", hwif->name);
+ return 0;
}
if (register_blkdev(hwif->major, hwif->name))
@@ -1124,29 +1001,12 @@
sg_init_table(hwif->sg_table, hwif->sg_max_nents);
- if (init_irq(hwif) == 0)
- goto done;
-
- old_irq = hwif->irq;
- /*
- * It failed to initialise. Find the default IRQ for
- * this port and try that.
- */
- hwif->irq = __ide_default_irq(hwif->io_ports.data_addr);
- if (!hwif->irq) {
- printk(KERN_ERR "%s: disabled, unable to get IRQ %d\n",
- hwif->name, old_irq);
- goto out;
- }
if (init_irq(hwif)) {
- printk(KERN_ERR "%s: probed IRQ %d and default IRQ %d failed\n",
- hwif->name, old_irq, hwif->irq);
+ printk(KERN_ERR "%s: disabled, unable to get IRQ %d\n",
+ hwif->name, hwif->irq);
goto out;
}
- printk(KERN_WARNING "%s: probed IRQ %d failed, using default\n",
- hwif->name, hwif->irq);
-done:
blk_register_region(MKDEV(hwif->major, 0), MAX_DRIVES << PARTN_BITS,
THIS_MODULE, ata_probe, ata_lock, hwif);
return 1;
@@ -1161,13 +1021,10 @@
ide_drive_t *drive;
unsigned int i;
- ide_port_for_each_dev(i, drive, hwif) {
+ ide_port_for_each_present_dev(i, drive, hwif) {
struct device *dev = &drive->gendev;
int ret;
- if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
- continue;
-
dev_set_name(dev, "%u.%u", hwif->index, i);
dev->parent = &hwif->gendev;
dev->bus = &ide_bus_type;
@@ -1192,6 +1049,8 @@
if (hwif->host_flags & IDE_HFLAG_IO_32BIT)
drive->io_32bit = 1;
+ if (hwif->host_flags & IDE_HFLAG_NO_IO_32BIT)
+ drive->dev_flags |= IDE_DFLAG_NO_IO_32BIT;
if (hwif->host_flags & IDE_HFLAG_UNMASK_IRQS)
drive->dev_flags |= IDE_DFLAG_UNMASK;
if (hwif->host_flags & IDE_HFLAG_NO_UNMASK_IRQS)
@@ -1213,10 +1072,6 @@
if (d->init_iops)
d->init_iops(hwif);
- if ((!hwif->irq && (d->host_flags & IDE_HFLAG_LEGACY_IRQS)) ||
- (d->host_flags & IDE_HFLAG_FORCE_LEGACY_IRQS))
- hwif->irq = port ? 15 : 14;
-
/* ->host_flags may be set by ->init_iops (or even earlier...) */
hwif->host_flags |= d->host_flags;
hwif->pio_mask = d->pio_mask;
@@ -1317,6 +1172,12 @@
hwif->name[2] = 'e';
hwif->name[3] = '0' + index;
+ spin_lock_init(&hwif->lock);
+
+ init_timer(&hwif->timer);
+ hwif->timer.function = &ide_timer_expiry;
+ hwif->timer.data = (unsigned long)hwif;
+
init_completion(&hwif->gendev_rel_comp);
hwif->tp_ops = &default_tp_ops;
@@ -1567,7 +1428,7 @@
j++;
- ide_acpi_init(hwif);
+ ide_acpi_init_port(hwif);
if (hwif->present)
ide_acpi_port_init_devices(hwif);
@@ -1624,11 +1485,9 @@
ide_drive_t *drive;
int i;
- ide_port_for_each_dev(i, drive, hwif) {
- if (drive->dev_flags & IDE_DFLAG_PRESENT) {
- device_unregister(&drive->gendev);
- wait_for_completion(&drive->gendev_rel_comp);
- }
+ ide_port_for_each_present_dev(i, drive, hwif) {
+ device_unregister(&drive->gendev);
+ wait_for_completion(&drive->gendev_rel_comp);
}
}
diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c
index a7b9287..417cde5 100644
--- a/drivers/ide/ide-proc.c
+++ b/drivers/ide/ide-proc.c
@@ -600,7 +600,7 @@
int i;
ide_port_for_each_dev(i, drive, hwif) {
- if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0 || drive->proc)
+ if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
continue;
drive->proc = proc_mkdir(drive->name, parent);
diff --git a/drivers/ide/ide-xfer-mode.c b/drivers/ide/ide-xfer-mode.c
new file mode 100644
index 0000000..6910f6a
--- /dev/null
+++ b/drivers/ide/ide-xfer-mode.c
@@ -0,0 +1,246 @@
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/ide.h>
+#include <linux/bitops.h>
+
+static const char *udma_str[] =
+ { "UDMA/16", "UDMA/25", "UDMA/33", "UDMA/44",
+ "UDMA/66", "UDMA/100", "UDMA/133", "UDMA7" };
+static const char *mwdma_str[] =
+ { "MWDMA0", "MWDMA1", "MWDMA2" };
+static const char *swdma_str[] =
+ { "SWDMA0", "SWDMA1", "SWDMA2" };
+static const char *pio_str[] =
+ { "PIO0", "PIO1", "PIO2", "PIO3", "PIO4", "PIO5" };
+
+/**
+ * ide_xfer_verbose - return IDE mode names
+ * @mode: transfer mode
+ *
+ * Returns a constant string giving the name of the mode
+ * requested.
+ */
+
+const char *ide_xfer_verbose(u8 mode)
+{
+ const char *s;
+ u8 i = mode & 0xf;
+
+ if (mode >= XFER_UDMA_0 && mode <= XFER_UDMA_7)
+ s = udma_str[i];
+ else if (mode >= XFER_MW_DMA_0 && mode <= XFER_MW_DMA_2)
+ s = mwdma_str[i];
+ else if (mode >= XFER_SW_DMA_0 && mode <= XFER_SW_DMA_2)
+ s = swdma_str[i];
+ else if (mode >= XFER_PIO_0 && mode <= XFER_PIO_5)
+ s = pio_str[i & 0x7];
+ else if (mode == XFER_PIO_SLOW)
+ s = "PIO SLOW";
+ else
+ s = "XFER ERROR";
+
+ return s;
+}
+EXPORT_SYMBOL(ide_xfer_verbose);
+
+/**
+ * ide_get_best_pio_mode - get PIO mode from drive
+ * @drive: drive to consider
+ * @mode_wanted: preferred mode
+ * @max_mode: highest allowed mode
+ *
+ * This routine returns the recommended PIO settings for a given drive,
+ * based on the drive->id information and the ide_pio_blacklist[].
+ *
+ * Drive PIO mode is auto-selected if 255 is passed as mode_wanted.
+ * This is used by most chipset support modules when "auto-tuning".
+ */
+
+u8 ide_get_best_pio_mode(ide_drive_t *drive, u8 mode_wanted, u8 max_mode)
+{
+ u16 *id = drive->id;
+ int pio_mode = -1, overridden = 0;
+
+ if (mode_wanted != 255)
+ return min_t(u8, mode_wanted, max_mode);
+
+ if ((drive->hwif->host_flags & IDE_HFLAG_PIO_NO_BLACKLIST) == 0)
+ pio_mode = ide_scan_pio_blacklist((char *)&id[ATA_ID_PROD]);
+
+ if (pio_mode != -1) {
+ printk(KERN_INFO "%s: is on PIO blacklist\n", drive->name);
+ } else {
+ pio_mode = id[ATA_ID_OLD_PIO_MODES] >> 8;
+ if (pio_mode > 2) { /* 2 is maximum allowed tPIO value */
+ pio_mode = 2;
+ overridden = 1;
+ }
+
+ if (id[ATA_ID_FIELD_VALID] & 2) { /* ATA2? */
+ if (ata_id_has_iordy(id)) {
+ if (id[ATA_ID_PIO_MODES] & 7) {
+ overridden = 0;
+ if (id[ATA_ID_PIO_MODES] & 4)
+ pio_mode = 5;
+ else if (id[ATA_ID_PIO_MODES] & 2)
+ pio_mode = 4;
+ else
+ pio_mode = 3;
+ }
+ }
+ }
+
+ if (overridden)
+ printk(KERN_INFO "%s: tPIO > 2, assuming tPIO = 2\n",
+ drive->name);
+ }
+
+ if (pio_mode > max_mode)
+ pio_mode = max_mode;
+
+ return pio_mode;
+}
+EXPORT_SYMBOL_GPL(ide_get_best_pio_mode);
+
+int ide_set_pio_mode(ide_drive_t *drive, const u8 mode)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ const struct ide_port_ops *port_ops = hwif->port_ops;
+
+ if (hwif->host_flags & IDE_HFLAG_NO_SET_MODE)
+ return 0;
+
+ if (port_ops == NULL || port_ops->set_pio_mode == NULL)
+ return -1;
+
+ /*
+ * TODO: temporary hack for some legacy host drivers that didn't
+ * set transfer mode on the device in ->set_pio_mode method...
+ */
+ if (port_ops->set_dma_mode == NULL) {
+ port_ops->set_pio_mode(drive, mode - XFER_PIO_0);
+ return 0;
+ }
+
+ if (hwif->host_flags & IDE_HFLAG_POST_SET_MODE) {
+ if (ide_config_drive_speed(drive, mode))
+ return -1;
+ port_ops->set_pio_mode(drive, mode - XFER_PIO_0);
+ return 0;
+ } else {
+ port_ops->set_pio_mode(drive, mode - XFER_PIO_0);
+ return ide_config_drive_speed(drive, mode);
+ }
+}
+
+int ide_set_dma_mode(ide_drive_t *drive, const u8 mode)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ const struct ide_port_ops *port_ops = hwif->port_ops;
+
+ if (hwif->host_flags & IDE_HFLAG_NO_SET_MODE)
+ return 0;
+
+ if (port_ops == NULL || port_ops->set_dma_mode == NULL)
+ return -1;
+
+ if (hwif->host_flags & IDE_HFLAG_POST_SET_MODE) {
+ if (ide_config_drive_speed(drive, mode))
+ return -1;
+ port_ops->set_dma_mode(drive, mode);
+ return 0;
+ } else {
+ port_ops->set_dma_mode(drive, mode);
+ return ide_config_drive_speed(drive, mode);
+ }
+}
+EXPORT_SYMBOL_GPL(ide_set_dma_mode);
+
+/* req_pio == "255" for auto-tune */
+void ide_set_pio(ide_drive_t *drive, u8 req_pio)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ const struct ide_port_ops *port_ops = hwif->port_ops;
+ u8 host_pio, pio;
+
+ if (port_ops == NULL || port_ops->set_pio_mode == NULL ||
+ (hwif->host_flags & IDE_HFLAG_NO_SET_MODE))
+ return;
+
+ BUG_ON(hwif->pio_mask == 0x00);
+
+ host_pio = fls(hwif->pio_mask) - 1;
+
+ pio = ide_get_best_pio_mode(drive, req_pio, host_pio);
+
+ /*
+ * TODO:
+ * - report device max PIO mode
+ * - check req_pio != 255 against device max PIO mode
+ */
+ printk(KERN_DEBUG "%s: host max PIO%d wanted PIO%d%s selected PIO%d\n",
+ drive->name, host_pio, req_pio,
+ req_pio == 255 ? "(auto-tune)" : "", pio);
+
+ (void)ide_set_pio_mode(drive, XFER_PIO_0 + pio);
+}
+EXPORT_SYMBOL_GPL(ide_set_pio);
+
+/**
+ * ide_rate_filter - filter transfer mode
+ * @drive: IDE device
+ * @speed: desired speed
+ *
+ * Given the available transfer modes this function returns
+ * the best available speed at or below the speed requested.
+ *
+ * TODO: check device PIO capabilities
+ */
+
+static u8 ide_rate_filter(ide_drive_t *drive, u8 speed)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ u8 mode = ide_find_dma_mode(drive, speed);
+
+ if (mode == 0) {
+ if (hwif->pio_mask)
+ mode = fls(hwif->pio_mask) - 1 + XFER_PIO_0;
+ else
+ mode = XFER_PIO_4;
+ }
+
+/* printk("%s: mode 0x%02x, speed 0x%02x\n", __func__, mode, speed); */
+
+ return min(speed, mode);
+}
+
+/**
+ * ide_set_xfer_rate - set transfer rate
+ * @drive: drive to set
+ * @rate: speed to attempt to set
+ *
+ * General helper for setting the speed of an IDE device. This
+ * function knows about user enforced limits from the configuration
+ * which ->set_pio_mode/->set_dma_mode does not.
+ */
+
+int ide_set_xfer_rate(ide_drive_t *drive, u8 rate)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ const struct ide_port_ops *port_ops = hwif->port_ops;
+
+ if (port_ops == NULL || port_ops->set_dma_mode == NULL ||
+ (hwif->host_flags & IDE_HFLAG_NO_SET_MODE))
+ return -1;
+
+ rate = ide_rate_filter(drive, rate);
+
+ BUG_ON(rate < XFER_PIO_0);
+
+ if (rate >= XFER_PIO_0 && rate <= XFER_PIO_5)
+ return ide_set_pio_mode(drive, rate);
+
+ return ide_set_dma_mode(drive, rate);
+}
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
index 0920e3b..92c9b90 100644
--- a/drivers/ide/ide.c
+++ b/drivers/ide/ide.c
@@ -62,160 +62,6 @@
struct class *ide_port_class;
-/*
- * Locks for IDE setting functionality
- */
-
-DEFINE_MUTEX(ide_setting_mtx);
-
-ide_devset_get(io_32bit, io_32bit);
-
-static int set_io_32bit(ide_drive_t *drive, int arg)
-{
- if (drive->dev_flags & IDE_DFLAG_NO_IO_32BIT)
- return -EPERM;
-
- if (arg < 0 || arg > 1 + (SUPPORT_VLB_SYNC << 1))
- return -EINVAL;
-
- drive->io_32bit = arg;
-
- return 0;
-}
-
-ide_devset_get_flag(ksettings, IDE_DFLAG_KEEP_SETTINGS);
-
-static int set_ksettings(ide_drive_t *drive, int arg)
-{
- if (arg < 0 || arg > 1)
- return -EINVAL;
-
- if (arg)
- drive->dev_flags |= IDE_DFLAG_KEEP_SETTINGS;
- else
- drive->dev_flags &= ~IDE_DFLAG_KEEP_SETTINGS;
-
- return 0;
-}
-
-ide_devset_get_flag(using_dma, IDE_DFLAG_USING_DMA);
-
-static int set_using_dma(ide_drive_t *drive, int arg)
-{
-#ifdef CONFIG_BLK_DEV_IDEDMA
- int err = -EPERM;
-
- if (arg < 0 || arg > 1)
- return -EINVAL;
-
- if (ata_id_has_dma(drive->id) == 0)
- goto out;
-
- if (drive->hwif->dma_ops == NULL)
- goto out;
-
- err = 0;
-
- if (arg) {
- if (ide_set_dma(drive))
- err = -EIO;
- } else
- ide_dma_off(drive);
-
-out:
- return err;
-#else
- if (arg < 0 || arg > 1)
- return -EINVAL;
-
- return -EPERM;
-#endif
-}
-
-/*
- * handle HDIO_SET_PIO_MODE ioctl abusers here, eventually it will go away
- */
-static int set_pio_mode_abuse(ide_hwif_t *hwif, u8 req_pio)
-{
- switch (req_pio) {
- case 202:
- case 201:
- case 200:
- case 102:
- case 101:
- case 100:
- return (hwif->host_flags & IDE_HFLAG_ABUSE_DMA_MODES) ? 1 : 0;
- case 9:
- case 8:
- return (hwif->host_flags & IDE_HFLAG_ABUSE_PREFETCH) ? 1 : 0;
- case 7:
- case 6:
- return (hwif->host_flags & IDE_HFLAG_ABUSE_FAST_DEVSEL) ? 1 : 0;
- default:
- return 0;
- }
-}
-
-static int set_pio_mode(ide_drive_t *drive, int arg)
-{
- ide_hwif_t *hwif = drive->hwif;
- const struct ide_port_ops *port_ops = hwif->port_ops;
-
- if (arg < 0 || arg > 255)
- return -EINVAL;
-
- if (port_ops == NULL || port_ops->set_pio_mode == NULL ||
- (hwif->host_flags & IDE_HFLAG_NO_SET_MODE))
- return -ENOSYS;
-
- if (set_pio_mode_abuse(drive->hwif, arg)) {
- if (arg == 8 || arg == 9) {
- unsigned long flags;
-
- /* take lock for IDE_DFLAG_[NO_]UNMASK/[NO_]IO_32BIT */
- spin_lock_irqsave(&hwif->lock, flags);
- port_ops->set_pio_mode(drive, arg);
- spin_unlock_irqrestore(&hwif->lock, flags);
- } else
- port_ops->set_pio_mode(drive, arg);
- } else {
- int keep_dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA);
-
- ide_set_pio(drive, arg);
-
- if (hwif->host_flags & IDE_HFLAG_SET_PIO_MODE_KEEP_DMA) {
- if (keep_dma)
- ide_dma_on(drive);
- }
- }
-
- return 0;
-}
-
-ide_devset_get_flag(unmaskirq, IDE_DFLAG_UNMASK);
-
-static int set_unmaskirq(ide_drive_t *drive, int arg)
-{
- if (drive->dev_flags & IDE_DFLAG_NO_UNMASK)
- return -EPERM;
-
- if (arg < 0 || arg > 1)
- return -EINVAL;
-
- if (arg)
- drive->dev_flags |= IDE_DFLAG_UNMASK;
- else
- drive->dev_flags &= ~IDE_DFLAG_UNMASK;
-
- return 0;
-}
-
-ide_ext_devset_rw_sync(io_32bit, io_32bit);
-ide_ext_devset_rw_sync(keepsettings, ksettings);
-ide_ext_devset_rw_sync(unmaskirq, unmaskirq);
-ide_ext_devset_rw_sync(using_dma, using_dma);
-__IDE_DEVSET(pio_mode, DS_SYNC, NULL, set_pio_mode);
-
/**
* ide_device_get - get an additional reference to a ide_drive_t
* @drive: device to get a reference to
@@ -527,6 +373,8 @@
goto out_port_class;
}
+ ide_acpi_init();
+
proc_ide_create();
return 0;
diff --git a/drivers/ide/it821x.c b/drivers/ide/it821x.c
index 13b8153..6b9fc95 100644
--- a/drivers/ide/it821x.c
+++ b/drivers/ide/it821x.c
@@ -603,7 +603,7 @@
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20);
}
-static unsigned int init_chipset_it821x(struct pci_dev *dev)
+static int init_chipset_it821x(struct pci_dev *dev)
{
u8 conf;
static char *mode[2] = { "pass through", "smart" };
diff --git a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c
index 83643ed..ea48a3e 100644
--- a/drivers/ide/ns87415.c
+++ b/drivers/ide/ns87415.c
@@ -286,9 +286,7 @@
}
if (!using_inta)
- hwif->irq = __ide_default_irq(hwif->io_ports.data_addr);
- else if (!hwif->irq && hwif->mate && hwif->mate->irq)
- hwif->irq = hwif->mate->irq; /* share IRQ with mate */
+ hwif->irq = pci_get_legacy_ide_irq(dev, hwif->channel);
if (!hwif->dma_base)
return;
diff --git a/drivers/ide/pdc202xx_new.c b/drivers/ide/pdc202xx_new.c
index f21290c..b68906c 100644
--- a/drivers/ide/pdc202xx_new.c
+++ b/drivers/ide/pdc202xx_new.c
@@ -325,7 +325,7 @@
}
#endif /* CONFIG_PPC_PMAC */
-static unsigned int init_chipset_pdcnew(struct pci_dev *dev)
+static int init_chipset_pdcnew(struct pci_dev *dev)
{
const char *name = DRV_NAME;
unsigned long dma_base = pci_resource_start(dev, 4);
@@ -444,7 +444,7 @@
#endif
out:
- return dev->irq;
+ return 0;
}
static struct pci_dev * __devinit pdc20270_get_dev2(struct pci_dev *dev)
diff --git a/drivers/ide/pdc202xx_old.c b/drivers/ide/pdc202xx_old.c
index 9719332..cba66eb 100644
--- a/drivers/ide/pdc202xx_old.c
+++ b/drivers/ide/pdc202xx_old.c
@@ -264,7 +264,7 @@
ide_dma_timeout(drive);
}
-static unsigned int init_chipset_pdc202xx(struct pci_dev *dev)
+static int init_chipset_pdc202xx(struct pci_dev *dev)
{
unsigned long dmabase = pci_resource_start(dev, 4);
u8 udma_speed_flag = 0, primary_mode = 0, secondary_mode = 0;
@@ -290,7 +290,7 @@
printk("%sACTIVE\n", (inb(dmabase | 0x1f) & 1) ? "" : "IN");
}
out:
- return dev->irq;
+ return 0;
}
static void __devinit pdc202ata4_fixup_irq(struct pci_dev *dev,
diff --git a/drivers/ide/piix.c b/drivers/ide/piix.c
index f1e2e4e..2aa6999 100644
--- a/drivers/ide/piix.c
+++ b/drivers/ide/piix.c
@@ -204,7 +204,7 @@
* out to be nice and simple.
*/
-static unsigned int init_chipset_ich(struct pci_dev *dev)
+static int init_chipset_ich(struct pci_dev *dev)
{
u32 extra = 0;
@@ -318,19 +318,12 @@
.cable_detect = piix_cable_detect,
};
-#ifndef CONFIG_IA64
- #define IDE_HFLAGS_PIIX IDE_HFLAG_LEGACY_IRQS
-#else
- #define IDE_HFLAGS_PIIX 0
-#endif
-
#define DECLARE_PIIX_DEV(udma) \
{ \
.name = DRV_NAME, \
.init_hwif = init_hwif_piix, \
.enablebits = {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, \
.port_ops = &piix_port_ops, \
- .host_flags = IDE_HFLAGS_PIIX, \
.pio_mask = ATA_PIO4, \
.swdma_mask = ATA_SWDMA2_ONLY, \
.mwdma_mask = ATA_MWDMA12_ONLY, \
@@ -344,7 +337,6 @@
.init_hwif = init_hwif_piix, \
.enablebits = {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, \
.port_ops = &ich_port_ops, \
- .host_flags = IDE_HFLAGS_PIIX, \
.pio_mask = ATA_PIO4, \
.swdma_mask = ATA_SWDMA2_ONLY, \
.mwdma_mask = ATA_MWDMA12_ONLY, \
@@ -360,8 +352,7 @@
*/
.name = DRV_NAME,
.enablebits = {{0x6d,0xc0,0x80}, {0x6d,0xc0,0xc0}},
- .host_flags = IDE_HFLAG_ISA_PORTS | IDE_HFLAG_NO_DMA |
- IDE_HFLAGS_PIIX,
+ .host_flags = IDE_HFLAG_ISA_PORTS | IDE_HFLAG_NO_DMA,
.pio_mask = ATA_PIO4,
/* This is a painful system best to let it self tune for now */
},
diff --git a/drivers/ide/serverworks.c b/drivers/ide/serverworks.c
index 382102b..b6554ef 100644
--- a/drivers/ide/serverworks.c
+++ b/drivers/ide/serverworks.c
@@ -175,7 +175,7 @@
pci_write_config_byte(dev, 0x54, ultra_enable);
}
-static unsigned int init_chipset_svwks(struct pci_dev *dev)
+static int init_chipset_svwks(struct pci_dev *dev)
{
unsigned int reg;
u8 btr;
@@ -270,7 +270,7 @@
pci_write_config_byte(dev, 0x5A, btr);
}
- return dev->irq;
+ return 0;
}
static u8 ata66_svwks_svwks(ide_hwif_t *hwif)
@@ -353,14 +353,11 @@
.cable_detect = svwks_cable_detect,
};
-#define IDE_HFLAGS_SVWKS IDE_HFLAG_LEGACY_IRQS
-
static const struct ide_port_info serverworks_chipsets[] __devinitdata = {
{ /* 0: OSB4 */
.name = DRV_NAME,
.init_chipset = init_chipset_svwks,
.port_ops = &osb4_port_ops,
- .host_flags = IDE_HFLAGS_SVWKS,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = 0x00, /* UDMA is problematic on OSB4 */
@@ -369,7 +366,6 @@
.name = DRV_NAME,
.init_chipset = init_chipset_svwks,
.port_ops = &svwks_port_ops,
- .host_flags = IDE_HFLAGS_SVWKS,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
@@ -378,7 +374,6 @@
.name = DRV_NAME,
.init_chipset = init_chipset_svwks,
.port_ops = &svwks_port_ops,
- .host_flags = IDE_HFLAGS_SVWKS,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
@@ -387,7 +382,7 @@
.name = DRV_NAME,
.init_chipset = init_chipset_svwks,
.port_ops = &svwks_port_ops,
- .host_flags = IDE_HFLAGS_SVWKS | IDE_HFLAG_SINGLE,
+ .host_flags = IDE_HFLAG_SINGLE,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
@@ -396,7 +391,7 @@
.name = DRV_NAME,
.init_chipset = init_chipset_svwks,
.port_ops = &svwks_port_ops,
- .host_flags = IDE_HFLAGS_SVWKS | IDE_HFLAG_SINGLE,
+ .host_flags = IDE_HFLAG_SINGLE,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c
index e85d1ed..24bc884 100644
--- a/drivers/ide/setup-pci.c
+++ b/drivers/ide/setup-pci.c
@@ -305,7 +305,6 @@
* @dev: PCI device holding interface
* @d: IDE port info
* @port: port number
- * @irq: PCI IRQ
* @hw: hw_regs_t instance corresponding to this port
*
* Perform the initial set up for the hardware interface structure. This
@@ -316,7 +315,7 @@
*/
static int ide_hw_configure(struct pci_dev *dev, const struct ide_port_info *d,
- unsigned int port, int irq, hw_regs_t *hw)
+ unsigned int port, hw_regs_t *hw)
{
unsigned long ctl = 0, base = 0;
@@ -344,7 +343,6 @@
}
memset(hw, 0, sizeof(*hw));
- hw->irq = irq;
hw->dev = &dev->dev;
hw->chipset = d->chipset ? d->chipset : ide_pci;
ide_std_init_ports(hw, base, ctl | 2);
@@ -448,7 +446,6 @@
* ide_pci_setup_ports - configure ports/devices on PCI IDE
* @dev: PCI device
* @d: IDE port info
- * @pciirq: IRQ line
* @hw: hw_regs_t instances corresponding to this PCI IDE device
* @hws: hw_regs_t pointers table to update
*
@@ -462,7 +459,7 @@
*/
void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d,
- int pciirq, hw_regs_t *hw, hw_regs_t **hws)
+ hw_regs_t *hw, hw_regs_t **hws)
{
int channels = (d->host_flags & IDE_HFLAG_SINGLE) ? 1 : 2, port;
u8 tmp;
@@ -481,7 +478,7 @@
continue; /* port not enabled */
}
- if (ide_hw_configure(dev, d, port, pciirq, hw + port))
+ if (ide_hw_configure(dev, d, port, hw + port))
continue;
*(hws + port) = hw + port;
@@ -524,7 +521,7 @@
if (noisy)
printk(KERN_INFO "%s %s: not 100%% native mode: will "
"probe irqs later\n", d->name, pci_name(dev));
- pciirq = ret;
+ pciirq = 0;
} else if (!pciirq && noisy) {
printk(KERN_WARNING "%s %s: bad irq (%d): will probe later\n",
d->name, pci_name(dev), pciirq);
@@ -549,7 +546,7 @@
if (ret < 0)
goto out;
- ide_pci_setup_ports(dev, d, 0, &hw[0], &hws[0]);
+ ide_pci_setup_ports(dev, d, &hw[0], &hws[0]);
host = ide_host_alloc(d, hws);
if (host == NULL) {
@@ -568,7 +565,11 @@
goto out;
/* fixup IRQ */
- hw[1].irq = hw[0].irq = ret;
+ if (ide_pci_is_in_compatibility_mode(dev)) {
+ hw[0].irq = pci_get_legacy_ide_irq(dev, 0);
+ hw[1].irq = pci_get_legacy_ide_irq(dev, 1);
+ } else
+ hw[1].irq = hw[0].irq = ret;
ret = ide_host_register(host, d, hws);
if (ret)
@@ -591,7 +592,7 @@
if (ret < 0)
goto out;
- ide_pci_setup_ports(pdev[i], d, 0, &hw[i*2], &hws[i*2]);
+ ide_pci_setup_ports(pdev[i], d, &hw[i*2], &hws[i*2]);
}
host = ide_host_alloc(d, hws);
@@ -619,7 +620,11 @@
goto out;
/* fixup IRQ */
- hw[i*2 + 1].irq = hw[i*2].irq = ret;
+ if (ide_pci_is_in_compatibility_mode(pdev[i])) {
+ hw[i*2].irq = pci_get_legacy_ide_irq(pdev[i], 0);
+ hw[i*2 + 1].irq = pci_get_legacy_ide_irq(pdev[i], 1);
+ } else
+ hw[i*2 + 1].irq = hw[i*2].irq = ret;
}
ret = ide_host_register(host, d, hws);
diff --git a/drivers/ide/siimage.c b/drivers/ide/siimage.c
index cb2b352..1811ae9 100644
--- a/drivers/ide/siimage.c
+++ b/drivers/ide/siimage.c
@@ -464,7 +464,7 @@
* to 133 MHz clocking if the system isn't already set up to do it.
*/
-static unsigned int init_chipset_siimage(struct pci_dev *dev)
+static int init_chipset_siimage(struct pci_dev *dev)
{
struct ide_host *host = pci_get_drvdata(dev);
void __iomem *ioaddr = host->host_priv;
diff --git a/drivers/ide/sis5513.c b/drivers/ide/sis5513.c
index 9ec1a4a..afca22b 100644
--- a/drivers/ide/sis5513.c
+++ b/drivers/ide/sis5513.c
@@ -447,7 +447,7 @@
return chipset_family;
}
-static unsigned int init_chipset_sis5513(struct pci_dev *dev)
+static int init_chipset_sis5513(struct pci_dev *dev)
{
/* Make general config ops here
1/ tell IDE channels to operate in Compatibility mode only
@@ -563,7 +563,7 @@
.name = DRV_NAME,
.init_chipset = init_chipset_sis5513,
.enablebits = { {0x4a, 0x02, 0x02}, {0x4a, 0x04, 0x04} },
- .host_flags = IDE_HFLAG_LEGACY_IRQS | IDE_HFLAG_NO_AUTODMA,
+ .host_flags = IDE_HFLAG_NO_AUTODMA,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
};
diff --git a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c
index 6297956..dba213c 100644
--- a/drivers/ide/sl82c105.c
+++ b/drivers/ide/sl82c105.c
@@ -271,7 +271,7 @@
* channel 0 here at least, but channel 1 has to be enabled by
* firmware or arch code. We still set both to 16 bits mode.
*/
-static unsigned int init_chipset_sl82c105(struct pci_dev *dev)
+static int init_chipset_sl82c105(struct pci_dev *dev)
{
u32 val;
@@ -281,7 +281,7 @@
val |= CTRL_P0EN | CTRL_P0F16 | CTRL_P1F16;
pci_write_config_dword(dev, 0x40, val);
- return dev->irq;
+ return 0;
}
static const struct ide_port_ops sl82c105_port_ops = {
diff --git a/drivers/ide/slc90e66.c b/drivers/ide/slc90e66.c
index 40b4b94..f55d7d6 100644
--- a/drivers/ide/slc90e66.c
+++ b/drivers/ide/slc90e66.c
@@ -136,7 +136,6 @@
.name = DRV_NAME,
.enablebits = { {0x41, 0x80, 0x80}, {0x43, 0x80, 0x80} },
.port_ops = &slc90e66_port_ops,
- .host_flags = IDE_HFLAG_LEGACY_IRQS,
.pio_mask = ATA_PIO4,
.swdma_mask = ATA_SWDMA2_ONLY,
.mwdma_mask = ATA_MWDMA12_ONLY,
diff --git a/drivers/ide/trm290.c b/drivers/ide/trm290.c
index b6a1285..1c09e54 100644
--- a/drivers/ide/trm290.c
+++ b/drivers/ide/trm290.c
@@ -277,9 +277,6 @@
if (reg & 0x10)
/* legacy mode */
hwif->irq = hwif->channel ? 15 : 14;
- else if (!hwif->irq && hwif->mate && hwif->mate->irq)
- /* sharing IRQ with mate */
- hwif->irq = hwif->mate->irq;
#if 1
{
diff --git a/drivers/ide/via82cxxx.c b/drivers/ide/via82cxxx.c
index 6092fe3..3ff7231 100644
--- a/drivers/ide/via82cxxx.c
+++ b/drivers/ide/via82cxxx.c
@@ -267,7 +267,7 @@
* and initialize its drive independent registers.
*/
-static unsigned int init_chipset_via82cxxx(struct pci_dev *dev)
+static int init_chipset_via82cxxx(struct pci_dev *dev)
{
struct ide_host *host = pci_get_drvdata(dev);
struct via82cxxx_dev *vdev = host->host_priv;
@@ -443,16 +443,6 @@
if ((via_config->flags & VIA_NO_UNMASK) == 0)
d.host_flags |= IDE_HFLAG_UNMASK_IRQS;
-#ifdef CONFIG_PPC_CHRP
- if (machine_is(chrp) && _chrp_type == _CHRP_Pegasos)
- d.host_flags |= IDE_HFLAG_FORCE_LEGACY_IRQS;
-#endif
-
-#ifdef CONFIG_AMIGAONE
- if (machine_is(amigaone))
- d.host_flags |= IDE_HFLAG_FORCE_LEGACY_IRQS;
-#endif
-
d.udma_mask = via_config->udma_mask;
vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
diff --git a/drivers/input/joystick/maplecontrol.c b/drivers/input/joystick/maplecontrol.c
index e50047b..77cfde5 100644
--- a/drivers/input/joystick/maplecontrol.c
+++ b/drivers/input/joystick/maplecontrol.c
@@ -3,7 +3,7 @@
* Based on drivers/usb/iforce.c
*
* Copyright Yaegashi Takeshi, 2001
- * Adrian McMenamin, 2008
+ * Adrian McMenamin, 2008 - 2009
*/
#include <linux/kernel.h>
@@ -29,7 +29,7 @@
struct maple_device *mapledev = mq->dev;
struct dc_pad *pad = maple_get_drvdata(mapledev);
struct input_dev *dev = pad->dev;
- unsigned char *res = mq->recvbuf;
+ unsigned char *res = mq->recvbuf->buf;
buttons = ~le16_to_cpup((__le16 *)(res + 8));
diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c
index 22f17a5..5aa2361 100644
--- a/drivers/input/keyboard/maple_keyb.c
+++ b/drivers/input/keyboard/maple_keyb.c
@@ -1,8 +1,8 @@
/*
* SEGA Dreamcast keyboard driver
* Based on drivers/usb/usbkbd.c
- * Copyright YAEGASHI Takeshi, 2001
- * Porting to 2.6 Copyright Adrian McMenamin, 2007, 2008
+ * Copyright (c) YAEGASHI Takeshi, 2001
+ * Porting to 2.6 Copyright (c) Adrian McMenamin, 2007 - 2009
*
* 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
@@ -33,7 +33,7 @@
#define NR_SCANCODES 256
-MODULE_AUTHOR("YAEGASHI Takeshi, Adrian McMenamin");
+MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk");
MODULE_DESCRIPTION("SEGA Dreamcast keyboard driver");
MODULE_LICENSE("GPL");
@@ -115,7 +115,7 @@
input_event(dev, EV_MSC, MSC_SCAN, code);
input_report_key(dev, keycode, 0);
} else
- printk(KERN_DEBUG "maple_keyb: "
+ dev_dbg(&dev->dev,
"Unknown key (scancode %#x) released.",
code);
}
@@ -127,7 +127,7 @@
input_event(dev, EV_MSC, MSC_SCAN, code);
input_report_key(dev, keycode, 1);
} else
- printk(KERN_DEBUG "maple_keyb: "
+ dev_dbg(&dev->dev,
"Unknown key (scancode %#x) pressed.",
code);
}
@@ -140,7 +140,7 @@
{
struct maple_device *mapledev = mq->dev;
struct dc_kbd *kbd = maple_get_drvdata(mapledev);
- unsigned long *buf = mq->recvbuf;
+ unsigned long *buf = (unsigned long *)(mq->recvbuf->buf);
/*
* We should always get the lock because the only
@@ -159,22 +159,27 @@
static int probe_maple_kbd(struct device *dev)
{
- struct maple_device *mdev = to_maple_dev(dev);
- struct maple_driver *mdrv = to_maple_driver(dev->driver);
+ struct maple_device *mdev;
+ struct maple_driver *mdrv;
int i, error;
struct dc_kbd *kbd;
struct input_dev *idev;
- if (!(mdev->function & MAPLE_FUNC_KEYBOARD))
- return -EINVAL;
+ mdev = to_maple_dev(dev);
+ mdrv = to_maple_driver(dev->driver);
kbd = kzalloc(sizeof(struct dc_kbd), GFP_KERNEL);
- idev = input_allocate_device();
- if (!kbd || !idev) {
+ if (!kbd) {
error = -ENOMEM;
goto fail;
}
+ idev = input_allocate_device();
+ if (!idev) {
+ error = -ENOMEM;
+ goto fail_idev_alloc;
+ }
+
kbd->dev = idev;
memcpy(kbd->keycode, dc_kbd_keycode, sizeof(kbd->keycode));
@@ -195,7 +200,7 @@
error = input_register_device(idev);
if (error)
- goto fail;
+ goto fail_register;
/* Maple polling is locked to VBLANK - which may be just 50/s */
maple_getcond_callback(mdev, dc_kbd_callback, HZ/50,
@@ -207,10 +212,12 @@
return error;
-fail:
- input_free_device(idev);
- kfree(kbd);
+fail_register:
maple_set_drvdata(mdev, NULL);
+ input_free_device(idev);
+fail_idev_alloc:
+ kfree(kbd);
+fail:
return error;
}
diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c
index 5c8a1bc..e1480fb 100644
--- a/drivers/input/keyboard/sh_keysc.c
+++ b/drivers/input/keyboard/sh_keysc.c
@@ -219,6 +219,8 @@
pdata->scan_timing, priv->iomem_base + KYCR1_OFFS);
iowrite16(0, priv->iomem_base + KYOUTDR_OFFS);
iowrite16(KYCR2_IRQ_LEVEL, priv->iomem_base + KYCR2_OFFS);
+
+ device_init_wakeup(&pdev->dev, 1);
return 0;
err5:
free_irq(irq, pdev);
@@ -253,17 +255,33 @@
return 0;
}
+static int sh_keysc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
+ unsigned short value;
-#define sh_keysc_suspend NULL
-#define sh_keysc_resume NULL
+ value = ioread16(priv->iomem_base + KYCR1_OFFS);
+
+ if (device_may_wakeup(dev))
+ value |= 0x80;
+ else
+ value &= ~0x80;
+
+ iowrite16(value, priv->iomem_base + KYCR1_OFFS);
+ return 0;
+}
+
+static struct dev_pm_ops sh_keysc_dev_pm_ops = {
+ .suspend = sh_keysc_suspend,
+};
struct platform_driver sh_keysc_device_driver = {
.probe = sh_keysc_probe,
.remove = __devexit_p(sh_keysc_remove),
- .suspend = sh_keysc_suspend,
- .resume = sh_keysc_resume,
.driver = {
.name = "sh_keysc",
+ .pm = &sh_keysc_dev_pm_ops,
}
};
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c
index 66c755c..ce98d95 100644
--- a/drivers/media/video/cx88/cx88-alsa.c
+++ b/drivers/media/video/cx88/cx88-alsa.c
@@ -803,9 +803,10 @@
return (-ENOENT);
}
- card = snd_card_new(index[devno], id[devno], THIS_MODULE, sizeof(snd_cx88_card_t));
- if (!card)
- return (-ENOMEM);
+ err = snd_card_create(index[devno], id[devno], THIS_MODULE,
+ sizeof(snd_cx88_card_t), &card);
+ if (err < 0)
+ return err;
card->private_free = snd_cx88_dev_free;
diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c
index 2ac738f..f132e31 100644
--- a/drivers/media/video/em28xx/em28xx-audio.c
+++ b/drivers/media/video/em28xx/em28xx-audio.c
@@ -448,9 +448,10 @@
printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus "
"Rechberger\n");
- card = snd_card_new(index[devnr], "Em28xx Audio", THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[devnr], "Em28xx Audio", THIS_MODULE, 0,
+ &card);
+ if (err < 0)
+ return err;
spin_lock_init(&adev->slock);
err = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm);
diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c
index c750d3d..8b0b64a 100644
--- a/drivers/media/video/saa7134/saa7134-alsa.c
+++ b/drivers/media/video/saa7134/saa7134-alsa.c
@@ -990,10 +990,10 @@
if (!enable[devnum])
return -ENODEV;
- card = snd_card_new(index[devnum], id[devnum], THIS_MODULE, sizeof(snd_card_saa7134_t));
-
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[devnum], id[devnum], THIS_MODULE,
+ sizeof(snd_card_saa7134_t), &card);
+ if (err < 0)
+ return err;
strcpy(card->driver, "SAA7134");
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 043d50f..729f899 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -551,5 +551,15 @@
This selection automatically selects the map_ram driver.
-endmenu
+config MTD_VMU
+ tristate "Map driver for Dreamcast VMU"
+ depends on MAPLE
+ help
+ This driver enables access to the Dreamcast Visual Memory Unit (VMU).
+ Most Dreamcast users will want to say Y here.
+
+ To build this as a module select M here, the module will be called
+ vmu-flash.
+
+endmenu
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index 6d9ba35..26b28a7 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -61,3 +61,4 @@
obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o
obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o
+obj-$(CONFIG_MTD_VMU) += vmu-flash.o
diff --git a/drivers/mtd/maps/vmu-flash.c b/drivers/mtd/maps/vmu-flash.c
new file mode 100644
index 0000000..1f73297
--- /dev/null
+++ b/drivers/mtd/maps/vmu-flash.c
@@ -0,0 +1,832 @@
+/* vmu-flash.c
+ * Driver for SEGA Dreamcast Visual Memory Unit
+ *
+ * Copyright (c) Adrian McMenamin 2002 - 2009
+ * Copyright (c) Paul Mundt 2001
+ *
+ * Licensed under version 2 of the
+ * GNU General Public Licence
+ */
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/maple.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+
+struct vmu_cache {
+ unsigned char *buffer; /* Cache */
+ unsigned int block; /* Which block was cached */
+ unsigned long jiffies_atc; /* When was it cached? */
+ int valid;
+};
+
+struct mdev_part {
+ struct maple_device *mdev;
+ int partition;
+};
+
+struct vmupart {
+ u16 user_blocks;
+ u16 root_block;
+ u16 numblocks;
+ char *name;
+ struct vmu_cache *pcache;
+};
+
+struct memcard {
+ u16 tempA;
+ u16 tempB;
+ u32 partitions;
+ u32 blocklen;
+ u32 writecnt;
+ u32 readcnt;
+ u32 removeable;
+ int partition;
+ int read;
+ unsigned char *blockread;
+ struct vmupart *parts;
+ struct mtd_info *mtd;
+};
+
+struct vmu_block {
+ unsigned int num; /* block number */
+ unsigned int ofs; /* block offset */
+};
+
+static struct vmu_block *ofs_to_block(unsigned long src_ofs,
+ struct mtd_info *mtd, int partition)
+{
+ struct vmu_block *vblock;
+ struct maple_device *mdev;
+ struct memcard *card;
+ struct mdev_part *mpart;
+ int num;
+
+ mpart = mtd->priv;
+ mdev = mpart->mdev;
+ card = maple_get_drvdata(mdev);
+
+ if (src_ofs >= card->parts[partition].numblocks * card->blocklen)
+ goto failed;
+
+ num = src_ofs / card->blocklen;
+ if (num > card->parts[partition].numblocks)
+ goto failed;
+
+ vblock = kmalloc(sizeof(struct vmu_block), GFP_KERNEL);
+ if (!vblock)
+ goto failed;
+
+ vblock->num = num;
+ vblock->ofs = src_ofs % card->blocklen;
+ return vblock;
+
+failed:
+ return NULL;
+}
+
+/* Maple bus callback function for reads */
+static void vmu_blockread(struct mapleq *mq)
+{
+ struct maple_device *mdev;
+ struct memcard *card;
+
+ mdev = mq->dev;
+ card = maple_get_drvdata(mdev);
+ /* copy the read in data */
+
+ if (unlikely(!card->blockread))
+ return;
+
+ memcpy(card->blockread, mq->recvbuf->buf + 12,
+ card->blocklen/card->readcnt);
+
+}
+
+/* Interface with maple bus to read blocks
+ * caching the results so that other parts
+ * of the driver can access block reads */
+static int maple_vmu_read_block(unsigned int num, unsigned char *buf,
+ struct mtd_info *mtd)
+{
+ struct memcard *card;
+ struct mdev_part *mpart;
+ struct maple_device *mdev;
+ int partition, error = 0, x, wait;
+ unsigned char *blockread = NULL;
+ struct vmu_cache *pcache;
+ __be32 sendbuf;
+
+ mpart = mtd->priv;
+ mdev = mpart->mdev;
+ partition = mpart->partition;
+ card = maple_get_drvdata(mdev);
+ pcache = card->parts[partition].pcache;
+ pcache->valid = 0;
+
+ /* prepare the cache for this block */
+ if (!pcache->buffer) {
+ pcache->buffer = kmalloc(card->blocklen, GFP_KERNEL);
+ if (!pcache->buffer) {
+ dev_err(&mdev->dev, "VMU at (%d, %d) - read fails due"
+ " to lack of memory\n", mdev->port,
+ mdev->unit);
+ error = -ENOMEM;
+ goto outB;
+ }
+ }
+
+ /*
+ * Reads may be phased - again the hardware spec
+ * supports this - though may not be any devices in
+ * the wild that implement it, but we will here
+ */
+ for (x = 0; x < card->readcnt; x++) {
+ sendbuf = cpu_to_be32(partition << 24 | x << 16 | num);
+
+ if (atomic_read(&mdev->busy) == 1) {
+ wait_event_interruptible_timeout(mdev->maple_wait,
+ atomic_read(&mdev->busy) == 0, HZ);
+ if (atomic_read(&mdev->busy) == 1) {
+ dev_notice(&mdev->dev, "VMU at (%d, %d)"
+ " is busy\n", mdev->port, mdev->unit);
+ error = -EAGAIN;
+ goto outB;
+ }
+ }
+
+ atomic_set(&mdev->busy, 1);
+ blockread = kmalloc(card->blocklen/card->readcnt, GFP_KERNEL);
+ if (!blockread) {
+ error = -ENOMEM;
+ atomic_set(&mdev->busy, 0);
+ goto outB;
+ }
+ card->blockread = blockread;
+
+ maple_getcond_callback(mdev, vmu_blockread, 0,
+ MAPLE_FUNC_MEMCARD);
+ error = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
+ MAPLE_COMMAND_BREAD, 2, &sendbuf);
+ /* Very long timeouts seem to be needed when box is stressed */
+ wait = wait_event_interruptible_timeout(mdev->maple_wait,
+ (atomic_read(&mdev->busy) == 0 ||
+ atomic_read(&mdev->busy) == 2), HZ * 3);
+ /*
+ * MTD layer does not handle hotplugging well
+ * so have to return errors when VMU is unplugged
+ * in the middle of a read (busy == 2)
+ */
+ if (error || atomic_read(&mdev->busy) == 2) {
+ if (atomic_read(&mdev->busy) == 2)
+ error = -ENXIO;
+ atomic_set(&mdev->busy, 0);
+ card->blockread = NULL;
+ goto outA;
+ }
+ if (wait == 0 || wait == -ERESTARTSYS) {
+ card->blockread = NULL;
+ atomic_set(&mdev->busy, 0);
+ error = -EIO;
+ list_del_init(&(mdev->mq->list));
+ kfree(mdev->mq->sendbuf);
+ mdev->mq->sendbuf = NULL;
+ if (wait == -ERESTARTSYS) {
+ dev_warn(&mdev->dev, "VMU read on (%d, %d)"
+ " interrupted on block 0x%X\n",
+ mdev->port, mdev->unit, num);
+ } else
+ dev_notice(&mdev->dev, "VMU read on (%d, %d)"
+ " timed out on block 0x%X\n",
+ mdev->port, mdev->unit, num);
+ goto outA;
+ }
+
+ memcpy(buf + (card->blocklen/card->readcnt) * x, blockread,
+ card->blocklen/card->readcnt);
+
+ memcpy(pcache->buffer + (card->blocklen/card->readcnt) * x,
+ card->blockread, card->blocklen/card->readcnt);
+ card->blockread = NULL;
+ pcache->block = num;
+ pcache->jiffies_atc = jiffies;
+ pcache->valid = 1;
+ kfree(blockread);
+ }
+
+ return error;
+
+outA:
+ kfree(blockread);
+outB:
+ return error;
+}
+
+/* communicate with maple bus for phased writing */
+static int maple_vmu_write_block(unsigned int num, const unsigned char *buf,
+ struct mtd_info *mtd)
+{
+ struct memcard *card;
+ struct mdev_part *mpart;
+ struct maple_device *mdev;
+ int partition, error, locking, x, phaselen, wait;
+ __be32 *sendbuf;
+
+ mpart = mtd->priv;
+ mdev = mpart->mdev;
+ partition = mpart->partition;
+ card = maple_get_drvdata(mdev);
+
+ phaselen = card->blocklen/card->writecnt;
+
+ sendbuf = kmalloc(phaselen + 4, GFP_KERNEL);
+ if (!sendbuf) {
+ error = -ENOMEM;
+ goto fail_nosendbuf;
+ }
+ for (x = 0; x < card->writecnt; x++) {
+ sendbuf[0] = cpu_to_be32(partition << 24 | x << 16 | num);
+ memcpy(&sendbuf[1], buf + phaselen * x, phaselen);
+ /* wait until the device is not busy doing something else
+ * or 1 second - which ever is longer */
+ if (atomic_read(&mdev->busy) == 1) {
+ wait_event_interruptible_timeout(mdev->maple_wait,
+ atomic_read(&mdev->busy) == 0, HZ);
+ if (atomic_read(&mdev->busy) == 1) {
+ error = -EBUSY;
+ dev_notice(&mdev->dev, "VMU write at (%d, %d)"
+ "failed - device is busy\n",
+ mdev->port, mdev->unit);
+ goto fail_nolock;
+ }
+ }
+ atomic_set(&mdev->busy, 1);
+
+ locking = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
+ MAPLE_COMMAND_BWRITE, phaselen / 4 + 2, sendbuf);
+ wait = wait_event_interruptible_timeout(mdev->maple_wait,
+ atomic_read(&mdev->busy) == 0, HZ/10);
+ if (locking) {
+ error = -EIO;
+ atomic_set(&mdev->busy, 0);
+ goto fail_nolock;
+ }
+ if (atomic_read(&mdev->busy) == 2) {
+ atomic_set(&mdev->busy, 0);
+ } else if (wait == 0 || wait == -ERESTARTSYS) {
+ error = -EIO;
+ dev_warn(&mdev->dev, "Write at (%d, %d) of block"
+ " 0x%X at phase %d failed: could not"
+ " communicate with VMU", mdev->port,
+ mdev->unit, num, x);
+ atomic_set(&mdev->busy, 0);
+ kfree(mdev->mq->sendbuf);
+ mdev->mq->sendbuf = NULL;
+ list_del_init(&(mdev->mq->list));
+ goto fail_nolock;
+ }
+ }
+ kfree(sendbuf);
+
+ return card->blocklen;
+
+fail_nolock:
+ kfree(sendbuf);
+fail_nosendbuf:
+ dev_err(&mdev->dev, "VMU (%d, %d): write failed\n", mdev->port,
+ mdev->unit);
+ return error;
+}
+
+/* mtd function to simulate reading byte by byte */
+static unsigned char vmu_flash_read_char(unsigned long ofs, int *retval,
+ struct mtd_info *mtd)
+{
+ struct vmu_block *vblock;
+ struct memcard *card;
+ struct mdev_part *mpart;
+ struct maple_device *mdev;
+ unsigned char *buf, ret;
+ int partition, error;
+
+ mpart = mtd->priv;
+ mdev = mpart->mdev;
+ partition = mpart->partition;
+ card = maple_get_drvdata(mdev);
+ *retval = 0;
+
+ buf = kmalloc(card->blocklen, GFP_KERNEL);
+ if (!buf) {
+ *retval = 1;
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+ vblock = ofs_to_block(ofs, mtd, partition);
+ if (!vblock) {
+ *retval = 3;
+ ret = -ENOMEM;
+ goto out_buf;
+ }
+
+ error = maple_vmu_read_block(vblock->num, buf, mtd);
+ if (error) {
+ ret = error;
+ *retval = 2;
+ goto out_vblock;
+ }
+
+ ret = buf[vblock->ofs];
+
+out_vblock:
+ kfree(vblock);
+out_buf:
+ kfree(buf);
+finish:
+ return ret;
+}
+
+/* mtd higher order function to read flash */
+static int vmu_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct maple_device *mdev;
+ struct memcard *card;
+ struct mdev_part *mpart;
+ struct vmu_cache *pcache;
+ struct vmu_block *vblock;
+ int index = 0, retval, partition, leftover, numblocks;
+ unsigned char cx;
+
+ if (len < 1)
+ return -EIO;
+
+ mpart = mtd->priv;
+ mdev = mpart->mdev;
+ partition = mpart->partition;
+ card = maple_get_drvdata(mdev);
+
+ numblocks = card->parts[partition].numblocks;
+ if (from + len > numblocks * card->blocklen)
+ len = numblocks * card->blocklen - from;
+ if (len == 0)
+ return -EIO;
+ /* Have we cached this bit already? */
+ pcache = card->parts[partition].pcache;
+ do {
+ vblock = ofs_to_block(from + index, mtd, partition);
+ if (!vblock)
+ return -ENOMEM;
+ /* Have we cached this and is the cache valid and timely? */
+ if (pcache->valid &&
+ time_before(jiffies, pcache->jiffies_atc + HZ) &&
+ (pcache->block == vblock->num)) {
+ /* we have cached it, so do necessary copying */
+ leftover = card->blocklen - vblock->ofs;
+ if (vblock->ofs + len - index < card->blocklen) {
+ /* only a bit of this block to copy */
+ memcpy(buf + index,
+ pcache->buffer + vblock->ofs,
+ len - index);
+ index = len;
+ } else {
+ /* otherwise copy remainder of whole block */
+ memcpy(buf + index, pcache->buffer +
+ vblock->ofs, leftover);
+ index += leftover;
+ }
+ } else {
+ /*
+ * Not cached so read one byte -
+ * but cache the rest of the block
+ */
+ cx = vmu_flash_read_char(from + index, &retval, mtd);
+ if (retval) {
+ *retlen = index;
+ kfree(vblock);
+ return cx;
+ }
+ memset(buf + index, cx, 1);
+ index++;
+ }
+ kfree(vblock);
+ } while (len > index);
+ *retlen = index;
+
+ return 0;
+}
+
+static int vmu_flash_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct maple_device *mdev;
+ struct memcard *card;
+ struct mdev_part *mpart;
+ int index = 0, partition, error = 0, numblocks;
+ struct vmu_cache *pcache;
+ struct vmu_block *vblock;
+ unsigned char *buffer;
+
+ mpart = mtd->priv;
+ mdev = mpart->mdev;
+ partition = mpart->partition;
+ card = maple_get_drvdata(mdev);
+
+ /* simple sanity checks */
+ if (len < 1) {
+ error = -EIO;
+ goto failed;
+ }
+ numblocks = card->parts[partition].numblocks;
+ if (to + len > numblocks * card->blocklen)
+ len = numblocks * card->blocklen - to;
+ if (len == 0) {
+ error = -EIO;
+ goto failed;
+ }
+
+ vblock = ofs_to_block(to, mtd, partition);
+ if (!vblock) {
+ error = -ENOMEM;
+ goto failed;
+ }
+
+ buffer = kmalloc(card->blocklen, GFP_KERNEL);
+ if (!buffer) {
+ error = -ENOMEM;
+ goto fail_buffer;
+ }
+
+ do {
+ /* Read in the block we are to write to */
+ error = maple_vmu_read_block(vblock->num, buffer, mtd);
+ if (error)
+ goto fail_io;
+
+ do {
+ buffer[vblock->ofs] = buf[index];
+ vblock->ofs++;
+ index++;
+ if (index >= len)
+ break;
+ } while (vblock->ofs < card->blocklen);
+
+ /* write out new buffer */
+ error = maple_vmu_write_block(vblock->num, buffer, mtd);
+ /* invalidate the cache */
+ pcache = card->parts[partition].pcache;
+ pcache->valid = 0;
+
+ if (error != card->blocklen)
+ goto fail_io;
+
+ vblock->num++;
+ vblock->ofs = 0;
+ } while (len > index);
+
+ kfree(buffer);
+ *retlen = index;
+ kfree(vblock);
+ return 0;
+
+fail_io:
+ kfree(buffer);
+fail_buffer:
+ kfree(vblock);
+failed:
+ dev_err(&mdev->dev, "VMU write failing with error %d\n", error);
+ return error;
+}
+
+static void vmu_flash_sync(struct mtd_info *mtd)
+{
+ /* Do nothing here */
+}
+
+/* Maple bus callback function to recursively query hardware details */
+static void vmu_queryblocks(struct mapleq *mq)
+{
+ struct maple_device *mdev;
+ unsigned short *res;
+ struct memcard *card;
+ __be32 partnum;
+ struct vmu_cache *pcache;
+ struct mdev_part *mpart;
+ struct mtd_info *mtd_cur;
+ struct vmupart *part_cur;
+ int error;
+
+ mdev = mq->dev;
+ card = maple_get_drvdata(mdev);
+ res = (unsigned short *) (mq->recvbuf->buf);
+ card->tempA = res[12];
+ card->tempB = res[6];
+
+ dev_info(&mdev->dev, "VMU device at partition %d has %d user "
+ "blocks with a root block at %d\n", card->partition,
+ card->tempA, card->tempB);
+
+ part_cur = &card->parts[card->partition];
+ part_cur->user_blocks = card->tempA;
+ part_cur->root_block = card->tempB;
+ part_cur->numblocks = card->tempB + 1;
+ part_cur->name = kmalloc(12, GFP_KERNEL);
+ if (!part_cur->name)
+ goto fail_name;
+
+ sprintf(part_cur->name, "vmu%d.%d.%d",
+ mdev->port, mdev->unit, card->partition);
+ mtd_cur = &card->mtd[card->partition];
+ mtd_cur->name = part_cur->name;
+ mtd_cur->type = 8;
+ mtd_cur->flags = MTD_WRITEABLE|MTD_NO_ERASE;
+ mtd_cur->size = part_cur->numblocks * card->blocklen;
+ mtd_cur->erasesize = card->blocklen;
+ mtd_cur->write = vmu_flash_write;
+ mtd_cur->read = vmu_flash_read;
+ mtd_cur->sync = vmu_flash_sync;
+ mtd_cur->writesize = card->blocklen;
+
+ mpart = kmalloc(sizeof(struct mdev_part), GFP_KERNEL);
+ if (!mpart)
+ goto fail_mpart;
+
+ mpart->mdev = mdev;
+ mpart->partition = card->partition;
+ mtd_cur->priv = mpart;
+ mtd_cur->owner = THIS_MODULE;
+
+ pcache = kzalloc(sizeof(struct vmu_cache), GFP_KERNEL);
+ if (!pcache)
+ goto fail_cache_create;
+ part_cur->pcache = pcache;
+
+ error = add_mtd_device(mtd_cur);
+ if (error)
+ goto fail_mtd_register;
+
+ maple_getcond_callback(mdev, NULL, 0,
+ MAPLE_FUNC_MEMCARD);
+
+ /*
+ * Set up a recursive call to the (probably theoretical)
+ * second or more partition
+ */
+ if (++card->partition < card->partitions) {
+ partnum = cpu_to_be32(card->partition << 24);
+ maple_getcond_callback(mdev, vmu_queryblocks, 0,
+ MAPLE_FUNC_MEMCARD);
+ maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
+ MAPLE_COMMAND_GETMINFO, 2, &partnum);
+ }
+ return;
+
+fail_mtd_register:
+ dev_err(&mdev->dev, "Could not register maple device at (%d, %d)"
+ "error is 0x%X\n", mdev->port, mdev->unit, error);
+ for (error = 0; error <= card->partition; error++) {
+ kfree(((card->parts)[error]).pcache);
+ ((card->parts)[error]).pcache = NULL;
+ }
+fail_cache_create:
+fail_mpart:
+ for (error = 0; error <= card->partition; error++) {
+ kfree(((card->mtd)[error]).priv);
+ ((card->mtd)[error]).priv = NULL;
+ }
+ maple_getcond_callback(mdev, NULL, 0,
+ MAPLE_FUNC_MEMCARD);
+ kfree(part_cur->name);
+fail_name:
+ return;
+}
+
+/* Handles very basic info about the flash, queries for details */
+static int __devinit vmu_connect(struct maple_device *mdev)
+{
+ unsigned long test_flash_data, basic_flash_data;
+ int c, error;
+ struct memcard *card;
+ u32 partnum = 0;
+
+ test_flash_data = be32_to_cpu(mdev->devinfo.function);
+ /* Need to count how many bits are set - to find out which
+ * function_data element has details of the memory card:
+ * using Brian Kernighan's/Peter Wegner's method */
+ for (c = 0; test_flash_data; c++)
+ test_flash_data &= test_flash_data - 1;
+
+ basic_flash_data = be32_to_cpu(mdev->devinfo.function_data[c - 1]);
+
+ card = kmalloc(sizeof(struct memcard), GFP_KERNEL);
+ if (!card) {
+ error = ENOMEM;
+ goto fail_nomem;
+ }
+
+ card->partitions = (basic_flash_data >> 24 & 0xFF) + 1;
+ card->blocklen = ((basic_flash_data >> 16 & 0xFF) + 1) << 5;
+ card->writecnt = basic_flash_data >> 12 & 0xF;
+ card->readcnt = basic_flash_data >> 8 & 0xF;
+ card->removeable = basic_flash_data >> 7 & 1;
+
+ card->partition = 0;
+
+ /*
+ * Not sure there are actually any multi-partition devices in the
+ * real world, but the hardware supports them, so, so will we
+ */
+ card->parts = kmalloc(sizeof(struct vmupart) * card->partitions,
+ GFP_KERNEL);
+ if (!card->parts) {
+ error = -ENOMEM;
+ goto fail_partitions;
+ }
+
+ card->mtd = kmalloc(sizeof(struct mtd_info) * card->partitions,
+ GFP_KERNEL);
+ if (!card->mtd) {
+ error = -ENOMEM;
+ goto fail_mtd_info;
+ }
+
+ maple_set_drvdata(mdev, card);
+
+ /*
+ * We want to trap meminfo not get cond
+ * so set interval to zero, but rely on maple bus
+ * driver to pass back the results of the meminfo
+ */
+ maple_getcond_callback(mdev, vmu_queryblocks, 0,
+ MAPLE_FUNC_MEMCARD);
+
+ /* Make sure we are clear to go */
+ if (atomic_read(&mdev->busy) == 1) {
+ wait_event_interruptible_timeout(mdev->maple_wait,
+ atomic_read(&mdev->busy) == 0, HZ);
+ if (atomic_read(&mdev->busy) == 1) {
+ dev_notice(&mdev->dev, "VMU at (%d, %d) is busy\n",
+ mdev->port, mdev->unit);
+ error = -EAGAIN;
+ goto fail_device_busy;
+ }
+ }
+
+ atomic_set(&mdev->busy, 1);
+
+ /*
+ * Set up the minfo call: vmu_queryblocks will handle
+ * the information passed back
+ */
+ error = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
+ MAPLE_COMMAND_GETMINFO, 2, &partnum);
+ if (error) {
+ dev_err(&mdev->dev, "Could not lock VMU at (%d, %d)"
+ " error is 0x%X\n", mdev->port, mdev->unit, error);
+ goto fail_mtd_info;
+ }
+ return 0;
+
+fail_device_busy:
+ kfree(card->mtd);
+fail_mtd_info:
+ kfree(card->parts);
+fail_partitions:
+ kfree(card);
+fail_nomem:
+ return error;
+}
+
+static void __devexit vmu_disconnect(struct maple_device *mdev)
+{
+ struct memcard *card;
+ struct mdev_part *mpart;
+ int x;
+
+ mdev->callback = NULL;
+ card = maple_get_drvdata(mdev);
+ for (x = 0; x < card->partitions; x++) {
+ mpart = ((card->mtd)[x]).priv;
+ mpart->mdev = NULL;
+ del_mtd_device(&((card->mtd)[x]));
+ kfree(((card->parts)[x]).name);
+ }
+ kfree(card->parts);
+ kfree(card->mtd);
+ kfree(card);
+}
+
+/* Callback to handle eccentricities of both mtd subsystem
+ * and general flakyness of Dreamcast VMUs
+ */
+static int vmu_can_unload(struct maple_device *mdev)
+{
+ struct memcard *card;
+ int x;
+ struct mtd_info *mtd;
+
+ card = maple_get_drvdata(mdev);
+ for (x = 0; x < card->partitions; x++) {
+ mtd = &((card->mtd)[x]);
+ if (mtd->usecount > 0)
+ return 0;
+ }
+ return 1;
+}
+
+#define ERRSTR "VMU at (%d, %d) file error -"
+
+static void vmu_file_error(struct maple_device *mdev, void *recvbuf)
+{
+ enum maple_file_errors error = ((int *)recvbuf)[1];
+
+ switch (error) {
+
+ case MAPLE_FILEERR_INVALID_PARTITION:
+ dev_notice(&mdev->dev, ERRSTR " invalid partition number\n",
+ mdev->port, mdev->unit);
+ break;
+
+ case MAPLE_FILEERR_PHASE_ERROR:
+ dev_notice(&mdev->dev, ERRSTR " phase error\n",
+ mdev->port, mdev->unit);
+ break;
+
+ case MAPLE_FILEERR_INVALID_BLOCK:
+ dev_notice(&mdev->dev, ERRSTR " invalid block number\n",
+ mdev->port, mdev->unit);
+ break;
+
+ case MAPLE_FILEERR_WRITE_ERROR:
+ dev_notice(&mdev->dev, ERRSTR " write error\n",
+ mdev->port, mdev->unit);
+ break;
+
+ case MAPLE_FILEERR_INVALID_WRITE_LENGTH:
+ dev_notice(&mdev->dev, ERRSTR " invalid write length\n",
+ mdev->port, mdev->unit);
+ break;
+
+ case MAPLE_FILEERR_BAD_CRC:
+ dev_notice(&mdev->dev, ERRSTR " bad CRC\n",
+ mdev->port, mdev->unit);
+ break;
+
+ default:
+ dev_notice(&mdev->dev, ERRSTR " 0x%X\n",
+ mdev->port, mdev->unit, error);
+ }
+}
+
+
+static int __devinit probe_maple_vmu(struct device *dev)
+{
+ int error;
+ struct maple_device *mdev = to_maple_dev(dev);
+ struct maple_driver *mdrv = to_maple_driver(dev->driver);
+
+ mdev->can_unload = vmu_can_unload;
+ mdev->fileerr_handler = vmu_file_error;
+ mdev->driver = mdrv;
+
+ error = vmu_connect(mdev);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static int __devexit remove_maple_vmu(struct device *dev)
+{
+ struct maple_device *mdev = to_maple_dev(dev);
+
+ vmu_disconnect(mdev);
+ return 0;
+}
+
+static struct maple_driver vmu_flash_driver = {
+ .function = MAPLE_FUNC_MEMCARD,
+ .drv = {
+ .name = "Dreamcast_visual_memory",
+ .probe = probe_maple_vmu,
+ .remove = __devexit_p(remove_maple_vmu),
+ },
+};
+
+static int __init vmu_flash_map_init(void)
+{
+ return maple_driver_register(&vmu_flash_driver);
+}
+
+static void __exit vmu_flash_map_exit(void)
+{
+ maple_driver_unregister(&vmu_flash_driver);
+}
+
+module_init(vmu_flash_map_init);
+module_exit(vmu_flash_map_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Adrian McMenamin");
+MODULE_DESCRIPTION("Flash mapping for Sega Dreamcast visual memory");
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c
index 1c3fc6b..4898f7f 100644
--- a/drivers/rtc/rtc-sh.c
+++ b/drivers/rtc/rtc-sh.c
@@ -28,7 +28,7 @@
#include <asm/rtc.h>
#define DRV_NAME "sh-rtc"
-#define DRV_VERSION "0.2.0"
+#define DRV_VERSION "0.2.1"
#define RTC_REG(r) ((r) * rtc_reg_size)
@@ -99,56 +99,51 @@
unsigned short periodic_freq;
};
-static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id)
+static int __sh_rtc_interrupt(struct sh_rtc *rtc)
{
- struct sh_rtc *rtc = dev_id;
- unsigned int tmp;
-
- spin_lock(&rtc->lock);
+ unsigned int tmp, pending;
tmp = readb(rtc->regbase + RCR1);
+ pending = tmp & RCR1_CF;
tmp &= ~RCR1_CF;
writeb(tmp, rtc->regbase + RCR1);
/* Users have requested One x Second IRQ */
- if (rtc->periodic_freq & PF_OXS)
+ if (pending && rtc->periodic_freq & PF_OXS)
rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF);
- spin_unlock(&rtc->lock);
-
- return IRQ_HANDLED;
+ return pending;
}
-static irqreturn_t sh_rtc_alarm(int irq, void *dev_id)
+static int __sh_rtc_alarm(struct sh_rtc *rtc)
{
- struct sh_rtc *rtc = dev_id;
- unsigned int tmp;
-
- spin_lock(&rtc->lock);
+ unsigned int tmp, pending;
tmp = readb(rtc->regbase + RCR1);
+ pending = tmp & RCR1_AF;
tmp &= ~(RCR1_AF | RCR1_AIE);
- writeb(tmp, rtc->regbase + RCR1);
+ writeb(tmp, rtc->regbase + RCR1);
- rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
+ if (pending)
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
- spin_unlock(&rtc->lock);
-
- return IRQ_HANDLED;
+ return pending;
}
-static irqreturn_t sh_rtc_periodic(int irq, void *dev_id)
+static int __sh_rtc_periodic(struct sh_rtc *rtc)
{
- struct sh_rtc *rtc = dev_id;
struct rtc_device *rtc_dev = rtc->rtc_dev;
- unsigned int tmp;
-
- spin_lock(&rtc->lock);
+ struct rtc_task *irq_task;
+ unsigned int tmp, pending;
tmp = readb(rtc->regbase + RCR2);
+ pending = tmp & RCR2_PEF;
tmp &= ~RCR2_PEF;
writeb(tmp, rtc->regbase + RCR2);
+ if (!pending)
+ return 0;
+
/* Half period enabled than one skipped and the next notified */
if ((rtc->periodic_freq & PF_HP) && (rtc->periodic_freq & PF_COUNT))
rtc->periodic_freq &= ~PF_COUNT;
@@ -157,16 +152,65 @@
rtc->periodic_freq |= PF_COUNT;
if (rtc->periodic_freq & PF_KOU) {
spin_lock(&rtc_dev->irq_task_lock);
- if (rtc_dev->irq_task)
- rtc_dev->irq_task->func(rtc_dev->irq_task->private_data);
+ irq_task = rtc_dev->irq_task;
+ if (irq_task)
+ irq_task->func(irq_task->private_data);
spin_unlock(&rtc_dev->irq_task_lock);
} else
rtc_update_irq(rtc->rtc_dev, 1, RTC_PF | RTC_IRQF);
}
+ return pending;
+}
+
+static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id)
+{
+ struct sh_rtc *rtc = dev_id;
+ int ret;
+
+ spin_lock(&rtc->lock);
+ ret = __sh_rtc_interrupt(rtc);
spin_unlock(&rtc->lock);
- return IRQ_HANDLED;
+ return IRQ_RETVAL(ret);
+}
+
+static irqreturn_t sh_rtc_alarm(int irq, void *dev_id)
+{
+ struct sh_rtc *rtc = dev_id;
+ int ret;
+
+ spin_lock(&rtc->lock);
+ ret = __sh_rtc_alarm(rtc);
+ spin_unlock(&rtc->lock);
+
+ return IRQ_RETVAL(ret);
+}
+
+static irqreturn_t sh_rtc_periodic(int irq, void *dev_id)
+{
+ struct sh_rtc *rtc = dev_id;
+ int ret;
+
+ spin_lock(&rtc->lock);
+ ret = __sh_rtc_periodic(rtc);
+ spin_unlock(&rtc->lock);
+
+ return IRQ_RETVAL(ret);
+}
+
+static irqreturn_t sh_rtc_shared(int irq, void *dev_id)
+{
+ struct sh_rtc *rtc = dev_id;
+ int ret;
+
+ spin_lock(&rtc->lock);
+ ret = __sh_rtc_interrupt(rtc);
+ ret |= __sh_rtc_alarm(rtc);
+ ret |= __sh_rtc_periodic(rtc);
+ spin_unlock(&rtc->lock);
+
+ return IRQ_RETVAL(ret);
}
static inline void sh_rtc_setpie(struct device *dev, unsigned int enable)
@@ -275,6 +319,25 @@
return 0;
}
+static inline void sh_rtc_setcie(struct device *dev, unsigned int enable)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int tmp;
+
+ spin_lock_irq(&rtc->lock);
+
+ tmp = readb(rtc->regbase + RCR1);
+
+ if (!enable)
+ tmp &= ~RCR1_CIE;
+ else
+ tmp |= RCR1_CIE;
+
+ writeb(tmp, rtc->regbase + RCR1);
+
+ spin_unlock_irq(&rtc->lock);
+}
+
static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
struct sh_rtc *rtc = dev_get_drvdata(dev);
@@ -291,9 +354,11 @@
break;
case RTC_UIE_OFF:
rtc->periodic_freq &= ~PF_OXS;
+ sh_rtc_setcie(dev, 0);
break;
case RTC_UIE_ON:
rtc->periodic_freq |= PF_OXS;
+ sh_rtc_setcie(dev, 1);
break;
case RTC_IRQP_READ:
ret = put_user(rtc->rtc_dev->irq_freq,
@@ -356,18 +421,17 @@
tm->tm_sec--;
#endif
+ /* only keep the carry interrupt enabled if UIE is on */
+ if (!(rtc->periodic_freq & PF_OXS))
+ sh_rtc_setcie(dev, 0);
+
dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__func__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday);
- if (rtc_valid_tm(tm) < 0) {
- dev_err(dev, "invalid date\n");
- rtc_time_to_tm(0, tm);
- }
-
- return 0;
+ return rtc_valid_tm(tm);
}
static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -572,7 +636,7 @@
{
struct sh_rtc *rtc;
struct resource *res;
- unsigned int tmp;
+ struct rtc_time r;
int ret;
rtc = kzalloc(sizeof(struct sh_rtc), GFP_KERNEL);
@@ -585,26 +649,12 @@
ret = platform_get_irq(pdev, 0);
if (unlikely(ret <= 0)) {
ret = -ENOENT;
- dev_err(&pdev->dev, "No IRQ for period\n");
+ dev_err(&pdev->dev, "No IRQ resource\n");
goto err_badres;
}
rtc->periodic_irq = ret;
-
- ret = platform_get_irq(pdev, 1);
- if (unlikely(ret <= 0)) {
- ret = -ENOENT;
- dev_err(&pdev->dev, "No IRQ for carry\n");
- goto err_badres;
- }
- rtc->carry_irq = ret;
-
- ret = platform_get_irq(pdev, 2);
- if (unlikely(ret <= 0)) {
- ret = -ENOENT;
- dev_err(&pdev->dev, "No IRQ for alarm\n");
- goto err_badres;
- }
- rtc->alarm_irq = ret;
+ rtc->carry_irq = platform_get_irq(pdev, 1);
+ rtc->alarm_irq = platform_get_irq(pdev, 2);
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (unlikely(res == NULL)) {
@@ -646,47 +696,66 @@
}
rtc->rtc_dev->max_user_freq = 256;
- rtc->rtc_dev->irq_freq = 1;
- rtc->periodic_freq = 0x60;
platform_set_drvdata(pdev, rtc);
- /* register periodic/carry/alarm irqs */
- ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, IRQF_DISABLED,
- "sh-rtc period", rtc);
- if (unlikely(ret)) {
- dev_err(&pdev->dev,
- "request period IRQ failed with %d, IRQ %d\n", ret,
- rtc->periodic_irq);
- goto err_unmap;
+ if (rtc->carry_irq <= 0) {
+ /* register shared periodic/carry/alarm irq */
+ ret = request_irq(rtc->periodic_irq, sh_rtc_shared,
+ IRQF_DISABLED, "sh-rtc", rtc);
+ if (unlikely(ret)) {
+ dev_err(&pdev->dev,
+ "request IRQ failed with %d, IRQ %d\n", ret,
+ rtc->periodic_irq);
+ goto err_unmap;
+ }
+ } else {
+ /* register periodic/carry/alarm irqs */
+ ret = request_irq(rtc->periodic_irq, sh_rtc_periodic,
+ IRQF_DISABLED, "sh-rtc period", rtc);
+ if (unlikely(ret)) {
+ dev_err(&pdev->dev,
+ "request period IRQ failed with %d, IRQ %d\n",
+ ret, rtc->periodic_irq);
+ goto err_unmap;
+ }
+
+ ret = request_irq(rtc->carry_irq, sh_rtc_interrupt,
+ IRQF_DISABLED, "sh-rtc carry", rtc);
+ if (unlikely(ret)) {
+ dev_err(&pdev->dev,
+ "request carry IRQ failed with %d, IRQ %d\n",
+ ret, rtc->carry_irq);
+ free_irq(rtc->periodic_irq, rtc);
+ goto err_unmap;
+ }
+
+ ret = request_irq(rtc->alarm_irq, sh_rtc_alarm,
+ IRQF_DISABLED, "sh-rtc alarm", rtc);
+ if (unlikely(ret)) {
+ dev_err(&pdev->dev,
+ "request alarm IRQ failed with %d, IRQ %d\n",
+ ret, rtc->alarm_irq);
+ free_irq(rtc->carry_irq, rtc);
+ free_irq(rtc->periodic_irq, rtc);
+ goto err_unmap;
+ }
}
- ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, IRQF_DISABLED,
- "sh-rtc carry", rtc);
- if (unlikely(ret)) {
- dev_err(&pdev->dev,
- "request carry IRQ failed with %d, IRQ %d\n", ret,
- rtc->carry_irq);
- free_irq(rtc->periodic_irq, rtc);
- goto err_unmap;
+ /* everything disabled by default */
+ rtc->periodic_freq = 0;
+ rtc->rtc_dev->irq_freq = 0;
+ sh_rtc_setpie(&pdev->dev, 0);
+ sh_rtc_setaie(&pdev->dev, 0);
+ sh_rtc_setcie(&pdev->dev, 0);
+
+ /* reset rtc to epoch 0 if time is invalid */
+ if (rtc_read_time(rtc->rtc_dev, &r) < 0) {
+ rtc_time_to_tm(0, &r);
+ rtc_set_time(rtc->rtc_dev, &r);
}
- ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, IRQF_DISABLED,
- "sh-rtc alarm", rtc);
- if (unlikely(ret)) {
- dev_err(&pdev->dev,
- "request alarm IRQ failed with %d, IRQ %d\n", ret,
- rtc->alarm_irq);
- free_irq(rtc->carry_irq, rtc);
- free_irq(rtc->periodic_irq, rtc);
- goto err_unmap;
- }
-
- tmp = readb(rtc->regbase + RCR1);
- tmp &= ~RCR1_CF;
- tmp |= RCR1_CIE;
- writeb(tmp, rtc->regbase + RCR1);
-
+ device_init_wakeup(&pdev->dev, 1);
return 0;
err_unmap:
@@ -708,10 +777,13 @@
sh_rtc_setpie(&pdev->dev, 0);
sh_rtc_setaie(&pdev->dev, 0);
+ sh_rtc_setcie(&pdev->dev, 0);
- free_irq(rtc->carry_irq, rtc);
free_irq(rtc->periodic_irq, rtc);
- free_irq(rtc->alarm_irq, rtc);
+ if (rtc->carry_irq > 0) {
+ free_irq(rtc->carry_irq, rtc);
+ free_irq(rtc->alarm_irq, rtc);
+ }
release_resource(rtc->res);
diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c
index 557b54a..dbf5357 100644
--- a/drivers/serial/sh-sci.c
+++ b/drivers/serial/sh-sci.c
@@ -139,7 +139,7 @@
} while (!(status & SCxSR_TDxE(port)));
sci_in(port, SCxSR); /* Dummy read */
- sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
+ sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port));
sci_out(port, SCxTDR, c);
}
#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */
@@ -263,6 +263,7 @@
#elif defined(CONFIG_CPU_SUBTYPE_SH7763) || \
defined(CONFIG_CPU_SUBTYPE_SH7780) || \
defined(CONFIG_CPU_SUBTYPE_SH7785) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7786) || \
defined(CONFIG_CPU_SUBTYPE_SHX3)
static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
{
@@ -284,7 +285,8 @@
#if defined(CONFIG_CPU_SUBTYPE_SH7760) || \
defined(CONFIG_CPU_SUBTYPE_SH7780) || \
- defined(CONFIG_CPU_SUBTYPE_SH7785)
+ defined(CONFIG_CPU_SUBTYPE_SH7785) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7786)
static inline int scif_txroom(struct uart_port *port)
{
return SCIF_TXROOM_MAX - (sci_in(port, SCTFDR) & 0xff);
@@ -1095,6 +1097,7 @@
unsigned count)
{
struct uart_port *port = &serial_console_port->port;
+ unsigned short bits;
int i;
for (i = 0; i < count; i++) {
@@ -1103,6 +1106,11 @@
sci_poll_put_char(port, *s++);
}
+
+ /* wait until fifo is empty and last bit has been transmitted */
+ bits = SCxSR_TDxE(port) | SCxSR_TEND(port);
+ while ((sci_in(port, SCxSR) & bits) != bits)
+ cpu_relax();
}
static int __init serial_console_setup(struct console *co, char *options)
diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h
index 022e89f..d0aa82d 100644
--- a/drivers/serial/sh-sci.h
+++ b/drivers/serial/sh-sci.h
@@ -1,6 +1,6 @@
#include <linux/serial_core.h>
#include <asm/io.h>
-#include <asm/gpio.h>
+#include <linux/gpio.h>
#if defined(CONFIG_H83007) || defined(CONFIG_H83068)
#include <asm/regs306x.h>
@@ -126,7 +126,8 @@
# define SCSPTR1 0xffe10024 /* 16 bit SCIF */
# define SCIF_ORER 0x0001 /* Overrun error bit */
# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7785)
+#elif defined(CONFIG_CPU_SUBTYPE_SH7785) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7786)
# define SCSPTR0 0xffea0024 /* 16 bit SCIF */
# define SCSPTR1 0xffeb0024 /* 16 bit SCIF */
# define SCSPTR2 0xffec0024 /* 16 bit SCIF */
@@ -182,6 +183,7 @@
defined(CONFIG_CPU_SUBTYPE_SH7763) || \
defined(CONFIG_CPU_SUBTYPE_SH7780) || \
defined(CONFIG_CPU_SUBTYPE_SH7785) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7786) || \
defined(CONFIG_CPU_SUBTYPE_SHX3)
#define SCI_CTRL_FLAGS_REIE 0x08 /* 7750 SCIF */
#else
@@ -413,7 +415,8 @@
SCIF_FNS(SCFCR, 0x0c, 8, 0x18, 16)
#if defined(CONFIG_CPU_SUBTYPE_SH7760) || \
defined(CONFIG_CPU_SUBTYPE_SH7780) || \
- defined(CONFIG_CPU_SUBTYPE_SH7785)
+ defined(CONFIG_CPU_SUBTYPE_SH7785) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7786)
SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16)
SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16)
SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16)
@@ -644,7 +647,8 @@
return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
return 1;
}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7785)
+#elif defined(CONFIG_CPU_SUBTYPE_SH7785) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7786)
static inline int sci_rxd_in(struct uart_port *port)
{
if (port->mapbase == 0xffea0000)
@@ -746,7 +750,8 @@
*/
#if defined(CONFIG_CPU_SUBTYPE_SH7780) || \
- defined(CONFIG_CPU_SUBTYPE_SH7785)
+ defined(CONFIG_CPU_SUBTYPE_SH7785) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7786)
#define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(16*bps)-1)
#elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \
defined(CONFIG_CPU_SUBTYPE_SH7720) || \
diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c
index 58d24c5..2269fbc 100644
--- a/drivers/sh/intc.c
+++ b/drivers/sh/intc.c
@@ -568,6 +568,10 @@
if (!data[0] && data[1])
primary = 1;
+ if (!data[0] && !data[1])
+ pr_warning("intc: missing unique irq mask for "
+ "irq %d (vect 0x%04x)\n", irq, irq2evt(irq));
+
data[0] = data[0] ? data[0] : intc_mask_data(desc, d, enum_id, 1);
data[1] = data[1] ? data[1] : intc_prio_data(desc, d, enum_id, 1);
@@ -641,6 +645,17 @@
return 0;
}
+static unsigned char *intc_evt2irq_table;
+
+unsigned int intc_evt2irq(unsigned int vector)
+{
+ unsigned int irq = evt2irq(vector);
+
+ if (intc_evt2irq_table && intc_evt2irq_table[irq])
+ irq = intc_evt2irq_table[irq];
+
+ return irq;
+}
void __init register_intc_controller(struct intc_desc *desc)
{
@@ -705,8 +720,40 @@
BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */
+ /* keep the first vector only if same enum is used multiple times */
for (i = 0; i < desc->nr_vectors; i++) {
struct intc_vect *vect = desc->vectors + i;
+ int first_irq = evt2irq(vect->vect);
+
+ if (!vect->enum_id)
+ continue;
+
+ for (k = i + 1; k < desc->nr_vectors; k++) {
+ struct intc_vect *vect2 = desc->vectors + k;
+
+ if (vect->enum_id != vect2->enum_id)
+ continue;
+
+ vect2->enum_id = 0;
+
+ if (!intc_evt2irq_table)
+ intc_evt2irq_table = alloc_bootmem(NR_IRQS);
+
+ if (!intc_evt2irq_table) {
+ pr_warning("intc: cannot allocate evt2irq!\n");
+ continue;
+ }
+
+ intc_evt2irq_table[evt2irq(vect2->vect)] = first_irq;
+ }
+ }
+
+ /* register the vectors one by one */
+ for (i = 0; i < desc->nr_vectors; i++) {
+ struct intc_vect *vect = desc->vectors + i;
+
+ if (!vect->enum_id)
+ continue;
intc_register_irq(desc, d, vect->enum_id, evt2irq(vect->vect));
}
diff --git a/drivers/sh/maple/maple.c b/drivers/sh/maple/maple.c
index 7e1257a..cab1ab7 100644
--- a/drivers/sh/maple/maple.c
+++ b/drivers/sh/maple/maple.c
@@ -1,16 +1,10 @@
/*
* Core maple bus functionality
*
- * Copyright (C) 2007, 2008 Adrian McMenamin
+ * Copyright (C) 2007 - 2009 Adrian McMenamin
* Copyright (C) 2001 - 2008 Paul Mundt
- *
- * Based on 2.4 code by:
- *
- * Copyright (C) 2000-2001 YAEGASHI Takeshi
+ * Copyright (C) 2000 - 2001 YAEGASHI Takeshi
* Copyright (C) 2001 M. R. Brown
- * Copyright (C) 2001 Paul Mundt
- *
- * and others.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
@@ -32,7 +26,7 @@
#include <mach/dma.h>
#include <mach/sysasic.h>
-MODULE_AUTHOR("Yaegashi Takeshi, Paul Mundt, M. R. Brown, Adrian McMenamin");
+MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
MODULE_DESCRIPTION("Maple bus driver for Dreamcast");
MODULE_LICENSE("GPL v2");
MODULE_SUPPORTED_DEVICE("{{SEGA, Dreamcast/Maple}}");
@@ -49,7 +43,7 @@
/* mutex to protect queue of waiting packets */
static DEFINE_MUTEX(maple_wlist_lock);
-static struct maple_driver maple_dummy_driver;
+static struct maple_driver maple_unsupported_device;
static struct device maple_bus;
static int subdevice_map[MAPLE_PORTS];
static unsigned long *maple_sendbuf, *maple_sendptr, *maple_lastptr;
@@ -62,8 +56,9 @@
int unit;
};
-static bool checked[4];
-static struct maple_device *baseunits[4];
+static bool checked[MAPLE_PORTS];
+static bool empty[MAPLE_PORTS];
+static struct maple_device *baseunits[MAPLE_PORTS];
/**
* maple_driver_register - register a maple driver
@@ -97,12 +92,20 @@
EXPORT_SYMBOL_GPL(maple_driver_unregister);
/* set hardware registers to enable next round of dma */
-static void maplebus_dma_reset(void)
+static void maple_dma_reset(void)
{
ctrl_outl(MAPLE_MAGIC, MAPLE_RESET);
/* set trig type to 0 for software trigger, 1 for hardware (VBLANK) */
ctrl_outl(1, MAPLE_TRIGTYPE);
- ctrl_outl(MAPLE_2MBPS | MAPLE_TIMEOUT(50000), MAPLE_SPEED);
+ /*
+ * Maple system register
+ * bits 31 - 16 timeout in units of 20nsec
+ * bit 12 hard trigger - set 0 to keep responding to VBLANK
+ * bits 9 - 8 set 00 for 2 Mbps, 01 for 1 Mbps
+ * bits 3 - 0 delay (in 1.3ms) between VBLANK and start of DMA
+ * max delay is 11
+ */
+ ctrl_outl(MAPLE_2MBPS | MAPLE_TIMEOUT(0xFFFF), MAPLE_SPEED);
ctrl_outl(PHYSADDR(maple_sendbuf), MAPLE_DMAADDR);
ctrl_outl(1, MAPLE_ENABLE);
}
@@ -134,21 +137,16 @@
{
struct maple_device *mdev;
struct mapleq *mq;
- if (!dev)
- return;
+
mdev = to_maple_dev(dev);
mq = mdev->mq;
- if (mq) {
- if (mq->recvbufdcsp)
- kmem_cache_free(maple_queue_cache, mq->recvbufdcsp);
- kfree(mq);
- mq = NULL;
- }
+ kmem_cache_free(maple_queue_cache, mq->recvbuf);
+ kfree(mq);
kfree(mdev);
}
/**
- * maple_add_packet - add a single instruction to the queue
+ * maple_add_packet - add a single instruction to the maple bus queue
* @mdev: maple device
* @function: function on device being queried
* @command: maple command to add
@@ -158,23 +156,12 @@
int maple_add_packet(struct maple_device *mdev, u32 function, u32 command,
size_t length, void *data)
{
- int locking, ret = 0;
+ int ret = 0;
void *sendbuf = NULL;
- mutex_lock(&maple_wlist_lock);
- /* bounce if device already locked */
- locking = mutex_is_locked(&mdev->mq->mutex);
- if (locking) {
- ret = -EBUSY;
- goto out;
- }
-
- mutex_lock(&mdev->mq->mutex);
-
if (length) {
- sendbuf = kmalloc(length * 4, GFP_KERNEL);
+ sendbuf = kzalloc(length * 4, GFP_KERNEL);
if (!sendbuf) {
- mutex_unlock(&mdev->mq->mutex);
ret = -ENOMEM;
goto out;
}
@@ -187,84 +174,36 @@
memcpy(sendbuf + 4, data, (length - 1) * 4);
mdev->mq->sendbuf = sendbuf;
- list_add(&mdev->mq->list, &maple_waitq);
-out:
+ mutex_lock(&maple_wlist_lock);
+ list_add_tail(&mdev->mq->list, &maple_waitq);
mutex_unlock(&maple_wlist_lock);
+out:
return ret;
}
EXPORT_SYMBOL_GPL(maple_add_packet);
-/**
- * maple_add_packet_sleeps - add a single instruction to the queue
- * @mdev: maple device
- * @function: function on device being queried
- * @command: maple command to add
- * @length: length of command string (in 32 bit words)
- * @data: remainder of command string
- *
- * Same as maple_add_packet(), but waits for the lock to become free.
- */
-int maple_add_packet_sleeps(struct maple_device *mdev, u32 function,
- u32 command, size_t length, void *data)
-{
- int locking, ret = 0;
- void *sendbuf = NULL;
-
- locking = mutex_lock_interruptible(&mdev->mq->mutex);
- if (locking) {
- ret = -EIO;
- goto out;
- }
-
- if (length) {
- sendbuf = kmalloc(length * 4, GFP_KERNEL);
- if (!sendbuf) {
- mutex_unlock(&mdev->mq->mutex);
- ret = -ENOMEM;
- goto out;
- }
- ((__be32 *)sendbuf)[0] = cpu_to_be32(function);
- }
-
- mdev->mq->command = command;
- mdev->mq->length = length;
- if (length > 1)
- memcpy(sendbuf + 4, data, (length - 1) * 4);
- mdev->mq->sendbuf = sendbuf;
-
- mutex_lock(&maple_wlist_lock);
- list_add(&mdev->mq->list, &maple_waitq);
- mutex_unlock(&maple_wlist_lock);
-out:
- return ret;
-}
-EXPORT_SYMBOL_GPL(maple_add_packet_sleeps);
-
static struct mapleq *maple_allocq(struct maple_device *mdev)
{
struct mapleq *mq;
- mq = kmalloc(sizeof(*mq), GFP_KERNEL);
+ mq = kzalloc(sizeof(*mq), GFP_KERNEL);
if (!mq)
goto failed_nomem;
+ INIT_LIST_HEAD(&mq->list);
mq->dev = mdev;
- mq->recvbufdcsp = kmem_cache_zalloc(maple_queue_cache, GFP_KERNEL);
- mq->recvbuf = (void *) P2SEGADDR(mq->recvbufdcsp);
+ mq->recvbuf = kmem_cache_zalloc(maple_queue_cache, GFP_KERNEL);
if (!mq->recvbuf)
goto failed_p2;
- /*
- * most devices do not need the mutex - but
- * anything that injects block reads or writes
- * will rely on it
- */
- mutex_init(&mq->mutex);
+ mq->recvbuf->buf = &((mq->recvbuf->bufx)[0]);
return mq;
failed_p2:
kfree(mq);
failed_nomem:
+ dev_err(&mdev->dev, "could not allocate memory for device (%d, %d)\n",
+ mdev->port, mdev->unit);
return NULL;
}
@@ -272,12 +211,16 @@
{
struct maple_device *mdev;
+ /* zero this out to avoid kobj subsystem
+ * thinking it has already been registered */
+
mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
if (!mdev)
return NULL;
mdev->port = port;
mdev->unit = unit;
+
mdev->mq = maple_allocq(mdev);
if (!mdev->mq) {
@@ -286,19 +229,14 @@
}
mdev->dev.bus = &maple_bus_type;
mdev->dev.parent = &maple_bus;
+ init_waitqueue_head(&mdev->maple_wait);
return mdev;
}
static void maple_free_dev(struct maple_device *mdev)
{
- if (!mdev)
- return;
- if (mdev->mq) {
- if (mdev->mq->recvbufdcsp)
- kmem_cache_free(maple_queue_cache,
- mdev->mq->recvbufdcsp);
- kfree(mdev->mq);
- }
+ kmem_cache_free(maple_queue_cache, mdev->mq->recvbuf);
+ kfree(mdev->mq);
kfree(mdev);
}
@@ -320,7 +258,7 @@
maple_lastptr = maple_sendptr;
*maple_sendptr++ = (port << 16) | len | 0x80000000;
- *maple_sendptr++ = PHYSADDR(mq->recvbuf);
+ *maple_sendptr++ = PHYSADDR(mq->recvbuf->buf);
*maple_sendptr++ =
mq->command | (to << 8) | (from << 16) | (len << 24);
while (len-- > 0)
@@ -333,20 +271,28 @@
int i, maple_packets = 0;
struct mapleq *mq, *nmq;
+ if (!maple_dma_done())
+ return;
+
+ /* disable DMA */
+ ctrl_outl(0, MAPLE_ENABLE);
+
if (!list_empty(&maple_sentq))
- return;
+ goto finish;
+
mutex_lock(&maple_wlist_lock);
- if (list_empty(&maple_waitq) || !maple_dma_done()) {
+ if (list_empty(&maple_waitq)) {
mutex_unlock(&maple_wlist_lock);
- return;
+ goto finish;
}
- mutex_unlock(&maple_wlist_lock);
+
maple_lastptr = maple_sendbuf;
maple_sendptr = maple_sendbuf;
- mutex_lock(&maple_wlist_lock);
+
list_for_each_entry_safe(mq, nmq, &maple_waitq, list) {
maple_build_block(mq);
- list_move(&mq->list, &maple_sentq);
+ list_del_init(&mq->list);
+ list_add_tail(&mq->list, &maple_sentq);
if (maple_packets++ > MAPLE_MAXPACKETS)
break;
}
@@ -356,10 +302,13 @@
dma_cache_sync(0, maple_sendbuf + i * PAGE_SIZE,
PAGE_SIZE, DMA_BIDIRECTIONAL);
}
+
+finish:
+ maple_dma_reset();
}
/* check if there is a driver registered likely to match this device */
-static int check_matching_maple_driver(struct device_driver *driver,
+static int maple_check_matching_driver(struct device_driver *driver,
void *devptr)
{
struct maple_driver *maple_drv;
@@ -374,10 +323,7 @@
static void maple_detach_driver(struct maple_device *mdev)
{
- if (!mdev)
- return;
device_unregister(&mdev->dev);
- mdev = NULL;
}
/* process initial MAPLE_COMMAND_DEVINFO for each device or port */
@@ -385,9 +331,9 @@
{
char *p, *recvbuf;
unsigned long function;
- int matched, retval;
+ int matched, error;
- recvbuf = mdev->mq->recvbuf;
+ recvbuf = mdev->mq->recvbuf->buf;
/* copy the data as individual elements in
* case of memory optimisation */
memcpy(&mdev->devinfo.function, recvbuf + 4, 4);
@@ -395,7 +341,6 @@
memcpy(&mdev->devinfo.area_code, recvbuf + 20, 1);
memcpy(&mdev->devinfo.connector_direction, recvbuf + 21, 1);
memcpy(&mdev->devinfo.product_name[0], recvbuf + 22, 30);
- memcpy(&mdev->devinfo.product_licence[0], recvbuf + 52, 60);
memcpy(&mdev->devinfo.standby_power, recvbuf + 112, 2);
memcpy(&mdev->devinfo.max_power, recvbuf + 114, 2);
memcpy(mdev->product_name, mdev->devinfo.product_name, 30);
@@ -414,43 +359,39 @@
else
break;
- printk(KERN_INFO "Maple device detected: %s\n",
- mdev->product_name);
- printk(KERN_INFO "Maple device: %s\n", mdev->product_licence);
-
function = be32_to_cpu(mdev->devinfo.function);
+ dev_info(&mdev->dev, "detected %s: function 0x%lX: at (%d, %d)\n",
+ mdev->product_name, function, mdev->port, mdev->unit);
+
if (function > 0x200) {
/* Do this silently - as not a real device */
function = 0;
- mdev->driver = &maple_dummy_driver;
+ mdev->driver = &maple_unsupported_device;
dev_set_name(&mdev->dev, "%d:0.port", mdev->port);
} else {
- printk(KERN_INFO
- "Maple bus at (%d, %d): Function 0x%lX\n",
- mdev->port, mdev->unit, function);
-
matched =
bus_for_each_drv(&maple_bus_type, NULL, mdev,
- check_matching_maple_driver);
+ maple_check_matching_driver);
if (matched == 0) {
/* Driver does not exist yet */
- printk(KERN_INFO
- "No maple driver found.\n");
- mdev->driver = &maple_dummy_driver;
+ dev_info(&mdev->dev, "no driver found\n");
+ mdev->driver = &maple_unsupported_device;
}
dev_set_name(&mdev->dev, "%d:0%d.%lX", mdev->port,
mdev->unit, function);
}
+
mdev->function = function;
mdev->dev.release = &maple_release_device;
- retval = device_register(&mdev->dev);
- if (retval) {
- printk(KERN_INFO
- "Maple bus: Attempt to register device"
- " (%x, %x) failed.\n",
- mdev->port, mdev->unit);
+
+ atomic_set(&mdev->busy, 0);
+ error = device_register(&mdev->dev);
+ if (error) {
+ dev_warn(&mdev->dev, "could not register device at"
+ " (%d, %d), with error 0x%X\n", mdev->unit,
+ mdev->port, error);
maple_free_dev(mdev);
mdev = NULL;
return;
@@ -462,7 +403,7 @@
* port and unit then return 1 - allows identification
* of which devices need to be attached or detached
*/
-static int detach_maple_device(struct device *device, void *portptr)
+static int check_maple_device(struct device *device, void *portptr)
{
struct maple_device_specify *ds;
struct maple_device *mdev;
@@ -477,21 +418,25 @@
static int setup_maple_commands(struct device *device, void *ignored)
{
int add;
- struct maple_device *maple_dev = to_maple_dev(device);
-
- if ((maple_dev->interval > 0)
- && time_after(jiffies, maple_dev->when)) {
- /* bounce if we cannot lock */
- add = maple_add_packet(maple_dev,
- be32_to_cpu(maple_dev->devinfo.function),
+ struct maple_device *mdev = to_maple_dev(device);
+ if (mdev->interval > 0 && atomic_read(&mdev->busy) == 0 &&
+ time_after(jiffies, mdev->when)) {
+ /* bounce if we cannot add */
+ add = maple_add_packet(mdev,
+ be32_to_cpu(mdev->devinfo.function),
MAPLE_COMMAND_GETCOND, 1, NULL);
if (!add)
- maple_dev->when = jiffies + maple_dev->interval;
+ mdev->when = jiffies + mdev->interval;
} else {
if (time_after(jiffies, maple_pnp_time))
- /* This will also bounce */
- maple_add_packet(maple_dev, 0,
- MAPLE_COMMAND_DEVINFO, 0, NULL);
+ /* Ensure we don't have block reads and devinfo
+ * calls interfering with one another - so flag the
+ * device as busy */
+ if (atomic_read(&mdev->busy) == 0) {
+ atomic_set(&mdev->busy, 1);
+ maple_add_packet(mdev, 0,
+ MAPLE_COMMAND_DEVINFO, 0, NULL);
+ }
}
return 0;
}
@@ -499,29 +444,50 @@
/* VBLANK bottom half - implemented via workqueue */
static void maple_vblank_handler(struct work_struct *work)
{
- if (!list_empty(&maple_sentq) || !maple_dma_done())
+ int x, locking;
+ struct maple_device *mdev;
+
+ if (!maple_dma_done())
return;
ctrl_outl(0, MAPLE_ENABLE);
+ if (!list_empty(&maple_sentq))
+ goto finish;
+
+ /*
+ * Set up essential commands - to fetch data and
+ * check devices are still present
+ */
bus_for_each_dev(&maple_bus_type, NULL, NULL,
- setup_maple_commands);
+ setup_maple_commands);
- if (time_after(jiffies, maple_pnp_time))
+ if (time_after(jiffies, maple_pnp_time)) {
+ /*
+ * Scan the empty ports - bus is flakey and may have
+ * mis-reported emptyness
+ */
+ for (x = 0; x < MAPLE_PORTS; x++) {
+ if (checked[x] && empty[x]) {
+ mdev = baseunits[x];
+ if (!mdev)
+ break;
+ atomic_set(&mdev->busy, 1);
+ locking = maple_add_packet(mdev, 0,
+ MAPLE_COMMAND_DEVINFO, 0, NULL);
+ if (!locking)
+ break;
+ }
+ }
+
maple_pnp_time = jiffies + MAPLE_PNP_INTERVAL;
-
- mutex_lock(&maple_wlist_lock);
- if (!list_empty(&maple_waitq) && list_empty(&maple_sentq)) {
- mutex_unlock(&maple_wlist_lock);
- maple_send();
- } else {
- mutex_unlock(&maple_wlist_lock);
}
- maplebus_dma_reset();
+finish:
+ maple_send();
}
-/* handle devices added via hotplugs - placing them on queue for DEVINFO*/
+/* handle devices added via hotplugs - placing them on queue for DEVINFO */
static void maple_map_subunits(struct maple_device *mdev, int submask)
{
int retval, k, devcheck;
@@ -533,7 +499,7 @@
ds.unit = k + 1;
retval =
bus_for_each_dev(&maple_bus_type, NULL, &ds,
- detach_maple_device);
+ check_maple_device);
if (retval) {
submask = submask >> 1;
continue;
@@ -543,6 +509,7 @@
mdev_add = maple_alloc_dev(mdev->port, k + 1);
if (!mdev_add)
return;
+ atomic_set(&mdev_add->busy, 1);
maple_add_packet(mdev_add, 0, MAPLE_COMMAND_DEVINFO,
0, NULL);
/* mark that we are checking sub devices */
@@ -564,27 +531,45 @@
}
/* handle empty port or hotplug removal */
-static void maple_response_none(struct maple_device *mdev,
- struct mapleq *mq)
+static void maple_response_none(struct maple_device *mdev)
{
- if (mdev->unit != 0) {
- list_del(&mq->list);
- maple_clean_submap(mdev);
- printk(KERN_INFO
- "Maple bus device detaching at (%d, %d)\n",
- mdev->port, mdev->unit);
+ maple_clean_submap(mdev);
+
+ if (likely(mdev->unit != 0)) {
+ /*
+ * Block devices play up
+ * and give the impression they have
+ * been removed even when still in place or
+ * trip the mtd layer when they have
+ * really gone - this code traps that eventuality
+ * and ensures we aren't overloaded with useless
+ * error messages
+ */
+ if (mdev->can_unload) {
+ if (!mdev->can_unload(mdev)) {
+ atomic_set(&mdev->busy, 2);
+ wake_up(&mdev->maple_wait);
+ return;
+ }
+ }
+
+ dev_info(&mdev->dev, "detaching device at (%d, %d)\n",
+ mdev->port, mdev->unit);
maple_detach_driver(mdev);
return;
- }
- if (!started || !fullscan) {
- if (checked[mdev->port] == false) {
- checked[mdev->port] = true;
- printk(KERN_INFO "No maple devices attached"
- " to port %d\n", mdev->port);
+ } else {
+ if (!started || !fullscan) {
+ if (checked[mdev->port] == false) {
+ checked[mdev->port] = true;
+ empty[mdev->port] = true;
+ dev_info(&mdev->dev, "no devices"
+ " to port %d\n", mdev->port);
+ }
+ return;
}
- return;
}
- maple_clean_submap(mdev);
+ /* Some hardware devices generate false detach messages on unit 0 */
+ atomic_set(&mdev->busy, 0);
}
/* preprocess hotplugs or scans */
@@ -599,8 +584,11 @@
} else {
if (mdev->unit != 0)
maple_attach_driver(mdev);
+ if (mdev->unit == 0) {
+ empty[mdev->port] = false;
+ maple_attach_driver(mdev);
+ }
}
- return;
}
if (mdev->unit == 0) {
submask = recvbuf[2] & 0x1F;
@@ -611,6 +599,17 @@
}
}
+static void maple_response_fileerr(struct maple_device *mdev, void *recvbuf)
+{
+ if (mdev->fileerr_handler) {
+ mdev->fileerr_handler(mdev, recvbuf);
+ return;
+ } else
+ dev_warn(&mdev->dev, "device at (%d, %d) reports"
+ "file error 0x%X\n", mdev->port, mdev->unit,
+ ((int *)recvbuf)[1]);
+}
+
static void maple_port_rescan(void)
{
int i;
@@ -621,12 +620,6 @@
if (checked[i] == false) {
fullscan = 0;
mdev = baseunits[i];
- /*
- * test lock in case scan has failed
- * but device is still locked
- */
- if (mutex_is_locked(&mdev->mq->mutex))
- mutex_unlock(&mdev->mq->mutex);
maple_add_packet(mdev, 0, MAPLE_COMMAND_DEVINFO,
0, NULL);
}
@@ -637,7 +630,7 @@
static void maple_dma_handler(struct work_struct *work)
{
struct mapleq *mq, *nmq;
- struct maple_device *dev;
+ struct maple_device *mdev;
char *recvbuf;
enum maple_code code;
@@ -646,43 +639,56 @@
ctrl_outl(0, MAPLE_ENABLE);
if (!list_empty(&maple_sentq)) {
list_for_each_entry_safe(mq, nmq, &maple_sentq, list) {
- recvbuf = mq->recvbuf;
+ mdev = mq->dev;
+ recvbuf = mq->recvbuf->buf;
+ dma_cache_sync(&mdev->dev, recvbuf, 0x400,
+ DMA_FROM_DEVICE);
code = recvbuf[0];
- dev = mq->dev;
kfree(mq->sendbuf);
- mutex_unlock(&mq->mutex);
list_del_init(&mq->list);
-
switch (code) {
case MAPLE_RESPONSE_NONE:
- maple_response_none(dev, mq);
+ maple_response_none(mdev);
break;
case MAPLE_RESPONSE_DEVINFO:
- maple_response_devinfo(dev, recvbuf);
+ maple_response_devinfo(mdev, recvbuf);
+ atomic_set(&mdev->busy, 0);
break;
case MAPLE_RESPONSE_DATATRF:
- if (dev->callback)
- dev->callback(mq);
+ if (mdev->callback)
+ mdev->callback(mq);
+ atomic_set(&mdev->busy, 0);
+ wake_up(&mdev->maple_wait);
break;
case MAPLE_RESPONSE_FILEERR:
+ maple_response_fileerr(mdev, recvbuf);
+ atomic_set(&mdev->busy, 0);
+ wake_up(&mdev->maple_wait);
+ break;
+
case MAPLE_RESPONSE_AGAIN:
case MAPLE_RESPONSE_BADCMD:
case MAPLE_RESPONSE_BADFUNC:
- printk(KERN_DEBUG
- "Maple non-fatal error 0x%X\n",
- code);
+ dev_warn(&mdev->dev, "non-fatal error"
+ " 0x%X at (%d, %d)\n", code,
+ mdev->port, mdev->unit);
+ atomic_set(&mdev->busy, 0);
break;
case MAPLE_RESPONSE_ALLINFO:
- printk(KERN_DEBUG
- "Maple - extended device information"
- " not supported\n");
+ dev_notice(&mdev->dev, "extended"
+ " device information request for (%d, %d)"
+ " but call is not supported\n", mdev->port,
+ mdev->unit);
+ atomic_set(&mdev->busy, 0);
break;
case MAPLE_RESPONSE_OK:
+ atomic_set(&mdev->busy, 0);
+ wake_up(&mdev->maple_wait);
break;
default:
@@ -699,20 +705,19 @@
if (!fullscan)
maple_port_rescan();
/* mark that we have been through the first scan */
- if (started == 0)
- started = 1;
+ started = 1;
}
- maplebus_dma_reset();
+ maple_send();
}
-static irqreturn_t maplebus_dma_interrupt(int irq, void *dev_id)
+static irqreturn_t maple_dma_interrupt(int irq, void *dev_id)
{
/* Load everything into the bottom half */
schedule_work(&maple_dma_process);
return IRQ_HANDLED;
}
-static irqreturn_t maplebus_vblank_interrupt(int irq, void *dev_id)
+static irqreturn_t maple_vblank_interrupt(int irq, void *dev_id)
{
schedule_work(&maple_vblank_process);
return IRQ_HANDLED;
@@ -720,14 +725,14 @@
static int maple_set_dma_interrupt_handler(void)
{
- return request_irq(HW_EVENT_MAPLE_DMA, maplebus_dma_interrupt,
- IRQF_SHARED, "maple bus DMA", &maple_dummy_driver);
+ return request_irq(HW_EVENT_MAPLE_DMA, maple_dma_interrupt,
+ IRQF_SHARED, "maple bus DMA", &maple_unsupported_device);
}
static int maple_set_vblank_interrupt_handler(void)
{
- return request_irq(HW_EVENT_VSYNC, maplebus_vblank_interrupt,
- IRQF_SHARED, "maple bus VBLANK", &maple_dummy_driver);
+ return request_irq(HW_EVENT_VSYNC, maple_vblank_interrupt,
+ IRQF_SHARED, "maple bus VBLANK", &maple_unsupported_device);
}
static int maple_get_dma_buffer(void)
@@ -740,7 +745,7 @@
return 0;
}
-static int match_maple_bus_driver(struct device *devptr,
+static int maple_match_bus_driver(struct device *devptr,
struct device_driver *drvptr)
{
struct maple_driver *maple_drv = to_maple_driver(drvptr);
@@ -765,16 +770,18 @@
{
}
-static struct maple_driver maple_dummy_driver = {
+static struct maple_driver maple_unsupported_device = {
.drv = {
- .name = "maple_dummy_driver",
+ .name = "maple_unsupported_device",
.bus = &maple_bus_type,
},
};
-
+/**
+ * maple_bus_type - core maple bus structure
+ */
struct bus_type maple_bus_type = {
.name = "maple",
- .match = match_maple_bus_driver,
+ .match = maple_match_bus_driver,
.uevent = maple_bus_uevent,
};
EXPORT_SYMBOL_GPL(maple_bus_type);
@@ -788,7 +795,8 @@
{
int retval, i;
struct maple_device *mdev[MAPLE_PORTS];
- ctrl_outl(0, MAPLE_STATE);
+
+ ctrl_outl(0, MAPLE_ENABLE);
retval = device_register(&maple_bus);
if (retval)
@@ -798,36 +806,33 @@
if (retval)
goto cleanup_device;
- retval = driver_register(&maple_dummy_driver.drv);
+ retval = driver_register(&maple_unsupported_device.drv);
if (retval)
goto cleanup_bus;
/* allocate memory for maple bus dma */
retval = maple_get_dma_buffer();
if (retval) {
- printk(KERN_INFO
- "Maple bus: Failed to allocate Maple DMA buffers\n");
+ dev_err(&maple_bus, "failed to allocate DMA buffers\n");
goto cleanup_basic;
}
/* set up DMA interrupt handler */
retval = maple_set_dma_interrupt_handler();
if (retval) {
- printk(KERN_INFO
- "Maple bus: Failed to grab maple DMA IRQ\n");
+ dev_err(&maple_bus, "bus failed to grab maple "
+ "DMA IRQ\n");
goto cleanup_dma;
}
/* set up VBLANK interrupt handler */
retval = maple_set_vblank_interrupt_handler();
if (retval) {
- printk(KERN_INFO "Maple bus: Failed to grab VBLANK IRQ\n");
+ dev_err(&maple_bus, "bus failed to grab VBLANK IRQ\n");
goto cleanup_irq;
}
- maple_queue_cache =
- kmem_cache_create("maple_queue_cache", 0x400, 0,
- SLAB_HWCACHE_ALIGN, NULL);
+ maple_queue_cache = KMEM_CACHE(maple_buffer, SLAB_HWCACHE_ALIGN);
if (!maple_queue_cache)
goto cleanup_bothirqs;
@@ -838,23 +843,23 @@
/* setup maple ports */
for (i = 0; i < MAPLE_PORTS; i++) {
checked[i] = false;
+ empty[i] = false;
mdev[i] = maple_alloc_dev(i, 0);
- baseunits[i] = mdev[i];
if (!mdev[i]) {
while (i-- > 0)
maple_free_dev(mdev[i]);
goto cleanup_cache;
}
+ baseunits[i] = mdev[i];
+ atomic_set(&mdev[i]->busy, 1);
maple_add_packet(mdev[i], 0, MAPLE_COMMAND_DEVINFO, 0, NULL);
subdevice_map[i] = 0;
}
- /* setup maplebus hardware */
- maplebus_dma_reset();
- /* initial detection */
+ maple_pnp_time = jiffies + HZ;
+ /* prepare initial queue */
maple_send();
- maple_pnp_time = jiffies;
- printk(KERN_INFO "Maple bus core now registered.\n");
+ dev_info(&maple_bus, "bus core now registered\n");
return 0;
@@ -871,7 +876,7 @@
free_pages((unsigned long) maple_sendbuf, MAPLE_DMA_PAGES);
cleanup_basic:
- driver_unregister(&maple_dummy_driver.drv);
+ driver_unregister(&maple_unsupported_device.drv);
cleanup_bus:
bus_unregister(&maple_bus_type);
@@ -880,7 +885,7 @@
device_unregister(&maple_bus);
cleanup:
- printk(KERN_INFO "Maple bus registration failed\n");
+ printk(KERN_ERR "Maple bus registration failed\n");
return retval;
}
/* Push init to later to ensure hardware gets detected */
diff --git a/drivers/staging/go7007/snd-go7007.c b/drivers/staging/go7007/snd-go7007.c
index a7de401..cd19be6 100644
--- a/drivers/staging/go7007/snd-go7007.c
+++ b/drivers/staging/go7007/snd-go7007.c
@@ -248,10 +248,11 @@
spin_lock_init(&gosnd->lock);
gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0;
gosnd->capturing = 0;
- gosnd->card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (gosnd->card == NULL) {
+ ret = snd_card_create(index[dev], id[dev], THIS_MODULE, 0,
+ &gosnd->card);
+ if (ret < 0) {
kfree(gosnd);
- return -ENOMEM;
+ return ret;
}
ret = snd_device_new(gosnd->card, SNDRV_DEV_LOWLEVEL, go,
&go7007_snd_device_ops);
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 83babb0..c6c816b 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -47,6 +47,7 @@
default y if CPU_SUBTYPE_SH7720
default y if CPU_SUBTYPE_SH7721
default y if CPU_SUBTYPE_SH7763
+ default y if CPU_SUBTYPE_SH7786
# more:
default PCI
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
index 60d3f9e..14e09ab 100644
--- a/drivers/usb/gadget/gmidi.c
+++ b/drivers/usb/gadget/gmidi.c
@@ -1099,10 +1099,9 @@
.dev_free = gmidi_snd_free,
};
- card = snd_card_new(index, id, THIS_MODULE, 0);
- if (!card) {
- ERROR(dev, "snd_card_new failed\n");
- err = -ENOMEM;
+ err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ if (err < 0) {
+ ERROR(dev, "snd_card_create failed\n");
goto fail;
}
dev->card = card;
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 5cf5f1e..7658589 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1049,7 +1049,8 @@
#if defined(CONFIG_CPU_SUBTYPE_SH7720) || \
defined(CONFIG_CPU_SUBTYPE_SH7721) || \
- defined(CONFIG_CPU_SUBTYPE_SH7763)
+ defined(CONFIG_CPU_SUBTYPE_SH7763) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7786)
#include "ohci-sh.c"
#define PLATFORM_DRIVER ohci_hcd_sh_driver
#endif
diff --git a/drivers/video/pvr2fb.c b/drivers/video/pvr2fb.c
index 0a0fd48..53f8f11 100644
--- a/drivers/video/pvr2fb.c
+++ b/drivers/video/pvr2fb.c
@@ -61,7 +61,7 @@
#include <mach-dreamcast/mach/sysasic.h>
#endif
-#ifdef CONFIG_SH_DMA
+#ifdef CONFIG_PVR2_DMA
#include <linux/pagemap.h>
#include <mach/dma.h>
#include <asm/dma.h>
@@ -188,7 +188,7 @@
static unsigned long pvr2fb_map;
#endif
-#ifdef CONFIG_SH_DMA
+#ifdef CONFIG_PVR2_DMA
static unsigned int shdma = PVR2_CASCADE_CHAN;
static unsigned int pvr2dma = ONCHIP_NR_DMA_CHANNELS;
#endif
@@ -207,7 +207,7 @@
static int pvr2_init_cable(void);
static int pvr2_get_param(const struct pvr2_params *p, const char *s,
int val, int size);
-#ifdef CONFIG_SH_DMA
+#ifdef CONFIG_PVR2_DMA
static ssize_t pvr2fb_write(struct fb_info *info, const char *buf,
size_t count, loff_t *ppos);
#endif
@@ -218,7 +218,7 @@
.fb_blank = pvr2fb_blank,
.fb_check_var = pvr2fb_check_var,
.fb_set_par = pvr2fb_set_par,
-#ifdef CONFIG_SH_DMA
+#ifdef CONFIG_PVR2_DMA
.fb_write = pvr2fb_write,
#endif
.fb_fillrect = cfb_fillrect,
@@ -671,7 +671,7 @@
return cable_type;
}
-#ifdef CONFIG_SH_DMA
+#ifdef CONFIG_PVR2_DMA
static ssize_t pvr2fb_write(struct fb_info *info, const char *buf,
size_t count, loff_t *ppos)
{
@@ -743,7 +743,7 @@
return ret;
}
-#endif /* CONFIG_SH_DMA */
+#endif /* CONFIG_PVR2_DMA */
/**
* pvr2fb_common_init
@@ -893,7 +893,7 @@
return -EBUSY;
}
-#ifdef CONFIG_SH_DMA
+#ifdef CONFIG_PVR2_DMA
if (request_dma(pvr2dma, "pvr2") != 0) {
free_irq(HW_EVENT_VSYNC, 0);
return -EBUSY;
@@ -915,7 +915,7 @@
}
free_irq(HW_EVENT_VSYNC, 0);
-#ifdef CONFIG_SH_DMA
+#ifdef CONFIG_PVR2_DMA
free_dma(pvr2dma);
#endif
}
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 1d2636a..92ea0ab 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -33,6 +33,8 @@
struct fb_info info;
dma_addr_t dma_handle;
struct fb_deferred_io defio;
+ unsigned long frame_end;
+ wait_queue_head_t frame_end_wait;
};
struct sh_mobile_lcdc_priv {
@@ -226,7 +228,10 @@
static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
{
struct sh_mobile_lcdc_priv *priv = data;
+ struct sh_mobile_lcdc_chan *ch;
unsigned long tmp;
+ int is_sub;
+ int k;
/* acknowledge interrupt */
tmp = lcdc_read(priv, _LDINTR);
@@ -234,8 +239,24 @@
tmp |= 0x000000ff ^ LDINTR_FS; /* status in low 8 */
lcdc_write(priv, _LDINTR, tmp);
- /* disable clocks */
- sh_mobile_lcdc_clk_off(priv);
+ /* figure out if this interrupt is for main or sub lcd */
+ is_sub = (lcdc_read(priv, _LDSR) & (1 << 10)) ? 1 : 0;
+
+ /* wake up channel and disable clocks*/
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
+ ch = &priv->ch[k];
+
+ if (!ch->enabled)
+ continue;
+
+ if (is_sub == lcdc_chan_is_sublcd(ch)) {
+ ch->frame_end = 1;
+ wake_up(&ch->frame_end_wait);
+
+ sh_mobile_lcdc_clk_off(priv);
+ }
+ }
+
return IRQ_HANDLED;
}
@@ -448,18 +469,27 @@
struct sh_mobile_lcdc_board_cfg *board_cfg;
int k;
- /* tell the board code to disable the panel */
+ /* clean up deferred io and ask board code to disable panel */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
ch = &priv->ch[k];
+
+ /* deferred io mode:
+ * flush frame, and wait for frame end interrupt
+ * clean up deferred io and enable clock
+ */
+ if (ch->info.fbdefio) {
+ ch->frame_end = 0;
+ schedule_delayed_work(&ch->info.deferred_work, 0);
+ wait_event(ch->frame_end_wait, ch->frame_end);
+ fb_deferred_io_cleanup(&ch->info);
+ ch->info.fbdefio = NULL;
+ sh_mobile_lcdc_clk_on(priv);
+ }
+
board_cfg = &ch->cfg.board_cfg;
if (board_cfg->display_off)
board_cfg->display_off(board_cfg->board_data);
- /* cleanup deferred io if enabled */
- if (ch->info.fbdefio) {
- fb_deferred_io_cleanup(&ch->info);
- ch->info.fbdefio = NULL;
- }
}
/* stop the lcdc */
@@ -652,6 +682,26 @@
return 0;
}
+static int sh_mobile_lcdc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ sh_mobile_lcdc_stop(platform_get_drvdata(pdev));
+ return 0;
+}
+
+static int sh_mobile_lcdc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ return sh_mobile_lcdc_start(platform_get_drvdata(pdev));
+}
+
+static struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
+ .suspend = sh_mobile_lcdc_suspend,
+ .resume = sh_mobile_lcdc_resume,
+};
+
static int sh_mobile_lcdc_remove(struct platform_device *pdev);
static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
@@ -707,6 +757,7 @@
dev_err(&pdev->dev, "unsupported interface type\n");
goto err1;
}
+ init_waitqueue_head(&priv->ch[i].frame_end_wait);
switch (pdata->ch[i].chan) {
case LCDC_CHAN_MAINLCD:
@@ -860,6 +911,7 @@
.driver = {
.name = "sh_mobile_lcdc_fb",
.owner = THIS_MODULE,
+ .pm = &sh_mobile_lcdc_dev_pm_ops,
},
.probe = sh_mobile_lcdc_probe,
.remove = sh_mobile_lcdc_remove,
diff --git a/fs/compat.c b/fs/compat.c
index d0145ca..0949b43 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -1402,6 +1402,7 @@
retval = mutex_lock_interruptible(¤t->cred_exec_mutex);
if (retval < 0)
goto out_free;
+ current->in_execve = 1;
retval = -ENOMEM;
bprm->cred = prepare_exec_creds();
@@ -1454,6 +1455,7 @@
goto out;
/* execve succeeded */
+ current->in_execve = 0;
mutex_unlock(¤t->cred_exec_mutex);
acct_update_integrals(current);
free_bprm(bprm);
@@ -1470,6 +1472,7 @@
}
out_unlock:
+ current->in_execve = 0;
mutex_unlock(¤t->cred_exec_mutex);
out_free:
diff --git a/fs/exec.c b/fs/exec.c
index 929b580..b9f1c14 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -45,6 +45,7 @@
#include <linux/proc_fs.h>
#include <linux/mount.h>
#include <linux/security.h>
+#include <linux/ima.h>
#include <linux/syscalls.h>
#include <linux/tsacct_kern.h>
#include <linux/cn_proc.h>
@@ -127,6 +128,9 @@
MAY_READ | MAY_EXEC | MAY_OPEN);
if (error)
goto exit;
+ error = ima_path_check(&nd.path, MAY_READ | MAY_EXEC | MAY_OPEN);
+ if (error)
+ goto exit;
file = nameidata_to_filp(&nd, O_RDONLY|O_LARGEFILE);
error = PTR_ERR(file);
@@ -674,6 +678,9 @@
err = inode_permission(nd.path.dentry->d_inode, MAY_EXEC | MAY_OPEN);
if (err)
goto out_path_put;
+ err = ima_path_check(&nd.path, MAY_EXEC | MAY_OPEN);
+ if (err)
+ goto out_path_put;
file = nameidata_to_filp(&nd, O_RDONLY|O_LARGEFILE);
if (IS_ERR(file))
@@ -1184,6 +1191,9 @@
retval = security_bprm_check(bprm);
if (retval)
return retval;
+ retval = ima_bprm_check(bprm);
+ if (retval)
+ return retval;
/* kernel module loader fixup */
/* so we don't try to load run modprobe in kernel space. */
@@ -1284,6 +1294,7 @@
retval = mutex_lock_interruptible(¤t->cred_exec_mutex);
if (retval < 0)
goto out_free;
+ current->in_execve = 1;
retval = -ENOMEM;
bprm->cred = prepare_exec_creds();
@@ -1337,6 +1348,7 @@
goto out;
/* execve succeeded */
+ current->in_execve = 0;
mutex_unlock(¤t->cred_exec_mutex);
acct_update_integrals(current);
free_bprm(bprm);
@@ -1355,6 +1367,7 @@
}
out_unlock:
+ current->in_execve = 0;
mutex_unlock(¤t->cred_exec_mutex);
out_free:
diff --git a/fs/file_table.c b/fs/file_table.c
index bbeeac6..da806ac 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/security.h>
+#include <linux/ima.h>
#include <linux/eventpoll.h>
#include <linux/rcupdate.h>
#include <linux/mount.h>
@@ -279,6 +280,7 @@
if (file->f_op && file->f_op->release)
file->f_op->release(inode, file);
security_file_free(file);
+ ima_file_free(file);
if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL))
cdev_put(inode->i_cdev);
fops_put(file->f_op);
diff --git a/fs/gfs2/Kconfig b/fs/gfs2/Kconfig
index e563a64..3a981b7 100644
--- a/fs/gfs2/Kconfig
+++ b/fs/gfs2/Kconfig
@@ -1,6 +1,10 @@
config GFS2_FS
tristate "GFS2 file system support"
depends on EXPERIMENTAL && (64BIT || LBD)
+ select DLM if GFS2_FS_LOCKING_DLM
+ select CONFIGFS_FS if GFS2_FS_LOCKING_DLM
+ select SYSFS if GFS2_FS_LOCKING_DLM
+ select IP_SCTP if DLM_SCTP
select FS_POSIX_ACL
select CRC32
help
@@ -18,17 +22,16 @@
the locking module below. Documentation and utilities for GFS2 can
be found here: http://sources.redhat.com/cluster
- The "nolock" lock module is now built in to GFS2 by default.
+ The "nolock" lock module is now built in to GFS2 by default. If
+ you want to use the DLM, be sure to enable HOTPLUG and IPv4/6
+ networking.
config GFS2_FS_LOCKING_DLM
- tristate "GFS2 DLM locking module"
- depends on GFS2_FS && SYSFS && NET && INET && (IPV6 || IPV6=n)
- select IP_SCTP if DLM_SCTP
- select CONFIGFS_FS
- select DLM
+ bool "GFS2 DLM locking"
+ depends on (GFS2_FS!=n) && NET && INET && (IPV6 || IPV6=n) && HOTPLUG
help
Multiple node locking module for GFS2
- Most users of GFS2 will require this module. It provides the locking
+ Most users of GFS2 will require this. It provides the locking
interface between GFS2 and the DLM, which is required to use GFS2
in a cluster environment.
diff --git a/fs/gfs2/Makefile b/fs/gfs2/Makefile
index c1b4ec6a..a851ea4 100644
--- a/fs/gfs2/Makefile
+++ b/fs/gfs2/Makefile
@@ -1,9 +1,9 @@
obj-$(CONFIG_GFS2_FS) += gfs2.o
gfs2-y := acl.o bmap.o dir.o eaops.o eattr.o glock.o \
- glops.o inode.o log.o lops.o locking.o main.o meta_io.o \
+ glops.o inode.o log.o lops.o main.o meta_io.o \
mount.o ops_address.o ops_dentry.o ops_export.o ops_file.o \
ops_fstype.o ops_inode.o ops_super.o quota.o \
recovery.o rgrp.o super.o sys.o trans.o util.o
-obj-$(CONFIG_GFS2_FS_LOCKING_DLM) += locking/dlm/
+gfs2-$(CONFIG_GFS2_FS_LOCKING_DLM) += lock_dlm.o
diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c
index e335dce..43764f4 100644
--- a/fs/gfs2/acl.c
+++ b/fs/gfs2/acl.c
@@ -15,7 +15,6 @@
#include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
#include <linux/gfs2_ondisk.h>
-#include <linux/lm_interface.h>
#include "gfs2.h"
#include "incore.h"
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index 11ffc56..3a5d3f8 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -13,7 +13,6 @@
#include <linux/buffer_head.h>
#include <linux/gfs2_ondisk.h>
#include <linux/crc32.h>
-#include <linux/lm_interface.h>
#include "gfs2.h"
#include "incore.h"
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index b7c8e5c..aef4d0c 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -60,7 +60,6 @@
#include <linux/gfs2_ondisk.h>
#include <linux/crc32.h>
#include <linux/vmalloc.h>
-#include <linux/lm_interface.h>
#include "gfs2.h"
#include "incore.h"
diff --git a/fs/gfs2/eaops.c b/fs/gfs2/eaops.c
index f114ba2..dee9b03 100644
--- a/fs/gfs2/eaops.c
+++ b/fs/gfs2/eaops.c
@@ -14,7 +14,6 @@
#include <linux/capability.h>
#include <linux/xattr.h>
#include <linux/gfs2_ondisk.h>
-#include <linux/lm_interface.h>
#include <asm/uaccess.h>
#include "gfs2.h"
diff --git a/fs/gfs2/eattr.c b/fs/gfs2/eattr.c
index 0d1c76d..899763a 100644
--- a/fs/gfs2/eattr.c
+++ b/fs/gfs2/eattr.c
@@ -13,7 +13,6 @@
#include <linux/buffer_head.h>
#include <linux/xattr.h>
#include <linux/gfs2_ondisk.h>
-#include <linux/lm_interface.h>
#include <asm/uaccess.h>
#include "gfs2.h"
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index 6b983ae..3984e47 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -10,7 +10,6 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include <linux/completion.h>
#include <linux/buffer_head.h>
#include <linux/delay.h>
#include <linux/sort.h>
@@ -18,7 +17,6 @@
#include <linux/kallsyms.h>
#include <linux/gfs2_ondisk.h>
#include <linux/list.h>
-#include <linux/lm_interface.h>
#include <linux/wait.h>
#include <linux/module.h>
#include <linux/rwsem.h>
@@ -155,13 +153,10 @@
struct gfs2_sbd *sdp = gl->gl_sbd;
struct inode *aspace = gl->gl_aspace;
- if (sdp->sd_lockstruct.ls_ops->lm_put_lock)
- sdp->sd_lockstruct.ls_ops->lm_put_lock(gl->gl_lock);
-
if (aspace)
gfs2_aspace_put(aspace);
- kmem_cache_free(gfs2_glock_cachep, gl);
+ sdp->sd_lockstruct.ls_ops->lm_put_lock(gfs2_glock_cachep, gl);
}
/**
@@ -172,6 +167,7 @@
static void gfs2_glock_hold(struct gfs2_glock *gl)
{
+ GLOCK_BUG_ON(gl, atomic_read(&gl->gl_ref) == 0);
atomic_inc(&gl->gl_ref);
}
@@ -211,17 +207,15 @@
atomic_dec(&lru_count);
}
spin_unlock(&lru_lock);
- GLOCK_BUG_ON(gl, gl->gl_state != LM_ST_UNLOCKED);
- GLOCK_BUG_ON(gl, !list_empty(&gl->gl_lru));
GLOCK_BUG_ON(gl, !list_empty(&gl->gl_holders));
glock_free(gl);
rv = 1;
goto out;
}
- write_unlock(gl_lock_addr(gl->gl_hash));
/* 1 for being hashed, 1 for having state != LM_ST_UNLOCKED */
if (atomic_read(&gl->gl_ref) == 2)
gfs2_glock_schedule_for_reclaim(gl);
+ write_unlock(gl_lock_addr(gl->gl_hash));
out:
return rv;
}
@@ -256,27 +250,6 @@
}
/**
- * gfs2_glock_find() - Find glock by lock number
- * @sdp: The GFS2 superblock
- * @name: The lock name
- *
- * Returns: NULL, or the struct gfs2_glock with the requested number
- */
-
-static struct gfs2_glock *gfs2_glock_find(const struct gfs2_sbd *sdp,
- const struct lm_lockname *name)
-{
- unsigned int hash = gl_hash(sdp, name);
- struct gfs2_glock *gl;
-
- read_lock(gl_lock_addr(hash));
- gl = search_bucket(hash, sdp, name);
- read_unlock(gl_lock_addr(hash));
-
- return gl;
-}
-
-/**
* may_grant - check if its ok to grant a new lock
* @gl: The glock
* @gh: The lock request which we wish to grant
@@ -523,7 +496,7 @@
}
static unsigned int gfs2_lm_lock(struct gfs2_sbd *sdp, void *lock,
- unsigned int cur_state, unsigned int req_state,
+ unsigned int req_state,
unsigned int flags)
{
int ret = LM_OUT_ERROR;
@@ -532,7 +505,7 @@
return req_state == LM_ST_UNLOCKED ? 0 : req_state;
if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
- ret = sdp->sd_lockstruct.ls_ops->lm_lock(lock, cur_state,
+ ret = sdp->sd_lockstruct.ls_ops->lm_lock(lock,
req_state, flags);
return ret;
}
@@ -575,7 +548,7 @@
gl->gl_state == LM_ST_DEFERRED) &&
!(lck_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB)))
lck_flags |= LM_FLAG_TRY_1CB;
- ret = gfs2_lm_lock(sdp, gl->gl_lock, gl->gl_state, target, lck_flags);
+ ret = gfs2_lm_lock(sdp, gl, target, lck_flags);
if (!(ret & LM_OUT_ASYNC)) {
finish_xmote(gl, ret);
@@ -624,10 +597,11 @@
GLOCK_BUG_ON(gl, test_bit(GLF_DEMOTE_IN_PROGRESS, &gl->gl_flags));
+ down_read(&gfs2_umount_flush_sem);
if (test_bit(GLF_DEMOTE, &gl->gl_flags) &&
gl->gl_demote_state != gl->gl_state) {
if (find_first_holder(gl))
- goto out;
+ goto out_unlock;
if (nonblock)
goto out_sched;
set_bit(GLF_DEMOTE_IN_PROGRESS, &gl->gl_flags);
@@ -638,23 +612,26 @@
gfs2_demote_wake(gl);
ret = do_promote(gl);
if (ret == 0)
- goto out;
+ goto out_unlock;
if (ret == 2)
- return;
+ goto out_sem;
gh = find_first_waiter(gl);
gl->gl_target = gh->gh_state;
if (!(gh->gh_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB)))
do_error(gl, 0); /* Fail queued try locks */
}
do_xmote(gl, gh, gl->gl_target);
+out_sem:
+ up_read(&gfs2_umount_flush_sem);
return;
out_sched:
gfs2_glock_hold(gl);
if (queue_delayed_work(glock_workqueue, &gl->gl_work, 0) == 0)
gfs2_glock_put(gl);
-out:
+out_unlock:
clear_bit(GLF_LOCK, &gl->gl_flags);
+ goto out_sem;
}
static void glock_work_func(struct work_struct *work)
@@ -681,18 +658,6 @@
gfs2_glock_put(gl);
}
-static int gfs2_lm_get_lock(struct gfs2_sbd *sdp, struct lm_lockname *name,
- void **lockp)
-{
- int error = -EIO;
- if (!sdp->sd_lockstruct.ls_ops->lm_get_lock)
- return 0;
- if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
- error = sdp->sd_lockstruct.ls_ops->lm_get_lock(
- sdp->sd_lockstruct.ls_lockspace, name, lockp);
- return error;
-}
-
/**
* gfs2_glock_get() - Get a glock, or create one if one doesn't exist
* @sdp: The GFS2 superblock
@@ -719,10 +684,11 @@
gl = search_bucket(hash, sdp, &name);
read_unlock(gl_lock_addr(hash));
- if (gl || !create) {
- *glp = gl;
+ *glp = gl;
+ if (gl)
return 0;
- }
+ if (!create)
+ return -ENOENT;
gl = kmem_cache_alloc(gfs2_glock_cachep, GFP_KERNEL);
if (!gl)
@@ -736,7 +702,9 @@
gl->gl_demote_state = LM_ST_EXCLUSIVE;
gl->gl_hash = hash;
gl->gl_ops = glops;
- gl->gl_stamp = jiffies;
+ snprintf(gl->gl_strname, GDLM_STRNAME_BYTES, "%8x%16llx", name.ln_type, (unsigned long long)number);
+ memset(&gl->gl_lksb, 0, sizeof(struct dlm_lksb));
+ gl->gl_lksb.sb_lvbptr = gl->gl_lvb;
gl->gl_tchange = jiffies;
gl->gl_object = NULL;
gl->gl_sbd = sdp;
@@ -753,10 +721,6 @@
}
}
- error = gfs2_lm_get_lock(sdp, &name, &gl->gl_lock);
- if (error)
- goto fail_aspace;
-
write_lock(gl_lock_addr(hash));
tmp = search_bucket(hash, sdp, &name);
if (tmp) {
@@ -772,9 +736,6 @@
return 0;
-fail_aspace:
- if (gl->gl_aspace)
- gfs2_aspace_put(gl->gl_aspace);
fail:
kmem_cache_free(gfs2_glock_cachep, gl);
return error;
@@ -966,7 +927,7 @@
if (!(gh->gh_flags & LM_FLAG_PRIORITY)) {
spin_unlock(&gl->gl_spin);
if (sdp->sd_lockstruct.ls_ops->lm_cancel)
- sdp->sd_lockstruct.ls_ops->lm_cancel(gl->gl_lock);
+ sdp->sd_lockstruct.ls_ops->lm_cancel(gl);
spin_lock(&gl->gl_spin);
}
return;
@@ -1051,7 +1012,6 @@
spin_lock(&gl->gl_spin);
clear_bit(GLF_LOCK, &gl->gl_flags);
}
- gl->gl_stamp = jiffies;
if (list_empty(&gl->gl_holders) &&
!test_bit(GLF_PENDING_DEMOTE, &gl->gl_flags) &&
!test_bit(GLF_DEMOTE, &gl->gl_flags))
@@ -1240,70 +1200,13 @@
gfs2_glock_dq_uninit(&ghs[x]);
}
-static int gfs2_lm_hold_lvb(struct gfs2_sbd *sdp, void *lock, char **lvbp)
+void gfs2_glock_cb(struct gfs2_glock *gl, unsigned int state)
{
- int error = -EIO;
- if (!sdp->sd_lockstruct.ls_ops->lm_hold_lvb)
- return 0;
- if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
- error = sdp->sd_lockstruct.ls_ops->lm_hold_lvb(lock, lvbp);
- return error;
-}
-
-/**
- * gfs2_lvb_hold - attach a LVB from a glock
- * @gl: The glock in question
- *
- */
-
-int gfs2_lvb_hold(struct gfs2_glock *gl)
-{
- int error;
-
- if (!atomic_read(&gl->gl_lvb_count)) {
- error = gfs2_lm_hold_lvb(gl->gl_sbd, gl->gl_lock, &gl->gl_lvb);
- if (error)
- return error;
- gfs2_glock_hold(gl);
- }
- atomic_inc(&gl->gl_lvb_count);
-
- return 0;
-}
-
-/**
- * gfs2_lvb_unhold - detach a LVB from a glock
- * @gl: The glock in question
- *
- */
-
-void gfs2_lvb_unhold(struct gfs2_glock *gl)
-{
- struct gfs2_sbd *sdp = gl->gl_sbd;
-
- gfs2_glock_hold(gl);
- gfs2_assert(gl->gl_sbd, atomic_read(&gl->gl_lvb_count) > 0);
- if (atomic_dec_and_test(&gl->gl_lvb_count)) {
- if (sdp->sd_lockstruct.ls_ops->lm_unhold_lvb)
- sdp->sd_lockstruct.ls_ops->lm_unhold_lvb(gl->gl_lock, gl->gl_lvb);
- gl->gl_lvb = NULL;
- gfs2_glock_put(gl);
- }
- gfs2_glock_put(gl);
-}
-
-static void blocking_cb(struct gfs2_sbd *sdp, struct lm_lockname *name,
- unsigned int state)
-{
- struct gfs2_glock *gl;
unsigned long delay = 0;
unsigned long holdtime;
unsigned long now = jiffies;
- gl = gfs2_glock_find(sdp, name);
- if (!gl)
- return;
-
+ gfs2_glock_hold(gl);
holdtime = gl->gl_tchange + gl->gl_ops->go_min_hold_time;
if (time_before(now, holdtime))
delay = holdtime - now;
@@ -1317,74 +1220,33 @@
gfs2_glock_put(gl);
}
-static void gfs2_jdesc_make_dirty(struct gfs2_sbd *sdp, unsigned int jid)
-{
- struct gfs2_jdesc *jd;
-
- spin_lock(&sdp->sd_jindex_spin);
- list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) {
- if (jd->jd_jid != jid)
- continue;
- jd->jd_dirty = 1;
- break;
- }
- spin_unlock(&sdp->sd_jindex_spin);
-}
-
/**
- * gfs2_glock_cb - Callback used by locking module
- * @sdp: Pointer to the superblock
- * @type: Type of callback
- * @data: Type dependent data pointer
+ * gfs2_glock_complete - Callback used by locking
+ * @gl: Pointer to the glock
+ * @ret: The return value from the dlm
*
- * Called by the locking module when it wants to tell us something.
- * Either we need to drop a lock, one of our ASYNC requests completed, or
- * a journal from another client needs to be recovered.
*/
-void gfs2_glock_cb(void *cb_data, unsigned int type, void *data)
+void gfs2_glock_complete(struct gfs2_glock *gl, int ret)
{
- struct gfs2_sbd *sdp = cb_data;
-
- switch (type) {
- case LM_CB_NEED_E:
- blocking_cb(sdp, data, LM_ST_UNLOCKED);
- return;
-
- case LM_CB_NEED_D:
- blocking_cb(sdp, data, LM_ST_DEFERRED);
- return;
-
- case LM_CB_NEED_S:
- blocking_cb(sdp, data, LM_ST_SHARED);
- return;
-
- case LM_CB_ASYNC: {
- struct lm_async_cb *async = data;
- struct gfs2_glock *gl;
-
- down_read(&gfs2_umount_flush_sem);
- gl = gfs2_glock_find(sdp, &async->lc_name);
- if (gfs2_assert_warn(sdp, gl))
+ struct lm_lockstruct *ls = &gl->gl_sbd->sd_lockstruct;
+ gl->gl_reply = ret;
+ if (unlikely(test_bit(DFL_BLOCK_LOCKS, &ls->ls_flags))) {
+ struct gfs2_holder *gh;
+ spin_lock(&gl->gl_spin);
+ gh = find_first_waiter(gl);
+ if ((!(gh && (gh->gh_flags & LM_FLAG_NOEXP)) &&
+ (gl->gl_target != LM_ST_UNLOCKED)) ||
+ ((ret & ~LM_OUT_ST_MASK) != 0))
+ set_bit(GLF_FROZEN, &gl->gl_flags);
+ spin_unlock(&gl->gl_spin);
+ if (test_bit(GLF_FROZEN, &gl->gl_flags))
return;
- gl->gl_reply = async->lc_ret;
- set_bit(GLF_REPLY_PENDING, &gl->gl_flags);
- if (queue_delayed_work(glock_workqueue, &gl->gl_work, 0) == 0)
- gfs2_glock_put(gl);
- up_read(&gfs2_umount_flush_sem);
- return;
}
-
- case LM_CB_NEED_RECOVERY:
- gfs2_jdesc_make_dirty(sdp, *(unsigned int *)data);
- if (sdp->sd_recoverd_process)
- wake_up_process(sdp->sd_recoverd_process);
- return;
-
- default:
- gfs2_assert_warn(sdp, 0);
- return;
- }
+ set_bit(GLF_REPLY_PENDING, &gl->gl_flags);
+ gfs2_glock_hold(gl);
+ if (queue_delayed_work(glock_workqueue, &gl->gl_work, 0) == 0)
+ gfs2_glock_put(gl);
}
/**
@@ -1515,6 +1377,25 @@
return has_entries;
}
+
+/**
+ * thaw_glock - thaw out a glock which has an unprocessed reply waiting
+ * @gl: The glock to thaw
+ *
+ * N.B. When we freeze a glock, we leave a ref to the glock outstanding,
+ * so this has to result in the ref count being dropped by one.
+ */
+
+static void thaw_glock(struct gfs2_glock *gl)
+{
+ if (!test_and_clear_bit(GLF_FROZEN, &gl->gl_flags))
+ return;
+ set_bit(GLF_REPLY_PENDING, &gl->gl_flags);
+ gfs2_glock_hold(gl);
+ if (queue_delayed_work(glock_workqueue, &gl->gl_work, 0) == 0)
+ gfs2_glock_put(gl);
+}
+
/**
* clear_glock - look at a glock and see if we can free it from glock cache
* @gl: the glock to look at
@@ -1540,6 +1421,20 @@
}
/**
+ * gfs2_glock_thaw - Thaw any frozen glocks
+ * @sdp: The super block
+ *
+ */
+
+void gfs2_glock_thaw(struct gfs2_sbd *sdp)
+{
+ unsigned x;
+
+ for (x = 0; x < GFS2_GL_HASH_SIZE; x++)
+ examine_bucket(thaw_glock, sdp, x);
+}
+
+/**
* gfs2_gl_hash_clear - Empty out the glock hash table
* @sdp: the filesystem
* @wait: wait until it's all gone
@@ -1619,7 +1514,7 @@
if (flags & LM_FLAG_NOEXP)
*p++ = 'e';
if (flags & LM_FLAG_ANY)
- *p++ = 'a';
+ *p++ = 'A';
if (flags & LM_FLAG_PRIORITY)
*p++ = 'p';
if (flags & GL_ASYNC)
@@ -1683,6 +1578,10 @@
*p++ = 'i';
if (test_bit(GLF_REPLY_PENDING, gflags))
*p++ = 'r';
+ if (test_bit(GLF_INITIAL, gflags))
+ *p++ = 'I';
+ if (test_bit(GLF_FROZEN, gflags))
+ *p++ = 'F';
*p = 0;
return buf;
}
@@ -1717,14 +1616,13 @@
dtime *= 1000000/HZ; /* demote time in uSec */
if (!test_bit(GLF_DEMOTE, &gl->gl_flags))
dtime = 0;
- gfs2_print_dbg(seq, "G: s:%s n:%u/%llu f:%s t:%s d:%s/%llu l:%d a:%d r:%d\n",
+ gfs2_print_dbg(seq, "G: s:%s n:%u/%llu f:%s t:%s d:%s/%llu a:%d r:%d\n",
state2str(gl->gl_state),
gl->gl_name.ln_type,
(unsigned long long)gl->gl_name.ln_number,
gflags2str(gflags_buf, &gl->gl_flags),
state2str(gl->gl_target),
state2str(gl->gl_demote_state), dtime,
- atomic_read(&gl->gl_lvb_count),
atomic_read(&gl->gl_ail_count),
atomic_read(&gl->gl_ref));
diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h
index 543ec7e..a602a28 100644
--- a/fs/gfs2/glock.h
+++ b/fs/gfs2/glock.h
@@ -11,15 +11,130 @@
#define __GLOCK_DOT_H__
#include <linux/sched.h>
+#include <linux/parser.h>
#include "incore.h"
-/* Flags for lock requests; used in gfs2_holder gh_flag field.
- From lm_interface.h:
+/* Options for hostdata parser */
+
+enum {
+ Opt_jid,
+ Opt_id,
+ Opt_first,
+ Opt_nodir,
+ Opt_err,
+};
+
+/*
+ * lm_lockname types
+ */
+
+#define LM_TYPE_RESERVED 0x00
+#define LM_TYPE_NONDISK 0x01
+#define LM_TYPE_INODE 0x02
+#define LM_TYPE_RGRP 0x03
+#define LM_TYPE_META 0x04
+#define LM_TYPE_IOPEN 0x05
+#define LM_TYPE_FLOCK 0x06
+#define LM_TYPE_PLOCK 0x07
+#define LM_TYPE_QUOTA 0x08
+#define LM_TYPE_JOURNAL 0x09
+
+/*
+ * lm_lock() states
+ *
+ * SHARED is compatible with SHARED, not with DEFERRED or EX.
+ * DEFERRED is compatible with DEFERRED, not with SHARED or EX.
+ */
+
+#define LM_ST_UNLOCKED 0
+#define LM_ST_EXCLUSIVE 1
+#define LM_ST_DEFERRED 2
+#define LM_ST_SHARED 3
+
+/*
+ * lm_lock() flags
+ *
+ * LM_FLAG_TRY
+ * Don't wait to acquire the lock if it can't be granted immediately.
+ *
+ * LM_FLAG_TRY_1CB
+ * Send one blocking callback if TRY is set and the lock is not granted.
+ *
+ * LM_FLAG_NOEXP
+ * GFS sets this flag on lock requests it makes while doing journal recovery.
+ * These special requests should not be blocked due to the recovery like
+ * ordinary locks would be.
+ *
+ * LM_FLAG_ANY
+ * A SHARED request may also be granted in DEFERRED, or a DEFERRED request may
+ * also be granted in SHARED. The preferred state is whichever is compatible
+ * with other granted locks, or the specified state if no other locks exist.
+ *
+ * LM_FLAG_PRIORITY
+ * Override fairness considerations. Suppose a lock is held in a shared state
+ * and there is a pending request for the deferred state. A shared lock
+ * request with the priority flag would be allowed to bypass the deferred
+ * request and directly join the other shared lock. A shared lock request
+ * without the priority flag might be forced to wait until the deferred
+ * requested had acquired and released the lock.
+ */
+
#define LM_FLAG_TRY 0x00000001
#define LM_FLAG_TRY_1CB 0x00000002
#define LM_FLAG_NOEXP 0x00000004
#define LM_FLAG_ANY 0x00000008
-#define LM_FLAG_PRIORITY 0x00000010 */
+#define LM_FLAG_PRIORITY 0x00000010
+#define GL_ASYNC 0x00000040
+#define GL_EXACT 0x00000080
+#define GL_SKIP 0x00000100
+#define GL_ATIME 0x00000200
+#define GL_NOCACHE 0x00000400
+
+/*
+ * lm_lock() and lm_async_cb return flags
+ *
+ * LM_OUT_ST_MASK
+ * Masks the lower two bits of lock state in the returned value.
+ *
+ * LM_OUT_CANCELED
+ * The lock request was canceled.
+ *
+ * LM_OUT_ASYNC
+ * The result of the request will be returned in an LM_CB_ASYNC callback.
+ *
+ */
+
+#define LM_OUT_ST_MASK 0x00000003
+#define LM_OUT_CANCELED 0x00000008
+#define LM_OUT_ASYNC 0x00000080
+#define LM_OUT_ERROR 0x00000100
+
+/*
+ * lm_recovery_done() messages
+ */
+
+#define LM_RD_GAVEUP 308
+#define LM_RD_SUCCESS 309
+
+#define GLR_TRYFAILED 13
+
+struct lm_lockops {
+ const char *lm_proto_name;
+ int (*lm_mount) (struct gfs2_sbd *sdp, const char *fsname);
+ void (*lm_unmount) (struct gfs2_sbd *sdp);
+ void (*lm_withdraw) (struct gfs2_sbd *sdp);
+ void (*lm_put_lock) (struct kmem_cache *cachep, void *gl);
+ unsigned int (*lm_lock) (struct gfs2_glock *gl,
+ unsigned int req_state, unsigned int flags);
+ void (*lm_cancel) (struct gfs2_glock *gl);
+ const match_table_t *lm_tokens;
+};
+
+#define LM_FLAG_TRY 0x00000001
+#define LM_FLAG_TRY_1CB 0x00000002
+#define LM_FLAG_NOEXP 0x00000004
+#define LM_FLAG_ANY 0x00000008
+#define LM_FLAG_PRIORITY 0x00000010
#define GL_ASYNC 0x00000040
#define GL_EXACT 0x00000080
@@ -128,10 +243,12 @@
int gfs2_lvb_hold(struct gfs2_glock *gl);
void gfs2_lvb_unhold(struct gfs2_glock *gl);
-void gfs2_glock_cb(void *cb_data, unsigned int type, void *data);
+void gfs2_glock_cb(struct gfs2_glock *gl, unsigned int state);
+void gfs2_glock_complete(struct gfs2_glock *gl, int ret);
void gfs2_reclaim_glock(struct gfs2_sbd *sdp);
void gfs2_gl_hash_clear(struct gfs2_sbd *sdp);
void gfs2_glock_finish_truncate(struct gfs2_inode *ip);
+void gfs2_glock_thaw(struct gfs2_sbd *sdp);
int __init gfs2_glock_init(void);
void gfs2_glock_exit(void);
@@ -141,4 +258,6 @@
int gfs2_register_debugfs(void);
void gfs2_unregister_debugfs(void);
+extern const struct lm_lockops gfs2_dlm_ops;
+
#endif /* __GLOCK_DOT_H__ */
diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c
index 8522d3a..bf23a62 100644
--- a/fs/gfs2/glops.c
+++ b/fs/gfs2/glops.c
@@ -12,7 +12,6 @@
#include <linux/completion.h>
#include <linux/buffer_head.h>
#include <linux/gfs2_ondisk.h>
-#include <linux/lm_interface.h>
#include <linux/bio.h>
#include "gfs2.h"
@@ -38,19 +37,24 @@
static void gfs2_ail_empty_gl(struct gfs2_glock *gl)
{
struct gfs2_sbd *sdp = gl->gl_sbd;
- unsigned int blocks;
struct list_head *head = &gl->gl_ail_list;
struct gfs2_bufdata *bd;
struct buffer_head *bh;
- int error;
+ struct gfs2_trans tr;
- blocks = atomic_read(&gl->gl_ail_count);
- if (!blocks)
+ memset(&tr, 0, sizeof(tr));
+ tr.tr_revokes = atomic_read(&gl->gl_ail_count);
+
+ if (!tr.tr_revokes)
return;
- error = gfs2_trans_begin(sdp, 0, blocks);
- if (gfs2_assert_withdraw(sdp, !error))
- return;
+ /* A shortened, inline version of gfs2_trans_begin() */
+ tr.tr_reserved = 1 + gfs2_struct2blk(sdp, tr.tr_revokes, sizeof(u64));
+ tr.tr_ip = (unsigned long)__builtin_return_address(0);
+ INIT_LIST_HEAD(&tr.tr_list_buf);
+ gfs2_log_reserve(sdp, tr.tr_reserved);
+ BUG_ON(current->journal_info);
+ current->journal_info = &tr;
gfs2_log_lock(sdp);
while (!list_empty(head)) {
@@ -72,29 +76,7 @@
}
/**
- * gfs2_pte_inval - Sync and invalidate all PTEs associated with a glock
- * @gl: the glock
- *
- */
-
-static void gfs2_pte_inval(struct gfs2_glock *gl)
-{
- struct gfs2_inode *ip;
- struct inode *inode;
-
- ip = gl->gl_object;
- inode = &ip->i_inode;
- if (!ip || !S_ISREG(inode->i_mode))
- return;
-
- unmap_shared_mapping_range(inode->i_mapping, 0, 0);
- if (test_bit(GIF_SW_PAGED, &ip->i_flags))
- set_bit(GLF_DIRTY, &gl->gl_flags);
-
-}
-
-/**
- * meta_go_sync - sync out the metadata for this glock
+ * rgrp_go_sync - sync out the metadata for this glock
* @gl: the glock
*
* Called when demoting or unlocking an EX glock. We must flush
@@ -102,36 +84,42 @@
* not return to caller to demote/unlock the glock until I/O is complete.
*/
-static void meta_go_sync(struct gfs2_glock *gl)
+static void rgrp_go_sync(struct gfs2_glock *gl)
{
- if (gl->gl_state != LM_ST_EXCLUSIVE)
- return;
+ struct address_space *metamapping = gl->gl_aspace->i_mapping;
+ int error;
- if (test_and_clear_bit(GLF_DIRTY, &gl->gl_flags)) {
- gfs2_log_flush(gl->gl_sbd, gl);
- gfs2_meta_sync(gl);
- gfs2_ail_empty_gl(gl);
- }
+ if (!test_and_clear_bit(GLF_DIRTY, &gl->gl_flags))
+ return;
+ BUG_ON(gl->gl_state != LM_ST_EXCLUSIVE);
+
+ gfs2_log_flush(gl->gl_sbd, gl);
+ filemap_fdatawrite(metamapping);
+ error = filemap_fdatawait(metamapping);
+ mapping_set_error(metamapping, error);
+ gfs2_ail_empty_gl(gl);
}
/**
- * meta_go_inval - invalidate the metadata for this glock
+ * rgrp_go_inval - invalidate the metadata for this glock
* @gl: the glock
* @flags:
*
+ * We never used LM_ST_DEFERRED with resource groups, so that we
+ * should always see the metadata flag set here.
+ *
*/
-static void meta_go_inval(struct gfs2_glock *gl, int flags)
+static void rgrp_go_inval(struct gfs2_glock *gl, int flags)
{
- if (!(flags & DIO_METADATA))
- return;
+ struct address_space *mapping = gl->gl_aspace->i_mapping;
- gfs2_meta_inval(gl);
- if (gl->gl_object == GFS2_I(gl->gl_sbd->sd_rindex))
- gl->gl_sbd->sd_rindex_uptodate = 0;
- else if (gl->gl_ops == &gfs2_rgrp_glops && gl->gl_object) {
+ BUG_ON(!(flags & DIO_METADATA));
+ gfs2_assert_withdraw(gl->gl_sbd, !atomic_read(&gl->gl_ail_count));
+ truncate_inode_pages(mapping, 0);
+
+ if (gl->gl_object) {
struct gfs2_rgrpd *rgd = (struct gfs2_rgrpd *)gl->gl_object;
-
rgd->rd_flags &= ~GFS2_RDF_UPTODATE;
}
}
@@ -148,48 +136,54 @@
struct address_space *metamapping = gl->gl_aspace->i_mapping;
int error;
- if (gl->gl_state != LM_ST_UNLOCKED)
- gfs2_pte_inval(gl);
- if (gl->gl_state != LM_ST_EXCLUSIVE)
- return;
-
if (ip && !S_ISREG(ip->i_inode.i_mode))
ip = NULL;
+ if (ip && test_and_clear_bit(GIF_SW_PAGED, &ip->i_flags))
+ unmap_shared_mapping_range(ip->i_inode.i_mapping, 0, 0);
+ if (!test_and_clear_bit(GLF_DIRTY, &gl->gl_flags))
+ return;
- if (test_bit(GLF_DIRTY, &gl->gl_flags)) {
- gfs2_log_flush(gl->gl_sbd, gl);
- filemap_fdatawrite(metamapping);
- if (ip) {
- struct address_space *mapping = ip->i_inode.i_mapping;
- filemap_fdatawrite(mapping);
- error = filemap_fdatawait(mapping);
- mapping_set_error(mapping, error);
- }
- error = filemap_fdatawait(metamapping);
- mapping_set_error(metamapping, error);
- clear_bit(GLF_DIRTY, &gl->gl_flags);
- gfs2_ail_empty_gl(gl);
+ BUG_ON(gl->gl_state != LM_ST_EXCLUSIVE);
+
+ gfs2_log_flush(gl->gl_sbd, gl);
+ filemap_fdatawrite(metamapping);
+ if (ip) {
+ struct address_space *mapping = ip->i_inode.i_mapping;
+ filemap_fdatawrite(mapping);
+ error = filemap_fdatawait(mapping);
+ mapping_set_error(mapping, error);
}
+ error = filemap_fdatawait(metamapping);
+ mapping_set_error(metamapping, error);
+ gfs2_ail_empty_gl(gl);
}
/**
* inode_go_inval - prepare a inode glock to be released
* @gl: the glock
* @flags:
+ *
+ * Normally we invlidate everything, but if we are moving into
+ * LM_ST_DEFERRED from LM_ST_SHARED or LM_ST_EXCLUSIVE then we
+ * can keep hold of the metadata, since it won't have changed.
*
*/
static void inode_go_inval(struct gfs2_glock *gl, int flags)
{
struct gfs2_inode *ip = gl->gl_object;
- int meta = (flags & DIO_METADATA);
- if (meta) {
- gfs2_meta_inval(gl);
+ gfs2_assert_withdraw(gl->gl_sbd, !atomic_read(&gl->gl_ail_count));
+
+ if (flags & DIO_METADATA) {
+ struct address_space *mapping = gl->gl_aspace->i_mapping;
+ truncate_inode_pages(mapping, 0);
if (ip)
set_bit(GIF_INVALID, &ip->i_flags);
}
+ if (ip == GFS2_I(gl->gl_sbd->sd_rindex))
+ gl->gl_sbd->sd_rindex_uptodate = 0;
if (ip && S_ISREG(ip->i_inode.i_mode))
truncate_inode_pages(ip->i_inode.i_mapping, 0);
}
@@ -390,20 +384,7 @@
return 0;
}
-/**
- * quota_go_demote_ok - Check to see if it's ok to unlock a quota glock
- * @gl: the glock
- *
- * Returns: 1 if it's ok
- */
-
-static int quota_go_demote_ok(const struct gfs2_glock *gl)
-{
- return !atomic_read(&gl->gl_lvb_count);
-}
-
const struct gfs2_glock_operations gfs2_meta_glops = {
- .go_xmote_th = meta_go_sync,
.go_type = LM_TYPE_META,
};
@@ -418,8 +399,8 @@
};
const struct gfs2_glock_operations gfs2_rgrp_glops = {
- .go_xmote_th = meta_go_sync,
- .go_inval = meta_go_inval,
+ .go_xmote_th = rgrp_go_sync,
+ .go_inval = rgrp_go_inval,
.go_demote_ok = rgrp_go_demote_ok,
.go_lock = rgrp_go_lock,
.go_unlock = rgrp_go_unlock,
@@ -448,7 +429,6 @@
};
const struct gfs2_glock_operations gfs2_quota_glops = {
- .go_demote_ok = quota_go_demote_ok,
.go_type = LM_TYPE_QUOTA,
};
@@ -456,3 +436,15 @@
.go_type = LM_TYPE_JOURNAL,
};
+const struct gfs2_glock_operations *gfs2_glops_list[] = {
+ [LM_TYPE_META] = &gfs2_meta_glops,
+ [LM_TYPE_INODE] = &gfs2_inode_glops,
+ [LM_TYPE_RGRP] = &gfs2_rgrp_glops,
+ [LM_TYPE_NONDISK] = &gfs2_trans_glops,
+ [LM_TYPE_IOPEN] = &gfs2_iopen_glops,
+ [LM_TYPE_FLOCK] = &gfs2_flock_glops,
+ [LM_TYPE_NONDISK] = &gfs2_nondisk_glops,
+ [LM_TYPE_QUOTA] = &gfs2_quota_glops,
+ [LM_TYPE_JOURNAL] = &gfs2_journal_glops,
+};
+
diff --git a/fs/gfs2/glops.h b/fs/gfs2/glops.h
index a1d9b5b..b3aa2e3 100644
--- a/fs/gfs2/glops.h
+++ b/fs/gfs2/glops.h
@@ -21,5 +21,6 @@
extern const struct gfs2_glock_operations gfs2_nondisk_glops;
extern const struct gfs2_glock_operations gfs2_quota_glops;
extern const struct gfs2_glock_operations gfs2_journal_glops;
+extern const struct gfs2_glock_operations *gfs2_glops_list[];
#endif /* __GLOPS_DOT_H__ */
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index 608849d..399d1b9 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -12,6 +12,8 @@
#include <linux/fs.h>
#include <linux/workqueue.h>
+#include <linux/dlm.h>
+#include <linux/buffer_head.h>
#define DIO_WAIT 0x00000010
#define DIO_METADATA 0x00000020
@@ -26,6 +28,7 @@
struct gfs2_ail;
struct gfs2_jdesc;
struct gfs2_sbd;
+struct lm_lockops;
typedef void (*gfs2_glop_bh_t) (struct gfs2_glock *gl, unsigned int ret);
@@ -121,6 +124,28 @@
struct list_head bd_ail_gl_list;
};
+/*
+ * Internally, we prefix things with gdlm_ and GDLM_ (for gfs-dlm) since a
+ * prefix of lock_dlm_ gets awkward.
+ */
+
+#define GDLM_STRNAME_BYTES 25
+#define GDLM_LVB_SIZE 32
+
+enum {
+ DFL_BLOCK_LOCKS = 0,
+};
+
+struct lm_lockname {
+ u64 ln_number;
+ unsigned int ln_type;
+};
+
+#define lm_name_equal(name1, name2) \
+ (((name1)->ln_number == (name2)->ln_number) && \
+ ((name1)->ln_type == (name2)->ln_type))
+
+
struct gfs2_glock_operations {
void (*go_xmote_th) (struct gfs2_glock *gl);
int (*go_xmote_bh) (struct gfs2_glock *gl, struct gfs2_holder *gh);
@@ -162,6 +187,8 @@
GLF_LFLUSH = 7,
GLF_INVALIDATE_IN_PROGRESS = 8,
GLF_REPLY_PENDING = 9,
+ GLF_INITIAL = 10,
+ GLF_FROZEN = 11,
};
struct gfs2_glock {
@@ -176,16 +203,15 @@
unsigned int gl_target;
unsigned int gl_reply;
unsigned int gl_hash;
+ unsigned int gl_req;
unsigned int gl_demote_state; /* state requested by remote node */
unsigned long gl_demote_time; /* time of first demote request */
struct list_head gl_holders;
const struct gfs2_glock_operations *gl_ops;
- void *gl_lock;
- char *gl_lvb;
- atomic_t gl_lvb_count;
-
- unsigned long gl_stamp;
+ char gl_strname[GDLM_STRNAME_BYTES];
+ struct dlm_lksb gl_lksb;
+ char gl_lvb[32];
unsigned long gl_tchange;
void *gl_object;
@@ -283,7 +309,9 @@
struct gfs2_quota_data {
struct list_head qd_list;
- unsigned int qd_count;
+ struct list_head qd_reclaim;
+
+ atomic_t qd_count;
u32 qd_id;
unsigned long qd_flags; /* QDF_... */
@@ -303,7 +331,6 @@
u64 qd_sync_gen;
unsigned long qd_last_warn;
- unsigned long qd_last_touched;
};
struct gfs2_trans {
@@ -390,7 +417,7 @@
unsigned int ar_suiddir:1; /* suiddir support */
unsigned int ar_data:2; /* ordered/writeback */
unsigned int ar_meta:1; /* mount metafs */
- unsigned int ar_num_glockd; /* Number of glockd threads */
+ unsigned int ar_discard:1; /* discard requests */
};
struct gfs2_tune {
@@ -406,7 +433,6 @@
unsigned int gt_quota_warn_period; /* Secs between quota warn msgs */
unsigned int gt_quota_scale_num; /* Numerator */
unsigned int gt_quota_scale_den; /* Denominator */
- unsigned int gt_quota_cache_secs;
unsigned int gt_quota_quantum; /* Secs between syncs to quota file */
unsigned int gt_new_files_jdata;
unsigned int gt_max_readahead; /* Max bytes to read-ahead from disk */
@@ -445,6 +471,31 @@
char sb_lockproto[GFS2_LOCKNAME_LEN];
char sb_locktable[GFS2_LOCKNAME_LEN];
+ u8 sb_uuid[16];
+};
+
+/*
+ * lm_mount() return values
+ *
+ * ls_jid - the journal ID this node should use
+ * ls_first - this node is the first to mount the file system
+ * ls_lockspace - lock module's context for this file system
+ * ls_ops - lock module's functions
+ */
+
+struct lm_lockstruct {
+ u32 ls_id;
+ unsigned int ls_jid;
+ unsigned int ls_first;
+ unsigned int ls_first_done;
+ unsigned int ls_nodir;
+ const struct lm_lockops *ls_ops;
+ unsigned long ls_flags;
+ dlm_lockspace_t *ls_dlm;
+
+ int ls_recover_jid;
+ int ls_recover_jid_done;
+ int ls_recover_jid_status;
};
struct gfs2_sbd {
@@ -520,7 +571,6 @@
spinlock_t sd_jindex_spin;
struct mutex sd_jindex_mutex;
unsigned int sd_journals;
- unsigned long sd_jindex_refresh_time;
struct gfs2_jdesc *sd_jdesc;
struct gfs2_holder sd_journal_gh;
@@ -540,7 +590,6 @@
struct list_head sd_quota_list;
atomic_t sd_quota_count;
- spinlock_t sd_quota_spin;
struct mutex sd_quota_mutex;
wait_queue_head_t sd_quota_wait;
struct list_head sd_trunc_list;
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 3b87c18..7b277d44 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -16,7 +16,6 @@
#include <linux/sort.h>
#include <linux/gfs2_ondisk.h>
#include <linux/crc32.h>
-#include <linux/lm_interface.h>
#include <linux/security.h>
#include <linux/time.h>
@@ -137,16 +136,16 @@
if (S_ISREG(mode)) {
inode->i_op = &gfs2_file_iops;
- if (sdp->sd_args.ar_localflocks)
- inode->i_fop = &gfs2_file_fops_nolock;
+ if (gfs2_localflocks(sdp))
+ inode->i_fop = gfs2_file_fops_nolock;
else
- inode->i_fop = &gfs2_file_fops;
+ inode->i_fop = gfs2_file_fops;
} else if (S_ISDIR(mode)) {
inode->i_op = &gfs2_dir_iops;
- if (sdp->sd_args.ar_localflocks)
- inode->i_fop = &gfs2_dir_fops_nolock;
+ if (gfs2_localflocks(sdp))
+ inode->i_fop = gfs2_dir_fops_nolock;
else
- inode->i_fop = &gfs2_dir_fops;
+ inode->i_fop = gfs2_dir_fops;
} else if (S_ISLNK(mode)) {
inode->i_op = &gfs2_symlink_iops;
} else {
diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h
index d532936..dca4fee 100644
--- a/fs/gfs2/inode.h
+++ b/fs/gfs2/inode.h
@@ -101,12 +101,26 @@
extern const struct inode_operations gfs2_file_iops;
extern const struct inode_operations gfs2_dir_iops;
extern const struct inode_operations gfs2_symlink_iops;
-extern const struct file_operations gfs2_file_fops;
-extern const struct file_operations gfs2_dir_fops;
-extern const struct file_operations gfs2_file_fops_nolock;
-extern const struct file_operations gfs2_dir_fops_nolock;
+extern const struct file_operations *gfs2_file_fops_nolock;
+extern const struct file_operations *gfs2_dir_fops_nolock;
extern void gfs2_set_inode_flags(struct inode *inode);
+
+#ifdef CONFIG_GFS2_FS_LOCKING_DLM
+extern const struct file_operations *gfs2_file_fops;
+extern const struct file_operations *gfs2_dir_fops;
+static inline int gfs2_localflocks(const struct gfs2_sbd *sdp)
+{
+ return sdp->sd_args.ar_localflocks;
+}
+#else /* Single node only */
+#define gfs2_file_fops NULL
+#define gfs2_dir_fops NULL
+static inline int gfs2_localflocks(const struct gfs2_sbd *sdp)
+{
+ return 1;
+}
+#endif /* CONFIG_GFS2_FS_LOCKING_DLM */
#endif /* __INODE_DOT_H__ */
diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c
new file mode 100644
index 0000000..46df988
--- /dev/null
+++ b/fs/gfs2/lock_dlm.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License version 2.
+ */
+
+#include <linux/fs.h>
+#include <linux/dlm.h>
+#include <linux/types.h>
+#include <linux/gfs2_ondisk.h>
+
+#include "incore.h"
+#include "glock.h"
+#include "util.h"
+
+
+static void gdlm_ast(void *arg)
+{
+ struct gfs2_glock *gl = arg;
+ unsigned ret = gl->gl_state;
+
+ BUG_ON(gl->gl_lksb.sb_flags & DLM_SBF_DEMOTED);
+
+ if (gl->gl_lksb.sb_flags & DLM_SBF_VALNOTVALID)
+ memset(gl->gl_lvb, 0, GDLM_LVB_SIZE);
+
+ switch (gl->gl_lksb.sb_status) {
+ case -DLM_EUNLOCK: /* Unlocked, so glock can be freed */
+ kmem_cache_free(gfs2_glock_cachep, gl);
+ return;
+ case -DLM_ECANCEL: /* Cancel while getting lock */
+ ret |= LM_OUT_CANCELED;
+ goto out;
+ case -EAGAIN: /* Try lock fails */
+ goto out;
+ case -EINVAL: /* Invalid */
+ case -ENOMEM: /* Out of memory */
+ ret |= LM_OUT_ERROR;
+ goto out;
+ case 0: /* Success */
+ break;
+ default: /* Something unexpected */
+ BUG();
+ }
+
+ ret = gl->gl_req;
+ if (gl->gl_lksb.sb_flags & DLM_SBF_ALTMODE) {
+ if (gl->gl_req == LM_ST_SHARED)
+ ret = LM_ST_DEFERRED;
+ else if (gl->gl_req == LM_ST_DEFERRED)
+ ret = LM_ST_SHARED;
+ else
+ BUG();
+ }
+
+ set_bit(GLF_INITIAL, &gl->gl_flags);
+ gfs2_glock_complete(gl, ret);
+ return;
+out:
+ if (!test_bit(GLF_INITIAL, &gl->gl_flags))
+ gl->gl_lksb.sb_lkid = 0;
+ gfs2_glock_complete(gl, ret);
+}
+
+static void gdlm_bast(void *arg, int mode)
+{
+ struct gfs2_glock *gl = arg;
+
+ switch (mode) {
+ case DLM_LOCK_EX:
+ gfs2_glock_cb(gl, LM_ST_UNLOCKED);
+ break;
+ case DLM_LOCK_CW:
+ gfs2_glock_cb(gl, LM_ST_DEFERRED);
+ break;
+ case DLM_LOCK_PR:
+ gfs2_glock_cb(gl, LM_ST_SHARED);
+ break;
+ default:
+ printk(KERN_ERR "unknown bast mode %d", mode);
+ BUG();
+ }
+}
+
+/* convert gfs lock-state to dlm lock-mode */
+
+static int make_mode(const unsigned int lmstate)
+{
+ switch (lmstate) {
+ case LM_ST_UNLOCKED:
+ return DLM_LOCK_NL;
+ case LM_ST_EXCLUSIVE:
+ return DLM_LOCK_EX;
+ case LM_ST_DEFERRED:
+ return DLM_LOCK_CW;
+ case LM_ST_SHARED:
+ return DLM_LOCK_PR;
+ }
+ printk(KERN_ERR "unknown LM state %d", lmstate);
+ BUG();
+ return -1;
+}
+
+static u32 make_flags(const u32 lkid, const unsigned int gfs_flags,
+ const int req)
+{
+ u32 lkf = 0;
+
+ if (gfs_flags & LM_FLAG_TRY)
+ lkf |= DLM_LKF_NOQUEUE;
+
+ if (gfs_flags & LM_FLAG_TRY_1CB) {
+ lkf |= DLM_LKF_NOQUEUE;
+ lkf |= DLM_LKF_NOQUEUEBAST;
+ }
+
+ if (gfs_flags & LM_FLAG_PRIORITY) {
+ lkf |= DLM_LKF_NOORDER;
+ lkf |= DLM_LKF_HEADQUE;
+ }
+
+ if (gfs_flags & LM_FLAG_ANY) {
+ if (req == DLM_LOCK_PR)
+ lkf |= DLM_LKF_ALTCW;
+ else if (req == DLM_LOCK_CW)
+ lkf |= DLM_LKF_ALTPR;
+ else
+ BUG();
+ }
+
+ if (lkid != 0)
+ lkf |= DLM_LKF_CONVERT;
+
+ lkf |= DLM_LKF_VALBLK;
+
+ return lkf;
+}
+
+static unsigned int gdlm_lock(struct gfs2_glock *gl,
+ unsigned int req_state, unsigned int flags)
+{
+ struct lm_lockstruct *ls = &gl->gl_sbd->sd_lockstruct;
+ int error;
+ int req;
+ u32 lkf;
+
+ gl->gl_req = req_state;
+ req = make_mode(req_state);
+ lkf = make_flags(gl->gl_lksb.sb_lkid, flags, req);
+
+ /*
+ * Submit the actual lock request.
+ */
+
+ error = dlm_lock(ls->ls_dlm, req, &gl->gl_lksb, lkf, gl->gl_strname,
+ GDLM_STRNAME_BYTES - 1, 0, gdlm_ast, gl, gdlm_bast);
+ if (error == -EAGAIN)
+ return 0;
+ if (error)
+ return LM_OUT_ERROR;
+ return LM_OUT_ASYNC;
+}
+
+static void gdlm_put_lock(struct kmem_cache *cachep, void *ptr)
+{
+ struct gfs2_glock *gl = ptr;
+ struct lm_lockstruct *ls = &gl->gl_sbd->sd_lockstruct;
+ int error;
+
+ if (gl->gl_lksb.sb_lkid == 0) {
+ kmem_cache_free(cachep, gl);
+ return;
+ }
+
+ error = dlm_unlock(ls->ls_dlm, gl->gl_lksb.sb_lkid, DLM_LKF_VALBLK,
+ NULL, gl);
+ if (error) {
+ printk(KERN_ERR "gdlm_unlock %x,%llx err=%d\n",
+ gl->gl_name.ln_type,
+ (unsigned long long)gl->gl_name.ln_number, error);
+ return;
+ }
+}
+
+static void gdlm_cancel(struct gfs2_glock *gl)
+{
+ struct lm_lockstruct *ls = &gl->gl_sbd->sd_lockstruct;
+ dlm_unlock(ls->ls_dlm, gl->gl_lksb.sb_lkid, DLM_LKF_CANCEL, NULL, gl);
+}
+
+static int gdlm_mount(struct gfs2_sbd *sdp, const char *fsname)
+{
+ struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+ int error;
+
+ if (fsname == NULL) {
+ fs_info(sdp, "no fsname found\n");
+ return -EINVAL;
+ }
+
+ error = dlm_new_lockspace(fsname, strlen(fsname), &ls->ls_dlm,
+ DLM_LSFL_FS | DLM_LSFL_NEWEXCL |
+ (ls->ls_nodir ? DLM_LSFL_NODIR : 0),
+ GDLM_LVB_SIZE);
+ if (error)
+ printk(KERN_ERR "dlm_new_lockspace error %d", error);
+
+ return error;
+}
+
+static void gdlm_unmount(struct gfs2_sbd *sdp)
+{
+ struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+
+ if (ls->ls_dlm) {
+ dlm_release_lockspace(ls->ls_dlm, 2);
+ ls->ls_dlm = NULL;
+ }
+}
+
+static const match_table_t dlm_tokens = {
+ { Opt_jid, "jid=%d"},
+ { Opt_id, "id=%d"},
+ { Opt_first, "first=%d"},
+ { Opt_nodir, "nodir=%d"},
+ { Opt_err, NULL },
+};
+
+const struct lm_lockops gfs2_dlm_ops = {
+ .lm_proto_name = "lock_dlm",
+ .lm_mount = gdlm_mount,
+ .lm_unmount = gdlm_unmount,
+ .lm_put_lock = gdlm_put_lock,
+ .lm_lock = gdlm_lock,
+ .lm_cancel = gdlm_cancel,
+ .lm_tokens = &dlm_tokens,
+};
+
diff --git a/fs/gfs2/locking.c b/fs/gfs2/locking.c
deleted file mode 100644
index 523243a..0000000
--- a/fs/gfs2/locking.c
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
- * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU General Public License version 2.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/wait.h>
-#include <linux/sched.h>
-#include <linux/kmod.h>
-#include <linux/fs.h>
-#include <linux/delay.h>
-#include <linux/lm_interface.h>
-
-struct lmh_wrapper {
- struct list_head lw_list;
- const struct lm_lockops *lw_ops;
-};
-
-static int nolock_mount(char *table_name, char *host_data,
- lm_callback_t cb, void *cb_data,
- unsigned int min_lvb_size, int flags,
- struct lm_lockstruct *lockstruct,
- struct kobject *fskobj);
-
-/* List of registered low-level locking protocols. A file system selects one
- of them by name at mount time, e.g. lock_nolock, lock_dlm. */
-
-static const struct lm_lockops nolock_ops = {
- .lm_proto_name = "lock_nolock",
- .lm_mount = nolock_mount,
-};
-
-static struct lmh_wrapper nolock_proto = {
- .lw_list = LIST_HEAD_INIT(nolock_proto.lw_list),
- .lw_ops = &nolock_ops,
-};
-
-static LIST_HEAD(lmh_list);
-static DEFINE_MUTEX(lmh_lock);
-
-static int nolock_mount(char *table_name, char *host_data,
- lm_callback_t cb, void *cb_data,
- unsigned int min_lvb_size, int flags,
- struct lm_lockstruct *lockstruct,
- struct kobject *fskobj)
-{
- char *c;
- unsigned int jid;
-
- c = strstr(host_data, "jid=");
- if (!c)
- jid = 0;
- else {
- c += 4;
- sscanf(c, "%u", &jid);
- }
-
- lockstruct->ls_jid = jid;
- lockstruct->ls_first = 1;
- lockstruct->ls_lvb_size = min_lvb_size;
- lockstruct->ls_ops = &nolock_ops;
- lockstruct->ls_flags = LM_LSFLAG_LOCAL;
-
- return 0;
-}
-
-/**
- * gfs2_register_lockproto - Register a low-level locking protocol
- * @proto: the protocol definition
- *
- * Returns: 0 on success, -EXXX on failure
- */
-
-int gfs2_register_lockproto(const struct lm_lockops *proto)
-{
- struct lmh_wrapper *lw;
-
- mutex_lock(&lmh_lock);
-
- list_for_each_entry(lw, &lmh_list, lw_list) {
- if (!strcmp(lw->lw_ops->lm_proto_name, proto->lm_proto_name)) {
- mutex_unlock(&lmh_lock);
- printk(KERN_INFO "GFS2: protocol %s already exists\n",
- proto->lm_proto_name);
- return -EEXIST;
- }
- }
-
- lw = kzalloc(sizeof(struct lmh_wrapper), GFP_KERNEL);
- if (!lw) {
- mutex_unlock(&lmh_lock);
- return -ENOMEM;
- }
-
- lw->lw_ops = proto;
- list_add(&lw->lw_list, &lmh_list);
-
- mutex_unlock(&lmh_lock);
-
- return 0;
-}
-
-/**
- * gfs2_unregister_lockproto - Unregister a low-level locking protocol
- * @proto: the protocol definition
- *
- */
-
-void gfs2_unregister_lockproto(const struct lm_lockops *proto)
-{
- struct lmh_wrapper *lw;
-
- mutex_lock(&lmh_lock);
-
- list_for_each_entry(lw, &lmh_list, lw_list) {
- if (!strcmp(lw->lw_ops->lm_proto_name, proto->lm_proto_name)) {
- list_del(&lw->lw_list);
- mutex_unlock(&lmh_lock);
- kfree(lw);
- return;
- }
- }
-
- mutex_unlock(&lmh_lock);
-
- printk(KERN_WARNING "GFS2: can't unregister lock protocol %s\n",
- proto->lm_proto_name);
-}
-
-/**
- * gfs2_mount_lockproto - Mount a lock protocol
- * @proto_name - the name of the protocol
- * @table_name - the name of the lock space
- * @host_data - data specific to this host
- * @cb - the callback to the code using the lock module
- * @sdp - The GFS2 superblock
- * @min_lvb_size - the mininum LVB size that the caller can deal with
- * @flags - LM_MFLAG_*
- * @lockstruct - a structure returned describing the mount
- *
- * Returns: 0 on success, -EXXX on failure
- */
-
-int gfs2_mount_lockproto(char *proto_name, char *table_name, char *host_data,
- lm_callback_t cb, void *cb_data,
- unsigned int min_lvb_size, int flags,
- struct lm_lockstruct *lockstruct,
- struct kobject *fskobj)
-{
- struct lmh_wrapper *lw = NULL;
- int try = 0;
- int error, found;
-
-
-retry:
- mutex_lock(&lmh_lock);
-
- if (list_empty(&nolock_proto.lw_list))
- list_add(&nolock_proto.lw_list, &lmh_list);
-
- found = 0;
- list_for_each_entry(lw, &lmh_list, lw_list) {
- if (!strcmp(lw->lw_ops->lm_proto_name, proto_name)) {
- found = 1;
- break;
- }
- }
-
- if (!found) {
- if (!try && capable(CAP_SYS_MODULE)) {
- try = 1;
- mutex_unlock(&lmh_lock);
- request_module(proto_name);
- goto retry;
- }
- printk(KERN_INFO "GFS2: can't find protocol %s\n", proto_name);
- error = -ENOENT;
- goto out;
- }
-
- if (lw->lw_ops->lm_owner &&
- !try_module_get(lw->lw_ops->lm_owner)) {
- try = 0;
- mutex_unlock(&lmh_lock);
- msleep(1000);
- goto retry;
- }
-
- error = lw->lw_ops->lm_mount(table_name, host_data, cb, cb_data,
- min_lvb_size, flags, lockstruct, fskobj);
- if (error)
- module_put(lw->lw_ops->lm_owner);
-out:
- mutex_unlock(&lmh_lock);
- return error;
-}
-
-void gfs2_unmount_lockproto(struct lm_lockstruct *lockstruct)
-{
- mutex_lock(&lmh_lock);
- if (lockstruct->ls_ops->lm_unmount)
- lockstruct->ls_ops->lm_unmount(lockstruct->ls_lockspace);
- if (lockstruct->ls_ops->lm_owner)
- module_put(lockstruct->ls_ops->lm_owner);
- mutex_unlock(&lmh_lock);
-}
-
-/**
- * gfs2_withdraw_lockproto - abnormally unmount a lock module
- * @lockstruct: the lockstruct passed into mount
- *
- */
-
-void gfs2_withdraw_lockproto(struct lm_lockstruct *lockstruct)
-{
- mutex_lock(&lmh_lock);
- lockstruct->ls_ops->lm_withdraw(lockstruct->ls_lockspace);
- if (lockstruct->ls_ops->lm_owner)
- module_put(lockstruct->ls_ops->lm_owner);
- mutex_unlock(&lmh_lock);
-}
-
-EXPORT_SYMBOL_GPL(gfs2_register_lockproto);
-EXPORT_SYMBOL_GPL(gfs2_unregister_lockproto);
-
diff --git a/fs/gfs2/locking/dlm/Makefile b/fs/gfs2/locking/dlm/Makefile
deleted file mode 100644
index 2609bb6..0000000
--- a/fs/gfs2/locking/dlm/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-obj-$(CONFIG_GFS2_FS_LOCKING_DLM) += lock_dlm.o
-lock_dlm-y := lock.o main.o mount.o sysfs.o thread.o
-
diff --git a/fs/gfs2/locking/dlm/lock.c b/fs/gfs2/locking/dlm/lock.c
deleted file mode 100644
index 2482c90..0000000
--- a/fs/gfs2/locking/dlm/lock.c
+++ /dev/null
@@ -1,708 +0,0 @@
-/*
- * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
- * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU General Public License version 2.
- */
-
-#include "lock_dlm.h"
-
-static char junk_lvb[GDLM_LVB_SIZE];
-
-
-/* convert dlm lock-mode to gfs lock-state */
-
-static s16 gdlm_make_lmstate(s16 dlmmode)
-{
- switch (dlmmode) {
- case DLM_LOCK_IV:
- case DLM_LOCK_NL:
- return LM_ST_UNLOCKED;
- case DLM_LOCK_EX:
- return LM_ST_EXCLUSIVE;
- case DLM_LOCK_CW:
- return LM_ST_DEFERRED;
- case DLM_LOCK_PR:
- return LM_ST_SHARED;
- }
- gdlm_assert(0, "unknown DLM mode %d", dlmmode);
- return -1;
-}
-
-/* A lock placed on this queue is re-submitted to DLM as soon as the lock_dlm
- thread gets to it. */
-
-static void queue_submit(struct gdlm_lock *lp)
-{
- struct gdlm_ls *ls = lp->ls;
-
- spin_lock(&ls->async_lock);
- list_add_tail(&lp->delay_list, &ls->submit);
- spin_unlock(&ls->async_lock);
- wake_up(&ls->thread_wait);
-}
-
-static void wake_up_ast(struct gdlm_lock *lp)
-{
- clear_bit(LFL_AST_WAIT, &lp->flags);
- smp_mb__after_clear_bit();
- wake_up_bit(&lp->flags, LFL_AST_WAIT);
-}
-
-static void gdlm_delete_lp(struct gdlm_lock *lp)
-{
- struct gdlm_ls *ls = lp->ls;
-
- spin_lock(&ls->async_lock);
- if (!list_empty(&lp->delay_list))
- list_del_init(&lp->delay_list);
- ls->all_locks_count--;
- spin_unlock(&ls->async_lock);
-
- kfree(lp);
-}
-
-static void gdlm_queue_delayed(struct gdlm_lock *lp)
-{
- struct gdlm_ls *ls = lp->ls;
-
- spin_lock(&ls->async_lock);
- list_add_tail(&lp->delay_list, &ls->delayed);
- spin_unlock(&ls->async_lock);
-}
-
-static void process_complete(struct gdlm_lock *lp)
-{
- struct gdlm_ls *ls = lp->ls;
- struct lm_async_cb acb;
-
- memset(&acb, 0, sizeof(acb));
-
- if (lp->lksb.sb_status == -DLM_ECANCEL) {
- log_info("complete dlm cancel %x,%llx flags %lx",
- lp->lockname.ln_type,
- (unsigned long long)lp->lockname.ln_number,
- lp->flags);
-
- lp->req = lp->cur;
- acb.lc_ret |= LM_OUT_CANCELED;
- if (lp->cur == DLM_LOCK_IV)
- lp->lksb.sb_lkid = 0;
- goto out;
- }
-
- if (test_and_clear_bit(LFL_DLM_UNLOCK, &lp->flags)) {
- if (lp->lksb.sb_status != -DLM_EUNLOCK) {
- log_info("unlock sb_status %d %x,%llx flags %lx",
- lp->lksb.sb_status, lp->lockname.ln_type,
- (unsigned long long)lp->lockname.ln_number,
- lp->flags);
- return;
- }
-
- lp->cur = DLM_LOCK_IV;
- lp->req = DLM_LOCK_IV;
- lp->lksb.sb_lkid = 0;
-
- if (test_and_clear_bit(LFL_UNLOCK_DELETE, &lp->flags)) {
- gdlm_delete_lp(lp);
- return;
- }
- goto out;
- }
-
- if (lp->lksb.sb_flags & DLM_SBF_VALNOTVALID)
- memset(lp->lksb.sb_lvbptr, 0, GDLM_LVB_SIZE);
-
- if (lp->lksb.sb_flags & DLM_SBF_ALTMODE) {
- if (lp->req == DLM_LOCK_PR)
- lp->req = DLM_LOCK_CW;
- else if (lp->req == DLM_LOCK_CW)
- lp->req = DLM_LOCK_PR;
- }
-
- /*
- * A canceled lock request. The lock was just taken off the delayed
- * list and was never even submitted to dlm.
- */
-
- if (test_and_clear_bit(LFL_CANCEL, &lp->flags)) {
- log_info("complete internal cancel %x,%llx",
- lp->lockname.ln_type,
- (unsigned long long)lp->lockname.ln_number);
- lp->req = lp->cur;
- acb.lc_ret |= LM_OUT_CANCELED;
- goto out;
- }
-
- /*
- * An error occured.
- */
-
- if (lp->lksb.sb_status) {
- /* a "normal" error */
- if ((lp->lksb.sb_status == -EAGAIN) &&
- (lp->lkf & DLM_LKF_NOQUEUE)) {
- lp->req = lp->cur;
- if (lp->cur == DLM_LOCK_IV)
- lp->lksb.sb_lkid = 0;
- goto out;
- }
-
- /* this could only happen with cancels I think */
- log_info("ast sb_status %d %x,%llx flags %lx",
- lp->lksb.sb_status, lp->lockname.ln_type,
- (unsigned long long)lp->lockname.ln_number,
- lp->flags);
- return;
- }
-
- /*
- * This is an AST for an EX->EX conversion for sync_lvb from GFS.
- */
-
- if (test_and_clear_bit(LFL_SYNC_LVB, &lp->flags)) {
- wake_up_ast(lp);
- return;
- }
-
- /*
- * A lock has been demoted to NL because it initially completed during
- * BLOCK_LOCKS. Now it must be requested in the originally requested
- * mode.
- */
-
- if (test_and_clear_bit(LFL_REREQUEST, &lp->flags)) {
- gdlm_assert(lp->req == DLM_LOCK_NL, "%x,%llx",
- lp->lockname.ln_type,
- (unsigned long long)lp->lockname.ln_number);
- gdlm_assert(lp->prev_req > DLM_LOCK_NL, "%x,%llx",
- lp->lockname.ln_type,
- (unsigned long long)lp->lockname.ln_number);
-
- lp->cur = DLM_LOCK_NL;
- lp->req = lp->prev_req;
- lp->prev_req = DLM_LOCK_IV;
- lp->lkf &= ~DLM_LKF_CONVDEADLK;
-
- set_bit(LFL_NOCACHE, &lp->flags);
-
- if (test_bit(DFL_BLOCK_LOCKS, &ls->flags) &&
- !test_bit(LFL_NOBLOCK, &lp->flags))
- gdlm_queue_delayed(lp);
- else
- queue_submit(lp);
- return;
- }
-
- /*
- * A request is granted during dlm recovery. It may be granted
- * because the locks of a failed node were cleared. In that case,
- * there may be inconsistent data beneath this lock and we must wait
- * for recovery to complete to use it. When gfs recovery is done this
- * granted lock will be converted to NL and then reacquired in this
- * granted state.
- */
-
- if (test_bit(DFL_BLOCK_LOCKS, &ls->flags) &&
- !test_bit(LFL_NOBLOCK, &lp->flags) &&
- lp->req != DLM_LOCK_NL) {
-
- lp->cur = lp->req;
- lp->prev_req = lp->req;
- lp->req = DLM_LOCK_NL;
- lp->lkf |= DLM_LKF_CONVERT;
- lp->lkf &= ~DLM_LKF_CONVDEADLK;
-
- log_debug("rereq %x,%llx id %x %d,%d",
- lp->lockname.ln_type,
- (unsigned long long)lp->lockname.ln_number,
- lp->lksb.sb_lkid, lp->cur, lp->req);
-
- set_bit(LFL_REREQUEST, &lp->flags);
- queue_submit(lp);
- return;
- }
-
- /*
- * DLM demoted the lock to NL before it was granted so GFS must be
- * told it cannot cache data for this lock.
- */
-
- if (lp->lksb.sb_flags & DLM_SBF_DEMOTED)
- set_bit(LFL_NOCACHE, &lp->flags);
-
-out:
- /*
- * This is an internal lock_dlm lock
- */
-
- if (test_bit(LFL_INLOCK, &lp->flags)) {
- clear_bit(LFL_NOBLOCK, &lp->flags);
- lp->cur = lp->req;
- wake_up_ast(lp);
- return;
- }
-
- /*
- * Normal completion of a lock request. Tell GFS it now has the lock.
- */
-
- clear_bit(LFL_NOBLOCK, &lp->flags);
- lp->cur = lp->req;
-
- acb.lc_name = lp->lockname;
- acb.lc_ret |= gdlm_make_lmstate(lp->cur);
-
- ls->fscb(ls->sdp, LM_CB_ASYNC, &acb);
-}
-
-static void gdlm_ast(void *astarg)
-{
- struct gdlm_lock *lp = astarg;
- clear_bit(LFL_ACTIVE, &lp->flags);
- process_complete(lp);
-}
-
-static void process_blocking(struct gdlm_lock *lp, int bast_mode)
-{
- struct gdlm_ls *ls = lp->ls;
- unsigned int cb = 0;
-
- switch (gdlm_make_lmstate(bast_mode)) {
- case LM_ST_EXCLUSIVE:
- cb = LM_CB_NEED_E;
- break;
- case LM_ST_DEFERRED:
- cb = LM_CB_NEED_D;
- break;
- case LM_ST_SHARED:
- cb = LM_CB_NEED_S;
- break;
- default:
- gdlm_assert(0, "unknown bast mode %u", bast_mode);
- }
-
- ls->fscb(ls->sdp, cb, &lp->lockname);
-}
-
-
-static void gdlm_bast(void *astarg, int mode)
-{
- struct gdlm_lock *lp = astarg;
-
- if (!mode) {
- printk(KERN_INFO "lock_dlm: bast mode zero %x,%llx\n",
- lp->lockname.ln_type,
- (unsigned long long)lp->lockname.ln_number);
- return;
- }
-
- process_blocking(lp, mode);
-}
-
-/* convert gfs lock-state to dlm lock-mode */
-
-static s16 make_mode(s16 lmstate)
-{
- switch (lmstate) {
- case LM_ST_UNLOCKED:
- return DLM_LOCK_NL;
- case LM_ST_EXCLUSIVE:
- return DLM_LOCK_EX;
- case LM_ST_DEFERRED:
- return DLM_LOCK_CW;
- case LM_ST_SHARED:
- return DLM_LOCK_PR;
- }
- gdlm_assert(0, "unknown LM state %d", lmstate);
- return -1;
-}
-
-
-/* verify agreement with GFS on the current lock state, NB: DLM_LOCK_NL and
- DLM_LOCK_IV are both considered LM_ST_UNLOCKED by GFS. */
-
-static void check_cur_state(struct gdlm_lock *lp, unsigned int cur_state)
-{
- s16 cur = make_mode(cur_state);
- if (lp->cur != DLM_LOCK_IV)
- gdlm_assert(lp->cur == cur, "%d, %d", lp->cur, cur);
-}
-
-static inline unsigned int make_flags(struct gdlm_lock *lp,
- unsigned int gfs_flags,
- s16 cur, s16 req)
-{
- unsigned int lkf = 0;
-
- if (gfs_flags & LM_FLAG_TRY)
- lkf |= DLM_LKF_NOQUEUE;
-
- if (gfs_flags & LM_FLAG_TRY_1CB) {
- lkf |= DLM_LKF_NOQUEUE;
- lkf |= DLM_LKF_NOQUEUEBAST;
- }
-
- if (gfs_flags & LM_FLAG_PRIORITY) {
- lkf |= DLM_LKF_NOORDER;
- lkf |= DLM_LKF_HEADQUE;
- }
-
- if (gfs_flags & LM_FLAG_ANY) {
- if (req == DLM_LOCK_PR)
- lkf |= DLM_LKF_ALTCW;
- else if (req == DLM_LOCK_CW)
- lkf |= DLM_LKF_ALTPR;
- }
-
- if (lp->lksb.sb_lkid != 0) {
- lkf |= DLM_LKF_CONVERT;
- }
-
- if (lp->lvb)
- lkf |= DLM_LKF_VALBLK;
-
- return lkf;
-}
-
-/* make_strname - convert GFS lock numbers to a string */
-
-static inline void make_strname(const struct lm_lockname *lockname,
- struct gdlm_strname *str)
-{
- sprintf(str->name, "%8x%16llx", lockname->ln_type,
- (unsigned long long)lockname->ln_number);
- str->namelen = GDLM_STRNAME_BYTES;
-}
-
-static int gdlm_create_lp(struct gdlm_ls *ls, struct lm_lockname *name,
- struct gdlm_lock **lpp)
-{
- struct gdlm_lock *lp;
-
- lp = kzalloc(sizeof(struct gdlm_lock), GFP_NOFS);
- if (!lp)
- return -ENOMEM;
-
- lp->lockname = *name;
- make_strname(name, &lp->strname);
- lp->ls = ls;
- lp->cur = DLM_LOCK_IV;
- INIT_LIST_HEAD(&lp->delay_list);
-
- spin_lock(&ls->async_lock);
- ls->all_locks_count++;
- spin_unlock(&ls->async_lock);
-
- *lpp = lp;
- return 0;
-}
-
-int gdlm_get_lock(void *lockspace, struct lm_lockname *name,
- void **lockp)
-{
- struct gdlm_lock *lp;
- int error;
-
- error = gdlm_create_lp(lockspace, name, &lp);
-
- *lockp = lp;
- return error;
-}
-
-void gdlm_put_lock(void *lock)
-{
- gdlm_delete_lp(lock);
-}
-
-unsigned int gdlm_do_lock(struct gdlm_lock *lp)
-{
- struct gdlm_ls *ls = lp->ls;
- int error, bast = 1;
-
- /*
- * When recovery is in progress, delay lock requests for submission
- * once recovery is done. Requests for recovery (NOEXP) and unlocks
- * can pass.
- */
-
- if (test_bit(DFL_BLOCK_LOCKS, &ls->flags) &&
- !test_bit(LFL_NOBLOCK, &lp->flags) && lp->req != DLM_LOCK_NL) {
- gdlm_queue_delayed(lp);
- return LM_OUT_ASYNC;
- }
-
- /*
- * Submit the actual lock request.
- */
-
- if (test_bit(LFL_NOBAST, &lp->flags))
- bast = 0;
-
- set_bit(LFL_ACTIVE, &lp->flags);
-
- log_debug("lk %x,%llx id %x %d,%d %x", lp->lockname.ln_type,
- (unsigned long long)lp->lockname.ln_number, lp->lksb.sb_lkid,
- lp->cur, lp->req, lp->lkf);
-
- error = dlm_lock(ls->dlm_lockspace, lp->req, &lp->lksb, lp->lkf,
- lp->strname.name, lp->strname.namelen, 0, gdlm_ast,
- lp, bast ? gdlm_bast : NULL);
-
- if ((error == -EAGAIN) && (lp->lkf & DLM_LKF_NOQUEUE)) {
- lp->lksb.sb_status = -EAGAIN;
- gdlm_ast(lp);
- error = 0;
- }
-
- if (error) {
- log_error("%s: gdlm_lock %x,%llx err=%d cur=%d req=%d lkf=%x "
- "flags=%lx", ls->fsname, lp->lockname.ln_type,
- (unsigned long long)lp->lockname.ln_number, error,
- lp->cur, lp->req, lp->lkf, lp->flags);
- return LM_OUT_ERROR;
- }
- return LM_OUT_ASYNC;
-}
-
-static unsigned int gdlm_do_unlock(struct gdlm_lock *lp)
-{
- struct gdlm_ls *ls = lp->ls;
- unsigned int lkf = 0;
- int error;
-
- set_bit(LFL_DLM_UNLOCK, &lp->flags);
- set_bit(LFL_ACTIVE, &lp->flags);
-
- if (lp->lvb)
- lkf = DLM_LKF_VALBLK;
-
- log_debug("un %x,%llx %x %d %x", lp->lockname.ln_type,
- (unsigned long long)lp->lockname.ln_number,
- lp->lksb.sb_lkid, lp->cur, lkf);
-
- error = dlm_unlock(ls->dlm_lockspace, lp->lksb.sb_lkid, lkf, NULL, lp);
-
- if (error) {
- log_error("%s: gdlm_unlock %x,%llx err=%d cur=%d req=%d lkf=%x "
- "flags=%lx", ls->fsname, lp->lockname.ln_type,
- (unsigned long long)lp->lockname.ln_number, error,
- lp->cur, lp->req, lp->lkf, lp->flags);
- return LM_OUT_ERROR;
- }
- return LM_OUT_ASYNC;
-}
-
-unsigned int gdlm_lock(void *lock, unsigned int cur_state,
- unsigned int req_state, unsigned int flags)
-{
- struct gdlm_lock *lp = lock;
-
- if (req_state == LM_ST_UNLOCKED)
- return gdlm_unlock(lock, cur_state);
-
- if (req_state == LM_ST_UNLOCKED)
- return gdlm_unlock(lock, cur_state);
-
- clear_bit(LFL_DLM_CANCEL, &lp->flags);
- if (flags & LM_FLAG_NOEXP)
- set_bit(LFL_NOBLOCK, &lp->flags);
-
- check_cur_state(lp, cur_state);
- lp->req = make_mode(req_state);
- lp->lkf = make_flags(lp, flags, lp->cur, lp->req);
-
- return gdlm_do_lock(lp);
-}
-
-unsigned int gdlm_unlock(void *lock, unsigned int cur_state)
-{
- struct gdlm_lock *lp = lock;
-
- clear_bit(LFL_DLM_CANCEL, &lp->flags);
- if (lp->cur == DLM_LOCK_IV)
- return 0;
- return gdlm_do_unlock(lp);
-}
-
-void gdlm_cancel(void *lock)
-{
- struct gdlm_lock *lp = lock;
- struct gdlm_ls *ls = lp->ls;
- int error, delay_list = 0;
-
- if (test_bit(LFL_DLM_CANCEL, &lp->flags))
- return;
-
- log_info("gdlm_cancel %x,%llx flags %lx", lp->lockname.ln_type,
- (unsigned long long)lp->lockname.ln_number, lp->flags);
-
- spin_lock(&ls->async_lock);
- if (!list_empty(&lp->delay_list)) {
- list_del_init(&lp->delay_list);
- delay_list = 1;
- }
- spin_unlock(&ls->async_lock);
-
- if (delay_list) {
- set_bit(LFL_CANCEL, &lp->flags);
- set_bit(LFL_ACTIVE, &lp->flags);
- gdlm_ast(lp);
- return;
- }
-
- if (!test_bit(LFL_ACTIVE, &lp->flags) ||
- test_bit(LFL_DLM_UNLOCK, &lp->flags)) {
- log_info("gdlm_cancel skip %x,%llx flags %lx",
- lp->lockname.ln_type,
- (unsigned long long)lp->lockname.ln_number, lp->flags);
- return;
- }
-
- /* the lock is blocked in the dlm */
-
- set_bit(LFL_DLM_CANCEL, &lp->flags);
- set_bit(LFL_ACTIVE, &lp->flags);
-
- error = dlm_unlock(ls->dlm_lockspace, lp->lksb.sb_lkid, DLM_LKF_CANCEL,
- NULL, lp);
-
- log_info("gdlm_cancel rv %d %x,%llx flags %lx", error,
- lp->lockname.ln_type,
- (unsigned long long)lp->lockname.ln_number, lp->flags);
-
- if (error == -EBUSY)
- clear_bit(LFL_DLM_CANCEL, &lp->flags);
-}
-
-static int gdlm_add_lvb(struct gdlm_lock *lp)
-{
- char *lvb;
-
- lvb = kzalloc(GDLM_LVB_SIZE, GFP_NOFS);
- if (!lvb)
- return -ENOMEM;
-
- lp->lksb.sb_lvbptr = lvb;
- lp->lvb = lvb;
- return 0;
-}
-
-static void gdlm_del_lvb(struct gdlm_lock *lp)
-{
- kfree(lp->lvb);
- lp->lvb = NULL;
- lp->lksb.sb_lvbptr = NULL;
-}
-
-static int gdlm_ast_wait(void *word)
-{
- schedule();
- return 0;
-}
-
-/* This can do a synchronous dlm request (requiring a lock_dlm thread to get
- the completion) because gfs won't call hold_lvb() during a callback (from
- the context of a lock_dlm thread). */
-
-static int hold_null_lock(struct gdlm_lock *lp)
-{
- struct gdlm_lock *lpn = NULL;
- int error;
-
- if (lp->hold_null) {
- printk(KERN_INFO "lock_dlm: lvb already held\n");
- return 0;
- }
-
- error = gdlm_create_lp(lp->ls, &lp->lockname, &lpn);
- if (error)
- goto out;
-
- lpn->lksb.sb_lvbptr = junk_lvb;
- lpn->lvb = junk_lvb;
-
- lpn->req = DLM_LOCK_NL;
- lpn->lkf = DLM_LKF_VALBLK | DLM_LKF_EXPEDITE;
- set_bit(LFL_NOBAST, &lpn->flags);
- set_bit(LFL_INLOCK, &lpn->flags);
- set_bit(LFL_AST_WAIT, &lpn->flags);
-
- gdlm_do_lock(lpn);
- wait_on_bit(&lpn->flags, LFL_AST_WAIT, gdlm_ast_wait, TASK_UNINTERRUPTIBLE);
- error = lpn->lksb.sb_status;
- if (error) {
- printk(KERN_INFO "lock_dlm: hold_null_lock dlm error %d\n",
- error);
- gdlm_delete_lp(lpn);
- lpn = NULL;
- }
-out:
- lp->hold_null = lpn;
- return error;
-}
-
-/* This cannot do a synchronous dlm request (requiring a lock_dlm thread to get
- the completion) because gfs may call unhold_lvb() during a callback (from
- the context of a lock_dlm thread) which could cause a deadlock since the
- other lock_dlm thread could be engaged in recovery. */
-
-static void unhold_null_lock(struct gdlm_lock *lp)
-{
- struct gdlm_lock *lpn = lp->hold_null;
-
- gdlm_assert(lpn, "%x,%llx", lp->lockname.ln_type,
- (unsigned long long)lp->lockname.ln_number);
- lpn->lksb.sb_lvbptr = NULL;
- lpn->lvb = NULL;
- set_bit(LFL_UNLOCK_DELETE, &lpn->flags);
- gdlm_do_unlock(lpn);
- lp->hold_null = NULL;
-}
-
-/* Acquire a NL lock because gfs requires the value block to remain
- intact on the resource while the lvb is "held" even if it's holding no locks
- on the resource. */
-
-int gdlm_hold_lvb(void *lock, char **lvbp)
-{
- struct gdlm_lock *lp = lock;
- int error;
-
- error = gdlm_add_lvb(lp);
- if (error)
- return error;
-
- *lvbp = lp->lvb;
-
- error = hold_null_lock(lp);
- if (error)
- gdlm_del_lvb(lp);
-
- return error;
-}
-
-void gdlm_unhold_lvb(void *lock, char *lvb)
-{
- struct gdlm_lock *lp = lock;
-
- unhold_null_lock(lp);
- gdlm_del_lvb(lp);
-}
-
-void gdlm_submit_delayed(struct gdlm_ls *ls)
-{
- struct gdlm_lock *lp, *safe;
-
- spin_lock(&ls->async_lock);
- list_for_each_entry_safe(lp, safe, &ls->delayed, delay_list) {
- list_del_init(&lp->delay_list);
- list_add_tail(&lp->delay_list, &ls->submit);
- }
- spin_unlock(&ls->async_lock);
- wake_up(&ls->thread_wait);
-}
-
diff --git a/fs/gfs2/locking/dlm/lock_dlm.h b/fs/gfs2/locking/dlm/lock_dlm.h
deleted file mode 100644
index 3c98e7c..0000000
--- a/fs/gfs2/locking/dlm/lock_dlm.h
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
- * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU General Public License version 2.
- */
-
-#ifndef LOCK_DLM_DOT_H
-#define LOCK_DLM_DOT_H
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/list.h>
-#include <linux/socket.h>
-#include <linux/delay.h>
-#include <linux/kthread.h>
-#include <linux/kobject.h>
-#include <linux/fcntl.h>
-#include <linux/wait.h>
-#include <net/sock.h>
-
-#include <linux/dlm.h>
-#include <linux/dlm_plock.h>
-#include <linux/lm_interface.h>
-
-/*
- * Internally, we prefix things with gdlm_ and GDLM_ (for gfs-dlm) since a
- * prefix of lock_dlm_ gets awkward. Externally, GFS refers to this module
- * as "lock_dlm".
- */
-
-#define GDLM_STRNAME_BYTES 24
-#define GDLM_LVB_SIZE 32
-#define GDLM_DROP_COUNT 0
-#define GDLM_DROP_PERIOD 60
-#define GDLM_NAME_LEN 128
-
-/* GFS uses 12 bytes to identify a resource (32 bit type + 64 bit number).
- We sprintf these numbers into a 24 byte string of hex values to make them
- human-readable (to make debugging simpler.) */
-
-struct gdlm_strname {
- unsigned char name[GDLM_STRNAME_BYTES];
- unsigned short namelen;
-};
-
-enum {
- DFL_BLOCK_LOCKS = 0,
- DFL_SPECTATOR = 1,
- DFL_WITHDRAW = 2,
-};
-
-struct gdlm_ls {
- u32 id;
- int jid;
- int first;
- int first_done;
- unsigned long flags;
- struct kobject kobj;
- char clustername[GDLM_NAME_LEN];
- char fsname[GDLM_NAME_LEN];
- int fsflags;
- dlm_lockspace_t *dlm_lockspace;
- lm_callback_t fscb;
- struct gfs2_sbd *sdp;
- int recover_jid;
- int recover_jid_done;
- int recover_jid_status;
- spinlock_t async_lock;
- struct list_head delayed;
- struct list_head submit;
- u32 all_locks_count;
- wait_queue_head_t wait_control;
- struct task_struct *thread;
- wait_queue_head_t thread_wait;
-};
-
-enum {
- LFL_NOBLOCK = 0,
- LFL_NOCACHE = 1,
- LFL_DLM_UNLOCK = 2,
- LFL_DLM_CANCEL = 3,
- LFL_SYNC_LVB = 4,
- LFL_FORCE_PROMOTE = 5,
- LFL_REREQUEST = 6,
- LFL_ACTIVE = 7,
- LFL_INLOCK = 8,
- LFL_CANCEL = 9,
- LFL_NOBAST = 10,
- LFL_HEADQUE = 11,
- LFL_UNLOCK_DELETE = 12,
- LFL_AST_WAIT = 13,
-};
-
-struct gdlm_lock {
- struct gdlm_ls *ls;
- struct lm_lockname lockname;
- struct gdlm_strname strname;
- char *lvb;
- struct dlm_lksb lksb;
-
- s16 cur;
- s16 req;
- s16 prev_req;
- u32 lkf; /* dlm flags DLM_LKF_ */
- unsigned long flags; /* lock_dlm flags LFL_ */
-
- struct list_head delay_list; /* delayed */
- struct gdlm_lock *hold_null; /* NL lock for hold_lvb */
-};
-
-#define gdlm_assert(assertion, fmt, args...) \
-do { \
- if (unlikely(!(assertion))) { \
- printk(KERN_EMERG "lock_dlm: fatal assertion failed \"%s\"\n" \
- "lock_dlm: " fmt "\n", \
- #assertion, ##args); \
- BUG(); \
- } \
-} while (0)
-
-#define log_print(lev, fmt, arg...) printk(lev "lock_dlm: " fmt "\n" , ## arg)
-#define log_info(fmt, arg...) log_print(KERN_INFO , fmt , ## arg)
-#define log_error(fmt, arg...) log_print(KERN_ERR , fmt , ## arg)
-#ifdef LOCK_DLM_LOG_DEBUG
-#define log_debug(fmt, arg...) log_print(KERN_DEBUG , fmt , ## arg)
-#else
-#define log_debug(fmt, arg...)
-#endif
-
-/* sysfs.c */
-
-int gdlm_sysfs_init(void);
-void gdlm_sysfs_exit(void);
-int gdlm_kobject_setup(struct gdlm_ls *, struct kobject *);
-void gdlm_kobject_release(struct gdlm_ls *);
-
-/* thread.c */
-
-int gdlm_init_threads(struct gdlm_ls *);
-void gdlm_release_threads(struct gdlm_ls *);
-
-/* lock.c */
-
-void gdlm_submit_delayed(struct gdlm_ls *);
-unsigned int gdlm_do_lock(struct gdlm_lock *);
-
-int gdlm_get_lock(void *, struct lm_lockname *, void **);
-void gdlm_put_lock(void *);
-unsigned int gdlm_lock(void *, unsigned int, unsigned int, unsigned int);
-unsigned int gdlm_unlock(void *, unsigned int);
-void gdlm_cancel(void *);
-int gdlm_hold_lvb(void *, char **);
-void gdlm_unhold_lvb(void *, char *);
-
-/* mount.c */
-
-extern const struct lm_lockops gdlm_ops;
-
-#endif
-
diff --git a/fs/gfs2/locking/dlm/main.c b/fs/gfs2/locking/dlm/main.c
deleted file mode 100644
index b9a03a7..0000000
--- a/fs/gfs2/locking/dlm/main.c
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
- * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU General Public License version 2.
- */
-
-#include <linux/init.h>
-
-#include "lock_dlm.h"
-
-static int __init init_lock_dlm(void)
-{
- int error;
-
- error = gfs2_register_lockproto(&gdlm_ops);
- if (error) {
- printk(KERN_WARNING "lock_dlm: can't register protocol: %d\n",
- error);
- return error;
- }
-
- error = gdlm_sysfs_init();
- if (error) {
- gfs2_unregister_lockproto(&gdlm_ops);
- return error;
- }
-
- printk(KERN_INFO
- "Lock_DLM (built %s %s) installed\n", __DATE__, __TIME__);
- return 0;
-}
-
-static void __exit exit_lock_dlm(void)
-{
- gdlm_sysfs_exit();
- gfs2_unregister_lockproto(&gdlm_ops);
-}
-
-module_init(init_lock_dlm);
-module_exit(exit_lock_dlm);
-
-MODULE_DESCRIPTION("GFS DLM Locking Module");
-MODULE_AUTHOR("Red Hat, Inc.");
-MODULE_LICENSE("GPL");
-
diff --git a/fs/gfs2/locking/dlm/mount.c b/fs/gfs2/locking/dlm/mount.c
deleted file mode 100644
index 1aa7eb6..0000000
--- a/fs/gfs2/locking/dlm/mount.c
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
- * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU General Public License version 2.
- */
-
-#include "lock_dlm.h"
-
-const struct lm_lockops gdlm_ops;
-
-
-static struct gdlm_ls *init_gdlm(lm_callback_t cb, struct gfs2_sbd *sdp,
- int flags, char *table_name)
-{
- struct gdlm_ls *ls;
- char buf[256], *p;
-
- ls = kzalloc(sizeof(struct gdlm_ls), GFP_KERNEL);
- if (!ls)
- return NULL;
-
- ls->fscb = cb;
- ls->sdp = sdp;
- ls->fsflags = flags;
- spin_lock_init(&ls->async_lock);
- INIT_LIST_HEAD(&ls->delayed);
- INIT_LIST_HEAD(&ls->submit);
- init_waitqueue_head(&ls->thread_wait);
- init_waitqueue_head(&ls->wait_control);
- ls->jid = -1;
-
- strncpy(buf, table_name, 256);
- buf[255] = '\0';
-
- p = strchr(buf, ':');
- if (!p) {
- log_info("invalid table_name \"%s\"", table_name);
- kfree(ls);
- return NULL;
- }
- *p = '\0';
- p++;
-
- strncpy(ls->clustername, buf, GDLM_NAME_LEN);
- strncpy(ls->fsname, p, GDLM_NAME_LEN);
-
- return ls;
-}
-
-static int make_args(struct gdlm_ls *ls, char *data_arg, int *nodir)
-{
- char data[256];
- char *options, *x, *y;
- int error = 0;
-
- memset(data, 0, 256);
- strncpy(data, data_arg, 255);
-
- if (!strlen(data)) {
- log_error("no mount options, (u)mount helpers not installed");
- return -EINVAL;
- }
-
- for (options = data; (x = strsep(&options, ":")); ) {
- if (!*x)
- continue;
-
- y = strchr(x, '=');
- if (y)
- *y++ = 0;
-
- if (!strcmp(x, "jid")) {
- if (!y) {
- log_error("need argument to jid");
- error = -EINVAL;
- break;
- }
- sscanf(y, "%u", &ls->jid);
-
- } else if (!strcmp(x, "first")) {
- if (!y) {
- log_error("need argument to first");
- error = -EINVAL;
- break;
- }
- sscanf(y, "%u", &ls->first);
-
- } else if (!strcmp(x, "id")) {
- if (!y) {
- log_error("need argument to id");
- error = -EINVAL;
- break;
- }
- sscanf(y, "%u", &ls->id);
-
- } else if (!strcmp(x, "nodir")) {
- if (!y) {
- log_error("need argument to nodir");
- error = -EINVAL;
- break;
- }
- sscanf(y, "%u", nodir);
-
- } else {
- log_error("unkonwn option: %s", x);
- error = -EINVAL;
- break;
- }
- }
-
- return error;
-}
-
-static int gdlm_mount(char *table_name, char *host_data,
- lm_callback_t cb, void *cb_data,
- unsigned int min_lvb_size, int flags,
- struct lm_lockstruct *lockstruct,
- struct kobject *fskobj)
-{
- struct gdlm_ls *ls;
- int error = -ENOMEM, nodir = 0;
-
- if (min_lvb_size > GDLM_LVB_SIZE)
- goto out;
-
- ls = init_gdlm(cb, cb_data, flags, table_name);
- if (!ls)
- goto out;
-
- error = make_args(ls, host_data, &nodir);
- if (error)
- goto out;
-
- error = gdlm_init_threads(ls);
- if (error)
- goto out_free;
-
- error = gdlm_kobject_setup(ls, fskobj);
- if (error)
- goto out_thread;
-
- error = dlm_new_lockspace(ls->fsname, strlen(ls->fsname),
- &ls->dlm_lockspace,
- DLM_LSFL_FS | DLM_LSFL_NEWEXCL |
- (nodir ? DLM_LSFL_NODIR : 0),
- GDLM_LVB_SIZE);
- if (error) {
- log_error("dlm_new_lockspace error %d", error);
- goto out_kobj;
- }
-
- lockstruct->ls_jid = ls->jid;
- lockstruct->ls_first = ls->first;
- lockstruct->ls_lockspace = ls;
- lockstruct->ls_ops = &gdlm_ops;
- lockstruct->ls_flags = 0;
- lockstruct->ls_lvb_size = GDLM_LVB_SIZE;
- return 0;
-
-out_kobj:
- gdlm_kobject_release(ls);
-out_thread:
- gdlm_release_threads(ls);
-out_free:
- kfree(ls);
-out:
- return error;
-}
-
-static void gdlm_unmount(void *lockspace)
-{
- struct gdlm_ls *ls = lockspace;
-
- log_debug("unmount flags %lx", ls->flags);
-
- /* FIXME: serialize unmount and withdraw in case they
- happen at once. Also, if unmount follows withdraw,
- wait for withdraw to finish. */
-
- if (test_bit(DFL_WITHDRAW, &ls->flags))
- goto out;
-
- gdlm_kobject_release(ls);
- dlm_release_lockspace(ls->dlm_lockspace, 2);
- gdlm_release_threads(ls);
- BUG_ON(ls->all_locks_count);
-out:
- kfree(ls);
-}
-
-static void gdlm_recovery_done(void *lockspace, unsigned int jid,
- unsigned int message)
-{
- char env_jid[20];
- char env_status[20];
- char *envp[] = { env_jid, env_status, NULL };
- struct gdlm_ls *ls = lockspace;
- ls->recover_jid_done = jid;
- ls->recover_jid_status = message;
- sprintf(env_jid, "JID=%d", jid);
- sprintf(env_status, "RECOVERY=%s",
- message == LM_RD_SUCCESS ? "Done" : "Failed");
- kobject_uevent_env(&ls->kobj, KOBJ_CHANGE, envp);
-}
-
-static void gdlm_others_may_mount(void *lockspace)
-{
- char *message = "FIRSTMOUNT=Done";
- char *envp[] = { message, NULL };
- struct gdlm_ls *ls = lockspace;
- ls->first_done = 1;
- kobject_uevent_env(&ls->kobj, KOBJ_CHANGE, envp);
-}
-
-/* Userspace gets the offline uevent, blocks new gfs locks on
- other mounters, and lets us know (sets WITHDRAW flag). Then,
- userspace leaves the mount group while we leave the lockspace. */
-
-static void gdlm_withdraw(void *lockspace)
-{
- struct gdlm_ls *ls = lockspace;
-
- kobject_uevent(&ls->kobj, KOBJ_OFFLINE);
-
- wait_event_interruptible(ls->wait_control,
- test_bit(DFL_WITHDRAW, &ls->flags));
-
- dlm_release_lockspace(ls->dlm_lockspace, 2);
- gdlm_release_threads(ls);
- gdlm_kobject_release(ls);
-}
-
-static int gdlm_plock(void *lockspace, struct lm_lockname *name,
- struct file *file, int cmd, struct file_lock *fl)
-{
- struct gdlm_ls *ls = lockspace;
- return dlm_posix_lock(ls->dlm_lockspace, name->ln_number, file, cmd, fl);
-}
-
-static int gdlm_punlock(void *lockspace, struct lm_lockname *name,
- struct file *file, struct file_lock *fl)
-{
- struct gdlm_ls *ls = lockspace;
- return dlm_posix_unlock(ls->dlm_lockspace, name->ln_number, file, fl);
-}
-
-static int gdlm_plock_get(void *lockspace, struct lm_lockname *name,
- struct file *file, struct file_lock *fl)
-{
- struct gdlm_ls *ls = lockspace;
- return dlm_posix_get(ls->dlm_lockspace, name->ln_number, file, fl);
-}
-
-const struct lm_lockops gdlm_ops = {
- .lm_proto_name = "lock_dlm",
- .lm_mount = gdlm_mount,
- .lm_others_may_mount = gdlm_others_may_mount,
- .lm_unmount = gdlm_unmount,
- .lm_withdraw = gdlm_withdraw,
- .lm_get_lock = gdlm_get_lock,
- .lm_put_lock = gdlm_put_lock,
- .lm_lock = gdlm_lock,
- .lm_unlock = gdlm_unlock,
- .lm_plock = gdlm_plock,
- .lm_punlock = gdlm_punlock,
- .lm_plock_get = gdlm_plock_get,
- .lm_cancel = gdlm_cancel,
- .lm_hold_lvb = gdlm_hold_lvb,
- .lm_unhold_lvb = gdlm_unhold_lvb,
- .lm_recovery_done = gdlm_recovery_done,
- .lm_owner = THIS_MODULE,
-};
-
diff --git a/fs/gfs2/locking/dlm/sysfs.c b/fs/gfs2/locking/dlm/sysfs.c
deleted file mode 100644
index 9b7edcf..0000000
--- a/fs/gfs2/locking/dlm/sysfs.c
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
- * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU General Public License version 2.
- */
-
-#include <linux/ctype.h>
-#include <linux/stat.h>
-
-#include "lock_dlm.h"
-
-static ssize_t proto_name_show(struct gdlm_ls *ls, char *buf)
-{
- return sprintf(buf, "%s\n", gdlm_ops.lm_proto_name);
-}
-
-static ssize_t block_show(struct gdlm_ls *ls, char *buf)
-{
- ssize_t ret;
- int val = 0;
-
- if (test_bit(DFL_BLOCK_LOCKS, &ls->flags))
- val = 1;
- ret = sprintf(buf, "%d\n", val);
- return ret;
-}
-
-static ssize_t block_store(struct gdlm_ls *ls, const char *buf, size_t len)
-{
- ssize_t ret = len;
- int val;
-
- val = simple_strtol(buf, NULL, 0);
-
- if (val == 1)
- set_bit(DFL_BLOCK_LOCKS, &ls->flags);
- else if (val == 0) {
- clear_bit(DFL_BLOCK_LOCKS, &ls->flags);
- gdlm_submit_delayed(ls);
- } else {
- ret = -EINVAL;
- }
- return ret;
-}
-
-static ssize_t withdraw_show(struct gdlm_ls *ls, char *buf)
-{
- ssize_t ret;
- int val = 0;
-
- if (test_bit(DFL_WITHDRAW, &ls->flags))
- val = 1;
- ret = sprintf(buf, "%d\n", val);
- return ret;
-}
-
-static ssize_t withdraw_store(struct gdlm_ls *ls, const char *buf, size_t len)
-{
- ssize_t ret = len;
- int val;
-
- val = simple_strtol(buf, NULL, 0);
-
- if (val == 1)
- set_bit(DFL_WITHDRAW, &ls->flags);
- else
- ret = -EINVAL;
- wake_up(&ls->wait_control);
- return ret;
-}
-
-static ssize_t id_show(struct gdlm_ls *ls, char *buf)
-{
- return sprintf(buf, "%u\n", ls->id);
-}
-
-static ssize_t jid_show(struct gdlm_ls *ls, char *buf)
-{
- return sprintf(buf, "%d\n", ls->jid);
-}
-
-static ssize_t first_show(struct gdlm_ls *ls, char *buf)
-{
- return sprintf(buf, "%d\n", ls->first);
-}
-
-static ssize_t first_done_show(struct gdlm_ls *ls, char *buf)
-{
- return sprintf(buf, "%d\n", ls->first_done);
-}
-
-static ssize_t recover_show(struct gdlm_ls *ls, char *buf)
-{
- return sprintf(buf, "%d\n", ls->recover_jid);
-}
-
-static ssize_t recover_store(struct gdlm_ls *ls, const char *buf, size_t len)
-{
- ls->recover_jid = simple_strtol(buf, NULL, 0);
- ls->fscb(ls->sdp, LM_CB_NEED_RECOVERY, &ls->recover_jid);
- return len;
-}
-
-static ssize_t recover_done_show(struct gdlm_ls *ls, char *buf)
-{
- return sprintf(buf, "%d\n", ls->recover_jid_done);
-}
-
-static ssize_t recover_status_show(struct gdlm_ls *ls, char *buf)
-{
- return sprintf(buf, "%d\n", ls->recover_jid_status);
-}
-
-struct gdlm_attr {
- struct attribute attr;
- ssize_t (*show)(struct gdlm_ls *, char *);
- ssize_t (*store)(struct gdlm_ls *, const char *, size_t);
-};
-
-#define GDLM_ATTR(_name,_mode,_show,_store) \
-static struct gdlm_attr gdlm_attr_##_name = __ATTR(_name,_mode,_show,_store)
-
-GDLM_ATTR(proto_name, 0444, proto_name_show, NULL);
-GDLM_ATTR(block, 0644, block_show, block_store);
-GDLM_ATTR(withdraw, 0644, withdraw_show, withdraw_store);
-GDLM_ATTR(id, 0444, id_show, NULL);
-GDLM_ATTR(jid, 0444, jid_show, NULL);
-GDLM_ATTR(first, 0444, first_show, NULL);
-GDLM_ATTR(first_done, 0444, first_done_show, NULL);
-GDLM_ATTR(recover, 0644, recover_show, recover_store);
-GDLM_ATTR(recover_done, 0444, recover_done_show, NULL);
-GDLM_ATTR(recover_status, 0444, recover_status_show, NULL);
-
-static struct attribute *gdlm_attrs[] = {
- &gdlm_attr_proto_name.attr,
- &gdlm_attr_block.attr,
- &gdlm_attr_withdraw.attr,
- &gdlm_attr_id.attr,
- &gdlm_attr_jid.attr,
- &gdlm_attr_first.attr,
- &gdlm_attr_first_done.attr,
- &gdlm_attr_recover.attr,
- &gdlm_attr_recover_done.attr,
- &gdlm_attr_recover_status.attr,
- NULL,
-};
-
-static ssize_t gdlm_attr_show(struct kobject *kobj, struct attribute *attr,
- char *buf)
-{
- struct gdlm_ls *ls = container_of(kobj, struct gdlm_ls, kobj);
- struct gdlm_attr *a = container_of(attr, struct gdlm_attr, attr);
- return a->show ? a->show(ls, buf) : 0;
-}
-
-static ssize_t gdlm_attr_store(struct kobject *kobj, struct attribute *attr,
- const char *buf, size_t len)
-{
- struct gdlm_ls *ls = container_of(kobj, struct gdlm_ls, kobj);
- struct gdlm_attr *a = container_of(attr, struct gdlm_attr, attr);
- return a->store ? a->store(ls, buf, len) : len;
-}
-
-static struct sysfs_ops gdlm_attr_ops = {
- .show = gdlm_attr_show,
- .store = gdlm_attr_store,
-};
-
-static struct kobj_type gdlm_ktype = {
- .default_attrs = gdlm_attrs,
- .sysfs_ops = &gdlm_attr_ops,
-};
-
-static struct kset *gdlm_kset;
-
-int gdlm_kobject_setup(struct gdlm_ls *ls, struct kobject *fskobj)
-{
- int error;
-
- ls->kobj.kset = gdlm_kset;
- error = kobject_init_and_add(&ls->kobj, &gdlm_ktype, fskobj,
- "lock_module");
- if (error)
- log_error("can't register kobj %d", error);
- kobject_uevent(&ls->kobj, KOBJ_ADD);
-
- return error;
-}
-
-void gdlm_kobject_release(struct gdlm_ls *ls)
-{
- kobject_put(&ls->kobj);
-}
-
-static int gdlm_uevent(struct kset *kset, struct kobject *kobj,
- struct kobj_uevent_env *env)
-{
- struct gdlm_ls *ls = container_of(kobj, struct gdlm_ls, kobj);
- add_uevent_var(env, "LOCKTABLE=%s:%s", ls->clustername, ls->fsname);
- add_uevent_var(env, "LOCKPROTO=lock_dlm");
- return 0;
-}
-
-static struct kset_uevent_ops gdlm_uevent_ops = {
- .uevent = gdlm_uevent,
-};
-
-
-int gdlm_sysfs_init(void)
-{
- gdlm_kset = kset_create_and_add("lock_dlm", &gdlm_uevent_ops, kernel_kobj);
- if (!gdlm_kset) {
- printk(KERN_WARNING "%s: can not create kset\n", __func__);
- return -ENOMEM;
- }
- return 0;
-}
-
-void gdlm_sysfs_exit(void)
-{
- kset_unregister(gdlm_kset);
-}
-
diff --git a/fs/gfs2/locking/dlm/thread.c b/fs/gfs2/locking/dlm/thread.c
deleted file mode 100644
index 38823ef..0000000
--- a/fs/gfs2/locking/dlm/thread.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
- * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU General Public License version 2.
- */
-
-#include "lock_dlm.h"
-
-static inline int no_work(struct gdlm_ls *ls)
-{
- int ret;
-
- spin_lock(&ls->async_lock);
- ret = list_empty(&ls->submit);
- spin_unlock(&ls->async_lock);
-
- return ret;
-}
-
-static int gdlm_thread(void *data)
-{
- struct gdlm_ls *ls = (struct gdlm_ls *) data;
- struct gdlm_lock *lp = NULL;
-
- while (!kthread_should_stop()) {
- wait_event_interruptible(ls->thread_wait,
- !no_work(ls) || kthread_should_stop());
-
- spin_lock(&ls->async_lock);
-
- if (!list_empty(&ls->submit)) {
- lp = list_entry(ls->submit.next, struct gdlm_lock,
- delay_list);
- list_del_init(&lp->delay_list);
- spin_unlock(&ls->async_lock);
- gdlm_do_lock(lp);
- spin_lock(&ls->async_lock);
- }
- spin_unlock(&ls->async_lock);
- }
-
- return 0;
-}
-
-int gdlm_init_threads(struct gdlm_ls *ls)
-{
- struct task_struct *p;
- int error;
-
- p = kthread_run(gdlm_thread, ls, "lock_dlm");
- error = IS_ERR(p);
- if (error) {
- log_error("can't start lock_dlm thread %d", error);
- return error;
- }
- ls->thread = p;
-
- return 0;
-}
-
-void gdlm_release_threads(struct gdlm_ls *ls)
-{
- kthread_stop(ls->thread);
-}
-
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index ad30585..98918a7 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -14,7 +14,6 @@
#include <linux/buffer_head.h>
#include <linux/gfs2_ondisk.h>
#include <linux/crc32.h>
-#include <linux/lm_interface.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index 4390f6f..80e4f5f 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -13,7 +13,6 @@
#include <linux/completion.h>
#include <linux/buffer_head.h>
#include <linux/gfs2_ondisk.h>
-#include <linux/lm_interface.h>
#include "gfs2.h"
#include "incore.h"
diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c
index 7cacfde..a6892ed 100644
--- a/fs/gfs2/main.c
+++ b/fs/gfs2/main.c
@@ -14,7 +14,6 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/gfs2_ondisk.h>
-#include <linux/lm_interface.h>
#include <asm/atomic.h>
#include "gfs2.h"
@@ -23,6 +22,12 @@
#include "sys.h"
#include "util.h"
#include "glock.h"
+#include "quota.h"
+
+static struct shrinker qd_shrinker = {
+ .shrink = gfs2_shrink_qd_memory,
+ .seeks = DEFAULT_SEEKS,
+};
static void gfs2_init_inode_once(void *foo)
{
@@ -41,8 +46,6 @@
INIT_HLIST_NODE(&gl->gl_list);
spin_lock_init(&gl->gl_spin);
INIT_LIST_HEAD(&gl->gl_holders);
- gl->gl_lvb = NULL;
- atomic_set(&gl->gl_lvb_count, 0);
INIT_LIST_HEAD(&gl->gl_lru);
INIT_LIST_HEAD(&gl->gl_ail_list);
atomic_set(&gl->gl_ail_count, 0);
@@ -100,6 +103,8 @@
if (!gfs2_quotad_cachep)
goto fail;
+ register_shrinker(&qd_shrinker);
+
error = register_filesystem(&gfs2_fs_type);
if (error)
goto fail;
@@ -117,6 +122,7 @@
fail_unregister:
unregister_filesystem(&gfs2_fs_type);
fail:
+ unregister_shrinker(&qd_shrinker);
gfs2_glock_exit();
if (gfs2_quotad_cachep)
@@ -145,6 +151,7 @@
static void __exit exit_gfs2_fs(void)
{
+ unregister_shrinker(&qd_shrinker);
gfs2_glock_exit();
gfs2_unregister_debugfs();
unregister_filesystem(&gfs2_fs_type);
diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index 0985362..8d6f132 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -19,7 +19,6 @@
#include <linux/delay.h>
#include <linux/bio.h>
#include <linux/gfs2_ondisk.h>
-#include <linux/lm_interface.h>
#include "gfs2.h"
#include "incore.h"
@@ -90,27 +89,6 @@
}
/**
- * gfs2_meta_inval - Invalidate all buffers associated with a glock
- * @gl: the glock
- *
- */
-
-void gfs2_meta_inval(struct gfs2_glock *gl)
-{
- struct gfs2_sbd *sdp = gl->gl_sbd;
- struct inode *aspace = gl->gl_aspace;
- struct address_space *mapping = gl->gl_aspace->i_mapping;
-
- gfs2_assert_withdraw(sdp, !atomic_read(&gl->gl_ail_count));
-
- atomic_inc(&aspace->i_writecount);
- truncate_inode_pages(mapping, 0);
- atomic_dec(&aspace->i_writecount);
-
- gfs2_assert_withdraw(sdp, !mapping->nrpages);
-}
-
-/**
* gfs2_meta_sync - Sync all buffers associated with a glock
* @gl: The glock
*
diff --git a/fs/gfs2/meta_io.h b/fs/gfs2/meta_io.h
index b1a5f36..de270c2 100644
--- a/fs/gfs2/meta_io.h
+++ b/fs/gfs2/meta_io.h
@@ -40,7 +40,6 @@
struct inode *gfs2_aspace_get(struct gfs2_sbd *sdp);
void gfs2_aspace_put(struct inode *aspace);
-void gfs2_meta_inval(struct gfs2_glock *gl);
void gfs2_meta_sync(struct gfs2_glock *gl);
struct buffer_head *gfs2_meta_new(struct gfs2_glock *gl, u64 blkno);
diff --git a/fs/gfs2/mount.c b/fs/gfs2/mount.c
index 3cb0a44..f7e8527 100644
--- a/fs/gfs2/mount.c
+++ b/fs/gfs2/mount.c
@@ -12,12 +12,11 @@
#include <linux/completion.h>
#include <linux/buffer_head.h>
#include <linux/gfs2_ondisk.h>
-#include <linux/lm_interface.h>
#include <linux/parser.h>
#include "gfs2.h"
#include "incore.h"
-#include "mount.h"
+#include "super.h"
#include "sys.h"
#include "util.h"
@@ -37,11 +36,15 @@
Opt_quota_off,
Opt_quota_account,
Opt_quota_on,
+ Opt_quota,
+ Opt_noquota,
Opt_suiddir,
Opt_nosuiddir,
Opt_data_writeback,
Opt_data_ordered,
Opt_meta,
+ Opt_discard,
+ Opt_nodiscard,
Opt_err,
};
@@ -61,11 +64,15 @@
{Opt_quota_off, "quota=off"},
{Opt_quota_account, "quota=account"},
{Opt_quota_on, "quota=on"},
+ {Opt_quota, "quota"},
+ {Opt_noquota, "noquota"},
{Opt_suiddir, "suiddir"},
{Opt_nosuiddir, "nosuiddir"},
{Opt_data_writeback, "data=writeback"},
{Opt_data_ordered, "data=ordered"},
{Opt_meta, "meta"},
+ {Opt_discard, "discard"},
+ {Opt_nodiscard, "nodiscard"},
{Opt_err, NULL}
};
@@ -77,101 +84,46 @@
* Return: errno
*/
-int gfs2_mount_args(struct gfs2_sbd *sdp, char *data_arg, int remount)
+int gfs2_mount_args(struct gfs2_sbd *sdp, struct gfs2_args *args, char *options)
{
- struct gfs2_args *args = &sdp->sd_args;
- char *data = data_arg;
- char *options, *o, *v;
- int error = 0;
-
- if (!remount) {
- /* Set some defaults */
- args->ar_quota = GFS2_QUOTA_DEFAULT;
- args->ar_data = GFS2_DATA_DEFAULT;
- }
+ char *o;
+ int token;
+ substring_t tmp[MAX_OPT_ARGS];
/* Split the options into tokens with the "," character and
process them */
- for (options = data; (o = strsep(&options, ",")); ) {
- int token;
- substring_t tmp[MAX_OPT_ARGS];
-
- if (!*o)
+ while (1) {
+ o = strsep(&options, ",");
+ if (o == NULL)
+ break;
+ if (*o == '\0')
continue;
token = match_token(o, tokens, tmp);
switch (token) {
case Opt_lockproto:
- v = match_strdup(&tmp[0]);
- if (!v) {
- fs_info(sdp, "no memory for lockproto\n");
- error = -ENOMEM;
- goto out_error;
- }
-
- if (remount && strcmp(v, args->ar_lockproto)) {
- kfree(v);
- goto cant_remount;
- }
-
- strncpy(args->ar_lockproto, v, GFS2_LOCKNAME_LEN);
- args->ar_lockproto[GFS2_LOCKNAME_LEN - 1] = 0;
- kfree(v);
+ match_strlcpy(args->ar_lockproto, &tmp[0],
+ GFS2_LOCKNAME_LEN);
break;
case Opt_locktable:
- v = match_strdup(&tmp[0]);
- if (!v) {
- fs_info(sdp, "no memory for locktable\n");
- error = -ENOMEM;
- goto out_error;
- }
-
- if (remount && strcmp(v, args->ar_locktable)) {
- kfree(v);
- goto cant_remount;
- }
-
- strncpy(args->ar_locktable, v, GFS2_LOCKNAME_LEN);
- args->ar_locktable[GFS2_LOCKNAME_LEN - 1] = 0;
- kfree(v);
+ match_strlcpy(args->ar_locktable, &tmp[0],
+ GFS2_LOCKNAME_LEN);
break;
case Opt_hostdata:
- v = match_strdup(&tmp[0]);
- if (!v) {
- fs_info(sdp, "no memory for hostdata\n");
- error = -ENOMEM;
- goto out_error;
- }
-
- if (remount && strcmp(v, args->ar_hostdata)) {
- kfree(v);
- goto cant_remount;
- }
-
- strncpy(args->ar_hostdata, v, GFS2_LOCKNAME_LEN);
- args->ar_hostdata[GFS2_LOCKNAME_LEN - 1] = 0;
- kfree(v);
+ match_strlcpy(args->ar_hostdata, &tmp[0],
+ GFS2_LOCKNAME_LEN);
break;
case Opt_spectator:
- if (remount && !args->ar_spectator)
- goto cant_remount;
args->ar_spectator = 1;
- sdp->sd_vfs->s_flags |= MS_RDONLY;
break;
case Opt_ignore_local_fs:
- if (remount && !args->ar_ignore_local_fs)
- goto cant_remount;
args->ar_ignore_local_fs = 1;
break;
case Opt_localflocks:
- if (remount && !args->ar_localflocks)
- goto cant_remount;
args->ar_localflocks = 1;
break;
case Opt_localcaching:
- if (remount && !args->ar_localcaching)
- goto cant_remount;
args->ar_localcaching = 1;
break;
case Opt_debug:
@@ -181,25 +133,23 @@
args->ar_debug = 0;
break;
case Opt_upgrade:
- if (remount && !args->ar_upgrade)
- goto cant_remount;
args->ar_upgrade = 1;
break;
case Opt_acl:
args->ar_posix_acl = 1;
- sdp->sd_vfs->s_flags |= MS_POSIXACL;
break;
case Opt_noacl:
args->ar_posix_acl = 0;
- sdp->sd_vfs->s_flags &= ~MS_POSIXACL;
break;
case Opt_quota_off:
+ case Opt_noquota:
args->ar_quota = GFS2_QUOTA_OFF;
break;
case Opt_quota_account:
args->ar_quota = GFS2_QUOTA_ACCOUNT;
break;
case Opt_quota_on:
+ case Opt_quota:
args->ar_quota = GFS2_QUOTA_ON;
break;
case Opt_suiddir:
@@ -215,29 +165,21 @@
args->ar_data = GFS2_DATA_ORDERED;
break;
case Opt_meta:
- if (remount && args->ar_meta != 1)
- goto cant_remount;
args->ar_meta = 1;
break;
+ case Opt_discard:
+ args->ar_discard = 1;
+ break;
+ case Opt_nodiscard:
+ args->ar_discard = 0;
+ break;
case Opt_err:
default:
- fs_info(sdp, "unknown option: %s\n", o);
- error = -EINVAL;
- goto out_error;
+ fs_info(sdp, "invalid mount option: %s\n", o);
+ return -EINVAL;
}
}
-out_error:
- if (error)
- fs_info(sdp, "invalid mount option(s)\n");
-
- if (data != data_arg)
- kfree(data);
-
- return error;
-
-cant_remount:
- fs_info(sdp, "can't remount with option %s\n", o);
- return -EINVAL;
+ return 0;
}
diff --git a/fs/gfs2/mount.h b/fs/gfs2/mount.h
deleted file mode 100644
index 401288a..0000000
--- a/fs/gfs2/mount.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
- * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU General Public License version 2.
- */
-
-#ifndef __MOUNT_DOT_H__
-#define __MOUNT_DOT_H__
-
-struct gfs2_sbd;
-
-int gfs2_mount_args(struct gfs2_sbd *sdp, char *data_arg, int remount);
-
-#endif /* __MOUNT_DOT_H__ */
diff --git a/fs/gfs2/ops_address.c b/fs/gfs2/ops_address.c
index 4ddab67..a6dde17 100644
--- a/fs/gfs2/ops_address.c
+++ b/fs/gfs2/ops_address.c
@@ -19,7 +19,6 @@
#include <linux/writeback.h>
#include <linux/swap.h>
#include <linux/gfs2_ondisk.h>
-#include <linux/lm_interface.h>
#include <linux/backing-dev.h>
#include "gfs2.h"
@@ -442,6 +441,7 @@
*/
if (unlikely(page->index)) {
zero_user(page, 0, PAGE_CACHE_SIZE);
+ SetPageUptodate(page);
return 0;
}
@@ -1096,6 +1096,7 @@
.releasepage = gfs2_releasepage,
.direct_IO = gfs2_direct_IO,
.migratepage = buffer_migrate_page,
+ .is_partially_uptodate = block_is_partially_uptodate,
};
static const struct address_space_operations gfs2_ordered_aops = {
@@ -1111,6 +1112,7 @@
.releasepage = gfs2_releasepage,
.direct_IO = gfs2_direct_IO,
.migratepage = buffer_migrate_page,
+ .is_partially_uptodate = block_is_partially_uptodate,
};
static const struct address_space_operations gfs2_jdata_aops = {
@@ -1125,6 +1127,7 @@
.bmap = gfs2_bmap,
.invalidatepage = gfs2_invalidatepage,
.releasepage = gfs2_releasepage,
+ .is_partially_uptodate = block_is_partially_uptodate,
};
void gfs2_set_aops(struct inode *inode)
diff --git a/fs/gfs2/ops_dentry.c b/fs/gfs2/ops_dentry.c
index c2ad363..5eb57b0 100644
--- a/fs/gfs2/ops_dentry.c
+++ b/fs/gfs2/ops_dentry.c
@@ -13,7 +13,6 @@
#include <linux/buffer_head.h>
#include <linux/gfs2_ondisk.h>
#include <linux/crc32.h>
-#include <linux/lm_interface.h>
#include "gfs2.h"
#include "incore.h"
diff --git a/fs/gfs2/ops_export.c b/fs/gfs2/ops_export.c
index 7fdeb14..9200ef2 100644
--- a/fs/gfs2/ops_export.c
+++ b/fs/gfs2/ops_export.c
@@ -14,7 +14,6 @@
#include <linux/exportfs.h>
#include <linux/gfs2_ondisk.h>
#include <linux/crc32.h>
-#include <linux/lm_interface.h>
#include "gfs2.h"
#include "incore.h"
diff --git a/fs/gfs2/ops_file.c b/fs/gfs2/ops_file.c
index 93fe41b..3b9e8de 100644
--- a/fs/gfs2/ops_file.c
+++ b/fs/gfs2/ops_file.c
@@ -20,9 +20,10 @@
#include <linux/gfs2_ondisk.h>
#include <linux/ext2_fs.h>
#include <linux/crc32.h>
-#include <linux/lm_interface.h>
#include <linux/writeback.h>
#include <asm/uaccess.h>
+#include <linux/dlm.h>
+#include <linux/dlm_plock.h>
#include "gfs2.h"
#include "incore.h"
@@ -354,7 +355,9 @@
if (ret)
goto out;
+ set_bit(GLF_DIRTY, &ip->i_gl->gl_flags);
set_bit(GIF_SW_PAGED, &ip->i_flags);
+
ret = gfs2_write_alloc_required(ip, pos, PAGE_CACHE_SIZE, &alloc_required);
if (ret || !alloc_required)
goto out_unlock;
@@ -560,57 +563,24 @@
return ret;
}
+#ifdef CONFIG_GFS2_FS_LOCKING_DLM
+
/**
* gfs2_setlease - acquire/release a file lease
* @file: the file pointer
* @arg: lease type
* @fl: file lock
*
+ * We don't currently have a way to enforce a lease across the whole
+ * cluster; until we do, disable leases (by just returning -EINVAL),
+ * unless the administrator has requested purely local locking.
+ *
* Returns: errno
*/
static int gfs2_setlease(struct file *file, long arg, struct file_lock **fl)
{
- struct gfs2_sbd *sdp = GFS2_SB(file->f_mapping->host);
-
- /*
- * We don't currently have a way to enforce a lease across the whole
- * cluster; until we do, disable leases (by just returning -EINVAL),
- * unless the administrator has requested purely local locking.
- */
- if (!sdp->sd_args.ar_localflocks)
- return -EINVAL;
- return generic_setlease(file, arg, fl);
-}
-
-static int gfs2_lm_plock_get(struct gfs2_sbd *sdp, struct lm_lockname *name,
- struct file *file, struct file_lock *fl)
-{
- int error = -EIO;
- if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
- error = sdp->sd_lockstruct.ls_ops->lm_plock_get(
- sdp->sd_lockstruct.ls_lockspace, name, file, fl);
- return error;
-}
-
-static int gfs2_lm_plock(struct gfs2_sbd *sdp, struct lm_lockname *name,
- struct file *file, int cmd, struct file_lock *fl)
-{
- int error = -EIO;
- if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
- error = sdp->sd_lockstruct.ls_ops->lm_plock(
- sdp->sd_lockstruct.ls_lockspace, name, file, cmd, fl);
- return error;
-}
-
-static int gfs2_lm_punlock(struct gfs2_sbd *sdp, struct lm_lockname *name,
- struct file *file, struct file_lock *fl)
-{
- int error = -EIO;
- if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
- error = sdp->sd_lockstruct.ls_ops->lm_punlock(
- sdp->sd_lockstruct.ls_lockspace, name, file, fl);
- return error;
+ return -EINVAL;
}
/**
@@ -626,9 +596,7 @@
{
struct gfs2_inode *ip = GFS2_I(file->f_mapping->host);
struct gfs2_sbd *sdp = GFS2_SB(file->f_mapping->host);
- struct lm_lockname name =
- { .ln_number = ip->i_no_addr,
- .ln_type = LM_TYPE_PLOCK };
+ struct lm_lockstruct *ls = &sdp->sd_lockstruct;
if (!(fl->fl_flags & FL_POSIX))
return -ENOLCK;
@@ -640,12 +608,14 @@
cmd = F_SETLK;
fl->fl_type = F_UNLCK;
}
+ if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
+ return -EIO;
if (IS_GETLK(cmd))
- return gfs2_lm_plock_get(sdp, &name, file, fl);
+ return dlm_posix_get(ls->ls_dlm, ip->i_no_addr, file, fl);
else if (fl->fl_type == F_UNLCK)
- return gfs2_lm_punlock(sdp, &name, file, fl);
+ return dlm_posix_unlock(ls->ls_dlm, ip->i_no_addr, file, fl);
else
- return gfs2_lm_plock(sdp, &name, file, cmd, fl);
+ return dlm_posix_lock(ls->ls_dlm, ip->i_no_addr, file, cmd, fl);
}
static int do_flock(struct file *file, int cmd, struct file_lock *fl)
@@ -732,7 +702,7 @@
}
}
-const struct file_operations gfs2_file_fops = {
+const struct file_operations *gfs2_file_fops = &(const struct file_operations){
.llseek = gfs2_llseek,
.read = do_sync_read,
.aio_read = generic_file_aio_read,
@@ -750,7 +720,7 @@
.setlease = gfs2_setlease,
};
-const struct file_operations gfs2_dir_fops = {
+const struct file_operations *gfs2_dir_fops = &(const struct file_operations){
.readdir = gfs2_readdir,
.unlocked_ioctl = gfs2_ioctl,
.open = gfs2_open,
@@ -760,7 +730,9 @@
.flock = gfs2_flock,
};
-const struct file_operations gfs2_file_fops_nolock = {
+#endif /* CONFIG_GFS2_FS_LOCKING_DLM */
+
+const struct file_operations *gfs2_file_fops_nolock = &(const struct file_operations){
.llseek = gfs2_llseek,
.read = do_sync_read,
.aio_read = generic_file_aio_read,
@@ -773,10 +745,10 @@
.fsync = gfs2_fsync,
.splice_read = generic_file_splice_read,
.splice_write = generic_file_splice_write,
- .setlease = gfs2_setlease,
+ .setlease = generic_setlease,
};
-const struct file_operations gfs2_dir_fops_nolock = {
+const struct file_operations *gfs2_dir_fops_nolock = &(const struct file_operations){
.readdir = gfs2_readdir,
.unlocked_ioctl = gfs2_ioctl,
.open = gfs2_open,
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index f91eebd..51883b3 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -17,7 +17,6 @@
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/gfs2_ondisk.h>
-#include <linux/lm_interface.h>
#include "gfs2.h"
#include "incore.h"
@@ -25,7 +24,6 @@
#include "glock.h"
#include "glops.h"
#include "inode.h"
-#include "mount.h"
#include "recovery.h"
#include "rgrp.h"
#include "super.h"
@@ -64,7 +62,6 @@
gt->gt_quota_warn_period = 10;
gt->gt_quota_scale_num = 1;
gt->gt_quota_scale_den = 1;
- gt->gt_quota_cache_secs = 300;
gt->gt_quota_quantum = 60;
gt->gt_new_files_jdata = 0;
gt->gt_max_readahead = 1 << 18;
@@ -100,7 +97,6 @@
mutex_init(&sdp->sd_jindex_mutex);
INIT_LIST_HEAD(&sdp->sd_quota_list);
- spin_lock_init(&sdp->sd_quota_spin);
mutex_init(&sdp->sd_quota_mutex);
init_waitqueue_head(&sdp->sd_quota_wait);
INIT_LIST_HEAD(&sdp->sd_trunc_list);
@@ -238,6 +234,7 @@
memcpy(sb->sb_lockproto, str->sb_lockproto, GFS2_LOCKNAME_LEN);
memcpy(sb->sb_locktable, str->sb_locktable, GFS2_LOCKNAME_LEN);
+ memcpy(sb->sb_uuid, str->sb_uuid, 16);
}
/**
@@ -299,15 +296,15 @@
__free_page(page);
return 0;
}
+
/**
* gfs2_read_sb - Read super block
* @sdp: The GFS2 superblock
- * @gl: the glock for the superblock (assumed to be held)
* @silent: Don't print message if mount fails
*
*/
-static int gfs2_read_sb(struct gfs2_sbd *sdp, struct gfs2_glock *gl, int silent)
+static int gfs2_read_sb(struct gfs2_sbd *sdp, int silent)
{
u32 hash_blocks, ind_blocks, leaf_blocks;
u32 tmp_blocks;
@@ -527,7 +524,7 @@
return ret;
}
- ret = gfs2_read_sb(sdp, sb_gh.gh_gl, silent);
+ ret = gfs2_read_sb(sdp, silent);
if (ret) {
fs_err(sdp, "can't read superblock: %d\n", ret);
goto out;
@@ -630,13 +627,13 @@
return rc;
}
-static void gfs2_lm_others_may_mount(struct gfs2_sbd *sdp)
+static void gfs2_others_may_mount(struct gfs2_sbd *sdp)
{
- if (!sdp->sd_lockstruct.ls_ops->lm_others_may_mount)
- return;
- if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
- sdp->sd_lockstruct.ls_ops->lm_others_may_mount(
- sdp->sd_lockstruct.ls_lockspace);
+ char *message = "FIRSTMOUNT=Done";
+ char *envp[] = { message, NULL };
+ struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+ ls->ls_first_done = 1;
+ kobject_uevent_env(&sdp->sd_kobj, KOBJ_CHANGE, envp);
}
/**
@@ -796,7 +793,7 @@
}
}
- gfs2_lm_others_may_mount(sdp);
+ gfs2_others_may_mount(sdp);
} else if (!sdp->sd_args.ar_spectator) {
error = gfs2_recover_journal(sdp->sd_jdesc);
if (error) {
@@ -1005,7 +1002,6 @@
goto fail_quotad;
sdp->sd_log_flush_time = jiffies;
- sdp->sd_jindex_refresh_time = jiffies;
p = kthread_run(gfs2_logd, sdp, "gfs2_logd");
error = IS_ERR(p);
@@ -1033,6 +1029,17 @@
return error;
}
+static const match_table_t nolock_tokens = {
+ { Opt_jid, "jid=%d\n", },
+ { Opt_err, NULL },
+};
+
+static const struct lm_lockops nolock_ops = {
+ .lm_proto_name = "lock_nolock",
+ .lm_put_lock = kmem_cache_free,
+ .lm_tokens = &nolock_tokens,
+};
+
/**
* gfs2_lm_mount - mount a locking protocol
* @sdp: the filesystem
@@ -1044,31 +1051,73 @@
static int gfs2_lm_mount(struct gfs2_sbd *sdp, int silent)
{
- char *proto = sdp->sd_proto_name;
- char *table = sdp->sd_table_name;
- int flags = LM_MFLAG_CONV_NODROP;
- int error;
+ const struct lm_lockops *lm;
+ struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+ struct gfs2_args *args = &sdp->sd_args;
+ const char *proto = sdp->sd_proto_name;
+ const char *table = sdp->sd_table_name;
+ const char *fsname;
+ char *o, *options;
+ int ret;
- if (sdp->sd_args.ar_spectator)
- flags |= LM_MFLAG_SPECTATOR;
+ if (!strcmp("lock_nolock", proto)) {
+ lm = &nolock_ops;
+ sdp->sd_args.ar_localflocks = 1;
+ sdp->sd_args.ar_localcaching = 1;
+#ifdef CONFIG_GFS2_FS_LOCKING_DLM
+ } else if (!strcmp("lock_dlm", proto)) {
+ lm = &gfs2_dlm_ops;
+#endif
+ } else {
+ printk(KERN_INFO "GFS2: can't find protocol %s\n", proto);
+ return -ENOENT;
+ }
fs_info(sdp, "Trying to join cluster \"%s\", \"%s\"\n", proto, table);
- error = gfs2_mount_lockproto(proto, table, sdp->sd_args.ar_hostdata,
- gfs2_glock_cb, sdp,
- GFS2_MIN_LVB_SIZE, flags,
- &sdp->sd_lockstruct, &sdp->sd_kobj);
- if (error) {
- fs_info(sdp, "can't mount proto=%s, table=%s, hostdata=%s\n",
- proto, table, sdp->sd_args.ar_hostdata);
- goto out;
- }
+ ls->ls_ops = lm;
+ ls->ls_first = 1;
+ ls->ls_id = 0;
- if (gfs2_assert_warn(sdp, sdp->sd_lockstruct.ls_ops) ||
- gfs2_assert_warn(sdp, sdp->sd_lockstruct.ls_lvb_size >=
- GFS2_MIN_LVB_SIZE)) {
- gfs2_unmount_lockproto(&sdp->sd_lockstruct);
- goto out;
+ for (options = args->ar_hostdata; (o = strsep(&options, ":")); ) {
+ substring_t tmp[MAX_OPT_ARGS];
+ int token, option;
+
+ if (!o || !*o)
+ continue;
+
+ token = match_token(o, *lm->lm_tokens, tmp);
+ switch (token) {
+ case Opt_jid:
+ ret = match_int(&tmp[0], &option);
+ if (ret || option < 0)
+ goto hostdata_error;
+ ls->ls_jid = option;
+ break;
+ case Opt_id:
+ ret = match_int(&tmp[0], &option);
+ if (ret)
+ goto hostdata_error;
+ ls->ls_id = option;
+ break;
+ case Opt_first:
+ ret = match_int(&tmp[0], &option);
+ if (ret || (option != 0 && option != 1))
+ goto hostdata_error;
+ ls->ls_first = option;
+ break;
+ case Opt_nodir:
+ ret = match_int(&tmp[0], &option);
+ if (ret || (option != 0 && option != 1))
+ goto hostdata_error;
+ ls->ls_nodir = option;
+ break;
+ case Opt_err:
+ default:
+hostdata_error:
+ fs_info(sdp, "unknown hostdata (%s)\n", o);
+ return -EINVAL;
+ }
}
if (sdp->sd_args.ar_spectator)
@@ -1077,22 +1126,25 @@
snprintf(sdp->sd_fsname, GFS2_FSNAME_LEN, "%s.%u", table,
sdp->sd_lockstruct.ls_jid);
- fs_info(sdp, "Joined cluster. Now mounting FS...\n");
-
- if ((sdp->sd_lockstruct.ls_flags & LM_LSFLAG_LOCAL) &&
- !sdp->sd_args.ar_ignore_local_fs) {
- sdp->sd_args.ar_localflocks = 1;
- sdp->sd_args.ar_localcaching = 1;
+ fsname = strchr(table, ':');
+ if (fsname)
+ fsname++;
+ if (lm->lm_mount == NULL) {
+ fs_info(sdp, "Now mounting FS...\n");
+ return 0;
}
-
-out:
- return error;
+ ret = lm->lm_mount(sdp, fsname);
+ if (ret == 0)
+ fs_info(sdp, "Joined cluster. Now mounting FS...\n");
+ return ret;
}
void gfs2_lm_unmount(struct gfs2_sbd *sdp)
{
- if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
- gfs2_unmount_lockproto(&sdp->sd_lockstruct);
+ const struct lm_lockops *lm = sdp->sd_lockstruct.ls_ops;
+ if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)) &&
+ lm->lm_unmount)
+ lm->lm_unmount(sdp);
}
/**
@@ -1116,12 +1168,20 @@
return -ENOMEM;
}
- error = gfs2_mount_args(sdp, (char *)data, 0);
+ sdp->sd_args.ar_quota = GFS2_QUOTA_DEFAULT;
+ sdp->sd_args.ar_data = GFS2_DATA_DEFAULT;
+
+ error = gfs2_mount_args(sdp, &sdp->sd_args, data);
if (error) {
printk(KERN_WARNING "GFS2: can't parse mount arguments\n");
goto fail;
}
+ if (sdp->sd_args.ar_spectator)
+ sb->s_flags |= MS_RDONLY;
+ if (sdp->sd_args.ar_posix_acl)
+ sb->s_flags |= MS_POSIXACL;
+
sb->s_magic = GFS2_MAGIC;
sb->s_op = &gfs2_super_ops;
sb->s_export_op = &gfs2_export_ops;
@@ -1199,6 +1259,8 @@
dput(sdp->sd_root_dir);
if (sdp->sd_master_dir)
dput(sdp->sd_master_dir);
+ if (sb->s_root)
+ dput(sb->s_root);
sb->s_root = NULL;
fail_locking:
init_locking(sdp, &mount_gh, UNDO);
diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c
index 4987754..abd5429 100644
--- a/fs/gfs2/ops_inode.c
+++ b/fs/gfs2/ops_inode.c
@@ -18,7 +18,6 @@
#include <linux/posix_acl.h>
#include <linux/gfs2_ondisk.h>
#include <linux/crc32.h>
-#include <linux/lm_interface.h>
#include <linux/fiemap.h>
#include <asm/uaccess.h>
diff --git a/fs/gfs2/ops_super.c b/fs/gfs2/ops_super.c
index 320323d..4580195 100644
--- a/fs/gfs2/ops_super.c
+++ b/fs/gfs2/ops_super.c
@@ -19,7 +19,6 @@
#include <linux/delay.h>
#include <linux/gfs2_ondisk.h>
#include <linux/crc32.h>
-#include <linux/lm_interface.h>
#include <linux/time.h>
#include "gfs2.h"
@@ -27,7 +26,6 @@
#include "glock.h"
#include "inode.h"
#include "log.h"
-#include "mount.h"
#include "quota.h"
#include "recovery.h"
#include "rgrp.h"
@@ -40,6 +38,8 @@
#include "bmap.h"
#include "meta_io.h"
+#define args_neq(a1, a2, x) ((a1)->ar_##x != (a2)->ar_##x)
+
/**
* gfs2_write_inode - Make sure the inode is stable on the disk
* @inode: The inode
@@ -435,25 +435,45 @@
static int gfs2_remount_fs(struct super_block *sb, int *flags, char *data)
{
struct gfs2_sbd *sdp = sb->s_fs_info;
+ struct gfs2_args args = sdp->sd_args; /* Default to current settings */
int error;
- error = gfs2_mount_args(sdp, data, 1);
+ error = gfs2_mount_args(sdp, &args, data);
if (error)
return error;
+ /* Not allowed to change locking details */
+ if (strcmp(args.ar_lockproto, sdp->sd_args.ar_lockproto) ||
+ strcmp(args.ar_locktable, sdp->sd_args.ar_locktable) ||
+ strcmp(args.ar_hostdata, sdp->sd_args.ar_hostdata))
+ return -EINVAL;
+
+ /* Some flags must not be changed */
+ if (args_neq(&args, &sdp->sd_args, spectator) ||
+ args_neq(&args, &sdp->sd_args, ignore_local_fs) ||
+ args_neq(&args, &sdp->sd_args, localflocks) ||
+ args_neq(&args, &sdp->sd_args, localcaching) ||
+ args_neq(&args, &sdp->sd_args, meta))
+ return -EINVAL;
+
if (sdp->sd_args.ar_spectator)
*flags |= MS_RDONLY;
- else {
- if (*flags & MS_RDONLY) {
- if (!(sb->s_flags & MS_RDONLY))
- error = gfs2_make_fs_ro(sdp);
- } else if (!(*flags & MS_RDONLY) &&
- (sb->s_flags & MS_RDONLY)) {
+
+ if ((sb->s_flags ^ *flags) & MS_RDONLY) {
+ if (*flags & MS_RDONLY)
+ error = gfs2_make_fs_ro(sdp);
+ else
error = gfs2_make_fs_rw(sdp);
- }
+ if (error)
+ return error;
}
- return error;
+ sdp->sd_args = args;
+ if (sdp->sd_args.ar_posix_acl)
+ sb->s_flags |= MS_POSIXACL;
+ else
+ sb->s_flags &= ~MS_POSIXACL;
+ return 0;
}
/**
@@ -588,6 +608,8 @@
}
seq_printf(s, ",data=%s", state);
}
+ if (args->ar_discard)
+ seq_printf(s, ",discard");
return 0;
}
diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
index b08d096..8d53f66 100644
--- a/fs/gfs2/quota.c
+++ b/fs/gfs2/quota.c
@@ -45,7 +45,6 @@
#include <linux/fs.h>
#include <linux/bio.h>
#include <linux/gfs2_ondisk.h>
-#include <linux/lm_interface.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
@@ -80,6 +79,51 @@
u32 qc_id;
};
+static LIST_HEAD(qd_lru_list);
+static atomic_t qd_lru_count = ATOMIC_INIT(0);
+static spinlock_t qd_lru_lock = SPIN_LOCK_UNLOCKED;
+
+int gfs2_shrink_qd_memory(int nr, gfp_t gfp_mask)
+{
+ struct gfs2_quota_data *qd;
+ struct gfs2_sbd *sdp;
+
+ if (nr == 0)
+ goto out;
+
+ if (!(gfp_mask & __GFP_FS))
+ return -1;
+
+ spin_lock(&qd_lru_lock);
+ while (nr && !list_empty(&qd_lru_list)) {
+ qd = list_entry(qd_lru_list.next,
+ struct gfs2_quota_data, qd_reclaim);
+ sdp = qd->qd_gl->gl_sbd;
+
+ /* Free from the filesystem-specific list */
+ list_del(&qd->qd_list);
+
+ gfs2_assert_warn(sdp, !qd->qd_change);
+ gfs2_assert_warn(sdp, !qd->qd_slot_count);
+ gfs2_assert_warn(sdp, !qd->qd_bh_count);
+
+ gfs2_glock_put(qd->qd_gl);
+ atomic_dec(&sdp->sd_quota_count);
+
+ /* Delete it from the common reclaim list */
+ list_del_init(&qd->qd_reclaim);
+ atomic_dec(&qd_lru_count);
+ spin_unlock(&qd_lru_lock);
+ kmem_cache_free(gfs2_quotad_cachep, qd);
+ spin_lock(&qd_lru_lock);
+ nr--;
+ }
+ spin_unlock(&qd_lru_lock);
+
+out:
+ return (atomic_read(&qd_lru_count) * sysctl_vfs_cache_pressure) / 100;
+}
+
static u64 qd2offset(struct gfs2_quota_data *qd)
{
u64 offset;
@@ -100,22 +144,18 @@
if (!qd)
return -ENOMEM;
- qd->qd_count = 1;
+ atomic_set(&qd->qd_count, 1);
qd->qd_id = id;
if (user)
set_bit(QDF_USER, &qd->qd_flags);
qd->qd_slot = -1;
+ INIT_LIST_HEAD(&qd->qd_reclaim);
error = gfs2_glock_get(sdp, 2 * (u64)id + !user,
&gfs2_quota_glops, CREATE, &qd->qd_gl);
if (error)
goto fail;
- error = gfs2_lvb_hold(qd->qd_gl);
- gfs2_glock_put(qd->qd_gl);
- if (error)
- goto fail;
-
*qdp = qd;
return 0;
@@ -135,11 +175,17 @@
for (;;) {
found = 0;
- spin_lock(&sdp->sd_quota_spin);
+ spin_lock(&qd_lru_lock);
list_for_each_entry(qd, &sdp->sd_quota_list, qd_list) {
if (qd->qd_id == id &&
!test_bit(QDF_USER, &qd->qd_flags) == !user) {
- qd->qd_count++;
+ if (!atomic_read(&qd->qd_count) &&
+ !list_empty(&qd->qd_reclaim)) {
+ /* Remove it from reclaim list */
+ list_del_init(&qd->qd_reclaim);
+ atomic_dec(&qd_lru_count);
+ }
+ atomic_inc(&qd->qd_count);
found = 1;
break;
}
@@ -155,11 +201,11 @@
new_qd = NULL;
}
- spin_unlock(&sdp->sd_quota_spin);
+ spin_unlock(&qd_lru_lock);
if (qd || !create) {
if (new_qd) {
- gfs2_lvb_unhold(new_qd->qd_gl);
+ gfs2_glock_put(new_qd->qd_gl);
kmem_cache_free(gfs2_quotad_cachep, new_qd);
}
*qdp = qd;
@@ -175,21 +221,18 @@
static void qd_hold(struct gfs2_quota_data *qd)
{
struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd;
-
- spin_lock(&sdp->sd_quota_spin);
- gfs2_assert(sdp, qd->qd_count);
- qd->qd_count++;
- spin_unlock(&sdp->sd_quota_spin);
+ gfs2_assert(sdp, atomic_read(&qd->qd_count));
+ atomic_inc(&qd->qd_count);
}
static void qd_put(struct gfs2_quota_data *qd)
{
- struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd;
- spin_lock(&sdp->sd_quota_spin);
- gfs2_assert(sdp, qd->qd_count);
- if (!--qd->qd_count)
- qd->qd_last_touched = jiffies;
- spin_unlock(&sdp->sd_quota_spin);
+ if (atomic_dec_and_lock(&qd->qd_count, &qd_lru_lock)) {
+ /* Add to the reclaim list */
+ list_add_tail(&qd->qd_reclaim, &qd_lru_list);
+ atomic_inc(&qd_lru_count);
+ spin_unlock(&qd_lru_lock);
+ }
}
static int slot_get(struct gfs2_quota_data *qd)
@@ -198,10 +241,10 @@
unsigned int c, o = 0, b;
unsigned char byte = 0;
- spin_lock(&sdp->sd_quota_spin);
+ spin_lock(&qd_lru_lock);
if (qd->qd_slot_count++) {
- spin_unlock(&sdp->sd_quota_spin);
+ spin_unlock(&qd_lru_lock);
return 0;
}
@@ -225,13 +268,13 @@
sdp->sd_quota_bitmap[c][o] |= 1 << b;
- spin_unlock(&sdp->sd_quota_spin);
+ spin_unlock(&qd_lru_lock);
return 0;
fail:
qd->qd_slot_count--;
- spin_unlock(&sdp->sd_quota_spin);
+ spin_unlock(&qd_lru_lock);
return -ENOSPC;
}
@@ -239,23 +282,23 @@
{
struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd;
- spin_lock(&sdp->sd_quota_spin);
+ spin_lock(&qd_lru_lock);
gfs2_assert(sdp, qd->qd_slot_count);
qd->qd_slot_count++;
- spin_unlock(&sdp->sd_quota_spin);
+ spin_unlock(&qd_lru_lock);
}
static void slot_put(struct gfs2_quota_data *qd)
{
struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd;
- spin_lock(&sdp->sd_quota_spin);
+ spin_lock(&qd_lru_lock);
gfs2_assert(sdp, qd->qd_slot_count);
if (!--qd->qd_slot_count) {
gfs2_icbit_munge(sdp, sdp->sd_quota_bitmap, qd->qd_slot, 0);
qd->qd_slot = -1;
}
- spin_unlock(&sdp->sd_quota_spin);
+ spin_unlock(&qd_lru_lock);
}
static int bh_get(struct gfs2_quota_data *qd)
@@ -330,7 +373,7 @@
if (sdp->sd_vfs->s_flags & MS_RDONLY)
return 0;
- spin_lock(&sdp->sd_quota_spin);
+ spin_lock(&qd_lru_lock);
list_for_each_entry(qd, &sdp->sd_quota_list, qd_list) {
if (test_bit(QDF_LOCKED, &qd->qd_flags) ||
@@ -341,8 +384,8 @@
list_move_tail(&qd->qd_list, &sdp->sd_quota_list);
set_bit(QDF_LOCKED, &qd->qd_flags);
- gfs2_assert_warn(sdp, qd->qd_count);
- qd->qd_count++;
+ gfs2_assert_warn(sdp, atomic_read(&qd->qd_count));
+ atomic_inc(&qd->qd_count);
qd->qd_change_sync = qd->qd_change;
gfs2_assert_warn(sdp, qd->qd_slot_count);
qd->qd_slot_count++;
@@ -354,7 +397,7 @@
if (!found)
qd = NULL;
- spin_unlock(&sdp->sd_quota_spin);
+ spin_unlock(&qd_lru_lock);
if (qd) {
gfs2_assert_warn(sdp, qd->qd_change_sync);
@@ -379,24 +422,24 @@
if (sdp->sd_vfs->s_flags & MS_RDONLY)
return 0;
- spin_lock(&sdp->sd_quota_spin);
+ spin_lock(&qd_lru_lock);
if (test_bit(QDF_LOCKED, &qd->qd_flags) ||
!test_bit(QDF_CHANGE, &qd->qd_flags)) {
- spin_unlock(&sdp->sd_quota_spin);
+ spin_unlock(&qd_lru_lock);
return 0;
}
list_move_tail(&qd->qd_list, &sdp->sd_quota_list);
set_bit(QDF_LOCKED, &qd->qd_flags);
- gfs2_assert_warn(sdp, qd->qd_count);
- qd->qd_count++;
+ gfs2_assert_warn(sdp, atomic_read(&qd->qd_count));
+ atomic_inc(&qd->qd_count);
qd->qd_change_sync = qd->qd_change;
gfs2_assert_warn(sdp, qd->qd_slot_count);
qd->qd_slot_count++;
- spin_unlock(&sdp->sd_quota_spin);
+ spin_unlock(&qd_lru_lock);
gfs2_assert_warn(sdp, qd->qd_change_sync);
if (bh_get(qd)) {
@@ -556,9 +599,9 @@
x = be64_to_cpu(qc->qc_change) + change;
qc->qc_change = cpu_to_be64(x);
- spin_lock(&sdp->sd_quota_spin);
+ spin_lock(&qd_lru_lock);
qd->qd_change = x;
- spin_unlock(&sdp->sd_quota_spin);
+ spin_unlock(&qd_lru_lock);
if (!x) {
gfs2_assert_warn(sdp, test_bit(QDF_CHANGE, &qd->qd_flags));
@@ -802,8 +845,8 @@
loff_t pos;
gfs2_glock_dq_uninit(q_gh);
error = gfs2_glock_nq_init(qd->qd_gl,
- LM_ST_EXCLUSIVE, GL_NOCACHE,
- q_gh);
+ LM_ST_EXCLUSIVE, GL_NOCACHE,
+ q_gh);
if (error)
return error;
@@ -820,7 +863,6 @@
gfs2_glock_dq_uninit(&i_gh);
-
gfs2_quota_in(&q, buf);
qlvb = (struct gfs2_quota_lvb *)qd->qd_gl->gl_lvb;
qlvb->qb_magic = cpu_to_be32(GFS2_MAGIC);
@@ -890,9 +932,9 @@
if (!qd->qd_qb.qb_limit)
return 0;
- spin_lock(&sdp->sd_quota_spin);
+ spin_lock(&qd_lru_lock);
value = qd->qd_change;
- spin_unlock(&sdp->sd_quota_spin);
+ spin_unlock(&qd_lru_lock);
spin_lock(>->gt_spin);
num = gt->gt_quota_scale_num;
@@ -985,9 +1027,9 @@
continue;
value = (s64)be64_to_cpu(qd->qd_qb.qb_value);
- spin_lock(&sdp->sd_quota_spin);
+ spin_lock(&qd_lru_lock);
value += qd->qd_change;
- spin_unlock(&sdp->sd_quota_spin);
+ spin_unlock(&qd_lru_lock);
if (be64_to_cpu(qd->qd_qb.qb_limit) && (s64)be64_to_cpu(qd->qd_qb.qb_limit) < value) {
print_message(qd, "exceeded");
@@ -1171,13 +1213,12 @@
qd->qd_change = qc.qc_change;
qd->qd_slot = slot;
qd->qd_slot_count = 1;
- qd->qd_last_touched = jiffies;
- spin_lock(&sdp->sd_quota_spin);
+ spin_lock(&qd_lru_lock);
gfs2_icbit_munge(sdp, sdp->sd_quota_bitmap, slot, 1);
list_add(&qd->qd_list, &sdp->sd_quota_list);
atomic_inc(&sdp->sd_quota_count);
- spin_unlock(&sdp->sd_quota_spin);
+ spin_unlock(&qd_lru_lock);
found++;
}
@@ -1197,73 +1238,48 @@
return error;
}
-static void gfs2_quota_scan(struct gfs2_sbd *sdp)
-{
- struct gfs2_quota_data *qd, *safe;
- LIST_HEAD(dead);
-
- spin_lock(&sdp->sd_quota_spin);
- list_for_each_entry_safe(qd, safe, &sdp->sd_quota_list, qd_list) {
- if (!qd->qd_count &&
- time_after_eq(jiffies, qd->qd_last_touched +
- gfs2_tune_get(sdp, gt_quota_cache_secs) * HZ)) {
- list_move(&qd->qd_list, &dead);
- gfs2_assert_warn(sdp,
- atomic_read(&sdp->sd_quota_count) > 0);
- atomic_dec(&sdp->sd_quota_count);
- }
- }
- spin_unlock(&sdp->sd_quota_spin);
-
- while (!list_empty(&dead)) {
- qd = list_entry(dead.next, struct gfs2_quota_data, qd_list);
- list_del(&qd->qd_list);
-
- gfs2_assert_warn(sdp, !qd->qd_change);
- gfs2_assert_warn(sdp, !qd->qd_slot_count);
- gfs2_assert_warn(sdp, !qd->qd_bh_count);
-
- gfs2_lvb_unhold(qd->qd_gl);
- kmem_cache_free(gfs2_quotad_cachep, qd);
- }
-}
-
void gfs2_quota_cleanup(struct gfs2_sbd *sdp)
{
struct list_head *head = &sdp->sd_quota_list;
struct gfs2_quota_data *qd;
unsigned int x;
- spin_lock(&sdp->sd_quota_spin);
+ spin_lock(&qd_lru_lock);
while (!list_empty(head)) {
qd = list_entry(head->prev, struct gfs2_quota_data, qd_list);
- if (qd->qd_count > 1 ||
- (qd->qd_count && !test_bit(QDF_CHANGE, &qd->qd_flags))) {
+ if (atomic_read(&qd->qd_count) > 1 ||
+ (atomic_read(&qd->qd_count) &&
+ !test_bit(QDF_CHANGE, &qd->qd_flags))) {
list_move(&qd->qd_list, head);
- spin_unlock(&sdp->sd_quota_spin);
+ spin_unlock(&qd_lru_lock);
schedule();
- spin_lock(&sdp->sd_quota_spin);
+ spin_lock(&qd_lru_lock);
continue;
}
list_del(&qd->qd_list);
+ /* Also remove if this qd exists in the reclaim list */
+ if (!list_empty(&qd->qd_reclaim)) {
+ list_del_init(&qd->qd_reclaim);
+ atomic_dec(&qd_lru_count);
+ }
atomic_dec(&sdp->sd_quota_count);
- spin_unlock(&sdp->sd_quota_spin);
+ spin_unlock(&qd_lru_lock);
- if (!qd->qd_count) {
+ if (!atomic_read(&qd->qd_count)) {
gfs2_assert_warn(sdp, !qd->qd_change);
gfs2_assert_warn(sdp, !qd->qd_slot_count);
} else
gfs2_assert_warn(sdp, qd->qd_slot_count == 1);
gfs2_assert_warn(sdp, !qd->qd_bh_count);
- gfs2_lvb_unhold(qd->qd_gl);
+ gfs2_glock_put(qd->qd_gl);
kmem_cache_free(gfs2_quotad_cachep, qd);
- spin_lock(&sdp->sd_quota_spin);
+ spin_lock(&qd_lru_lock);
}
- spin_unlock(&sdp->sd_quota_spin);
+ spin_unlock(&qd_lru_lock);
gfs2_assert_warn(sdp, !atomic_read(&sdp->sd_quota_count));
@@ -1341,9 +1357,6 @@
quotad_check_timeo(sdp, "sync", gfs2_quota_sync, t,
"ad_timeo, &tune->gt_quota_quantum);
- /* FIXME: This should be turned into a shrinker */
- gfs2_quota_scan(sdp);
-
/* Check for & recover partially truncated inodes */
quotad_check_trunc_list(sdp);
diff --git a/fs/gfs2/quota.h b/fs/gfs2/quota.h
index cec9032..0fa5fa6 100644
--- a/fs/gfs2/quota.h
+++ b/fs/gfs2/quota.h
@@ -49,4 +49,6 @@
return ret;
}
+extern int gfs2_shrink_qd_memory(int nr, gfp_t gfp_mask);
+
#endif /* __QUOTA_DOT_H__ */
diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c
index efd09c3..247e8f7 100644
--- a/fs/gfs2/recovery.c
+++ b/fs/gfs2/recovery.c
@@ -13,7 +13,6 @@
#include <linux/buffer_head.h>
#include <linux/gfs2_ondisk.h>
#include <linux/crc32.h>
-#include <linux/lm_interface.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
@@ -427,20 +426,23 @@
}
-static void gfs2_lm_recovery_done(struct gfs2_sbd *sdp, unsigned int jid,
- unsigned int message)
+static void gfs2_recovery_done(struct gfs2_sbd *sdp, unsigned int jid,
+ unsigned int message)
{
- if (!sdp->sd_lockstruct.ls_ops->lm_recovery_done)
- return;
-
- if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
- sdp->sd_lockstruct.ls_ops->lm_recovery_done(
- sdp->sd_lockstruct.ls_lockspace, jid, message);
+ char env_jid[20];
+ char env_status[20];
+ char *envp[] = { env_jid, env_status, NULL };
+ struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+ ls->ls_recover_jid_done = jid;
+ ls->ls_recover_jid_status = message;
+ sprintf(env_jid, "JID=%d", jid);
+ sprintf(env_status, "RECOVERY=%s",
+ message == LM_RD_SUCCESS ? "Done" : "Failed");
+ kobject_uevent_env(&sdp->sd_kobj, KOBJ_CHANGE, envp);
}
-
/**
- * gfs2_recover_journal - recovery a given journal
+ * gfs2_recover_journal - recover a given journal
* @jd: the struct gfs2_jdesc describing the journal
*
* Acquire the journal's lock, check to see if the journal is clean, and
@@ -561,7 +563,7 @@
if (jd->jd_jid != sdp->sd_lockstruct.ls_jid)
gfs2_glock_dq_uninit(&ji_gh);
- gfs2_lm_recovery_done(sdp, jd->jd_jid, LM_RD_SUCCESS);
+ gfs2_recovery_done(sdp, jd->jd_jid, LM_RD_SUCCESS);
if (jd->jd_jid != sdp->sd_lockstruct.ls_jid)
gfs2_glock_dq_uninit(&j_gh);
@@ -581,7 +583,7 @@
fs_info(sdp, "jid=%u: %s\n", jd->jd_jid, (error) ? "Failed" : "Done");
fail:
- gfs2_lm_recovery_done(sdp, jd->jd_jid, LM_RD_GAVEUP);
+ gfs2_recovery_done(sdp, jd->jd_jid, LM_RD_GAVEUP);
return error;
}
diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c
index 8b01c63..f03d024 100644
--- a/fs/gfs2/rgrp.c
+++ b/fs/gfs2/rgrp.c
@@ -13,8 +13,8 @@
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/gfs2_ondisk.h>
-#include <linux/lm_interface.h>
#include <linux/prefetch.h>
+#include <linux/blkdev.h>
#include "gfs2.h"
#include "incore.h"
@@ -132,81 +132,90 @@
}
/**
+ * gfs2_bit_search
+ * @ptr: Pointer to bitmap data
+ * @mask: Mask to use (normally 0x55555.... but adjusted for search start)
+ * @state: The state we are searching for
+ *
+ * We xor the bitmap data with a patter which is the bitwise opposite
+ * of what we are looking for, this gives rise to a pattern of ones
+ * wherever there is a match. Since we have two bits per entry, we
+ * take this pattern, shift it down by one place and then and it with
+ * the original. All the even bit positions (0,2,4, etc) then represent
+ * successful matches, so we mask with 0x55555..... to remove the unwanted
+ * odd bit positions.
+ *
+ * This allows searching of a whole u64 at once (32 blocks) with a
+ * single test (on 64 bit arches).
+ */
+
+static inline u64 gfs2_bit_search(const __le64 *ptr, u64 mask, u8 state)
+{
+ u64 tmp;
+ static const u64 search[] = {
+ [0] = 0xffffffffffffffffULL,
+ [1] = 0xaaaaaaaaaaaaaaaaULL,
+ [2] = 0x5555555555555555ULL,
+ [3] = 0x0000000000000000ULL,
+ };
+ tmp = le64_to_cpu(*ptr) ^ search[state];
+ tmp &= (tmp >> 1);
+ tmp &= mask;
+ return tmp;
+}
+
+/**
* gfs2_bitfit - Search an rgrp's bitmap buffer to find a bit-pair representing
* a block in a given allocation state.
* @buffer: the buffer that holds the bitmaps
- * @buflen: the length (in bytes) of the buffer
+ * @len: the length (in bytes) of the buffer
* @goal: start search at this block's bit-pair (within @buffer)
- * @old_state: GFS2_BLKST_XXX the state of the block we're looking for.
+ * @state: GFS2_BLKST_XXX the state of the block we're looking for.
*
* Scope of @goal and returned block number is only within this bitmap buffer,
* not entire rgrp or filesystem. @buffer will be offset from the actual
- * beginning of a bitmap block buffer, skipping any header structures.
+ * beginning of a bitmap block buffer, skipping any header structures, but
+ * headers are always a multiple of 64 bits long so that the buffer is
+ * always aligned to a 64 bit boundary.
+ *
+ * The size of the buffer is in bytes, but is it assumed that it is
+ * always ok to to read a complete multiple of 64 bits at the end
+ * of the block in case the end is no aligned to a natural boundary.
*
* Return: the block number (bitmap buffer scope) that was found
*/
-static u32 gfs2_bitfit(const u8 *buffer, unsigned int buflen, u32 goal,
- u8 old_state)
+static u32 gfs2_bitfit(const u8 *buf, const unsigned int len,
+ u32 goal, u8 state)
{
- const u8 *byte, *start, *end;
- int bit, startbit;
- u32 g1, g2, misaligned;
- unsigned long *plong;
- unsigned long lskipval;
+ u32 spoint = (goal << 1) & ((8*sizeof(u64)) - 1);
+ const __le64 *ptr = ((__le64 *)buf) + (goal >> 5);
+ const __le64 *end = (__le64 *)(buf + ALIGN(len, sizeof(u64)));
+ u64 tmp;
+ u64 mask = 0x5555555555555555ULL;
+ u32 bit;
- lskipval = (old_state & GFS2_BLKST_USED) ? LBITSKIP00 : LBITSKIP55;
- g1 = (goal / GFS2_NBBY);
- start = buffer + g1;
- byte = start;
- end = buffer + buflen;
- g2 = ALIGN(g1, sizeof(unsigned long));
- plong = (unsigned long *)(buffer + g2);
- startbit = bit = (goal % GFS2_NBBY) * GFS2_BIT_SIZE;
- misaligned = g2 - g1;
- if (!misaligned)
- goto ulong_aligned;
-/* parse the bitmap a byte at a time */
-misaligned:
- while (byte < end) {
- if (((*byte >> bit) & GFS2_BIT_MASK) == old_state) {
- return goal +
- (((byte - start) * GFS2_NBBY) +
- ((bit - startbit) >> 1));
- }
- bit += GFS2_BIT_SIZE;
- if (bit >= GFS2_NBBY * GFS2_BIT_SIZE) {
- bit = 0;
- byte++;
- misaligned--;
- if (!misaligned) {
- plong = (unsigned long *)byte;
- goto ulong_aligned;
- }
- }
- }
- return BFITNOENT;
+ BUG_ON(state > 3);
-/* parse the bitmap a unsigned long at a time */
-ulong_aligned:
- /* Stop at "end - 1" or else prefetch can go past the end and segfault.
- We could "if" it but we'd lose some of the performance gained.
- This way will only slow down searching the very last 4/8 bytes
- depending on architecture. I've experimented with several ways
- of writing this section such as using an else before the goto
- but this one seems to be the fastest. */
- while ((unsigned char *)plong < end - sizeof(unsigned long)) {
- prefetch(plong + 1);
- if (((*plong) & LBITMASK) != lskipval)
- break;
- plong++;
+ /* Mask off bits we don't care about at the start of the search */
+ mask <<= spoint;
+ tmp = gfs2_bit_search(ptr, mask, state);
+ ptr++;
+ while(tmp == 0 && ptr < end) {
+ tmp = gfs2_bit_search(ptr, 0x5555555555555555ULL, state);
+ ptr++;
}
- if ((unsigned char *)plong < end) {
- byte = (const u8 *)plong;
- misaligned += sizeof(unsigned long) - 1;
- goto misaligned;
- }
- return BFITNOENT;
+ /* Mask off any bits which are more than len bytes from the start */
+ if (ptr == end && (len & (sizeof(u64) - 1)))
+ tmp &= (((u64)~0) >> (64 - 8*(len & (sizeof(u64) - 1))));
+ /* Didn't find anything, so return */
+ if (tmp == 0)
+ return BFITNOENT;
+ ptr--;
+ bit = fls64(tmp);
+ bit--; /* fls64 always adds one to the bit count */
+ bit /= 2; /* two bits per entry in the bitmap */
+ return (((const unsigned char *)ptr - buf) * GFS2_NBBY) + bit;
}
/**
@@ -831,6 +840,58 @@
spin_unlock(&sdp->sd_rindex_spin);
}
+static void gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 offset,
+ const struct gfs2_bitmap *bi)
+{
+ struct super_block *sb = sdp->sd_vfs;
+ struct block_device *bdev = sb->s_bdev;
+ const unsigned int sects_per_blk = sdp->sd_sb.sb_bsize /
+ bdev_hardsect_size(sb->s_bdev);
+ u64 blk;
+ sector_t start = 0;
+ sector_t nr_sects = 0;
+ int rv;
+ unsigned int x;
+
+ for (x = 0; x < bi->bi_len; x++) {
+ const u8 *orig = bi->bi_bh->b_data + bi->bi_offset + x;
+ const u8 *clone = bi->bi_clone + bi->bi_offset + x;
+ u8 diff = ~(*orig | (*orig >> 1)) & (*clone | (*clone >> 1));
+ diff &= 0x55;
+ if (diff == 0)
+ continue;
+ blk = offset + ((bi->bi_start + x) * GFS2_NBBY);
+ blk *= sects_per_blk; /* convert to sectors */
+ while(diff) {
+ if (diff & 1) {
+ if (nr_sects == 0)
+ goto start_new_extent;
+ if ((start + nr_sects) != blk) {
+ rv = blkdev_issue_discard(bdev, start,
+ nr_sects, GFP_NOFS);
+ if (rv)
+ goto fail;
+ nr_sects = 0;
+start_new_extent:
+ start = blk;
+ }
+ nr_sects += sects_per_blk;
+ }
+ diff >>= 2;
+ blk += sects_per_blk;
+ }
+ }
+ if (nr_sects) {
+ rv = blkdev_issue_discard(bdev, start, nr_sects, GFP_NOFS);
+ if (rv)
+ goto fail;
+ }
+ return;
+fail:
+ fs_warn(sdp, "error %d on discard request, turning discards off for this filesystem", rv);
+ sdp->sd_args.ar_discard = 0;
+}
+
void gfs2_rgrp_repolish_clones(struct gfs2_rgrpd *rgd)
{
struct gfs2_sbd *sdp = rgd->rd_sbd;
@@ -841,6 +902,8 @@
struct gfs2_bitmap *bi = rgd->rd_bits + x;
if (!bi->bi_clone)
continue;
+ if (sdp->sd_args.ar_discard)
+ gfs2_rgrp_send_discards(sdp, rgd->rd_data0, bi);
memcpy(bi->bi_clone + bi->bi_offset,
bi->bi_bh->b_data + bi->bi_offset, bi->bi_len);
}
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index 141b781..601913e 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -15,7 +15,6 @@
#include <linux/crc32.h>
#include <linux/gfs2_ondisk.h>
#include <linux/bio.h>
-#include <linux/lm_interface.h>
#include "gfs2.h"
#include "incore.h"
@@ -339,7 +338,6 @@
struct gfs2_holder *t_gh)
{
struct gfs2_inode *ip;
- struct gfs2_holder ji_gh;
struct gfs2_jdesc *jd;
struct lfcc *lfcc;
LIST_HEAD(list);
@@ -387,7 +385,6 @@
gfs2_glock_dq_uninit(&lfcc->gh);
kfree(lfcc);
}
- gfs2_glock_dq_uninit(&ji_gh);
return error;
}
diff --git a/fs/gfs2/super.h b/fs/gfs2/super.h
index f6b8b00..91abdbe 100644
--- a/fs/gfs2/super.h
+++ b/fs/gfs2/super.h
@@ -14,7 +14,7 @@
#include <linux/dcache.h>
#include "incore.h"
-void gfs2_lm_unmount(struct gfs2_sbd *sdp);
+extern void gfs2_lm_unmount(struct gfs2_sbd *sdp);
static inline unsigned int gfs2_jindex_size(struct gfs2_sbd *sdp)
{
@@ -27,21 +27,23 @@
void gfs2_jindex_free(struct gfs2_sbd *sdp);
-struct gfs2_jdesc *gfs2_jdesc_find(struct gfs2_sbd *sdp, unsigned int jid);
-int gfs2_jdesc_check(struct gfs2_jdesc *jd);
+extern int gfs2_mount_args(struct gfs2_sbd *sdp, struct gfs2_args *args, char *data);
-int gfs2_lookup_in_master_dir(struct gfs2_sbd *sdp, char *filename,
- struct gfs2_inode **ipp);
+extern struct gfs2_jdesc *gfs2_jdesc_find(struct gfs2_sbd *sdp, unsigned int jid);
+extern int gfs2_jdesc_check(struct gfs2_jdesc *jd);
-int gfs2_make_fs_rw(struct gfs2_sbd *sdp);
+extern int gfs2_lookup_in_master_dir(struct gfs2_sbd *sdp, char *filename,
+ struct gfs2_inode **ipp);
-int gfs2_statfs_init(struct gfs2_sbd *sdp);
-void gfs2_statfs_change(struct gfs2_sbd *sdp,
- s64 total, s64 free, s64 dinodes);
-int gfs2_statfs_sync(struct gfs2_sbd *sdp);
+extern int gfs2_make_fs_rw(struct gfs2_sbd *sdp);
-int gfs2_freeze_fs(struct gfs2_sbd *sdp);
-void gfs2_unfreeze_fs(struct gfs2_sbd *sdp);
+extern int gfs2_statfs_init(struct gfs2_sbd *sdp);
+extern void gfs2_statfs_change(struct gfs2_sbd *sdp, s64 total, s64 free,
+ s64 dinodes);
+extern int gfs2_statfs_sync(struct gfs2_sbd *sdp);
+
+extern int gfs2_freeze_fs(struct gfs2_sbd *sdp);
+extern void gfs2_unfreeze_fs(struct gfs2_sbd *sdp);
extern struct file_system_type gfs2_fs_type;
extern struct file_system_type gfs2meta_fs_type;
diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c
index 26c1fa7..7655f502 100644
--- a/fs/gfs2/sys.c
+++ b/fs/gfs2/sys.c
@@ -14,9 +14,8 @@
#include <linux/buffer_head.h>
#include <linux/module.h>
#include <linux/kobject.h>
-#include <linux/gfs2_ondisk.h>
-#include <linux/lm_interface.h>
#include <asm/uaccess.h>
+#include <linux/gfs2_ondisk.h>
#include "gfs2.h"
#include "incore.h"
@@ -25,6 +24,7 @@
#include "glock.h"
#include "quota.h"
#include "util.h"
+#include "glops.h"
static ssize_t id_show(struct gfs2_sbd *sdp, char *buf)
{
@@ -37,6 +37,30 @@
return snprintf(buf, PAGE_SIZE, "%s\n", sdp->sd_fsname);
}
+static int gfs2_uuid_valid(const u8 *uuid)
+{
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ if (uuid[i])
+ return 1;
+ }
+ return 0;
+}
+
+static ssize_t uuid_show(struct gfs2_sbd *sdp, char *buf)
+{
+ const u8 *uuid = sdp->sd_sb.sb_uuid;
+ buf[0] = '\0';
+ if (!gfs2_uuid_valid(uuid))
+ return 0;
+ return snprintf(buf, PAGE_SIZE, "%02X%02X%02X%02X-%02X%02X-"
+ "%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
+ uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5],
+ uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11],
+ uuid[12], uuid[13], uuid[14], uuid[15]);
+}
+
static ssize_t freeze_show(struct gfs2_sbd *sdp, char *buf)
{
unsigned int count;
@@ -148,6 +172,46 @@
return len;
}
+static ssize_t demote_rq_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
+{
+ struct gfs2_glock *gl;
+ const struct gfs2_glock_operations *glops;
+ unsigned int glmode;
+ unsigned int gltype;
+ unsigned long long glnum;
+ char mode[16];
+ int rv;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ rv = sscanf(buf, "%u:%llu %15s", &gltype, &glnum,
+ mode);
+ if (rv != 3)
+ return -EINVAL;
+
+ if (strcmp(mode, "EX") == 0)
+ glmode = LM_ST_UNLOCKED;
+ else if ((strcmp(mode, "CW") == 0) || (strcmp(mode, "DF") == 0))
+ glmode = LM_ST_DEFERRED;
+ else if ((strcmp(mode, "PR") == 0) || (strcmp(mode, "SH") == 0))
+ glmode = LM_ST_SHARED;
+ else
+ return -EINVAL;
+
+ if (gltype > LM_TYPE_JOURNAL)
+ return -EINVAL;
+ glops = gfs2_glops_list[gltype];
+ if (glops == NULL)
+ return -EINVAL;
+ rv = gfs2_glock_get(sdp, glnum, glops, 0, &gl);
+ if (rv)
+ return rv;
+ gfs2_glock_cb(gl, glmode);
+ gfs2_glock_put(gl);
+ return len;
+}
+
struct gfs2_attr {
struct attribute attr;
ssize_t (*show)(struct gfs2_sbd *, char *);
@@ -159,22 +223,26 @@
GFS2_ATTR(id, 0444, id_show, NULL);
GFS2_ATTR(fsname, 0444, fsname_show, NULL);
+GFS2_ATTR(uuid, 0444, uuid_show, NULL);
GFS2_ATTR(freeze, 0644, freeze_show, freeze_store);
GFS2_ATTR(withdraw, 0644, withdraw_show, withdraw_store);
GFS2_ATTR(statfs_sync, 0200, NULL, statfs_sync_store);
GFS2_ATTR(quota_sync, 0200, NULL, quota_sync_store);
GFS2_ATTR(quota_refresh_user, 0200, NULL, quota_refresh_user_store);
GFS2_ATTR(quota_refresh_group, 0200, NULL, quota_refresh_group_store);
+GFS2_ATTR(demote_rq, 0200, NULL, demote_rq_store);
static struct attribute *gfs2_attrs[] = {
&gfs2_attr_id.attr,
&gfs2_attr_fsname.attr,
+ &gfs2_attr_uuid.attr,
&gfs2_attr_freeze.attr,
&gfs2_attr_withdraw.attr,
&gfs2_attr_statfs_sync.attr,
&gfs2_attr_quota_sync.attr,
&gfs2_attr_quota_refresh_user.attr,
&gfs2_attr_quota_refresh_group.attr,
+ &gfs2_attr_demote_rq.attr,
NULL,
};
@@ -224,14 +292,145 @@
LOCKSTRUCT_ATTR(jid, "%u\n");
LOCKSTRUCT_ATTR(first, "%u\n");
-LOCKSTRUCT_ATTR(lvb_size, "%u\n");
-LOCKSTRUCT_ATTR(flags, "%d\n");
static struct attribute *lockstruct_attrs[] = {
&lockstruct_attr_jid.attr,
&lockstruct_attr_first.attr,
- &lockstruct_attr_lvb_size.attr,
- &lockstruct_attr_flags.attr,
+ NULL,
+};
+
+/*
+ * lock_module. Originally from lock_dlm
+ */
+
+static ssize_t proto_name_show(struct gfs2_sbd *sdp, char *buf)
+{
+ const struct lm_lockops *ops = sdp->sd_lockstruct.ls_ops;
+ return sprintf(buf, "%s\n", ops->lm_proto_name);
+}
+
+static ssize_t block_show(struct gfs2_sbd *sdp, char *buf)
+{
+ struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+ ssize_t ret;
+ int val = 0;
+
+ if (test_bit(DFL_BLOCK_LOCKS, &ls->ls_flags))
+ val = 1;
+ ret = sprintf(buf, "%d\n", val);
+ return ret;
+}
+
+static ssize_t block_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
+{
+ struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+ ssize_t ret = len;
+ int val;
+
+ val = simple_strtol(buf, NULL, 0);
+
+ if (val == 1)
+ set_bit(DFL_BLOCK_LOCKS, &ls->ls_flags);
+ else if (val == 0) {
+ clear_bit(DFL_BLOCK_LOCKS, &ls->ls_flags);
+ smp_mb__after_clear_bit();
+ gfs2_glock_thaw(sdp);
+ } else {
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static ssize_t lkid_show(struct gfs2_sbd *sdp, char *buf)
+{
+ struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+ return sprintf(buf, "%u\n", ls->ls_id);
+}
+
+static ssize_t lkfirst_show(struct gfs2_sbd *sdp, char *buf)
+{
+ struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+ return sprintf(buf, "%d\n", ls->ls_first);
+}
+
+static ssize_t first_done_show(struct gfs2_sbd *sdp, char *buf)
+{
+ struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+ return sprintf(buf, "%d\n", ls->ls_first_done);
+}
+
+static ssize_t recover_show(struct gfs2_sbd *sdp, char *buf)
+{
+ struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+ return sprintf(buf, "%d\n", ls->ls_recover_jid);
+}
+
+static void gfs2_jdesc_make_dirty(struct gfs2_sbd *sdp, unsigned int jid)
+{
+ struct gfs2_jdesc *jd;
+
+ spin_lock(&sdp->sd_jindex_spin);
+ list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) {
+ if (jd->jd_jid != jid)
+ continue;
+ jd->jd_dirty = 1;
+ break;
+ }
+ spin_unlock(&sdp->sd_jindex_spin);
+}
+
+static ssize_t recover_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
+{
+ struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+ ls->ls_recover_jid = simple_strtol(buf, NULL, 0);
+ gfs2_jdesc_make_dirty(sdp, ls->ls_recover_jid);
+ if (sdp->sd_recoverd_process)
+ wake_up_process(sdp->sd_recoverd_process);
+ return len;
+}
+
+static ssize_t recover_done_show(struct gfs2_sbd *sdp, char *buf)
+{
+ struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+ return sprintf(buf, "%d\n", ls->ls_recover_jid_done);
+}
+
+static ssize_t recover_status_show(struct gfs2_sbd *sdp, char *buf)
+{
+ struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+ return sprintf(buf, "%d\n", ls->ls_recover_jid_status);
+}
+
+struct gdlm_attr {
+ struct attribute attr;
+ ssize_t (*show)(struct gfs2_sbd *sdp, char *);
+ ssize_t (*store)(struct gfs2_sbd *sdp, const char *, size_t);
+};
+
+#define GDLM_ATTR(_name,_mode,_show,_store) \
+static struct gdlm_attr gdlm_attr_##_name = __ATTR(_name,_mode,_show,_store)
+
+GDLM_ATTR(proto_name, 0444, proto_name_show, NULL);
+GDLM_ATTR(block, 0644, block_show, block_store);
+GDLM_ATTR(withdraw, 0644, withdraw_show, withdraw_store);
+GDLM_ATTR(id, 0444, lkid_show, NULL);
+GDLM_ATTR(first, 0444, lkfirst_show, NULL);
+GDLM_ATTR(first_done, 0444, first_done_show, NULL);
+GDLM_ATTR(recover, 0644, recover_show, recover_store);
+GDLM_ATTR(recover_done, 0444, recover_done_show, NULL);
+GDLM_ATTR(recover_status, 0444, recover_status_show, NULL);
+
+static struct attribute *lock_module_attrs[] = {
+ &gdlm_attr_proto_name.attr,
+ &gdlm_attr_block.attr,
+ &gdlm_attr_withdraw.attr,
+ &gdlm_attr_id.attr,
+ &lockstruct_attr_jid.attr,
+ &gdlm_attr_first.attr,
+ &gdlm_attr_first_done.attr,
+ &gdlm_attr_recover.attr,
+ &gdlm_attr_recover_done.attr,
+ &gdlm_attr_recover_status.attr,
NULL,
};
@@ -373,7 +572,6 @@
TUNE_ATTR(statfs_slow, 0);
TUNE_ATTR(new_files_jdata, 0);
TUNE_ATTR(quota_simul_sync, 1);
-TUNE_ATTR(quota_cache_secs, 1);
TUNE_ATTR(stall_secs, 1);
TUNE_ATTR(statfs_quantum, 1);
TUNE_ATTR_DAEMON(recoverd_secs, recoverd_process);
@@ -389,7 +587,6 @@
&tune_attr_complain_secs.attr,
&tune_attr_statfs_slow.attr,
&tune_attr_quota_simul_sync.attr,
- &tune_attr_quota_cache_secs.attr,
&tune_attr_stall_secs.attr,
&tune_attr_statfs_quantum.attr,
&tune_attr_recoverd_secs.attr,
@@ -414,6 +611,11 @@
.attrs = tune_attrs,
};
+static struct attribute_group lock_module_group = {
+ .name = "lock_module",
+ .attrs = lock_module_attrs,
+};
+
int gfs2_sys_fs_add(struct gfs2_sbd *sdp)
{
int error;
@@ -436,9 +638,15 @@
if (error)
goto fail_args;
+ error = sysfs_create_group(&sdp->sd_kobj, &lock_module_group);
+ if (error)
+ goto fail_tune;
+
kobject_uevent(&sdp->sd_kobj, KOBJ_ADD);
return 0;
+fail_tune:
+ sysfs_remove_group(&sdp->sd_kobj, &tune_group);
fail_args:
sysfs_remove_group(&sdp->sd_kobj, &args_group);
fail_lockstruct:
@@ -455,15 +663,27 @@
sysfs_remove_group(&sdp->sd_kobj, &tune_group);
sysfs_remove_group(&sdp->sd_kobj, &args_group);
sysfs_remove_group(&sdp->sd_kobj, &lockstruct_group);
+ sysfs_remove_group(&sdp->sd_kobj, &lock_module_group);
kobject_put(&sdp->sd_kobj);
}
+
static int gfs2_uevent(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env)
{
struct gfs2_sbd *sdp = container_of(kobj, struct gfs2_sbd, sd_kobj);
+ const u8 *uuid = sdp->sd_sb.sb_uuid;
+
add_uevent_var(env, "LOCKTABLE=%s", sdp->sd_table_name);
add_uevent_var(env, "LOCKPROTO=%s", sdp->sd_proto_name);
+ if (gfs2_uuid_valid(uuid)) {
+ add_uevent_var(env, "UUID=%02X%02X%02X%02X-%02X%02X-%02X%02X-"
+ "%02X%02X-%02X%02X%02X%02X%02X%02X",
+ uuid[0], uuid[1], uuid[2], uuid[3], uuid[4],
+ uuid[5], uuid[6], uuid[7], uuid[8], uuid[9],
+ uuid[10], uuid[11], uuid[12], uuid[13],
+ uuid[14], uuid[15]);
+ }
return 0;
}
diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c
index f677b8a..053752d 100644
--- a/fs/gfs2/trans.c
+++ b/fs/gfs2/trans.c
@@ -12,9 +12,8 @@
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/buffer_head.h>
-#include <linux/gfs2_ondisk.h>
#include <linux/kallsyms.h>
-#include <linux/lm_interface.h>
+#include <linux/gfs2_ondisk.h>
#include "gfs2.h"
#include "incore.h"
@@ -88,9 +87,11 @@
if (!tr->tr_touched) {
gfs2_log_release(sdp, tr->tr_reserved);
- gfs2_glock_dq(&tr->tr_t_gh);
- gfs2_holder_uninit(&tr->tr_t_gh);
- kfree(tr);
+ if (tr->tr_t_gh.gh_gl) {
+ gfs2_glock_dq(&tr->tr_t_gh);
+ gfs2_holder_uninit(&tr->tr_t_gh);
+ kfree(tr);
+ }
return;
}
@@ -106,9 +107,11 @@
}
gfs2_log_commit(sdp, tr);
- gfs2_glock_dq(&tr->tr_t_gh);
- gfs2_holder_uninit(&tr->tr_t_gh);
- kfree(tr);
+ if (tr->tr_t_gh.gh_gl) {
+ gfs2_glock_dq(&tr->tr_t_gh);
+ gfs2_holder_uninit(&tr->tr_t_gh);
+ kfree(tr);
+ }
if (sdp->sd_vfs->s_flags & MS_SYNCHRONOUS)
gfs2_log_flush(sdp, NULL);
diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c
index 374f50e..9d12b11 100644
--- a/fs/gfs2/util.c
+++ b/fs/gfs2/util.c
@@ -13,7 +13,6 @@
#include <linux/buffer_head.h>
#include <linux/crc32.h>
#include <linux/gfs2_ondisk.h>
-#include <linux/lm_interface.h>
#include <asm/uaccess.h>
#include "gfs2.h"
@@ -35,6 +34,8 @@
int gfs2_lm_withdraw(struct gfs2_sbd *sdp, char *fmt, ...)
{
+ struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+ const struct lm_lockops *lm = ls->ls_ops;
va_list args;
if (test_and_set_bit(SDF_SHUTDOWN, &sdp->sd_flags))
@@ -47,8 +48,12 @@
fs_err(sdp, "about to withdraw this file system\n");
BUG_ON(sdp->sd_args.ar_debug);
- fs_err(sdp, "telling LM to withdraw\n");
- gfs2_withdraw_lockproto(&sdp->sd_lockstruct);
+ kobject_uevent(&sdp->sd_kobj, KOBJ_OFFLINE);
+
+ if (lm->lm_unmount) {
+ fs_err(sdp, "telling LM to unmount\n");
+ lm->lm_unmount(sdp);
+ }
fs_err(sdp, "withdrawn\n");
dump_stack();
diff --git a/fs/inode.c b/fs/inode.c
index 826fb0b..643ac43 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -17,6 +17,7 @@
#include <linux/hash.h>
#include <linux/swap.h>
#include <linux/security.h>
+#include <linux/ima.h>
#include <linux/pagemap.h>
#include <linux/cdev.h>
#include <linux/bootmem.h>
@@ -147,13 +148,13 @@
inode->i_cdev = NULL;
inode->i_rdev = 0;
inode->dirtied_when = 0;
- if (security_inode_alloc(inode)) {
- if (inode->i_sb->s_op->destroy_inode)
- inode->i_sb->s_op->destroy_inode(inode);
- else
- kmem_cache_free(inode_cachep, (inode));
- return NULL;
- }
+
+ if (security_inode_alloc(inode))
+ goto out_free_inode;
+
+ /* allocate and initialize an i_integrity */
+ if (ima_inode_alloc(inode))
+ goto out_free_security;
spin_lock_init(&inode->i_lock);
lockdep_set_class(&inode->i_lock, &sb->s_type->i_lock_key);
@@ -189,6 +190,15 @@
inode->i_mapping = mapping;
return inode;
+
+out_free_security:
+ security_inode_free(inode);
+out_free_inode:
+ if (inode->i_sb->s_op->destroy_inode)
+ inode->i_sb->s_op->destroy_inode(inode);
+ else
+ kmem_cache_free(inode_cachep, (inode));
+ return NULL;
}
EXPORT_SYMBOL(inode_init_always);
@@ -1290,6 +1300,40 @@
}
EXPORT_SYMBOL(bmap);
+/*
+ * With relative atime, only update atime if the previous atime is
+ * earlier than either the ctime or mtime or if at least a day has
+ * passed since the last atime update.
+ */
+static int relatime_need_update(struct vfsmount *mnt, struct inode *inode,
+ struct timespec now)
+{
+
+ if (!(mnt->mnt_flags & MNT_RELATIME))
+ return 1;
+ /*
+ * Is mtime younger than atime? If yes, update atime:
+ */
+ if (timespec_compare(&inode->i_mtime, &inode->i_atime) >= 0)
+ return 1;
+ /*
+ * Is ctime younger than atime? If yes, update atime:
+ */
+ if (timespec_compare(&inode->i_ctime, &inode->i_atime) >= 0)
+ return 1;
+
+ /*
+ * Is the previous atime value older than a day? If yes,
+ * update atime:
+ */
+ if ((long)(now.tv_sec - inode->i_atime.tv_sec) >= 24*60*60)
+ return 1;
+ /*
+ * Good, we can skip the atime update:
+ */
+ return 0;
+}
+
/**
* touch_atime - update the access time
* @mnt: mount the inode is accessed on
@@ -1317,17 +1361,12 @@
goto out;
if ((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode))
goto out;
- if (mnt->mnt_flags & MNT_RELATIME) {
- /*
- * With relative atime, only update atime if the previous
- * atime is earlier than either the ctime or mtime.
- */
- if (timespec_compare(&inode->i_mtime, &inode->i_atime) < 0 &&
- timespec_compare(&inode->i_ctime, &inode->i_atime) < 0)
- goto out;
- }
now = current_fs_time(inode->i_sb);
+
+ if (!relatime_need_update(mnt, inode, now))
+ goto out;
+
if (timespec_equal(&inode->i_atime, &now))
goto out;
diff --git a/fs/namei.c b/fs/namei.c
index bbc15c2..1993176 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -24,6 +24,7 @@
#include <linux/fsnotify.h>
#include <linux/personality.h>
#include <linux/security.h>
+#include <linux/ima.h>
#include <linux/syscalls.h>
#include <linux/mount.h>
#include <linux/audit.h>
@@ -850,6 +851,8 @@
if (err == -EAGAIN)
err = inode_permission(nd->path.dentry->d_inode,
MAY_EXEC);
+ if (!err)
+ err = ima_path_check(&nd->path, MAY_EXEC);
if (err)
break;
@@ -1509,6 +1512,11 @@
error = inode_permission(inode, acc_mode);
if (error)
return error;
+
+ error = ima_path_check(path,
+ acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));
+ if (error)
+ return error;
/*
* An append-only file must be opened in append mode for writing.
*/
diff --git a/fs/namespace.c b/fs/namespace.c
index 06f8e63..f0e7530 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -780,6 +780,7 @@
{ MNT_NOATIME, ",noatime" },
{ MNT_NODIRATIME, ",nodiratime" },
{ MNT_RELATIME, ",relatime" },
+ { MNT_STRICTATIME, ",strictatime" },
{ 0, NULL }
};
const struct proc_fs_info *fs_infop;
@@ -1919,6 +1920,9 @@
if (data_page)
((char *)data_page)[PAGE_SIZE - 1] = 0;
+ /* Default to relatime */
+ mnt_flags |= MNT_RELATIME;
+
/* Separate the per-mountpoint flags */
if (flags & MS_NOSUID)
mnt_flags |= MNT_NOSUID;
@@ -1930,13 +1934,14 @@
mnt_flags |= MNT_NOATIME;
if (flags & MS_NODIRATIME)
mnt_flags |= MNT_NODIRATIME;
- if (flags & MS_RELATIME)
- mnt_flags |= MNT_RELATIME;
+ if (flags & MS_STRICTATIME)
+ mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME);
if (flags & MS_RDONLY)
mnt_flags |= MNT_READONLY;
flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE |
- MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT);
+ MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
+ MS_STRICTATIME);
/* ... and get the mountpoint */
retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
diff --git a/include/asm-frv/ide.h b/include/asm-frv/ide.h
deleted file mode 100644
index 3610766..0000000
--- a/include/asm-frv/ide.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* ide.h: FRV IDE declarations
- *
- * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.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 _ASM_IDE_H
-#define _ASM_IDE_H
-
-#ifdef __KERNEL__
-
-#include <asm/setup.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#include <asm-generic/ide_iops.h>
-
-#endif /* __KERNEL__ */
-#endif /* _ASM_IDE_H */
diff --git a/include/asm-mn10300/ide.h b/include/asm-mn10300/ide.h
deleted file mode 100644
index 6adcdd9..0000000
--- a/include/asm-mn10300/ide.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/* MN10300 Arch-specific IDE code
- *
- * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- * - Derived from include/asm-i386/ide.h
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public Licence
- * as published by the Free Software Foundation; either version
- * 2 of the Licence, or (at your option) any later version.
- */
-
-#ifndef _ASM_IDE_H
-#define _ASM_IDE_H
-
-#ifdef __KERNEL__
-
-#include <asm/intctl-regs.h>
-
-#undef SUPPORT_SLOW_DATA_PORTS
-#define SUPPORT_SLOW_DATA_PORTS 0
-
-#undef SUPPORT_VLB_SYNC
-#define SUPPORT_VLB_SYNC 0
-
-/*
- * some bits needed for parts of the IDE subsystem to compile
- */
-#define __ide_mm_insw(port, addr, n) \
- insw((unsigned long) (port), (addr), (n))
-#define __ide_mm_insl(port, addr, n) \
- insl((unsigned long) (port), (addr), (n))
-#define __ide_mm_outsw(port, addr, n) \
- outsw((unsigned long) (port), (addr), (n))
-#define __ide_mm_outsl(port, addr, n) \
- outsl((unsigned long) (port), (addr), (n))
-
-#endif /* __KERNEL__ */
-#endif /* _ASM_IDE_H */
diff --git a/include/asm-mn10300/pci.h b/include/asm-mn10300/pci.h
index cd9cc5c..0517b45 100644
--- a/include/asm-mn10300/pci.h
+++ b/include/asm-mn10300/pci.h
@@ -121,4 +121,9 @@
#define pcibios_scan_all_fns(a, b) 0
+static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
+{
+ return channel ? 15 : 14;
+}
+
#endif /* _ASM_PCI_H */
diff --git a/include/crypto/aes.h b/include/crypto/aes.h
index 656a4c6..7524ba3 100644
--- a/include/crypto/aes.h
+++ b/include/crypto/aes.h
@@ -17,10 +17,14 @@
#define AES_MAX_KEYLENGTH (15 * 16)
#define AES_MAX_KEYLENGTH_U32 (AES_MAX_KEYLENGTH / sizeof(u32))
+/*
+ * Please ensure that the first two fields are 16-byte aligned
+ * relative to the start of the structure, i.e., don't move them!
+ */
struct crypto_aes_ctx {
- u32 key_length;
u32 key_enc[AES_MAX_KEYLENGTH_U32];
u32 key_dec[AES_MAX_KEYLENGTH_U32];
+ u32 key_length;
};
extern const u32 crypto_ft_tab[4][256];
diff --git a/include/crypto/compress.h b/include/crypto/compress.h
new file mode 100644
index 0000000..86163ef
--- /dev/null
+++ b/include/crypto/compress.h
@@ -0,0 +1,145 @@
+/*
+ * Compress: Compression algorithms under the cryptographic API.
+ *
+ * Copyright 2008 Sony 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; version 2 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _CRYPTO_COMPRESS_H
+#define _CRYPTO_COMPRESS_H
+
+#include <linux/crypto.h>
+
+
+struct comp_request {
+ const void *next_in; /* next input byte */
+ void *next_out; /* next output byte */
+ unsigned int avail_in; /* bytes available at next_in */
+ unsigned int avail_out; /* bytes available at next_out */
+};
+
+enum zlib_comp_params {
+ ZLIB_COMP_LEVEL = 1, /* e.g. Z_DEFAULT_COMPRESSION */
+ ZLIB_COMP_METHOD, /* e.g. Z_DEFLATED */
+ ZLIB_COMP_WINDOWBITS, /* e.g. MAX_WBITS */
+ ZLIB_COMP_MEMLEVEL, /* e.g. DEF_MEM_LEVEL */
+ ZLIB_COMP_STRATEGY, /* e.g. Z_DEFAULT_STRATEGY */
+ __ZLIB_COMP_MAX,
+};
+
+#define ZLIB_COMP_MAX (__ZLIB_COMP_MAX - 1)
+
+
+enum zlib_decomp_params {
+ ZLIB_DECOMP_WINDOWBITS = 1, /* e.g. DEF_WBITS */
+ __ZLIB_DECOMP_MAX,
+};
+
+#define ZLIB_DECOMP_MAX (__ZLIB_DECOMP_MAX - 1)
+
+
+struct crypto_pcomp {
+ struct crypto_tfm base;
+};
+
+struct pcomp_alg {
+ int (*compress_setup)(struct crypto_pcomp *tfm, void *params,
+ unsigned int len);
+ int (*compress_init)(struct crypto_pcomp *tfm);
+ int (*compress_update)(struct crypto_pcomp *tfm,
+ struct comp_request *req);
+ int (*compress_final)(struct crypto_pcomp *tfm,
+ struct comp_request *req);
+ int (*decompress_setup)(struct crypto_pcomp *tfm, void *params,
+ unsigned int len);
+ int (*decompress_init)(struct crypto_pcomp *tfm);
+ int (*decompress_update)(struct crypto_pcomp *tfm,
+ struct comp_request *req);
+ int (*decompress_final)(struct crypto_pcomp *tfm,
+ struct comp_request *req);
+
+ struct crypto_alg base;
+};
+
+extern struct crypto_pcomp *crypto_alloc_pcomp(const char *alg_name, u32 type,
+ u32 mask);
+
+static inline struct crypto_tfm *crypto_pcomp_tfm(struct crypto_pcomp *tfm)
+{
+ return &tfm->base;
+}
+
+static inline void crypto_free_pcomp(struct crypto_pcomp *tfm)
+{
+ crypto_destroy_tfm(tfm, crypto_pcomp_tfm(tfm));
+}
+
+static inline struct pcomp_alg *__crypto_pcomp_alg(struct crypto_alg *alg)
+{
+ return container_of(alg, struct pcomp_alg, base);
+}
+
+static inline struct pcomp_alg *crypto_pcomp_alg(struct crypto_pcomp *tfm)
+{
+ return __crypto_pcomp_alg(crypto_pcomp_tfm(tfm)->__crt_alg);
+}
+
+static inline int crypto_compress_setup(struct crypto_pcomp *tfm,
+ void *params, unsigned int len)
+{
+ return crypto_pcomp_alg(tfm)->compress_setup(tfm, params, len);
+}
+
+static inline int crypto_compress_init(struct crypto_pcomp *tfm)
+{
+ return crypto_pcomp_alg(tfm)->compress_init(tfm);
+}
+
+static inline int crypto_compress_update(struct crypto_pcomp *tfm,
+ struct comp_request *req)
+{
+ return crypto_pcomp_alg(tfm)->compress_update(tfm, req);
+}
+
+static inline int crypto_compress_final(struct crypto_pcomp *tfm,
+ struct comp_request *req)
+{
+ return crypto_pcomp_alg(tfm)->compress_final(tfm, req);
+}
+
+static inline int crypto_decompress_setup(struct crypto_pcomp *tfm,
+ void *params, unsigned int len)
+{
+ return crypto_pcomp_alg(tfm)->decompress_setup(tfm, params, len);
+}
+
+static inline int crypto_decompress_init(struct crypto_pcomp *tfm)
+{
+ return crypto_pcomp_alg(tfm)->decompress_init(tfm);
+}
+
+static inline int crypto_decompress_update(struct crypto_pcomp *tfm,
+ struct comp_request *req)
+{
+ return crypto_pcomp_alg(tfm)->decompress_update(tfm, req);
+}
+
+static inline int crypto_decompress_final(struct crypto_pcomp *tfm,
+ struct comp_request *req)
+{
+ return crypto_pcomp_alg(tfm)->decompress_final(tfm, req);
+}
+
+#endif /* _CRYPTO_COMPRESS_H */
diff --git a/include/crypto/cryptd.h b/include/crypto/cryptd.h
new file mode 100644
index 0000000..55fa7bb
--- /dev/null
+++ b/include/crypto/cryptd.h
@@ -0,0 +1,27 @@
+/*
+ * Software async crypto daemon
+ */
+
+#ifndef _CRYPTO_CRYPT_H
+#define _CRYPTO_CRYPT_H
+
+#include <linux/crypto.h>
+#include <linux/kernel.h>
+
+struct cryptd_ablkcipher {
+ struct crypto_ablkcipher base;
+};
+
+static inline struct cryptd_ablkcipher *__cryptd_ablkcipher_cast(
+ struct crypto_ablkcipher *tfm)
+{
+ return (struct cryptd_ablkcipher *)tfm;
+}
+
+/* alg_name should be algorithm to be cryptd-ed */
+struct cryptd_ablkcipher *cryptd_alloc_ablkcipher(const char *alg_name,
+ u32 type, u32 mask);
+struct crypto_blkcipher *cryptd_ablkcipher_child(struct cryptd_ablkcipher *tfm);
+void cryptd_free_ablkcipher(struct cryptd_ablkcipher *tfm);
+
+#endif
diff --git a/include/crypto/crypto_wq.h b/include/crypto/crypto_wq.h
new file mode 100644
index 0000000..a7d252d
--- /dev/null
+++ b/include/crypto/crypto_wq.h
@@ -0,0 +1,7 @@
+#ifndef CRYPTO_WQ_H
+#define CRYPTO_WQ_H
+
+#include <linux/workqueue.h>
+
+extern struct workqueue_struct *kcrypto_wq;
+#endif
diff --git a/include/crypto/hash.h b/include/crypto/hash.h
index d797e11..d56bb71 100644
--- a/include/crypto/hash.h
+++ b/include/crypto/hash.h
@@ -231,6 +231,11 @@
return crypto_tfm_alg_alignmask(crypto_shash_tfm(tfm));
}
+static inline unsigned int crypto_shash_blocksize(struct crypto_shash *tfm)
+{
+ return crypto_tfm_alg_blocksize(crypto_shash_tfm(tfm));
+}
+
static inline struct shash_alg *__crypto_shash_alg(struct crypto_alg *alg)
{
return container_of(alg, struct shash_alg, base);
diff --git a/include/crypto/internal/compress.h b/include/crypto/internal/compress.h
new file mode 100644
index 0000000..178a888
--- /dev/null
+++ b/include/crypto/internal/compress.h
@@ -0,0 +1,28 @@
+/*
+ * Compress: Compression algorithms under the cryptographic API.
+ *
+ * Copyright 2008 Sony 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; version 2 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _CRYPTO_INTERNAL_COMPRESS_H
+#define _CRYPTO_INTERNAL_COMPRESS_H
+
+#include <crypto/compress.h>
+
+extern int crypto_register_pcomp(struct pcomp_alg *alg);
+extern int crypto_unregister_pcomp(struct pcomp_alg *alg);
+
+#endif /* _CRYPTO_INTERNAL_COMPRESS_H */
diff --git a/include/linux/ata.h b/include/linux/ata.h
index 9a061ac..68132c4 100644
--- a/include/linux/ata.h
+++ b/include/linux/ata.h
@@ -244,8 +244,6 @@
ATA_CMD_MEDIA_UNLOCK = 0xDF,
/* marked obsolete in the ATA/ATAPI-7 spec */
ATA_CMD_RESTORE = 0x10,
- /* EXABYTE specific */
- ATA_EXABYTE_ENABLE_NEST = 0xF0,
/* READ_LOG_EXT pages */
ATA_LOG_SATA_NCQ = 0x10,
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 67e5dbf..4fa2810 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -36,7 +36,8 @@
* 1500 - 1599 kernel LSPP events
* 1600 - 1699 kernel crypto events
* 1700 - 1799 kernel anomaly records
- * 1800 - 1999 future kernel use (maybe integrity labels and related events)
+ * 1800 - 1899 kernel integrity events
+ * 1900 - 1999 future kernel use
* 2000 is for otherwise unclassified kernel audit messages (legacy)
* 2001 - 2099 unused (kernel)
* 2100 - 2199 user space anomaly records
@@ -125,6 +126,12 @@
#define AUDIT_LAST_KERN_ANOM_MSG 1799
#define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */
#define AUDIT_ANOM_ABEND 1701 /* Process ended abnormally */
+#define AUDIT_INTEGRITY_DATA 1800 /* Data integrity verification */
+#define AUDIT_INTEGRITY_METADATA 1801 /* Metadata integrity verification */
+#define AUDIT_INTEGRITY_STATUS 1802 /* Integrity enable status */
+#define AUDIT_INTEGRITY_HASH 1803 /* Integrity HASH type */
+#define AUDIT_INTEGRITY_PCR 1804 /* PCR invalidation msgs */
+#define AUDIT_INTEGRITY_RULE 1805 /* policy rule */
#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
diff --git a/include/linux/crypto.h b/include/linux/crypto.h
index 1f2e902..ec29fa2 100644
--- a/include/linux/crypto.h
+++ b/include/linux/crypto.h
@@ -40,6 +40,7 @@
#define CRYPTO_ALG_TYPE_SHASH 0x00000009
#define CRYPTO_ALG_TYPE_AHASH 0x0000000a
#define CRYPTO_ALG_TYPE_RNG 0x0000000c
+#define CRYPTO_ALG_TYPE_PCOMPRESS 0x0000000f
#define CRYPTO_ALG_TYPE_HASH_MASK 0x0000000e
#define CRYPTO_ALG_TYPE_AHASH_MASK 0x0000000c
@@ -548,9 +549,6 @@
* Transform user interface.
*/
-struct crypto_tfm *crypto_alloc_tfm(const char *alg_name,
- const struct crypto_type *frontend,
- u32 type, u32 mask);
struct crypto_tfm *crypto_alloc_base(const char *alg_name, u32 type, u32 mask);
void crypto_destroy_tfm(void *mem, struct crypto_tfm *tfm);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 92734c0..5bc81c4 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -141,6 +141,7 @@
#define MS_RELATIME (1<<21) /* Update atime relative to mtime/ctime. */
#define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */
#define MS_I_VERSION (1<<23) /* Update inode I_version field */
+#define MS_STRICTATIME (1<<24) /* Always perform atime updates */
#define MS_ACTIVE (1<<30)
#define MS_NOUSER (1<<31)
diff --git a/include/linux/ide.h b/include/linux/ide.h
index 25087ae..854eba8 100644
--- a/include/linux/ide.h
+++ b/include/linux/ide.h
@@ -26,7 +26,7 @@
#include <asm/io.h>
#include <asm/mutex.h>
-#if defined(CONFIG_CRIS) || defined(CONFIG_FRV)
+#if defined(CONFIG_CRIS) || defined(CONFIG_FRV) || defined(CONFIG_MN10300)
# define SUPPORT_VLB_SYNC 0
#else
# define SUPPORT_VLB_SYNC 1
@@ -193,24 +193,8 @@
hw->io_ports.ctl_addr = ctl_addr;
}
-/* for IDE PCI controllers in legacy mode, temporary */
-static inline int __ide_default_irq(unsigned long base)
-{
- switch (base) {
-#ifdef CONFIG_IA64
- case 0x1f0: return isa_irq_to_vector(14);
- case 0x170: return isa_irq_to_vector(15);
-#else
- case 0x1f0: return 14;
- case 0x170: return 15;
-#endif
- }
- return 0;
-}
-
-#if defined(CONFIG_ARM) || defined(CONFIG_FRV) || defined(CONFIG_M68K) || \
- defined(CONFIG_MIPS) || defined(CONFIG_MN10300) || defined(CONFIG_PARISC) \
- || defined(CONFIG_PPC) || defined(CONFIG_SPARC) || defined(CONFIG_SPARC64)
+#if defined(CONFIG_ARM) || defined(CONFIG_M68K) || defined(CONFIG_MIPS) || \
+ defined(CONFIG_PARISC) || defined(CONFIG_PPC) || defined(CONFIG_SPARC)
#include <asm/ide.h>
#else
#include <asm-generic/ide_iops.h>
@@ -866,7 +850,7 @@
ide_hwif_t *ports[MAX_HOST_PORTS + 1];
unsigned int n_ports;
struct device *dev[2];
- unsigned int (*init_chipset)(struct pci_dev *);
+ int (*init_chipset)(struct pci_dev *);
irq_handler_t irq_handler;
unsigned long host_flags;
void *host_priv;
@@ -1146,11 +1130,14 @@
extern int ide_vlb_clk;
extern int ide_pci_clk;
-extern int ide_end_request (ide_drive_t *drive, int uptodate, int nrsecs);
-int ide_end_dequeued_request(ide_drive_t *drive, struct request *rq,
- int uptodate, int nr_sectors);
+int ide_end_request(ide_drive_t *, int, int);
+int ide_end_dequeued_request(ide_drive_t *, struct request *, int, int);
+void ide_kill_rq(ide_drive_t *, struct request *);
-extern void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry);
+void __ide_set_handler(ide_drive_t *, ide_handler_t *, unsigned int,
+ ide_expiry_t *);
+void ide_set_handler(ide_drive_t *, ide_handler_t *, unsigned int,
+ ide_expiry_t *);
void ide_execute_command(ide_drive_t *, u8, ide_handler_t *, unsigned int,
ide_expiry_t *);
@@ -1169,13 +1156,14 @@
int ide_wait_stat(ide_startstop_t *, ide_drive_t *, u8, u8, unsigned long);
+ide_startstop_t ide_do_park_unpark(ide_drive_t *, struct request *);
+ide_startstop_t ide_do_devset(ide_drive_t *, struct request *);
+
extern ide_startstop_t ide_do_reset (ide_drive_t *);
extern int ide_devset_execute(ide_drive_t *drive,
const struct ide_devset *setting, int arg);
-extern void ide_do_drive_cmd(ide_drive_t *, struct request *);
-
extern void ide_end_drive_cmd(ide_drive_t *, u8, u8);
void ide_tf_dump(const char *, struct ide_taskfile *);
@@ -1200,10 +1188,6 @@
u8 ide_read_error(ide_drive_t *);
void ide_read_bcount_and_ireason(ide_drive_t *, u16 *, u8 *);
-extern int drive_is_ready(ide_drive_t *);
-
-void ide_pktcmd_tf_load(ide_drive_t *, u32, u16, u8);
-
int ide_check_atapi_device(ide_drive_t *, const char *);
void ide_init_pc(struct ide_atapi_pc *);
@@ -1251,6 +1235,8 @@
int ide_taskfile_ioctl(ide_drive_t *, unsigned int, unsigned long);
+int ide_dev_read_id(ide_drive_t *, u8, u16 *);
+
extern int ide_driveid_update(ide_drive_t *);
extern int ide_config_drive_speed(ide_drive_t *, u8);
extern u8 eighty_ninty_three (ide_drive_t *);
@@ -1280,7 +1266,7 @@
return 0;
}
-void ide_pci_setup_ports(struct pci_dev *, const struct ide_port_info *, int,
+void ide_pci_setup_ports(struct pci_dev *, const struct ide_port_info *,
hw_regs_t *, hw_regs_t **);
void ide_setup_pci_noise(struct pci_dev *, const struct ide_port_info *);
@@ -1349,10 +1335,6 @@
IDE_HFLAG_ERROR_STOPS_FIFO = (1 << 19),
/* serialize ports */
IDE_HFLAG_SERIALIZE = (1 << 20),
- /* use legacy IRQs */
- IDE_HFLAG_LEGACY_IRQS = (1 << 21),
- /* force use of legacy IRQs */
- IDE_HFLAG_FORCE_LEGACY_IRQS = (1 << 22),
/* host is TRM290 */
IDE_HFLAG_TRM290 = (1 << 23),
/* use 32-bit I/O ops */
@@ -1380,7 +1362,7 @@
struct ide_port_info {
char *name;
- unsigned int (*init_chipset)(struct pci_dev *);
+ int (*init_chipset)(struct pci_dev *);
void (*init_iops)(ide_hwif_t *);
void (*init_hwif)(ide_hwif_t *);
int (*init_dma)(ide_hwif_t *,
@@ -1471,6 +1453,7 @@
void ide_dma_lost_irq(ide_drive_t *);
void ide_dma_timeout(ide_drive_t *);
+ide_startstop_t ide_dma_timeout_retry(ide_drive_t *, int);
#else
static inline int ide_id_dma_bug(ide_drive_t *drive) { return 0; }
@@ -1482,21 +1465,24 @@
static inline void ide_dma_verbose(ide_drive_t *drive) { ; }
static inline int ide_set_dma(ide_drive_t *drive) { return 1; }
static inline void ide_check_dma_crc(ide_drive_t *drive) { ; }
+static inline ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) { return ide_stopped; }
static inline void ide_release_dma_engine(ide_hwif_t *hwif) { ; }
#endif /* CONFIG_BLK_DEV_IDEDMA */
#ifdef CONFIG_BLK_DEV_IDEACPI
+int ide_acpi_init(void);
extern int ide_acpi_exec_tfs(ide_drive_t *drive);
extern void ide_acpi_get_timing(ide_hwif_t *hwif);
extern void ide_acpi_push_timing(ide_hwif_t *hwif);
-extern void ide_acpi_init(ide_hwif_t *hwif);
+void ide_acpi_init_port(ide_hwif_t *);
void ide_acpi_port_init_devices(ide_hwif_t *);
extern void ide_acpi_set_state(ide_hwif_t *hwif, int on);
#else
+static inline int ide_acpi_init(void) { return 0; }
static inline int ide_acpi_exec_tfs(ide_drive_t *drive) { return 0; }
static inline void ide_acpi_get_timing(ide_hwif_t *hwif) { ; }
static inline void ide_acpi_push_timing(ide_hwif_t *hwif) { ; }
-static inline void ide_acpi_init(ide_hwif_t *hwif) { ; }
+static inline void ide_acpi_init_port(ide_hwif_t *hwif) { ; }
static inline void ide_acpi_port_init_devices(ide_hwif_t *hwif) { ; }
static inline void ide_acpi_set_state(ide_hwif_t *hwif, int on) {}
#endif
@@ -1530,9 +1516,7 @@
hwif->hwif_data = data;
}
-const char *ide_xfer_verbose(u8 mode);
extern void ide_toggle_bounce(ide_drive_t *drive, int on);
-extern int ide_set_xfer_rate(ide_drive_t *drive, u8 rate);
u64 ide_get_lba_addr(struct ide_taskfile *, int);
u8 ide_dump_status(ide_drive_t *, const char *, u8);
@@ -1571,14 +1555,18 @@
struct ide_timing *, unsigned int);
int ide_timing_compute(ide_drive_t *, u8, struct ide_timing *, int, int);
+#ifdef CONFIG_IDE_XFER_MODE
int ide_scan_pio_blacklist(char *);
-
+const char *ide_xfer_verbose(u8);
u8 ide_get_best_pio_mode(ide_drive_t *, u8, u8);
-
int ide_set_pio_mode(ide_drive_t *, u8);
int ide_set_dma_mode(ide_drive_t *, u8);
-
void ide_set_pio(ide_drive_t *, u8);
+int ide_set_xfer_rate(ide_drive_t *, u8);
+#else
+static inline void ide_set_pio(ide_drive_t *drive, u8 pio) { ; }
+static inline int ide_set_xfer_rate(ide_drive_t *drive, u8 rate) { return -1; }
+#endif
static inline void ide_set_max_pio(ide_drive_t *drive)
{
@@ -1611,6 +1599,10 @@
#define ide_port_for_each_dev(i, dev, port) \
for ((i) = 0; ((dev) = (port)->devices[i]) || (i) < MAX_DRIVES; (i)++)
+#define ide_port_for_each_present_dev(i, dev, port) \
+ for ((i) = 0; ((dev) = (port)->devices[i]) || (i) < MAX_DRIVES; (i)++) \
+ if ((dev)->dev_flags & IDE_DFLAG_PRESENT)
+
#define ide_host_for_each_port(i, port, host) \
for ((i) = 0; ((port) = (host)->ports[i]) || (i) < MAX_HOST_PORTS; (i)++)
diff --git a/include/linux/ima.h b/include/linux/ima.h
new file mode 100644
index 0000000..0e2aa45
--- /dev/null
+++ b/include/linux/ima.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ * Author: Mimi Zohar <zohar@us.ibm.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, version 2 of the License.
+ */
+
+#ifndef _LINUX_IMA_H
+#define _LINUX_IMA_H
+
+#include <linux/fs.h>
+struct linux_binprm;
+
+#ifdef CONFIG_IMA
+extern int ima_bprm_check(struct linux_binprm *bprm);
+extern int ima_inode_alloc(struct inode *inode);
+extern void ima_inode_free(struct inode *inode);
+extern int ima_path_check(struct path *path, int mask);
+extern void ima_file_free(struct file *file);
+extern int ima_file_mmap(struct file *file, unsigned long prot);
+extern void ima_shm_check(struct file *file);
+
+#else
+static inline int ima_bprm_check(struct linux_binprm *bprm)
+{
+ return 0;
+}
+
+static inline int ima_inode_alloc(struct inode *inode)
+{
+ return 0;
+}
+
+static inline void ima_inode_free(struct inode *inode)
+{
+ return;
+}
+
+static inline int ima_path_check(struct path *path, int mask)
+{
+ return 0;
+}
+
+static inline void ima_file_free(struct file *file)
+{
+ return;
+}
+
+static inline int ima_file_mmap(struct file *file, unsigned long prot)
+{
+ return 0;
+}
+
+static inline void ima_shm_check(struct file *file)
+{
+ return;
+}
+#endif /* CONFIG_IMA_H */
+#endif /* _LINUX_IMA_H */
diff --git a/include/linux/input.h b/include/linux/input.h
index 1249a0c..6b28048 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -661,6 +661,7 @@
#define SW_DOCK 0x05 /* set = plugged into dock */
#define SW_LINEOUT_INSERT 0x06 /* set = inserted */
#define SW_JACK_PHYSICAL_INSERT 0x07 /* set = mechanical switch set */
+#define SW_VIDEOOUT_INSERT 0x08 /* set = inserted */
#define SW_MAX 0x0f
#define SW_CNT (SW_MAX+1)
diff --git a/include/linux/lm_interface.h b/include/linux/lm_interface.h
deleted file mode 100644
index 2ed8fa1..0000000
--- a/include/linux/lm_interface.h
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
- * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU General Public License version 2.
- */
-
-#ifndef __LM_INTERFACE_DOT_H__
-#define __LM_INTERFACE_DOT_H__
-
-
-typedef void (*lm_callback_t) (void *ptr, unsigned int type, void *data);
-
-/*
- * lm_mount() flags
- *
- * LM_MFLAG_SPECTATOR
- * GFS is asking to join the filesystem's lockspace, but it doesn't want to
- * modify the filesystem. The lock module shouldn't assign a journal to the FS
- * mount. It shouldn't send recovery callbacks to the FS mount. If the node
- * dies or withdraws, all locks can be wiped immediately.
- *
- * LM_MFLAG_CONV_NODROP
- * Do not allow the dlm to internally resolve conversion deadlocks by demoting
- * the lock to unlocked and then reacquiring it in the requested mode. Instead,
- * it should cancel the request and return LM_OUT_CONV_DEADLK.
- */
-
-#define LM_MFLAG_SPECTATOR 0x00000001
-#define LM_MFLAG_CONV_NODROP 0x00000002
-
-/*
- * lm_lockstruct flags
- *
- * LM_LSFLAG_LOCAL
- * The lock_nolock module returns LM_LSFLAG_LOCAL to GFS, indicating that GFS
- * can make single-node optimizations.
- */
-
-#define LM_LSFLAG_LOCAL 0x00000001
-
-/*
- * lm_lockname types
- */
-
-#define LM_TYPE_RESERVED 0x00
-#define LM_TYPE_NONDISK 0x01
-#define LM_TYPE_INODE 0x02
-#define LM_TYPE_RGRP 0x03
-#define LM_TYPE_META 0x04
-#define LM_TYPE_IOPEN 0x05
-#define LM_TYPE_FLOCK 0x06
-#define LM_TYPE_PLOCK 0x07
-#define LM_TYPE_QUOTA 0x08
-#define LM_TYPE_JOURNAL 0x09
-
-/*
- * lm_lock() states
- *
- * SHARED is compatible with SHARED, not with DEFERRED or EX.
- * DEFERRED is compatible with DEFERRED, not with SHARED or EX.
- */
-
-#define LM_ST_UNLOCKED 0
-#define LM_ST_EXCLUSIVE 1
-#define LM_ST_DEFERRED 2
-#define LM_ST_SHARED 3
-
-/*
- * lm_lock() flags
- *
- * LM_FLAG_TRY
- * Don't wait to acquire the lock if it can't be granted immediately.
- *
- * LM_FLAG_TRY_1CB
- * Send one blocking callback if TRY is set and the lock is not granted.
- *
- * LM_FLAG_NOEXP
- * GFS sets this flag on lock requests it makes while doing journal recovery.
- * These special requests should not be blocked due to the recovery like
- * ordinary locks would be.
- *
- * LM_FLAG_ANY
- * A SHARED request may also be granted in DEFERRED, or a DEFERRED request may
- * also be granted in SHARED. The preferred state is whichever is compatible
- * with other granted locks, or the specified state if no other locks exist.
- *
- * LM_FLAG_PRIORITY
- * Override fairness considerations. Suppose a lock is held in a shared state
- * and there is a pending request for the deferred state. A shared lock
- * request with the priority flag would be allowed to bypass the deferred
- * request and directly join the other shared lock. A shared lock request
- * without the priority flag might be forced to wait until the deferred
- * requested had acquired and released the lock.
- */
-
-#define LM_FLAG_TRY 0x00000001
-#define LM_FLAG_TRY_1CB 0x00000002
-#define LM_FLAG_NOEXP 0x00000004
-#define LM_FLAG_ANY 0x00000008
-#define LM_FLAG_PRIORITY 0x00000010
-
-/*
- * lm_lock() and lm_async_cb return flags
- *
- * LM_OUT_ST_MASK
- * Masks the lower two bits of lock state in the returned value.
- *
- * LM_OUT_CACHEABLE
- * The lock hasn't been released so GFS can continue to cache data for it.
- *
- * LM_OUT_CANCELED
- * The lock request was canceled.
- *
- * LM_OUT_ASYNC
- * The result of the request will be returned in an LM_CB_ASYNC callback.
- *
- * LM_OUT_CONV_DEADLK
- * The lock request was canceled do to a conversion deadlock.
- */
-
-#define LM_OUT_ST_MASK 0x00000003
-#define LM_OUT_CANCELED 0x00000008
-#define LM_OUT_ASYNC 0x00000080
-#define LM_OUT_ERROR 0x00000100
-
-/*
- * lm_callback_t types
- *
- * LM_CB_NEED_E LM_CB_NEED_D LM_CB_NEED_S
- * Blocking callback, a remote node is requesting the given lock in
- * EXCLUSIVE, DEFERRED, or SHARED.
- *
- * LM_CB_NEED_RECOVERY
- * The given journal needs to be recovered.
- *
- * LM_CB_ASYNC
- * The given lock has been granted.
- */
-
-#define LM_CB_NEED_E 257
-#define LM_CB_NEED_D 258
-#define LM_CB_NEED_S 259
-#define LM_CB_NEED_RECOVERY 260
-#define LM_CB_ASYNC 262
-
-/*
- * lm_recovery_done() messages
- */
-
-#define LM_RD_GAVEUP 308
-#define LM_RD_SUCCESS 309
-
-
-struct lm_lockname {
- u64 ln_number;
- unsigned int ln_type;
-};
-
-#define lm_name_equal(name1, name2) \
- (((name1)->ln_number == (name2)->ln_number) && \
- ((name1)->ln_type == (name2)->ln_type)) \
-
-struct lm_async_cb {
- struct lm_lockname lc_name;
- int lc_ret;
-};
-
-struct lm_lockstruct;
-
-struct lm_lockops {
- const char *lm_proto_name;
-
- /*
- * Mount/Unmount
- */
-
- int (*lm_mount) (char *table_name, char *host_data,
- lm_callback_t cb, void *cb_data,
- unsigned int min_lvb_size, int flags,
- struct lm_lockstruct *lockstruct,
- struct kobject *fskobj);
-
- void (*lm_others_may_mount) (void *lockspace);
-
- void (*lm_unmount) (void *lockspace);
-
- void (*lm_withdraw) (void *lockspace);
-
- /*
- * Lock oriented operations
- */
-
- int (*lm_get_lock) (void *lockspace, struct lm_lockname *name, void **lockp);
-
- void (*lm_put_lock) (void *lock);
-
- unsigned int (*lm_lock) (void *lock, unsigned int cur_state,
- unsigned int req_state, unsigned int flags);
-
- unsigned int (*lm_unlock) (void *lock, unsigned int cur_state);
-
- void (*lm_cancel) (void *lock);
-
- int (*lm_hold_lvb) (void *lock, char **lvbp);
- void (*lm_unhold_lvb) (void *lock, char *lvb);
-
- /*
- * Posix Lock oriented operations
- */
-
- int (*lm_plock_get) (void *lockspace, struct lm_lockname *name,
- struct file *file, struct file_lock *fl);
-
- int (*lm_plock) (void *lockspace, struct lm_lockname *name,
- struct file *file, int cmd, struct file_lock *fl);
-
- int (*lm_punlock) (void *lockspace, struct lm_lockname *name,
- struct file *file, struct file_lock *fl);
-
- /*
- * Client oriented operations
- */
-
- void (*lm_recovery_done) (void *lockspace, unsigned int jid,
- unsigned int message);
-
- struct module *lm_owner;
-};
-
-/*
- * lm_mount() return values
- *
- * ls_jid - the journal ID this node should use
- * ls_first - this node is the first to mount the file system
- * ls_lvb_size - size in bytes of lock value blocks
- * ls_lockspace - lock module's context for this file system
- * ls_ops - lock module's functions
- * ls_flags - lock module features
- */
-
-struct lm_lockstruct {
- unsigned int ls_jid;
- unsigned int ls_first;
- unsigned int ls_lvb_size;
- void *ls_lockspace;
- const struct lm_lockops *ls_ops;
- int ls_flags;
-};
-
-/*
- * Lock module bottom interface. A lock module makes itself available to GFS
- * with these functions.
- */
-
-int gfs2_register_lockproto(const struct lm_lockops *proto);
-void gfs2_unregister_lockproto(const struct lm_lockops *proto);
-
-/*
- * Lock module top interface. GFS calls these functions when mounting or
- * unmounting a file system.
- */
-
-int gfs2_mount_lockproto(char *proto_name, char *table_name, char *host_data,
- lm_callback_t cb, void *cb_data,
- unsigned int min_lvb_size, int flags,
- struct lm_lockstruct *lockstruct,
- struct kobject *fskobj);
-
-void gfs2_unmount_lockproto(struct lm_lockstruct *lockstruct);
-
-void gfs2_withdraw_lockproto(struct lm_lockstruct *lockstruct);
-
-#endif /* __LM_INTERFACE_DOT_H__ */
-
diff --git a/include/linux/maple.h b/include/linux/maple.h
index c23d3f5..d9a51b9 100644
--- a/include/linux/maple.h
+++ b/include/linux/maple.h
@@ -8,33 +8,49 @@
/* Maple Bus command and response codes */
enum maple_code {
- MAPLE_RESPONSE_FILEERR = -5,
- MAPLE_RESPONSE_AGAIN = -4, /* request should be retransmitted */
- MAPLE_RESPONSE_BADCMD = -3,
- MAPLE_RESPONSE_BADFUNC = -2,
- MAPLE_RESPONSE_NONE = -1, /* unit didn't respond at all */
- MAPLE_COMMAND_DEVINFO = 1,
- MAPLE_COMMAND_ALLINFO = 2,
- MAPLE_COMMAND_RESET = 3,
- MAPLE_COMMAND_KILL = 4,
- MAPLE_RESPONSE_DEVINFO = 5,
- MAPLE_RESPONSE_ALLINFO = 6,
- MAPLE_RESPONSE_OK = 7,
- MAPLE_RESPONSE_DATATRF = 8,
- MAPLE_COMMAND_GETCOND = 9,
- MAPLE_COMMAND_GETMINFO = 10,
- MAPLE_COMMAND_BREAD = 11,
- MAPLE_COMMAND_BWRITE = 12,
- MAPLE_COMMAND_SETCOND = 14
+ MAPLE_RESPONSE_FILEERR = -5,
+ MAPLE_RESPONSE_AGAIN, /* retransmit */
+ MAPLE_RESPONSE_BADCMD,
+ MAPLE_RESPONSE_BADFUNC,
+ MAPLE_RESPONSE_NONE, /* unit didn't respond*/
+ MAPLE_COMMAND_DEVINFO = 1,
+ MAPLE_COMMAND_ALLINFO,
+ MAPLE_COMMAND_RESET,
+ MAPLE_COMMAND_KILL,
+ MAPLE_RESPONSE_DEVINFO,
+ MAPLE_RESPONSE_ALLINFO,
+ MAPLE_RESPONSE_OK,
+ MAPLE_RESPONSE_DATATRF,
+ MAPLE_COMMAND_GETCOND,
+ MAPLE_COMMAND_GETMINFO,
+ MAPLE_COMMAND_BREAD,
+ MAPLE_COMMAND_BWRITE,
+ MAPLE_COMMAND_BSYNC,
+ MAPLE_COMMAND_SETCOND,
+ MAPLE_COMMAND_MICCONTROL
+};
+
+enum maple_file_errors {
+ MAPLE_FILEERR_INVALID_PARTITION = 0x01000000,
+ MAPLE_FILEERR_PHASE_ERROR = 0x02000000,
+ MAPLE_FILEERR_INVALID_BLOCK = 0x04000000,
+ MAPLE_FILEERR_WRITE_ERROR = 0x08000000,
+ MAPLE_FILEERR_INVALID_WRITE_LENGTH = 0x10000000,
+ MAPLE_FILEERR_BAD_CRC = 0x20000000
+};
+
+struct maple_buffer {
+ char bufx[0x400];
+ void *buf;
};
struct mapleq {
struct list_head list;
struct maple_device *dev;
- void *sendbuf, *recvbuf, *recvbufdcsp;
+ struct maple_buffer *recvbuf;
+ void *sendbuf, *recvbuf_p2;
unsigned char length;
enum maple_code command;
- struct mutex mutex;
};
struct maple_devinfo {
@@ -52,11 +68,15 @@
struct maple_driver *driver;
struct mapleq *mq;
void (*callback) (struct mapleq * mq);
+ void (*fileerr_handler)(struct maple_device *mdev, void *recvbuf);
+ int (*can_unload)(struct maple_device *mdev);
unsigned long when, interval, function;
struct maple_devinfo devinfo;
unsigned char port, unit;
char product_name[32];
char product_licence[64];
+ atomic_t busy;
+ wait_queue_head_t maple_wait;
struct device dev;
};
@@ -72,7 +92,7 @@
int maple_driver_register(struct maple_driver *);
void maple_driver_unregister(struct maple_driver *);
-int maple_add_packet_sleeps(struct maple_device *mdev, u32 function,
+int maple_add_packet(struct maple_device *mdev, u32 function,
u32 command, u32 length, void *data);
void maple_clear_dev(struct maple_device *mdev);
diff --git a/include/linux/mfd/wm8350/audio.h b/include/linux/mfd/wm8350/audio.h
index af95a1d..d899dc0 100644
--- a/include/linux/mfd/wm8350/audio.h
+++ b/include/linux/mfd/wm8350/audio.h
@@ -490,6 +490,7 @@
/*
* R231 (0xE7) - Jack Status
*/
+#define WM8350_JACK_L_LVL 0x0800
#define WM8350_JACK_R_LVL 0x0400
/*
diff --git a/include/linux/mfd/wm8400-audio.h b/include/linux/mfd/wm8400-audio.h
index b6640e0..e06ed3e 100644
--- a/include/linux/mfd/wm8400-audio.h
+++ b/include/linux/mfd/wm8400-audio.h
@@ -1181,6 +1181,7 @@
#define WM8400_FLL_OUTDIV_SHIFT 0 /* FLL_OUTDIV - [2:0] */
#define WM8400_FLL_OUTDIV_WIDTH 3 /* FLL_OUTDIV - [2:0] */
+struct wm8400;
void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400);
#endif
diff --git a/include/linux/mount.h b/include/linux/mount.h
index cab2a85..51f55f9 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -27,6 +27,7 @@
#define MNT_NODIRATIME 0x10
#define MNT_RELATIME 0x20
#define MNT_READONLY 0x40 /* does the user want this to be r/o? */
+#define MNT_STRICTATIME 0x80
#define MNT_SHRINKABLE 0x100
#define MNT_IMBALANCED_WRITE_COUNT 0x200 /* just for debugging */
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index aca8c45..02c18b9 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2112,6 +2112,8 @@
#define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c
#define PCI_DEVICE_ID_MELLANOX_SINAI 0x6274
+#define PCI_VENDOR_ID_DFI 0x15bd
+
#define PCI_VENDOR_ID_QUICKNET 0x15e2
#define PCI_DEVICE_ID_QUICKNET_XJ 0x0500
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 011db2f..2c36f62 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1175,6 +1175,8 @@
/* ??? */
unsigned int personality;
unsigned did_exec:1;
+ unsigned in_execve:1; /* Tell the LSMs that the process is doing an
+ * execve */
pid_t pid;
pid_t tgid;
diff --git a/include/linux/sh_cmt.h b/include/linux/sh_cmt.h
new file mode 100644
index 0000000..68cacde
--- /dev/null
+++ b/include/linux/sh_cmt.h
@@ -0,0 +1,13 @@
+#ifndef __SH_CMT_H__
+#define __SH_CMT_H__
+
+struct sh_cmt_config {
+ char *name;
+ unsigned long channel_offset;
+ int timer_bit;
+ char *clk;
+ unsigned long clockevent_rating;
+ unsigned long clocksource_rating;
+};
+
+#endif /* __SH_CMT_H__ */
diff --git a/include/linux/sh_intc.h b/include/linux/sh_intc.h
index 68e212f..eb1423a 100644
--- a/include/linux/sh_intc.h
+++ b/include/linux/sh_intc.h
@@ -85,6 +85,7 @@
}
#endif
+unsigned int intc_evt2irq(unsigned int vector);
void __init register_intc_controller(struct intc_desc *desc);
int intc_set_priority(unsigned int irq, unsigned int prio);
diff --git a/include/linux/timeriomem-rng.h b/include/linux/timeriomem-rng.h
new file mode 100644
index 0000000..dd25317
--- /dev/null
+++ b/include/linux/timeriomem-rng.h
@@ -0,0 +1,21 @@
+/*
+ * linux/include/linux/timeriomem-rng.h
+ *
+ * Copyright (c) 2009 Alexander Clouter <alex@digriz.org.uk>
+ *
+ * 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/completion.h>
+
+struct timeriomem_rng_data {
+ struct completion completion;
+ unsigned int present:1;
+
+ u32 __iomem *address;
+
+ /* measures in usecs */
+ unsigned int period;
+};
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
new file mode 100644
index 0000000..3338b3f
--- /dev/null
+++ b/include/linux/tpm.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2004,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ * Debora Velarde <dvelarde@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.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, version 2 of the
+ * License.
+ *
+ */
+#ifndef __LINUX_TPM_H__
+#define __LINUX_TPM_H__
+
+/*
+ * Chip num is this value or a valid tpm idx
+ */
+#define TPM_ANY_NUM 0xFFFF
+
+#if defined(CONFIG_TCG_TPM)
+
+extern int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf);
+extern int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash);
+#endif
+#endif
diff --git a/include/sound/ad1816a.h b/include/sound/ad1816a.h
index b3aa62e..d010858 100644
--- a/include/sound/ad1816a.h
+++ b/include/sound/ad1816a.h
@@ -169,5 +169,7 @@
extern int snd_ad1816a_pcm(struct snd_ad1816a *chip, int device, struct snd_pcm **rpcm);
extern int snd_ad1816a_mixer(struct snd_ad1816a *chip);
+extern int snd_ad1816a_timer(struct snd_ad1816a *chip, int device,
+ struct snd_timer **rtimer);
#endif /* __SOUND_AD1816A_H */
diff --git a/include/sound/asound.h b/include/sound/asound.h
index 1c02ed1..fad3e0c 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -126,12 +126,10 @@
unsigned long driver_data; /* W: driver-specific data */
};
-enum {
- SNDRV_HWDEP_IOCTL_PVERSION = _IOR ('H', 0x00, int),
- SNDRV_HWDEP_IOCTL_INFO = _IOR ('H', 0x01, struct snd_hwdep_info),
- SNDRV_HWDEP_IOCTL_DSP_STATUS = _IOR('H', 0x02, struct snd_hwdep_dsp_status),
- SNDRV_HWDEP_IOCTL_DSP_LOAD = _IOW('H', 0x03, struct snd_hwdep_dsp_image)
-};
+#define SNDRV_HWDEP_IOCTL_PVERSION _IOR ('H', 0x00, int)
+#define SNDRV_HWDEP_IOCTL_INFO _IOR ('H', 0x01, struct snd_hwdep_info)
+#define SNDRV_HWDEP_IOCTL_DSP_STATUS _IOR('H', 0x02, struct snd_hwdep_dsp_status)
+#define SNDRV_HWDEP_IOCTL_DSP_LOAD _IOW('H', 0x03, struct snd_hwdep_dsp_image)
/*****************************************************************************
* *
@@ -451,40 +449,35 @@
SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC,
};
-enum {
- SNDRV_PCM_IOCTL_PVERSION = _IOR('A', 0x00, int),
- SNDRV_PCM_IOCTL_INFO = _IOR('A', 0x01, struct snd_pcm_info),
- SNDRV_PCM_IOCTL_TSTAMP = _IOW('A', 0x02, int),
- SNDRV_PCM_IOCTL_TTSTAMP = _IOW('A', 0x03, int),
- SNDRV_PCM_IOCTL_HW_REFINE = _IOWR('A', 0x10, struct snd_pcm_hw_params),
- SNDRV_PCM_IOCTL_HW_PARAMS = _IOWR('A', 0x11, struct snd_pcm_hw_params),
- SNDRV_PCM_IOCTL_HW_FREE = _IO('A', 0x12),
- SNDRV_PCM_IOCTL_SW_PARAMS = _IOWR('A', 0x13, struct snd_pcm_sw_params),
- SNDRV_PCM_IOCTL_STATUS = _IOR('A', 0x20, struct snd_pcm_status),
- SNDRV_PCM_IOCTL_DELAY = _IOR('A', 0x21, snd_pcm_sframes_t),
- SNDRV_PCM_IOCTL_HWSYNC = _IO('A', 0x22),
- SNDRV_PCM_IOCTL_SYNC_PTR = _IOWR('A', 0x23, struct snd_pcm_sync_ptr),
- SNDRV_PCM_IOCTL_CHANNEL_INFO = _IOR('A', 0x32, struct snd_pcm_channel_info),
- SNDRV_PCM_IOCTL_PREPARE = _IO('A', 0x40),
- SNDRV_PCM_IOCTL_RESET = _IO('A', 0x41),
- SNDRV_PCM_IOCTL_START = _IO('A', 0x42),
- SNDRV_PCM_IOCTL_DROP = _IO('A', 0x43),
- SNDRV_PCM_IOCTL_DRAIN = _IO('A', 0x44),
- SNDRV_PCM_IOCTL_PAUSE = _IOW('A', 0x45, int),
- SNDRV_PCM_IOCTL_REWIND = _IOW('A', 0x46, snd_pcm_uframes_t),
- SNDRV_PCM_IOCTL_RESUME = _IO('A', 0x47),
- SNDRV_PCM_IOCTL_XRUN = _IO('A', 0x48),
- SNDRV_PCM_IOCTL_FORWARD = _IOW('A', 0x49, snd_pcm_uframes_t),
- SNDRV_PCM_IOCTL_WRITEI_FRAMES = _IOW('A', 0x50, struct snd_xferi),
- SNDRV_PCM_IOCTL_READI_FRAMES = _IOR('A', 0x51, struct snd_xferi),
- SNDRV_PCM_IOCTL_WRITEN_FRAMES = _IOW('A', 0x52, struct snd_xfern),
- SNDRV_PCM_IOCTL_READN_FRAMES = _IOR('A', 0x53, struct snd_xfern),
- SNDRV_PCM_IOCTL_LINK = _IOW('A', 0x60, int),
- SNDRV_PCM_IOCTL_UNLINK = _IO('A', 0x61),
-};
-
-/* Trick to make alsa-lib/acinclude.m4 happy */
-#define SNDRV_PCM_IOCTL_REWIND SNDRV_PCM_IOCTL_REWIND
+#define SNDRV_PCM_IOCTL_PVERSION _IOR('A', 0x00, int)
+#define SNDRV_PCM_IOCTL_INFO _IOR('A', 0x01, struct snd_pcm_info)
+#define SNDRV_PCM_IOCTL_TSTAMP _IOW('A', 0x02, int)
+#define SNDRV_PCM_IOCTL_TTSTAMP _IOW('A', 0x03, int)
+#define SNDRV_PCM_IOCTL_HW_REFINE _IOWR('A', 0x10, struct snd_pcm_hw_params)
+#define SNDRV_PCM_IOCTL_HW_PARAMS _IOWR('A', 0x11, struct snd_pcm_hw_params)
+#define SNDRV_PCM_IOCTL_HW_FREE _IO('A', 0x12)
+#define SNDRV_PCM_IOCTL_SW_PARAMS _IOWR('A', 0x13, struct snd_pcm_sw_params)
+#define SNDRV_PCM_IOCTL_STATUS _IOR('A', 0x20, struct snd_pcm_status)
+#define SNDRV_PCM_IOCTL_DELAY _IOR('A', 0x21, snd_pcm_sframes_t)
+#define SNDRV_PCM_IOCTL_HWSYNC _IO('A', 0x22)
+#define SNDRV_PCM_IOCTL_SYNC_PTR _IOWR('A', 0x23, struct snd_pcm_sync_ptr)
+#define SNDRV_PCM_IOCTL_CHANNEL_INFO _IOR('A', 0x32, struct snd_pcm_channel_info)
+#define SNDRV_PCM_IOCTL_PREPARE _IO('A', 0x40)
+#define SNDRV_PCM_IOCTL_RESET _IO('A', 0x41)
+#define SNDRV_PCM_IOCTL_START _IO('A', 0x42)
+#define SNDRV_PCM_IOCTL_DROP _IO('A', 0x43)
+#define SNDRV_PCM_IOCTL_DRAIN _IO('A', 0x44)
+#define SNDRV_PCM_IOCTL_PAUSE _IOW('A', 0x45, int)
+#define SNDRV_PCM_IOCTL_REWIND _IOW('A', 0x46, snd_pcm_uframes_t)
+#define SNDRV_PCM_IOCTL_RESUME _IO('A', 0x47)
+#define SNDRV_PCM_IOCTL_XRUN _IO('A', 0x48)
+#define SNDRV_PCM_IOCTL_FORWARD _IOW('A', 0x49, snd_pcm_uframes_t)
+#define SNDRV_PCM_IOCTL_WRITEI_FRAMES _IOW('A', 0x50, struct snd_xferi)
+#define SNDRV_PCM_IOCTL_READI_FRAMES _IOR('A', 0x51, struct snd_xferi)
+#define SNDRV_PCM_IOCTL_WRITEN_FRAMES _IOW('A', 0x52, struct snd_xfern)
+#define SNDRV_PCM_IOCTL_READN_FRAMES _IOR('A', 0x53, struct snd_xfern)
+#define SNDRV_PCM_IOCTL_LINK _IOW('A', 0x60, int)
+#define SNDRV_PCM_IOCTL_UNLINK _IO('A', 0x61)
/*****************************************************************************
* *
@@ -538,14 +531,12 @@
unsigned char reserved[16]; /* reserved for future use */
};
-enum {
- SNDRV_RAWMIDI_IOCTL_PVERSION = _IOR('W', 0x00, int),
- SNDRV_RAWMIDI_IOCTL_INFO = _IOR('W', 0x01, struct snd_rawmidi_info),
- SNDRV_RAWMIDI_IOCTL_PARAMS = _IOWR('W', 0x10, struct snd_rawmidi_params),
- SNDRV_RAWMIDI_IOCTL_STATUS = _IOWR('W', 0x20, struct snd_rawmidi_status),
- SNDRV_RAWMIDI_IOCTL_DROP = _IOW('W', 0x30, int),
- SNDRV_RAWMIDI_IOCTL_DRAIN = _IOW('W', 0x31, int),
-};
+#define SNDRV_RAWMIDI_IOCTL_PVERSION _IOR('W', 0x00, int)
+#define SNDRV_RAWMIDI_IOCTL_INFO _IOR('W', 0x01, struct snd_rawmidi_info)
+#define SNDRV_RAWMIDI_IOCTL_PARAMS _IOWR('W', 0x10, struct snd_rawmidi_params)
+#define SNDRV_RAWMIDI_IOCTL_STATUS _IOWR('W', 0x20, struct snd_rawmidi_status)
+#define SNDRV_RAWMIDI_IOCTL_DROP _IOW('W', 0x30, int)
+#define SNDRV_RAWMIDI_IOCTL_DRAIN _IOW('W', 0x31, int)
/*
* Timer section - /dev/snd/timer
@@ -654,23 +645,21 @@
unsigned char reserved[64]; /* reserved */
};
-enum {
- SNDRV_TIMER_IOCTL_PVERSION = _IOR('T', 0x00, int),
- SNDRV_TIMER_IOCTL_NEXT_DEVICE = _IOWR('T', 0x01, struct snd_timer_id),
- SNDRV_TIMER_IOCTL_TREAD = _IOW('T', 0x02, int),
- SNDRV_TIMER_IOCTL_GINFO = _IOWR('T', 0x03, struct snd_timer_ginfo),
- SNDRV_TIMER_IOCTL_GPARAMS = _IOW('T', 0x04, struct snd_timer_gparams),
- SNDRV_TIMER_IOCTL_GSTATUS = _IOWR('T', 0x05, struct snd_timer_gstatus),
- SNDRV_TIMER_IOCTL_SELECT = _IOW('T', 0x10, struct snd_timer_select),
- SNDRV_TIMER_IOCTL_INFO = _IOR('T', 0x11, struct snd_timer_info),
- SNDRV_TIMER_IOCTL_PARAMS = _IOW('T', 0x12, struct snd_timer_params),
- SNDRV_TIMER_IOCTL_STATUS = _IOR('T', 0x14, struct snd_timer_status),
- /* The following four ioctls are changed since 1.0.9 due to confliction */
- SNDRV_TIMER_IOCTL_START = _IO('T', 0xa0),
- SNDRV_TIMER_IOCTL_STOP = _IO('T', 0xa1),
- SNDRV_TIMER_IOCTL_CONTINUE = _IO('T', 0xa2),
- SNDRV_TIMER_IOCTL_PAUSE = _IO('T', 0xa3),
-};
+#define SNDRV_TIMER_IOCTL_PVERSION _IOR('T', 0x00, int)
+#define SNDRV_TIMER_IOCTL_NEXT_DEVICE _IOWR('T', 0x01, struct snd_timer_id)
+#define SNDRV_TIMER_IOCTL_TREAD _IOW('T', 0x02, int)
+#define SNDRV_TIMER_IOCTL_GINFO _IOWR('T', 0x03, struct snd_timer_ginfo)
+#define SNDRV_TIMER_IOCTL_GPARAMS _IOW('T', 0x04, struct snd_timer_gparams)
+#define SNDRV_TIMER_IOCTL_GSTATUS _IOWR('T', 0x05, struct snd_timer_gstatus)
+#define SNDRV_TIMER_IOCTL_SELECT _IOW('T', 0x10, struct snd_timer_select)
+#define SNDRV_TIMER_IOCTL_INFO _IOR('T', 0x11, struct snd_timer_info)
+#define SNDRV_TIMER_IOCTL_PARAMS _IOW('T', 0x12, struct snd_timer_params)
+#define SNDRV_TIMER_IOCTL_STATUS _IOR('T', 0x14, struct snd_timer_status)
+/* The following four ioctls are changed since 1.0.9 due to confliction */
+#define SNDRV_TIMER_IOCTL_START _IO('T', 0xa0)
+#define SNDRV_TIMER_IOCTL_STOP _IO('T', 0xa1)
+#define SNDRV_TIMER_IOCTL_CONTINUE _IO('T', 0xa2)
+#define SNDRV_TIMER_IOCTL_PAUSE _IO('T', 0xa3)
struct snd_timer_read {
unsigned int resolution;
@@ -847,33 +836,31 @@
unsigned int tlv[0]; /* first TLV */
};
-enum {
- SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int),
- SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct snd_ctl_card_info),
- SNDRV_CTL_IOCTL_ELEM_LIST = _IOWR('U', 0x10, struct snd_ctl_elem_list),
- SNDRV_CTL_IOCTL_ELEM_INFO = _IOWR('U', 0x11, struct snd_ctl_elem_info),
- SNDRV_CTL_IOCTL_ELEM_READ = _IOWR('U', 0x12, struct snd_ctl_elem_value),
- SNDRV_CTL_IOCTL_ELEM_WRITE = _IOWR('U', 0x13, struct snd_ctl_elem_value),
- SNDRV_CTL_IOCTL_ELEM_LOCK = _IOW('U', 0x14, struct snd_ctl_elem_id),
- SNDRV_CTL_IOCTL_ELEM_UNLOCK = _IOW('U', 0x15, struct snd_ctl_elem_id),
- SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS = _IOWR('U', 0x16, int),
- SNDRV_CTL_IOCTL_ELEM_ADD = _IOWR('U', 0x17, struct snd_ctl_elem_info),
- SNDRV_CTL_IOCTL_ELEM_REPLACE = _IOWR('U', 0x18, struct snd_ctl_elem_info),
- SNDRV_CTL_IOCTL_ELEM_REMOVE = _IOWR('U', 0x19, struct snd_ctl_elem_id),
- SNDRV_CTL_IOCTL_TLV_READ = _IOWR('U', 0x1a, struct snd_ctl_tlv),
- SNDRV_CTL_IOCTL_TLV_WRITE = _IOWR('U', 0x1b, struct snd_ctl_tlv),
- SNDRV_CTL_IOCTL_TLV_COMMAND = _IOWR('U', 0x1c, struct snd_ctl_tlv),
- SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int),
- SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct snd_hwdep_info),
- SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int),
- SNDRV_CTL_IOCTL_PCM_INFO = _IOWR('U', 0x31, struct snd_pcm_info),
- SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE = _IOW('U', 0x32, int),
- SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE = _IOWR('U', 0x40, int),
- SNDRV_CTL_IOCTL_RAWMIDI_INFO = _IOWR('U', 0x41, struct snd_rawmidi_info),
- SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE = _IOW('U', 0x42, int),
- SNDRV_CTL_IOCTL_POWER = _IOWR('U', 0xd0, int),
- SNDRV_CTL_IOCTL_POWER_STATE = _IOR('U', 0xd1, int),
-};
+#define SNDRV_CTL_IOCTL_PVERSION _IOR('U', 0x00, int)
+#define SNDRV_CTL_IOCTL_CARD_INFO _IOR('U', 0x01, struct snd_ctl_card_info)
+#define SNDRV_CTL_IOCTL_ELEM_LIST _IOWR('U', 0x10, struct snd_ctl_elem_list)
+#define SNDRV_CTL_IOCTL_ELEM_INFO _IOWR('U', 0x11, struct snd_ctl_elem_info)
+#define SNDRV_CTL_IOCTL_ELEM_READ _IOWR('U', 0x12, struct snd_ctl_elem_value)
+#define SNDRV_CTL_IOCTL_ELEM_WRITE _IOWR('U', 0x13, struct snd_ctl_elem_value)
+#define SNDRV_CTL_IOCTL_ELEM_LOCK _IOW('U', 0x14, struct snd_ctl_elem_id)
+#define SNDRV_CTL_IOCTL_ELEM_UNLOCK _IOW('U', 0x15, struct snd_ctl_elem_id)
+#define SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS _IOWR('U', 0x16, int)
+#define SNDRV_CTL_IOCTL_ELEM_ADD _IOWR('U', 0x17, struct snd_ctl_elem_info)
+#define SNDRV_CTL_IOCTL_ELEM_REPLACE _IOWR('U', 0x18, struct snd_ctl_elem_info)
+#define SNDRV_CTL_IOCTL_ELEM_REMOVE _IOWR('U', 0x19, struct snd_ctl_elem_id)
+#define SNDRV_CTL_IOCTL_TLV_READ _IOWR('U', 0x1a, struct snd_ctl_tlv)
+#define SNDRV_CTL_IOCTL_TLV_WRITE _IOWR('U', 0x1b, struct snd_ctl_tlv)
+#define SNDRV_CTL_IOCTL_TLV_COMMAND _IOWR('U', 0x1c, struct snd_ctl_tlv)
+#define SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE _IOWR('U', 0x20, int)
+#define SNDRV_CTL_IOCTL_HWDEP_INFO _IOR('U', 0x21, struct snd_hwdep_info)
+#define SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE _IOR('U', 0x30, int)
+#define SNDRV_CTL_IOCTL_PCM_INFO _IOWR('U', 0x31, struct snd_pcm_info)
+#define SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE _IOW('U', 0x32, int)
+#define SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE _IOWR('U', 0x40, int)
+#define SNDRV_CTL_IOCTL_RAWMIDI_INFO _IOWR('U', 0x41, struct snd_rawmidi_info)
+#define SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE _IOW('U', 0x42, int)
+#define SNDRV_CTL_IOCTL_POWER _IOWR('U', 0xd0, int)
+#define SNDRV_CTL_IOCTL_POWER_STATE _IOR('U', 0xd1, int)
/*
* Read interface.
@@ -919,18 +906,4 @@
#define SNDRV_CTL_NAME_IEC958_PCM_STREAM "PCM Stream"
#define SNDRV_CTL_NAME_IEC958(expl,direction,what) "IEC958 " expl SNDRV_CTL_NAME_##direction SNDRV_CTL_NAME_IEC958_##what
-/*
- *
- */
-
-struct snd_xferv {
- const struct iovec *vector;
- unsigned long count;
-};
-
-enum {
- SNDRV_IOCTL_READV = _IOW('K', 0x00, struct snd_xferv),
- SNDRV_IOCTL_WRITEV = _IOW('K', 0x01, struct snd_xferv),
-};
-
#endif /* __SOUND_ASOUND_H */
diff --git a/include/sound/atmel-abdac.h b/include/sound/atmel-abdac.h
new file mode 100644
index 0000000..edff6a8
--- /dev/null
+++ b/include/sound/atmel-abdac.h
@@ -0,0 +1,23 @@
+/*
+ * Driver for the Atmel Audio Bitstream DAC (ABDAC)
+ *
+ * Copyright (C) 2009 Atmel Corporation
+ *
+ * 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 __INCLUDE_SOUND_ATMEL_ABDAC_H
+#define __INCLUDE_SOUND_ATMEL_ABDAC_H
+
+#include <linux/dw_dmac.h>
+
+/**
+ * struct atmel_abdac_pdata - board specific ABDAC configuration
+ * @dws: DMA slave interface to use for sound playback.
+ */
+struct atmel_abdac_pdata {
+ struct dw_dma_slave dws;
+};
+
+#endif /* __INCLUDE_SOUND_ATMEL_ABDAC_H */
diff --git a/include/sound/atmel-ac97c.h b/include/sound/atmel-ac97c.h
new file mode 100644
index 0000000..e6aabdb
--- /dev/null
+++ b/include/sound/atmel-ac97c.h
@@ -0,0 +1,40 @@
+/*
+ * Driver for the Atmel AC97C controller
+ *
+ * Copyright (C) 2005-2009 Atmel Corporation
+ *
+ * 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 __INCLUDE_SOUND_ATMEL_AC97C_H
+#define __INCLUDE_SOUND_ATMEL_AC97C_H
+
+#include <linux/dw_dmac.h>
+
+#define AC97C_CAPTURE 0x01
+#define AC97C_PLAYBACK 0x02
+#define AC97C_BOTH (AC97C_CAPTURE | AC97C_PLAYBACK)
+
+/**
+ * struct atmel_ac97c_pdata - board specific AC97C configuration
+ * @rx_dws: DMA slave interface to use for sound capture.
+ * @tx_dws: DMA slave interface to use for sound playback.
+ * @reset_pin: GPIO pin wired to the reset input on the external AC97 codec,
+ * optional to use, set to -ENODEV if not in use. AC97 layer will
+ * try to do a software reset of the external codec anyway.
+ * @flags: Flags for which directions should be enabled.
+ *
+ * If the user do not want to use a DMA channel for playback or capture, i.e.
+ * only one feature is required on the board. The slave for playback or capture
+ * can be set to NULL. The AC97C driver will take use of this when setting up
+ * the sound streams.
+ */
+struct ac97c_platform_data {
+ struct dw_dma_slave rx_dws;
+ struct dw_dma_slave tx_dws;
+ unsigned int flags;
+ int reset_pin;
+};
+
+#endif /* __INCLUDE_SOUND_ATMEL_AC97C_H */
diff --git a/include/sound/control.h b/include/sound/control.h
index 4721b4b..ef96f07 100644
--- a/include/sound/control.h
+++ b/include/sound/control.h
@@ -171,6 +171,54 @@
*/
struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
const unsigned int *tlv);
-int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave);
-
+int _snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave,
+ unsigned int flags);
+/* optional flags for slave */
+#define SND_CTL_SLAVE_NEED_UPDATE (1 << 0)
+
+/**
+ * snd_ctl_add_slave - Add a virtual slave control
+ * @master: vmaster element
+ * @slave: slave element to add
+ *
+ * Add a virtual slave control to the given master element created via
+ * snd_ctl_create_virtual_master() beforehand.
+ * Returns zero if successful or a negative error code.
+ *
+ * All slaves must be the same type (returning the same information
+ * via info callback). The fucntion doesn't check it, so it's your
+ * responsibility.
+ *
+ * Also, some additional limitations:
+ * at most two channels,
+ * logarithmic volume control (dB level) thus no linear volume,
+ * master can only attenuate the volume without gain
+ */
+static inline int
+snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave)
+{
+ return _snd_ctl_add_slave(master, slave, 0);
+}
+
+/**
+ * snd_ctl_add_slave_uncached - Add a virtual slave control
+ * @master: vmaster element
+ * @slave: slave element to add
+ *
+ * Add a virtual slave control to the given master.
+ * Unlike snd_ctl_add_slave(), the element added via this function
+ * is supposed to have volatile values, and get callback is called
+ * at each time quried from the master.
+ *
+ * When the control peeks the hardware values directly and the value
+ * can be changed by other means than the put callback of the element,
+ * this function should be used to keep the value always up-to-date.
+ */
+static inline int
+snd_ctl_add_slave_uncached(struct snd_kcontrol *master,
+ struct snd_kcontrol *slave)
+{
+ return _snd_ctl_add_slave(master, slave, SND_CTL_SLAVE_NEED_UPDATE);
+}
+
#endif /* __SOUND_CONTROL_H */
diff --git a/include/sound/core.h b/include/sound/core.h
index f632484..3dea798 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -97,9 +97,9 @@
struct snd_monitor_file {
struct file *file;
- struct snd_monitor_file *next;
const struct file_operations *disconnected_f_op;
- struct list_head shutdown_list;
+ struct list_head shutdown_list; /* still need to shutdown */
+ struct list_head list; /* link of monitor files */
};
/* main structure for soundcard */
@@ -134,7 +134,7 @@
struct snd_info_entry *proc_id; /* the card id */
struct proc_dir_entry *proc_root_link; /* number link to real id */
- struct snd_monitor_file *files; /* all files associated to this card */
+ struct list_head files_list; /* all files associated to this card */
struct snd_shutdown_f_ops *s_f_ops; /* file operations in the shutdown
state */
spinlock_t files_lock; /* lock the files for this card */
@@ -296,8 +296,20 @@
extern int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int cmd);
#endif
+int snd_card_create(int idx, const char *id,
+ struct module *module, int extra_size,
+ struct snd_card **card_ret);
+
+static inline __deprecated
struct snd_card *snd_card_new(int idx, const char *id,
- struct module *module, int extra_size);
+ struct module *module, int extra_size)
+{
+ struct snd_card *card;
+ if (snd_card_create(idx, id, module, extra_size, &card) < 0)
+ return NULL;
+ return card;
+}
+
int snd_card_disconnect(struct snd_card *card);
int snd_card_free(struct snd_card *card);
int snd_card_free_when_closed(struct snd_card *card);
@@ -446,21 +458,33 @@
struct snd_pci_quirk {
unsigned short subvendor; /* PCI subvendor ID */
unsigned short subdevice; /* PCI subdevice ID */
+ unsigned short subdevice_mask; /* bitmask to match */
int value; /* value */
#ifdef CONFIG_SND_DEBUG_VERBOSE
const char *name; /* name of the device (optional) */
#endif
};
-#define _SND_PCI_QUIRK_ID(vend,dev) \
- .subvendor = (vend), .subdevice = (dev)
+#define _SND_PCI_QUIRK_ID_MASK(vend, mask, dev) \
+ .subvendor = (vend), .subdevice = (dev), .subdevice_mask = (mask)
+#define _SND_PCI_QUIRK_ID(vend, dev) \
+ _SND_PCI_QUIRK_ID_MASK(vend, 0xffff, dev)
#define SND_PCI_QUIRK_ID(vend,dev) {_SND_PCI_QUIRK_ID(vend, dev)}
#ifdef CONFIG_SND_DEBUG_VERBOSE
#define SND_PCI_QUIRK(vend,dev,xname,val) \
{_SND_PCI_QUIRK_ID(vend, dev), .value = (val), .name = (xname)}
+#define SND_PCI_QUIRK_VENDOR(vend, xname, val) \
+ {_SND_PCI_QUIRK_ID_MASK(vend, 0, 0), .value = (val), .name = (xname)}
+#define SND_PCI_QUIRK_MASK(vend, mask, dev, xname, val) \
+ {_SND_PCI_QUIRK_ID_MASK(vend, mask, dev), \
+ .value = (val), .name = (xname)}
#else
#define SND_PCI_QUIRK(vend,dev,xname,val) \
{_SND_PCI_QUIRK_ID(vend, dev), .value = (val)}
+#define SND_PCI_QUIRK_MASK(vend, mask, dev, xname, val) \
+ {_SND_PCI_QUIRK_ID_MASK(vend, mask, dev), .value = (val)}
+#define SND_PCI_QUIRK_VENDOR(vend, xname, val) \
+ {_SND_PCI_QUIRK_ID_MASK(vend, 0, 0), .value = (val)}
#endif
const struct snd_pci_quirk *
diff --git a/include/sound/hwdep.h b/include/sound/hwdep.h
index d9eea01..8c05e47a 100644
--- a/include/sound/hwdep.h
+++ b/include/sound/hwdep.h
@@ -27,18 +27,28 @@
struct snd_hwdep;
+/* hwdep file ops; all ops can be NULL */
struct snd_hwdep_ops {
- long long (*llseek) (struct snd_hwdep *hw, struct file * file, long long offset, int orig);
- long (*read) (struct snd_hwdep *hw, char __user *buf, long count, loff_t *offset);
- long (*write) (struct snd_hwdep *hw, const char __user *buf, long count, loff_t *offset);
- int (*open) (struct snd_hwdep * hw, struct file * file);
- int (*release) (struct snd_hwdep *hw, struct file * file);
- unsigned int (*poll) (struct snd_hwdep *hw, struct file * file, poll_table * wait);
- int (*ioctl) (struct snd_hwdep *hw, struct file * file, unsigned int cmd, unsigned long arg);
- int (*ioctl_compat) (struct snd_hwdep *hw, struct file * file, unsigned int cmd, unsigned long arg);
- int (*mmap) (struct snd_hwdep *hw, struct file * file, struct vm_area_struct * vma);
- int (*dsp_status) (struct snd_hwdep *hw, struct snd_hwdep_dsp_status *status);
- int (*dsp_load) (struct snd_hwdep *hw, struct snd_hwdep_dsp_image *image);
+ long long (*llseek)(struct snd_hwdep *hw, struct file *file,
+ long long offset, int orig);
+ long (*read)(struct snd_hwdep *hw, char __user *buf,
+ long count, loff_t *offset);
+ long (*write)(struct snd_hwdep *hw, const char __user *buf,
+ long count, loff_t *offset);
+ int (*open)(struct snd_hwdep *hw, struct file * file);
+ int (*release)(struct snd_hwdep *hw, struct file * file);
+ unsigned int (*poll)(struct snd_hwdep *hw, struct file *file,
+ poll_table *wait);
+ int (*ioctl)(struct snd_hwdep *hw, struct file *file,
+ unsigned int cmd, unsigned long arg);
+ int (*ioctl_compat)(struct snd_hwdep *hw, struct file *file,
+ unsigned int cmd, unsigned long arg);
+ int (*mmap)(struct snd_hwdep *hw, struct file *file,
+ struct vm_area_struct *vma);
+ int (*dsp_status)(struct snd_hwdep *hw,
+ struct snd_hwdep_dsp_status *status);
+ int (*dsp_load)(struct snd_hwdep *hw,
+ struct snd_hwdep_dsp_image *image);
};
struct snd_hwdep {
@@ -61,9 +71,9 @@
void (*private_free) (struct snd_hwdep *hwdep);
struct mutex open_mutex;
- int used;
- unsigned int dsp_loaded;
- unsigned int exclusive: 1;
+ int used; /* reference counter */
+ unsigned int dsp_loaded; /* bit fields of loaded dsp indices */
+ unsigned int exclusive:1; /* exclusive access mode */
};
extern int snd_hwdep_new(struct snd_card *card, char *id, int device,
diff --git a/include/sound/jack.h b/include/sound/jack.h
index 2e0315c..6b013c6 100644
--- a/include/sound/jack.h
+++ b/include/sound/jack.h
@@ -30,6 +30,9 @@
/**
* Jack types which can be reported. These values are used as a
* bitmask.
+ *
+ * Note that this must be kept in sync with the lookup table in
+ * sound/core/jack.c.
*/
enum snd_jack_types {
SND_JACK_HEADPHONE = 0x0001,
@@ -37,6 +40,8 @@
SND_JACK_HEADSET = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE,
SND_JACK_LINEOUT = 0x0004,
SND_JACK_MECHANICAL = 0x0008, /* If detected separately */
+ SND_JACK_VIDEOOUT = 0x0010,
+ SND_JACK_AVOUT = SND_JACK_LINEOUT | SND_JACK_VIDEOOUT,
};
struct snd_jack {
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 40c5a6f..8904b19 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -364,7 +364,6 @@
/* -- timer section -- */
struct snd_timer *timer; /* timer */
unsigned timer_running: 1; /* time is running */
- spinlock_t timer_lock;
/* -- next substream -- */
struct snd_pcm_substream *next;
/* -- linked substreams -- */
@@ -451,7 +450,7 @@
extern const struct file_operations snd_pcm_f_ops[2];
-int snd_pcm_new(struct snd_card *card, char *id, int device,
+int snd_pcm_new(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count,
struct snd_pcm **rpcm);
int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count);
diff --git a/include/sound/pxa2xx-lib.h b/include/sound/pxa2xx-lib.h
index 2fd3d25..2c894b6 100644
--- a/include/sound/pxa2xx-lib.h
+++ b/include/sound/pxa2xx-lib.h
@@ -42,4 +42,19 @@
extern int pxa2xx_ac97_hw_probe(struct platform_device *dev);
extern void pxa2xx_ac97_hw_remove(struct platform_device *dev);
+/* AC97 platform_data */
+/**
+ * struct pxa2xx_ac97_platform_data - pxa ac97 platform data
+ * @reset_gpio: AC97 reset gpio (normally gpio113 or gpio95)
+ * a -1 value means no gpio will be used for reset
+ *
+ * Platform data should only be specified for pxa27x CPUs where a silicon bug
+ * prevents correct operation of the reset line. If not specified, the default
+ * behaviour is to consider gpio 113 as the AC97 reset line, which is the
+ * default on most boards.
+ */
+struct pxa2xx_ac97_platform_data {
+ int reset_gpio;
+};
+
#endif
diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h
index b550a41..c23c265 100644
--- a/include/sound/rawmidi.h
+++ b/include/sound/rawmidi.h
@@ -42,7 +42,6 @@
#define SNDRV_RAWMIDI_LFLG_INPUT (1<<1)
#define SNDRV_RAWMIDI_LFLG_OPEN (3<<0)
#define SNDRV_RAWMIDI_LFLG_APPEND (1<<2)
-#define SNDRV_RAWMIDI_LFLG_NOOPENLOCK (1<<3)
struct snd_rawmidi;
struct snd_rawmidi_substream;
diff --git a/include/sound/sb.h b/include/sound/sb.h
index 85f93c5..4e62ee1 100644
--- a/include/sound/sb.h
+++ b/include/sound/sb.h
@@ -249,6 +249,7 @@
#define SB_ALS4000_3D_AUTO_MUTE 0x52
#define SB_ALS4000_ANALOG_BLOCK_CTRL 0x53
#define SB_ALS4000_3D_DELAYLINE_PATTERN 0x54
+#define SB_ALS4000_CR3_CONFIGURATION 0xc3 /* bit 7 is Digital Loop Enable */
#define SB_ALS4000_QSOUND 0xdb
/* IRQ setting bitmap */
@@ -330,7 +331,8 @@
SB_MIX_DOUBLE,
SB_MIX_INPUT_SW,
SB_MIX_CAPTURE_PRO,
- SB_MIX_CAPTURE_DT019X
+ SB_MIX_CAPTURE_DT019X,
+ SB_MIX_MONO_CAPTURE_ALS4K
};
#define SB_MIXVAL_DOUBLE(left_reg, right_reg, left_shift, right_shift, mask) \
diff --git a/include/sound/sfnt_info.h b/include/sound/sfnt_info.h
index 5d1ab9c..1bce7fd 100644
--- a/include/sound/sfnt_info.h
+++ b/include/sound/sfnt_info.h
@@ -202,13 +202,11 @@
int value2; /* reserved */
};
-enum {
- SNDRV_EMUX_IOCTL_VERSION = _IOR('H', 0x80, unsigned int),
- SNDRV_EMUX_IOCTL_LOAD_PATCH = _IOWR('H', 0x81, struct soundfont_patch_info),
- SNDRV_EMUX_IOCTL_RESET_SAMPLES = _IO('H', 0x82),
- SNDRV_EMUX_IOCTL_REMOVE_LAST_SAMPLES = _IO('H', 0x83),
- SNDRV_EMUX_IOCTL_MEM_AVAIL = _IOW('H', 0x84, int),
- SNDRV_EMUX_IOCTL_MISC_MODE = _IOWR('H', 0x84, struct snd_emux_misc_mode),
-};
+#define SNDRV_EMUX_IOCTL_VERSION _IOR('H', 0x80, unsigned int)
+#define SNDRV_EMUX_IOCTL_LOAD_PATCH _IOWR('H', 0x81, struct soundfont_patch_info)
+#define SNDRV_EMUX_IOCTL_RESET_SAMPLES _IO('H', 0x82)
+#define SNDRV_EMUX_IOCTL_REMOVE_LAST_SAMPLES _IO('H', 0x83)
+#define SNDRV_EMUX_IOCTL_MEM_AVAIL _IOW('H', 0x84, int)
+#define SNDRV_EMUX_IOCTL_MISC_MODE _IOWR('H', 0x84, struct snd_emux_misc_mode)
#endif /* __SOUND_SFNT_INFO_H */
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index 24247f7..1367647 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -203,7 +203,7 @@
int (*resume)(struct snd_soc_dai *dai);
/* ops */
- struct snd_soc_dai_ops ops;
+ struct snd_soc_dai_ops *ops;
/* DAI capabilities */
struct snd_soc_pcm_stream capture;
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index dfa8049..a7def6a 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -76,6 +76,11 @@
wcontrols, wncontrols)\
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = 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, \
+ .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}
@@ -101,6 +106,11 @@
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = 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, \
+ .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, \
@@ -182,6 +192,12 @@
.get = snd_soc_dapm_get_value_enum_double, \
.put = snd_soc_dapm_put_value_enum_double, \
.private_value = (unsigned long)&xenum }
+#define SOC_DAPM_PIN_SWITCH(xname) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname " Switch", \
+ .info = snd_soc_dapm_info_pin_switch, \
+ .get = snd_soc_dapm_get_pin_switch, \
+ .put = snd_soc_dapm_put_pin_switch, \
+ .private_value = (unsigned long)xname }
/* dapm stream operations */
#define SND_SOC_DAPM_STREAM_NOP 0x0
@@ -228,6 +244,12 @@
struct snd_ctl_elem_value *ucontrol);
int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_info_pin_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uncontrol);
+int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uncontrol);
int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
const struct snd_soc_dapm_widget *widget);
int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
@@ -250,10 +272,10 @@
int snd_soc_dapm_sys_add(struct device *dev);
/* dapm audio pin control and status */
-int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, char *pin);
-int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin);
-int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, char *pin);
-int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, char *pin);
+int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin);
+int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, const char *pin);
+int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, const char *pin);
+int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, const char *pin);
int snd_soc_dapm_sync(struct snd_soc_codec *codec);
/* dapm widget types */
@@ -263,6 +285,7 @@
snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */
snd_soc_dapm_value_mux, /* selects 1 analog signal from many inputs */
snd_soc_dapm_mixer, /* mixes several analog signals together */
+ snd_soc_dapm_mixer_named_ctl, /* mixer with named controls */
snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */
snd_soc_dapm_adc, /* analog to digital converter */
snd_soc_dapm_dac, /* digital to analog converter */
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 24593ac..a40bc6f 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -16,6 +16,8 @@
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/control.h>
@@ -154,6 +156,8 @@
SND_SOC_BIAS_OFF,
};
+struct snd_jack;
+struct snd_soc_card;
struct snd_soc_device;
struct snd_soc_pcm_stream;
struct snd_soc_ops;
@@ -164,6 +168,11 @@
struct snd_soc_codec;
struct soc_enum;
struct snd_soc_ac97_ops;
+struct snd_soc_jack;
+struct snd_soc_jack_pin;
+#ifdef CONFIG_GPIOLIB
+struct snd_soc_jack_gpio;
+#endif
typedef int (*hw_write_t)(void *,const char* ,int);
typedef int (*hw_read_t)(void *,char* ,int);
@@ -184,6 +193,19 @@
int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
const struct snd_pcm_hardware *hw);
+/* Jack reporting */
+int snd_soc_jack_new(struct snd_soc_card *card, const char *id, int type,
+ struct snd_soc_jack *jack);
+void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask);
+int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
+ struct snd_soc_jack_pin *pins);
+#ifdef CONFIG_GPIOLIB
+int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
+ struct snd_soc_jack_gpio *gpios);
+void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
+ struct snd_soc_jack_gpio *gpios);
+#endif
+
/* codec IO */
#define snd_soc_read(codec, reg) codec->read(codec, reg)
#define snd_soc_write(codec, reg, value) codec->write(codec, reg, value)
@@ -203,6 +225,8 @@
*/
struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
void *data, char *long_name);
+int snd_soc_add_controls(struct snd_soc_codec *codec,
+ const struct snd_kcontrol_new *controls, int num_controls);
int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol,
@@ -237,6 +261,48 @@
int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
+/**
+ * struct snd_soc_jack_pin - Describes a pin to update based on jack detection
+ *
+ * @pin: name of the pin to update
+ * @mask: bits to check for in reported jack status
+ * @invert: if non-zero then pin is enabled when status is not reported
+ */
+struct snd_soc_jack_pin {
+ struct list_head list;
+ const char *pin;
+ int mask;
+ bool invert;
+};
+
+/**
+ * struct snd_soc_jack_gpio - Describes a gpio pin for jack detection
+ *
+ * @gpio: gpio number
+ * @name: gpio name
+ * @report: value to report when jack detected
+ * @invert: report presence in low state
+ * @debouce_time: debouce time in ms
+ */
+#ifdef CONFIG_GPIOLIB
+struct snd_soc_jack_gpio {
+ unsigned int gpio;
+ const char *name;
+ int report;
+ int invert;
+ int debounce_time;
+ struct snd_soc_jack *jack;
+ struct work_struct work;
+};
+#endif
+
+struct snd_soc_jack {
+ struct snd_jack *jack;
+ struct snd_soc_card *card;
+ struct list_head pins;
+ int status;
+};
+
/* SoC PCM stream information */
struct snd_soc_pcm_stream {
char *stream_name;
@@ -384,6 +450,8 @@
struct snd_soc_device *socdev;
+ struct snd_soc_codec *codec;
+
struct snd_soc_platform *platform;
struct delayed_work delayed_work;
struct work_struct deferred_resume_work;
@@ -393,7 +461,6 @@
struct snd_soc_device {
struct device *dev;
struct snd_soc_card *card;
- struct snd_soc_codec *codec;
struct snd_soc_codec_device *codec_dev;
void *codec_data;
};
diff --git a/include/sound/uda1341.h b/include/sound/uda1341.h
deleted file mode 100644
index 110d5dc..0000000
--- a/include/sound/uda1341.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * linux/include/linux/l3/uda1341.h
- *
- * Philips UDA1341 mixer device driver for ALSA
- *
- * Copyright (c) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License.
- *
- * History:
- *
- * 2002-03-13 Tomas Kasparek Initial release - based on uda1341.h from OSS
- * 2002-03-30 Tomas Kasparek Proc filesystem support, complete mixer and DSP
- * features support
- */
-
-#define UDA1341_ALSA_NAME "snd-uda1341"
-
-/*
- * Default rate set after inicialization
- */
-#define AUDIO_RATE_DEFAULT 44100
-
-/*
- * UDA1341 L3 address and command types
- */
-#define UDA1341_L3ADDR 5
-#define UDA1341_DATA0 (UDA1341_L3ADDR << 2 | 0)
-#define UDA1341_DATA1 (UDA1341_L3ADDR << 2 | 1)
-#define UDA1341_STATUS (UDA1341_L3ADDR << 2 | 2)
-
-enum uda1341_onoff {
- OFF=0,
- ON,
-};
-
-enum uda1341_format {
- I2S=0,
- LSB16,
- LSB18,
- LSB20,
- MSB,
- LSB16MSB,
- LSB18MSB,
- LSB20MSB,
-};
-
-enum uda1341_fs {
- F512=0,
- F384,
- F256,
- Funused,
-};
-
-enum uda1341_peak {
- BEFORE=0,
- AFTER,
-};
-
-enum uda1341_filter {
- FLAT=0,
- MIN,
- MIN2,
- MAX,
-};
-
-enum uda1341_mixer {
- DOUBLE,
- LINE,
- MIC,
- MIXER,
-};
-
-enum uda1341_deemp {
- NONE,
- D32,
- D44,
- D48,
-};
-
-enum uda1341_config {
- CMD_READ_REG = 0,
- CMD_RESET,
- CMD_FS,
- CMD_FORMAT,
- CMD_OGAIN,
- CMD_IGAIN,
- CMD_DAC,
- CMD_ADC,
- CMD_VOLUME,
- CMD_BASS,
- CMD_TREBBLE,
- CMD_PEAK,
- CMD_DEEMP,
- CMD_MUTE,
- CMD_FILTER,
- CMD_CH1,
- CMD_CH2,
- CMD_MIC,
- CMD_MIXER,
- CMD_AGC,
- CMD_IG,
- CMD_AGC_TIME,
- CMD_AGC_LEVEL,
-#ifdef CONFIG_PM
- CMD_SUSPEND,
- CMD_RESUME,
-#endif
- CMD_LAST,
-};
-
-enum write_through {
- //used in update_bits (write_cfg) to avoid l3_write - just update local copy of regs.
- REGS_ONLY=0,
- //update local regs and write value to uda1341 - do l3_write
- FLUSH,
-};
-
-int __init snd_chip_uda1341_mixer_new(struct snd_card *card, struct l3_client **clnt);
-
-/*
- * Local variables:
- * indent-tabs-mode: t
- * End:
- */
diff --git a/include/sound/version.h b/include/sound/version.h
index 2b48237..a7e74e2 100644
--- a/include/sound/version.h
+++ b/include/sound/version.h
@@ -1,3 +1,3 @@
/* include/version.h */
-#define CONFIG_SND_VERSION "1.0.18a"
+#define CONFIG_SND_VERSION "1.0.19"
#define CONFIG_SND_DATE ""
diff --git a/include/sound/wss.h b/include/sound/wss.h
index fd01f22..6d65f32 100644
--- a/include/sound/wss.h
+++ b/include/sound/wss.h
@@ -154,6 +154,7 @@
unsigned short hardware,
unsigned short hwshare,
struct snd_wss **rchip);
+int snd_wss_free(struct snd_wss *chip);
int snd_wss_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm);
int snd_wss_timer(struct snd_wss *chip, int device, struct snd_timer **rtimer);
int snd_wss_mixer(struct snd_wss *chip);
diff --git a/ipc/shm.c b/ipc/shm.c
index 05d51d2..f239d87 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -39,6 +39,7 @@
#include <linux/nsproxy.h>
#include <linux/mount.h>
#include <linux/ipc_namespace.h>
+#include <linux/ima.h>
#include <asm/uaccess.h>
@@ -383,6 +384,7 @@
error = PTR_ERR(file);
if (IS_ERR(file))
goto no_file;
+ ima_shm_check(file);
id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
if (id < 0) {
@@ -887,6 +889,7 @@
file = alloc_file(path.mnt, path.dentry, f_mode, &shm_file_operations);
if (!file)
goto out_free;
+ ima_shm_check(file);
file->private_data = sfd;
file->f_mapping = shp->shm_file->f_mapping;
diff --git a/kernel/user.c b/kernel/user.c
index fbb300e..850e0ba 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -20,7 +20,7 @@
struct user_namespace init_user_ns = {
.kref = {
- .refcount = ATOMIC_INIT(1),
+ .refcount = ATOMIC_INIT(2),
},
.creator = &root_user,
};
diff --git a/lib/Kconfig b/lib/Kconfig
index 03c2c24..cea9e30 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -174,4 +174,10 @@
bool "Disable obsolete cpumask functions" if DEBUG_PER_CPU_MAPS
depends on EXPERIMENTAL && BROKEN
+#
+# Netlink attribute parsing support is select'ed if needed
+#
+config NLATTR
+ bool
+
endmenu
diff --git a/lib/Makefile b/lib/Makefile
index 8633d6b..0dd9229 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -84,6 +84,8 @@
obj-$(CONFIG_DYNAMIC_DEBUG) += dynamic_debug.o
+obj-$(CONFIG_NLATTR) += nlattr.o
+
hostprogs-y := gen_crc32table
clean-files := crc32table.h
diff --git a/net/netlink/attr.c b/lib/nlattr.c
similarity index 99%
rename from net/netlink/attr.c
rename to lib/nlattr.c
index 56c3ce7..80009a2 100644
--- a/net/netlink/attr.c
+++ b/lib/nlattr.c
@@ -281,6 +281,7 @@
return d;
}
+#ifdef CONFIG_NET
/**
* __nla_reserve - reserve room for attribute on the skb
* @skb: socket buffer to reserve room on
@@ -305,6 +306,7 @@
return nla;
}
+EXPORT_SYMBOL(__nla_reserve);
/**
* __nla_reserve_nohdr - reserve room for attribute without header
@@ -325,6 +327,7 @@
return start;
}
+EXPORT_SYMBOL(__nla_reserve_nohdr);
/**
* nla_reserve - reserve room for attribute on the skb
@@ -345,6 +348,7 @@
return __nla_reserve(skb, attrtype, attrlen);
}
+EXPORT_SYMBOL(nla_reserve);
/**
* nla_reserve_nohdr - reserve room for attribute without header
@@ -363,6 +367,7 @@
return __nla_reserve_nohdr(skb, attrlen);
}
+EXPORT_SYMBOL(nla_reserve_nohdr);
/**
* __nla_put - Add a netlink attribute to a socket buffer
@@ -382,6 +387,7 @@
nla = __nla_reserve(skb, attrtype, attrlen);
memcpy(nla_data(nla), data, attrlen);
}
+EXPORT_SYMBOL(__nla_put);
/**
* __nla_put_nohdr - Add a netlink attribute without header
@@ -399,6 +405,7 @@
start = __nla_reserve_nohdr(skb, attrlen);
memcpy(start, data, attrlen);
}
+EXPORT_SYMBOL(__nla_put_nohdr);
/**
* nla_put - Add a netlink attribute to a socket buffer
@@ -418,6 +425,7 @@
__nla_put(skb, attrtype, attrlen, data);
return 0;
}
+EXPORT_SYMBOL(nla_put);
/**
* nla_put_nohdr - Add a netlink attribute without header
@@ -436,6 +444,7 @@
__nla_put_nohdr(skb, attrlen, data);
return 0;
}
+EXPORT_SYMBOL(nla_put_nohdr);
/**
* nla_append - Add a netlink attribute without header or padding
@@ -454,20 +463,13 @@
memcpy(skb_put(skb, attrlen), data, attrlen);
return 0;
}
+EXPORT_SYMBOL(nla_append);
+#endif
EXPORT_SYMBOL(nla_validate);
EXPORT_SYMBOL(nla_parse);
EXPORT_SYMBOL(nla_find);
EXPORT_SYMBOL(nla_strlcpy);
-EXPORT_SYMBOL(__nla_reserve);
-EXPORT_SYMBOL(__nla_reserve_nohdr);
-EXPORT_SYMBOL(nla_reserve);
-EXPORT_SYMBOL(nla_reserve_nohdr);
-EXPORT_SYMBOL(__nla_put);
-EXPORT_SYMBOL(__nla_put_nohdr);
-EXPORT_SYMBOL(nla_put);
-EXPORT_SYMBOL(nla_put_nohdr);
EXPORT_SYMBOL(nla_memcpy);
EXPORT_SYMBOL(nla_memcmp);
EXPORT_SYMBOL(nla_strcmp);
-EXPORT_SYMBOL(nla_append);
diff --git a/mm/mmap.c b/mm/mmap.c
index 00ced3e..1abb918 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -20,6 +20,7 @@
#include <linux/fs.h>
#include <linux/personality.h>
#include <linux/security.h>
+#include <linux/ima.h>
#include <linux/hugetlb.h>
#include <linux/profile.h>
#include <linux/module.h>
@@ -1049,6 +1050,9 @@
error = security_file_mmap(file, reqprot, prot, flags, addr, 0);
if (error)
return error;
+ error = ima_file_mmap(file, prot);
+ if (error)
+ return error;
return mmap_region(file, addr, len, flags, vm_flags, pgoff);
}
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index 74dc57c..40ca7cd 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -66,7 +66,7 @@
/*
* Start background writeback (via pdflush) at this percentage
*/
-int dirty_background_ratio = 5;
+int dirty_background_ratio = 10;
/*
* dirty_background_bytes starts at 0 (disabled) so that it is a function of
@@ -83,7 +83,7 @@
/*
* The generator of dirty data starts writeback at this percentage
*/
-int vm_dirty_ratio = 10;
+int vm_dirty_ratio = 20;
/*
* vm_dirty_bytes starts at 0 (disabled) so that it is a function of
diff --git a/mm/shmem.c b/mm/shmem.c
index 4103a23..7ec78e2 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -28,6 +28,7 @@
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/swap.h>
+#include <linux/ima.h>
static struct vfsmount *shm_mnt;
@@ -2665,6 +2666,7 @@
if (IS_ERR(file))
return PTR_ERR(file);
+ ima_shm_check(file);
if (vma->vm_file)
fput(vma->vm_file);
vma->vm_file = file;
diff --git a/net/Kconfig b/net/Kconfig
index cdb8fde..eab40a4 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -4,6 +4,7 @@
menuconfig NET
bool "Networking support"
+ select NLATTR
---help---
Unless you really know what you are doing, you should say Y here.
The reason is that some programs need kernel networking support even
diff --git a/net/netlink/Makefile b/net/netlink/Makefile
index e3589c2..bdd6ddf 100644
--- a/net/netlink/Makefile
+++ b/net/netlink/Makefile
@@ -2,4 +2,4 @@
# Makefile for the netlink driver.
#
-obj-y := af_netlink.o attr.o genetlink.o
+obj-y := af_netlink.o genetlink.o
diff --git a/security/Kconfig b/security/Kconfig
index 9438535..bb24477 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -55,7 +55,8 @@
bool "Enable the securityfs filesystem"
help
This will build the securityfs filesystem. It is currently used by
- the TPM bios character driver. It is not used by SELinux or SMACK.
+ the TPM bios character driver and IMA, an integrity provider. It is
+ not used by SELinux or SMACK.
If you are unsure how to answer this question, answer N.
@@ -134,6 +135,9 @@
source security/selinux/Kconfig
source security/smack/Kconfig
+source security/tomoyo/Kconfig
+
+source security/integrity/ima/Kconfig
endmenu
diff --git a/security/Makefile b/security/Makefile
index c05c127..fa77021 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -5,6 +5,7 @@
obj-$(CONFIG_KEYS) += keys/
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
subdir-$(CONFIG_SECURITY_SMACK) += smack
+subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo
# always enable default capabilities
obj-y += commoncap.o
@@ -15,5 +16,10 @@
# Must precede capability.o in order to stack properly.
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o
+obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o
obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
+
+# Object integrity file lists
+subdir-$(CONFIG_IMA) += integrity/ima
+obj-$(CONFIG_IMA) += integrity/ima/built-in.o
diff --git a/security/inode.c b/security/inode.c
index 007ef25..f3b91bf 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -202,12 +202,11 @@
* This function returns a pointer to a dentry if it succeeds. This
* pointer must be passed to the securityfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded,
- * you are responsible here). If an error occurs, %NULL is returned.
+ * you are responsible here). If an error occurs, the function will return
+ * the erorr value (via ERR_PTR).
*
* If securityfs is not enabled in the kernel, the value %-ENODEV is
- * returned. It is not wise to check for this value, but rather, check for
- * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
- * code.
+ * returned.
*/
struct dentry *securityfs_create_file(const char *name, mode_t mode,
struct dentry *parent, void *data,
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
new file mode 100644
index 0000000..53d9764
--- /dev/null
+++ b/security/integrity/ima/Kconfig
@@ -0,0 +1,55 @@
+# IBM Integrity Measurement Architecture
+#
+config IMA
+ bool "Integrity Measurement Architecture(IMA)"
+ depends on ACPI
+ select SECURITYFS
+ select CRYPTO
+ select CRYPTO_HMAC
+ select CRYPTO_MD5
+ select CRYPTO_SHA1
+ select TCG_TPM
+ select TCG_TIS
+ help
+ The Trusted Computing Group(TCG) runtime Integrity
+ Measurement Architecture(IMA) maintains a list of hash
+ values of executables and other sensitive system files,
+ as they are read or executed. If an attacker manages
+ to change the contents of an important system file
+ being measured, we can tell.
+
+ If your system has a TPM chip, then IMA also maintains
+ an aggregate integrity value over this list inside the
+ TPM hardware, so that the TPM can prove to a third party
+ whether or not critical system files have been modified.
+ Read <http://www.usenix.org/events/sec04/tech/sailer.html>
+ to learn more about IMA.
+ If unsure, say N.
+
+config IMA_MEASURE_PCR_IDX
+ int
+ depends on IMA
+ range 8 14
+ default 10
+ help
+ IMA_MEASURE_PCR_IDX determines the TPM PCR register index
+ that IMA uses to maintain the integrity aggregate of the
+ measurement list. If unsure, use the default 10.
+
+config IMA_AUDIT
+ bool
+ depends on IMA
+ default y
+ help
+ This option adds a kernel parameter 'ima_audit', which
+ allows informational auditing messages to be enabled
+ at boot. If this option is selected, informational integrity
+ auditing messages can be enabled with 'ima_audit=1' on
+ the kernel command line.
+
+config IMA_LSM_RULES
+ bool
+ depends on IMA && AUDIT && (SECURITY_SELINUX || SECURITY_SMACK)
+ default y
+ help
+ Disabling this option will disregard LSM based policy rules.
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
new file mode 100644
index 0000000..787c4cb
--- /dev/null
+++ b/security/integrity/ima/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for building Trusted Computing Group's(TCG) runtime Integrity
+# Measurement Architecture(IMA).
+#
+
+obj-$(CONFIG_IMA) += ima.o
+
+ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
+ ima_policy.o ima_iint.o ima_audit.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
new file mode 100644
index 0000000..165eb53
--- /dev/null
+++ b/security/integrity/ima/ima.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Mimi Zohar <zohar@us.ibm.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, version 2 of the
+ * License.
+ *
+ * File: ima.h
+ * internal Integrity Measurement Architecture (IMA) definitions
+ */
+
+#ifndef __LINUX_IMA_H
+#define __LINUX_IMA_H
+
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <linux/security.h>
+#include <linux/hash.h>
+#include <linux/tpm.h>
+#include <linux/audit.h>
+
+enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII };
+enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
+
+/* digest size for IMA, fits SHA1 or MD5 */
+#define IMA_DIGEST_SIZE 20
+#define IMA_EVENT_NAME_LEN_MAX 255
+
+#define IMA_HASH_BITS 9
+#define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
+
+/* set during initialization */
+extern int ima_initialized;
+extern int ima_used_chip;
+extern char *ima_hash;
+
+/* IMA inode template definition */
+struct ima_template_data {
+ u8 digest[IMA_DIGEST_SIZE]; /* sha1/md5 measurement hash */
+ char file_name[IMA_EVENT_NAME_LEN_MAX + 1]; /* name + \0 */
+};
+
+struct ima_template_entry {
+ u8 digest[IMA_DIGEST_SIZE]; /* sha1 or md5 measurement hash */
+ const char *template_name;
+ int template_len;
+ struct ima_template_data template;
+};
+
+struct ima_queue_entry {
+ struct hlist_node hnext; /* place in hash collision list */
+ struct list_head later; /* place in ima_measurements list */
+ struct ima_template_entry *entry;
+};
+extern struct list_head ima_measurements; /* list of all measurements */
+
+/* declarations */
+void integrity_audit_msg(int audit_msgno, struct inode *inode,
+ const unsigned char *fname, const char *op,
+ const char *cause, int result, int info);
+
+/* Internal IMA function definitions */
+void ima_iintcache_init(void);
+int ima_init(void);
+void ima_cleanup(void);
+int ima_fs_init(void);
+void ima_fs_cleanup(void);
+int ima_add_template_entry(struct ima_template_entry *entry, int violation,
+ const char *op, struct inode *inode);
+int ima_calc_hash(struct file *file, char *digest);
+int ima_calc_template_hash(int template_len, void *template, char *digest);
+int ima_calc_boot_aggregate(char *digest);
+void ima_add_violation(struct inode *inode, const unsigned char *filename,
+ const char *op, const char *cause);
+
+/*
+ * used to protect h_table and sha_table
+ */
+extern spinlock_t ima_queue_lock;
+
+struct ima_h_table {
+ atomic_long_t len; /* number of stored measurements in the list */
+ atomic_long_t violations;
+ struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
+};
+extern struct ima_h_table ima_htable;
+
+static inline unsigned long ima_hash_key(u8 *digest)
+{
+ return hash_long(*digest, IMA_HASH_BITS);
+}
+
+/* iint cache flags */
+#define IMA_MEASURED 1
+#define IMA_IINT_DUMP_STACK 512
+
+/* integrity data associated with an inode */
+struct ima_iint_cache {
+ u64 version; /* track inode changes */
+ unsigned long flags;
+ u8 digest[IMA_DIGEST_SIZE];
+ struct mutex mutex; /* protects: version, flags, digest */
+ long readcount; /* measured files readcount */
+ long writecount; /* measured files writecount */
+ long opencount; /* opens reference count */
+ struct kref refcount; /* ima_iint_cache reference count */
+ struct rcu_head rcu;
+};
+
+/* LIM API function definitions */
+int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode,
+ int mask, int function);
+int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file);
+void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
+ const unsigned char *filename);
+int ima_store_template(struct ima_template_entry *entry, int violation,
+ struct inode *inode);
+void ima_template_show(struct seq_file *m, void *e,
+ enum ima_show_type show);
+
+/* radix tree calls to lookup, insert, delete
+ * integrity data associated with an inode.
+ */
+struct ima_iint_cache *ima_iint_insert(struct inode *inode);
+struct ima_iint_cache *ima_iint_find_get(struct inode *inode);
+struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode);
+void ima_iint_delete(struct inode *inode);
+void iint_free(struct kref *kref);
+void iint_rcu_free(struct rcu_head *rcu);
+
+/* IMA policy related functions */
+enum ima_hooks { PATH_CHECK = 1, FILE_MMAP, BPRM_CHECK };
+
+int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask);
+void ima_init_policy(void);
+void ima_update_policy(void);
+int ima_parse_add_rule(char *);
+void ima_delete_rules(void);
+
+/* LSM based policy rules require audit */
+#ifdef CONFIG_IMA_LSM_RULES
+
+#define security_filter_rule_init security_audit_rule_init
+#define security_filter_rule_match security_audit_rule_match
+
+#else
+
+static inline int security_filter_rule_init(u32 field, u32 op, char *rulestr,
+ void **lsmrule)
+{
+ return -EINVAL;
+}
+
+static inline int security_filter_rule_match(u32 secid, u32 field, u32 op,
+ void *lsmrule,
+ struct audit_context *actx)
+{
+ return -EINVAL;
+}
+#endif /* CONFIG_IMA_LSM_RULES */
+#endif
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
new file mode 100644
index 0000000..3cd58b6
--- /dev/null
+++ b/security/integrity/ima/ima_api.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ *
+ * Author: Mimi Zohar <zohar@us.ibm.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, version 2 of the
+ * License.
+ *
+ * File: ima_api.c
+ * Implements must_measure, collect_measurement, store_measurement,
+ * and store_template.
+ */
+#include <linux/module.h>
+
+#include "ima.h"
+static const char *IMA_TEMPLATE_NAME = "ima";
+
+/*
+ * ima_store_template - store ima template measurements
+ *
+ * Calculate the hash of a template entry, add the template entry
+ * to an ordered list of measurement entries maintained inside the kernel,
+ * and also update the aggregate integrity value (maintained inside the
+ * configured TPM PCR) over the hashes of the current list of measurement
+ * entries.
+ *
+ * Applications retrieve the current kernel-held measurement list through
+ * the securityfs entries in /sys/kernel/security/ima. The signed aggregate
+ * TPM PCR (called quote) can be retrieved using a TPM user space library
+ * and is used to validate the measurement list.
+ *
+ * Returns 0 on success, error code otherwise
+ */
+int ima_store_template(struct ima_template_entry *entry,
+ int violation, struct inode *inode)
+{
+ const char *op = "add_template_measure";
+ const char *audit_cause = "hashing_error";
+ int result;
+
+ memset(entry->digest, 0, sizeof(entry->digest));
+ entry->template_name = IMA_TEMPLATE_NAME;
+ entry->template_len = sizeof(entry->template);
+
+ if (!violation) {
+ result = ima_calc_template_hash(entry->template_len,
+ &entry->template,
+ entry->digest);
+ if (result < 0) {
+ integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
+ entry->template_name, op,
+ audit_cause, result, 0);
+ return result;
+ }
+ }
+ result = ima_add_template_entry(entry, violation, op, inode);
+ return result;
+}
+
+/*
+ * ima_add_violation - add violation to measurement list.
+ *
+ * Violations are flagged in the measurement list with zero hash values.
+ * By extending the PCR with 0xFF's instead of with zeroes, the PCR
+ * value is invalidated.
+ */
+void ima_add_violation(struct inode *inode, const unsigned char *filename,
+ const char *op, const char *cause)
+{
+ struct ima_template_entry *entry;
+ int violation = 1;
+ int result;
+
+ /* can overflow, only indicator */
+ atomic_long_inc(&ima_htable.violations);
+
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ result = -ENOMEM;
+ goto err_out;
+ }
+ memset(&entry->template, 0, sizeof(entry->template));
+ strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
+ result = ima_store_template(entry, violation, inode);
+ if (result < 0)
+ kfree(entry);
+err_out:
+ integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
+ op, cause, result, 0);
+}
+
+/**
+ * ima_must_measure - measure decision based on policy.
+ * @inode: pointer to inode to measure
+ * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
+ * @function: calling function (PATH_CHECK, BPRM_CHECK, FILE_MMAP)
+ *
+ * The policy is defined in terms of keypairs:
+ * subj=, obj=, type=, func=, mask=, fsmagic=
+ * subj,obj, and type: are LSM specific.
+ * func: PATH_CHECK | BPRM_CHECK | FILE_MMAP
+ * mask: contains the permission mask
+ * fsmagic: hex value
+ *
+ * Must be called with iint->mutex held.
+ *
+ * Return 0 to measure. Return 1 if already measured.
+ * For matching a DONT_MEASURE policy, no policy, or other
+ * error, return an error code.
+*/
+int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode,
+ int mask, int function)
+{
+ int must_measure;
+
+ if (iint->flags & IMA_MEASURED)
+ return 1;
+
+ must_measure = ima_match_policy(inode, function, mask);
+ return must_measure ? 0 : -EACCES;
+}
+
+/*
+ * ima_collect_measurement - collect file measurement
+ *
+ * Calculate the file hash, if it doesn't already exist,
+ * storing the measurement and i_version in the iint.
+ *
+ * Must be called with iint->mutex held.
+ *
+ * Return 0 on success, error code otherwise
+ */
+int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
+{
+ int result = -EEXIST;
+
+ if (!(iint->flags & IMA_MEASURED)) {
+ u64 i_version = file->f_dentry->d_inode->i_version;
+
+ memset(iint->digest, 0, IMA_DIGEST_SIZE);
+ result = ima_calc_hash(file, iint->digest);
+ if (!result)
+ iint->version = i_version;
+ }
+ return result;
+}
+
+/*
+ * ima_store_measurement - store file measurement
+ *
+ * Create an "ima" template and then store the template by calling
+ * ima_store_template.
+ *
+ * We only get here if the inode has not already been measured,
+ * but the measurement could already exist:
+ * - multiple copies of the same file on either the same or
+ * different filesystems.
+ * - the inode was previously flushed as well as the iint info,
+ * containing the hashing info.
+ *
+ * Must be called with iint->mutex held.
+ */
+void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
+ const unsigned char *filename)
+{
+ const char *op = "add_template_measure";
+ const char *audit_cause = "ENOMEM";
+ int result = -ENOMEM;
+ struct inode *inode = file->f_dentry->d_inode;
+ struct ima_template_entry *entry;
+ int violation = 0;
+
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
+ op, audit_cause, result, 0);
+ return;
+ }
+ memset(&entry->template, 0, sizeof(entry->template));
+ memcpy(entry->template.digest, iint->digest, IMA_DIGEST_SIZE);
+ strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
+
+ result = ima_store_template(entry, violation, inode);
+ if (!result)
+ iint->flags |= IMA_MEASURED;
+ else
+ kfree(entry);
+}
diff --git a/security/integrity/ima/ima_audit.c b/security/integrity/ima/ima_audit.c
new file mode 100644
index 0000000..1e082bb
--- /dev/null
+++ b/security/integrity/ima/ima_audit.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ * Author: Mimi Zohar <zohar@us.ibm.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, version 2 of the License.
+ *
+ * File: integrity_audit.c
+ * Audit calls for the integrity subsystem
+ */
+
+#include <linux/fs.h>
+#include <linux/audit.h>
+#include "ima.h"
+
+static int ima_audit;
+
+#ifdef CONFIG_IMA_AUDIT
+
+/* ima_audit_setup - enable informational auditing messages */
+static int __init ima_audit_setup(char *str)
+{
+ unsigned long audit;
+ int rc, result = 0;
+ char *op = "ima_audit";
+ char *cause;
+
+ rc = strict_strtoul(str, 0, &audit);
+ if (rc || audit > 1)
+ result = 1;
+ else
+ ima_audit = audit;
+ cause = ima_audit ? "enabled" : "not_enabled";
+ integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
+ op, cause, result, 0);
+ return 1;
+}
+__setup("ima_audit=", ima_audit_setup);
+#endif
+
+void integrity_audit_msg(int audit_msgno, struct inode *inode,
+ const unsigned char *fname, const char *op,
+ const char *cause, int result, int audit_info)
+{
+ struct audit_buffer *ab;
+
+ if (!ima_audit && audit_info == 1) /* Skip informational messages */
+ return;
+
+ ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno);
+ audit_log_format(ab, "integrity: pid=%d uid=%u auid=%u ses=%u",
+ current->pid, current->cred->uid,
+ audit_get_loginuid(current),
+ audit_get_sessionid(current));
+ audit_log_task_context(ab);
+ switch (audit_msgno) {
+ case AUDIT_INTEGRITY_DATA:
+ case AUDIT_INTEGRITY_METADATA:
+ case AUDIT_INTEGRITY_PCR:
+ case AUDIT_INTEGRITY_STATUS:
+ audit_log_format(ab, " op=%s cause=%s", op, cause);
+ break;
+ case AUDIT_INTEGRITY_HASH:
+ audit_log_format(ab, " op=%s hash=%s", op, cause);
+ break;
+ default:
+ audit_log_format(ab, " op=%s", op);
+ }
+ audit_log_format(ab, " comm=");
+ audit_log_untrustedstring(ab, current->comm);
+ if (fname) {
+ audit_log_format(ab, " name=");
+ audit_log_untrustedstring(ab, fname);
+ }
+ if (inode)
+ audit_log_format(ab, " dev=%s ino=%lu",
+ inode->i_sb->s_id, inode->i_ino);
+ audit_log_format(ab, " res=%d", !result ? 0 : 1);
+ audit_log_end(ab);
+}
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
new file mode 100644
index 0000000..50d572b
--- /dev/null
+++ b/security/integrity/ima/ima_crypto.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.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, version 2 of the License.
+ *
+ * File: ima_crypto.c
+ * Calculates md5/sha1 file hash, template hash, boot-aggreate hash
+ */
+
+#include <linux/kernel.h>
+#include <linux/file.h>
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+#include <linux/err.h>
+#include "ima.h"
+
+static int init_desc(struct hash_desc *desc)
+{
+ int rc;
+
+ desc->tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(desc->tfm)) {
+ pr_info("failed to load %s transform: %ld\n",
+ ima_hash, PTR_ERR(desc->tfm));
+ rc = PTR_ERR(desc->tfm);
+ return rc;
+ }
+ desc->flags = 0;
+ rc = crypto_hash_init(desc);
+ if (rc)
+ crypto_free_hash(desc->tfm);
+ return rc;
+}
+
+/*
+ * Calculate the MD5/SHA1 file digest
+ */
+int ima_calc_hash(struct file *file, char *digest)
+{
+ struct hash_desc desc;
+ struct scatterlist sg[1];
+ loff_t i_size;
+ char *rbuf;
+ int rc, offset = 0;
+
+ rc = init_desc(&desc);
+ if (rc != 0)
+ return rc;
+
+ rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!rbuf) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ i_size = i_size_read(file->f_dentry->d_inode);
+ while (offset < i_size) {
+ int rbuf_len;
+
+ rbuf_len = kernel_read(file, offset, rbuf, PAGE_SIZE);
+ if (rbuf_len < 0) {
+ rc = rbuf_len;
+ break;
+ }
+ offset += rbuf_len;
+ sg_init_one(sg, rbuf, rbuf_len);
+
+ rc = crypto_hash_update(&desc, sg, rbuf_len);
+ if (rc)
+ break;
+ }
+ kfree(rbuf);
+ if (!rc)
+ rc = crypto_hash_final(&desc, digest);
+out:
+ crypto_free_hash(desc.tfm);
+ return rc;
+}
+
+/*
+ * Calculate the hash of a given template
+ */
+int ima_calc_template_hash(int template_len, void *template, char *digest)
+{
+ struct hash_desc desc;
+ struct scatterlist sg[1];
+ int rc;
+
+ rc = init_desc(&desc);
+ if (rc != 0)
+ return rc;
+
+ sg_init_one(sg, template, template_len);
+ rc = crypto_hash_update(&desc, sg, template_len);
+ if (!rc)
+ rc = crypto_hash_final(&desc, digest);
+ crypto_free_hash(desc.tfm);
+ return rc;
+}
+
+static void ima_pcrread(int idx, u8 *pcr)
+{
+ if (!ima_used_chip)
+ return;
+
+ if (tpm_pcr_read(TPM_ANY_NUM, idx, pcr) != 0)
+ pr_err("Error Communicating to TPM chip\n");
+}
+
+/*
+ * Calculate the boot aggregate hash
+ */
+int ima_calc_boot_aggregate(char *digest)
+{
+ struct hash_desc desc;
+ struct scatterlist sg;
+ u8 pcr_i[IMA_DIGEST_SIZE];
+ int rc, i;
+
+ rc = init_desc(&desc);
+ if (rc != 0)
+ return rc;
+
+ /* cumulative sha1 over tpm registers 0-7 */
+ for (i = TPM_PCR0; i < TPM_PCR8; i++) {
+ ima_pcrread(i, pcr_i);
+ /* now accumulate with current aggregate */
+ sg_init_one(&sg, pcr_i, IMA_DIGEST_SIZE);
+ rc = crypto_hash_update(&desc, &sg, IMA_DIGEST_SIZE);
+ }
+ if (!rc)
+ crypto_hash_final(&desc, digest);
+ crypto_free_hash(desc.tfm);
+ return rc;
+}
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
new file mode 100644
index 0000000..ffbe259
--- /dev/null
+++ b/security/integrity/ima/ima_fs.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Kylene Hall <kjhall@us.ibm.com>
+ * Reiner Sailer <sailer@us.ibm.com>
+ * Mimi Zohar <zohar@us.ibm.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, version 2 of the
+ * License.
+ *
+ * File: ima_fs.c
+ * implemenents security file system for reporting
+ * current measurement list and IMA statistics
+ */
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/rculist.h>
+#include <linux/rcupdate.h>
+#include <linux/parser.h>
+
+#include "ima.h"
+
+static int valid_policy = 1;
+#define TMPBUFLEN 12
+static ssize_t ima_show_htable_value(char __user *buf, size_t count,
+ loff_t *ppos, atomic_long_t *val)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t len;
+
+ len = scnprintf(tmpbuf, TMPBUFLEN, "%li\n", atomic_long_read(val));
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, len);
+}
+
+static ssize_t ima_show_htable_violations(struct file *filp,
+ char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return ima_show_htable_value(buf, count, ppos, &ima_htable.violations);
+}
+
+static struct file_operations ima_htable_violations_ops = {
+ .read = ima_show_htable_violations
+};
+
+static ssize_t ima_show_measurements_count(struct file *filp,
+ char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return ima_show_htable_value(buf, count, ppos, &ima_htable.len);
+
+}
+
+static struct file_operations ima_measurements_count_ops = {
+ .read = ima_show_measurements_count
+};
+
+/* returns pointer to hlist_node */
+static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
+{
+ loff_t l = *pos;
+ struct ima_queue_entry *qe;
+
+ /* we need a lock since pos could point beyond last element */
+ rcu_read_lock();
+ list_for_each_entry_rcu(qe, &ima_measurements, later) {
+ if (!l--) {
+ rcu_read_unlock();
+ return qe;
+ }
+ }
+ rcu_read_unlock();
+ return NULL;
+}
+
+static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct ima_queue_entry *qe = v;
+
+ /* lock protects when reading beyond last element
+ * against concurrent list-extension
+ */
+ rcu_read_lock();
+ qe = list_entry(rcu_dereference(qe->later.next),
+ struct ima_queue_entry, later);
+ rcu_read_unlock();
+ (*pos)++;
+
+ return (&qe->later == &ima_measurements) ? NULL : qe;
+}
+
+static void ima_measurements_stop(struct seq_file *m, void *v)
+{
+}
+
+static void ima_putc(struct seq_file *m, void *data, int datalen)
+{
+ while (datalen--)
+ seq_putc(m, *(char *)data++);
+}
+
+/* print format:
+ * 32bit-le=pcr#
+ * char[20]=template digest
+ * 32bit-le=template name size
+ * char[n]=template name
+ * eventdata[n]=template specific data
+ */
+static int ima_measurements_show(struct seq_file *m, void *v)
+{
+ /* the list never shrinks, so we don't need a lock here */
+ struct ima_queue_entry *qe = v;
+ struct ima_template_entry *e;
+ int namelen;
+ u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX;
+
+ /* get entry */
+ e = qe->entry;
+ if (e == NULL)
+ return -1;
+
+ /*
+ * 1st: PCRIndex
+ * PCR used is always the same (config option) in
+ * little-endian format
+ */
+ ima_putc(m, &pcr, sizeof pcr);
+
+ /* 2nd: template digest */
+ ima_putc(m, e->digest, IMA_DIGEST_SIZE);
+
+ /* 3rd: template name size */
+ namelen = strlen(e->template_name);
+ ima_putc(m, &namelen, sizeof namelen);
+
+ /* 4th: template name */
+ ima_putc(m, (void *)e->template_name, namelen);
+
+ /* 5th: template specific data */
+ ima_template_show(m, (struct ima_template_data *)&e->template,
+ IMA_SHOW_BINARY);
+ return 0;
+}
+
+static struct seq_operations ima_measurments_seqops = {
+ .start = ima_measurements_start,
+ .next = ima_measurements_next,
+ .stop = ima_measurements_stop,
+ .show = ima_measurements_show
+};
+
+static int ima_measurements_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &ima_measurments_seqops);
+}
+
+static struct file_operations ima_measurements_ops = {
+ .open = ima_measurements_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static void ima_print_digest(struct seq_file *m, u8 *digest)
+{
+ int i;
+
+ for (i = 0; i < IMA_DIGEST_SIZE; i++)
+ seq_printf(m, "%02x", *(digest + i));
+}
+
+void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show)
+{
+ struct ima_template_data *entry = e;
+ int namelen;
+
+ switch (show) {
+ case IMA_SHOW_ASCII:
+ ima_print_digest(m, entry->digest);
+ seq_printf(m, " %s\n", entry->file_name);
+ break;
+ case IMA_SHOW_BINARY:
+ ima_putc(m, entry->digest, IMA_DIGEST_SIZE);
+
+ namelen = strlen(entry->file_name);
+ ima_putc(m, &namelen, sizeof namelen);
+ ima_putc(m, entry->file_name, namelen);
+ default:
+ break;
+ }
+}
+
+/* print in ascii */
+static int ima_ascii_measurements_show(struct seq_file *m, void *v)
+{
+ /* the list never shrinks, so we don't need a lock here */
+ struct ima_queue_entry *qe = v;
+ struct ima_template_entry *e;
+
+ /* get entry */
+ e = qe->entry;
+ if (e == NULL)
+ return -1;
+
+ /* 1st: PCR used (config option) */
+ seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX);
+
+ /* 2nd: SHA1 template hash */
+ ima_print_digest(m, e->digest);
+
+ /* 3th: template name */
+ seq_printf(m, " %s ", e->template_name);
+
+ /* 4th: template specific data */
+ ima_template_show(m, (struct ima_template_data *)&e->template,
+ IMA_SHOW_ASCII);
+ return 0;
+}
+
+static struct seq_operations ima_ascii_measurements_seqops = {
+ .start = ima_measurements_start,
+ .next = ima_measurements_next,
+ .stop = ima_measurements_stop,
+ .show = ima_ascii_measurements_show
+};
+
+static int ima_ascii_measurements_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &ima_ascii_measurements_seqops);
+}
+
+static struct file_operations ima_ascii_measurements_ops = {
+ .open = ima_ascii_measurements_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static ssize_t ima_write_policy(struct file *file, const char __user *buf,
+ size_t datalen, loff_t *ppos)
+{
+ char *data;
+ int rc;
+
+ if (datalen >= PAGE_SIZE)
+ return -ENOMEM;
+ if (*ppos != 0) {
+ /* No partial writes. */
+ return -EINVAL;
+ }
+ data = kmalloc(datalen + 1, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (copy_from_user(data, buf, datalen)) {
+ kfree(data);
+ return -EFAULT;
+ }
+ *(data + datalen) = '\0';
+ rc = ima_parse_add_rule(data);
+ if (rc < 0) {
+ datalen = -EINVAL;
+ valid_policy = 0;
+ }
+
+ kfree(data);
+ return datalen;
+}
+
+static struct dentry *ima_dir;
+static struct dentry *binary_runtime_measurements;
+static struct dentry *ascii_runtime_measurements;
+static struct dentry *runtime_measurements_count;
+static struct dentry *violations;
+static struct dentry *ima_policy;
+
+static atomic_t policy_opencount = ATOMIC_INIT(1);
+/*
+ * ima_open_policy: sequentialize access to the policy file
+ */
+int ima_open_policy(struct inode * inode, struct file * filp)
+{
+ if (atomic_dec_and_test(&policy_opencount))
+ return 0;
+ return -EBUSY;
+}
+
+/*
+ * ima_release_policy - start using the new measure policy rules.
+ *
+ * Initially, ima_measure points to the default policy rules, now
+ * point to the new policy rules, and remove the securityfs policy file,
+ * assuming a valid policy.
+ */
+static int ima_release_policy(struct inode *inode, struct file *file)
+{
+ if (!valid_policy) {
+ ima_delete_rules();
+ valid_policy = 1;
+ atomic_set(&policy_opencount, 1);
+ return 0;
+ }
+ ima_update_policy();
+ securityfs_remove(ima_policy);
+ ima_policy = NULL;
+ return 0;
+}
+
+static struct file_operations ima_measure_policy_ops = {
+ .open = ima_open_policy,
+ .write = ima_write_policy,
+ .release = ima_release_policy
+};
+
+int ima_fs_init(void)
+{
+ ima_dir = securityfs_create_dir("ima", NULL);
+ if (IS_ERR(ima_dir))
+ return -1;
+
+ binary_runtime_measurements =
+ securityfs_create_file("binary_runtime_measurements",
+ S_IRUSR | S_IRGRP, ima_dir, NULL,
+ &ima_measurements_ops);
+ if (IS_ERR(binary_runtime_measurements))
+ goto out;
+
+ ascii_runtime_measurements =
+ securityfs_create_file("ascii_runtime_measurements",
+ S_IRUSR | S_IRGRP, ima_dir, NULL,
+ &ima_ascii_measurements_ops);
+ if (IS_ERR(ascii_runtime_measurements))
+ goto out;
+
+ runtime_measurements_count =
+ securityfs_create_file("runtime_measurements_count",
+ S_IRUSR | S_IRGRP, ima_dir, NULL,
+ &ima_measurements_count_ops);
+ if (IS_ERR(runtime_measurements_count))
+ goto out;
+
+ violations =
+ securityfs_create_file("violations", S_IRUSR | S_IRGRP,
+ ima_dir, NULL, &ima_htable_violations_ops);
+ if (IS_ERR(violations))
+ goto out;
+
+ ima_policy = securityfs_create_file("policy",
+ S_IRUSR | S_IRGRP | S_IWUSR,
+ ima_dir, NULL,
+ &ima_measure_policy_ops);
+ if (IS_ERR(ima_policy))
+ goto out;
+
+ return 0;
+out:
+ securityfs_remove(runtime_measurements_count);
+ securityfs_remove(ascii_runtime_measurements);
+ securityfs_remove(binary_runtime_measurements);
+ securityfs_remove(ima_dir);
+ securityfs_remove(ima_policy);
+ return -1;
+}
+
+void __exit ima_fs_cleanup(void)
+{
+ securityfs_remove(violations);
+ securityfs_remove(runtime_measurements_count);
+ securityfs_remove(ascii_runtime_measurements);
+ securityfs_remove(binary_runtime_measurements);
+ securityfs_remove(ima_dir);
+ securityfs_remove(ima_policy);
+}
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c
new file mode 100644
index 0000000..ec79f1e
--- /dev/null
+++ b/security/integrity/ima/ima_iint.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.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, version 2 of the
+ * License.
+ *
+ * File: ima_iint.c
+ * - implements the IMA hooks: ima_inode_alloc, ima_inode_free
+ * - cache integrity information associated with an inode
+ * using a radix tree.
+ */
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/radix-tree.h>
+#include "ima.h"
+
+#define ima_iint_delete ima_inode_free
+
+RADIX_TREE(ima_iint_store, GFP_ATOMIC);
+DEFINE_SPINLOCK(ima_iint_lock);
+
+static struct kmem_cache *iint_cache __read_mostly;
+
+/* ima_iint_find_get - return the iint associated with an inode
+ *
+ * ima_iint_find_get gets a reference to the iint. Caller must
+ * remember to put the iint reference.
+ */
+struct ima_iint_cache *ima_iint_find_get(struct inode *inode)
+{
+ struct ima_iint_cache *iint;
+
+ rcu_read_lock();
+ iint = radix_tree_lookup(&ima_iint_store, (unsigned long)inode);
+ if (!iint)
+ goto out;
+ kref_get(&iint->refcount);
+out:
+ rcu_read_unlock();
+ return iint;
+}
+
+/* Allocate memory for the iint associated with the inode
+ * from the iint_cache slab, initialize the iint, and
+ * insert it into the radix tree.
+ *
+ * On success return a pointer to the iint; on failure return NULL.
+ */
+struct ima_iint_cache *ima_iint_insert(struct inode *inode)
+{
+ struct ima_iint_cache *iint = NULL;
+ int rc = 0;
+
+ if (!ima_initialized)
+ return iint;
+ iint = kmem_cache_alloc(iint_cache, GFP_KERNEL);
+ if (!iint)
+ return iint;
+
+ rc = radix_tree_preload(GFP_KERNEL);
+ if (rc < 0)
+ goto out;
+
+ spin_lock(&ima_iint_lock);
+ rc = radix_tree_insert(&ima_iint_store, (unsigned long)inode, iint);
+ spin_unlock(&ima_iint_lock);
+out:
+ if (rc < 0) {
+ kmem_cache_free(iint_cache, iint);
+ if (rc == -EEXIST) {
+ spin_lock(&ima_iint_lock);
+ iint = radix_tree_lookup(&ima_iint_store,
+ (unsigned long)inode);
+ spin_unlock(&ima_iint_lock);
+ } else
+ iint = NULL;
+ }
+ radix_tree_preload_end();
+ return iint;
+}
+
+/**
+ * ima_inode_alloc - allocate an iint associated with an inode
+ * @inode: pointer to the inode
+ *
+ * Return 0 on success, 1 on failure.
+ */
+int ima_inode_alloc(struct inode *inode)
+{
+ struct ima_iint_cache *iint;
+
+ if (!ima_initialized)
+ return 0;
+
+ iint = ima_iint_insert(inode);
+ if (!iint)
+ return 1;
+ return 0;
+}
+
+/* ima_iint_find_insert_get - get the iint associated with an inode
+ *
+ * Most insertions are done at inode_alloc, except those allocated
+ * before late_initcall. When the iint does not exist, allocate it,
+ * initialize and insert it, and increment the iint refcount.
+ *
+ * (Can't initialize at security_initcall before any inodes are
+ * allocated, got to wait at least until proc_init.)
+ *
+ * Return the iint.
+ */
+struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode)
+{
+ struct ima_iint_cache *iint = NULL;
+
+ iint = ima_iint_find_get(inode);
+ if (iint)
+ return iint;
+
+ iint = ima_iint_insert(inode);
+ if (iint)
+ kref_get(&iint->refcount);
+
+ return iint;
+}
+EXPORT_SYMBOL_GPL(ima_iint_find_insert_get);
+
+/* iint_free - called when the iint refcount goes to zero */
+void iint_free(struct kref *kref)
+{
+ struct ima_iint_cache *iint = container_of(kref, struct ima_iint_cache,
+ refcount);
+ iint->version = 0;
+ iint->flags = 0UL;
+ if (iint->readcount != 0) {
+ printk(KERN_INFO "%s: readcount: %ld\n", __FUNCTION__,
+ iint->readcount);
+ iint->readcount = 0;
+ }
+ if (iint->writecount != 0) {
+ printk(KERN_INFO "%s: writecount: %ld\n", __FUNCTION__,
+ iint->writecount);
+ iint->writecount = 0;
+ }
+ if (iint->opencount != 0) {
+ printk(KERN_INFO "%s: opencount: %ld\n", __FUNCTION__,
+ iint->opencount);
+ iint->opencount = 0;
+ }
+ kref_set(&iint->refcount, 1);
+ kmem_cache_free(iint_cache, iint);
+}
+
+void iint_rcu_free(struct rcu_head *rcu_head)
+{
+ struct ima_iint_cache *iint = container_of(rcu_head,
+ struct ima_iint_cache, rcu);
+ kref_put(&iint->refcount, iint_free);
+}
+
+/**
+ * ima_iint_delete - called on integrity_inode_free
+ * @inode: pointer to the inode
+ *
+ * Free the integrity information(iint) associated with an inode.
+ */
+void ima_iint_delete(struct inode *inode)
+{
+ struct ima_iint_cache *iint;
+
+ if (!ima_initialized)
+ return;
+ spin_lock(&ima_iint_lock);
+ iint = radix_tree_delete(&ima_iint_store, (unsigned long)inode);
+ spin_unlock(&ima_iint_lock);
+ if (iint)
+ call_rcu(&iint->rcu, iint_rcu_free);
+}
+
+static void init_once(void *foo)
+{
+ struct ima_iint_cache *iint = foo;
+
+ memset(iint, 0, sizeof *iint);
+ iint->version = 0;
+ iint->flags = 0UL;
+ mutex_init(&iint->mutex);
+ iint->readcount = 0;
+ iint->writecount = 0;
+ iint->opencount = 0;
+ kref_set(&iint->refcount, 1);
+}
+
+void ima_iintcache_init(void)
+{
+ iint_cache =
+ kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0,
+ SLAB_PANIC, init_once);
+}
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
new file mode 100644
index 0000000..0b0bb8c
--- /dev/null
+++ b/security/integrity/ima/ima_init.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Mimi Zohar <zohar@us.ibm.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, version 2 of the
+ * License.
+ *
+ * File: ima_init.c
+ * initialization and cleanup functions
+ */
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include <linux/err.h>
+#include "ima.h"
+
+/* name for boot aggregate entry */
+static const char *boot_aggregate_name = "boot_aggregate";
+int ima_used_chip;
+
+/* Add the boot aggregate to the IMA measurement list and extend
+ * the PCR register.
+ *
+ * Calculate the boot aggregate, a SHA1 over tpm registers 0-7,
+ * assuming a TPM chip exists, and zeroes if the TPM chip does not
+ * exist. Add the boot aggregate measurement to the measurement
+ * list and extend the PCR register.
+ *
+ * If a tpm chip does not exist, indicate the core root of trust is
+ * not hardware based by invalidating the aggregate PCR value.
+ * (The aggregate PCR value is invalidated by adding one value to
+ * the measurement list and extending the aggregate PCR value with
+ * a different value.) Violations add a zero entry to the measurement
+ * list and extend the aggregate PCR value with ff...ff's.
+ */
+static void ima_add_boot_aggregate(void)
+{
+ struct ima_template_entry *entry;
+ const char *op = "add_boot_aggregate";
+ const char *audit_cause = "ENOMEM";
+ int result = -ENOMEM;
+ int violation = 1;
+
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ goto err_out;
+
+ memset(&entry->template, 0, sizeof(entry->template));
+ strncpy(entry->template.file_name, boot_aggregate_name,
+ IMA_EVENT_NAME_LEN_MAX);
+ if (ima_used_chip) {
+ violation = 0;
+ result = ima_calc_boot_aggregate(entry->template.digest);
+ if (result < 0) {
+ audit_cause = "hashing_error";
+ kfree(entry);
+ goto err_out;
+ }
+ }
+ result = ima_store_template(entry, violation, NULL);
+ if (result < 0)
+ kfree(entry);
+ return;
+err_out:
+ integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, boot_aggregate_name, op,
+ audit_cause, result, 0);
+}
+
+int ima_init(void)
+{
+ u8 pcr_i[IMA_DIGEST_SIZE];
+ int rc;
+
+ ima_used_chip = 0;
+ rc = tpm_pcr_read(TPM_ANY_NUM, 0, pcr_i);
+ if (rc == 0)
+ ima_used_chip = 1;
+
+ if (!ima_used_chip)
+ pr_info("No TPM chip found, activating TPM-bypass!\n");
+
+ ima_add_boot_aggregate(); /* boot aggregate must be first entry */
+ ima_init_policy();
+
+ return ima_fs_init();
+}
+
+void __exit ima_cleanup(void)
+{
+ ima_fs_cleanup();
+}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
new file mode 100644
index 0000000..f4e7266
--- /dev/null
+++ b/security/integrity/ima/ima_main.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Serge Hallyn <serue@us.ibm.com>
+ * Kylene Hall <kylene@us.ibm.com>
+ * Mimi Zohar <zohar@us.ibm.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, version 2 of the
+ * License.
+ *
+ * File: ima_main.c
+ * implements the IMA hooks: ima_bprm_check, ima_file_mmap,
+ * and ima_path_check.
+ */
+#include <linux/module.h>
+#include <linux/file.h>
+#include <linux/binfmts.h>
+#include <linux/mount.h>
+#include <linux/mman.h>
+
+#include "ima.h"
+
+int ima_initialized;
+
+char *ima_hash = "sha1";
+static int __init hash_setup(char *str)
+{
+ const char *op = "hash_setup";
+ const char *hash = "sha1";
+ int result = 0;
+ int audit_info = 0;
+
+ if (strncmp(str, "md5", 3) == 0) {
+ hash = "md5";
+ ima_hash = str;
+ } else if (strncmp(str, "sha1", 4) != 0) {
+ hash = "invalid_hash_type";
+ result = 1;
+ }
+ integrity_audit_msg(AUDIT_INTEGRITY_HASH, NULL, NULL, op, hash,
+ result, audit_info);
+ return 1;
+}
+__setup("ima_hash=", hash_setup);
+
+/**
+ * ima_file_free - called on __fput()
+ * @file: pointer to file structure being freed
+ *
+ * Flag files that changed, based on i_version;
+ * and decrement the iint readcount/writecount.
+ */
+void ima_file_free(struct file *file)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ struct ima_iint_cache *iint;
+
+ if (!ima_initialized || !S_ISREG(inode->i_mode))
+ return;
+ iint = ima_iint_find_get(inode);
+ if (!iint)
+ return;
+
+ mutex_lock(&iint->mutex);
+ if (iint->opencount <= 0) {
+ printk(KERN_INFO
+ "%s: %s open/free imbalance (r:%ld w:%ld o:%ld f:%ld)\n",
+ __FUNCTION__, file->f_dentry->d_name.name,
+ iint->readcount, iint->writecount,
+ iint->opencount, atomic_long_read(&file->f_count));
+ if (!(iint->flags & IMA_IINT_DUMP_STACK)) {
+ dump_stack();
+ iint->flags |= IMA_IINT_DUMP_STACK;
+ }
+ }
+ iint->opencount--;
+
+ if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
+ iint->readcount--;
+
+ if (file->f_mode & FMODE_WRITE) {
+ iint->writecount--;
+ if (iint->writecount == 0) {
+ if (iint->version != inode->i_version)
+ iint->flags &= ~IMA_MEASURED;
+ }
+ }
+ mutex_unlock(&iint->mutex);
+ kref_put(&iint->refcount, iint_free);
+}
+
+/* ima_read_write_check - reflect possible reading/writing errors in the PCR.
+ *
+ * When opening a file for read, if the file is already open for write,
+ * the file could change, resulting in a file measurement error.
+ *
+ * Opening a file for write, if the file is already open for read, results
+ * in a time of measure, time of use (ToMToU) error.
+ *
+ * In either case invalidate the PCR.
+ */
+enum iint_pcr_error { TOMTOU, OPEN_WRITERS };
+static void ima_read_write_check(enum iint_pcr_error error,
+ struct ima_iint_cache *iint,
+ struct inode *inode,
+ const unsigned char *filename)
+{
+ switch (error) {
+ case TOMTOU:
+ if (iint->readcount > 0)
+ ima_add_violation(inode, filename, "invalid_pcr",
+ "ToMToU");
+ break;
+ case OPEN_WRITERS:
+ if (iint->writecount > 0)
+ ima_add_violation(inode, filename, "invalid_pcr",
+ "open_writers");
+ break;
+ }
+}
+
+static int get_path_measurement(struct ima_iint_cache *iint, struct file *file,
+ const unsigned char *filename)
+{
+ int rc = 0;
+
+ if (IS_ERR(file)) {
+ pr_info("%s dentry_open failed\n", filename);
+ return rc;
+ }
+ iint->opencount++;
+ iint->readcount++;
+
+ rc = ima_collect_measurement(iint, file);
+ if (!rc)
+ ima_store_measurement(iint, file, filename);
+ return rc;
+}
+
+/**
+ * ima_path_check - based on policy, collect/store measurement.
+ * @path: contains a pointer to the path to be measured
+ * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE
+ *
+ * Measure the file being open for readonly, based on the
+ * ima_must_measure() policy decision.
+ *
+ * Keep read/write counters for all files, but only
+ * invalidate the PCR for measured files:
+ * - Opening a file for write when already open for read,
+ * results in a time of measure, time of use (ToMToU) error.
+ * - Opening a file for read when already open for write,
+ * could result in a file measurement error.
+ *
+ * Return 0 on success, an error code on failure.
+ * (Based on the results of appraise_measurement().)
+ */
+int ima_path_check(struct path *path, int mask)
+{
+ struct inode *inode = path->dentry->d_inode;
+ struct ima_iint_cache *iint;
+ struct file *file = NULL;
+ int rc;
+
+ if (!ima_initialized || !S_ISREG(inode->i_mode))
+ return 0;
+ iint = ima_iint_find_insert_get(inode);
+ if (!iint)
+ return 0;
+
+ mutex_lock(&iint->mutex);
+ iint->opencount++;
+ if ((mask & MAY_WRITE) || (mask == 0))
+ iint->writecount++;
+ else if (mask & (MAY_READ | MAY_EXEC))
+ iint->readcount++;
+
+ rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK);
+ if (rc < 0)
+ goto out;
+
+ if ((mask & MAY_WRITE) || (mask == 0))
+ ima_read_write_check(TOMTOU, iint, inode,
+ path->dentry->d_name.name);
+
+ if ((mask & (MAY_WRITE | MAY_READ | MAY_EXEC)) != MAY_READ)
+ goto out;
+
+ ima_read_write_check(OPEN_WRITERS, iint, inode,
+ path->dentry->d_name.name);
+ if (!(iint->flags & IMA_MEASURED)) {
+ struct dentry *dentry = dget(path->dentry);
+ struct vfsmount *mnt = mntget(path->mnt);
+
+ file = dentry_open(dentry, mnt, O_RDONLY, current->cred);
+ rc = get_path_measurement(iint, file, dentry->d_name.name);
+ }
+out:
+ mutex_unlock(&iint->mutex);
+ if (file)
+ fput(file);
+ kref_put(&iint->refcount, iint_free);
+ return 0;
+}
+
+static int process_measurement(struct file *file, const unsigned char *filename,
+ int mask, int function)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ struct ima_iint_cache *iint;
+ int rc;
+
+ if (!ima_initialized || !S_ISREG(inode->i_mode))
+ return 0;
+ iint = ima_iint_find_insert_get(inode);
+ if (!iint)
+ return -ENOMEM;
+
+ mutex_lock(&iint->mutex);
+ rc = ima_must_measure(iint, inode, mask, function);
+ if (rc != 0)
+ goto out;
+
+ rc = ima_collect_measurement(iint, file);
+ if (!rc)
+ ima_store_measurement(iint, file, filename);
+out:
+ mutex_unlock(&iint->mutex);
+ kref_put(&iint->refcount, iint_free);
+ return rc;
+}
+
+static void opencount_get(struct file *file)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ struct ima_iint_cache *iint;
+
+ if (!ima_initialized || !S_ISREG(inode->i_mode))
+ return;
+ iint = ima_iint_find_insert_get(inode);
+ if (!iint)
+ return;
+ mutex_lock(&iint->mutex);
+ iint->opencount++;
+ mutex_unlock(&iint->mutex);
+}
+
+/**
+ * ima_file_mmap - based on policy, collect/store measurement.
+ * @file: pointer to the file to be measured (May be NULL)
+ * @prot: contains the protection that will be applied by the kernel.
+ *
+ * Measure files being mmapped executable based on the ima_must_measure()
+ * policy decision.
+ *
+ * Return 0 on success, an error code on failure.
+ * (Based on the results of appraise_measurement().)
+ */
+int ima_file_mmap(struct file *file, unsigned long prot)
+{
+ int rc;
+
+ if (!file)
+ return 0;
+ if (prot & PROT_EXEC)
+ rc = process_measurement(file, file->f_dentry->d_name.name,
+ MAY_EXEC, FILE_MMAP);
+ return 0;
+}
+
+/*
+ * ima_shm_check - IPC shm and shmat create/fput a file
+ *
+ * Maintain the opencount for these files to prevent unnecessary
+ * imbalance messages.
+ */
+void ima_shm_check(struct file *file)
+{
+ opencount_get(file);
+ return;
+}
+
+/**
+ * ima_bprm_check - based on policy, collect/store measurement.
+ * @bprm: contains the linux_binprm structure
+ *
+ * The OS protects against an executable file, already open for write,
+ * from being executed in deny_write_access() and an executable file,
+ * already open for execute, from being modified in get_write_access().
+ * So we can be certain that what we verify and measure here is actually
+ * what is being executed.
+ *
+ * Return 0 on success, an error code on failure.
+ * (Based on the results of appraise_measurement().)
+ */
+int ima_bprm_check(struct linux_binprm *bprm)
+{
+ int rc;
+
+ rc = process_measurement(bprm->file, bprm->filename,
+ MAY_EXEC, BPRM_CHECK);
+ return 0;
+}
+
+static int __init init_ima(void)
+{
+ int error;
+
+ ima_iintcache_init();
+ error = ima_init();
+ ima_initialized = 1;
+ return error;
+}
+
+static void __exit cleanup_ima(void)
+{
+ ima_cleanup();
+}
+
+late_initcall(init_ima); /* Start IMA after the TPM is available */
+
+MODULE_DESCRIPTION("Integrity Measurement Architecture");
+MODULE_LICENSE("GPL");
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
new file mode 100644
index 0000000..b5291ad
--- /dev/null
+++ b/security/integrity/ima/ima_policy.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ * Author: Mimi Zohar <zohar@us.ibm.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, version 2 of the License.
+ *
+ * ima_policy.c
+ * - initialize default measure policy rules
+ *
+ */
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/security.h>
+#include <linux/magic.h>
+#include <linux/parser.h>
+
+#include "ima.h"
+
+/* flags definitions */
+#define IMA_FUNC 0x0001
+#define IMA_MASK 0x0002
+#define IMA_FSMAGIC 0x0004
+#define IMA_UID 0x0008
+
+enum ima_action { UNKNOWN = -1, DONT_MEASURE = 0, MEASURE };
+
+#define MAX_LSM_RULES 6
+enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
+ LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
+};
+
+struct ima_measure_rule_entry {
+ struct list_head list;
+ enum ima_action action;
+ unsigned int flags;
+ enum ima_hooks func;
+ int mask;
+ unsigned long fsmagic;
+ uid_t uid;
+ struct {
+ void *rule; /* LSM file metadata specific */
+ int type; /* audit type */
+ } lsm[MAX_LSM_RULES];
+};
+
+/* Without LSM specific knowledge, the default policy can only be
+ * written in terms of .action, .func, .mask, .fsmagic, and .uid
+ */
+static struct ima_measure_rule_entry default_rules[] = {
+ {.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,
+ .flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,
+ .flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE,.fsmagic = 0xF97CFF8C,.flags = IMA_FSMAGIC},
+ {.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC,
+ .flags = IMA_FUNC | IMA_MASK},
+ {.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
+ .flags = IMA_FUNC | IMA_MASK},
+ {.action = MEASURE,.func = PATH_CHECK,.mask = MAY_READ,.uid = 0,
+ .flags = IMA_FUNC | IMA_MASK | IMA_UID}
+};
+
+static LIST_HEAD(measure_default_rules);
+static LIST_HEAD(measure_policy_rules);
+static struct list_head *ima_measure;
+
+static DEFINE_MUTEX(ima_measure_mutex);
+
+/**
+ * ima_match_rules - determine whether an inode matches the measure rule.
+ * @rule: a pointer to a rule
+ * @inode: a pointer to an inode
+ * @func: LIM hook identifier
+ * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
+ *
+ * Returns true on rule match, false on failure.
+ */
+static bool ima_match_rules(struct ima_measure_rule_entry *rule,
+ struct inode *inode, enum ima_hooks func, int mask)
+{
+ struct task_struct *tsk = current;
+ int i;
+
+ if ((rule->flags & IMA_FUNC) && rule->func != func)
+ return false;
+ if ((rule->flags & IMA_MASK) && rule->mask != mask)
+ return false;
+ if ((rule->flags & IMA_FSMAGIC)
+ && rule->fsmagic != inode->i_sb->s_magic)
+ return false;
+ if ((rule->flags & IMA_UID) && rule->uid != tsk->cred->uid)
+ return false;
+ for (i = 0; i < MAX_LSM_RULES; i++) {
+ int rc;
+ u32 osid, sid;
+
+ if (!rule->lsm[i].rule)
+ continue;
+
+ switch (i) {
+ case LSM_OBJ_USER:
+ case LSM_OBJ_ROLE:
+ case LSM_OBJ_TYPE:
+ security_inode_getsecid(inode, &osid);
+ rc = security_filter_rule_match(osid,
+ rule->lsm[i].type,
+ AUDIT_EQUAL,
+ rule->lsm[i].rule,
+ NULL);
+ break;
+ case LSM_SUBJ_USER:
+ case LSM_SUBJ_ROLE:
+ case LSM_SUBJ_TYPE:
+ security_task_getsecid(tsk, &sid);
+ rc = security_filter_rule_match(sid,
+ rule->lsm[i].type,
+ AUDIT_EQUAL,
+ rule->lsm[i].rule,
+ NULL);
+ default:
+ break;
+ }
+ if (!rc)
+ return false;
+ }
+ return true;
+}
+
+/**
+ * ima_match_policy - decision based on LSM and other conditions
+ * @inode: pointer to an inode for which the policy decision is being made
+ * @func: IMA hook identifier
+ * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
+ *
+ * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
+ * conditions.
+ *
+ * (There is no need for locking when walking the policy list,
+ * as elements in the list are never deleted, nor does the list
+ * change.)
+ */
+int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask)
+{
+ struct ima_measure_rule_entry *entry;
+
+ list_for_each_entry(entry, ima_measure, list) {
+ bool rc;
+
+ rc = ima_match_rules(entry, inode, func, mask);
+ if (rc)
+ return entry->action;
+ }
+ return 0;
+}
+
+/**
+ * ima_init_policy - initialize the default measure rules.
+ *
+ * ima_measure points to either the measure_default_rules or the
+ * the new measure_policy_rules.
+ */
+void ima_init_policy(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(default_rules); i++)
+ list_add_tail(&default_rules[i].list, &measure_default_rules);
+ ima_measure = &measure_default_rules;
+}
+
+/**
+ * ima_update_policy - update default_rules with new measure rules
+ *
+ * Called on file .release to update the default rules with a complete new
+ * policy. Once updated, the policy is locked, no additional rules can be
+ * added to the policy.
+ */
+void ima_update_policy(void)
+{
+ const char *op = "policy_update";
+ const char *cause = "already exists";
+ int result = 1;
+ int audit_info = 0;
+
+ if (ima_measure == &measure_default_rules) {
+ ima_measure = &measure_policy_rules;
+ cause = "complete";
+ result = 0;
+ }
+ integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+ NULL, op, cause, result, audit_info);
+}
+
+enum {
+ Opt_err = -1,
+ Opt_measure = 1, Opt_dont_measure,
+ Opt_obj_user, Opt_obj_role, Opt_obj_type,
+ Opt_subj_user, Opt_subj_role, Opt_subj_type,
+ Opt_func, Opt_mask, Opt_fsmagic, Opt_uid
+};
+
+static match_table_t policy_tokens = {
+ {Opt_measure, "measure"},
+ {Opt_dont_measure, "dont_measure"},
+ {Opt_obj_user, "obj_user=%s"},
+ {Opt_obj_role, "obj_role=%s"},
+ {Opt_obj_type, "obj_type=%s"},
+ {Opt_subj_user, "subj_user=%s"},
+ {Opt_subj_role, "subj_role=%s"},
+ {Opt_subj_type, "subj_type=%s"},
+ {Opt_func, "func=%s"},
+ {Opt_mask, "mask=%s"},
+ {Opt_fsmagic, "fsmagic=%s"},
+ {Opt_uid, "uid=%s"},
+ {Opt_err, NULL}
+};
+
+static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry,
+ char *args, int lsm_rule, int audit_type)
+{
+ int result;
+
+ entry->lsm[lsm_rule].type = audit_type;
+ result = security_filter_rule_init(entry->lsm[lsm_rule].type,
+ AUDIT_EQUAL, args,
+ &entry->lsm[lsm_rule].rule);
+ return result;
+}
+
+static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
+{
+ struct audit_buffer *ab;
+ char *p;
+ int result = 0;
+
+ ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE);
+
+ entry->action = -1;
+ while ((p = strsep(&rule, " \n")) != NULL) {
+ substring_t args[MAX_OPT_ARGS];
+ int token;
+ unsigned long lnum;
+
+ if (result < 0)
+ break;
+ if (!*p)
+ continue;
+ token = match_token(p, policy_tokens, args);
+ switch (token) {
+ case Opt_measure:
+ audit_log_format(ab, "%s ", "measure");
+ entry->action = MEASURE;
+ break;
+ case Opt_dont_measure:
+ audit_log_format(ab, "%s ", "dont_measure");
+ entry->action = DONT_MEASURE;
+ break;
+ case Opt_func:
+ audit_log_format(ab, "func=%s ", args[0].from);
+ if (strcmp(args[0].from, "PATH_CHECK") == 0)
+ entry->func = PATH_CHECK;
+ else if (strcmp(args[0].from, "FILE_MMAP") == 0)
+ entry->func = FILE_MMAP;
+ else if (strcmp(args[0].from, "BPRM_CHECK") == 0)
+ entry->func = BPRM_CHECK;
+ else
+ result = -EINVAL;
+ if (!result)
+ entry->flags |= IMA_FUNC;
+ break;
+ case Opt_mask:
+ audit_log_format(ab, "mask=%s ", args[0].from);
+ if ((strcmp(args[0].from, "MAY_EXEC")) == 0)
+ entry->mask = MAY_EXEC;
+ else if (strcmp(args[0].from, "MAY_WRITE") == 0)
+ entry->mask = MAY_WRITE;
+ else if (strcmp(args[0].from, "MAY_READ") == 0)
+ entry->mask = MAY_READ;
+ else if (strcmp(args[0].from, "MAY_APPEND") == 0)
+ entry->mask = MAY_APPEND;
+ else
+ result = -EINVAL;
+ if (!result)
+ entry->flags |= IMA_MASK;
+ break;
+ case Opt_fsmagic:
+ audit_log_format(ab, "fsmagic=%s ", args[0].from);
+ result = strict_strtoul(args[0].from, 16,
+ &entry->fsmagic);
+ if (!result)
+ entry->flags |= IMA_FSMAGIC;
+ break;
+ case Opt_uid:
+ audit_log_format(ab, "uid=%s ", args[0].from);
+ result = strict_strtoul(args[0].from, 10, &lnum);
+ if (!result) {
+ entry->uid = (uid_t) lnum;
+ if (entry->uid != lnum)
+ result = -EINVAL;
+ else
+ entry->flags |= IMA_UID;
+ }
+ break;
+ case Opt_obj_user:
+ audit_log_format(ab, "obj_user=%s ", args[0].from);
+ result = ima_lsm_rule_init(entry, args[0].from,
+ LSM_OBJ_USER,
+ AUDIT_OBJ_USER);
+ break;
+ case Opt_obj_role:
+ audit_log_format(ab, "obj_role=%s ", args[0].from);
+ result = ima_lsm_rule_init(entry, args[0].from,
+ LSM_OBJ_ROLE,
+ AUDIT_OBJ_ROLE);
+ break;
+ case Opt_obj_type:
+ audit_log_format(ab, "obj_type=%s ", args[0].from);
+ result = ima_lsm_rule_init(entry, args[0].from,
+ LSM_OBJ_TYPE,
+ AUDIT_OBJ_TYPE);
+ break;
+ case Opt_subj_user:
+ audit_log_format(ab, "subj_user=%s ", args[0].from);
+ result = ima_lsm_rule_init(entry, args[0].from,
+ LSM_SUBJ_USER,
+ AUDIT_SUBJ_USER);
+ break;
+ case Opt_subj_role:
+ audit_log_format(ab, "subj_role=%s ", args[0].from);
+ result = ima_lsm_rule_init(entry, args[0].from,
+ LSM_SUBJ_ROLE,
+ AUDIT_SUBJ_ROLE);
+ break;
+ case Opt_subj_type:
+ audit_log_format(ab, "subj_type=%s ", args[0].from);
+ result = ima_lsm_rule_init(entry, args[0].from,
+ LSM_SUBJ_TYPE,
+ AUDIT_SUBJ_TYPE);
+ break;
+ case Opt_err:
+ audit_log_format(ab, "UNKNOWN=%s ", p);
+ break;
+ }
+ }
+ if (entry->action == UNKNOWN)
+ result = -EINVAL;
+
+ audit_log_format(ab, "res=%d", !result ? 0 : 1);
+ audit_log_end(ab);
+ return result;
+}
+
+/**
+ * ima_parse_add_rule - add a rule to measure_policy_rules
+ * @rule - ima measurement policy rule
+ *
+ * Uses a mutex to protect the policy list from multiple concurrent writers.
+ * Returns 0 on success, an error code on failure.
+ */
+int ima_parse_add_rule(char *rule)
+{
+ const char *op = "update_policy";
+ struct ima_measure_rule_entry *entry;
+ int result = 0;
+ int audit_info = 0;
+
+ /* Prevent installed policy from changing */
+ if (ima_measure != &measure_default_rules) {
+ integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+ NULL, op, "already exists",
+ -EACCES, audit_info);
+ return -EACCES;
+ }
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+ NULL, op, "-ENOMEM", -ENOMEM, audit_info);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&entry->list);
+
+ result = ima_parse_rule(rule, entry);
+ if (!result) {
+ mutex_lock(&ima_measure_mutex);
+ list_add_tail(&entry->list, &measure_policy_rules);
+ mutex_unlock(&ima_measure_mutex);
+ } else {
+ kfree(entry);
+ integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+ NULL, op, "invalid policy", result,
+ audit_info);
+ }
+ return result;
+}
+
+/* ima_delete_rules called to cleanup invalid policy */
+void ima_delete_rules(void)
+{
+ struct ima_measure_rule_entry *entry, *tmp;
+
+ mutex_lock(&ima_measure_mutex);
+ list_for_each_entry_safe(entry, tmp, &measure_policy_rules, list) {
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ mutex_unlock(&ima_measure_mutex);
+}
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
new file mode 100644
index 0000000..7ec9431
--- /dev/null
+++ b/security/integrity/ima/ima_queue.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Serge Hallyn <serue@us.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Mimi Zohar <zohar@us.ibm.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, version 2 of the
+ * License.
+ *
+ * File: ima_queue.c
+ * Implements queues that store template measurements and
+ * maintains aggregate over the stored measurements
+ * in the pre-configured TPM PCR (if available).
+ * The measurement list is append-only. No entry is
+ * ever removed or changed during the boot-cycle.
+ */
+#include <linux/module.h>
+#include <linux/rculist.h>
+#include "ima.h"
+
+LIST_HEAD(ima_measurements); /* list of all measurements */
+
+/* key: inode (before secure-hashing a file) */
+struct ima_h_table ima_htable = {
+ .len = ATOMIC_LONG_INIT(0),
+ .violations = ATOMIC_LONG_INIT(0),
+ .queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
+};
+
+/* mutex protects atomicity of extending measurement list
+ * and extending the TPM PCR aggregate. Since tpm_extend can take
+ * long (and the tpm driver uses a mutex), we can't use the spinlock.
+ */
+static DEFINE_MUTEX(ima_extend_list_mutex);
+
+/* lookup up the digest value in the hash table, and return the entry */
+static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value)
+{
+ struct ima_queue_entry *qe, *ret = NULL;
+ unsigned int key;
+ struct hlist_node *pos;
+ int rc;
+
+ key = ima_hash_key(digest_value);
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(qe, pos, &ima_htable.queue[key], hnext) {
+ rc = memcmp(qe->entry->digest, digest_value, IMA_DIGEST_SIZE);
+ if (rc == 0) {
+ ret = qe;
+ break;
+ }
+ }
+ rcu_read_unlock();
+ return ret;
+}
+
+/* ima_add_template_entry helper function:
+ * - Add template entry to measurement list and hash table.
+ *
+ * (Called with ima_extend_list_mutex held.)
+ */
+static int ima_add_digest_entry(struct ima_template_entry *entry)
+{
+ struct ima_queue_entry *qe;
+ unsigned int key;
+
+ qe = kmalloc(sizeof(*qe), GFP_KERNEL);
+ if (qe == NULL) {
+ pr_err("OUT OF MEMORY ERROR creating queue entry.\n");
+ return -ENOMEM;
+ }
+ qe->entry = entry;
+
+ INIT_LIST_HEAD(&qe->later);
+ list_add_tail_rcu(&qe->later, &ima_measurements);
+
+ atomic_long_inc(&ima_htable.len);
+ key = ima_hash_key(entry->digest);
+ hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]);
+ return 0;
+}
+
+static int ima_pcr_extend(const u8 *hash)
+{
+ int result = 0;
+
+ if (!ima_used_chip)
+ return result;
+
+ result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash);
+ if (result != 0)
+ pr_err("Error Communicating to TPM chip\n");
+ return result;
+}
+
+/* Add template entry to the measurement list and hash table,
+ * and extend the pcr.
+ */
+int ima_add_template_entry(struct ima_template_entry *entry, int violation,
+ const char *op, struct inode *inode)
+{
+ u8 digest[IMA_DIGEST_SIZE];
+ const char *audit_cause = "hash_added";
+ int audit_info = 1;
+ int result = 0;
+
+ mutex_lock(&ima_extend_list_mutex);
+ if (!violation) {
+ memcpy(digest, entry->digest, sizeof digest);
+ if (ima_lookup_digest_entry(digest)) {
+ audit_cause = "hash_exists";
+ goto out;
+ }
+ }
+
+ result = ima_add_digest_entry(entry);
+ if (result < 0) {
+ audit_cause = "ENOMEM";
+ audit_info = 0;
+ goto out;
+ }
+
+ if (violation) /* invalidate pcr */
+ memset(digest, 0xff, sizeof digest);
+
+ result = ima_pcr_extend(digest);
+ if (result != 0) {
+ audit_cause = "TPM error";
+ audit_info = 0;
+ }
+out:
+ mutex_unlock(&ima_extend_list_mutex);
+ integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, entry->template_name,
+ op, audit_cause, result, audit_info);
+ return result;
+}
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 81932ab..9fb679c 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -53,6 +53,7 @@
atomic_t nkeys; /* number of keys */
atomic_t nikeys; /* number of instantiated keys */
uid_t uid;
+ struct user_namespace *user_ns;
int qnkeys; /* number of keys allocated to this user */
int qnbytes; /* number of bytes allocated to this user */
};
@@ -61,7 +62,8 @@
extern spinlock_t key_user_lock;
extern struct key_user root_key_user;
-extern struct key_user *key_user_lookup(uid_t uid);
+extern struct key_user *key_user_lookup(uid_t uid,
+ struct user_namespace *user_ns);
extern void key_user_put(struct key_user *user);
/*
diff --git a/security/keys/key.c b/security/keys/key.c
index f76c8a5..4a1297d 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -18,6 +18,7 @@
#include <linux/workqueue.h>
#include <linux/random.h>
#include <linux/err.h>
+#include <linux/user_namespace.h>
#include "internal.h"
static struct kmem_cache *key_jar;
@@ -60,7 +61,7 @@
* get the key quota record for a user, allocating a new record if one doesn't
* already exist
*/
-struct key_user *key_user_lookup(uid_t uid)
+struct key_user *key_user_lookup(uid_t uid, struct user_namespace *user_ns)
{
struct key_user *candidate = NULL, *user;
struct rb_node *parent = NULL;
@@ -79,6 +80,10 @@
p = &(*p)->rb_left;
else if (uid > user->uid)
p = &(*p)->rb_right;
+ else if (user_ns < user->user_ns)
+ p = &(*p)->rb_left;
+ else if (user_ns > user->user_ns)
+ p = &(*p)->rb_right;
else
goto found;
}
@@ -106,6 +111,7 @@
atomic_set(&candidate->nkeys, 0);
atomic_set(&candidate->nikeys, 0);
candidate->uid = uid;
+ candidate->user_ns = get_user_ns(user_ns);
candidate->qnkeys = 0;
candidate->qnbytes = 0;
spin_lock_init(&candidate->lock);
@@ -136,6 +142,7 @@
if (atomic_dec_and_lock(&user->usage, &key_user_lock)) {
rb_erase(&user->node, &key_user_tree);
spin_unlock(&key_user_lock);
+ put_user_ns(user->user_ns);
kfree(user);
}
@@ -234,7 +241,7 @@
quotalen = desclen + type->def_datalen;
/* get hold of the key tracking for this user */
- user = key_user_lookup(uid);
+ user = key_user_lookup(uid, cred->user->user_ns);
if (!user)
goto no_memory_1;
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index b1ec3b4..7f09fb8 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -726,7 +726,7 @@
/* change the UID */
if (uid != (uid_t) -1 && uid != key->uid) {
ret = -ENOMEM;
- newowner = key_user_lookup(uid);
+ newowner = key_user_lookup(uid, current_user_ns());
if (!newowner)
goto error_put;
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index ed85157..3dba81c 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -539,6 +539,9 @@
&keyring_name_hash[bucket],
type_data.link
) {
+ if (keyring->user->user_ns != current_user_ns())
+ continue;
+
if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
continue;
diff --git a/security/keys/permission.c b/security/keys/permission.c
index 5d9fc7b..0ed802c 100644
--- a/security/keys/permission.c
+++ b/security/keys/permission.c
@@ -35,6 +35,9 @@
key = key_ref_to_ptr(key_ref);
+ if (key->user->user_ns != cred->user->user_ns)
+ goto use_other_perms;
+
/* use the second 8-bits of permissions for keys the caller owns */
if (key->uid == cred->fsuid) {
kperm = key->perm >> 16;
@@ -56,6 +59,8 @@
}
}
+use_other_perms:
+
/* otherwise use the least-significant 8-bits */
kperm = key->perm;
diff --git a/security/keys/proc.c b/security/keys/proc.c
index 7f508de..769f9bd 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -91,6 +91,28 @@
*/
#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
+static struct rb_node *__key_serial_next(struct rb_node *n)
+{
+ while (n) {
+ struct key *key = rb_entry(n, struct key, serial_node);
+ if (key->user->user_ns == current_user_ns())
+ break;
+ n = rb_next(n);
+ }
+ return n;
+}
+
+static struct rb_node *key_serial_next(struct rb_node *n)
+{
+ return __key_serial_next(rb_next(n));
+}
+
+static struct rb_node *key_serial_first(struct rb_root *r)
+{
+ struct rb_node *n = rb_first(r);
+ return __key_serial_next(n);
+}
+
static int proc_keys_open(struct inode *inode, struct file *file)
{
return seq_open(file, &proc_keys_ops);
@@ -104,10 +126,10 @@
spin_lock(&key_serial_lock);
- _p = rb_first(&key_serial_tree);
+ _p = key_serial_first(&key_serial_tree);
while (pos > 0 && _p) {
pos--;
- _p = rb_next(_p);
+ _p = key_serial_next(_p);
}
return _p;
@@ -117,7 +139,7 @@
static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos)
{
(*_pos)++;
- return rb_next((struct rb_node *) v);
+ return key_serial_next((struct rb_node *) v);
}
@@ -203,6 +225,27 @@
#endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */
+static struct rb_node *__key_user_next(struct rb_node *n)
+{
+ while (n) {
+ struct key_user *user = rb_entry(n, struct key_user, node);
+ if (user->user_ns == current_user_ns())
+ break;
+ n = rb_next(n);
+ }
+ return n;
+}
+
+static struct rb_node *key_user_next(struct rb_node *n)
+{
+ return __key_user_next(rb_next(n));
+}
+
+static struct rb_node *key_user_first(struct rb_root *r)
+{
+ struct rb_node *n = rb_first(r);
+ return __key_user_next(n);
+}
/*****************************************************************************/
/*
* implement "/proc/key-users" to provides a list of the key users
@@ -220,10 +263,10 @@
spin_lock(&key_user_lock);
- _p = rb_first(&key_user_tree);
+ _p = key_user_first(&key_user_tree);
while (pos > 0 && _p) {
pos--;
- _p = rb_next(_p);
+ _p = key_user_next(_p);
}
return _p;
@@ -233,7 +276,7 @@
static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos)
{
(*_pos)++;
- return rb_next((struct rb_node *) v);
+ return key_user_next((struct rb_node *) v);
}
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 2f5d89e..276d278 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -17,6 +17,7 @@
#include <linux/fs.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/user_namespace.h>
#include <asm/uaccess.h>
#include "internal.h"
@@ -34,6 +35,7 @@
.nkeys = ATOMIC_INIT(2),
.nikeys = ATOMIC_INIT(2),
.uid = 0,
+ .user_ns = &init_user_ns,
};
/*****************************************************************************/
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index 0e04f72..22a3158 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -365,7 +365,7 @@
kenter("");
- user = key_user_lookup(current_fsuid());
+ user = key_user_lookup(current_fsuid(), current_user_ns());
if (!user)
return ERR_PTR(-ENOMEM);
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index eb41f43..7f9b5fa 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -88,17 +88,16 @@
u32 tsid;
u16 tclass;
struct av_decision avd;
- atomic_t used; /* used recently */
};
struct avc_node {
struct avc_entry ae;
- struct list_head list;
+ struct hlist_node list; /* anchored in avc_cache->slots[i] */
struct rcu_head rhead;
};
struct avc_cache {
- struct list_head slots[AVC_CACHE_SLOTS];
+ struct hlist_head slots[AVC_CACHE_SLOTS]; /* head for avc_node->list */
spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */
atomic_t lru_hint; /* LRU hint for reclaim scan */
atomic_t active_nodes;
@@ -234,7 +233,7 @@
int i;
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
- INIT_LIST_HEAD(&avc_cache.slots[i]);
+ INIT_HLIST_HEAD(&avc_cache.slots[i]);
spin_lock_init(&avc_cache.slots_lock[i]);
}
atomic_set(&avc_cache.active_nodes, 0);
@@ -250,16 +249,20 @@
{
int i, chain_len, max_chain_len, slots_used;
struct avc_node *node;
+ struct hlist_head *head;
rcu_read_lock();
slots_used = 0;
max_chain_len = 0;
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
- if (!list_empty(&avc_cache.slots[i])) {
+ head = &avc_cache.slots[i];
+ if (!hlist_empty(head)) {
+ struct hlist_node *next;
+
slots_used++;
chain_len = 0;
- list_for_each_entry_rcu(node, &avc_cache.slots[i], list)
+ hlist_for_each_entry_rcu(node, next, head, list)
chain_len++;
if (chain_len > max_chain_len)
max_chain_len = chain_len;
@@ -283,7 +286,7 @@
static void avc_node_delete(struct avc_node *node)
{
- list_del_rcu(&node->list);
+ hlist_del_rcu(&node->list);
call_rcu(&node->rhead, avc_node_free);
atomic_dec(&avc_cache.active_nodes);
}
@@ -297,7 +300,7 @@
static void avc_node_replace(struct avc_node *new, struct avc_node *old)
{
- list_replace_rcu(&old->list, &new->list);
+ hlist_replace_rcu(&old->list, &new->list);
call_rcu(&old->rhead, avc_node_free);
atomic_dec(&avc_cache.active_nodes);
}
@@ -307,29 +310,31 @@
struct avc_node *node;
int hvalue, try, ecx;
unsigned long flags;
+ struct hlist_head *head;
+ struct hlist_node *next;
+ spinlock_t *lock;
for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++) {
hvalue = atomic_inc_return(&avc_cache.lru_hint) & (AVC_CACHE_SLOTS - 1);
+ head = &avc_cache.slots[hvalue];
+ lock = &avc_cache.slots_lock[hvalue];
- if (!spin_trylock_irqsave(&avc_cache.slots_lock[hvalue], flags))
+ if (!spin_trylock_irqsave(lock, flags))
continue;
rcu_read_lock();
- list_for_each_entry(node, &avc_cache.slots[hvalue], list) {
- if (atomic_dec_and_test(&node->ae.used)) {
- /* Recently Unused */
- avc_node_delete(node);
- avc_cache_stats_incr(reclaims);
- ecx++;
- if (ecx >= AVC_CACHE_RECLAIM) {
- rcu_read_unlock();
- spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags);
- goto out;
- }
+ hlist_for_each_entry(node, next, head, list) {
+ avc_node_delete(node);
+ avc_cache_stats_incr(reclaims);
+ ecx++;
+ if (ecx >= AVC_CACHE_RECLAIM) {
+ rcu_read_unlock();
+ spin_unlock_irqrestore(lock, flags);
+ goto out;
}
}
rcu_read_unlock();
- spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags);
+ spin_unlock_irqrestore(lock, flags);
}
out:
return ecx;
@@ -344,8 +349,7 @@
goto out;
INIT_RCU_HEAD(&node->rhead);
- INIT_LIST_HEAD(&node->list);
- atomic_set(&node->ae.used, 1);
+ INIT_HLIST_NODE(&node->list);
avc_cache_stats_incr(allocations);
if (atomic_inc_return(&avc_cache.active_nodes) > avc_cache_threshold)
@@ -355,21 +359,24 @@
return node;
}
-static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae)
+static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd)
{
node->ae.ssid = ssid;
node->ae.tsid = tsid;
node->ae.tclass = tclass;
- memcpy(&node->ae.avd, &ae->avd, sizeof(node->ae.avd));
+ memcpy(&node->ae.avd, avd, sizeof(node->ae.avd));
}
static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass)
{
struct avc_node *node, *ret = NULL;
int hvalue;
+ struct hlist_head *head;
+ struct hlist_node *next;
hvalue = avc_hash(ssid, tsid, tclass);
- list_for_each_entry_rcu(node, &avc_cache.slots[hvalue], list) {
+ head = &avc_cache.slots[hvalue];
+ hlist_for_each_entry_rcu(node, next, head, list) {
if (ssid == node->ae.ssid &&
tclass == node->ae.tclass &&
tsid == node->ae.tsid) {
@@ -378,15 +385,6 @@
}
}
- if (ret == NULL) {
- /* cache miss */
- goto out;
- }
-
- /* cache hit */
- if (atomic_read(&ret->ae.used) != 1)
- atomic_set(&ret->ae.used, 1);
-out:
return ret;
}
@@ -395,30 +393,25 @@
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
- * @requested: requested permissions, interpreted based on @tclass
*
* Look up an AVC entry that is valid for the
- * @requested permissions between the SID pair
* (@ssid, @tsid), interpreting the permissions
* based on @tclass. If a valid AVC entry exists,
* then this function return the avc_node.
* Otherwise, this function returns NULL.
*/
-static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass, u32 requested)
+static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass)
{
struct avc_node *node;
avc_cache_stats_incr(lookups);
node = avc_search_node(ssid, tsid, tclass);
- if (node && ((node->ae.avd.decided & requested) == requested)) {
+ if (node)
avc_cache_stats_incr(hits);
- goto out;
- }
+ else
+ avc_cache_stats_incr(misses);
- node = NULL;
- avc_cache_stats_incr(misses);
-out:
return node;
}
@@ -449,34 +442,41 @@
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
- * @ae: AVC entry
+ * @avd: resulting av decision
*
* Insert an AVC entry for the SID pair
* (@ssid, @tsid) and class @tclass.
* The access vectors and the sequence number are
* normally provided by the security server in
* response to a security_compute_av() call. If the
- * sequence number @ae->avd.seqno is not less than the latest
+ * sequence number @avd->seqno is not less than the latest
* revocation notification, then the function copies
* the access vectors into a cache entry, returns
* avc_node inserted. Otherwise, this function returns NULL.
*/
-static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae)
+static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd)
{
struct avc_node *pos, *node = NULL;
int hvalue;
unsigned long flag;
- if (avc_latest_notif_update(ae->avd.seqno, 1))
+ if (avc_latest_notif_update(avd->seqno, 1))
goto out;
node = avc_alloc_node();
if (node) {
- hvalue = avc_hash(ssid, tsid, tclass);
- avc_node_populate(node, ssid, tsid, tclass, ae);
+ struct hlist_head *head;
+ struct hlist_node *next;
+ spinlock_t *lock;
- spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag);
- list_for_each_entry(pos, &avc_cache.slots[hvalue], list) {
+ hvalue = avc_hash(ssid, tsid, tclass);
+ avc_node_populate(node, ssid, tsid, tclass, avd);
+
+ head = &avc_cache.slots[hvalue];
+ lock = &avc_cache.slots_lock[hvalue];
+
+ spin_lock_irqsave(lock, flag);
+ hlist_for_each_entry(pos, next, head, list) {
if (pos->ae.ssid == ssid &&
pos->ae.tsid == tsid &&
pos->ae.tclass == tclass) {
@@ -484,9 +484,9 @@
goto found;
}
}
- list_add_rcu(&node->list, &avc_cache.slots[hvalue]);
+ hlist_add_head_rcu(&node->list, head);
found:
- spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag);
+ spin_unlock_irqrestore(lock, flag);
}
out:
return node;
@@ -742,17 +742,22 @@
* @event : Updating event
* @perms : Permission mask bits
* @ssid,@tsid,@tclass : identifier of an AVC entry
+ * @seqno : sequence number when decision was made
*
* if a valid AVC entry doesn't exist,this function returns -ENOENT.
* if kmalloc() called internal returns NULL, this function returns -ENOMEM.
* otherwise, this function update the AVC entry. The original AVC-entry object
* will release later by RCU.
*/
-static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass)
+static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass,
+ u32 seqno)
{
int hvalue, rc = 0;
unsigned long flag;
struct avc_node *pos, *node, *orig = NULL;
+ struct hlist_head *head;
+ struct hlist_node *next;
+ spinlock_t *lock;
node = avc_alloc_node();
if (!node) {
@@ -762,12 +767,17 @@
/* Lock the target slot */
hvalue = avc_hash(ssid, tsid, tclass);
- spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag);
- list_for_each_entry(pos, &avc_cache.slots[hvalue], list) {
+ head = &avc_cache.slots[hvalue];
+ lock = &avc_cache.slots_lock[hvalue];
+
+ spin_lock_irqsave(lock, flag);
+
+ hlist_for_each_entry(pos, next, head, list) {
if (ssid == pos->ae.ssid &&
tsid == pos->ae.tsid &&
- tclass == pos->ae.tclass){
+ tclass == pos->ae.tclass &&
+ seqno == pos->ae.avd.seqno){
orig = pos;
break;
}
@@ -783,7 +793,7 @@
* Copy and replace original node.
*/
- avc_node_populate(node, ssid, tsid, tclass, &orig->ae);
+ avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd);
switch (event) {
case AVC_CALLBACK_GRANT:
@@ -808,7 +818,7 @@
}
avc_node_replace(node, orig);
out_unlock:
- spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag);
+ spin_unlock_irqrestore(lock, flag);
out:
return rc;
}
@@ -823,18 +833,24 @@
int i, rc = 0, tmprc;
unsigned long flag;
struct avc_node *node;
+ struct hlist_head *head;
+ struct hlist_node *next;
+ spinlock_t *lock;
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
- spin_lock_irqsave(&avc_cache.slots_lock[i], flag);
+ head = &avc_cache.slots[i];
+ lock = &avc_cache.slots_lock[i];
+
+ spin_lock_irqsave(lock, flag);
/*
* With preemptable RCU, the outer spinlock does not
* prevent RCU grace periods from ending.
*/
rcu_read_lock();
- list_for_each_entry(node, &avc_cache.slots[i], list)
+ hlist_for_each_entry(node, next, head, list)
avc_node_delete(node);
rcu_read_unlock();
- spin_unlock_irqrestore(&avc_cache.slots_lock[i], flag);
+ spin_unlock_irqrestore(lock, flag);
}
for (c = avc_callbacks; c; c = c->next) {
@@ -875,10 +891,10 @@
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
unsigned flags,
- struct av_decision *avd)
+ struct av_decision *in_avd)
{
struct avc_node *node;
- struct avc_entry entry, *p_ae;
+ struct av_decision avd_entry, *avd;
int rc = 0;
u32 denied;
@@ -886,29 +902,34 @@
rcu_read_lock();
- node = avc_lookup(ssid, tsid, tclass, requested);
+ node = avc_lookup(ssid, tsid, tclass);
if (!node) {
rcu_read_unlock();
- rc = security_compute_av(ssid, tsid, tclass, requested, &entry.avd);
+
+ if (in_avd)
+ avd = in_avd;
+ else
+ avd = &avd_entry;
+
+ rc = security_compute_av(ssid, tsid, tclass, requested, avd);
if (rc)
goto out;
rcu_read_lock();
- node = avc_insert(ssid, tsid, tclass, &entry);
+ node = avc_insert(ssid, tsid, tclass, avd);
+ } else {
+ if (in_avd)
+ memcpy(in_avd, &node->ae.avd, sizeof(*in_avd));
+ avd = &node->ae.avd;
}
- p_ae = node ? &node->ae : &entry;
-
- if (avd)
- memcpy(avd, &p_ae->avd, sizeof(*avd));
-
- denied = requested & ~(p_ae->avd.allowed);
+ denied = requested & ~(avd->allowed);
if (denied) {
if (flags & AVC_STRICT)
rc = -EACCES;
else if (!selinux_enforcing || security_permissive_sid(ssid))
avc_update_node(AVC_CALLBACK_GRANT, requested, ssid,
- tsid, tclass);
+ tsid, tclass, avd->seqno);
else
rc = -EACCES;
}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 0081597..7c52ba2 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -89,7 +89,7 @@
#define XATTR_SELINUX_SUFFIX "selinux"
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
-#define NUM_SEL_MNT_OPTS 4
+#define NUM_SEL_MNT_OPTS 5
extern unsigned int policydb_loaded_version;
extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
@@ -353,6 +353,7 @@
Opt_fscontext = 2,
Opt_defcontext = 3,
Opt_rootcontext = 4,
+ Opt_labelsupport = 5,
};
static const match_table_t tokens = {
@@ -360,6 +361,7 @@
{Opt_fscontext, FSCONTEXT_STR "%s"},
{Opt_defcontext, DEFCONTEXT_STR "%s"},
{Opt_rootcontext, ROOTCONTEXT_STR "%s"},
+ {Opt_labelsupport, LABELSUPP_STR},
{Opt_error, NULL},
};
@@ -431,7 +433,7 @@
}
}
- sbsec->initialized = 1;
+ sbsec->flags |= (SE_SBINITIALIZED | SE_SBLABELSUPP);
if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",
@@ -441,6 +443,12 @@
sb->s_id, sb->s_type->name,
labeling_behaviors[sbsec->behavior-1]);
+ if (sbsec->behavior == SECURITY_FS_USE_GENFS ||
+ sbsec->behavior == SECURITY_FS_USE_MNTPOINT ||
+ sbsec->behavior == SECURITY_FS_USE_NONE ||
+ sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
+ sbsec->flags &= ~SE_SBLABELSUPP;
+
/* Initialize the root inode. */
rc = inode_doinit_with_dentry(root_inode, root);
@@ -487,23 +495,22 @@
security_init_mnt_opts(opts);
- if (!sbsec->initialized)
+ if (!(sbsec->flags & SE_SBINITIALIZED))
return -EINVAL;
if (!ss_initialized)
return -EINVAL;
- /*
- * if we ever use sbsec flags for anything other than tracking mount
- * settings this is going to need a mask
- */
- tmp = sbsec->flags;
+ tmp = sbsec->flags & SE_MNTMASK;
/* count the number of mount options for this sb */
for (i = 0; i < 8; i++) {
if (tmp & 0x01)
opts->num_mnt_opts++;
tmp >>= 1;
}
+ /* Check if the Label support flag is set */
+ if (sbsec->flags & SE_SBLABELSUPP)
+ opts->num_mnt_opts++;
opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC);
if (!opts->mnt_opts) {
@@ -549,6 +556,10 @@
opts->mnt_opts[i] = context;
opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT;
}
+ if (sbsec->flags & SE_SBLABELSUPP) {
+ opts->mnt_opts[i] = NULL;
+ opts->mnt_opts_flags[i++] = SE_SBLABELSUPP;
+ }
BUG_ON(i != opts->num_mnt_opts);
@@ -562,8 +573,10 @@
static int bad_option(struct superblock_security_struct *sbsec, char flag,
u32 old_sid, u32 new_sid)
{
+ char mnt_flags = sbsec->flags & SE_MNTMASK;
+
/* check if the old mount command had the same options */
- if (sbsec->initialized)
+ if (sbsec->flags & SE_SBINITIALIZED)
if (!(sbsec->flags & flag) ||
(old_sid != new_sid))
return 1;
@@ -571,8 +584,8 @@
/* check if we were passed the same options twice,
* aka someone passed context=a,context=b
*/
- if (!sbsec->initialized)
- if (sbsec->flags & flag)
+ if (!(sbsec->flags & SE_SBINITIALIZED))
+ if (mnt_flags & flag)
return 1;
return 0;
}
@@ -626,7 +639,7 @@
* this sb does not set any security options. (The first options
* will be used for both mounts)
*/
- if (sbsec->initialized && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
+ if ((sbsec->flags & SE_SBINITIALIZED) && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
&& (num_opts == 0))
goto out;
@@ -637,6 +650,9 @@
*/
for (i = 0; i < num_opts; i++) {
u32 sid;
+
+ if (flags[i] == SE_SBLABELSUPP)
+ continue;
rc = security_context_to_sid(mount_options[i],
strlen(mount_options[i]), &sid);
if (rc) {
@@ -690,19 +706,19 @@
}
}
- if (sbsec->initialized) {
+ if (sbsec->flags & SE_SBINITIALIZED) {
/* previously mounted with options, but not on this attempt? */
- if (sbsec->flags && !num_opts)
+ if ((sbsec->flags & SE_MNTMASK) && !num_opts)
goto out_double_mount;
rc = 0;
goto out;
}
if (strcmp(sb->s_type->name, "proc") == 0)
- sbsec->proc = 1;
+ sbsec->flags |= SE_SBPROC;
/* Determine the labeling behavior to use for this filesystem type. */
- rc = security_fs_use(sbsec->proc ? "proc" : sb->s_type->name, &sbsec->behavior, &sbsec->sid);
+ rc = security_fs_use((sbsec->flags & SE_SBPROC) ? "proc" : sb->s_type->name, &sbsec->behavior, &sbsec->sid);
if (rc) {
printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
__func__, sb->s_type->name, rc);
@@ -806,10 +822,10 @@
}
/* how can we clone if the old one wasn't set up?? */
- BUG_ON(!oldsbsec->initialized);
+ BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED));
/* if fs is reusing a sb, just let its options stand... */
- if (newsbsec->initialized)
+ if (newsbsec->flags & SE_SBINITIALIZED)
return;
mutex_lock(&newsbsec->lock);
@@ -917,7 +933,8 @@
goto out_err;
}
break;
-
+ case Opt_labelsupport:
+ break;
default:
rc = -EINVAL;
printk(KERN_WARNING "SELinux: unknown mount option\n");
@@ -999,7 +1016,12 @@
char *prefix;
for (i = 0; i < opts->num_mnt_opts; i++) {
- char *has_comma = strchr(opts->mnt_opts[i], ',');
+ char *has_comma;
+
+ if (opts->mnt_opts[i])
+ has_comma = strchr(opts->mnt_opts[i], ',');
+ else
+ has_comma = NULL;
switch (opts->mnt_opts_flags[i]) {
case CONTEXT_MNT:
@@ -1014,6 +1036,10 @@
case DEFCONTEXT_MNT:
prefix = DEFCONTEXT_STR;
break;
+ case SE_SBLABELSUPP:
+ seq_putc(m, ',');
+ seq_puts(m, LABELSUPP_STR);
+ continue;
default:
BUG();
};
@@ -1209,7 +1235,7 @@
goto out_unlock;
sbsec = inode->i_sb->s_security;
- if (!sbsec->initialized) {
+ if (!(sbsec->flags & SE_SBINITIALIZED)) {
/* Defer initialization until selinux_complete_init,
after the initial policy is loaded and the security
server is ready to handle calls. */
@@ -1237,19 +1263,26 @@
dentry = d_find_alias(inode);
}
if (!dentry) {
- printk(KERN_WARNING "SELinux: %s: no dentry for dev=%s "
- "ino=%ld\n", __func__, inode->i_sb->s_id,
- inode->i_ino);
+ /*
+ * this is can be hit on boot when a file is accessed
+ * before the policy is loaded. When we load policy we
+ * may find inodes that have no dentry on the
+ * sbsec->isec_head list. No reason to complain as these
+ * will get fixed up the next time we go through
+ * inode_doinit with a dentry, before these inodes could
+ * be used again by userspace.
+ */
goto out_unlock;
}
len = INITCONTEXTLEN;
- context = kmalloc(len, GFP_NOFS);
+ context = kmalloc(len+1, GFP_NOFS);
if (!context) {
rc = -ENOMEM;
dput(dentry);
goto out_unlock;
}
+ context[len] = '\0';
rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
context, len);
if (rc == -ERANGE) {
@@ -1262,12 +1295,13 @@
}
kfree(context);
len = rc;
- context = kmalloc(len, GFP_NOFS);
+ context = kmalloc(len+1, GFP_NOFS);
if (!context) {
rc = -ENOMEM;
dput(dentry);
goto out_unlock;
}
+ context[len] = '\0';
rc = inode->i_op->getxattr(dentry,
XATTR_NAME_SELINUX,
context, len);
@@ -1289,10 +1323,19 @@
sbsec->def_sid,
GFP_NOFS);
if (rc) {
- printk(KERN_WARNING "SELinux: %s: context_to_sid(%s) "
- "returned %d for dev=%s ino=%ld\n",
- __func__, context, -rc,
- inode->i_sb->s_id, inode->i_ino);
+ char *dev = inode->i_sb->s_id;
+ unsigned long ino = inode->i_ino;
+
+ if (rc == -EINVAL) {
+ if (printk_ratelimit())
+ printk(KERN_NOTICE "SELinux: inode=%lu on dev=%s was found to have an invalid "
+ "context=%s. This indicates you may need to relabel the inode or the "
+ "filesystem in question.\n", ino, dev, context);
+ } else {
+ printk(KERN_WARNING "SELinux: %s: context_to_sid(%s) "
+ "returned %d for dev=%s ino=%ld\n",
+ __func__, context, -rc, dev, ino);
+ }
kfree(context);
/* Leave with the unlabeled SID */
rc = 0;
@@ -1326,7 +1369,7 @@
/* Default to the fs superblock SID. */
isec->sid = sbsec->sid;
- if (sbsec->proc && !S_ISLNK(inode->i_mode)) {
+ if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) {
struct proc_inode *proci = PROC_I(inode);
if (proci->pde) {
isec->sclass = inode_mode_to_security_class(inode->i_mode);
@@ -1587,7 +1630,7 @@
if (rc)
return rc;
- if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) {
+ if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
rc = security_transition_sid(sid, dsec->sid, tclass, &newsid);
if (rc)
return rc;
@@ -1801,6 +1844,8 @@
av |= FIFO_FILE__OPEN;
else if (S_ISDIR(mode))
av |= DIR__OPEN;
+ else if (S_ISSOCK(mode))
+ av |= SOCK_FILE__OPEN;
else
printk(KERN_ERR "SELinux: WARNING: inside %s with "
"unknown mode:%o\n", __func__, mode);
@@ -1815,7 +1860,7 @@
{
int rc;
- rc = secondary_ops->ptrace_may_access(child, mode);
+ rc = cap_ptrace_may_access(child, mode);
if (rc)
return rc;
@@ -1832,7 +1877,7 @@
{
int rc;
- rc = secondary_ops->ptrace_traceme(parent);
+ rc = cap_ptrace_traceme(parent);
if (rc)
return rc;
@@ -1848,7 +1893,7 @@
if (error)
return error;
- return secondary_ops->capget(target, effective, inheritable, permitted);
+ return cap_capget(target, effective, inheritable, permitted);
}
static int selinux_capset(struct cred *new, const struct cred *old,
@@ -1858,7 +1903,7 @@
{
int error;
- error = secondary_ops->capset(new, old,
+ error = cap_capset(new, old,
effective, inheritable, permitted);
if (error)
return error;
@@ -1866,12 +1911,22 @@
return cred_has_perm(old, new, PROCESS__SETCAP);
}
+/*
+ * (This comment used to live with the selinux_task_setuid hook,
+ * which was removed).
+ *
+ * Since setuid only affects the current process, and since the SELinux
+ * controls are not based on the Linux identity attributes, SELinux does not
+ * need to control this operation. However, SELinux does control the use of
+ * the CAP_SETUID and CAP_SETGID capabilities using the capable hook.
+ */
+
static int selinux_capable(struct task_struct *tsk, const struct cred *cred,
int cap, int audit)
{
int rc;
- rc = secondary_ops->capable(tsk, cred, cap, audit);
+ rc = cap_capable(tsk, cred, cap, audit);
if (rc)
return rc;
@@ -1997,7 +2052,7 @@
{
int rc;
- rc = secondary_ops->syslog(type);
+ rc = cap_syslog(type);
if (rc)
return rc;
@@ -2028,10 +2083,6 @@
* mapping. 0 means there is enough memory for the allocation to
* succeed and -ENOMEM implies there is not.
*
- * Note that secondary_ops->capable and task_has_perm_noaudit return 0
- * if the capability is granted, but __vm_enough_memory requires 1 if
- * the capability is granted.
- *
* Do not audit the selinux permission check, as this is applied to all
* processes that allocate mappings.
*/
@@ -2058,7 +2109,7 @@
struct inode *inode = bprm->file->f_path.dentry->d_inode;
int rc;
- rc = secondary_ops->bprm_set_creds(bprm);
+ rc = cap_bprm_set_creds(bprm);
if (rc)
return rc;
@@ -2156,11 +2207,6 @@
return 0;
}
-static int selinux_bprm_check_security(struct linux_binprm *bprm)
-{
- return secondary_ops->bprm_check_security(bprm);
-}
-
static int selinux_bprm_secureexec(struct linux_binprm *bprm)
{
const struct cred *cred = current_cred();
@@ -2180,7 +2226,7 @@
PROCESS__NOATSECURE, NULL);
}
- return (atsecure || secondary_ops->bprm_secureexec(bprm));
+ return (atsecure || cap_bprm_secureexec(bprm));
}
extern struct vfsmount *selinuxfs_mount;
@@ -2290,8 +2336,6 @@
struct rlimit *rlim, *initrlim;
int rc, i;
- secondary_ops->bprm_committing_creds(bprm);
-
new_tsec = bprm->cred->security;
if (new_tsec->sid == new_tsec->osid)
return;
@@ -2337,8 +2381,6 @@
int rc, i;
unsigned long flags;
- secondary_ops->bprm_committed_creds(bprm);
-
osid = tsec->osid;
sid = tsec->sid;
@@ -2400,7 +2442,8 @@
return (match_prefix(CONTEXT_STR, sizeof(CONTEXT_STR)-1, option, len) ||
match_prefix(FSCONTEXT_STR, sizeof(FSCONTEXT_STR)-1, option, len) ||
match_prefix(DEFCONTEXT_STR, sizeof(DEFCONTEXT_STR)-1, option, len) ||
- match_prefix(ROOTCONTEXT_STR, sizeof(ROOTCONTEXT_STR)-1, option, len));
+ match_prefix(ROOTCONTEXT_STR, sizeof(ROOTCONTEXT_STR)-1, option, len) ||
+ match_prefix(LABELSUPP_STR, sizeof(LABELSUPP_STR)-1, option, len));
}
static inline void take_option(char **to, char *from, int *first, int len)
@@ -2513,11 +2556,6 @@
void *data)
{
const struct cred *cred = current_cred();
- int rc;
-
- rc = secondary_ops->sb_mount(dev_name, path, type, flags, data);
- if (rc)
- return rc;
if (flags & MS_REMOUNT)
return superblock_has_perm(cred, path->mnt->mnt_sb,
@@ -2530,11 +2568,6 @@
static int selinux_umount(struct vfsmount *mnt, int flags)
{
const struct cred *cred = current_cred();
- int rc;
-
- rc = secondary_ops->sb_umount(mnt, flags);
- if (rc)
- return rc;
return superblock_has_perm(cred, mnt->mnt_sb,
FILESYSTEM__UNMOUNT, NULL);
@@ -2570,7 +2603,7 @@
sid = tsec->sid;
newsid = tsec->create_sid;
- if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) {
+ if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
rc = security_transition_sid(sid, dsec->sid,
inode_mode_to_security_class(inode->i_mode),
&newsid);
@@ -2585,14 +2618,14 @@
}
/* Possibly defer initialization to selinux_complete_init. */
- if (sbsec->initialized) {
+ if (sbsec->flags & SE_SBINITIALIZED) {
struct inode_security_struct *isec = inode->i_security;
isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = newsid;
isec->initialized = 1;
}
- if (!ss_initialized || sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
+ if (!ss_initialized || !(sbsec->flags & SE_SBLABELSUPP))
return -EOPNOTSUPP;
if (name) {
@@ -2622,21 +2655,11 @@
static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
- int rc;
-
- rc = secondary_ops->inode_link(old_dentry, dir, new_dentry);
- if (rc)
- return rc;
return may_link(dir, old_dentry, MAY_LINK);
}
static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
{
- int rc;
-
- rc = secondary_ops->inode_unlink(dir, dentry);
- if (rc)
- return rc;
return may_link(dir, dentry, MAY_UNLINK);
}
@@ -2657,12 +2680,6 @@
static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
- int rc;
-
- rc = secondary_ops->inode_mknod(dir, dentry, mode, dev);
- if (rc)
- return rc;
-
return may_create(dir, dentry, inode_mode_to_security_class(mode));
}
@@ -2682,22 +2699,13 @@
static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
{
const struct cred *cred = current_cred();
- int rc;
- rc = secondary_ops->inode_follow_link(dentry, nameidata);
- if (rc)
- return rc;
return dentry_has_perm(cred, NULL, dentry, FILE__READ);
}
static int selinux_inode_permission(struct inode *inode, int mask)
{
const struct cred *cred = current_cred();
- int rc;
-
- rc = secondary_ops->inode_permission(inode, mask);
- if (rc)
- return rc;
if (!mask) {
/* No permission to check. Existence test. */
@@ -2711,11 +2719,6 @@
static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
{
const struct cred *cred = current_cred();
- int rc;
-
- rc = secondary_ops->inode_setattr(dentry, iattr);
- if (rc)
- return rc;
if (iattr->ia_valid & ATTR_FORCE)
return 0;
@@ -2769,7 +2772,7 @@
return selinux_inode_setotherxattr(dentry, name);
sbsec = inode->i_sb->s_security;
- if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
+ if (!(sbsec->flags & SE_SBLABELSUPP))
return -EOPNOTSUPP;
if (!is_owner_or_cap(inode))
@@ -2931,16 +2934,6 @@
return len;
}
-static int selinux_inode_need_killpriv(struct dentry *dentry)
-{
- return secondary_ops->inode_need_killpriv(dentry);
-}
-
-static int selinux_inode_killpriv(struct dentry *dentry)
-{
- return secondary_ops->inode_killpriv(dentry);
-}
-
static void selinux_inode_getsecid(const struct inode *inode, u32 *secid)
{
struct inode_security_struct *isec = inode->i_security;
@@ -3078,18 +3071,13 @@
unsigned long prot)
{
const struct cred *cred = current_cred();
- int rc;
-
- rc = secondary_ops->file_mprotect(vma, reqprot, prot);
- if (rc)
- return rc;
if (selinux_checkreqprot)
prot = reqprot;
#ifndef CONFIG_PPC32
if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
- rc = 0;
+ int rc = 0;
if (vma->vm_start >= vma->vm_mm->start_brk &&
vma->vm_end <= vma->vm_mm->brk) {
rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP);
@@ -3239,12 +3227,6 @@
static int selinux_task_create(unsigned long clone_flags)
{
- int rc;
-
- rc = secondary_ops->task_create(clone_flags);
- if (rc)
- return rc;
-
return current_has_perm(current, PROCESS__FORK);
}
@@ -3278,14 +3260,6 @@
}
/*
- * commit new credentials
- */
-static void selinux_cred_commit(struct cred *new, const struct cred *old)
-{
- secondary_ops->cred_commit(new, old);
-}
-
-/*
* set the security data for a kernel service
* - all the creation contexts are set to unlabelled
*/
@@ -3329,29 +3303,6 @@
return 0;
}
-static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
-{
- /* Since setuid only affects the current process, and
- since the SELinux controls are not based on the Linux
- identity attributes, SELinux does not need to control
- this operation. However, SELinux does control the use
- of the CAP_SETUID and CAP_SETGID capabilities using the
- capable hook. */
- return 0;
-}
-
-static int selinux_task_fix_setuid(struct cred *new, const struct cred *old,
- int flags)
-{
- return secondary_ops->task_fix_setuid(new, old, flags);
-}
-
-static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
-{
- /* See the comment for setuid above. */
- return 0;
-}
-
static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
{
return current_has_perm(p, PROCESS__SETPGID);
@@ -3372,17 +3323,11 @@
*secid = task_sid(p);
}
-static int selinux_task_setgroups(struct group_info *group_info)
-{
- /* See the comment for setuid above. */
- return 0;
-}
-
static int selinux_task_setnice(struct task_struct *p, int nice)
{
int rc;
- rc = secondary_ops->task_setnice(p, nice);
+ rc = cap_task_setnice(p, nice);
if (rc)
return rc;
@@ -3393,7 +3338,7 @@
{
int rc;
- rc = secondary_ops->task_setioprio(p, ioprio);
+ rc = cap_task_setioprio(p, ioprio);
if (rc)
return rc;
@@ -3408,11 +3353,6 @@
static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
{
struct rlimit *old_rlim = current->signal->rlim + resource;
- int rc;
-
- rc = secondary_ops->task_setrlimit(resource, new_rlim);
- if (rc)
- return rc;
/* Control the ability to change the hard limit (whether
lowering or raising it), so that the hard limit can
@@ -3428,7 +3368,7 @@
{
int rc;
- rc = secondary_ops->task_setscheduler(p, policy, lp);
+ rc = cap_task_setscheduler(p, policy, lp);
if (rc)
return rc;
@@ -3451,10 +3391,6 @@
u32 perm;
int rc;
- rc = secondary_ops->task_kill(p, info, sig, secid);
- if (rc)
- return rc;
-
if (!sig)
perm = PROCESS__SIGNULL; /* null signal; existence test */
else
@@ -3467,18 +3403,6 @@
return rc;
}
-static int selinux_task_prctl(int option,
- unsigned long arg2,
- unsigned long arg3,
- unsigned long arg4,
- unsigned long arg5)
-{
- /* The current prctl operations do not appear to require
- any SELinux controls since they merely observe or modify
- the state of the current process. */
- return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5);
-}
-
static int selinux_task_wait(struct task_struct *p)
{
return task_has_perm(p, current, PROCESS__SIGCHLD);
@@ -4047,10 +3971,6 @@
struct avc_audit_data ad;
int err;
- err = secondary_ops->unix_stream_connect(sock, other, newsk);
- if (err)
- return err;
-
isec = SOCK_INODE(sock)->i_security;
other_isec = SOCK_INODE(other)->i_security;
@@ -4844,7 +4764,7 @@
{
int err;
- err = secondary_ops->netlink_send(sk, skb);
+ err = cap_netlink_send(sk, skb);
if (err)
return err;
@@ -4859,7 +4779,7 @@
int err;
struct avc_audit_data ad;
- err = secondary_ops->netlink_recv(skb, capability);
+ err = cap_netlink_recv(skb, capability);
if (err)
return err;
@@ -5167,11 +5087,6 @@
char __user *shmaddr, int shmflg)
{
u32 perms;
- int rc;
-
- rc = secondary_ops->shm_shmat(shp, shmaddr, shmflg);
- if (rc)
- return rc;
if (shmflg & SHM_RDONLY)
perms = SHM__READ;
@@ -5581,7 +5496,6 @@
.netlink_recv = selinux_netlink_recv,
.bprm_set_creds = selinux_bprm_set_creds,
- .bprm_check_security = selinux_bprm_check_security,
.bprm_committing_creds = selinux_bprm_committing_creds,
.bprm_committed_creds = selinux_bprm_committed_creds,
.bprm_secureexec = selinux_bprm_secureexec,
@@ -5623,8 +5537,6 @@
.inode_getsecurity = selinux_inode_getsecurity,
.inode_setsecurity = selinux_inode_setsecurity,
.inode_listsecurity = selinux_inode_listsecurity,
- .inode_need_killpriv = selinux_inode_need_killpriv,
- .inode_killpriv = selinux_inode_killpriv,
.inode_getsecid = selinux_inode_getsecid,
.file_permission = selinux_file_permission,
@@ -5644,17 +5556,12 @@
.task_create = selinux_task_create,
.cred_free = selinux_cred_free,
.cred_prepare = selinux_cred_prepare,
- .cred_commit = selinux_cred_commit,
.kernel_act_as = selinux_kernel_act_as,
.kernel_create_files_as = selinux_kernel_create_files_as,
- .task_setuid = selinux_task_setuid,
- .task_fix_setuid = selinux_task_fix_setuid,
- .task_setgid = selinux_task_setgid,
.task_setpgid = selinux_task_setpgid,
.task_getpgid = selinux_task_getpgid,
.task_getsid = selinux_task_getsid,
.task_getsecid = selinux_task_getsecid,
- .task_setgroups = selinux_task_setgroups,
.task_setnice = selinux_task_setnice,
.task_setioprio = selinux_task_setioprio,
.task_getioprio = selinux_task_getioprio,
@@ -5664,7 +5571,6 @@
.task_movememory = selinux_task_movememory,
.task_kill = selinux_task_kill,
.task_wait = selinux_task_wait,
- .task_prctl = selinux_task_prctl,
.task_to_inode = selinux_task_to_inode,
.ipc_permission = selinux_ipc_permission,
diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h
index c0c8854..31df1d7 100644
--- a/security/selinux/include/av_perm_to_string.h
+++ b/security/selinux/include/av_perm_to_string.h
@@ -24,6 +24,7 @@
S_(SECCLASS_CHR_FILE, CHR_FILE__EXECMOD, "execmod")
S_(SECCLASS_CHR_FILE, CHR_FILE__OPEN, "open")
S_(SECCLASS_BLK_FILE, BLK_FILE__OPEN, "open")
+ S_(SECCLASS_SOCK_FILE, SOCK_FILE__OPEN, "open")
S_(SECCLASS_FIFO_FILE, FIFO_FILE__OPEN, "open")
S_(SECCLASS_FD, FD__USE, "use")
S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__CONNECTTO, "connectto")
@@ -152,6 +153,7 @@
S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE, "nlmsg_write")
S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_RELAY, "nlmsg_relay")
S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV, "nlmsg_readpriv")
+ S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT, "nlmsg_tty_audit")
S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_READ, "nlmsg_read")
S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_WRITE, "nlmsg_write")
S_(SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, "sendto")
diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h
index 0ba79fe..d645192 100644
--- a/security/selinux/include/av_permissions.h
+++ b/security/selinux/include/av_permissions.h
@@ -174,6 +174,7 @@
#define SOCK_FILE__SWAPON 0x00004000UL
#define SOCK_FILE__QUOTAON 0x00008000UL
#define SOCK_FILE__MOUNTON 0x00010000UL
+#define SOCK_FILE__OPEN 0x00020000UL
#define FIFO_FILE__IOCTL 0x00000001UL
#define FIFO_FILE__READ 0x00000002UL
#define FIFO_FILE__WRITE 0x00000004UL
@@ -707,6 +708,7 @@
#define NETLINK_AUDIT_SOCKET__NLMSG_WRITE 0x00800000UL
#define NETLINK_AUDIT_SOCKET__NLMSG_RELAY 0x01000000UL
#define NETLINK_AUDIT_SOCKET__NLMSG_READPRIV 0x02000000UL
+#define NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT 0x04000000UL
#define NETLINK_IP6FW_SOCKET__IOCTL 0x00000001UL
#define NETLINK_IP6FW_SOCKET__READ 0x00000002UL
#define NETLINK_IP6FW_SOCKET__WRITE 0x00000004UL
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 3cc4516..c4e0623 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -60,9 +60,7 @@
u32 def_sid; /* default SID for labeling */
u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */
unsigned int behavior; /* labeling behavior */
- unsigned char initialized; /* initialization flag */
unsigned char flags; /* which mount options were specified */
- unsigned char proc; /* proc fs */
struct mutex lock;
struct list_head isec_head;
spinlock_t isec_lock;
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 7244737..5c3434f 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -37,15 +37,23 @@
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY
#endif
+/* Mask for just the mount related flags */
+#define SE_MNTMASK 0x0f
+/* Super block security struct flags for mount options */
#define CONTEXT_MNT 0x01
#define FSCONTEXT_MNT 0x02
#define ROOTCONTEXT_MNT 0x04
#define DEFCONTEXT_MNT 0x08
+/* Non-mount related flags */
+#define SE_SBINITIALIZED 0x10
+#define SE_SBPROC 0x20
+#define SE_SBLABELSUPP 0x40
#define CONTEXT_STR "context="
#define FSCONTEXT_STR "fscontext="
#define ROOTCONTEXT_STR "rootcontext="
#define DEFCONTEXT_STR "defcontext="
+#define LABELSUPP_STR "seclabel"
struct netlbl_lsm_secattr;
@@ -80,7 +88,6 @@
#define SEL_VEC_MAX 32
struct av_decision {
u32 allowed;
- u32 decided;
u32 auditallow;
u32 auditdeny;
u32 seqno;
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 4ed7bab..c6875fd 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -113,7 +113,7 @@
{ AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY },
{ AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ },
{ AUDIT_TTY_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ },
- { AUDIT_TTY_SET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_TTY_SET, NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT },
};
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 01ec6d2..d3c8b98 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -595,7 +595,7 @@
length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT,
"%x %x %x %x %u",
- avd.allowed, avd.decided,
+ avd.allowed, 0xffffffff,
avd.auditallow, avd.auditdeny,
avd.seqno);
out2:
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index c65e4fe..deeec6c 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -407,7 +407,6 @@
* Initialize the access vectors to the default values.
*/
avd->allowed = 0;
- avd->decided = 0xffffffff;
avd->auditallow = 0;
avd->auditdeny = 0xffffffff;
avd->seqno = latest_granting;
@@ -743,7 +742,6 @@
if (!ss_initialized) {
avd->allowed = 0xffffffff;
- avd->decided = 0xffffffff;
avd->auditallow = 0;
avd->auditdeny = 0xffffffff;
avd->seqno = latest_granting;
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index 2e0b83e..cfa19ca 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -162,8 +162,8 @@
/**
* smk_curacc - determine if current has a specific access to an object
- * @object_label: a pointer to the object's Smack label
- * @request: the access requested, in "MAY" format
+ * @obj_label: a pointer to the object's Smack label
+ * @mode: the access requested, in "MAY" format
*
* This function checks the current subject label/object label pair
* in the access rule list and returns 0 if the access is permitted,
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index e7ded13..84b62b5 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -91,6 +91,7 @@
/**
* smack_ptrace_may_access - Smack approval on PTRACE_ATTACH
* @ctp: child task pointer
+ * @mode: ptrace attachment mode
*
* Returns 0 if access is OK, an error code otherwise
*
@@ -203,9 +204,8 @@
/**
* smack_sb_copy_data - copy mount options data for processing
- * @type: file system type
* @orig: where to start
- * @smackopts
+ * @smackopts: mount options string
*
* Returns 0 on success or -ENOMEM on error.
*
@@ -331,7 +331,7 @@
/**
* smack_sb_mount - Smack check for mounting
* @dev_name: unused
- * @nd: mount point
+ * @path: mount point
* @type: unused
* @flags: unused
* @data: unused
@@ -370,7 +370,7 @@
/**
* smack_inode_alloc_security - allocate an inode blob
- * @inode - the inode in need of a blob
+ * @inode: the inode in need of a blob
*
* Returns 0 if it gets a blob, -ENOMEM otherwise
*/
@@ -384,7 +384,7 @@
/**
* smack_inode_free_security - free an inode blob
- * @inode - the inode with a blob
+ * @inode: the inode with a blob
*
* Clears the blob pointer in inode
*/
@@ -538,7 +538,6 @@
* smack_inode_permission - Smack version of permission()
* @inode: the inode in question
* @mask: the access requested
- * @nd: unused
*
* This is the important Smack hook.
*
@@ -701,8 +700,7 @@
* @inode: the object
* @name: attribute name
* @buffer: where to put the result
- * @size: size of the buffer
- * @err: unused
+ * @alloc: unused
*
* Returns the size of the attribute or an error code
*/
@@ -864,7 +862,7 @@
/**
* smack_file_lock - Smack check on file locking
* @file: the object
- * @cmd unused
+ * @cmd: unused
*
* Returns 0 if current has write access, error code otherwise
*/
@@ -1003,8 +1001,8 @@
return 0;
}
-/*
- * commit new credentials
+/**
+ * smack_cred_commit - commit new credentials
* @new: the new credentials
* @old: the original credentials
*/
@@ -1014,8 +1012,8 @@
/**
* smack_kernel_act_as - Set the subjective context in a set of credentials
- * @new points to the set of credentials to be modified.
- * @secid specifies the security ID to be set
+ * @new: points to the set of credentials to be modified.
+ * @secid: specifies the security ID to be set
*
* Set the security data for a kernel service.
*/
@@ -1032,8 +1030,8 @@
/**
* smack_kernel_create_files_as - Set the file creation label in a set of creds
- * @new points to the set of credentials to be modified
- * @inode points to the inode to use as a reference
+ * @new: points to the set of credentials to be modified
+ * @inode: points to the inode to use as a reference
*
* Set the file creation context in a set of credentials to the same
* as the objective context of the specified inode
@@ -1242,7 +1240,7 @@
/**
* smack_task_to_inode - copy task smack into the inode blob
* @p: task to copy from
- * inode: inode to copy to
+ * @inode: inode to copy to
*
* Sets the smack pointer in the inode security blob
*/
@@ -1260,7 +1258,7 @@
* smack_sk_alloc_security - Allocate a socket blob
* @sk: the socket
* @family: unused
- * @priority: memory allocation priority
+ * @gfp_flags: memory allocation flags
*
* Assign Smack pointers to current
*
@@ -1974,7 +1972,7 @@
/**
* smack_ipc_getsecid - Extract smack security id
- * @ipcp: the object permissions
+ * @ipp: the object permissions
* @secid: where result will be saved
*/
static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid)
@@ -2251,7 +2249,7 @@
/**
* smack_socket_sendmsg - Smack check based on destination host
* @sock: the socket
- * @msghdr: the message
+ * @msg: the message
* @size: the size of the message
*
* Return 0 if the current subject can write to the destination
@@ -2292,8 +2290,7 @@
/**
- * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat
- * pair to smack
+ * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat pair to smack
* @sap: netlabel secattr
* @sip: where to put the result
*
@@ -2414,7 +2411,7 @@
* @sock: the socket
* @optval: user's destination
* @optlen: size thereof
- * @len: max thereoe
+ * @len: max thereof
*
* returns zero on success, an error code otherwise
*/
@@ -2749,7 +2746,7 @@
#endif /* CONFIG_AUDIT */
-/*
+/**
* smack_secid_to_secctx - return the smack label for a secid
* @secid: incoming integer
* @secdata: destination
@@ -2766,7 +2763,7 @@
return 0;
}
-/*
+/**
* smack_secctx_to_secid - return the secid for a smack label
* @secdata: smack label
* @seclen: how long result is
@@ -2780,11 +2777,10 @@
return 0;
}
-/*
+/**
* smack_release_secctx - don't do anything.
- * @key_ref: unused
- * @context: unused
- * @perm: unused
+ * @secdata: unused
+ * @seclen: unused
*
* Exists to make sure nothing gets done, and properly
*/
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index 51f0efc..a1b57e4 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -245,7 +245,7 @@
/**
* smk_write_load - write() for /smack/load
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start - must be 0
@@ -402,6 +402,7 @@
/**
* smk_unlbl_ambient - initialize the unlabeled domain
+ * @oldambient: previous domain string
*/
static void smk_unlbl_ambient(char *oldambient)
{
@@ -513,7 +514,7 @@
/**
* smk_write_cipso - write() for /smack/cipso
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start
@@ -733,7 +734,7 @@
/**
* smk_write_netlbladdr - write() for /smack/netlabel
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start
@@ -884,7 +885,7 @@
/**
* smk_write_doi - write() for /smack/doi
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start
@@ -949,7 +950,7 @@
/**
* smk_write_direct - write() for /smack/direct
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start
@@ -1024,7 +1025,7 @@
/**
* smk_write_ambient - write() for /smack/ambient
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start
@@ -1099,7 +1100,7 @@
/**
* smk_write_onlycap - write() for /smack/onlycap
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start
diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig
new file mode 100644
index 0000000..c8f3857
--- /dev/null
+++ b/security/tomoyo/Kconfig
@@ -0,0 +1,11 @@
+config SECURITY_TOMOYO
+ bool "TOMOYO Linux Support"
+ depends on SECURITY
+ select SECURITYFS
+ select SECURITY_PATH
+ default n
+ help
+ This selects TOMOYO Linux, pathname-based access control.
+ Required userspace tools and further information may be
+ found at <http://tomoyo.sourceforge.jp/>.
+ If you are unsure how to answer this question, answer N.
diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile
new file mode 100644
index 0000000..10ccd68
--- /dev/null
+++ b/security/tomoyo/Makefile
@@ -0,0 +1 @@
+obj-y = common.o realpath.o tomoyo.o domain.o file.o
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
new file mode 100644
index 0000000..92cea65
--- /dev/null
+++ b/security/tomoyo/common.c
@@ -0,0 +1,2206 @@
+/*
+ * security/tomoyo/common.c
+ *
+ * Common functions for TOMOYO.
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre 2009/02/01
+ *
+ */
+
+#include <linux/uaccess.h>
+#include <linux/security.h>
+#include <linux/hardirq.h>
+#include "realpath.h"
+#include "common.h"
+#include "tomoyo.h"
+
+/* Has loading policy done? */
+bool tomoyo_policy_loaded;
+
+/* String table for functionality that takes 4 modes. */
+static const char *tomoyo_mode_4[4] = {
+ "disabled", "learning", "permissive", "enforcing"
+};
+/* String table for functionality that takes 2 modes. */
+static const char *tomoyo_mode_2[4] = {
+ "disabled", "enabled", "enabled", "enabled"
+};
+
+/* Table for profile. */
+static struct {
+ const char *keyword;
+ unsigned int current_value;
+ const unsigned int max_value;
+} tomoyo_control_array[TOMOYO_MAX_CONTROL_INDEX] = {
+ [TOMOYO_MAC_FOR_FILE] = { "MAC_FOR_FILE", 0, 3 },
+ [TOMOYO_MAX_ACCEPT_ENTRY] = { "MAX_ACCEPT_ENTRY", 2048, INT_MAX },
+ [TOMOYO_VERBOSE] = { "TOMOYO_VERBOSE", 1, 1 },
+};
+
+/* Profile table. Memory is allocated as needed. */
+static struct tomoyo_profile {
+ unsigned int value[TOMOYO_MAX_CONTROL_INDEX];
+ const struct tomoyo_path_info *comment;
+} *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES];
+
+/* Permit policy management by non-root user? */
+static bool tomoyo_manage_by_non_root;
+
+/* Utility functions. */
+
+/* Open operation for /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_open_control(const u8 type, struct file *file);
+/* Close /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_close_control(struct file *file);
+/* Read operation for /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_read_control(struct file *file, char __user *buffer,
+ const int buffer_len);
+/* Write operation for /sys/kernel/security/tomoyo/ interface. */
+static int tomoyo_write_control(struct file *file, const char __user *buffer,
+ const int buffer_len);
+
+/**
+ * tomoyo_is_byte_range - Check whether the string isa \ooo style octal value.
+ *
+ * @str: Pointer to the string.
+ *
+ * Returns true if @str is a \ooo style octal value, false otherwise.
+ *
+ * TOMOYO uses \ooo style representation for 0x01 - 0x20 and 0x7F - 0xFF.
+ * This function verifies that \ooo is in valid range.
+ */
+static inline bool tomoyo_is_byte_range(const char *str)
+{
+ return *str >= '0' && *str++ <= '3' &&
+ *str >= '0' && *str++ <= '7' &&
+ *str >= '0' && *str <= '7';
+}
+
+/**
+ * tomoyo_is_alphabet_char - Check whether the character is an alphabet.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is an alphabet character, false otherwise.
+ */
+static inline bool tomoyo_is_alphabet_char(const char c)
+{
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+
+/**
+ * tomoyo_make_byte - Make byte value from three octal characters.
+ *
+ * @c1: The first character.
+ * @c2: The second character.
+ * @c3: The third character.
+ *
+ * Returns byte value.
+ */
+static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3)
+{
+ return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0');
+}
+
+/**
+ * tomoyo_str_starts - Check whether the given string starts with the given keyword.
+ *
+ * @src: Pointer to pointer to the string.
+ * @find: Pointer to the keyword.
+ *
+ * Returns true if @src starts with @find, false otherwise.
+ *
+ * The @src is updated to point the first character after the @find
+ * if @src starts with @find.
+ */
+static bool tomoyo_str_starts(char **src, const char *find)
+{
+ const int len = strlen(find);
+ char *tmp = *src;
+
+ if (strncmp(tmp, find, len))
+ return false;
+ tmp += len;
+ *src = tmp;
+ return true;
+}
+
+/**
+ * tomoyo_normalize_line - Format string.
+ *
+ * @buffer: The line to normalize.
+ *
+ * Leading and trailing whitespaces are removed.
+ * Multiple whitespaces are packed into single space.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_normalize_line(unsigned char *buffer)
+{
+ unsigned char *sp = buffer;
+ unsigned char *dp = buffer;
+ bool first = true;
+
+ while (tomoyo_is_invalid(*sp))
+ sp++;
+ while (*sp) {
+ if (!first)
+ *dp++ = ' ';
+ first = false;
+ while (tomoyo_is_valid(*sp))
+ *dp++ = *sp++;
+ while (tomoyo_is_invalid(*sp))
+ sp++;
+ }
+ *dp = '\0';
+}
+
+/**
+ * tomoyo_is_correct_path - Validate a pathname.
+ * @filename: The pathname to check.
+ * @start_type: Should the pathname start with '/'?
+ * 1 = must / -1 = must not / 0 = don't care
+ * @pattern_type: Can the pathname contain a wildcard?
+ * 1 = must / -1 = must not / 0 = don't care
+ * @end_type: Should the pathname end with '/'?
+ * 1 = must / -1 = must not / 0 = don't care
+ * @function: The name of function calling me.
+ *
+ * Check whether the given filename follows the naming rules.
+ * Returns true if @filename follows the naming rules, false otherwise.
+ */
+bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
+ const s8 pattern_type, const s8 end_type,
+ const char *function)
+{
+ bool contains_pattern = false;
+ unsigned char c;
+ unsigned char d;
+ unsigned char e;
+ const char *original_filename = filename;
+
+ if (!filename)
+ goto out;
+ c = *filename;
+ if (start_type == 1) { /* Must start with '/' */
+ if (c != '/')
+ goto out;
+ } else if (start_type == -1) { /* Must not start with '/' */
+ if (c == '/')
+ goto out;
+ }
+ if (c)
+ c = *(filename + strlen(filename) - 1);
+ if (end_type == 1) { /* Must end with '/' */
+ if (c != '/')
+ goto out;
+ } else if (end_type == -1) { /* Must not end with '/' */
+ if (c == '/')
+ goto out;
+ }
+ while ((c = *filename++) != '\0') {
+ if (c == '\\') {
+ switch ((c = *filename++)) {
+ case '\\': /* "\\" */
+ continue;
+ case '$': /* "\$" */
+ case '+': /* "\+" */
+ case '?': /* "\?" */
+ case '*': /* "\*" */
+ case '@': /* "\@" */
+ case 'x': /* "\x" */
+ case 'X': /* "\X" */
+ case 'a': /* "\a" */
+ case 'A': /* "\A" */
+ case '-': /* "\-" */
+ if (pattern_type == -1)
+ break; /* Must not contain pattern */
+ contains_pattern = true;
+ continue;
+ case '0': /* "\ooo" */
+ case '1':
+ case '2':
+ case '3':
+ d = *filename++;
+ if (d < '0' || d > '7')
+ break;
+ e = *filename++;
+ if (e < '0' || e > '7')
+ break;
+ c = tomoyo_make_byte(c, d, e);
+ if (tomoyo_is_invalid(c))
+ continue; /* pattern is not \000 */
+ }
+ goto out;
+ } else if (tomoyo_is_invalid(c)) {
+ goto out;
+ }
+ }
+ if (pattern_type == 1) { /* Must contain pattern */
+ if (!contains_pattern)
+ goto out;
+ }
+ return true;
+ out:
+ printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function,
+ original_filename);
+ return false;
+}
+
+/**
+ * tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules.
+ * @domainname: The domainname to check.
+ * @function: The name of function calling me.
+ *
+ * Returns true if @domainname follows the naming rules, false otherwise.
+ */
+bool tomoyo_is_correct_domain(const unsigned char *domainname,
+ const char *function)
+{
+ unsigned char c;
+ unsigned char d;
+ unsigned char e;
+ const char *org_domainname = domainname;
+
+ if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME,
+ TOMOYO_ROOT_NAME_LEN))
+ goto out;
+ domainname += TOMOYO_ROOT_NAME_LEN;
+ if (!*domainname)
+ return true;
+ do {
+ if (*domainname++ != ' ')
+ goto out;
+ if (*domainname++ != '/')
+ goto out;
+ while ((c = *domainname) != '\0' && c != ' ') {
+ domainname++;
+ if (c == '\\') {
+ c = *domainname++;
+ switch ((c)) {
+ case '\\': /* "\\" */
+ continue;
+ case '0': /* "\ooo" */
+ case '1':
+ case '2':
+ case '3':
+ d = *domainname++;
+ if (d < '0' || d > '7')
+ break;
+ e = *domainname++;
+ if (e < '0' || e > '7')
+ break;
+ c = tomoyo_make_byte(c, d, e);
+ if (tomoyo_is_invalid(c))
+ /* pattern is not \000 */
+ continue;
+ }
+ goto out;
+ } else if (tomoyo_is_invalid(c)) {
+ goto out;
+ }
+ }
+ } while (*domainname);
+ return true;
+ out:
+ printk(KERN_DEBUG "%s: Invalid domainname '%s'\n", function,
+ org_domainname);
+ return false;
+}
+
+/**
+ * tomoyo_is_domain_def - Check whether the given token can be a domainname.
+ *
+ * @buffer: The token to check.
+ *
+ * Returns true if @buffer possibly be a domainname, false otherwise.
+ */
+bool tomoyo_is_domain_def(const unsigned char *buffer)
+{
+ return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN);
+}
+
+/**
+ * tomoyo_find_domain - Find a domain by the given name.
+ *
+ * @domainname: The domainname to find.
+ *
+ * Caller must call down_read(&tomoyo_domain_list_lock); or
+ * down_write(&tomoyo_domain_list_lock); .
+ *
+ * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
+ */
+struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
+{
+ struct tomoyo_domain_info *domain;
+ struct tomoyo_path_info name;
+
+ name.name = domainname;
+ tomoyo_fill_path_info(&name);
+ list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ if (!domain->is_deleted &&
+ !tomoyo_pathcmp(&name, domain->domainname))
+ return domain;
+ }
+ return NULL;
+}
+
+/**
+ * tomoyo_path_depth - Evaluate the number of '/' in a string.
+ *
+ * @pathname: The string to evaluate.
+ *
+ * Returns path depth of the string.
+ *
+ * I score 2 for each of the '/' in the @pathname
+ * and score 1 if the @pathname ends with '/'.
+ */
+static int tomoyo_path_depth(const char *pathname)
+{
+ int i = 0;
+
+ if (pathname) {
+ const char *ep = pathname + strlen(pathname);
+ if (pathname < ep--) {
+ if (*ep != '/')
+ i++;
+ while (pathname <= ep)
+ if (*ep-- == '/')
+ i += 2;
+ }
+ }
+ return i;
+}
+
+/**
+ * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token.
+ *
+ * @filename: The string to evaluate.
+ *
+ * Returns the initial length without a pattern in @filename.
+ */
+static int tomoyo_const_part_length(const char *filename)
+{
+ char c;
+ int len = 0;
+
+ if (!filename)
+ return 0;
+ while ((c = *filename++) != '\0') {
+ if (c != '\\') {
+ len++;
+ continue;
+ }
+ c = *filename++;
+ switch (c) {
+ case '\\': /* "\\" */
+ len += 2;
+ continue;
+ case '0': /* "\ooo" */
+ case '1':
+ case '2':
+ case '3':
+ c = *filename++;
+ if (c < '0' || c > '7')
+ break;
+ c = *filename++;
+ if (c < '0' || c > '7')
+ break;
+ len += 4;
+ continue;
+ }
+ break;
+ }
+ return len;
+}
+
+/**
+ * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members.
+ *
+ * @ptr: Pointer to "struct tomoyo_path_info" to fill in.
+ *
+ * The caller sets "struct tomoyo_path_info"->name.
+ */
+void tomoyo_fill_path_info(struct tomoyo_path_info *ptr)
+{
+ const char *name = ptr->name;
+ const int len = strlen(name);
+
+ ptr->total_len = len;
+ ptr->const_len = tomoyo_const_part_length(name);
+ ptr->is_dir = len && (name[len - 1] == '/');
+ ptr->is_patterned = (ptr->const_len < len);
+ ptr->hash = full_name_hash(name, len);
+ ptr->depth = tomoyo_path_depth(name);
+}
+
+/**
+ * tomoyo_file_matches_to_pattern2 - Pattern matching without '/' character
+ * and "\-" pattern.
+ *
+ * @filename: The start of string to check.
+ * @filename_end: The end of string to check.
+ * @pattern: The start of pattern to compare.
+ * @pattern_end: The end of pattern to compare.
+ *
+ * Returns true if @filename matches @pattern, false otherwise.
+ */
+static bool tomoyo_file_matches_to_pattern2(const char *filename,
+ const char *filename_end,
+ const char *pattern,
+ const char *pattern_end)
+{
+ while (filename < filename_end && pattern < pattern_end) {
+ char c;
+ if (*pattern != '\\') {
+ if (*filename++ != *pattern++)
+ return false;
+ continue;
+ }
+ c = *filename;
+ pattern++;
+ switch (*pattern) {
+ int i;
+ int j;
+ case '?':
+ if (c == '/') {
+ return false;
+ } else if (c == '\\') {
+ if (filename[1] == '\\')
+ filename++;
+ else if (tomoyo_is_byte_range(filename + 1))
+ filename += 3;
+ else
+ return false;
+ }
+ break;
+ case '\\':
+ if (c != '\\')
+ return false;
+ if (*++filename != '\\')
+ return false;
+ break;
+ case '+':
+ if (!isdigit(c))
+ return false;
+ break;
+ case 'x':
+ if (!isxdigit(c))
+ return false;
+ break;
+ case 'a':
+ if (!tomoyo_is_alphabet_char(c))
+ return false;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ if (c == '\\' && tomoyo_is_byte_range(filename + 1)
+ && strncmp(filename + 1, pattern, 3) == 0) {
+ filename += 3;
+ pattern += 2;
+ break;
+ }
+ return false; /* Not matched. */
+ case '*':
+ case '@':
+ for (i = 0; i <= filename_end - filename; i++) {
+ if (tomoyo_file_matches_to_pattern2(
+ filename + i, filename_end,
+ pattern + 1, pattern_end))
+ return true;
+ c = filename[i];
+ if (c == '.' && *pattern == '@')
+ break;
+ if (c != '\\')
+ continue;
+ if (filename[i + 1] == '\\')
+ i++;
+ else if (tomoyo_is_byte_range(filename + i + 1))
+ i += 3;
+ else
+ break; /* Bad pattern. */
+ }
+ return false; /* Not matched. */
+ default:
+ j = 0;
+ c = *pattern;
+ if (c == '$') {
+ while (isdigit(filename[j]))
+ j++;
+ } else if (c == 'X') {
+ while (isxdigit(filename[j]))
+ j++;
+ } else if (c == 'A') {
+ while (tomoyo_is_alphabet_char(filename[j]))
+ j++;
+ }
+ for (i = 1; i <= j; i++) {
+ if (tomoyo_file_matches_to_pattern2(
+ filename + i, filename_end,
+ pattern + 1, pattern_end))
+ return true;
+ }
+ return false; /* Not matched or bad pattern. */
+ }
+ filename++;
+ pattern++;
+ }
+ while (*pattern == '\\' &&
+ (*(pattern + 1) == '*' || *(pattern + 1) == '@'))
+ pattern += 2;
+ return filename == filename_end && pattern == pattern_end;
+}
+
+/**
+ * tomoyo_file_matches_to_pattern - Pattern matching without without '/' character.
+ *
+ * @filename: The start of string to check.
+ * @filename_end: The end of string to check.
+ * @pattern: The start of pattern to compare.
+ * @pattern_end: The end of pattern to compare.
+ *
+ * Returns true if @filename matches @pattern, false otherwise.
+ */
+static bool tomoyo_file_matches_to_pattern(const char *filename,
+ const char *filename_end,
+ const char *pattern,
+ const char *pattern_end)
+{
+ const char *pattern_start = pattern;
+ bool first = true;
+ bool result;
+
+ while (pattern < pattern_end - 1) {
+ /* Split at "\-" pattern. */
+ if (*pattern++ != '\\' || *pattern++ != '-')
+ continue;
+ result = tomoyo_file_matches_to_pattern2(filename,
+ filename_end,
+ pattern_start,
+ pattern - 2);
+ if (first)
+ result = !result;
+ if (result)
+ return false;
+ first = false;
+ pattern_start = pattern;
+ }
+ result = tomoyo_file_matches_to_pattern2(filename, filename_end,
+ pattern_start, pattern_end);
+ return first ? result : !result;
+}
+
+/**
+ * tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern.
+ * @filename: The filename to check.
+ * @pattern: The pattern to compare.
+ *
+ * Returns true if matches, false otherwise.
+ *
+ * The following patterns are available.
+ * \\ \ itself.
+ * \ooo Octal representation of a byte.
+ * \* More than or equals to 0 character other than '/'.
+ * \@ More than or equals to 0 character other than '/' or '.'.
+ * \? 1 byte character other than '/'.
+ * \$ More than or equals to 1 decimal digit.
+ * \+ 1 decimal digit.
+ * \X More than or equals to 1 hexadecimal digit.
+ * \x 1 hexadecimal digit.
+ * \A More than or equals to 1 alphabet character.
+ * \a 1 alphabet character.
+ * \- Subtraction operator.
+ */
+bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
+ const struct tomoyo_path_info *pattern)
+{
+ /*
+ if (!filename || !pattern)
+ return false;
+ */
+ const char *f = filename->name;
+ const char *p = pattern->name;
+ const int len = pattern->const_len;
+
+ /* If @pattern doesn't contain pattern, I can use strcmp(). */
+ if (!pattern->is_patterned)
+ return !tomoyo_pathcmp(filename, pattern);
+ /* Dont compare if the number of '/' differs. */
+ if (filename->depth != pattern->depth)
+ return false;
+ /* Compare the initial length without patterns. */
+ if (strncmp(f, p, len))
+ return false;
+ f += len;
+ p += len;
+ /* Main loop. Compare each directory component. */
+ while (*f && *p) {
+ const char *f_delimiter = strchr(f, '/');
+ const char *p_delimiter = strchr(p, '/');
+ if (!f_delimiter)
+ f_delimiter = f + strlen(f);
+ if (!p_delimiter)
+ p_delimiter = p + strlen(p);
+ if (!tomoyo_file_matches_to_pattern(f, f_delimiter,
+ p, p_delimiter))
+ return false;
+ f = f_delimiter;
+ if (*f)
+ f++;
+ p = p_delimiter;
+ if (*p)
+ p++;
+ }
+ /* Ignore trailing "\*" and "\@" in @pattern. */
+ while (*p == '\\' &&
+ (*(p + 1) == '*' || *(p + 1) == '@'))
+ p += 2;
+ return !*f && !*p;
+}
+
+/**
+ * tomoyo_io_printf - Transactional printf() to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @fmt: The printf()'s format string, followed by parameters.
+ *
+ * Returns true if output was written, false otherwise.
+ *
+ * The snprintf() will truncate, but tomoyo_io_printf() won't.
+ */
+bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+{
+ va_list args;
+ int len;
+ int pos = head->read_avail;
+ int size = head->readbuf_size - pos;
+
+ if (size <= 0)
+ return false;
+ va_start(args, fmt);
+ len = vsnprintf(head->read_buf + pos, size, fmt, args);
+ va_end(args);
+ if (pos + len >= head->readbuf_size)
+ return false;
+ head->read_avail += len;
+ return true;
+}
+
+/**
+ * tomoyo_get_exe - Get tomoyo_realpath() of current process.
+ *
+ * Returns the tomoyo_realpath() of current process on success, NULL otherwise.
+ *
+ * This function uses tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if this function didn't return NULL.
+ */
+static const char *tomoyo_get_exe(void)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ const char *cp = NULL;
+
+ if (!mm)
+ return NULL;
+ down_read(&mm->mmap_sem);
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) {
+ cp = tomoyo_realpath_from_path(&vma->vm_file->f_path);
+ break;
+ }
+ }
+ up_read(&mm->mmap_sem);
+ return cp;
+}
+
+/**
+ * tomoyo_get_msg - Get warning message.
+ *
+ * @is_enforce: Is it enforcing mode?
+ *
+ * Returns "ERROR" or "WARNING".
+ */
+const char *tomoyo_get_msg(const bool is_enforce)
+{
+ if (is_enforce)
+ return "ERROR";
+ else
+ return "WARNING";
+}
+
+/**
+ * tomoyo_check_flags - Check mode for specified functionality.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @index: The functionality to check mode.
+ *
+ * TOMOYO checks only process context.
+ * This code disables TOMOYO's enforcement in case the function is called from
+ * interrupt context.
+ */
+unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
+ const u8 index)
+{
+ const u8 profile = domain->profile;
+
+ if (WARN_ON(in_interrupt()))
+ return 0;
+ return tomoyo_policy_loaded && index < TOMOYO_MAX_CONTROL_INDEX
+#if TOMOYO_MAX_PROFILES != 256
+ && profile < TOMOYO_MAX_PROFILES
+#endif
+ && tomoyo_profile_ptr[profile] ?
+ tomoyo_profile_ptr[profile]->value[index] : 0;
+}
+
+/**
+ * tomoyo_verbose_mode - Check whether TOMOYO is verbose mode.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * Returns true if domain policy violation warning should be printed to
+ * console.
+ */
+bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain)
+{
+ return tomoyo_check_flags(domain, TOMOYO_VERBOSE) != 0;
+}
+
+/**
+ * tomoyo_domain_quota_is_ok - Check for domain's quota.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * Returns true if the domain is not exceeded quota, false otherwise.
+ */
+bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
+{
+ unsigned int count = 0;
+ struct tomoyo_acl_info *ptr;
+
+ if (!domain)
+ return true;
+ down_read(&tomoyo_domain_acl_info_list_lock);
+ list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ if (ptr->type & TOMOYO_ACL_DELETED)
+ continue;
+ switch (tomoyo_acl_type2(ptr)) {
+ struct tomoyo_single_path_acl_record *acl1;
+ struct tomoyo_double_path_acl_record *acl2;
+ u16 perm;
+ case TOMOYO_TYPE_SINGLE_PATH_ACL:
+ acl1 = container_of(ptr,
+ struct tomoyo_single_path_acl_record,
+ head);
+ perm = acl1->perm;
+ if (perm & (1 << TOMOYO_TYPE_EXECUTE_ACL))
+ count++;
+ if (perm &
+ ((1 << TOMOYO_TYPE_READ_ACL) |
+ (1 << TOMOYO_TYPE_WRITE_ACL)))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_CREATE_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_UNLINK_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_MKDIR_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_RMDIR_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_MKFIFO_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_MKSOCK_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_MKBLOCK_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_MKCHAR_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_TRUNCATE_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_SYMLINK_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_REWRITE_ACL))
+ count++;
+ break;
+ case TOMOYO_TYPE_DOUBLE_PATH_ACL:
+ acl2 = container_of(ptr,
+ struct tomoyo_double_path_acl_record,
+ head);
+ perm = acl2->perm;
+ if (perm & (1 << TOMOYO_TYPE_LINK_ACL))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_RENAME_ACL))
+ count++;
+ break;
+ }
+ }
+ up_read(&tomoyo_domain_acl_info_list_lock);
+ if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY))
+ return true;
+ if (!domain->quota_warned) {
+ domain->quota_warned = true;
+ printk(KERN_WARNING "TOMOYO-WARNING: "
+ "Domain '%s' has so many ACLs to hold. "
+ "Stopped learning mode.\n", domain->domainname->name);
+ }
+ return false;
+}
+
+/**
+ * tomoyo_find_or_assign_new_profile - Create a new profile.
+ *
+ * @profile: Profile number to create.
+ *
+ * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise.
+ */
+static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned
+ int profile)
+{
+ static DEFINE_MUTEX(lock);
+ struct tomoyo_profile *ptr = NULL;
+ int i;
+
+ if (profile >= TOMOYO_MAX_PROFILES)
+ return NULL;
+ /***** EXCLUSIVE SECTION START *****/
+ mutex_lock(&lock);
+ ptr = tomoyo_profile_ptr[profile];
+ if (ptr)
+ goto ok;
+ ptr = tomoyo_alloc_element(sizeof(*ptr));
+ if (!ptr)
+ goto ok;
+ for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++)
+ ptr->value[i] = tomoyo_control_array[i].current_value;
+ mb(); /* Avoid out-of-order execution. */
+ tomoyo_profile_ptr[profile] = ptr;
+ ok:
+ mutex_unlock(&lock);
+ /***** EXCLUSIVE SECTION END *****/
+ return ptr;
+}
+
+/**
+ * tomoyo_write_profile - Write to profile table.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_profile(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ unsigned int i;
+ unsigned int value;
+ char *cp;
+ struct tomoyo_profile *profile;
+ unsigned long num;
+
+ cp = strchr(data, '-');
+ if (cp)
+ *cp = '\0';
+ if (strict_strtoul(data, 10, &num))
+ return -EINVAL;
+ if (cp)
+ data = cp + 1;
+ profile = tomoyo_find_or_assign_new_profile(num);
+ if (!profile)
+ return -EINVAL;
+ cp = strchr(data, '=');
+ if (!cp)
+ return -EINVAL;
+ *cp = '\0';
+ if (!strcmp(data, "COMMENT")) {
+ profile->comment = tomoyo_save_name(cp + 1);
+ return 0;
+ }
+ for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) {
+ if (strcmp(data, tomoyo_control_array[i].keyword))
+ continue;
+ if (sscanf(cp + 1, "%u", &value) != 1) {
+ int j;
+ const char **modes;
+ switch (i) {
+ case TOMOYO_VERBOSE:
+ modes = tomoyo_mode_2;
+ break;
+ default:
+ modes = tomoyo_mode_4;
+ break;
+ }
+ for (j = 0; j < 4; j++) {
+ if (strcmp(cp + 1, modes[j]))
+ continue;
+ value = j;
+ break;
+ }
+ if (j == 4)
+ return -EINVAL;
+ } else if (value > tomoyo_control_array[i].max_value) {
+ value = tomoyo_control_array[i].max_value;
+ }
+ profile->value[i] = value;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/**
+ * tomoyo_read_profile - Read from profile table.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_read_profile(struct tomoyo_io_buffer *head)
+{
+ static const int total = TOMOYO_MAX_CONTROL_INDEX + 1;
+ int step;
+
+ if (head->read_eof)
+ return 0;
+ for (step = head->read_step; step < TOMOYO_MAX_PROFILES * total;
+ step++) {
+ const u8 index = step / total;
+ u8 type = step % total;
+ const struct tomoyo_profile *profile
+ = tomoyo_profile_ptr[index];
+ head->read_step = step;
+ if (!profile)
+ continue;
+ if (!type) { /* Print profile' comment tag. */
+ if (!tomoyo_io_printf(head, "%u-COMMENT=%s\n",
+ index, profile->comment ?
+ profile->comment->name : ""))
+ break;
+ continue;
+ }
+ type--;
+ if (type < TOMOYO_MAX_CONTROL_INDEX) {
+ const unsigned int value = profile->value[type];
+ const char **modes = NULL;
+ const char *keyword
+ = tomoyo_control_array[type].keyword;
+ switch (tomoyo_control_array[type].max_value) {
+ case 3:
+ modes = tomoyo_mode_4;
+ break;
+ case 1:
+ modes = tomoyo_mode_2;
+ break;
+ }
+ if (modes) {
+ if (!tomoyo_io_printf(head, "%u-%s=%s\n", index,
+ keyword, modes[value]))
+ break;
+ } else {
+ if (!tomoyo_io_printf(head, "%u-%s=%u\n", index,
+ keyword, value))
+ break;
+ }
+ }
+ }
+ if (step == TOMOYO_MAX_PROFILES * total)
+ head->read_eof = true;
+ return 0;
+}
+
+/* Structure for policy manager. */
+struct tomoyo_policy_manager_entry {
+ struct list_head list;
+ /* A path to program or a domainname. */
+ const struct tomoyo_path_info *manager;
+ bool is_domain; /* True if manager is a domainname. */
+ bool is_deleted; /* True if this entry is deleted. */
+};
+
+/* The list for "struct tomoyo_policy_manager_entry". */
+static LIST_HEAD(tomoyo_policy_manager_list);
+static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);
+
+/**
+ * tomoyo_update_manager_entry - Add a manager entry.
+ *
+ * @manager: The path to manager or the domainnamme.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_manager_entry(const char *manager,
+ const bool is_delete)
+{
+ struct tomoyo_policy_manager_entry *new_entry;
+ struct tomoyo_policy_manager_entry *ptr;
+ const struct tomoyo_path_info *saved_manager;
+ int error = -ENOMEM;
+ bool is_domain = false;
+
+ if (tomoyo_is_domain_def(manager)) {
+ if (!tomoyo_is_correct_domain(manager, __func__))
+ return -EINVAL;
+ is_domain = true;
+ } else {
+ if (!tomoyo_is_correct_path(manager, 1, -1, -1, __func__))
+ return -EINVAL;
+ }
+ saved_manager = tomoyo_save_name(manager);
+ if (!saved_manager)
+ return -ENOMEM;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_policy_manager_list_lock);
+ list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ if (ptr->manager != saved_manager)
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ goto out;
+ }
+ if (is_delete) {
+ error = -ENOENT;
+ goto out;
+ }
+ new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+ if (!new_entry)
+ goto out;
+ new_entry->manager = saved_manager;
+ new_entry->is_domain = is_domain;
+ list_add_tail(&new_entry->list, &tomoyo_policy_manager_list);
+ error = 0;
+ out:
+ up_write(&tomoyo_policy_manager_list_lock);
+ /***** EXCLUSIVE SECTION END *****/
+ return error;
+}
+
+/**
+ * tomoyo_write_manager_policy - Write manager policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
+
+ if (!strcmp(data, "manage_by_non_root")) {
+ tomoyo_manage_by_non_root = !is_delete;
+ return 0;
+ }
+ return tomoyo_update_manager_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_manager_policy - Read manager policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ bool done = true;
+
+ if (head->read_eof)
+ return 0;
+ down_read(&tomoyo_policy_manager_list_lock);
+ list_for_each_cookie(pos, head->read_var2,
+ &tomoyo_policy_manager_list) {
+ struct tomoyo_policy_manager_entry *ptr;
+ ptr = list_entry(pos, struct tomoyo_policy_manager_entry,
+ list);
+ if (ptr->is_deleted)
+ continue;
+ if (!tomoyo_io_printf(head, "%s\n", ptr->manager->name)) {
+ done = false;
+ break;
+ }
+ }
+ up_read(&tomoyo_policy_manager_list_lock);
+ head->read_eof = done;
+ return 0;
+}
+
+/**
+ * tomoyo_is_policy_manager - Check whether the current process is a policy manager.
+ *
+ * Returns true if the current process is permitted to modify policy
+ * via /sys/kernel/security/tomoyo/ interface.
+ */
+static bool tomoyo_is_policy_manager(void)
+{
+ struct tomoyo_policy_manager_entry *ptr;
+ const char *exe;
+ const struct task_struct *task = current;
+ const struct tomoyo_path_info *domainname = tomoyo_domain()->domainname;
+ bool found = false;
+
+ if (!tomoyo_policy_loaded)
+ return true;
+ if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
+ return false;
+ down_read(&tomoyo_policy_manager_list_lock);
+ list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ if (!ptr->is_deleted && ptr->is_domain
+ && !tomoyo_pathcmp(domainname, ptr->manager)) {
+ found = true;
+ break;
+ }
+ }
+ up_read(&tomoyo_policy_manager_list_lock);
+ if (found)
+ return true;
+ exe = tomoyo_get_exe();
+ if (!exe)
+ return false;
+ down_read(&tomoyo_policy_manager_list_lock);
+ list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ if (!ptr->is_deleted && !ptr->is_domain
+ && !strcmp(exe, ptr->manager->name)) {
+ found = true;
+ break;
+ }
+ }
+ up_read(&tomoyo_policy_manager_list_lock);
+ if (!found) { /* Reduce error messages. */
+ static pid_t last_pid;
+ const pid_t pid = current->pid;
+ if (last_pid != pid) {
+ printk(KERN_WARNING "%s ( %s ) is not permitted to "
+ "update policies.\n", domainname->name, exe);
+ last_pid = pid;
+ }
+ }
+ tomoyo_free(exe);
+ return found;
+}
+
+/**
+ * tomoyo_is_select_one - Parse select command.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @data: String to parse.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
+ const char *data)
+{
+ unsigned int pid;
+ struct tomoyo_domain_info *domain = NULL;
+
+ if (sscanf(data, "pid=%u", &pid) == 1) {
+ struct task_struct *p;
+ /***** CRITICAL SECTION START *****/
+ read_lock(&tasklist_lock);
+ p = find_task_by_vpid(pid);
+ if (p)
+ domain = tomoyo_real_domain(p);
+ read_unlock(&tasklist_lock);
+ /***** CRITICAL SECTION END *****/
+ } else if (!strncmp(data, "domain=", 7)) {
+ if (tomoyo_is_domain_def(data + 7)) {
+ down_read(&tomoyo_domain_list_lock);
+ domain = tomoyo_find_domain(data + 7);
+ up_read(&tomoyo_domain_list_lock);
+ }
+ } else
+ return false;
+ head->write_var1 = domain;
+ /* Accessing read_buf is safe because head->io_sem is held. */
+ if (!head->read_buf)
+ return true; /* Do nothing if open(O_WRONLY). */
+ head->read_avail = 0;
+ tomoyo_io_printf(head, "# select %s\n", data);
+ head->read_single_domain = true;
+ head->read_eof = !domain;
+ if (domain) {
+ struct tomoyo_domain_info *d;
+ head->read_var1 = NULL;
+ down_read(&tomoyo_domain_list_lock);
+ list_for_each_entry(d, &tomoyo_domain_list, list) {
+ if (d == domain)
+ break;
+ head->read_var1 = &d->list;
+ }
+ up_read(&tomoyo_domain_list_lock);
+ head->read_var2 = NULL;
+ head->read_bit = 0;
+ head->read_step = 0;
+ if (domain->is_deleted)
+ tomoyo_io_printf(head, "# This is a deleted domain.\n");
+ }
+ return true;
+}
+
+/**
+ * tomoyo_write_domain_policy - Write domain policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ struct tomoyo_domain_info *domain = head->write_var1;
+ bool is_delete = false;
+ bool is_select = false;
+ bool is_undelete = false;
+ unsigned int profile;
+
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE))
+ is_delete = true;
+ else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT))
+ is_select = true;
+ else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_UNDELETE))
+ is_undelete = true;
+ if (is_select && tomoyo_is_select_one(head, data))
+ return 0;
+ /* Don't allow updating policies by non manager programs. */
+ if (!tomoyo_is_policy_manager())
+ return -EPERM;
+ if (tomoyo_is_domain_def(data)) {
+ domain = NULL;
+ if (is_delete)
+ tomoyo_delete_domain(data);
+ else if (is_select) {
+ down_read(&tomoyo_domain_list_lock);
+ domain = tomoyo_find_domain(data);
+ up_read(&tomoyo_domain_list_lock);
+ } else if (is_undelete)
+ domain = tomoyo_undelete_domain(data);
+ else
+ domain = tomoyo_find_or_assign_new_domain(data, 0);
+ head->write_var1 = domain;
+ return 0;
+ }
+ if (!domain)
+ return -EINVAL;
+
+ if (sscanf(data, TOMOYO_KEYWORD_USE_PROFILE "%u", &profile) == 1
+ && profile < TOMOYO_MAX_PROFILES) {
+ if (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded)
+ domain->profile = (u8) profile;
+ return 0;
+ }
+ if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) {
+ tomoyo_set_domain_flag(domain, is_delete,
+ TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ);
+ return 0;
+ }
+ return tomoyo_write_file_policy(data, domain, is_delete);
+}
+
+/**
+ * tomoyo_print_single_path_acl - Print a single path ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_single_path_acl_record".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_single_path_acl(struct tomoyo_io_buffer *head,
+ struct tomoyo_single_path_acl_record *
+ ptr)
+{
+ int pos;
+ u8 bit;
+ const char *atmark = "";
+ const char *filename;
+ const u16 perm = ptr->perm;
+
+ filename = ptr->filename->name;
+ for (bit = head->read_bit; bit < TOMOYO_MAX_SINGLE_PATH_OPERATION;
+ bit++) {
+ const char *msg;
+ if (!(perm & (1 << bit)))
+ continue;
+ /* Print "read/write" instead of "read" and "write". */
+ if ((bit == TOMOYO_TYPE_READ_ACL ||
+ bit == TOMOYO_TYPE_WRITE_ACL)
+ && (perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL)))
+ continue;
+ msg = tomoyo_sp2keyword(bit);
+ pos = head->read_avail;
+ if (!tomoyo_io_printf(head, "allow_%s %s%s\n", msg,
+ atmark, filename))
+ goto out;
+ }
+ head->read_bit = 0;
+ return true;
+ out:
+ head->read_bit = bit;
+ head->read_avail = pos;
+ return false;
+}
+
+/**
+ * tomoyo_print_double_path_acl - Print a double path ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_double_path_acl_record".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_double_path_acl(struct tomoyo_io_buffer *head,
+ struct tomoyo_double_path_acl_record *
+ ptr)
+{
+ int pos;
+ const char *atmark1 = "";
+ const char *atmark2 = "";
+ const char *filename1;
+ const char *filename2;
+ const u8 perm = ptr->perm;
+ u8 bit;
+
+ filename1 = ptr->filename1->name;
+ filename2 = ptr->filename2->name;
+ for (bit = head->read_bit; bit < TOMOYO_MAX_DOUBLE_PATH_OPERATION;
+ bit++) {
+ const char *msg;
+ if (!(perm & (1 << bit)))
+ continue;
+ msg = tomoyo_dp2keyword(bit);
+ pos = head->read_avail;
+ if (!tomoyo_io_printf(head, "allow_%s %s%s %s%s\n", msg,
+ atmark1, filename1, atmark2, filename2))
+ goto out;
+ }
+ head->read_bit = 0;
+ return true;
+ out:
+ head->read_bit = bit;
+ head->read_avail = pos;
+ return false;
+}
+
+/**
+ * tomoyo_print_entry - Print an ACL entry.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to an ACL entry.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
+ struct tomoyo_acl_info *ptr)
+{
+ const u8 acl_type = tomoyo_acl_type2(ptr);
+
+ if (acl_type & TOMOYO_ACL_DELETED)
+ return true;
+ if (acl_type == TOMOYO_TYPE_SINGLE_PATH_ACL) {
+ struct tomoyo_single_path_acl_record *acl
+ = container_of(ptr,
+ struct tomoyo_single_path_acl_record,
+ head);
+ return tomoyo_print_single_path_acl(head, acl);
+ }
+ if (acl_type == TOMOYO_TYPE_DOUBLE_PATH_ACL) {
+ struct tomoyo_double_path_acl_record *acl
+ = container_of(ptr,
+ struct tomoyo_double_path_acl_record,
+ head);
+ return tomoyo_print_double_path_acl(head, acl);
+ }
+ BUG(); /* This must not happen. */
+ return false;
+}
+
+/**
+ * tomoyo_read_domain_policy - Read domain policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *dpos;
+ struct list_head *apos;
+ bool done = true;
+
+ if (head->read_eof)
+ return 0;
+ if (head->read_step == 0)
+ head->read_step = 1;
+ down_read(&tomoyo_domain_list_lock);
+ list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) {
+ struct tomoyo_domain_info *domain;
+ const char *quota_exceeded = "";
+ const char *transition_failed = "";
+ const char *ignore_global_allow_read = "";
+ domain = list_entry(dpos, struct tomoyo_domain_info, list);
+ if (head->read_step != 1)
+ goto acl_loop;
+ if (domain->is_deleted && !head->read_single_domain)
+ continue;
+ /* Print domainname and flags. */
+ if (domain->quota_warned)
+ quota_exceeded = "quota_exceeded\n";
+ if (domain->flags & TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED)
+ transition_failed = "transition_failed\n";
+ if (domain->flags &
+ TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ)
+ ignore_global_allow_read
+ = TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "\n";
+ if (!tomoyo_io_printf(head,
+ "%s\n" TOMOYO_KEYWORD_USE_PROFILE "%u\n"
+ "%s%s%s\n", domain->domainname->name,
+ domain->profile, quota_exceeded,
+ transition_failed,
+ ignore_global_allow_read)) {
+ done = false;
+ break;
+ }
+ head->read_step = 2;
+acl_loop:
+ if (head->read_step == 3)
+ goto tail_mark;
+ /* Print ACL entries in the domain. */
+ down_read(&tomoyo_domain_acl_info_list_lock);
+ list_for_each_cookie(apos, head->read_var2,
+ &domain->acl_info_list) {
+ struct tomoyo_acl_info *ptr
+ = list_entry(apos, struct tomoyo_acl_info,
+ list);
+ if (!tomoyo_print_entry(head, ptr)) {
+ done = false;
+ break;
+ }
+ }
+ up_read(&tomoyo_domain_acl_info_list_lock);
+ if (!done)
+ break;
+ head->read_step = 3;
+tail_mark:
+ if (!tomoyo_io_printf(head, "\n")) {
+ done = false;
+ break;
+ }
+ head->read_step = 1;
+ if (head->read_single_domain)
+ break;
+ }
+ up_read(&tomoyo_domain_list_lock);
+ head->read_eof = done;
+ return 0;
+}
+
+/**
+ * tomoyo_write_domain_profile - Assign profile for specified domain.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, -EINVAL otherwise.
+ *
+ * This is equivalent to doing
+ *
+ * ( echo "select " $domainname; echo "use_profile " $profile ) |
+ * /usr/lib/ccs/loadpolicy -d
+ */
+static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ char *cp = strchr(data, ' ');
+ struct tomoyo_domain_info *domain;
+ unsigned long profile;
+
+ if (!cp)
+ return -EINVAL;
+ *cp = '\0';
+ down_read(&tomoyo_domain_list_lock);
+ domain = tomoyo_find_domain(cp + 1);
+ up_read(&tomoyo_domain_list_lock);
+ if (strict_strtoul(data, 10, &profile))
+ return -EINVAL;
+ if (domain && profile < TOMOYO_MAX_PROFILES
+ && (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded))
+ domain->profile = (u8) profile;
+ return 0;
+}
+
+/**
+ * tomoyo_read_domain_profile - Read only domainname and profile.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns list of profile number and domainname pairs.
+ *
+ * This is equivalent to doing
+ *
+ * grep -A 1 '^<kernel>' /sys/kernel/security/tomoyo/domain_policy |
+ * awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
+ * domainname = $0; } else if ( $1 == "use_profile" ) {
+ * print $2 " " domainname; domainname = ""; } } ; '
+ */
+static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ bool done = true;
+
+ if (head->read_eof)
+ return 0;
+ down_read(&tomoyo_domain_list_lock);
+ list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) {
+ struct tomoyo_domain_info *domain;
+ domain = list_entry(pos, struct tomoyo_domain_info, list);
+ if (domain->is_deleted)
+ continue;
+ if (!tomoyo_io_printf(head, "%u %s\n", domain->profile,
+ domain->domainname->name)) {
+ done = false;
+ break;
+ }
+ }
+ up_read(&tomoyo_domain_list_lock);
+ head->read_eof = done;
+ return 0;
+}
+
+/**
+ * tomoyo_write_pid: Specify PID to obtain domainname.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_write_pid(struct tomoyo_io_buffer *head)
+{
+ unsigned long pid;
+ /* No error check. */
+ strict_strtoul(head->write_buf, 10, &pid);
+ head->read_step = (int) pid;
+ head->read_eof = false;
+ return 0;
+}
+
+/**
+ * tomoyo_read_pid - Get domainname of the specified PID.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns the domainname which the specified PID is in on success,
+ * empty string otherwise.
+ * The PID is specified by tomoyo_write_pid() so that the user can obtain
+ * using read()/write() interface rather than sysctl() interface.
+ */
+static int tomoyo_read_pid(struct tomoyo_io_buffer *head)
+{
+ if (head->read_avail == 0 && !head->read_eof) {
+ const int pid = head->read_step;
+ struct task_struct *p;
+ struct tomoyo_domain_info *domain = NULL;
+ /***** CRITICAL SECTION START *****/
+ read_lock(&tasklist_lock);
+ p = find_task_by_vpid(pid);
+ if (p)
+ domain = tomoyo_real_domain(p);
+ read_unlock(&tasklist_lock);
+ /***** CRITICAL SECTION END *****/
+ if (domain)
+ tomoyo_io_printf(head, "%d %u %s", pid, domain->profile,
+ domain->domainname->name);
+ head->read_eof = true;
+ }
+ return 0;
+}
+
+/**
+ * tomoyo_write_exception_policy - Write exception policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
+
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_KEEP_DOMAIN))
+ return tomoyo_write_domain_keeper_policy(data, false,
+ is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_KEEP_DOMAIN))
+ return tomoyo_write_domain_keeper_policy(data, true, is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_INITIALIZE_DOMAIN))
+ return tomoyo_write_domain_initializer_policy(data, false,
+ is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN))
+ return tomoyo_write_domain_initializer_policy(data, true,
+ is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALIAS))
+ return tomoyo_write_alias_policy(data, is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_READ))
+ return tomoyo_write_globally_readable_policy(data, is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_FILE_PATTERN))
+ return tomoyo_write_pattern_policy(data, is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DENY_REWRITE))
+ return tomoyo_write_no_rewrite_policy(data, is_delete);
+ return -EINVAL;
+}
+
+/**
+ * tomoyo_read_exception_policy - Read exception policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, -EINVAL otherwise.
+ */
+static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head)
+{
+ if (!head->read_eof) {
+ switch (head->read_step) {
+ case 0:
+ head->read_var2 = NULL;
+ head->read_step = 1;
+ case 1:
+ if (!tomoyo_read_domain_keeper_policy(head))
+ break;
+ head->read_var2 = NULL;
+ head->read_step = 2;
+ case 2:
+ if (!tomoyo_read_globally_readable_policy(head))
+ break;
+ head->read_var2 = NULL;
+ head->read_step = 3;
+ case 3:
+ head->read_var2 = NULL;
+ head->read_step = 4;
+ case 4:
+ if (!tomoyo_read_domain_initializer_policy(head))
+ break;
+ head->read_var2 = NULL;
+ head->read_step = 5;
+ case 5:
+ if (!tomoyo_read_alias_policy(head))
+ break;
+ head->read_var2 = NULL;
+ head->read_step = 6;
+ case 6:
+ head->read_var2 = NULL;
+ head->read_step = 7;
+ case 7:
+ if (!tomoyo_read_file_pattern(head))
+ break;
+ head->read_var2 = NULL;
+ head->read_step = 8;
+ case 8:
+ if (!tomoyo_read_no_rewrite_policy(head))
+ break;
+ head->read_var2 = NULL;
+ head->read_step = 9;
+ case 9:
+ head->read_eof = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/* path to policy loader */
+static const char *tomoyo_loader = "/sbin/tomoyo-init";
+
+/**
+ * tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists.
+ *
+ * Returns true if /sbin/tomoyo-init exists, false otherwise.
+ */
+static bool tomoyo_policy_loader_exists(void)
+{
+ /*
+ * Don't activate MAC if the policy loader doesn't exist.
+ * If the initrd includes /sbin/init but real-root-dev has not
+ * mounted on / yet, activating MAC will block the system since
+ * policies are not loaded yet.
+ * Thus, let do_execve() call this function everytime.
+ */
+ struct nameidata nd;
+
+ if (path_lookup(tomoyo_loader, LOOKUP_FOLLOW, &nd)) {
+ printk(KERN_INFO "Not activating Mandatory Access Control now "
+ "since %s doesn't exist.\n", tomoyo_loader);
+ return false;
+ }
+ path_put(&nd.path);
+ return true;
+}
+
+/**
+ * tomoyo_load_policy - Run external policy loader to load policy.
+ *
+ * @filename: The program about to start.
+ *
+ * This function checks whether @filename is /sbin/init , and if so
+ * invoke /sbin/tomoyo-init and wait for the termination of /sbin/tomoyo-init
+ * and then continues invocation of /sbin/init.
+ * /sbin/tomoyo-init reads policy files in /etc/tomoyo/ directory and
+ * writes to /sys/kernel/security/tomoyo/ interfaces.
+ *
+ * Returns nothing.
+ */
+void tomoyo_load_policy(const char *filename)
+{
+ char *argv[2];
+ char *envp[3];
+
+ if (tomoyo_policy_loaded)
+ return;
+ /*
+ * Check filename is /sbin/init or /sbin/tomoyo-start.
+ * /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't
+ * be passed.
+ * You can create /sbin/tomoyo-start by
+ * "ln -s /bin/true /sbin/tomoyo-start".
+ */
+ if (strcmp(filename, "/sbin/init") &&
+ strcmp(filename, "/sbin/tomoyo-start"))
+ return;
+ if (!tomoyo_policy_loader_exists())
+ return;
+
+ printk(KERN_INFO "Calling %s to load policy. Please wait.\n",
+ tomoyo_loader);
+ argv[0] = (char *) tomoyo_loader;
+ argv[1] = NULL;
+ envp[0] = "HOME=/";
+ envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+ envp[2] = NULL;
+ call_usermodehelper(argv[0], argv, envp, 1);
+
+ printk(KERN_INFO "TOMOYO: 2.2.0-pre 2009/02/01\n");
+ printk(KERN_INFO "Mandatory Access Control activated.\n");
+ tomoyo_policy_loaded = true;
+ { /* Check all profiles currently assigned to domains are defined. */
+ struct tomoyo_domain_info *domain;
+ down_read(&tomoyo_domain_list_lock);
+ list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ const u8 profile = domain->profile;
+ if (tomoyo_profile_ptr[profile])
+ continue;
+ panic("Profile %u (used by '%s') not defined.\n",
+ profile, domain->domainname->name);
+ }
+ up_read(&tomoyo_domain_list_lock);
+ }
+}
+
+/**
+ * tomoyo_read_version: Get version.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns version information.
+ */
+static int tomoyo_read_version(struct tomoyo_io_buffer *head)
+{
+ if (!head->read_eof) {
+ tomoyo_io_printf(head, "2.2.0-pre");
+ head->read_eof = true;
+ }
+ return 0;
+}
+
+/**
+ * tomoyo_read_self_domain - Get the current process's domainname.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns the current process's domainname.
+ */
+static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
+{
+ if (!head->read_eof) {
+ /*
+ * tomoyo_domain()->domainname != NULL
+ * because every process belongs to a domain and
+ * the domain's name cannot be NULL.
+ */
+ tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name);
+ head->read_eof = true;
+ }
+ return 0;
+}
+
+/**
+ * tomoyo_open_control - open() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @type: Type of interface.
+ * @file: Pointer to "struct file".
+ *
+ * Associates policy handler and returns 0 on success, -ENOMEM otherwise.
+ */
+static int tomoyo_open_control(const u8 type, struct file *file)
+{
+ struct tomoyo_io_buffer *head = tomoyo_alloc(sizeof(*head));
+
+ if (!head)
+ return -ENOMEM;
+ mutex_init(&head->io_sem);
+ switch (type) {
+ case TOMOYO_DOMAINPOLICY:
+ /* /sys/kernel/security/tomoyo/domain_policy */
+ head->write = tomoyo_write_domain_policy;
+ head->read = tomoyo_read_domain_policy;
+ break;
+ case TOMOYO_EXCEPTIONPOLICY:
+ /* /sys/kernel/security/tomoyo/exception_policy */
+ head->write = tomoyo_write_exception_policy;
+ head->read = tomoyo_read_exception_policy;
+ break;
+ case TOMOYO_SELFDOMAIN:
+ /* /sys/kernel/security/tomoyo/self_domain */
+ head->read = tomoyo_read_self_domain;
+ break;
+ case TOMOYO_DOMAIN_STATUS:
+ /* /sys/kernel/security/tomoyo/.domain_status */
+ head->write = tomoyo_write_domain_profile;
+ head->read = tomoyo_read_domain_profile;
+ break;
+ case TOMOYO_PROCESS_STATUS:
+ /* /sys/kernel/security/tomoyo/.process_status */
+ head->write = tomoyo_write_pid;
+ head->read = tomoyo_read_pid;
+ break;
+ case TOMOYO_VERSION:
+ /* /sys/kernel/security/tomoyo/version */
+ head->read = tomoyo_read_version;
+ head->readbuf_size = 128;
+ break;
+ case TOMOYO_MEMINFO:
+ /* /sys/kernel/security/tomoyo/meminfo */
+ head->write = tomoyo_write_memory_quota;
+ head->read = tomoyo_read_memory_counter;
+ head->readbuf_size = 512;
+ break;
+ case TOMOYO_PROFILE:
+ /* /sys/kernel/security/tomoyo/profile */
+ head->write = tomoyo_write_profile;
+ head->read = tomoyo_read_profile;
+ break;
+ case TOMOYO_MANAGER:
+ /* /sys/kernel/security/tomoyo/manager */
+ head->write = tomoyo_write_manager_policy;
+ head->read = tomoyo_read_manager_policy;
+ break;
+ }
+ if (!(file->f_mode & FMODE_READ)) {
+ /*
+ * No need to allocate read_buf since it is not opened
+ * for reading.
+ */
+ head->read = NULL;
+ } else {
+ if (!head->readbuf_size)
+ head->readbuf_size = 4096 * 2;
+ head->read_buf = tomoyo_alloc(head->readbuf_size);
+ if (!head->read_buf) {
+ tomoyo_free(head);
+ return -ENOMEM;
+ }
+ }
+ if (!(file->f_mode & FMODE_WRITE)) {
+ /*
+ * No need to allocate write_buf since it is not opened
+ * for writing.
+ */
+ head->write = NULL;
+ } else if (head->write) {
+ head->writebuf_size = 4096 * 2;
+ head->write_buf = tomoyo_alloc(head->writebuf_size);
+ if (!head->write_buf) {
+ tomoyo_free(head->read_buf);
+ tomoyo_free(head);
+ return -ENOMEM;
+ }
+ }
+ file->private_data = head;
+ /*
+ * Call the handler now if the file is
+ * /sys/kernel/security/tomoyo/self_domain
+ * so that the user can use
+ * cat < /sys/kernel/security/tomoyo/self_domain"
+ * to know the current process's domainname.
+ */
+ if (type == TOMOYO_SELFDOMAIN)
+ tomoyo_read_control(file, NULL, 0);
+ return 0;
+}
+
+/**
+ * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ * @buffer: Poiner to buffer to write to.
+ * @buffer_len: Size of @buffer.
+ *
+ * Returns bytes read on success, negative value otherwise.
+ */
+static int tomoyo_read_control(struct file *file, char __user *buffer,
+ const int buffer_len)
+{
+ int len = 0;
+ struct tomoyo_io_buffer *head = file->private_data;
+ char *cp;
+
+ if (!head->read)
+ return -ENOSYS;
+ if (mutex_lock_interruptible(&head->io_sem))
+ return -EINTR;
+ /* Call the policy handler. */
+ len = head->read(head);
+ if (len < 0)
+ goto out;
+ /* Write to buffer. */
+ len = head->read_avail;
+ if (len > buffer_len)
+ len = buffer_len;
+ if (!len)
+ goto out;
+ /* head->read_buf changes by some functions. */
+ cp = head->read_buf;
+ if (copy_to_user(buffer, cp, len)) {
+ len = -EFAULT;
+ goto out;
+ }
+ head->read_avail -= len;
+ memmove(cp, cp + len, head->read_avail);
+ out:
+ mutex_unlock(&head->io_sem);
+ return len;
+}
+
+/**
+ * tomoyo_write_control - write() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ * @buffer: Pointer to buffer to read from.
+ * @buffer_len: Size of @buffer.
+ *
+ * Returns @buffer_len on success, negative value otherwise.
+ */
+static int tomoyo_write_control(struct file *file, const char __user *buffer,
+ const int buffer_len)
+{
+ struct tomoyo_io_buffer *head = file->private_data;
+ int error = buffer_len;
+ int avail_len = buffer_len;
+ char *cp0 = head->write_buf;
+
+ if (!head->write)
+ return -ENOSYS;
+ if (!access_ok(VERIFY_READ, buffer, buffer_len))
+ return -EFAULT;
+ /* Don't allow updating policies by non manager programs. */
+ if (head->write != tomoyo_write_pid &&
+ head->write != tomoyo_write_domain_policy &&
+ !tomoyo_is_policy_manager())
+ return -EPERM;
+ if (mutex_lock_interruptible(&head->io_sem))
+ return -EINTR;
+ /* Read a line and dispatch it to the policy handler. */
+ while (avail_len > 0) {
+ char c;
+ if (head->write_avail >= head->writebuf_size - 1) {
+ error = -ENOMEM;
+ break;
+ } else if (get_user(c, buffer)) {
+ error = -EFAULT;
+ break;
+ }
+ buffer++;
+ avail_len--;
+ cp0[head->write_avail++] = c;
+ if (c != '\n')
+ continue;
+ cp0[head->write_avail - 1] = '\0';
+ head->write_avail = 0;
+ tomoyo_normalize_line(cp0);
+ head->write(head);
+ }
+ mutex_unlock(&head->io_sem);
+ return error;
+}
+
+/**
+ * tomoyo_close_control - close() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ *
+ * Releases memory and returns 0.
+ */
+static int tomoyo_close_control(struct file *file)
+{
+ struct tomoyo_io_buffer *head = file->private_data;
+
+ /* Release memory used for policy I/O. */
+ tomoyo_free(head->read_buf);
+ head->read_buf = NULL;
+ tomoyo_free(head->write_buf);
+ head->write_buf = NULL;
+ tomoyo_free(head);
+ head = NULL;
+ file->private_data = NULL;
+ return 0;
+}
+
+/**
+ * tomoyo_alloc_acl_element - Allocate permanent memory for ACL entry.
+ *
+ * @acl_type: Type of ACL entry.
+ *
+ * Returns pointer to the ACL entry on success, NULL otherwise.
+ */
+void *tomoyo_alloc_acl_element(const u8 acl_type)
+{
+ int len;
+ struct tomoyo_acl_info *ptr;
+
+ switch (acl_type) {
+ case TOMOYO_TYPE_SINGLE_PATH_ACL:
+ len = sizeof(struct tomoyo_single_path_acl_record);
+ break;
+ case TOMOYO_TYPE_DOUBLE_PATH_ACL:
+ len = sizeof(struct tomoyo_double_path_acl_record);
+ break;
+ default:
+ return NULL;
+ }
+ ptr = tomoyo_alloc_element(len);
+ if (!ptr)
+ return NULL;
+ ptr->type = acl_type;
+ return ptr;
+}
+
+/**
+ * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @inode: Pointer to "struct inode".
+ * @file: Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_open(struct inode *inode, struct file *file)
+{
+ const int key = ((u8 *) file->f_path.dentry->d_inode->i_private)
+ - ((u8 *) NULL);
+ return tomoyo_open_control(key, file);
+}
+
+/**
+ * tomoyo_release - close() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @inode: Pointer to "struct inode".
+ * @file: Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_release(struct inode *inode, struct file *file)
+{
+ return tomoyo_close_control(file);
+}
+
+/**
+ * tomoyo_read - read() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ * @buf: Pointer to buffer.
+ * @count: Size of @buf.
+ * @ppos: Unused.
+ *
+ * Returns bytes read on success, negative value otherwise.
+ */
+static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ return tomoyo_read_control(file, buf, count);
+}
+
+/**
+ * tomoyo_write - write() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ * @buf: Pointer to buffer.
+ * @count: Size of @buf.
+ * @ppos: Unused.
+ *
+ * Returns @count on success, negative value otherwise.
+ */
+static ssize_t tomoyo_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return tomoyo_write_control(file, buf, count);
+}
+
+/* Operations for /sys/kernel/security/tomoyo/ interface. */
+static const struct file_operations tomoyo_operations = {
+ .open = tomoyo_open,
+ .release = tomoyo_release,
+ .read = tomoyo_read,
+ .write = tomoyo_write,
+};
+
+/**
+ * tomoyo_create_entry - Create interface files under /sys/kernel/security/tomoyo/ directory.
+ *
+ * @name: The name of the interface file.
+ * @mode: The permission of the interface file.
+ * @parent: The parent directory.
+ * @key: Type of interface.
+ *
+ * Returns nothing.
+ */
+static void __init tomoyo_create_entry(const char *name, const mode_t mode,
+ struct dentry *parent, const u8 key)
+{
+ securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key,
+ &tomoyo_operations);
+}
+
+/**
+ * tomoyo_initerface_init - Initialize /sys/kernel/security/tomoyo/ interface.
+ *
+ * Returns 0.
+ */
+static int __init tomoyo_initerface_init(void)
+{
+ struct dentry *tomoyo_dir;
+
+ /* Don't create securityfs entries unless registered. */
+ if (current_cred()->security != &tomoyo_kernel_domain)
+ return 0;
+
+ tomoyo_dir = securityfs_create_dir("tomoyo", NULL);
+ tomoyo_create_entry("domain_policy", 0600, tomoyo_dir,
+ TOMOYO_DOMAINPOLICY);
+ tomoyo_create_entry("exception_policy", 0600, tomoyo_dir,
+ TOMOYO_EXCEPTIONPOLICY);
+ tomoyo_create_entry("self_domain", 0400, tomoyo_dir,
+ TOMOYO_SELFDOMAIN);
+ tomoyo_create_entry(".domain_status", 0600, tomoyo_dir,
+ TOMOYO_DOMAIN_STATUS);
+ tomoyo_create_entry(".process_status", 0600, tomoyo_dir,
+ TOMOYO_PROCESS_STATUS);
+ tomoyo_create_entry("meminfo", 0600, tomoyo_dir,
+ TOMOYO_MEMINFO);
+ tomoyo_create_entry("profile", 0600, tomoyo_dir,
+ TOMOYO_PROFILE);
+ tomoyo_create_entry("manager", 0600, tomoyo_dir,
+ TOMOYO_MANAGER);
+ tomoyo_create_entry("version", 0400, tomoyo_dir,
+ TOMOYO_VERSION);
+ return 0;
+}
+
+fs_initcall(tomoyo_initerface_init);
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
new file mode 100644
index 0000000..6dcb7cc
--- /dev/null
+++ b/security/tomoyo/common.h
@@ -0,0 +1,359 @@
+/*
+ * security/tomoyo/common.h
+ *
+ * Common functions for TOMOYO.
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre 2009/02/01
+ *
+ */
+
+#ifndef _SECURITY_TOMOYO_COMMON_H
+#define _SECURITY_TOMOYO_COMMON_H
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/file.h>
+#include <linux/kmod.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/list.h>
+
+struct dentry;
+struct vfsmount;
+
+/* Temporary buffer for holding pathnames. */
+struct tomoyo_page_buffer {
+ char buffer[4096];
+};
+
+/* Structure for holding a token. */
+struct tomoyo_path_info {
+ const char *name;
+ u32 hash; /* = full_name_hash(name, strlen(name)) */
+ u16 total_len; /* = strlen(name) */
+ u16 const_len; /* = tomoyo_const_part_length(name) */
+ bool is_dir; /* = tomoyo_strendswith(name, "/") */
+ bool is_patterned; /* = tomoyo_path_contains_pattern(name) */
+ u16 depth; /* = tomoyo_path_depth(name) */
+};
+
+/*
+ * This is the max length of a token.
+ *
+ * A token consists of only ASCII printable characters.
+ * Non printable characters in a token is represented in \ooo style
+ * octal string. Thus, \ itself is represented as \\.
+ */
+#define TOMOYO_MAX_PATHNAME_LEN 4000
+
+/* Structure for holding requested pathname. */
+struct tomoyo_path_info_with_data {
+ /* Keep "head" first, for this pointer is passed to tomoyo_free(). */
+ struct tomoyo_path_info head;
+ char bariier1[16]; /* Safeguard for overrun. */
+ char body[TOMOYO_MAX_PATHNAME_LEN];
+ char barrier2[16]; /* Safeguard for overrun. */
+};
+
+/*
+ * Common header for holding ACL entries.
+ *
+ * Packing "struct tomoyo_acl_info" allows
+ * "struct tomoyo_single_path_acl_record" to embed "u16" and
+ * "struct tomoyo_double_path_acl_record" to embed "u8"
+ * without enlarging their structure size.
+ */
+struct tomoyo_acl_info {
+ struct list_head list;
+ /*
+ * Type of this ACL entry.
+ *
+ * MSB is is_deleted flag.
+ */
+ u8 type;
+} __packed;
+
+/* This ACL entry is deleted. */
+#define TOMOYO_ACL_DELETED 0x80
+
+/* Structure for domain information. */
+struct tomoyo_domain_info {
+ struct list_head list;
+ struct list_head acl_info_list;
+ /* Name of this domain. Never NULL. */
+ const struct tomoyo_path_info *domainname;
+ u8 profile; /* Profile number to use. */
+ u8 is_deleted; /* Delete flag.
+ 0 = active.
+ 1 = deleted but undeletable.
+ 255 = deleted and no longer undeletable. */
+ bool quota_warned; /* Quota warnning flag. */
+ /* DOMAIN_FLAGS_*. Use tomoyo_set_domain_flag() to modify. */
+ u8 flags;
+};
+
+/* Profile number is an integer between 0 and 255. */
+#define TOMOYO_MAX_PROFILES 256
+
+/* Ignore "allow_read" directive in exception policy. */
+#define TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ 1
+/*
+ * This domain was unable to create a new domain at tomoyo_find_next_domain()
+ * because the name of the domain to be created was too long or
+ * it could not allocate memory.
+ * More than one process continued execve() without domain transition.
+ */
+#define TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED 2
+
+/*
+ * Structure for "allow_read/write", "allow_execute", "allow_read",
+ * "allow_write", "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir",
+ * "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar",
+ * "allow_truncate", "allow_symlink" and "allow_rewrite" directive.
+ */
+struct tomoyo_single_path_acl_record {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_SINGLE_PATH_ACL */
+ u16 perm;
+ /* Pointer to single pathname. */
+ const struct tomoyo_path_info *filename;
+};
+
+/* Structure for "allow_rename" and "allow_link" directive. */
+struct tomoyo_double_path_acl_record {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_DOUBLE_PATH_ACL */
+ u8 perm;
+ /* Pointer to single pathname. */
+ const struct tomoyo_path_info *filename1;
+ /* Pointer to single pathname. */
+ const struct tomoyo_path_info *filename2;
+};
+
+/* Keywords for ACLs. */
+#define TOMOYO_KEYWORD_ALIAS "alias "
+#define TOMOYO_KEYWORD_ALLOW_READ "allow_read "
+#define TOMOYO_KEYWORD_DELETE "delete "
+#define TOMOYO_KEYWORD_DENY_REWRITE "deny_rewrite "
+#define TOMOYO_KEYWORD_FILE_PATTERN "file_pattern "
+#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN "initialize_domain "
+#define TOMOYO_KEYWORD_KEEP_DOMAIN "keep_domain "
+#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN "no_initialize_domain "
+#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN "no_keep_domain "
+#define TOMOYO_KEYWORD_SELECT "select "
+#define TOMOYO_KEYWORD_UNDELETE "undelete "
+#define TOMOYO_KEYWORD_USE_PROFILE "use_profile "
+#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read"
+/* A domain definition starts with <kernel>. */
+#define TOMOYO_ROOT_NAME "<kernel>"
+#define TOMOYO_ROOT_NAME_LEN (sizeof(TOMOYO_ROOT_NAME) - 1)
+
+/* Index numbers for Access Controls. */
+#define TOMOYO_MAC_FOR_FILE 0 /* domain_policy.conf */
+#define TOMOYO_MAX_ACCEPT_ENTRY 1
+#define TOMOYO_VERBOSE 2
+#define TOMOYO_MAX_CONTROL_INDEX 3
+
+/* Structure for reading/writing policy via securityfs interfaces. */
+struct tomoyo_io_buffer {
+ int (*read) (struct tomoyo_io_buffer *);
+ int (*write) (struct tomoyo_io_buffer *);
+ /* Exclusive lock for this structure. */
+ struct mutex io_sem;
+ /* The position currently reading from. */
+ struct list_head *read_var1;
+ /* Extra variables for reading. */
+ struct list_head *read_var2;
+ /* The position currently writing to. */
+ struct tomoyo_domain_info *write_var1;
+ /* The step for reading. */
+ int read_step;
+ /* Buffer for reading. */
+ char *read_buf;
+ /* EOF flag for reading. */
+ bool read_eof;
+ /* Read domain ACL of specified PID? */
+ bool read_single_domain;
+ /* Extra variable for reading. */
+ u8 read_bit;
+ /* Bytes available for reading. */
+ int read_avail;
+ /* Size of read buffer. */
+ int readbuf_size;
+ /* Buffer for writing. */
+ char *write_buf;
+ /* Bytes available for writing. */
+ int write_avail;
+ /* Size of write buffer. */
+ int writebuf_size;
+};
+
+/* Check whether the domain has too many ACL entries to hold. */
+bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain);
+/* Transactional sprintf() for policy dump. */
+bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+/* Check whether the domainname is correct. */
+bool tomoyo_is_correct_domain(const unsigned char *domainname,
+ const char *function);
+/* Check whether the token is correct. */
+bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
+ const s8 pattern_type, const s8 end_type,
+ const char *function);
+/* Check whether the token can be a domainname. */
+bool tomoyo_is_domain_def(const unsigned char *buffer);
+/* Check whether the given filename matches the given pattern. */
+bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
+ const struct tomoyo_path_info *pattern);
+/* Read "alias" entry in exception policy. */
+bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head);
+/*
+ * Read "initialize_domain" and "no_initialize_domain" entry
+ * in exception policy.
+ */
+bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head);
+/* Read "keep_domain" and "no_keep_domain" entry in exception policy. */
+bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head);
+/* Read "file_pattern" entry in exception policy. */
+bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head);
+/* Read "allow_read" entry in exception policy. */
+bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head);
+/* Read "deny_rewrite" entry in exception policy. */
+bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head);
+/* Write domain policy violation warning message to console? */
+bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain);
+/* Convert double path operation to operation name. */
+const char *tomoyo_dp2keyword(const u8 operation);
+/* Get the last component of the given domainname. */
+const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain);
+/* Get warning message. */
+const char *tomoyo_get_msg(const bool is_enforce);
+/* Convert single path operation to operation name. */
+const char *tomoyo_sp2keyword(const u8 operation);
+/* Delete a domain. */
+int tomoyo_delete_domain(char *data);
+/* Create "alias" entry in exception policy. */
+int tomoyo_write_alias_policy(char *data, const bool is_delete);
+/*
+ * Create "initialize_domain" and "no_initialize_domain" entry
+ * in exception policy.
+ */
+int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
+ const bool is_delete);
+/* Create "keep_domain" and "no_keep_domain" entry in exception policy. */
+int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
+ const bool is_delete);
+/*
+ * Create "allow_read/write", "allow_execute", "allow_read", "allow_write",
+ * "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir",
+ * "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar",
+ * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_rename" and
+ * "allow_link" entry in domain policy.
+ */
+int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
+ const bool is_delete);
+/* Create "allow_read" entry in exception policy. */
+int tomoyo_write_globally_readable_policy(char *data, const bool is_delete);
+/* Create "deny_rewrite" entry in exception policy. */
+int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete);
+/* Create "file_pattern" entry in exception policy. */
+int tomoyo_write_pattern_policy(char *data, const bool is_delete);
+/* Find a domain by the given name. */
+struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
+/* Find or create a domain by the given name. */
+struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
+ domainname,
+ const u8 profile);
+/* Undelete a domain. */
+struct tomoyo_domain_info *tomoyo_undelete_domain(const char *domainname);
+/* Check mode for specified functionality. */
+unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
+ const u8 index);
+/* Allocate memory for structures. */
+void *tomoyo_alloc_acl_element(const u8 acl_type);
+/* Fill in "struct tomoyo_path_info" members. */
+void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
+/* Run policy loader when /sbin/init starts. */
+void tomoyo_load_policy(const char *filename);
+/* Change "struct tomoyo_domain_info"->flags. */
+void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain,
+ const bool is_delete, const u8 flags);
+
+/* strcmp() for "struct tomoyo_path_info" structure. */
+static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
+ const struct tomoyo_path_info *b)
+{
+ return a->hash != b->hash || strcmp(a->name, b->name);
+}
+
+/* Get type of an ACL entry. */
+static inline u8 tomoyo_acl_type1(struct tomoyo_acl_info *ptr)
+{
+ return ptr->type & ~TOMOYO_ACL_DELETED;
+}
+
+/* Get type of an ACL entry. */
+static inline u8 tomoyo_acl_type2(struct tomoyo_acl_info *ptr)
+{
+ return ptr->type;
+}
+
+/**
+ * tomoyo_is_valid - Check whether the character is a valid char.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is a valid character, false otherwise.
+ */
+static inline bool tomoyo_is_valid(const unsigned char c)
+{
+ return c > ' ' && c < 127;
+}
+
+/**
+ * tomoyo_is_invalid - Check whether the character is an invalid char.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is an invalid character, false otherwise.
+ */
+static inline bool tomoyo_is_invalid(const unsigned char c)
+{
+ return c && (c <= ' ' || c >= 127);
+}
+
+/* The list for "struct tomoyo_domain_info". */
+extern struct list_head tomoyo_domain_list;
+extern struct rw_semaphore tomoyo_domain_list_lock;
+
+/* Lock for domain->acl_info_list. */
+extern struct rw_semaphore tomoyo_domain_acl_info_list_lock;
+
+/* Has /sbin/init started? */
+extern bool tomoyo_policy_loaded;
+
+/* The kernel's domain. */
+extern struct tomoyo_domain_info tomoyo_kernel_domain;
+
+/**
+ * list_for_each_cookie - iterate over a list with cookie.
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @cookie: the &struct list_head to use as a cookie.
+ * @head: the head for your list.
+ *
+ * Same with list_for_each() except that this primitive uses @cookie
+ * so that we can continue iteration.
+ * @cookie must be NULL when iteration starts, and @cookie will become
+ * NULL when iteration finishes.
+ */
+#define list_for_each_cookie(pos, cookie, head) \
+ for (({ if (!cookie) \
+ cookie = head; }), \
+ pos = (cookie)->next; \
+ prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
+ (cookie) = pos, pos = pos->next)
+
+#endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
new file mode 100644
index 0000000..093a756
--- /dev/null
+++ b/security/tomoyo/domain.c
@@ -0,0 +1,878 @@
+/*
+ * security/tomoyo/domain.c
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre 2009/02/01
+ *
+ */
+
+#include "common.h"
+#include "tomoyo.h"
+#include "realpath.h"
+#include <linux/binfmts.h>
+
+/* Variables definitions.*/
+
+/* The initial domain. */
+struct tomoyo_domain_info tomoyo_kernel_domain;
+
+/* The list for "struct tomoyo_domain_info". */
+LIST_HEAD(tomoyo_domain_list);
+DECLARE_RWSEM(tomoyo_domain_list_lock);
+
+/* Structure for "initialize_domain" and "no_initialize_domain" keyword. */
+struct tomoyo_domain_initializer_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *domainname; /* This may be NULL */
+ const struct tomoyo_path_info *program;
+ bool is_deleted;
+ bool is_not; /* True if this entry is "no_initialize_domain". */
+ /* True if the domainname is tomoyo_get_last_name(). */
+ bool is_last_name;
+};
+
+/* Structure for "keep_domain" and "no_keep_domain" keyword. */
+struct tomoyo_domain_keeper_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *domainname;
+ const struct tomoyo_path_info *program; /* This may be NULL */
+ bool is_deleted;
+ bool is_not; /* True if this entry is "no_keep_domain". */
+ /* True if the domainname is tomoyo_get_last_name(). */
+ bool is_last_name;
+};
+
+/* Structure for "alias" keyword. */
+struct tomoyo_alias_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *original_name;
+ const struct tomoyo_path_info *aliased_name;
+ bool is_deleted;
+};
+
+/**
+ * tomoyo_set_domain_flag - Set or clear domain's attribute flags.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ * @flags: Flags to set or clear.
+ *
+ * Returns nothing.
+ */
+void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain,
+ const bool is_delete, const u8 flags)
+{
+ /* We need to serialize because this is bitfield operation. */
+ static DEFINE_SPINLOCK(lock);
+ /***** CRITICAL SECTION START *****/
+ spin_lock(&lock);
+ if (!is_delete)
+ domain->flags |= flags;
+ else
+ domain->flags &= ~flags;
+ spin_unlock(&lock);
+ /***** CRITICAL SECTION END *****/
+}
+
+/**
+ * tomoyo_get_last_name - Get last component of a domainname.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * Returns the last component of the domainname.
+ */
+const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain)
+{
+ const char *cp0 = domain->domainname->name;
+ const char *cp1 = strrchr(cp0, ' ');
+
+ if (cp1)
+ return cp1 + 1;
+ return cp0;
+}
+
+/* The list for "struct tomoyo_domain_initializer_entry". */
+static LIST_HEAD(tomoyo_domain_initializer_list);
+static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);
+
+/**
+ * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list.
+ *
+ * @domainname: The name of domain. May be NULL.
+ * @program: The name of program.
+ * @is_not: True if it is "no_initialize_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_domain_initializer_entry(const char *domainname,
+ const char *program,
+ const bool is_not,
+ const bool is_delete)
+{
+ struct tomoyo_domain_initializer_entry *new_entry;
+ struct tomoyo_domain_initializer_entry *ptr;
+ const struct tomoyo_path_info *saved_program;
+ const struct tomoyo_path_info *saved_domainname = NULL;
+ int error = -ENOMEM;
+ bool is_last_name = false;
+
+ if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__))
+ return -EINVAL; /* No patterns allowed. */
+ if (domainname) {
+ if (!tomoyo_is_domain_def(domainname) &&
+ tomoyo_is_correct_path(domainname, 1, -1, -1, __func__))
+ is_last_name = true;
+ else if (!tomoyo_is_correct_domain(domainname, __func__))
+ return -EINVAL;
+ saved_domainname = tomoyo_save_name(domainname);
+ if (!saved_domainname)
+ return -ENOMEM;
+ }
+ saved_program = tomoyo_save_name(program);
+ if (!saved_program)
+ return -ENOMEM;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_domain_initializer_list_lock);
+ list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+ if (ptr->is_not != is_not ||
+ ptr->domainname != saved_domainname ||
+ ptr->program != saved_program)
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ goto out;
+ }
+ if (is_delete) {
+ error = -ENOENT;
+ goto out;
+ }
+ new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+ if (!new_entry)
+ goto out;
+ new_entry->domainname = saved_domainname;
+ new_entry->program = saved_program;
+ new_entry->is_not = is_not;
+ new_entry->is_last_name = is_last_name;
+ list_add_tail(&new_entry->list, &tomoyo_domain_initializer_list);
+ error = 0;
+ out:
+ up_write(&tomoyo_domain_initializer_list_lock);
+ /***** EXCLUSIVE SECTION END *****/
+ return error;
+}
+
+/**
+ * tomoyo_read_domain_initializer_policy - Read "struct tomoyo_domain_initializer_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ bool done = true;
+
+ down_read(&tomoyo_domain_initializer_list_lock);
+ list_for_each_cookie(pos, head->read_var2,
+ &tomoyo_domain_initializer_list) {
+ const char *no;
+ const char *from = "";
+ const char *domain = "";
+ struct tomoyo_domain_initializer_entry *ptr;
+ ptr = list_entry(pos, struct tomoyo_domain_initializer_entry,
+ list);
+ if (ptr->is_deleted)
+ continue;
+ no = ptr->is_not ? "no_" : "";
+ if (ptr->domainname) {
+ from = " from ";
+ domain = ptr->domainname->name;
+ }
+ if (!tomoyo_io_printf(head,
+ "%s" TOMOYO_KEYWORD_INITIALIZE_DOMAIN
+ "%s%s%s\n", no, ptr->program->name, from,
+ domain)) {
+ done = false;
+ break;
+ }
+ }
+ up_read(&tomoyo_domain_initializer_list_lock);
+ return done;
+}
+
+/**
+ * tomoyo_write_domain_initializer_policy - Write "struct tomoyo_domain_initializer_entry" list.
+ *
+ * @data: String to parse.
+ * @is_not: True if it is "no_initialize_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
+ const bool is_delete)
+{
+ char *cp = strstr(data, " from ");
+
+ if (cp) {
+ *cp = '\0';
+ return tomoyo_update_domain_initializer_entry(cp + 6, data,
+ is_not,
+ is_delete);
+ }
+ return tomoyo_update_domain_initializer_entry(NULL, data, is_not,
+ is_delete);
+}
+
+/**
+ * tomoyo_is_domain_initializer - Check whether the given program causes domainname reinitialization.
+ *
+ * @domainname: The name of domain.
+ * @program: The name of program.
+ * @last_name: The last component of @domainname.
+ *
+ * Returns true if executing @program reinitializes domain transition,
+ * false otherwise.
+ */
+static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
+ domainname,
+ const struct tomoyo_path_info *program,
+ const struct tomoyo_path_info *
+ last_name)
+{
+ struct tomoyo_domain_initializer_entry *ptr;
+ bool flag = false;
+
+ down_read(&tomoyo_domain_initializer_list_lock);
+ list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+ if (ptr->is_deleted)
+ continue;
+ if (ptr->domainname) {
+ if (!ptr->is_last_name) {
+ if (ptr->domainname != domainname)
+ continue;
+ } else {
+ if (tomoyo_pathcmp(ptr->domainname, last_name))
+ continue;
+ }
+ }
+ if (tomoyo_pathcmp(ptr->program, program))
+ continue;
+ if (ptr->is_not) {
+ flag = false;
+ break;
+ }
+ flag = true;
+ }
+ up_read(&tomoyo_domain_initializer_list_lock);
+ return flag;
+}
+
+/* The list for "struct tomoyo_domain_keeper_entry". */
+static LIST_HEAD(tomoyo_domain_keeper_list);
+static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
+
+/**
+ * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list.
+ *
+ * @domainname: The name of domain.
+ * @program: The name of program. May be NULL.
+ * @is_not: True if it is "no_keep_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_domain_keeper_entry(const char *domainname,
+ const char *program,
+ const bool is_not,
+ const bool is_delete)
+{
+ struct tomoyo_domain_keeper_entry *new_entry;
+ struct tomoyo_domain_keeper_entry *ptr;
+ const struct tomoyo_path_info *saved_domainname;
+ const struct tomoyo_path_info *saved_program = NULL;
+ static DEFINE_MUTEX(lock);
+ int error = -ENOMEM;
+ bool is_last_name = false;
+
+ if (!tomoyo_is_domain_def(domainname) &&
+ tomoyo_is_correct_path(domainname, 1, -1, -1, __func__))
+ is_last_name = true;
+ else if (!tomoyo_is_correct_domain(domainname, __func__))
+ return -EINVAL;
+ if (program) {
+ if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__))
+ return -EINVAL;
+ saved_program = tomoyo_save_name(program);
+ if (!saved_program)
+ return -ENOMEM;
+ }
+ saved_domainname = tomoyo_save_name(domainname);
+ if (!saved_domainname)
+ return -ENOMEM;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_domain_keeper_list_lock);
+ list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+ if (ptr->is_not != is_not ||
+ ptr->domainname != saved_domainname ||
+ ptr->program != saved_program)
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ goto out;
+ }
+ if (is_delete) {
+ error = -ENOENT;
+ goto out;
+ }
+ new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+ if (!new_entry)
+ goto out;
+ new_entry->domainname = saved_domainname;
+ new_entry->program = saved_program;
+ new_entry->is_not = is_not;
+ new_entry->is_last_name = is_last_name;
+ list_add_tail(&new_entry->list, &tomoyo_domain_keeper_list);
+ error = 0;
+ out:
+ up_write(&tomoyo_domain_keeper_list_lock);
+ /***** EXCLUSIVE SECTION END *****/
+ return error;
+}
+
+/**
+ * tomoyo_write_domain_keeper_policy - Write "struct tomoyo_domain_keeper_entry" list.
+ *
+ * @data: String to parse.
+ * @is_not: True if it is "no_keep_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ */
+int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
+ const bool is_delete)
+{
+ char *cp = strstr(data, " from ");
+
+ if (cp) {
+ *cp = '\0';
+ return tomoyo_update_domain_keeper_entry(cp + 6, data, is_not,
+ is_delete);
+ }
+ return tomoyo_update_domain_keeper_entry(data, NULL, is_not, is_delete);
+}
+
+/**
+ * tomoyo_read_domain_keeper_policy - Read "struct tomoyo_domain_keeper_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ bool done = true;
+
+ down_read(&tomoyo_domain_keeper_list_lock);
+ list_for_each_cookie(pos, head->read_var2,
+ &tomoyo_domain_keeper_list) {
+ struct tomoyo_domain_keeper_entry *ptr;
+ const char *no;
+ const char *from = "";
+ const char *program = "";
+
+ ptr = list_entry(pos, struct tomoyo_domain_keeper_entry, list);
+ if (ptr->is_deleted)
+ continue;
+ no = ptr->is_not ? "no_" : "";
+ if (ptr->program) {
+ from = " from ";
+ program = ptr->program->name;
+ }
+ if (!tomoyo_io_printf(head,
+ "%s" TOMOYO_KEYWORD_KEEP_DOMAIN
+ "%s%s%s\n", no, program, from,
+ ptr->domainname->name)) {
+ done = false;
+ break;
+ }
+ }
+ up_read(&tomoyo_domain_keeper_list_lock);
+ return done;
+}
+
+/**
+ * tomoyo_is_domain_keeper - Check whether the given program causes domain transition suppression.
+ *
+ * @domainname: The name of domain.
+ * @program: The name of program.
+ * @last_name: The last component of @domainname.
+ *
+ * Returns true if executing @program supresses domain transition,
+ * false otherwise.
+ */
+static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
+ const struct tomoyo_path_info *program,
+ const struct tomoyo_path_info *last_name)
+{
+ struct tomoyo_domain_keeper_entry *ptr;
+ bool flag = false;
+
+ down_read(&tomoyo_domain_keeper_list_lock);
+ list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+ if (ptr->is_deleted)
+ continue;
+ if (!ptr->is_last_name) {
+ if (ptr->domainname != domainname)
+ continue;
+ } else {
+ if (tomoyo_pathcmp(ptr->domainname, last_name))
+ continue;
+ }
+ if (ptr->program && tomoyo_pathcmp(ptr->program, program))
+ continue;
+ if (ptr->is_not) {
+ flag = false;
+ break;
+ }
+ flag = true;
+ }
+ up_read(&tomoyo_domain_keeper_list_lock);
+ return flag;
+}
+
+/* The list for "struct tomoyo_alias_entry". */
+static LIST_HEAD(tomoyo_alias_list);
+static DECLARE_RWSEM(tomoyo_alias_list_lock);
+
+/**
+ * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list.
+ *
+ * @original_name: The original program's real name.
+ * @aliased_name: The symbolic program's symbolic link's name.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_alias_entry(const char *original_name,
+ const char *aliased_name,
+ const bool is_delete)
+{
+ struct tomoyo_alias_entry *new_entry;
+ struct tomoyo_alias_entry *ptr;
+ const struct tomoyo_path_info *saved_original_name;
+ const struct tomoyo_path_info *saved_aliased_name;
+ int error = -ENOMEM;
+
+ if (!tomoyo_is_correct_path(original_name, 1, -1, -1, __func__) ||
+ !tomoyo_is_correct_path(aliased_name, 1, -1, -1, __func__))
+ return -EINVAL; /* No patterns allowed. */
+ saved_original_name = tomoyo_save_name(original_name);
+ saved_aliased_name = tomoyo_save_name(aliased_name);
+ if (!saved_original_name || !saved_aliased_name)
+ return -ENOMEM;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_alias_list_lock);
+ list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+ if (ptr->original_name != saved_original_name ||
+ ptr->aliased_name != saved_aliased_name)
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ goto out;
+ }
+ if (is_delete) {
+ error = -ENOENT;
+ goto out;
+ }
+ new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+ if (!new_entry)
+ goto out;
+ new_entry->original_name = saved_original_name;
+ new_entry->aliased_name = saved_aliased_name;
+ list_add_tail(&new_entry->list, &tomoyo_alias_list);
+ error = 0;
+ out:
+ up_write(&tomoyo_alias_list_lock);
+ /***** EXCLUSIVE SECTION END *****/
+ return error;
+}
+
+/**
+ * tomoyo_read_alias_policy - Read "struct tomoyo_alias_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ bool done = true;
+
+ down_read(&tomoyo_alias_list_lock);
+ list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
+ struct tomoyo_alias_entry *ptr;
+
+ ptr = list_entry(pos, struct tomoyo_alias_entry, list);
+ if (ptr->is_deleted)
+ continue;
+ if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALIAS "%s %s\n",
+ ptr->original_name->name,
+ ptr->aliased_name->name)) {
+ done = false;
+ break;
+ }
+ }
+ up_read(&tomoyo_alias_list_lock);
+ return done;
+}
+
+/**
+ * tomoyo_write_alias_policy - Write "struct tomoyo_alias_entry" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_alias_policy(char *data, const bool is_delete)
+{
+ char *cp = strchr(data, ' ');
+
+ if (!cp)
+ return -EINVAL;
+ *cp++ = '\0';
+ return tomoyo_update_alias_entry(data, cp, is_delete);
+}
+
+/* Domain create/delete/undelete handler. */
+
+/* #define TOMOYO_DEBUG_DOMAIN_UNDELETE */
+
+/**
+ * tomoyo_delete_domain - Delete a domain.
+ *
+ * @domainname: The name of domain.
+ *
+ * Returns 0.
+ */
+int tomoyo_delete_domain(char *domainname)
+{
+ struct tomoyo_domain_info *domain;
+ struct tomoyo_path_info name;
+
+ name.name = domainname;
+ tomoyo_fill_path_info(&name);
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_domain_list_lock);
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+ printk(KERN_DEBUG "tomoyo_delete_domain %s\n", domainname);
+ list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ if (tomoyo_pathcmp(domain->domainname, &name))
+ continue;
+ printk(KERN_DEBUG "List: %p %u\n", domain, domain->is_deleted);
+ }
+#endif
+ /* Is there an active domain? */
+ list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ struct tomoyo_domain_info *domain2;
+ /* Never delete tomoyo_kernel_domain */
+ if (domain == &tomoyo_kernel_domain)
+ continue;
+ if (domain->is_deleted ||
+ tomoyo_pathcmp(domain->domainname, &name))
+ continue;
+ /* Mark already deleted domains as non undeletable. */
+ list_for_each_entry(domain2, &tomoyo_domain_list, list) {
+ if (!domain2->is_deleted ||
+ tomoyo_pathcmp(domain2->domainname, &name))
+ continue;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+ if (domain2->is_deleted != 255)
+ printk(KERN_DEBUG
+ "Marked %p as non undeletable\n",
+ domain2);
+#endif
+ domain2->is_deleted = 255;
+ }
+ /* Delete and mark active domain as undeletable. */
+ domain->is_deleted = 1;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+ printk(KERN_DEBUG "Marked %p as undeletable\n", domain);
+#endif
+ break;
+ }
+ up_write(&tomoyo_domain_list_lock);
+ /***** EXCLUSIVE SECTION END *****/
+ return 0;
+}
+
+/**
+ * tomoyo_undelete_domain - Undelete a domain.
+ *
+ * @domainname: The name of domain.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ */
+struct tomoyo_domain_info *tomoyo_undelete_domain(const char *domainname)
+{
+ struct tomoyo_domain_info *domain;
+ struct tomoyo_domain_info *candidate_domain = NULL;
+ struct tomoyo_path_info name;
+
+ name.name = domainname;
+ tomoyo_fill_path_info(&name);
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_domain_list_lock);
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+ printk(KERN_DEBUG "tomoyo_undelete_domain %s\n", domainname);
+ list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ if (tomoyo_pathcmp(domain->domainname, &name))
+ continue;
+ printk(KERN_DEBUG "List: %p %u\n", domain, domain->is_deleted);
+ }
+#endif
+ list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ if (tomoyo_pathcmp(&name, domain->domainname))
+ continue;
+ if (!domain->is_deleted) {
+ /* This domain is active. I can't undelete. */
+ candidate_domain = NULL;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+ printk(KERN_DEBUG "%p is active. I can't undelete.\n",
+ domain);
+#endif
+ break;
+ }
+ /* Is this domain undeletable? */
+ if (domain->is_deleted == 1)
+ candidate_domain = domain;
+ }
+ if (candidate_domain) {
+ candidate_domain->is_deleted = 0;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+ printk(KERN_DEBUG "%p was undeleted.\n", candidate_domain);
+#endif
+ }
+ up_write(&tomoyo_domain_list_lock);
+ /***** EXCLUSIVE SECTION END *****/
+ return candidate_domain;
+}
+
+/**
+ * tomoyo_find_or_assign_new_domain - Create a domain.
+ *
+ * @domainname: The name of domain.
+ * @profile: Profile number to assign if the domain was newly created.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ */
+struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
+ domainname,
+ const u8 profile)
+{
+ struct tomoyo_domain_info *domain = NULL;
+ const struct tomoyo_path_info *saved_domainname;
+
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_domain_list_lock);
+ domain = tomoyo_find_domain(domainname);
+ if (domain)
+ goto out;
+ if (!tomoyo_is_correct_domain(domainname, __func__))
+ goto out;
+ saved_domainname = tomoyo_save_name(domainname);
+ if (!saved_domainname)
+ goto out;
+ /* Can I reuse memory of deleted domain? */
+ list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ struct task_struct *p;
+ struct tomoyo_acl_info *ptr;
+ bool flag;
+ if (!domain->is_deleted ||
+ domain->domainname != saved_domainname)
+ continue;
+ flag = false;
+ /***** CRITICAL SECTION START *****/
+ read_lock(&tasklist_lock);
+ for_each_process(p) {
+ if (tomoyo_real_domain(p) != domain)
+ continue;
+ flag = true;
+ break;
+ }
+ read_unlock(&tasklist_lock);
+ /***** CRITICAL SECTION END *****/
+ if (flag)
+ continue;
+#ifdef TOMOYO_DEBUG_DOMAIN_UNDELETE
+ printk(KERN_DEBUG "Reusing %p %s\n", domain,
+ domain->domainname->name);
+#endif
+ list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ ptr->type |= TOMOYO_ACL_DELETED;
+ }
+ tomoyo_set_domain_flag(domain, true, domain->flags);
+ domain->profile = profile;
+ domain->quota_warned = false;
+ mb(); /* Avoid out-of-order execution. */
+ domain->is_deleted = 0;
+ goto out;
+ }
+ /* No memory reusable. Create using new memory. */
+ domain = tomoyo_alloc_element(sizeof(*domain));
+ if (domain) {
+ INIT_LIST_HEAD(&domain->acl_info_list);
+ domain->domainname = saved_domainname;
+ domain->profile = profile;
+ list_add_tail(&domain->list, &tomoyo_domain_list);
+ }
+ out:
+ up_write(&tomoyo_domain_list_lock);
+ /***** EXCLUSIVE SECTION END *****/
+ return domain;
+}
+
+/**
+ * tomoyo_find_next_domain - Find a domain.
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ * @next_domain: Pointer to pointer to "struct tomoyo_domain_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_find_next_domain(struct linux_binprm *bprm,
+ struct tomoyo_domain_info **next_domain)
+{
+ /*
+ * This function assumes that the size of buffer returned by
+ * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN.
+ */
+ struct tomoyo_page_buffer *tmp = tomoyo_alloc(sizeof(*tmp));
+ struct tomoyo_domain_info *old_domain = tomoyo_domain();
+ struct tomoyo_domain_info *domain = NULL;
+ const char *old_domain_name = old_domain->domainname->name;
+ const char *original_name = bprm->filename;
+ char *new_domain_name = NULL;
+ char *real_program_name = NULL;
+ char *symlink_program_name = NULL;
+ const u8 mode = tomoyo_check_flags(old_domain, TOMOYO_MAC_FOR_FILE);
+ const bool is_enforce = (mode == 3);
+ int retval = -ENOMEM;
+ struct tomoyo_path_info r; /* real name */
+ struct tomoyo_path_info s; /* symlink name */
+ struct tomoyo_path_info l; /* last name */
+ static bool initialized;
+
+ if (!tmp)
+ goto out;
+
+ if (!initialized) {
+ /*
+ * Built-in initializers. This is needed because policies are
+ * not loaded until starting /sbin/init.
+ */
+ tomoyo_update_domain_initializer_entry(NULL, "/sbin/hotplug",
+ false, false);
+ tomoyo_update_domain_initializer_entry(NULL, "/sbin/modprobe",
+ false, false);
+ initialized = true;
+ }
+
+ /* Get tomoyo_realpath of program. */
+ retval = -ENOENT;
+ /* I hope tomoyo_realpath() won't fail with -ENOMEM. */
+ real_program_name = tomoyo_realpath(original_name);
+ if (!real_program_name)
+ goto out;
+ /* Get tomoyo_realpath of symbolic link. */
+ symlink_program_name = tomoyo_realpath_nofollow(original_name);
+ if (!symlink_program_name)
+ goto out;
+
+ r.name = real_program_name;
+ tomoyo_fill_path_info(&r);
+ s.name = symlink_program_name;
+ tomoyo_fill_path_info(&s);
+ l.name = tomoyo_get_last_name(old_domain);
+ tomoyo_fill_path_info(&l);
+
+ /* Check 'alias' directive. */
+ if (tomoyo_pathcmp(&r, &s)) {
+ struct tomoyo_alias_entry *ptr;
+ /* Is this program allowed to be called via symbolic links? */
+ down_read(&tomoyo_alias_list_lock);
+ list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+ if (ptr->is_deleted ||
+ tomoyo_pathcmp(&r, ptr->original_name) ||
+ tomoyo_pathcmp(&s, ptr->aliased_name))
+ continue;
+ memset(real_program_name, 0, TOMOYO_MAX_PATHNAME_LEN);
+ strncpy(real_program_name, ptr->aliased_name->name,
+ TOMOYO_MAX_PATHNAME_LEN - 1);
+ tomoyo_fill_path_info(&r);
+ break;
+ }
+ up_read(&tomoyo_alias_list_lock);
+ }
+
+ /* Check execute permission. */
+ retval = tomoyo_check_exec_perm(old_domain, &r, tmp);
+ if (retval < 0)
+ goto out;
+
+ new_domain_name = tmp->buffer;
+ if (tomoyo_is_domain_initializer(old_domain->domainname, &r, &l)) {
+ /* Transit to the child of tomoyo_kernel_domain domain. */
+ snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1,
+ TOMOYO_ROOT_NAME " " "%s", real_program_name);
+ } else if (old_domain == &tomoyo_kernel_domain &&
+ !tomoyo_policy_loaded) {
+ /*
+ * Needn't to transit from kernel domain before starting
+ * /sbin/init. But transit from kernel domain if executing
+ * initializers because they might start before /sbin/init.
+ */
+ domain = old_domain;
+ } else if (tomoyo_is_domain_keeper(old_domain->domainname, &r, &l)) {
+ /* Keep current domain. */
+ domain = old_domain;
+ } else {
+ /* Normal domain transition. */
+ snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1,
+ "%s %s", old_domain_name, real_program_name);
+ }
+ if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN)
+ goto done;
+ down_read(&tomoyo_domain_list_lock);
+ domain = tomoyo_find_domain(new_domain_name);
+ up_read(&tomoyo_domain_list_lock);
+ if (domain)
+ goto done;
+ if (is_enforce)
+ goto done;
+ domain = tomoyo_find_or_assign_new_domain(new_domain_name,
+ old_domain->profile);
+ done:
+ if (domain)
+ goto out;
+ printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n",
+ new_domain_name);
+ if (is_enforce)
+ retval = -EPERM;
+ else
+ tomoyo_set_domain_flag(old_domain, false,
+ TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED);
+ out:
+ tomoyo_free(real_program_name);
+ tomoyo_free(symlink_program_name);
+ *next_domain = domain ? domain : old_domain;
+ tomoyo_free(tmp);
+ return retval;
+}
diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c
new file mode 100644
index 0000000..65f50c1
--- /dev/null
+++ b/security/tomoyo/file.c
@@ -0,0 +1,1241 @@
+/*
+ * security/tomoyo/file.c
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre 2009/02/01
+ *
+ */
+
+#include "common.h"
+#include "tomoyo.h"
+#include "realpath.h"
+#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
+
+/* Structure for "allow_read" keyword. */
+struct tomoyo_globally_readable_file_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *filename;
+ bool is_deleted;
+};
+
+/* Structure for "file_pattern" keyword. */
+struct tomoyo_pattern_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *pattern;
+ bool is_deleted;
+};
+
+/* Structure for "deny_rewrite" keyword. */
+struct tomoyo_no_rewrite_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *pattern;
+ bool is_deleted;
+};
+
+/* Keyword array for single path operations. */
+static const char *tomoyo_sp_keyword[TOMOYO_MAX_SINGLE_PATH_OPERATION] = {
+ [TOMOYO_TYPE_READ_WRITE_ACL] = "read/write",
+ [TOMOYO_TYPE_EXECUTE_ACL] = "execute",
+ [TOMOYO_TYPE_READ_ACL] = "read",
+ [TOMOYO_TYPE_WRITE_ACL] = "write",
+ [TOMOYO_TYPE_CREATE_ACL] = "create",
+ [TOMOYO_TYPE_UNLINK_ACL] = "unlink",
+ [TOMOYO_TYPE_MKDIR_ACL] = "mkdir",
+ [TOMOYO_TYPE_RMDIR_ACL] = "rmdir",
+ [TOMOYO_TYPE_MKFIFO_ACL] = "mkfifo",
+ [TOMOYO_TYPE_MKSOCK_ACL] = "mksock",
+ [TOMOYO_TYPE_MKBLOCK_ACL] = "mkblock",
+ [TOMOYO_TYPE_MKCHAR_ACL] = "mkchar",
+ [TOMOYO_TYPE_TRUNCATE_ACL] = "truncate",
+ [TOMOYO_TYPE_SYMLINK_ACL] = "symlink",
+ [TOMOYO_TYPE_REWRITE_ACL] = "rewrite",
+};
+
+/* Keyword array for double path operations. */
+static const char *tomoyo_dp_keyword[TOMOYO_MAX_DOUBLE_PATH_OPERATION] = {
+ [TOMOYO_TYPE_LINK_ACL] = "link",
+ [TOMOYO_TYPE_RENAME_ACL] = "rename",
+};
+
+/**
+ * tomoyo_sp2keyword - Get the name of single path operation.
+ *
+ * @operation: Type of operation.
+ *
+ * Returns the name of single path operation.
+ */
+const char *tomoyo_sp2keyword(const u8 operation)
+{
+ return (operation < TOMOYO_MAX_SINGLE_PATH_OPERATION)
+ ? tomoyo_sp_keyword[operation] : NULL;
+}
+
+/**
+ * tomoyo_dp2keyword - Get the name of double path operation.
+ *
+ * @operation: Type of operation.
+ *
+ * Returns the name of double path operation.
+ */
+const char *tomoyo_dp2keyword(const u8 operation)
+{
+ return (operation < TOMOYO_MAX_DOUBLE_PATH_OPERATION)
+ ? tomoyo_dp_keyword[operation] : NULL;
+}
+
+/**
+ * tomoyo_strendswith - Check whether the token ends with the given token.
+ *
+ * @name: The token to check.
+ * @tail: The token to find.
+ *
+ * Returns true if @name ends with @tail, false otherwise.
+ */
+static bool tomoyo_strendswith(const char *name, const char *tail)
+{
+ int len;
+
+ if (!name || !tail)
+ return false;
+ len = strlen(name) - strlen(tail);
+ return len >= 0 && !strcmp(name + len, tail);
+}
+
+/**
+ * tomoyo_get_path - Get realpath.
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
+ */
+static struct tomoyo_path_info *tomoyo_get_path(struct path *path)
+{
+ int error;
+ struct tomoyo_path_info_with_data *buf = tomoyo_alloc(sizeof(*buf));
+
+ if (!buf)
+ return NULL;
+ /* Reserve one byte for appending "/". */
+ error = tomoyo_realpath_from_path2(path, buf->body,
+ sizeof(buf->body) - 2);
+ if (!error) {
+ buf->head.name = buf->body;
+ tomoyo_fill_path_info(&buf->head);
+ return &buf->head;
+ }
+ tomoyo_free(buf);
+ return NULL;
+}
+
+/* Lock for domain->acl_info_list. */
+DECLARE_RWSEM(tomoyo_domain_acl_info_list_lock);
+
+static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
+ const char *filename2,
+ struct tomoyo_domain_info *
+ const domain, const bool is_delete);
+static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
+ struct tomoyo_domain_info *
+ const domain, const bool is_delete);
+
+/* The list for "struct tomoyo_globally_readable_file_entry". */
+static LIST_HEAD(tomoyo_globally_readable_list);
+static DECLARE_RWSEM(tomoyo_globally_readable_list_lock);
+
+/**
+ * tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list.
+ *
+ * @filename: Filename unconditionally permitted to open() for reading.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_globally_readable_entry(const char *filename,
+ const bool is_delete)
+{
+ struct tomoyo_globally_readable_file_entry *new_entry;
+ struct tomoyo_globally_readable_file_entry *ptr;
+ const struct tomoyo_path_info *saved_filename;
+ int error = -ENOMEM;
+
+ if (!tomoyo_is_correct_path(filename, 1, 0, -1, __func__))
+ return -EINVAL;
+ saved_filename = tomoyo_save_name(filename);
+ if (!saved_filename)
+ return -ENOMEM;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_globally_readable_list_lock);
+ list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+ if (ptr->filename != saved_filename)
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ goto out;
+ }
+ if (is_delete) {
+ error = -ENOENT;
+ goto out;
+ }
+ new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+ if (!new_entry)
+ goto out;
+ new_entry->filename = saved_filename;
+ list_add_tail(&new_entry->list, &tomoyo_globally_readable_list);
+ error = 0;
+ out:
+ up_write(&tomoyo_globally_readable_list_lock);
+ /***** EXCLUSIVE SECTION END *****/
+ return error;
+}
+
+/**
+ * tomoyo_is_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading.
+ *
+ * @filename: The filename to check.
+ *
+ * Returns true if any domain can open @filename for reading, false otherwise.
+ */
+static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
+ filename)
+{
+ struct tomoyo_globally_readable_file_entry *ptr;
+ bool found = false;
+ down_read(&tomoyo_globally_readable_list_lock);
+ list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+ if (!ptr->is_deleted &&
+ tomoyo_path_matches_pattern(filename, ptr->filename)) {
+ found = true;
+ break;
+ }
+ }
+ up_read(&tomoyo_globally_readable_list_lock);
+ return found;
+}
+
+/**
+ * tomoyo_write_globally_readable_policy - Write "struct tomoyo_globally_readable_file_entry" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
+{
+ return tomoyo_update_globally_readable_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_globally_readable_policy - Read "struct tomoyo_globally_readable_file_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ bool done = true;
+
+ down_read(&tomoyo_globally_readable_list_lock);
+ list_for_each_cookie(pos, head->read_var2,
+ &tomoyo_globally_readable_list) {
+ struct tomoyo_globally_readable_file_entry *ptr;
+ ptr = list_entry(pos,
+ struct tomoyo_globally_readable_file_entry,
+ list);
+ if (ptr->is_deleted)
+ continue;
+ if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n",
+ ptr->filename->name)) {
+ done = false;
+ break;
+ }
+ }
+ up_read(&tomoyo_globally_readable_list_lock);
+ return done;
+}
+
+/* The list for "struct tomoyo_pattern_entry". */
+static LIST_HEAD(tomoyo_pattern_list);
+static DECLARE_RWSEM(tomoyo_pattern_list_lock);
+
+/**
+ * tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list.
+ *
+ * @pattern: Pathname pattern.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_file_pattern_entry(const char *pattern,
+ const bool is_delete)
+{
+ struct tomoyo_pattern_entry *new_entry;
+ struct tomoyo_pattern_entry *ptr;
+ const struct tomoyo_path_info *saved_pattern;
+ int error = -ENOMEM;
+
+ if (!tomoyo_is_correct_path(pattern, 0, 1, 0, __func__))
+ return -EINVAL;
+ saved_pattern = tomoyo_save_name(pattern);
+ if (!saved_pattern)
+ return -ENOMEM;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_pattern_list_lock);
+ list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+ if (saved_pattern != ptr->pattern)
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ goto out;
+ }
+ if (is_delete) {
+ error = -ENOENT;
+ goto out;
+ }
+ new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+ if (!new_entry)
+ goto out;
+ new_entry->pattern = saved_pattern;
+ list_add_tail(&new_entry->list, &tomoyo_pattern_list);
+ error = 0;
+ out:
+ up_write(&tomoyo_pattern_list_lock);
+ /***** EXCLUSIVE SECTION END *****/
+ return error;
+}
+
+/**
+ * tomoyo_get_file_pattern - Get patterned pathname.
+ *
+ * @filename: The filename to find patterned pathname.
+ *
+ * Returns pointer to pathname pattern if matched, @filename otherwise.
+ */
+static const struct tomoyo_path_info *
+tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
+{
+ struct tomoyo_pattern_entry *ptr;
+ const struct tomoyo_path_info *pattern = NULL;
+
+ down_read(&tomoyo_pattern_list_lock);
+ list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+ if (ptr->is_deleted)
+ continue;
+ if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
+ continue;
+ pattern = ptr->pattern;
+ if (tomoyo_strendswith(pattern->name, "/\\*")) {
+ /* Do nothing. Try to find the better match. */
+ } else {
+ /* This would be the better match. Use this. */
+ break;
+ }
+ }
+ up_read(&tomoyo_pattern_list_lock);
+ if (pattern)
+ filename = pattern;
+ return filename;
+}
+
+/**
+ * tomoyo_write_pattern_policy - Write "struct tomoyo_pattern_entry" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_pattern_policy(char *data, const bool is_delete)
+{
+ return tomoyo_update_file_pattern_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_file_pattern - Read "struct tomoyo_pattern_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ bool done = true;
+
+ down_read(&tomoyo_pattern_list_lock);
+ list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
+ struct tomoyo_pattern_entry *ptr;
+ ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
+ if (ptr->is_deleted)
+ continue;
+ if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN "%s\n",
+ ptr->pattern->name)) {
+ done = false;
+ break;
+ }
+ }
+ up_read(&tomoyo_pattern_list_lock);
+ return done;
+}
+
+/* The list for "struct tomoyo_no_rewrite_entry". */
+static LIST_HEAD(tomoyo_no_rewrite_list);
+static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock);
+
+/**
+ * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list.
+ *
+ * @pattern: Pathname pattern that are not rewritable by default.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_no_rewrite_entry(const char *pattern,
+ const bool is_delete)
+{
+ struct tomoyo_no_rewrite_entry *new_entry, *ptr;
+ const struct tomoyo_path_info *saved_pattern;
+ int error = -ENOMEM;
+
+ if (!tomoyo_is_correct_path(pattern, 0, 0, 0, __func__))
+ return -EINVAL;
+ saved_pattern = tomoyo_save_name(pattern);
+ if (!saved_pattern)
+ return -ENOMEM;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_no_rewrite_list_lock);
+ list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+ if (ptr->pattern != saved_pattern)
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ goto out;
+ }
+ if (is_delete) {
+ error = -ENOENT;
+ goto out;
+ }
+ new_entry = tomoyo_alloc_element(sizeof(*new_entry));
+ if (!new_entry)
+ goto out;
+ new_entry->pattern = saved_pattern;
+ list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list);
+ error = 0;
+ out:
+ up_write(&tomoyo_no_rewrite_list_lock);
+ /***** EXCLUSIVE SECTION END *****/
+ return error;
+}
+
+/**
+ * tomoyo_is_no_rewrite_file - Check if the given pathname is not permitted to be rewrited.
+ *
+ * @filename: Filename to check.
+ *
+ * Returns true if @filename is specified by "deny_rewrite" directive,
+ * false otherwise.
+ */
+static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
+{
+ struct tomoyo_no_rewrite_entry *ptr;
+ bool found = false;
+
+ down_read(&tomoyo_no_rewrite_list_lock);
+ list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+ if (ptr->is_deleted)
+ continue;
+ if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
+ continue;
+ found = true;
+ break;
+ }
+ up_read(&tomoyo_no_rewrite_list_lock);
+ return found;
+}
+
+/**
+ * tomoyo_write_no_rewrite_policy - Write "struct tomoyo_no_rewrite_entry" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
+{
+ return tomoyo_update_no_rewrite_entry(data, is_delete);
+}
+
+/**
+ * tomoyo_read_no_rewrite_policy - Read "struct tomoyo_no_rewrite_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ bool done = true;
+
+ down_read(&tomoyo_no_rewrite_list_lock);
+ list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) {
+ struct tomoyo_no_rewrite_entry *ptr;
+ ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
+ if (ptr->is_deleted)
+ continue;
+ if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE "%s\n",
+ ptr->pattern->name)) {
+ done = false;
+ break;
+ }
+ }
+ up_read(&tomoyo_no_rewrite_list_lock);
+ return done;
+}
+
+/**
+ * tomoyo_update_file_acl - Update file's read/write/execute ACL.
+ *
+ * @filename: Filename.
+ * @perm: Permission (between 1 to 7).
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * This is legacy support interface for older policy syntax.
+ * Current policy syntax uses "allow_read/write" instead of "6",
+ * "allow_read" instead of "4", "allow_write" instead of "2",
+ * "allow_execute" instead of "1".
+ */
+static int tomoyo_update_file_acl(const char *filename, u8 perm,
+ struct tomoyo_domain_info * const domain,
+ const bool is_delete)
+{
+ if (perm > 7 || !perm) {
+ printk(KERN_DEBUG "%s: Invalid permission '%d %s'\n",
+ __func__, perm, filename);
+ return -EINVAL;
+ }
+ if (filename[0] != '@' && tomoyo_strendswith(filename, "/"))
+ /*
+ * Only 'allow_mkdir' and 'allow_rmdir' are valid for
+ * directory permissions.
+ */
+ return 0;
+ if (perm & 4)
+ tomoyo_update_single_path_acl(TOMOYO_TYPE_READ_ACL, filename,
+ domain, is_delete);
+ if (perm & 2)
+ tomoyo_update_single_path_acl(TOMOYO_TYPE_WRITE_ACL, filename,
+ domain, is_delete);
+ if (perm & 1)
+ tomoyo_update_single_path_acl(TOMOYO_TYPE_EXECUTE_ACL,
+ filename, domain, is_delete);
+ return 0;
+}
+
+/**
+ * tomoyo_check_single_path_acl2 - Check permission for single path operation.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @filename: Filename to check.
+ * @perm: Permission.
+ * @may_use_pattern: True if patterned ACL is permitted.
+ *
+ * Returns 0 on success, -EPERM otherwise.
+ */
+static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
+ domain,
+ const struct tomoyo_path_info *
+ filename,
+ const u16 perm,
+ const bool may_use_pattern)
+{
+ struct tomoyo_acl_info *ptr;
+ int error = -EPERM;
+
+ down_read(&tomoyo_domain_acl_info_list_lock);
+ list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_single_path_acl_record *acl;
+ if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
+ continue;
+ acl = container_of(ptr, struct tomoyo_single_path_acl_record,
+ head);
+ if (!(acl->perm & perm))
+ continue;
+ if (may_use_pattern || !acl->filename->is_patterned) {
+ if (!tomoyo_path_matches_pattern(filename,
+ acl->filename))
+ continue;
+ } else {
+ continue;
+ }
+ error = 0;
+ break;
+ }
+ up_read(&tomoyo_domain_acl_info_list_lock);
+ return error;
+}
+
+/**
+ * tomoyo_check_file_acl - Check permission for opening files.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @filename: Filename to check.
+ * @operation: Mode ("read" or "write" or "read/write" or "execute").
+ *
+ * Returns 0 on success, -EPERM otherwise.
+ */
+static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
+ const struct tomoyo_path_info *filename,
+ const u8 operation)
+{
+ u16 perm = 0;
+
+ if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
+ return 0;
+ if (operation == 6)
+ perm = 1 << TOMOYO_TYPE_READ_WRITE_ACL;
+ else if (operation == 4)
+ perm = 1 << TOMOYO_TYPE_READ_ACL;
+ else if (operation == 2)
+ perm = 1 << TOMOYO_TYPE_WRITE_ACL;
+ else if (operation == 1)
+ perm = 1 << TOMOYO_TYPE_EXECUTE_ACL;
+ else
+ BUG();
+ return tomoyo_check_single_path_acl2(domain, filename, perm,
+ operation != 1);
+}
+
+/**
+ * tomoyo_check_file_perm2 - Check permission for opening files.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @filename: Filename to check.
+ * @perm: Mode ("read" or "write" or "read/write" or "execute").
+ * @operation: Operation name passed used for verbose mode.
+ * @mode: Access control mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
+ const struct tomoyo_path_info *filename,
+ const u8 perm, const char *operation,
+ const u8 mode)
+{
+ const bool is_enforce = (mode == 3);
+ const char *msg = "<unknown>";
+ int error = 0;
+
+ if (!filename)
+ return 0;
+ error = tomoyo_check_file_acl(domain, filename, perm);
+ if (error && perm == 4 &&
+ (domain->flags & TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ) == 0
+ && tomoyo_is_globally_readable_file(filename))
+ error = 0;
+ if (perm == 6)
+ msg = tomoyo_sp2keyword(TOMOYO_TYPE_READ_WRITE_ACL);
+ else if (perm == 4)
+ msg = tomoyo_sp2keyword(TOMOYO_TYPE_READ_ACL);
+ else if (perm == 2)
+ msg = tomoyo_sp2keyword(TOMOYO_TYPE_WRITE_ACL);
+ else if (perm == 1)
+ msg = tomoyo_sp2keyword(TOMOYO_TYPE_EXECUTE_ACL);
+ else
+ BUG();
+ if (!error)
+ return 0;
+ if (tomoyo_verbose_mode(domain))
+ printk(KERN_WARNING "TOMOYO-%s: Access '%s(%s) %s' denied "
+ "for %s\n", tomoyo_get_msg(is_enforce), msg, operation,
+ filename->name, tomoyo_get_last_name(domain));
+ if (is_enforce)
+ return error;
+ if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
+ /* Don't use patterns for execute permission. */
+ const struct tomoyo_path_info *patterned_file = (perm != 1) ?
+ tomoyo_get_file_pattern(filename) : filename;
+ tomoyo_update_file_acl(patterned_file->name, perm,
+ domain, false);
+ }
+ return 0;
+}
+
+/**
+ * tomoyo_write_file_policy - Update file related list.
+ *
+ * @data: String to parse.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
+ const bool is_delete)
+{
+ char *filename = strchr(data, ' ');
+ char *filename2;
+ unsigned int perm;
+ u8 type;
+
+ if (!filename)
+ return -EINVAL;
+ *filename++ = '\0';
+ if (sscanf(data, "%u", &perm) == 1)
+ return tomoyo_update_file_acl(filename, (u8) perm, domain,
+ is_delete);
+ if (strncmp(data, "allow_", 6))
+ goto out;
+ data += 6;
+ for (type = 0; type < TOMOYO_MAX_SINGLE_PATH_OPERATION; type++) {
+ if (strcmp(data, tomoyo_sp_keyword[type]))
+ continue;
+ return tomoyo_update_single_path_acl(type, filename,
+ domain, is_delete);
+ }
+ filename2 = strchr(filename, ' ');
+ if (!filename2)
+ goto out;
+ *filename2++ = '\0';
+ for (type = 0; type < TOMOYO_MAX_DOUBLE_PATH_OPERATION; type++) {
+ if (strcmp(data, tomoyo_dp_keyword[type]))
+ continue;
+ return tomoyo_update_double_path_acl(type, filename, filename2,
+ domain, is_delete);
+ }
+ out:
+ return -EINVAL;
+}
+
+/**
+ * tomoyo_update_single_path_acl - Update "struct tomoyo_single_path_acl_record" list.
+ *
+ * @type: Type of operation.
+ * @filename: Filename.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
+ struct tomoyo_domain_info *
+ const domain, const bool is_delete)
+{
+ static const u16 rw_mask =
+ (1 << TOMOYO_TYPE_READ_ACL) | (1 << TOMOYO_TYPE_WRITE_ACL);
+ const struct tomoyo_path_info *saved_filename;
+ struct tomoyo_acl_info *ptr;
+ struct tomoyo_single_path_acl_record *acl;
+ int error = -ENOMEM;
+ const u16 perm = 1 << type;
+
+ if (!domain)
+ return -EINVAL;
+ if (!tomoyo_is_correct_path(filename, 0, 0, 0, __func__))
+ return -EINVAL;
+ saved_filename = tomoyo_save_name(filename);
+ if (!saved_filename)
+ return -ENOMEM;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_domain_acl_info_list_lock);
+ if (is_delete)
+ goto delete;
+ list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
+ continue;
+ acl = container_of(ptr, struct tomoyo_single_path_acl_record,
+ head);
+ if (acl->filename != saved_filename)
+ continue;
+ /* Special case. Clear all bits if marked as deleted. */
+ if (ptr->type & TOMOYO_ACL_DELETED)
+ acl->perm = 0;
+ acl->perm |= perm;
+ if ((acl->perm & rw_mask) == rw_mask)
+ acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE_ACL;
+ else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL))
+ acl->perm |= rw_mask;
+ ptr->type &= ~TOMOYO_ACL_DELETED;
+ error = 0;
+ goto out;
+ }
+ /* Not found. Append it to the tail. */
+ acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_SINGLE_PATH_ACL);
+ if (!acl)
+ goto out;
+ acl->perm = perm;
+ if (perm == (1 << TOMOYO_TYPE_READ_WRITE_ACL))
+ acl->perm |= rw_mask;
+ acl->filename = saved_filename;
+ list_add_tail(&acl->head.list, &domain->acl_info_list);
+ error = 0;
+ goto out;
+ delete:
+ error = -ENOENT;
+ list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
+ continue;
+ acl = container_of(ptr, struct tomoyo_single_path_acl_record,
+ head);
+ if (acl->filename != saved_filename)
+ continue;
+ acl->perm &= ~perm;
+ if ((acl->perm & rw_mask) != rw_mask)
+ acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE_ACL);
+ else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL)))
+ acl->perm &= ~rw_mask;
+ if (!acl->perm)
+ ptr->type |= TOMOYO_ACL_DELETED;
+ error = 0;
+ break;
+ }
+ out:
+ up_write(&tomoyo_domain_acl_info_list_lock);
+ /***** EXCLUSIVE SECTION END *****/
+ return error;
+}
+
+/**
+ * tomoyo_update_double_path_acl - Update "struct tomoyo_double_path_acl_record" list.
+ *
+ * @type: Type of operation.
+ * @filename1: First filename.
+ * @filename2: Second filename.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
+ const char *filename2,
+ struct tomoyo_domain_info *
+ const domain, const bool is_delete)
+{
+ const struct tomoyo_path_info *saved_filename1;
+ const struct tomoyo_path_info *saved_filename2;
+ struct tomoyo_acl_info *ptr;
+ struct tomoyo_double_path_acl_record *acl;
+ int error = -ENOMEM;
+ const u8 perm = 1 << type;
+
+ if (!domain)
+ return -EINVAL;
+ if (!tomoyo_is_correct_path(filename1, 0, 0, 0, __func__) ||
+ !tomoyo_is_correct_path(filename2, 0, 0, 0, __func__))
+ return -EINVAL;
+ saved_filename1 = tomoyo_save_name(filename1);
+ saved_filename2 = tomoyo_save_name(filename2);
+ if (!saved_filename1 || !saved_filename2)
+ return -ENOMEM;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_domain_acl_info_list_lock);
+ if (is_delete)
+ goto delete;
+ list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
+ continue;
+ acl = container_of(ptr, struct tomoyo_double_path_acl_record,
+ head);
+ if (acl->filename1 != saved_filename1 ||
+ acl->filename2 != saved_filename2)
+ continue;
+ /* Special case. Clear all bits if marked as deleted. */
+ if (ptr->type & TOMOYO_ACL_DELETED)
+ acl->perm = 0;
+ acl->perm |= perm;
+ ptr->type &= ~TOMOYO_ACL_DELETED;
+ error = 0;
+ goto out;
+ }
+ /* Not found. Append it to the tail. */
+ acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_DOUBLE_PATH_ACL);
+ if (!acl)
+ goto out;
+ acl->perm = perm;
+ acl->filename1 = saved_filename1;
+ acl->filename2 = saved_filename2;
+ list_add_tail(&acl->head.list, &domain->acl_info_list);
+ error = 0;
+ goto out;
+ delete:
+ error = -ENOENT;
+ list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
+ continue;
+ acl = container_of(ptr, struct tomoyo_double_path_acl_record,
+ head);
+ if (acl->filename1 != saved_filename1 ||
+ acl->filename2 != saved_filename2)
+ continue;
+ acl->perm &= ~perm;
+ if (!acl->perm)
+ ptr->type |= TOMOYO_ACL_DELETED;
+ error = 0;
+ break;
+ }
+ out:
+ up_write(&tomoyo_domain_acl_info_list_lock);
+ /***** EXCLUSIVE SECTION END *****/
+ return error;
+}
+
+/**
+ * tomoyo_check_single_path_acl - Check permission for single path operation.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @type: Type of operation.
+ * @filename: Filename to check.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain,
+ const u8 type,
+ const struct tomoyo_path_info *filename)
+{
+ if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
+ return 0;
+ return tomoyo_check_single_path_acl2(domain, filename, 1 << type, 1);
+}
+
+/**
+ * tomoyo_check_double_path_acl - Check permission for double path operation.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @type: Type of operation.
+ * @filename1: First filename to check.
+ * @filename2: Second filename to check.
+ *
+ * Returns 0 on success, -EPERM otherwise.
+ */
+static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
+ const u8 type,
+ const struct tomoyo_path_info *
+ filename1,
+ const struct tomoyo_path_info *
+ filename2)
+{
+ struct tomoyo_acl_info *ptr;
+ const u8 perm = 1 << type;
+ int error = -EPERM;
+
+ if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
+ return 0;
+ down_read(&tomoyo_domain_acl_info_list_lock);
+ list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_double_path_acl_record *acl;
+ if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
+ continue;
+ acl = container_of(ptr, struct tomoyo_double_path_acl_record,
+ head);
+ if (!(acl->perm & perm))
+ continue;
+ if (!tomoyo_path_matches_pattern(filename1, acl->filename1))
+ continue;
+ if (!tomoyo_path_matches_pattern(filename2, acl->filename2))
+ continue;
+ error = 0;
+ break;
+ }
+ up_read(&tomoyo_domain_acl_info_list_lock);
+ return error;
+}
+
+/**
+ * tomoyo_check_single_path_permission2 - Check permission for single path operation.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @operation: Type of operation.
+ * @filename: Filename to check.
+ * @mode: Access control mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
+ const domain, u8 operation,
+ const struct tomoyo_path_info *
+ filename, const u8 mode)
+{
+ const char *msg;
+ int error;
+ const bool is_enforce = (mode == 3);
+
+ if (!mode)
+ return 0;
+ next:
+ error = tomoyo_check_single_path_acl(domain, operation, filename);
+ msg = tomoyo_sp2keyword(operation);
+ if (!error)
+ goto ok;
+ if (tomoyo_verbose_mode(domain))
+ printk(KERN_WARNING "TOMOYO-%s: Access '%s %s' denied for %s\n",
+ tomoyo_get_msg(is_enforce), msg, filename->name,
+ tomoyo_get_last_name(domain));
+ if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
+ const char *name = tomoyo_get_file_pattern(filename)->name;
+ tomoyo_update_single_path_acl(operation, name, domain, false);
+ }
+ if (!is_enforce)
+ error = 0;
+ ok:
+ /*
+ * Since "allow_truncate" doesn't imply "allow_rewrite" permission,
+ * we need to check "allow_rewrite" permission if the filename is
+ * specified by "deny_rewrite" keyword.
+ */
+ if (!error && operation == TOMOYO_TYPE_TRUNCATE_ACL &&
+ tomoyo_is_no_rewrite_file(filename)) {
+ operation = TOMOYO_TYPE_REWRITE_ACL;
+ goto next;
+ }
+ return error;
+}
+
+/**
+ * tomoyo_check_file_perm - Check permission for sysctl()'s "read" and "write".
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @filename: Filename to check.
+ * @perm: Mode ("read" or "write" or "read/write").
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_file_perm(struct tomoyo_domain_info *domain,
+ const char *filename, const u8 perm)
+{
+ struct tomoyo_path_info name;
+ const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+
+ if (!mode)
+ return 0;
+ name.name = filename;
+ tomoyo_fill_path_info(&name);
+ return tomoyo_check_file_perm2(domain, &name, perm, "sysctl", mode);
+}
+
+/**
+ * tomoyo_check_exec_perm - Check permission for "execute".
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @filename: Check permission for "execute".
+ * @tmp: Buffer for temporary use.
+ *
+ * Returns 0 on success, negativevalue otherwise.
+ */
+int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
+ const struct tomoyo_path_info *filename,
+ struct tomoyo_page_buffer *tmp)
+{
+ const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+
+ if (!mode)
+ return 0;
+ return tomoyo_check_file_perm2(domain, filename, 1, "do_execve", mode);
+}
+
+/**
+ * tomoyo_check_open_permission - Check permission for "read" and "write".
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @path: Pointer to "struct path".
+ * @flag: Flags for open().
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
+ struct path *path, const int flag)
+{
+ const u8 acc_mode = ACC_MODE(flag);
+ int error = -ENOMEM;
+ struct tomoyo_path_info *buf;
+ const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+ const bool is_enforce = (mode == 3);
+
+ if (!mode || !path->mnt)
+ return 0;
+ if (acc_mode == 0)
+ return 0;
+ if (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode))
+ /*
+ * I don't check directories here because mkdir() and rmdir()
+ * don't call me.
+ */
+ return 0;
+ buf = tomoyo_get_path(path);
+ if (!buf)
+ goto out;
+ error = 0;
+ /*
+ * If the filename is specified by "deny_rewrite" keyword,
+ * we need to check "allow_rewrite" permission when the filename is not
+ * opened for append mode or the filename is truncated at open time.
+ */
+ if ((acc_mode & MAY_WRITE) &&
+ ((flag & O_TRUNC) || !(flag & O_APPEND)) &&
+ (tomoyo_is_no_rewrite_file(buf))) {
+ error = tomoyo_check_single_path_permission2(domain,
+ TOMOYO_TYPE_REWRITE_ACL,
+ buf, mode);
+ }
+ if (!error)
+ error = tomoyo_check_file_perm2(domain, buf, acc_mode, "open",
+ mode);
+ if (!error && (flag & O_TRUNC))
+ error = tomoyo_check_single_path_permission2(domain,
+ TOMOYO_TYPE_TRUNCATE_ACL,
+ buf, mode);
+ out:
+ tomoyo_free(buf);
+ if (!is_enforce)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_check_1path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate" and "symlink".
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @operation: Type of operation.
+ * @path: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
+ const u8 operation, struct path *path)
+{
+ int error = -ENOMEM;
+ struct tomoyo_path_info *buf;
+ const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+ const bool is_enforce = (mode == 3);
+
+ if (!mode || !path->mnt)
+ return 0;
+ buf = tomoyo_get_path(path);
+ if (!buf)
+ goto out;
+ switch (operation) {
+ case TOMOYO_TYPE_MKDIR_ACL:
+ case TOMOYO_TYPE_RMDIR_ACL:
+ if (!buf->is_dir) {
+ /*
+ * tomoyo_get_path() reserves space for appending "/."
+ */
+ strcat((char *) buf->name, "/");
+ tomoyo_fill_path_info(buf);
+ }
+ }
+ error = tomoyo_check_single_path_permission2(domain, operation, buf,
+ mode);
+ out:
+ tomoyo_free(buf);
+ if (!is_enforce)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_check_rewrite_permission - Check permission for "rewrite".
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @filp: Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
+ struct file *filp)
+{
+ int error = -ENOMEM;
+ const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+ const bool is_enforce = (mode == 3);
+ struct tomoyo_path_info *buf;
+
+ if (!mode || !filp->f_path.mnt)
+ return 0;
+ buf = tomoyo_get_path(&filp->f_path);
+ if (!buf)
+ goto out;
+ if (!tomoyo_is_no_rewrite_file(buf)) {
+ error = 0;
+ goto out;
+ }
+ error = tomoyo_check_single_path_permission2(domain,
+ TOMOYO_TYPE_REWRITE_ACL,
+ buf, mode);
+ out:
+ tomoyo_free(buf);
+ if (!is_enforce)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_check_2path_perm - Check permission for "rename" and "link".
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @operation: Type of operation.
+ * @path1: Pointer to "struct path".
+ * @path2: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
+ const u8 operation, struct path *path1,
+ struct path *path2)
+{
+ int error = -ENOMEM;
+ struct tomoyo_path_info *buf1, *buf2;
+ const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+ const bool is_enforce = (mode == 3);
+ const char *msg;
+
+ if (!mode || !path1->mnt || !path2->mnt)
+ return 0;
+ buf1 = tomoyo_get_path(path1);
+ buf2 = tomoyo_get_path(path2);
+ if (!buf1 || !buf2)
+ goto out;
+ {
+ struct dentry *dentry = path1->dentry;
+ if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
+ /*
+ * tomoyo_get_path() reserves space for appending "/."
+ */
+ if (!buf1->is_dir) {
+ strcat((char *) buf1->name, "/");
+ tomoyo_fill_path_info(buf1);
+ }
+ if (!buf2->is_dir) {
+ strcat((char *) buf2->name, "/");
+ tomoyo_fill_path_info(buf2);
+ }
+ }
+ }
+ error = tomoyo_check_double_path_acl(domain, operation, buf1, buf2);
+ msg = tomoyo_dp2keyword(operation);
+ if (!error)
+ goto out;
+ if (tomoyo_verbose_mode(domain))
+ printk(KERN_WARNING "TOMOYO-%s: Access '%s %s %s' "
+ "denied for %s\n", tomoyo_get_msg(is_enforce),
+ msg, buf1->name, buf2->name,
+ tomoyo_get_last_name(domain));
+ if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
+ const char *name1 = tomoyo_get_file_pattern(buf1)->name;
+ const char *name2 = tomoyo_get_file_pattern(buf2)->name;
+ tomoyo_update_double_path_acl(operation, name1, name2, domain,
+ false);
+ }
+ out:
+ tomoyo_free(buf1);
+ tomoyo_free(buf2);
+ if (!is_enforce)
+ error = 0;
+ return error;
+}
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c
new file mode 100644
index 0000000..d47f16b
--- /dev/null
+++ b/security/tomoyo/realpath.c
@@ -0,0 +1,482 @@
+/*
+ * security/tomoyo/realpath.c
+ *
+ * Get the canonicalized absolute pathnames. The basis for TOMOYO.
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre 2009/02/01
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/mount.h>
+#include <linux/mnt_namespace.h>
+#include "common.h"
+#include "realpath.h"
+
+/**
+ * tomoyo_encode: Convert binary string to ascii string.
+ *
+ * @buffer: Buffer for ASCII string.
+ * @buflen: Size of @buffer.
+ * @str: Binary string.
+ *
+ * Returns 0 on success, -ENOMEM otherwise.
+ */
+int tomoyo_encode(char *buffer, int buflen, const char *str)
+{
+ while (1) {
+ const unsigned char c = *(unsigned char *) str++;
+
+ if (tomoyo_is_valid(c)) {
+ if (--buflen <= 0)
+ break;
+ *buffer++ = (char) c;
+ if (c != '\\')
+ continue;
+ if (--buflen <= 0)
+ break;
+ *buffer++ = (char) c;
+ continue;
+ }
+ if (!c) {
+ if (--buflen <= 0)
+ break;
+ *buffer = '\0';
+ return 0;
+ }
+ buflen -= 4;
+ if (buflen <= 0)
+ break;
+ *buffer++ = '\\';
+ *buffer++ = (c >> 6) + '0';
+ *buffer++ = ((c >> 3) & 7) + '0';
+ *buffer++ = (c & 7) + '0';
+ }
+ return -ENOMEM;
+}
+
+/**
+ * tomoyo_realpath_from_path2 - Returns realpath(3) of the given dentry but ignores chroot'ed root.
+ *
+ * @path: Pointer to "struct path".
+ * @newname: Pointer to buffer to return value in.
+ * @newname_len: Size of @newname.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * If dentry is a directory, trailing '/' is appended.
+ * Characters out of 0x20 < c < 0x7F range are converted to
+ * \ooo style octal string.
+ * Character \ is converted to \\ string.
+ */
+int tomoyo_realpath_from_path2(struct path *path, char *newname,
+ int newname_len)
+{
+ int error = -ENOMEM;
+ struct dentry *dentry = path->dentry;
+ char *sp;
+
+ if (!dentry || !path->mnt || !newname || newname_len <= 2048)
+ return -EINVAL;
+ if (dentry->d_op && dentry->d_op->d_dname) {
+ /* For "socket:[\$]" and "pipe:[\$]". */
+ static const int offset = 1536;
+ sp = dentry->d_op->d_dname(dentry, newname + offset,
+ newname_len - offset);
+ } else {
+ /* Taken from d_namespace_path(). */
+ struct path root;
+ struct path ns_root = { };
+ struct path tmp;
+
+ read_lock(¤t->fs->lock);
+ root = current->fs->root;
+ path_get(&root);
+ read_unlock(¤t->fs->lock);
+ spin_lock(&vfsmount_lock);
+ if (root.mnt && root.mnt->mnt_ns)
+ ns_root.mnt = mntget(root.mnt->mnt_ns->root);
+ if (ns_root.mnt)
+ ns_root.dentry = dget(ns_root.mnt->mnt_root);
+ spin_unlock(&vfsmount_lock);
+ spin_lock(&dcache_lock);
+ tmp = ns_root;
+ sp = __d_path(path, &tmp, newname, newname_len);
+ spin_unlock(&dcache_lock);
+ path_put(&root);
+ path_put(&ns_root);
+ }
+ if (IS_ERR(sp))
+ error = PTR_ERR(sp);
+ else
+ error = tomoyo_encode(newname, sp - newname, sp);
+ /* Append trailing '/' if dentry is a directory. */
+ if (!error && dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)
+ && *newname) {
+ sp = newname + strlen(newname);
+ if (*(sp - 1) != '/') {
+ if (sp < newname + newname_len - 4) {
+ *sp++ = '/';
+ *sp = '\0';
+ } else {
+ error = -ENOMEM;
+ }
+ }
+ }
+ if (error)
+ printk(KERN_WARNING "tomoyo_realpath: Pathname too long.\n");
+ return error;
+}
+
+/**
+ * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns the realpath of the given @path on success, NULL otherwise.
+ *
+ * These functions use tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if these functions didn't return NULL.
+ */
+char *tomoyo_realpath_from_path(struct path *path)
+{
+ char *buf = tomoyo_alloc(sizeof(struct tomoyo_page_buffer));
+
+ BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer)
+ <= TOMOYO_MAX_PATHNAME_LEN - 1);
+ if (!buf)
+ return NULL;
+ if (tomoyo_realpath_from_path2(path, buf,
+ TOMOYO_MAX_PATHNAME_LEN - 1) == 0)
+ return buf;
+ tomoyo_free(buf);
+ return NULL;
+}
+
+/**
+ * tomoyo_realpath - Get realpath of a pathname.
+ *
+ * @pathname: The pathname to solve.
+ *
+ * Returns the realpath of @pathname on success, NULL otherwise.
+ */
+char *tomoyo_realpath(const char *pathname)
+{
+ struct nameidata nd;
+
+ if (pathname && path_lookup(pathname, LOOKUP_FOLLOW, &nd) == 0) {
+ char *buf = tomoyo_realpath_from_path(&nd.path);
+ path_put(&nd.path);
+ return buf;
+ }
+ return NULL;
+}
+
+/**
+ * tomoyo_realpath_nofollow - Get realpath of a pathname.
+ *
+ * @pathname: The pathname to solve.
+ *
+ * Returns the realpath of @pathname on success, NULL otherwise.
+ */
+char *tomoyo_realpath_nofollow(const char *pathname)
+{
+ struct nameidata nd;
+
+ if (pathname && path_lookup(pathname, 0, &nd) == 0) {
+ char *buf = tomoyo_realpath_from_path(&nd.path);
+ path_put(&nd.path);
+ return buf;
+ }
+ return NULL;
+}
+
+/* Memory allocated for non-string data. */
+static unsigned int tomoyo_allocated_memory_for_elements;
+/* Quota for holding non-string data. */
+static unsigned int tomoyo_quota_for_elements;
+
+/**
+ * tomoyo_alloc_element - Allocate permanent memory for structures.
+ *
+ * @size: Size in bytes.
+ *
+ * Returns pointer to allocated memory on success, NULL otherwise.
+ *
+ * Memory has to be zeroed.
+ * The RAM is chunked, so NEVER try to kfree() the returned pointer.
+ */
+void *tomoyo_alloc_element(const unsigned int size)
+{
+ static char *buf;
+ static DEFINE_MUTEX(lock);
+ static unsigned int buf_used_len = PATH_MAX;
+ char *ptr = NULL;
+ /*Assumes sizeof(void *) >= sizeof(long) is true. */
+ const unsigned int word_aligned_size
+ = roundup(size, max(sizeof(void *), sizeof(long)));
+ if (word_aligned_size > PATH_MAX)
+ return NULL;
+ /***** EXCLUSIVE SECTION START *****/
+ mutex_lock(&lock);
+ if (buf_used_len + word_aligned_size > PATH_MAX) {
+ if (!tomoyo_quota_for_elements ||
+ tomoyo_allocated_memory_for_elements
+ + PATH_MAX <= tomoyo_quota_for_elements)
+ ptr = kzalloc(PATH_MAX, GFP_KERNEL);
+ if (!ptr) {
+ printk(KERN_WARNING "ERROR: Out of memory "
+ "for tomoyo_alloc_element().\n");
+ if (!tomoyo_policy_loaded)
+ panic("MAC Initialization failed.\n");
+ } else {
+ buf = ptr;
+ tomoyo_allocated_memory_for_elements += PATH_MAX;
+ buf_used_len = word_aligned_size;
+ ptr = buf;
+ }
+ } else if (word_aligned_size) {
+ int i;
+ ptr = buf + buf_used_len;
+ buf_used_len += word_aligned_size;
+ for (i = 0; i < word_aligned_size; i++) {
+ if (!ptr[i])
+ continue;
+ printk(KERN_ERR "WARNING: Reserved memory was tainted! "
+ "The system might go wrong.\n");
+ ptr[i] = '\0';
+ }
+ }
+ mutex_unlock(&lock);
+ /***** EXCLUSIVE SECTION END *****/
+ return ptr;
+}
+
+/* Memory allocated for string data in bytes. */
+static unsigned int tomoyo_allocated_memory_for_savename;
+/* Quota for holding string data in bytes. */
+static unsigned int tomoyo_quota_for_savename;
+
+/*
+ * TOMOYO uses this hash only when appending a string into the string
+ * table. Frequency of appending strings is very low. So we don't need
+ * large (e.g. 64k) hash size. 256 will be sufficient.
+ */
+#define TOMOYO_MAX_HASH 256
+
+/* Structure for string data. */
+struct tomoyo_name_entry {
+ struct list_head list;
+ struct tomoyo_path_info entry;
+};
+
+/* Structure for available memory region. */
+struct tomoyo_free_memory_block_list {
+ struct list_head list;
+ char *ptr; /* Pointer to a free area. */
+ int len; /* Length of the area. */
+};
+
+/*
+ * The list for "struct tomoyo_name_entry".
+ *
+ * This list is updated only inside tomoyo_save_name(), thus
+ * no global mutex exists.
+ */
+static struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
+
+/**
+ * tomoyo_save_name - Allocate permanent memory for string data.
+ *
+ * @name: The string to store into the permernent memory.
+ *
+ * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
+ *
+ * The RAM is shared, so NEVER try to modify or kfree() the returned name.
+ */
+const struct tomoyo_path_info *tomoyo_save_name(const char *name)
+{
+ static LIST_HEAD(fmb_list);
+ static DEFINE_MUTEX(lock);
+ struct tomoyo_name_entry *ptr;
+ unsigned int hash;
+ /* fmb contains available size in bytes.
+ fmb is removed from the fmb_list when fmb->len becomes 0. */
+ struct tomoyo_free_memory_block_list *fmb;
+ int len;
+ char *cp;
+
+ if (!name)
+ return NULL;
+ len = strlen(name) + 1;
+ if (len > TOMOYO_MAX_PATHNAME_LEN) {
+ printk(KERN_WARNING "ERROR: Name too long "
+ "for tomoyo_save_name().\n");
+ return NULL;
+ }
+ hash = full_name_hash((const unsigned char *) name, len - 1);
+ /***** EXCLUSIVE SECTION START *****/
+ mutex_lock(&lock);
+ list_for_each_entry(ptr, &tomoyo_name_list[hash % TOMOYO_MAX_HASH],
+ list) {
+ if (hash == ptr->entry.hash && !strcmp(name, ptr->entry.name))
+ goto out;
+ }
+ list_for_each_entry(fmb, &fmb_list, list) {
+ if (len <= fmb->len)
+ goto ready;
+ }
+ if (!tomoyo_quota_for_savename ||
+ tomoyo_allocated_memory_for_savename + PATH_MAX
+ <= tomoyo_quota_for_savename)
+ cp = kzalloc(PATH_MAX, GFP_KERNEL);
+ else
+ cp = NULL;
+ fmb = kzalloc(sizeof(*fmb), GFP_KERNEL);
+ if (!cp || !fmb) {
+ kfree(cp);
+ kfree(fmb);
+ printk(KERN_WARNING "ERROR: Out of memory "
+ "for tomoyo_save_name().\n");
+ if (!tomoyo_policy_loaded)
+ panic("MAC Initialization failed.\n");
+ ptr = NULL;
+ goto out;
+ }
+ tomoyo_allocated_memory_for_savename += PATH_MAX;
+ list_add(&fmb->list, &fmb_list);
+ fmb->ptr = cp;
+ fmb->len = PATH_MAX;
+ ready:
+ ptr = tomoyo_alloc_element(sizeof(*ptr));
+ if (!ptr)
+ goto out;
+ ptr->entry.name = fmb->ptr;
+ memmove(fmb->ptr, name, len);
+ tomoyo_fill_path_info(&ptr->entry);
+ fmb->ptr += len;
+ fmb->len -= len;
+ list_add_tail(&ptr->list, &tomoyo_name_list[hash % TOMOYO_MAX_HASH]);
+ if (fmb->len == 0) {
+ list_del(&fmb->list);
+ kfree(fmb);
+ }
+ out:
+ mutex_unlock(&lock);
+ /***** EXCLUSIVE SECTION END *****/
+ return ptr ? &ptr->entry : NULL;
+}
+
+/**
+ * tomoyo_realpath_init - Initialize realpath related code.
+ */
+void __init tomoyo_realpath_init(void)
+{
+ int i;
+
+ BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX);
+ for (i = 0; i < TOMOYO_MAX_HASH; i++)
+ INIT_LIST_HEAD(&tomoyo_name_list[i]);
+ INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
+ tomoyo_kernel_domain.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME);
+ list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
+ down_read(&tomoyo_domain_list_lock);
+ if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
+ panic("Can't register tomoyo_kernel_domain");
+ up_read(&tomoyo_domain_list_lock);
+}
+
+/* Memory allocated for temporary purpose. */
+static atomic_t tomoyo_dynamic_memory_size;
+
+/**
+ * tomoyo_alloc - Allocate memory for temporary purpose.
+ *
+ * @size: Size in bytes.
+ *
+ * Returns pointer to allocated memory on success, NULL otherwise.
+ */
+void *tomoyo_alloc(const size_t size)
+{
+ void *p = kzalloc(size, GFP_KERNEL);
+ if (p)
+ atomic_add(ksize(p), &tomoyo_dynamic_memory_size);
+ return p;
+}
+
+/**
+ * tomoyo_free - Release memory allocated by tomoyo_alloc().
+ *
+ * @p: Pointer returned by tomoyo_alloc(). May be NULL.
+ *
+ * Returns nothing.
+ */
+void tomoyo_free(const void *p)
+{
+ if (p) {
+ atomic_sub(ksize(p), &tomoyo_dynamic_memory_size);
+ kfree(p);
+ }
+}
+
+/**
+ * tomoyo_read_memory_counter - Check for memory usage in bytes.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns memory usage.
+ */
+int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head)
+{
+ if (!head->read_eof) {
+ const unsigned int shared
+ = tomoyo_allocated_memory_for_savename;
+ const unsigned int private
+ = tomoyo_allocated_memory_for_elements;
+ const unsigned int dynamic
+ = atomic_read(&tomoyo_dynamic_memory_size);
+ char buffer[64];
+
+ memset(buffer, 0, sizeof(buffer));
+ if (tomoyo_quota_for_savename)
+ snprintf(buffer, sizeof(buffer) - 1,
+ " (Quota: %10u)",
+ tomoyo_quota_for_savename);
+ else
+ buffer[0] = '\0';
+ tomoyo_io_printf(head, "Shared: %10u%s\n", shared, buffer);
+ if (tomoyo_quota_for_elements)
+ snprintf(buffer, sizeof(buffer) - 1,
+ " (Quota: %10u)",
+ tomoyo_quota_for_elements);
+ else
+ buffer[0] = '\0';
+ tomoyo_io_printf(head, "Private: %10u%s\n", private, buffer);
+ tomoyo_io_printf(head, "Dynamic: %10u\n", dynamic);
+ tomoyo_io_printf(head, "Total: %10u\n",
+ shared + private + dynamic);
+ head->read_eof = true;
+ }
+ return 0;
+}
+
+/**
+ * tomoyo_write_memory_quota - Set memory quota.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ unsigned int size;
+
+ if (sscanf(data, "Shared: %u", &size) == 1)
+ tomoyo_quota_for_savename = size;
+ else if (sscanf(data, "Private: %u", &size) == 1)
+ tomoyo_quota_for_elements = size;
+ return 0;
+}
diff --git a/security/tomoyo/realpath.h b/security/tomoyo/realpath.h
new file mode 100644
index 0000000..7ec9fc9c
--- /dev/null
+++ b/security/tomoyo/realpath.h
@@ -0,0 +1,66 @@
+/*
+ * security/tomoyo/realpath.h
+ *
+ * Get the canonicalized absolute pathnames. The basis for TOMOYO.
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre 2009/02/01
+ *
+ */
+
+#ifndef _SECURITY_TOMOYO_REALPATH_H
+#define _SECURITY_TOMOYO_REALPATH_H
+
+struct path;
+struct tomoyo_path_info;
+struct tomoyo_io_buffer;
+
+/* Convert binary string to ascii string. */
+int tomoyo_encode(char *buffer, int buflen, const char *str);
+
+/* Returns realpath(3) of the given pathname but ignores chroot'ed root. */
+int tomoyo_realpath_from_path2(struct path *path, char *newname,
+ int newname_len);
+
+/*
+ * Returns realpath(3) of the given pathname but ignores chroot'ed root.
+ * These functions use tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if these functions didn't return NULL.
+ */
+char *tomoyo_realpath(const char *pathname);
+/*
+ * Same with tomoyo_realpath() except that it doesn't follow the final symlink.
+ */
+char *tomoyo_realpath_nofollow(const char *pathname);
+/* Same with tomoyo_realpath() except that the pathname is already solved. */
+char *tomoyo_realpath_from_path(struct path *path);
+
+/*
+ * Allocate memory for ACL entry.
+ * The RAM is chunked, so NEVER try to kfree() the returned pointer.
+ */
+void *tomoyo_alloc_element(const unsigned int size);
+
+/*
+ * Keep the given name on the RAM.
+ * The RAM is shared, so NEVER try to modify or kfree() the returned name.
+ */
+const struct tomoyo_path_info *tomoyo_save_name(const char *name);
+
+/* Allocate memory for temporary use (e.g. permission checks). */
+void *tomoyo_alloc(const size_t size);
+
+/* Free memory allocated by tomoyo_alloc(). */
+void tomoyo_free(const void *p);
+
+/* Check for memory usage. */
+int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head);
+
+/* Set memory quota. */
+int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head);
+
+/* Initialize realpath related code. */
+void __init tomoyo_realpath_init(void);
+
+#endif /* !defined(_SECURITY_TOMOYO_REALPATH_H) */
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
new file mode 100644
index 0000000..3eeeae1
--- /dev/null
+++ b/security/tomoyo/tomoyo.c
@@ -0,0 +1,294 @@
+/*
+ * security/tomoyo/tomoyo.c
+ *
+ * LSM hooks for TOMOYO Linux.
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre 2009/02/01
+ *
+ */
+
+#include <linux/security.h>
+#include "common.h"
+#include "tomoyo.h"
+#include "realpath.h"
+
+static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
+ gfp_t gfp)
+{
+ /*
+ * Since "struct tomoyo_domain_info *" is a sharable pointer,
+ * we don't need to duplicate.
+ */
+ new->security = old->security;
+ return 0;
+}
+
+static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
+{
+ /*
+ * Do only if this function is called for the first time of an execve
+ * operation.
+ */
+ if (bprm->cred_prepared)
+ return 0;
+ /*
+ * Load policy if /sbin/tomoyo-init exists and /sbin/init is requested
+ * for the first time.
+ */
+ if (!tomoyo_policy_loaded)
+ tomoyo_load_policy(bprm->filename);
+ /*
+ * Tell tomoyo_bprm_check_security() is called for the first time of an
+ * execve operation.
+ */
+ bprm->cred->security = NULL;
+ return 0;
+}
+
+static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
+{
+ struct tomoyo_domain_info *domain = bprm->cred->security;
+
+ /*
+ * Execute permission is checked against pathname passed to do_execve()
+ * using current domain.
+ */
+ if (!domain) {
+ struct tomoyo_domain_info *next_domain = NULL;
+ int retval = tomoyo_find_next_domain(bprm, &next_domain);
+
+ if (!retval)
+ bprm->cred->security = next_domain;
+ return retval;
+ }
+ /*
+ * Read permission is checked against interpreters using next domain.
+ * '1' is the result of open_to_namei_flags(O_RDONLY).
+ */
+ return tomoyo_check_open_permission(domain, &bprm->file->f_path, 1);
+}
+
+#ifdef CONFIG_SYSCTL
+
+static int tomoyo_prepend(char **buffer, int *buflen, const char *str)
+{
+ int namelen = strlen(str);
+
+ if (*buflen < namelen)
+ return -ENOMEM;
+ *buflen -= namelen;
+ *buffer -= namelen;
+ memcpy(*buffer, str, namelen);
+ return 0;
+}
+
+/**
+ * tomoyo_sysctl_path - return the realpath of a ctl_table.
+ * @table: pointer to "struct ctl_table".
+ *
+ * Returns realpath(3) of the @table on success.
+ * Returns NULL on failure.
+ *
+ * This function uses tomoyo_alloc(), so the caller must call tomoyo_free()
+ * if this function didn't return NULL.
+ */
+static char *tomoyo_sysctl_path(struct ctl_table *table)
+{
+ int buflen = TOMOYO_MAX_PATHNAME_LEN;
+ char *buf = tomoyo_alloc(buflen);
+ char *end = buf + buflen;
+ int error = -ENOMEM;
+
+ if (!buf)
+ return NULL;
+
+ *--end = '\0';
+ buflen--;
+ while (table) {
+ char num[32];
+ const char *sp = table->procname;
+
+ if (!sp) {
+ memset(num, 0, sizeof(num));
+ snprintf(num, sizeof(num) - 1, "=%d=", table->ctl_name);
+ sp = num;
+ }
+ if (tomoyo_prepend(&end, &buflen, sp) ||
+ tomoyo_prepend(&end, &buflen, "/"))
+ goto out;
+ table = table->parent;
+ }
+ if (tomoyo_prepend(&end, &buflen, "/proc/sys"))
+ goto out;
+ error = tomoyo_encode(buf, end - buf, end);
+ out:
+ if (!error)
+ return buf;
+ tomoyo_free(buf);
+ return NULL;
+}
+
+static int tomoyo_sysctl(struct ctl_table *table, int op)
+{
+ int error;
+ char *name;
+
+ op &= MAY_READ | MAY_WRITE;
+ if (!op)
+ return 0;
+ name = tomoyo_sysctl_path(table);
+ if (!name)
+ return -ENOMEM;
+ error = tomoyo_check_file_perm(tomoyo_domain(), name, op);
+ tomoyo_free(name);
+ return error;
+}
+#endif
+
+static int tomoyo_path_truncate(struct path *path, loff_t length,
+ unsigned int time_attrs)
+{
+ return tomoyo_check_1path_perm(tomoyo_domain(),
+ TOMOYO_TYPE_TRUNCATE_ACL,
+ path);
+}
+
+static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry)
+{
+ struct path path = { parent->mnt, dentry };
+ return tomoyo_check_1path_perm(tomoyo_domain(),
+ TOMOYO_TYPE_UNLINK_ACL,
+ &path);
+}
+
+static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry,
+ int mode)
+{
+ struct path path = { parent->mnt, dentry };
+ return tomoyo_check_1path_perm(tomoyo_domain(),
+ TOMOYO_TYPE_MKDIR_ACL,
+ &path);
+}
+
+static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry)
+{
+ struct path path = { parent->mnt, dentry };
+ return tomoyo_check_1path_perm(tomoyo_domain(),
+ TOMOYO_TYPE_RMDIR_ACL,
+ &path);
+}
+
+static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry,
+ const char *old_name)
+{
+ struct path path = { parent->mnt, dentry };
+ return tomoyo_check_1path_perm(tomoyo_domain(),
+ TOMOYO_TYPE_SYMLINK_ACL,
+ &path);
+}
+
+static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,
+ int mode, unsigned int dev)
+{
+ struct path path = { parent->mnt, dentry };
+ int type = TOMOYO_TYPE_CREATE_ACL;
+
+ switch (mode & S_IFMT) {
+ case S_IFCHR:
+ type = TOMOYO_TYPE_MKCHAR_ACL;
+ break;
+ case S_IFBLK:
+ type = TOMOYO_TYPE_MKBLOCK_ACL;
+ break;
+ case S_IFIFO:
+ type = TOMOYO_TYPE_MKFIFO_ACL;
+ break;
+ case S_IFSOCK:
+ type = TOMOYO_TYPE_MKSOCK_ACL;
+ break;
+ }
+ return tomoyo_check_1path_perm(tomoyo_domain(),
+ type, &path);
+}
+
+static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
+ struct dentry *new_dentry)
+{
+ struct path path1 = { new_dir->mnt, old_dentry };
+ struct path path2 = { new_dir->mnt, new_dentry };
+ return tomoyo_check_2path_perm(tomoyo_domain(),
+ TOMOYO_TYPE_LINK_ACL,
+ &path1, &path2);
+}
+
+static int tomoyo_path_rename(struct path *old_parent,
+ struct dentry *old_dentry,
+ struct path *new_parent,
+ struct dentry *new_dentry)
+{
+ struct path path1 = { old_parent->mnt, old_dentry };
+ struct path path2 = { new_parent->mnt, new_dentry };
+ return tomoyo_check_2path_perm(tomoyo_domain(),
+ TOMOYO_TYPE_RENAME_ACL,
+ &path1, &path2);
+}
+
+static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))
+ return tomoyo_check_rewrite_permission(tomoyo_domain(), file);
+ return 0;
+}
+
+static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
+{
+ int flags = f->f_flags;
+
+ if ((flags + 1) & O_ACCMODE)
+ flags++;
+ flags |= f->f_flags & (O_APPEND | O_TRUNC);
+ /* Don't check read permission here if called from do_execve(). */
+ if (current->in_execve)
+ return 0;
+ return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
+}
+
+static struct security_operations tomoyo_security_ops = {
+ .name = "tomoyo",
+ .cred_prepare = tomoyo_cred_prepare,
+ .bprm_set_creds = tomoyo_bprm_set_creds,
+ .bprm_check_security = tomoyo_bprm_check_security,
+#ifdef CONFIG_SYSCTL
+ .sysctl = tomoyo_sysctl,
+#endif
+ .file_fcntl = tomoyo_file_fcntl,
+ .dentry_open = tomoyo_dentry_open,
+ .path_truncate = tomoyo_path_truncate,
+ .path_unlink = tomoyo_path_unlink,
+ .path_mkdir = tomoyo_path_mkdir,
+ .path_rmdir = tomoyo_path_rmdir,
+ .path_symlink = tomoyo_path_symlink,
+ .path_mknod = tomoyo_path_mknod,
+ .path_link = tomoyo_path_link,
+ .path_rename = tomoyo_path_rename,
+};
+
+static int __init tomoyo_init(void)
+{
+ struct cred *cred = (struct cred *) current_cred();
+
+ if (!security_module_enable(&tomoyo_security_ops))
+ return 0;
+ /* register ourselves with the security framework */
+ if (register_security(&tomoyo_security_ops))
+ panic("Failure registering TOMOYO Linux");
+ printk(KERN_INFO "TOMOYO Linux initialized\n");
+ cred->security = &tomoyo_kernel_domain;
+ tomoyo_realpath_init();
+ return 0;
+}
+
+security_initcall(tomoyo_init);
diff --git a/security/tomoyo/tomoyo.h b/security/tomoyo/tomoyo.h
new file mode 100644
index 0000000..a0c8f6e
--- /dev/null
+++ b/security/tomoyo/tomoyo.h
@@ -0,0 +1,106 @@
+/*
+ * security/tomoyo/tomoyo.h
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre 2009/02/01
+ *
+ */
+
+#ifndef _SECURITY_TOMOYO_TOMOYO_H
+#define _SECURITY_TOMOYO_TOMOYO_H
+
+struct tomoyo_path_info;
+struct path;
+struct inode;
+struct linux_binprm;
+struct pt_regs;
+struct tomoyo_page_buffer;
+
+int tomoyo_check_file_perm(struct tomoyo_domain_info *domain,
+ const char *filename, const u8 perm);
+int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
+ const struct tomoyo_path_info *filename,
+ struct tomoyo_page_buffer *buf);
+int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
+ struct path *path, const int flag);
+int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
+ const u8 operation, struct path *path);
+int tomoyo_check_2path_perm(struct tomoyo_domain_info *domain,
+ const u8 operation, struct path *path1,
+ struct path *path2);
+int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
+ struct file *filp);
+int tomoyo_find_next_domain(struct linux_binprm *bprm,
+ struct tomoyo_domain_info **next_domain);
+
+/* Index numbers for Access Controls. */
+
+#define TOMOYO_TYPE_SINGLE_PATH_ACL 0
+#define TOMOYO_TYPE_DOUBLE_PATH_ACL 1
+
+/* Index numbers for File Controls. */
+
+/*
+ * TYPE_READ_WRITE_ACL is special. TYPE_READ_WRITE_ACL is automatically set
+ * if both TYPE_READ_ACL and TYPE_WRITE_ACL are set. Both TYPE_READ_ACL and
+ * TYPE_WRITE_ACL are automatically set if TYPE_READ_WRITE_ACL is set.
+ * TYPE_READ_WRITE_ACL is automatically cleared if either TYPE_READ_ACL or
+ * TYPE_WRITE_ACL is cleared. Both TYPE_READ_ACL and TYPE_WRITE_ACL are
+ * automatically cleared if TYPE_READ_WRITE_ACL is cleared.
+ */
+
+#define TOMOYO_TYPE_READ_WRITE_ACL 0
+#define TOMOYO_TYPE_EXECUTE_ACL 1
+#define TOMOYO_TYPE_READ_ACL 2
+#define TOMOYO_TYPE_WRITE_ACL 3
+#define TOMOYO_TYPE_CREATE_ACL 4
+#define TOMOYO_TYPE_UNLINK_ACL 5
+#define TOMOYO_TYPE_MKDIR_ACL 6
+#define TOMOYO_TYPE_RMDIR_ACL 7
+#define TOMOYO_TYPE_MKFIFO_ACL 8
+#define TOMOYO_TYPE_MKSOCK_ACL 9
+#define TOMOYO_TYPE_MKBLOCK_ACL 10
+#define TOMOYO_TYPE_MKCHAR_ACL 11
+#define TOMOYO_TYPE_TRUNCATE_ACL 12
+#define TOMOYO_TYPE_SYMLINK_ACL 13
+#define TOMOYO_TYPE_REWRITE_ACL 14
+#define TOMOYO_MAX_SINGLE_PATH_OPERATION 15
+
+#define TOMOYO_TYPE_LINK_ACL 0
+#define TOMOYO_TYPE_RENAME_ACL 1
+#define TOMOYO_MAX_DOUBLE_PATH_OPERATION 2
+
+#define TOMOYO_DOMAINPOLICY 0
+#define TOMOYO_EXCEPTIONPOLICY 1
+#define TOMOYO_DOMAIN_STATUS 2
+#define TOMOYO_PROCESS_STATUS 3
+#define TOMOYO_MEMINFO 4
+#define TOMOYO_SELFDOMAIN 5
+#define TOMOYO_VERSION 6
+#define TOMOYO_PROFILE 7
+#define TOMOYO_MANAGER 8
+
+extern struct tomoyo_domain_info tomoyo_kernel_domain;
+
+static inline struct tomoyo_domain_info *tomoyo_domain(void)
+{
+ return current_cred()->security;
+}
+
+/* Caller holds tasklist_lock spinlock. */
+static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
+ *task)
+{
+ /***** CRITICAL SECTION START *****/
+ const struct cred *cred = get_task_cred(task);
+ struct tomoyo_domain_info *domain = cred->security;
+
+ put_cred(cred);
+ return domain;
+ /***** CRITICAL SECTION END *****/
+}
+
+#endif /* !defined(_SECURITY_TOMOYO_TOMOYO_H) */
diff --git a/sound/Kconfig b/sound/Kconfig
index 200aca1..1eceb85 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -60,6 +60,8 @@
source "sound/arm/Kconfig"
+source "sound/atmel/Kconfig"
+
source "sound/spi/Kconfig"
source "sound/mips/Kconfig"
diff --git a/sound/Makefile b/sound/Makefile
index c76d707..ec467de 100644
--- a/sound/Makefile
+++ b/sound/Makefile
@@ -6,7 +6,7 @@
obj-$(CONFIG_SOUND_PRIME) += oss/
obj-$(CONFIG_DMASOUND) += oss/
obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \
- sparc/ spi/ parisc/ pcmcia/ mips/ soc/
+ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/
obj-$(CONFIG_SND_AOA) += aoa/
# This one must be compilable even if sound is configured out
diff --git a/sound/aoa/aoa-gpio.h b/sound/aoa/aoa-gpio.h
index ee64f5d..6065b03 100644
--- a/sound/aoa/aoa-gpio.h
+++ b/sound/aoa/aoa-gpio.h
@@ -34,10 +34,12 @@
void (*set_headphone)(struct gpio_runtime *rt, int on);
void (*set_speakers)(struct gpio_runtime *rt, int on);
void (*set_lineout)(struct gpio_runtime *rt, int on);
+ void (*set_master)(struct gpio_runtime *rt, int on);
int (*get_headphone)(struct gpio_runtime *rt);
int (*get_speakers)(struct gpio_runtime *rt);
int (*get_lineout)(struct gpio_runtime *rt);
+ int (*get_master)(struct gpio_runtime *rt);
void (*set_hw_reset)(struct gpio_runtime *rt, int on);
diff --git a/sound/aoa/core/alsa.c b/sound/aoa/core/alsa.c
index 6178504..0fa3855 100644
--- a/sound/aoa/core/alsa.c
+++ b/sound/aoa/core/alsa.c
@@ -23,9 +23,10 @@
/* cannot be EEXIST due to usage in aoa_fabric_register */
return -EBUSY;
- alsa_card = snd_card_new(index, name, mod, sizeof(struct aoa_card));
- if (!alsa_card)
- return -ENOMEM;
+ err = snd_card_create(index, name, mod, sizeof(struct aoa_card),
+ &alsa_card);
+ if (err < 0)
+ return err;
aoa_card = alsa_card->private_data;
aoa_card->alsa_card = alsa_card;
alsa_card->dev = dev;
diff --git a/sound/aoa/core/gpio-feature.c b/sound/aoa/core/gpio-feature.c
index c93ad5d..de8e03a 100644
--- a/sound/aoa/core/gpio-feature.c
+++ b/sound/aoa/core/gpio-feature.c
@@ -14,7 +14,7 @@
#include <linux/interrupt.h>
#include "../aoa.h"
-/* TODO: these are 20 global variables
+/* TODO: these are lots of global variables
* that aren't used on most machines...
* Move them into a dynamically allocated
* structure and use that.
@@ -23,6 +23,7 @@
/* these are the GPIO numbers (register addresses as offsets into
* the GPIO space) */
static int headphone_mute_gpio;
+static int master_mute_gpio;
static int amp_mute_gpio;
static int lineout_mute_gpio;
static int hw_reset_gpio;
@@ -32,6 +33,7 @@
/* see the SWITCH_GPIO macro */
static int headphone_mute_gpio_activestate;
+static int master_mute_gpio_activestate;
static int amp_mute_gpio_activestate;
static int lineout_mute_gpio_activestate;
static int hw_reset_gpio_activestate;
@@ -156,6 +158,7 @@
FTR_GPIO(headphone, 0);
FTR_GPIO(amp, 1);
FTR_GPIO(lineout, 2);
+FTR_GPIO(master, 3);
static void ftr_gpio_set_hw_reset(struct gpio_runtime *rt, int on)
{
@@ -172,6 +175,8 @@
hw_reset_gpio, v);
}
+static struct gpio_methods methods;
+
static void ftr_gpio_all_amps_off(struct gpio_runtime *rt)
{
int saved;
@@ -181,6 +186,8 @@
ftr_gpio_set_headphone(rt, 0);
ftr_gpio_set_amp(rt, 0);
ftr_gpio_set_lineout(rt, 0);
+ if (methods.set_master)
+ ftr_gpio_set_master(rt, 0);
rt->implementation_private = saved;
}
@@ -193,6 +200,8 @@
ftr_gpio_set_headphone(rt, (s>>0)&1);
ftr_gpio_set_amp(rt, (s>>1)&1);
ftr_gpio_set_lineout(rt, (s>>2)&1);
+ if (methods.set_master)
+ ftr_gpio_set_master(rt, (s>>3)&1);
}
static void ftr_handle_notify(struct work_struct *work)
@@ -231,6 +240,12 @@
get_gpio("hw-reset", "audio-hw-reset",
&hw_reset_gpio,
&hw_reset_gpio_activestate);
+ if (get_gpio("master-mute", NULL,
+ &master_mute_gpio,
+ &master_mute_gpio_activestate)) {
+ methods.set_master = ftr_gpio_set_master;
+ methods.get_master = ftr_gpio_get_master;
+ }
headphone_detect_node = get_gpio("headphone-detect", NULL,
&headphone_detect_gpio,
diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c
index ad60f5d..fbf5c93 100644
--- a/sound/aoa/fabrics/layout.c
+++ b/sound/aoa/fabrics/layout.c
@@ -1,16 +1,14 @@
/*
- * Apple Onboard Audio driver -- layout fabric
+ * Apple Onboard Audio driver -- layout/machine id fabric
*
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2006-2008 Johannes Berg <johannes@sipsolutions.net>
*
* GPL v2, can be found in COPYING.
*
*
- * This fabric module looks for sound codecs
- * based on the layout-id property in the device tree.
- *
+ * This fabric module looks for sound codecs based on the
+ * layout-id or device-id property in the device tree.
*/
-
#include <asm/prom.h>
#include <linux/list.h>
#include <linux/module.h>
@@ -63,7 +61,7 @@
#define LAYOUT_FLAG_COMBO_LINEOUT_SPDIF (1<<0)
struct layout {
- unsigned int layout_id;
+ unsigned int layout_id, device_id;
struct codec_connect_info codecs[MAX_CODECS_PER_BUS];
int flags;
@@ -111,6 +109,10 @@
MODULE_ALIAS("sound-layout-98");
MODULE_ALIAS("sound-layout-100");
+MODULE_ALIAS("aoa-device-id-14");
+MODULE_ALIAS("aoa-device-id-22");
+MODULE_ALIAS("aoa-device-id-35");
+
/* onyx with all but microphone connected */
static struct codec_connection onyx_connections_nomic[] = {
{
@@ -518,6 +520,27 @@
.connections = onyx_connections_noheadphones,
},
},
+ /* PowerMac3,4 */
+ { .device_id = 14,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_noline,
+ },
+ },
+ /* PowerMac3,6 */
+ { .device_id = 22,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_all,
+ },
+ },
+ /* PowerBook5,2 */
+ { .device_id = 35,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_all,
+ },
+ },
{}
};
@@ -526,7 +549,7 @@
struct layout *l;
l = layouts;
- while (l->layout_id) {
+ while (l->codecs[0].name) {
if (l->layout_id == id)
return l;
l++;
@@ -534,6 +557,19 @@
return NULL;
}
+static struct layout *find_layout_by_device(unsigned int id)
+{
+ struct layout *l;
+
+ l = layouts;
+ while (l->codecs[0].name) {
+ if (l->device_id == id)
+ return l;
+ l++;
+ }
+ return NULL;
+}
+
static void use_layout(struct layout *l)
{
int i;
@@ -564,6 +600,7 @@
struct snd_kcontrol *headphone_ctrl;
struct snd_kcontrol *lineout_ctrl;
struct snd_kcontrol *speaker_ctrl;
+ struct snd_kcontrol *master_ctrl;
struct snd_kcontrol *headphone_detected_ctrl;
struct snd_kcontrol *lineout_detected_ctrl;
@@ -615,6 +652,7 @@
AMP_CONTROL(headphone, "Headphone Switch");
AMP_CONTROL(speakers, "Speakers Switch");
AMP_CONTROL(lineout, "Line-Out Switch");
+AMP_CONTROL(master, "Master Switch");
static int detect_choice_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -855,6 +893,11 @@
lineout = codec->gpio->methods->get_detect(codec->gpio,
AOA_NOTIFY_LINE_OUT);
+ if (codec->gpio->methods->set_master) {
+ ctl = snd_ctl_new1(&master_ctl, codec->gpio);
+ ldev->master_ctrl = ctl;
+ aoa_snd_ctl_add(ctl);
+ }
while (cc->connected) {
if (cc->connected & CC_SPEAKERS) {
if (headphones <= 0 && lineout <= 0)
@@ -938,8 +981,8 @@
static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
{
struct device_node *sound = NULL;
- const unsigned int *layout_id;
- struct layout *layout;
+ const unsigned int *id;
+ struct layout *layout = NULL;
struct layout_dev *ldev = NULL;
int err;
@@ -952,15 +995,18 @@
if (sound->type && strcasecmp(sound->type, "soundchip") == 0)
break;
}
- if (!sound) return -ENODEV;
+ if (!sound)
+ return -ENODEV;
- layout_id = of_get_property(sound, "layout-id", NULL);
- if (!layout_id)
- goto outnodev;
- printk(KERN_INFO "snd-aoa-fabric-layout: found bus with layout %d\n",
- *layout_id);
+ id = of_get_property(sound, "layout-id", NULL);
+ if (id) {
+ layout = find_layout_by_id(*id);
+ } else {
+ id = of_get_property(sound, "device-id", NULL);
+ if (id)
+ layout = find_layout_by_device(*id);
+ }
- layout = find_layout_by_id(*layout_id);
if (!layout) {
printk(KERN_ERR "snd-aoa-fabric-layout: unknown layout\n");
goto outnodev;
@@ -976,6 +1022,7 @@
ldev->layout = layout;
ldev->gpio.node = sound->parent;
switch (layout->layout_id) {
+ case 0: /* anything with device_id, not layout_id */
case 41: /* that unknown machine no one seems to have */
case 51: /* PowerBook5,4 */
case 58: /* Mac Mini */
diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c
index be468edf..418c84c 100644
--- a/sound/aoa/soundbus/i2sbus/core.c
+++ b/sound/aoa/soundbus/i2sbus/core.c
@@ -1,7 +1,7 @@
/*
* i2sbus driver
*
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2006-2008 Johannes Berg <johannes@sipsolutions.net>
*
* GPL v2, can be found in COPYING.
*/
@@ -186,13 +186,25 @@
}
}
if (i == 1) {
- const u32 *layout_id =
- of_get_property(sound, "layout-id", NULL);
- if (layout_id) {
- layout = *layout_id;
+ const u32 *id = of_get_property(sound, "layout-id", NULL);
+
+ if (id) {
+ layout = *id;
snprintf(dev->sound.modalias, 32,
"sound-layout-%d", layout);
ok = 1;
+ } else {
+ id = of_get_property(sound, "device-id", NULL);
+ /*
+ * We probably cannot handle all device-id machines,
+ * so restrict to those we do handle for now.
+ */
+ if (id && (*id == 22 || *id == 14 || *id == 35)) {
+ snprintf(dev->sound.modalias, 32,
+ "aoa-device-id-%d", *id);
+ ok = 1;
+ layout = -1;
+ }
}
}
/* for the time being, until we can handle non-layout-id
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index f8e6de4..885683a 100644
--- a/sound/arm/Kconfig
+++ b/sound/arm/Kconfig
@@ -11,17 +11,6 @@
if SND_ARM
-config SND_SA11XX_UDA1341
- tristate "SA11xx UDA1341TS driver (iPaq H3600)"
- depends on ARCH_SA1100 && L3
- select SND_PCM
- help
- Say Y here if you have a Compaq iPaq H3x00 handheld computer
- and want to use its Philips UDA 1341 audio chip.
-
- To compile this driver as a module, choose M here: the module
- will be called snd-sa11xx-uda1341.
-
config SND_ARMAACI
tristate "ARM PrimeCell PL041 AC Link support"
depends on ARM_AMBA
diff --git a/sound/arm/Makefile b/sound/arm/Makefile
index 2054de1..5a549ed 100644
--- a/sound/arm/Makefile
+++ b/sound/arm/Makefile
@@ -2,9 +2,6 @@
# Makefile for ALSA
#
-obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-sa11xx-uda1341.o
-snd-sa11xx-uda1341-objs := sa11xx-uda1341.o
-
obj-$(CONFIG_SND_ARMAACI) += snd-aaci.o
snd-aaci-objs := aaci.o devdma.o
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
index 772901e..7fbd68f 100644
--- a/sound/arm/aaci.c
+++ b/sound/arm/aaci.c
@@ -995,10 +995,11 @@
{
struct aaci *aaci;
struct snd_card *card;
+ int err;
- card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
- THIS_MODULE, sizeof(struct aaci));
- if (card == NULL)
+ err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, sizeof(struct aaci), &card);
+ if (err < 0)
return NULL;
card->private_free = aaci_free_card;
diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c
index 35afd0c..2e6355f 100644
--- a/sound/arm/pxa2xx-ac97-lib.c
+++ b/sound/arm/pxa2xx-ac97-lib.c
@@ -31,6 +31,7 @@
static volatile long gsr_bits;
static struct clk *ac97_clk;
static struct clk *ac97conf_clk;
+static int reset_gpio;
/*
* Beware PXA27x bugs:
@@ -42,6 +43,45 @@
* 1 jiffy timeout if interrupt never comes).
*/
+enum {
+ RESETGPIO_FORCE_HIGH,
+ RESETGPIO_FORCE_LOW,
+ RESETGPIO_NORMAL_ALTFUNC
+};
+
+/**
+ * set_resetgpio_mode - computes and sets the AC97_RESET gpio mode on PXA
+ * @mode: chosen action
+ *
+ * As the PXA27x CPUs suffer from a AC97 bug, a manual control of the reset line
+ * must be done to insure proper work of AC97 reset line. This function
+ * computes the correct gpio_mode for further use by reset functions, and
+ * applied the change through pxa_gpio_mode.
+ */
+static void set_resetgpio_mode(int resetgpio_action)
+{
+ int mode = 0;
+
+ if (reset_gpio)
+ switch (resetgpio_action) {
+ case RESETGPIO_NORMAL_ALTFUNC:
+ if (reset_gpio == 113)
+ mode = 113 | GPIO_OUT | GPIO_DFLT_LOW;
+ if (reset_gpio == 95)
+ mode = 95 | GPIO_ALT_FN_1_OUT;
+ break;
+ case RESETGPIO_FORCE_LOW:
+ mode = reset_gpio | GPIO_OUT | GPIO_DFLT_LOW;
+ break;
+ case RESETGPIO_FORCE_HIGH:
+ mode = reset_gpio | GPIO_OUT | GPIO_DFLT_HIGH;
+ break;
+ };
+
+ if (mode)
+ pxa_gpio_mode(mode);
+}
+
unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
unsigned short val = -1;
@@ -137,10 +177,10 @@
/* warm reset broken on Bulverde,
so manually keep AC97 reset high */
- pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH);
+ set_resetgpio_mode(RESETGPIO_FORCE_HIGH);
udelay(10);
GCR |= GCR_WARM_RST;
- pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
+ set_resetgpio_mode(RESETGPIO_NORMAL_ALTFUNC);
udelay(500);
}
@@ -308,8 +348,8 @@
pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
}
if (cpu_is_pxa27x()) {
- /* Use GPIO 113 as AC97 Reset on Bulverde */
- pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
+ /* Use GPIO 113 or 95 as AC97 Reset on Bulverde */
+ set_resetgpio_mode(RESETGPIO_NORMAL_ALTFUNC);
}
clk_enable(ac97_clk);
return 0;
@@ -320,6 +360,27 @@
int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev)
{
int ret;
+ struct pxa2xx_ac97_platform_data *pdata = dev->dev.platform_data;
+
+ if (pdata) {
+ switch (pdata->reset_gpio) {
+ case 95:
+ case 113:
+ reset_gpio = pdata->reset_gpio;
+ break;
+ case 0:
+ reset_gpio = 113;
+ break;
+ case -1:
+ break;
+ default:
+ dev_err(&dev->dev, "Invalid reset GPIO %d\n",
+ pdata->reset_gpio);
+ }
+ } else {
+ if (cpu_is_pxa27x())
+ reset_gpio = 113;
+ }
if (cpu_is_pxa25x() || cpu_is_pxa27x()) {
pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
@@ -330,7 +391,7 @@
if (cpu_is_pxa27x()) {
/* Use GPIO 113 as AC97 Reset on Bulverde */
- pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
+ set_resetgpio_mode(RESETGPIO_NORMAL_ALTFUNC);
ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK");
if (IS_ERR(ac97conf_clk)) {
ret = PTR_ERR(ac97conf_clk);
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index 85cf591..7ed100c 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -173,10 +173,9 @@
struct snd_ac97_template ac97_template;
int ret;
- ret = -ENOMEM;
- card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
- THIS_MODULE, 0);
- if (!card)
+ ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, 0, &card);
+ if (ret < 0)
goto err;
card->dev = &dev->dev;
diff --git a/sound/arm/sa11xx-uda1341.c b/sound/arm/sa11xx-uda1341.c
deleted file mode 100644
index 1dcd51d..0000000
--- a/sound/arm/sa11xx-uda1341.c
+++ /dev/null
@@ -1,983 +0,0 @@
-/*
- * Driver for Philips UDA1341TS on Compaq iPAQ H3600 soundcard
- * Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License.
- *
- * History:
- *
- * 2002-03-13 Tomas Kasparek initial release - based on h3600-uda1341.c from OSS
- * 2002-03-20 Tomas Kasparek playback over ALSA is working
- * 2002-03-28 Tomas Kasparek playback over OSS emulation is working
- * 2002-03-29 Tomas Kasparek basic capture is working (native ALSA)
- * 2002-03-29 Tomas Kasparek capture is working (OSS emulation)
- * 2002-04-04 Tomas Kasparek better rates handling (allow non-standard rates)
- * 2003-02-14 Brian Avery fixed full duplex mode, other updates
- * 2003-02-20 Tomas Kasparek merged updates by Brian (except HAL)
- * 2003-04-19 Jaroslav Kysela recoded DMA stuff to follow 2.4.18rmk3-hh24 kernel
- * working suspend and resume
- * 2003-04-28 Tomas Kasparek updated work by Jaroslav to compile it under 2.5.x again
- * merged HAL layer (patches from Brian)
- */
-
-/***************************************************************************************************
-*
-* To understand what Alsa Drivers should be doing look at "Writing an Alsa Driver" by Takashi Iwai
-* available in the Alsa doc section on the website
-*
-* A few notes to make things clearer. The UDA1341 is hooked up to Serial port 4 on the SA1100.
-* We are using SSP mode to talk to the UDA1341. The UDA1341 bit & wordselect clocks are generated
-* by this UART. Unfortunately, the clock only runs if the transmit buffer has something in it.
-* So, if we are just recording, we feed the transmit DMA stream a bunch of 0x0000 so that the
-* transmit buffer is full and the clock keeps going. The zeroes come from FLUSH_BASE_PHYS which
-* is a mem loc that always decodes to 0's w/ no off chip access.
-*
-* Some alsa terminology:
-* frame => num_channels * sample_size e.g stereo 16 bit is 2 * 16 = 32 bytes
-* period => the least number of bytes that will generate an interrupt e.g. we have a 1024 byte
-* buffer and 4 periods in the runtime structure this means we'll get an int every 256
-* bytes or 4 times per buffer.
-* A number of the sizes are in frames rather than bytes, use frames_to_bytes and
-* bytes_to_frames to convert. The easiest way to tell the units is to look at the
-* type i.e. runtime-> buffer_size is in frames and its type is snd_pcm_uframes_t
-*
-* Notes about the pointer fxn:
-* The pointer fxn needs to return the offset into the dma buffer in frames.
-* Interrupts must be blocked before calling the dma_get_pos fxn to avoid race with interrupts.
-*
-* Notes about pause/resume
-* Implementing this would be complicated so it's skipped. The problem case is:
-* A full duplex connection is going, then play is paused. At this point you need to start xmitting
-* 0's to keep the record active which means you cant just freeze the dma and resume it later you'd
-* need to save off the dma info, and restore it properly on a resume. Yeach!
-*
-* Notes about transfer methods:
-* The async write calls fail. I probably need to implement something else to support them?
-*
-***************************************************************************************************/
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/platform_device.h>
-#include <linux/errno.h>
-#include <linux/ioctl.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-
-#ifdef CONFIG_PM
-#include <linux/pm.h>
-#endif
-
-#include <mach/hardware.h>
-#include <mach/h3600.h>
-#include <asm/mach-types.h>
-#include <asm/dma.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/initval.h>
-
-#include <linux/l3/l3.h>
-
-#undef DEBUG_MODE
-#undef DEBUG_FUNCTION_NAMES
-#include <sound/uda1341.h>
-
-/*
- * FIXME: Is this enough as autodetection of 2.4.X-rmkY-hhZ kernels?
- * We use DMA stuff from 2.4.18-rmk3-hh24 here to be able to compile this
- * module for Familiar 0.6.1
- */
-
-/* {{{ Type definitions */
-
-MODULE_AUTHOR("Tomas Kasparek <tomas.kasparek@seznam.cz>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SA1100/SA1111 + UDA1341TS driver for ALSA");
-MODULE_SUPPORTED_DEVICE("{{UDA1341,iPAQ H3600 UDA1341TS}}");
-
-static char *id; /* ID for this card */
-
-module_param(id, charp, 0444);
-MODULE_PARM_DESC(id, "ID string for SA1100/SA1111 + UDA1341TS soundcard.");
-
-struct audio_stream {
- char *id; /* identification string */
- int stream_id; /* numeric identification */
- dma_device_t dma_dev; /* device identifier for DMA */
-#ifdef HH_VERSION
- dmach_t dmach; /* dma channel identification */
-#else
- dma_regs_t *dma_regs; /* points to our DMA registers */
-#endif
- unsigned int active:1; /* we are using this stream for transfer now */
- int period; /* current transfer period */
- int periods; /* current count of periods registerd in the DMA engine */
- int tx_spin; /* are we recoding - flag used to do DMA trans. for sync */
- unsigned int old_offset;
- spinlock_t dma_lock; /* for locking in DMA operations (see dma-sa1100.c in the kernel) */
- struct snd_pcm_substream *stream;
-};
-
-struct sa11xx_uda1341 {
- struct snd_card *card;
- struct l3_client *uda1341;
- struct snd_pcm *pcm;
- long samplerate;
- struct audio_stream s[2]; /* playback & capture */
-};
-
-static unsigned int rates[] = {
- 8000, 10666, 10985, 14647,
- 16000, 21970, 22050, 24000,
- 29400, 32000, 44100, 48000,
-};
-
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
- .count = ARRAY_SIZE(rates),
- .list = rates,
- .mask = 0,
-};
-
-static struct platform_device *device;
-
-/* }}} */
-
-/* {{{ Clock and sample rate stuff */
-
-/*
- * Stop-gap solution until rest of hh.org HAL stuff is merged.
- */
-#define GPIO_H3600_CLK_SET0 GPIO_GPIO (12)
-#define GPIO_H3600_CLK_SET1 GPIO_GPIO (13)
-
-#ifdef CONFIG_SA1100_H3XXX
-#define clr_sa11xx_uda1341_egpio(x) clr_h3600_egpio(x)
-#define set_sa11xx_uda1341_egpio(x) set_h3600_egpio(x)
-#else
-#error This driver could serve H3x00 handhelds only!
-#endif
-
-static void sa11xx_uda1341_set_audio_clock(long val)
-{
- switch (val) {
- case 24000: case 32000: case 48000: /* 00: 12.288 MHz */
- GPCR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1;
- break;
-
- case 22050: case 29400: case 44100: /* 01: 11.2896 MHz */
- GPSR = GPIO_H3600_CLK_SET0;
- GPCR = GPIO_H3600_CLK_SET1;
- break;
-
- case 8000: case 10666: case 16000: /* 10: 4.096 MHz */
- GPCR = GPIO_H3600_CLK_SET0;
- GPSR = GPIO_H3600_CLK_SET1;
- break;
-
- case 10985: case 14647: case 21970: /* 11: 5.6245 MHz */
- GPSR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1;
- break;
- }
-}
-
-static void sa11xx_uda1341_set_samplerate(struct sa11xx_uda1341 *sa11xx_uda1341, long rate)
-{
- int clk_div = 0;
- int clk=0;
-
- /* We don't want to mess with clocks when frames are in flight */
- Ser4SSCR0 &= ~SSCR0_SSE;
- /* wait for any frame to complete */
- udelay(125);
-
- /*
- * We have the following clock sources:
- * 4.096 MHz, 5.6245 MHz, 11.2896 MHz, 12.288 MHz
- * Those can be divided either by 256, 384 or 512.
- * This makes up 12 combinations for the following samplerates...
- */
- if (rate >= 48000)
- rate = 48000;
- else if (rate >= 44100)
- rate = 44100;
- else if (rate >= 32000)
- rate = 32000;
- else if (rate >= 29400)
- rate = 29400;
- else if (rate >= 24000)
- rate = 24000;
- else if (rate >= 22050)
- rate = 22050;
- else if (rate >= 21970)
- rate = 21970;
- else if (rate >= 16000)
- rate = 16000;
- else if (rate >= 14647)
- rate = 14647;
- else if (rate >= 10985)
- rate = 10985;
- else if (rate >= 10666)
- rate = 10666;
- else
- rate = 8000;
-
- /* Set the external clock generator */
-
- sa11xx_uda1341_set_audio_clock(rate);
-
- /* Select the clock divisor */
- switch (rate) {
- case 8000:
- case 10985:
- case 22050:
- case 24000:
- clk = F512;
- clk_div = SSCR0_SerClkDiv(16);
- break;
- case 16000:
- case 21970:
- case 44100:
- case 48000:
- clk = F256;
- clk_div = SSCR0_SerClkDiv(8);
- break;
- case 10666:
- case 14647:
- case 29400:
- case 32000:
- clk = F384;
- clk_div = SSCR0_SerClkDiv(12);
- break;
- }
-
- /* FMT setting should be moved away when other FMTs are added (FIXME) */
- l3_command(sa11xx_uda1341->uda1341, CMD_FORMAT, (void *)LSB16);
-
- l3_command(sa11xx_uda1341->uda1341, CMD_FS, (void *)clk);
- Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE;
- sa11xx_uda1341->samplerate = rate;
-}
-
-/* }}} */
-
-/* {{{ HW init and shutdown */
-
-static void sa11xx_uda1341_audio_init(struct sa11xx_uda1341 *sa11xx_uda1341)
-{
- unsigned long flags;
-
- /* Setup DMA stuff */
- sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].id = "UDA1341 out";
- sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id = SNDRV_PCM_STREAM_PLAYBACK;
- sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev = DMA_Ser4SSPWr;
-
- sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].id = "UDA1341 in";
- sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].stream_id = SNDRV_PCM_STREAM_CAPTURE;
- sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev = DMA_Ser4SSPRd;
-
- /* Initialize the UDA1341 internal state */
-
- /* Setup the uarts */
- local_irq_save(flags);
- GAFR |= (GPIO_SSP_CLK);
- GPDR &= ~(GPIO_SSP_CLK);
- Ser4SSCR0 = 0;
- Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(8);
- Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk;
- Ser4SSCR0 |= SSCR0_SSE;
- local_irq_restore(flags);
-
- /* Enable the audio power */
-
- clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
- set_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON);
- set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
-
- /* Wait for the UDA1341 to wake up */
- mdelay(1); //FIXME - was removed by Perex - Why?
-
- /* Initialize the UDA1341 internal state */
- l3_open(sa11xx_uda1341->uda1341);
-
- /* external clock configuration (after l3_open - regs must be initialized */
- sa11xx_uda1341_set_samplerate(sa11xx_uda1341, sa11xx_uda1341->samplerate);
-
- /* Wait for the UDA1341 to wake up */
- set_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
- mdelay(1);
-
- /* make the left and right channels unswapped (flip the WS latch) */
- Ser4SSDR = 0;
-
- clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
-}
-
-static void sa11xx_uda1341_audio_shutdown(struct sa11xx_uda1341 *sa11xx_uda1341)
-{
- /* mute on */
- set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
-
- /* disable the audio power and all signals leading to the audio chip */
- l3_close(sa11xx_uda1341->uda1341);
- Ser4SSCR0 = 0;
- clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
-
- /* power off and mute off */
- /* FIXME - is muting off necesary??? */
-
- clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON);
- clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
-}
-
-/* }}} */
-
-/* {{{ DMA staff */
-
-/*
- * these are the address and sizes used to fill the xmit buffer
- * so we can get a clock in record only mode
- */
-#define FORCE_CLOCK_ADDR (dma_addr_t)FLUSH_BASE_PHYS
-#define FORCE_CLOCK_SIZE 4096 // was 2048
-
-// FIXME Why this value exactly - wrote comment
-#define DMA_BUF_SIZE 8176 /* <= MAX_DMA_SIZE from asm/arch-sa1100/dma.h */
-
-#ifdef HH_VERSION
-
-static int audio_dma_request(struct audio_stream *s, void (*callback)(void *, int))
-{
- int ret;
-
- ret = sa1100_request_dma(&s->dmach, s->id, s->dma_dev);
- if (ret < 0) {
- printk(KERN_ERR "unable to grab audio dma 0x%x\n", s->dma_dev);
- return ret;
- }
- sa1100_dma_set_callback(s->dmach, callback);
- return 0;
-}
-
-static inline void audio_dma_free(struct audio_stream *s)
-{
- sa1100_free_dma(s->dmach);
- s->dmach = -1;
-}
-
-#else
-
-static int audio_dma_request(struct audio_stream *s, void (*callback)(void *))
-{
- int ret;
-
- ret = sa1100_request_dma(s->dma_dev, s->id, callback, s, &s->dma_regs);
- if (ret < 0)
- printk(KERN_ERR "unable to grab audio dma 0x%x\n", s->dma_dev);
- return ret;
-}
-
-static void audio_dma_free(struct audio_stream *s)
-{
- sa1100_free_dma(s->dma_regs);
- s->dma_regs = 0;
-}
-
-#endif
-
-static u_int audio_get_dma_pos(struct audio_stream *s)
-{
- struct snd_pcm_substream *substream = s->stream;
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int offset;
- unsigned long flags;
- dma_addr_t addr;
-
- // this must be called w/ interrupts locked out see dma-sa1100.c in the kernel
- spin_lock_irqsave(&s->dma_lock, flags);
-#ifdef HH_VERSION
- sa1100_dma_get_current(s->dmach, NULL, &addr);
-#else
- addr = sa1100_get_dma_pos((s)->dma_regs);
-#endif
- offset = addr - runtime->dma_addr;
- spin_unlock_irqrestore(&s->dma_lock, flags);
-
- offset = bytes_to_frames(runtime,offset);
- if (offset >= runtime->buffer_size)
- offset = 0;
-
- return offset;
-}
-
-/*
- * this stops the dma and clears the dma ptrs
- */
-static void audio_stop_dma(struct audio_stream *s)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&s->dma_lock, flags);
- s->active = 0;
- s->period = 0;
- /* this stops the dma channel and clears the buffer ptrs */
-#ifdef HH_VERSION
- sa1100_dma_flush_all(s->dmach);
-#else
- sa1100_clear_dma(s->dma_regs);
-#endif
- spin_unlock_irqrestore(&s->dma_lock, flags);
-}
-
-static void audio_process_dma(struct audio_stream *s)
-{
- struct snd_pcm_substream *substream = s->stream;
- struct snd_pcm_runtime *runtime;
- unsigned int dma_size;
- unsigned int offset;
- int ret;
-
- /* we are requested to process synchronization DMA transfer */
- if (s->tx_spin) {
- if (snd_BUG_ON(s->stream_id != SNDRV_PCM_STREAM_PLAYBACK))
- return;
- /* fill the xmit dma buffers and return */
-#ifdef HH_VERSION
- sa1100_dma_set_spin(s->dmach, FORCE_CLOCK_ADDR, FORCE_CLOCK_SIZE);
-#else
- while (1) {
- ret = sa1100_start_dma(s->dma_regs, FORCE_CLOCK_ADDR, FORCE_CLOCK_SIZE);
- if (ret)
- return;
- }
-#endif
- return;
- }
-
- /* must be set here - only valid for running streams, not for forced_clock dma fills */
- runtime = substream->runtime;
- while (s->active && s->periods < runtime->periods) {
- dma_size = frames_to_bytes(runtime, runtime->period_size);
- if (s->old_offset) {
- /* a little trick, we need resume from old position */
- offset = frames_to_bytes(runtime, s->old_offset - 1);
- s->old_offset = 0;
- s->periods = 0;
- s->period = offset / dma_size;
- offset %= dma_size;
- dma_size = dma_size - offset;
- if (!dma_size)
- continue; /* special case */
- } else {
- offset = dma_size * s->period;
- snd_BUG_ON(dma_size > DMA_BUF_SIZE);
- }
-#ifdef HH_VERSION
- ret = sa1100_dma_queue_buffer(s->dmach, s, runtime->dma_addr + offset, dma_size);
- if (ret)
- return; //FIXME
-#else
- ret = sa1100_start_dma((s)->dma_regs, runtime->dma_addr + offset, dma_size);
- if (ret) {
- printk(KERN_ERR "audio_process_dma: cannot queue DMA buffer (%i)\n", ret);
- return;
- }
-#endif
-
- s->period++;
- s->period %= runtime->periods;
- s->periods++;
- }
-}
-
-#ifdef HH_VERSION
-static void audio_dma_callback(void *data, int size)
-#else
-static void audio_dma_callback(void *data)
-#endif
-{
- struct audio_stream *s = data;
-
- /*
- * If we are getting a callback for an active stream then we inform
- * the PCM middle layer we've finished a period
- */
- if (s->active)
- snd_pcm_period_elapsed(s->stream);
-
- spin_lock(&s->dma_lock);
- if (!s->tx_spin && s->periods > 0)
- s->periods--;
- audio_process_dma(s);
- spin_unlock(&s->dma_lock);
-}
-
-/* }}} */
-
-/* {{{ PCM setting */
-
-/* {{{ trigger & timer */
-
-static int snd_sa11xx_uda1341_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct sa11xx_uda1341 *chip = snd_pcm_substream_chip(substream);
- int stream_id = substream->pstr->stream;
- struct audio_stream *s = &chip->s[stream_id];
- struct audio_stream *s1 = &chip->s[stream_id ^ 1];
- int err = 0;
-
- /* note local interrupts are already disabled in the midlevel code */
- spin_lock(&s->dma_lock);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- /* now we need to make sure a record only stream has a clock */
- if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) {
- /* we need to force fill the xmit DMA with zeros */
- s1->tx_spin = 1;
- audio_process_dma(s1);
- }
- /* this case is when you were recording then you turn on a
- * playback stream so we stop (also clears it) the dma first,
- * clear the sync flag and then we let it turned on
- */
- else {
- s->tx_spin = 0;
- }
-
- /* requested stream startup */
- s->active = 1;
- audio_process_dma(s);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- /* requested stream shutdown */
- audio_stop_dma(s);
-
- /*
- * now we need to make sure a record only stream has a clock
- * so if we're stopping a playback with an active capture
- * we need to turn the 0 fill dma on for the xmit side
- */
- if (stream_id == SNDRV_PCM_STREAM_PLAYBACK && s1->active) {
- /* we need to force fill the xmit DMA with zeros */
- s->tx_spin = 1;
- audio_process_dma(s);
- }
- /*
- * we killed a capture only stream, so we should also kill
- * the zero fill transmit
- */
- else {
- if (s1->tx_spin) {
- s1->tx_spin = 0;
- audio_stop_dma(s1);
- }
- }
-
- break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- s->active = 0;
-#ifdef HH_VERSION
- sa1100_dma_stop(s->dmach);
-#else
- //FIXME - DMA API
-#endif
- s->old_offset = audio_get_dma_pos(s) + 1;
-#ifdef HH_VERSION
- sa1100_dma_flush_all(s->dmach);
-#else
- //FIXME - DMA API
-#endif
- s->periods = 0;
- break;
- case SNDRV_PCM_TRIGGER_RESUME:
- s->active = 1;
- s->tx_spin = 0;
- audio_process_dma(s);
- if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) {
- s1->tx_spin = 1;
- audio_process_dma(s1);
- }
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-#ifdef HH_VERSION
- sa1100_dma_stop(s->dmach);
-#else
- //FIXME - DMA API
-#endif
- s->active = 0;
- if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
- if (s1->active) {
- s->tx_spin = 1;
- s->old_offset = audio_get_dma_pos(s) + 1;
-#ifdef HH_VERSION
- sa1100_dma_flush_all(s->dmach);
-#else
- //FIXME - DMA API
-#endif
- audio_process_dma(s);
- }
- } else {
- if (s1->tx_spin) {
- s1->tx_spin = 0;
-#ifdef HH_VERSION
- sa1100_dma_flush_all(s1->dmach);
-#else
- //FIXME - DMA API
-#endif
- }
- }
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- s->active = 1;
- if (s->old_offset) {
- s->tx_spin = 0;
- audio_process_dma(s);
- break;
- }
- if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) {
- s1->tx_spin = 1;
- audio_process_dma(s1);
- }
-#ifdef HH_VERSION
- sa1100_dma_resume(s->dmach);
-#else
- //FIXME - DMA API
-#endif
- break;
- default:
- err = -EINVAL;
- break;
- }
- spin_unlock(&s->dma_lock);
- return err;
-}
-
-static int snd_sa11xx_uda1341_prepare(struct snd_pcm_substream *substream)
-{
- struct sa11xx_uda1341 *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct audio_stream *s = &chip->s[substream->pstr->stream];
-
- /* set requested samplerate */
- sa11xx_uda1341_set_samplerate(chip, runtime->rate);
-
- /* set requestd format when available */
- /* set FMT here !!! FIXME */
-
- s->period = 0;
- s->periods = 0;
-
- return 0;
-}
-
-static snd_pcm_uframes_t snd_sa11xx_uda1341_pointer(struct snd_pcm_substream *substream)
-{
- struct sa11xx_uda1341 *chip = snd_pcm_substream_chip(substream);
- return audio_get_dma_pos(&chip->s[substream->pstr->stream]);
-}
-
-/* }}} */
-
-static struct snd_pcm_hardware snd_sa11xx_uda1341_capture =
-{
- .info = (SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
- SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
- SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
- SNDRV_PCM_RATE_KNOT),
- .rate_min = 8000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = 64*1024,
- .period_bytes_min = 64,
- .period_bytes_max = DMA_BUF_SIZE,
- .periods_min = 2,
- .periods_max = 255,
- .fifo_size = 0,
-};
-
-static struct snd_pcm_hardware snd_sa11xx_uda1341_playback =
-{
- .info = (SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
- SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
- SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
- SNDRV_PCM_RATE_KNOT),
- .rate_min = 8000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = 64*1024,
- .period_bytes_min = 64,
- .period_bytes_max = DMA_BUF_SIZE,
- .periods_min = 2,
- .periods_max = 255,
- .fifo_size = 0,
-};
-
-static int snd_card_sa11xx_uda1341_open(struct snd_pcm_substream *substream)
-{
- struct sa11xx_uda1341 *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- int stream_id = substream->pstr->stream;
- int err;
-
- chip->s[stream_id].stream = substream;
-
- if (stream_id == SNDRV_PCM_STREAM_PLAYBACK)
- runtime->hw = snd_sa11xx_uda1341_playback;
- else
- runtime->hw = snd_sa11xx_uda1341_capture;
- if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
- return err;
- if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates)) < 0)
- return err;
-
- return 0;
-}
-
-static int snd_card_sa11xx_uda1341_close(struct snd_pcm_substream *substream)
-{
- struct sa11xx_uda1341 *chip = snd_pcm_substream_chip(substream);
-
- chip->s[substream->pstr->stream].stream = NULL;
- return 0;
-}
-
-/* {{{ HW params & free */
-
-static int snd_sa11xx_uda1341_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
-
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_sa11xx_uda1341_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-/* }}} */
-
-static struct snd_pcm_ops snd_card_sa11xx_uda1341_playback_ops = {
- .open = snd_card_sa11xx_uda1341_open,
- .close = snd_card_sa11xx_uda1341_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sa11xx_uda1341_hw_params,
- .hw_free = snd_sa11xx_uda1341_hw_free,
- .prepare = snd_sa11xx_uda1341_prepare,
- .trigger = snd_sa11xx_uda1341_trigger,
- .pointer = snd_sa11xx_uda1341_pointer,
-};
-
-static struct snd_pcm_ops snd_card_sa11xx_uda1341_capture_ops = {
- .open = snd_card_sa11xx_uda1341_open,
- .close = snd_card_sa11xx_uda1341_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sa11xx_uda1341_hw_params,
- .hw_free = snd_sa11xx_uda1341_hw_free,
- .prepare = snd_sa11xx_uda1341_prepare,
- .trigger = snd_sa11xx_uda1341_trigger,
- .pointer = snd_sa11xx_uda1341_pointer,
-};
-
-static int __init snd_card_sa11xx_uda1341_pcm(struct sa11xx_uda1341 *sa11xx_uda1341, int device)
-{
- struct snd_pcm *pcm;
- int err;
-
- if ((err = snd_pcm_new(sa11xx_uda1341->card, "UDA1341 PCM", device, 1, 1, &pcm)) < 0)
- return err;
-
- /*
- * this sets up our initial buffers and sets the dma_type to isa.
- * isa works but I'm not sure why (or if) it's the right choice
- * this may be too large, trying it for now
- */
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024, 64*1024);
-
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_sa11xx_uda1341_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_sa11xx_uda1341_capture_ops);
- pcm->private_data = sa11xx_uda1341;
- pcm->info_flags = 0;
- strcpy(pcm->name, "UDA1341 PCM");
-
- sa11xx_uda1341_audio_init(sa11xx_uda1341);
-
- /* setup DMA controller */
- audio_dma_request(&sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK], audio_dma_callback);
- audio_dma_request(&sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE], audio_dma_callback);
-
- sa11xx_uda1341->pcm = pcm;
-
- return 0;
-}
-
-/* }}} */
-
-/* {{{ module init & exit */
-
-#ifdef CONFIG_PM
-
-static int snd_sa11xx_uda1341_suspend(struct platform_device *devptr,
- pm_message_t state)
-{
- struct snd_card *card = platform_get_drvdata(devptr);
- struct sa11xx_uda1341 *chip = card->private_data;
-
- snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
-#ifdef HH_VERSION
- sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach);
- sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach);
-#else
- //FIXME
-#endif
- l3_command(chip->uda1341, CMD_SUSPEND, NULL);
- sa11xx_uda1341_audio_shutdown(chip);
-
- return 0;
-}
-
-static int snd_sa11xx_uda1341_resume(struct platform_device *devptr)
-{
- struct snd_card *card = platform_get_drvdata(devptr);
- struct sa11xx_uda1341 *chip = card->private_data;
-
- sa11xx_uda1341_audio_init(chip);
- l3_command(chip->uda1341, CMD_RESUME, NULL);
-#ifdef HH_VERSION
- sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach);
- sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach);
-#else
- //FIXME
-#endif
- snd_power_change_state(card, SNDRV_CTL_POWER_D0);
- return 0;
-}
-#endif /* COMFIG_PM */
-
-void snd_sa11xx_uda1341_free(struct snd_card *card)
-{
- struct sa11xx_uda1341 *chip = card->private_data;
-
- audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]);
- audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]);
-}
-
-static int __devinit sa11xx_uda1341_probe(struct platform_device *devptr)
-{
- int err;
- struct snd_card *card;
- struct sa11xx_uda1341 *chip;
-
- /* register the soundcard */
- card = snd_card_new(-1, id, THIS_MODULE, sizeof(struct sa11xx_uda1341));
- if (card == NULL)
- return -ENOMEM;
-
- chip = card->private_data;
- spin_lock_init(&chip->s[0].dma_lock);
- spin_lock_init(&chip->s[1].dma_lock);
-
- card->private_free = snd_sa11xx_uda1341_free;
- chip->card = card;
- chip->samplerate = AUDIO_RATE_DEFAULT;
-
- // mixer
- if ((err = snd_chip_uda1341_mixer_new(card, &chip->uda1341)))
- goto nodev;
-
- // PCM
- if ((err = snd_card_sa11xx_uda1341_pcm(chip, 0)) < 0)
- goto nodev;
-
- strcpy(card->driver, "UDA1341");
- strcpy(card->shortname, "H3600 UDA1341TS");
- sprintf(card->longname, "Compaq iPAQ H3600 with Philips UDA1341TS");
-
- snd_card_set_dev(card, &devptr->dev);
-
- if ((err = snd_card_register(card)) == 0) {
- printk( KERN_INFO "iPAQ audio support initialized\n" );
- platform_set_drvdata(devptr, card);
- return 0;
- }
-
- nodev:
- snd_card_free(card);
- return err;
-}
-
-static int __devexit sa11xx_uda1341_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
- return 0;
-}
-
-#define SA11XX_UDA1341_DRIVER "sa11xx_uda1341"
-
-static struct platform_driver sa11xx_uda1341_driver = {
- .probe = sa11xx_uda1341_probe,
- .remove = __devexit_p(sa11xx_uda1341_remove),
-#ifdef CONFIG_PM
- .suspend = snd_sa11xx_uda1341_suspend,
- .resume = snd_sa11xx_uda1341_resume,
-#endif
- .driver = {
- .name = SA11XX_UDA1341_DRIVER,
- },
-};
-
-static int __init sa11xx_uda1341_init(void)
-{
- int err;
-
- if (!machine_is_h3xxx())
- return -ENODEV;
- if ((err = platform_driver_register(&sa11xx_uda1341_driver)) < 0)
- return err;
- device = platform_device_register_simple(SA11XX_UDA1341_DRIVER, -1, NULL, 0);
- if (!IS_ERR(device)) {
- if (platform_get_drvdata(device))
- return 0;
- platform_device_unregister(device);
- err = -ENODEV;
- } else
- err = PTR_ERR(device);
- platform_driver_unregister(&sa11xx_uda1341_driver);
- return err;
-}
-
-static void __exit sa11xx_uda1341_exit(void)
-{
- platform_device_unregister(device);
- platform_driver_unregister(&sa11xx_uda1341_driver);
-}
-
-module_init(sa11xx_uda1341_init);
-module_exit(sa11xx_uda1341_exit);
-
-/* }}} */
-
-/*
- * Local variables:
- * indent-tabs-mode: t
- * End:
- */
diff --git a/sound/atmel/Kconfig b/sound/atmel/Kconfig
new file mode 100644
index 0000000..6c228a9
--- /dev/null
+++ b/sound/atmel/Kconfig
@@ -0,0 +1,19 @@
+menu "Atmel devices (AVR32 and AT91)"
+ depends on AVR32 || ARCH_AT91
+
+config SND_ATMEL_ABDAC
+ tristate "Atmel Audio Bitstream DAC (ABDAC) driver"
+ select SND_PCM
+ depends on DW_DMAC && AVR32
+ help
+ ALSA sound driver for the Atmel Audio Bitstream DAC (ABDAC).
+
+config SND_ATMEL_AC97C
+ tristate "Atmel AC97 Controller (AC97C) driver"
+ select SND_PCM
+ select SND_AC97_CODEC
+ depends on DW_DMAC && AVR32
+ help
+ ALSA sound driver for the Atmel AC97 controller.
+
+endmenu
diff --git a/sound/atmel/Makefile b/sound/atmel/Makefile
new file mode 100644
index 0000000..219dcfa
--- /dev/null
+++ b/sound/atmel/Makefile
@@ -0,0 +1,5 @@
+snd-atmel-abdac-objs := abdac.o
+snd-atmel-ac97c-objs := ac97c.o
+
+obj-$(CONFIG_SND_ATMEL_ABDAC) += snd-atmel-abdac.o
+obj-$(CONFIG_SND_ATMEL_AC97C) += snd-atmel-ac97c.o
diff --git a/sound/atmel/abdac.c b/sound/atmel/abdac.c
new file mode 100644
index 0000000..28b3c7f
--- /dev/null
+++ b/sound/atmel/abdac.c
@@ -0,0 +1,602 @@
+/*
+ * Driver for the Atmel on-chip Audio Bitstream DAC (ABDAC)
+ *
+ * Copyright (C) 2006-2009 Atmel Corporation
+ *
+ * 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/clk.h>
+#include <linux/bitmap.h>
+#include <linux/dw_dmac.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/atmel-abdac.h>
+
+/* DAC register offsets */
+#define DAC_DATA 0x0000
+#define DAC_CTRL 0x0008
+#define DAC_INT_MASK 0x000c
+#define DAC_INT_EN 0x0010
+#define DAC_INT_DIS 0x0014
+#define DAC_INT_CLR 0x0018
+#define DAC_INT_STATUS 0x001c
+
+/* Bitfields in CTRL */
+#define DAC_SWAP_OFFSET 30
+#define DAC_SWAP_SIZE 1
+#define DAC_EN_OFFSET 31
+#define DAC_EN_SIZE 1
+
+/* Bitfields in INT_MASK/INT_EN/INT_DIS/INT_STATUS/INT_CLR */
+#define DAC_UNDERRUN_OFFSET 28
+#define DAC_UNDERRUN_SIZE 1
+#define DAC_TX_READY_OFFSET 29
+#define DAC_TX_READY_SIZE 1
+
+/* Bit manipulation macros */
+#define DAC_BIT(name) \
+ (1 << DAC_##name##_OFFSET)
+#define DAC_BF(name, value) \
+ (((value) & ((1 << DAC_##name##_SIZE) - 1)) \
+ << DAC_##name##_OFFSET)
+#define DAC_BFEXT(name, value) \
+ (((value) >> DAC_##name##_OFFSET) \
+ & ((1 << DAC_##name##_SIZE) - 1))
+#define DAC_BFINS(name, value, old) \
+ (((old) & ~(((1 << DAC_##name##_SIZE) - 1) \
+ << DAC_##name##_OFFSET)) \
+ | DAC_BF(name, value))
+
+/* Register access macros */
+#define dac_readl(port, reg) \
+ __raw_readl((port)->regs + DAC_##reg)
+#define dac_writel(port, reg, value) \
+ __raw_writel((value), (port)->regs + DAC_##reg)
+
+/*
+ * ABDAC supports a maximum of 6 different rates from a generic clock. The
+ * generic clock has a power of two divider, which gives 6 steps from 192 kHz
+ * to 5112 Hz.
+ */
+#define MAX_NUM_RATES 6
+/* ALSA seems to use rates between 192000 Hz and 5112 Hz. */
+#define RATE_MAX 192000
+#define RATE_MIN 5112
+
+enum {
+ DMA_READY = 0,
+};
+
+struct atmel_abdac_dma {
+ struct dma_chan *chan;
+ struct dw_cyclic_desc *cdesc;
+};
+
+struct atmel_abdac {
+ struct clk *pclk;
+ struct clk *sample_clk;
+ struct platform_device *pdev;
+ struct atmel_abdac_dma dma;
+
+ struct snd_pcm_hw_constraint_list constraints_rates;
+ struct snd_pcm_substream *substream;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+
+ void __iomem *regs;
+ unsigned long flags;
+ unsigned int rates[MAX_NUM_RATES];
+ unsigned int rates_num;
+ int irq;
+};
+
+#define get_dac(card) ((struct atmel_abdac *)(card)->private_data)
+
+/* This function is called by the DMA driver. */
+static void atmel_abdac_dma_period_done(void *arg)
+{
+ struct atmel_abdac *dac = arg;
+ snd_pcm_period_elapsed(dac->substream);
+}
+
+static int atmel_abdac_prepare_dma(struct atmel_abdac *dac,
+ struct snd_pcm_substream *substream,
+ enum dma_data_direction direction)
+{
+ struct dma_chan *chan = dac->dma.chan;
+ struct dw_cyclic_desc *cdesc;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned long buffer_len, period_len;
+
+ /*
+ * We don't do DMA on "complex" transfers, i.e. with
+ * non-halfword-aligned buffers or lengths.
+ */
+ if (runtime->dma_addr & 1 || runtime->buffer_size & 1) {
+ dev_dbg(&dac->pdev->dev, "too complex transfer\n");
+ return -EINVAL;
+ }
+
+ buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
+ period_len = frames_to_bytes(runtime, runtime->period_size);
+
+ cdesc = dw_dma_cyclic_prep(chan, runtime->dma_addr, buffer_len,
+ period_len, DMA_TO_DEVICE);
+ if (IS_ERR(cdesc)) {
+ dev_dbg(&dac->pdev->dev, "could not prepare cyclic DMA\n");
+ return PTR_ERR(cdesc);
+ }
+
+ cdesc->period_callback = atmel_abdac_dma_period_done;
+ cdesc->period_callback_param = dac;
+
+ dac->dma.cdesc = cdesc;
+
+ set_bit(DMA_READY, &dac->flags);
+
+ return 0;
+}
+
+static struct snd_pcm_hardware atmel_abdac_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP
+ | SNDRV_PCM_INFO_MMAP_VALID
+ | SNDRV_PCM_INFO_INTERLEAVED
+ | SNDRV_PCM_INFO_BLOCK_TRANSFER
+ | SNDRV_PCM_INFO_RESUME
+ | SNDRV_PCM_INFO_PAUSE),
+ .formats = (SNDRV_PCM_FMTBIT_S16_BE),
+ .rates = (SNDRV_PCM_RATE_KNOT),
+ .rate_min = RATE_MIN,
+ .rate_max = RATE_MAX,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 64 * 4096,
+ .period_bytes_min = 4096,
+ .period_bytes_max = 4096,
+ .periods_min = 4,
+ .periods_max = 64,
+};
+
+static int atmel_abdac_open(struct snd_pcm_substream *substream)
+{
+ struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
+
+ dac->substream = substream;
+ atmel_abdac_hw.rate_max = dac->rates[dac->rates_num - 1];
+ atmel_abdac_hw.rate_min = dac->rates[0];
+ substream->runtime->hw = atmel_abdac_hw;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &dac->constraints_rates);
+}
+
+static int atmel_abdac_close(struct snd_pcm_substream *substream)
+{
+ struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
+ dac->substream = NULL;
+ return 0;
+}
+
+static int atmel_abdac_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
+ int retval;
+
+ retval = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ if (retval < 0)
+ return retval;
+ /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
+ if (retval == 1)
+ if (test_and_clear_bit(DMA_READY, &dac->flags))
+ dw_dma_cyclic_free(dac->dma.chan);
+
+ return retval;
+}
+
+static int atmel_abdac_hw_free(struct snd_pcm_substream *substream)
+{
+ struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
+ if (test_and_clear_bit(DMA_READY, &dac->flags))
+ dw_dma_cyclic_free(dac->dma.chan);
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int atmel_abdac_prepare(struct snd_pcm_substream *substream)
+{
+ struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
+ int retval;
+
+ retval = clk_set_rate(dac->sample_clk, 256 * substream->runtime->rate);
+ if (retval)
+ return retval;
+
+ if (!test_bit(DMA_READY, &dac->flags))
+ retval = atmel_abdac_prepare_dma(dac, substream, DMA_TO_DEVICE);
+
+ return retval;
+}
+
+static int atmel_abdac_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
+ int retval = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
+ case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
+ case SNDRV_PCM_TRIGGER_START:
+ clk_enable(dac->sample_clk);
+ retval = dw_dma_cyclic_start(dac->dma.chan);
+ if (retval)
+ goto out;
+ dac_writel(dac, CTRL, DAC_BIT(EN));
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
+ case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
+ case SNDRV_PCM_TRIGGER_STOP:
+ dw_dma_cyclic_stop(dac->dma.chan);
+ dac_writel(dac, DATA, 0);
+ dac_writel(dac, CTRL, 0);
+ clk_disable(dac->sample_clk);
+ break;
+ default:
+ retval = -EINVAL;
+ break;
+ }
+out:
+ return retval;
+}
+
+static snd_pcm_uframes_t
+atmel_abdac_pointer(struct snd_pcm_substream *substream)
+{
+ struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_uframes_t frames;
+ unsigned long bytes;
+
+ bytes = dw_dma_get_src_addr(dac->dma.chan);
+ bytes -= runtime->dma_addr;
+
+ frames = bytes_to_frames(runtime, bytes);
+ if (frames >= runtime->buffer_size)
+ frames -= runtime->buffer_size;
+
+ return frames;
+}
+
+static irqreturn_t abdac_interrupt(int irq, void *dev_id)
+{
+ struct atmel_abdac *dac = dev_id;
+ u32 status;
+
+ status = dac_readl(dac, INT_STATUS);
+ if (status & DAC_BIT(UNDERRUN)) {
+ dev_err(&dac->pdev->dev, "underrun detected\n");
+ dac_writel(dac, INT_CLR, DAC_BIT(UNDERRUN));
+ } else {
+ dev_err(&dac->pdev->dev, "spurious interrupt (status=0x%x)\n",
+ status);
+ dac_writel(dac, INT_CLR, status);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static struct snd_pcm_ops atmel_abdac_ops = {
+ .open = atmel_abdac_open,
+ .close = atmel_abdac_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = atmel_abdac_hw_params,
+ .hw_free = atmel_abdac_hw_free,
+ .prepare = atmel_abdac_prepare,
+ .trigger = atmel_abdac_trigger,
+ .pointer = atmel_abdac_pointer,
+};
+
+static int __devinit atmel_abdac_pcm_new(struct atmel_abdac *dac)
+{
+ struct snd_pcm_hardware hw = atmel_abdac_hw;
+ struct snd_pcm *pcm;
+ int retval;
+
+ retval = snd_pcm_new(dac->card, dac->card->shortname,
+ dac->pdev->id, 1, 0, &pcm);
+ if (retval)
+ return retval;
+
+ strcpy(pcm->name, dac->card->shortname);
+ pcm->private_data = dac;
+ pcm->info_flags = 0;
+ dac->pcm = pcm;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &atmel_abdac_ops);
+
+ retval = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &dac->pdev->dev, hw.periods_min * hw.period_bytes_min,
+ hw.buffer_bytes_max);
+
+ return retval;
+}
+
+static bool filter(struct dma_chan *chan, void *slave)
+{
+ struct dw_dma_slave *dws = slave;
+
+ if (dws->dma_dev == chan->device->dev) {
+ chan->private = dws;
+ return true;
+ } else
+ return false;
+}
+
+static int set_sample_rates(struct atmel_abdac *dac)
+{
+ long new_rate = RATE_MAX;
+ int retval = -EINVAL;
+ int index = 0;
+
+ /* we start at 192 kHz and work our way down to 5112 Hz */
+ while (new_rate >= RATE_MIN && index < (MAX_NUM_RATES + 1)) {
+ new_rate = clk_round_rate(dac->sample_clk, 256 * new_rate);
+ if (new_rate < 0)
+ break;
+ /* make sure we are below the ABDAC clock */
+ if (new_rate <= clk_get_rate(dac->pclk)) {
+ dac->rates[index] = new_rate / 256;
+ index++;
+ }
+ /* divide by 256 and then by two to get next rate */
+ new_rate /= 256 * 2;
+ }
+
+ if (index) {
+ int i;
+
+ /* reverse array, smallest go first */
+ for (i = 0; i < (index / 2); i++) {
+ unsigned int tmp = dac->rates[index - 1 - i];
+ dac->rates[index - 1 - i] = dac->rates[i];
+ dac->rates[i] = tmp;
+ }
+
+ dac->constraints_rates.count = index;
+ dac->constraints_rates.list = dac->rates;
+ dac->constraints_rates.mask = 0;
+ dac->rates_num = index;
+
+ retval = 0;
+ }
+
+ return retval;
+}
+
+static int __devinit atmel_abdac_probe(struct platform_device *pdev)
+{
+ struct snd_card *card;
+ struct atmel_abdac *dac;
+ struct resource *regs;
+ struct atmel_abdac_pdata *pdata;
+ struct clk *pclk;
+ struct clk *sample_clk;
+ int retval;
+ int irq;
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs) {
+ dev_dbg(&pdev->dev, "no memory resource\n");
+ return -ENXIO;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_dbg(&pdev->dev, "could not get IRQ number\n");
+ return irq;
+ }
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_dbg(&pdev->dev, "no platform data\n");
+ return -ENXIO;
+ }
+
+ pclk = clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(pclk)) {
+ dev_dbg(&pdev->dev, "no peripheral clock\n");
+ return PTR_ERR(pclk);
+ }
+ sample_clk = clk_get(&pdev->dev, "sample_clk");
+ if (IS_ERR(pclk)) {
+ dev_dbg(&pdev->dev, "no sample clock\n");
+ retval = PTR_ERR(pclk);
+ goto out_put_pclk;
+ }
+ clk_enable(pclk);
+
+ retval = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, sizeof(struct atmel_abdac), &card);
+ if (retval) {
+ dev_dbg(&pdev->dev, "could not create sound card device\n");
+ goto out_put_sample_clk;
+ }
+
+ dac = get_dac(card);
+
+ dac->irq = irq;
+ dac->card = card;
+ dac->pclk = pclk;
+ dac->sample_clk = sample_clk;
+ dac->pdev = pdev;
+
+ retval = set_sample_rates(dac);
+ if (retval < 0) {
+ dev_dbg(&pdev->dev, "could not set supported rates\n");
+ goto out_free_card;
+ }
+
+ dac->regs = ioremap(regs->start, regs->end - regs->start + 1);
+ if (!dac->regs) {
+ dev_dbg(&pdev->dev, "could not remap register memory\n");
+ goto out_free_card;
+ }
+
+ /* make sure the DAC is silent and disabled */
+ dac_writel(dac, DATA, 0);
+ dac_writel(dac, CTRL, 0);
+
+ retval = request_irq(irq, abdac_interrupt, 0, "abdac", dac);
+ if (retval) {
+ dev_dbg(&pdev->dev, "could not request irq\n");
+ goto out_unmap_regs;
+ }
+
+ snd_card_set_dev(card, &pdev->dev);
+
+ if (pdata->dws.dma_dev) {
+ struct dw_dma_slave *dws = &pdata->dws;
+ dma_cap_mask_t mask;
+
+ dws->tx_reg = regs->start + DAC_DATA;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ dac->dma.chan = dma_request_channel(mask, filter, dws);
+ }
+ if (!pdata->dws.dma_dev || !dac->dma.chan) {
+ dev_dbg(&pdev->dev, "DMA not available\n");
+ retval = -ENODEV;
+ goto out_unset_card_dev;
+ }
+
+ strcpy(card->driver, "Atmel ABDAC");
+ strcpy(card->shortname, "Atmel ABDAC");
+ sprintf(card->longname, "Atmel Audio Bitstream DAC");
+
+ retval = atmel_abdac_pcm_new(dac);
+ if (retval) {
+ dev_dbg(&pdev->dev, "could not register ABDAC pcm device\n");
+ goto out_release_dma;
+ }
+
+ retval = snd_card_register(card);
+ if (retval) {
+ dev_dbg(&pdev->dev, "could not register sound card\n");
+ goto out_release_dma;
+ }
+
+ platform_set_drvdata(pdev, card);
+
+ dev_info(&pdev->dev, "Atmel ABDAC at 0x%p using %s\n",
+ dac->regs, dac->dma.chan->dev->device.bus_id);
+
+ return retval;
+
+out_release_dma:
+ dma_release_channel(dac->dma.chan);
+ dac->dma.chan = NULL;
+out_unset_card_dev:
+ snd_card_set_dev(card, NULL);
+ free_irq(irq, dac);
+out_unmap_regs:
+ iounmap(dac->regs);
+out_free_card:
+ snd_card_free(card);
+out_put_sample_clk:
+ clk_put(sample_clk);
+ clk_disable(pclk);
+out_put_pclk:
+ clk_put(pclk);
+ return retval;
+}
+
+#ifdef CONFIG_PM
+static int atmel_abdac_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+ struct snd_card *card = platform_get_drvdata(pdev);
+ struct atmel_abdac *dac = card->private_data;
+
+ dw_dma_cyclic_stop(dac->dma.chan);
+ clk_disable(dac->sample_clk);
+ clk_disable(dac->pclk);
+
+ return 0;
+}
+
+static int atmel_abdac_resume(struct platform_device *pdev)
+{
+ struct snd_card *card = platform_get_drvdata(pdev);
+ struct atmel_abdac *dac = card->private_data;
+
+ clk_enable(dac->pclk);
+ clk_enable(dac->sample_clk);
+ if (test_bit(DMA_READY, &dac->flags))
+ dw_dma_cyclic_start(dac->dma.chan);
+
+ return 0;
+}
+#else
+#define atmel_abdac_suspend NULL
+#define atmel_abdac_resume NULL
+#endif
+
+static int __devexit atmel_abdac_remove(struct platform_device *pdev)
+{
+ struct snd_card *card = platform_get_drvdata(pdev);
+ struct atmel_abdac *dac = get_dac(card);
+
+ clk_put(dac->sample_clk);
+ clk_disable(dac->pclk);
+ clk_put(dac->pclk);
+
+ dma_release_channel(dac->dma.chan);
+ dac->dma.chan = NULL;
+ snd_card_set_dev(card, NULL);
+ iounmap(dac->regs);
+ free_irq(dac->irq, dac);
+ snd_card_free(card);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver atmel_abdac_driver = {
+ .remove = __devexit_p(atmel_abdac_remove),
+ .driver = {
+ .name = "atmel_abdac",
+ },
+ .suspend = atmel_abdac_suspend,
+ .resume = atmel_abdac_resume,
+};
+
+static int __init atmel_abdac_init(void)
+{
+ return platform_driver_probe(&atmel_abdac_driver,
+ atmel_abdac_probe);
+}
+module_init(atmel_abdac_init);
+
+static void __exit atmel_abdac_exit(void)
+{
+ platform_driver_unregister(&atmel_abdac_driver);
+}
+module_exit(atmel_abdac_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Driver for Atmel Audio Bitstream DAC (ABDAC)");
+MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>");
diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c
new file mode 100644
index 0000000..dd72e00
--- /dev/null
+++ b/sound/atmel/ac97c.c
@@ -0,0 +1,932 @@
+/*
+ * Driver for the Atmel AC97C controller
+ *
+ * Copyright (C) 2005-2009 Atmel Corporation
+ *
+ * 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/clk.h>
+#include <linux/delay.h>
+#include <linux/bitmap.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/ac97_codec.h>
+#include <sound/atmel-ac97c.h>
+#include <sound/memalloc.h>
+
+#include <linux/dw_dmac.h>
+
+#include "ac97c.h"
+
+enum {
+ DMA_TX_READY = 0,
+ DMA_RX_READY,
+ DMA_TX_CHAN_PRESENT,
+ DMA_RX_CHAN_PRESENT,
+};
+
+/* Serialize access to opened variable */
+static DEFINE_MUTEX(opened_mutex);
+
+struct atmel_ac97c_dma {
+ struct dma_chan *rx_chan;
+ struct dma_chan *tx_chan;
+};
+
+struct atmel_ac97c {
+ struct clk *pclk;
+ struct platform_device *pdev;
+ struct atmel_ac97c_dma dma;
+
+ struct snd_pcm_substream *playback_substream;
+ struct snd_pcm_substream *capture_substream;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct snd_ac97 *ac97;
+ struct snd_ac97_bus *ac97_bus;
+
+ u64 cur_format;
+ unsigned int cur_rate;
+ unsigned long flags;
+ /* Serialize access to opened variable */
+ spinlock_t lock;
+ void __iomem *regs;
+ int opened;
+ int reset_pin;
+};
+
+#define get_chip(card) ((struct atmel_ac97c *)(card)->private_data)
+
+#define ac97c_writel(chip, reg, val) \
+ __raw_writel((val), (chip)->regs + AC97C_##reg)
+#define ac97c_readl(chip, reg) \
+ __raw_readl((chip)->regs + AC97C_##reg)
+
+/* This function is called by the DMA driver. */
+static void atmel_ac97c_dma_playback_period_done(void *arg)
+{
+ struct atmel_ac97c *chip = arg;
+ snd_pcm_period_elapsed(chip->playback_substream);
+}
+
+static void atmel_ac97c_dma_capture_period_done(void *arg)
+{
+ struct atmel_ac97c *chip = arg;
+ snd_pcm_period_elapsed(chip->capture_substream);
+}
+
+static int atmel_ac97c_prepare_dma(struct atmel_ac97c *chip,
+ struct snd_pcm_substream *substream,
+ enum dma_data_direction direction)
+{
+ struct dma_chan *chan;
+ struct dw_cyclic_desc *cdesc;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned long buffer_len, period_len;
+
+ /*
+ * We don't do DMA on "complex" transfers, i.e. with
+ * non-halfword-aligned buffers or lengths.
+ */
+ if (runtime->dma_addr & 1 || runtime->buffer_size & 1) {
+ dev_dbg(&chip->pdev->dev, "too complex transfer\n");
+ return -EINVAL;
+ }
+
+ if (direction == DMA_TO_DEVICE)
+ chan = chip->dma.tx_chan;
+ else
+ chan = chip->dma.rx_chan;
+
+ buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
+ period_len = frames_to_bytes(runtime, runtime->period_size);
+
+ cdesc = dw_dma_cyclic_prep(chan, runtime->dma_addr, buffer_len,
+ period_len, direction);
+ if (IS_ERR(cdesc)) {
+ dev_dbg(&chip->pdev->dev, "could not prepare cyclic DMA\n");
+ return PTR_ERR(cdesc);
+ }
+
+ if (direction == DMA_TO_DEVICE) {
+ cdesc->period_callback = atmel_ac97c_dma_playback_period_done;
+ set_bit(DMA_TX_READY, &chip->flags);
+ } else {
+ cdesc->period_callback = atmel_ac97c_dma_capture_period_done;
+ set_bit(DMA_RX_READY, &chip->flags);
+ }
+
+ cdesc->period_callback_param = chip;
+
+ return 0;
+}
+
+static struct snd_pcm_hardware atmel_ac97c_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP
+ | SNDRV_PCM_INFO_MMAP_VALID
+ | SNDRV_PCM_INFO_INTERLEAVED
+ | SNDRV_PCM_INFO_BLOCK_TRANSFER
+ | SNDRV_PCM_INFO_JOINT_DUPLEX
+ | SNDRV_PCM_INFO_RESUME
+ | SNDRV_PCM_INFO_PAUSE),
+ .formats = (SNDRV_PCM_FMTBIT_S16_BE
+ | SNDRV_PCM_FMTBIT_S16_LE),
+ .rates = (SNDRV_PCM_RATE_CONTINUOUS),
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 64 * 4096,
+ .period_bytes_min = 4096,
+ .period_bytes_max = 4096,
+ .periods_min = 4,
+ .periods_max = 64,
+};
+
+static int atmel_ac97c_playback_open(struct snd_pcm_substream *substream)
+{
+ struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ mutex_lock(&opened_mutex);
+ chip->opened++;
+ runtime->hw = atmel_ac97c_hw;
+ if (chip->cur_rate) {
+ runtime->hw.rate_min = chip->cur_rate;
+ runtime->hw.rate_max = chip->cur_rate;
+ }
+ if (chip->cur_format)
+ runtime->hw.formats = (1ULL << chip->cur_format);
+ mutex_unlock(&opened_mutex);
+ chip->playback_substream = substream;
+ return 0;
+}
+
+static int atmel_ac97c_capture_open(struct snd_pcm_substream *substream)
+{
+ struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ mutex_lock(&opened_mutex);
+ chip->opened++;
+ runtime->hw = atmel_ac97c_hw;
+ if (chip->cur_rate) {
+ runtime->hw.rate_min = chip->cur_rate;
+ runtime->hw.rate_max = chip->cur_rate;
+ }
+ if (chip->cur_format)
+ runtime->hw.formats = (1ULL << chip->cur_format);
+ mutex_unlock(&opened_mutex);
+ chip->capture_substream = substream;
+ return 0;
+}
+
+static int atmel_ac97c_playback_close(struct snd_pcm_substream *substream)
+{
+ struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
+
+ mutex_lock(&opened_mutex);
+ chip->opened--;
+ if (!chip->opened) {
+ chip->cur_rate = 0;
+ chip->cur_format = 0;
+ }
+ mutex_unlock(&opened_mutex);
+
+ chip->playback_substream = NULL;
+
+ return 0;
+}
+
+static int atmel_ac97c_capture_close(struct snd_pcm_substream *substream)
+{
+ struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
+
+ mutex_lock(&opened_mutex);
+ chip->opened--;
+ if (!chip->opened) {
+ chip->cur_rate = 0;
+ chip->cur_format = 0;
+ }
+ mutex_unlock(&opened_mutex);
+
+ chip->capture_substream = NULL;
+
+ return 0;
+}
+
+static int atmel_ac97c_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
+ int retval;
+
+ retval = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ if (retval < 0)
+ return retval;
+ /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
+ if (retval == 1)
+ if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
+ dw_dma_cyclic_free(chip->dma.tx_chan);
+
+ /* Set restrictions to params. */
+ mutex_lock(&opened_mutex);
+ chip->cur_rate = params_rate(hw_params);
+ chip->cur_format = params_format(hw_params);
+ mutex_unlock(&opened_mutex);
+
+ return retval;
+}
+
+static int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
+ int retval;
+
+ retval = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ if (retval < 0)
+ return retval;
+ /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
+ if (retval == 1)
+ if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
+ dw_dma_cyclic_free(chip->dma.rx_chan);
+
+ /* Set restrictions to params. */
+ mutex_lock(&opened_mutex);
+ chip->cur_rate = params_rate(hw_params);
+ chip->cur_format = params_format(hw_params);
+ mutex_unlock(&opened_mutex);
+
+ return retval;
+}
+
+static int atmel_ac97c_playback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
+ if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
+ dw_dma_cyclic_free(chip->dma.tx_chan);
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int atmel_ac97c_capture_hw_free(struct snd_pcm_substream *substream)
+{
+ struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
+ if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
+ dw_dma_cyclic_free(chip->dma.rx_chan);
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned long word = 0;
+ int retval;
+
+ /* assign channels to AC97C channel A */
+ switch (runtime->channels) {
+ case 1:
+ word |= AC97C_CH_ASSIGN(PCM_LEFT, A);
+ break;
+ case 2:
+ word |= AC97C_CH_ASSIGN(PCM_LEFT, A)
+ | AC97C_CH_ASSIGN(PCM_RIGHT, A);
+ break;
+ default:
+ /* TODO: support more than two channels */
+ return -EINVAL;
+ break;
+ }
+ ac97c_writel(chip, OCA, word);
+
+ /* configure sample format and size */
+ word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
+
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ word |= AC97C_CMR_CEM_LITTLE;
+ break;
+ case SNDRV_PCM_FORMAT_S16_BE: /* fall through */
+ default:
+ word &= ~(AC97C_CMR_CEM_LITTLE);
+ break;
+ }
+
+ ac97c_writel(chip, CAMR, word);
+
+ /* set variable rate if needed */
+ if (runtime->rate != 48000) {
+ word = ac97c_readl(chip, MR);
+ word |= AC97C_MR_VRA;
+ ac97c_writel(chip, MR, word);
+ } else {
+ word = ac97c_readl(chip, MR);
+ word &= ~(AC97C_MR_VRA);
+ ac97c_writel(chip, MR, word);
+ }
+
+ retval = snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE,
+ runtime->rate);
+ if (retval)
+ dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n",
+ runtime->rate);
+
+ if (!test_bit(DMA_TX_READY, &chip->flags))
+ retval = atmel_ac97c_prepare_dma(chip, substream,
+ DMA_TO_DEVICE);
+
+ return retval;
+}
+
+static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned long word = 0;
+ int retval;
+
+ /* assign channels to AC97C channel A */
+ switch (runtime->channels) {
+ case 1:
+ word |= AC97C_CH_ASSIGN(PCM_LEFT, A);
+ break;
+ case 2:
+ word |= AC97C_CH_ASSIGN(PCM_LEFT, A)
+ | AC97C_CH_ASSIGN(PCM_RIGHT, A);
+ break;
+ default:
+ /* TODO: support more than two channels */
+ return -EINVAL;
+ break;
+ }
+ ac97c_writel(chip, ICA, word);
+
+ /* configure sample format and size */
+ word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
+
+ switch (runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ word |= AC97C_CMR_CEM_LITTLE;
+ break;
+ case SNDRV_PCM_FORMAT_S16_BE: /* fall through */
+ default:
+ word &= ~(AC97C_CMR_CEM_LITTLE);
+ break;
+ }
+
+ ac97c_writel(chip, CAMR, word);
+
+ /* set variable rate if needed */
+ if (runtime->rate != 48000) {
+ word = ac97c_readl(chip, MR);
+ word |= AC97C_MR_VRA;
+ ac97c_writel(chip, MR, word);
+ } else {
+ word = ac97c_readl(chip, MR);
+ word &= ~(AC97C_MR_VRA);
+ ac97c_writel(chip, MR, word);
+ }
+
+ retval = snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE,
+ runtime->rate);
+ if (retval)
+ dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n",
+ runtime->rate);
+
+ if (!test_bit(DMA_RX_READY, &chip->flags))
+ retval = atmel_ac97c_prepare_dma(chip, substream,
+ DMA_FROM_DEVICE);
+
+ return retval;
+}
+
+static int
+atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
+ unsigned long camr;
+ int retval = 0;
+
+ camr = ac97c_readl(chip, CAMR);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
+ case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
+ case SNDRV_PCM_TRIGGER_START:
+ retval = dw_dma_cyclic_start(chip->dma.tx_chan);
+ if (retval)
+ goto out;
+ camr |= AC97C_CMR_CENA;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
+ case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
+ case SNDRV_PCM_TRIGGER_STOP:
+ dw_dma_cyclic_stop(chip->dma.tx_chan);
+ if (chip->opened <= 1)
+ camr &= ~AC97C_CMR_CENA;
+ break;
+ default:
+ retval = -EINVAL;
+ goto out;
+ }
+
+ ac97c_writel(chip, CAMR, camr);
+out:
+ return retval;
+}
+
+static int
+atmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
+ unsigned long camr;
+ int retval = 0;
+
+ camr = ac97c_readl(chip, CAMR);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
+ case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
+ case SNDRV_PCM_TRIGGER_START:
+ retval = dw_dma_cyclic_start(chip->dma.rx_chan);
+ if (retval)
+ goto out;
+ camr |= AC97C_CMR_CENA;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
+ case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
+ case SNDRV_PCM_TRIGGER_STOP:
+ dw_dma_cyclic_stop(chip->dma.rx_chan);
+ if (chip->opened <= 1)
+ camr &= ~AC97C_CMR_CENA;
+ break;
+ default:
+ retval = -EINVAL;
+ break;
+ }
+
+ ac97c_writel(chip, CAMR, camr);
+out:
+ return retval;
+}
+
+static snd_pcm_uframes_t
+atmel_ac97c_playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_uframes_t frames;
+ unsigned long bytes;
+
+ bytes = dw_dma_get_src_addr(chip->dma.tx_chan);
+ bytes -= runtime->dma_addr;
+
+ frames = bytes_to_frames(runtime, bytes);
+ if (frames >= runtime->buffer_size)
+ frames -= runtime->buffer_size;
+ return frames;
+}
+
+static snd_pcm_uframes_t
+atmel_ac97c_capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_uframes_t frames;
+ unsigned long bytes;
+
+ bytes = dw_dma_get_dst_addr(chip->dma.rx_chan);
+ bytes -= runtime->dma_addr;
+
+ frames = bytes_to_frames(runtime, bytes);
+ if (frames >= runtime->buffer_size)
+ frames -= runtime->buffer_size;
+ return frames;
+}
+
+static struct snd_pcm_ops atmel_ac97_playback_ops = {
+ .open = atmel_ac97c_playback_open,
+ .close = atmel_ac97c_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = atmel_ac97c_playback_hw_params,
+ .hw_free = atmel_ac97c_playback_hw_free,
+ .prepare = atmel_ac97c_playback_prepare,
+ .trigger = atmel_ac97c_playback_trigger,
+ .pointer = atmel_ac97c_playback_pointer,
+};
+
+static struct snd_pcm_ops atmel_ac97_capture_ops = {
+ .open = atmel_ac97c_capture_open,
+ .close = atmel_ac97c_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = atmel_ac97c_capture_hw_params,
+ .hw_free = atmel_ac97c_capture_hw_free,
+ .prepare = atmel_ac97c_capture_prepare,
+ .trigger = atmel_ac97c_capture_trigger,
+ .pointer = atmel_ac97c_capture_pointer,
+};
+
+static int __devinit atmel_ac97c_pcm_new(struct atmel_ac97c *chip)
+{
+ struct snd_pcm *pcm;
+ struct snd_pcm_hardware hw = atmel_ac97c_hw;
+ int capture, playback, retval;
+
+ capture = test_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
+ playback = test_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
+
+ retval = snd_pcm_new(chip->card, chip->card->shortname,
+ chip->pdev->id, playback, capture, &pcm);
+ if (retval)
+ return retval;
+
+ if (capture)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &atmel_ac97_capture_ops);
+ if (playback)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &atmel_ac97_playback_ops);
+
+ retval = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pdev->dev, hw.periods_min * hw.period_bytes_min,
+ hw.buffer_bytes_max);
+ if (retval)
+ return retval;
+
+ pcm->private_data = chip;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, chip->card->shortname);
+ chip->pcm = pcm;
+
+ return 0;
+}
+
+static int atmel_ac97c_mixer_new(struct atmel_ac97c *chip)
+{
+ struct snd_ac97_template template;
+ memset(&template, 0, sizeof(template));
+ template.private_data = chip;
+ return snd_ac97_mixer(chip->ac97_bus, &template, &chip->ac97);
+}
+
+static void atmel_ac97c_write(struct snd_ac97 *ac97, unsigned short reg,
+ unsigned short val)
+{
+ struct atmel_ac97c *chip = get_chip(ac97);
+ unsigned long word;
+ int timeout = 40;
+
+ word = (reg & 0x7f) << 16 | val;
+
+ do {
+ if (ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) {
+ ac97c_writel(chip, COTHR, word);
+ return;
+ }
+ udelay(1);
+ } while (--timeout);
+
+ dev_dbg(&chip->pdev->dev, "codec write timeout\n");
+}
+
+static unsigned short atmel_ac97c_read(struct snd_ac97 *ac97,
+ unsigned short reg)
+{
+ struct atmel_ac97c *chip = get_chip(ac97);
+ unsigned long word;
+ int timeout = 40;
+ int write = 10;
+
+ word = (0x80 | (reg & 0x7f)) << 16;
+
+ if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0)
+ ac97c_readl(chip, CORHR);
+
+retry_write:
+ timeout = 40;
+
+ do {
+ if ((ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) != 0) {
+ ac97c_writel(chip, COTHR, word);
+ goto read_reg;
+ }
+ udelay(10);
+ } while (--timeout);
+
+ if (!--write)
+ goto timed_out;
+ goto retry_write;
+
+read_reg:
+ do {
+ if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0) {
+ unsigned short val = ac97c_readl(chip, CORHR);
+ return val;
+ }
+ udelay(10);
+ } while (--timeout);
+
+ if (!--write)
+ goto timed_out;
+ goto retry_write;
+
+timed_out:
+ dev_dbg(&chip->pdev->dev, "codec read timeout\n");
+ return 0xffff;
+}
+
+static bool filter(struct dma_chan *chan, void *slave)
+{
+ struct dw_dma_slave *dws = slave;
+
+ if (dws->dma_dev == chan->device->dev) {
+ chan->private = dws;
+ return true;
+ } else
+ return false;
+}
+
+static void atmel_ac97c_reset(struct atmel_ac97c *chip)
+{
+ ac97c_writel(chip, MR, AC97C_MR_WRST);
+
+ if (gpio_is_valid(chip->reset_pin)) {
+ gpio_set_value(chip->reset_pin, 0);
+ /* AC97 v2.2 specifications says minimum 1 us. */
+ udelay(10);
+ gpio_set_value(chip->reset_pin, 1);
+ }
+
+ udelay(1);
+ ac97c_writel(chip, MR, AC97C_MR_ENA);
+}
+
+static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
+{
+ struct snd_card *card;
+ struct atmel_ac97c *chip;
+ struct resource *regs;
+ struct ac97c_platform_data *pdata;
+ struct clk *pclk;
+ static struct snd_ac97_bus_ops ops = {
+ .write = atmel_ac97c_write,
+ .read = atmel_ac97c_read,
+ };
+ int retval;
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs) {
+ dev_dbg(&pdev->dev, "no memory resource\n");
+ return -ENXIO;
+ }
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_dbg(&pdev->dev, "no platform data\n");
+ return -ENXIO;
+ }
+
+ pclk = clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(pclk)) {
+ dev_dbg(&pdev->dev, "no peripheral clock\n");
+ return PTR_ERR(pclk);
+ }
+ clk_enable(pclk);
+
+ retval = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, sizeof(struct atmel_ac97c), &card);
+ if (retval) {
+ dev_dbg(&pdev->dev, "could not create sound card device\n");
+ goto err_snd_card_new;
+ }
+
+ chip = get_chip(card);
+
+ spin_lock_init(&chip->lock);
+
+ strcpy(card->driver, "Atmel AC97C");
+ strcpy(card->shortname, "Atmel AC97C");
+ sprintf(card->longname, "Atmel AC97 controller");
+
+ chip->card = card;
+ chip->pclk = pclk;
+ chip->pdev = pdev;
+ chip->regs = ioremap(regs->start, regs->end - regs->start + 1);
+
+ if (!chip->regs) {
+ dev_dbg(&pdev->dev, "could not remap register memory\n");
+ goto err_ioremap;
+ }
+
+ if (gpio_is_valid(pdata->reset_pin)) {
+ if (gpio_request(pdata->reset_pin, "reset_pin")) {
+ dev_dbg(&pdev->dev, "reset pin not available\n");
+ chip->reset_pin = -ENODEV;
+ } else {
+ gpio_direction_output(pdata->reset_pin, 1);
+ chip->reset_pin = pdata->reset_pin;
+ }
+ }
+
+ snd_card_set_dev(card, &pdev->dev);
+
+ retval = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus);
+ if (retval) {
+ dev_dbg(&pdev->dev, "could not register on ac97 bus\n");
+ goto err_ac97_bus;
+ }
+
+ atmel_ac97c_reset(chip);
+
+ retval = atmel_ac97c_mixer_new(chip);
+ if (retval) {
+ dev_dbg(&pdev->dev, "could not register ac97 mixer\n");
+ goto err_ac97_bus;
+ }
+
+ if (pdata->rx_dws.dma_dev) {
+ struct dw_dma_slave *dws = &pdata->rx_dws;
+ dma_cap_mask_t mask;
+
+ dws->rx_reg = regs->start + AC97C_CARHR + 2;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ chip->dma.rx_chan = dma_request_channel(mask, filter, dws);
+
+ dev_info(&chip->pdev->dev, "using %s for DMA RX\n",
+ chip->dma.rx_chan->dev->device.bus_id);
+ set_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
+ }
+
+ if (pdata->tx_dws.dma_dev) {
+ struct dw_dma_slave *dws = &pdata->tx_dws;
+ dma_cap_mask_t mask;
+
+ dws->tx_reg = regs->start + AC97C_CATHR + 2;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ chip->dma.tx_chan = dma_request_channel(mask, filter, dws);
+
+ dev_info(&chip->pdev->dev, "using %s for DMA TX\n",
+ chip->dma.tx_chan->dev->device.bus_id);
+ set_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
+ }
+
+ if (!test_bit(DMA_RX_CHAN_PRESENT, &chip->flags) &&
+ !test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) {
+ dev_dbg(&pdev->dev, "DMA not available\n");
+ retval = -ENODEV;
+ goto err_dma;
+ }
+
+ retval = atmel_ac97c_pcm_new(chip);
+ if (retval) {
+ dev_dbg(&pdev->dev, "could not register ac97 pcm device\n");
+ goto err_dma;
+ }
+
+ retval = snd_card_register(card);
+ if (retval) {
+ dev_dbg(&pdev->dev, "could not register sound card\n");
+ goto err_ac97_bus;
+ }
+
+ platform_set_drvdata(pdev, card);
+
+ dev_info(&pdev->dev, "Atmel AC97 controller at 0x%p\n",
+ chip->regs);
+
+ return 0;
+
+err_dma:
+ if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
+ dma_release_channel(chip->dma.rx_chan);
+ if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
+ dma_release_channel(chip->dma.tx_chan);
+ clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
+ clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
+ chip->dma.rx_chan = NULL;
+ chip->dma.tx_chan = NULL;
+err_ac97_bus:
+ snd_card_set_dev(card, NULL);
+
+ if (gpio_is_valid(chip->reset_pin))
+ gpio_free(chip->reset_pin);
+
+ iounmap(chip->regs);
+err_ioremap:
+ snd_card_free(card);
+err_snd_card_new:
+ clk_disable(pclk);
+ clk_put(pclk);
+ return retval;
+}
+
+#ifdef CONFIG_PM
+static int atmel_ac97c_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+ struct snd_card *card = platform_get_drvdata(pdev);
+ struct atmel_ac97c *chip = card->private_data;
+
+ if (test_bit(DMA_RX_READY, &chip->flags))
+ dw_dma_cyclic_stop(chip->dma.rx_chan);
+ if (test_bit(DMA_TX_READY, &chip->flags))
+ dw_dma_cyclic_stop(chip->dma.tx_chan);
+ clk_disable(chip->pclk);
+
+ return 0;
+}
+
+static int atmel_ac97c_resume(struct platform_device *pdev)
+{
+ struct snd_card *card = platform_get_drvdata(pdev);
+ struct atmel_ac97c *chip = card->private_data;
+
+ clk_enable(chip->pclk);
+ if (test_bit(DMA_RX_READY, &chip->flags))
+ dw_dma_cyclic_start(chip->dma.rx_chan);
+ if (test_bit(DMA_TX_READY, &chip->flags))
+ dw_dma_cyclic_start(chip->dma.tx_chan);
+
+ return 0;
+}
+#else
+#define atmel_ac97c_suspend NULL
+#define atmel_ac97c_resume NULL
+#endif
+
+static int __devexit atmel_ac97c_remove(struct platform_device *pdev)
+{
+ struct snd_card *card = platform_get_drvdata(pdev);
+ struct atmel_ac97c *chip = get_chip(card);
+
+ if (gpio_is_valid(chip->reset_pin))
+ gpio_free(chip->reset_pin);
+
+ clk_disable(chip->pclk);
+ clk_put(chip->pclk);
+ iounmap(chip->regs);
+
+ if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
+ dma_release_channel(chip->dma.rx_chan);
+ if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
+ dma_release_channel(chip->dma.tx_chan);
+ clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
+ clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
+ chip->dma.rx_chan = NULL;
+ chip->dma.tx_chan = NULL;
+
+ snd_card_set_dev(card, NULL);
+ snd_card_free(card);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver atmel_ac97c_driver = {
+ .remove = __devexit_p(atmel_ac97c_remove),
+ .driver = {
+ .name = "atmel_ac97c",
+ },
+ .suspend = atmel_ac97c_suspend,
+ .resume = atmel_ac97c_resume,
+};
+
+static int __init atmel_ac97c_init(void)
+{
+ return platform_driver_probe(&atmel_ac97c_driver,
+ atmel_ac97c_probe);
+}
+module_init(atmel_ac97c_init);
+
+static void __exit atmel_ac97c_exit(void)
+{
+ platform_driver_unregister(&atmel_ac97c_driver);
+}
+module_exit(atmel_ac97c_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Driver for Atmel AC97 controller");
+MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>");
diff --git a/sound/atmel/ac97c.h b/sound/atmel/ac97c.h
new file mode 100644
index 0000000..c17bd58
--- /dev/null
+++ b/sound/atmel/ac97c.h
@@ -0,0 +1,71 @@
+/*
+ * Register definitions for the Atmel AC97C controller
+ *
+ * Copyright (C) 2005-2009 Atmel Corporation
+ *
+ * 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 __SOUND_ATMEL_AC97C_H
+#define __SOUND_ATMEL_AC97C_H
+
+#define AC97C_MR 0x08
+#define AC97C_ICA 0x10
+#define AC97C_OCA 0x14
+#define AC97C_CARHR 0x20
+#define AC97C_CATHR 0x24
+#define AC97C_CASR 0x28
+#define AC97C_CAMR 0x2c
+#define AC97C_CBRHR 0x30
+#define AC97C_CBTHR 0x34
+#define AC97C_CBSR 0x38
+#define AC97C_CBMR 0x3c
+#define AC97C_CORHR 0x40
+#define AC97C_COTHR 0x44
+#define AC97C_COSR 0x48
+#define AC97C_COMR 0x4c
+#define AC97C_SR 0x50
+#define AC97C_IER 0x54
+#define AC97C_IDR 0x58
+#define AC97C_IMR 0x5c
+#define AC97C_VERSION 0xfc
+
+#define AC97C_CATPR PDC_TPR
+#define AC97C_CATCR PDC_TCR
+#define AC97C_CATNPR PDC_TNPR
+#define AC97C_CATNCR PDC_TNCR
+#define AC97C_CARPR PDC_RPR
+#define AC97C_CARCR PDC_RCR
+#define AC97C_CARNPR PDC_RNPR
+#define AC97C_CARNCR PDC_RNCR
+#define AC97C_PTCR PDC_PTCR
+
+#define AC97C_MR_ENA (1 << 0)
+#define AC97C_MR_WRST (1 << 1)
+#define AC97C_MR_VRA (1 << 2)
+
+#define AC97C_CSR_TXRDY (1 << 0)
+#define AC97C_CSR_UNRUN (1 << 2)
+#define AC97C_CSR_RXRDY (1 << 4)
+#define AC97C_CSR_ENDTX (1 << 10)
+#define AC97C_CSR_ENDRX (1 << 14)
+
+#define AC97C_CMR_SIZE_20 (0 << 16)
+#define AC97C_CMR_SIZE_18 (1 << 16)
+#define AC97C_CMR_SIZE_16 (2 << 16)
+#define AC97C_CMR_SIZE_10 (3 << 16)
+#define AC97C_CMR_CEM_LITTLE (1 << 18)
+#define AC97C_CMR_CEM_BIG (0 << 18)
+#define AC97C_CMR_CENA (1 << 21)
+#define AC97C_CMR_DMAEN (1 << 22)
+
+#define AC97C_SR_CAEVT (1 << 3)
+
+#define AC97C_CH_ASSIGN(slot, channel) \
+ (AC97C_CHANNEL_##channel << (3 * (AC97_SLOT_##slot - 3)))
+#define AC97C_CHANNEL_NONE 0x0
+#define AC97C_CHANNEL_A 0x1
+#define AC97C_CHANNEL_B 0x2
+
+#endif /* __SOUND_ATMEL_AC97C_H */
diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c
index 195cafc..a70ee7f 100644
--- a/sound/core/hwdep.c
+++ b/sound/core/hwdep.c
@@ -99,9 +99,6 @@
if (hw == NULL)
return -ENODEV;
- if (!hw->ops.open)
- return -ENXIO;
-
if (!try_module_get(hw->card->module))
return -EFAULT;
@@ -113,6 +110,10 @@
err = -EBUSY;
break;
}
+ if (!hw->ops.open) {
+ err = 0;
+ break;
+ }
err = hw->ops.open(hw, file);
if (err >= 0)
break;
@@ -151,7 +152,7 @@
static int snd_hwdep_release(struct inode *inode, struct file * file)
{
- int err = -ENXIO;
+ int err = 0;
struct snd_hwdep *hw = file->private_data;
struct module *mod = hw->card->module;
diff --git a/sound/core/init.c b/sound/core/init.c
index 0d5520c..fd56afe 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -121,31 +121,44 @@
#endif
/**
- * snd_card_new - create and initialize a soundcard structure
+ * snd_card_create - create and initialize a soundcard structure
* @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
* @xid: card identification (ASCII string)
* @module: top level module for locking
* @extra_size: allocate this extra size after the main soundcard structure
+ * @card_ret: the pointer to store the created card instance
*
* Creates and initializes a soundcard structure.
*
- * Returns kmallocated snd_card structure. Creates the ALSA control interface
- * (which is blocked until snd_card_register function is called).
+ * The function allocates snd_card instance via kzalloc with the given
+ * space for the driver to use freely. The allocated struct is stored
+ * in the given card_ret pointer.
+ *
+ * Returns zero if successful or a negative error code.
*/
-struct snd_card *snd_card_new(int idx, const char *xid,
- struct module *module, int extra_size)
+int snd_card_create(int idx, const char *xid,
+ struct module *module, int extra_size,
+ struct snd_card **card_ret)
{
struct snd_card *card;
int err, idx2;
+ if (snd_BUG_ON(!card_ret))
+ return -EINVAL;
+ *card_ret = NULL;
+
if (extra_size < 0)
extra_size = 0;
card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
- if (card == NULL)
- return NULL;
+ if (!card)
+ return -ENOMEM;
if (xid) {
- if (!snd_info_check_reserved_words(xid))
+ if (!snd_info_check_reserved_words(xid)) {
+ snd_printk(KERN_ERR
+ "given id string '%s' is reserved.\n", xid);
+ err = -EBUSY;
goto __error;
+ }
strlcpy(card->id, xid, sizeof(card->id));
}
err = 0;
@@ -195,6 +208,7 @@
INIT_LIST_HEAD(&card->controls);
INIT_LIST_HEAD(&card->ctl_files);
spin_lock_init(&card->files_lock);
+ INIT_LIST_HEAD(&card->files_list);
init_waitqueue_head(&card->shutdown_sleep);
#ifdef CONFIG_PM
mutex_init(&card->power_lock);
@@ -202,26 +216,28 @@
#endif
/* the control interface cannot be accessed from the user space until */
/* snd_cards_bitmask and snd_cards are set with snd_card_register */
- if ((err = snd_ctl_create(card)) < 0) {
- snd_printd("unable to register control minors\n");
+ err = snd_ctl_create(card);
+ if (err < 0) {
+ snd_printk(KERN_ERR "unable to register control minors\n");
goto __error;
}
- if ((err = snd_info_card_create(card)) < 0) {
- snd_printd("unable to create card info\n");
+ err = snd_info_card_create(card);
+ if (err < 0) {
+ snd_printk(KERN_ERR "unable to create card info\n");
goto __error_ctl;
}
if (extra_size > 0)
card->private_data = (char *)card + sizeof(struct snd_card);
- return card;
+ *card_ret = card;
+ return 0;
__error_ctl:
snd_device_free_all(card, SNDRV_DEV_CMD_PRE);
__error:
kfree(card);
- return NULL;
+ return err;
}
-
-EXPORT_SYMBOL(snd_card_new);
+EXPORT_SYMBOL(snd_card_create);
/* return non-zero if a card is already locked */
int snd_card_locked(int card)
@@ -259,6 +275,7 @@
list_for_each_entry(_df, &shutdown_files, shutdown_list) {
if (_df->file == file) {
df = _df;
+ list_del_init(&df->shutdown_list);
break;
}
}
@@ -347,8 +364,7 @@
/* phase 2: replace file->f_op with special dummy operations */
spin_lock(&card->files_lock);
- mfile = card->files;
- while (mfile) {
+ list_for_each_entry(mfile, &card->files_list, list) {
file = mfile->file;
/* it's critical part, use endless loop */
@@ -361,8 +377,6 @@
mfile->file->f_op = &snd_shutdown_f_ops;
fops_get(mfile->file->f_op);
-
- mfile = mfile->next;
}
spin_unlock(&card->files_lock);
@@ -442,7 +456,7 @@
return ret;
spin_lock(&card->files_lock);
- if (card->files == NULL)
+ if (list_empty(&card->files_list))
free_now = 1;
else
card->free_on_last_close = 1;
@@ -462,7 +476,7 @@
return ret;
/* wait, until all devices are ready for the free operation */
- wait_event(card->shutdown_sleep, card->files == NULL);
+ wait_event(card->shutdown_sleep, list_empty(&card->files_list));
snd_card_do_free(card);
return 0;
}
@@ -809,15 +823,13 @@
return -ENOMEM;
mfile->file = file;
mfile->disconnected_f_op = NULL;
- mfile->next = NULL;
spin_lock(&card->files_lock);
if (card->shutdown) {
spin_unlock(&card->files_lock);
kfree(mfile);
return -ENODEV;
}
- mfile->next = card->files;
- card->files = mfile;
+ list_add(&mfile->list, &card->files_list);
spin_unlock(&card->files_lock);
return 0;
}
@@ -839,29 +851,20 @@
*/
int snd_card_file_remove(struct snd_card *card, struct file *file)
{
- struct snd_monitor_file *mfile, *pfile = NULL;
+ struct snd_monitor_file *mfile, *found = NULL;
int last_close = 0;
spin_lock(&card->files_lock);
- mfile = card->files;
- while (mfile) {
+ list_for_each_entry(mfile, &card->files_list, list) {
if (mfile->file == file) {
- if (pfile)
- pfile->next = mfile->next;
- else
- card->files = mfile->next;
+ list_del(&mfile->list);
+ if (mfile->disconnected_f_op)
+ fops_put(mfile->disconnected_f_op);
+ found = mfile;
break;
}
- pfile = mfile;
- mfile = mfile->next;
}
- if (mfile && mfile->disconnected_f_op) {
- fops_put(mfile->disconnected_f_op);
- spin_lock(&shutdown_lock);
- list_del(&mfile->shutdown_list);
- spin_unlock(&shutdown_lock);
- }
- if (card->files == NULL)
+ if (list_empty(&card->files_list))
last_close = 1;
spin_unlock(&card->files_lock);
if (last_close) {
@@ -869,11 +872,11 @@
if (card->free_on_last_close)
snd_card_do_free(card);
}
- if (!mfile) {
+ if (!found) {
snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);
return -ENOENT;
}
- kfree(mfile);
+ kfree(found);
return 0;
}
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 077a852..c8254c6 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -23,6 +23,14 @@
#include <sound/jack.h>
#include <sound/core.h>
+static int jack_types[] = {
+ SW_HEADPHONE_INSERT,
+ SW_MICROPHONE_INSERT,
+ SW_LINEOUT_INSERT,
+ SW_JACK_PHYSICAL_INSERT,
+ SW_VIDEOOUT_INSERT,
+};
+
static int snd_jack_dev_free(struct snd_device *device)
{
struct snd_jack *jack = device->device_data;
@@ -79,6 +87,7 @@
{
struct snd_jack *jack;
int err;
+ int i;
static struct snd_device_ops ops = {
.dev_free = snd_jack_dev_free,
.dev_register = snd_jack_dev_register,
@@ -100,18 +109,10 @@
jack->type = type;
- if (type & SND_JACK_HEADPHONE)
- input_set_capability(jack->input_dev, EV_SW,
- SW_HEADPHONE_INSERT);
- if (type & SND_JACK_LINEOUT)
- input_set_capability(jack->input_dev, EV_SW,
- SW_LINEOUT_INSERT);
- if (type & SND_JACK_MICROPHONE)
- input_set_capability(jack->input_dev, EV_SW,
- SW_MICROPHONE_INSERT);
- if (type & SND_JACK_MECHANICAL)
- input_set_capability(jack->input_dev, EV_SW,
- SW_JACK_PHYSICAL_INSERT);
+ for (i = 0; i < ARRAY_SIZE(jack_types); i++)
+ if (type & (1 << i))
+ input_set_capability(jack->input_dev, EV_SW,
+ jack_types[i]);
err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
if (err < 0)
@@ -154,21 +155,17 @@
*/
void snd_jack_report(struct snd_jack *jack, int status)
{
+ int i;
+
if (!jack)
return;
- if (jack->type & SND_JACK_HEADPHONE)
- input_report_switch(jack->input_dev, SW_HEADPHONE_INSERT,
- status & SND_JACK_HEADPHONE);
- if (jack->type & SND_JACK_LINEOUT)
- input_report_switch(jack->input_dev, SW_LINEOUT_INSERT,
- status & SND_JACK_LINEOUT);
- if (jack->type & SND_JACK_MICROPHONE)
- input_report_switch(jack->input_dev, SW_MICROPHONE_INSERT,
- status & SND_JACK_MICROPHONE);
- if (jack->type & SND_JACK_MECHANICAL)
- input_report_switch(jack->input_dev, SW_JACK_PHYSICAL_INSERT,
- status & SND_JACK_MECHANICAL);
+ for (i = 0; i < ARRAY_SIZE(jack_types); i++) {
+ int testbit = 1 << i;
+ if (jack->type & testbit)
+ input_report_switch(jack->input_dev, jack_types[i],
+ status & testbit);
+ }
input_sync(jack->input_dev);
}
diff --git a/sound/core/misc.c b/sound/core/misc.c
index 38524f6..a9710e0 100644
--- a/sound/core/misc.c
+++ b/sound/core/misc.c
@@ -95,12 +95,14 @@
{
const struct snd_pci_quirk *q;
- for (q = list; q->subvendor; q++)
- if (q->subvendor == pci->subsystem_vendor &&
- (!q->subdevice || q->subdevice == pci->subsystem_device))
+ for (q = list; q->subvendor; q++) {
+ if (q->subvendor != pci->subsystem_vendor)
+ continue;
+ if (!q->subdevice ||
+ (pci->subsystem_device & q->subdevice_mask) == q->subdevice)
return q;
+ }
return NULL;
}
-
EXPORT_SYMBOL(snd_pci_quirk_lookup);
#endif
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index 699d289..2864cef 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -1160,9 +1160,11 @@
runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
- printk("pcm_oss: write: recovering from XRUN\n");
+ printk(KERN_DEBUG "pcm_oss: write: "
+ "recovering from XRUN\n");
else
- printk("pcm_oss: write: recovering from SUSPEND\n");
+ printk(KERN_DEBUG "pcm_oss: write: "
+ "recovering from SUSPEND\n");
#endif
ret = snd_pcm_oss_prepare(substream);
if (ret < 0)
@@ -1196,9 +1198,11 @@
runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
- printk("pcm_oss: read: recovering from XRUN\n");
+ printk(KERN_DEBUG "pcm_oss: read: "
+ "recovering from XRUN\n");
else
- printk("pcm_oss: read: recovering from SUSPEND\n");
+ printk(KERN_DEBUG "pcm_oss: read: "
+ "recovering from SUSPEND\n");
#endif
ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
if (ret < 0)
@@ -1242,9 +1246,11 @@
runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
- printk("pcm_oss: writev: recovering from XRUN\n");
+ printk(KERN_DEBUG "pcm_oss: writev: "
+ "recovering from XRUN\n");
else
- printk("pcm_oss: writev: recovering from SUSPEND\n");
+ printk(KERN_DEBUG "pcm_oss: writev: "
+ "recovering from SUSPEND\n");
#endif
ret = snd_pcm_oss_prepare(substream);
if (ret < 0)
@@ -1278,9 +1284,11 @@
runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
- printk("pcm_oss: readv: recovering from XRUN\n");
+ printk(KERN_DEBUG "pcm_oss: readv: "
+ "recovering from XRUN\n");
else
- printk("pcm_oss: readv: recovering from SUSPEND\n");
+ printk(KERN_DEBUG "pcm_oss: readv: "
+ "recovering from SUSPEND\n");
#endif
ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
if (ret < 0)
@@ -1533,7 +1541,7 @@
init_waitqueue_entry(&wait, current);
add_wait_queue(&runtime->sleep, &wait);
#ifdef OSS_DEBUG
- printk("sync1: size = %li\n", size);
+ printk(KERN_DEBUG "sync1: size = %li\n", size);
#endif
while (1) {
result = snd_pcm_oss_write2(substream, runtime->oss.buffer, size, 1);
@@ -1590,7 +1598,7 @@
mutex_lock(&runtime->oss.params_lock);
if (runtime->oss.buffer_used > 0) {
#ifdef OSS_DEBUG
- printk("sync: buffer_used\n");
+ printk(KERN_DEBUG "sync: buffer_used\n");
#endif
size = (8 * (runtime->oss.period_bytes - runtime->oss.buffer_used) + 7) / width;
snd_pcm_format_set_silence(format,
@@ -1603,7 +1611,7 @@
}
} else if (runtime->oss.period_ptr > 0) {
#ifdef OSS_DEBUG
- printk("sync: period_ptr\n");
+ printk(KERN_DEBUG "sync: period_ptr\n");
#endif
size = runtime->oss.period_bytes - runtime->oss.period_ptr;
snd_pcm_format_set_silence(format,
@@ -1952,7 +1960,7 @@
int err, cmd;
#ifdef OSS_DEBUG
- printk("pcm_oss: trigger = 0x%x\n", trigger);
+ printk(KERN_DEBUG "pcm_oss: trigger = 0x%x\n", trigger);
#endif
psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
@@ -2170,7 +2178,9 @@
}
#ifdef OSS_DEBUG
- printk("pcm_oss: space: bytes = %i, fragments = %i, fragstotal = %i, fragsize = %i\n", info.bytes, info.fragments, info.fragstotal, info.fragsize);
+ printk(KERN_DEBUG "pcm_oss: space: bytes = %i, fragments = %i, "
+ "fragstotal = %i, fragsize = %i\n",
+ info.bytes, info.fragments, info.fragstotal, info.fragsize);
#endif
if (copy_to_user(_info, &info, sizeof(info)))
return -EFAULT;
@@ -2473,7 +2483,7 @@
if (((cmd >> 8) & 0xff) != 'P')
return -EINVAL;
#ifdef OSS_DEBUG
- printk("pcm_oss: ioctl = 0x%x\n", cmd);
+ printk(KERN_DEBUG "pcm_oss: ioctl = 0x%x\n", cmd);
#endif
switch (cmd) {
case SNDCTL_DSP_RESET:
@@ -2627,7 +2637,8 @@
#else
{
ssize_t res = snd_pcm_oss_read1(substream, buf, count);
- printk("pcm_oss: read %li bytes (returned %li bytes)\n", (long)count, (long)res);
+ printk(KERN_DEBUG "pcm_oss: read %li bytes "
+ "(returned %li bytes)\n", (long)count, (long)res);
return res;
}
#endif
@@ -2646,7 +2657,8 @@
substream->f_flags = file->f_flags & O_NONBLOCK;
result = snd_pcm_oss_write1(substream, buf, count);
#ifdef OSS_DEBUG
- printk("pcm_oss: write %li bytes (wrote %li bytes)\n", (long)count, (long)result);
+ printk(KERN_DEBUG "pcm_oss: write %li bytes (wrote %li bytes)\n",
+ (long)count, (long)result);
#endif
return result;
}
@@ -2720,7 +2732,7 @@
int err;
#ifdef OSS_DEBUG
- printk("pcm_oss: mmap begin\n");
+ printk(KERN_DEBUG "pcm_oss: mmap begin\n");
#endif
pcm_oss_file = file->private_data;
switch ((area->vm_flags & (VM_READ | VM_WRITE))) {
@@ -2770,7 +2782,8 @@
runtime->silence_threshold = 0;
runtime->silence_size = 0;
#ifdef OSS_DEBUG
- printk("pcm_oss: mmap ok, bytes = 0x%x\n", runtime->oss.mmap_bytes);
+ printk(KERN_DEBUG "pcm_oss: mmap ok, bytes = 0x%x\n",
+ runtime->oss.mmap_bytes);
#endif
/* In mmap mode we never stop */
runtime->stop_threshold = runtime->boundary;
diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h
index ca2f4c3..b9afab6 100644
--- a/sound/core/oss/pcm_plugin.h
+++ b/sound/core/oss/pcm_plugin.h
@@ -176,9 +176,9 @@
#endif
#ifdef PLUGIN_DEBUG
-#define pdprintf( fmt, args... ) printk( "plugin: " fmt, ##args)
+#define pdprintf(fmt, args...) printk(KERN_DEBUG "plugin: " fmt, ##args)
#else
-#define pdprintf( fmt, args... )
+#define pdprintf(fmt, args...)
#endif
#endif /* __PCM_PLUGIN_H */
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 192a433..145931a 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -667,7 +667,6 @@
spin_lock_init(&substream->self_group.lock);
INIT_LIST_HEAD(&substream->self_group.substreams);
list_add_tail(&substream->link_list, &substream->self_group.substreams);
- spin_lock_init(&substream->timer_lock);
atomic_set(&substream->mmap_count, 0);
prev = substream;
}
@@ -692,7 +691,7 @@
*
* Returns zero if successful, or a negative error code on failure.
*/
-int snd_pcm_new(struct snd_card *card, char *id, int device,
+int snd_pcm_new(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count,
struct snd_pcm ** rpcm)
{
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 9216910..fbb2e39 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -125,23 +125,32 @@
}
}
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+#define xrun_debug(substream) ((substream)->pstr->xrun_debug)
+#else
+#define xrun_debug(substream) 0
+#endif
+
+#define dump_stack_on_xrun(substream) do { \
+ if (xrun_debug(substream) > 1) \
+ dump_stack(); \
+ } while (0)
+
static void xrun(struct snd_pcm_substream *substream)
{
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- if (substream->pstr->xrun_debug) {
+ if (xrun_debug(substream)) {
snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n",
substream->pcm->card->number,
substream->pcm->device,
substream->stream ? 'c' : 'p');
- if (substream->pstr->xrun_debug > 1)
- dump_stack();
+ dump_stack_on_xrun(substream);
}
-#endif
}
-static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
- struct snd_pcm_runtime *runtime)
+static snd_pcm_uframes_t
+snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
+ struct snd_pcm_runtime *runtime)
{
snd_pcm_uframes_t pos;
@@ -150,17 +159,21 @@
pos = substream->ops->pointer(substream);
if (pos == SNDRV_PCM_POS_XRUN)
return pos; /* XRUN */
-#ifdef CONFIG_SND_DEBUG
if (pos >= runtime->buffer_size) {
- snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size);
+ if (printk_ratelimit()) {
+ snd_printd(KERN_ERR "BUG: stream = %i, pos = 0x%lx, "
+ "buffer size = 0x%lx, period size = 0x%lx\n",
+ substream->stream, pos, runtime->buffer_size,
+ runtime->period_size);
+ }
+ pos = 0;
}
-#endif
pos -= pos % runtime->min_align;
return pos;
}
-static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
- struct snd_pcm_runtime *runtime)
+static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
+ struct snd_pcm_runtime *runtime)
{
snd_pcm_uframes_t avail;
@@ -182,11 +195,21 @@
return 0;
}
-static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
+#define hw_ptr_error(substream, fmt, args...) \
+ do { \
+ if (xrun_debug(substream)) { \
+ if (printk_ratelimit()) { \
+ snd_printd("PCM: " fmt, ##args); \
+ } \
+ dump_stack_on_xrun(substream); \
+ } \
+ } while (0)
+
+static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t pos;
- snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt;
+ snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt, hw_base;
snd_pcm_sframes_t delta;
pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
@@ -194,36 +217,53 @@
xrun(substream);
return -EPIPE;
}
- if (runtime->period_size == runtime->buffer_size)
- goto __next_buf;
- new_hw_ptr = runtime->hw_ptr_base + pos;
+ hw_base = runtime->hw_ptr_base;
+ new_hw_ptr = hw_base + pos;
hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size;
-
- delta = hw_ptr_interrupt - new_hw_ptr;
- if (delta > 0) {
- if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) {
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- if (runtime->periods > 1 && substream->pstr->xrun_debug) {
- snd_printd(KERN_ERR "Unexpected hw_pointer value [1] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2);
- if (substream->pstr->xrun_debug > 1)
- dump_stack();
- }
-#endif
- return 0;
- }
- __next_buf:
- runtime->hw_ptr_base += runtime->buffer_size;
- if (runtime->hw_ptr_base == runtime->boundary)
- runtime->hw_ptr_base = 0;
- new_hw_ptr = runtime->hw_ptr_base + pos;
+ delta = new_hw_ptr - hw_ptr_interrupt;
+ if (hw_ptr_interrupt >= runtime->boundary) {
+ hw_ptr_interrupt -= runtime->boundary;
+ if (hw_base < runtime->boundary / 2)
+ /* hw_base was already lapped; recalc delta */
+ delta = new_hw_ptr - hw_ptr_interrupt;
}
-
+ if (delta < 0) {
+ delta += runtime->buffer_size;
+ if (delta < 0) {
+ hw_ptr_error(substream,
+ "Unexpected hw_pointer value "
+ "(stream=%i, pos=%ld, intr_ptr=%ld)\n",
+ substream->stream, (long)pos,
+ (long)hw_ptr_interrupt);
+ /* rebase to interrupt position */
+ hw_base = new_hw_ptr = hw_ptr_interrupt;
+ /* align hw_base to buffer_size */
+ hw_base -= hw_base % runtime->buffer_size;
+ delta = 0;
+ } else {
+ hw_base += runtime->buffer_size;
+ if (hw_base >= runtime->boundary)
+ hw_base = 0;
+ new_hw_ptr = hw_base + pos;
+ }
+ }
+ if (delta > runtime->period_size) {
+ hw_ptr_error(substream,
+ "Lost interrupts? "
+ "(stream=%i, delta=%ld, intr_ptr=%ld)\n",
+ substream->stream, (long)delta,
+ (long)hw_ptr_interrupt);
+ /* rebase hw_ptr_interrupt */
+ hw_ptr_interrupt =
+ new_hw_ptr - new_hw_ptr % runtime->period_size;
+ }
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
snd_pcm_playback_silence(substream, new_hw_ptr);
+ runtime->hw_ptr_base = hw_base;
runtime->status->hw_ptr = new_hw_ptr;
- runtime->hw_ptr_interrupt = new_hw_ptr - new_hw_ptr % runtime->period_size;
+ runtime->hw_ptr_interrupt = hw_ptr_interrupt;
return snd_pcm_update_hw_ptr_post(substream, runtime);
}
@@ -233,7 +273,7 @@
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t pos;
- snd_pcm_uframes_t old_hw_ptr, new_hw_ptr;
+ snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
snd_pcm_sframes_t delta;
old_hw_ptr = runtime->status->hw_ptr;
@@ -242,29 +282,38 @@
xrun(substream);
return -EPIPE;
}
- new_hw_ptr = runtime->hw_ptr_base + pos;
+ hw_base = runtime->hw_ptr_base;
+ new_hw_ptr = hw_base + pos;
- delta = old_hw_ptr - new_hw_ptr;
- if (delta > 0) {
- if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) {
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- if (runtime->periods > 2 && substream->pstr->xrun_debug) {
- snd_printd(KERN_ERR "Unexpected hw_pointer value [2] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2);
- if (substream->pstr->xrun_debug > 1)
- dump_stack();
- }
-#endif
+ delta = new_hw_ptr - old_hw_ptr;
+ if (delta < 0) {
+ delta += runtime->buffer_size;
+ if (delta < 0) {
+ hw_ptr_error(substream,
+ "Unexpected hw_pointer value [2] "
+ "(stream=%i, pos=%ld, old_ptr=%ld)\n",
+ substream->stream, (long)pos,
+ (long)old_hw_ptr);
return 0;
}
- runtime->hw_ptr_base += runtime->buffer_size;
- if (runtime->hw_ptr_base == runtime->boundary)
- runtime->hw_ptr_base = 0;
- new_hw_ptr = runtime->hw_ptr_base + pos;
+ hw_base += runtime->buffer_size;
+ if (hw_base >= runtime->boundary)
+ hw_base = 0;
+ new_hw_ptr = hw_base + pos;
+ }
+ if (delta > runtime->period_size && runtime->periods > 1) {
+ hw_ptr_error(substream,
+ "hw_ptr skipping! "
+ "(pos=%ld, delta=%ld, period=%ld)\n",
+ (long)pos, (long)delta,
+ (long)runtime->period_size);
+ return 0;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
snd_pcm_playback_silence(substream, new_hw_ptr);
+ runtime->hw_ptr_base = hw_base;
runtime->status->hw_ptr = new_hw_ptr;
return snd_pcm_update_hw_ptr_post(substream, runtime);
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index a789efc..d9b8f53 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -186,7 +186,7 @@
if (!(params->rmask & (1 << k)))
continue;
#ifdef RULES_DEBUG
- printk("%s = ", snd_pcm_hw_param_names[k]);
+ printk(KERN_DEBUG "%s = ", snd_pcm_hw_param_names[k]);
printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
#endif
changed = snd_mask_refine(m, constrs_mask(constrs, k));
@@ -206,7 +206,7 @@
if (!(params->rmask & (1 << k)))
continue;
#ifdef RULES_DEBUG
- printk("%s = ", snd_pcm_hw_param_names[k]);
+ printk(KERN_DEBUG "%s = ", snd_pcm_hw_param_names[k]);
if (i->empty)
printk("empty");
else
@@ -251,7 +251,7 @@
if (!doit)
continue;
#ifdef RULES_DEBUG
- printk("Rule %d [%p]: ", k, r->func);
+ printk(KERN_DEBUG "Rule %d [%p]: ", k, r->func);
if (r->var >= 0) {
printk("%s = ", snd_pcm_hw_param_names[r->var]);
if (hw_is_mask(r->var)) {
diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c
index 2c89c04..ca8068b 100644
--- a/sound/core/pcm_timer.c
+++ b/sound/core/pcm_timer.c
@@ -85,25 +85,19 @@
static int snd_pcm_timer_start(struct snd_timer * timer)
{
- unsigned long flags;
struct snd_pcm_substream *substream;
substream = snd_timer_chip(timer);
- spin_lock_irqsave(&substream->timer_lock, flags);
substream->timer_running = 1;
- spin_unlock_irqrestore(&substream->timer_lock, flags);
return 0;
}
static int snd_pcm_timer_stop(struct snd_timer * timer)
{
- unsigned long flags;
struct snd_pcm_substream *substream;
substream = snd_timer_chip(timer);
- spin_lock_irqsave(&substream->timer_lock, flags);
substream->timer_running = 0;
- spin_unlock_irqrestore(&substream->timer_lock, flags);
return 0;
}
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 002777b..473247c 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -224,156 +224,143 @@
return 0;
}
+/* look for an available substream for the given stream direction;
+ * if a specific subdevice is given, try to assign it
+ */
+static int assign_substream(struct snd_rawmidi *rmidi, int subdevice,
+ int stream, int mode,
+ struct snd_rawmidi_substream **sub_ret)
+{
+ struct snd_rawmidi_substream *substream;
+ struct snd_rawmidi_str *s = &rmidi->streams[stream];
+ static unsigned int info_flags[2] = {
+ [SNDRV_RAWMIDI_STREAM_OUTPUT] = SNDRV_RAWMIDI_INFO_OUTPUT,
+ [SNDRV_RAWMIDI_STREAM_INPUT] = SNDRV_RAWMIDI_INFO_INPUT,
+ };
+
+ if (!(rmidi->info_flags & info_flags[stream]))
+ return -ENXIO;
+ if (subdevice >= 0 && subdevice >= s->substream_count)
+ return -ENODEV;
+ if (s->substream_opened >= s->substream_count)
+ return -EAGAIN;
+
+ list_for_each_entry(substream, &s->substreams, list) {
+ if (substream->opened) {
+ if (stream == SNDRV_RAWMIDI_STREAM_INPUT ||
+ !(mode & SNDRV_RAWMIDI_LFLG_APPEND))
+ continue;
+ }
+ if (subdevice < 0 || subdevice == substream->number) {
+ *sub_ret = substream;
+ return 0;
+ }
+ }
+ return -EAGAIN;
+}
+
+/* open and do ref-counting for the given substream */
+static int open_substream(struct snd_rawmidi *rmidi,
+ struct snd_rawmidi_substream *substream,
+ int mode)
+{
+ int err;
+
+ err = snd_rawmidi_runtime_create(substream);
+ if (err < 0)
+ return err;
+ err = substream->ops->open(substream);
+ if (err < 0)
+ return err;
+ substream->opened = 1;
+ if (substream->use_count++ == 0)
+ substream->active_sensing = 1;
+ if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
+ substream->append = 1;
+ rmidi->streams[substream->stream].substream_opened++;
+ return 0;
+}
+
+static void close_substream(struct snd_rawmidi *rmidi,
+ struct snd_rawmidi_substream *substream,
+ int cleanup);
+
+static int rawmidi_open_priv(struct snd_rawmidi *rmidi, int subdevice, int mode,
+ struct snd_rawmidi_file *rfile)
+{
+ struct snd_rawmidi_substream *sinput = NULL, *soutput = NULL;
+ int err;
+
+ rfile->input = rfile->output = NULL;
+ if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
+ err = assign_substream(rmidi, subdevice,
+ SNDRV_RAWMIDI_STREAM_INPUT,
+ mode, &sinput);
+ if (err < 0)
+ goto __error;
+ }
+ if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
+ err = assign_substream(rmidi, subdevice,
+ SNDRV_RAWMIDI_STREAM_OUTPUT,
+ mode, &soutput);
+ if (err < 0)
+ goto __error;
+ }
+
+ if (sinput) {
+ err = open_substream(rmidi, sinput, mode);
+ if (err < 0)
+ goto __error;
+ }
+ if (soutput) {
+ err = open_substream(rmidi, soutput, mode);
+ if (err < 0) {
+ if (sinput)
+ close_substream(rmidi, sinput, 0);
+ goto __error;
+ }
+ }
+
+ rfile->rmidi = rmidi;
+ rfile->input = sinput;
+ rfile->output = soutput;
+ return 0;
+
+ __error:
+ if (sinput && sinput->runtime)
+ snd_rawmidi_runtime_free(sinput);
+ if (soutput && soutput->runtime)
+ snd_rawmidi_runtime_free(soutput);
+ return err;
+}
+
+/* called from sound/core/seq/seq_midi.c */
int snd_rawmidi_kernel_open(struct snd_card *card, int device, int subdevice,
int mode, struct snd_rawmidi_file * rfile)
{
struct snd_rawmidi *rmidi;
- struct list_head *list1, *list2;
- struct snd_rawmidi_substream *sinput = NULL, *soutput = NULL;
- struct snd_rawmidi_runtime *input = NULL, *output = NULL;
int err;
- if (rfile)
- rfile->input = rfile->output = NULL;
+ if (snd_BUG_ON(!rfile))
+ return -EINVAL;
+
mutex_lock(®ister_mutex);
rmidi = snd_rawmidi_search(card, device);
- mutex_unlock(®ister_mutex);
if (rmidi == NULL) {
- err = -ENODEV;
- goto __error1;
+ mutex_unlock(®ister_mutex);
+ return -ENODEV;
}
if (!try_module_get(rmidi->card->module)) {
- err = -EFAULT;
- goto __error1;
+ mutex_unlock(®ister_mutex);
+ return -ENXIO;
}
- if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))
- mutex_lock(&rmidi->open_mutex);
- if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
- if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT)) {
- err = -ENXIO;
- goto __error;
- }
- if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) {
- err = -ENODEV;
- goto __error;
- }
- if (rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened >=
- rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) {
- err = -EAGAIN;
- goto __error;
- }
- }
- if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
- if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT)) {
- err = -ENXIO;
- goto __error;
- }
- if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) {
- err = -ENODEV;
- goto __error;
- }
- if (rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened >=
- rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) {
- err = -EAGAIN;
- goto __error;
- }
- }
- list1 = rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams.next;
- while (1) {
- if (list1 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
- sinput = NULL;
- if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
- err = -EAGAIN;
- goto __error;
- }
- break;
- }
- sinput = list_entry(list1, struct snd_rawmidi_substream, list);
- if ((mode & SNDRV_RAWMIDI_LFLG_INPUT) && sinput->opened)
- goto __nexti;
- if (subdevice < 0 || (subdevice >= 0 && subdevice == sinput->number))
- break;
- __nexti:
- list1 = list1->next;
- }
- list2 = rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams.next;
- while (1) {
- if (list2 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
- soutput = NULL;
- if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
- err = -EAGAIN;
- goto __error;
- }
- break;
- }
- soutput = list_entry(list2, struct snd_rawmidi_substream, list);
- if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
- if (mode & SNDRV_RAWMIDI_LFLG_APPEND) {
- if (soutput->opened && !soutput->append)
- goto __nexto;
- } else {
- if (soutput->opened)
- goto __nexto;
- }
- }
- if (subdevice < 0 || (subdevice >= 0 && subdevice == soutput->number))
- break;
- __nexto:
- list2 = list2->next;
- }
- if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
- if ((err = snd_rawmidi_runtime_create(sinput)) < 0)
- goto __error;
- input = sinput->runtime;
- if ((err = sinput->ops->open(sinput)) < 0)
- goto __error;
- sinput->opened = 1;
- rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened++;
- } else {
- sinput = NULL;
- }
- if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
- if (soutput->opened)
- goto __skip_output;
- if ((err = snd_rawmidi_runtime_create(soutput)) < 0) {
- if (mode & SNDRV_RAWMIDI_LFLG_INPUT)
- sinput->ops->close(sinput);
- goto __error;
- }
- output = soutput->runtime;
- if ((err = soutput->ops->open(soutput)) < 0) {
- if (mode & SNDRV_RAWMIDI_LFLG_INPUT)
- sinput->ops->close(sinput);
- goto __error;
- }
- __skip_output:
- soutput->opened = 1;
- if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
- soutput->append = 1;
- if (soutput->use_count++ == 0)
- soutput->active_sensing = 1;
- rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened++;
- } else {
- soutput = NULL;
- }
- if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))
- mutex_unlock(&rmidi->open_mutex);
- if (rfile) {
- rfile->rmidi = rmidi;
- rfile->input = sinput;
- rfile->output = soutput;
- }
- return 0;
+ mutex_unlock(®ister_mutex);
- __error:
- if (input != NULL)
- snd_rawmidi_runtime_free(sinput);
- if (output != NULL)
- snd_rawmidi_runtime_free(soutput);
- module_put(rmidi->card->module);
- if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))
- mutex_unlock(&rmidi->open_mutex);
- __error1:
+ mutex_lock(&rmidi->open_mutex);
+ err = rawmidi_open_priv(rmidi, subdevice, mode, rfile);
+ mutex_unlock(&rmidi->open_mutex);
+ if (err < 0)
+ module_put(rmidi->card->module);
return err;
}
@@ -385,10 +372,13 @@
unsigned short fflags;
int err;
struct snd_rawmidi *rmidi;
- struct snd_rawmidi_file *rawmidi_file;
+ struct snd_rawmidi_file *rawmidi_file = NULL;
wait_queue_t wait;
struct snd_ctl_file *kctl;
+ if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK))
+ return -EINVAL; /* invalid combination */
+
if (maj == snd_major) {
rmidi = snd_lookup_minor_data(iminor(inode),
SNDRV_DEVICE_TYPE_RAWMIDI);
@@ -402,24 +392,25 @@
if (rmidi == NULL)
return -ENODEV;
- if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK))
- return -EINVAL; /* invalid combination */
+
+ if (!try_module_get(rmidi->card->module))
+ return -ENXIO;
+
+ mutex_lock(&rmidi->open_mutex);
card = rmidi->card;
err = snd_card_file_add(card, file);
if (err < 0)
- return -ENODEV;
+ goto __error_card;
fflags = snd_rawmidi_file_flags(file);
if ((file->f_flags & O_APPEND) || maj == SOUND_MAJOR) /* OSS emul? */
fflags |= SNDRV_RAWMIDI_LFLG_APPEND;
- fflags |= SNDRV_RAWMIDI_LFLG_NOOPENLOCK;
rawmidi_file = kmalloc(sizeof(*rawmidi_file), GFP_KERNEL);
if (rawmidi_file == NULL) {
- snd_card_file_remove(card, file);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto __error;
}
init_waitqueue_entry(&wait, current);
add_wait_queue(&rmidi->open_wait, &wait);
- mutex_lock(&rmidi->open_mutex);
while (1) {
subdevice = -1;
read_lock(&card->ctl_files_rwlock);
@@ -431,8 +422,7 @@
}
}
read_unlock(&card->ctl_files_rwlock);
- err = snd_rawmidi_kernel_open(rmidi->card, rmidi->device,
- subdevice, fflags, rawmidi_file);
+ err = rawmidi_open_priv(rmidi, subdevice, fflags, rawmidi_file);
if (err >= 0)
break;
if (err == -EAGAIN) {
@@ -451,67 +441,89 @@
break;
}
}
+ remove_wait_queue(&rmidi->open_wait, &wait);
+ if (err < 0) {
+ kfree(rawmidi_file);
+ goto __error;
+ }
#ifdef CONFIG_SND_OSSEMUL
if (rawmidi_file->input && rawmidi_file->input->runtime)
rawmidi_file->input->runtime->oss = (maj == SOUND_MAJOR);
if (rawmidi_file->output && rawmidi_file->output->runtime)
rawmidi_file->output->runtime->oss = (maj == SOUND_MAJOR);
#endif
- remove_wait_queue(&rmidi->open_wait, &wait);
- if (err >= 0) {
- file->private_data = rawmidi_file;
- } else {
- snd_card_file_remove(card, file);
- kfree(rawmidi_file);
- }
+ file->private_data = rawmidi_file;
mutex_unlock(&rmidi->open_mutex);
+ return 0;
+
+ __error:
+ snd_card_file_remove(card, file);
+ __error_card:
+ mutex_unlock(&rmidi->open_mutex);
+ module_put(rmidi->card->module);
return err;
}
-int snd_rawmidi_kernel_release(struct snd_rawmidi_file * rfile)
+static void close_substream(struct snd_rawmidi *rmidi,
+ struct snd_rawmidi_substream *substream,
+ int cleanup)
{
- struct snd_rawmidi *rmidi;
- struct snd_rawmidi_substream *substream;
- struct snd_rawmidi_runtime *runtime;
+ rmidi->streams[substream->stream].substream_opened--;
+ if (--substream->use_count)
+ return;
- if (snd_BUG_ON(!rfile))
- return -ENXIO;
- rmidi = rfile->rmidi;
- mutex_lock(&rmidi->open_mutex);
- if (rfile->input != NULL) {
- substream = rfile->input;
- rfile->input = NULL;
- runtime = substream->runtime;
- snd_rawmidi_input_trigger(substream, 0);
- substream->ops->close(substream);
- if (runtime->private_free != NULL)
- runtime->private_free(substream);
- snd_rawmidi_runtime_free(substream);
- substream->opened = 0;
- rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened--;
- }
- if (rfile->output != NULL) {
- substream = rfile->output;
- rfile->output = NULL;
- if (--substream->use_count == 0) {
- runtime = substream->runtime;
+ if (cleanup) {
+ if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)
+ snd_rawmidi_input_trigger(substream, 0);
+ else {
if (substream->active_sensing) {
unsigned char buf = 0xfe;
- /* sending single active sensing message to shut the device up */
+ /* sending single active sensing message
+ * to shut the device up
+ */
snd_rawmidi_kernel_write(substream, &buf, 1);
}
if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS)
snd_rawmidi_output_trigger(substream, 0);
- substream->ops->close(substream);
- if (runtime->private_free != NULL)
- runtime->private_free(substream);
- snd_rawmidi_runtime_free(substream);
- substream->opened = 0;
- substream->append = 0;
}
- rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened--;
}
+ substream->ops->close(substream);
+ if (substream->runtime->private_free)
+ substream->runtime->private_free(substream);
+ snd_rawmidi_runtime_free(substream);
+ substream->opened = 0;
+ substream->append = 0;
+}
+
+static void rawmidi_release_priv(struct snd_rawmidi_file *rfile)
+{
+ struct snd_rawmidi *rmidi;
+
+ rmidi = rfile->rmidi;
+ mutex_lock(&rmidi->open_mutex);
+ if (rfile->input) {
+ close_substream(rmidi, rfile->input, 1);
+ rfile->input = NULL;
+ }
+ if (rfile->output) {
+ close_substream(rmidi, rfile->output, 1);
+ rfile->output = NULL;
+ }
+ rfile->rmidi = NULL;
mutex_unlock(&rmidi->open_mutex);
+ wake_up(&rmidi->open_wait);
+}
+
+/* called from sound/core/seq/seq_midi.c */
+int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile)
+{
+ struct snd_rawmidi *rmidi;
+
+ if (snd_BUG_ON(!rfile))
+ return -ENXIO;
+
+ rmidi = rfile->rmidi;
+ rawmidi_release_priv(rfile);
module_put(rmidi->card->module);
return 0;
}
@@ -520,15 +532,14 @@
{
struct snd_rawmidi_file *rfile;
struct snd_rawmidi *rmidi;
- int err;
rfile = file->private_data;
- err = snd_rawmidi_kernel_release(rfile);
rmidi = rfile->rmidi;
- wake_up(&rmidi->open_wait);
+ rawmidi_release_priv(rfile);
kfree(rfile);
snd_card_file_remove(rmidi->card, file);
- return err;
+ module_put(rmidi->card->module);
+ return 0;
}
static int snd_rawmidi_info(struct snd_rawmidi_substream *substream,
diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h
index bf8d2b4..c0154a9 100644
--- a/sound/core/seq/oss/seq_oss_device.h
+++ b/sound/core/seq/oss/seq_oss_device.h
@@ -181,7 +181,7 @@
/* for debug */
#ifdef SNDRV_SEQ_OSS_DEBUG
extern int seq_oss_debug;
-#define debug_printk(x) do { if (seq_oss_debug > 0) snd_printk x; } while (0)
+#define debug_printk(x) do { if (seq_oss_debug > 0) snd_printd x; } while (0)
#else
#define debug_printk(x) /**/
#endif
diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c
index 0101a8b..29896ab 100644
--- a/sound/core/seq/seq_prioq.c
+++ b/sound/core/seq/seq_prioq.c
@@ -321,7 +321,8 @@
freeprev = cell;
} else {
#if 0
- printk("type = %i, source = %i, dest = %i, client = %i\n",
+ printk(KERN_DEBUG "type = %i, source = %i, dest = %i, "
+ "client = %i\n",
cell->event.type,
cell->event.source.client,
cell->event.dest.client,
diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c
index 4cc57f9..257624b 100644
--- a/sound/core/vmaster.c
+++ b/sound/core/vmaster.c
@@ -50,18 +50,38 @@
struct link_master *master;
struct link_ctl_info info;
int vals[2]; /* current values */
+ unsigned int flags;
struct snd_kcontrol slave; /* the copy of original control entry */
};
+static int slave_update(struct link_slave *slave)
+{
+ struct snd_ctl_elem_value *uctl;
+ int err, ch;
+
+ uctl = kmalloc(sizeof(*uctl), GFP_KERNEL);
+ if (!uctl)
+ return -ENOMEM;
+ uctl->id = slave->slave.id;
+ err = slave->slave.get(&slave->slave, uctl);
+ for (ch = 0; ch < slave->info.count; ch++)
+ slave->vals[ch] = uctl->value.integer.value[ch];
+ kfree(uctl);
+ return 0;
+}
+
/* get the slave ctl info and save the initial values */
static int slave_init(struct link_slave *slave)
{
struct snd_ctl_elem_info *uinfo;
- struct snd_ctl_elem_value *uctl;
- int err, ch;
+ int err;
- if (slave->info.count)
- return 0; /* already initialized */
+ if (slave->info.count) {
+ /* already initialized */
+ if (slave->flags & SND_CTL_SLAVE_NEED_UPDATE)
+ return slave_update(slave);
+ return 0;
+ }
uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
if (!uinfo)
@@ -85,15 +105,7 @@
slave->info.max_val = uinfo->value.integer.max;
kfree(uinfo);
- uctl = kmalloc(sizeof(*uctl), GFP_KERNEL);
- if (!uctl)
- return -ENOMEM;
- uctl->id = slave->slave.id;
- err = slave->slave.get(&slave->slave, uctl);
- for (ch = 0; ch < slave->info.count; ch++)
- slave->vals[ch] = uctl->value.integer.value[ch];
- kfree(uctl);
- return 0;
+ return slave_update(slave);
}
/* initialize master volume */
@@ -229,7 +241,8 @@
* - logarithmic volume control (dB level), no linear volume
* - master can only attenuate the volume, no gain
*/
-int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave)
+int _snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave,
+ unsigned int flags)
{
struct link_master *master_link = snd_kcontrol_chip(master);
struct link_slave *srec;
@@ -241,6 +254,7 @@
srec->slave = *slave;
memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd));
srec->master = master_link;
+ srec->flags = flags;
/* override callbacks */
slave->info = slave_info;
@@ -254,8 +268,7 @@
list_add_tail(&srec->list, &master_link->slaves);
return 0;
}
-
-EXPORT_SYMBOL(snd_ctl_add_slave);
+EXPORT_SYMBOL(_snd_ctl_add_slave);
/*
* ctl callbacks for master controls
@@ -327,8 +340,20 @@
}
-/*
- * Create a virtual master control with the given name
+/**
+ * snd_ctl_make_virtual_master - Create a virtual master control
+ * @name: name string of the control element to create
+ * @tlv: optional TLV int array for dB information
+ *
+ * Creates a virtual matster control with the given name string.
+ * Returns the created control element, or NULL for errors (ENOMEM).
+ *
+ * After creating a vmaster element, you can add the slave controls
+ * via snd_ctl_add_slave() or snd_ctl_add_slave_uncached().
+ *
+ * The optional argument @tlv can be used to specify the TLV information
+ * for dB scale of the master control. It should be a single element
+ * with #SNDRV_CTL_TLVT_DB_SCALE type, and should be the max 0dB.
*/
struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
const unsigned int *tlv)
@@ -367,5 +392,4 @@
return kctl;
}
-
EXPORT_SYMBOL(snd_ctl_make_virtual_master);
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index 73be7e1..54239d2 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -588,10 +588,10 @@
int idx, err;
int dev = devptr->id;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_dummy));
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_dummy), &card);
+ if (err < 0)
+ return err;
dummy = card->private_data;
dummy->card = card;
for (idx = 0; idx < MAX_PCM_DEVICES && idx < pcm_devs[dev]; idx++) {
diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c
index 7783843..1950ffc 100644
--- a/sound/drivers/ml403-ac97cr.c
+++ b/sound/drivers/ml403-ac97cr.c
@@ -1279,9 +1279,9 @@
if (!enable[dev])
return -ENOENT;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
err = snd_ml403_ac97cr_create(card, pfdev, &ml403_ac97cr);
if (err < 0) {
PDEBUG(INIT_FAILURE, "probe(): create failed!\n");
diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c
index 5b996f3..149d05a 100644
--- a/sound/drivers/mpu401/mpu401.c
+++ b/sound/drivers/mpu401/mpu401.c
@@ -73,9 +73,9 @@
snd_printk(KERN_ERR "the uart_enter option is obsolete; remove it\n");
*rcard = NULL;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
strcpy(card->driver, "MPU-401 UART");
strcpy(card->shortname, card->driver);
sprintf(card->longname, "%s at %#lx, ", card->shortname, port[dev]);
diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c
index 48b64e6..2f8f295 100644
--- a/sound/drivers/mtpav.c
+++ b/sound/drivers/mtpav.c
@@ -303,8 +303,10 @@
snd_mtpav_send_byte(mtp_card, 0xf5);
snd_mtpav_send_byte(mtp_card, portp->hwport);
- //snd_printk("new outport: 0x%x\n", (unsigned int) portp->hwport);
-
+ /*
+ snd_printk(KERN_DEBUG "new outport: 0x%x\n",
+ (unsigned int) portp->hwport);
+ */
if (!(outbyte & 0x80) && portp->running_status)
snd_mtpav_send_byte(mtp_card, portp->running_status);
}
@@ -540,7 +542,7 @@
u8 sbyt = snd_mtpav_getreg(mcrd, SREG);
- //printk("snd_mtpav_read_bytes() sbyt: 0x%x\n", sbyt);
+ /* printk(KERN_DEBUG "snd_mtpav_read_bytes() sbyt: 0x%x\n", sbyt); */
if (!(sbyt & SIGS_BYTE))
return;
@@ -585,12 +587,12 @@
static int __devinit snd_mtpav_get_ISA(struct mtpav * mcard)
{
if ((mcard->res_port = request_region(port, 3, "MotuMTPAV MIDI")) == NULL) {
- snd_printk("MTVAP port 0x%lx is busy\n", port);
+ snd_printk(KERN_ERR "MTVAP port 0x%lx is busy\n", port);
return -EBUSY;
}
mcard->port = port;
if (request_irq(irq, snd_mtpav_irqh, IRQF_DISABLED, "MOTU MTPAV", mcard)) {
- snd_printk("MTVAP IRQ %d busy\n", irq);
+ snd_printk(KERN_ERR "MTVAP IRQ %d busy\n", irq);
return -EBUSY;
}
mcard->irq = irq;
@@ -696,9 +698,9 @@
int err;
struct mtpav *mtp_card;
- card = snd_card_new(index, id, THIS_MODULE, sizeof(*mtp_card));
- if (! card)
- return -ENOMEM;
+ err = snd_card_create(index, id, THIS_MODULE, sizeof(*mtp_card), &card);
+ if (err < 0)
+ return err;
mtp_card = card->private_data;
spin_lock_init(&mtp_card->spinlock);
diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c
index 87ba1dd..9284829 100644
--- a/sound/drivers/mts64.c
+++ b/sound/drivers/mts64.c
@@ -957,10 +957,10 @@
if ((err = snd_mts64_probe_port(p)) < 0)
return err;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL) {
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0) {
snd_printd("Cannot create card\n");
- return -ENOMEM;
+ return err;
}
strcpy(card->driver, DRIVER_NAME);
strcpy(card->shortname, "ESI " CARD_NAME);
@@ -1015,7 +1015,7 @@
goto __err;
}
- snd_printk("ESI Miditerminal 4140 on 0x%lx\n", p->base);
+ snd_printk(KERN_INFO "ESI Miditerminal 4140 on 0x%lx\n", p->base);
return 0;
__err:
diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c
index 7805823..6e31e46 100644
--- a/sound/drivers/opl3/opl3_lib.c
+++ b/sound/drivers/opl3/opl3_lib.c
@@ -302,7 +302,7 @@
opl3 = hw->private_data;
status = inb(opl3->l_port);
#if 0
- snd_printk("AdLib IRQ status = 0x%x\n", status);
+ snd_printk(KERN_DEBUG "AdLib IRQ status = 0x%x\n", status);
#endif
if (!(status & 0x80))
return;
diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c
index 16feafa..6e7d09a 100644
--- a/sound/drivers/opl3/opl3_midi.c
+++ b/sound/drivers/opl3/opl3_midi.c
@@ -125,7 +125,7 @@
int i;
char *str = "x.24";
- printk("time %.5i: %s [%.2i]: ", opl3->use_time, s, voice);
+ printk(KERN_DEBUG "time %.5i: %s [%.2i]: ", opl3->use_time, s, voice);
for (i = 0; i < opl3->max_voices; i++)
printk("%c", *(str + opl3->voices[i].state + 1));
printk("\n");
@@ -218,7 +218,7 @@
for (i = 0; i < END; i++) {
if (best[i].voice >= 0) {
#ifdef DEBUG_ALLOC
- printk("%s %iop allocation on voice %i\n",
+ printk(KERN_DEBUG "%s %iop allocation on voice %i\n",
alloc_type[i], instr_4op ? 4 : 2,
best[i].voice);
#endif
@@ -317,7 +317,7 @@
opl3 = p;
#ifdef DEBUG_MIDI
- snd_printk("Note on, ch %i, inst %i, note %i, vel %i\n",
+ snd_printk(KERN_DEBUG "Note on, ch %i, inst %i, note %i, vel %i\n",
chan->number, chan->midi_program, note, vel);
#endif
@@ -372,7 +372,7 @@
return;
}
#ifdef DEBUG_MIDI
- snd_printk(" --> OPL%i instrument: %s\n",
+ snd_printk(KERN_DEBUG " --> OPL%i instrument: %s\n",
instr_4op ? 3 : 2, patch->name);
#endif
/* in SYNTH mode, application takes care of voices */
@@ -431,7 +431,7 @@
}
#ifdef DEBUG_MIDI
- snd_printk(" --> setting OPL3 connection: 0x%x\n",
+ snd_printk(KERN_DEBUG " --> setting OPL3 connection: 0x%x\n",
opl3->connection_reg);
#endif
/*
@@ -466,7 +466,7 @@
/* Program the FM voice characteristics */
for (i = 0; i < (instr_4op ? 4 : 2); i++) {
#ifdef DEBUG_MIDI
- snd_printk(" --> programming operator %i\n", i);
+ snd_printk(KERN_DEBUG " --> programming operator %i\n", i);
#endif
op_offset = snd_opl3_regmap[voice_offset][i];
@@ -546,7 +546,7 @@
blocknum |= OPL3_KEYON_BIT;
#ifdef DEBUG_MIDI
- snd_printk(" --> trigger voice %i\n", voice);
+ snd_printk(KERN_DEBUG " --> trigger voice %i\n", voice);
#endif
/* Set OPL3 KEYON_BLOCK register of requested voice */
opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
@@ -602,7 +602,7 @@
prg = extra_prg - 1;
}
#ifdef DEBUG_MIDI
- snd_printk(" *** allocating extra program\n");
+ snd_printk(KERN_DEBUG " *** allocating extra program\n");
#endif
goto __extra_prg;
}
@@ -633,7 +633,7 @@
/* kill voice */
#ifdef DEBUG_MIDI
- snd_printk(" --> kill voice %i\n", voice);
+ snd_printk(KERN_DEBUG " --> kill voice %i\n", voice);
#endif
opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
/* clear Key ON bit */
@@ -670,7 +670,7 @@
opl3 = p;
#ifdef DEBUG_MIDI
- snd_printk("Note off, ch %i, inst %i, note %i\n",
+ snd_printk(KERN_DEBUG "Note off, ch %i, inst %i, note %i\n",
chan->number, chan->midi_program, note);
#endif
@@ -709,7 +709,7 @@
opl3 = p;
#ifdef DEBUG_MIDI
- snd_printk("Key pressure, ch#: %i, inst#: %i\n",
+ snd_printk(KERN_DEBUG "Key pressure, ch#: %i, inst#: %i\n",
chan->number, chan->midi_program);
#endif
}
@@ -723,7 +723,7 @@
opl3 = p;
#ifdef DEBUG_MIDI
- snd_printk("Terminate note, ch#: %i, inst#: %i\n",
+ snd_printk(KERN_DEBUG "Terminate note, ch#: %i, inst#: %i\n",
chan->number, chan->midi_program);
#endif
}
@@ -812,7 +812,7 @@
opl3 = p;
#ifdef DEBUG_MIDI
- snd_printk("Controller, TYPE = %i, ch#: %i, inst#: %i\n",
+ snd_printk(KERN_DEBUG "Controller, TYPE = %i, ch#: %i, inst#: %i\n",
type, chan->number, chan->midi_program);
#endif
@@ -849,7 +849,7 @@
opl3 = p;
#ifdef DEBUG_MIDI
- snd_printk("NRPN, ch#: %i, inst#: %i\n",
+ snd_printk(KERN_DEBUG "NRPN, ch#: %i, inst#: %i\n",
chan->number, chan->midi_program);
#endif
}
@@ -864,6 +864,6 @@
opl3 = p;
#ifdef DEBUG_MIDI
- snd_printk("SYSEX\n");
+ snd_printk(KERN_DEBUG "SYSEX\n");
#endif
}
diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c
index 9a2271d..a54b1dc 100644
--- a/sound/drivers/opl3/opl3_oss.c
+++ b/sound/drivers/opl3/opl3_oss.c
@@ -220,14 +220,14 @@
return -EINVAL;
if (count < (int)sizeof(sbi)) {
- snd_printk("FM Error: Patch record too short\n");
+ snd_printk(KERN_ERR "FM Error: Patch record too short\n");
return -EINVAL;
}
if (copy_from_user(&sbi, buf, sizeof(sbi)))
return -EFAULT;
if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) {
- snd_printk("FM Error: Invalid instrument number %d\n",
+ snd_printk(KERN_ERR "FM Error: Invalid instrument number %d\n",
sbi.channel);
return -EINVAL;
}
@@ -254,7 +254,9 @@
opl3 = arg->private_data;
switch (cmd) {
case SNDCTL_FM_LOAD_INSTR:
- snd_printk("OPL3: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
+ snd_printk(KERN_ERR "OPL3: "
+ "Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. "
+ "Fix the program.\n");
return -EINVAL;
case SNDCTL_SYNTH_MEMAVL:
diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c
index 962bb9c..6d57b6441 100644
--- a/sound/drivers/opl3/opl3_synth.c
+++ b/sound/drivers/opl3/opl3_synth.c
@@ -168,7 +168,7 @@
#ifdef CONFIG_SND_DEBUG
default:
- snd_printk("unknown IOCTL: 0x%x\n", cmd);
+ snd_printk(KERN_WARNING "unknown IOCTL: 0x%x\n", cmd);
#endif
}
return -ENOTTY;
diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c
index a4049eb..b60cef2 100644
--- a/sound/drivers/pcsp/pcsp.c
+++ b/sound/drivers/pcsp/pcsp.c
@@ -57,7 +57,7 @@
else
min_div = MAX_DIV;
#if PCSP_DEBUG
- printk("PCSP: lpj=%li, min_div=%i, res=%li\n",
+ printk(KERN_DEBUG "PCSP: lpj=%li, min_div=%i, res=%li\n",
loops_per_jiffy, min_div, tp.tv_nsec);
#endif
@@ -98,9 +98,9 @@
hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
pcsp_chip.timer.function = pcsp_do_timer;
- card = snd_card_new(index, id, THIS_MODULE, 0);
- if (!card)
- return -ENOMEM;
+ err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
err = snd_pcsp_create(card);
if (err < 0) {
diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c
index b1c047e..60158e2 100644
--- a/sound/drivers/portman2x4.c
+++ b/sound/drivers/portman2x4.c
@@ -746,10 +746,10 @@
if ((err = snd_portman_probe_port(p)) < 0)
return err;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL) {
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0) {
snd_printd("Cannot create card\n");
- return -ENOMEM;
+ return err;
}
strcpy(card->driver, DRIVER_NAME);
strcpy(card->shortname, CARD_NAME);
diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c
index d8aab9d..b2b6d50 100644
--- a/sound/drivers/serial-u16550.c
+++ b/sound/drivers/serial-u16550.c
@@ -241,7 +241,8 @@
snd_rawmidi_receive(uart->midi_input[substream], &c, 1);
if (status & UART_LSR_OE)
- snd_printk("%s: Overrun on device at 0x%lx\n",
+ snd_printk(KERN_WARNING
+ "%s: Overrun on device at 0x%lx\n",
uart->rmidi->name, uart->base);
}
@@ -636,7 +637,8 @@
}
} else {
if (!snd_uart16550_write_buffer(uart, midi_byte)) {
- snd_printk("%s: Buffer overrun on device at 0x%lx\n",
+ snd_printk(KERN_WARNING
+ "%s: Buffer overrun on device at 0x%lx\n",
uart->rmidi->name, uart->base);
return 0;
}
@@ -815,7 +817,8 @@
if (irq >= 0 && irq != SNDRV_AUTO_IRQ) {
if (request_irq(irq, snd_uart16550_interrupt,
IRQF_DISABLED, "Serial MIDI", uart)) {
- snd_printk("irq %d busy. Using Polling.\n", irq);
+ snd_printk(KERN_WARNING
+ "irq %d busy. Using Polling.\n", irq);
} else {
uart->irq = irq;
}
@@ -919,26 +922,29 @@
case SNDRV_SERIAL_GENERIC:
break;
default:
- snd_printk("Adaptor type is out of range 0-%d (%d)\n",
+ snd_printk(KERN_ERR
+ "Adaptor type is out of range 0-%d (%d)\n",
SNDRV_SERIAL_MAX_ADAPTOR, adaptor[dev]);
return -ENODEV;
}
if (outs[dev] < 1 || outs[dev] > SNDRV_SERIAL_MAX_OUTS) {
- snd_printk("Count of outputs is out of range 1-%d (%d)\n",
+ snd_printk(KERN_ERR
+ "Count of outputs is out of range 1-%d (%d)\n",
SNDRV_SERIAL_MAX_OUTS, outs[dev]);
return -ENODEV;
}
if (ins[dev] < 1 || ins[dev] > SNDRV_SERIAL_MAX_INS) {
- snd_printk("Count of inputs is out of range 1-%d (%d)\n",
+ snd_printk(KERN_ERR
+ "Count of inputs is out of range 1-%d (%d)\n",
SNDRV_SERIAL_MAX_INS, ins[dev]);
return -ENODEV;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
strcpy(card->driver, "Serial");
strcpy(card->shortname, "Serial MIDI (UART16550A)");
diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c
index f79e361..0e631c3 100644
--- a/sound/drivers/virmidi.c
+++ b/sound/drivers/virmidi.c
@@ -90,15 +90,17 @@
int idx, err;
int dev = devptr->id;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_virmidi));
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_virmidi), &card);
+ if (err < 0)
+ return err;
vmidi = (struct snd_card_virmidi *)card->private_data;
vmidi->card = card;
if (midi_devs[dev] > MAX_MIDI_DEVICES) {
- snd_printk("too much midi devices for virmidi %d: force to use %d\n", dev, MAX_MIDI_DEVICES);
+ snd_printk(KERN_WARNING
+ "too much midi devices for virmidi %d: "
+ "force to use %d\n", dev, MAX_MIDI_DEVICES);
midi_devs[dev] = MAX_MIDI_DEVICES;
}
for (idx = 0; idx < midi_devs[dev]; idx++) {
diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c
index 14e3354..19c6e37 100644
--- a/sound/drivers/vx/vx_core.c
+++ b/sound/drivers/vx/vx_core.c
@@ -688,7 +688,8 @@
image = dsp->data + i;
/* Wait DSP ready for a new read */
if ((err = vx_wait_isr_bit(chip, ISR_TX_EMPTY)) < 0) {
- printk("dsp loading error at position %d\n", i);
+ printk(KERN_ERR
+ "dsp loading error at position %d\n", i);
return err;
}
cptr = image;
diff --git a/sound/drivers/vx/vx_hwdep.c b/sound/drivers/vx/vx_hwdep.c
index 8d6362e..46df881 100644
--- a/sound/drivers/vx/vx_hwdep.c
+++ b/sound/drivers/vx/vx_hwdep.c
@@ -119,16 +119,6 @@
#else /* old style firmware loading */
-static int vx_hwdep_open(struct snd_hwdep *hw, struct file *file)
-{
- return 0;
-}
-
-static int vx_hwdep_release(struct snd_hwdep *hw, struct file *file)
-{
- return 0;
-}
-
static int vx_hwdep_dsp_status(struct snd_hwdep *hw,
struct snd_hwdep_dsp_status *info)
{
@@ -243,8 +233,6 @@
hw->iface = SNDRV_HWDEP_IFACE_VX;
hw->private_data = chip;
- hw->ops.open = vx_hwdep_open;
- hw->ops.release = vx_hwdep_release;
hw->ops.dsp_status = vx_hwdep_dsp_status;
hw->ops.dsp_load = vx_hwdep_dsp_load;
hw->exclusive = 1;
diff --git a/sound/drivers/vx/vx_uer.c b/sound/drivers/vx/vx_uer.c
index 0e1ba9b..b0560fec 100644
--- a/sound/drivers/vx/vx_uer.c
+++ b/sound/drivers/vx/vx_uer.c
@@ -103,7 +103,7 @@
* returns the frequency of UER, or 0 if not sync,
* or a negative error code.
*/
-static int vx_read_uer_status(struct vx_core *chip, int *mode)
+static int vx_read_uer_status(struct vx_core *chip, unsigned int *mode)
{
int val, freq;
diff --git a/sound/i2c/Makefile b/sound/i2c/Makefile
index 37970666..36879bf 100644
--- a/sound/i2c/Makefile
+++ b/sound/i2c/Makefile
@@ -7,8 +7,6 @@
snd-cs8427-objs := cs8427.o
snd-tea6330t-objs := tea6330t.o
-obj-$(CONFIG_L3) += l3/
-
obj-$(CONFIG_SND) += other/
# Toplevel Module Dependency
diff --git a/sound/i2c/l3/Makefile b/sound/i2c/l3/Makefile
deleted file mode 100644
index 49455b8..0000000
--- a/sound/i2c/l3/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# Makefile for ALSA
-#
-
-snd-uda1341-objs := uda1341.o
-
-# Module Dependency
-obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-uda1341.o
diff --git a/sound/i2c/l3/uda1341.c b/sound/i2c/l3/uda1341.c
deleted file mode 100644
index 9840eb4..0000000
--- a/sound/i2c/l3/uda1341.c
+++ /dev/null
@@ -1,935 +0,0 @@
-/*
- * Philips UDA1341 mixer device driver
- * Copyright (c) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
- *
- * Portions are Copyright (C) 2000 Lernout & Hauspie Speech Products, N.V.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License.
- *
- * History:
- *
- * 2002-03-13 Tomas Kasparek initial release - based on uda1341.c from OSS
- * 2002-03-28 Tomas Kasparek basic mixer is working (volume, bass, treble)
- * 2002-03-30 Tomas Kasparek proc filesystem support, complete mixer and DSP
- * features support
- * 2002-04-12 Tomas Kasparek proc interface update, code cleanup
- * 2002-05-12 Tomas Kasparek another code cleanup
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/ioctl.h>
-
-#include <asm/uaccess.h>
-
-#include <sound/core.h>
-#include <sound/control.h>
-#include <sound/initval.h>
-#include <sound/info.h>
-
-#include <linux/l3/l3.h>
-
-#include <sound/uda1341.h>
-
-/* {{{ HW regs definition */
-
-#define STAT0 0x00
-#define STAT1 0x80
-#define STAT_MASK 0x80
-
-#define DATA0_0 0x00
-#define DATA0_1 0x40
-#define DATA0_2 0x80
-#define DATA_MASK 0xc0
-
-#define IS_DATA0(x) ((x) >= data0_0 && (x) <= data0_2)
-#define IS_DATA1(x) ((x) == data1)
-#define IS_STATUS(x) ((x) == stat0 || (x) == stat1)
-#define IS_EXTEND(x) ((x) >= ext0 && (x) <= ext6)
-
-/* }}} */
-
-
-static const char *peak_names[] = {
- "before",
- "after",
-};
-
-static const char *filter_names[] = {
- "flat",
- "min",
- "min",
- "max",
-};
-
-static const char *mixer_names[] = {
- "double differential",
- "input channel 1 (line in)",
- "input channel 2 (microphone)",
- "digital mixer",
-};
-
-static const char *deemp_names[] = {
- "none",
- "32 kHz",
- "44.1 kHz",
- "48 kHz",
-};
-
-enum uda1341_regs_names {
- stat0,
- stat1,
- data0_0,
- data0_1,
- data0_2,
- data1,
- ext0,
- ext1,
- ext2,
- empty,
- ext4,
- ext5,
- ext6,
- uda1341_reg_last,
-};
-
-static const char *uda1341_reg_names[] = {
- "stat 0 ",
- "stat 1 ",
- "data 00",
- "data 01",
- "data 02",
- "data 1 ",
- "ext 0",
- "ext 1",
- "ext 2",
- "empty",
- "ext 4",
- "ext 5",
- "ext 6",
-};
-
-static const int uda1341_enum_items[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 2, //peak - before/after
- 4, //deemp - none/32/44.1/48
- 0,
- 4, //filter - flat/min/min/max
- 0, 0, 0,
- 4, //mixer - differ/line/mic/mixer
- 0, 0, 0, 0, 0,
-};
-
-static const char ** uda1341_enum_names[] = {
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- peak_names, //peak - before/after
- deemp_names, //deemp - none/32/44.1/48
- NULL,
- filter_names, //filter - flat/min/min/max
- NULL, NULL, NULL,
- mixer_names, //mixer - differ/line/mic/mixer
- NULL, NULL, NULL, NULL, NULL,
-};
-
-typedef int uda1341_cfg[CMD_LAST];
-
-struct uda1341 {
- int (*write) (struct l3_client *uda1341, unsigned short reg, unsigned short val);
- int (*read) (struct l3_client *uda1341, unsigned short reg);
- unsigned char regs[uda1341_reg_last];
- int active;
- spinlock_t reg_lock;
- struct snd_card *card;
- uda1341_cfg cfg;
-#ifdef CONFIG_PM
- unsigned char suspend_regs[uda1341_reg_last];
- uda1341_cfg suspend_cfg;
-#endif
-};
-
-/* transfer 8bit integer into string with binary representation */
-static void int2str_bin8(uint8_t val, char *buf)
-{
- const int size = sizeof(val) * 8;
- int i;
-
- for (i= 0; i < size; i++){
- *(buf++) = (val >> (size - 1)) ? '1' : '0';
- val <<= 1;
- }
- *buf = '\0'; //end the string with zero
-}
-
-/* {{{ HW manipulation routines */
-
-static int snd_uda1341_codec_write(struct l3_client *clnt, unsigned short reg, unsigned short val)
-{
- struct uda1341 *uda = clnt->driver_data;
- unsigned char buf[2] = { 0xc0, 0xe0 }; // for EXT addressing
- int err = 0;
-
- uda->regs[reg] = val;
-
- if (uda->active) {
- if (IS_DATA0(reg)) {
- err = l3_write(clnt, UDA1341_DATA0, (const unsigned char *)&val, 1);
- } else if (IS_DATA1(reg)) {
- err = l3_write(clnt, UDA1341_DATA1, (const unsigned char *)&val, 1);
- } else if (IS_STATUS(reg)) {
- err = l3_write(clnt, UDA1341_STATUS, (const unsigned char *)&val, 1);
- } else if (IS_EXTEND(reg)) {
- buf[0] |= (reg - ext0) & 0x7; //EXT address
- buf[1] |= val; //EXT data
- err = l3_write(clnt, UDA1341_DATA0, (const unsigned char *)buf, 2);
- }
- } else
- printk(KERN_ERR "UDA1341 codec not active!\n");
- return err;
-}
-
-static int snd_uda1341_codec_read(struct l3_client *clnt, unsigned short reg)
-{
- unsigned char val;
- int err;
-
- err = l3_read(clnt, reg, &val, 1);
- if (err == 1)
- // use just 6bits - the rest is address of the reg
- return val & 63;
- return err < 0 ? err : -EIO;
-}
-
-static inline int snd_uda1341_valid_reg(struct l3_client *clnt, unsigned short reg)
-{
- return reg < uda1341_reg_last;
-}
-
-static int snd_uda1341_update_bits(struct l3_client *clnt, unsigned short reg,
- unsigned short mask, unsigned short shift,
- unsigned short value, int flush)
-{
- int change;
- unsigned short old, new;
- struct uda1341 *uda = clnt->driver_data;
-
-#if 0
- printk(KERN_DEBUG "update_bits: reg: %s mask: %d shift: %d val: %d\n",
- uda1341_reg_names[reg], mask, shift, value);
-#endif
-
- if (!snd_uda1341_valid_reg(clnt, reg))
- return -EINVAL;
- spin_lock(&uda->reg_lock);
- old = uda->regs[reg];
- new = (old & ~(mask << shift)) | (value << shift);
- change = old != new;
- if (change) {
- if (flush) uda->write(clnt, reg, new);
- uda->regs[reg] = new;
- }
- spin_unlock(&uda->reg_lock);
- return change;
-}
-
-static int snd_uda1341_cfg_write(struct l3_client *clnt, unsigned short what,
- unsigned short value, int flush)
-{
- struct uda1341 *uda = clnt->driver_data;
- int ret = 0;
-#ifdef CONFIG_PM
- int reg;
-#endif
-
-#if 0
- printk(KERN_DEBUG "cfg_write what: %d value: %d\n", what, value);
-#endif
-
- uda->cfg[what] = value;
-
- switch(what) {
- case CMD_RESET:
- ret = snd_uda1341_update_bits(clnt, data0_2, 1, 2, 1, flush); // MUTE
- ret = snd_uda1341_update_bits(clnt, stat0, 1, 6, 1, flush); // RESET
- ret = snd_uda1341_update_bits(clnt, stat0, 1, 6, 0, flush); // RESTORE
- uda->cfg[CMD_RESET]=0;
- break;
- case CMD_FS:
- ret = snd_uda1341_update_bits(clnt, stat0, 3, 4, value, flush);
- break;
- case CMD_FORMAT:
- ret = snd_uda1341_update_bits(clnt, stat0, 7, 1, value, flush);
- break;
- case CMD_OGAIN:
- ret = snd_uda1341_update_bits(clnt, stat1, 1, 6, value, flush);
- break;
- case CMD_IGAIN:
- ret = snd_uda1341_update_bits(clnt, stat1, 1, 5, value, flush);
- break;
- case CMD_DAC:
- ret = snd_uda1341_update_bits(clnt, stat1, 1, 0, value, flush);
- break;
- case CMD_ADC:
- ret = snd_uda1341_update_bits(clnt, stat1, 1, 1, value, flush);
- break;
- case CMD_VOLUME:
- ret = snd_uda1341_update_bits(clnt, data0_0, 63, 0, value, flush);
- break;
- case CMD_BASS:
- ret = snd_uda1341_update_bits(clnt, data0_1, 15, 2, value, flush);
- break;
- case CMD_TREBBLE:
- ret = snd_uda1341_update_bits(clnt, data0_1, 3, 0, value, flush);
- break;
- case CMD_PEAK:
- ret = snd_uda1341_update_bits(clnt, data0_2, 1, 5, value, flush);
- break;
- case CMD_DEEMP:
- ret = snd_uda1341_update_bits(clnt, data0_2, 3, 3, value, flush);
- break;
- case CMD_MUTE:
- ret = snd_uda1341_update_bits(clnt, data0_2, 1, 2, value, flush);
- break;
- case CMD_FILTER:
- ret = snd_uda1341_update_bits(clnt, data0_2, 3, 0, value, flush);
- break;
- case CMD_CH1:
- ret = snd_uda1341_update_bits(clnt, ext0, 31, 0, value, flush);
- break;
- case CMD_CH2:
- ret = snd_uda1341_update_bits(clnt, ext1, 31, 0, value, flush);
- break;
- case CMD_MIC:
- ret = snd_uda1341_update_bits(clnt, ext2, 7, 2, value, flush);
- break;
- case CMD_MIXER:
- ret = snd_uda1341_update_bits(clnt, ext2, 3, 0, value, flush);
- break;
- case CMD_AGC:
- ret = snd_uda1341_update_bits(clnt, ext4, 1, 4, value, flush);
- break;
- case CMD_IG:
- ret = snd_uda1341_update_bits(clnt, ext4, 3, 0, value & 0x3, flush);
- ret = snd_uda1341_update_bits(clnt, ext5, 31, 0, value >> 2, flush);
- break;
- case CMD_AGC_TIME:
- ret = snd_uda1341_update_bits(clnt, ext6, 7, 2, value, flush);
- break;
- case CMD_AGC_LEVEL:
- ret = snd_uda1341_update_bits(clnt, ext6, 3, 0, value, flush);
- break;
-#ifdef CONFIG_PM
- case CMD_SUSPEND:
- for (reg = stat0; reg < uda1341_reg_last; reg++)
- uda->suspend_regs[reg] = uda->regs[reg];
- for (reg = 0; reg < CMD_LAST; reg++)
- uda->suspend_cfg[reg] = uda->cfg[reg];
- break;
- case CMD_RESUME:
- for (reg = stat0; reg < uda1341_reg_last; reg++)
- snd_uda1341_codec_write(clnt, reg, uda->suspend_regs[reg]);
- for (reg = 0; reg < CMD_LAST; reg++)
- uda->cfg[reg] = uda->suspend_cfg[reg];
- break;
-#endif
- default:
- ret = -EINVAL;
- break;
- }
-
- if (!uda->active)
- printk(KERN_ERR "UDA1341 codec not active!\n");
- return ret;
-}
-
-/* }}} */
-
-/* {{{ Proc interface */
-#ifdef CONFIG_PROC_FS
-
-static const char *format_names[] = {
- "I2S-bus",
- "LSB 16bits",
- "LSB 18bits",
- "LSB 20bits",
- "MSB",
- "in LSB 16bits/out MSB",
- "in LSB 18bits/out MSB",
- "in LSB 20bits/out MSB",
-};
-
-static const char *fs_names[] = {
- "512*fs",
- "384*fs",
- "256*fs",
- "Unused - bad value!",
-};
-
-static const char* bass_values[][16] = {
- {"0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB",
- "0 dB", "0 dB", "0 dB", "0 dB", "undefined", }, //flat
- {"0 dB", "2 dB", "4 dB", "6 dB", "8 dB", "10 dB", "12 dB", "14 dB", "16 dB", "18 dB", "18 dB",
- "18 dB", "18 dB", "18 dB", "18 dB", "undefined",}, // min
- {"0 dB", "2 dB", "4 dB", "6 dB", "8 dB", "10 dB", "12 dB", "14 dB", "16 dB", "18 dB", "18 dB",
- "18 dB", "18 dB", "18 dB", "18 dB", "undefined",}, // min
- {"0 dB", "2 dB", "4 dB", "6 dB", "8 dB", "10 dB", "12 dB", "14 dB", "16 dB", "18 dB", "20 dB",
- "22 dB", "24 dB", "24 dB", "24 dB", "undefined",}, // max
-};
-
-static const char *mic_sens_value[] = {
- "-3 dB", "0 dB", "3 dB", "9 dB", "15 dB", "21 dB", "27 dB", "not used",
-};
-
-static const unsigned short AGC_atime[] = {
- 11, 16, 11, 16, 21, 11, 16, 21,
-};
-
-static const unsigned short AGC_dtime[] = {
- 100, 100, 200, 200, 200, 400, 400, 400,
-};
-
-static const char *AGC_level[] = {
- "-9.0", "-11.5", "-15.0", "-17.5",
-};
-
-static const char *ig_small_value[] = {
- "-3.0", "-2.5", "-2.0", "-1.5", "-1.0", "-0.5",
-};
-
-/*
- * this was computed as peak_value[i] = pow((63-i)*1.42,1.013)
- *
- * UDA1341 datasheet on page 21: Peak value (dB) = (Peak level - 63.5)*5*log2
- * There is an table with these values [level]=value: [3]=-90.31, [7]=-84.29
- * [61]=-2.78, [62] = -1.48, [63] = 0.0
- * I tried to compute it, but using but even using logarithm with base either 10 or 2
- * i was'n able to get values in the table from the formula. So I constructed another
- * formula (see above) to interpolate the values as good as possible. If there is some
- * mistake, please contact me on tomas.kasparek@seznam.cz. Thanks.
- * UDA1341TS datasheet is available at:
- * http://www-us9.semiconductors.com/acrobat/datasheets/UDA1341TS_3.pdf
- */
-static const char *peak_value[] = {
- "-INF dB", "N.A.", "N.A", "90.31 dB", "N.A.", "N.A.", "N.A.", "-84.29 dB",
- "-82.65 dB", "-81.13 dB", "-79.61 dB", "-78.09 dB", "-76.57 dB", "-75.05 dB", "-73.53 dB",
- "-72.01 dB", "-70.49 dB", "-68.97 dB", "-67.45 dB", "-65.93 dB", "-64.41 dB", "-62.90 dB",
- "-61.38 dB", "-59.86 dB", "-58.35 dB", "-56.83 dB", "-55.32 dB", "-53.80 dB", "-52.29 dB",
- "-50.78 dB", "-49.26 dB", "-47.75 dB", "-46.24 dB", "-44.73 dB", "-43.22 dB", "-41.71 dB",
- "-40.20 dB", "-38.69 dB", "-37.19 dB", "-35.68 dB", "-34.17 dB", "-32.67 dB", "-31.17 dB",
- "-29.66 dB", "-28.16 dB", "-26.66 dB", "-25.16 dB", "-23.66 dB", "-22.16 dB", "-20.67 dB",
- "-19.17 dB", "-17.68 dB", "-16.19 dB", "-14.70 dB", "-13.21 dB", "-11.72 dB", "-10.24 dB",
- "-8.76 dB", "-7.28 dB", "-5.81 dB", "-4.34 dB", "-2.88 dB", "-1.43 dB", "0.00 dB",
-};
-
-static void snd_uda1341_proc_read(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
-{
- struct l3_client *clnt = entry->private_data;
- struct uda1341 *uda = clnt->driver_data;
- int peak;
-
- peak = snd_uda1341_codec_read(clnt, UDA1341_DATA1);
- if (peak < 0)
- peak = 0;
-
- snd_iprintf(buffer, "%s\n\n", uda->card->longname);
-
- // for information about computed values see UDA1341TS datasheet pages 15 - 21
- snd_iprintf(buffer, "DAC power : %s\n", uda->cfg[CMD_DAC] ? "on" : "off");
- snd_iprintf(buffer, "ADC power : %s\n", uda->cfg[CMD_ADC] ? "on" : "off");
- snd_iprintf(buffer, "Clock frequency : %s\n", fs_names[uda->cfg[CMD_FS]]);
- snd_iprintf(buffer, "Data format : %s\n\n", format_names[uda->cfg[CMD_FORMAT]]);
-
- snd_iprintf(buffer, "Filter mode : %s\n", filter_names[uda->cfg[CMD_FILTER]]);
- snd_iprintf(buffer, "Mixer mode : %s\n", mixer_names[uda->cfg[CMD_MIXER]]);
- snd_iprintf(buffer, "De-emphasis : %s\n", deemp_names[uda->cfg[CMD_DEEMP]]);
- snd_iprintf(buffer, "Peak detection pos. : %s\n", uda->cfg[CMD_PEAK] ? "after" : "before");
- snd_iprintf(buffer, "Peak value : %s\n\n", peak_value[peak]);
-
- snd_iprintf(buffer, "Automatic Gain Ctrl : %s\n", uda->cfg[CMD_AGC] ? "on" : "off");
- snd_iprintf(buffer, "AGC attack time : %d ms\n", AGC_atime[uda->cfg[CMD_AGC_TIME]]);
- snd_iprintf(buffer, "AGC decay time : %d ms\n", AGC_dtime[uda->cfg[CMD_AGC_TIME]]);
- snd_iprintf(buffer, "AGC output level : %s dB\n\n", AGC_level[uda->cfg[CMD_AGC_LEVEL]]);
-
- snd_iprintf(buffer, "Mute : %s\n", uda->cfg[CMD_MUTE] ? "on" : "off");
-
- if (uda->cfg[CMD_VOLUME] == 0)
- snd_iprintf(buffer, "Volume : 0 dB\n");
- else if (uda->cfg[CMD_VOLUME] < 62)
- snd_iprintf(buffer, "Volume : %d dB\n", -1*uda->cfg[CMD_VOLUME] +1);
- else
- snd_iprintf(buffer, "Volume : -INF dB\n");
- snd_iprintf(buffer, "Bass : %s\n", bass_values[uda->cfg[CMD_FILTER]][uda->cfg[CMD_BASS]]);
- snd_iprintf(buffer, "Trebble : %d dB\n", uda->cfg[CMD_FILTER] ? 2*uda->cfg[CMD_TREBBLE] : 0);
- snd_iprintf(buffer, "Input Gain (6dB) : %s\n", uda->cfg[CMD_IGAIN] ? "on" : "off");
- snd_iprintf(buffer, "Output Gain (6dB) : %s\n", uda->cfg[CMD_OGAIN] ? "on" : "off");
- snd_iprintf(buffer, "Mic sensitivity : %s\n", mic_sens_value[uda->cfg[CMD_MIC]]);
-
-
- if(uda->cfg[CMD_CH1] < 31)
- snd_iprintf(buffer, "Mixer gain channel 1: -%d.%c dB\n",
- ((uda->cfg[CMD_CH1] >> 1) * 3) + (uda->cfg[CMD_CH1] & 1),
- uda->cfg[CMD_CH1] & 1 ? '5' : '0');
- else
- snd_iprintf(buffer, "Mixer gain channel 1: -INF dB\n");
- if(uda->cfg[CMD_CH2] < 31)
- snd_iprintf(buffer, "Mixer gain channel 2: -%d.%c dB\n",
- ((uda->cfg[CMD_CH2] >> 1) * 3) + (uda->cfg[CMD_CH2] & 1),
- uda->cfg[CMD_CH2] & 1 ? '5' : '0');
- else
- snd_iprintf(buffer, "Mixer gain channel 2: -INF dB\n");
-
- if(uda->cfg[CMD_IG] > 5)
- snd_iprintf(buffer, "Input Amp. Gain ch 2: %d.%c dB\n",
- (uda->cfg[CMD_IG] >> 1) -3, uda->cfg[CMD_IG] & 1 ? '5' : '0');
- else
- snd_iprintf(buffer, "Input Amp. Gain ch 2: %s dB\n", ig_small_value[uda->cfg[CMD_IG]]);
-}
-
-static void snd_uda1341_proc_regs_read(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
-{
- struct l3_client *clnt = entry->private_data;
- struct uda1341 *uda = clnt->driver_data;
- int reg;
- char buf[12];
-
- for (reg = 0; reg < uda1341_reg_last; reg ++) {
- if (reg == empty)
- continue;
- int2str_bin8(uda->regs[reg], buf);
- snd_iprintf(buffer, "%s = %s\n", uda1341_reg_names[reg], buf);
- }
-
- int2str_bin8(snd_uda1341_codec_read(clnt, UDA1341_DATA1), buf);
- snd_iprintf(buffer, "DATA1 = %s\n", buf);
-}
-#endif /* CONFIG_PROC_FS */
-
-static void __devinit snd_uda1341_proc_init(struct snd_card *card, struct l3_client *clnt)
-{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(card, "uda1341", &entry))
- snd_info_set_text_ops(entry, clnt, snd_uda1341_proc_read);
- if (! snd_card_proc_new(card, "uda1341-regs", &entry))
- snd_info_set_text_ops(entry, clnt, snd_uda1341_proc_regs_read);
-}
-
-/* }}} */
-
-/* {{{ Mixer controls setting */
-
-/* {{{ UDA1341 single functions */
-
-#define UDA1341_SINGLE(xname, where, reg, shift, mask, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_uda1341_info_single, \
- .get = snd_uda1341_get_single, .put = snd_uda1341_put_single, \
- .private_value = where | (reg << 5) | (shift << 9) | (mask << 12) | (invert << 18) \
-}
-
-static int snd_uda1341_info_single(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- int mask = (kcontrol->private_value >> 12) & 63;
-
- uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = mask;
- return 0;
-}
-
-static int snd_uda1341_get_single(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct l3_client *clnt = snd_kcontrol_chip(kcontrol);
- struct uda1341 *uda = clnt->driver_data;
- int where = kcontrol->private_value & 31;
- int mask = (kcontrol->private_value >> 12) & 63;
- int invert = (kcontrol->private_value >> 18) & 1;
-
- ucontrol->value.integer.value[0] = uda->cfg[where];
- if (invert)
- ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
-
- return 0;
-}
-
-static int snd_uda1341_put_single(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct l3_client *clnt = snd_kcontrol_chip(kcontrol);
- struct uda1341 *uda = clnt->driver_data;
- int where = kcontrol->private_value & 31;
- int reg = (kcontrol->private_value >> 5) & 15;
- int shift = (kcontrol->private_value >> 9) & 7;
- int mask = (kcontrol->private_value >> 12) & 63;
- int invert = (kcontrol->private_value >> 18) & 1;
- unsigned short val;
-
- val = (ucontrol->value.integer.value[0] & mask);
- if (invert)
- val = mask - val;
-
- uda->cfg[where] = val;
- return snd_uda1341_update_bits(clnt, reg, mask, shift, val, FLUSH);
-}
-
-/* }}} */
-
-/* {{{ UDA1341 enum functions */
-
-#define UDA1341_ENUM(xname, where, reg, shift, mask, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_uda1341_info_enum, \
- .get = snd_uda1341_get_enum, .put = snd_uda1341_put_enum, \
- .private_value = where | (reg << 5) | (shift << 9) | (mask << 12) | (invert << 18) \
-}
-
-static int snd_uda1341_info_enum(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- int where = kcontrol->private_value & 31;
- const char **texts;
-
- // this register we don't handle this way
- if (!uda1341_enum_items[where])
- return -EINVAL;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = uda1341_enum_items[where];
-
- if (uinfo->value.enumerated.item >= uda1341_enum_items[where])
- uinfo->value.enumerated.item = uda1341_enum_items[where] - 1;
-
- texts = uda1341_enum_names[where];
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
-
-static int snd_uda1341_get_enum(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct l3_client *clnt = snd_kcontrol_chip(kcontrol);
- struct uda1341 *uda = clnt->driver_data;
- int where = kcontrol->private_value & 31;
-
- ucontrol->value.enumerated.item[0] = uda->cfg[where];
- return 0;
-}
-
-static int snd_uda1341_put_enum(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct l3_client *clnt = snd_kcontrol_chip(kcontrol);
- struct uda1341 *uda = clnt->driver_data;
- int where = kcontrol->private_value & 31;
- int reg = (kcontrol->private_value >> 5) & 15;
- int shift = (kcontrol->private_value >> 9) & 7;
- int mask = (kcontrol->private_value >> 12) & 63;
-
- uda->cfg[where] = (ucontrol->value.enumerated.item[0] & mask);
-
- return snd_uda1341_update_bits(clnt, reg, mask, shift, uda->cfg[where], FLUSH);
-}
-
-/* }}} */
-
-/* {{{ UDA1341 2regs functions */
-
-#define UDA1341_2REGS(xname, where, reg_1, reg_2, shift_1, shift_2, mask_1, mask_2, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), .info = snd_uda1341_info_2regs, \
- .get = snd_uda1341_get_2regs, .put = snd_uda1341_put_2regs, \
- .private_value = where | (reg_1 << 5) | (reg_2 << 9) | (shift_1 << 13) | (shift_2 << 16) | \
- (mask_1 << 19) | (mask_2 << 25) | (invert << 31) \
-}
-
-
-static int snd_uda1341_info_2regs(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- int mask_1 = (kcontrol->private_value >> 19) & 63;
- int mask_2 = (kcontrol->private_value >> 25) & 63;
- int mask;
-
- mask = (mask_2 + 1) * (mask_1 + 1) - 1;
- uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = mask;
- return 0;
-}
-
-static int snd_uda1341_get_2regs(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct l3_client *clnt = snd_kcontrol_chip(kcontrol);
- struct uda1341 *uda = clnt->driver_data;
- int where = kcontrol->private_value & 31;
- int mask_1 = (kcontrol->private_value >> 19) & 63;
- int mask_2 = (kcontrol->private_value >> 25) & 63;
- int invert = (kcontrol->private_value >> 31) & 1;
- int mask;
-
- mask = (mask_2 + 1) * (mask_1 + 1) - 1;
-
- ucontrol->value.integer.value[0] = uda->cfg[where];
- if (invert)
- ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
- return 0;
-}
-
-static int snd_uda1341_put_2regs(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct l3_client *clnt = snd_kcontrol_chip(kcontrol);
- struct uda1341 *uda = clnt->driver_data;
- int where = kcontrol->private_value & 31;
- int reg_1 = (kcontrol->private_value >> 5) & 15;
- int reg_2 = (kcontrol->private_value >> 9) & 15;
- int shift_1 = (kcontrol->private_value >> 13) & 7;
- int shift_2 = (kcontrol->private_value >> 16) & 7;
- int mask_1 = (kcontrol->private_value >> 19) & 63;
- int mask_2 = (kcontrol->private_value >> 25) & 63;
- int invert = (kcontrol->private_value >> 31) & 1;
- int mask;
- unsigned short val1, val2, val;
-
- val = ucontrol->value.integer.value[0];
-
- mask = (mask_2 + 1) * (mask_1 + 1) - 1;
-
- val1 = val & mask_1;
- val2 = (val / (mask_1 + 1)) & mask_2;
-
- if (invert) {
- val1 = mask_1 - val1;
- val2 = mask_2 - val2;
- }
-
- uda->cfg[where] = invert ? mask - val : val;
-
- //FIXME - return value
- snd_uda1341_update_bits(clnt, reg_1, mask_1, shift_1, val1, FLUSH);
- return snd_uda1341_update_bits(clnt, reg_2, mask_2, shift_2, val2, FLUSH);
-}
-
-/* }}} */
-
-static struct snd_kcontrol_new snd_uda1341_controls[] = {
- UDA1341_SINGLE("Master Playback Switch", CMD_MUTE, data0_2, 2, 1, 1),
- UDA1341_SINGLE("Master Playback Volume", CMD_VOLUME, data0_0, 0, 63, 1),
-
- UDA1341_SINGLE("Bass Playback Volume", CMD_BASS, data0_1, 2, 15, 0),
- UDA1341_SINGLE("Treble Playback Volume", CMD_TREBBLE, data0_1, 0, 3, 0),
-
- UDA1341_SINGLE("Input Gain Switch", CMD_IGAIN, stat1, 5, 1, 0),
- UDA1341_SINGLE("Output Gain Switch", CMD_OGAIN, stat1, 6, 1, 0),
-
- UDA1341_SINGLE("Mixer Gain Channel 1 Volume", CMD_CH1, ext0, 0, 31, 1),
- UDA1341_SINGLE("Mixer Gain Channel 2 Volume", CMD_CH2, ext1, 0, 31, 1),
-
- UDA1341_SINGLE("Mic Sensitivity Volume", CMD_MIC, ext2, 2, 7, 0),
-
- UDA1341_SINGLE("AGC Output Level", CMD_AGC_LEVEL, ext6, 0, 3, 0),
- UDA1341_SINGLE("AGC Time Constant", CMD_AGC_TIME, ext6, 2, 7, 0),
- UDA1341_SINGLE("AGC Time Constant Switch", CMD_AGC, ext4, 4, 1, 0),
-
- UDA1341_SINGLE("DAC Power", CMD_DAC, stat1, 0, 1, 0),
- UDA1341_SINGLE("ADC Power", CMD_ADC, stat1, 1, 1, 0),
-
- UDA1341_ENUM("Peak detection", CMD_PEAK, data0_2, 5, 1, 0),
- UDA1341_ENUM("De-emphasis", CMD_DEEMP, data0_2, 3, 3, 0),
- UDA1341_ENUM("Mixer mode", CMD_MIXER, ext2, 0, 3, 0),
- UDA1341_ENUM("Filter mode", CMD_FILTER, data0_2, 0, 3, 0),
-
- UDA1341_2REGS("Gain Input Amplifier Gain (channel 2)", CMD_IG, ext4, ext5, 0, 0, 3, 31, 0),
-};
-
-static void uda1341_free(struct l3_client *clnt)
-{
- l3_detach_client(clnt); // calls kfree for driver_data (struct uda1341)
- kfree(clnt);
-}
-
-static int uda1341_dev_free(struct snd_device *device)
-{
- struct l3_client *clnt = device->device_data;
- uda1341_free(clnt);
- return 0;
-}
-
-int __init snd_chip_uda1341_mixer_new(struct snd_card *card, struct l3_client **clntp)
-{
- static struct snd_device_ops ops = {
- .dev_free = uda1341_dev_free,
- };
- struct l3_client *clnt;
- int idx, err;
-
- if (snd_BUG_ON(!card))
- return -EINVAL;
-
- clnt = kzalloc(sizeof(*clnt), GFP_KERNEL);
- if (clnt == NULL)
- return -ENOMEM;
-
- if ((err = l3_attach_client(clnt, "l3-bit-sa1100-gpio", UDA1341_ALSA_NAME))) {
- kfree(clnt);
- return err;
- }
-
- for (idx = 0; idx < ARRAY_SIZE(snd_uda1341_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_uda1341_controls[idx], clnt))) < 0) {
- uda1341_free(clnt);
- return err;
- }
- }
-
- if ((err = snd_device_new(card, SNDRV_DEV_CODEC, clnt, &ops)) < 0) {
- uda1341_free(clnt);
- return err;
- }
-
- *clntp = clnt;
- strcpy(card->mixername, "UDA1341TS Mixer");
- ((struct uda1341 *)clnt->driver_data)->card = card;
-
- snd_uda1341_proc_init(card, clnt);
-
- return 0;
-}
-
-/* }}} */
-
-/* {{{ L3 operations */
-
-static int uda1341_attach(struct l3_client *clnt)
-{
- struct uda1341 *uda;
-
- uda = kzalloc(sizeof(*uda), 0, GFP_KERNEL);
- if (!uda)
- return -ENOMEM;
-
- /* init fixed parts of my copy of registers */
- uda->regs[stat0] = STAT0;
- uda->regs[stat1] = STAT1;
-
- uda->regs[data0_0] = DATA0_0;
- uda->regs[data0_1] = DATA0_1;
- uda->regs[data0_2] = DATA0_2;
-
- uda->write = snd_uda1341_codec_write;
- uda->read = snd_uda1341_codec_read;
-
- spin_lock_init(&uda->reg_lock);
-
- clnt->driver_data = uda;
- return 0;
-}
-
-static void uda1341_detach(struct l3_client *clnt)
-{
- kfree(clnt->driver_data);
-}
-
-static int
-uda1341_command(struct l3_client *clnt, int cmd, void *arg)
-{
- if (cmd != CMD_READ_REG)
- return snd_uda1341_cfg_write(clnt, cmd, (int) arg, FLUSH);
-
- return snd_uda1341_codec_read(clnt, (int) arg);
-}
-
-static int uda1341_open(struct l3_client *clnt)
-{
- struct uda1341 *uda = clnt->driver_data;
-
- uda->active = 1;
-
- /* init default configuration */
- snd_uda1341_cfg_write(clnt, CMD_RESET, 0, REGS_ONLY);
- snd_uda1341_cfg_write(clnt, CMD_FS, F256, FLUSH); // unknown state after reset
- snd_uda1341_cfg_write(clnt, CMD_FORMAT, LSB16, FLUSH); // unknown state after reset
- snd_uda1341_cfg_write(clnt, CMD_OGAIN, ON, FLUSH); // default off after reset
- snd_uda1341_cfg_write(clnt, CMD_IGAIN, ON, FLUSH); // default off after reset
- snd_uda1341_cfg_write(clnt, CMD_DAC, ON, FLUSH); // ??? default value after reset
- snd_uda1341_cfg_write(clnt, CMD_ADC, ON, FLUSH); // ??? default value after reset
- snd_uda1341_cfg_write(clnt, CMD_VOLUME, 20, FLUSH); // default 0dB after reset
- snd_uda1341_cfg_write(clnt, CMD_BASS, 0, REGS_ONLY); // default value after reset
- snd_uda1341_cfg_write(clnt, CMD_TREBBLE, 0, REGS_ONLY); // default value after reset
- snd_uda1341_cfg_write(clnt, CMD_PEAK, AFTER, REGS_ONLY);// default value after reset
- snd_uda1341_cfg_write(clnt, CMD_DEEMP, NONE, REGS_ONLY);// default value after reset
- //at this moment should be QMUTED by h3600_audio_init
- snd_uda1341_cfg_write(clnt, CMD_MUTE, OFF, REGS_ONLY); // default value after reset
- snd_uda1341_cfg_write(clnt, CMD_FILTER, MAX, FLUSH); // defaul flat after reset
- snd_uda1341_cfg_write(clnt, CMD_CH1, 31, FLUSH); // default value after reset
- snd_uda1341_cfg_write(clnt, CMD_CH2, 4, FLUSH); // default value after reset
- snd_uda1341_cfg_write(clnt, CMD_MIC, 4, FLUSH); // default 0dB after reset
- snd_uda1341_cfg_write(clnt, CMD_MIXER, MIXER, FLUSH); // default doub.dif.mode
- snd_uda1341_cfg_write(clnt, CMD_AGC, OFF, FLUSH); // default value after reset
- snd_uda1341_cfg_write(clnt, CMD_IG, 0, FLUSH); // unknown state after reset
- snd_uda1341_cfg_write(clnt, CMD_AGC_TIME, 0, FLUSH); // default value after reset
- snd_uda1341_cfg_write(clnt, CMD_AGC_LEVEL, 0, FLUSH); // default value after reset
-
- return 0;
-}
-
-static void uda1341_close(struct l3_client *clnt)
-{
- struct uda1341 *uda = clnt->driver_data;
-
- uda->active = 0;
-}
-
-/* }}} */
-
-/* {{{ Module and L3 initialization */
-
-static struct l3_ops uda1341_ops = {
- .open = uda1341_open,
- .command = uda1341_command,
- .close = uda1341_close,
-};
-
-static struct l3_driver uda1341_driver = {
- .name = UDA1341_ALSA_NAME,
- .attach_client = uda1341_attach,
- .detach_client = uda1341_detach,
- .ops = &uda1341_ops,
- .owner = THIS_MODULE,
-};
-
-static int __init uda1341_init(void)
-{
- return l3_add_driver(&uda1341_driver);
-}
-
-static void __exit uda1341_exit(void)
-{
- l3_del_driver(&uda1341_driver);
-}
-
-module_init(uda1341_init);
-module_exit(uda1341_exit);
-
-MODULE_AUTHOR("Tomas Kasparek <tomas.kasparek@seznam.cz>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Philips UDA1341 CODEC driver for ALSA");
-MODULE_SUPPORTED_DEVICE("{{UDA1341,UDA1341TS}}");
-
-EXPORT_SYMBOL(snd_chip_uda1341_mixer_new);
-
-/* }}} */
-
-/*
- * Local variables:
- * indent-tabs-mode: t
- * End:
- */
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index ce0aa04..c5c9a92 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -56,8 +56,8 @@
Say Y here to include support for AD1848 (Analog Devices) or
CS4248 (Cirrus Logic - Crystal Semiconductors) chips.
- For newer chips from Cirrus Logic, use the CS4231, CS4232 or
- CS4236+ drivers.
+ For newer chips from Cirrus Logic, use the CS4231 or CS4232+
+ drivers.
To compile this driver as a module, choose M here: the module
will be called snd-ad1848.
@@ -94,6 +94,8 @@
tristate "C-Media CMI8330"
select SND_WSS_LIB
select SND_SB16_DSP
+ select SND_OPL3_LIB
+ select SND_MPU401_UART
help
Say Y here to include support for soundcards based on the
C-Media CMI8330 chip.
@@ -112,26 +114,15 @@
To compile this driver as a module, choose M here: the module
will be called snd-cs4231.
-config SND_CS4232
- tristate "Generic Cirrus Logic CS4232 driver"
- select SND_OPL3_LIB
- select SND_MPU401_UART
- select SND_WSS_LIB
- help
- Say Y here to include support for CS4232 chips from Cirrus
- Logic - Crystal Semiconductors.
-
- To compile this driver as a module, choose M here: the module
- will be called snd-cs4232.
-
config SND_CS4236
- tristate "Generic Cirrus Logic CS4236+ driver"
+ tristate "Generic Cirrus Logic CS4232/CS4236+ driver"
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_WSS_LIB
help
- Say Y to include support for CS4235,CS4236,CS4237B,CS4238B,
- CS4239 chips from Cirrus Logic - Crystal Semiconductors.
+ Say Y to include support for CS4232,CS4235,CS4236,CS4237B,
+ CS4238B,CS4239 chips from Cirrus Logic - Crystal
+ Semiconductors.
To compile this driver as a module, choose M here: the module
will be called snd-cs4236.
@@ -377,14 +368,17 @@
will be called snd-sgalaxy.
config SND_SSCAPE
- tristate "Ensoniq SoundScape PnP driver"
+ tristate "Ensoniq SoundScape driver"
select SND_HWDEP
select SND_MPU401_UART
select SND_WSS_LIB
help
- Say Y here to include support for Ensoniq SoundScape PnP
+ Say Y here to include support for Ensoniq SoundScape
soundcards.
+ The PCM audio is supported on SoundScape Classic, Elite, PnP
+ and VIVO cards. The MIDI support is very experimental.
+
To compile this driver as a module, choose M here: the module
will be called snd-sscape.
@@ -411,5 +405,36 @@
you need to install the firmware files from the
alsa-firmware package.
+config SND_MSND_PINNACLE
+ tristate "Turtle Beach MultiSound Pinnacle/Fiji driver"
+ depends on X86 && EXPERIMENTAL
+ select FW_LOADER
+ select SND_MPU401_UART
+ select SND_PCM
+ help
+ Say Y to include support for Turtle Beach MultiSound Pinnacle/
+ Fiji soundcards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-msnd-pinnacle.
+
+config SND_MSND_CLASSIC
+ tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey"
+ depends on X86 && EXPERIMENTAL
+ select FW_LOADER
+ select SND_MPU401_UART
+ select SND_PCM
+ help
+ Say M here if you have a Turtle Beach MultiSound Classic, Tahiti or
+ Monterey (not for the Pinnacle or Fiji).
+
+ See <file:Documentation/sound/oss/MultiSound> for important information
+ about this driver. Note that it has been discontinued, but the
+ Voyetra Turtle Beach knowledge base entry for it is still available
+ at <http://www.turtlebeach.com/site/kb_ftp/790.asp>.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-msnd-classic.
+
endif # SND_ISA
diff --git a/sound/isa/Makefile b/sound/isa/Makefile
index 63af13d..b906b9a 100644
--- a/sound/isa/Makefile
+++ b/sound/isa/Makefile
@@ -26,5 +26,5 @@
obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o
obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o
-obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ opti9xx/ \
+obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ msnd/ opti9xx/ \
sb/ wavefront/ wss/
diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c
index 7752424..bbcbf92 100644
--- a/sound/isa/ad1816a/ad1816a.c
+++ b/sound/isa/ad1816a/ad1816a.c
@@ -156,10 +156,12 @@
struct snd_card_ad1816a *acard;
struct snd_ad1816a *chip;
struct snd_opl3 *opl3;
+ struct snd_timer *timer;
- if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_ad1816a))) == NULL)
- return -ENOMEM;
+ error = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_ad1816a), &card);
+ if (error < 0)
+ return error;
acard = (struct snd_card_ad1816a *)card->private_data;
if ((error = snd_card_ad1816a_pnp(dev, acard, pcard, pid))) {
@@ -194,6 +196,12 @@
return error;
}
+ error = snd_ad1816a_timer(chip, 0, &timer);
+ if (error < 0) {
+ snd_card_free(card);
+ return error;
+ }
+
if (mpu_port[dev] > 0) {
if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
mpu_port[dev], 0, mpu_irq[dev], IRQF_DISABLED,
@@ -207,11 +215,8 @@
OPL3_HW_AUTO, 0, &opl3) < 0) {
printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx.\n", fm_port[dev], fm_port[dev] + 2);
} else {
- if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
- snd_card_free(card);
- return error;
- }
- if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+ error = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (error < 0) {
snd_card_free(card);
return error;
}
diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c
index 3bfca7c..05aef8b 100644
--- a/sound/isa/ad1816a/ad1816a_lib.c
+++ b/sound/isa/ad1816a/ad1816a_lib.c
@@ -37,7 +37,7 @@
if (inb(AD1816A_REG(AD1816A_CHIP_STATUS)) & AD1816A_READY)
return 0;
- snd_printk("chip busy.\n");
+ snd_printk(KERN_WARNING "chip busy.\n");
return -EBUSY;
}
@@ -196,7 +196,7 @@
spin_unlock(&chip->lock);
break;
default:
- snd_printk("invalid trigger mode 0x%x.\n", what);
+ snd_printk(KERN_WARNING "invalid trigger mode 0x%x.\n", what);
error = -EINVAL;
}
@@ -377,7 +377,6 @@
.fifo_size = 0,
};
-#if 0 /* not used now */
static int snd_ad1816a_timer_close(struct snd_timer *timer)
{
struct snd_ad1816a *chip = snd_timer_chip(timer);
@@ -442,8 +441,6 @@
.start = snd_ad1816a_timer_start,
.stop = snd_ad1816a_timer_stop,
};
-#endif /* not used now */
-
static int snd_ad1816a_playback_open(struct snd_pcm_substream *substream)
{
@@ -568,7 +565,7 @@
case AD1816A_HW_AD1815: return "AD1815";
case AD1816A_HW_AD18MAX10: return "AD18max10";
default:
- snd_printk("Unknown chip version %d:%d.\n",
+ snd_printk(KERN_WARNING "Unknown chip version %d:%d.\n",
chip->version, chip->hardware);
return "AD1816A - unknown";
}
@@ -687,7 +684,6 @@
return 0;
}
-#if 0 /* not used now */
int __devinit snd_ad1816a_timer(struct snd_ad1816a *chip, int device, struct snd_timer **rtimer)
{
struct snd_timer *timer;
@@ -709,7 +705,6 @@
*rtimer = timer;
return 0;
}
-#endif /* not used now */
/*
*
diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c
index 223a6c0..4beeb6f 100644
--- a/sound/isa/ad1848/ad1848.c
+++ b/sound/isa/ad1848/ad1848.c
@@ -91,9 +91,9 @@
struct snd_pcm *pcm;
int error;
- card = snd_card_new(index[n], id[n], THIS_MODULE, 0);
- if (!card)
- return -EINVAL;
+ error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
+ if (error < 0)
+ return error;
error = snd_wss_create(card, port[n], -1, irq[n], dma1[n], -1,
thinkpad[n] ? WSS_HW_THINKPAD : WSS_HW_DETECT,
diff --git a/sound/isa/adlib.c b/sound/isa/adlib.c
index 374b717..7465ae0 100644
--- a/sound/isa/adlib.c
+++ b/sound/isa/adlib.c
@@ -53,10 +53,10 @@
struct snd_opl3 *opl3;
int error;
- card = snd_card_new(index[n], id[n], THIS_MODULE, 0);
- if (!card) {
+ error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
+ if (error < 0) {
dev_err(dev, "could not create card\n");
- return -EINVAL;
+ return error;
}
card->private_data = request_region(port[n], 4, CRD_NAME);
diff --git a/sound/isa/als100.c b/sound/isa/als100.c
index f1ce30f..5fd52e4 100644
--- a/sound/isa/als100.c
+++ b/sound/isa/als100.c
@@ -163,9 +163,10 @@
struct snd_card_als100 *acard;
struct snd_opl3 *opl3;
- if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_als100))) == NULL)
- return -ENOMEM;
+ error = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_als100), &card);
+ if (error < 0)
+ return error;
acard = card->private_data;
if ((error = snd_card_als100_pnp(dev, acard, pcard, pid))) {
diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c
index 3e74d1a..f7aa637 100644
--- a/sound/isa/azt2320.c
+++ b/sound/isa/azt2320.c
@@ -184,9 +184,10 @@
struct snd_wss *chip;
struct snd_opl3 *opl3;
- if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_azt2320))) == NULL)
- return -ENOMEM;
+ error = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_azt2320), &card);
+ if (error < 0)
+ return error;
acard = (struct snd_card_azt2320 *)card->private_data;
if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) {
diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c
index e49aec7..de83608 100644
--- a/sound/isa/cmi8330.c
+++ b/sound/isa/cmi8330.c
@@ -31,11 +31,11 @@
* To quickly load the module,
*
* modprobe -a snd-cmi8330 sbport=0x220 sbirq=5 sbdma8=1
- * sbdma16=5 wssport=0x530 wssirq=11 wssdma=0
+ * sbdma16=5 wssport=0x530 wssirq=11 wssdma=0 fmport=0x388
*
* This card has two mixers and two PCM devices. I've cheesed it such
* that recording and playback can be done through the same device.
- * The driver "magically" routes the capturing to the AD1848 codec,
+ * The driver "magically" routes the capturing to the CMI8330 codec,
* and playback to the SB16 codec. This allows for full-duplex mode
* to some extent.
* The utilities in alsa-utils are aware of both devices, so passing
@@ -51,6 +51,8 @@
#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/wss.h>
+#include <sound/opl3.h>
+#include <sound/mpu401.h>
#include <sound/sb.h>
#include <sound/initval.h>
@@ -79,6 +81,9 @@
static long wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
static int wssirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
static int wssdma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+static long fmport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static long mpuport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static int mpuirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for CMI8330 soundcard.");
@@ -107,6 +112,12 @@
module_param_array(wssdma, int, NULL, 0444);
MODULE_PARM_DESC(wssdma, "DMA for CMI8330 WSS driver.");
+module_param_array(fmport, long, NULL, 0444);
+MODULE_PARM_DESC(fmport, "FM port # for CMI8330 driver.");
+module_param_array(mpuport, long, NULL, 0444);
+MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8330 driver.");
+module_param_array(mpuirq, int, NULL, 0444);
+MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8330 MPU-401 port.");
#ifdef CONFIG_PNP
static int isa_registered;
static int pnp_registered;
@@ -149,6 +160,7 @@
#ifdef CONFIG_PNP
struct pnp_dev *cap;
struct pnp_dev *play;
+ struct pnp_dev *mpu;
#endif
struct snd_card *card;
struct snd_wss *wss;
@@ -165,7 +177,7 @@
#ifdef CONFIG_PNP
static struct pnp_card_device_id snd_cmi8330_pnpids[] = {
- { .id = "CMI0001", .devs = { { "@@@0001" }, { "@X@0001" } } },
+ { .id = "CMI0001", .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } } },
{ .id = "" }
};
@@ -219,8 +231,10 @@
CMI8330_RMUX3D, 5, 1, 1),
WSS_SINGLE("PC Speaker Playback Volume", 0,
CMI8330_OUTPUTVOL, 3, 3, 0),
-WSS_SINGLE("FM Playback Switch", 0,
- CMI8330_RECMUX, 3, 1, 1),
+WSS_DOUBLE("FM Playback Switch", 0,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE("FM Playback Volume", 0,
+ CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
WSS_SINGLE(SNDRV_CTL_NAME_IEC958("Input ", CAPTURE, SWITCH), 0,
CMI8330_RMUX3D, 7, 1, 1),
WSS_SINGLE(SNDRV_CTL_NAME_IEC958("Input ", PLAYBACK, SWITCH), 0,
@@ -323,16 +337,21 @@
if (acard->play == NULL)
return -EBUSY;
+ acard->mpu = pnp_request_card_device(card, id->devs[2].id, NULL);
+ if (acard->play == NULL)
+ return -EBUSY;
+
pdev = acard->cap;
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "CMI8330/C3D (AD1848) PnP configure failure\n");
+ snd_printk(KERN_ERR "CMI8330/C3D PnP configure failure\n");
return -EBUSY;
}
wssport[dev] = pnp_port_start(pdev, 0);
wssdma[dev] = pnp_dma(pdev, 0);
wssirq[dev] = pnp_irq(pdev, 0);
+ fmport[dev] = pnp_port_start(pdev, 1);
/* allocate SB16 resources */
pdev = acard->play;
@@ -347,6 +366,17 @@
sbdma16[dev] = pnp_dma(pdev, 1);
sbirq[dev] = pnp_irq(pdev, 0);
+ /* allocate MPU-401 resources */
+ pdev = acard->mpu;
+
+ err = pnp_activate_dev(pdev);
+ if (err < 0) {
+ snd_printk(KERN_ERR
+ "CMI8330/C3D (MPU-401) PnP configure failure\n");
+ return -EBUSY;
+ }
+ mpuport[dev] = pnp_port_start(pdev, 0);
+ mpuirq[dev] = pnp_irq(pdev, 0);
return 0;
}
#endif
@@ -467,26 +497,29 @@
#define PFX "cmi8330: "
-static struct snd_card *snd_cmi8330_card_new(int dev)
+static int snd_cmi8330_card_new(int dev, struct snd_card **cardp)
{
struct snd_card *card;
struct snd_cmi8330 *acard;
+ int err;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_cmi8330));
- if (card == NULL) {
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_cmi8330), &card);
+ if (err < 0) {
snd_printk(KERN_ERR PFX "could not get a new card\n");
- return NULL;
+ return err;
}
acard = card->private_data;
acard->card = card;
- return card;
+ *cardp = card;
+ return 0;
}
static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
{
struct snd_cmi8330 *acard;
int i, err;
+ struct snd_opl3 *opl3;
acard = card->private_data;
err = snd_wss_create(card, wssport[dev] + 4, -1,
@@ -494,11 +527,11 @@
wssdma[dev], -1,
WSS_HW_DETECT, 0, &acard->wss);
if (err < 0) {
- snd_printk(KERN_ERR PFX "(AD1848) device busy??\n");
+ snd_printk(KERN_ERR PFX "(CMI8330) device busy??\n");
return err;
}
if (acard->wss->hardware != WSS_HW_CMI8330) {
- snd_printk(KERN_ERR PFX "(AD1848) not found during probe\n");
+ snd_printk(KERN_ERR PFX "(CMI8330) not found during probe\n");
return -ENODEV;
}
@@ -530,6 +563,27 @@
snd_printk(KERN_ERR PFX "failed to create pcms\n");
return err;
}
+ if (fmport[dev] != SNDRV_AUTO_PORT) {
+ if (snd_opl3_create(card,
+ fmport[dev], fmport[dev] + 2,
+ OPL3_HW_AUTO, 0, &opl3) < 0) {
+ snd_printk(KERN_ERR PFX
+ "no OPL device at 0x%lx-0x%lx ?\n",
+ fmport[dev], fmport[dev] + 2);
+ } else {
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ if (mpuport[dev] != SNDRV_AUTO_PORT) {
+ if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+ mpuport[dev], 0, mpuirq[dev],
+ IRQF_DISABLED, NULL) < 0)
+ printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n",
+ mpuport[dev]);
+ }
strcpy(card->driver, "CMI8330/C3D");
strcpy(card->shortname, "C-Media CMI8330/C3D");
@@ -564,9 +618,9 @@
struct snd_card *card;
int err;
- card = snd_cmi8330_card_new(dev);
- if (! card)
- return -ENOMEM;
+ err = snd_cmi8330_card_new(dev, &card);
+ if (err < 0)
+ return err;
snd_card_set_dev(card, pdev);
if ((err = snd_cmi8330_probe(card, dev)) < 0) {
snd_card_free(card);
@@ -628,9 +682,9 @@
if (dev >= SNDRV_CARDS)
return -ENODEV;
- card = snd_cmi8330_card_new(dev);
- if (! card)
- return -ENOMEM;
+ res = snd_cmi8330_card_new(dev, &card);
+ if (res < 0)
+ return res;
if ((res = snd_cmi8330_pnp(dev, card->private_data, pcard, pid)) < 0) {
snd_printk(KERN_ERR PFX "PnP detection failed\n");
snd_card_free(card);
diff --git a/sound/isa/cs423x/Makefile b/sound/isa/cs423x/Makefile
index 5870ca2..6d397e8 100644
--- a/sound/isa/cs423x/Makefile
+++ b/sound/isa/cs423x/Makefile
@@ -3,13 +3,11 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-cs4236-lib-objs := cs4236_lib.o
snd-cs4231-objs := cs4231.o
-snd-cs4232-objs := cs4232.o
-snd-cs4236-objs := cs4236.o
+snd-cs4236-objs := cs4236.o cs4236_lib.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_CS4231) += snd-cs4231.o
-obj-$(CONFIG_SND_CS4232) += snd-cs4232.o
-obj-$(CONFIG_SND_CS4236) += snd-cs4236.o snd-cs4236-lib.o
+obj-$(CONFIG_SND_CS4236) += snd-cs4236.o
+
diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c
index f019d44..cb9153e 100644
--- a/sound/isa/cs423x/cs4231.c
+++ b/sound/isa/cs423x/cs4231.c
@@ -95,9 +95,9 @@
struct snd_pcm *pcm;
int error;
- card = snd_card_new(index[n], id[n], THIS_MODULE, 0);
- if (!card)
- return -EINVAL;
+ error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
+ if (error < 0)
+ return error;
error = snd_wss_create(card, port[n], -1, irq[n], dma1[n], dma2[n],
WSS_HW_DETECT, 0, &chip);
diff --git a/sound/isa/cs423x/cs4232.c b/sound/isa/cs423x/cs4232.c
deleted file mode 100644
index 9fad2e6..0000000
--- a/sound/isa/cs423x/cs4232.c
+++ /dev/null
@@ -1,2 +0,0 @@
-#define CS4232
-#include "cs4236.c"
diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c
index 019c940..a076a6c 100644
--- a/sound/isa/cs423x/cs4236.c
+++ b/sound/isa/cs423x/cs4236.c
@@ -33,17 +33,14 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
-#ifdef CS4232
-MODULE_DESCRIPTION("Cirrus Logic CS4232");
+MODULE_DESCRIPTION("Cirrus Logic CS4232-9");
MODULE_SUPPORTED_DEVICE("{{Turtle Beach,TBS-2000},"
"{Turtle Beach,Tropez Plus},"
"{SIC CrystalWave 32},"
"{Hewlett Packard,Omnibook 5500},"
"{TerraTec,Maestro 32/96},"
- "{Philips,PCA70PS}}");
-#else
-MODULE_DESCRIPTION("Cirrus Logic CS4235-9");
-MODULE_SUPPORTED_DEVICE("{{Crystal Semiconductors,CS4235},"
+ "{Philips,PCA70PS}},"
+ "{{Crystal Semiconductors,CS4235},"
"{Crystal Semiconductors,CS4236},"
"{Crystal Semiconductors,CS4237},"
"{Crystal Semiconductors,CS4238},"
@@ -70,15 +67,11 @@
"{Typhoon Soundsystem,CS4236B},"
"{Turtle Beach,Malibu},"
"{Unknown,Digital PC 5000 Onboard}}");
-#endif
-#ifdef CS4232
-#define IDENT "CS4232"
-#define DEV_NAME "cs4232"
-#else
-#define IDENT "CS4236+"
-#define DEV_NAME "cs4236"
-#endif
+MODULE_ALIAS("snd_cs4232");
+
+#define IDENT "CS4232+"
+#define DEV_NAME "cs4232+"
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
@@ -128,9 +121,7 @@
#ifdef CONFIG_PNP
static int isa_registered;
static int pnpc_registered;
-#ifdef CS4232
static int pnp_registered;
-#endif
#endif /* CONFIG_PNP */
struct snd_card_cs4236 {
@@ -145,11 +136,10 @@
#ifdef CONFIG_PNP
-#ifdef CS4232
/*
* PNP BIOS
*/
-static const struct pnp_device_id snd_cs4232_pnpbiosids[] = {
+static const struct pnp_device_id snd_cs423x_pnpbiosids[] = {
{ .id = "CSC0100" },
{ .id = "CSC0000" },
/* Guillemot Turtlebeach something appears to be cs4232 compatible
@@ -157,10 +147,8 @@
{ .id = "GIM0100" },
{ .id = "" }
};
-MODULE_DEVICE_TABLE(pnp, snd_cs4232_pnpbiosids);
-#endif /* CS4232 */
+MODULE_DEVICE_TABLE(pnp, snd_cs423x_pnpbiosids);
-#ifdef CS4232
#define CS423X_ISAPNP_DRIVER "cs4232_isapnp"
static struct pnp_card_device_id snd_cs423x_pnpids[] = {
/* Philips PCA70PS */
@@ -179,12 +167,6 @@
{ .id = "CSCf032", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } },
/* Netfinity 3000 on-board soundcard */
{ .id = "CSCe825", .devs = { { "CSC0100" }, { "CSC0110" }, { "CSC010f" } } },
- /* --- */
- { .id = "" } /* end */
-};
-#else /* CS4236 */
-#define CS423X_ISAPNP_DRIVER "cs4236_isapnp"
-static struct pnp_card_device_id snd_cs423x_pnpids[] = {
/* Intel Marlin Spike Motherboard - CS4235 */
{ .id = "CSC0225", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } },
/* Intel Marlin Spike Motherboard (#2) - CS4235 */
@@ -266,7 +248,6 @@
/* --- */
{ .id = "" } /* end */
};
-#endif
MODULE_DEVICE_TABLE(pnp_card, snd_cs423x_pnpids);
@@ -323,17 +304,19 @@
return 0;
}
-#ifdef CS4232
-static int __devinit snd_card_cs4232_pnp(int dev, struct snd_card_cs4236 *acard,
- struct pnp_dev *pdev)
+static int __devinit snd_card_cs423x_pnp(int dev, struct snd_card_cs4236 *acard,
+ struct pnp_dev *pdev,
+ struct pnp_dev *cdev)
{
acard->wss = pdev;
if (snd_cs423x_pnp_init_wss(dev, acard->wss) < 0)
return -EBUSY;
- cport[dev] = -1;
+ if (cdev)
+ cport[dev] = pnp_port_start(cdev, 0);
+ else
+ cport[dev] = -1;
return 0;
}
-#endif
static int __devinit snd_card_cs423x_pnpc(int dev, struct snd_card_cs4236 *acard,
struct pnp_card_link *card,
@@ -382,16 +365,18 @@
release_and_free_resource(acard->res_sb_port);
}
-static struct snd_card *snd_cs423x_card_new(int dev)
+static int snd_cs423x_card_new(int dev, struct snd_card **cardp)
{
struct snd_card *card;
+ int err;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_cs4236));
- if (card == NULL)
- return NULL;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_cs4236), &card);
+ if (err < 0)
+ return err;
card->private_free = snd_card_cs4236_free;
- return card;
+ *cardp = card;
+ return 0;
}
static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
@@ -409,40 +394,39 @@
return -EBUSY;
}
-#ifdef CS4232
err = snd_wss_create(card, port[dev], cport[dev],
irq[dev],
dma1[dev], dma2[dev],
- WSS_HW_DETECT, 0, &chip);
+ WSS_HW_DETECT3, 0, &chip);
if (err < 0)
return err;
- acard->chip = chip;
+ if (chip->hardware & WSS_HW_CS4236B_MASK) {
+ snd_wss_free(chip);
+ err = snd_cs4236_create(card,
+ port[dev], cport[dev],
+ irq[dev], dma1[dev], dma2[dev],
+ WSS_HW_DETECT, 0, &chip);
+ if (err < 0)
+ return err;
+ acard->chip = chip;
- err = snd_wss_pcm(chip, 0, &pcm);
- if (err < 0)
- return err;
+ err = snd_cs4236_pcm(chip, 0, &pcm);
+ if (err < 0)
+ return err;
- err = snd_wss_mixer(chip);
- if (err < 0)
- return err;
+ err = snd_cs4236_mixer(chip);
+ if (err < 0)
+ return err;
+ } else {
+ acard->chip = chip;
+ err = snd_wss_pcm(chip, 0, &pcm);
+ if (err < 0)
+ return err;
-#else /* CS4236 */
- err = snd_cs4236_create(card,
- port[dev], cport[dev],
- irq[dev], dma1[dev], dma2[dev],
- WSS_HW_DETECT, 0, &chip);
- if (err < 0)
- return err;
- acard->chip = chip;
-
- err = snd_cs4236_pcm(chip, 0, &pcm);
- if (err < 0)
- return err;
-
- err = snd_cs4236_mixer(chip);
- if (err < 0)
- return err;
-#endif
+ err = snd_wss_mixer(chip);
+ if (err < 0)
+ return err;
+ }
strcpy(card->driver, pcm->name);
strcpy(card->shortname, pcm->name);
sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i",
@@ -512,9 +496,9 @@
struct snd_card *card;
int err;
- card = snd_cs423x_card_new(dev);
- if (! card)
- return -ENOMEM;
+ err = snd_cs423x_card_new(dev, &card);
+ if (err < 0)
+ return err;
snd_card_set_dev(card, pdev);
if ((err = snd_cs423x_probe(card, dev)) < 0) {
snd_card_free(card);
@@ -577,13 +561,14 @@
#ifdef CONFIG_PNP
-#ifdef CS4232
-static int __devinit snd_cs4232_pnpbios_detect(struct pnp_dev *pdev,
+static int __devinit snd_cs423x_pnpbios_detect(struct pnp_dev *pdev,
const struct pnp_device_id *id)
{
static int dev;
int err;
struct snd_card *card;
+ struct pnp_dev *cdev;
+ char cid[PNP_ID_LEN];
if (pnp_device_is_isapnp(pdev))
return -ENOENT; /* we have another procedure - card */
@@ -594,10 +579,19 @@
if (dev >= SNDRV_CARDS)
return -ENODEV;
- card = snd_cs423x_card_new(dev);
- if (! card)
- return -ENOMEM;
- if ((err = snd_card_cs4232_pnp(dev, card->private_data, pdev)) < 0) {
+ /* prepare second id */
+ strcpy(cid, pdev->id[0].id);
+ cid[5] = '1';
+ cdev = NULL;
+ list_for_each_entry(cdev, &(pdev->protocol->devices), protocol_list) {
+ if (!strcmp(cdev->id[0].id, cid))
+ break;
+ }
+ err = snd_cs423x_card_new(dev, &card);
+ if (err < 0)
+ return err;
+ err = snd_card_cs423x_pnp(dev, card->private_data, pdev, cdev);
+ if (err < 0) {
printk(KERN_ERR "PnP BIOS detection failed for " IDENT "\n");
snd_card_free(card);
return err;
@@ -612,35 +606,34 @@
return 0;
}
-static void __devexit snd_cs4232_pnp_remove(struct pnp_dev * pdev)
+static void __devexit snd_cs423x_pnp_remove(struct pnp_dev *pdev)
{
snd_card_free(pnp_get_drvdata(pdev));
pnp_set_drvdata(pdev, NULL);
}
#ifdef CONFIG_PM
-static int snd_cs4232_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
+static int snd_cs423x_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
{
return snd_cs423x_suspend(pnp_get_drvdata(pdev));
}
-static int snd_cs4232_pnp_resume(struct pnp_dev *pdev)
+static int snd_cs423x_pnp_resume(struct pnp_dev *pdev)
{
return snd_cs423x_resume(pnp_get_drvdata(pdev));
}
#endif
-static struct pnp_driver cs4232_pnp_driver = {
- .name = "cs4232-pnpbios",
- .id_table = snd_cs4232_pnpbiosids,
- .probe = snd_cs4232_pnpbios_detect,
- .remove = __devexit_p(snd_cs4232_pnp_remove),
+static struct pnp_driver cs423x_pnp_driver = {
+ .name = "cs423x-pnpbios",
+ .id_table = snd_cs423x_pnpbiosids,
+ .probe = snd_cs423x_pnpbios_detect,
+ .remove = __devexit_p(snd_cs423x_pnp_remove),
#ifdef CONFIG_PM
- .suspend = snd_cs4232_pnp_suspend,
- .resume = snd_cs4232_pnp_resume,
+ .suspend = snd_cs423x_pnp_suspend,
+ .resume = snd_cs423x_pnp_resume,
#endif
};
-#endif /* CS4232 */
static int __devinit snd_cs423x_pnpc_detect(struct pnp_card_link *pcard,
const struct pnp_card_device_id *pid)
@@ -656,9 +649,9 @@
if (dev >= SNDRV_CARDS)
return -ENODEV;
- card = snd_cs423x_card_new(dev);
- if (! card)
- return -ENOMEM;
+ res = snd_cs423x_card_new(dev, &card);
+ if (res < 0)
+ return res;
if ((res = snd_card_cs423x_pnpc(dev, card->private_data, pcard, pid)) < 0) {
printk(KERN_ERR "isapnp detection failed and probing for " IDENT
" is not supported\n");
@@ -714,18 +707,14 @@
#ifdef CONFIG_PNP
if (!err)
isa_registered = 1;
-#ifdef CS4232
- err = pnp_register_driver(&cs4232_pnp_driver);
+ err = pnp_register_driver(&cs423x_pnp_driver);
if (!err)
pnp_registered = 1;
-#endif
err = pnp_register_card_driver(&cs423x_pnpc_driver);
if (!err)
pnpc_registered = 1;
-#ifdef CS4232
if (pnp_registered)
err = 0;
-#endif
if (isa_registered)
err = 0;
#endif
@@ -737,10 +726,8 @@
#ifdef CONFIG_PNP
if (pnpc_registered)
pnp_unregister_card_driver(&cs423x_pnpc_driver);
-#ifdef CS4232
if (pnp_registered)
- pnp_unregister_driver(&cs4232_pnp_driver);
-#endif
+ pnp_unregister_driver(&cs423x_pnp_driver);
if (isa_registered)
#endif
isa_unregister_driver(&cs423x_isa_driver);
diff --git a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c
index 6a85fdc..38835f3 100644
--- a/sound/isa/cs423x/cs4236_lib.c
+++ b/sound/isa/cs423x/cs4236_lib.c
@@ -88,10 +88,6 @@
#include <sound/wss.h>
#include <sound/asoundef.h>
-MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
-MODULE_DESCRIPTION("Routines for control of CS4235/4236B/4237B/4238B/4239 chips");
-MODULE_LICENSE("GPL");
-
/*
*
*/
@@ -286,7 +282,8 @@
if (hardware == WSS_HW_DETECT)
hardware = WSS_HW_DETECT3;
if (cport < 0x100) {
- snd_printk("please, specify control port for CS4236+ chips\n");
+ snd_printk(KERN_ERR "please, specify control port "
+ "for CS4236+ chips\n");
return -ENODEV;
}
err = snd_wss_create(card, port, cport,
@@ -295,7 +292,8 @@
return err;
if (!(chip->hardware & WSS_HW_CS4236B_MASK)) {
- snd_printk("CS4236+: MODE3 and extended registers not available, hardware=0x%x\n",chip->hardware);
+ snd_printk(KERN_ERR "CS4236+: MODE3 and extended registers "
+ "not available, hardware=0x%x\n", chip->hardware);
snd_device_free(card, chip);
return -ENODEV;
}
@@ -303,16 +301,19 @@
{
int idx;
for (idx = 0; idx < 8; idx++)
- snd_printk("CD%i = 0x%x\n", idx, inb(chip->cport + idx));
+ snd_printk(KERN_DEBUG "CD%i = 0x%x\n",
+ idx, inb(chip->cport + idx));
for (idx = 0; idx < 9; idx++)
- snd_printk("C%i = 0x%x\n", idx, snd_cs4236_ctrl_in(chip, idx));
+ snd_printk(KERN_DEBUG "C%i = 0x%x\n",
+ idx, snd_cs4236_ctrl_in(chip, idx));
}
#endif
ver1 = snd_cs4236_ctrl_in(chip, 1);
ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION);
snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n", cport, ver1, ver2);
if (ver1 != ver2) {
- snd_printk("CS4236+ chip detected, but control port 0x%lx is not valid\n", cport);
+ snd_printk(KERN_ERR "CS4236+ chip detected, but "
+ "control port 0x%lx is not valid\n", cport);
snd_device_free(card, chip);
return -ENODEV;
}
@@ -883,7 +884,8 @@
spin_lock_irqsave(&chip->reg_lock, flags);
ucontrol->value.integer.value[0] = chip->image[CS4231_ALT_FEATURE_1] & 0x02 ? 1 : 0;
#if 0
- printk("get valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
+ printk(KERN_DEBUG "get valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, "
+ "C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
snd_wss_in(chip, CS4231_ALT_FEATURE_1),
snd_cs4236_ctrl_in(chip, 3),
snd_cs4236_ctrl_in(chip, 4),
@@ -920,7 +922,8 @@
mutex_unlock(&chip->mce_mutex);
#if 0
- printk("set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
+ printk(KERN_DEBUG "set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, "
+ "C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
snd_wss_in(chip, CS4231_ALT_FEATURE_1),
snd_cs4236_ctrl_in(chip, 3),
snd_cs4236_ctrl_in(chip, 4),
@@ -1015,23 +1018,3 @@
}
return 0;
}
-
-EXPORT_SYMBOL(snd_cs4236_create);
-EXPORT_SYMBOL(snd_cs4236_pcm);
-EXPORT_SYMBOL(snd_cs4236_mixer);
-
-/*
- * INIT part
- */
-
-static int __init alsa_cs4236_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_cs4236_exit(void)
-{
-}
-
-module_init(alsa_cs4236_init)
-module_exit(alsa_cs4236_exit)
diff --git a/sound/isa/dt019x.c b/sound/isa/dt019x.c
index a0242c3..80f5b1a 100644
--- a/sound/isa/dt019x.c
+++ b/sound/isa/dt019x.c
@@ -150,9 +150,10 @@
struct snd_card_dt019x *acard;
struct snd_opl3 *opl3;
- if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_dt019x))) == NULL)
- return -ENOMEM;
+ error = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_dt019x), &card);
+ if (error < 0)
+ return error;
acard = card->private_data;
snd_card_set_dev(card, &pcard->card->dev);
diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c
index b463771..442b081 100644
--- a/sound/isa/es1688/es1688.c
+++ b/sound/isa/es1688/es1688.c
@@ -49,6 +49,7 @@
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */
+static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* Usually 0x388 */
static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */
static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */
@@ -65,6 +66,8 @@
module_param_array(mpu_port, long, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
module_param_array(irq, int, NULL, 0444);
+module_param_array(fm_port, long, NULL, 0444);
+MODULE_PARM_DESC(fm_port, "FM port # for ES1688 driver.");
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
module_param_array(mpu_irq, int, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
@@ -122,9 +125,9 @@
struct snd_pcm *pcm;
int error;
- card = snd_card_new(index[n], id[n], THIS_MODULE, 0);
- if (!card)
- return -EINVAL;
+ error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
+ if (error < 0)
+ return error;
error = snd_es1688_legacy_create(card, dev, n, &chip);
if (error < 0)
@@ -143,13 +146,19 @@
sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i", pcm->name,
chip->port, chip->irq, chip->dma8);
- if (snd_opl3_create(card, chip->port, chip->port + 2,
- OPL3_HW_OPL3, 0, &opl3) < 0)
- dev_warn(dev, "opl3 not detected at 0x%lx\n", chip->port);
- else {
- error = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
- if (error < 0)
- goto out;
+ if (fm_port[n] == SNDRV_AUTO_PORT)
+ fm_port[n] = port[n]; /* share the same port */
+
+ if (fm_port[n] > 0) {
+ if (snd_opl3_create(card, fm_port[n], fm_port[n] + 2,
+ OPL3_HW_OPL3, 0, &opl3) < 0)
+ dev_warn(dev,
+ "opl3 not detected at 0x%lx\n", fm_port[n]);
+ else {
+ error = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (error < 0)
+ goto out;
+ }
}
if (mpu_irq[n] >= 0 && mpu_irq[n] != SNDRV_AUTO_IRQ &&
diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c
index 4fbb508..4c6e14f 100644
--- a/sound/isa/es1688/es1688_lib.c
+++ b/sound/isa/es1688/es1688_lib.c
@@ -45,7 +45,7 @@
return 1;
}
#ifdef CONFIG_SND_DEBUG
- printk("snd_es1688_dsp_command: timeout (0x%x)\n", val);
+ printk(KERN_DEBUG "snd_es1688_dsp_command: timeout (0x%x)\n", val);
#endif
return 0;
}
@@ -167,13 +167,16 @@
hw = ES1688_HW_AUTO;
switch (chip->version & 0xfff0) {
case 0x4880:
- snd_printk("[0x%lx] ESS: AudioDrive ES488 detected, but driver is in another place\n", chip->port);
+ snd_printk(KERN_ERR "[0x%lx] ESS: AudioDrive ES488 detected, "
+ "but driver is in another place\n", chip->port);
return -ENODEV;
case 0x6880:
hw = (chip->version & 0x0f) >= 8 ? ES1688_HW_1688 : ES1688_HW_688;
break;
default:
- snd_printk("[0x%lx] ESS: unknown AudioDrive chip with version 0x%x (Jazz16 soundcard?)\n", chip->port, chip->version);
+ snd_printk(KERN_ERR "[0x%lx] ESS: unknown AudioDrive chip "
+ "with version 0x%x (Jazz16 soundcard?)\n",
+ chip->port, chip->version);
return -ENODEV;
}
@@ -223,7 +226,7 @@
}
}
#if 0
- snd_printk("mpu cfg = 0x%x\n", cfg);
+ snd_printk(KERN_DEBUG "mpu cfg = 0x%x\n", cfg);
#endif
spin_lock_irqsave(&chip->reg_lock, flags);
snd_es1688_mixer_write(chip, 0x40, cfg);
@@ -237,7 +240,9 @@
cfg = 0xf0; /* enable only DMA counter interrupt */
irq_bits = irqs[chip->irq & 0x0f];
if (irq_bits < 0) {
- snd_printk("[0x%lx] ESS: bad IRQ %d for ES1688 chip!!\n", chip->port, chip->irq);
+ snd_printk(KERN_ERR "[0x%lx] ESS: bad IRQ %d "
+ "for ES1688 chip!!\n",
+ chip->port, chip->irq);
#if 0
irq_bits = 0;
cfg = 0x10;
@@ -250,7 +255,8 @@
cfg = 0xf0; /* extended mode DMA enable */
dma = chip->dma8;
if (dma > 3 || dma == 2) {
- snd_printk("[0x%lx] ESS: bad DMA channel %d for ES1688 chip!!\n", chip->port, dma);
+ snd_printk(KERN_ERR "[0x%lx] ESS: bad DMA channel %d "
+ "for ES1688 chip!!\n", chip->port, dma);
#if 0
dma_bits = 0;
cfg = 0x00; /* disable all DMA */
@@ -341,8 +347,9 @@
return -EINVAL; /* something is wrong */
}
#if 0
- printk("trigger: val = 0x%x, value = 0x%x\n", val, value);
- printk("trigger: pointer = 0x%x\n", snd_dma_pointer(chip->dma8, chip->dma_size));
+ printk(KERN_DEBUG "trigger: val = 0x%x, value = 0x%x\n", val, value);
+ printk(KERN_DEBUG "trigger: pointer = 0x%x\n",
+ snd_dma_pointer(chip->dma8, chip->dma_size));
#endif
snd_es1688_write(chip, 0xb8, (val & 0xf0) | value);
spin_unlock(&chip->reg_lock);
diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c
index 90498e4..8cfbff7 100644
--- a/sound/isa/es18xx.c
+++ b/sound/isa/es18xx.c
@@ -2125,10 +2125,10 @@
#define is_isapnp_selected(dev) 0
#endif
-static struct snd_card *snd_es18xx_card_new(int dev)
+static int snd_es18xx_card_new(int dev, struct snd_card **cardp)
{
- return snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_audiodrive));
+ return snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_audiodrive), cardp);
}
static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
@@ -2197,9 +2197,9 @@
struct snd_card *card;
int err;
- card = snd_es18xx_card_new(dev);
- if (! card)
- return -ENOMEM;
+ err = snd_es18xx_card_new(dev, &card);
+ if (err < 0)
+ return err;
snd_card_set_dev(card, devptr);
if ((err = snd_audiodrive_probe(card, dev)) < 0) {
snd_card_free(card);
@@ -2303,9 +2303,9 @@
if (dev >= SNDRV_CARDS)
return -ENODEV;
- card = snd_es18xx_card_new(dev);
- if (! card)
- return -ENOMEM;
+ err = snd_es18xx_card_new(dev, &card);
+ if (err < 0)
+ return err;
if ((err = snd_audiodrive_pnp(dev, card->private_data, pdev)) < 0) {
snd_card_free(card);
return err;
@@ -2362,9 +2362,9 @@
if (dev >= SNDRV_CARDS)
return -ENODEV;
- card = snd_es18xx_card_new(dev);
- if (! card)
- return -ENOMEM;
+ res = snd_es18xx_card_new(dev, &card);
+ if (res < 0)
+ return res;
if ((res = snd_audiodrive_pnpc(dev, card->private_data, pcard, pid)) < 0) {
snd_card_free(card);
diff --git a/sound/isa/gus/gus_dma.c b/sound/isa/gus/gus_dma.c
index f45f611..36c27c8 100644
--- a/sound/isa/gus/gus_dma.c
+++ b/sound/isa/gus/gus_dma.c
@@ -45,7 +45,8 @@
unsigned char dma_cmd;
unsigned int address_high;
- // snd_printk("dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n", addr, (long) buf, count);
+ snd_printdd("dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n",
+ addr, buf_addr, count);
if (gus->gf1.dma1 > 3) {
if (gus->gf1.enh_mode) {
@@ -77,7 +78,8 @@
snd_gf1_dma_ack(gus);
snd_dma_program(gus->gf1.dma1, buf_addr, count, dma_cmd & SNDRV_GF1_DMA_READ ? DMA_MODE_READ : DMA_MODE_WRITE);
#if 0
- snd_printk("address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n", address << 1, count, dma_cmd);
+ snd_printk(KERN_DEBUG "address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n",
+ address << 1, count, dma_cmd);
#endif
spin_lock_irqsave(&gus->reg_lock, flags);
if (gus->gf1.enh_mode) {
@@ -142,7 +144,9 @@
snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
kfree(block);
#if 0
- printk("program dma (IRQ) - addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", addr, (long) buffer, count, cmd);
+ snd_printd(KERN_DEBUG "program dma (IRQ) - "
+ "addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
+ block->addr, block->buf_addr, block->count, block->cmd);
#endif
}
@@ -203,13 +207,16 @@
}
*block = *__block;
block->next = NULL;
-#if 0
- printk("addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n", block->addr, (long) block->buffer, block->count, block->cmd);
-#endif
-#if 0
- printk("gus->gf1.dma_data_pcm_last = 0x%lx\n", (long)gus->gf1.dma_data_pcm_last);
- printk("gus->gf1.dma_data_pcm = 0x%lx\n", (long)gus->gf1.dma_data_pcm);
-#endif
+
+ snd_printdd("addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
+ block->addr, (long) block->buffer, block->count,
+ block->cmd);
+
+ snd_printdd("gus->gf1.dma_data_pcm_last = 0x%lx\n",
+ (long)gus->gf1.dma_data_pcm_last);
+ snd_printdd("gus->gf1.dma_data_pcm = 0x%lx\n",
+ (long)gus->gf1.dma_data_pcm);
+
spin_lock_irqsave(&gus->dma_lock, flags);
if (synth) {
if (gus->gf1.dma_data_synth_last) {
diff --git a/sound/isa/gus/gus_irq.c b/sound/isa/gus/gus_irq.c
index 041894d..2055aff 100644
--- a/sound/isa/gus/gus_irq.c
+++ b/sound/isa/gus/gus_irq.c
@@ -41,7 +41,7 @@
if (status == 0)
return IRQ_RETVAL(handled);
handled = 1;
- // snd_printk("IRQ: status = 0x%x\n", status);
+ /* snd_printk(KERN_DEBUG "IRQ: status = 0x%x\n", status); */
if (status & 0x02) {
STAT_ADD(gus->gf1.interrupt_stat_midi_in);
if (gus->gf1.interrupt_handler_midi_in)
@@ -65,7 +65,9 @@
continue; /* multi request */
already |= _current_; /* mark request */
#if 0
- printk("voice = %i, voice_status = 0x%x, voice_verify = %i\n", voice, voice_status, inb(GUSP(gus, GF1PAGE)));
+ printk(KERN_DEBUG "voice = %i, voice_status = 0x%x, "
+ "voice_verify = %i\n",
+ voice, voice_status, inb(GUSP(gus, GF1PAGE)));
#endif
pvoice = &gus->gf1.voices[voice];
if (pvoice->use) {
diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c
index 38510ae..edb11ee 100644
--- a/sound/isa/gus/gus_pcm.c
+++ b/sound/isa/gus/gus_pcm.c
@@ -82,7 +82,10 @@
count += offset & 31;
offset &= ~31;
- // snd_printk("block change - offset = 0x%x, count = 0x%x\n", offset, count);
+ /*
+ snd_printk(KERN_DEBUG "block change - offset = 0x%x, count = 0x%x\n",
+ offset, count);
+ */
memset(&block, 0, sizeof(block));
block.cmd = SNDRV_GF1_DMA_IRQ;
if (snd_pcm_format_unsigned(runtime->format))
@@ -135,7 +138,11 @@
curr = begin + (pcmp->bpos * pcmp->block_size) / runtime->channels;
end = curr + (pcmp->block_size / runtime->channels);
end -= snd_pcm_format_width(runtime->format) == 16 ? 2 : 1;
- // snd_printk("init: curr=0x%x, begin=0x%x, end=0x%x, ctrl=0x%x, ramp=0x%x, rate=0x%x\n", curr, begin, end, voice_ctrl, ramp_ctrl, rate);
+ /*
+ snd_printk(KERN_DEBUG "init: curr=0x%x, begin=0x%x, end=0x%x, "
+ "ctrl=0x%x, ramp=0x%x, rate=0x%x\n",
+ curr, begin, end, voice_ctrl, ramp_ctrl, rate);
+ */
pan = runtime->channels == 2 ? (!voice ? 1 : 14) : 8;
vol = !voice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
spin_lock_irqsave(&gus->reg_lock, flags);
@@ -205,9 +212,11 @@
ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03;
#if 0
snd_gf1_select_voice(gus, pvoice->number);
- printk("position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
+ printk(KERN_DEBUG "position = 0x%x\n",
+ (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
snd_gf1_select_voice(gus, pcmp->pvoices[1]->number);
- printk("position = 0x%x\n", (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
+ printk(KERN_DEBUG "position = 0x%x\n",
+ (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
snd_gf1_select_voice(gus, pvoice->number);
#endif
pcmp->bpos++;
@@ -299,7 +308,11 @@
unsigned int len;
unsigned long flags;
- // printk("poke block; buf = 0x%x, pos = %i, count = %i, port = 0x%x\n", (int)buf, pos, count, gus->gf1.port);
+ /*
+ printk(KERN_DEBUG
+ "poke block; buf = 0x%x, pos = %i, count = %i, port = 0x%x\n",
+ (int)buf, pos, count, gus->gf1.port);
+ */
while (count > 0) {
len = count;
if (len > 512) /* limit, to allow IRQ */
@@ -680,7 +693,8 @@
runtime->private_free = snd_gf1_pcm_playback_free;
#if 0
- printk("playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n", (long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer);
+ printk(KERN_DEBUG "playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n",
+ (long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer);
#endif
if ((err = snd_gf1_dma_init(gus)) < 0)
return err;
diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c
index f0af3f7..21cc42e 100644
--- a/sound/isa/gus/gus_uart.c
+++ b/sound/isa/gus/gus_uart.c
@@ -129,8 +129,14 @@
}
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
#if 0
- snd_printk("read init - enable = %i, cmd = 0x%x, stat = 0x%x\n", gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
- snd_printk("[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n", gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100), inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));
+ snd_printk(KERN_DEBUG
+ "read init - enable = %i, cmd = 0x%x, stat = 0x%x\n",
+ gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
+ snd_printk(KERN_DEBUG
+ "[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x "
+ "(page = 0x%x)\n",
+ gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100),
+ inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));
#endif
return 0;
}
diff --git a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c
index 426532a..086b8f0 100644
--- a/sound/isa/gus/gusclassic.c
+++ b/sound/isa/gus/gusclassic.c
@@ -148,9 +148,9 @@
struct snd_gus_card *gus;
int error;
- card = snd_card_new(index[n], id[n], THIS_MODULE, 0);
- if (!card)
- return -EINVAL;
+ error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
+ if (error < 0)
+ return error;
if (pcm_channels[n] < 2)
pcm_channels[n] = 2;
diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c
index 7ad4c3b..180a8de 100644
--- a/sound/isa/gus/gusextreme.c
+++ b/sound/isa/gus/gusextreme.c
@@ -241,9 +241,9 @@
struct snd_opl3 *opl3;
int error;
- card = snd_card_new(index[n], id[n], THIS_MODULE, 0);
- if (!card)
- return -EINVAL;
+ error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
+ if (error < 0)
+ return error;
if (mpu_port[n] == SNDRV_AUTO_PORT)
mpu_port[n] = 0;
diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c
index f94c197..f26eac8 100644
--- a/sound/isa/gus/gusmax.c
+++ b/sound/isa/gus/gusmax.c
@@ -214,10 +214,10 @@
struct snd_wss *wss;
struct snd_gusmax *maxcard;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_gusmax));
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_gusmax), &card);
+ if (err < 0)
+ return err;
card->private_free = snd_gusmax_free;
maxcard = (struct snd_gusmax *)card->private_data;
maxcard->card = card;
diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c
index 5faecfb..534a6ec 100644
--- a/sound/isa/gus/interwave.c
+++ b/sound/isa/gus/interwave.c
@@ -170,7 +170,7 @@
unsigned long port = bus->private_value;
#if 0
- printk("i2c_setlines - 0x%lx <- %i,%i\n", port, ctrl, data);
+ printk(KERN_DEBUG "i2c_setlines - 0x%lx <- %i,%i\n", port, ctrl, data);
#endif
outb((data << 1) | ctrl, port);
udelay(10);
@@ -183,7 +183,7 @@
res = inb(port) & 1;
#if 0
- printk("i2c_getclockline - 0x%lx -> %i\n", port, res);
+ printk(KERN_DEBUG "i2c_getclockline - 0x%lx -> %i\n", port, res);
#endif
return res;
}
@@ -197,7 +197,7 @@
udelay(10);
res = (inb(port) & 2) >> 1;
#if 0
- printk("i2c_getdataline - 0x%lx -> %i\n", port, res);
+ printk(KERN_DEBUG "i2c_getdataline - 0x%lx -> %i\n", port, res);
#endif
return res;
}
@@ -342,7 +342,8 @@
snd_gf1_poke(gus, local, d);
snd_gf1_poke(gus, local + 1, d + 1);
#if 0
- printk("d = 0x%x, local = 0x%x, local + 1 = 0x%x, idx << 22 = 0x%x\n",
+ printk(KERN_DEBUG "d = 0x%x, local = 0x%x, "
+ "local + 1 = 0x%x, idx << 22 = 0x%x\n",
d,
snd_gf1_peek(gus, local),
snd_gf1_peek(gus, local + 1),
@@ -356,7 +357,8 @@
}
}
#if 0
- printk("sizes: %i %i %i %i\n", sizes[0], sizes[1], sizes[2], sizes[3]);
+ printk(KERN_DEBUG "sizes: %i %i %i %i\n",
+ sizes[0], sizes[1], sizes[2], sizes[3]);
#endif
}
@@ -410,12 +412,12 @@
lmct = (psizes[3] << 24) | (psizes[2] << 16) |
(psizes[1] << 8) | psizes[0];
#if 0
- printk("lmct = 0x%08x\n", lmct);
+ printk(KERN_DEBUG "lmct = 0x%08x\n", lmct);
#endif
for (i = 0; i < ARRAY_SIZE(lmc); i++)
if (lmct == lmc[i]) {
#if 0
- printk("found !!! %i\n", i);
+ printk(KERN_DEBUG "found !!! %i\n", i);
#endif
snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | i);
snd_interwave_bank_sizes(gus, psizes);
@@ -626,20 +628,22 @@
free_irq(iwcard->irq, (void *)iwcard);
}
-static struct snd_card *snd_interwave_card_new(int dev)
+static int snd_interwave_card_new(int dev, struct snd_card **cardp)
{
struct snd_card *card;
struct snd_interwave *iwcard;
+ int err;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_interwave));
- if (card == NULL)
- return NULL;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_interwave), &card);
+ if (err < 0)
+ return err;
iwcard = card->private_data;
iwcard->card = card;
iwcard->irq = -1;
card->private_free = snd_interwave_free;
- return card;
+ *cardp = card;
+ return 0;
}
static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
@@ -778,9 +782,9 @@
struct snd_card *card;
int err;
- card = snd_interwave_card_new(dev);
- if (! card)
- return -ENOMEM;
+ err = snd_interwave_card_new(dev, &card);
+ if (err < 0)
+ return err;
snd_card_set_dev(card, devptr);
if ((err = snd_interwave_probe(card, dev)) < 0) {
@@ -876,9 +880,9 @@
if (dev >= SNDRV_CARDS)
return -ENODEV;
- card = snd_interwave_card_new(dev);
- if (! card)
- return -ENOMEM;
+ res = snd_interwave_card_new(dev, &card);
+ if (res < 0)
+ return res;
if ((res = snd_interwave_pnp(dev, card->private_data, pcard, pid)) < 0) {
snd_card_free(card);
diff --git a/sound/isa/msnd/Makefile b/sound/isa/msnd/Makefile
new file mode 100644
index 0000000..2171c0a
--- /dev/null
+++ b/sound/isa/msnd/Makefile
@@ -0,0 +1,9 @@
+
+snd-msnd-lib-objs := msnd.o msnd_midi.o msnd_pinnacle_mixer.o
+snd-msnd-pinnacle-objs := msnd_pinnacle.o
+snd-msnd-classic-objs := msnd_classic.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_MSND_PINNACLE) += snd-msnd-pinnacle.o snd-msnd-lib.o
+obj-$(CONFIG_SND_MSND_CLASSIC) += snd-msnd-classic.o snd-msnd-lib.o
+
diff --git a/sound/isa/msnd/msnd.c b/sound/isa/msnd/msnd.c
new file mode 100644
index 0000000..9064544
--- /dev/null
+++ b/sound/isa/msnd/msnd.c
@@ -0,0 +1,705 @@
+/*********************************************************************
+ *
+ * 2002/06/30 Karsten Wiese:
+ * removed kernel-version dependencies.
+ * ripped from linux kernel 2.4.18 (OSS Implementation) by me.
+ * In the OSS Version, this file is compiled to a separate MODULE,
+ * that is used by the pinnacle and the classic driver.
+ * since there is no classic driver for alsa yet (i dont have a classic
+ * & writing one blindfold is difficult) this file's object is statically
+ * linked into the pinnacle-driver-module for now. look for the string
+ * "uncomment this to make this a module again"
+ * to do guess what.
+ *
+ * the following is a copy of the 2.4.18 OSS FREE file-heading comment:
+ *
+ * msnd.c - Driver Base
+ *
+ * Turtle Beach MultiSound Sound Card Driver for Linux
+ *
+ * Copyright (C) 1998 Andrew Veliath
+ *
+ * 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.
+ *
+ ********************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "msnd.h"
+
+#define LOGNAME "msnd"
+
+
+void snd_msnd_init_queue(void *base, int start, int size)
+{
+ writew(PCTODSP_BASED(start), base + JQS_wStart);
+ writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize);
+ writew(0, base + JQS_wHead);
+ writew(0, base + JQS_wTail);
+}
+EXPORT_SYMBOL(snd_msnd_init_queue);
+
+static int snd_msnd_wait_TXDE(struct snd_msnd *dev)
+{
+ unsigned int io = dev->io;
+ int timeout = 1000;
+
+ while (timeout-- > 0)
+ if (inb(io + HP_ISR) & HPISR_TXDE)
+ return 0;
+
+ return -EIO;
+}
+
+static int snd_msnd_wait_HC0(struct snd_msnd *dev)
+{
+ unsigned int io = dev->io;
+ int timeout = 1000;
+
+ while (timeout-- > 0)
+ if (!(inb(io + HP_CVR) & HPCVR_HC))
+ return 0;
+
+ return -EIO;
+}
+
+int snd_msnd_send_dsp_cmd(struct snd_msnd *dev, u8 cmd)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (snd_msnd_wait_HC0(dev) == 0) {
+ outb(cmd, dev->io + HP_CVR);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ snd_printd(KERN_ERR LOGNAME ": Send DSP command timeout\n");
+
+ return -EIO;
+}
+EXPORT_SYMBOL(snd_msnd_send_dsp_cmd);
+
+int snd_msnd_send_word(struct snd_msnd *dev, unsigned char high,
+ unsigned char mid, unsigned char low)
+{
+ unsigned int io = dev->io;
+
+ if (snd_msnd_wait_TXDE(dev) == 0) {
+ outb(high, io + HP_TXH);
+ outb(mid, io + HP_TXM);
+ outb(low, io + HP_TXL);
+ return 0;
+ }
+
+ snd_printd(KERN_ERR LOGNAME ": Send host word timeout\n");
+
+ return -EIO;
+}
+EXPORT_SYMBOL(snd_msnd_send_word);
+
+int snd_msnd_upload_host(struct snd_msnd *dev, const u8 *bin, int len)
+{
+ int i;
+
+ if (len % 3 != 0) {
+ snd_printk(KERN_ERR LOGNAME
+ ": Upload host data not multiple of 3!\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < len; i += 3)
+ if (snd_msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]))
+ return -EIO;
+
+ inb(dev->io + HP_RXL);
+ inb(dev->io + HP_CVR);
+
+ return 0;
+}
+EXPORT_SYMBOL(snd_msnd_upload_host);
+
+int snd_msnd_enable_irq(struct snd_msnd *dev)
+{
+ unsigned long flags;
+
+ if (dev->irq_ref++)
+ return 0;
+
+ snd_printdd(LOGNAME ": Enabling IRQ\n");
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (snd_msnd_wait_TXDE(dev) == 0) {
+ outb(inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR);
+ if (dev->type == msndClassic)
+ outb(dev->irqid, dev->io + HP_IRQM);
+
+ outb(inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR);
+ outb(inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR);
+ enable_irq(dev->irq);
+ snd_msnd_init_queue(dev->DSPQ, dev->dspq_data_buff,
+ dev->dspq_buff_size);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ snd_printd(KERN_ERR LOGNAME ": Enable IRQ failed\n");
+
+ return -EIO;
+}
+EXPORT_SYMBOL(snd_msnd_enable_irq);
+
+int snd_msnd_disable_irq(struct snd_msnd *dev)
+{
+ unsigned long flags;
+
+ if (--dev->irq_ref > 0)
+ return 0;
+
+ if (dev->irq_ref < 0)
+ snd_printd(KERN_WARNING LOGNAME ": IRQ ref count is %d\n",
+ dev->irq_ref);
+
+ snd_printdd(LOGNAME ": Disabling IRQ\n");
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (snd_msnd_wait_TXDE(dev) == 0) {
+ outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR);
+ if (dev->type == msndClassic)
+ outb(HPIRQ_NONE, dev->io + HP_IRQM);
+ disable_irq(dev->irq);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ snd_printd(KERN_ERR LOGNAME ": Disable IRQ failed\n");
+
+ return -EIO;
+}
+EXPORT_SYMBOL(snd_msnd_disable_irq);
+
+static inline long get_play_delay_jiffies(struct snd_msnd *chip, long size)
+{
+ long tmp = (size * HZ * chip->play_sample_size) / 8;
+ return tmp / (chip->play_sample_rate * chip->play_channels);
+}
+
+static void snd_msnd_dsp_write_flush(struct snd_msnd *chip)
+{
+ if (!(chip->mode & FMODE_WRITE) || !test_bit(F_WRITING, &chip->flags))
+ return;
+ set_bit(F_WRITEFLUSH, &chip->flags);
+/* interruptible_sleep_on_timeout(
+ &chip->writeflush,
+ get_play_delay_jiffies(&chip, chip->DAPF.len));*/
+ clear_bit(F_WRITEFLUSH, &chip->flags);
+ if (!signal_pending(current))
+ schedule_timeout_interruptible(
+ get_play_delay_jiffies(chip, chip->play_period_bytes));
+ clear_bit(F_WRITING, &chip->flags);
+}
+
+void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file)
+{
+ if ((file ? file->f_mode : chip->mode) & FMODE_READ) {
+ clear_bit(F_READING, &chip->flags);
+ snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP);
+ snd_msnd_disable_irq(chip);
+ if (file) {
+ snd_printd(KERN_INFO LOGNAME
+ ": Stopping read for %p\n", file);
+ chip->mode &= ~FMODE_READ;
+ }
+ clear_bit(F_AUDIO_READ_INUSE, &chip->flags);
+ }
+ if ((file ? file->f_mode : chip->mode) & FMODE_WRITE) {
+ if (test_bit(F_WRITING, &chip->flags)) {
+ snd_msnd_dsp_write_flush(chip);
+ snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP);
+ }
+ snd_msnd_disable_irq(chip);
+ if (file) {
+ snd_printd(KERN_INFO
+ LOGNAME ": Stopping write for %p\n", file);
+ chip->mode &= ~FMODE_WRITE;
+ }
+ clear_bit(F_AUDIO_WRITE_INUSE, &chip->flags);
+ }
+}
+EXPORT_SYMBOL(snd_msnd_dsp_halt);
+
+
+int snd_msnd_DARQ(struct snd_msnd *chip, int bank)
+{
+ int /*size, n,*/ timeout = 3;
+ u16 wTmp;
+ /* void *DAQD; */
+
+ /* Increment the tail and check for queue wrap */
+ wTmp = readw(chip->DARQ + JQS_wTail) + PCTODSP_OFFSET(DAQDS__size);
+ if (wTmp > readw(chip->DARQ + JQS_wSize))
+ wTmp = 0;
+ while (wTmp == readw(chip->DARQ + JQS_wHead) && timeout--)
+ udelay(1);
+
+ if (chip->capturePeriods == 2) {
+ void *pDAQ = chip->mappedbase + DARQ_DATA_BUFF +
+ bank * DAQDS__size + DAQDS_wStart;
+ unsigned short offset = 0x3000 + chip->capturePeriodBytes;
+
+ if (readw(pDAQ) != PCTODSP_BASED(0x3000))
+ offset = 0x3000;
+ writew(PCTODSP_BASED(offset), pDAQ);
+ }
+
+ writew(wTmp, chip->DARQ + JQS_wTail);
+
+#if 0
+ /* Get our digital audio queue struct */
+ DAQD = bank * DAQDS__size + chip->mappedbase + DARQ_DATA_BUFF;
+
+ /* Get length of data */
+ size = readw(DAQD + DAQDS_wSize);
+
+ /* Read data from the head (unprotected bank 1 access okay
+ since this is only called inside an interrupt) */
+ outb(HPBLKSEL_1, chip->io + HP_BLKS);
+ n = msnd_fifo_write(&chip->DARF,
+ (char *)(chip->base + bank * DAR_BUFF_SIZE),
+ size, 0);
+ if (n <= 0) {
+ outb(HPBLKSEL_0, chip->io + HP_BLKS);
+ return n;
+ }
+ outb(HPBLKSEL_0, chip->io + HP_BLKS);
+#endif
+
+ return 1;
+}
+EXPORT_SYMBOL(snd_msnd_DARQ);
+
+int snd_msnd_DAPQ(struct snd_msnd *chip, int start)
+{
+ u16 DAPQ_tail;
+ int protect = start, nbanks = 0;
+ void *DAQD;
+ static int play_banks_submitted;
+ /* unsigned long flags;
+ spin_lock_irqsave(&chip->lock, flags); not necessary */
+
+ DAPQ_tail = readw(chip->DAPQ + JQS_wTail);
+ while (DAPQ_tail != readw(chip->DAPQ + JQS_wHead) || start) {
+ int bank_num = DAPQ_tail / PCTODSP_OFFSET(DAQDS__size);
+
+ if (start) {
+ start = 0;
+ play_banks_submitted = 0;
+ }
+
+ /* Get our digital audio queue struct */
+ DAQD = bank_num * DAQDS__size + chip->mappedbase +
+ DAPQ_DATA_BUFF;
+
+ /* Write size of this bank */
+ writew(chip->play_period_bytes, DAQD + DAQDS_wSize);
+ if (play_banks_submitted < 3)
+ ++play_banks_submitted;
+ else if (chip->playPeriods == 2) {
+ unsigned short offset = chip->play_period_bytes;
+
+ if (readw(DAQD + DAQDS_wStart) != PCTODSP_BASED(0x0))
+ offset = 0;
+
+ writew(PCTODSP_BASED(offset), DAQD + DAQDS_wStart);
+ }
+ ++nbanks;
+
+ /* Then advance the tail */
+ /*
+ if (protect)
+ snd_printd(KERN_INFO "B %X %lX\n",
+ bank_num, xtime.tv_usec);
+ */
+
+ DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size);
+ writew(DAPQ_tail, chip->DAPQ + JQS_wTail);
+ /* Tell the DSP to play the bank */
+ snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_START);
+ if (protect)
+ if (2 == bank_num)
+ break;
+ }
+ /*
+ if (protect)
+ snd_printd(KERN_INFO "%lX\n", xtime.tv_usec);
+ */
+ /* spin_unlock_irqrestore(&chip->lock, flags); not necessary */
+ return nbanks;
+}
+EXPORT_SYMBOL(snd_msnd_DAPQ);
+
+static void snd_msnd_play_reset_queue(struct snd_msnd *chip,
+ unsigned int pcm_periods,
+ unsigned int pcm_count)
+{
+ int n;
+ void *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF;
+
+ chip->last_playbank = -1;
+ chip->playLimit = pcm_count * (pcm_periods - 1);
+ chip->playPeriods = pcm_periods;
+ writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DAPQ + JQS_wHead);
+ writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DAPQ + JQS_wTail);
+
+ chip->play_period_bytes = pcm_count;
+
+ for (n = 0; n < pcm_periods; ++n, pDAQ += DAQDS__size) {
+ writew(PCTODSP_BASED((u32)(pcm_count * n)),
+ pDAQ + DAQDS_wStart);
+ writew(0, pDAQ + DAQDS_wSize);
+ writew(1, pDAQ + DAQDS_wFormat);
+ writew(chip->play_sample_size, pDAQ + DAQDS_wSampleSize);
+ writew(chip->play_channels, pDAQ + DAQDS_wChannels);
+ writew(chip->play_sample_rate, pDAQ + DAQDS_wSampleRate);
+ writew(HIMT_PLAY_DONE * 0x100 + n, pDAQ + DAQDS_wIntMsg);
+ writew(n, pDAQ + DAQDS_wFlags);
+ }
+}
+
+static void snd_msnd_capture_reset_queue(struct snd_msnd *chip,
+ unsigned int pcm_periods,
+ unsigned int pcm_count)
+{
+ int n;
+ void *pDAQ;
+ /* unsigned long flags; */
+
+ /* snd_msnd_init_queue(chip->DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE); */
+
+ chip->last_recbank = 2;
+ chip->captureLimit = pcm_count * (pcm_periods - 1);
+ chip->capturePeriods = pcm_periods;
+ writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DARQ + JQS_wHead);
+ writew(PCTODSP_OFFSET(chip->last_recbank * DAQDS__size),
+ chip->DARQ + JQS_wTail);
+
+#if 0 /* Critical section: bank 1 access. this is how the OSS driver does it:*/
+ spin_lock_irqsave(&chip->lock, flags);
+ outb(HPBLKSEL_1, chip->io + HP_BLKS);
+ memset_io(chip->mappedbase, 0, DAR_BUFF_SIZE * 3);
+ outb(HPBLKSEL_0, chip->io + HP_BLKS);
+ spin_unlock_irqrestore(&chip->lock, flags);
+#endif
+
+ chip->capturePeriodBytes = pcm_count;
+ snd_printdd("snd_msnd_capture_reset_queue() %i\n", pcm_count);
+
+ pDAQ = chip->mappedbase + DARQ_DATA_BUFF;
+
+ for (n = 0; n < pcm_periods; ++n, pDAQ += DAQDS__size) {
+ u32 tmp = pcm_count * n;
+
+ writew(PCTODSP_BASED(tmp + 0x3000), pDAQ + DAQDS_wStart);
+ writew(pcm_count, pDAQ + DAQDS_wSize);
+ writew(1, pDAQ + DAQDS_wFormat);
+ writew(chip->capture_sample_size, pDAQ + DAQDS_wSampleSize);
+ writew(chip->capture_channels, pDAQ + DAQDS_wChannels);
+ writew(chip->capture_sample_rate, pDAQ + DAQDS_wSampleRate);
+ writew(HIMT_RECORD_DONE * 0x100 + n, pDAQ + DAQDS_wIntMsg);
+ writew(n, pDAQ + DAQDS_wFlags);
+ }
+}
+
+static struct snd_pcm_hardware snd_msnd_playback = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 0x3000,
+ .period_bytes_min = 0x40,
+ .period_bytes_max = 0x1800,
+ .periods_min = 2,
+ .periods_max = 3,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware snd_msnd_capture = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 0x3000,
+ .period_bytes_min = 0x40,
+ .period_bytes_max = 0x1800,
+ .periods_min = 2,
+ .periods_max = 3,
+ .fifo_size = 0,
+};
+
+
+static int snd_msnd_playback_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_msnd *chip = snd_pcm_substream_chip(substream);
+
+ set_bit(F_AUDIO_WRITE_INUSE, &chip->flags);
+ clear_bit(F_WRITING, &chip->flags);
+ snd_msnd_enable_irq(chip);
+
+ runtime->dma_area = chip->mappedbase;
+ runtime->dma_bytes = 0x3000;
+
+ chip->playback_substream = substream;
+ runtime->hw = snd_msnd_playback;
+ return 0;
+}
+
+static int snd_msnd_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_msnd *chip = snd_pcm_substream_chip(substream);
+
+ snd_msnd_disable_irq(chip);
+ clear_bit(F_AUDIO_WRITE_INUSE, &chip->flags);
+ return 0;
+}
+
+
+static int snd_msnd_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int i;
+ struct snd_msnd *chip = snd_pcm_substream_chip(substream);
+ void *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF;
+
+ chip->play_sample_size = snd_pcm_format_width(params_format(params));
+ chip->play_channels = params_channels(params);
+ chip->play_sample_rate = params_rate(params);
+
+ for (i = 0; i < 3; ++i, pDAQ += DAQDS__size) {
+ writew(chip->play_sample_size, pDAQ + DAQDS_wSampleSize);
+ writew(chip->play_channels, pDAQ + DAQDS_wChannels);
+ writew(chip->play_sample_rate, pDAQ + DAQDS_wSampleRate);
+ }
+ /* dont do this here:
+ * snd_msnd_calibrate_adc(chip->play_sample_rate);
+ */
+
+ return 0;
+}
+
+static int snd_msnd_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_msnd *chip = snd_pcm_substream_chip(substream);
+ unsigned int pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ unsigned int pcm_count = snd_pcm_lib_period_bytes(substream);
+ unsigned int pcm_periods = pcm_size / pcm_count;
+
+ snd_msnd_play_reset_queue(chip, pcm_periods, pcm_count);
+ chip->playDMAPos = 0;
+ return 0;
+}
+
+static int snd_msnd_playback_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_msnd *chip = snd_pcm_substream_chip(substream);
+ int result = 0;
+
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ snd_printdd("snd_msnd_playback_trigger(START)\n");
+ chip->banksPlayed = 0;
+ set_bit(F_WRITING, &chip->flags);
+ snd_msnd_DAPQ(chip, 1);
+ } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ snd_printdd("snd_msnd_playback_trigger(STop)\n");
+ /* interrupt diagnostic, comment this out later */
+ clear_bit(F_WRITING, &chip->flags);
+ snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP);
+ } else {
+ snd_printd(KERN_ERR "snd_msnd_playback_trigger(?????)\n");
+ result = -EINVAL;
+ }
+
+ snd_printdd("snd_msnd_playback_trigger() ENDE\n");
+ return result;
+}
+
+static snd_pcm_uframes_t
+snd_msnd_playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_msnd *chip = snd_pcm_substream_chip(substream);
+
+ return bytes_to_frames(substream->runtime, chip->playDMAPos);
+}
+
+
+static struct snd_pcm_ops snd_msnd_playback_ops = {
+ .open = snd_msnd_playback_open,
+ .close = snd_msnd_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_msnd_playback_hw_params,
+ .prepare = snd_msnd_playback_prepare,
+ .trigger = snd_msnd_playback_trigger,
+ .pointer = snd_msnd_playback_pointer,
+};
+
+static int snd_msnd_capture_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_msnd *chip = snd_pcm_substream_chip(substream);
+
+ set_bit(F_AUDIO_READ_INUSE, &chip->flags);
+ snd_msnd_enable_irq(chip);
+ runtime->dma_area = chip->mappedbase + 0x3000;
+ runtime->dma_bytes = 0x3000;
+ memset(runtime->dma_area, 0, runtime->dma_bytes);
+ chip->capture_substream = substream;
+ runtime->hw = snd_msnd_capture;
+ return 0;
+}
+
+static int snd_msnd_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_msnd *chip = snd_pcm_substream_chip(substream);
+
+ snd_msnd_disable_irq(chip);
+ clear_bit(F_AUDIO_READ_INUSE, &chip->flags);
+ return 0;
+}
+
+static int snd_msnd_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_msnd *chip = snd_pcm_substream_chip(substream);
+ unsigned int pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ unsigned int pcm_count = snd_pcm_lib_period_bytes(substream);
+ unsigned int pcm_periods = pcm_size / pcm_count;
+
+ snd_msnd_capture_reset_queue(chip, pcm_periods, pcm_count);
+ chip->captureDMAPos = 0;
+ return 0;
+}
+
+static int snd_msnd_capture_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_msnd *chip = snd_pcm_substream_chip(substream);
+
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ chip->last_recbank = -1;
+ set_bit(F_READING, &chip->flags);
+ if (snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_START) == 0)
+ return 0;
+
+ clear_bit(F_READING, &chip->flags);
+ } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ clear_bit(F_READING, &chip->flags);
+ snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+
+static snd_pcm_uframes_t
+snd_msnd_capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_msnd *chip = snd_pcm_substream_chip(substream);
+
+ return bytes_to_frames(runtime, chip->captureDMAPos);
+}
+
+
+static int snd_msnd_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int i;
+ struct snd_msnd *chip = snd_pcm_substream_chip(substream);
+ void *pDAQ = chip->mappedbase + DARQ_DATA_BUFF;
+
+ chip->capture_sample_size = snd_pcm_format_width(params_format(params));
+ chip->capture_channels = params_channels(params);
+ chip->capture_sample_rate = params_rate(params);
+
+ for (i = 0; i < 3; ++i, pDAQ += DAQDS__size) {
+ writew(chip->capture_sample_size, pDAQ + DAQDS_wSampleSize);
+ writew(chip->capture_channels, pDAQ + DAQDS_wChannels);
+ writew(chip->capture_sample_rate, pDAQ + DAQDS_wSampleRate);
+ }
+ return 0;
+}
+
+
+static struct snd_pcm_ops snd_msnd_capture_ops = {
+ .open = snd_msnd_capture_open,
+ .close = snd_msnd_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_msnd_capture_hw_params,
+ .prepare = snd_msnd_capture_prepare,
+ .trigger = snd_msnd_capture_trigger,
+ .pointer = snd_msnd_capture_pointer,
+};
+
+
+int snd_msnd_pcm(struct snd_card *card, int device,
+ struct snd_pcm **rpcm)
+{
+ struct snd_msnd *chip = card->private_data;
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(card, "MSNDPINNACLE", device, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_msnd_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_msnd_capture_ops);
+
+ pcm->private_data = chip;
+ strcpy(pcm->name, "Hurricane");
+
+
+ if (rpcm)
+ *rpcm = pcm;
+ return 0;
+}
+EXPORT_SYMBOL(snd_msnd_pcm);
+
+MODULE_DESCRIPTION("Common routines for Turtle Beach Multisound drivers");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/isa/msnd/msnd.h b/sound/isa/msnd/msnd.h
new file mode 100644
index 0000000..3773e24
--- /dev/null
+++ b/sound/isa/msnd/msnd.h
@@ -0,0 +1,308 @@
+/*********************************************************************
+ *
+ * msnd.h
+ *
+ * Turtle Beach MultiSound Sound Card Driver for Linux
+ *
+ * Some parts of this header file were derived from the Turtle Beach
+ * MultiSound Driver Development Kit.
+ *
+ * Copyright (C) 1998 Andrew Veliath
+ * Copyright (C) 1993 Turtle Beach Systems, Inc.
+ *
+ * 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.
+ *
+ ********************************************************************/
+#ifndef __MSND_H
+#define __MSND_H
+
+#define DEFSAMPLERATE 44100
+#define DEFSAMPLESIZE SNDRV_PCM_FORMAT_S16
+#define DEFCHANNELS 1
+
+#define SRAM_BANK_SIZE 0x8000
+#define SRAM_CNTL_START 0x7F00
+#define SMA_STRUCT_START 0x7F40
+
+#define DSP_BASE_ADDR 0x4000
+#define DSP_BANK_BASE 0x4000
+
+#define AGND 0x01
+#define SIGNAL 0x02
+
+#define EXT_DSP_BIT_DCAL 0x0001
+#define EXT_DSP_BIT_MIDI_CON 0x0002
+
+#define BUFFSIZE 0x8000
+#define HOSTQ_SIZE 0x40
+
+#define DAP_BUFF_SIZE 0x2400
+
+#define DAPQ_STRUCT_SIZE 0x10
+#define DARQ_STRUCT_SIZE 0x10
+#define DAPQ_BUFF_SIZE (3 * 0x10)
+#define DARQ_BUFF_SIZE (3 * 0x10)
+#define MODQ_BUFF_SIZE 0x400
+
+#define DAPQ_DATA_BUFF 0x6C00
+#define DARQ_DATA_BUFF 0x6C30
+#define MODQ_DATA_BUFF 0x6C60
+#define MIDQ_DATA_BUFF 0x7060
+
+#define DAPQ_OFFSET SRAM_CNTL_START
+#define DARQ_OFFSET (SRAM_CNTL_START + 0x08)
+#define MODQ_OFFSET (SRAM_CNTL_START + 0x10)
+#define MIDQ_OFFSET (SRAM_CNTL_START + 0x18)
+#define DSPQ_OFFSET (SRAM_CNTL_START + 0x20)
+
+#define HP_ICR 0x00
+#define HP_CVR 0x01
+#define HP_ISR 0x02
+#define HP_IVR 0x03
+#define HP_NU 0x04
+#define HP_INFO 0x04
+#define HP_TXH 0x05
+#define HP_RXH 0x05
+#define HP_TXM 0x06
+#define HP_RXM 0x06
+#define HP_TXL 0x07
+#define HP_RXL 0x07
+
+#define HP_ICR_DEF 0x00
+#define HP_CVR_DEF 0x12
+#define HP_ISR_DEF 0x06
+#define HP_IVR_DEF 0x0f
+#define HP_NU_DEF 0x00
+
+#define HP_IRQM 0x09
+
+#define HPR_BLRC 0x08
+#define HPR_SPR1 0x09
+#define HPR_SPR2 0x0A
+#define HPR_TCL0 0x0B
+#define HPR_TCL1 0x0C
+#define HPR_TCL2 0x0D
+#define HPR_TCL3 0x0E
+#define HPR_TCL4 0x0F
+
+#define HPICR_INIT 0x80
+#define HPICR_HM1 0x40
+#define HPICR_HM0 0x20
+#define HPICR_HF1 0x10
+#define HPICR_HF0 0x08
+#define HPICR_TREQ 0x02
+#define HPICR_RREQ 0x01
+
+#define HPCVR_HC 0x80
+
+#define HPISR_HREQ 0x80
+#define HPISR_DMA 0x40
+#define HPISR_HF3 0x10
+#define HPISR_HF2 0x08
+#define HPISR_TRDY 0x04
+#define HPISR_TXDE 0x02
+#define HPISR_RXDF 0x01
+
+#define HPIO_290 0
+#define HPIO_260 1
+#define HPIO_250 2
+#define HPIO_240 3
+#define HPIO_230 4
+#define HPIO_220 5
+#define HPIO_210 6
+#define HPIO_3E0 7
+
+#define HPMEM_NONE 0
+#define HPMEM_B000 1
+#define HPMEM_C800 2
+#define HPMEM_D000 3
+#define HPMEM_D400 4
+#define HPMEM_D800 5
+#define HPMEM_E000 6
+#define HPMEM_E800 7
+
+#define HPIRQ_NONE 0
+#define HPIRQ_5 1
+#define HPIRQ_7 2
+#define HPIRQ_9 3
+#define HPIRQ_10 4
+#define HPIRQ_11 5
+#define HPIRQ_12 6
+#define HPIRQ_15 7
+
+#define HIMT_PLAY_DONE 0x00
+#define HIMT_RECORD_DONE 0x01
+#define HIMT_MIDI_EOS 0x02
+#define HIMT_MIDI_OUT 0x03
+
+#define HIMT_MIDI_IN_UCHAR 0x0E
+#define HIMT_DSP 0x0F
+
+#define HDEX_BASE 0x92
+#define HDEX_PLAY_START (0 + HDEX_BASE)
+#define HDEX_PLAY_STOP (1 + HDEX_BASE)
+#define HDEX_PLAY_PAUSE (2 + HDEX_BASE)
+#define HDEX_PLAY_RESUME (3 + HDEX_BASE)
+#define HDEX_RECORD_START (4 + HDEX_BASE)
+#define HDEX_RECORD_STOP (5 + HDEX_BASE)
+#define HDEX_MIDI_IN_START (6 + HDEX_BASE)
+#define HDEX_MIDI_IN_STOP (7 + HDEX_BASE)
+#define HDEX_MIDI_OUT_START (8 + HDEX_BASE)
+#define HDEX_MIDI_OUT_STOP (9 + HDEX_BASE)
+#define HDEX_AUX_REQ (10 + HDEX_BASE)
+
+#define HDEXAR_CLEAR_PEAKS 1
+#define HDEXAR_IN_SET_POTS 2
+#define HDEXAR_AUX_SET_POTS 3
+#define HDEXAR_CAL_A_TO_D 4
+#define HDEXAR_RD_EXT_DSP_BITS 5
+
+/* Pinnacle only HDEXAR defs */
+#define HDEXAR_SET_ANA_IN 0
+#define HDEXAR_SET_SYNTH_IN 4
+#define HDEXAR_READ_DAT_IN 5
+#define HDEXAR_MIC_SET_POTS 6
+#define HDEXAR_SET_DAT_IN 7
+
+#define HDEXAR_SET_SYNTH_48 8
+#define HDEXAR_SET_SYNTH_44 9
+
+#define HIWORD(l) ((u16)((((u32)(l)) >> 16) & 0xFFFF))
+#define LOWORD(l) ((u16)(u32)(l))
+#define HIBYTE(w) ((u8)(((u16)(w) >> 8) & 0xFF))
+#define LOBYTE(w) ((u8)(w))
+#define MAKELONG(low, hi) ((long)(((u16)(low))|(((u32)((u16)(hi)))<<16)))
+#define MAKEWORD(low, hi) ((u16)(((u8)(low))|(((u16)((u8)(hi)))<<8)))
+
+#define PCTODSP_OFFSET(w) (u16)((w)/2)
+#define PCTODSP_BASED(w) (u16)(((w)/2) + DSP_BASE_ADDR)
+#define DSPTOPC_BASED(w) (((w) - DSP_BASE_ADDR) * 2)
+
+#ifdef SLOWIO
+# undef outb
+# undef inb
+# define outb outb_p
+# define inb inb_p
+#endif
+
+/* JobQueueStruct */
+#define JQS_wStart 0x00
+#define JQS_wSize 0x02
+#define JQS_wHead 0x04
+#define JQS_wTail 0x06
+#define JQS__size 0x08
+
+/* DAQueueDataStruct */
+#define DAQDS_wStart 0x00
+#define DAQDS_wSize 0x02
+#define DAQDS_wFormat 0x04
+#define DAQDS_wSampleSize 0x06
+#define DAQDS_wChannels 0x08
+#define DAQDS_wSampleRate 0x0A
+#define DAQDS_wIntMsg 0x0C
+#define DAQDS_wFlags 0x0E
+#define DAQDS__size 0x10
+
+#include <sound/pcm.h>
+
+struct snd_msnd {
+ void __iomem *mappedbase;
+ int play_period_bytes;
+ int playLimit;
+ int playPeriods;
+ int playDMAPos;
+ int banksPlayed;
+ int captureDMAPos;
+ int capturePeriodBytes;
+ int captureLimit;
+ int capturePeriods;
+ struct snd_card *card;
+ void *msndmidi_mpu;
+ struct snd_rawmidi *rmidi;
+
+ /* Hardware resources */
+ long io;
+ int memid, irqid;
+ int irq, irq_ref;
+ unsigned long base;
+
+ /* Motorola 56k DSP SMA */
+ void __iomem *SMA;
+ void __iomem *DAPQ;
+ void __iomem *DARQ;
+ void __iomem *MODQ;
+ void __iomem *MIDQ;
+ void __iomem *DSPQ;
+ int dspq_data_buff, dspq_buff_size;
+
+ /* State variables */
+ enum { msndClassic, msndPinnacle } type;
+ mode_t mode;
+ unsigned long flags;
+#define F_RESETTING 0
+#define F_HAVEDIGITAL 1
+#define F_AUDIO_WRITE_INUSE 2
+#define F_WRITING 3
+#define F_WRITEBLOCK 4
+#define F_WRITEFLUSH 5
+#define F_AUDIO_READ_INUSE 6
+#define F_READING 7
+#define F_READBLOCK 8
+#define F_EXT_MIDI_INUSE 9
+#define F_HDR_MIDI_INUSE 10
+#define F_DISABLE_WRITE_NDELAY 11
+ spinlock_t lock;
+ spinlock_t mixer_lock;
+ int nresets;
+ unsigned recsrc;
+#define LEVEL_ENTRIES 32
+ int left_levels[LEVEL_ENTRIES];
+ int right_levels[LEVEL_ENTRIES];
+ int calibrate_signal;
+ int play_sample_size, play_sample_rate, play_channels;
+ int play_ndelay;
+ int capture_sample_size, capture_sample_rate, capture_channels;
+ int capture_ndelay;
+ u8 bCurrentMidiPatch;
+
+ int last_playbank, last_recbank;
+ struct snd_pcm_substream *playback_substream;
+ struct snd_pcm_substream *capture_substream;
+
+};
+
+void snd_msnd_init_queue(void *base, int start, int size);
+
+int snd_msnd_send_dsp_cmd(struct snd_msnd *chip, u8 cmd);
+int snd_msnd_send_word(struct snd_msnd *chip,
+ unsigned char high,
+ unsigned char mid,
+ unsigned char low);
+int snd_msnd_upload_host(struct snd_msnd *chip,
+ const u8 *bin, int len);
+int snd_msnd_enable_irq(struct snd_msnd *chip);
+int snd_msnd_disable_irq(struct snd_msnd *chip);
+void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file);
+int snd_msnd_DAPQ(struct snd_msnd *chip, int start);
+int snd_msnd_DARQ(struct snd_msnd *chip, int start);
+int snd_msnd_pcm(struct snd_card *card, int device, struct snd_pcm **rpcm);
+
+int snd_msndmidi_new(struct snd_card *card, int device);
+void snd_msndmidi_input_read(void *mpu);
+
+void snd_msndmix_setup(struct snd_msnd *chip);
+int __devinit snd_msndmix_new(struct snd_card *card);
+int snd_msndmix_force_recsrc(struct snd_msnd *chip, int recsrc);
+#endif /* __MSND_H */
diff --git a/sound/isa/msnd/msnd_classic.c b/sound/isa/msnd/msnd_classic.c
new file mode 100644
index 0000000..3b23a09
--- /dev/null
+++ b/sound/isa/msnd/msnd_classic.c
@@ -0,0 +1,3 @@
+/* The work is in msnd_pinnacle.c, just define MSND_CLASSIC before it. */
+#define MSND_CLASSIC
+#include "msnd_pinnacle.c"
diff --git a/sound/isa/msnd/msnd_classic.h b/sound/isa/msnd/msnd_classic.h
new file mode 100644
index 0000000..f18d5fa
--- /dev/null
+++ b/sound/isa/msnd/msnd_classic.h
@@ -0,0 +1,129 @@
+/*********************************************************************
+ *
+ * msnd_classic.h
+ *
+ * Turtle Beach MultiSound Sound Card Driver for Linux
+ *
+ * Some parts of this header file were derived from the Turtle Beach
+ * MultiSound Driver Development Kit.
+ *
+ * Copyright (C) 1998 Andrew Veliath
+ * Copyright (C) 1993 Turtle Beach Systems, Inc.
+ *
+ * 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.
+ *
+ ********************************************************************/
+#ifndef __MSND_CLASSIC_H
+#define __MSND_CLASSIC_H
+
+#define DSP_NUMIO 0x10
+
+#define HP_MEMM 0x08
+
+#define HP_BITM 0x0E
+#define HP_WAIT 0x0D
+#define HP_DSPR 0x0A
+#define HP_PROR 0x0B
+#define HP_BLKS 0x0C
+
+#define HPPRORESET_OFF 0
+#define HPPRORESET_ON 1
+
+#define HPDSPRESET_OFF 0
+#define HPDSPRESET_ON 1
+
+#define HPBLKSEL_0 0
+#define HPBLKSEL_1 1
+
+#define HPWAITSTATE_0 0
+#define HPWAITSTATE_1 1
+
+#define HPBITMODE_16 0
+#define HPBITMODE_8 1
+
+#define HIDSP_INT_PLAY_UNDER 0x00
+#define HIDSP_INT_RECORD_OVER 0x01
+#define HIDSP_INPUT_CLIPPING 0x02
+#define HIDSP_MIDI_IN_OVER 0x10
+#define HIDSP_MIDI_OVERRUN_ERR 0x13
+
+#define TIME_PRO_RESET_DONE 0x028A
+#define TIME_PRO_SYSEX 0x0040
+#define TIME_PRO_RESET 0x0032
+
+#define DAR_BUFF_SIZE 0x2000
+
+#define MIDQ_BUFF_SIZE 0x200
+#define DSPQ_BUFF_SIZE 0x40
+
+#define DSPQ_DATA_BUFF 0x7260
+
+#define MOP_SYNTH 0x10
+#define MOP_EXTOUT 0x32
+#define MOP_EXTTHRU 0x02
+#define MOP_OUTMASK 0x01
+
+#define MIP_EXTIN 0x01
+#define MIP_SYNTH 0x00
+#define MIP_INMASK 0x32
+
+/* Classic SMA Common Data */
+#define SMA_wCurrPlayBytes 0x0000
+#define SMA_wCurrRecordBytes 0x0002
+#define SMA_wCurrPlayVolLeft 0x0004
+#define SMA_wCurrPlayVolRight 0x0006
+#define SMA_wCurrInVolLeft 0x0008
+#define SMA_wCurrInVolRight 0x000a
+#define SMA_wUser_3 0x000c
+#define SMA_wUser_4 0x000e
+#define SMA_dwUser_5 0x0010
+#define SMA_dwUser_6 0x0014
+#define SMA_wUser_7 0x0018
+#define SMA_wReserved_A 0x001a
+#define SMA_wReserved_B 0x001c
+#define SMA_wReserved_C 0x001e
+#define SMA_wReserved_D 0x0020
+#define SMA_wReserved_E 0x0022
+#define SMA_wReserved_F 0x0024
+#define SMA_wReserved_G 0x0026
+#define SMA_wReserved_H 0x0028
+#define SMA_wCurrDSPStatusFlags 0x002a
+#define SMA_wCurrHostStatusFlags 0x002c
+#define SMA_wCurrInputTagBits 0x002e
+#define SMA_wCurrLeftPeak 0x0030
+#define SMA_wCurrRightPeak 0x0032
+#define SMA_wExtDSPbits 0x0034
+#define SMA_bExtHostbits 0x0036
+#define SMA_bBoardLevel 0x0037
+#define SMA_bInPotPosRight 0x0038
+#define SMA_bInPotPosLeft 0x0039
+#define SMA_bAuxPotPosRight 0x003a
+#define SMA_bAuxPotPosLeft 0x003b
+#define SMA_wCurrMastVolLeft 0x003c
+#define SMA_wCurrMastVolRight 0x003e
+#define SMA_bUser_12 0x0040
+#define SMA_bUser_13 0x0041
+#define SMA_wUser_14 0x0042
+#define SMA_wUser_15 0x0044
+#define SMA_wCalFreqAtoD 0x0046
+#define SMA_wUser_16 0x0048
+#define SMA_wUser_17 0x004a
+#define SMA__size 0x004c
+
+#define INITCODEFILE "turtlebeach/msndinit.bin"
+#define PERMCODEFILE "turtlebeach/msndperm.bin"
+#define LONGNAME "MultiSound (Classic/Monterey/Tahiti)"
+
+#endif /* __MSND_CLASSIC_H */
diff --git a/sound/isa/msnd/msnd_midi.c b/sound/isa/msnd/msnd_midi.c
new file mode 100644
index 0000000..cb9aa4c
--- /dev/null
+++ b/sound/isa/msnd/msnd_midi.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * Copyright (c) 2009 by Krzysztof Helt
+ * Routines for control of MPU-401 in UART mode
+ *
+ * MPU-401 supports UART mode which is not capable generate transmit
+ * interrupts thus output is done via polling. Also, if irq < 0, then
+ * input is done also via polling. Do not expect good performance.
+ *
+ *
+ * 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/io.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <sound/core.h>
+#include <sound/rawmidi.h>
+
+#include "msnd.h"
+
+#define MSNDMIDI_MODE_BIT_INPUT 0
+#define MSNDMIDI_MODE_BIT_OUTPUT 1
+#define MSNDMIDI_MODE_BIT_INPUT_TRIGGER 2
+#define MSNDMIDI_MODE_BIT_OUTPUT_TRIGGER 3
+
+struct snd_msndmidi {
+ struct snd_msnd *dev;
+
+ unsigned long mode; /* MSNDMIDI_MODE_XXXX */
+
+ struct snd_rawmidi_substream *substream_input;
+
+ spinlock_t input_lock;
+};
+
+/*
+ * input/output open/close - protected by open_mutex in rawmidi.c
+ */
+static int snd_msndmidi_input_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_msndmidi *mpu;
+
+ snd_printdd("snd_msndmidi_input_open()\n");
+
+ mpu = substream->rmidi->private_data;
+
+ mpu->substream_input = substream;
+
+ snd_msnd_enable_irq(mpu->dev);
+
+ snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_START);
+ set_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode);
+ return 0;
+}
+
+static int snd_msndmidi_input_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_msndmidi *mpu;
+
+ mpu = substream->rmidi->private_data;
+ snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_STOP);
+ clear_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode);
+ mpu->substream_input = NULL;
+ snd_msnd_disable_irq(mpu->dev);
+ return 0;
+}
+
+static void snd_msndmidi_input_drop(struct snd_msndmidi *mpu)
+{
+ u16 tail;
+
+ tail = readw(mpu->dev->MIDQ + JQS_wTail);
+ writew(tail, mpu->dev->MIDQ + JQS_wHead);
+}
+
+/*
+ * trigger input
+ */
+static void snd_msndmidi_input_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ unsigned long flags;
+ struct snd_msndmidi *mpu;
+
+ snd_printdd("snd_msndmidi_input_trigger(, %i)\n", up);
+
+ mpu = substream->rmidi->private_data;
+ spin_lock_irqsave(&mpu->input_lock, flags);
+ if (up) {
+ if (!test_and_set_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER,
+ &mpu->mode))
+ snd_msndmidi_input_drop(mpu);
+ } else {
+ clear_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode);
+ }
+ spin_unlock_irqrestore(&mpu->input_lock, flags);
+ if (up)
+ snd_msndmidi_input_read(mpu);
+}
+
+void snd_msndmidi_input_read(void *mpuv)
+{
+ unsigned long flags;
+ struct snd_msndmidi *mpu = mpuv;
+ void *pwMIDQData = mpu->dev->mappedbase + MIDQ_DATA_BUFF;
+
+ spin_lock_irqsave(&mpu->input_lock, flags);
+ while (readw(mpu->dev->MIDQ + JQS_wTail) !=
+ readw(mpu->dev->MIDQ + JQS_wHead)) {
+ u16 wTmp, val;
+ val = readw(pwMIDQData + 2 * readw(mpu->dev->MIDQ + JQS_wHead));
+
+ if (test_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER,
+ &mpu->mode))
+ snd_rawmidi_receive(mpu->substream_input,
+ (unsigned char *)&val, 1);
+
+ wTmp = readw(mpu->dev->MIDQ + JQS_wHead) + 1;
+ if (wTmp > readw(mpu->dev->MIDQ + JQS_wSize))
+ writew(0, mpu->dev->MIDQ + JQS_wHead);
+ else
+ writew(wTmp, mpu->dev->MIDQ + JQS_wHead);
+ }
+ spin_unlock_irqrestore(&mpu->input_lock, flags);
+}
+EXPORT_SYMBOL(snd_msndmidi_input_read);
+
+static struct snd_rawmidi_ops snd_msndmidi_input = {
+ .open = snd_msndmidi_input_open,
+ .close = snd_msndmidi_input_close,
+ .trigger = snd_msndmidi_input_trigger,
+};
+
+static void snd_msndmidi_free(struct snd_rawmidi *rmidi)
+{
+ struct snd_msndmidi *mpu = rmidi->private_data;
+ kfree(mpu);
+}
+
+int snd_msndmidi_new(struct snd_card *card, int device)
+{
+ struct snd_msnd *chip = card->private_data;
+ struct snd_msndmidi *mpu;
+ struct snd_rawmidi *rmidi;
+ int err;
+
+ err = snd_rawmidi_new(card, "MSND-MIDI", device, 1, 1, &rmidi);
+ if (err < 0)
+ return err;
+ mpu = kcalloc(1, sizeof(*mpu), GFP_KERNEL);
+ if (mpu == NULL) {
+ snd_device_free(card, rmidi);
+ return -ENOMEM;
+ }
+ mpu->dev = chip;
+ chip->msndmidi_mpu = mpu;
+ rmidi->private_data = mpu;
+ rmidi->private_free = snd_msndmidi_free;
+ spin_lock_init(&mpu->input_lock);
+ strcpy(rmidi->name, "MSND MIDI");
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &snd_msndmidi_input);
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ return 0;
+}
diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c
new file mode 100644
index 0000000..60b6abd
--- /dev/null
+++ b/sound/isa/msnd/msnd_pinnacle.c
@@ -0,0 +1,1238 @@
+/*********************************************************************
+ *
+ * Linux multisound pinnacle/fiji driver for ALSA.
+ *
+ * 2002/06/30 Karsten Wiese:
+ * for now this is only used to build a pinnacle / fiji driver.
+ * the OSS parent of this code is designed to also support
+ * the multisound classic via the file msnd_classic.c.
+ * to make it easier for some brave heart to implemt classic
+ * support in alsa, i left all the MSND_CLASSIC tokens in this file.
+ * but for now this untested & undone.
+ *
+ *
+ * ripped from linux kernel 2.4.18 by Karsten Wiese.
+ *
+ * the following is a copy of the 2.4.18 OSS FREE file-heading comment:
+ *
+ * Turtle Beach MultiSound Sound Card Driver for Linux
+ * msnd_pinnacle.c / msnd_classic.c
+ *
+ * -- If MSND_CLASSIC is defined:
+ *
+ * -> driver for Turtle Beach Classic/Monterey/Tahiti
+ *
+ * -- Else
+ *
+ * -> driver for Turtle Beach Pinnacle/Fiji
+ *
+ * 12-3-2000 Modified IO port validation Steve Sycamore
+ *
+ * Copyright (C) 1998 Andrew Veliath
+ *
+ * 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.
+ *
+ ********************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/firmware.h>
+#include <linux/isa.h>
+#include <linux/isapnp.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/asound.h>
+#include <sound/pcm.h>
+#include <sound/mpu401.h>
+
+#ifdef MSND_CLASSIC
+# ifndef __alpha__
+# define SLOWIO
+# endif
+#endif
+#include "msnd.h"
+#ifdef MSND_CLASSIC
+# include "msnd_classic.h"
+# define LOGNAME "msnd_classic"
+#else
+# include "msnd_pinnacle.h"
+# define LOGNAME "snd_msnd_pinnacle"
+#endif
+
+static void __devinit set_default_audio_parameters(struct snd_msnd *chip)
+{
+ chip->play_sample_size = DEFSAMPLESIZE;
+ chip->play_sample_rate = DEFSAMPLERATE;
+ chip->play_channels = DEFCHANNELS;
+ chip->capture_sample_size = DEFSAMPLESIZE;
+ chip->capture_sample_rate = DEFSAMPLERATE;
+ chip->capture_channels = DEFCHANNELS;
+}
+
+static void snd_msnd_eval_dsp_msg(struct snd_msnd *chip, u16 wMessage)
+{
+ switch (HIBYTE(wMessage)) {
+ case HIMT_PLAY_DONE: {
+ if (chip->banksPlayed < 3)
+ snd_printdd("%08X: HIMT_PLAY_DONE: %i\n",
+ (unsigned)jiffies, LOBYTE(wMessage));
+
+ if (chip->last_playbank == LOBYTE(wMessage)) {
+ snd_printdd("chip.last_playbank == LOBYTE(wMessage)\n");
+ break;
+ }
+ chip->banksPlayed++;
+
+ if (test_bit(F_WRITING, &chip->flags))
+ snd_msnd_DAPQ(chip, 0);
+
+ chip->last_playbank = LOBYTE(wMessage);
+ chip->playDMAPos += chip->play_period_bytes;
+ if (chip->playDMAPos > chip->playLimit)
+ chip->playDMAPos = 0;
+ snd_pcm_period_elapsed(chip->playback_substream);
+
+ break;
+ }
+ case HIMT_RECORD_DONE:
+ if (chip->last_recbank == LOBYTE(wMessage))
+ break;
+ chip->last_recbank = LOBYTE(wMessage);
+ chip->captureDMAPos += chip->capturePeriodBytes;
+ if (chip->captureDMAPos > (chip->captureLimit))
+ chip->captureDMAPos = 0;
+
+ if (test_bit(F_READING, &chip->flags))
+ snd_msnd_DARQ(chip, chip->last_recbank);
+
+ snd_pcm_period_elapsed(chip->capture_substream);
+ break;
+
+ case HIMT_DSP:
+ switch (LOBYTE(wMessage)) {
+#ifndef MSND_CLASSIC
+ case HIDSP_PLAY_UNDER:
+#endif
+ case HIDSP_INT_PLAY_UNDER:
+ snd_printd(KERN_WARNING LOGNAME ": Play underflow %i\n",
+ chip->banksPlayed);
+ if (chip->banksPlayed > 2)
+ clear_bit(F_WRITING, &chip->flags);
+ break;
+
+ case HIDSP_INT_RECORD_OVER:
+ snd_printd(KERN_WARNING LOGNAME ": Record overflow\n");
+ clear_bit(F_READING, &chip->flags);
+ break;
+
+ default:
+ snd_printd(KERN_WARNING LOGNAME
+ ": DSP message %d 0x%02x\n",
+ LOBYTE(wMessage), LOBYTE(wMessage));
+ break;
+ }
+ break;
+
+ case HIMT_MIDI_IN_UCHAR:
+ if (chip->msndmidi_mpu)
+ snd_msndmidi_input_read(chip->msndmidi_mpu);
+ break;
+
+ default:
+ snd_printd(KERN_WARNING LOGNAME ": HIMT message %d 0x%02x\n",
+ HIBYTE(wMessage), HIBYTE(wMessage));
+ break;
+ }
+}
+
+static irqreturn_t snd_msnd_interrupt(int irq, void *dev_id)
+{
+ struct snd_msnd *chip = dev_id;
+ void *pwDSPQData = chip->mappedbase + DSPQ_DATA_BUFF;
+
+ /* Send ack to DSP */
+ /* inb(chip->io + HP_RXL); */
+
+ /* Evaluate queued DSP messages */
+ while (readw(chip->DSPQ + JQS_wTail) != readw(chip->DSPQ + JQS_wHead)) {
+ u16 wTmp;
+
+ snd_msnd_eval_dsp_msg(chip,
+ readw(pwDSPQData + 2 * readw(chip->DSPQ + JQS_wHead)));
+
+ wTmp = readw(chip->DSPQ + JQS_wHead) + 1;
+ if (wTmp > readw(chip->DSPQ + JQS_wSize))
+ writew(0, chip->DSPQ + JQS_wHead);
+ else
+ writew(wTmp, chip->DSPQ + JQS_wHead);
+ }
+ /* Send ack to DSP */
+ inb(chip->io + HP_RXL);
+ return IRQ_HANDLED;
+}
+
+
+static int snd_msnd_reset_dsp(long io, unsigned char *info)
+{
+ int timeout = 100;
+
+ outb(HPDSPRESET_ON, io + HP_DSPR);
+ msleep(1);
+#ifndef MSND_CLASSIC
+ if (info)
+ *info = inb(io + HP_INFO);
+#endif
+ outb(HPDSPRESET_OFF, io + HP_DSPR);
+ msleep(1);
+ while (timeout-- > 0) {
+ if (inb(io + HP_CVR) == HP_CVR_DEF)
+ return 0;
+ msleep(1);
+ }
+ snd_printk(KERN_ERR LOGNAME ": Cannot reset DSP\n");
+
+ return -EIO;
+}
+
+static int __devinit snd_msnd_probe(struct snd_card *card)
+{
+ struct snd_msnd *chip = card->private_data;
+ unsigned char info;
+#ifndef MSND_CLASSIC
+ char *xv, *rev = NULL;
+ char *pin = "TB Pinnacle", *fiji = "TB Fiji";
+ char *pinfiji = "TB Pinnacle/Fiji";
+#endif
+
+ if (!request_region(chip->io, DSP_NUMIO, "probing")) {
+ snd_printk(KERN_ERR LOGNAME ": I/O port conflict\n");
+ return -ENODEV;
+ }
+
+ if (snd_msnd_reset_dsp(chip->io, &info) < 0) {
+ release_region(chip->io, DSP_NUMIO);
+ return -ENODEV;
+ }
+
+#ifdef MSND_CLASSIC
+ strcpy(card->shortname, "Classic/Tahiti/Monterey");
+ strcpy(card->longname, "Turtle Beach Multisound");
+ printk(KERN_INFO LOGNAME ": %s, "
+ "I/O 0x%lx-0x%lx, IRQ %d, memory mapped to 0x%lX-0x%lX\n",
+ card->shortname,
+ chip->io, chip->io + DSP_NUMIO - 1,
+ chip->irq,
+ chip->base, chip->base + 0x7fff);
+#else
+ switch (info >> 4) {
+ case 0xf:
+ xv = "<= 1.15";
+ break;
+ case 0x1:
+ xv = "1.18/1.2";
+ break;
+ case 0x2:
+ xv = "1.3";
+ break;
+ case 0x3:
+ xv = "1.4";
+ break;
+ default:
+ xv = "unknown";
+ break;
+ }
+
+ switch (info & 0x7) {
+ case 0x0:
+ rev = "I";
+ strcpy(card->shortname, pin);
+ break;
+ case 0x1:
+ rev = "F";
+ strcpy(card->shortname, pin);
+ break;
+ case 0x2:
+ rev = "G";
+ strcpy(card->shortname, pin);
+ break;
+ case 0x3:
+ rev = "H";
+ strcpy(card->shortname, pin);
+ break;
+ case 0x4:
+ rev = "E";
+ strcpy(card->shortname, fiji);
+ break;
+ case 0x5:
+ rev = "C";
+ strcpy(card->shortname, fiji);
+ break;
+ case 0x6:
+ rev = "D";
+ strcpy(card->shortname, fiji);
+ break;
+ case 0x7:
+ rev = "A-B (Fiji) or A-E (Pinnacle)";
+ strcpy(card->shortname, pinfiji);
+ break;
+ }
+ strcpy(card->longname, "Turtle Beach Multisound Pinnacle");
+ printk(KERN_INFO LOGNAME ": %s revision %s, Xilinx version %s, "
+ "I/O 0x%lx-0x%lx, IRQ %d, memory mapped to 0x%lX-0x%lX\n",
+ card->shortname,
+ rev, xv,
+ chip->io, chip->io + DSP_NUMIO - 1,
+ chip->irq,
+ chip->base, chip->base + 0x7fff);
+#endif
+
+ release_region(chip->io, DSP_NUMIO);
+ return 0;
+}
+
+static int snd_msnd_init_sma(struct snd_msnd *chip)
+{
+ static int initted;
+ u16 mastVolLeft, mastVolRight;
+ unsigned long flags;
+
+#ifdef MSND_CLASSIC
+ outb(chip->memid, chip->io + HP_MEMM);
+#endif
+ outb(HPBLKSEL_0, chip->io + HP_BLKS);
+ /* Motorola 56k shared memory base */
+ chip->SMA = chip->mappedbase + SMA_STRUCT_START;
+
+ if (initted) {
+ mastVolLeft = readw(chip->SMA + SMA_wCurrMastVolLeft);
+ mastVolRight = readw(chip->SMA + SMA_wCurrMastVolRight);
+ } else
+ mastVolLeft = mastVolRight = 0;
+ memset_io(chip->mappedbase, 0, 0x8000);
+
+ /* Critical section: bank 1 access */
+ spin_lock_irqsave(&chip->lock, flags);
+ outb(HPBLKSEL_1, chip->io + HP_BLKS);
+ memset_io(chip->mappedbase, 0, 0x8000);
+ outb(HPBLKSEL_0, chip->io + HP_BLKS);
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ /* Digital audio play queue */
+ chip->DAPQ = chip->mappedbase + DAPQ_OFFSET;
+ snd_msnd_init_queue(chip->DAPQ, DAPQ_DATA_BUFF, DAPQ_BUFF_SIZE);
+
+ /* Digital audio record queue */
+ chip->DARQ = chip->mappedbase + DARQ_OFFSET;
+ snd_msnd_init_queue(chip->DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE);
+
+ /* MIDI out queue */
+ chip->MODQ = chip->mappedbase + MODQ_OFFSET;
+ snd_msnd_init_queue(chip->MODQ, MODQ_DATA_BUFF, MODQ_BUFF_SIZE);
+
+ /* MIDI in queue */
+ chip->MIDQ = chip->mappedbase + MIDQ_OFFSET;
+ snd_msnd_init_queue(chip->MIDQ, MIDQ_DATA_BUFF, MIDQ_BUFF_SIZE);
+
+ /* DSP -> host message queue */
+ chip->DSPQ = chip->mappedbase + DSPQ_OFFSET;
+ snd_msnd_init_queue(chip->DSPQ, DSPQ_DATA_BUFF, DSPQ_BUFF_SIZE);
+
+ /* Setup some DSP values */
+#ifndef MSND_CLASSIC
+ writew(1, chip->SMA + SMA_wCurrPlayFormat);
+ writew(chip->play_sample_size, chip->SMA + SMA_wCurrPlaySampleSize);
+ writew(chip->play_channels, chip->SMA + SMA_wCurrPlayChannels);
+ writew(chip->play_sample_rate, chip->SMA + SMA_wCurrPlaySampleRate);
+#endif
+ writew(chip->play_sample_rate, chip->SMA + SMA_wCalFreqAtoD);
+ writew(mastVolLeft, chip->SMA + SMA_wCurrMastVolLeft);
+ writew(mastVolRight, chip->SMA + SMA_wCurrMastVolRight);
+#ifndef MSND_CLASSIC
+ writel(0x00010000, chip->SMA + SMA_dwCurrPlayPitch);
+ writel(0x00000001, chip->SMA + SMA_dwCurrPlayRate);
+#endif
+ writew(0x303, chip->SMA + SMA_wCurrInputTagBits);
+
+ initted = 1;
+
+ return 0;
+}
+
+
+static int upload_dsp_code(struct snd_card *card)
+{
+ struct snd_msnd *chip = card->private_data;
+ const struct firmware *init_fw = NULL, *perm_fw = NULL;
+ int err;
+
+ outb(HPBLKSEL_0, chip->io + HP_BLKS);
+
+ err = request_firmware(&init_fw, INITCODEFILE, card->dev);
+ if (err < 0) {
+ printk(KERN_ERR LOGNAME ": Error loading " INITCODEFILE);
+ goto cleanup1;
+ }
+ err = request_firmware(&perm_fw, PERMCODEFILE, card->dev);
+ if (err < 0) {
+ printk(KERN_ERR LOGNAME ": Error loading " PERMCODEFILE);
+ goto cleanup;
+ }
+
+ memcpy_toio(chip->mappedbase, perm_fw->data, perm_fw->size);
+ if (snd_msnd_upload_host(chip, init_fw->data, init_fw->size) < 0) {
+ printk(KERN_WARNING LOGNAME ": Error uploading to DSP\n");
+ err = -ENODEV;
+ goto cleanup;
+ }
+ printk(KERN_INFO LOGNAME ": DSP firmware uploaded\n");
+ err = 0;
+
+cleanup:
+ release_firmware(perm_fw);
+cleanup1:
+ release_firmware(init_fw);
+ return err;
+}
+
+#ifdef MSND_CLASSIC
+static void reset_proteus(struct snd_msnd *chip)
+{
+ outb(HPPRORESET_ON, chip->io + HP_PROR);
+ msleep(TIME_PRO_RESET);
+ outb(HPPRORESET_OFF, chip->io + HP_PROR);
+ msleep(TIME_PRO_RESET_DONE);
+}
+#endif
+
+static int snd_msnd_initialize(struct snd_card *card)
+{
+ struct snd_msnd *chip = card->private_data;
+ int err, timeout;
+
+#ifdef MSND_CLASSIC
+ outb(HPWAITSTATE_0, chip->io + HP_WAIT);
+ outb(HPBITMODE_16, chip->io + HP_BITM);
+
+ reset_proteus(chip);
+#endif
+ err = snd_msnd_init_sma(chip);
+ if (err < 0) {
+ printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n");
+ return err;
+ }
+
+ err = snd_msnd_reset_dsp(chip->io, NULL);
+ if (err < 0)
+ return err;
+
+ err = upload_dsp_code(card);
+ if (err < 0) {
+ printk(KERN_WARNING LOGNAME ": Cannot upload DSP code\n");
+ return err;
+ }
+
+ timeout = 200;
+
+ while (readw(chip->mappedbase)) {
+ msleep(1);
+ if (!timeout--) {
+ snd_printd(KERN_ERR LOGNAME ": DSP reset timeout\n");
+ return -EIO;
+ }
+ }
+
+ snd_msndmix_setup(chip);
+ return 0;
+}
+
+static int snd_msnd_dsp_full_reset(struct snd_card *card)
+{
+ struct snd_msnd *chip = card->private_data;
+ int rv;
+
+ if (test_bit(F_RESETTING, &chip->flags) || ++chip->nresets > 10)
+ return 0;
+
+ set_bit(F_RESETTING, &chip->flags);
+ snd_msnd_dsp_halt(chip, NULL); /* Unconditionally halt */
+
+ rv = snd_msnd_initialize(card);
+ if (rv)
+ printk(KERN_WARNING LOGNAME ": DSP reset failed\n");
+ snd_msndmix_force_recsrc(chip, 0);
+ clear_bit(F_RESETTING, &chip->flags);
+ return rv;
+}
+
+static int snd_msnd_dev_free(struct snd_device *device)
+{
+ snd_printdd("snd_msnd_chip_free()\n");
+ return 0;
+}
+
+static int snd_msnd_send_dsp_cmd_chk(struct snd_msnd *chip, u8 cmd)
+{
+ if (snd_msnd_send_dsp_cmd(chip, cmd) == 0)
+ return 0;
+ snd_msnd_dsp_full_reset(chip->card);
+ return snd_msnd_send_dsp_cmd(chip, cmd);
+}
+
+static int __devinit snd_msnd_calibrate_adc(struct snd_msnd *chip, u16 srate)
+{
+ snd_printdd("snd_msnd_calibrate_adc(%i)\n", srate);
+ writew(srate, chip->SMA + SMA_wCalFreqAtoD);
+ if (chip->calibrate_signal == 0)
+ writew(readw(chip->SMA + SMA_wCurrHostStatusFlags)
+ | 0x0001, chip->SMA + SMA_wCurrHostStatusFlags);
+ else
+ writew(readw(chip->SMA + SMA_wCurrHostStatusFlags)
+ & ~0x0001, chip->SMA + SMA_wCurrHostStatusFlags);
+ if (snd_msnd_send_word(chip, 0, 0, HDEXAR_CAL_A_TO_D) == 0 &&
+ snd_msnd_send_dsp_cmd_chk(chip, HDEX_AUX_REQ) == 0) {
+ schedule_timeout_interruptible(msecs_to_jiffies(333));
+ return 0;
+ }
+ printk(KERN_WARNING LOGNAME ": ADC calibration failed\n");
+ return -EIO;
+}
+
+/*
+ * ALSA callback function, called when attempting to open the MIDI device.
+ */
+static int snd_msnd_mpu401_open(struct snd_mpu401 *mpu)
+{
+ snd_msnd_enable_irq(mpu->private_data);
+ snd_msnd_send_dsp_cmd(mpu->private_data, HDEX_MIDI_IN_START);
+ return 0;
+}
+
+static void snd_msnd_mpu401_close(struct snd_mpu401 *mpu)
+{
+ snd_msnd_send_dsp_cmd(mpu->private_data, HDEX_MIDI_IN_STOP);
+ snd_msnd_disable_irq(mpu->private_data);
+}
+
+static long mpu_io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+
+static int __devinit snd_msnd_attach(struct snd_card *card)
+{
+ struct snd_msnd *chip = card->private_data;
+ int err;
+ static struct snd_device_ops ops = {
+ .dev_free = snd_msnd_dev_free,
+ };
+
+ err = request_irq(chip->irq, snd_msnd_interrupt, 0, card->shortname,
+ chip);
+ if (err < 0) {
+ printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", chip->irq);
+ return err;
+ }
+ request_region(chip->io, DSP_NUMIO, card->shortname);
+
+ if (!request_mem_region(chip->base, BUFFSIZE, card->shortname)) {
+ printk(KERN_ERR LOGNAME
+ ": unable to grab memory region 0x%lx-0x%lx\n",
+ chip->base, chip->base + BUFFSIZE - 1);
+ release_region(chip->io, DSP_NUMIO);
+ free_irq(chip->irq, chip);
+ return -EBUSY;
+ }
+ chip->mappedbase = ioremap_nocache(chip->base, 0x8000);
+ if (!chip->mappedbase) {
+ printk(KERN_ERR LOGNAME
+ ": unable to map memory region 0x%lx-0x%lx\n",
+ chip->base, chip->base + BUFFSIZE - 1);
+ err = -EIO;
+ goto err_release_region;
+ }
+
+ err = snd_msnd_dsp_full_reset(card);
+ if (err < 0)
+ goto err_release_region;
+
+ /* Register device */
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0)
+ goto err_release_region;
+
+ err = snd_msnd_pcm(card, 0, NULL);
+ if (err < 0) {
+ printk(KERN_ERR LOGNAME ": error creating new PCM device\n");
+ goto err_release_region;
+ }
+
+ err = snd_msndmix_new(card);
+ if (err < 0) {
+ printk(KERN_ERR LOGNAME ": error creating new Mixer device\n");
+ goto err_release_region;
+ }
+
+
+ if (mpu_io[0] != SNDRV_AUTO_PORT) {
+ struct snd_mpu401 *mpu;
+
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+ mpu_io[0],
+ MPU401_MODE_INPUT |
+ MPU401_MODE_OUTPUT,
+ mpu_irq[0], IRQF_DISABLED,
+ &chip->rmidi);
+ if (err < 0) {
+ printk(KERN_ERR LOGNAME
+ ": error creating new Midi device\n");
+ goto err_release_region;
+ }
+ mpu = chip->rmidi->private_data;
+
+ mpu->open_input = snd_msnd_mpu401_open;
+ mpu->close_input = snd_msnd_mpu401_close;
+ mpu->private_data = chip;
+ }
+
+ disable_irq(chip->irq);
+ snd_msnd_calibrate_adc(chip, chip->play_sample_rate);
+ snd_msndmix_force_recsrc(chip, 0);
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto err_release_region;
+
+ return 0;
+
+err_release_region:
+ if (chip->mappedbase)
+ iounmap(chip->mappedbase);
+ release_mem_region(chip->base, BUFFSIZE);
+ release_region(chip->io, DSP_NUMIO);
+ free_irq(chip->irq, chip);
+ return err;
+}
+
+
+static void __devexit snd_msnd_unload(struct snd_card *card)
+{
+ struct snd_msnd *chip = card->private_data;
+
+ iounmap(chip->mappedbase);
+ release_mem_region(chip->base, BUFFSIZE);
+ release_region(chip->io, DSP_NUMIO);
+ free_irq(chip->irq, chip);
+ snd_card_free(card);
+}
+
+#ifndef MSND_CLASSIC
+
+/* Pinnacle/Fiji Logical Device Configuration */
+
+static int __devinit snd_msnd_write_cfg(int cfg, int reg, int value)
+{
+ outb(reg, cfg);
+ outb(value, cfg + 1);
+ if (value != inb(cfg + 1)) {
+ printk(KERN_ERR LOGNAME ": snd_msnd_write_cfg: I/O error\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static int __devinit snd_msnd_write_cfg_io0(int cfg, int num, u16 io)
+{
+ if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ return -EIO;
+ if (snd_msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io)))
+ return -EIO;
+ if (snd_msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io)))
+ return -EIO;
+ return 0;
+}
+
+static int __devinit snd_msnd_write_cfg_io1(int cfg, int num, u16 io)
+{
+ if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ return -EIO;
+ if (snd_msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io)))
+ return -EIO;
+ if (snd_msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io)))
+ return -EIO;
+ return 0;
+}
+
+static int __devinit snd_msnd_write_cfg_irq(int cfg, int num, u16 irq)
+{
+ if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ return -EIO;
+ if (snd_msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq)))
+ return -EIO;
+ if (snd_msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE))
+ return -EIO;
+ return 0;
+}
+
+static int __devinit snd_msnd_write_cfg_mem(int cfg, int num, int mem)
+{
+ u16 wmem;
+
+ mem >>= 8;
+ wmem = (u16)(mem & 0xfff);
+ if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ return -EIO;
+ if (snd_msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem)))
+ return -EIO;
+ if (snd_msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem)))
+ return -EIO;
+ if (wmem && snd_msnd_write_cfg(cfg, IREG_MEMCONTROL,
+ MEMTYPE_HIADDR | MEMTYPE_16BIT))
+ return -EIO;
+ return 0;
+}
+
+static int __devinit snd_msnd_activate_logical(int cfg, int num)
+{
+ if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ return -EIO;
+ if (snd_msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE))
+ return -EIO;
+ return 0;
+}
+
+static int __devinit snd_msnd_write_cfg_logical(int cfg, int num, u16 io0,
+ u16 io1, u16 irq, int mem)
+{
+ if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ return -EIO;
+ if (snd_msnd_write_cfg_io0(cfg, num, io0))
+ return -EIO;
+ if (snd_msnd_write_cfg_io1(cfg, num, io1))
+ return -EIO;
+ if (snd_msnd_write_cfg_irq(cfg, num, irq))
+ return -EIO;
+ if (snd_msnd_write_cfg_mem(cfg, num, mem))
+ return -EIO;
+ if (snd_msnd_activate_logical(cfg, num))
+ return -EIO;
+ return 0;
+}
+
+static int __devinit snd_msnd_pinnacle_cfg_reset(int cfg)
+{
+ int i;
+
+ /* Reset devices if told to */
+ printk(KERN_INFO LOGNAME ": Resetting all devices\n");
+ for (i = 0; i < 4; ++i)
+ if (snd_msnd_write_cfg_logical(cfg, i, 0, 0, 0, 0))
+ return -EIO;
+
+ return 0;
+}
+#endif
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+
+module_param_array(index, int, NULL, S_IRUGO);
+MODULE_PARM_DESC(index, "Index value for msnd_pinnacle soundcard.");
+module_param_array(id, charp, NULL, S_IRUGO);
+MODULE_PARM_DESC(id, "ID string for msnd_pinnacle soundcard.");
+
+static long io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+static long mem[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+
+static long cfg[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+
+#ifndef MSND_CLASSIC
+/* Extra Peripheral Configuration (Default: Disable) */
+static long ide_io0[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static long ide_io1[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static int ide_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+
+static long joystick_io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+/* If we have the digital daugherboard... */
+static int digital[SNDRV_CARDS];
+
+/* Extra Peripheral Configuration */
+static int reset[SNDRV_CARDS];
+#endif
+
+static int write_ndelay[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 1 };
+
+static int calibrate_signal;
+
+#ifdef CONFIG_PNP
+static int isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+module_param_array(isapnp, bool, NULL, 0444);
+MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard.");
+#define has_isapnp(x) isapnp[x]
+#else
+#define has_isapnp(x) 0
+#endif
+
+MODULE_AUTHOR("Karsten Wiese <annabellesgarden@yahoo.de>");
+MODULE_DESCRIPTION("Turtle Beach " LONGNAME " Linux Driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(INITCODEFILE);
+MODULE_FIRMWARE(PERMCODEFILE);
+
+module_param_array(io, long, NULL, S_IRUGO);
+MODULE_PARM_DESC(io, "IO port #");
+module_param_array(irq, int, NULL, S_IRUGO);
+module_param_array(mem, long, NULL, S_IRUGO);
+module_param_array(write_ndelay, int, NULL, S_IRUGO);
+module_param(calibrate_signal, int, S_IRUGO);
+#ifndef MSND_CLASSIC
+module_param_array(digital, int, NULL, S_IRUGO);
+module_param_array(cfg, long, NULL, S_IRUGO);
+module_param_array(reset, int, 0, S_IRUGO);
+module_param_array(mpu_io, long, NULL, S_IRUGO);
+module_param_array(mpu_irq, int, NULL, S_IRUGO);
+module_param_array(ide_io0, long, NULL, S_IRUGO);
+module_param_array(ide_io1, long, NULL, S_IRUGO);
+module_param_array(ide_irq, int, NULL, S_IRUGO);
+module_param_array(joystick_io, long, NULL, S_IRUGO);
+#endif
+
+
+static int __devinit snd_msnd_isa_match(struct device *pdev, unsigned int i)
+{
+ if (io[i] == SNDRV_AUTO_PORT)
+ return 0;
+
+ if (irq[i] == SNDRV_AUTO_PORT || mem[i] == SNDRV_AUTO_PORT) {
+ printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n");
+ return 0;
+ }
+
+#ifdef MSND_CLASSIC
+ if (!(io[i] == 0x290 ||
+ io[i] == 0x260 ||
+ io[i] == 0x250 ||
+ io[i] == 0x240 ||
+ io[i] == 0x230 ||
+ io[i] == 0x220 ||
+ io[i] == 0x210 ||
+ io[i] == 0x3e0)) {
+ printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set "
+ " to 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x290, "
+ "or 0x3E0\n");
+ return 0;
+ }
+#else
+ if (io[i] < 0x100 || io[i] > 0x3e0 || (io[i] % 0x10) != 0) {
+ printk(KERN_ERR LOGNAME
+ ": \"io\" - DSP I/O base must within the range 0x100 "
+ "to 0x3E0 and must be evenly divisible by 0x10\n");
+ return 0;
+ }
+#endif /* MSND_CLASSIC */
+
+ if (!(irq[i] == 5 ||
+ irq[i] == 7 ||
+ irq[i] == 9 ||
+ irq[i] == 10 ||
+ irq[i] == 11 ||
+ irq[i] == 12)) {
+ printk(KERN_ERR LOGNAME
+ ": \"irq\" - must be set to 5, 7, 9, 10, 11 or 12\n");
+ return 0;
+ }
+
+ if (!(mem[i] == 0xb0000 ||
+ mem[i] == 0xc8000 ||
+ mem[i] == 0xd0000 ||
+ mem[i] == 0xd8000 ||
+ mem[i] == 0xe0000 ||
+ mem[i] == 0xe8000)) {
+ printk(KERN_ERR LOGNAME ": \"mem\" - must be set to "
+ "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or "
+ "0xe8000\n");
+ return 0;
+ }
+
+#ifndef MSND_CLASSIC
+ if (cfg[i] == SNDRV_AUTO_PORT) {
+ printk(KERN_INFO LOGNAME ": Assuming PnP mode\n");
+ } else if (cfg[i] != 0x250 && cfg[i] != 0x260 && cfg[i] != 0x270) {
+ printk(KERN_INFO LOGNAME
+ ": Config port must be 0x250, 0x260 or 0x270 "
+ "(or unspecified for PnP mode)\n");
+ return 0;
+ }
+#endif /* MSND_CLASSIC */
+
+ return 1;
+}
+
+static int __devinit snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
+{
+ int err;
+ struct snd_card *card;
+ struct snd_msnd *chip;
+
+ if (has_isapnp(idx) || cfg[idx] == SNDRV_AUTO_PORT) {
+ printk(KERN_INFO LOGNAME ": Assuming PnP mode\n");
+ return -ENODEV;
+ }
+
+ err = snd_card_create(index[idx], id[idx], THIS_MODULE,
+ sizeof(struct snd_msnd), &card);
+ if (err < 0)
+ return err;
+
+ snd_card_set_dev(card, pdev);
+ chip = card->private_data;
+ chip->card = card;
+
+#ifdef MSND_CLASSIC
+ switch (irq[idx]) {
+ case 5:
+ chip->irqid = HPIRQ_5; break;
+ case 7:
+ chip->irqid = HPIRQ_7; break;
+ case 9:
+ chip->irqid = HPIRQ_9; break;
+ case 10:
+ chip->irqid = HPIRQ_10; break;
+ case 11:
+ chip->irqid = HPIRQ_11; break;
+ case 12:
+ chip->irqid = HPIRQ_12; break;
+ }
+
+ switch (mem[idx]) {
+ case 0xb0000:
+ chip->memid = HPMEM_B000; break;
+ case 0xc8000:
+ chip->memid = HPMEM_C800; break;
+ case 0xd0000:
+ chip->memid = HPMEM_D000; break;
+ case 0xd8000:
+ chip->memid = HPMEM_D800; break;
+ case 0xe0000:
+ chip->memid = HPMEM_E000; break;
+ case 0xe8000:
+ chip->memid = HPMEM_E800; break;
+ }
+#else
+ printk(KERN_INFO LOGNAME ": Non-PnP mode: configuring at port 0x%lx\n",
+ cfg[idx]);
+
+ if (!request_region(cfg[idx], 2, "Pinnacle/Fiji Config")) {
+ printk(KERN_ERR LOGNAME ": Config port 0x%lx conflict\n",
+ cfg[idx]);
+ snd_card_free(card);
+ return -EIO;
+ }
+ if (reset[idx])
+ if (snd_msnd_pinnacle_cfg_reset(cfg[idx])) {
+ err = -EIO;
+ goto cfg_error;
+ }
+
+ /* DSP */
+ err = snd_msnd_write_cfg_logical(cfg[idx], 0,
+ io[idx], 0,
+ irq[idx], mem[idx]);
+
+ if (err)
+ goto cfg_error;
+
+ /* The following are Pinnacle specific */
+
+ /* MPU */
+ if (mpu_io[idx] != SNDRV_AUTO_PORT
+ && mpu_irq[idx] != SNDRV_AUTO_IRQ) {
+ printk(KERN_INFO LOGNAME
+ ": Configuring MPU to I/O 0x%lx IRQ %d\n",
+ mpu_io[idx], mpu_irq[idx]);
+ err = snd_msnd_write_cfg_logical(cfg[idx], 1,
+ mpu_io[idx], 0,
+ mpu_irq[idx], 0);
+
+ if (err)
+ goto cfg_error;
+ }
+
+ /* IDE */
+ if (ide_io0[idx] != SNDRV_AUTO_PORT
+ && ide_io1[idx] != SNDRV_AUTO_PORT
+ && ide_irq[idx] != SNDRV_AUTO_IRQ) {
+ printk(KERN_INFO LOGNAME
+ ": Configuring IDE to I/O 0x%lx, 0x%lx IRQ %d\n",
+ ide_io0[idx], ide_io1[idx], ide_irq[idx]);
+ err = snd_msnd_write_cfg_logical(cfg[idx], 2,
+ ide_io0[idx], ide_io1[idx],
+ ide_irq[idx], 0);
+
+ if (err)
+ goto cfg_error;
+ }
+
+ /* Joystick */
+ if (joystick_io[idx] != SNDRV_AUTO_PORT) {
+ printk(KERN_INFO LOGNAME
+ ": Configuring joystick to I/O 0x%lx\n",
+ joystick_io[idx]);
+ err = snd_msnd_write_cfg_logical(cfg[idx], 3,
+ joystick_io[idx], 0,
+ 0, 0);
+
+ if (err)
+ goto cfg_error;
+ }
+ release_region(cfg[idx], 2);
+
+#endif /* MSND_CLASSIC */
+
+ set_default_audio_parameters(chip);
+#ifdef MSND_CLASSIC
+ chip->type = msndClassic;
+#else
+ chip->type = msndPinnacle;
+#endif
+ chip->io = io[idx];
+ chip->irq = irq[idx];
+ chip->base = mem[idx];
+
+ chip->calibrate_signal = calibrate_signal ? 1 : 0;
+ chip->recsrc = 0;
+ chip->dspq_data_buff = DSPQ_DATA_BUFF;
+ chip->dspq_buff_size = DSPQ_BUFF_SIZE;
+ if (write_ndelay[idx])
+ clear_bit(F_DISABLE_WRITE_NDELAY, &chip->flags);
+ else
+ set_bit(F_DISABLE_WRITE_NDELAY, &chip->flags);
+#ifndef MSND_CLASSIC
+ if (digital[idx])
+ set_bit(F_HAVEDIGITAL, &chip->flags);
+#endif
+ spin_lock_init(&chip->lock);
+ err = snd_msnd_probe(card);
+ if (err < 0) {
+ printk(KERN_ERR LOGNAME ": Probe failed\n");
+ snd_card_free(card);
+ return err;
+ }
+
+ err = snd_msnd_attach(card);
+ if (err < 0) {
+ printk(KERN_ERR LOGNAME ": Attach failed\n");
+ snd_card_free(card);
+ return err;
+ }
+ dev_set_drvdata(pdev, card);
+
+ return 0;
+
+#ifndef MSND_CLASSIC
+cfg_error:
+ release_region(cfg[idx], 2);
+ snd_card_free(card);
+ return err;
+#endif
+}
+
+static int __devexit snd_msnd_isa_remove(struct device *pdev, unsigned int dev)
+{
+ snd_msnd_unload(dev_get_drvdata(pdev));
+ dev_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+#define DEV_NAME "msnd-pinnacle"
+
+static struct isa_driver snd_msnd_driver = {
+ .match = snd_msnd_isa_match,
+ .probe = snd_msnd_isa_probe,
+ .remove = __devexit_p(snd_msnd_isa_remove),
+ /* FIXME: suspend, resume */
+ .driver = {
+ .name = DEV_NAME
+ },
+};
+
+#ifdef CONFIG_PNP
+static int __devinit snd_msnd_pnp_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
+{
+ static int idx;
+ struct pnp_dev *pnp_dev;
+ struct pnp_dev *mpu_dev;
+ struct snd_card *card;
+ struct snd_msnd *chip;
+ int ret;
+
+ for ( ; idx < SNDRV_CARDS; idx++) {
+ if (has_isapnp(idx))
+ break;
+ }
+ if (idx >= SNDRV_CARDS)
+ return -ENODEV;
+
+ /*
+ * Check that we still have room for another sound card ...
+ */
+ pnp_dev = pnp_request_card_device(pcard, pid->devs[0].id, NULL);
+ if (!pnp_dev)
+ return -ENODEV;
+
+ mpu_dev = pnp_request_card_device(pcard, pid->devs[1].id, NULL);
+ if (!mpu_dev)
+ return -ENODEV;
+
+ if (!pnp_is_active(pnp_dev) && pnp_activate_dev(pnp_dev) < 0) {
+ printk(KERN_INFO "msnd_pinnacle: device is inactive\n");
+ return -EBUSY;
+ }
+
+ if (!pnp_is_active(mpu_dev) && pnp_activate_dev(mpu_dev) < 0) {
+ printk(KERN_INFO "msnd_pinnacle: MPU device is inactive\n");
+ return -EBUSY;
+ }
+
+ /*
+ * Create a new ALSA sound card entry, in anticipation
+ * of detecting our hardware ...
+ */
+ ret = snd_card_create(index[idx], id[idx], THIS_MODULE,
+ sizeof(struct snd_msnd), &card);
+ if (ret < 0)
+ return ret;
+
+ chip = card->private_data;
+ chip->card = card;
+ snd_card_set_dev(card, &pcard->card->dev);
+
+ /*
+ * Read the correct parameters off the ISA PnP bus ...
+ */
+ io[idx] = pnp_port_start(pnp_dev, 0);
+ irq[idx] = pnp_irq(pnp_dev, 0);
+ mem[idx] = pnp_mem_start(pnp_dev, 0);
+ mpu_io[idx] = pnp_port_start(mpu_dev, 0);
+ mpu_irq[idx] = pnp_irq(mpu_dev, 0);
+
+ set_default_audio_parameters(chip);
+#ifdef MSND_CLASSIC
+ chip->type = msndClassic;
+#else
+ chip->type = msndPinnacle;
+#endif
+ chip->io = io[idx];
+ chip->irq = irq[idx];
+ chip->base = mem[idx];
+
+ chip->calibrate_signal = calibrate_signal ? 1 : 0;
+ chip->recsrc = 0;
+ chip->dspq_data_buff = DSPQ_DATA_BUFF;
+ chip->dspq_buff_size = DSPQ_BUFF_SIZE;
+ if (write_ndelay[idx])
+ clear_bit(F_DISABLE_WRITE_NDELAY, &chip->flags);
+ else
+ set_bit(F_DISABLE_WRITE_NDELAY, &chip->flags);
+#ifndef MSND_CLASSIC
+ if (digital[idx])
+ set_bit(F_HAVEDIGITAL, &chip->flags);
+#endif
+ spin_lock_init(&chip->lock);
+ ret = snd_msnd_probe(card);
+ if (ret < 0) {
+ printk(KERN_ERR LOGNAME ": Probe failed\n");
+ goto _release_card;
+ }
+
+ ret = snd_msnd_attach(card);
+ if (ret < 0) {
+ printk(KERN_ERR LOGNAME ": Attach failed\n");
+ goto _release_card;
+ }
+
+ pnp_set_card_drvdata(pcard, card);
+ ++idx;
+ return 0;
+
+_release_card:
+ snd_card_free(card);
+ return ret;
+}
+
+static void __devexit snd_msnd_pnp_remove(struct pnp_card_link *pcard)
+{
+ snd_msnd_unload(pnp_get_card_drvdata(pcard));
+ pnp_set_card_drvdata(pcard, NULL);
+}
+
+static int isa_registered;
+static int pnp_registered;
+
+static struct pnp_card_device_id msnd_pnpids[] = {
+ /* Pinnacle PnP */
+ { .id = "BVJ0440", .devs = { { "TBS0000" }, { "TBS0001" } } },
+ { .id = "" } /* end */
+};
+
+MODULE_DEVICE_TABLE(pnp_card, msnd_pnpids);
+
+static struct pnp_card_driver msnd_pnpc_driver = {
+ .flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
+ .name = "msnd_pinnacle",
+ .id_table = msnd_pnpids,
+ .probe = snd_msnd_pnp_detect,
+ .remove = __devexit_p(snd_msnd_pnp_remove),
+};
+#endif /* CONFIG_PNP */
+
+static int __init snd_msnd_init(void)
+{
+ int err;
+
+ err = isa_register_driver(&snd_msnd_driver, SNDRV_CARDS);
+#ifdef CONFIG_PNP
+ if (!err)
+ isa_registered = 1;
+
+ err = pnp_register_card_driver(&msnd_pnpc_driver);
+ if (!err)
+ pnp_registered = 1;
+
+ if (isa_registered)
+ err = 0;
+#endif
+ return err;
+}
+
+static void __exit snd_msnd_exit(void)
+{
+#ifdef CONFIG_PNP
+ if (pnp_registered)
+ pnp_unregister_card_driver(&msnd_pnpc_driver);
+ if (isa_registered)
+#endif
+ isa_unregister_driver(&snd_msnd_driver);
+}
+
+module_init(snd_msnd_init);
+module_exit(snd_msnd_exit);
+
diff --git a/sound/isa/msnd/msnd_pinnacle.h b/sound/isa/msnd/msnd_pinnacle.h
new file mode 100644
index 0000000..48318d1
--- /dev/null
+++ b/sound/isa/msnd/msnd_pinnacle.h
@@ -0,0 +1,181 @@
+/*********************************************************************
+ *
+ * msnd_pinnacle.h
+ *
+ * Turtle Beach MultiSound Sound Card Driver for Linux
+ *
+ * Some parts of this header file were derived from the Turtle Beach
+ * MultiSound Driver Development Kit.
+ *
+ * Copyright (C) 1998 Andrew Veliath
+ * Copyright (C) 1993 Turtle Beach Systems, Inc.
+ *
+ * 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.
+ *
+ ********************************************************************/
+#ifndef __MSND_PINNACLE_H
+#define __MSND_PINNACLE_H
+
+#define DSP_NUMIO 0x08
+
+#define IREG_LOGDEVICE 0x07
+#define IREG_ACTIVATE 0x30
+#define LD_ACTIVATE 0x01
+#define LD_DISACTIVATE 0x00
+#define IREG_EECONTROL 0x3F
+#define IREG_MEMBASEHI 0x40
+#define IREG_MEMBASELO 0x41
+#define IREG_MEMCONTROL 0x42
+#define IREG_MEMRANGEHI 0x43
+#define IREG_MEMRANGELO 0x44
+#define MEMTYPE_8BIT 0x00
+#define MEMTYPE_16BIT 0x02
+#define MEMTYPE_RANGE 0x00
+#define MEMTYPE_HIADDR 0x01
+#define IREG_IO0_BASEHI 0x60
+#define IREG_IO0_BASELO 0x61
+#define IREG_IO1_BASEHI 0x62
+#define IREG_IO1_BASELO 0x63
+#define IREG_IRQ_NUMBER 0x70
+#define IREG_IRQ_TYPE 0x71
+#define IRQTYPE_HIGH 0x02
+#define IRQTYPE_LOW 0x00
+#define IRQTYPE_LEVEL 0x01
+#define IRQTYPE_EDGE 0x00
+
+#define HP_DSPR 0x04
+#define HP_BLKS 0x04
+
+#define HPDSPRESET_OFF 2
+#define HPDSPRESET_ON 0
+
+#define HPBLKSEL_0 2
+#define HPBLKSEL_1 3
+
+#define HIMT_DAT_OFF 0x03
+
+#define HIDSP_PLAY_UNDER 0x00
+#define HIDSP_INT_PLAY_UNDER 0x01
+#define HIDSP_SSI_TX_UNDER 0x02
+#define HIDSP_RECQ_OVERFLOW 0x08
+#define HIDSP_INT_RECORD_OVER 0x09
+#define HIDSP_SSI_RX_OVERFLOW 0x0a
+
+#define HIDSP_MIDI_IN_OVER 0x10
+
+#define HIDSP_MIDI_FRAME_ERR 0x11
+#define HIDSP_MIDI_PARITY_ERR 0x12
+#define HIDSP_MIDI_OVERRUN_ERR 0x13
+
+#define HIDSP_INPUT_CLIPPING 0x20
+#define HIDSP_MIX_CLIPPING 0x30
+#define HIDSP_DAT_IN_OFF 0x21
+
+#define TIME_PRO_RESET_DONE 0x028A
+#define TIME_PRO_SYSEX 0x001E
+#define TIME_PRO_RESET 0x0032
+
+#define DAR_BUFF_SIZE 0x1000
+
+#define MIDQ_BUFF_SIZE 0x800
+#define DSPQ_BUFF_SIZE 0x5A0
+
+#define DSPQ_DATA_BUFF 0x7860
+
+#define MOP_WAVEHDR 0
+#define MOP_EXTOUT 1
+#define MOP_HWINIT 0xfe
+#define MOP_NONE 0xff
+#define MOP_MAX 1
+
+#define MIP_EXTIN 0
+#define MIP_WAVEHDR 1
+#define MIP_HWINIT 0xfe
+#define MIP_MAX 1
+
+/* Pinnacle/Fiji SMA Common Data */
+#define SMA_wCurrPlayBytes 0x0000
+#define SMA_wCurrRecordBytes 0x0002
+#define SMA_wCurrPlayVolLeft 0x0004
+#define SMA_wCurrPlayVolRight 0x0006
+#define SMA_wCurrInVolLeft 0x0008
+#define SMA_wCurrInVolRight 0x000a
+#define SMA_wCurrMHdrVolLeft 0x000c
+#define SMA_wCurrMHdrVolRight 0x000e
+#define SMA_dwCurrPlayPitch 0x0010
+#define SMA_dwCurrPlayRate 0x0014
+#define SMA_wCurrMIDIIOPatch 0x0018
+#define SMA_wCurrPlayFormat 0x001a
+#define SMA_wCurrPlaySampleSize 0x001c
+#define SMA_wCurrPlayChannels 0x001e
+#define SMA_wCurrPlaySampleRate 0x0020
+#define SMA_wCurrRecordFormat 0x0022
+#define SMA_wCurrRecordSampleSize 0x0024
+#define SMA_wCurrRecordChannels 0x0026
+#define SMA_wCurrRecordSampleRate 0x0028
+#define SMA_wCurrDSPStatusFlags 0x002a
+#define SMA_wCurrHostStatusFlags 0x002c
+#define SMA_wCurrInputTagBits 0x002e
+#define SMA_wCurrLeftPeak 0x0030
+#define SMA_wCurrRightPeak 0x0032
+#define SMA_bMicPotPosLeft 0x0034
+#define SMA_bMicPotPosRight 0x0035
+#define SMA_bMicPotMaxLeft 0x0036
+#define SMA_bMicPotMaxRight 0x0037
+#define SMA_bInPotPosLeft 0x0038
+#define SMA_bInPotPosRight 0x0039
+#define SMA_bAuxPotPosLeft 0x003a
+#define SMA_bAuxPotPosRight 0x003b
+#define SMA_bInPotMaxLeft 0x003c
+#define SMA_bInPotMaxRight 0x003d
+#define SMA_bAuxPotMaxLeft 0x003e
+#define SMA_bAuxPotMaxRight 0x003f
+#define SMA_bInPotMaxMethod 0x0040
+#define SMA_bAuxPotMaxMethod 0x0041
+#define SMA_wCurrMastVolLeft 0x0042
+#define SMA_wCurrMastVolRight 0x0044
+#define SMA_wCalFreqAtoD 0x0046
+#define SMA_wCurrAuxVolLeft 0x0048
+#define SMA_wCurrAuxVolRight 0x004a
+#define SMA_wCurrPlay1VolLeft 0x004c
+#define SMA_wCurrPlay1VolRight 0x004e
+#define SMA_wCurrPlay2VolLeft 0x0050
+#define SMA_wCurrPlay2VolRight 0x0052
+#define SMA_wCurrPlay3VolLeft 0x0054
+#define SMA_wCurrPlay3VolRight 0x0056
+#define SMA_wCurrPlay4VolLeft 0x0058
+#define SMA_wCurrPlay4VolRight 0x005a
+#define SMA_wCurrPlay1PeakLeft 0x005c
+#define SMA_wCurrPlay1PeakRight 0x005e
+#define SMA_wCurrPlay2PeakLeft 0x0060
+#define SMA_wCurrPlay2PeakRight 0x0062
+#define SMA_wCurrPlay3PeakLeft 0x0064
+#define SMA_wCurrPlay3PeakRight 0x0066
+#define SMA_wCurrPlay4PeakLeft 0x0068
+#define SMA_wCurrPlay4PeakRight 0x006a
+#define SMA_wCurrPlayPeakLeft 0x006c
+#define SMA_wCurrPlayPeakRight 0x006e
+#define SMA_wCurrDATSR 0x0070
+#define SMA_wCurrDATRXCHNL 0x0072
+#define SMA_wCurrDATTXCHNL 0x0074
+#define SMA_wCurrDATRXRate 0x0076
+#define SMA_dwDSPPlayCount 0x0078
+#define SMA__size 0x007c
+
+#define INITCODEFILE "turtlebeach/pndspini.bin"
+#define PERMCODEFILE "turtlebeach/pndsperm.bin"
+#define LONGNAME "MultiSound (Pinnacle/Fiji)"
+
+#endif /* __MSND_PINNACLE_H */
diff --git a/sound/isa/msnd/msnd_pinnacle_mixer.c b/sound/isa/msnd/msnd_pinnacle_mixer.c
new file mode 100644
index 0000000..494058a
--- /dev/null
+++ b/sound/isa/msnd/msnd_pinnacle_mixer.c
@@ -0,0 +1,343 @@
+/***************************************************************************
+ msnd_pinnacle_mixer.c - description
+ -------------------
+ begin : Fre Jun 7 2002
+ copyright : (C) 2002 by karsten wiese
+ email : annabellesgarden@yahoo.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. *
+ * *
+ ***************************************************************************/
+
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include "msnd.h"
+#include "msnd_pinnacle.h"
+
+
+#define MSND_MIXER_VOLUME 0
+#define MSND_MIXER_PCM 1
+#define MSND_MIXER_AUX 2 /* Input source 1 (aux1) */
+#define MSND_MIXER_IMIX 3 /* Recording monitor */
+#define MSND_MIXER_SYNTH 4
+#define MSND_MIXER_SPEAKER 5
+#define MSND_MIXER_LINE 6
+#define MSND_MIXER_MIC 7
+#define MSND_MIXER_RECLEV 11 /* Recording level */
+#define MSND_MIXER_IGAIN 12 /* Input gain */
+#define MSND_MIXER_OGAIN 13 /* Output gain */
+#define MSND_MIXER_DIGITAL 17 /* Digital (input) 1 */
+
+/* Device mask bits */
+
+#define MSND_MASK_VOLUME (1 << MSND_MIXER_VOLUME)
+#define MSND_MASK_SYNTH (1 << MSND_MIXER_SYNTH)
+#define MSND_MASK_PCM (1 << MSND_MIXER_PCM)
+#define MSND_MASK_SPEAKER (1 << MSND_MIXER_SPEAKER)
+#define MSND_MASK_LINE (1 << MSND_MIXER_LINE)
+#define MSND_MASK_MIC (1 << MSND_MIXER_MIC)
+#define MSND_MASK_IMIX (1 << MSND_MIXER_IMIX)
+#define MSND_MASK_RECLEV (1 << MSND_MIXER_RECLEV)
+#define MSND_MASK_IGAIN (1 << MSND_MIXER_IGAIN)
+#define MSND_MASK_OGAIN (1 << MSND_MIXER_OGAIN)
+#define MSND_MASK_AUX (1 << MSND_MIXER_AUX)
+#define MSND_MASK_DIGITAL (1 << MSND_MIXER_DIGITAL)
+
+static int snd_msndmix_info_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[3] = {
+ "Analog", "MASS", "SPDIF",
+ };
+ struct snd_msnd *chip = snd_kcontrol_chip(kcontrol);
+ unsigned items = test_bit(F_HAVEDIGITAL, &chip->flags) ? 3 : 2;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = items;
+ if (uinfo->value.enumerated.item >= items)
+ uinfo->value.enumerated.item = items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_msndmix_get_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_msnd *chip = snd_kcontrol_chip(kcontrol);
+ /* MSND_MASK_IMIX is the default */
+ ucontrol->value.enumerated.item[0] = 0;
+
+ if (chip->recsrc & MSND_MASK_SYNTH) {
+ ucontrol->value.enumerated.item[0] = 1;
+ } else if ((chip->recsrc & MSND_MASK_DIGITAL) &&
+ test_bit(F_HAVEDIGITAL, &chip->flags)) {
+ ucontrol->value.enumerated.item[0] = 2;
+ }
+
+
+ return 0;
+}
+
+static int snd_msndmix_set_mux(struct snd_msnd *chip, int val)
+{
+ unsigned newrecsrc;
+ int change;
+ unsigned char msndbyte;
+
+ switch (val) {
+ case 0:
+ newrecsrc = MSND_MASK_IMIX;
+ msndbyte = HDEXAR_SET_ANA_IN;
+ break;
+ case 1:
+ newrecsrc = MSND_MASK_SYNTH;
+ msndbyte = HDEXAR_SET_SYNTH_IN;
+ break;
+ case 2:
+ newrecsrc = MSND_MASK_DIGITAL;
+ msndbyte = HDEXAR_SET_DAT_IN;
+ break;
+ default:
+ return -EINVAL;
+ }
+ change = newrecsrc != chip->recsrc;
+ if (change) {
+ change = 0;
+ if (!snd_msnd_send_word(chip, 0, 0, msndbyte))
+ if (!snd_msnd_send_dsp_cmd(chip, HDEX_AUX_REQ)) {
+ chip->recsrc = newrecsrc;
+ change = 1;
+ }
+ }
+ return change;
+}
+
+static int snd_msndmix_put_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
+ return snd_msndmix_set_mux(msnd, ucontrol->value.enumerated.item[0]);
+}
+
+
+static int snd_msndmix_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 100;
+ return 0;
+}
+
+static int snd_msndmix_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
+ int addr = kcontrol->private_value;
+ unsigned long flags;
+
+ spin_lock_irqsave(&msnd->mixer_lock, flags);
+ ucontrol->value.integer.value[0] = msnd->left_levels[addr] * 100;
+ ucontrol->value.integer.value[0] /= 0xFFFF;
+ ucontrol->value.integer.value[1] = msnd->right_levels[addr] * 100;
+ ucontrol->value.integer.value[1] /= 0xFFFF;
+ spin_unlock_irqrestore(&msnd->mixer_lock, flags);
+ return 0;
+}
+
+#define update_volm(a, b) \
+ do { \
+ writew((dev->left_levels[a] >> 1) * \
+ readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \
+ dev->SMA + SMA_##b##Left); \
+ writew((dev->right_levels[a] >> 1) * \
+ readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \
+ dev->SMA + SMA_##b##Right); \
+ } while (0);
+
+#define update_potm(d, s, ar) \
+ do { \
+ writeb((dev->left_levels[d] >> 8) * \
+ readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \
+ dev->SMA + SMA_##s##Left); \
+ writeb((dev->right_levels[d] >> 8) * \
+ readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \
+ dev->SMA + SMA_##s##Right); \
+ if (snd_msnd_send_word(dev, 0, 0, ar) == 0) \
+ snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); \
+ } while (0);
+
+#define update_pot(d, s, ar) \
+ do { \
+ writeb(dev->left_levels[d] >> 8, \
+ dev->SMA + SMA_##s##Left); \
+ writeb(dev->right_levels[d] >> 8, \
+ dev->SMA + SMA_##s##Right); \
+ if (snd_msnd_send_word(dev, 0, 0, ar) == 0) \
+ snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); \
+ } while (0);
+
+
+static int snd_msndmix_set(struct snd_msnd *dev, int d, int left, int right)
+{
+ int bLeft, bRight;
+ int wLeft, wRight;
+ int updatemaster = 0;
+
+ if (d >= LEVEL_ENTRIES)
+ return -EINVAL;
+
+ bLeft = left * 0xff / 100;
+ wLeft = left * 0xffff / 100;
+
+ bRight = right * 0xff / 100;
+ wRight = right * 0xffff / 100;
+
+ dev->left_levels[d] = wLeft;
+ dev->right_levels[d] = wRight;
+
+ switch (d) {
+ /* master volume unscaled controls */
+ case MSND_MIXER_LINE: /* line pot control */
+ /* scaled by IMIX in digital mix */
+ writeb(bLeft, dev->SMA + SMA_bInPotPosLeft);
+ writeb(bRight, dev->SMA + SMA_bInPotPosRight);
+ if (snd_msnd_send_word(dev, 0, 0, HDEXAR_IN_SET_POTS) == 0)
+ snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);
+ break;
+ case MSND_MIXER_MIC: /* mic pot control */
+ if (dev->type == msndClassic)
+ return -EINVAL;
+ /* scaled by IMIX in digital mix */
+ writeb(bLeft, dev->SMA + SMA_bMicPotPosLeft);
+ writeb(bRight, dev->SMA + SMA_bMicPotPosRight);
+ if (snd_msnd_send_word(dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0)
+ snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);
+ break;
+ case MSND_MIXER_VOLUME: /* master volume */
+ writew(wLeft, dev->SMA + SMA_wCurrMastVolLeft);
+ writew(wRight, dev->SMA + SMA_wCurrMastVolRight);
+ /* fall through */
+
+ case MSND_MIXER_AUX: /* aux pot control */
+ /* scaled by master volume */
+ /* fall through */
+
+ /* digital controls */
+ case MSND_MIXER_SYNTH: /* synth vol (dsp mix) */
+ case MSND_MIXER_PCM: /* pcm vol (dsp mix) */
+ case MSND_MIXER_IMIX: /* input monitor (dsp mix) */
+ /* scaled by master volume */
+ updatemaster = 1;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (updatemaster) {
+ /* update master volume scaled controls */
+ update_volm(MSND_MIXER_PCM, wCurrPlayVol);
+ update_volm(MSND_MIXER_IMIX, wCurrInVol);
+ if (dev->type == msndPinnacle)
+ update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol);
+ update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);
+ }
+
+ return 0;
+}
+
+static int snd_msndmix_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
+ int change, addr = kcontrol->private_value;
+ int left, right;
+ unsigned long flags;
+
+ left = ucontrol->value.integer.value[0] % 101;
+ right = ucontrol->value.integer.value[1] % 101;
+ spin_lock_irqsave(&msnd->mixer_lock, flags);
+ change = msnd->left_levels[addr] != left
+ || msnd->right_levels[addr] != right;
+ snd_msndmix_set(msnd, addr, left, right);
+ spin_unlock_irqrestore(&msnd->mixer_lock, flags);
+ return change;
+}
+
+
+#define DUMMY_VOLUME(xname, xindex, addr) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .info = snd_msndmix_volume_info, \
+ .get = snd_msndmix_volume_get, .put = snd_msndmix_volume_put, \
+ .private_value = addr }
+
+
+static struct snd_kcontrol_new snd_msnd_controls[] = {
+DUMMY_VOLUME("Master Volume", 0, MSND_MIXER_VOLUME),
+DUMMY_VOLUME("PCM Volume", 0, MSND_MIXER_PCM),
+DUMMY_VOLUME("Aux Volume", 0, MSND_MIXER_AUX),
+DUMMY_VOLUME("Line Volume", 0, MSND_MIXER_LINE),
+DUMMY_VOLUME("Mic Volume", 0, MSND_MIXER_MIC),
+DUMMY_VOLUME("Monitor", 0, MSND_MIXER_IMIX),
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = snd_msndmix_info_mux,
+ .get = snd_msndmix_get_mux,
+ .put = snd_msndmix_put_mux,
+}
+};
+
+
+int __devinit snd_msndmix_new(struct snd_card *card)
+{
+ struct snd_msnd *chip = card->private_data;
+ unsigned int idx;
+ int err;
+
+ if (snd_BUG_ON(!chip))
+ return -EINVAL;
+ spin_lock_init(&chip->mixer_lock);
+ strcpy(card->mixername, "MSND Pinnacle Mixer");
+
+ for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++)
+ err = snd_ctl_add(card,
+ snd_ctl_new1(snd_msnd_controls + idx, chip));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+EXPORT_SYMBOL(snd_msndmix_new);
+
+void snd_msndmix_setup(struct snd_msnd *dev)
+{
+ update_pot(MSND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS);
+ update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);
+ update_volm(MSND_MIXER_PCM, wCurrPlayVol);
+ update_volm(MSND_MIXER_IMIX, wCurrInVol);
+ if (dev->type == msndPinnacle) {
+ update_pot(MSND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS);
+ update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol);
+ }
+}
+EXPORT_SYMBOL(snd_msndmix_setup);
+
+int snd_msndmix_force_recsrc(struct snd_msnd *dev, int recsrc)
+{
+ dev->recsrc = -1;
+ return snd_msndmix_set_mux(dev, recsrc);
+}
+EXPORT_SYMBOL(snd_msndmix_force_recsrc);
diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c
index b848d10..ef95279d 100644
--- a/sound/isa/opl3sa2.c
+++ b/sound/isa/opl3sa2.c
@@ -179,12 +179,13 @@
unsigned char result;
#if 0
outb(0x1d, port); /* password */
- printk("read [0x%lx] = 0x%x\n", port, inb(port));
+ printk(KERN_DEBUG "read [0x%lx] = 0x%x\n", port, inb(port));
#endif
outb(reg, chip->port); /* register */
result = inb(chip->port + 1);
#if 0
- printk("read [0x%lx] = 0x%x [0x%x]\n", port, result, inb(port));
+ printk(KERN_DEBUG "read [0x%lx] = 0x%x [0x%x]\n",
+ port, result, inb(port));
#endif
return result;
}
@@ -233,7 +234,10 @@
snd_printk(KERN_ERR PFX "can't grab port 0x%lx\n", port);
return -EBUSY;
}
- // snd_printk("REG 0A = 0x%x\n", snd_opl3sa2_read(chip, 0x0a));
+ /*
+ snd_printk(KERN_DEBUG "REG 0A = 0x%x\n",
+ snd_opl3sa2_read(chip, 0x0a));
+ */
chip->version = 0;
tmp = snd_opl3sa2_read(chip, OPL3SA2_MISC);
if (tmp == 0xff) {
@@ -619,25 +623,28 @@
{
struct snd_opl3sa2 *chip = card->private_data;
if (chip->irq >= 0)
- free_irq(chip->irq, (void *)chip);
+ free_irq(chip->irq, card);
release_and_free_resource(chip->res_port);
}
-static struct snd_card *snd_opl3sa2_card_new(int dev)
+static int snd_opl3sa2_card_new(int dev, struct snd_card **cardp)
{
struct snd_card *card;
struct snd_opl3sa2 *chip;
+ int err;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(struct snd_opl3sa2));
- if (card == NULL)
- return NULL;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_opl3sa2), &card);
+ if (err < 0)
+ return err;
strcpy(card->driver, "OPL3SA2");
- strcpy(card->shortname, "Yamaha OPL3-SA2");
+ strcpy(card->shortname, "Yamaha OPL3-SA");
chip = card->private_data;
spin_lock_init(&chip->reg_lock);
chip->irq = -1;
card->private_free = snd_opl3sa2_free;
- return card;
+ *cardp = card;
+ return 0;
}
static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)
@@ -729,9 +736,9 @@
if (dev >= SNDRV_CARDS)
return -ENODEV;
- card = snd_opl3sa2_card_new(dev);
- if (! card)
- return -ENOMEM;
+ err = snd_opl3sa2_card_new(dev, &card);
+ if (err < 0)
+ return err;
if ((err = snd_opl3sa2_pnp(dev, card->private_data, pdev)) < 0) {
snd_card_free(card);
return err;
@@ -795,9 +802,9 @@
if (dev >= SNDRV_CARDS)
return -ENODEV;
- card = snd_opl3sa2_card_new(dev);
- if (! card)
- return -ENOMEM;
+ err = snd_opl3sa2_card_new(dev, &card);
+ if (err < 0)
+ return err;
if ((err = snd_opl3sa2_pnp(dev, card->private_data, pdev)) < 0) {
snd_card_free(card);
return err;
@@ -876,9 +883,9 @@
struct snd_card *card;
int err;
- card = snd_opl3sa2_card_new(dev);
- if (! card)
- return -ENOMEM;
+ err = snd_opl3sa2_card_new(dev, &card);
+ if (err < 0)
+ return err;
snd_card_set_dev(card, pdev);
if ((err = snd_opl3sa2_probe(card, dev)) < 0) {
snd_card_free(card);
diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c
index 440755c..02e30d7 100644
--- a/sound/isa/opti9xx/miro.c
+++ b/sound/isa/opti9xx/miro.c
@@ -1228,9 +1228,10 @@
struct snd_pcm *pcm;
struct snd_rawmidi *rmidi;
- if (!(card = snd_card_new(index, id, THIS_MODULE,
- sizeof(struct snd_miro))))
- return -ENOMEM;
+ error = snd_card_create(index, id, THIS_MODULE,
+ sizeof(struct snd_miro), &card);
+ if (error < 0)
+ return error;
card->private_free = snd_card_miro_free;
miro = card->private_data;
diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c
index 19706b0..5cd5553 100644
--- a/sound/isa/opti9xx/opti92x-ad1848.c
+++ b/sound/isa/opti9xx/opti92x-ad1848.c
@@ -252,7 +252,7 @@
#endif /* OPTi93X */
default:
- snd_printk("chip %d not supported\n", hardware);
+ snd_printk(KERN_ERR "chip %d not supported\n", hardware);
return -ENODEV;
}
return 0;
@@ -294,7 +294,7 @@
#endif /* OPTi93X */
default:
- snd_printk("chip %d not supported\n", chip->hardware);
+ snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
}
spin_unlock_irqrestore(&chip->lock, flags);
@@ -336,7 +336,7 @@
#endif /* OPTi93X */
default:
- snd_printk("chip %d not supported\n", chip->hardware);
+ snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
}
spin_unlock_irqrestore(&chip->lock, flags);
@@ -412,7 +412,7 @@
#endif /* OPTi93X */
default:
- snd_printk("chip %d not supported\n", chip->hardware);
+ snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
return -EINVAL;
}
@@ -430,7 +430,8 @@
wss_base_bits = 0x02;
break;
default:
- snd_printk("WSS port 0x%lx not valid\n", chip->wss_base);
+ snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n",
+ chip->wss_base);
goto __skip_base;
}
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
@@ -455,7 +456,7 @@
irq_bits = 0x04;
break;
default:
- snd_printk("WSS irq # %d not valid\n", chip->irq);
+ snd_printk(KERN_WARNING "WSS irq # %d not valid\n", chip->irq);
goto __skip_resources;
}
@@ -470,13 +471,14 @@
dma_bits = 0x03;
break;
default:
- snd_printk("WSS dma1 # %d not valid\n", chip->dma1);
+ snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n",
+ chip->dma1);
goto __skip_resources;
}
#if defined(CS4231) || defined(OPTi93X)
if (chip->dma1 == chip->dma2) {
- snd_printk("don't want to share dmas\n");
+ snd_printk(KERN_ERR "don't want to share dmas\n");
return -EBUSY;
}
@@ -485,7 +487,8 @@
case 1:
break;
default:
- snd_printk("WSS dma2 # %d not valid\n", chip->dma2);
+ snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n",
+ chip->dma2);
goto __skip_resources;
}
dma_bits |= 0x04;
@@ -516,7 +519,8 @@
mpu_port_bits = 0x00;
break;
default:
- snd_printk("MPU-401 port 0x%lx not valid\n",
+ snd_printk(KERN_WARNING
+ "MPU-401 port 0x%lx not valid\n",
chip->mpu_port);
goto __skip_mpu;
}
@@ -535,7 +539,7 @@
mpu_irq_bits = 0x01;
break;
default:
- snd_printk("MPU-401 irq # %d not valid\n",
+ snd_printk(KERN_WARNING "MPU-401 irq # %d not valid\n",
chip->mpu_irq);
goto __skip_mpu;
}
@@ -726,7 +730,7 @@
if (chip->wss_base == SNDRV_AUTO_PORT) {
chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4);
if (chip->wss_base < 0) {
- snd_printk("unable to find a free WSS port\n");
+ snd_printk(KERN_ERR "unable to find a free WSS port\n");
return -EBUSY;
}
}
@@ -815,14 +819,8 @@
chip->fm_port, chip->fm_port + 4 - 1);
}
if (opl3) {
-#ifdef CS4231
- const int t1dev = 1;
-#else
- const int t1dev = 0;
-#endif
- if ((error = snd_opl3_timer_new(opl3, t1dev, t1dev+1)) < 0)
- return error;
- if ((error = snd_opl3_hwdep_new(opl3, 0, 1, &synth)) < 0)
+ error = snd_opl3_hwdep_new(opl3, 0, 1, &synth);
+ if (error < 0)
return error;
}
}
@@ -830,15 +828,18 @@
return snd_card_register(card);
}
-static struct snd_card *snd_opti9xx_card_new(void)
+static int snd_opti9xx_card_new(struct snd_card **cardp)
{
struct snd_card *card;
+ int err;
- card = snd_card_new(index, id, THIS_MODULE, sizeof(struct snd_opti9xx));
- if (! card)
- return NULL;
+ err = snd_card_create(index, id, THIS_MODULE,
+ sizeof(struct snd_opti9xx), &card);
+ if (err < 0)
+ return err;
card->private_free = snd_card_opti9xx_free;
- return card;
+ *cardp = card;
+ return 0;
}
static int __devinit snd_opti9xx_isa_match(struct device *devptr,
@@ -897,15 +898,15 @@
#if defined(CS4231) || defined(OPTi93X)
if (dma2 == SNDRV_AUTO_DMA) {
if ((dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4])) < 0) {
- snd_printk("unable to find a free DMA2\n");
+ snd_printk(KERN_ERR "unable to find a free DMA2\n");
return -EBUSY;
}
}
#endif
- card = snd_opti9xx_card_new();
- if (! card)
- return -ENOMEM;
+ error = snd_opti9xx_card_new(&card);
+ if (error < 0)
+ return error;
if ((error = snd_card_opti9xx_detect(card, card->private_data)) < 0) {
snd_card_free(card);
@@ -950,9 +951,9 @@
return -EBUSY;
if (! isapnp)
return -ENODEV;
- card = snd_opti9xx_card_new();
- if (! card)
- return -ENOMEM;
+ error = snd_opti9xx_card_new(&card);
+ if (error < 0)
+ return error;
chip = card->private_data;
hw = snd_card_opti9xx_pnp(chip, pcard, pid);
diff --git a/sound/isa/sb/es968.c b/sound/isa/sb/es968.c
index c8c8e21..cafc3a7 100644
--- a/sound/isa/sb/es968.c
+++ b/sound/isa/sb/es968.c
@@ -108,9 +108,10 @@
struct snd_card *card;
struct snd_card_es968 *acard;
- if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_es968))) == NULL)
- return -ENOMEM;
+ error = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_es968), &card);
+ if (error < 0)
+ return error;
acard = card->private_data;
if ((error = snd_card_es968_pnp(dev, acard, pcard, pid))) {
snd_card_free(card);
diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c
index 2c201f7..519c363 100644
--- a/sound/isa/sb/sb16.c
+++ b/sound/isa/sb/sb16.c
@@ -324,14 +324,18 @@
#define is_isapnp_selected(dev) 0
#endif
-static struct snd_card *snd_sb16_card_new(int dev)
+static int snd_sb16_card_new(int dev, struct snd_card **cardp)
{
- struct snd_card *card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_sb16));
- if (card == NULL)
- return NULL;
+ struct snd_card *card;
+ int err;
+
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_sb16), &card);
+ if (err < 0)
+ return err;
card->private_free = snd_sb16_free;
- return card;
+ *cardp = card;
+ return 0;
}
static int __devinit snd_sb16_probe(struct snd_card *card, int dev)
@@ -489,9 +493,9 @@
struct snd_card *card;
int err;
- card = snd_sb16_card_new(dev);
- if (! card)
- return -ENOMEM;
+ err = snd_sb16_card_new(dev, &card);
+ if (err < 0)
+ return err;
acard = card->private_data;
/* non-PnP FM port address is hardwired with base port address */
@@ -610,9 +614,9 @@
for ( ; dev < SNDRV_CARDS; dev++) {
if (!enable[dev] || !isapnp[dev])
continue;
- card = snd_sb16_card_new(dev);
- if (! card)
- return -ENOMEM;
+ res = snd_sb16_card_new(dev, &card);
+ if (res < 0)
+ return res;
snd_card_set_dev(card, &pcard->card->dev);
if ((res = snd_card_sb16_pnp(dev, card->private_data, pcard, pid)) < 0 ||
(res = snd_sb16_probe(card, dev)) < 0) {
diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c
index ea06877..3cd57ee 100644
--- a/sound/isa/sb/sb8.c
+++ b/sound/isa/sb/sb8.c
@@ -103,10 +103,10 @@
struct snd_opl3 *opl3;
int err;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_sb8));
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_sb8), &card);
+ if (err < 0)
+ return err;
acard = card->private_data;
card->private_free = snd_sb8_free;
diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c
index 406a431..475220b 100644
--- a/sound/isa/sb/sb_mixer.c
+++ b/sound/isa/sb/sb_mixer.c
@@ -182,7 +182,7 @@
static int snd_dt019x_input_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[5] = {
+ static const char *texts[5] = {
"CD", "Mic", "Line", "Synth", "Master"
};
@@ -269,12 +269,73 @@
}
/*
+ * ALS4000 mono recording control switch
+ */
+
+static int snd_als4k_mono_capture_route_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char *texts[3] = {
+ "L chan only", "R chan only", "L ch/2 + R ch/2"
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item > 2)
+ uinfo->value.enumerated.item = 2;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_als4k_mono_capture_route_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ unsigned char oval;
+
+ spin_lock_irqsave(&sb->mixer_lock, flags);
+ oval = snd_sbmixer_read(sb, SB_ALS4000_MONO_IO_CTRL);
+ spin_unlock_irqrestore(&sb->mixer_lock, flags);
+ oval >>= 6;
+ if (oval > 2)
+ oval = 2;
+
+ ucontrol->value.enumerated.item[0] = oval;
+ return 0;
+}
+
+static int snd_als4k_mono_capture_route_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int change;
+ unsigned char nval, oval;
+
+ if (ucontrol->value.enumerated.item[0] > 2)
+ return -EINVAL;
+ spin_lock_irqsave(&sb->mixer_lock, flags);
+ oval = snd_sbmixer_read(sb, SB_ALS4000_MONO_IO_CTRL);
+
+ nval = (oval & ~(3 << 6))
+ | (ucontrol->value.enumerated.item[0] << 6);
+ change = nval != oval;
+ if (change)
+ snd_sbmixer_write(sb, SB_ALS4000_MONO_IO_CTRL, nval);
+ spin_unlock_irqrestore(&sb->mixer_lock, flags);
+ return change;
+}
+
+/*
* SBPRO input multiplexer
*/
static int snd_sb8mixer_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {
+ static const char *texts[3] = {
"Mic", "CD", "Line"
};
@@ -442,6 +503,12 @@
.get = snd_dt019x_input_sw_get,
.put = snd_dt019x_input_sw_put,
},
+ [SB_MIX_MONO_CAPTURE_ALS4K] = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_als4k_mono_capture_route_info,
+ .get = snd_als4k_mono_capture_route_get,
+ .put = snd_als4k_mono_capture_route_put,
+ },
};
struct snd_kcontrol *ctl;
int err;
@@ -636,6 +703,8 @@
};
static struct sbmix_elem *snd_dt019x_controls[] = {
+ /* ALS4000 below has some parts which we might be lacking,
+ * e.g. snd_als4000_ctl_mono_playback_switch - check it! */
&snd_dt019x_ctl_master_play_vol,
&snd_dt019x_ctl_pcm_play_vol,
&snd_dt019x_ctl_synth_play_vol,
@@ -666,18 +735,21 @@
/*
* ALS4000 specific mixer elements
*/
-/* FIXME: SB_ALS4000_MONO_IO_CTRL needs output select ctrl! */
static struct sbmix_elem snd_als4000_ctl_master_mono_playback_switch =
SB_SINGLE("Master Mono Playback Switch", SB_ALS4000_MONO_IO_CTRL, 5, 1);
-static struct sbmix_elem snd_als4000_ctl_master_mono_capture_route =
- SB_SINGLE("Master Mono Capture Route", SB_ALS4000_MONO_IO_CTRL, 6, 0x03);
-/* FIXME: mono playback switch also available on DT019X? */
+static struct sbmix_elem snd_als4k_ctl_master_mono_capture_route = {
+ .name = "Master Mono Capture Route",
+ .type = SB_MIX_MONO_CAPTURE_ALS4K
+ };
static struct sbmix_elem snd_als4000_ctl_mono_playback_switch =
SB_SINGLE("Mono Playback Switch", SB_DT019X_OUTPUT_SW2, 0, 1);
static struct sbmix_elem snd_als4000_ctl_mic_20db_boost =
SB_SINGLE("Mic Boost (+20dB)", SB_ALS4000_MIC_IN_GAIN, 0, 0x03);
-static struct sbmix_elem snd_als4000_ctl_mixer_loopback =
- SB_SINGLE("Analog Loopback", SB_ALS4000_MIC_IN_GAIN, 7, 0x01);
+static struct sbmix_elem snd_als4000_ctl_mixer_analog_loopback =
+ SB_SINGLE("Analog Loopback Switch", SB_ALS4000_MIC_IN_GAIN, 7, 0x01);
+static struct sbmix_elem snd_als4000_ctl_mixer_digital_loopback =
+ SB_SINGLE("Digital Loopback Switch",
+ SB_ALS4000_CR3_CONFIGURATION, 7, 0x01);
/* FIXME: functionality of 3D controls might be swapped, I didn't find
* a description of how to identify what is supposed to be what */
static struct sbmix_elem snd_als4000_3d_control_switch =
@@ -694,6 +766,9 @@
SB_SINGLE("3D Control - Wide", SB_ALS4000_3D_TIME_DELAY, 0, 0x0f);
static struct sbmix_elem snd_als4000_3d_control_poweroff_switch =
SB_SINGLE("3D PowerOff Switch", SB_ALS4000_3D_TIME_DELAY, 4, 0x01);
+static struct sbmix_elem snd_als4000_ctl_3db_freq_control_switch =
+ SB_SINGLE("Master Playback 8kHz / 20kHz LPF Switch",
+ SB_ALS4000_FMDAC, 5, 0x01);
#ifdef NOT_AVAILABLE
static struct sbmix_elem snd_als4000_ctl_fmdac =
SB_SINGLE("FMDAC Switch (Option ?)", SB_ALS4000_FMDAC, 0, 0x01);
@@ -702,35 +777,37 @@
#endif
static struct sbmix_elem *snd_als4000_controls[] = {
- &snd_sb16_ctl_master_play_vol,
- &snd_dt019x_ctl_pcm_play_switch,
- &snd_sb16_ctl_pcm_play_vol,
- &snd_sb16_ctl_synth_capture_route,
- &snd_dt019x_ctl_synth_play_switch,
- &snd_sb16_ctl_synth_play_vol,
- &snd_sb16_ctl_cd_capture_route,
- &snd_sb16_ctl_cd_play_switch,
- &snd_sb16_ctl_cd_play_vol,
- &snd_sb16_ctl_line_capture_route,
- &snd_sb16_ctl_line_play_switch,
- &snd_sb16_ctl_line_play_vol,
- &snd_sb16_ctl_mic_capture_route,
- &snd_als4000_ctl_mic_20db_boost,
- &snd_sb16_ctl_auto_mic_gain,
- &snd_sb16_ctl_mic_play_switch,
- &snd_sb16_ctl_mic_play_vol,
- &snd_sb16_ctl_pc_speaker_vol,
- &snd_sb16_ctl_capture_vol,
- &snd_sb16_ctl_play_vol,
- &snd_als4000_ctl_master_mono_playback_switch,
- &snd_als4000_ctl_master_mono_capture_route,
- &snd_als4000_ctl_mono_playback_switch,
- &snd_als4000_ctl_mixer_loopback,
- &snd_als4000_3d_control_switch,
- &snd_als4000_3d_control_ratio,
- &snd_als4000_3d_control_freq,
- &snd_als4000_3d_control_delay,
- &snd_als4000_3d_control_poweroff_switch,
+ /* ALS4000a.PDF regs page */
+ &snd_sb16_ctl_master_play_vol, /* MX30/31 12 */
+ &snd_dt019x_ctl_pcm_play_switch, /* MX4C 16 */
+ &snd_sb16_ctl_pcm_play_vol, /* MX32/33 12 */
+ &snd_sb16_ctl_synth_capture_route, /* MX3D/3E 14 */
+ &snd_dt019x_ctl_synth_play_switch, /* MX4C 16 */
+ &snd_sb16_ctl_synth_play_vol, /* MX34/35 12/13 */
+ &snd_sb16_ctl_cd_capture_route, /* MX3D/3E 14 */
+ &snd_sb16_ctl_cd_play_switch, /* MX3C 14 */
+ &snd_sb16_ctl_cd_play_vol, /* MX36/37 13 */
+ &snd_sb16_ctl_line_capture_route, /* MX3D/3E 14 */
+ &snd_sb16_ctl_line_play_switch, /* MX3C 14 */
+ &snd_sb16_ctl_line_play_vol, /* MX38/39 13 */
+ &snd_sb16_ctl_mic_capture_route, /* MX3D/3E 14 */
+ &snd_als4000_ctl_mic_20db_boost, /* MX4D 16 */
+ &snd_sb16_ctl_mic_play_switch, /* MX3C 14 */
+ &snd_sb16_ctl_mic_play_vol, /* MX3A 13 */
+ &snd_sb16_ctl_pc_speaker_vol, /* MX3B 14 */
+ &snd_sb16_ctl_capture_vol, /* MX3F/40 15 */
+ &snd_sb16_ctl_play_vol, /* MX41/42 15 */
+ &snd_als4000_ctl_master_mono_playback_switch, /* MX4C 16 */
+ &snd_als4k_ctl_master_mono_capture_route, /* MX4B 16 */
+ &snd_als4000_ctl_mono_playback_switch, /* MX4C 16 */
+ &snd_als4000_ctl_mixer_analog_loopback, /* MX4D 16 */
+ &snd_als4000_ctl_mixer_digital_loopback, /* CR3 21 */
+ &snd_als4000_3d_control_switch, /* MX50 17 */
+ &snd_als4000_3d_control_ratio, /* MX50 17 */
+ &snd_als4000_3d_control_freq, /* MX50 17 */
+ &snd_als4000_3d_control_delay, /* MX51 18 */
+ &snd_als4000_3d_control_poweroff_switch, /* MX51 18 */
+ &snd_als4000_ctl_3db_freq_control_switch, /* MX4F 17 */
#ifdef NOT_AVAILABLE
&snd_als4000_ctl_fmdac,
&snd_als4000_ctl_qsound,
@@ -905,13 +982,14 @@
};
static unsigned char als4000_saved_regs[] = {
+ /* please verify in dsheet whether regs to be added
+ are actually real H/W or just dummy */
SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
SB_DSP4_OUTPUT_SW,
SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,
- SB_DSP4_MIC_AGC,
SB_DSP4_MIC_DEV,
SB_DSP4_SPEAKER_DEV,
SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1,
@@ -919,8 +997,10 @@
SB_DT019X_OUTPUT_SW2,
SB_ALS4000_MONO_IO_CTRL,
SB_ALS4000_MIC_IN_GAIN,
+ SB_ALS4000_FMDAC,
SB_ALS4000_3D_SND_FX,
SB_ALS4000_3D_TIME_DELAY,
+ SB_ALS4000_CR3_CONFIGURATION,
};
static void save_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c
index ca35924..7820106 100644
--- a/sound/isa/sc6000.c
+++ b/sound/isa/sc6000.c
@@ -489,9 +489,9 @@
char __iomem *vmss_port;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (!card)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
if (xirq == SNDRV_AUTO_IRQ) {
xirq = snd_legacy_find_free_irq(possible_irqs);
@@ -576,10 +576,6 @@
snd_printk(KERN_ERR PFX "no OPL device at 0x%x-0x%x ?\n",
0x388, 0x388 + 2);
} else {
- err = snd_opl3_timer_new(opl3, 0, 1);
- if (err < 0)
- goto err_unmap2;
-
err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
if (err < 0)
goto err_unmap2;
diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c
index 2c7503b..6fe27b9 100644
--- a/sound/isa/sgalaxy.c
+++ b/sound/isa/sgalaxy.c
@@ -243,9 +243,9 @@
struct snd_card *card;
struct snd_wss *chip;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
xirq = irq[dev];
if (xirq == SNDRV_AUTO_IRQ) {
diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c
index 48a16d8..6618712 100644
--- a/sound/isa/sscape.c
+++ b/sound/isa/sscape.c
@@ -89,9 +89,6 @@
#endif
-#define MPU401_IO(i) ((i) + 0)
-#define MIDI_DATA_IO(i) ((i) + 0)
-#define MIDI_CTRL_IO(i) ((i) + 1)
#define HOST_CTRL_IO(i) ((i) + 2)
#define HOST_DATA_IO(i) ((i) + 3)
#define ODIE_ADDR_IO(i) ((i) + 4)
@@ -129,9 +126,6 @@
#define DMA_8BIT 0x80
-#define AD1845_FREQ_SEL_MSB 0x16
-#define AD1845_FREQ_SEL_LSB 0x17
-
enum card_type {
SSCAPE,
SSCAPE_PNP,
@@ -141,8 +135,6 @@
struct soundscape {
spinlock_t lock;
unsigned io_base;
- unsigned wss_base;
- int codec_type;
int ic_type;
enum card_type type;
struct resource *io_res;
@@ -330,7 +322,7 @@
*/
static inline int verify_mpu401(const struct snd_mpu401 * mpu)
{
- return ((inb(MIDI_CTRL_IO(mpu->port)) & 0xc0) == 0x80);
+ return ((inb(MPU401C(mpu)) & 0xc0) == 0x80);
}
/*
@@ -338,7 +330,7 @@
*/
static inline void initialise_mpu401(const struct snd_mpu401 * mpu)
{
- outb(0, MIDI_DATA_IO(mpu->port));
+ outb(0, MPU401D(mpu));
}
/*
@@ -396,20 +388,20 @@
*/
static int obp_startup_ack(struct soundscape *s, unsigned timeout)
{
- while (timeout != 0) {
+ unsigned long end_time = jiffies + msecs_to_jiffies(timeout);
+
+ do {
unsigned long flags;
unsigned char x;
- schedule_timeout_uninterruptible(1);
-
spin_lock_irqsave(&s->lock, flags);
x = inb(HOST_DATA_IO(s->io_base));
spin_unlock_irqrestore(&s->lock, flags);
if ((x & 0xfe) == 0xfe)
return 1;
- --timeout;
- } /* while */
+ msleep(10);
+ } while (time_before(jiffies, end_time));
return 0;
}
@@ -423,20 +415,20 @@
*/
static int host_startup_ack(struct soundscape *s, unsigned timeout)
{
- while (timeout != 0) {
+ unsigned long end_time = jiffies + msecs_to_jiffies(timeout);
+
+ do {
unsigned long flags;
unsigned char x;
- schedule_timeout_uninterruptible(1);
-
spin_lock_irqsave(&s->lock, flags);
x = inb(HOST_DATA_IO(s->io_base));
spin_unlock_irqrestore(&s->lock, flags);
if (x == 0xfe)
return 1;
- --timeout;
- } /* while */
+ msleep(10);
+ } while (time_before(jiffies, end_time));
return 0;
}
@@ -532,10 +524,10 @@
* give it 5 seconds (max) ...
*/
ret = 0;
- if (!obp_startup_ack(s, 5)) {
+ if (!obp_startup_ack(s, 5000)) {
snd_printk(KERN_ERR "sscape: No response from on-board processor after upload\n");
ret = -EAGAIN;
- } else if (!host_startup_ack(s, 5)) {
+ } else if (!host_startup_ack(s, 5000)) {
snd_printk(KERN_ERR "sscape: SoundScape failed to initialise\n");
ret = -EAGAIN;
}
@@ -732,13 +724,7 @@
unsigned long flags;
spin_lock_irqsave(&s->lock, flags);
- set_host_mode_unsafe(s->io_base);
-
- if (host_write_ctrl_unsafe(s->io_base, CMD_GET_MIDI_VOL, 100)) {
- uctl->value.integer.value[0] = host_read_ctrl_unsafe(s->io_base, 100);
- }
-
- set_midi_mode_unsafe(s->io_base);
+ uctl->value.integer.value[0] = s->midi_vol;
spin_unlock_irqrestore(&s->lock, flags);
return 0;
}
@@ -773,6 +759,7 @@
change = (host_write_ctrl_unsafe(s->io_base, CMD_SET_MIDI_VOL, 100)
&& host_write_ctrl_unsafe(s->io_base, ((unsigned char) uctl->value.integer. value[0]) & 127, 100)
&& host_write_ctrl_unsafe(s->io_base, CMD_XXX_MIDI_VOL, 100));
+ s->midi_vol = (unsigned char) uctl->value.integer.value[0] & 127;
__skip_change:
/*
@@ -815,12 +802,11 @@
* Perform certain arcane port-checks to see whether there
* is a SoundScape board lurking behind the given ports.
*/
-static int __devinit detect_sscape(struct soundscape *s)
+static int __devinit detect_sscape(struct soundscape *s, long wss_io)
{
unsigned long flags;
unsigned d;
int retval = 0;
- int codec = s->wss_base;
spin_lock_irqsave(&s->lock, flags);
@@ -836,13 +822,11 @@
if ((d & 0x80) != 0)
goto _done;
- if (d == 0) {
- s->codec_type = 1;
+ if (d == 0)
s->ic_type = IC_ODIE;
- } else if ((d & 0x60) != 0) {
- s->codec_type = 2;
+ else if ((d & 0x60) != 0)
s->ic_type = IC_OPUS;
- } else
+ else
goto _done;
outb(0xfa, ODIE_ADDR_IO(s->io_base));
@@ -862,10 +846,10 @@
sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d | 0xc0);
if (s->type == SSCAPE_VIVO)
- codec += 4;
+ wss_io += 4;
/* wait for WSS codec */
for (d = 0; d < 500; d++) {
- if ((inb(codec) & 0x80) == 0)
+ if ((inb(wss_io) & 0x80) == 0)
break;
spin_unlock_irqrestore(&s->lock, flags);
msleep(1);
@@ -955,82 +939,6 @@
/*
- * Override for the CS4231 playback format function.
- * The AD1845 has much simpler format and rate selection.
- */
-static void ad1845_playback_format(struct snd_wss *chip,
- struct snd_pcm_hw_params *params,
- unsigned char format)
-{
- unsigned long flags;
- unsigned rate = params_rate(params);
-
- /*
- * The AD1845 can't handle sample frequencies
- * outside of 4 kHZ to 50 kHZ
- */
- if (rate > 50000)
- rate = 50000;
- else if (rate < 4000)
- rate = 4000;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
-
- /*
- * Program the AD1845 correctly for the playback stream.
- * Note that we do NOT need to toggle the MCE bit because
- * the PLAYBACK_ENABLE bit of the Interface Configuration
- * register is set.
- *
- * NOTE: We seem to need to write to the MSB before the LSB
- * to get the correct sample frequency.
- */
- snd_wss_out(chip, CS4231_PLAYBK_FORMAT, (format & 0xf0));
- snd_wss_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8));
- snd_wss_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate);
-
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-}
-
-/*
- * Override for the CS4231 capture format function.
- * The AD1845 has much simpler format and rate selection.
- */
-static void ad1845_capture_format(struct snd_wss *chip,
- struct snd_pcm_hw_params *params,
- unsigned char format)
-{
- unsigned long flags;
- unsigned rate = params_rate(params);
-
- /*
- * The AD1845 can't handle sample frequencies
- * outside of 4 kHZ to 50 kHZ
- */
- if (rate > 50000)
- rate = 50000;
- else if (rate < 4000)
- rate = 4000;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
-
- /*
- * Program the AD1845 correctly for the playback stream.
- * Note that we do NOT need to toggle the MCE bit because
- * the CAPTURE_ENABLE bit of the Interface Configuration
- * register is set.
- *
- * NOTE: We seem to need to write to the MSB before the LSB
- * to get the correct sample frequency.
- */
- snd_wss_out(chip, CS4231_REC_FORMAT, (format & 0xf0));
- snd_wss_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8));
- snd_wss_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate);
-
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-}
-
-/*
* Create an AD1845 PCM subdevice on the SoundScape. The AD1845
* is very much like a CS4231, with a few extra bits. We will
* try to support at least some of the extra bits by overriding
@@ -1055,11 +963,6 @@
unsigned long flags;
struct snd_pcm *pcm;
-#define AD1845_FREQ_SEL_ENABLE 0x08
-
-#define AD1845_PWR_DOWN_CTRL 0x1b
-#define AD1845_CRYS_CLOCK_SEL 0x1d
-
/*
* It turns out that the PLAYBACK_ENABLE bit is set
* by the lowlevel driver ...
@@ -1074,7 +977,6 @@
*/
if (sscape->type != SSCAPE_VIVO) {
- int val;
/*
* The input clock frequency on the SoundScape must
* be 14.31818 MHz, because we must set this register
@@ -1082,22 +984,10 @@
*/
snd_wss_mce_up(chip);
spin_lock_irqsave(&chip->reg_lock, flags);
- snd_wss_out(chip, AD1845_CRYS_CLOCK_SEL, 0x20);
+ snd_wss_out(chip, AD1845_CLOCK, 0x20);
spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_wss_mce_down(chip);
- /*
- * More custom configuration:
- * a) select "mode 2" and provide a current drive of 8mA
- * b) enable frequency selection (for capture/playback)
- */
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_wss_out(chip, CS4231_MISC_INFO,
- CS4231_MODE2 | 0x10);
- val = snd_wss_in(chip, AD1845_PWR_DOWN_CTRL);
- snd_wss_out(chip, AD1845_PWR_DOWN_CTRL,
- val | AD1845_FREQ_SEL_ENABLE);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
err = snd_wss_pcm(chip, 0, &pcm);
@@ -1113,11 +1003,13 @@
"for AD1845 chip\n");
goto _error;
}
- err = snd_wss_timer(chip, 0, NULL);
- if (err < 0) {
- snd_printk(KERN_ERR "sscape: No timer device "
- "for AD1845 chip\n");
- goto _error;
+ if (chip->hardware != WSS_HW_AD1848) {
+ err = snd_wss_timer(chip, 0, NULL);
+ if (err < 0) {
+ snd_printk(KERN_ERR "sscape: No timer device "
+ "for AD1845 chip\n");
+ goto _error;
+ }
}
if (sscape->type != SSCAPE_VIVO) {
@@ -1128,8 +1020,6 @@
"MIDI mixer control\n");
goto _error;
}
- chip->set_playback_format = ad1845_playback_format;
- chip->set_capture_format = ad1845_capture_format;
}
strcpy(card->driver, "SoundScape");
@@ -1157,7 +1047,6 @@
unsigned dma_cfg;
unsigned irq_cfg;
unsigned mpu_irq_cfg;
- unsigned xport;
struct resource *io_res;
struct resource *wss_res;
unsigned long flags;
@@ -1177,15 +1066,15 @@
printk(KERN_ERR "sscape: Invalid IRQ %d\n", mpu_irq[dev]);
return -ENXIO;
}
- xport = port[dev];
/*
* Grab IO ports that we will need to probe so that we
* can detect and control this hardware ...
*/
- io_res = request_region(xport, 8, "SoundScape");
+ io_res = request_region(port[dev], 8, "SoundScape");
if (!io_res) {
- snd_printk(KERN_ERR "sscape: can't grab port 0x%x\n", xport);
+ snd_printk(KERN_ERR
+ "sscape: can't grab port 0x%lx\n", port[dev]);
return -EBUSY;
}
wss_res = NULL;
@@ -1212,10 +1101,9 @@
spin_lock_init(&sscape->fwlock);
sscape->io_res = io_res;
sscape->wss_res = wss_res;
- sscape->io_base = xport;
- sscape->wss_base = wss_port[dev];
+ sscape->io_base = port[dev];
- if (!detect_sscape(sscape)) {
+ if (!detect_sscape(sscape, wss_port[dev])) {
printk(KERN_ERR "sscape: hardware not detected at 0x%x\n", sscape->io_base);
err = -ENODEV;
goto _release_dma;
@@ -1288,12 +1176,11 @@
}
#define MIDI_DEVNUM 0
if (sscape->type != SSCAPE_VIVO) {
- err = create_mpu401(card, MIDI_DEVNUM,
- MPU401_IO(xport), mpu_irq[dev]);
+ err = create_mpu401(card, MIDI_DEVNUM, port[dev], mpu_irq[dev]);
if (err < 0) {
printk(KERN_ERR "sscape: Failed to create "
- "MPU-401 device at 0x%x\n",
- MPU401_IO(xport));
+ "MPU-401 device at 0x%lx\n",
+ port[dev]);
goto _release_dma;
}
@@ -1357,10 +1244,10 @@
struct soundscape *sscape;
int ret;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct soundscape));
- if (!card)
- return -ENOMEM;
+ ret = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct soundscape), &card);
+ if (ret < 0)
+ return ret;
sscape = get_card_soundscape(card);
sscape->type = SSCAPE;
@@ -1462,10 +1349,10 @@
* Create a new ALSA sound card entry, in anticipation
* of detecting our hardware ...
*/
- card = snd_card_new(index[idx], id[idx], THIS_MODULE,
- sizeof(struct soundscape));
- if (!card)
- return -ENOMEM;
+ ret = snd_card_create(index[idx], id[idx], THIS_MODULE,
+ sizeof(struct soundscape), &card);
+ if (ret < 0)
+ return ret;
sscape = get_card_soundscape(card);
diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c
index 4c095bc..a34ae7b 100644
--- a/sound/isa/wavefront/wavefront.c
+++ b/sound/isa/wavefront/wavefront.c
@@ -338,15 +338,16 @@
}
}
-static struct snd_card *snd_wavefront_card_new(int dev)
+static int snd_wavefront_card_new(int dev, struct snd_card **cardp)
{
struct snd_card *card;
snd_wavefront_card_t *acard;
+ int err;
- card = snd_card_new (index[dev], id[dev], THIS_MODULE,
- sizeof(snd_wavefront_card_t));
- if (card == NULL)
- return NULL;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(snd_wavefront_card_t), &card);
+ if (err < 0)
+ return err;
acard = card->private_data;
acard->wavefront.irq = -1;
@@ -357,7 +358,8 @@
acard->wavefront.card = card;
card->private_free = snd_wavefront_free;
- return card;
+ *cardp = card;
+ return 0;
}
static int __devinit
@@ -551,11 +553,11 @@
return 0;
#endif
if (cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) {
- snd_printk("specify CS4232 port\n");
+ snd_printk(KERN_ERR "specify CS4232 port\n");
return 0;
}
if (ics2115_port[dev] == SNDRV_AUTO_PORT) {
- snd_printk("specify ICS2115 port\n");
+ snd_printk(KERN_ERR "specify ICS2115 port\n");
return 0;
}
return 1;
@@ -567,9 +569,9 @@
struct snd_card *card;
int err;
- card = snd_wavefront_card_new(dev);
- if (! card)
- return -ENOMEM;
+ err = snd_wavefront_card_new(dev, &card);
+ if (err < 0)
+ return err;
snd_card_set_dev(card, pdev);
if ((err = snd_wavefront_probe(card, dev)) < 0) {
snd_card_free(card);
@@ -616,9 +618,9 @@
if (dev >= SNDRV_CARDS)
return -ENODEV;
- card = snd_wavefront_card_new(dev);
- if (! card)
- return -ENOMEM;
+ res = snd_wavefront_card_new(dev, &card);
+ if (res < 0)
+ return res;
if (snd_wavefront_pnp (dev, card->private_data, pcard, pid) < 0) {
if (cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) {
diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c
index 4c41082..beb312c 100644
--- a/sound/isa/wavefront/wavefront_synth.c
+++ b/sound/isa/wavefront/wavefront_synth.c
@@ -633,7 +633,7 @@
wbuf[1] = i >> 7;
if (snd_wavefront_cmd (dev, WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) {
- snd_printk("cannot identify sample "
+ snd_printk(KERN_WARNING "cannot identify sample "
"type of slot %d\n", i);
dev->sample_status[i] = WF_ST_EMPTY;
continue;
diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c
index 3d6c5f2..5d2ba1b 100644
--- a/sound/isa/wss/wss_lib.c
+++ b/sound/isa/wss/wss_lib.c
@@ -181,25 +181,6 @@
udelay(100);
}
-static void snd_wss_outm(struct snd_wss *chip, unsigned char reg,
- unsigned char mask, unsigned char value)
-{
- unsigned char tmp = (chip->image[reg] & mask) | value;
-
- snd_wss_wait(chip);
-#ifdef CONFIG_SND_DEBUG
- if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk("outm: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
-#endif
- chip->image[reg] = tmp;
- if (!chip->calibrate_mute) {
- wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
- wmb();
- wss_outb(chip, CS4231P(REG), tmp);
- mb();
- }
-}
-
static void snd_wss_dout(struct snd_wss *chip, unsigned char reg,
unsigned char value)
{
@@ -219,7 +200,8 @@
snd_wss_wait(chip);
#ifdef CONFIG_SND_DEBUG
if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
+ snd_printk(KERN_DEBUG "out: auto calibration time out "
+ "- reg = 0x%x, value = 0x%x\n", reg, value);
#endif
wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
wss_outb(chip, CS4231P(REG), value);
@@ -235,7 +217,8 @@
snd_wss_wait(chip);
#ifdef CONFIG_SND_DEBUG
if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk("in: auto calibration time out - reg = 0x%x\n", reg);
+ snd_printk(KERN_DEBUG "in: auto calibration time out "
+ "- reg = 0x%x\n", reg);
#endif
wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
mb();
@@ -252,7 +235,7 @@
wss_outb(chip, CS4231P(REG), val);
chip->eimage[CS4236_REG(reg)] = val;
#if 0
- printk("ext out : reg = 0x%x, val = 0x%x\n", reg, val);
+ printk(KERN_DEBUG "ext out : reg = 0x%x, val = 0x%x\n", reg, val);
#endif
}
EXPORT_SYMBOL(snd_cs4236_ext_out);
@@ -268,7 +251,8 @@
{
unsigned char res;
res = wss_inb(chip, CS4231P(REG));
- printk("ext in : reg = 0x%x, val = 0x%x\n", reg, res);
+ printk(KERN_DEBUG "ext in : reg = 0x%x, val = 0x%x\n",
+ reg, res);
return res;
}
#endif
@@ -394,13 +378,16 @@
snd_wss_wait(chip);
#ifdef CONFIG_SND_DEBUG
if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk("mce_up - auto calibration time out (0)\n");
+ snd_printk(KERN_DEBUG
+ "mce_up - auto calibration time out (0)\n");
#endif
spin_lock_irqsave(&chip->reg_lock, flags);
chip->mce_bit |= CS4231_MCE;
timeout = wss_inb(chip, CS4231P(REGSEL));
if (timeout == 0x80)
- snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port);
+ snd_printk(KERN_DEBUG "mce_up [0x%lx]: "
+ "serious init problem - codec still busy\n",
+ chip->port);
if (!(timeout & CS4231_MCE))
wss_outb(chip, CS4231P(REGSEL),
chip->mce_bit | (timeout & 0x1f));
@@ -419,7 +406,9 @@
#ifdef CONFIG_SND_DEBUG
if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", (long)CS4231P(REGSEL));
+ snd_printk(KERN_DEBUG "mce_down [0x%lx] - "
+ "auto calibration time out (0)\n",
+ (long)CS4231P(REGSEL));
#endif
spin_lock_irqsave(&chip->reg_lock, flags);
chip->mce_bit &= ~CS4231_MCE;
@@ -427,7 +416,9 @@
wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
spin_unlock_irqrestore(&chip->reg_lock, flags);
if (timeout == 0x80)
- snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port);
+ snd_printk(KERN_DEBUG "mce_down [0x%lx]: "
+ "serious init problem - codec still busy\n",
+ chip->port);
if ((timeout & CS4231_MCE) == 0 || !(chip->hardware & hw_mask))
return;
@@ -565,7 +556,7 @@
if (channels > 1)
rformat |= CS4231_STEREO;
#if 0
- snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode);
+ snd_printk(KERN_DEBUG "get_format: 0x%x (mode=0x%x)\n", format, mode);
#endif
return rformat;
}
@@ -587,7 +578,15 @@
chip->image[CS4231_RIGHT_INPUT]);
snd_wss_dout(chip, CS4231_LOOPBACK,
chip->image[CS4231_LOOPBACK]);
+ } else {
+ snd_wss_dout(chip, CS4231_LEFT_INPUT,
+ 0);
+ snd_wss_dout(chip, CS4231_RIGHT_INPUT,
+ 0);
+ snd_wss_dout(chip, CS4231_LOOPBACK,
+ 0xfd);
}
+
snd_wss_dout(chip, CS4231_AUX1_LEFT_INPUT,
mute | chip->image[CS4231_AUX1_LEFT_INPUT]);
snd_wss_dout(chip, CS4231_AUX1_RIGHT_INPUT,
@@ -630,7 +629,6 @@
int full_calib = 1;
mutex_lock(&chip->mce_mutex);
- snd_wss_calibrate_mute(chip, 1);
if (chip->hardware == WSS_HW_CS4231A ||
(chip->hardware & WSS_HW_CS4232_MASK)) {
spin_lock_irqsave(&chip->reg_lock, flags);
@@ -646,6 +644,24 @@
full_calib = 0;
}
spin_unlock_irqrestore(&chip->reg_lock, flags);
+ } else if (chip->hardware == WSS_HW_AD1845) {
+ unsigned rate = params_rate(params);
+
+ /*
+ * Program the AD1845 correctly for the playback stream.
+ * Note that we do NOT need to toggle the MCE bit because
+ * the PLAYBACK_ENABLE bit of the Interface Configuration
+ * register is set.
+ *
+ * NOTE: We seem to need to write to the MSB before the LSB
+ * to get the correct sample frequency.
+ */
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_wss_out(chip, CS4231_PLAYBK_FORMAT, (pdfr & 0xf0));
+ snd_wss_out(chip, AD1845_UPR_FREQ_SEL, (rate >> 8) & 0xff);
+ snd_wss_out(chip, AD1845_LWR_FREQ_SEL, rate & 0xff);
+ full_calib = 0;
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
}
if (full_calib) {
snd_wss_mce_up(chip);
@@ -663,7 +679,6 @@
udelay(100); /* this seems to help */
snd_wss_mce_down(chip);
}
- snd_wss_calibrate_mute(chip, 0);
mutex_unlock(&chip->mce_mutex);
}
@@ -675,7 +690,6 @@
int full_calib = 1;
mutex_lock(&chip->mce_mutex);
- snd_wss_calibrate_mute(chip, 1);
if (chip->hardware == WSS_HW_CS4231A ||
(chip->hardware & WSS_HW_CS4232_MASK)) {
spin_lock_irqsave(&chip->reg_lock, flags);
@@ -690,6 +704,24 @@
full_calib = 0;
}
spin_unlock_irqrestore(&chip->reg_lock, flags);
+ } else if (chip->hardware == WSS_HW_AD1845) {
+ unsigned rate = params_rate(params);
+
+ /*
+ * Program the AD1845 correctly for the capture stream.
+ * Note that we do NOT need to toggle the MCE bit because
+ * the PLAYBACK_ENABLE bit of the Interface Configuration
+ * register is set.
+ *
+ * NOTE: We seem to need to write to the MSB before the LSB
+ * to get the correct sample frequency.
+ */
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_wss_out(chip, CS4231_REC_FORMAT, (cdfr & 0xf0));
+ snd_wss_out(chip, AD1845_UPR_FREQ_SEL, (rate >> 8) & 0xff);
+ snd_wss_out(chip, AD1845_LWR_FREQ_SEL, rate & 0xff);
+ full_calib = 0;
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
}
if (full_calib) {
snd_wss_mce_up(chip);
@@ -714,7 +746,6 @@
spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_wss_mce_down(chip);
}
- snd_wss_calibrate_mute(chip, 0);
mutex_unlock(&chip->mce_mutex);
}
@@ -771,10 +802,11 @@
{
unsigned long flags;
+ snd_wss_calibrate_mute(chip, 1);
snd_wss_mce_down(chip);
#ifdef SNDRV_DEBUG_MCE
- snd_printk("init: (1)\n");
+ snd_printk(KERN_DEBUG "init: (1)\n");
#endif
snd_wss_mce_up(chip);
spin_lock_irqsave(&chip->reg_lock, flags);
@@ -789,18 +821,20 @@
snd_wss_mce_down(chip);
#ifdef SNDRV_DEBUG_MCE
- snd_printk("init: (2)\n");
+ snd_printk(KERN_DEBUG "init: (2)\n");
#endif
snd_wss_mce_up(chip);
spin_lock_irqsave(&chip->reg_lock, flags);
+ chip->image[CS4231_IFACE_CTRL] &= ~CS4231_AUTOCALIB;
+ snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
snd_wss_out(chip,
CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]);
spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_wss_mce_down(chip);
#ifdef SNDRV_DEBUG_MCE
- snd_printk("init: (3) - afei = 0x%x\n",
+ snd_printk(KERN_DEBUG "init: (3) - afei = 0x%x\n",
chip->image[CS4231_ALT_FEATURE_1]);
#endif
@@ -817,7 +851,7 @@
snd_wss_mce_down(chip);
#ifdef SNDRV_DEBUG_MCE
- snd_printk("init: (4)\n");
+ snd_printk(KERN_DEBUG "init: (4)\n");
#endif
snd_wss_mce_up(chip);
@@ -827,9 +861,10 @@
chip->image[CS4231_REC_FORMAT]);
spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_wss_mce_down(chip);
+ snd_wss_calibrate_mute(chip, 0);
#ifdef SNDRV_DEBUG_MCE
- snd_printk("init: (5)\n");
+ snd_printk(KERN_DEBUG "init: (5)\n");
#endif
}
@@ -885,8 +920,6 @@
mutex_unlock(&chip->open_mutex);
return;
}
- snd_wss_calibrate_mute(chip, 1);
-
/* disable IRQ */
spin_lock_irqsave(&chip->reg_lock, flags);
if (!(chip->hardware & WSS_HW_AD1848_MASK))
@@ -919,8 +952,6 @@
wss_outb(chip, CS4231P(STATUS), 0); /* clear IRQ */
spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_wss_calibrate_mute(chip, 0);
-
chip->mode = 0;
mutex_unlock(&chip->open_mutex);
}
@@ -1113,7 +1144,7 @@
if (chip->hardware & WSS_HW_AD1848_MASK)
wss_outb(chip, CS4231P(STATUS), 0);
else
- snd_wss_outm(chip, CS4231_IRQ_STATUS, status, 0);
+ snd_wss_out(chip, CS4231_IRQ_STATUS, status);
spin_unlock(&chip->reg_lock);
return IRQ_HANDLED;
}
@@ -1278,7 +1309,8 @@
} else if (rev == 0x03) {
chip->hardware = WSS_HW_CS4236B;
} else {
- snd_printk("unknown CS chip with version 0x%x\n", rev);
+ snd_printk(KERN_ERR
+ "unknown CS chip with version 0x%x\n", rev);
return -ENODEV; /* unknown CS4231 chip? */
}
}
@@ -1314,6 +1346,10 @@
chip->image[CS4231_ALT_FEATURE_2] =
chip->hardware == WSS_HW_INTERWAVE ? 0xc2 : 0x01;
}
+ /* enable fine grained frequency selection */
+ if (chip->hardware == WSS_HW_AD1845)
+ chip->image[AD1845_PWR_DOWN] = 8;
+
ptr = (unsigned char *) &chip->image;
regnum = (chip->hardware & WSS_HW_AD1848_MASK) ? 16 : 32;
snd_wss_mce_down(chip);
@@ -1342,7 +1378,10 @@
case 6:
break;
default:
- snd_printk("unknown CS4235 chip (enhanced version = 0x%x)\n", id);
+ snd_printk(KERN_WARNING
+ "unknown CS4235 chip "
+ "(enhanced version = 0x%x)\n",
+ id);
}
} else if ((id & 0x1f) == 0x0b) { /* CS4236/B */
switch (id >> 5) {
@@ -1353,7 +1392,10 @@
chip->hardware = WSS_HW_CS4236B;
break;
default:
- snd_printk("unknown CS4236 chip (enhanced version = 0x%x)\n", id);
+ snd_printk(KERN_WARNING
+ "unknown CS4236 chip "
+ "(enhanced version = 0x%x)\n",
+ id);
}
} else if ((id & 0x1f) == 0x08) { /* CS4237B */
chip->hardware = WSS_HW_CS4237B;
@@ -1364,7 +1406,10 @@
case 7:
break;
default:
- snd_printk("unknown CS4237B chip (enhanced version = 0x%x)\n", id);
+ snd_printk(KERN_WARNING
+ "unknown CS4237B chip "
+ "(enhanced version = 0x%x)\n",
+ id);
}
} else if ((id & 0x1f) == 0x09) { /* CS4238B */
chip->hardware = WSS_HW_CS4238B;
@@ -1374,7 +1419,10 @@
case 7:
break;
default:
- snd_printk("unknown CS4238B chip (enhanced version = 0x%x)\n", id);
+ snd_printk(KERN_WARNING
+ "unknown CS4238B chip "
+ "(enhanced version = 0x%x)\n",
+ id);
}
} else if ((id & 0x1f) == 0x1e) { /* CS4239 */
chip->hardware = WSS_HW_CS4239;
@@ -1384,10 +1432,15 @@
case 6:
break;
default:
- snd_printk("unknown CS4239 chip (enhanced version = 0x%x)\n", id);
+ snd_printk(KERN_WARNING
+ "unknown CS4239 chip "
+ "(enhanced version = 0x%x)\n",
+ id);
}
} else {
- snd_printk("unknown CS4236/CS423xB chip (enhanced version = 0x%x)\n", id);
+ snd_printk(KERN_WARNING
+ "unknown CS4236/CS423xB chip "
+ "(enhanced version = 0x%x)\n", id);
}
}
}
@@ -1618,7 +1671,8 @@
wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
spin_unlock_irqrestore(&chip->reg_lock, flags);
if (timeout == 0x80)
- snd_printk("down [0x%lx]: serious init problem - codec still busy\n", chip->port);
+ snd_printk(KERN_ERR "down [0x%lx]: serious init problem "
+ "- codec still busy\n", chip->port);
if ((timeout & CS4231_MCE) == 0 ||
!(chip->hardware & (WSS_HW_CS4231_MASK | WSS_HW_CS4232_MASK))) {
return;
@@ -1628,7 +1682,7 @@
}
#endif /* CONFIG_PM */
-static int snd_wss_free(struct snd_wss *chip)
+int snd_wss_free(struct snd_wss *chip)
{
release_and_free_resource(chip->res_port);
release_and_free_resource(chip->res_cport);
@@ -1651,6 +1705,7 @@
kfree(chip);
return 0;
}
+EXPORT_SYMBOL(snd_wss_free);
static int snd_wss_dev_free(struct snd_device *device)
{
@@ -1820,7 +1875,8 @@
#if 0
if (chip->hardware & WSS_HW_CS4232_MASK) {
if (chip->res_cport == NULL)
- snd_printk("CS4232 control port features are not accessible\n");
+ snd_printk(KERN_ERR "CS4232 control port features are "
+ "not accessible\n");
}
#endif
diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c
index 1881cec..3e763d6 100644
--- a/sound/mips/au1x00.c
+++ b/sound/mips/au1x00.c
@@ -636,9 +636,10 @@
struct snd_card *card;
struct snd_au1000 *au1000;
- card = snd_card_new(-1, "AC97", THIS_MODULE, sizeof(struct snd_au1000));
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(-1, "AC97", THIS_MODULE,
+ sizeof(struct snd_au1000), &card);
+ if (err < 0)
+ return err;
card->private_free = snd_au1000_free;
au1000 = card->private_data;
@@ -678,7 +679,7 @@
return err;
}
- printk( KERN_INFO "ALSA AC97: Driver Initialized\n" );
+ printk(KERN_INFO "ALSA AC97: Driver Initialized\n");
au1000_card = card;
return 0;
}
diff --git a/sound/mips/hal2.c b/sound/mips/hal2.c
index db495be..c52691c 100644
--- a/sound/mips/hal2.c
+++ b/sound/mips/hal2.c
@@ -878,9 +878,9 @@
struct snd_hal2 *chip;
int err;
- card = snd_card_new(index, id, THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
err = hal2_create(card, &chip);
if (err < 0) {
diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c
index 4c63504..66f3b48 100644
--- a/sound/mips/sgio2audio.c
+++ b/sound/mips/sgio2audio.c
@@ -936,9 +936,9 @@
struct snd_sgio2audio *chip;
int err;
- card = snd_card_new(index, id, THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
err = snd_sgio2audio_create(card, &chip);
if (err < 0) {
diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c
index 7cf9913..d12bd98 100644
--- a/sound/oss/ad1848.c
+++ b/sound/oss/ad1848.c
@@ -280,7 +280,7 @@
while (timeout > 0 && (ad_read(devc, 11) & 0x20))
timeout--;
if (ad_read(devc, 11) & 0x20)
- if ( (devc->model != MD_1845) || (devc->model != MD_1845_SSCAPE))
+ if ((devc->model != MD_1845) && (devc->model != MD_1845_SSCAPE))
printk(KERN_WARNING "ad1848: Auto calibration timed out(3).\n");
}
@@ -2107,7 +2107,7 @@
switch (cmd)
{
case AD1848_SET_XTAL: /* Change clock frequency of AD1845 (only ) */
- if (devc->model != MD_1845 || devc->model != MD_1845_SSCAPE)
+ if (devc->model != MD_1845 && devc->model != MD_1845_SSCAPE)
return -EINVAL;
spin_lock_irqsave(&devc->lock,flags);
ad_enter_MCE(devc);
diff --git a/sound/oss/dmabuf.c b/sound/oss/dmabuf.c
index 1e90d76..1bfcf7e 100644
--- a/sound/oss/dmabuf.c
+++ b/sound/oss/dmabuf.c
@@ -439,7 +439,7 @@
DMAbuf_launch_output(dev, dmap);
adev->dmap_out->flags |= DMA_SYNCING;
adev->dmap_out->underrun_count = 0;
- while (!signal_pending(current) && n++ <= adev->dmap_out->nbufs &&
+ while (!signal_pending(current) && n++ < adev->dmap_out->nbufs &&
adev->dmap_out->qlen && adev->dmap_out->underrun_count == 0) {
long t = dmabuf_timeout(dmap);
spin_unlock_irqrestore(&dmap->lock,flags);
diff --git a/sound/oss/pas2_card.c b/sound/oss/pas2_card.c
index 25f3a22..7f377ec 100644
--- a/sound/oss/pas2_card.c
+++ b/sound/oss/pas2_card.c
@@ -156,9 +156,7 @@
* 0x80
*/ , 0xB88);
- pas_write(0x80
- | joystick?0x40:0
- ,0xF388);
+ pas_write(0x80 | (joystick ? 0x40 : 0), 0xF388);
if (pas_irq < 0 || pas_irq > 15)
{
diff --git a/sound/oss/pss.c b/sound/oss/pss.c
index 16ed069..16517a5 100644
--- a/sound/oss/pss.c
+++ b/sound/oss/pss.c
@@ -457,10 +457,9 @@
}
}
-static int set_volume_mono(unsigned __user *p, int *aleft)
+static int set_volume_mono(unsigned __user *p, unsigned int *aleft)
{
- int left;
- unsigned volume;
+ unsigned int left, volume;
if (get_user(volume, p))
return -EFAULT;
@@ -471,10 +470,11 @@
return 0;
}
-static int set_volume_stereo(unsigned __user *p, int *aleft, int *aright)
+static int set_volume_stereo(unsigned __user *p,
+ unsigned int *aleft,
+ unsigned int *aright)
{
- int left, right;
- unsigned volume;
+ unsigned int left, right, volume;
if (get_user(volume, p))
return -EFAULT;
diff --git a/sound/oss/sequencer.c b/sound/oss/sequencer.c
index 5c215f7..c798746 100644
--- a/sound/oss/sequencer.c
+++ b/sound/oss/sequencer.c
@@ -212,7 +212,6 @@
{
unsigned char event_rec[EV_SZ], ev_code;
int p = 0, c, ev_size;
- int err;
int mode = translate_mode(file);
dev = dev >> 4;
@@ -285,7 +284,7 @@
{
if (!midi_opened[event_rec[2]])
{
- int mode;
+ int err, mode;
int dev = event_rec[2];
if (dev >= max_mididev || midi_devs[dev]==NULL)
diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c
index 41f870f..6055fd6 100644
--- a/sound/parisc/harmony.c
+++ b/sound/parisc/harmony.c
@@ -975,9 +975,9 @@
struct snd_card *card;
struct snd_harmony *h;
- card = snd_card_new(index, id, THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
err = snd_harmony_create(card, padev, &h);
if (err < 0)
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 82b9bdd..ca25e61 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -400,6 +400,26 @@
To compile this driver as a module, choose M here: the module
will be called snd-indigodj
+config SND_INDIGOIOX
+ tristate "(Echoaudio) Indigo IOx"
+ select FW_LOADER
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for Echoaudio Indigo IOx.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-indigoiox
+
+config SND_INDIGODJX
+ tristate "(Echoaudio) Indigo DJx"
+ select FW_LOADER
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for Echoaudio Indigo DJx.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-indigodjx
+
config SND_EMU10K1
tristate "Emu10k1 (SB Live!, Audigy, E-mu APS)"
select FW_LOADER
@@ -744,7 +764,8 @@
select SND_OXYGEN_LIB
help
Say Y here to include support for sound cards based on the
- Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2 and D2X.
+ Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, and
+ Essence STX.
Support for the HDAV1.3 (Deluxe) is very experimental.
To compile this driver as a module, choose M here: the module
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index e2b843b..97ee127 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -143,6 +143,7 @@
{ 0x43525970, 0xfffffff8, "CS4202", NULL, NULL },
{ 0x43585421, 0xffffffff, "HSD11246", NULL, NULL }, // SmartMC II
{ 0x43585428, 0xfffffff8, "Cx20468", patch_conexant, NULL }, // SmartAMC fixme: the mask might be different
+{ 0x43585430, 0xffffffff, "Cx20468-31", patch_conexant, NULL },
{ 0x43585431, 0xffffffff, "Cx20551", patch_cx20551, NULL },
{ 0x44543031, 0xfffffff0, "DT0398", NULL, NULL },
{ 0x454d4328, 0xffffffff, "EM28028", NULL, NULL }, // same as TR28028?
@@ -383,7 +384,7 @@
EXPORT_SYMBOL(snd_ac97_update_bits);
-/* no lock version - see snd_ac97_updat_bits() */
+/* no lock version - see snd_ac97_update_bits() */
int snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsigned short reg,
unsigned short mask, unsigned short value)
{
@@ -1643,7 +1644,10 @@
{
int err, idx;
- //printk("AC97_GPIO_CFG = %x\n",snd_ac97_read(ac97,AC97_GPIO_CFG));
+ /*
+ printk(KERN_DEBUG "AC97_GPIO_CFG = %x\n",
+ snd_ac97_read(ac97,AC97_GPIO_CFG));
+ */
snd_ac97_write(ac97, AC97_GPIO_CFG, 0xffff & ~(AC97_GPIO_LINE1_OH));
snd_ac97_write(ac97, AC97_GPIO_POLARITY, 0xffff & ~(AC97_GPIO_LINE1_OH));
snd_ac97_write(ac97, AC97_GPIO_STICKY, 0xffff);
diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c
index 060ea59..73b17d5 100644
--- a/sound/pci/ac97/ac97_proc.c
+++ b/sound/pci/ac97/ac97_proc.c
@@ -125,6 +125,8 @@
snd_iprintf(buffer, "PCI Subsys Device: 0x%04x\n\n",
ac97->subsystem_device);
+ snd_iprintf(buffer, "Flags: %x\n", ac97->flags);
+
if ((ac97->ext_id & AC97_EI_REV_MASK) >= AC97_EI_REV_23) {
val = snd_ac97_read(ac97, AC97_INT_PAGING);
snd_ac97_update_bits(ac97, AC97_INT_PAGING,
diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
index a7f38e6..d1f242b 100644
--- a/sound/pci/ad1889.c
+++ b/sound/pci/ad1889.c
@@ -995,10 +995,10 @@
}
/* (2) */
- card = snd_card_new(index[devno], id[devno], THIS_MODULE, 0);
+ err = snd_card_create(index[devno], id[devno], THIS_MODULE, 0, &card);
/* XXX REVISIT: we can probably allocate chip in this call */
- if (card == NULL)
- return -ENOMEM;
+ if (err < 0)
+ return err;
strcpy(card->driver, "AD1889");
strcpy(card->shortname, "Analog Devices AD1889");
diff --git a/sound/pci/ak4531_codec.c b/sound/pci/ak4531_codec.c
index 0f819dd..fd135e3 100644
--- a/sound/pci/ak4531_codec.c
+++ b/sound/pci/ak4531_codec.c
@@ -51,7 +51,8 @@
int idx;
for (idx = 0; idx < 0x19; idx++)
- printk("ak4531 0x%x: 0x%x\n", idx, ak4531->regs[idx]);
+ printk(KERN_DEBUG "ak4531 0x%x: 0x%x\n",
+ idx, ak4531->regs[idx]);
}
#endif
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index 1a0fd65..4edf270 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -2142,7 +2142,7 @@
{
int err;
- snd_ali_printk("resouces allocation ...\n");
+ snd_ali_printk("resources allocation ...\n");
err = pci_request_regions(codec->pci, "ALI 5451");
if (err < 0)
return err;
@@ -2154,7 +2154,7 @@
return -EBUSY;
}
codec->irq = codec->pci->irq;
- snd_ali_printk("resouces allocated.\n");
+ snd_ali_printk("resources allocated.\n");
return 0;
}
static int snd_ali_dev_free(struct snd_device *device)
@@ -2307,9 +2307,9 @@
snd_ali_printk("probe ...\n");
- card = snd_card_new(index, id, THIS_MODULE, 0);
- if (!card)
- return -ENOMEM;
+ err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
err = snd_ali_create(card, pci, pcm_channels, spdif, &codec);
if (err < 0)
diff --git a/sound/pci/als300.c b/sound/pci/als300.c
index 8df6824..009b4c8 100644
--- a/sound/pci/als300.c
+++ b/sound/pci/als300.c
@@ -91,7 +91,7 @@
#define DEBUG_PLAY_REC 0
#if DEBUG_CALLS
-#define snd_als300_dbgcalls(format, args...) printk(format, ##args)
+#define snd_als300_dbgcalls(format, args...) printk(KERN_DEBUG format, ##args)
#define snd_als300_dbgcallenter() printk(KERN_ERR "--> %s\n", __func__)
#define snd_als300_dbgcallleave() printk(KERN_ERR "<-- %s\n", __func__)
#else
@@ -812,10 +812,10 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
- if (card == NULL)
- return -ENOMEM;
+ if (err < 0)
+ return err;
chip_type = pci_id->driver_data;
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index ba57005..542a0c6 100644
--- a/sound/pci/als4000.c
+++ b/sound/pci/als4000.c
@@ -889,12 +889,13 @@
pci_write_config_word(pci, PCI_COMMAND, word | PCI_COMMAND_IO);
pci_set_master(pci);
- card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(*acard) /* private_data: acard */);
- if (card == NULL) {
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(*acard) /* private_data: acard */,
+ &card);
+ if (err < 0) {
pci_release_regions(pci);
pci_disable_device(pci);
- return -ENOMEM;
+ return err;
}
acard = card->private_data;
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index 226fe82..9ce8548 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -1645,9 +1645,9 @@
struct atiixp *chip;
int err;
- card = snd_card_new(index, id, THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
strcpy(card->driver, spdif_aclink ? "ATIIXP" : "ATIIXP-SPDMA");
strcpy(card->shortname, "ATI IXP");
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index 0e6e5cc..c3136cc 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -1288,9 +1288,9 @@
struct atiixp_modem *chip;
int err;
- card = snd_card_new(index, id, THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
strcpy(card->driver, "ATIIXP-MODEM");
strcpy(card->shortname, "ATI IXP Modem");
diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c
index a36d4d1..9ec1223 100644
--- a/sound/pci/au88x0/au88x0.c
+++ b/sound/pci/au88x0/au88x0.c
@@ -250,9 +250,9 @@
return -ENOENT;
}
// (2)
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
// (3)
if ((err = snd_vortex_create(card, pci, &chip)) < 0) {
diff --git a/sound/pci/au88x0/au88x0_a3d.c b/sound/pci/au88x0/au88x0_a3d.c
index 649849e..f4aa8ff 100644
--- a/sound/pci/au88x0/au88x0_a3d.c
+++ b/sound/pci/au88x0/au88x0_a3d.c
@@ -462,9 +462,10 @@
/* Reset Single A3D source. */
static void a3dsrc_ZeroState(a3dsrc_t * a)
{
-
- //printk("vortex: ZeroState slice: %d, source %d\n", a->slice, a->source);
-
+ /*
+ printk(KERN_DEBUG "vortex: ZeroState slice: %d, source %d\n",
+ a->slice, a->source);
+ */
a3dsrc_SetAtmosState(a, 0, 0, 0, 0);
a3dsrc_SetHrtfState(a, A3dHrirZeros, A3dHrirZeros);
a3dsrc_SetItdDline(a, A3dItdDlineZeros);
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
index b070e57..3906f5a 100644
--- a/sound/pci/au88x0/au88x0_core.c
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -1135,7 +1135,10 @@
snd_pcm_sgbuf_get_addr(dma->substream, 0));
break;
}
- //printk("vortex: cfg0 = 0x%x\nvortex: cfg1=0x%x\n", dma->cfg0, dma->cfg1);
+ /*
+ printk(KERN_DEBUG "vortex: cfg0 = 0x%x\nvortex: cfg1=0x%x\n",
+ dma->cfg0, dma->cfg1);
+ */
hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFCFG0 + (adbdma << 3), dma->cfg0);
hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFCFG1 + (adbdma << 3), dma->cfg1);
@@ -1959,7 +1962,7 @@
ADB_CODECOUT(0 + 4));
vortex_connection_mix_adb(vortex, en, 0x11, mixers[3],
ADB_CODECOUT(1 + 4));
- //printk("SDAC detected ");
+ /* printk(KERN_DEBUG "SDAC detected "); */
}
#else
// Use plain direct output to codec.
@@ -2013,7 +2016,11 @@
resmap[restype] |= (1 << i);
else
vortex->dma_adb[i].resources[restype] |= (1 << i);
- //printk("vortex: ResManager: type %d out %d\n", restype, i);
+ /*
+ printk(KERN_DEBUG
+ "vortex: ResManager: type %d out %d\n",
+ restype, i);
+ */
return i;
}
}
@@ -2024,7 +2031,11 @@
for (i = 0; i < qty; i++) {
if (resmap[restype] & (1 << i)) {
resmap[restype] &= ~(1 << i);
- //printk("vortex: ResManager: type %d in %d\n",restype, i);
+ /*
+ printk(KERN_DEBUG
+ "vortex: ResManager: type %d in %d\n",
+ restype, i);
+ */
return i;
}
}
@@ -2789,7 +2800,7 @@
{
int a, this_194;
- if ((bits != 8) || (bits != 16))
+ if ((bits != 8) && (bits != 16))
return -1;
switch (encod) {
diff --git a/sound/pci/au88x0/au88x0_synth.c b/sound/pci/au88x0/au88x0_synth.c
index 978b856..2805e34 100644
--- a/sound/pci/au88x0/au88x0_synth.c
+++ b/sound/pci/au88x0/au88x0_synth.c
@@ -213,38 +213,59 @@
switch (reg) {
/* Voice specific parameters */
case 0: /* running */
- //printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_RUN(wt), (int)val);
+ /*
+ printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ WT_RUN(wt), (int)val);
+ */
hwwrite(vortex->mmio, WT_RUN(wt), val);
return 0xc;
break;
case 1: /* param 0 */
- //printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_PARM(wt,0), (int)val);
+ /*
+ printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ WT_PARM(wt,0), (int)val);
+ */
hwwrite(vortex->mmio, WT_PARM(wt, 0), val);
return 0xc;
break;
case 2: /* param 1 */
- //printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_PARM(wt,1), (int)val);
+ /*
+ printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ WT_PARM(wt,1), (int)val);
+ */
hwwrite(vortex->mmio, WT_PARM(wt, 1), val);
return 0xc;
break;
case 3: /* param 2 */
- //printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_PARM(wt,2), (int)val);
+ /*
+ printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ WT_PARM(wt,2), (int)val);
+ */
hwwrite(vortex->mmio, WT_PARM(wt, 2), val);
return 0xc;
break;
case 4: /* param 3 */
- //printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_PARM(wt,3), (int)val);
+ /*
+ printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ WT_PARM(wt,3), (int)val);
+ */
hwwrite(vortex->mmio, WT_PARM(wt, 3), val);
return 0xc;
break;
case 6: /* mute */
- //printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_MUTE(wt), (int)val);
+ /*
+ printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ WT_MUTE(wt), (int)val);
+ */
hwwrite(vortex->mmio, WT_MUTE(wt), val);
return 0xc;
break;
case 0xb:
{ /* delay */
- //printk("vortex: WT SetReg(0x%x) = 0x%08x\n", WT_DELAY(wt,0), (int)val);
+ /*
+ printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ WT_DELAY(wt,0), (int)val);
+ */
hwwrite(vortex->mmio, WT_DELAY(wt, 3), val);
hwwrite(vortex->mmio, WT_DELAY(wt, 2), val);
hwwrite(vortex->mmio, WT_DELAY(wt, 1), val);
@@ -272,7 +293,9 @@
return 0;
break;
}
- //printk("vortex: WT SetReg(0x%x) = 0x%08x\n", ecx, (int)val);
+ /*
+ printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", ecx, (int)val);
+ */
hwwrite(vortex->mmio, ecx, val);
return 1;
}
diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c
index c7c54e7..8eea29f 100644
--- a/sound/pci/aw2/aw2-alsa.c
+++ b/sound/pci/aw2/aw2-alsa.c
@@ -368,9 +368,9 @@
}
/* (2) Create card instance */
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
/* (3) Create main component */
err = snd_aw2_create(card, pci, &chip);
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 333007c..e9e9b58 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -211,25 +211,25 @@
#endif
#if DEBUG_MIXER
-#define snd_azf3328_dbgmixer(format, args...) printk(format, ##args)
+#define snd_azf3328_dbgmixer(format, args...) printk(KERN_DEBUG format, ##args)
#else
#define snd_azf3328_dbgmixer(format, args...)
#endif
#if DEBUG_PLAY_REC
-#define snd_azf3328_dbgplay(format, args...) printk(KERN_ERR format, ##args)
+#define snd_azf3328_dbgplay(format, args...) printk(KERN_DEBUG format, ##args)
#else
#define snd_azf3328_dbgplay(format, args...)
#endif
#if DEBUG_MISC
-#define snd_azf3328_dbgtimer(format, args...) printk(KERN_ERR format, ##args)
+#define snd_azf3328_dbgtimer(format, args...) printk(KERN_DEBUG format, ##args)
#else
#define snd_azf3328_dbgtimer(format, args...)
#endif
#if DEBUG_GAME
-#define snd_azf3328_dbggame(format, args...) printk(KERN_ERR format, ##args)
+#define snd_azf3328_dbggame(format, args...) printk(KERN_DEBUG format, ##args)
#else
#define snd_azf3328_dbggame(format, args...)
#endif
@@ -2216,9 +2216,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
strcpy(card->driver, "AZF3328");
strcpy(card->shortname, "Aztech AZF3328 (PCI168)");
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
index 1aa1c04..a299340 100644
--- a/sound/pci/bt87x.c
+++ b/sound/pci/bt87x.c
@@ -888,9 +888,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (!card)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
err = snd_bt87x_create(card, pci, &chip);
if (err < 0)
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index 0e62205..df75757 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -255,6 +255,14 @@
.gpio_type = 2,
.i2c_adc = 1,
.spi_dac = 1 } ,
+ /* Giga-byte GA-G1975X mobo
+ * Novell bnc#395807
+ */
+ /* FIXME: the GPIO and I2C setting aren't tested well */
+ { .serial = 0x1458a006,
+ .name = "Giga-byte GA-G1975X",
+ .gpio_type = 1,
+ .i2c_adc = 1 },
/* Shuttle XPC SD31P which has an onboard Creative Labs
* Sound Blaster Live! 24-bit EAX
* high-definition 7.1 audio processor".
@@ -404,7 +412,9 @@
}
tmp = reg << 25 | value << 16;
- // snd_printk("I2C-write:reg=0x%x, value=0x%x\n", reg, value);
+ /*
+ snd_printk(KERN_DEBUG "I2C-write:reg=0x%x, value=0x%x\n", reg, value);
+ */
/* Not sure what this I2C channel controls. */
/* snd_ca0106_ptr_write(emu, I2C_D0, 0, tmp); */
@@ -422,7 +432,7 @@
/* Wait till the transaction ends */
while (1) {
status = snd_ca0106_ptr_read(emu, I2C_A, 0);
- //snd_printk("I2C:status=0x%x\n", status);
+ /*snd_printk(KERN_DEBUG "I2C:status=0x%x\n", status);*/
timeout++;
if ((status & I2C_A_ADC_START) == 0)
break;
@@ -521,7 +531,10 @@
channel->number = channel_id;
channel->use = 1;
- //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
+ /*
+ printk(KERN_DEBUG "open:channel_id=%d, chip=%p, channel=%p\n",
+ channel_id, chip, channel);
+ */
//channel->interrupt = snd_ca0106_pcm_channel_interrupt;
channel->epcm = epcm;
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
@@ -614,7 +627,10 @@
channel->number = channel_id;
channel->use = 1;
- //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
+ /*
+ printk(KERN_DEBUG "open:channel_id=%d, chip=%p, channel=%p\n",
+ channel_id, chip, channel);
+ */
//channel->interrupt = snd_ca0106_pcm_channel_interrupt;
channel->epcm = epcm;
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
@@ -705,9 +721,20 @@
u32 reg71;
int i;
- //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1));
- //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base);
- //snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->buffer.addr, emu->buffer.area, emu->buffer.bytes);
+#if 0 /* debug */
+ snd_printk(KERN_DEBUG
+ "prepare:channel_number=%d, rate=%d, format=0x%x, "
+ "channels=%d, buffer_size=%ld, period_size=%ld, "
+ "periods=%u, frames_to_bytes=%d\n",
+ channel, runtime->rate, runtime->format,
+ runtime->channels, runtime->buffer_size,
+ runtime->period_size, runtime->periods,
+ frames_to_bytes(runtime, 1));
+ snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, table_base=%p\n",
+ runtime->dma_addr, runtime->dma_area, table_base);
+ snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
+ emu->buffer.addr, emu->buffer.area, emu->buffer.bytes);
+#endif /* debug */
/* Rate can be set per channel. */
/* reg40 control host to fifo */
/* reg71 controls DAC rate. */
@@ -799,9 +826,20 @@
u32 reg71_set = 0;
u32 reg71;
- //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1));
- //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base);
- //snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->buffer.addr, emu->buffer.area, emu->buffer.bytes);
+#if 0 /* debug */
+ snd_printk(KERN_DEBUG
+ "prepare:channel_number=%d, rate=%d, format=0x%x, "
+ "channels=%d, buffer_size=%ld, period_size=%ld, "
+ "periods=%u, frames_to_bytes=%d\n",
+ channel, runtime->rate, runtime->format,
+ runtime->channels, runtime->buffer_size,
+ runtime->period_size, runtime->periods,
+ frames_to_bytes(runtime, 1));
+ snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, table_base=%p\n",
+ runtime->dma_addr, runtime->dma_area, table_base);
+ snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
+ emu->buffer.addr, emu->buffer.area, emu->buffer.bytes);
+#endif /* debug */
/* reg71 controls ADC rate. */
switch (runtime->rate) {
case 44100:
@@ -846,7 +884,14 @@
}
- //printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, frames_to_bytes(runtime, 1));
+ /*
+ printk(KERN_DEBUG
+ "prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, "
+ "buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",
+ channel, runtime->rate, runtime->format, runtime->channels,
+ runtime->buffer_size, runtime->period_size,
+ frames_to_bytes(runtime, 1));
+ */
snd_ca0106_ptr_write(emu, 0x13, channel, 0);
snd_ca0106_ptr_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr);
snd_ca0106_ptr_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes
@@ -888,13 +933,13 @@
runtime = s->runtime;
epcm = runtime->private_data;
channel = epcm->channel_id;
- /* snd_printk("channel=%d\n",channel); */
+ /* snd_printk(KERN_DEBUG "channel=%d\n", channel); */
epcm->running = running;
basic |= (0x1 << channel);
extended |= (0x10 << channel);
snd_pcm_trigger_done(s, substream);
}
- /* snd_printk("basic=0x%x, extended=0x%x\n",basic, extended); */
+ /* snd_printk(KERN_DEBUG "basic=0x%x, extended=0x%x\n",basic, extended); */
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -972,8 +1017,13 @@
ptr=ptr2;
if (ptr >= runtime->buffer_size)
ptr -= runtime->buffer_size;
- //printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate);
-
+ /*
+ printk(KERN_DEBUG "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
+ "buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n",
+ ptr1, ptr2, ptr, (int)runtime->buffer_size,
+ (int)runtime->period_size, (int)runtime->frame_bits,
+ (int)runtime->rate);
+ */
return ptr;
}
@@ -995,8 +1045,13 @@
ptr=ptr2;
if (ptr >= runtime->buffer_size)
ptr -= runtime->buffer_size;
- //printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate);
-
+ /*
+ printk(KERN_DEBUG "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
+ "buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n",
+ ptr1, ptr2, ptr, (int)runtime->buffer_size,
+ (int)runtime->period_size, (int)runtime->frame_bits,
+ (int)runtime->rate);
+ */
return ptr;
}
@@ -1181,8 +1236,12 @@
return IRQ_NONE;
stat76 = snd_ca0106_ptr_read(chip, EXTENDED_INT, 0);
- //snd_printk("interrupt status = 0x%08x, stat76=0x%08x\n", status, stat76);
- //snd_printk("ptr=0x%08x\n",snd_ca0106_ptr_read(chip, PLAYBACK_POINTER, 0));
+ /*
+ snd_printk(KERN_DEBUG "interrupt status = 0x%08x, stat76=0x%08x\n",
+ status, stat76);
+ snd_printk(KERN_DEBUG "ptr=0x%08x\n",
+ snd_ca0106_ptr_read(chip, PLAYBACK_POINTER, 0));
+ */
mask = 0x11; /* 0x1 for one half, 0x10 for the other half period. */
for(i = 0; i < 4; i++) {
pchannel = &(chip->playback_channels[i]);
@@ -1470,7 +1529,7 @@
int size, n;
size = ARRAY_SIZE(i2c_adc_init);
- /* snd_printk("I2C:array size=0x%x\n", size); */
+ /* snd_printk(KERN_DEBUG "I2C:array size=0x%x\n", size); */
for (n = 0; n < size; n++)
snd_ca0106_i2c_write(chip, i2c_adc_init[n][0],
i2c_adc_init[n][1]);
@@ -1707,9 +1766,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
err = snd_ca0106_create(dev, card, pci, &chip);
if (err < 0)
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 1a74ca6..c7899c3 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -3272,9 +3272,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
switch (pci->device) {
case PCI_DEVICE_ID_CMEDIA_CM8738:
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index 192e784..f6286f8 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -834,7 +834,11 @@
struct cs4281_dma *dma = runtime->private_data;
struct cs4281 *chip = snd_pcm_substream_chip(substream);
- // printk("DCC = 0x%x, buffer_size = 0x%x, jiffies = %li\n", snd_cs4281_peekBA0(chip, dma->regDCC), runtime->buffer_size, jiffies);
+ /*
+ printk(KERN_DEBUG "DCC = 0x%x, buffer_size = 0x%x, jiffies = %li\n",
+ snd_cs4281_peekBA0(chip, dma->regDCC), runtime->buffer_size,
+ jiffies);
+ */
return runtime->buffer_size -
snd_cs4281_peekBA0(chip, dma->regDCC) - 1;
}
@@ -1925,9 +1929,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
if ((err = snd_cs4281_create(card, pci, &chip, dual_codec[dev])) < 0) {
snd_card_free(card);
diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c
index e876b32..c9b3e3d 100644
--- a/sound/pci/cs46xx/cs46xx.c
+++ b/sound/pci/cs46xx/cs46xx.c
@@ -88,9 +88,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
if ((err = snd_cs46xx_create(card, pci,
external_amp[dev], thinkpad[dev],
&chip)) < 0) {
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index 8ab07aa..1be96ea 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -194,7 +194,7 @@
* ACSDA = Status Data Register = 474h
*/
#if 0
- printk("e) reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", reg,
+ printk(KERN_DEBUG "e) reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", reg,
snd_cs46xx_peekBA0(chip, BA0_ACSDA),
snd_cs46xx_peekBA0(chip, BA0_ACCAD));
#endif
@@ -428,8 +428,8 @@
}
if(status & SERBST_WBSY) {
- snd_printk( KERN_ERR "cs46xx: failure waiting for FIFO command to complete\n");
-
+ snd_printk(KERN_ERR "cs46xx: failure waiting for "
+ "FIFO command to complete\n");
return -EINVAL;
}
diff --git a/sound/pci/cs46xx/cs46xx_lib.h b/sound/pci/cs46xx/cs46xx_lib.h
index 018a7de..4eb55aa 100644
--- a/sound/pci/cs46xx/cs46xx_lib.h
+++ b/sound/pci/cs46xx/cs46xx_lib.h
@@ -62,7 +62,11 @@
unsigned int bank = reg >> 16;
unsigned int offset = reg & 0xffff;
- /*if (bank == 0) printk("snd_cs46xx_poke: %04X - %08X\n",reg >> 2,val); */
+ /*
+ if (bank == 0)
+ printk(KERN_DEBUG "snd_cs46xx_poke: %04X - %08X\n",
+ reg >> 2,val);
+ */
writel(val, chip->region.idx[bank+1].remap_addr + offset);
}
diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c
index 6dea5b5..dc46432 100644
--- a/sound/pci/cs5530.c
+++ b/sound/pci/cs5530.c
@@ -258,10 +258,10 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
- if (card == NULL)
- return -ENOMEM;
+ if (err < 0)
+ return err;
err = snd_cs5530_create(card, pci, &chip);
if (err < 0) {
diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
index 826e6de..c89ed1f 100644
--- a/sound/pci/cs5535audio/cs5535audio.c
+++ b/sound/pci/cs5535audio/cs5535audio.c
@@ -312,7 +312,7 @@
if (request_irq(pci->irq, snd_cs5535audio_interrupt,
IRQF_SHARED, "CS5535 Audio", cs5535au)) {
- snd_printk("unable to grab IRQ %d\n", pci->irq);
+ snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
err = -EBUSY;
goto sndfail;
}
@@ -353,9 +353,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
if ((err = snd_cs5535audio_create(card, pci, &cs5535au)) < 0)
goto probefail_out;
diff --git a/sound/pci/echoaudio/Makefile b/sound/pci/echoaudio/Makefile
index 7b576ae..1361de7 100644
--- a/sound/pci/echoaudio/Makefile
+++ b/sound/pci/echoaudio/Makefile
@@ -15,6 +15,8 @@
snd-indigo-objs := indigo.o
snd-indigoio-objs := indigoio.o
snd-indigodj-objs := indigodj.o
+snd-indigoiox-objs := indigoiox.o
+snd-indigodjx-objs := indigodjx.o
obj-$(CONFIG_SND_DARLA20) += snd-darla20.o
obj-$(CONFIG_SND_GINA20) += snd-gina20.o
@@ -28,3 +30,5 @@
obj-$(CONFIG_SND_INDIGO) += snd-indigo.o
obj-$(CONFIG_SND_INDIGOIO) += snd-indigoio.o
obj-$(CONFIG_SND_INDIGODJ) += snd-indigodj.o
+obj-$(CONFIG_SND_INDIGOIOX) += snd-indigoiox.o
+obj-$(CONFIG_SND_INDIGODJX) += snd-indigodjx.o
diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c
index 417e25a..57967e5 100644
--- a/sound/pci/echoaudio/echo3g_dsp.c
+++ b/sound/pci/echoaudio/echo3g_dsp.c
@@ -56,7 +56,7 @@
}
chip->comm_page->e3g_frq_register =
- __constant_cpu_to_le32((E3G_MAGIC_NUMBER / 48000) - 2);
+ cpu_to_le32((E3G_MAGIC_NUMBER / 48000) - 2);
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
chip->bad_board = TRUE;
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 8dbc5c4..da2065c 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -950,6 +950,8 @@
Control interface
******************************************************************************/
+#ifndef ECHOCARD_HAS_VMIXER
+
/******************* PCM output volume *******************/
static int snd_echo_output_gain_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -1001,18 +1003,6 @@
return changed;
}
-#ifdef ECHOCARD_HAS_VMIXER
-/* On Vmixer cards this one controls the line-out volume */
-static struct snd_kcontrol_new snd_echo_line_output_gain __devinitdata = {
- .name = "Line Playback Volume",
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
- .info = snd_echo_output_gain_info,
- .get = snd_echo_output_gain_get,
- .put = snd_echo_output_gain_put,
- .tlv = {.p = db_scale_output_gain},
-};
-#else
static struct snd_kcontrol_new snd_echo_pcm_output_gain __devinitdata = {
.name = "PCM Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1022,6 +1012,7 @@
.put = snd_echo_output_gain_put,
.tlv = {.p = db_scale_output_gain},
};
+
#endif
@@ -1997,9 +1988,9 @@
DE_INIT(("Echoaudio driver starting...\n"));
i = 0;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
snd_card_set_dev(card, &pci->dev);
@@ -2037,8 +2028,6 @@
#ifdef ECHOCARD_HAS_VMIXER
snd_echo_vmixer.count = num_pipes_out(chip) * num_busses_out(chip);
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_output_gain, chip))) < 0)
- goto ctl_error;
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vmixer, chip))) < 0)
goto ctl_error;
#else
diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h
index 1c88e05..f9490ae 100644
--- a/sound/pci/echoaudio/echoaudio.h
+++ b/sound/pci/echoaudio/echoaudio.h
@@ -189,6 +189,9 @@
#define INDIGO 0x0090
#define INDIGO_IO 0x00a0
#define INDIGO_DJ 0x00b0
+#define DC8 0x00c0
+#define INDIGO_IOX 0x00d0
+#define INDIGO_DJX 0x00e0
#define ECHO3G 0x0100
diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c
index c3736bb..e32a748 100644
--- a/sound/pci/echoaudio/echoaudio_3g.c
+++ b/sound/pci/echoaudio/echoaudio_3g.c
@@ -40,8 +40,7 @@
if (wait_handshake(chip))
return -EIO;
- chip->comm_page->ext_box_status =
- __constant_cpu_to_le32(E3G_ASIC_NOT_LOADED);
+ chip->comm_page->ext_box_status = cpu_to_le32(E3G_ASIC_NOT_LOADED);
chip->asic_loaded = FALSE;
clear_handshake(chip);
send_vector(chip, DSP_VC_TEST_ASIC);
diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c
index be0e181..4df51ef 100644
--- a/sound/pci/echoaudio/echoaudio_dsp.c
+++ b/sound/pci/echoaudio/echoaudio_dsp.c
@@ -926,11 +926,11 @@
/* Init the comm page */
chip->comm_page->comm_size =
- __constant_cpu_to_le32(sizeof(struct comm_page));
+ cpu_to_le32(sizeof(struct comm_page));
chip->comm_page->handshake = 0xffffffff;
chip->comm_page->midi_out_free_count =
- __constant_cpu_to_le32(DSP_MIDI_OUT_FIFO_SIZE);
- chip->comm_page->sample_rate = __constant_cpu_to_le32(44100);
+ cpu_to_le32(DSP_MIDI_OUT_FIFO_SIZE);
+ chip->comm_page->sample_rate = cpu_to_le32(44100);
chip->sample_rate = 44100;
/* Set line levels so we don't blast any inputs on startup */
diff --git a/sound/pci/echoaudio/echoaudio_dsp.h b/sound/pci/echoaudio/echoaudio_dsp.h
index e352f3a..cb7d75a 100644
--- a/sound/pci/echoaudio/echoaudio_dsp.h
+++ b/sound/pci/echoaudio/echoaudio_dsp.h
@@ -576,8 +576,13 @@
#define E3G_ASIC_NOT_LOADED 0xffff
#define E3G_BOX_TYPE_MASK 0xf0
-#define EXT_3GBOX_NC 0x01
-#define EXT_3GBOX_NOT_SET 0x02
+/* Indigo express control register values */
+#define INDIGO_EXPRESS_32000 0x02
+#define INDIGO_EXPRESS_44100 0x01
+#define INDIGO_EXPRESS_48000 0x00
+#define INDIGO_EXPRESS_DOUBLE_SPEED 0x10
+#define INDIGO_EXPRESS_QUAD_SPEED 0x04
+#define INDIGO_EXPRESS_CLOCK_MASK 0x17
/*
diff --git a/sound/pci/echoaudio/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c
index db6c952..3f1e747 100644
--- a/sound/pci/echoaudio/gina20_dsp.c
+++ b/sound/pci/echoaudio/gina20_dsp.c
@@ -208,10 +208,10 @@
DE_ACT(("set_professional_spdif %d\n", prof));
if (prof)
chip->comm_page->flags |=
- __constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+ cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
else
chip->comm_page->flags &=
- ~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+ ~cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
chip->professional_spdif = prof;
return update_flags(chip);
}
diff --git a/sound/pci/echoaudio/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c
index f05e39f..0b2cd9c 100644
--- a/sound/pci/echoaudio/indigo_dsp.c
+++ b/sound/pci/echoaudio/indigo_dsp.c
@@ -63,18 +63,6 @@
if ((err = init_line_levels(chip)) < 0)
return err;
- /* Default routing of the virtual channels: all vchannels are routed
- to the stereo output */
- set_vmixer_gain(chip, 0, 0, 0);
- set_vmixer_gain(chip, 1, 1, 0);
- set_vmixer_gain(chip, 0, 2, 0);
- set_vmixer_gain(chip, 1, 3, 0);
- set_vmixer_gain(chip, 0, 4, 0);
- set_vmixer_gain(chip, 1, 5, 0);
- set_vmixer_gain(chip, 0, 6, 0);
- set_vmixer_gain(chip, 1, 7, 0);
- err = update_vmixer_level(chip);
-
DE_INIT(("init_hw done\n"));
return err;
}
diff --git a/sound/pci/echoaudio/indigo_express_dsp.c b/sound/pci/echoaudio/indigo_express_dsp.c
new file mode 100644
index 0000000..9ab625e
--- /dev/null
+++ b/sound/pci/echoaudio/indigo_express_dsp.c
@@ -0,0 +1,119 @@
+/************************************************************************
+
+This file is part of Echo Digital Audio's generic driver library.
+Copyright Echo Digital Audio Corporation (c) 1998 - 2005
+All rights reserved
+www.echoaudio.com
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+*************************************************************************/
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+ u32 clock, control_reg, old_control_reg;
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ old_control_reg = le32_to_cpu(chip->comm_page->control_register);
+ control_reg = old_control_reg & ~INDIGO_EXPRESS_CLOCK_MASK;
+
+ switch (rate) {
+ case 32000:
+ clock = INDIGO_EXPRESS_32000;
+ break;
+ case 44100:
+ clock = INDIGO_EXPRESS_44100;
+ break;
+ case 48000:
+ clock = INDIGO_EXPRESS_48000;
+ break;
+ case 64000:
+ clock = INDIGO_EXPRESS_32000|INDIGO_EXPRESS_DOUBLE_SPEED;
+ break;
+ case 88200:
+ clock = INDIGO_EXPRESS_44100|INDIGO_EXPRESS_DOUBLE_SPEED;
+ break;
+ case 96000:
+ clock = INDIGO_EXPRESS_48000|INDIGO_EXPRESS_DOUBLE_SPEED;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ control_reg |= clock;
+ if (control_reg != old_control_reg) {
+ chip->comm_page->control_register = cpu_to_le32(control_reg);
+ chip->sample_rate = rate;
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+ }
+ return 0;
+}
+
+
+
+/* This function routes the sound from a virtual channel to a real output */
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+ int gain)
+{
+ int index;
+
+ if (snd_BUG_ON(pipe >= num_pipes_out(chip) ||
+ output >= num_busses_out(chip)))
+ return -EINVAL;
+
+ if (wait_handshake(chip))
+ return -EIO;
+
+ chip->vmixer_gain[output][pipe] = gain;
+ index = output * num_pipes_out(chip) + pipe;
+ chip->comm_page->vmixer[index] = gain;
+
+ DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+ return 0;
+}
+
+
+
+/* Tell the DSP to read and update virtual mixer levels in comm page. */
+static int update_vmixer_level(struct echoaudio *chip)
+{
+ if (wait_handshake(chip))
+ return -EIO;
+ clear_handshake(chip);
+ return send_vector(chip, DSP_VC_SET_VMIXER_GAIN);
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+ return ECHO_CLOCK_BIT_INTERNAL;
+}
+
+
+
+/* The IndigoIO has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+ return 0;
+}
diff --git a/sound/pci/echoaudio/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c
index 90730a5..0839291 100644
--- a/sound/pci/echoaudio/indigodj_dsp.c
+++ b/sound/pci/echoaudio/indigodj_dsp.c
@@ -63,18 +63,6 @@
if ((err = init_line_levels(chip)) < 0)
return err;
- /* Default routing of the virtual channels: vchannels 0-3 and
- vchannels 4-7 are routed to real channels 0-4 */
- set_vmixer_gain(chip, 0, 0, 0);
- set_vmixer_gain(chip, 1, 1, 0);
- set_vmixer_gain(chip, 2, 2, 0);
- set_vmixer_gain(chip, 3, 3, 0);
- set_vmixer_gain(chip, 0, 4, 0);
- set_vmixer_gain(chip, 1, 5, 0);
- set_vmixer_gain(chip, 2, 6, 0);
- set_vmixer_gain(chip, 3, 7, 0);
- err = update_vmixer_level(chip);
-
DE_INIT(("init_hw done\n"));
return err;
}
diff --git a/sound/pci/echoaudio/indigodjx.c b/sound/pci/echoaudio/indigodjx.c
new file mode 100644
index 0000000..3482ef6
--- /dev/null
+++ b/sound/pci/echoaudio/indigodjx.c
@@ -0,0 +1,107 @@
+/*
+ * ALSA driver for Echoaudio soundcards.
+ * Copyright (C) 2009 Giuliano Pochini <pochini@shiny.it>
+ *
+ * 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; version 2 of the License.
+ *
+ * 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.
+ */
+
+#define INDIGO_FAMILY
+#define ECHOCARD_INDIGO_DJX
+#define ECHOCARD_NAME "Indigo DJx"
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_VMIXER
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT 0 /* 8 */
+#define PX_DIGITAL_OUT 8 /* 0 */
+#define PX_ANALOG_IN 8 /* 0 */
+#define PX_DIGITAL_IN 8 /* 0 */
+#define PX_NUM 8
+
+/* Bus indexes */
+#define BX_ANALOG_OUT 0 /* 4 */
+#define BX_DIGITAL_OUT 4 /* 0 */
+#define BX_ANALOG_IN 4 /* 0 */
+#define BX_DIGITAL_IN 4 /* 0 */
+#define BX_NUM 4
+
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+MODULE_FIRMWARE("ea/loader_dsp.fw");
+MODULE_FIRMWARE("ea/indigo_djx_dsp.fw");
+
+#define FW_361_LOADER 0
+#define FW_INDIGO_DJX_DSP 1
+
+static const struct firmware card_fw[] = {
+ {0, "loader_dsp.fw"},
+ {0, "indigo_djx_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+ {0x1057, 0x3410, 0xECC0, 0x00E0, 0, 0, 0}, /* Indigo DJx*/
+ {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START,
+ .formats = SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S32_BE,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 32000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 4,
+ .buffer_bytes_max = 262144,
+ .period_bytes_min = 32,
+ .period_bytes_max = 131072,
+ .periods_min = 2,
+ .periods_max = 220,
+};
+
+#include "indigodjx_dsp.c"
+#include "indigo_express_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
diff --git a/sound/pci/echoaudio/indigodjx_dsp.c b/sound/pci/echoaudio/indigodjx_dsp.c
new file mode 100644
index 0000000..f591fc2
--- /dev/null
+++ b/sound/pci/echoaudio/indigodjx_dsp.c
@@ -0,0 +1,68 @@
+/************************************************************************
+
+This file is part of Echo Digital Audio's generic driver library.
+Copyright Echo Digital Audio Corporation (c) 1998 - 2005
+All rights reserved
+www.echoaudio.com
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+*************************************************************************/
+
+static int update_vmixer_level(struct echoaudio *chip);
+static int set_vmixer_gain(struct echoaudio *chip, u16 output,
+ u16 pipe, int gain);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+ int err;
+
+ DE_INIT(("init_hw() - Indigo DJx\n"));
+ if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_DJX))
+ return -ENODEV;
+
+ err = init_dsp_comm_page(chip);
+ if (err < 0) {
+ DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ return err;
+ }
+
+ chip->device_id = device_id;
+ chip->subdevice_id = subdevice_id;
+ chip->bad_board = TRUE;
+ chip->dsp_code_to_load = &card_fw[FW_INDIGO_DJX_DSP];
+ /* Since this card has no ASIC, mark it as loaded so everything
+ works OK */
+ chip->asic_loaded = TRUE;
+ chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
+
+ err = load_firmware(chip);
+ if (err < 0)
+ return err;
+ chip->bad_board = FALSE;
+
+ err = init_line_levels(chip);
+ if (err < 0)
+ return err;
+
+ DE_INIT(("init_hw done\n"));
+ return err;
+}
diff --git a/sound/pci/echoaudio/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c
index a7e09ec..0604c8a 100644
--- a/sound/pci/echoaudio/indigoio_dsp.c
+++ b/sound/pci/echoaudio/indigoio_dsp.c
@@ -63,18 +63,6 @@
if ((err = init_line_levels(chip)) < 0)
return err;
- /* Default routing of the virtual channels: all vchannels are routed
- to the stereo output */
- set_vmixer_gain(chip, 0, 0, 0);
- set_vmixer_gain(chip, 1, 1, 0);
- set_vmixer_gain(chip, 0, 2, 0);
- set_vmixer_gain(chip, 1, 3, 0);
- set_vmixer_gain(chip, 0, 4, 0);
- set_vmixer_gain(chip, 1, 5, 0);
- set_vmixer_gain(chip, 0, 6, 0);
- set_vmixer_gain(chip, 1, 7, 0);
- err = update_vmixer_level(chip);
-
DE_INIT(("init_hw done\n"));
return err;
}
diff --git a/sound/pci/echoaudio/indigoiox.c b/sound/pci/echoaudio/indigoiox.c
new file mode 100644
index 0000000..aebee27
--- /dev/null
+++ b/sound/pci/echoaudio/indigoiox.c
@@ -0,0 +1,109 @@
+/*
+ * ALSA driver for Echoaudio soundcards.
+ * Copyright (C) 2009 Giuliano Pochini <pochini@shiny.it>
+ *
+ * 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; version 2 of the License.
+ *
+ * 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.
+ */
+
+#define INDIGO_FAMILY
+#define ECHOCARD_INDIGO_IOX
+#define ECHOCARD_NAME "Indigo IOx"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_VMIXER
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT 0 /* 8 */
+#define PX_DIGITAL_OUT 8 /* 0 */
+#define PX_ANALOG_IN 8 /* 2 */
+#define PX_DIGITAL_IN 10 /* 0 */
+#define PX_NUM 10
+
+/* Bus indexes */
+#define BX_ANALOG_OUT 0 /* 2 */
+#define BX_DIGITAL_OUT 2 /* 0 */
+#define BX_ANALOG_IN 2 /* 2 */
+#define BX_DIGITAL_IN 4 /* 0 */
+#define BX_NUM 4
+
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+MODULE_FIRMWARE("ea/loader_dsp.fw");
+MODULE_FIRMWARE("ea/indigo_iox_dsp.fw");
+
+#define FW_361_LOADER 0
+#define FW_INDIGO_IOX_DSP 1
+
+static const struct firmware card_fw[] = {
+ {0, "loader_dsp.fw"},
+ {0, "indigo_iox_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+ {0x1057, 0x3410, 0xECC0, 0x00D0, 0, 0, 0}, /* Indigo IOx */
+ {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START,
+ .formats = SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S32_BE,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 32000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 8,
+ .buffer_bytes_max = 262144,
+ .period_bytes_min = 32,
+ .period_bytes_max = 131072,
+ .periods_min = 2,
+ .periods_max = 220,
+};
+
+#include "indigoiox_dsp.c"
+#include "indigo_express_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
+
diff --git a/sound/pci/echoaudio/indigoiox_dsp.c b/sound/pci/echoaudio/indigoiox_dsp.c
new file mode 100644
index 0000000..f357521
--- /dev/null
+++ b/sound/pci/echoaudio/indigoiox_dsp.c
@@ -0,0 +1,68 @@
+/************************************************************************
+
+This file is part of Echo Digital Audio's generic driver library.
+Copyright Echo Digital Audio Corporation (c) 1998 - 2005
+All rights reserved
+www.echoaudio.com
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+*************************************************************************/
+
+static int update_vmixer_level(struct echoaudio *chip);
+static int set_vmixer_gain(struct echoaudio *chip, u16 output,
+ u16 pipe, int gain);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+ int err;
+
+ DE_INIT(("init_hw() - Indigo IOx\n"));
+ if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_IOX))
+ return -ENODEV;
+
+ err = init_dsp_comm_page(chip);
+ if (err < 0) {
+ DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ return err;
+ }
+
+ chip->device_id = device_id;
+ chip->subdevice_id = subdevice_id;
+ chip->bad_board = TRUE;
+ chip->dsp_code_to_load = &card_fw[FW_INDIGO_IOX_DSP];
+ /* Since this card has no ASIC, mark it as loaded so everything
+ works OK */
+ chip->asic_loaded = TRUE;
+ chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
+
+ err = load_firmware(chip);
+ if (err < 0)
+ return err;
+ chip->bad_board = FALSE;
+
+ err = init_line_levels(chip);
+ if (err < 0)
+ return err;
+
+ DE_INIT(("init_hw done\n"));
+ return err;
+}
diff --git a/sound/pci/echoaudio/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c
index ede75c6..83750e9 100644
--- a/sound/pci/echoaudio/layla20_dsp.c
+++ b/sound/pci/echoaudio/layla20_dsp.c
@@ -284,10 +284,10 @@
DE_ACT(("set_professional_spdif %d\n", prof));
if (prof)
chip->comm_page->flags |=
- __constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+ cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
else
chip->comm_page->flags &=
- ~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+ ~cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
chip->professional_spdif = prof;
return update_flags(chip);
}
diff --git a/sound/pci/echoaudio/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c
index 2273866..5514051 100644
--- a/sound/pci/echoaudio/mia_dsp.c
+++ b/sound/pci/echoaudio/mia_dsp.c
@@ -69,18 +69,6 @@
if ((err = init_line_levels(chip)))
return err;
- /* Default routing of the virtual channels: vchannels 0-3 go to analog
- outputs and vchannels 4-7 go to S/PDIF outputs */
- set_vmixer_gain(chip, 0, 0, 0);
- set_vmixer_gain(chip, 1, 1, 0);
- set_vmixer_gain(chip, 0, 2, 0);
- set_vmixer_gain(chip, 1, 3, 0);
- set_vmixer_gain(chip, 2, 4, 0);
- set_vmixer_gain(chip, 3, 5, 0);
- set_vmixer_gain(chip, 2, 6, 0);
- set_vmixer_gain(chip, 3, 7, 0);
- err = update_vmixer_level(chip);
-
DE_INIT(("init_hw done\n"));
return err;
}
@@ -222,10 +210,10 @@
DE_ACT(("set_professional_spdif %d\n", prof));
if (prof)
chip->comm_page->flags |=
- __constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+ cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
else
chip->comm_page->flags &=
- ~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+ ~cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
chip->professional_spdif = prof;
return update_flags(chip);
}
diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c
index 77bf2a8..a953d14 100644
--- a/sound/pci/echoaudio/midi.c
+++ b/sound/pci/echoaudio/midi.c
@@ -44,10 +44,10 @@
if (enable) {
chip->mtc_state = MIDI_IN_STATE_NORMAL;
chip->comm_page->flags |=
- __constant_cpu_to_le32(DSP_FLAG_MIDI_INPUT);
+ cpu_to_le32(DSP_FLAG_MIDI_INPUT);
} else
chip->comm_page->flags &=
- ~__constant_cpu_to_le32(DSP_FLAG_MIDI_INPUT);
+ ~cpu_to_le32(DSP_FLAG_MIDI_INPUT);
clear_handshake(chip);
return send_vector(chip, DSP_VC_UPDATE_FLAGS);
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index 8354c1a..c7f3b99 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -114,9 +114,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
if (max_buffer_size[dev] < 32)
max_buffer_size[dev] = 32;
else if (max_buffer_size[dev] > 1024)
diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c
index 0e649dc..7ef949d9 100644
--- a/sound/pci/emu10k1/emu10k1_callback.c
+++ b/sound/pci/emu10k1/emu10k1_callback.c
@@ -103,7 +103,10 @@
int ch;
vp = &emu->voices[best[i].voice];
if ((ch = vp->ch) < 0) {
- //printk("synth_get_voice: ch < 0 (%d) ??", i);
+ /*
+ printk(KERN_WARNING
+ "synth_get_voice: ch < 0 (%d) ??", i);
+ */
continue;
}
vp->emu->num_voices--;
@@ -335,7 +338,7 @@
return -EINVAL;
emem->map_locked++;
if (snd_emu10k1_memblk_map(hw, emem) < 0) {
- // printk("emu: cannot map!\n");
+ /* printk(KERN_ERR "emu: cannot map!\n"); */
return -ENOMEM;
}
mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1;
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index 101a1c1..f18bd62 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -711,8 +711,7 @@
static int emu1010_firmware_thread(void *data)
{
struct snd_emu10k1 *emu = data;
- int tmp, tmp2;
- int reg;
+ u32 tmp, tmp2, reg;
int err;
for (;;) {
@@ -758,7 +757,8 @@
snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n");
snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp);
snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2);
- snd_printk("Audio Dock ver:%d.%d\n", tmp, tmp2);
+ snd_printk(KERN_INFO "Audio Dock ver: %u.%u\n",
+ tmp, tmp2);
/* Sync clocking between 1010 and Dock */
/* Allow DLL to settle */
msleep(10);
@@ -804,8 +804,7 @@
static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
{
unsigned int i;
- int tmp, tmp2;
- int reg;
+ u32 tmp, tmp2, reg;
int err;
const char *filename = NULL;
@@ -887,7 +886,7 @@
snd_printk(KERN_INFO "emu1010: Hana Firmware loaded\n");
snd_emu1010_fpga_read(emu, EMU_HANA_MAJOR_REV, &tmp);
snd_emu1010_fpga_read(emu, EMU_HANA_MINOR_REV, &tmp2);
- snd_printk("emu1010: Hana version: %d.%d\n", tmp, tmp2);
+ snd_printk(KERN_INFO "emu1010: Hana version: %u.%u\n", tmp, tmp2);
/* Enable 48Volt power to Audio Dock */
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, EMU_HANA_DOCK_PWR_ON);
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 5ff4dbb..31542ad 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -1544,9 +1544,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
if ((err = snd_emu10k1x_create(card, pci, &chip)) < 0) {
snd_card_free(card);
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index 7dba08f..191e1cd 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -1519,7 +1519,7 @@
/* A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); */
if (emu->card_capabilities->emu_model) {
/* EMU1010 Outputs from PCM Front, Rear, Center, LFE, Side */
- snd_printk("EMU outputs on\n");
+ snd_printk(KERN_INFO "EMU outputs on\n");
for (z = 0; z < 8; z++) {
if (emu->card_capabilities->ca0108_chip) {
A_OP(icode, &ptr, iACC3, A3_EMU32OUT(z), A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_C_00000000, A_C_00000000);
@@ -1567,7 +1567,7 @@
if (emu->card_capabilities->emu_model) {
if (emu->card_capabilities->ca0108_chip) {
- snd_printk("EMU2 inputs on\n");
+ snd_printk(KERN_INFO "EMU2 inputs on\n");
for (z = 0; z < 0x10; z++) {
snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp,
bit_shifter16,
@@ -1575,10 +1575,13 @@
A_FXBUS2(z*2) );
}
} else {
- snd_printk("EMU inputs on\n");
+ snd_printk(KERN_INFO "EMU inputs on\n");
/* Capture 16 (originally 8) channels of S32_LE sound */
- /* printk("emufx.c: gpr=0x%x, tmp=0x%x\n",gpr, tmp); */
+ /*
+ printk(KERN_DEBUG "emufx.c: gpr=0x%x, tmp=0x%x\n",
+ gpr, tmp);
+ */
/* For the EMU1010: How to get 32bit values from the DSP. High 16bits into L, low 16bits into R. */
/* A_P16VIN(0) is delayed by one sample,
* so all other A_P16VIN channels will need to also be delayed
diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
index cf9276d..78f62fd 100644
--- a/sound/pci/emu10k1/emupcm.c
+++ b/sound/pci/emu10k1/emupcm.c
@@ -44,7 +44,7 @@
if (epcm->substream == NULL)
return;
#if 0
- printk("IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
+ printk(KERN_DEBUG "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
epcm->substream->runtime->hw->pointer(emu, epcm->substream),
snd_pcm_lib_period_bytes(epcm->substream),
snd_pcm_lib_buffer_bytes(epcm->substream));
@@ -146,7 +146,11 @@
1,
&epcm->extra);
if (err < 0) {
- /* printk("pcm_channel_alloc: failed extra: voices=%d, frame=%d\n", voices, frame); */
+ /*
+ printk(KERN_DEBUG "pcm_channel_alloc: "
+ "failed extra: voices=%d, frame=%d\n",
+ voices, frame);
+ */
for (i = 0; i < voices; i++) {
snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
epcm->voices[i] = NULL;
@@ -737,7 +741,10 @@
struct snd_emu10k1_pcm_mixer *mix;
int result = 0;
- /* printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream)); */
+ /*
+ printk(KERN_DEBUG "trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n",
+ (int)emu, cmd, substream->ops->pointer(substream))
+ */
spin_lock(&emu->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -786,7 +793,10 @@
/* hmm this should cause full and half full interrupt to be raised? */
outl(epcm->capture_ipr, emu->port + IPR);
snd_emu10k1_intr_enable(emu, epcm->capture_inte);
- /* printk("adccr = 0x%x, adcbs = 0x%x\n", epcm->adccr, epcm->adcbs); */
+ /*
+ printk(KERN_DEBUG "adccr = 0x%x, adcbs = 0x%x\n",
+ epcm->adccr, epcm->adcbs);
+ */
switch (epcm->type) {
case CAPTURE_AC97ADC:
snd_emu10k1_ptr_write(emu, ADCCR, 0, epcm->capture_cr_val);
@@ -857,7 +867,11 @@
ptr -= runtime->buffer_size;
}
#endif
- /* printk("ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x\n", ptr, runtime->buffer_size, runtime->period_size); */
+ /*
+ printk(KERN_DEBUG
+ "ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x\n",
+ ptr, runtime->buffer_size, runtime->period_size);
+ */
return ptr;
}
@@ -1546,7 +1560,11 @@
unsigned int count,
unsigned int tram_shift)
{
- /* printk("tram_poke1: dst_left = 0x%p, dst_right = 0x%p, src = 0x%p, count = 0x%x\n", dst_left, dst_right, src, count); */
+ /*
+ printk(KERN_DEBUG "tram_poke1: dst_left = 0x%p, dst_right = 0x%p, "
+ "src = 0x%p, count = 0x%x\n",
+ dst_left, dst_right, src, count);
+ */
if ((tram_shift & 1) == 0) {
while (count--) {
*dst_left-- = *src++;
@@ -1623,7 +1641,12 @@
struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
unsigned int i;
- /* printk("prepare: etram_pages = 0x%p, dma_area = 0x%x, buffer_size = 0x%x (0x%x)\n", emu->fx8010.etram_pages, runtime->dma_area, runtime->buffer_size, runtime->buffer_size << 2); */
+ /*
+ printk(KERN_DEBUG "prepare: etram_pages = 0x%p, dma_area = 0x%x, "
+ "buffer_size = 0x%x (0x%x)\n",
+ emu->fx8010.etram_pages, runtime->dma_area,
+ runtime->buffer_size, runtime->buffer_size << 2);
+ */
memset(&pcm->pcm_rec, 0, sizeof(pcm->pcm_rec));
pcm->pcm_rec.hw_buffer_size = pcm->buffer_size * 2; /* byte size */
pcm->pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c
index b5a802b..4bfc31d 100644
--- a/sound/pci/emu10k1/io.c
+++ b/sound/pci/emu10k1/io.c
@@ -226,7 +226,9 @@
break;
if (timeout > 1000) {
- snd_printk("emu10k1:I2C:timeout status=0x%x\n", status);
+ snd_printk(KERN_WARNING
+ "emu10k1:I2C:timeout status=0x%x\n",
+ status);
break;
}
}
diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c
index 749a21b..e617aca 100644
--- a/sound/pci/emu10k1/p16v.c
+++ b/sound/pci/emu10k1/p16v.c
@@ -168,7 +168,7 @@
struct snd_emu10k1_pcm *epcm = runtime->private_data;
if (epcm) {
- //snd_printk("epcm free: %p\n", epcm);
+ /* snd_printk(KERN_DEBUG "epcm free: %p\n", epcm); */
kfree(epcm);
}
}
@@ -183,14 +183,16 @@
int err;
epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
- //snd_printk("epcm kcalloc: %p\n", epcm);
+ /* snd_printk(KERN_DEBUG "epcm kcalloc: %p\n", epcm); */
if (epcm == NULL)
return -ENOMEM;
epcm->emu = emu;
epcm->substream = substream;
- //snd_printk("epcm device=%d, channel_id=%d\n", substream->pcm->device, channel_id);
-
+ /*
+ snd_printk(KERN_DEBUG "epcm device=%d, channel_id=%d\n",
+ substream->pcm->device, channel_id);
+ */
runtime->private_data = epcm;
runtime->private_free = snd_p16v_pcm_free_substream;
@@ -200,10 +202,15 @@
channel->number = channel_id;
channel->use=1;
- //snd_printk("p16v: open channel_id=%d, channel=%p, use=0x%x\n", channel_id, channel, channel->use);
- //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
- //channel->interrupt = snd_p16v_pcm_channel_interrupt;
- channel->epcm=epcm;
+#if 0 /* debug */
+ snd_printk(KERN_DEBUG
+ "p16v: open channel_id=%d, channel=%p, use=0x%x\n",
+ channel_id, channel, channel->use);
+ printk(KERN_DEBUG "open:channel_id=%d, chip=%p, channel=%p\n",
+ channel_id, chip, channel);
+#endif /* debug */
+ /* channel->interrupt = snd_p16v_pcm_channel_interrupt; */
+ channel->epcm = epcm;
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
return err;
@@ -224,14 +231,16 @@
int err;
epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
- //snd_printk("epcm kcalloc: %p\n", epcm);
+ /* snd_printk(KERN_DEBUG "epcm kcalloc: %p\n", epcm); */
if (epcm == NULL)
return -ENOMEM;
epcm->emu = emu;
epcm->substream = substream;
- //snd_printk("epcm device=%d, channel_id=%d\n", substream->pcm->device, channel_id);
-
+ /*
+ snd_printk(KERN_DEBUG "epcm device=%d, channel_id=%d\n",
+ substream->pcm->device, channel_id);
+ */
runtime->private_data = epcm;
runtime->private_free = snd_p16v_pcm_free_substream;
@@ -241,10 +250,15 @@
channel->number = channel_id;
channel->use=1;
- //snd_printk("p16v: open channel_id=%d, channel=%p, use=0x%x\n", channel_id, channel, channel->use);
- //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
- //channel->interrupt = snd_p16v_pcm_channel_interrupt;
- channel->epcm=epcm;
+#if 0 /* debug */
+ snd_printk(KERN_DEBUG
+ "p16v: open channel_id=%d, channel=%p, use=0x%x\n",
+ channel_id, channel, channel->use);
+ printk(KERN_DEBUG "open:channel_id=%d, chip=%p, channel=%p\n",
+ channel_id, chip, channel);
+#endif /* debug */
+ /* channel->interrupt = snd_p16v_pcm_channel_interrupt; */
+ channel->epcm = epcm;
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
return err;
@@ -334,9 +348,19 @@
int i;
u32 tmp;
- //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1));
- //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base);
- //snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->p16v_buffer.addr, emu->p16v_buffer.area, emu->p16v_buffer.bytes);
+#if 0 /* debug */
+ snd_printk(KERN_DEBUG "prepare:channel_number=%d, rate=%d, "
+ "format=0x%x, channels=%d, buffer_size=%ld, "
+ "period_size=%ld, periods=%u, frames_to_bytes=%d\n",
+ channel, runtime->rate, runtime->format, runtime->channels,
+ runtime->buffer_size, runtime->period_size,
+ runtime->periods, frames_to_bytes(runtime, 1));
+ snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, table_base=%p\n",
+ runtime->dma_addr, runtime->dma_area, table_base);
+ snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
+ emu->p16v_buffer.addr, emu->p16v_buffer.area,
+ emu->p16v_buffer.bytes);
+#endif /* debug */
tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
switch (runtime->rate) {
case 44100:
@@ -379,7 +403,15 @@
struct snd_pcm_runtime *runtime = substream->runtime;
int channel = substream->pcm->device - emu->p16v_device_offset;
u32 tmp;
- //printk("prepare capture:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, frames_to_bytes(runtime, 1));
+
+ /*
+ printk(KERN_DEBUG "prepare capture:channel_number=%d, rate=%d, "
+ "format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, "
+ "frames_to_bytes=%d\n",
+ channel, runtime->rate, runtime->format, runtime->channels,
+ runtime->buffer_size, runtime->period_size,
+ frames_to_bytes(runtime, 1));
+ */
tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
switch (runtime->rate) {
case 44100:
@@ -459,13 +491,13 @@
runtime = s->runtime;
epcm = runtime->private_data;
channel = substream->pcm->device-emu->p16v_device_offset;
- //snd_printk("p16v channel=%d\n",channel);
+ /* snd_printk(KERN_DEBUG "p16v channel=%d\n", channel); */
epcm->running = running;
basic |= (0x1<<channel);
inte |= (INTE2_PLAYBACK_CH_0_LOOP<<channel);
snd_pcm_trigger_done(s, substream);
}
- //snd_printk("basic=0x%x, inte=0x%x\n",basic, inte);
+ /* snd_printk(KERN_DEBUG "basic=0x%x, inte=0x%x\n", basic, inte); */
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -558,8 +590,13 @@
ptr -= runtime->buffer_size;
printk(KERN_WARNING "buffer capture limited!\n");
}
- //printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate);
-
+ /*
+ printk(KERN_DEBUG "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
+ "buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n",
+ ptr1, ptr2, ptr, (int)runtime->buffer_size,
+ (int)runtime->period_size, (int)runtime->frame_bits,
+ (int)runtime->rate);
+ */
return ptr;
}
@@ -592,7 +629,10 @@
// release the data
if (chip->p16v_buffer.area) {
snd_dma_free_pages(&chip->p16v_buffer);
- //snd_printk("period lables free: %p\n", &chip->p16v_buffer);
+ /*
+ snd_printk(KERN_DEBUG "period lables free: %p\n",
+ &chip->p16v_buffer);
+ */
}
return 0;
}
@@ -604,7 +644,7 @@
int err;
int capture=1;
- //snd_printk("snd_p16v_pcm called. device=%d\n", device);
+ /* snd_printk("KERN_DEBUG snd_p16v_pcm called. device=%d\n", device); */
emu->p16v_device_offset = device;
if (rpcm)
*rpcm = NULL;
@@ -631,7 +671,10 @@
snd_dma_pci_data(emu->pci),
((65536 - 64) * 8), ((65536 - 64) * 8))) < 0)
return err;
- //snd_printk("preallocate playback substream: err=%d\n", err);
+ /*
+ snd_printk(KERN_DEBUG
+ "preallocate playback substream: err=%d\n", err);
+ */
}
for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
@@ -642,7 +685,10 @@
snd_dma_pci_data(emu->pci),
65536 - 64, 65536 - 64)) < 0)
return err;
- //snd_printk("preallocate capture substream: err=%d\n", err);
+ /*
+ snd_printk(KERN_DEBUG
+ "preallocate capture substream: err=%d\n", err);
+ */
}
if (rpcm)
diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c
index d7300a1..20b8da2 100644
--- a/sound/pci/emu10k1/voice.c
+++ b/sound/pci/emu10k1/voice.c
@@ -53,7 +53,10 @@
*rvoice = NULL;
first_voice = last_voice = 0;
for (i = emu->next_free_voice, j = 0; j < NUM_G ; i += number, j += number) {
- // printk("i %d j %d next free %d!\n", i, j, emu->next_free_voice);
+ /*
+ printk(KERN_DEBUG "i %d j %d next free %d!\n",
+ i, j, emu->next_free_voice);
+ */
i %= NUM_G;
/* stereo voices must be even/odd */
@@ -71,7 +74,7 @@
}
}
if (!skip) {
- // printk("allocated voice %d\n", i);
+ /* printk(KERN_DEBUG "allocated voice %d\n", i); */
first_voice = i;
last_voice = (i + number) % NUM_G;
emu->next_free_voice = last_voice;
@@ -84,7 +87,10 @@
for (i = 0; i < number; i++) {
voice = &emu->voices[(first_voice + i) % NUM_G];
- // printk("voice alloc - %i, %i of %i\n", voice->number, idx-first_voice+1, number);
+ /*
+ printk(kERN_DEBUG "voice alloc - %i, %i of %i\n",
+ voice->number, idx-first_voice+1, number);
+ */
voice->use = 1;
switch (type) {
case EMU10K1_PCM:
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index 9bf9536..18f4d1e 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -584,7 +584,8 @@
unsigned long end_time = jiffies + HZ / 10;
#if 0
- printk("CODEC WRITE: reg = 0x%x, val = 0x%x (0x%x), creg = 0x%x\n",
+ printk(KERN_DEBUG
+ "CODEC WRITE: reg = 0x%x, val = 0x%x (0x%x), creg = 0x%x\n",
reg, val, ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC));
#endif
do {
@@ -2409,9 +2410,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
if ((err = snd_ensoniq_create(card, pci, &ensoniq)) < 0) {
snd_card_free(card);
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index 4cd9a1fa..dd63b13 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -1673,18 +1673,22 @@
status = inb(SLIO_REG(chip, IRQCONTROL));
#if 0
- printk("Es1938debug - interrupt status: =0x%x\n", status);
+ printk(KERN_DEBUG "Es1938debug - interrupt status: =0x%x\n", status);
#endif
/* AUDIO 1 */
if (status & 0x10) {
#if 0
- printk("Es1938debug - AUDIO channel 1 interrupt\n");
- printk("Es1938debug - AUDIO channel 1 DMAC DMA count: %u\n",
+ printk(KERN_DEBUG
+ "Es1938debug - AUDIO channel 1 interrupt\n");
+ printk(KERN_DEBUG
+ "Es1938debug - AUDIO channel 1 DMAC DMA count: %u\n",
inw(SLDM_REG(chip, DMACOUNT)));
- printk("Es1938debug - AUDIO channel 1 DMAC DMA base: %u\n",
+ printk(KERN_DEBUG
+ "Es1938debug - AUDIO channel 1 DMAC DMA base: %u\n",
inl(SLDM_REG(chip, DMAADDR)));
- printk("Es1938debug - AUDIO channel 1 DMAC DMA status: 0x%x\n",
+ printk(KERN_DEBUG
+ "Es1938debug - AUDIO channel 1 DMAC DMA status: 0x%x\n",
inl(SLDM_REG(chip, DMASTATUS)));
#endif
/* clear irq */
@@ -1699,10 +1703,13 @@
/* AUDIO 2 */
if (status & 0x20) {
#if 0
- printk("Es1938debug - AUDIO channel 2 interrupt\n");
- printk("Es1938debug - AUDIO channel 2 DMAC DMA count: %u\n",
+ printk(KERN_DEBUG
+ "Es1938debug - AUDIO channel 2 interrupt\n");
+ printk(KERN_DEBUG
+ "Es1938debug - AUDIO channel 2 DMAC DMA count: %u\n",
inw(SLIO_REG(chip, AUDIO2DMACOUNT)));
- printk("Es1938debug - AUDIO channel 2 DMAC DMA base: %u\n",
+ printk(KERN_DEBUG
+ "Es1938debug - AUDIO channel 2 DMAC DMA base: %u\n",
inl(SLIO_REG(chip, AUDIO2DMAADDR)));
#endif
@@ -1799,9 +1806,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
for (idx = 0; idx < 5; idx++) {
if (pci_resource_start(pci, idx) == 0 ||
!(pci_resource_flags(pci, idx) & IORESOURCE_IO)) {
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index e9c3794..dc97e81 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -2645,9 +2645,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (!card)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
if (total_bufsize[dev] < 128)
total_bufsize[dev] = 128;
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index c129f9e..60cdb9e 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -1468,9 +1468,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
if ((err = snd_fm801_create(card, pci, tea575x_tuner[dev], &chip)) < 0) {
snd_card_free(card);
return err;
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
index 960fd79..4de5bac 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/pci/hda/hda_beep.c
@@ -138,6 +138,7 @@
input_unregister_device(beep->dev);
kfree(beep);
+ codec->beep = NULL;
}
}
EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h
index b9679f0..51bf6a5 100644
--- a/sound/pci/hda/hda_beep.h
+++ b/sound/pci/hda/hda_beep.h
@@ -39,7 +39,7 @@
int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
void snd_hda_detach_beep_device(struct hda_codec *codec);
#else
-#define snd_hda_attach_beep_device(...)
+#define snd_hda_attach_beep_device(...) 0
#define snd_hda_detach_beep_device(...)
#endif
#endif
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index d03f992..a4e5e59 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -647,9 +647,9 @@
total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
for (i = 0; i < total_nodes; i++, nid++) {
- unsigned int func;
- func = snd_hda_param_read(codec, nid, AC_PAR_FUNCTION_TYPE);
- switch (func & 0xff) {
+ codec->function_id = snd_hda_param_read(codec, nid,
+ AC_PAR_FUNCTION_TYPE) & 0xff;
+ switch (codec->function_id) {
case AC_GRP_AUDIO_FUNCTION:
codec->afg = nid;
break;
@@ -682,11 +682,140 @@
return 0;
}
+/* read all pin default configurations and save codec->init_pins */
+static int read_pin_defaults(struct hda_codec *codec)
+{
+ int i;
+ hda_nid_t nid = codec->start_nid;
+
+ for (i = 0; i < codec->num_nodes; i++, nid++) {
+ struct hda_pincfg *pin;
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>
+ AC_WCAP_TYPE_SHIFT;
+ if (wid_type != AC_WID_PIN)
+ continue;
+ pin = snd_array_new(&codec->init_pins);
+ if (!pin)
+ return -ENOMEM;
+ pin->nid = nid;
+ pin->cfg = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONFIG_DEFAULT, 0);
+ }
+ return 0;
+}
+
+/* look up the given pin config list and return the item matching with NID */
+static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec,
+ struct snd_array *array,
+ hda_nid_t nid)
+{
+ int i;
+ for (i = 0; i < array->used; i++) {
+ struct hda_pincfg *pin = snd_array_elem(array, i);
+ if (pin->nid == nid)
+ return pin;
+ }
+ return NULL;
+}
+
+/* write a config value for the given NID */
+static void set_pincfg(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int cfg)
+{
+ int i;
+ for (i = 0; i < 4; i++) {
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i,
+ cfg & 0xff);
+ cfg >>= 8;
+ }
+}
+
+/* set the current pin config value for the given NID.
+ * the value is cached, and read via snd_hda_codec_get_pincfg()
+ */
+int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
+ hda_nid_t nid, unsigned int cfg)
+{
+ struct hda_pincfg *pin;
+ unsigned int oldcfg;
+
+ oldcfg = snd_hda_codec_get_pincfg(codec, nid);
+ pin = look_up_pincfg(codec, list, nid);
+ if (!pin) {
+ pin = snd_array_new(list);
+ if (!pin)
+ return -ENOMEM;
+ pin->nid = nid;
+ }
+ pin->cfg = cfg;
+
+ /* change only when needed; e.g. if the pincfg is already present
+ * in user_pins[], don't write it
+ */
+ cfg = snd_hda_codec_get_pincfg(codec, nid);
+ if (oldcfg != cfg)
+ set_pincfg(codec, nid, cfg);
+ return 0;
+}
+
+int snd_hda_codec_set_pincfg(struct hda_codec *codec,
+ hda_nid_t nid, unsigned int cfg)
+{
+ return snd_hda_add_pincfg(codec, &codec->driver_pins, nid, cfg);
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg);
+
+/* get the current pin config value of the given pin NID */
+unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_pincfg *pin;
+
+#ifdef CONFIG_SND_HDA_HWDEP
+ pin = look_up_pincfg(codec, &codec->user_pins, nid);
+ if (pin)
+ return pin->cfg;
+#endif
+ pin = look_up_pincfg(codec, &codec->driver_pins, nid);
+ if (pin)
+ return pin->cfg;
+ pin = look_up_pincfg(codec, &codec->init_pins, nid);
+ if (pin)
+ return pin->cfg;
+ return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg);
+
+/* restore all current pin configs */
+static void restore_pincfgs(struct hda_codec *codec)
+{
+ int i;
+ for (i = 0; i < codec->init_pins.used; i++) {
+ struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+ set_pincfg(codec, pin->nid,
+ snd_hda_codec_get_pincfg(codec, pin->nid));
+ }
+}
static void init_hda_cache(struct hda_cache_rec *cache,
unsigned int record_size);
static void free_hda_cache(struct hda_cache_rec *cache);
+/* restore the initial pin cfgs and release all pincfg lists */
+static void restore_init_pincfgs(struct hda_codec *codec)
+{
+ /* first free driver_pins and user_pins, then call restore_pincfg
+ * so that only the values in init_pins are restored
+ */
+ snd_array_free(&codec->driver_pins);
+#ifdef CONFIG_SND_HDA_HWDEP
+ snd_array_free(&codec->user_pins);
+#endif
+ restore_pincfgs(codec);
+ snd_array_free(&codec->init_pins);
+}
+
/*
* codec destructor
*/
@@ -694,6 +823,7 @@
{
if (!codec)
return;
+ restore_init_pincfgs(codec);
#ifdef CONFIG_SND_HDA_POWER_SAVE
cancel_delayed_work(&codec->power_work);
flush_workqueue(codec->bus->workq);
@@ -712,6 +842,9 @@
kfree(codec);
}
+static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+ unsigned int power_state);
+
/**
* snd_hda_codec_new - create a HDA codec
* @bus: the bus to assign
@@ -751,6 +884,8 @@
init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32);
+ snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
+ snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
if (codec->bus->modelname) {
codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
if (!codec->modelname) {
@@ -787,15 +922,18 @@
setup_fg_nodes(codec);
if (!codec->afg && !codec->mfg) {
snd_printdd("hda_codec: no AFG or MFG node found\n");
- snd_hda_codec_free(codec);
- return -ENODEV;
+ err = -ENODEV;
+ goto error;
}
- if (read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg) < 0) {
+ err = read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg);
+ if (err < 0) {
snd_printk(KERN_ERR "hda_codec: cannot malloc\n");
- snd_hda_codec_free(codec);
- return -ENOMEM;
+ goto error;
}
+ err = read_pin_defaults(codec);
+ if (err < 0)
+ goto error;
if (!codec->subsystem_id) {
hda_nid_t nid = codec->afg ? codec->afg : codec->mfg;
@@ -806,12 +944,15 @@
if (bus->modelname)
codec->modelname = kstrdup(bus->modelname, GFP_KERNEL);
+ /* power-up all before initialization */
+ hda_set_power_state(codec,
+ codec->afg ? codec->afg : codec->mfg,
+ AC_PWRST_D0);
+
if (do_init) {
err = snd_hda_codec_configure(codec);
- if (err < 0) {
- snd_hda_codec_free(codec);
- return err;
- }
+ if (err < 0)
+ goto error;
}
snd_hda_codec_proc_new(codec);
@@ -824,6 +965,10 @@
if (codecp)
*codecp = codec;
return 0;
+
+ error:
+ snd_hda_codec_free(codec);
+ return err;
}
EXPORT_SYMBOL_HDA(snd_hda_codec_new);
@@ -907,6 +1052,7 @@
/* FIXME: more better hash key? */
#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
+#define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
#define INFO_AMP_CAPS (1<<0)
#define INFO_AMP_VOL(ch) (1 << (1 + (ch)))
@@ -997,6 +1143,21 @@
}
EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
+u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_amp_info *info;
+
+ info = get_alloc_amp_hash(codec, HDA_HASH_PINCAP_KEY(nid));
+ if (!info)
+ return 0;
+ if (!info->head.val) {
+ info->amp_caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+ info->head.val |= INFO_AMP_CAPS;
+ }
+ return info->amp_caps;
+}
+EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
+
/*
* read the current volume to info
* if the cache exists, read the cache value.
@@ -1120,6 +1281,7 @@
u16 nid = get_amp_nid(kcontrol);
u8 chs = get_amp_channels(kcontrol);
int dir = get_amp_direction(kcontrol);
+ unsigned int ofs = get_amp_offset(kcontrol);
u32 caps;
caps = query_amp_caps(codec, nid, dir);
@@ -1131,6 +1293,8 @@
kcontrol->id.name);
return -EINVAL;
}
+ if (ofs < caps)
+ caps -= ofs;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = chs == 3 ? 2 : 1;
uinfo->value.integer.min = 0;
@@ -1139,6 +1303,32 @@
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info);
+
+static inline unsigned int
+read_amp_value(struct hda_codec *codec, hda_nid_t nid,
+ int ch, int dir, int idx, unsigned int ofs)
+{
+ unsigned int val;
+ val = snd_hda_codec_amp_read(codec, nid, ch, dir, idx);
+ val &= HDA_AMP_VOLMASK;
+ if (val >= ofs)
+ val -= ofs;
+ else
+ val = 0;
+ return val;
+}
+
+static inline int
+update_amp_value(struct hda_codec *codec, hda_nid_t nid,
+ int ch, int dir, int idx, unsigned int ofs,
+ unsigned int val)
+{
+ if (val > 0)
+ val += ofs;
+ return snd_hda_codec_amp_update(codec, nid, ch, dir, idx,
+ HDA_AMP_VOLMASK, val);
+}
+
int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1147,14 +1337,13 @@
int chs = get_amp_channels(kcontrol);
int dir = get_amp_direction(kcontrol);
int idx = get_amp_index(kcontrol);
+ unsigned int ofs = get_amp_offset(kcontrol);
long *valp = ucontrol->value.integer.value;
if (chs & 1)
- *valp++ = snd_hda_codec_amp_read(codec, nid, 0, dir, idx)
- & HDA_AMP_VOLMASK;
+ *valp++ = read_amp_value(codec, nid, 0, dir, idx, ofs);
if (chs & 2)
- *valp = snd_hda_codec_amp_read(codec, nid, 1, dir, idx)
- & HDA_AMP_VOLMASK;
+ *valp = read_amp_value(codec, nid, 1, dir, idx, ofs);
return 0;
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get);
@@ -1167,18 +1356,17 @@
int chs = get_amp_channels(kcontrol);
int dir = get_amp_direction(kcontrol);
int idx = get_amp_index(kcontrol);
+ unsigned int ofs = get_amp_offset(kcontrol);
long *valp = ucontrol->value.integer.value;
int change = 0;
snd_hda_power_up(codec);
if (chs & 1) {
- change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
- 0x7f, *valp);
+ change = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp);
valp++;
}
if (chs & 2)
- change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
- 0x7f, *valp);
+ change |= update_amp_value(codec, nid, 1, dir, idx, ofs, *valp);
snd_hda_power_down(codec);
return change;
}
@@ -1190,6 +1378,7 @@
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = get_amp_nid(kcontrol);
int dir = get_amp_direction(kcontrol);
+ unsigned int ofs = get_amp_offset(kcontrol);
u32 caps, val1, val2;
if (size < 4 * sizeof(unsigned int))
@@ -1198,6 +1387,7 @@
val2 = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
val2 = (val2 + 1) * 25;
val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
+ val1 += ofs;
val1 = ((int)val1) * ((int)val2);
if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
return -EFAULT;
@@ -1268,7 +1458,6 @@
}
EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
-#ifdef CONFIG_SND_HDA_RECONFIG
/* Clear all controls assigned to the given codec */
void snd_hda_ctls_clear(struct hda_codec *codec)
{
@@ -1279,9 +1468,52 @@
snd_array_free(&codec->mixers);
}
-void snd_hda_codec_reset(struct hda_codec *codec)
+/* pseudo device locking
+ * toggle card->shutdown to allow/disallow the device access (as a hack)
+ */
+static int hda_lock_devices(struct snd_card *card)
{
- int i;
+ spin_lock(&card->files_lock);
+ if (card->shutdown) {
+ spin_unlock(&card->files_lock);
+ return -EINVAL;
+ }
+ card->shutdown = 1;
+ spin_unlock(&card->files_lock);
+ return 0;
+}
+
+static void hda_unlock_devices(struct snd_card *card)
+{
+ spin_lock(&card->files_lock);
+ card->shutdown = 0;
+ spin_unlock(&card->files_lock);
+}
+
+int snd_hda_codec_reset(struct hda_codec *codec)
+{
+ struct snd_card *card = codec->bus->card;
+ int i, pcm;
+
+ if (hda_lock_devices(card) < 0)
+ return -EBUSY;
+ /* check whether the codec isn't used by any mixer or PCM streams */
+ if (!list_empty(&card->ctl_files)) {
+ hda_unlock_devices(card);
+ return -EBUSY;
+ }
+ for (pcm = 0; pcm < codec->num_pcms; pcm++) {
+ struct hda_pcm *cpcm = &codec->pcm_info[pcm];
+ if (!cpcm->pcm)
+ continue;
+ if (cpcm->pcm->streams[0].substream_opened ||
+ cpcm->pcm->streams[1].substream_opened) {
+ hda_unlock_devices(card);
+ return -EBUSY;
+ }
+ }
+
+ /* OK, let it free */
#ifdef CONFIG_SND_HDA_POWER_SAVE
cancel_delayed_work(&codec->power_work);
@@ -1291,8 +1523,7 @@
/* relase PCMs */
for (i = 0; i < codec->num_pcms; i++) {
if (codec->pcm_info[i].pcm) {
- snd_device_free(codec->bus->card,
- codec->pcm_info[i].pcm);
+ snd_device_free(card, codec->pcm_info[i].pcm);
clear_bit(codec->pcm_info[i].device,
codec->bus->pcm_dev_bits);
}
@@ -1305,13 +1536,22 @@
free_hda_cache(&codec->cmd_cache);
init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
+ /* free only driver_pins so that init_pins + user_pins are restored */
+ snd_array_free(&codec->driver_pins);
+ restore_pincfgs(codec);
codec->num_pcms = 0;
codec->pcm_info = NULL;
codec->preset = NULL;
+ memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
+ codec->slave_dig_outs = NULL;
+ codec->spdif_status_reset = 0;
module_put(codec->owner);
codec->owner = NULL;
+
+ /* allow device access again */
+ hda_unlock_devices(card);
+ return 0;
}
-#endif /* CONFIG_SND_HDA_RECONFIG */
/* create a virtual master control and add slaves */
int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
@@ -1336,15 +1576,20 @@
for (s = slaves; *s; s++) {
struct snd_kcontrol *sctl;
-
- sctl = snd_hda_find_mixer_ctl(codec, *s);
- if (!sctl) {
- snd_printdd("Cannot find slave %s, skipped\n", *s);
- continue;
+ int i = 0;
+ for (;;) {
+ sctl = _snd_hda_find_mixer_ctl(codec, *s, i);
+ if (!sctl) {
+ if (!i)
+ snd_printdd("Cannot find slave %s, "
+ "skipped\n", *s);
+ break;
+ }
+ err = snd_ctl_add_slave(kctl, sctl);
+ if (err < 0)
+ return err;
+ i++;
}
- err = snd_ctl_add_slave(kctl, sctl);
- if (err < 0)
- return err;
}
return 0;
}
@@ -1955,6 +2200,8 @@
}
for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
kctl = snd_ctl_new1(dig_mix, codec);
+ if (!kctl)
+ return -ENOMEM;
kctl->private_value = nid;
err = snd_hda_ctl_add(codec, kctl);
if (err < 0)
@@ -2074,8 +2321,7 @@
* don't power down the widget if it controls
* eapd and EAPD_BTLENABLE is set.
*/
- pincap = snd_hda_param_read(codec, nid,
- AC_PAR_PIN_CAP);
+ pincap = snd_hda_query_pin_caps(codec, nid);
if (pincap & AC_PINCAP_EAPD) {
int eapd = snd_hda_codec_read(codec,
nid, 0,
@@ -2144,6 +2390,7 @@
hda_set_power_state(codec,
codec->afg ? codec->afg : codec->mfg,
AC_PWRST_D0);
+ restore_pincfgs(codec); /* restore all current pin configs */
hda_exec_init_verbs(codec);
if (codec->patch_ops.resume)
codec->patch_ops.resume(codec);
@@ -2171,8 +2418,16 @@
list_for_each_entry(codec, &bus->codec_list, list) {
int err = snd_hda_codec_build_controls(codec);
- if (err < 0)
- return err;
+ if (err < 0) {
+ printk(KERN_ERR "hda_codec: cannot build controls"
+ "for #%d (error %d)\n", codec->addr, err);
+ err = snd_hda_codec_reset(codec);
+ if (err < 0) {
+ printk(KERN_ERR
+ "hda_codec: cannot revert codec\n");
+ return err;
+ }
+ }
}
return 0;
}
@@ -2181,19 +2436,12 @@
int snd_hda_codec_build_controls(struct hda_codec *codec)
{
int err = 0;
- /* fake as if already powered-on */
- hda_keep_power_on(codec);
- /* then fire up */
- hda_set_power_state(codec,
- codec->afg ? codec->afg : codec->mfg,
- AC_PWRST_D0);
hda_exec_init_verbs(codec);
/* continue to initialize... */
if (codec->patch_ops.init)
err = codec->patch_ops.init(codec);
if (!err && codec->patch_ops.build_controls)
err = codec->patch_ops.build_controls(codec);
- snd_hda_power_down(codec);
if (err < 0)
return err;
return 0;
@@ -2306,12 +2554,11 @@
static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
{
- int i;
- unsigned int val, streams;
+ unsigned int i, val, wcaps;
val = 0;
- if (nid != codec->afg &&
- (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {
+ wcaps = get_wcaps(codec, nid);
+ if (nid != codec->afg && (wcaps & AC_WCAP_FORMAT_OVRD)) {
val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
if (val == -1)
return -EIO;
@@ -2325,15 +2572,20 @@
if (val & (1 << i))
rates |= rate_bits[i].alsa_bits;
}
+ if (rates == 0) {
+ snd_printk(KERN_ERR "hda_codec: rates == 0 "
+ "(nid=0x%x, val=0x%x, ovrd=%i)\n",
+ nid, val,
+ (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0);
+ return -EIO;
+ }
*ratesp = rates;
}
if (formatsp || bpsp) {
u64 formats = 0;
- unsigned int bps;
- unsigned int wcaps;
+ unsigned int streams, bps;
- wcaps = get_wcaps(codec, nid);
streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
if (streams == -1)
return -EIO;
@@ -2386,6 +2638,15 @@
formats |= SNDRV_PCM_FMTBIT_U8;
bps = 8;
}
+ if (formats == 0) {
+ snd_printk(KERN_ERR "hda_codec: formats == 0 "
+ "(nid=0x%x, val=0x%x, ovrd=%i, "
+ "streams=0x%x)\n",
+ nid, val,
+ (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0,
+ streams);
+ return -EIO;
+ }
if (formatsp)
*formatsp = formats;
if (bpsp)
@@ -2501,12 +2762,16 @@
static int set_pcm_default_values(struct hda_codec *codec,
struct hda_pcm_stream *info)
{
+ int err;
+
/* query support PCM information from the given NID */
if (info->nid && (!info->rates || !info->formats)) {
- snd_hda_query_supported_pcm(codec, info->nid,
+ err = snd_hda_query_supported_pcm(codec, info->nid,
info->rates ? NULL : &info->rates,
info->formats ? NULL : &info->formats,
info->maxbps ? NULL : &info->maxbps);
+ if (err < 0)
+ return err;
}
if (info->ops.open == NULL)
info->ops.open = hda_pcm_default_open_close;
@@ -2549,13 +2814,10 @@
for (i = 0; i < ARRAY_SIZE(audio_idx); i++) {
dev = audio_idx[i];
if (!test_bit(dev, bus->pcm_dev_bits))
- break;
+ goto ok;
}
- if (i >= ARRAY_SIZE(audio_idx)) {
- snd_printk(KERN_WARNING "Too many audio devices\n");
- return -EAGAIN;
- }
- break;
+ snd_printk(KERN_WARNING "Too many audio devices\n");
+ return -EAGAIN;
case HDA_PCM_TYPE_SPDIF:
case HDA_PCM_TYPE_HDMI:
case HDA_PCM_TYPE_MODEM:
@@ -2570,6 +2832,7 @@
snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
return -EINVAL;
}
+ ok:
set_bit(dev, bus->pcm_dev_bits);
return dev;
}
@@ -2606,24 +2869,36 @@
if (!codec->patch_ops.build_pcms)
return 0;
err = codec->patch_ops.build_pcms(codec);
- if (err < 0)
- return err;
+ if (err < 0) {
+ printk(KERN_ERR "hda_codec: cannot build PCMs"
+ "for #%d (error %d)\n", codec->addr, err);
+ err = snd_hda_codec_reset(codec);
+ if (err < 0) {
+ printk(KERN_ERR
+ "hda_codec: cannot revert codec\n");
+ return err;
+ }
+ }
}
for (pcm = 0; pcm < codec->num_pcms; pcm++) {
struct hda_pcm *cpcm = &codec->pcm_info[pcm];
int dev;
if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
- return 0; /* no substreams assigned */
+ continue; /* no substreams assigned */
if (!cpcm->pcm) {
dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type);
if (dev < 0)
- return 0;
+ continue; /* no fatal error */
cpcm->device = dev;
err = snd_hda_attach_pcm(codec, cpcm);
- if (err < 0)
- return err;
+ if (err < 0) {
+ printk(KERN_ERR "hda_codec: cannot attach "
+ "PCM stream %d for codec #%d\n",
+ dev, codec->addr);
+ continue; /* no fatal error */
+ }
}
}
return 0;
@@ -3324,8 +3599,7 @@
if (ignore_nids && is_in_nid_list(nid, ignore_nids))
continue;
- def_conf = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0);
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
continue;
loc = get_defcfg_location(def_conf);
@@ -3401,10 +3675,22 @@
cfg->input_pins[AUTO_PIN_AUX] = nid;
break;
case AC_JACK_SPDIF_OUT:
- cfg->dig_out_pin = nid;
+ case AC_JACK_DIG_OTHER_OUT:
+ if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins))
+ continue;
+ cfg->dig_out_pins[cfg->dig_outs] = nid;
+ cfg->dig_out_type[cfg->dig_outs] =
+ (loc == AC_JACK_LOC_HDMI) ?
+ HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
+ cfg->dig_outs++;
break;
case AC_JACK_SPDIF_IN:
+ case AC_JACK_DIG_OTHER_IN:
cfg->dig_in_pin = nid;
+ if (loc == AC_JACK_LOC_HDMI)
+ cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
+ else
+ cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
break;
}
}
@@ -3510,6 +3796,9 @@
cfg->hp_pins[1], cfg->hp_pins[2],
cfg->hp_pins[3], cfg->hp_pins[4]);
snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin);
+ if (cfg->dig_outs)
+ snd_printd(" dig-out=0x%x/0x%x\n",
+ cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
" cd=0x%x, aux=0x%x\n",
cfg->input_pins[AUTO_PIN_MIC],
@@ -3518,6 +3807,8 @@
cfg->input_pins[AUTO_PIN_FRONT_LINE],
cfg->input_pins[AUTO_PIN_CD],
cfg->input_pins[AUTO_PIN_AUX]);
+ if (cfg->dig_in_pin)
+ snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin);
return 0;
}
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 09a332a..2fdecf4 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -739,6 +739,7 @@
hda_nid_t mfg; /* MFG node id */
/* ids */
+ u32 function_id;
u32 vendor_id;
u32 subsystem_id;
u32 revision_id;
@@ -778,11 +779,14 @@
unsigned short spdif_ctls; /* SPDIF control bits */
unsigned int spdif_in_enable; /* SPDIF input enable? */
hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
+ struct snd_array init_pins; /* initial (BIOS) pin configurations */
+ struct snd_array driver_pins; /* pin configs set by codec parser */
#ifdef CONFIG_SND_HDA_HWDEP
struct snd_hwdep *hwdep; /* assigned hwdep device */
struct snd_array init_verbs; /* additional init verbs */
struct snd_array hints; /* additional hints */
+ struct snd_array user_pins; /* default pin configs to override */
#endif
/* misc flags */
@@ -790,6 +794,9 @@
* status change
* (e.g. Realtek codecs)
*/
+ unsigned int pin_amp_workaround:1; /* pin out-amp takes index
+ * (e.g. Conexant codecs)
+ */
#ifdef CONFIG_SND_HDA_POWER_SAVE
unsigned int power_on :1; /* current (global) power-state */
unsigned int power_transition :1; /* power-state in transition */
@@ -855,6 +862,18 @@
#define snd_hda_sequence_write_cache snd_hda_sequence_write
#endif
+/* the struct for codec->pin_configs */
+struct hda_pincfg {
+ hda_nid_t nid;
+ unsigned int cfg;
+};
+
+unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int cfg);
+int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
+ hda_nid_t nid, unsigned int cfg); /* for hwdep */
+
/*
* Mixer
*/
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 65745e9..1d5797a 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -144,9 +144,9 @@
node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
if (node->type == AC_WID_PIN) {
- node->pin_caps = snd_hda_param_read(codec, node->nid, AC_PAR_PIN_CAP);
+ node->pin_caps = snd_hda_query_pin_caps(codec, node->nid);
node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- node->def_cfg = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+ node->def_cfg = snd_hda_codec_get_pincfg(codec, node->nid);
}
if (node->wid_caps & AC_WCAP_OUT_AMP) {
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index 4ae51dc..1c57505 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -30,6 +30,12 @@
#include <sound/hda_hwdep.h>
#include <sound/minors.h>
+/* hint string pair */
+struct hda_hint {
+ const char *key;
+ const char *val; /* contained in the same alloc as key */
+};
+
/*
* write/read an out-of-bound verb
*/
@@ -99,16 +105,17 @@
static void clear_hwdep_elements(struct hda_codec *codec)
{
- char **head;
int i;
/* clear init verbs */
snd_array_free(&codec->init_verbs);
/* clear hints */
- head = codec->hints.list;
- for (i = 0; i < codec->hints.used; i++, head++)
- kfree(*head);
+ for (i = 0; i < codec->hints.used; i++) {
+ struct hda_hint *hint = snd_array_elem(&codec->hints, i);
+ kfree(hint->key); /* we don't need to free hint->val */
+ }
snd_array_free(&codec->hints);
+ snd_array_free(&codec->user_pins);
}
static void hwdep_free(struct snd_hwdep *hwdep)
@@ -140,7 +147,8 @@
#endif
snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
- snd_array_init(&codec->hints, sizeof(char *), 32);
+ snd_array_init(&codec->hints, sizeof(struct hda_hint), 32);
+ snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);
return 0;
}
@@ -153,7 +161,13 @@
static int clear_codec(struct hda_codec *codec)
{
- snd_hda_codec_reset(codec);
+ int err;
+
+ err = snd_hda_codec_reset(codec);
+ if (err < 0) {
+ snd_printk(KERN_ERR "The codec is being used, can't free.\n");
+ return err;
+ }
clear_hwdep_elements(codec);
return 0;
}
@@ -162,20 +176,29 @@
{
int err;
+ snd_hda_power_up(codec);
snd_printk(KERN_INFO "hda-codec: reconfiguring\n");
- snd_hda_codec_reset(codec);
+ err = snd_hda_codec_reset(codec);
+ if (err < 0) {
+ snd_printk(KERN_ERR
+ "The codec is being used, can't reconfigure.\n");
+ goto error;
+ }
err = snd_hda_codec_configure(codec);
if (err < 0)
- return err;
+ goto error;
/* rebuild PCMs */
err = snd_hda_codec_build_pcms(codec);
if (err < 0)
- return err;
+ goto error;
/* rebuild mixers */
err = snd_hda_codec_build_controls(codec);
if (err < 0)
- return err;
- return snd_card_register(codec->bus->card);
+ goto error;
+ err = snd_card_register(codec->bus->card);
+ error:
+ snd_hda_power_down(codec);
+ return err;
}
/*
@@ -271,6 +294,22 @@
CODEC_ACTION_STORE(reconfig);
CODEC_ACTION_STORE(clear);
+static ssize_t init_verbs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+ struct hda_codec *codec = hwdep->private_data;
+ int i, len = 0;
+ for (i = 0; i < codec->init_verbs.used; i++) {
+ struct hda_verb *v = snd_array_elem(&codec->init_verbs, i);
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "0x%02x 0x%03x 0x%04x\n",
+ v->nid, v->verb, v->param);
+ }
+ return len;
+}
+
static ssize_t init_verbs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -293,26 +332,157 @@
return count;
}
+static ssize_t hints_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+ struct hda_codec *codec = hwdep->private_data;
+ int i, len = 0;
+ for (i = 0; i < codec->hints.used; i++) {
+ struct hda_hint *hint = snd_array_elem(&codec->hints, i);
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "%s = %s\n", hint->key, hint->val);
+ }
+ return len;
+}
+
+static struct hda_hint *get_hint(struct hda_codec *codec, const char *key)
+{
+ int i;
+
+ for (i = 0; i < codec->hints.used; i++) {
+ struct hda_hint *hint = snd_array_elem(&codec->hints, i);
+ if (!strcmp(hint->key, key))
+ return hint;
+ }
+ return NULL;
+}
+
+static void remove_trail_spaces(char *str)
+{
+ char *p;
+ if (!*str)
+ return;
+ p = str + strlen(str) - 1;
+ for (; isspace(*p); p--) {
+ *p = 0;
+ if (p == str)
+ return;
+ }
+}
+
+#define MAX_HINTS 1024
+
static ssize_t hints_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
struct hda_codec *codec = hwdep->private_data;
- char *p;
- char **hint;
+ char *key, *val;
+ struct hda_hint *hint;
- if (!*buf || isspace(*buf) || *buf == '#' || *buf == '\n')
+ while (isspace(*buf))
+ buf++;
+ if (!*buf || *buf == '#' || *buf == '\n')
return count;
- p = kstrndup_noeol(buf, 1024);
- if (!p)
+ if (*buf == '=')
+ return -EINVAL;
+ key = kstrndup_noeol(buf, 1024);
+ if (!key)
return -ENOMEM;
- hint = snd_array_new(&codec->hints);
+ /* extract key and val */
+ val = strchr(key, '=');
+ if (!val) {
+ kfree(key);
+ return -EINVAL;
+ }
+ *val++ = 0;
+ while (isspace(*val))
+ val++;
+ remove_trail_spaces(key);
+ remove_trail_spaces(val);
+ hint = get_hint(codec, key);
+ if (hint) {
+ /* replace */
+ kfree(hint->key);
+ hint->key = key;
+ hint->val = val;
+ return count;
+ }
+ /* allocate a new hint entry */
+ if (codec->hints.used >= MAX_HINTS)
+ hint = NULL;
+ else
+ hint = snd_array_new(&codec->hints);
if (!hint) {
- kfree(p);
+ kfree(key);
return -ENOMEM;
}
- *hint = p;
+ hint->key = key;
+ hint->val = val;
+ return count;
+}
+
+static ssize_t pin_configs_show(struct hda_codec *codec,
+ struct snd_array *list,
+ char *buf)
+{
+ int i, len = 0;
+ for (i = 0; i < list->used; i++) {
+ struct hda_pincfg *pin = snd_array_elem(list, i);
+ len += sprintf(buf + len, "0x%02x 0x%08x\n",
+ pin->nid, pin->cfg);
+ }
+ return len;
+}
+
+static ssize_t init_pin_configs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+ struct hda_codec *codec = hwdep->private_data;
+ return pin_configs_show(codec, &codec->init_pins, buf);
+}
+
+static ssize_t user_pin_configs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+ struct hda_codec *codec = hwdep->private_data;
+ return pin_configs_show(codec, &codec->user_pins, buf);
+}
+
+static ssize_t driver_pin_configs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+ struct hda_codec *codec = hwdep->private_data;
+ return pin_configs_show(codec, &codec->driver_pins, buf);
+}
+
+#define MAX_PIN_CONFIGS 32
+
+static ssize_t user_pin_configs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+ struct hda_codec *codec = hwdep->private_data;
+ int nid, cfg;
+ int err;
+
+ if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
+ return -EINVAL;
+ if (!nid)
+ return -EINVAL;
+ err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
+ if (err < 0)
+ return err;
return count;
}
@@ -331,8 +501,11 @@
CODEC_ATTR_RO(mfg),
CODEC_ATTR_RW(name),
CODEC_ATTR_RW(modelname),
- CODEC_ATTR_WO(init_verbs),
- CODEC_ATTR_WO(hints),
+ CODEC_ATTR_RW(init_verbs),
+ CODEC_ATTR_RW(hints),
+ CODEC_ATTR_RO(init_pin_configs),
+ CODEC_ATTR_RW(user_pin_configs),
+ CODEC_ATTR_RO(driver_pin_configs),
CODEC_ATTR_WO(reconfig),
CODEC_ATTR_WO(clear),
};
@@ -351,4 +524,29 @@
return 0;
}
+/*
+ * Look for hint string
+ */
+const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
+{
+ struct hda_hint *hint = get_hint(codec, key);
+ return hint ? hint->val : NULL;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_hint);
+
+int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
+{
+ const char *p = snd_hda_get_hint(codec, key);
+ if (!p || !*p)
+ return -ENOENT;
+ switch (toupper(*p)) {
+ case 'T': /* true */
+ case 'Y': /* yes */
+ case '1':
+ return 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint);
+
#endif /* CONFIG_SND_HDA_RECONFIG */
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index f3b5723..30829ee 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -381,6 +381,7 @@
/* HD codec */
unsigned short codec_mask;
+ int codec_probe_mask; /* copied from probe_mask option */
struct hda_bus *bus;
/* CORB/RIRB */
@@ -858,13 +859,18 @@
SD_CTL_DMA_START | SD_INT_MASK);
}
-/* stop a stream */
-static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev)
+/* stop DMA */
+static void azx_stream_clear(struct azx *chip, struct azx_dev *azx_dev)
{
- /* stop DMA */
azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) &
~(SD_CTL_DMA_START | SD_INT_MASK));
azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
+}
+
+/* stop a stream */
+static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev)
+{
+ azx_stream_clear(chip, azx_dev);
/* disable SIE */
azx_writeb(chip, INTCTL,
azx_readb(chip, INTCTL) & ~(1 << azx_dev->index));
@@ -1075,8 +1081,7 @@
azx_sd_writel(azx_dev, SD_BDLPL, 0);
azx_sd_writel(azx_dev, SD_BDLPU, 0);
- period_bytes = snd_pcm_lib_period_bytes(substream);
- azx_dev->period_bytes = period_bytes;
+ period_bytes = azx_dev->period_bytes;
periods = azx_dev->bufsize / period_bytes;
/* program the initial BDL entries */
@@ -1123,24 +1128,17 @@
error:
snd_printk(KERN_ERR "Too many BDL entries: buffer=%d, period=%d\n",
azx_dev->bufsize, period_bytes);
- /* reset */
- azx_sd_writel(azx_dev, SD_BDLPL, 0);
- azx_sd_writel(azx_dev, SD_BDLPU, 0);
return -EINVAL;
}
-/*
- * set up the SD for streaming
- */
-static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
+/* reset stream */
+static void azx_stream_reset(struct azx *chip, struct azx_dev *azx_dev)
{
unsigned char val;
int timeout;
- /* make sure the run bit is zero for SD */
- azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) &
- ~SD_CTL_DMA_START);
- /* reset stream */
+ azx_stream_clear(chip, azx_dev);
+
azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) |
SD_CTL_STREAM_RESET);
udelay(3);
@@ -1157,7 +1155,15 @@
while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
--timeout)
;
+}
+/*
+ * set up the SD for streaming
+ */
+static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
+{
+ /* make sure the run bit is zero for SD */
+ azx_stream_clear(chip, azx_dev);
/* program the stream_tag */
azx_sd_writel(azx_dev, SD_CTL,
(azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK)|
@@ -1228,7 +1234,6 @@
};
static int __devinit azx_codec_create(struct azx *chip, const char *model,
- unsigned int codec_probe_mask,
int no_init)
{
struct hda_bus_template bus_temp;
@@ -1261,7 +1266,7 @@
/* First try to probe all given codec slots */
for (c = 0; c < max_slots; c++) {
- if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
+ if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
if (probe_codec(chip, c) < 0) {
/* Some BIOSen give you wrong codec addresses
* that don't exist
@@ -1285,7 +1290,7 @@
/* Then create codec instances */
for (c = 0; c < max_slots; c++) {
- if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
+ if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
struct hda_codec *codec;
err = snd_hda_codec_new(chip->bus, c, !no_init, &codec);
if (err < 0)
@@ -1403,6 +1408,8 @@
runtime->private_data = azx_dev;
snd_pcm_set_sync(substream);
mutex_unlock(&chip->open_mutex);
+
+ azx_stream_reset(chip, azx_dev);
return 0;
}
@@ -1429,6 +1436,11 @@
static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+
+ azx_dev->bufsize = 0;
+ azx_dev->period_bytes = 0;
+ azx_dev->format_val = 0;
return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
}
@@ -1443,6 +1455,9 @@
azx_sd_writel(azx_dev, SD_BDLPL, 0);
azx_sd_writel(azx_dev, SD_BDLPU, 0);
azx_sd_writel(azx_dev, SD_CTL, 0);
+ azx_dev->bufsize = 0;
+ azx_dev->period_bytes = 0;
+ azx_dev->format_val = 0;
hinfo->ops.cleanup(hinfo, apcm->codec, substream);
@@ -1456,23 +1471,37 @@
struct azx_dev *azx_dev = get_azx_dev(substream);
struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int bufsize, period_bytes, format_val;
+ int err;
- azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream);
- azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate,
- runtime->channels,
- runtime->format,
- hinfo->maxbps);
- if (!azx_dev->format_val) {
+ format_val = snd_hda_calc_stream_format(runtime->rate,
+ runtime->channels,
+ runtime->format,
+ hinfo->maxbps);
+ if (!format_val) {
snd_printk(KERN_ERR SFX
"invalid format_val, rate=%d, ch=%d, format=%d\n",
runtime->rate, runtime->channels, runtime->format);
return -EINVAL;
}
+ bufsize = snd_pcm_lib_buffer_bytes(substream);
+ period_bytes = snd_pcm_lib_period_bytes(substream);
+
snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
- azx_dev->bufsize, azx_dev->format_val);
- if (azx_setup_periods(chip, substream, azx_dev) < 0)
- return -EINVAL;
+ bufsize, format_val);
+
+ if (bufsize != azx_dev->bufsize ||
+ period_bytes != azx_dev->period_bytes ||
+ format_val != azx_dev->format_val) {
+ azx_dev->bufsize = bufsize;
+ azx_dev->period_bytes = period_bytes;
+ azx_dev->format_val = format_val;
+ err = azx_setup_periods(chip, substream, azx_dev);
+ if (err < 0)
+ return err;
+ }
+
azx_setup_controller(chip, azx_dev);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
@@ -2100,25 +2129,36 @@
SND_PCI_QUIRK(0x1028, 0x20ac, "Dell Studio Desktop", 0x01),
/* including bogus ALC268 in slot#2 that conflicts with ALC888 */
SND_PCI_QUIRK(0x17c0, 0x4085, "Medion MD96630", 0x01),
- /* conflict of ALC268 in slot#3 (digital I/O); a temporary fix */
- SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba laptop", 0x03),
+ /* forced codec slots */
+ SND_PCI_QUIRK(0x1046, 0x1262, "ASUS W5F", 0x103),
{}
};
+#define AZX_FORCE_CODEC_MASK 0x100
+
static void __devinit check_probe_mask(struct azx *chip, int dev)
{
const struct snd_pci_quirk *q;
- if (probe_mask[dev] == -1) {
+ chip->codec_probe_mask = probe_mask[dev];
+ if (chip->codec_probe_mask == -1) {
q = snd_pci_quirk_lookup(chip->pci, probe_mask_list);
if (q) {
printk(KERN_INFO
"hda_intel: probe_mask set to 0x%x "
"for device %04x:%04x\n",
q->value, q->subvendor, q->subdevice);
- probe_mask[dev] = q->value;
+ chip->codec_probe_mask = q->value;
}
}
+
+ /* check forced option */
+ if (chip->codec_probe_mask != -1 &&
+ (chip->codec_probe_mask & AZX_FORCE_CODEC_MASK)) {
+ chip->codec_mask = chip->codec_probe_mask & 0xff;
+ printk(KERN_INFO "hda_intel: codec_mask forced to 0x%x\n",
+ chip->codec_mask);
+ }
}
@@ -2347,10 +2387,10 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (!card) {
+ 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 -ENOMEM;
+ return err;
}
err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
@@ -2359,8 +2399,7 @@
card->private_data = chip;
/* create codec instances */
- err = azx_codec_create(chip, model[dev], probe_mask[dev],
- probe_only[dev]);
+ err = azx_codec_create(chip, model[dev], probe_only[dev]);
if (err < 0)
goto out_free;
@@ -2457,10 +2496,10 @@
{ PCI_DEVICE(0x10de, 0x0ac1), .driver_data = AZX_DRIVER_NVIDIA },
{ PCI_DEVICE(0x10de, 0x0ac2), .driver_data = AZX_DRIVER_NVIDIA },
{ PCI_DEVICE(0x10de, 0x0ac3), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0bd4), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0bd5), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0bd6), .driver_data = AZX_DRIVER_NVIDIA },
- { PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0d94), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0d95), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0d96), .driver_data = AZX_DRIVER_NVIDIA },
+ { PCI_DEVICE(0x10de, 0x0d97), .driver_data = AZX_DRIVER_NVIDIA },
/* Teradici */
{ PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
/* AMD Generic, PCI class code and Vendor ID for HD Audio */
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 44f189c..8334901 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -26,8 +26,10 @@
/*
* for mixer controls
*/
+#define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs) \
+ ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23))
#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \
- ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19))
+ HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0)
/* mono volume with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
@@ -96,7 +98,7 @@
const char *name);
int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
unsigned int *tlv, const char **slaves);
-void snd_hda_codec_reset(struct hda_codec *codec);
+int snd_hda_codec_reset(struct hda_codec *codec);
int snd_hda_codec_configure(struct hda_codec *codec);
/* amp value bits */
@@ -134,7 +136,7 @@
struct hda_bind_ctls {
struct hda_ctl_ops *ops;
- long values[];
+ unsigned long values[];
};
int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
@@ -227,6 +229,7 @@
hda_nid_t hp_nid; /* optional DAC for HP, 0 when not exists */
hda_nid_t extra_out_nid[3]; /* optional DACs, 0 when not exists */
hda_nid_t dig_out_nid; /* digital out audio widget */
+ hda_nid_t *slave_dig_outs;
int max_channels; /* currently supported analog channels */
int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */
int no_share_stream; /* don't share a stream with multiple pins */
@@ -354,9 +357,12 @@
int line_out_type; /* AUTO_PIN_XXX_OUT */
hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
hda_nid_t input_pins[AUTO_PIN_LAST];
- hda_nid_t dig_out_pin;
+ int dig_outs;
+ hda_nid_t dig_out_pins[2];
hda_nid_t dig_in_pin;
hda_nid_t mono_out_pin;
+ int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
+ int dig_in_type; /* HDA_PCM_TYPE_XXX */
};
#define get_defcfg_connect(cfg) \
@@ -405,6 +411,7 @@
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps);
+u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl);
void snd_hda_ctls_clear(struct hda_codec *codec);
@@ -427,6 +434,23 @@
}
#endif
+#ifdef CONFIG_SND_HDA_RECONFIG
+const char *snd_hda_get_hint(struct hda_codec *codec, const char *key);
+int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key);
+#else
+static inline
+const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
+{
+ return NULL;
+}
+
+static inline
+int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
+{
+ return -ENOENT;
+}
+#endif
+
/*
* power-management
*/
@@ -458,6 +482,7 @@
#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3)
#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
+#define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f)
/*
* CEA Short Audio Descriptor data
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 144b852..93d7499 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -399,8 +399,10 @@
{
int c, curr = -1;
- if (conn_len > 1 && wid_type != AC_WID_AUD_MIX &&
- wid_type != AC_WID_VOL_KNB)
+ if (conn_len > 1 &&
+ wid_type != AC_WID_AUD_MIX &&
+ wid_type != AC_WID_VOL_KNB &&
+ wid_type != AC_WID_POWER)
curr = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CONNECT_SEL, 0);
snd_iprintf(buffer, " Connection: %d\n", conn_len);
@@ -467,8 +469,9 @@
snd_iprintf(buffer, "Codec: %s\n",
codec->name ? codec->name : "Not Set");
snd_iprintf(buffer, "Address: %d\n", codec->addr);
- snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id);
- snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id);
+ snd_iprintf(buffer, "Function Id: 0x%x\n", codec->function_id);
+ snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_id);
+ snd_iprintf(buffer, "Subsystem Id: 0x%08x\n", codec->subsystem_id);
snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
if (codec->mfg)
@@ -554,8 +557,14 @@
snd_iprintf(buffer, " Amp-Out caps: ");
print_amp_caps(buffer, codec, nid, HDA_OUTPUT);
snd_iprintf(buffer, " Amp-Out vals: ");
- print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
- wid_caps & AC_WCAP_STEREO, 1);
+ if (wid_type == AC_WID_PIN &&
+ codec->pin_amp_workaround)
+ print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
+ wid_caps & AC_WCAP_STEREO,
+ conn_len);
+ else
+ print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
+ wid_caps & AC_WCAP_STEREO, 1);
}
switch (wid_type) {
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index e486123..5bb48ee 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -27,11 +27,12 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_beep.h"
struct ad198x_spec {
struct snd_kcontrol_new *mixers[5];
int num_mixers;
-
+ unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
const struct hda_verb *init_verbs[5]; /* initialization verbs
* don't forget NULL termination!
*/
@@ -154,6 +155,16 @@
static void ad198x_free_kctls(struct hda_codec *codec);
+/* additional beep mixers; the actual parameters are overwritten at build */
+static struct snd_kcontrol_new ad_beep_mixer[] = {
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_OUTPUT),
+ { } /* end */
+};
+
+#define set_beep_amp(spec, nid, idx, dir) \
+ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
+
static int ad198x_build_controls(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
@@ -181,6 +192,21 @@
return err;
}
+ /* create beep controls if needed */
+ if (spec->beep_amp) {
+ struct snd_kcontrol_new *knew;
+ for (knew = ad_beep_mixer; knew->name; knew++) {
+ struct snd_kcontrol *kctl;
+ kctl = snd_ctl_new1(knew, codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->private_value = spec->beep_amp;
+ err = snd_hda_ctl_add(codec, kctl);
+ if (err < 0)
+ return err;
+ }
+ }
+
/* if we have no master control, let's create it */
if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
unsigned int vmaster_tlv[4];
@@ -406,7 +432,8 @@
return;
ad198x_free_kctls(codec);
- kfree(codec->spec);
+ kfree(spec);
+ snd_hda_detach_beep_device(codec);
}
static struct hda_codec_ops ad198x_patch_ops = {
@@ -545,8 +572,6 @@
HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
@@ -610,8 +635,7 @@
HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
- /* HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
+ /*
HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
@@ -809,8 +833,6 @@
HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x18, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x18, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
{
@@ -1002,10 +1024,8 @@
SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_SAMSUNG),
- SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_SAMSUNG),
- SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_SAMSUNG),
SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
+ SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
@@ -1027,15 +1047,14 @@
static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
{
- unsigned int conf = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0);
+ unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
}
static int patch_ad1986a(struct hda_codec *codec)
{
struct ad198x_spec *spec;
- int board_config;
+ int err, board_config;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -1043,6 +1062,13 @@
codec->spec = spec;
+ err = snd_hda_attach_beep_device(codec, 0x19);
+ if (err < 0) {
+ ad198x_free(codec);
+ return err;
+ }
+ set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
+
spec->multiout.max_channels = 6;
spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
spec->multiout.dac_nids = ad1986a_dac_nids;
@@ -1222,8 +1248,6 @@
HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x10, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x10, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
@@ -1294,6 +1318,7 @@
static int patch_ad1983(struct hda_codec *codec)
{
struct ad198x_spec *spec;
+ int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -1301,6 +1326,13 @@
codec->spec = spec;
+ err = snd_hda_attach_beep_device(codec, 0x10);
+ if (err < 0) {
+ ad198x_free(codec);
+ return err;
+ }
+ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
spec->multiout.dac_nids = ad1983_dac_nids;
@@ -1370,8 +1402,6 @@
HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x0d, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
@@ -1416,8 +1446,8 @@
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
/* Mic boost: 0dB */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
/* Record selector: Front mic */
{0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
@@ -1682,10 +1712,10 @@
SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
/* All HP models */
- SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),
+ SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
/* Lenovo Thinkpad T60/X60/Z6xx */
- SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
/* HP nx6320 (reversed SSID, H/W bug) */
SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
{}
@@ -1694,7 +1724,7 @@
static int patch_ad1981(struct hda_codec *codec)
{
struct ad198x_spec *spec;
- int board_config;
+ int err, board_config;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -1702,6 +1732,13 @@
codec->spec = spec;
+ err = snd_hda_attach_beep_device(codec, 0x10);
+ if (err < 0) {
+ ad198x_free(codec);
+ return err;
+ }
+ set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
+
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
spec->multiout.dac_nids = ad1981_dac_nids;
@@ -1988,9 +2025,6 @@
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
-
HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
@@ -2034,9 +2068,6 @@
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
-
HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
@@ -2066,9 +2097,6 @@
HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
-
HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
@@ -2297,10 +2325,6 @@
{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* ADCs; muted */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{ }
};
@@ -2408,10 +2432,6 @@
{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* ADCs; muted */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* Analog Mix output amp */
{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
{ }
@@ -2483,10 +2503,6 @@
{0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
{0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* ADCs; muted */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* Analog Mix output amp */
{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
{ }
@@ -2890,7 +2906,7 @@
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- if (spec->autocfg.dig_out_pin)
+ if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = AD1988_SPDIF_IN;
@@ -2940,7 +2956,7 @@
static int patch_ad1988(struct hda_codec *codec)
{
struct ad198x_spec *spec;
- int board_config;
+ int err, board_config;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -2960,7 +2976,7 @@
if (board_config == AD1988_AUTO) {
/* automatic parse from the BIOS config */
- int err = ad1988_parse_auto_config(codec);
+ err = ad1988_parse_auto_config(codec);
if (err < 0) {
ad198x_free(codec);
return err;
@@ -2970,6 +2986,13 @@
}
}
+ err = snd_hda_attach_beep_device(codec, 0x10);
+ if (err < 0) {
+ ad198x_free(codec);
+ return err;
+ }
+ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
switch (board_config) {
case AD1988_6STACK:
case AD1988_6STACK_DIG:
@@ -3126,12 +3149,6 @@
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
- /*
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Digital Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
- */
HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -3204,10 +3221,10 @@
{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
/* Port-B (front mic) pin */
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
/* Port-C (rear mic) pin */
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
/* Analog mixer; mute as default */
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
@@ -3240,7 +3257,7 @@
"CD Playback Volume",
"Internal Mic Playback Volume",
"Docking Mic Playback Volume"
- "Beep Playback Volume",
+ /* "Beep Playback Volume", */
"IEC958 Playback Volume",
NULL
};
@@ -3248,6 +3265,7 @@
static int patch_ad1884(struct hda_codec *codec)
{
struct ad198x_spec *spec;
+ int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -3255,6 +3273,13 @@
codec->spec = spec;
+ err = snd_hda_attach_beep_device(codec, 0x10);
+ if (err < 0) {
+ ad198x_free(codec);
+ return err;
+ }
+ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
spec->multiout.dac_nids = ad1884_dac_nids;
@@ -3321,8 +3346,6 @@
HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
@@ -3358,7 +3381,7 @@
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* docking mic boost */
- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
/* Analog mixer - docking mic; mute as default */
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
/* enable EAPD bit */
@@ -3379,10 +3402,6 @@
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
- /*
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
- */
HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -3468,7 +3487,7 @@
static struct snd_pci_quirk ad1984_cfg_tbl[] = {
/* Lenovo Thinkpad T61/X61 */
- SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
{}
};
@@ -3561,8 +3580,6 @@
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
@@ -3622,10 +3639,10 @@
{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* Port-B (front mic) pin */
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
/* Port-C (rear line-in) pin */
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
/* Port-E (rear mic) pin */
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
@@ -3695,8 +3712,6 @@
HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
@@ -3724,8 +3739,6 @@
HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -3836,8 +3849,6 @@
HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -3911,9 +3922,9 @@
SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
- SND_PCI_QUIRK(0x103c, 0x30e6, "HP 6730b", AD1884A_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x30e7, "HP EliteBook 8530p", AD1884A_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x3614, "HP 6730s", AD1884A_LAPTOP),
+ SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
+ SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
+ SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
{}
};
@@ -3921,7 +3932,7 @@
static int patch_ad1884a(struct hda_codec *codec)
{
struct ad198x_spec *spec;
- int board_config;
+ int err, board_config;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -3929,6 +3940,13 @@
codec->spec = spec;
+ err = snd_hda_attach_beep_device(codec, 0x10);
+ if (err < 0) {
+ ad198x_free(codec);
+ return err;
+ }
+ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
spec->multiout.dac_nids = ad1884a_dac_nids;
@@ -3966,6 +3984,14 @@
spec->multiout.dig_out_nid = 0;
codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
codec->patch_ops.init = ad1884a_hp_init;
+ /* set the upper-limit for mixer amp to 0dB for avoiding the
+ * possible damage by overloading
+ */
+ snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
+ (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (1 << AC_AMPCAP_MUTE_SHIFT));
break;
case AD1884A_THINKPAD:
spec->mixers[0] = ad1984a_thinkpad_mixers;
@@ -4083,8 +4109,6 @@
HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
{ } /* end */
};
@@ -4097,8 +4121,6 @@
HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
{ } /* end */
};
@@ -4257,7 +4279,7 @@
static int patch_ad1882(struct hda_codec *codec)
{
struct ad198x_spec *spec;
- int board_config;
+ int err, board_config;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -4265,6 +4287,13 @@
codec->spec = spec;
+ err = snd_hda_attach_beep_device(codec, 0x10);
+ if (err < 0) {
+ ad198x_free(codec);
+ return err;
+ }
+ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
spec->multiout.max_channels = 6;
spec->multiout.num_dacs = 3;
spec->multiout.dac_nids = ad1882_dac_nids;
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index f3ebe83..c921264 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -680,13 +680,13 @@
struct auto_pin_cfg cfg;
/* collect pin default configuration */
- port_e = snd_hda_codec_read(codec, 0x0f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
- port_f = snd_hda_codec_read(codec, 0x10, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+ port_e = snd_hda_codec_get_pincfg(codec, 0x0f);
+ port_f = snd_hda_codec_get_pincfg(codec, 0x10);
spec->front_panel = 1;
if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE ||
get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) {
- port_g = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
- port_h = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+ port_g = snd_hda_codec_get_pincfg(codec, 0x1f);
+ port_h = snd_hda_codec_get_pincfg(codec, 0x20);
spec->channel_modes = cmi9880_channel_modes;
/* no front panel */
if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE ||
@@ -703,8 +703,8 @@
spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
} else {
spec->input_mux = &cmi9880_basic_mux;
- port_spdifi = snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
- port_spdifo = snd_hda_codec_read(codec, 0x12, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+ port_spdifi = snd_hda_codec_get_pincfg(codec, 0x13);
+ port_spdifo = snd_hda_codec_get_pincfg(codec, 0x12);
if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE)
spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE)
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 0177ef8..1f2ad76 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -58,6 +58,7 @@
struct snd_kcontrol_new *mixers[5];
int num_mixers;
+ hda_nid_t vmaster_nid;
const struct hda_verb *init_verbs[5]; /* initialization verbs
* don't forget NULL
@@ -72,6 +73,7 @@
*/
unsigned int cur_eapd;
unsigned int hp_present;
+ unsigned int no_auto_mic;
unsigned int need_dac_fix;
/* capture */
@@ -461,6 +463,29 @@
kfree(codec->spec);
}
+static struct snd_kcontrol_new cxt_capture_mixers[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = conexant_mux_enum_info,
+ .get = conexant_mux_enum_get,
+ .put = conexant_mux_enum_put
+ },
+ {}
+};
+
+static const char *slave_vols[] = {
+ "Headphone Playback Volume",
+ "Speaker Playback Volume",
+ NULL
+};
+
+static const char *slave_sws[] = {
+ "Headphone Playback Switch",
+ "Speaker Playback Switch",
+ NULL
+};
+
static int conexant_build_controls(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
@@ -488,6 +513,32 @@
if (err < 0)
return err;
}
+
+ /* if we have no master control, let's create it */
+ if (spec->vmaster_nid &&
+ !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+ unsigned int vmaster_tlv[4];
+ snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
+ HDA_OUTPUT, vmaster_tlv);
+ err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+ vmaster_tlv, slave_vols);
+ if (err < 0)
+ return err;
+ }
+ if (spec->vmaster_nid &&
+ !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+ err = snd_hda_add_vmaster(codec, "Master Playback Switch",
+ NULL, slave_sws);
+ if (err < 0)
+ return err;
+ }
+
+ if (spec->input_mux) {
+ err = snd_hda_add_new_ctls(codec, cxt_capture_mixers);
+ if (err < 0)
+ return err;
+ }
+
return 0;
}
@@ -719,13 +770,6 @@
}
static struct snd_kcontrol_new cxt5045_mixers[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = conexant_mux_enum_info,
- .get = conexant_mux_enum_get,
- .put = conexant_mux_enum_put
- },
HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
@@ -759,13 +803,6 @@
};
static struct snd_kcontrol_new cxt5045_mixers_hp530[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = conexant_mux_enum_info,
- .get = conexant_mux_enum_get,
- .put = conexant_mux_enum_put
- },
HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
@@ -1002,15 +1039,9 @@
};
static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x30a5, "HP", CXT5045_LAPTOP_HPSENSE),
- SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
- SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP_HPSENSE),
- SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP_HPSENSE),
- SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP_HPSENSE),
- SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
- SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE),
SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530),
- SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series",
+ CXT5045_LAPTOP_HPSENSE),
SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE),
SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
@@ -1020,8 +1051,8 @@
SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE),
SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE),
SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE),
- SND_PCI_QUIRK(0x1631, 0xc106, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE),
- SND_PCI_QUIRK(0x1631, 0xc107, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE),
+ SND_PCI_QUIRK_MASK(0x1631, 0xff00, 0xc100, "Packard Bell",
+ CXT5045_LAPTOP_HPMICSENSE),
SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE),
{}
};
@@ -1035,6 +1066,7 @@
if (!spec)
return -ENOMEM;
codec->spec = spec;
+ codec->pin_amp_workaround = 1;
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = ARRAY_SIZE(cxt5045_dac_nids);
@@ -1134,7 +1166,7 @@
/* Conexant 5047 specific */
#define CXT5047_SPDIF_OUT 0x11
-static hda_nid_t cxt5047_dac_nids[2] = { 0x10, 0x1c };
+static hda_nid_t cxt5047_dac_nids[1] = { 0x10 }; /* 0x1c */
static hda_nid_t cxt5047_adc_nids[1] = { 0x12 };
static hda_nid_t cxt5047_capsrc_nids[1] = { 0x1a };
@@ -1142,20 +1174,6 @@
{ 2, NULL },
};
-static struct hda_input_mux cxt5047_capture_source = {
- .num_items = 1,
- .items = {
- { "Mic", 0x2 },
- }
-};
-
-static struct hda_input_mux cxt5047_hp_capture_source = {
- .num_items = 1,
- .items = {
- { "ExtMic", 0x2 },
- }
-};
-
static struct hda_input_mux cxt5047_toshiba_capture_source = {
.num_items = 2,
.items = {
@@ -1179,7 +1197,11 @@
* the headphone jack
*/
bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0,
+ /* NOTE: Conexat codec needs the index for *OUTPUT* amp of
+ * pin widgets unlike other codecs. In this case, we need to
+ * set index 0x01 for the volume from the mixer amp 0x19.
+ */
+ snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01,
HDA_AMP_MUTE, bits);
bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE;
snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
@@ -1187,16 +1209,6 @@
return 1;
}
-/* bind volumes of both NID 0x13 (Headphones) and 0x1d (Speakers) */
-static struct hda_bind_ctls cxt5047_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x13, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
/* mute internal speaker if HP is plugged */
static void cxt5047_hp_automute(struct hda_codec *codec)
{
@@ -1207,27 +1219,8 @@
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
- /* Mute/Unmute PCM 2 for good measure - some systems need this */
- snd_hda_codec_amp_stereo(codec, 0x1c, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-}
-
-/* mute internal speaker if HP is plugged */
-static void cxt5047_hp2_automute(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- unsigned int bits;
-
- spec->hp_present = snd_hda_codec_read(codec, 0x13, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-
- bits = spec->hp_present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
- /* Mute/Unmute PCM 2 for good measure - some systems need this */
- snd_hda_codec_amp_stereo(codec, 0x1c, HDA_OUTPUT, 0,
+ /* See the note in cxt5047_hp_master_sw_put */
+ snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01,
HDA_AMP_MUTE, bits);
}
@@ -1268,90 +1261,35 @@
}
}
-/* unsolicited event for HP jack sensing - non-EAPD systems */
-static void cxt5047_hp2_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- res >>= 26;
- switch (res) {
- case CONEXANT_HP_EVENT:
- cxt5047_hp2_automute(codec);
- break;
- case CONEXANT_MIC_EVENT:
- cxt5047_hp_automic(codec);
- break;
- }
-}
-
-static struct snd_kcontrol_new cxt5047_mixers[] = {
- HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Gain Volume", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Gain Switch", 0x1a, 0x0, HDA_OUTPUT),
+static struct snd_kcontrol_new cxt5047_base_mixers[] = {
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x19, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x19, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x1a, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM-2 Volume", 0x1c, 0x00, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM-2 Switch", 0x1c, 0x00, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x00, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x1d, 0x00, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = cxt_eapd_info,
+ .get = cxt_eapd_get,
+ .put = cxt5047_hp_master_sw_put,
+ .private_value = 0x13,
+ },
+
+ {}
+};
+
+static struct snd_kcontrol_new cxt5047_hp_spk_mixers[] = {
+ /* See the note in cxt5047_hp_master_sw_put */
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x01, HDA_OUTPUT),
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x13, 0x00, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x13, 0x00, HDA_OUTPUT),
-
{}
};
-static struct snd_kcontrol_new cxt5047_toshiba_mixers[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = conexant_mux_enum_info,
- .get = conexant_mux_enum_get,
- .put = conexant_mux_enum_put
- },
- HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT),
- HDA_BIND_VOL("Master Playback Volume", &cxt5047_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .info = cxt_eapd_info,
- .get = cxt_eapd_get,
- .put = cxt5047_hp_master_sw_put,
- .private_value = 0x13,
- },
-
- {}
-};
-
-static struct snd_kcontrol_new cxt5047_hp_mixers[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = conexant_mux_enum_info,
- .get = conexant_mux_enum_get,
- .put = conexant_mux_enum_put
- },
- HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19,0x02,HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT),
+static struct snd_kcontrol_new cxt5047_hp_only_mixers[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0x13, 0x00, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .info = cxt_eapd_info,
- .get = cxt_eapd_get,
- .put = cxt5047_hp_master_sw_put,
- .private_value = 0x13,
- },
{ } /* end */
};
@@ -1362,8 +1300,8 @@
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 },
/* HP, Speaker */
{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- {0x13, AC_VERB_SET_CONNECT_SEL,0x1},
- {0x1d, AC_VERB_SET_CONNECT_SEL,0x0},
+ {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, /* mixer(0x19) */
+ {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mixer(0x19) */
/* Record selector: Mic */
{0x12, AC_VERB_SET_CONNECT_SEL,0x03},
{0x19, AC_VERB_SET_AMP_GAIN_MUTE,
@@ -1383,30 +1321,7 @@
/* configuration for Toshiba Laptops */
static struct hda_verb cxt5047_toshiba_init_verbs[] = {
- {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x0 }, /* default on */
- /* pin sensing on HP and Mic jacks */
- {0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- /* Speaker routing */
- {0x1d, AC_VERB_SET_CONNECT_SEL,0x1},
- {}
-};
-
-/* configuration for HP Laptops */
-static struct hda_verb cxt5047_hp_init_verbs[] = {
- /* pin sensing on HP jack */
- {0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- /* 0x13 is actually shared by both HP and speaker;
- * setting the connection to 0 (=0x19) makes the master volume control
- * working mysteriouslly...
- */
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Record selector: Ext Mic */
- {0x12, AC_VERB_SET_CONNECT_SEL,0x03},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE,
- AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
- /* Speaker routing */
- {0x1d, AC_VERB_SET_CONNECT_SEL,0x1},
+ {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x0}, /* default off */
{}
};
@@ -1571,11 +1486,9 @@
};
static struct snd_pci_quirk cxt5047_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x30a0, "HP DV1000", CXT5047_LAPTOP),
SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
- SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV2000T/DV3000T", CXT5047_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2000Z", CXT5047_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6700", CXT5047_LAPTOP),
+ SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series",
+ CXT5047_LAPTOP),
SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD),
{}
};
@@ -1589,6 +1502,7 @@
if (!spec)
return -ENOMEM;
codec->spec = spec;
+ codec->pin_amp_workaround = 1;
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = ARRAY_SIZE(cxt5047_dac_nids);
@@ -1597,9 +1511,8 @@
spec->num_adc_nids = 1;
spec->adc_nids = cxt5047_adc_nids;
spec->capsrc_nids = cxt5047_capsrc_nids;
- spec->input_mux = &cxt5047_capture_source;
spec->num_mixers = 1;
- spec->mixers[0] = cxt5047_mixers;
+ spec->mixers[0] = cxt5047_base_mixers;
spec->num_init_verbs = 1;
spec->init_verbs[0] = cxt5047_init_verbs;
spec->spdif_route = 0;
@@ -1613,21 +1526,22 @@
cxt5047_cfg_tbl);
switch (board_config) {
case CXT5047_LAPTOP:
- codec->patch_ops.unsol_event = cxt5047_hp2_unsol_event;
+ spec->num_mixers = 2;
+ spec->mixers[1] = cxt5047_hp_spk_mixers;
+ codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
break;
case CXT5047_LAPTOP_HP:
- spec->input_mux = &cxt5047_hp_capture_source;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = cxt5047_hp_init_verbs;
- spec->mixers[0] = cxt5047_hp_mixers;
+ spec->num_mixers = 2;
+ spec->mixers[1] = cxt5047_hp_only_mixers;
codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
codec->patch_ops.init = cxt5047_hp_init;
break;
case CXT5047_LAPTOP_EAPD:
spec->input_mux = &cxt5047_toshiba_capture_source;
+ spec->num_mixers = 2;
+ spec->mixers[1] = cxt5047_hp_spk_mixers;
spec->num_init_verbs = 2;
spec->init_verbs[1] = cxt5047_toshiba_init_verbs;
- spec->mixers[0] = cxt5047_toshiba_mixers;
codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
break;
#ifdef CONFIG_SND_DEBUG
@@ -1638,6 +1552,7 @@
codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
#endif
}
+ spec->vmaster_nid = 0x13;
return 0;
}
@@ -1673,8 +1588,11 @@
/* toggle input of built-in and mic jack appropriately */
static void cxt5051_portb_automic(struct hda_codec *codec)
{
+ struct conexant_spec *spec = codec->spec;
unsigned int present;
+ if (spec->no_auto_mic)
+ return;
present = snd_hda_codec_read(codec, 0x17, 0,
AC_VERB_GET_PIN_SENSE, 0) &
AC_PINSENSE_PRESENCE;
@@ -1690,6 +1608,8 @@
unsigned int present;
hda_nid_t new_adc;
+ if (spec->no_auto_mic)
+ return;
present = snd_hda_codec_read(codec, 0x18, 0,
AC_VERB_GET_PIN_SENSE, 0) &
AC_PINSENSE_PRESENCE;
@@ -1776,6 +1696,22 @@
{}
};
+static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = {
+ HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Switch", 0x14, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = cxt_eapd_info,
+ .get = cxt_eapd_get,
+ .put = cxt5051_hp_master_sw_put,
+ .private_value = 0x1a,
+ },
+
+ {}
+};
+
static struct hda_verb cxt5051_init_verbs[] = {
/* Line in, Mic */
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
@@ -1806,6 +1742,66 @@
{ } /* end */
};
+static struct hda_verb cxt5051_hp_dv6736_init_verbs[] = {
+ /* Line in, Mic */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
+ /* SPK */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* HP, Amp */
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* DAC1 */
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* Record selector: Int mic */
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x1},
+ /* SPDIF route: PCM */
+ {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* EAPD */
+ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
+ {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
+ { } /* end */
+};
+
+static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = {
+ /* Line in, Mic */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+ /* SPK */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* HP, Amp */
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* Docking HP */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x19, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* DAC1 */
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* Record selector: Int mic */
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
+ /* SPDIF route: PCM */
+ {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* EAPD */
+ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
+ {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT},
+ {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
+ { } /* end */
+};
+
/* initialize jack-sensing, too */
static int cxt5051_init(struct hda_codec *codec)
{
@@ -1823,18 +1819,24 @@
enum {
CXT5051_LAPTOP, /* Laptops w/ EAPD support */
CXT5051_HP, /* no docking */
+ CXT5051_HP_DV6736, /* HP without mic switch */
+ CXT5051_LENOVO_X200, /* Lenovo X200 laptop */
CXT5051_MODELS
};
static const char *cxt5051_models[CXT5051_MODELS] = {
[CXT5051_LAPTOP] = "laptop",
[CXT5051_HP] = "hp",
+ [CXT5051_HP_DV6736] = "hp-dv6736",
+ [CXT5051_LENOVO_X200] = "lenovo-x200",
};
static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6736", CXT5051_HP_DV6736),
SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
CXT5051_LAPTOP),
SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
+ SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200),
{}
};
@@ -1847,6 +1849,7 @@
if (!spec)
return -ENOMEM;
codec->spec = spec;
+ codec->pin_amp_workaround = 1;
codec->patch_ops = conexant_patch_ops;
codec->patch_ops.init = cxt5051_init;
@@ -1867,17 +1870,22 @@
spec->cur_adc = 0;
spec->cur_adc_idx = 0;
+ codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
+
board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
cxt5051_models,
cxt5051_cfg_tbl);
switch (board_config) {
case CXT5051_HP:
- codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
spec->mixers[0] = cxt5051_hp_mixers;
break;
- default:
- case CXT5051_LAPTOP:
- codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
+ case CXT5051_HP_DV6736:
+ spec->init_verbs[0] = cxt5051_hp_dv6736_init_verbs;
+ spec->mixers[0] = cxt5051_hp_dv6736_mixers;
+ spec->no_auto_mic = 1;
+ break;
+ case CXT5051_LENOVO_X200:
+ spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs;
break;
}
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 6c26afc..8209779 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -30,6 +30,7 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_beep.h"
#define ALC880_FRONT_EVENT 0x01
#define ALC880_DCVOL_EVENT 0x02
@@ -77,6 +78,7 @@
ALC260_ACER,
ALC260_WILL,
ALC260_REPLACER_672V,
+ ALC260_FAVORIT100,
#ifdef CONFIG_SND_DEBUG
ALC260_TEST,
#endif
@@ -103,6 +105,7 @@
ALC262_NEC,
ALC262_TOSHIBA_S06,
ALC262_TOSHIBA_RX1,
+ ALC262_TYAN,
ALC262_AUTO,
ALC262_MODEL_LAST /* last tag */
};
@@ -238,6 +241,13 @@
ALC883_MODEL_LAST,
};
+/* styles of capture selection */
+enum {
+ CAPT_MUX = 0, /* only mux based */
+ CAPT_MIX, /* only mixer based */
+ CAPT_1MUX_MIX, /* first mux and other mixers */
+};
+
/* for GPIO Poll */
#define GPIO_MASK 0x03
@@ -246,6 +256,7 @@
struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
unsigned int num_mixers;
struct snd_kcontrol_new *cap_mixer; /* capture mixer */
+ unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
const struct hda_verb *init_verbs[5]; /* initialization verbs
* don't forget NULL
@@ -269,13 +280,15 @@
* dig_out_nid and hp_nid are optional
*/
hda_nid_t alt_dac_nid;
+ hda_nid_t slave_dig_outs[3]; /* optional - for auto-parsing */
+ int dig_out_type;
/* capture */
unsigned int num_adc_nids;
hda_nid_t *adc_nids;
hda_nid_t *capsrc_nids;
hda_nid_t dig_in_nid; /* digital-in NID; optional */
- unsigned char is_mix_capture; /* matrix-style capture (non-mux) */
+ int capture_style; /* capture style (CAPT_*) */
/* capture source */
unsigned int num_mux_defs;
@@ -293,7 +306,7 @@
/* dynamic controls, init_verbs and input_mux */
struct auto_pin_cfg autocfg;
struct snd_array kctls;
- struct hda_input_mux private_imux;
+ struct hda_input_mux private_imux[3];
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
/* hooks */
@@ -305,6 +318,9 @@
unsigned int jack_present: 1;
unsigned int master_sw: 1;
+ /* other flags */
+ unsigned int no_analog :1; /* digital I/O only */
+
/* for virtual master */
hda_nid_t vmaster_nid;
#ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -314,13 +330,6 @@
/* for PLL fix */
hda_nid_t pll_nid;
unsigned int pll_coef_idx, pll_coef_bit;
-
-#ifdef SND_HDA_NEEDS_RESUME
-#define ALC_MAX_PINS 16
- unsigned int num_pins;
- hda_nid_t pin_nids[ALC_MAX_PINS];
- unsigned int pin_cfgs[ALC_MAX_PINS];
-#endif
};
/*
@@ -336,6 +345,7 @@
hda_nid_t *dac_nids;
hda_nid_t dig_out_nid; /* optional */
hda_nid_t hp_nid; /* optional */
+ hda_nid_t *slave_dig_outs;
unsigned int num_adc_nids;
hda_nid_t *adc_nids;
hda_nid_t *capsrc_nids;
@@ -392,7 +402,8 @@
mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
imux = &spec->input_mux[mux_idx];
- if (spec->is_mix_capture) {
+ if (spec->capture_style &&
+ !(spec->capture_style == CAPT_1MUX_MIX && !adc_idx)) {
/* Matrix-mixer style (e.g. ALC882) */
unsigned int *cur_val = &spec->cur_mux[adc_idx];
unsigned int i, idx;
@@ -750,6 +761,24 @@
#endif /* CONFIG_SND_DEBUG */
/*
+ * set up the input pin config (depending on the given auto-pin type)
+ */
+static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
+ int auto_pin_type)
+{
+ unsigned int val = PIN_IN;
+
+ if (auto_pin_type <= AUTO_PIN_FRONT_MIC) {
+ unsigned int pincap;
+ pincap = snd_hda_query_pin_caps(codec, nid);
+ pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+ if (pincap & AC_PINCAP_VREF_80)
+ val = PIN_VREF80;
+ }
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+}
+
+/*
*/
static void add_mixer(struct alc_spec *spec, struct snd_kcontrol_new *mix)
{
@@ -810,6 +839,7 @@
spec->multiout.num_dacs = preset->num_dacs;
spec->multiout.dac_nids = preset->dac_nids;
spec->multiout.dig_out_nid = preset->dig_out_nid;
+ spec->multiout.slave_dig_outs = preset->slave_dig_outs;
spec->multiout.hp_nid = preset->hp_nid;
spec->num_mux_defs = preset->num_mux_defs;
@@ -921,7 +951,7 @@
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
}
#else
-#define alc_mic_automute(codec) /* NOP */
+#define alc_mic_automute(codec) do {} while(0) /* NOP */
#endif /* disabled */
/* unsolicited event for HP jack sensing */
@@ -952,7 +982,7 @@
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0);
tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7);
- if ((tmp & 0xf0) == 2)
+ if ((tmp & 0xf0) == 0x20)
/* alc888S-VC */
snd_hda_codec_read(codec, 0x20, 0,
AC_VERB_SET_PROC_COEF, 0x830);
@@ -991,8 +1021,7 @@
nid = 0x1d;
if (codec->vendor_id == 0x10ec0260)
nid = 0x17;
- ass = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0);
+ ass = snd_hda_codec_get_pincfg(codec, nid);
if (!(ass & 1) && !(ass & 0x100000))
return;
if ((ass >> 30) != 1) /* no physical connection */
@@ -1166,16 +1195,8 @@
return;
cfg = pinfix[quirk->value];
- for (; cfg->nid; cfg++) {
- int i;
- u32 val = cfg->val;
- for (i = 0; i < 4; i++) {
- snd_hda_codec_write(codec, cfg->nid, 0,
- AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i,
- val & 0xff);
- val >>= 8;
- }
- }
+ for (; cfg->nid; cfg++)
+ snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
}
/*
@@ -1375,8 +1396,6 @@
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
{ } /* end */
};
@@ -1483,8 +1502,6 @@
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1578,8 +1595,7 @@
snd_hda_mixer_amp_switch_put);
}
-#define DEFINE_CAPMIX(num) \
-static struct snd_kcontrol_new alc_capture_mixer ## num[] = { \
+#define _DEFINE_CAPMIX(num) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = "Capture Switch", \
@@ -1600,7 +1616,9 @@
.get = alc_cap_vol_get, \
.put = alc_cap_vol_put, \
.tlv = { .c = alc_cap_vol_tlv }, \
- }, \
+ }
+
+#define _DEFINE_CAPSRC(num) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
/* .name = "Capture Source", */ \
@@ -1609,15 +1627,28 @@
.info = alc_mux_enum_info, \
.get = alc_mux_enum_get, \
.put = alc_mux_enum_put, \
- }, \
- { } /* end */ \
+ }
+
+#define DEFINE_CAPMIX(num) \
+static struct snd_kcontrol_new alc_capture_mixer ## num[] = { \
+ _DEFINE_CAPMIX(num), \
+ _DEFINE_CAPSRC(num), \
+ { } /* end */ \
+}
+
+#define DEFINE_CAPMIX_NOSRC(num) \
+static struct snd_kcontrol_new alc_capture_mixer_nosrc ## num[] = { \
+ _DEFINE_CAPMIX(num), \
+ { } /* end */ \
}
/* up to three ADCs */
DEFINE_CAPMIX(1);
DEFINE_CAPMIX(2);
DEFINE_CAPMIX(3);
-
+DEFINE_CAPMIX_NOSRC(1);
+DEFINE_CAPMIX_NOSRC(2);
+DEFINE_CAPMIX_NOSRC(3);
/*
* ALC880 5-stack model
@@ -1706,8 +1737,6 @@
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Channel Mode",
@@ -1884,13 +1913,6 @@
{ } /* end */
};
-/* additional mixers to alc880_asus_mixer */
-static struct snd_kcontrol_new alc880_pcbeep_mixer[] = {
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
- { } /* end */
-};
-
/* TCL S700 */
static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -1923,8 +1945,6 @@
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Channel Mode",
@@ -1999,6 +2019,13 @@
static void alc_free_kctls(struct hda_codec *codec);
+/* additional beep mixers; the actual parameters are overwritten at build */
+static struct snd_kcontrol_new alc_beep_mixer[] = {
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_INPUT),
+ { } /* end */
+};
+
static int alc_build_controls(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -2020,11 +2047,13 @@
spec->multiout.dig_out_nid);
if (err < 0)
return err;
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
+ if (!spec->no_analog) {
+ err = snd_hda_create_spdif_share_sw(codec,
+ &spec->multiout);
+ if (err < 0)
+ return err;
+ spec->multiout.share_spdif = 1;
+ }
}
if (spec->dig_in_nid) {
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
@@ -2032,8 +2061,24 @@
return err;
}
+ /* create beep controls if needed */
+ if (spec->beep_amp) {
+ struct snd_kcontrol_new *knew;
+ for (knew = alc_beep_mixer; knew->name; knew++) {
+ struct snd_kcontrol *kctl;
+ kctl = snd_ctl_new1(knew, codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->private_value = spec->beep_amp;
+ err = snd_hda_ctl_add(codec, kctl);
+ if (err < 0)
+ return err;
+ }
+ }
+
/* if we have no master control, let's create it */
- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+ if (!spec->no_analog &&
+ !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
unsigned int vmaster_tlv[4];
snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
HDA_OUTPUT, vmaster_tlv);
@@ -2042,7 +2087,8 @@
if (err < 0)
return err;
}
- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+ if (!spec->no_analog &&
+ !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
err = snd_hda_add_vmaster(codec, "Master Playback Switch",
NULL, alc_slave_sws);
if (err < 0)
@@ -2951,6 +2997,14 @@
stream_tag, format, substream);
}
+static int alc880_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct alc_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+}
+
static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
@@ -3034,7 +3088,8 @@
.ops = {
.open = alc880_dig_playback_pcm_open,
.close = alc880_dig_playback_pcm_close,
- .prepare = alc880_dig_playback_pcm_prepare
+ .prepare = alc880_dig_playback_pcm_prepare,
+ .cleanup = alc880_dig_playback_pcm_cleanup
},
};
@@ -3061,6 +3116,9 @@
codec->num_pcms = 1;
codec->pcm_info = info;
+ if (spec->no_analog)
+ goto skip_analog;
+
info->name = spec->stream_name_analog;
if (spec->stream_analog_playback) {
if (snd_BUG_ON(!spec->multiout.dac_nids))
@@ -3084,12 +3142,17 @@
}
}
+ skip_analog:
/* SPDIF for stream index #1 */
if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
codec->num_pcms = 2;
+ codec->slave_dig_outs = spec->multiout.slave_dig_outs;
info = spec->pcm_rec + 1;
info->name = spec->stream_name_digital;
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
+ if (spec->dig_out_type)
+ info->pcm_type = spec->dig_out_type;
+ else
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
if (spec->multiout.dig_out_nid &&
spec->stream_digital_playback) {
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback);
@@ -3104,6 +3167,9 @@
codec->spdif_status_reset = 1;
}
+ if (spec->no_analog)
+ return 0;
+
/* If the use of more than one ADC is requested for the current
* model, configure a second analog capture-only PCM.
*/
@@ -3162,65 +3228,17 @@
alc_free_kctls(codec);
kfree(spec);
- codec->spec = NULL; /* to be sure */
+ snd_hda_detach_beep_device(codec);
}
#ifdef SND_HDA_NEEDS_RESUME
-static void store_pin_configs(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t nid, end_nid;
-
- end_nid = codec->start_nid + codec->num_nodes;
- for (nid = codec->start_nid; nid < end_nid; nid++) {
- unsigned int wid_caps = get_wcaps(codec, nid);
- unsigned int wid_type =
- (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
- if (wid_type != AC_WID_PIN)
- continue;
- if (spec->num_pins >= ARRAY_SIZE(spec->pin_nids))
- break;
- spec->pin_nids[spec->num_pins] = nid;
- spec->pin_cfgs[spec->num_pins] =
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0);
- spec->num_pins++;
- }
-}
-
-static void resume_pin_configs(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_pins; i++) {
- hda_nid_t pin_nid = spec->pin_nids[i];
- unsigned int pin_config = spec->pin_cfgs[i];
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
- pin_config & 0x000000ff);
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
- (pin_config & 0x0000ff00) >> 8);
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
- (pin_config & 0x00ff0000) >> 16);
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
- pin_config >> 24);
- }
-}
-
static int alc_resume(struct hda_codec *codec)
{
- resume_pin_configs(codec);
codec->patch_ops.init(codec);
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
return 0;
}
-#else
-#define store_pin_configs(codec)
#endif
/*
@@ -3559,7 +3577,7 @@
SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG),
SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST),
SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST),
- SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS), /* default ASUS */
+ SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_ASUS), /* default ASUS */
SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST),
SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST),
SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST),
@@ -3602,7 +3620,8 @@
SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST), /* default Intel */
+ /* default Intel */
+ SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_3ST),
SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG),
SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG),
{}
@@ -3782,7 +3801,7 @@
.input_mux = &alc880_capture_source,
},
[ALC880_UNIWILL_DIG] = {
- .mixers = { alc880_asus_mixer, alc880_pcbeep_mixer },
+ .mixers = { alc880_asus_mixer },
.init_verbs = { alc880_volume_init_verbs,
alc880_pin_asus_init_verbs },
.num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
@@ -3820,8 +3839,7 @@
.init_hook = alc880_uniwill_p53_hp_automute,
},
[ALC880_FUJITSU] = {
- .mixers = { alc880_fujitsu_mixer,
- alc880_pcbeep_mixer, },
+ .mixers = { alc880_fujitsu_mixer },
.init_verbs = { alc880_volume_init_verbs,
alc880_uniwill_p53_init_verbs,
alc880_beep_init_verbs },
@@ -4114,7 +4132,7 @@
static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec,
const struct auto_pin_cfg *cfg)
{
- struct hda_input_mux *imux = &spec->private_imux;
+ struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx;
for (i = 0; i < AUTO_PIN_LAST; i++) {
@@ -4202,11 +4220,9 @@
for (i = 0; i < AUTO_PIN_LAST; i++) {
hda_nid_t nid = spec->autocfg.input_pins[i];
if (alc880_is_input_pin(nid)) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- i <= AUTO_PIN_FRONT_MIC ?
- PIN_VREF80 : PIN_IN);
- if (nid != ALC880_PIN_CD_NID)
+ alc_set_input_pin(codec, nid, i);
+ if (nid != ALC880_PIN_CD_NID &&
+ (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_MUTE);
@@ -4221,7 +4237,7 @@
static int alc880_parse_auto_config(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- int err;
+ int i, err;
static hda_nid_t alc880_ignore[] = { 0x1d, 0 };
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
@@ -4252,8 +4268,23 @@
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- if (spec->autocfg.dig_out_pin)
- spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
+ /* check multiple SPDIF-out (for recent codecs) */
+ for (i = 0; i < spec->autocfg.dig_outs; i++) {
+ hda_nid_t dig_nid;
+ err = snd_hda_get_connections(codec,
+ spec->autocfg.dig_out_pins[i],
+ &dig_nid, 1);
+ if (err < 0)
+ continue;
+ if (!i)
+ spec->multiout.dig_out_nid = dig_nid;
+ else {
+ spec->multiout.slave_dig_outs = spec->slave_dig_outs;
+ spec->slave_dig_outs[i - 1] = dig_nid;
+ if (i == ARRAY_SIZE(spec->slave_dig_outs) - 1)
+ break;
+ }
+ }
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = ALC880_DIGIN_NID;
@@ -4263,9 +4294,8 @@
add_verb(spec, alc880_volume_init_verbs);
spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux;
+ spec->input_mux = &spec->private_imux[0];
- store_pin_configs(codec);
return 1;
}
@@ -4280,21 +4310,33 @@
alc_inithook(codec);
}
+static void set_capture_mixer(struct alc_spec *spec)
+{
+ static struct snd_kcontrol_new *caps[2][3] = {
+ { alc_capture_mixer_nosrc1,
+ alc_capture_mixer_nosrc2,
+ alc_capture_mixer_nosrc3 },
+ { alc_capture_mixer1,
+ alc_capture_mixer2,
+ alc_capture_mixer3 },
+ };
+ if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) {
+ int mux;
+ if (spec->input_mux && spec->input_mux->num_items > 1)
+ mux = 1;
+ else
+ mux = 0;
+ spec->cap_mixer = caps[mux][spec->num_adc_nids - 1];
+ }
+}
+
+#define set_beep_amp(spec, nid, idx, dir) \
+ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir))
+
/*
* OK, here we have finally the patch for ALC880
*/
-static void set_capture_mixer(struct alc_spec *spec)
-{
- static struct snd_kcontrol_new *caps[3] = {
- alc_capture_mixer1,
- alc_capture_mixer2,
- alc_capture_mixer3,
- };
- if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3)
- spec->cap_mixer = caps[spec->num_adc_nids - 1];
-}
-
static int patch_alc880(struct hda_codec *codec)
{
struct alc_spec *spec;
@@ -4330,6 +4372,12 @@
}
}
+ err = snd_hda_attach_beep_device(codec, 0x1);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ }
+
if (board_config != ALC880_AUTO)
setup_preset(spec, &alc880_presets[board_config]);
@@ -4356,6 +4404,7 @@
}
}
set_capture_mixer(spec);
+ set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
spec->vmaster_nid = 0x0c;
@@ -4463,6 +4512,26 @@
},
},
};
+
+/* Maxdata Favorit 100XS */
+static struct hda_input_mux alc260_favorit100_capture_sources[2] = {
+ {
+ .num_items = 2,
+ .items = {
+ { "Line/Mic", 0x0 },
+ { "CD", 0x4 },
+ },
+ },
+ {
+ .num_items = 3,
+ .items = {
+ { "Line/Mic", 0x0 },
+ { "CD", 0x4 },
+ { "Mixer", 0x5 },
+ },
+ },
+};
+
/*
* This is just place-holder, so there's something for alc_build_pcms to look
* at when it calculates the maximum number of channels. ALC260 has no mixer
@@ -4505,12 +4574,6 @@
{ } /* end */
};
-static struct snd_kcontrol_new alc260_pc_beep_mixer[] = {
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x07, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x07, 0x05, HDA_INPUT),
- { } /* end */
-};
-
/* update HP, line and mono out pins according to the master switch */
static void alc260_hp_master_update(struct hda_codec *codec,
hda_nid_t hp, hda_nid_t line,
@@ -4702,8 +4765,6 @@
HDA_CODEC_VOLUME("Mic/Line Playback Volume", 0x07, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Mic/Line Playback Switch", 0x07, 0x0, HDA_INPUT),
ALC_PIN_MODE("Mic/Line Jack Mode", 0x12, ALC_PIN_DIR_IN),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT),
HDA_CODEC_VOLUME("Speaker Playback Volume", 0x09, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Speaker Playback Switch", 0x09, 2, HDA_INPUT),
{ } /* end */
@@ -4748,8 +4809,18 @@
HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT),
+ { } /* end */
+};
+
+/* Maxdata Favorit 100XS: one output and one input (0x12) jack
+ */
+static struct snd_kcontrol_new alc260_favorit100_mixer[] = {
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT),
+ ALC_PIN_MODE("Output Jack Mode", 0x0f, ALC_PIN_DIR_INOUT),
+ HDA_CODEC_VOLUME("Line/Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Line/Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
+ ALC_PIN_MODE("Line/Mic Jack Mode", 0x12, ALC_PIN_DIR_IN),
{ } /* end */
};
@@ -4767,8 +4838,6 @@
ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT),
{ } /* end */
};
@@ -5126,6 +5195,89 @@
{ }
};
+/* Initialisation sequence for Maxdata Favorit 100XS
+ * (adapted from Acer init verbs).
+ */
+static struct hda_verb alc260_favorit100_init_verbs[] = {
+ /* GPIO 0 enables the output jack.
+ * Turn this on and rely on the standard mute
+ * methods whenever the user wants to turn these outputs off.
+ */
+ {0x01, AC_VERB_SET_GPIO_MASK, 0x01},
+ {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
+ {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
+ /* Line/Mic input jack is connected to Mic1 pin */
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
+ /* Ensure all other unused pins are disabled and muted. */
+ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ /* Disable digital (SPDIF) pins */
+ {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
+ {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
+
+ /* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum
+ * bus when acting as outputs.
+ */
+ {0x0b, AC_VERB_SET_CONNECT_SEL, 0},
+ {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
+
+ /* Start with output sum widgets muted and their output gains at min */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+ /* Unmute Line-out pin widget amp left and right
+ * (no equiv mixer ctrl)
+ */
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* Unmute Mic1 and Line1 pin widget input buffers since they start as
+ * inputs. If the pin mode is changed by the user the pin mode control
+ * will take care of enabling the pin's input/output buffers as needed.
+ * Therefore there's no need to enable the input buffer at this
+ * stage.
+ */
+ {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+ /* Mute capture amp left and right */
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ /* Set ADC connection select to match default mixer setting - mic
+ * (on mic1 pin)
+ */
+ {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+ /* Do similar with the second ADC: mute capture input amp and
+ * set ADC connection to mic to match ALSA's default state.
+ */
+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+ /* Mute all inputs to mixer widget (even unconnected ones) */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
+
+ { }
+};
+
static struct hda_verb alc260_will_verbs[] = {
{0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x0b, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -5272,8 +5424,6 @@
HDA_CODEC_MUTE("LINE2 Playback Switch", 0x07, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT),
HDA_CODEC_VOLUME("LINE-OUT loopback Playback Volume", 0x07, 0x06, HDA_INPUT),
HDA_CODEC_MUTE("LINE-OUT loopback Playback Switch", 0x07, 0x06, HDA_INPUT),
HDA_CODEC_VOLUME("HP-OUT loopback Playback Volume", 0x07, 0x7, HDA_INPUT),
@@ -5471,7 +5621,7 @@
static int alc260_auto_create_analog_input_ctls(struct alc_spec *spec,
const struct auto_pin_cfg *cfg)
{
- struct hda_input_mux *imux = &spec->private_imux;
+ struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx;
for (i = 0; i < AUTO_PIN_LAST; i++) {
@@ -5546,11 +5696,9 @@
for (i = 0; i < AUTO_PIN_LAST; i++) {
hda_nid_t nid = spec->autocfg.input_pins[i];
if (nid >= 0x12) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- i <= AUTO_PIN_FRONT_MIC ?
- PIN_VREF80 : PIN_IN);
- if (nid != ALC260_PIN_CD_NID)
+ alc_set_input_pin(codec, nid, i);
+ if (nid != ALC260_PIN_CD_NID &&
+ (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_MUTE);
@@ -5623,7 +5771,7 @@
spec->multiout.max_channels = 2;
- if (spec->autocfg.dig_out_pin)
+ if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = ALC260_DIGOUT_NID;
if (spec->kctls.list)
add_mixer(spec, spec->kctls.list);
@@ -5631,9 +5779,8 @@
add_verb(spec, alc260_volume_init_verbs);
spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux;
+ spec->input_mux = &spec->private_imux[0];
- store_pin_configs(codec);
return 1;
}
@@ -5670,6 +5817,7 @@
[ALC260_ACER] = "acer",
[ALC260_WILL] = "will",
[ALC260_REPLACER_672V] = "replacer",
+ [ALC260_FAVORIT100] = "favorit100",
#ifdef CONFIG_SND_DEBUG
[ALC260_TEST] = "test",
#endif
@@ -5679,6 +5827,7 @@
static struct snd_pci_quirk alc260_cfg_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_ACER),
SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_ACER),
+ SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FAVORIT100),
SND_PCI_QUIRK(0x103c, 0x2808, "HP d5700", ALC260_HP_3013),
SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_HP_3013),
SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013),
@@ -5701,8 +5850,7 @@
static struct alc_config_preset alc260_presets[] = {
[ALC260_BASIC] = {
.mixers = { alc260_base_output_mixer,
- alc260_input_mixer,
- alc260_pc_beep_mixer },
+ alc260_input_mixer },
.init_verbs = { alc260_init_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
@@ -5781,6 +5929,18 @@
.num_mux_defs = ARRAY_SIZE(alc260_acer_capture_sources),
.input_mux = alc260_acer_capture_sources,
},
+ [ALC260_FAVORIT100] = {
+ .mixers = { alc260_favorit100_mixer },
+ .init_verbs = { alc260_favorit100_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc260_dac_nids),
+ .dac_nids = alc260_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
+ .adc_nids = alc260_dual_adc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc260_modes),
+ .channel_mode = alc260_modes,
+ .num_mux_defs = ARRAY_SIZE(alc260_favorit100_capture_sources),
+ .input_mux = alc260_favorit100_capture_sources,
+ },
[ALC260_WILL] = {
.mixers = { alc260_will_mixer },
.init_verbs = { alc260_init_verbs, alc260_will_verbs },
@@ -5857,6 +6017,12 @@
}
}
+ err = snd_hda_attach_beep_device(codec, 0x1);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ }
+
if (board_config != ALC260_AUTO)
setup_preset(spec, &alc260_presets[board_config]);
@@ -5882,6 +6048,7 @@
}
}
set_capture_mixer(spec);
+ set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
spec->vmaster_nid = 0x08;
@@ -6053,8 +6220,6 @@
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
{ } /* end */
};
@@ -6081,8 +6246,6 @@
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
{ } /* end */
};
@@ -6134,8 +6297,6 @@
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
{ } /* end */
};
@@ -6244,8 +6405,10 @@
HDA_CODEC_MUTE("Headphone Playback Switch", 0x18, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
+ /* FIXME: this looks suspicious...
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ */
{ } /* end */
};
@@ -6877,19 +7040,9 @@
for (i = 0; i < AUTO_PIN_LAST; i++) {
hda_nid_t nid = spec->autocfg.input_pins[i];
- unsigned int vref;
if (!nid)
continue;
- vref = PIN_IN;
- if (1 /*i <= AUTO_PIN_FRONT_MIC*/) {
- unsigned int pincap;
- pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
- if ((pincap >> AC_PINCAP_VREF_SHIFT) &
- AC_PINCAP_VREF_80)
- vref = PIN_VREF80;
- }
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, vref);
+ alc_set_input_pin(codec, nid, AUTO_PIN_FRONT_MIC /*i*/);
if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
@@ -6900,18 +7053,21 @@
static void alc882_auto_init_input_src(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- const struct hda_input_mux *imux = spec->input_mux;
int c;
for (c = 0; c < spec->num_adc_nids; c++) {
hda_nid_t conn_list[HDA_MAX_NUM_INPUTS];
hda_nid_t nid = spec->capsrc_nids[c];
+ unsigned int mux_idx;
+ const struct hda_input_mux *imux;
int conns, mute, idx, item;
conns = snd_hda_get_connections(codec, nid, conn_list,
ARRAY_SIZE(conn_list));
if (conns < 0)
continue;
+ mux_idx = c >= spec->num_mux_defs ? 0 : c;
+ imux = &spec->input_mux[mux_idx];
for (idx = 0; idx < conns; idx++) {
/* if the current connection is the selected one,
* unmute it as default - otherwise mute it
@@ -6924,8 +7080,20 @@
break;
}
}
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, mute);
+ /* check if we have a selector or mixer
+ * we could check for the widget type instead, but
+ * just check for Amp-In presence (in case of mixer
+ * without amp-in there is something wrong, this
+ * function shouldn't be used or capsrc nid is wrong)
+ */
+ if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ mute);
+ else if (mute != AMP_IN_MUTE(idx))
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ idx);
}
}
}
@@ -7054,6 +7222,12 @@
}
}
+ err = snd_hda_attach_beep_device(codec, 0x1);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ }
+
if (board_config != ALC882_AUTO)
setup_preset(spec, &alc882_presets[board_config]);
@@ -7074,7 +7248,7 @@
spec->stream_digital_playback = &alc882_pcm_digital_playback;
spec->stream_digital_capture = &alc882_pcm_digital_capture;
- spec->is_mix_capture = 1; /* matrix-style capture */
+ spec->capture_style = CAPT_MIX; /* matrix-style capture */
if (!spec->adc_nids && spec->input_mux) {
/* check whether NID 0x07 is valid */
unsigned int wcap = get_wcaps(codec, 0x07);
@@ -7091,6 +7265,7 @@
}
}
set_capture_mixer(spec);
+ set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
spec->vmaster_nid = 0x0c;
@@ -7142,10 +7317,14 @@
0x09, 0x08
};
+#define alc889_adc_nids alc880_adc_nids
+
static hda_nid_t alc883_capsrc_nids[2] = { 0x23, 0x22 };
static hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 };
+#define alc889_capsrc_nids alc882_capsrc_nids
+
/* input MUX */
/* FIXME: should be a matrix-type input source selection */
@@ -7363,8 +7542,6 @@
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
{ } /* end */
};
@@ -7427,8 +7604,6 @@
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
{ } /* end */
};
@@ -7452,8 +7627,6 @@
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
{ } /* end */
};
@@ -7478,8 +7651,6 @@
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
{ } /* end */
};
@@ -7503,8 +7674,6 @@
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
{ } /* end */
};
@@ -7912,36 +8081,83 @@
{ } /* end */
};
-static struct hda_verb alc888_3st_hp_verbs[] = {
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */
- { }
-};
-
static struct hda_verb alc888_6st_dell_verbs[] = {
{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
{ }
};
+static void alc888_3st_hp_front_automute(struct hda_codec *codec)
+{
+ unsigned int present, bits;
+
+ present = snd_hda_codec_read(codec, 0x1b, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ bits = present ? HDA_AMP_MUTE : 0;
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, bits);
+ snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, bits);
+ snd_hda_codec_amp_stereo(codec, 0x18, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, bits);
+}
+
+static void alc888_3st_hp_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc888_3st_hp_front_automute(codec);
+ break;
+ }
+}
+
+static struct hda_verb alc888_3st_hp_verbs[] = {
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */
+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */
+ {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ { } /* end */
+};
+
+/*
+ * 2ch mode
+ */
static struct hda_verb alc888_3st_hp_2ch_init[] = {
{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
{ 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
{ 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { }
+ { } /* end */
};
+/*
+ * 4ch mode
+ */
+static struct hda_verb alc888_3st_hp_4ch_init[] = {
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x16, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { } /* end */
+};
+
+/*
+ * 6ch mode
+ */
static struct hda_verb alc888_3st_hp_6ch_init[] = {
{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
{ 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
{ 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { }
+ { 0x16, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { } /* end */
};
-static struct hda_channel_mode alc888_3st_hp_modes[2] = {
+static struct hda_channel_mode alc888_3st_hp_modes[3] = {
{ 2, alc888_3st_hp_2ch_init },
+ { 4, alc888_3st_hp_4ch_init },
{ 6, alc888_3st_hp_6ch_init },
};
@@ -8202,7 +8418,7 @@
{
switch (res >> 26) {
case ALC880_HP_EVENT:
- printk("hp_event\n");
+ /* printk(KERN_DEBUG "hp_event\n"); */
alc888_6st_dell_front_automute(codec);
break;
}
@@ -8461,6 +8677,7 @@
SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG),
SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),
SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_ACER_ASPIRE),
+ SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_ACER_ASPIRE),
SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),
SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE),
SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE),
@@ -8468,17 +8685,21 @@
ALC888_ACER_ASPIRE_4930G),
SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G",
ALC888_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC883_AUTO),
+ SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC883_AUTO),
SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G",
ALC888_ACER_ASPIRE_4930G),
SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G",
ALC888_ACER_ASPIRE_4930G),
- SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), /* default Acer */
+ /* default Acer */
+ SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_ACER),
SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL),
SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x103c, 0x2a66, "HP Acacia", ALC888_3ST_HP),
+ SND_PCI_QUIRK(0x103c, 0x2a72, "HP Educ.ar", ALC888_3ST_HP),
SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V),
SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
SND_PCI_QUIRK(0x1043, 0x8284, "Asus Z37E", ALC883_6ST_DIG),
@@ -8518,7 +8739,7 @@
SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720),
SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720),
- SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD),
+ SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC883_LAPTOP_EAPD),
SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch),
SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION),
SND_PCI_QUIRK(0x1734, 0x1107, "FSC AMILO Xi2550",
@@ -8543,6 +8764,10 @@
{}
};
+static hda_nid_t alc1200_slave_dig_outs[] = {
+ ALC883_DIGOUT_NID, 0,
+};
+
static struct alc_config_preset alc883_presets[] = {
[ALC883_3ST_2ch_DIG] = {
.mixers = { alc883_3ST_2ch_mixer },
@@ -8778,6 +9003,8 @@
.channel_mode = alc888_3st_hp_modes,
.need_dac_fix = 1,
.input_mux = &alc883_capture_source,
+ .unsol_event = alc888_3st_hp_unsol_event,
+ .init_hook = alc888_3st_hp_front_automute,
},
[ALC888_6ST_DELL] = {
.mixers = { alc883_base_mixer, alc883_chmode_mixer },
@@ -8883,6 +9110,7 @@
.dac_nids = alc883_dac_nids,
.dig_out_nid = ALC1200_DIGOUT_NID,
.dig_in_nid = ALC883_DIGIN_NID,
+ .slave_dig_outs = alc1200_slave_dig_outs,
.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
.channel_mode = alc883_sixstack_modes,
.input_mux = &alc883_capture_source,
@@ -8950,11 +9178,9 @@
for (i = 0; i < AUTO_PIN_LAST; i++) {
hda_nid_t nid = spec->autocfg.input_pins[i];
if (alc883_is_input_pin(nid)) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- (i <= AUTO_PIN_FRONT_MIC ?
- PIN_VREF80 : PIN_IN));
- if (nid != ALC883_PIN_CD_NID)
+ alc_set_input_pin(codec, nid, i);
+ if (nid != ALC883_PIN_CD_NID &&
+ (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_MUTE);
@@ -8969,6 +9195,8 @@
{
struct alc_spec *spec = codec->spec;
int err = alc880_parse_auto_config(codec);
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
if (err < 0)
return err;
@@ -8982,6 +9210,26 @@
/* hack - override the init verbs */
spec->init_verbs[0] = alc883_auto_init_verbs;
+ /* setup input_mux for ALC889 */
+ if (codec->vendor_id == 0x10ec0889) {
+ /* digital-mic input pin is excluded in alc880_auto_create..()
+ * because it's under 0x18
+ */
+ if (cfg->input_pins[AUTO_PIN_MIC] == 0x12 ||
+ cfg->input_pins[AUTO_PIN_FRONT_MIC] == 0x12) {
+ struct hda_input_mux *imux = &spec->private_imux[0];
+ for (i = 1; i < 3; i++)
+ memcpy(&spec->private_imux[i],
+ &spec->private_imux[0],
+ sizeof(spec->private_imux[0]));
+ imux->items[imux->num_items].label = "Int DMic";
+ imux->items[imux->num_items].index = 0x0b;
+ imux->num_items++;
+ spec->num_mux_defs = 3;
+ spec->input_mux = spec->private_imux;
+ }
+ }
+
return 1; /* config found */
}
@@ -9033,6 +9281,12 @@
}
}
+ err = snd_hda_attach_beep_device(codec, 0x1);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ }
+
if (board_config != ALC883_AUTO)
setup_preset(spec, &alc883_presets[board_config]);
@@ -9045,14 +9299,36 @@
spec->stream_name_analog = "ALC888 Analog";
spec->stream_name_digital = "ALC888 Digital";
}
+ if (!spec->num_adc_nids) {
+ spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
+ spec->adc_nids = alc883_adc_nids;
+ }
+ if (!spec->capsrc_nids)
+ spec->capsrc_nids = alc883_capsrc_nids;
+ spec->capture_style = CAPT_MIX; /* matrix-style capture */
break;
case 0x10ec0889:
spec->stream_name_analog = "ALC889 Analog";
spec->stream_name_digital = "ALC889 Digital";
+ if (!spec->num_adc_nids) {
+ spec->num_adc_nids = ARRAY_SIZE(alc889_adc_nids);
+ spec->adc_nids = alc889_adc_nids;
+ }
+ if (!spec->capsrc_nids)
+ spec->capsrc_nids = alc889_capsrc_nids;
+ spec->capture_style = CAPT_1MUX_MIX; /* 1mux/Nmix-style
+ capture */
break;
default:
spec->stream_name_analog = "ALC883 Analog";
spec->stream_name_digital = "ALC883 Digital";
+ if (!spec->num_adc_nids) {
+ spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
+ spec->adc_nids = alc883_adc_nids;
+ }
+ if (!spec->capsrc_nids)
+ spec->capsrc_nids = alc883_capsrc_nids;
+ spec->capture_style = CAPT_MIX; /* matrix-style capture */
break;
}
@@ -9063,15 +9339,9 @@
spec->stream_digital_playback = &alc883_pcm_digital_playback;
spec->stream_digital_capture = &alc883_pcm_digital_capture;
- if (!spec->num_adc_nids) {
- spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
- spec->adc_nids = alc883_adc_nids;
- }
- if (!spec->capsrc_nids)
- spec->capsrc_nids = alc883_capsrc_nids;
- spec->is_mix_capture = 1; /* matrix-style capture */
if (!spec->cap_mixer)
set_capture_mixer(spec);
+ set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
spec->vmaster_nid = 0x0c;
@@ -9124,8 +9394,6 @@
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- /* HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT), */
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
@@ -9146,8 +9414,6 @@
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- /* HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT), */
/*HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT),*/
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
{ } /* end */
@@ -9256,8 +9522,6 @@
HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_VOLUME("AUX IN Playback Volume", 0x0b, 0x06, HDA_INPUT),
HDA_CODEC_MUTE("AUX IN Playback Switch", 0x0b, 0x06, HDA_INPUT),
{ } /* end */
@@ -9286,8 +9550,6 @@
HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT),
{ } /* end */
};
@@ -9435,6 +9697,67 @@
{ } /* end */
};
+static struct snd_kcontrol_new alc262_tyan_mixer[] = {
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Aux Playback Volume", 0x0b, 0x06, HDA_INPUT),
+ HDA_CODEC_MUTE("Aux Playback Switch", 0x0b, 0x06, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+ { } /* end */
+};
+
+static struct hda_verb alc262_tyan_verbs[] = {
+ /* Headphone automute */
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+ /* P11 AUX_IN, white 4-pin connector */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, 0xe1},
+ {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, 0x93},
+ {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0x19},
+
+ {}
+};
+
+/* unsolicited event for HP jack sensing */
+static void alc262_tyan_automute(struct hda_codec *codec)
+{
+ unsigned int mute;
+ unsigned int present;
+
+ snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
+ present = snd_hda_codec_read(codec, 0x1b, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ present = (present & 0x80000000) != 0;
+ if (present) {
+ /* mute line output on ATX panel */
+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, HDA_AMP_MUTE);
+ } else {
+ /* unmute line output if necessary */
+ mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0);
+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+ }
+}
+
+static void alc262_tyan_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) != ALC880_HP_EVENT)
+ return;
+ alc262_tyan_automute(codec);
+}
+
#define alc262_capture_mixer alc882_capture_mixer
#define alc262_capture_alt_mixer alc882_capture_alt_mixer
@@ -9901,8 +10224,6 @@
},
HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Switch", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
@@ -10474,8 +10795,14 @@
alc262_ignore);
if (err < 0)
return err;
- if (!spec->autocfg.line_outs)
+ if (!spec->autocfg.line_outs) {
+ if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) {
+ spec->multiout.max_channels = 2;
+ spec->no_analog = 1;
+ goto dig_only;
+ }
return 0; /* can't find valid BIOS pin config */
+ }
err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg);
if (err < 0)
return err;
@@ -10485,8 +10812,11 @@
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- if (spec->autocfg.dig_out_pin)
+ dig_only:
+ if (spec->autocfg.dig_outs) {
spec->multiout.dig_out_nid = ALC262_DIGOUT_NID;
+ spec->dig_out_type = spec->autocfg.dig_out_type[0];
+ }
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = ALC262_DIGIN_NID;
@@ -10495,13 +10825,12 @@
add_verb(spec, alc262_volume_init_verbs);
spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux;
+ spec->input_mux = &spec->private_imux[0];
err = alc_auto_add_mic_boost(codec);
if (err < 0)
return err;
- store_pin_configs(codec);
return 1;
}
@@ -10543,21 +10872,19 @@
[ALC262_ULTRA] = "ultra",
[ALC262_LENOVO_3000] = "lenovo-3000",
[ALC262_NEC] = "nec",
+ [ALC262_TYAN] = "tyan",
[ALC262_AUTO] = "auto",
};
static struct snd_pci_quirk alc262_cfg_tbl[] = {
SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO),
SND_PCI_QUIRK(0x1033, 0x8895, "NEC Versa S9100", ALC262_NEC),
- SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x12ff, "HP xw4550", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x1306, "HP xw8600", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x1309, "HP xw4*00", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x130a, "HP xw6*00", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x130b, "HP xw8*00", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x170b, "HP xw*", ALC262_HP_BPC),
+ SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1200, "HP xw series",
+ ALC262_HP_BPC),
+ SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1300, "HP xw series",
+ ALC262_HP_BPC),
+ SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1700, "HP xw series",
+ ALC262_HP_BPC),
SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL),
SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF),
SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL),
@@ -10575,17 +10902,17 @@
SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD),
SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO),
SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
- SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
- SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
- SND_PCI_QUIRK(0x104d, 0x9033, "Sony VAIO VGN-SR19XN",
- ALC262_SONY_ASSAMD),
+ SND_PCI_QUIRK(0x104d, 0x9016, "Sony VAIO", ALC262_AUTO), /* dig-only */
+ SND_PCI_QUIRK_MASK(0x104d, 0xff00, 0x9000, "Sony VAIO",
+ ALC262_SONY_ASSAMD),
SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
ALC262_TOSHIBA_RX1),
SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06),
SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU),
- SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA),
- SND_PCI_QUIRK(0x144d, 0xc039, "Samsung Q1U EL", ALC262_ULTRA),
+ SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_TYAN),
+ SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc032, "Samsung Q1",
+ ALC262_ULTRA),
SND_PCI_QUIRK(0x144d, 0xc510, "Samsung Q45", ALC262_HIPPO),
SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 y410", ALC262_LENOVO_3000),
SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8),
@@ -10802,6 +11129,19 @@
.unsol_event = alc262_hippo_unsol_event,
.init_hook = alc262_hippo_automute,
},
+ [ALC262_TYAN] = {
+ .mixers = { alc262_tyan_mixer },
+ .init_verbs = { alc262_init_verbs, alc262_tyan_verbs},
+ .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+ .dac_nids = alc262_dac_nids,
+ .hp_nid = 0x02,
+ .dig_out_nid = ALC262_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc262_modes),
+ .channel_mode = alc262_modes,
+ .input_mux = &alc262_capture_source,
+ .unsol_event = alc262_tyan_unsol_event,
+ .init_hook = alc262_tyan_automute,
+ },
};
static int patch_alc262(struct hda_codec *codec)
@@ -10854,6 +11194,14 @@
}
}
+ if (!spec->no_analog) {
+ err = snd_hda_attach_beep_device(codec, 0x1);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ }
+ }
+
if (board_config != ALC262_AUTO)
setup_preset(spec, &alc262_presets[board_config]);
@@ -10865,7 +11213,7 @@
spec->stream_digital_playback = &alc262_pcm_digital_playback;
spec->stream_digital_capture = &alc262_pcm_digital_capture;
- spec->is_mix_capture = 1;
+ spec->capture_style = CAPT_MIX;
if (!spec->adc_nids && spec->input_mux) {
/* check whether NID 0x07 is valid */
unsigned int wcap = get_wcaps(codec, 0x07);
@@ -10882,8 +11230,10 @@
spec->capsrc_nids = alc262_capsrc_nids;
}
}
- if (!spec->cap_mixer)
+ if (!spec->cap_mixer && !spec->no_analog)
set_capture_mixer(spec);
+ if (!spec->no_analog)
+ set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
spec->vmaster_nid = 0x0c;
@@ -11263,19 +11613,13 @@
static struct hda_verb alc268_base_init_verbs[] = {
/* Unmute DAC0-1 and set vol = 0 */
{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
/*
* Set up output mixers (0x0c - 0x0e)
*/
/* set vol=0 to output mixers */
{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
{0x0e, AC_VERB_SET_CONNECT_SEL, 0x00},
{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -11294,9 +11638,7 @@
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* set PCBEEP vol = 0, mute connections */
{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -11318,10 +11660,8 @@
*/
static struct hda_verb alc268_volume_init_verbs[] = {
/* set output DAC */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
@@ -11329,16 +11669,12 @@
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* set PCBEEP vol = 0, mute connections */
{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -11537,7 +11873,7 @@
static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec,
const struct auto_pin_cfg *cfg)
{
- struct hda_input_mux *imux = &spec->private_imux;
+ struct hda_input_mux *imux = &spec->private_imux[0];
int i, idx1;
for (i = 0; i < AUTO_PIN_LAST; i++) {
@@ -11631,9 +11967,14 @@
alc268_ignore);
if (err < 0)
return err;
- if (!spec->autocfg.line_outs)
+ if (!spec->autocfg.line_outs) {
+ if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) {
+ spec->multiout.max_channels = 2;
+ spec->no_analog = 1;
+ goto dig_only;
+ }
return 0; /* can't find valid BIOS pin config */
-
+ }
err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg);
if (err < 0)
return err;
@@ -11643,25 +11984,26 @@
spec->multiout.max_channels = 2;
+ dig_only:
/* digital only support output */
- if (spec->autocfg.dig_out_pin)
+ if (spec->autocfg.dig_outs) {
spec->multiout.dig_out_nid = ALC268_DIGOUT_NID;
-
+ spec->dig_out_type = spec->autocfg.dig_out_type[0];
+ }
if (spec->kctls.list)
add_mixer(spec, spec->kctls.list);
- if (spec->autocfg.speaker_pins[0] != 0x1d)
+ if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d)
add_mixer(spec, alc268_beep_mixer);
add_verb(spec, alc268_volume_init_verbs);
spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux;
+ spec->input_mux = &spec->private_imux[0];
err = alc_auto_add_mic_boost(codec);
if (err < 0)
return err;
- store_pin_configs(codec);
return 1;
}
@@ -11723,7 +12065,7 @@
static struct alc_config_preset alc268_presets[] = {
[ALC267_QUANTA_IL1] = {
- .mixers = { alc267_quanta_il1_mixer },
+ .mixers = { alc267_quanta_il1_mixer, alc268_beep_mixer },
.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
alc267_quanta_il1_verbs },
.num_dacs = ARRAY_SIZE(alc268_dac_nids),
@@ -11805,7 +12147,8 @@
},
[ALC268_ACER_ASPIRE_ONE] = {
.mixers = { alc268_acer_aspire_one_mixer,
- alc268_capture_alt_mixer },
+ alc268_beep_mixer,
+ alc268_capture_alt_mixer },
.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
alc268_acer_aspire_one_verbs },
.num_dacs = ARRAY_SIZE(alc268_dac_nids),
@@ -11874,7 +12217,7 @@
{
struct alc_spec *spec;
int board_config;
- int err;
+ int i, has_beep, err;
spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -11923,15 +12266,30 @@
spec->stream_digital_playback = &alc268_pcm_digital_playback;
- if (!query_amp_caps(codec, 0x1d, HDA_INPUT))
- /* override the amp caps for beep generator */
- snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT,
+ has_beep = 0;
+ for (i = 0; i < spec->num_mixers; i++) {
+ if (spec->mixers[i] == alc268_beep_mixer) {
+ has_beep = 1;
+ break;
+ }
+ }
+
+ if (has_beep) {
+ err = snd_hda_attach_beep_device(codec, 0x1);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ }
+ if (!query_amp_caps(codec, 0x1d, HDA_INPUT))
+ /* override the amp caps for beep generator */
+ snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT,
(0x0c << AC_AMPCAP_OFFSET_SHIFT) |
(0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) |
(0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) |
(0 << AC_AMPCAP_MUTE_SHIFT));
+ }
- if (!spec->adc_nids && spec->input_mux) {
+ if (!spec->no_analog && !spec->adc_nids && spec->input_mux) {
/* check whether NID 0x07 is valid */
unsigned int wcap = get_wcaps(codec, 0x07);
int i;
@@ -12012,8 +12370,6 @@
HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x4, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
@@ -12040,8 +12396,6 @@
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x04, HDA_INPUT),
{ }
};
@@ -12065,8 +12419,6 @@
HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x0b, 0x03, HDA_INPUT),
HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x0b, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("Dock Mic Boost", 0x1b, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x04, HDA_INPUT),
{ }
};
@@ -12103,13 +12455,6 @@
{ } /* end */
};
-/* beep control */
-static struct snd_kcontrol_new alc269_beep_mixer[] = {
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x4, HDA_INPUT),
- { } /* end */
-};
-
static struct hda_verb alc269_quanta_fl1_verbs[] = {
{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
@@ -12509,7 +12854,7 @@
*/
if (cfg->input_pins[AUTO_PIN_MIC] == 0x12 ||
cfg->input_pins[AUTO_PIN_FRONT_MIC] == 0x12) {
- struct hda_input_mux *imux = &spec->private_imux;
+ struct hda_input_mux *imux = &spec->private_imux[0];
imux->items[imux->num_items].label = "Int Mic";
imux->items[imux->num_items].index = 0x05;
imux->num_items++;
@@ -12527,13 +12872,34 @@
#define alc269_pcm_digital_playback alc880_pcm_digital_playback
#define alc269_pcm_digital_capture alc880_pcm_digital_capture
+static struct hda_pcm_stream alc269_44k_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
+ /* NID is set in alc_build_pcms */
+ .ops = {
+ .open = alc880_playback_pcm_open,
+ .prepare = alc880_playback_pcm_prepare,
+ .cleanup = alc880_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream alc269_44k_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
+ /* NID is set in alc_build_pcms */
+};
+
/*
* BIOS auto configuration
*/
static int alc269_parse_auto_config(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- int i, err;
+ int err;
static hda_nid_t alc269_ignore[] = { 0x1d, 0 };
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
@@ -12550,22 +12916,15 @@
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- if (spec->autocfg.dig_out_pin)
+ if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = ALC269_DIGOUT_NID;
if (spec->kctls.list)
add_mixer(spec, spec->kctls.list);
- /* create a beep mixer control if the pin 0x1d isn't assigned */
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.input_pins); i++)
- if (spec->autocfg.input_pins[i] == 0x1d)
- break;
- if (i >= ARRAY_SIZE(spec->autocfg.input_pins))
- add_mixer(spec, alc269_beep_mixer);
-
add_verb(spec, alc269_init_verbs);
spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux;
+ spec->input_mux = &spec->private_imux[0];
/* set default input source */
snd_hda_codec_write_cache(codec, alc269_capsrc_nids[0],
0, AC_VERB_SET_CONNECT_SEL,
@@ -12575,10 +12934,9 @@
if (err < 0)
return err;
- if (!spec->cap_mixer)
+ if (!spec->cap_mixer && !spec->no_analog)
set_capture_mixer(spec);
- store_pin_configs(codec);
return 1;
}
@@ -12675,7 +13033,7 @@
.init_hook = alc269_eeepc_dmic_inithook,
},
[ALC269_FUJITSU] = {
- .mixers = { alc269_fujitsu_mixer, alc269_beep_mixer },
+ .mixers = { alc269_fujitsu_mixer },
.cap_mixer = alc269_epc_capture_mixer,
.init_verbs = { alc269_init_verbs,
alc269_eeepc_dmic_init_verbs },
@@ -12740,13 +13098,26 @@
}
}
+ err = snd_hda_attach_beep_device(codec, 0x1);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ }
+
if (board_config != ALC269_AUTO)
setup_preset(spec, &alc269_presets[board_config]);
spec->stream_name_analog = "ALC269 Analog";
- spec->stream_analog_playback = &alc269_pcm_analog_playback;
- spec->stream_analog_capture = &alc269_pcm_analog_capture;
-
+ if (codec->subsystem_id == 0x17aa3bf8) {
+ /* Due to a hardware problem on Lenovo Ideadpad, we need to
+ * fix the sample rate of analog I/O to 44.1kHz
+ */
+ spec->stream_analog_playback = &alc269_44k_pcm_analog_playback;
+ spec->stream_analog_capture = &alc269_44k_pcm_analog_capture;
+ } else {
+ spec->stream_analog_playback = &alc269_pcm_analog_playback;
+ spec->stream_analog_capture = &alc269_pcm_analog_capture;
+ }
spec->stream_name_digital = "ALC269 Digital";
spec->stream_digital_playback = &alc269_pcm_digital_playback;
spec->stream_digital_capture = &alc269_pcm_digital_capture;
@@ -12756,6 +13127,7 @@
spec->capsrc_nids = alc269_capsrc_nids;
if (!spec->cap_mixer)
set_capture_mixer(spec);
+ set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
codec->patch_ops = alc_patch_ops;
if (board_config == ALC269_AUTO)
@@ -13006,8 +13378,6 @@
static struct snd_kcontrol_new alc861_asus_laptop_mixer[] = {
HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x23, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PC Beep Playback Switch", 0x23, 0x0, HDA_OUTPUT),
{ }
};
@@ -13481,7 +13851,7 @@
static int alc861_auto_create_analog_input_ctls(struct alc_spec *spec,
const struct auto_pin_cfg *cfg)
{
- struct hda_input_mux *imux = &spec->private_imux;
+ struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx, idx1;
for (i = 0; i < AUTO_PIN_LAST; i++) {
@@ -13568,12 +13938,8 @@
for (i = 0; i < AUTO_PIN_LAST; i++) {
hda_nid_t nid = spec->autocfg.input_pins[i];
- if (nid >= 0x0c && nid <= 0x11) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- i <= AUTO_PIN_FRONT_MIC ?
- PIN_VREF80 : PIN_IN);
- }
+ if (nid >= 0x0c && nid <= 0x11)
+ alc_set_input_pin(codec, nid, i);
}
}
@@ -13609,7 +13975,7 @@
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- if (spec->autocfg.dig_out_pin)
+ if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = ALC861_DIGOUT_NID;
if (spec->kctls.list)
@@ -13618,13 +13984,12 @@
add_verb(spec, alc861_auto_init_verbs);
spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux;
+ spec->input_mux = &spec->private_imux[0];
spec->adc_nids = alc861_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
set_capture_mixer(spec);
- store_pin_configs(codec);
return 1;
}
@@ -13833,6 +14198,12 @@
}
}
+ err = snd_hda_attach_beep_device(codec, 0x23);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ }
+
if (board_config != ALC861_AUTO)
setup_preset(spec, &alc861_presets[board_config]);
@@ -13844,6 +14215,8 @@
spec->stream_digital_playback = &alc861_pcm_digital_playback;
spec->stream_digital_capture = &alc861_pcm_digital_capture;
+ set_beep_amp(spec, 0x23, 0, HDA_OUTPUT);
+
spec->vmaster_nid = 0x03;
codec->patch_ops = alc_patch_ops;
@@ -14000,9 +14373,6 @@
HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
-
{ } /* end */
};
@@ -14026,9 +14396,6 @@
HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
-
{ } /* end */
};
@@ -14067,8 +14434,6 @@
HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Beep Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Beep Switch", 0x0b, 0x05, HDA_INPUT),
{ } /* end */
};
@@ -14379,9 +14744,7 @@
SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO),
SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_DALLAS),
SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG),
- SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO),
- SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO),
- SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 N200", ALC861VD_LENOVO),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", ALC861VD_LENOVO),
SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG),
{}
};
@@ -14543,11 +14906,9 @@
for (i = 0; i < AUTO_PIN_LAST; i++) {
hda_nid_t nid = spec->autocfg.input_pins[i];
if (alc861vd_is_input_pin(nid)) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- i <= AUTO_PIN_FRONT_MIC ?
- PIN_VREF80 : PIN_IN);
- if (nid != ALC861VD_PIN_CD_NID)
+ alc_set_input_pin(codec, nid, i);
+ if (nid != ALC861VD_PIN_CD_NID &&
+ (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_MUTE);
@@ -14713,7 +15074,7 @@
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- if (spec->autocfg.dig_out_pin)
+ if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = ALC861VD_DIGOUT_NID;
if (spec->kctls.list)
@@ -14722,13 +15083,12 @@
add_verb(spec, alc861vd_volume_init_verbs);
spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux;
+ spec->input_mux = &spec->private_imux[0];
err = alc_auto_add_mic_boost(codec);
if (err < 0)
return err;
- store_pin_configs(codec);
return 1;
}
@@ -14779,6 +15139,12 @@
}
}
+ err = snd_hda_attach_beep_device(codec, 0x23);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ }
+
if (board_config != ALC861VD_AUTO)
setup_preset(spec, &alc861vd_presets[board_config]);
@@ -14801,9 +15167,10 @@
spec->adc_nids = alc861vd_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids);
spec->capsrc_nids = alc861vd_capsrc_nids;
- spec->is_mix_capture = 1;
+ spec->capture_style = CAPT_MIX;
set_capture_mixer(spec);
+ set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
spec->vmaster_nid = 0x02;
@@ -14992,8 +15359,6 @@
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
{ } /* end */
};
@@ -15015,8 +15380,6 @@
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
{ } /* end */
};
@@ -15992,56 +16355,55 @@
};
static struct snd_pci_quirk alc662_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_ECS),
+ SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC663_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC663_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC663_ASUS_MODE6),
+ SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC663_ASUS_MODE6),
+ SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC663_ASUS_MODE5),
+ SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC663_ASUS_MODE6),
+ SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_ASUS_MODE2),
SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M51VA", ALC663_ASUS_M51VA),
+ /*SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M50Vr", ALC663_ASUS_MODE1),*/
+ SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC663_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC663_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC663_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC663_ASUS_MODE1),
SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS G50V", ALC663_ASUS_G50V),
+ /*SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS NB", ALC663_ASUS_MODE1),*/
+ SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC663_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC663_ASUS_MODE4),
SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG),
SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701),
SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20),
- SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M50Vr", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS NB", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC663_ASUS_MODE4),
- SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC663_ASUS_MODE5),
- SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC663_ASUS_MODE6),
- SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC663_ASUS_MODE6),
- SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC663_ASUS_MODE6),
+ SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS),
SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K",
ALC662_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
- SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_ECS),
- SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS),
SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L",
ALC662_3ST_6ch_DIG),
SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG),
+ SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
SND_PCI_QUIRK(0x1849, 0x3662, "ASROCK K10N78FullHD-hSLI R3.0",
ALC662_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1854, 0x2000, "ASUS H13-2000", ALC663_ASUS_H13),
- SND_PCI_QUIRK(0x1854, 0x2001, "ASUS H13-2001", ALC663_ASUS_H13),
- SND_PCI_QUIRK(0x1854, 0x2002, "ASUS H13-2002", ALC663_ASUS_H13),
+ SND_PCI_QUIRK_MASK(0x1854, 0xf000, 0x2000, "ASUS H13-200x",
+ ALC663_ASUS_H13),
{}
};
@@ -16361,7 +16723,7 @@
if (alc880_is_fixed_pin(pin)) {
nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin));
- /* printk("DAC nid=%x\n",nid); */
+ /* printk(KERN_DEBUG "DAC nid=%x\n",nid); */
/* specify the DAC as the extra output */
if (!spec->multiout.hp_nid)
spec->multiout.hp_nid = nid;
@@ -16391,26 +16753,58 @@
return 0;
}
+/* return the index of the src widget from the connection list of the nid.
+ * return -1 if not found
+ */
+static int alc662_input_pin_idx(struct hda_codec *codec, hda_nid_t nid,
+ hda_nid_t src)
+{
+ hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
+ int i, conns;
+
+ conns = snd_hda_get_connections(codec, nid, conn_list,
+ ARRAY_SIZE(conn_list));
+ if (conns < 0)
+ return -1;
+ for (i = 0; i < conns; i++)
+ if (conn_list[i] == src)
+ return i;
+ return -1;
+}
+
+static int alc662_is_input_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
+ return (pincap & AC_PINCAP_IN) != 0;
+}
+
/* create playback/capture controls for input pins */
-static int alc662_auto_create_analog_input_ctls(struct alc_spec *spec,
+static int alc662_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- struct hda_input_mux *imux = &spec->private_imux;
+ struct alc_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx;
for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (alc880_is_input_pin(cfg->input_pins[i])) {
- idx = alc880_input_pin_idx(cfg->input_pins[i]);
- err = new_analog_input(spec, cfg->input_pins[i],
- auto_pin_cfg_labels[i],
- idx, 0x0b);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label =
- auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index =
- alc880_input_pin_idx(cfg->input_pins[i]);
- imux->num_items++;
+ if (alc662_is_input_pin(codec, cfg->input_pins[i])) {
+ idx = alc662_input_pin_idx(codec, 0x0b,
+ cfg->input_pins[i]);
+ if (idx >= 0) {
+ err = new_analog_input(spec, cfg->input_pins[i],
+ auto_pin_cfg_labels[i],
+ idx, 0x0b);
+ if (err < 0)
+ return err;
+ }
+ idx = alc662_input_pin_idx(codec, 0x22,
+ cfg->input_pins[i]);
+ if (idx >= 0) {
+ imux->items[imux->num_items].label =
+ auto_pin_cfg_labels[i];
+ imux->items[imux->num_items].index = idx;
+ imux->num_items++;
+ }
}
}
return 0;
@@ -16460,7 +16854,6 @@
alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
}
-#define alc662_is_input_pin(nid) alc880_is_input_pin(nid)
#define ALC662_PIN_CD_NID ALC880_PIN_CD_NID
static void alc662_auto_init_analog_input(struct hda_codec *codec)
@@ -16470,12 +16863,10 @@
for (i = 0; i < AUTO_PIN_LAST; i++) {
hda_nid_t nid = spec->autocfg.input_pins[i];
- if (alc662_is_input_pin(nid)) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- (i <= AUTO_PIN_FRONT_MIC ?
- PIN_VREF80 : PIN_IN));
- if (nid != ALC662_PIN_CD_NID)
+ if (alc662_is_input_pin(codec, nid)) {
+ alc_set_input_pin(codec, nid, i);
+ if (nid != ALC662_PIN_CD_NID &&
+ (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_MUTE);
@@ -16513,20 +16904,20 @@
"Headphone");
if (err < 0)
return err;
- err = alc662_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = alc662_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- if (spec->autocfg.dig_out_pin)
+ if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
if (spec->kctls.list)
add_mixer(spec, spec->kctls.list);
spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux;
+ spec->input_mux = &spec->private_imux[0];
add_verb(spec, alc662_auto_init_verbs);
if (codec->vendor_id == 0x10ec0663)
@@ -16536,7 +16927,6 @@
if (err < 0)
return err;
- store_pin_configs(codec);
return 1;
}
@@ -16588,6 +16978,12 @@
}
}
+ err = snd_hda_attach_beep_device(codec, 0x1);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ }
+
if (board_config != ALC662_AUTO)
setup_preset(spec, &alc662_presets[board_config]);
@@ -16611,10 +17007,14 @@
spec->adc_nids = alc662_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
spec->capsrc_nids = alc662_capsrc_nids;
- spec->is_mix_capture = 1;
+ spec->capture_style = CAPT_MIX;
if (!spec->cap_mixer)
set_capture_mixer(spec);
+ if (codec->vendor_id == 0x10ec0662)
+ set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ else
+ set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
spec->vmaster_nid = 0x02;
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 6094344..b5e108a 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -43,6 +43,7 @@
};
enum {
+ STAC_AUTO,
STAC_REF,
STAC_9200_OQO,
STAC_9200_DELL_D21,
@@ -62,14 +63,17 @@
};
enum {
+ STAC_9205_AUTO,
STAC_9205_REF,
STAC_9205_DELL_M42,
STAC_9205_DELL_M43,
STAC_9205_DELL_M44,
+ STAC_9205_EAPD,
STAC_9205_MODELS
};
enum {
+ STAC_92HD73XX_AUTO,
STAC_92HD73XX_NO_JD, /* no jack-detection */
STAC_92HD73XX_REF,
STAC_DELL_M6_AMIC,
@@ -80,22 +84,27 @@
};
enum {
+ STAC_92HD83XXX_AUTO,
STAC_92HD83XXX_REF,
STAC_92HD83XXX_PWR_REF,
+ STAC_DELL_S14,
STAC_92HD83XXX_MODELS
};
enum {
+ STAC_92HD71BXX_AUTO,
STAC_92HD71BXX_REF,
STAC_DELL_M4_1,
STAC_DELL_M4_2,
STAC_DELL_M4_3,
STAC_HP_M4,
STAC_HP_DV5,
+ STAC_HP_HDX,
STAC_92HD71BXX_MODELS
};
enum {
+ STAC_925x_AUTO,
STAC_925x_REF,
STAC_M1,
STAC_M1_2,
@@ -108,6 +117,7 @@
};
enum {
+ STAC_922X_AUTO,
STAC_D945_REF,
STAC_D945GTP3,
STAC_D945GTP5,
@@ -135,6 +145,7 @@
};
enum {
+ STAC_927X_AUTO,
STAC_D965_REF_NO_JD, /* no jack-detection */
STAC_D965_REF,
STAC_D965_3ST,
@@ -144,6 +155,12 @@
STAC_927X_MODELS
};
+enum {
+ STAC_9872_AUTO,
+ STAC_9872_VAIO,
+ STAC_9872_MODELS
+};
+
struct sigmatel_event {
hda_nid_t nid;
unsigned char type;
@@ -167,6 +184,7 @@
unsigned int alt_switch: 1;
unsigned int hp_detect: 1;
unsigned int spdif_mute: 1;
+ unsigned int check_volume_offset:1;
/* gpio lines */
unsigned int eapd_mask;
@@ -179,6 +197,7 @@
unsigned int stream_delay;
/* analog loopback */
+ struct snd_kcontrol_new *aloopback_ctl;
unsigned char aloopback_mask;
unsigned char aloopback_shift;
@@ -203,6 +222,8 @@
hda_nid_t hp_dacs[5];
hda_nid_t speaker_dacs[5];
+ int volume_offset;
+
/* capture */
hda_nid_t *adc_nids;
unsigned int num_adcs;
@@ -224,7 +245,6 @@
/* pin widgets */
hda_nid_t *pin_nids;
unsigned int num_pins;
- unsigned int *pin_configs;
/* codec specific stuff */
struct hda_verb *init;
@@ -400,6 +420,10 @@
0x12, 0x13,
};
+static hda_nid_t stac927x_slave_dig_outs[2] = {
+ 0x1f, 0,
+};
+
static hda_nid_t stac927x_adc_nids[3] = {
0x07, 0x08, 0x09
};
@@ -472,15 +496,21 @@
0x14, 0x22, 0x23
};
-static hda_nid_t stac92hd83xxx_pin_nids[14] = {
+static hda_nid_t stac92hd83xxx_pin_nids[10] = {
0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x12, 0x13,
- 0x1d, 0x1e, 0x1f, 0x20
+ 0x0f, 0x10, 0x11, 0x1f, 0x20,
};
-static hda_nid_t stac92hd71bxx_pin_nids[11] = {
+
+#define STAC92HD71BXX_NUM_PINS 13
+static hda_nid_t stac92hd71bxx_pin_nids_4port[STAC92HD71BXX_NUM_PINS] = {
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x00,
+ 0x00, 0x14, 0x18, 0x19, 0x1e,
+ 0x1f, 0x20, 0x27
+};
+static hda_nid_t stac92hd71bxx_pin_nids_6port[STAC92HD71BXX_NUM_PINS] = {
0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x14, 0x18, 0x19, 0x1e,
- 0x1f,
+ 0x1f, 0x20, 0x27
};
static hda_nid_t stac927x_pin_nids[14] = {
@@ -842,9 +872,9 @@
};
static struct hda_verb stac92hd83xxx_core_init[] = {
- { 0xa, AC_VERB_SET_CONNECT_SEL, 0x0},
- { 0xb, AC_VERB_SET_CONNECT_SEL, 0x0},
- { 0xd, AC_VERB_SET_CONNECT_SEL, 0x1},
+ { 0xa, AC_VERB_SET_CONNECT_SEL, 0x1},
+ { 0xb, AC_VERB_SET_CONNECT_SEL, 0x1},
+ { 0xd, AC_VERB_SET_CONNECT_SEL, 0x0},
/* power state controls amps */
{ 0x01, AC_VERB_SET_EAPD, 1 << 2},
@@ -854,26 +884,25 @@
static struct hda_verb stac92hd71bxx_core_init[] = {
/* set master volume and direct control */
{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
- { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{}
};
-#define HD_DISABLE_PORTF 2
+#define HD_DISABLE_PORTF 1
static struct hda_verb stac92hd71bxx_analog_core_init[] = {
/* start of config #1 */
/* connect port 0f to audio mixer */
{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2},
- /* unmute right and left channels for node 0x0f */
- { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
/* start of config #2 */
/* set master volume and direct control */
{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- /* unmute right and left channels for nodes 0x0a, 0xd */
+ {}
+};
+
+static struct hda_verb stac92hd71bxx_unmute_core_init[] = {
+ /* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */
+ { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{ 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{ 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{}
@@ -954,16 +983,6 @@
.private_value = HDA_COMPOSE_AMP_VAL(nid, chs, idx, dir) \
}
-#define STAC_INPUT_SOURCE(cnt) \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Input Source", \
- .count = cnt, \
- .info = stac92xx_mux_enum_info, \
- .get = stac92xx_mux_enum_get, \
- .put = stac92xx_mux_enum_put, \
- }
-
#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
@@ -978,7 +997,6 @@
static struct snd_kcontrol_new stac9200_mixer[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
- STAC_INPUT_SOURCE(1),
HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
{ } /* end */
@@ -1003,8 +1021,6 @@
HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
-
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
@@ -1014,9 +1030,22 @@
{ } /* end */
};
-static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4),
+static struct snd_kcontrol_new stac92hd73xx_6ch_loopback[] = {
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
+ {}
+};
+static struct snd_kcontrol_new stac92hd73xx_8ch_loopback[] = {
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4),
+ {}
+};
+
+static struct snd_kcontrol_new stac92hd73xx_10ch_loopback[] = {
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5),
+ {}
+};
+
+static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = {
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
@@ -1041,8 +1070,6 @@
};
static struct snd_kcontrol_new stac92hd73xx_10ch_mixer[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5),
-
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
@@ -1094,9 +1121,6 @@
};
static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = {
- STAC_INPUT_SOURCE(2),
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2),
-
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
@@ -1122,10 +1146,11 @@
{ } /* end */
};
-static struct snd_kcontrol_new stac92hd71bxx_mixer[] = {
- STAC_INPUT_SOURCE(2),
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2),
+static struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2)
+};
+static struct snd_kcontrol_new stac92hd71bxx_mixer[] = {
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
@@ -1137,16 +1162,12 @@
static struct snd_kcontrol_new stac925x_mixer[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0x0e, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT),
- STAC_INPUT_SOURCE(1),
HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x14, 0, HDA_OUTPUT),
{ } /* end */
};
static struct snd_kcontrol_new stac9205_mixer[] = {
- STAC_INPUT_SOURCE(2),
- STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1),
-
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT),
@@ -1155,9 +1176,13 @@
{ } /* end */
};
+static struct snd_kcontrol_new stac9205_loopback[] = {
+ STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1),
+ {}
+};
+
/* This needs to be generated dynamically based on sequence */
static struct snd_kcontrol_new stac922x_mixer[] = {
- STAC_INPUT_SOURCE(2),
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_INPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_INPUT),
@@ -1168,9 +1193,6 @@
static struct snd_kcontrol_new stac927x_mixer[] = {
- STAC_INPUT_SOURCE(3),
- STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
-
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT),
@@ -1182,6 +1204,11 @@
{ } /* end */
};
+static struct snd_kcontrol_new stac927x_loopback[] = {
+ STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
+ {}
+};
+
static struct snd_kcontrol_new stac_dmux_mixer = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Digital Input Source",
@@ -1207,10 +1234,7 @@
"LFE Playback Volume",
"Side Playback Volume",
"Headphone Playback Volume",
- "Headphone2 Playback Volume",
"Speaker Playback Volume",
- "External Speaker Playback Volume",
- "Speaker2 Playback Volume",
NULL
};
@@ -1221,10 +1245,7 @@
"LFE Playback Switch",
"Side Playback Switch",
"Headphone Playback Switch",
- "Headphone2 Playback Switch",
"Speaker Playback Switch",
- "External Speaker Playback Switch",
- "Speaker2 Playback Switch",
"IEC958 Playback Switch",
NULL
};
@@ -1294,6 +1315,8 @@
unsigned int vmaster_tlv[4];
snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
HDA_OUTPUT, vmaster_tlv);
+ /* correct volume offset */
+ vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset;
err = snd_hda_add_vmaster(codec, "Master Playback Volume",
vmaster_tlv, slave_vols);
if (err < 0)
@@ -1306,6 +1329,13 @@
return err;
}
+ if (spec->aloopback_ctl &&
+ snd_hda_get_bool_hint(codec, "loopback") == 1) {
+ err = snd_hda_add_new_ctls(codec, spec->aloopback_ctl);
+ if (err < 0)
+ return err;
+ }
+
stac92xx_free_kctls(codec); /* no longer needed */
/* create jack input elements */
@@ -1490,6 +1520,7 @@
};
static const char *stac9200_models[STAC_9200_MODELS] = {
+ [STAC_AUTO] = "auto",
[STAC_REF] = "ref",
[STAC_9200_OQO] = "oqo",
[STAC_9200_DELL_D21] = "dell-d21",
@@ -1511,6 +1542,8 @@
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+ "DFI LanParty", STAC_REF),
/* Dell laptops have BIOS problem */
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a8,
"unknown Dell", STAC_9200_DELL_D21),
@@ -1633,6 +1666,7 @@
};
static const char *stac925x_models[STAC_925x_MODELS] = {
+ [STAC_925x_AUTO] = "auto",
[STAC_REF] = "ref",
[STAC_M1] = "m1",
[STAC_M1_2] = "m1-2",
@@ -1660,6 +1694,7 @@
static struct snd_pci_quirk stac925x_cfg_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF),
SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF),
/* Default table for unknown ID */
@@ -1691,6 +1726,7 @@
};
static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
+ [STAC_92HD73XX_AUTO] = "auto",
[STAC_92HD73XX_NO_JD] = "no-jd",
[STAC_92HD73XX_REF] = "ref",
[STAC_DELL_M6_AMIC] = "dell-m6-amic",
@@ -1703,6 +1739,8 @@
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_92HD73XX_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+ "DFI LanParty", STAC_92HD73XX_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0254,
"Dell Studio 1535", STAC_DELL_M6_DMIC),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0255,
@@ -1726,52 +1764,68 @@
{} /* terminator */
};
-static unsigned int ref92hd83xxx_pin_configs[14] = {
+static unsigned int ref92hd83xxx_pin_configs[10] = {
0x02214030, 0x02211010, 0x02a19020, 0x02170130,
0x01014050, 0x01819040, 0x01014020, 0x90a3014e,
- 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x40f000f0,
0x01451160, 0x98560170,
};
+static unsigned int dell_s14_pin_configs[10] = {
+ 0x02214030, 0x02211010, 0x02a19020, 0x01014050,
+ 0x40f000f0, 0x01819040, 0x40f000f0, 0x90a60160,
+ 0x40f000f0, 0x40f000f0,
+};
+
static unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = {
[STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs,
[STAC_92HD83XXX_PWR_REF] = ref92hd83xxx_pin_configs,
+ [STAC_DELL_S14] = dell_s14_pin_configs,
};
static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
+ [STAC_92HD83XXX_AUTO] = "auto",
[STAC_92HD83XXX_REF] = "ref",
[STAC_92HD83XXX_PWR_REF] = "mic-ref",
+ [STAC_DELL_S14] = "dell-s14",
};
static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_92HD83XXX_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+ "DFI LanParty", STAC_92HD83XXX_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba,
+ "unknown Dell", STAC_DELL_S14),
{} /* terminator */
};
-static unsigned int ref92hd71bxx_pin_configs[11] = {
+static unsigned int ref92hd71bxx_pin_configs[STAC92HD71BXX_NUM_PINS] = {
0x02214030, 0x02a19040, 0x01a19020, 0x01014010,
0x0181302e, 0x01014010, 0x01019020, 0x90a000f0,
- 0x90a000f0, 0x01452050, 0x01452050,
+ 0x90a000f0, 0x01452050, 0x01452050, 0x00000000,
+ 0x00000000
};
-static unsigned int dell_m4_1_pin_configs[11] = {
+static unsigned int dell_m4_1_pin_configs[STAC92HD71BXX_NUM_PINS] = {
0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110,
0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0,
- 0x40f000f0, 0x4f0000f0, 0x4f0000f0,
+ 0x40f000f0, 0x4f0000f0, 0x4f0000f0, 0x00000000,
+ 0x00000000
};
-static unsigned int dell_m4_2_pin_configs[11] = {
+static unsigned int dell_m4_2_pin_configs[STAC92HD71BXX_NUM_PINS] = {
0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0,
- 0x40f000f0, 0x044413b0, 0x044413b0,
+ 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000,
+ 0x00000000
};
-static unsigned int dell_m4_3_pin_configs[11] = {
+static unsigned int dell_m4_3_pin_configs[STAC92HD71BXX_NUM_PINS] = {
0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a000f0,
- 0x40f000f0, 0x044413b0, 0x044413b0,
+ 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000,
+ 0x00000000
};
static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
@@ -1781,35 +1835,38 @@
[STAC_DELL_M4_3] = dell_m4_3_pin_configs,
[STAC_HP_M4] = NULL,
[STAC_HP_DV5] = NULL,
+ [STAC_HP_HDX] = NULL,
};
static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
+ [STAC_92HD71BXX_AUTO] = "auto",
[STAC_92HD71BXX_REF] = "ref",
[STAC_DELL_M4_1] = "dell-m4-1",
[STAC_DELL_M4_2] = "dell-m4-2",
[STAC_DELL_M4_3] = "dell-m4-3",
[STAC_HP_M4] = "hp-m4",
[STAC_HP_DV5] = "hp-dv5",
+ [STAC_HP_HDX] = "hp-hdx",
};
static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_92HD71BXX_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f2,
- "HP dv5", STAC_HP_M4),
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f4,
- "HP dv7", STAC_HP_DV5),
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f7,
- "HP dv4", STAC_HP_DV5),
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fc,
- "HP dv7", STAC_HP_M4),
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3600,
- "HP dv5", STAC_HP_DV5),
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3603,
- "HP dv5", STAC_HP_DV5),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+ "DFI LanParty", STAC_92HD71BXX_REF),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080,
+ "HP", STAC_HP_DV5),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0,
+ "HP dv4-7", STAC_HP_DV5),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3600,
+ "HP dv4-7", STAC_HP_DV5),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3610,
+ "HP HDX", STAC_HP_HDX), /* HDX18 */
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a,
- "unknown HP", STAC_HP_M4),
+ "HP mini 1000", STAC_HP_M4),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361b,
+ "HP HDX", STAC_HP_HDX), /* HDX16 */
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
"unknown Dell", STAC_DELL_M4_1),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
@@ -1961,6 +2018,7 @@
};
static const char *stac922x_models[STAC_922X_MODELS] = {
+ [STAC_922X_AUTO] = "auto",
[STAC_D945_REF] = "ref",
[STAC_D945GTP5] = "5stack",
[STAC_D945GTP3] = "3stack",
@@ -1988,6 +2046,8 @@
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_D945_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+ "DFI LanParty", STAC_D945_REF),
/* Intel 945G based systems */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0101,
"Intel D945G", STAC_D945GTP3),
@@ -2041,6 +2101,9 @@
"Intel D945P", STAC_D945GTP3),
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0707,
"Intel D945P", STAC_D945GTP5),
+ /* other intel */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0204,
+ "Intel D945", STAC_D945_REF),
/* other systems */
/* Apple Intel Mac (Mac Mini, MacBook, MacBook Pro...) */
SND_PCI_QUIRK(0x8384, 0x7680,
@@ -2065,31 +2128,7 @@
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d7,
"Dell XPS M1210", STAC_922X_DELL_M82),
/* ECS/PC Chips boards */
- SND_PCI_QUIRK(0x1019, 0x2144,
- "ECS/PC chips", STAC_ECS_202),
- SND_PCI_QUIRK(0x1019, 0x2608,
- "ECS/PC chips", STAC_ECS_202),
- SND_PCI_QUIRK(0x1019, 0x2633,
- "ECS/PC chips P17G/1333", STAC_ECS_202),
- SND_PCI_QUIRK(0x1019, 0x2811,
- "ECS/PC chips", STAC_ECS_202),
- SND_PCI_QUIRK(0x1019, 0x2812,
- "ECS/PC chips", STAC_ECS_202),
- SND_PCI_QUIRK(0x1019, 0x2813,
- "ECS/PC chips", STAC_ECS_202),
- SND_PCI_QUIRK(0x1019, 0x2814,
- "ECS/PC chips", STAC_ECS_202),
- SND_PCI_QUIRK(0x1019, 0x2815,
- "ECS/PC chips", STAC_ECS_202),
- SND_PCI_QUIRK(0x1019, 0x2816,
- "ECS/PC chips", STAC_ECS_202),
- SND_PCI_QUIRK(0x1019, 0x2817,
- "ECS/PC chips", STAC_ECS_202),
- SND_PCI_QUIRK(0x1019, 0x2818,
- "ECS/PC chips", STAC_ECS_202),
- SND_PCI_QUIRK(0x1019, 0x2819,
- "ECS/PC chips", STAC_ECS_202),
- SND_PCI_QUIRK(0x1019, 0x2820,
+ SND_PCI_QUIRK_MASK(0x1019, 0xf000, 0x2000,
"ECS/PC chips", STAC_ECS_202),
{} /* terminator */
};
@@ -2132,6 +2171,7 @@
};
static const char *stac927x_models[STAC_927X_MODELS] = {
+ [STAC_927X_AUTO] = "auto",
[STAC_D965_REF_NO_JD] = "ref-no-jd",
[STAC_D965_REF] = "ref",
[STAC_D965_3ST] = "3stack",
@@ -2144,26 +2184,16 @@
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_D965_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+ "DFI LanParty", STAC_D965_REF),
/* Intel 946 based systems */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x3d01, "Intel D946", STAC_D965_3ST),
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xa301, "Intel D946", STAC_D965_3ST),
/* 965 based 3 stack systems */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2116, "Intel D965", STAC_D965_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2115, "Intel D965", STAC_D965_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2114, "Intel D965", STAC_D965_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2113, "Intel D965", STAC_D965_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2112, "Intel D965", STAC_D965_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2111, "Intel D965", STAC_D965_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2110, "Intel D965", STAC_D965_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2009, "Intel D965", STAC_D965_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2008, "Intel D965", STAC_D965_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2007, "Intel D965", STAC_D965_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2006, "Intel D965", STAC_D965_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2005, "Intel D965", STAC_D965_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2004, "Intel D965", STAC_D965_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2003, "Intel D965", STAC_D965_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2100,
+ "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2000,
+ "Intel D965", STAC_D965_3ST),
/* Dell 3 stack systems */
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_3ST),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01dd, "Dell Dimension E520", STAC_DELL_3ST),
@@ -2179,15 +2209,10 @@
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS),
/* 965 based 5 stack systems */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2301, "Intel D965", STAC_D965_5ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2302, "Intel D965", STAC_D965_5ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2303, "Intel D965", STAC_D965_5ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2304, "Intel D965", STAC_D965_5ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2305, "Intel D965", STAC_D965_5ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2501, "Intel D965", STAC_D965_5ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2502, "Intel D965", STAC_D965_5ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2503, "Intel D965", STAC_D965_5ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2504, "Intel D965", STAC_D965_5ST),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2300,
+ "Intel D965", STAC_D965_5ST),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2500,
+ "Intel D965", STAC_D965_5ST),
{} /* terminator */
};
@@ -2240,19 +2265,25 @@
[STAC_9205_DELL_M42] = dell_9205_m42_pin_configs,
[STAC_9205_DELL_M43] = dell_9205_m43_pin_configs,
[STAC_9205_DELL_M44] = dell_9205_m44_pin_configs,
+ [STAC_9205_EAPD] = NULL,
};
static const char *stac9205_models[STAC_9205_MODELS] = {
+ [STAC_9205_AUTO] = "auto",
[STAC_9205_REF] = "ref",
[STAC_9205_DELL_M42] = "dell-m42",
[STAC_9205_DELL_M43] = "dell-m43",
[STAC_9205_DELL_M44] = "dell-m44",
+ [STAC_9205_EAPD] = "eapd",
};
static struct snd_pci_quirk stac9205_cfg_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_9205_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+ "DFI LanParty", STAC_9205_REF),
+ /* Dell */
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
"unknown Dell", STAC_9205_DELL_M42),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2,
@@ -2283,101 +2314,24 @@
"Dell Inspiron", STAC_9205_DELL_M44),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228,
"Dell Vostro 1500", STAC_9205_DELL_M42),
+ /* Gateway */
+ SND_PCI_QUIRK(0x107b, 0x0565, "Gateway T1616", STAC_9205_EAPD),
{} /* terminator */
};
-static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
-{
- int i;
- struct sigmatel_spec *spec = codec->spec;
-
- kfree(spec->pin_configs);
- spec->pin_configs = kcalloc(spec->num_pins, sizeof(*spec->pin_configs),
- GFP_KERNEL);
- if (!spec->pin_configs)
- return -ENOMEM;
-
- for (i = 0; i < spec->num_pins; i++) {
- hda_nid_t nid = spec->pin_nids[i];
- unsigned int pin_cfg;
-
- pin_cfg = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0x00);
- snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x bios pin config %8.8x\n",
- nid, pin_cfg);
- spec->pin_configs[i] = pin_cfg;
- }
-
- return 0;
-}
-
-static void stac92xx_set_config_reg(struct hda_codec *codec,
- hda_nid_t pin_nid, unsigned int pin_config)
-{
- int i;
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
- pin_config & 0x000000ff);
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
- (pin_config & 0x0000ff00) >> 8);
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
- (pin_config & 0x00ff0000) >> 16);
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
- pin_config >> 24);
- i = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT,
- 0x00);
- snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n",
- pin_nid, i);
-}
-
-static void stac92xx_set_config_regs(struct hda_codec *codec)
+static void stac92xx_set_config_regs(struct hda_codec *codec,
+ unsigned int *pincfgs)
{
int i;
struct sigmatel_spec *spec = codec->spec;
- if (!spec->pin_configs)
- return;
+ if (!pincfgs)
+ return;
for (i = 0; i < spec->num_pins; i++)
- stac92xx_set_config_reg(codec, spec->pin_nids[i],
- spec->pin_configs[i]);
-}
-
-static int stac_save_pin_cfgs(struct hda_codec *codec, unsigned int *pins)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (!pins)
- return stac92xx_save_bios_config_regs(codec);
-
- kfree(spec->pin_configs);
- spec->pin_configs = kmemdup(pins,
- spec->num_pins * sizeof(*pins),
- GFP_KERNEL);
- if (!spec->pin_configs)
- return -ENOMEM;
-
- stac92xx_set_config_regs(codec);
- return 0;
-}
-
-static void stac_change_pin_config(struct hda_codec *codec, hda_nid_t nid,
- unsigned int cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_pins; i++) {
- if (spec->pin_nids[i] == nid) {
- spec->pin_configs[i] = cfg;
- stac92xx_set_config_reg(codec, nid, cfg);
- break;
- }
- }
+ if (spec->pin_nids[i] && pincfgs[i])
+ snd_hda_codec_set_pincfg(codec, spec->pin_nids[i],
+ pincfgs[i]);
}
/*
@@ -2567,7 +2521,7 @@
codec->num_pcms++;
info++;
info->name = "STAC92xx Digital";
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
+ info->pcm_type = spec->autocfg.dig_out_type[0];
if (spec->multiout.dig_out_nid) {
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
@@ -2583,8 +2537,7 @@
static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid)
{
- unsigned int pincap = snd_hda_param_read(codec, nid,
- AC_PAR_PIN_CAP);
+ unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
if (pincap & AC_PINCAP_VREF_100)
return AC_PINCTL_VREF_100;
@@ -2759,22 +2712,37 @@
};
/* add dynamic controls */
-static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
- struct snd_kcontrol_new *ktemp,
- int idx, const char *name,
- unsigned long val)
+static struct snd_kcontrol_new *
+stac_control_new(struct sigmatel_spec *spec,
+ struct snd_kcontrol_new *ktemp,
+ const char *name)
{
struct snd_kcontrol_new *knew;
snd_array_init(&spec->kctls, sizeof(*knew), 32);
knew = snd_array_new(&spec->kctls);
if (!knew)
- return -ENOMEM;
+ return NULL;
*knew = *ktemp;
- knew->index = idx;
knew->name = kstrdup(name, GFP_KERNEL);
- if (!knew->name)
+ if (!knew->name) {
+ /* roolback */
+ memset(knew, 0, sizeof(*knew));
+ spec->kctls.alloced--;
+ return NULL;
+ }
+ return knew;
+}
+
+static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
+ struct snd_kcontrol_new *ktemp,
+ int idx, const char *name,
+ unsigned long val)
+{
+ struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name);
+ if (!knew)
return -ENOMEM;
+ knew->index = idx;
knew->private_value = val;
return 0;
}
@@ -2796,6 +2764,29 @@
return stac92xx_add_control_idx(spec, type, 0, name, val);
}
+static struct snd_kcontrol_new stac_input_src_temp = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input Source",
+ .info = stac92xx_mux_enum_info,
+ .get = stac92xx_mux_enum_get,
+ .put = stac92xx_mux_enum_put,
+};
+
+static int stac92xx_add_input_source(struct sigmatel_spec *spec)
+{
+ struct snd_kcontrol_new *knew;
+ struct hda_input_mux *imux = &spec->private_imux;
+
+ if (!spec->num_adcs || imux->num_items <= 1)
+ return 0; /* no need for input source control */
+ knew = stac_control_new(spec, &stac_input_src_temp,
+ stac_input_src_temp.name);
+ if (!knew)
+ return -ENOMEM;
+ knew->count = spec->num_adcs;
+ return 0;
+}
+
/* check whether the line-input can be used as line-out */
static hda_nid_t check_line_out_switch(struct hda_codec *codec)
{
@@ -2807,7 +2798,7 @@
if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
return 0;
nid = cfg->input_pins[AUTO_PIN_LINE];
- pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+ pincap = snd_hda_query_pin_caps(codec, nid);
if (pincap & AC_PINCAP_OUT)
return nid;
return 0;
@@ -2826,12 +2817,11 @@
mic_pin = AUTO_PIN_MIC;
for (;;) {
hda_nid_t nid = cfg->input_pins[mic_pin];
- def_conf = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0);
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
/* some laptops have an internal analog microphone
* which can't be used as a output */
if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
- pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+ pincap = snd_hda_query_pin_caps(codec, nid);
if (pincap & AC_PINCAP_OUT)
return nid;
}
@@ -2879,8 +2869,7 @@
conn_len = snd_hda_get_connections(codec, nid, conn,
HDA_MAX_CONNECTIONS);
for (j = 0; j < conn_len; j++) {
- wcaps = snd_hda_param_read(codec, conn[j],
- AC_PAR_AUDIO_WIDGET_CAP);
+ wcaps = get_wcaps(codec, conn[j]);
wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
/* we check only analog outputs */
if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL))
@@ -2895,6 +2884,16 @@
return conn[j];
}
}
+ /* if all DACs are already assigned, connect to the primary DAC */
+ if (conn_len > 1) {
+ for (j = 0; j < conn_len; j++) {
+ if (conn[j] == spec->multiout.dac_nids[0]) {
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_CONNECT_SEL, j);
+ break;
+ }
+ }
+ }
return 0;
}
@@ -2935,6 +2934,26 @@
add_spec_dacs(spec, dac);
}
+ for (i = 0; i < cfg->hp_outs; i++) {
+ nid = cfg->hp_pins[i];
+ dac = get_unassigned_dac(codec, nid);
+ if (dac) {
+ if (!spec->multiout.hp_nid)
+ spec->multiout.hp_nid = dac;
+ else
+ add_spec_extra_dacs(spec, dac);
+ }
+ spec->hp_dacs[i] = dac;
+ }
+
+ for (i = 0; i < cfg->speaker_outs; i++) {
+ nid = cfg->speaker_pins[i];
+ dac = get_unassigned_dac(codec, nid);
+ if (dac)
+ add_spec_extra_dacs(spec, dac);
+ spec->speaker_dacs[i] = dac;
+ }
+
/* add line-in as output */
nid = check_line_out_switch(codec);
if (nid) {
@@ -2962,26 +2981,6 @@
}
}
- for (i = 0; i < cfg->hp_outs; i++) {
- nid = cfg->hp_pins[i];
- dac = get_unassigned_dac(codec, nid);
- if (dac) {
- if (!spec->multiout.hp_nid)
- spec->multiout.hp_nid = dac;
- else
- add_spec_extra_dacs(spec, dac);
- }
- spec->hp_dacs[i] = dac;
- }
-
- for (i = 0; i < cfg->speaker_outs; i++) {
- nid = cfg->speaker_pins[i];
- dac = get_unassigned_dac(codec, nid);
- if (dac)
- add_spec_extra_dacs(spec, dac);
- spec->speaker_dacs[i] = dac;
- }
-
snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
spec->multiout.num_dacs,
spec->multiout.dac_nids[0],
@@ -2994,24 +2993,47 @@
}
/* create volume control/switch for the given prefx type */
-static int create_controls(struct sigmatel_spec *spec, const char *pfx, hda_nid_t nid, int chs)
+static int create_controls_idx(struct hda_codec *codec, const char *pfx,
+ int idx, hda_nid_t nid, int chs)
{
+ struct sigmatel_spec *spec = codec->spec;
char name[32];
int err;
+ if (!spec->check_volume_offset) {
+ unsigned int caps, step, nums, db_scale;
+ caps = query_amp_caps(codec, nid, HDA_OUTPUT);
+ step = (caps & AC_AMPCAP_STEP_SIZE) >>
+ AC_AMPCAP_STEP_SIZE_SHIFT;
+ step = (step + 1) * 25; /* in .01dB unit */
+ nums = (caps & AC_AMPCAP_NUM_STEPS) >>
+ AC_AMPCAP_NUM_STEPS_SHIFT;
+ db_scale = nums * step;
+ /* if dB scale is over -64dB, and finer enough,
+ * let's reduce it to half
+ */
+ if (db_scale > 6400 && nums >= 0x1f)
+ spec->volume_offset = nums / 2;
+ spec->check_volume_offset = 1;
+ }
+
sprintf(name, "%s Playback Volume", pfx);
- err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
+ err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, idx, name,
+ HDA_COMPOSE_AMP_VAL_OFS(nid, chs, 0, HDA_OUTPUT,
+ spec->volume_offset));
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", pfx);
- err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, name,
+ err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_MUTE, idx, name,
HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
if (err < 0)
return err;
return 0;
}
+#define create_controls(codec, pfx, nid, chs) \
+ create_controls_idx(codec, pfx, 0, nid, chs)
+
static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
{
if (spec->multiout.num_dacs > 4) {
@@ -3037,40 +3059,32 @@
return 1;
}
-static int is_unique_dac(struct sigmatel_spec *spec, hda_nid_t nid)
-{
- int i;
-
- if (spec->autocfg.line_outs != 1)
- return 0;
- if (spec->multiout.hp_nid == nid)
- return 0;
- for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++)
- if (spec->multiout.extra_out_nid[i] == nid)
- return 0;
- return 1;
-}
-
-/* add playback controls from the parsed DAC table */
-static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+/* Create output controls
+ * The mixer elements are named depending on the given type (AUTO_PIN_XXX_OUT)
+ */
+static int create_multi_out_ctls(struct hda_codec *codec, int num_outs,
+ const hda_nid_t *pins,
+ const hda_nid_t *dac_nids,
+ int type)
{
struct sigmatel_spec *spec = codec->spec;
static const char *chname[4] = {
"Front", "Surround", NULL /*CLFE*/, "Side"
};
- hda_nid_t nid = 0;
+ hda_nid_t nid;
int i, err;
unsigned int wid_caps;
- for (i = 0; i < cfg->line_outs && spec->multiout.dac_nids[i]; i++) {
- nid = spec->multiout.dac_nids[i];
- if (i == 2) {
+ for (i = 0; i < num_outs && i < ARRAY_SIZE(chname); i++) {
+ nid = dac_nids[i];
+ if (!nid)
+ continue;
+ if (type != AUTO_PIN_HP_OUT && i == 2) {
/* Center/LFE */
- err = create_controls(spec, "Center", nid, 1);
+ err = create_controls(codec, "Center", nid, 1);
if (err < 0)
return err;
- err = create_controls(spec, "LFE", nid, 2);
+ err = create_controls(codec, "LFE", nid, 2);
if (err < 0)
return err;
@@ -3086,23 +3100,47 @@
}
} else {
- const char *name = chname[i];
- /* if it's a single DAC, assign a better name */
- if (!i && is_unique_dac(spec, nid)) {
- switch (cfg->line_out_type) {
- case AUTO_PIN_HP_OUT:
- name = "Headphone";
- break;
- case AUTO_PIN_SPEAKER_OUT:
- name = "Speaker";
- break;
- }
+ const char *name;
+ int idx;
+ switch (type) {
+ case AUTO_PIN_HP_OUT:
+ name = "Headphone";
+ idx = i;
+ break;
+ case AUTO_PIN_SPEAKER_OUT:
+ name = "Speaker";
+ idx = i;
+ break;
+ default:
+ name = chname[i];
+ idx = 0;
+ break;
}
- err = create_controls(spec, name, nid, 3);
+ err = create_controls_idx(codec, name, idx, nid, 3);
if (err < 0)
return err;
+ if (type == AUTO_PIN_HP_OUT && !spec->hp_detect) {
+ wid_caps = get_wcaps(codec, pins[i]);
+ if (wid_caps & AC_WCAP_UNSOL_CAP)
+ spec->hp_detect = 1;
+ }
}
}
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int err;
+
+ err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins,
+ spec->multiout.dac_nids,
+ cfg->line_out_type);
+ if (err < 0)
+ return err;
if (cfg->hp_outs > 1 && cfg->line_out_type == AUTO_PIN_LINE_OUT) {
err = stac92xx_add_control(spec,
@@ -3137,40 +3175,18 @@
struct auto_pin_cfg *cfg)
{
struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid;
- int i, err, nums;
+ int err;
- nums = 0;
- for (i = 0; i < cfg->hp_outs; i++) {
- static const char *pfxs[] = {
- "Headphone", "Headphone2", "Headphone3",
- };
- unsigned int wid_caps = get_wcaps(codec, cfg->hp_pins[i]);
- if (wid_caps & AC_WCAP_UNSOL_CAP)
- spec->hp_detect = 1;
- if (nums >= ARRAY_SIZE(pfxs))
- continue;
- nid = spec->hp_dacs[i];
- if (!nid)
- continue;
- err = create_controls(spec, pfxs[nums++], nid, 3);
- if (err < 0)
- return err;
- }
- nums = 0;
- for (i = 0; i < cfg->speaker_outs; i++) {
- static const char *pfxs[] = {
- "Speaker", "External Speaker", "Speaker2",
- };
- if (nums >= ARRAY_SIZE(pfxs))
- continue;
- nid = spec->speaker_dacs[i];
- if (!nid)
- continue;
- err = create_controls(spec, pfxs[nums++], nid, 3);
- if (err < 0)
- return err;
- }
+ err = create_multi_out_ctls(codec, cfg->hp_outs, cfg->hp_pins,
+ spec->hp_dacs, AUTO_PIN_HP_OUT);
+ if (err < 0)
+ return err;
+
+ err = create_multi_out_ctls(codec, cfg->speaker_outs, cfg->speaker_pins,
+ spec->speaker_dacs, AUTO_PIN_SPEAKER_OUT);
+ if (err < 0)
+ return err;
+
return 0;
}
@@ -3379,11 +3395,7 @@
unsigned int wcaps;
unsigned int def_conf;
- def_conf = snd_hda_codec_read(codec,
- spec->dmic_nids[i],
- 0,
- AC_VERB_GET_CONFIG_DEFAULT,
- 0);
+ def_conf = snd_hda_codec_get_pincfg(codec, spec->dmic_nids[i]);
if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
continue;
@@ -3507,6 +3519,7 @@
static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in)
{
struct sigmatel_spec *spec = codec->spec;
+ int hp_swap = 0;
int err;
if ((err = snd_hda_parse_pin_def_config(codec,
@@ -3516,7 +3529,6 @@
if (! spec->autocfg.line_outs)
return 0; /* can't find valid pin config */
-#if 0 /* FIXME: temporarily disabled */
/* If we have no real line-out pin and multiple hp-outs, HPs should
* be set up as multi-channel outputs.
*/
@@ -3535,8 +3547,8 @@
spec->autocfg.line_outs = spec->autocfg.hp_outs;
spec->autocfg.line_out_type = AUTO_PIN_HP_OUT;
spec->autocfg.hp_outs = 0;
+ hp_swap = 1;
}
-#endif /* FIXME: temporarily disabled */
if (spec->autocfg.mono_out_pin) {
int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) &
(AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP);
@@ -3629,12 +3641,19 @@
#endif
err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg);
-
if (err < 0)
return err;
- err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg);
+ /* All output parsing done, now restore the swapped hp pins */
+ if (hp_swap) {
+ memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
+ sizeof(spec->autocfg.hp_pins));
+ spec->autocfg.hp_outs = spec->autocfg.line_outs;
+ spec->autocfg.line_out_type = AUTO_PIN_HP_OUT;
+ spec->autocfg.line_outs = 0;
+ }
+ err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -3663,11 +3682,15 @@
return err;
}
+ err = stac92xx_add_input_source(spec);
+ if (err < 0)
+ return err;
+
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
if (spec->multiout.max_channels > 2)
spec->surr_switch = 1;
- if (spec->autocfg.dig_out_pin)
+ if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = dig_out;
if (dig_in && spec->autocfg.dig_in_pin)
spec->dig_in_nid = dig_in;
@@ -3730,9 +3753,7 @@
for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) {
hda_nid_t pin = spec->autocfg.line_out_pins[i];
unsigned int defcfg;
- defcfg = snd_hda_codec_read(codec, pin, 0,
- AC_VERB_GET_CONFIG_DEFAULT,
- 0x00);
+ defcfg = snd_hda_codec_get_pincfg(codec, pin);
if (get_defcfg_device(defcfg) == AC_JACK_SPEAKER) {
unsigned int wcaps = get_wcaps(codec, pin);
wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
@@ -3745,7 +3766,7 @@
}
if (lfe_pin) {
- err = create_controls(spec, "LFE", lfe_pin, 1);
+ err = create_controls(codec, "LFE", lfe_pin, 1);
if (err < 0)
return err;
}
@@ -3776,7 +3797,11 @@
return err;
}
- if (spec->autocfg.dig_out_pin)
+ err = stac92xx_add_input_source(spec);
+ if (err < 0)
+ return err;
+
+ if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = 0x05;
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = 0x04;
@@ -3832,8 +3857,7 @@
#ifdef CONFIG_SND_JACK
struct sigmatel_spec *spec = codec->spec;
struct sigmatel_jack *jack;
- int def_conf = snd_hda_codec_read(codec, nid,
- 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+ int def_conf = snd_hda_codec_get_pincfg(codec, nid);
int connectivity = get_defcfg_connect(def_conf);
char name[32];
@@ -3948,6 +3972,36 @@
static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
int enable);
+/* override some hints from the hwdep entry */
+static void stac_store_hints(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ const char *p;
+ int val;
+
+ val = snd_hda_get_bool_hint(codec, "hp_detect");
+ if (val >= 0)
+ spec->hp_detect = val;
+ p = snd_hda_get_hint(codec, "gpio_mask");
+ if (p) {
+ spec->gpio_mask = simple_strtoul(p, NULL, 0);
+ spec->eapd_mask = spec->gpio_dir = spec->gpio_data =
+ spec->gpio_mask;
+ }
+ p = snd_hda_get_hint(codec, "gpio_dir");
+ if (p)
+ spec->gpio_dir = simple_strtoul(p, NULL, 0) & spec->gpio_mask;
+ p = snd_hda_get_hint(codec, "gpio_data");
+ if (p)
+ spec->gpio_data = simple_strtoul(p, NULL, 0) & spec->gpio_mask;
+ p = snd_hda_get_hint(codec, "eapd_mask");
+ if (p)
+ spec->eapd_mask = simple_strtoul(p, NULL, 0) & spec->gpio_mask;
+ val = snd_hda_get_bool_hint(codec, "eapd_switch");
+ if (val >= 0)
+ spec->eapd_switch = val;
+}
+
static int stac92xx_init(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -3964,6 +4018,9 @@
spec->adc_nids[i], 0,
AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ /* override some hints */
+ stac_store_hints(codec);
+
/* set up GPIO */
gpio = spec->gpio_data;
/* turn on EAPD statically when spec->eapd_switch isn't set.
@@ -4013,8 +4070,7 @@
pinctl);
}
}
- conf = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0);
+ conf = snd_hda_codec_get_pincfg(codec, nid);
if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
enable_pin_detect(codec, nid,
STAC_INSERT_EVENT);
@@ -4026,8 +4082,8 @@
for (i = 0; i < spec->num_dmics; i++)
stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
AC_PINCTL_IN_EN);
- if (cfg->dig_out_pin)
- stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
+ if (cfg->dig_out_pins[0])
+ stac92xx_auto_set_pinctl(codec, cfg->dig_out_pins[0],
AC_PINCTL_OUT_EN);
if (cfg->dig_in_pin)
stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
@@ -4055,8 +4111,7 @@
stac_toggle_power_map(codec, nid, 1);
continue;
}
- def_conf = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0);
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
def_conf = get_defcfg_connect(def_conf);
/* skip any ports that don't have jacks since presence
* detection is useless */
@@ -4110,7 +4165,6 @@
if (! spec)
return;
- kfree(spec->pin_configs);
stac92xx_free_jacks(codec);
snd_array_free(&spec->events);
@@ -4121,7 +4175,9 @@
static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
unsigned int flag)
{
- unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
+ unsigned int old_ctl, pin_ctl;
+
+ pin_ctl = snd_hda_codec_read(codec, nid,
0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
if (pin_ctl & AC_PINCTL_IN_EN) {
@@ -4135,14 +4191,17 @@
return;
}
+ old_ctl = pin_ctl;
/* if setting pin direction bits, clear the current
direction bits first */
if (flag & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))
pin_ctl &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_ctl | flag);
+ pin_ctl |= flag;
+ if (old_ctl != pin_ctl)
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pin_ctl);
}
static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
@@ -4150,9 +4209,10 @@
{
unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_ctl & ~flag);
+ if (pin_ctl & flag)
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pin_ctl & ~flag);
}
static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
@@ -4415,7 +4475,6 @@
{
struct sigmatel_spec *spec = codec->spec;
- stac92xx_set_config_regs(codec);
stac92xx_init(codec);
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
@@ -4426,6 +4485,37 @@
return 0;
}
+
+/*
+ * using power check for controlling mute led of HP HDX notebooks
+ * check for mute state only on Speakers (nid = 0x10)
+ *
+ * For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise
+ * the LED is NOT working properly !
+ */
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static int stac92xx_hp_hdx_check_power_status(struct hda_codec *codec,
+ hda_nid_t nid)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (nid == 0x10) {
+ if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
+ HDA_AMP_MUTE)
+ spec->gpio_data &= ~0x08; /* orange */
+ else
+ spec->gpio_data |= 0x08; /* white */
+
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir,
+ spec->gpio_data);
+ }
+
+ return 0;
+}
+#endif
+
static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
{
struct sigmatel_spec *spec = codec->spec;
@@ -4464,16 +4554,11 @@
spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
stac9200_models,
stac9200_cfg_tbl);
- if (spec->board_config < 0) {
+ if (spec->board_config < 0)
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n");
- err = stac92xx_save_bios_config_regs(codec);
- } else
- err = stac_save_pin_cfgs(codec,
+ else
+ stac92xx_set_config_regs(codec,
stac9200_brd_tbl[spec->board_config]);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = 1;
@@ -4541,17 +4626,12 @@
stac925x_models,
stac925x_cfg_tbl);
again:
- if (spec->board_config < 0) {
+ if (spec->board_config < 0)
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x,"
"using BIOS defaults\n");
- err = stac92xx_save_bios_config_regs(codec);
- } else
- err = stac_save_pin_cfgs(codec,
+ else
+ stac92xx_set_config_regs(codec,
stac925x_brd_tbl[spec->board_config]);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = 1;
@@ -4629,17 +4709,12 @@
stac92hd73xx_models,
stac92hd73xx_cfg_tbl);
again:
- if (spec->board_config < 0) {
+ if (spec->board_config < 0)
snd_printdd(KERN_INFO "hda_codec: Unknown model for"
" STAC92HD73XX, using BIOS defaults\n");
- err = stac92xx_save_bios_config_regs(codec);
- } else
- err = stac_save_pin_cfgs(codec,
+ else
+ stac92xx_set_config_regs(codec,
stac92hd73xx_brd_tbl[spec->board_config]);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
num_dacs = snd_hda_get_connections(codec, 0x0a,
conn, STAC92HD73_DAC_COUNT + 2) - 1;
@@ -4653,14 +4728,18 @@
case 0x3: /* 6 Channel */
spec->mixer = stac92hd73xx_6ch_mixer;
spec->init = stac92hd73xx_6ch_core_init;
+ spec->aloopback_ctl = stac92hd73xx_6ch_loopback;
break;
case 0x4: /* 8 Channel */
spec->mixer = stac92hd73xx_8ch_mixer;
spec->init = stac92hd73xx_8ch_core_init;
+ spec->aloopback_ctl = stac92hd73xx_8ch_loopback;
break;
case 0x5: /* 10 Channel */
spec->mixer = stac92hd73xx_10ch_mixer;
spec->init = stac92hd73xx_10ch_core_init;
+ spec->aloopback_ctl = stac92hd73xx_10ch_loopback;
+ break;
}
spec->multiout.dac_nids = spec->dac_nids;
@@ -4699,18 +4778,18 @@
spec->init = dell_m6_core_init;
switch (spec->board_config) {
case STAC_DELL_M6_AMIC: /* Analog Mics */
- stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
+ snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
spec->num_dmics = 0;
spec->private_dimux.num_items = 1;
break;
case STAC_DELL_M6_DMIC: /* Digital Mics */
- stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
+ snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
spec->num_dmics = 1;
spec->private_dimux.num_items = 2;
break;
case STAC_DELL_M6_BOTH: /* Both */
- stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
- stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
+ snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
+ snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
spec->num_dmics = 1;
spec->private_dimux.num_items = 2;
break;
@@ -4773,6 +4852,7 @@
hda_nid_t conn[STAC92HD83_DAC_COUNT + 1];
int err;
int num_dacs;
+ hda_nid_t nid;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -4791,15 +4871,6 @@
spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
spec->multiout.dac_nids = spec->dac_nids;
-
- /* set port 0xe to select the last DAC
- */
- num_dacs = snd_hda_get_connections(codec, 0x0e,
- conn, STAC92HD83_DAC_COUNT + 1) - 1;
-
- snd_hda_codec_write_cache(codec, 0xe, 0,
- AC_VERB_SET_CONNECT_SEL, num_dacs);
-
spec->init = stac92hd83xxx_core_init;
spec->mixer = stac92hd83xxx_mixer;
spec->num_pins = ARRAY_SIZE(stac92hd83xxx_pin_nids);
@@ -4814,17 +4885,12 @@
stac92hd83xxx_models,
stac92hd83xxx_cfg_tbl);
again:
- if (spec->board_config < 0) {
+ if (spec->board_config < 0)
snd_printdd(KERN_INFO "hda_codec: Unknown model for"
" STAC92HD83XXX, using BIOS defaults\n");
- err = stac92xx_save_bios_config_regs(codec);
- } else
- err = stac_save_pin_cfgs(codec,
+ else
+ stac92xx_set_config_regs(codec,
stac92hd83xxx_brd_tbl[spec->board_config]);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
switch (codec->vendor_id) {
case 0x111d7604:
@@ -4851,6 +4917,23 @@
return err;
}
+ switch (spec->board_config) {
+ case STAC_DELL_S14:
+ nid = 0xf;
+ break;
+ default:
+ nid = 0xe;
+ break;
+ }
+
+ num_dacs = snd_hda_get_connections(codec, nid,
+ conn, STAC92HD83_DAC_COUNT + 1) - 1;
+
+ /* set port X to select the last DAC
+ */
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_CONNECT_SEL, num_dacs);
+
codec->patch_ops = stac92xx_patch_ops;
codec->proc_widget_hook = stac92hd_proc_hook;
@@ -4858,7 +4941,16 @@
return 0;
}
-static struct hda_input_mux stac92hd71bxx_dmux = {
+static struct hda_input_mux stac92hd71bxx_dmux_nomixer = {
+ .num_items = 3,
+ .items = {
+ { "Analog Inputs", 0x00 },
+ { "Digital Mic 1", 0x02 },
+ { "Digital Mic 2", 0x03 },
+ }
+};
+
+static struct hda_input_mux stac92hd71bxx_dmux_amixer = {
.num_items = 4,
.items = {
{ "Analog Inputs", 0x00 },
@@ -4868,10 +4960,67 @@
}
};
+/* get the pin connection (fixed, none, etc) */
+static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int cfg;
+
+ cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
+ return get_defcfg_connect(cfg);
+}
+
+static int stac92hd71bxx_connected_ports(struct hda_codec *codec,
+ hda_nid_t *nids, int num_nids)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int idx, num;
+ unsigned int def_conf;
+
+ for (num = 0; num < num_nids; num++) {
+ for (idx = 0; idx < spec->num_pins; idx++)
+ if (spec->pin_nids[idx] == nids[num])
+ break;
+ if (idx >= spec->num_pins)
+ break;
+ def_conf = stac_get_defcfg_connect(codec, idx);
+ if (def_conf == AC_JACK_PORT_NONE)
+ break;
+ }
+ return num;
+}
+
+static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec,
+ hda_nid_t dig0pin)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int idx;
+
+ for (idx = 0; idx < spec->num_pins; idx++)
+ if (spec->pin_nids[idx] == dig0pin)
+ break;
+ if ((idx + 2) >= spec->num_pins)
+ return 0;
+
+ /* dig1pin case */
+ if (stac_get_defcfg_connect(codec, idx + 1) != AC_JACK_PORT_NONE)
+ return 2;
+
+ /* dig0pin + dig2pin case */
+ if (stac_get_defcfg_connect(codec, idx + 2) != AC_JACK_PORT_NONE)
+ return 2;
+ if (stac_get_defcfg_connect(codec, idx) != AC_JACK_PORT_NONE)
+ return 1;
+ else
+ return 0;
+}
+
static int patch_stac92hd71bxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
+ struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init;
int err = 0;
+ unsigned int ndmic_nids = 0;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -4879,27 +5028,32 @@
codec->spec = spec;
codec->patch_ops = stac92xx_patch_ops;
- spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids);
+ spec->num_pins = STAC92HD71BXX_NUM_PINS;
+ switch (codec->vendor_id) {
+ case 0x111d76b6:
+ case 0x111d76b7:
+ spec->pin_nids = stac92hd71bxx_pin_nids_4port;
+ break;
+ case 0x111d7603:
+ case 0x111d7608:
+ /* On 92HD75Bx 0x27 isn't a pin nid */
+ spec->num_pins--;
+ /* fallthrough */
+ default:
+ spec->pin_nids = stac92hd71bxx_pin_nids_6port;
+ }
spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
- spec->pin_nids = stac92hd71bxx_pin_nids;
- memcpy(&spec->private_dimux, &stac92hd71bxx_dmux,
- sizeof(stac92hd71bxx_dmux));
spec->board_config = snd_hda_check_board_config(codec,
STAC_92HD71BXX_MODELS,
stac92hd71bxx_models,
stac92hd71bxx_cfg_tbl);
again:
- if (spec->board_config < 0) {
+ if (spec->board_config < 0)
snd_printdd(KERN_INFO "hda_codec: Unknown model for"
" STAC92HD71BXX, using BIOS defaults\n");
- err = stac92xx_save_bios_config_regs(codec);
- } else
- err = stac_save_pin_cfgs(codec,
+ else
+ stac92xx_set_config_regs(codec,
stac92hd71bxx_brd_tbl[spec->board_config]);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
if (spec->board_config > STAC_92HD71BXX_REF) {
/* GPIO0 = EAPD */
@@ -4908,16 +5062,34 @@
spec->gpio_data = 0x01;
}
+ spec->dmic_nids = stac92hd71bxx_dmic_nids;
+ spec->dmux_nids = stac92hd71bxx_dmux_nids;
+
switch (codec->vendor_id) {
case 0x111d76b6: /* 4 Port without Analog Mixer */
case 0x111d76b7:
+ unmute_init++;
+ /* fallthru */
case 0x111d76b4: /* 6 Port without Analog Mixer */
case 0x111d76b5:
+ memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_nomixer,
+ sizeof(stac92hd71bxx_dmux_nomixer));
spec->mixer = stac92hd71bxx_mixer;
spec->init = stac92hd71bxx_core_init;
codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
+ spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+ stac92hd71bxx_dmic_nids,
+ STAC92HD71BXX_NUM_DMICS);
+ if (spec->num_dmics) {
+ spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
+ spec->dinput_mux = &spec->private_dimux;
+ ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1;
+ }
break;
case 0x111d7608: /* 5 Port with Analog Mixer */
+ memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_amixer,
+ sizeof(stac92hd71bxx_dmux_amixer));
+ spec->private_dimux.num_items--;
switch (spec->board_config) {
case STAC_HP_M4:
/* Enable VREF power saving on GPIO1 detect */
@@ -4944,7 +5116,15 @@
/* disable VSW */
spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF];
- stac_change_pin_config(codec, 0xf, 0x40f000f0);
+ unmute_init++;
+ snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
+ snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
+ stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS - 1] = 0;
+ spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+ stac92hd71bxx_dmic_nids,
+ STAC92HD71BXX_NUM_DMICS - 1);
+ spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
+ ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 2;
break;
case 0x111d7603: /* 6 Port with Analog Mixer */
if ((codec->revision_id & 0xf) == 1)
@@ -4954,12 +5134,23 @@
spec->num_pwrs = 0;
/* fallthru */
default:
+ memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_amixer,
+ sizeof(stac92hd71bxx_dmux_amixer));
spec->dinput_mux = &spec->private_dimux;
spec->mixer = stac92hd71bxx_analog_mixer;
spec->init = stac92hd71bxx_analog_core_init;
codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
+ spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+ stac92hd71bxx_dmic_nids,
+ STAC92HD71BXX_NUM_DMICS);
+ spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
+ ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1;
}
+ if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
+ snd_hda_sequence_write_cache(codec, unmute_init);
+
+ spec->aloopback_ctl = stac92hd71bxx_loopback;
spec->aloopback_mask = 0x50;
spec->aloopback_shift = 0;
@@ -4967,18 +5158,17 @@
spec->digbeep_nid = 0x26;
spec->mux_nids = stac92hd71bxx_mux_nids;
spec->adc_nids = stac92hd71bxx_adc_nids;
- spec->dmic_nids = stac92hd71bxx_dmic_nids;
- spec->dmux_nids = stac92hd71bxx_dmux_nids;
spec->smux_nids = stac92hd71bxx_smux_nids;
spec->pwr_nids = stac92hd71bxx_pwr_nids;
spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids);
spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids);
+ spec->num_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e);
switch (spec->board_config) {
case STAC_HP_M4:
/* enable internal microphone */
- stac_change_pin_config(codec, 0x0e, 0x01813040);
+ snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040);
stac92xx_auto_set_pinctl(codec, 0x0e,
AC_PINCTL_IN_EN | AC_PINCTL_VREF_80);
/* fallthru */
@@ -4993,19 +5183,36 @@
spec->num_smuxes = 0;
spec->num_dmuxes = 1;
break;
- default:
- spec->num_dmics = STAC92HD71BXX_NUM_DMICS;
- spec->num_smuxes = ARRAY_SIZE(stac92hd71bxx_smux_nids);
- spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
+ case STAC_HP_DV5:
+ snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010);
+ stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN);
+ break;
+ case STAC_HP_HDX:
+ spec->num_dmics = 1;
+ spec->num_dmuxes = 1;
+ spec->num_smuxes = 1;
+ /*
+ * For controlling MUTE LED on HP HDX16/HDX18 notebooks,
+ * the CONFIG_SND_HDA_POWER_SAVE is needed to be set.
+ */
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ /* orange/white mute led on GPIO3, orange=0, white=1 */
+ spec->gpio_mask |= 0x08;
+ spec->gpio_dir |= 0x08;
+ spec->gpio_data |= 0x08; /* set to white */
+
+ /* register check_power_status callback. */
+ codec->patch_ops.check_power_status =
+ stac92xx_hp_hdx_check_power_status;
+#endif
+ break;
};
spec->multiout.dac_nids = spec->dac_nids;
if (spec->dinput_mux)
- spec->private_dimux.num_items +=
- spec->num_dmics -
- (ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1);
+ spec->private_dimux.num_items += spec->num_dmics - ndmic_nids;
- err = stac92xx_parse_auto_config(codec, 0x21, 0x23);
+ err = stac92xx_parse_auto_config(codec, 0x21, 0);
if (!err) {
if (spec->board_config < 0) {
printk(KERN_WARNING "hda_codec: No auto-config is "
@@ -5080,17 +5287,12 @@
}
again:
- if (spec->board_config < 0) {
+ if (spec->board_config < 0)
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
"using BIOS defaults\n");
- err = stac92xx_save_bios_config_regs(codec);
- } else
- err = stac_save_pin_cfgs(codec,
+ else
+ stac92xx_set_config_regs(codec,
stac922x_brd_tbl[spec->board_config]);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
spec->adc_nids = stac922x_adc_nids;
spec->mux_nids = stac922x_mux_nids;
@@ -5141,24 +5343,19 @@
return -ENOMEM;
codec->spec = spec;
+ codec->slave_dig_outs = stac927x_slave_dig_outs;
spec->num_pins = ARRAY_SIZE(stac927x_pin_nids);
spec->pin_nids = stac927x_pin_nids;
spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS,
stac927x_models,
stac927x_cfg_tbl);
again:
- if (spec->board_config < 0 || !stac927x_brd_tbl[spec->board_config]) {
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: Unknown model for"
- "STAC927x, using BIOS defaults\n");
- err = stac92xx_save_bios_config_regs(codec);
- } else
- err = stac_save_pin_cfgs(codec,
+ if (spec->board_config < 0)
+ snd_printdd(KERN_INFO "hda_codec: Unknown model for"
+ "STAC927x, using BIOS defaults\n");
+ else
+ stac92xx_set_config_regs(codec,
stac927x_brd_tbl[spec->board_config]);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
spec->digbeep_nid = 0x23;
spec->adc_nids = stac927x_adc_nids;
@@ -5187,15 +5384,15 @@
case 0x10280209:
case 0x1028022e:
/* correct the device field to SPDIF out */
- stac_change_pin_config(codec, 0x21, 0x01442070);
+ snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070);
break;
};
/* configure the analog microphone on some laptops */
- stac_change_pin_config(codec, 0x0c, 0x90a79130);
+ snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130);
/* correct the front output jack as a hp out */
- stac_change_pin_config(codec, 0x0f, 0x0227011f);
+ snd_hda_codec_set_pincfg(codec, 0x0f, 0x0227011f);
/* correct the front input jack as a mic */
- stac_change_pin_config(codec, 0x0e, 0x02a79130);
+ snd_hda_codec_set_pincfg(codec, 0x0e, 0x02a79130);
/* fallthru */
case STAC_DELL_3ST:
/* GPIO2 High = Enable EAPD */
@@ -5222,6 +5419,7 @@
}
spec->num_pwrs = 0;
+ spec->aloopback_ctl = stac927x_loopback;
spec->aloopback_mask = 0x40;
spec->aloopback_shift = 0;
spec->eapd_switch = 1;
@@ -5280,16 +5478,11 @@
stac9205_models,
stac9205_cfg_tbl);
again:
- if (spec->board_config < 0) {
+ if (spec->board_config < 0)
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n");
- err = stac92xx_save_bios_config_regs(codec);
- } else
- err = stac_save_pin_cfgs(codec,
+ else
+ stac92xx_set_config_regs(codec,
stac9205_brd_tbl[spec->board_config]);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
spec->digbeep_nid = 0x23;
spec->adc_nids = stac9205_adc_nids;
@@ -5306,17 +5499,20 @@
spec->init = stac9205_core_init;
spec->mixer = stac9205_mixer;
+ spec->aloopback_ctl = stac9205_loopback;
spec->aloopback_mask = 0x40;
spec->aloopback_shift = 0;
- spec->eapd_switch = 1;
+ /* Turn on/off EAPD per HP plugging */
+ if (spec->board_config != STAC_9205_EAPD)
+ spec->eapd_switch = 1;
spec->multiout.dac_nids = spec->dac_nids;
switch (spec->board_config){
case STAC_9205_DELL_M43:
/* Enable SPDIF in/out */
- stac_change_pin_config(codec, 0x1f, 0x01441030);
- stac_change_pin_config(codec, 0x20, 0x1c410030);
+ snd_hda_codec_set_pincfg(codec, 0x1f, 0x01441030);
+ snd_hda_codec_set_pincfg(codec, 0x20, 0x1c410030);
/* Enable unsol response for GPIO4/Dock HP connection */
err = stac_add_event(spec, codec->afg, STAC_VREF_EVENT, 0x01);
@@ -5373,223 +5569,87 @@
* STAC9872 hack
*/
-/* static config for Sony VAIO FE550G and Sony VAIO AR */
-static hda_nid_t vaio_dacs[] = { 0x2 };
-#define VAIO_HP_DAC 0x5
-static hda_nid_t vaio_adcs[] = { 0x8 /*,0x6*/ };
-static hda_nid_t vaio_mux_nids[] = { 0x15 };
-
-static struct hda_input_mux vaio_mux = {
- .num_items = 3,
- .items = {
- /* { "HP", 0x0 }, */
- { "Mic Jack", 0x1 },
- { "Internal Mic", 0x2 },
- { "PCM", 0x3 },
- }
-};
-
-static struct hda_verb vaio_init[] = {
- {0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */
- {0x0a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | STAC_HP_EVENT},
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */
- {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
- {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
+static struct hda_verb stac9872_core_init[] = {
{0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */
{}
};
-static struct hda_verb vaio_ar_init[] = {
- {0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */
- {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
- {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
-/* {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
-/* {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */
- {}
-};
-
-static struct snd_kcontrol_new vaio_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x02, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x02, 0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x05, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x05, 0, HDA_OUTPUT),
- /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
+static struct snd_kcontrol_new stac9872_mixer[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .count = 1,
- .info = stac92xx_mux_enum_info,
- .get = stac92xx_mux_enum_get,
- .put = stac92xx_mux_enum_put,
- },
- {}
+ { } /* end */
};
-static struct snd_kcontrol_new vaio_ar_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x02, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x02, 0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x05, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x05, 0, HDA_OUTPUT),
- /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
- /*HDA_CODEC_MUTE("Optical Out Switch", 0x10, 0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Optical Out Volume", 0x10, 0, HDA_OUTPUT),*/
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .count = 1,
- .info = stac92xx_mux_enum_info,
- .get = stac92xx_mux_enum_get,
- .put = stac92xx_mux_enum_put,
- },
- {}
+static hda_nid_t stac9872_pin_nids[] = {
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x11, 0x13, 0x14,
};
-static struct hda_codec_ops stac9872_patch_ops = {
- .build_controls = stac92xx_build_controls,
- .build_pcms = stac92xx_build_pcms,
- .init = stac92xx_init,
- .free = stac92xx_free,
-#ifdef SND_HDA_NEEDS_RESUME
- .resume = stac92xx_resume,
-#endif
+static hda_nid_t stac9872_adc_nids[] = {
+ 0x8 /*,0x6*/
};
-static int stac9872_vaio_init(struct hda_codec *codec)
-{
- int err;
-
- err = stac92xx_init(codec);
- if (err < 0)
- return err;
- if (codec->patch_ops.unsol_event)
- codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
- return 0;
-}
-
-static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res)
-{
- if (get_pin_presence(codec, 0x0a)) {
- stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
- stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
- } else {
- stac92xx_reset_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
- stac92xx_set_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
- }
-}
-
-static void stac9872_vaio_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- switch (res >> 26) {
- case STAC_HP_EVENT:
- stac9872_vaio_hp_detect(codec, res);
- break;
- }
-}
-
-static struct hda_codec_ops stac9872_vaio_patch_ops = {
- .build_controls = stac92xx_build_controls,
- .build_pcms = stac92xx_build_pcms,
- .init = stac9872_vaio_init,
- .free = stac92xx_free,
- .unsol_event = stac9872_vaio_unsol_event,
-#ifdef CONFIG_PM
- .resume = stac92xx_resume,
-#endif
+static hda_nid_t stac9872_mux_nids[] = {
+ 0x15
};
-enum { /* FE and SZ series. id=0x83847661 and subsys=0x104D0700 or 104D1000. */
- CXD9872RD_VAIO,
- /* Unknown. id=0x83847662 and subsys=0x104D1200 or 104D1000. */
- STAC9872AK_VAIO,
- /* Unknown. id=0x83847661 and subsys=0x104D1200. */
- STAC9872K_VAIO,
- /* AR Series. id=0x83847664 and subsys=104D1300 */
- CXD9872AKD_VAIO,
- STAC_9872_MODELS,
+static unsigned int stac9872_vaio_pin_configs[9] = {
+ 0x03211020, 0x411111f0, 0x411111f0, 0x03a15030,
+ 0x411111f0, 0x90170110, 0x411111f0, 0x411111f0,
+ 0x90a7013e
};
static const char *stac9872_models[STAC_9872_MODELS] = {
- [CXD9872RD_VAIO] = "vaio",
- [CXD9872AKD_VAIO] = "vaio-ar",
+ [STAC_9872_AUTO] = "auto",
+ [STAC_9872_VAIO] = "vaio",
+};
+
+static unsigned int *stac9872_brd_tbl[STAC_9872_MODELS] = {
+ [STAC_9872_VAIO] = stac9872_vaio_pin_configs,
};
static struct snd_pci_quirk stac9872_cfg_tbl[] = {
- SND_PCI_QUIRK(0x104d, 0x81e6, "Sony VAIO F/S", CXD9872RD_VAIO),
- SND_PCI_QUIRK(0x104d, 0x81ef, "Sony VAIO F/S", CXD9872RD_VAIO),
- SND_PCI_QUIRK(0x104d, 0x81fd, "Sony VAIO AR", CXD9872AKD_VAIO),
- SND_PCI_QUIRK(0x104d, 0x8205, "Sony VAIO AR", CXD9872AKD_VAIO),
- {}
+ {} /* terminator */
};
static int patch_stac9872(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
- int board_config;
+ int err;
- board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS,
- stac9872_models,
- stac9872_cfg_tbl);
- if (board_config < 0)
- /* unknown config, let generic-parser do its job... */
- return snd_hda_parse_generic_codec(codec);
-
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
-
codec->spec = spec;
- switch (board_config) {
- case CXD9872RD_VAIO:
- case STAC9872AK_VAIO:
- case STAC9872K_VAIO:
- spec->mixer = vaio_mixer;
- spec->init = vaio_init;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs);
- spec->multiout.dac_nids = vaio_dacs;
- spec->multiout.hp_nid = VAIO_HP_DAC;
- spec->num_adcs = ARRAY_SIZE(vaio_adcs);
- spec->adc_nids = vaio_adcs;
- spec->num_pwrs = 0;
- spec->input_mux = &vaio_mux;
- spec->mux_nids = vaio_mux_nids;
- codec->patch_ops = stac9872_vaio_patch_ops;
- break;
-
- case CXD9872AKD_VAIO:
- spec->mixer = vaio_ar_mixer;
- spec->init = vaio_ar_init;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs);
- spec->multiout.dac_nids = vaio_dacs;
- spec->multiout.hp_nid = VAIO_HP_DAC;
- spec->num_adcs = ARRAY_SIZE(vaio_adcs);
- spec->num_pwrs = 0;
- spec->adc_nids = vaio_adcs;
- spec->input_mux = &vaio_mux;
- spec->mux_nids = vaio_mux_nids;
- codec->patch_ops = stac9872_patch_ops;
- break;
- }
+ spec->board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS,
+ stac9872_models,
+ stac9872_cfg_tbl);
+ if (spec->board_config < 0)
+ snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9872, "
+ "using BIOS defaults\n");
+ else
+ stac92xx_set_config_regs(codec,
+ stac9872_brd_tbl[spec->board_config]);
+
+ spec->num_pins = ARRAY_SIZE(stac9872_pin_nids);
+ spec->pin_nids = stac9872_pin_nids;
+ spec->multiout.dac_nids = spec->dac_nids;
+ spec->num_adcs = ARRAY_SIZE(stac9872_adc_nids);
+ spec->adc_nids = stac9872_adc_nids;
+ spec->num_muxes = ARRAY_SIZE(stac9872_mux_nids);
+ spec->mux_nids = stac9872_mux_nids;
+ spec->mixer = stac9872_mixer;
+ spec->init = stac9872_core_init;
+
+ err = stac92xx_parse_auto_config(codec, 0x10, 0x12);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return -EINVAL;
+ }
+ spec->input_mux = &spec->private_imux;
+ codec->patch_ops = stac92xx_patch_ops;
return 0;
}
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index c761394..b25a5cc 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -1308,16 +1308,13 @@
unsigned int def_conf;
unsigned char seqassoc;
- def_conf = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0);
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
seqassoc = (unsigned char) get_defcfg_association(def_conf);
seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) {
if (seqassoc == 0xff) {
def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
- def_conf >> 24);
+ snd_hda_codec_set_pincfg(codec, nid, def_conf);
}
}
@@ -1354,7 +1351,7 @@
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- if (spec->autocfg.dig_out_pin)
+ if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = VT1708_DIGIN_NID;
@@ -1827,7 +1824,7 @@
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- if (spec->autocfg.dig_out_pin)
+ if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = VT1709_DIGIN_NID;
@@ -2371,7 +2368,7 @@
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- if (spec->autocfg.dig_out_pin)
+ if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = VT1708B_DIGIN_NID;
@@ -2836,7 +2833,7 @@
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- if (spec->autocfg.dig_out_pin)
+ if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = VT1708S_DIGOUT_NID;
spec->extra_dig_out_nid = 0x15;
@@ -3155,7 +3152,7 @@
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- if (spec->autocfg.dig_out_pin)
+ if (spec->autocfg.dig_outs)
spec->multiout.dig_out_nid = VT1702_DIGOUT_NID;
spec->extra_dig_out_nid = 0x1B;
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index 58d7cda..3dd63f1 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -458,7 +458,7 @@
u16 pbkstatus;
struct snd_pcm_substream *substream;
pbkstatus = inw(ICEDS(ice, INTSTAT));
- /* printk("pbkstatus = 0x%x\n", pbkstatus); */
+ /* printk(KERN_DEBUG "pbkstatus = 0x%x\n", pbkstatus); */
for (idx = 0; idx < 6; idx++) {
if ((pbkstatus & (3 << (idx * 2))) == 0)
continue;
@@ -2648,9 +2648,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
strcpy(card->driver, "ICE1712");
strcpy(card->shortname, "ICEnsemble ICE1712");
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index bb8d8c7..128510e7 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -241,6 +241,8 @@
struct snd_rawmidi_substream, list);
}
+static void enable_midi_irq(struct snd_ice1712 *ice, u8 flag, int enable);
+
static void vt1724_midi_write(struct snd_ice1712 *ice)
{
struct snd_rawmidi_substream *s;
@@ -254,6 +256,11 @@
for (i = 0; i < count; ++i)
outb(buffer[i], ICEREG1724(ice, MPU_DATA));
}
+ /* mask irq when all bytes have been transmitted.
+ * enabled again in output_trigger when the new data comes in.
+ */
+ enable_midi_irq(ice, VT1724_IRQ_MPU_TX,
+ !snd_rawmidi_transmit_empty(s));
}
static void vt1724_midi_read(struct snd_ice1712 *ice)
@@ -272,31 +279,34 @@
}
}
-static void vt1724_enable_midi_irq(struct snd_rawmidi_substream *substream,
- u8 flag, int enable)
+/* call with ice->reg_lock */
+static void enable_midi_irq(struct snd_ice1712 *ice, u8 flag, int enable)
{
- struct snd_ice1712 *ice = substream->rmidi->private_data;
- u8 mask;
-
- spin_lock_irq(&ice->reg_lock);
- mask = inb(ICEREG1724(ice, IRQMASK));
+ u8 mask = inb(ICEREG1724(ice, IRQMASK));
if (enable)
mask &= ~flag;
else
mask |= flag;
outb(mask, ICEREG1724(ice, IRQMASK));
+}
+
+static void vt1724_enable_midi_irq(struct snd_rawmidi_substream *substream,
+ u8 flag, int enable)
+{
+ struct snd_ice1712 *ice = substream->rmidi->private_data;
+
+ spin_lock_irq(&ice->reg_lock);
+ enable_midi_irq(ice, flag, enable);
spin_unlock_irq(&ice->reg_lock);
}
static int vt1724_midi_output_open(struct snd_rawmidi_substream *s)
{
- vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_TX, 1);
return 0;
}
static int vt1724_midi_output_close(struct snd_rawmidi_substream *s)
{
- vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_TX, 0);
return 0;
}
@@ -311,6 +321,7 @@
vt1724_midi_write(ice);
} else {
ice->midi_output = 0;
+ enable_midi_irq(ice, VT1724_IRQ_MPU_TX, 0);
}
spin_unlock_irqrestore(&ice->reg_lock, flags);
}
@@ -320,6 +331,7 @@
struct snd_ice1712 *ice = s->rmidi->private_data;
unsigned long timeout;
+ vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_TX, 0);
/* 32 bytes should be transmitted in less than about 12 ms */
timeout = jiffies + msecs_to_jiffies(15);
do {
@@ -389,24 +401,24 @@
status &= status_mask;
if (status == 0)
break;
+ spin_lock(&ice->reg_lock);
if (++timeout > 10) {
status = inb(ICEREG1724(ice, IRQSTAT));
printk(KERN_ERR "ice1724: Too long irq loop, "
"status = 0x%x\n", status);
if (status & VT1724_IRQ_MPU_TX) {
printk(KERN_ERR "ice1724: Disabling MPU_TX\n");
- outb(inb(ICEREG1724(ice, IRQMASK)) |
- VT1724_IRQ_MPU_TX,
- ICEREG1724(ice, IRQMASK));
+ enable_midi_irq(ice, VT1724_IRQ_MPU_TX, 0);
}
+ spin_unlock(&ice->reg_lock);
break;
}
handled = 1;
if (status & VT1724_IRQ_MPU_TX) {
- spin_lock(&ice->reg_lock);
if (ice->midi_output)
vt1724_midi_write(ice);
- spin_unlock(&ice->reg_lock);
+ else
+ enable_midi_irq(ice, VT1724_IRQ_MPU_TX, 0);
/* Due to mysterical reasons, MPU_TX is always
* generated (and can't be cleared) when a PCM
* playback is going. So let's ignore at the
@@ -415,15 +427,14 @@
status_mask &= ~VT1724_IRQ_MPU_TX;
}
if (status & VT1724_IRQ_MPU_RX) {
- spin_lock(&ice->reg_lock);
if (ice->midi_input)
vt1724_midi_read(ice);
else
vt1724_midi_clear_rx(ice);
- spin_unlock(&ice->reg_lock);
}
/* ack MPU irq */
outb(status, ICEREG1724(ice, IRQSTAT));
+ spin_unlock(&ice->reg_lock);
if (status & VT1724_IRQ_MTPCM) {
/*
* Multi-track PCM
@@ -745,7 +756,14 @@
spin_unlock_irq(&ice->reg_lock);
- /* printk("pro prepare: ch = %d, addr = 0x%x, buffer = 0x%x, period = 0x%x\n", substream->runtime->channels, (unsigned int)substream->runtime->dma_addr, snd_pcm_lib_buffer_bytes(substream), snd_pcm_lib_period_bytes(substream)); */
+ /*
+ printk(KERN_DEBUG "pro prepare: ch = %d, addr = 0x%x, "
+ "buffer = 0x%x, period = 0x%x\n",
+ substream->runtime->channels,
+ (unsigned int)substream->runtime->dma_addr,
+ snd_pcm_lib_buffer_bytes(substream),
+ snd_pcm_lib_period_bytes(substream));
+ */
return 0;
}
@@ -2122,7 +2140,9 @@
wait_i2c_busy(ice);
val = inb(ICEREG1724(ice, I2C_DATA));
mutex_unlock(&ice->i2c_mutex);
- /* printk("i2c_read: [0x%x,0x%x] = 0x%x\n", dev, addr, val); */
+ /*
+ printk(KERN_DEBUG "i2c_read: [0x%x,0x%x] = 0x%x\n", dev, addr, val);
+ */
return val;
}
@@ -2131,7 +2151,9 @@
{
mutex_lock(&ice->i2c_mutex);
wait_i2c_busy(ice);
- /* printk("i2c_write: [0x%x,0x%x] = 0x%x\n", dev, addr, data); */
+ /*
+ printk(KERN_DEBUG "i2c_write: [0x%x,0x%x] = 0x%x\n", dev, addr, data);
+ */
outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR));
outb(data, ICEREG1724(ice, I2C_DATA));
outb(dev | VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR));
@@ -2456,9 +2478,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
strcpy(card->driver, "ICE1724");
strcpy(card->shortname, "ICEnsemble ICE1724");
diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c
index c51659b..fd948bf 100644
--- a/sound/pci/ice1712/juli.c
+++ b/sound/pci/ice1712/juli.c
@@ -345,8 +345,9 @@
new_gpio = old_gpio &
~((unsigned int) kcontrol->private_value);
}
- /* printk("JULI - mute/unmute: control_value: 0x%x, old_gpio: 0x%x, \
- new_gpio 0x%x\n",
+ /* printk(KERN_DEBUG
+ "JULI - mute/unmute: control_value: 0x%x, old_gpio: 0x%x, "
+ "new_gpio 0x%x\n",
(unsigned int)ucontrol->value.integer.value[0], old_gpio,
new_gpio); */
if (old_gpio != new_gpio) {
diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c
index 48d3679..2a8e5cd 100644
--- a/sound/pci/ice1712/prodigy192.c
+++ b/sound/pci/ice1712/prodigy192.c
@@ -133,8 +133,10 @@
idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
/* due to possible conflicts with stac9460_set_rate_val, mutexing */
mutex_lock(&spec->mute_mutex);
- /*printk("Mute put: reg 0x%02x, ctrl value: 0x%02x\n", idx,
- ucontrol->value.integer.value[0]);*/
+ /*
+ printk(KERN_DEBUG "Mute put: reg 0x%02x, ctrl value: 0x%02x\n", idx,
+ ucontrol->value.integer.value[0]);
+ */
change = stac9460_dac_mute(ice, idx, ucontrol->value.integer.value[0]);
mutex_unlock(&spec->mute_mutex);
return change;
@@ -185,7 +187,10 @@
change = (ovol != nvol);
if (change) {
ovol = (0x7f - nvol) | (tmp & 0x80);
- /*printk("DAC Volume: reg 0x%02x: 0x%02x\n", idx, ovol);*/
+ /*
+ printk(KERN_DEBUG "DAC Volume: reg 0x%02x: 0x%02x\n",
+ idx, ovol);
+ */
stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
}
return change;
@@ -344,7 +349,7 @@
for (idx = 0; idx < 7 ; ++idx)
changed[idx] = stac9460_dac_mute(ice,
STAC946X_MASTER_VOLUME + idx, 0);
- /*printk("Rate change: %d, new MC: 0x%02x\n", rate, new);*/
+ /*printk(KERN_DEBUG "Rate change: %d, new MC: 0x%02x\n", rate, new);*/
stac9460_put(ice, STAC946X_MASTER_CLOCKING, new);
udelay(10);
/* unmuting - only originally unmuted dacs -
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index e900cdc..5764881 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -689,7 +689,7 @@
bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
ichdev->fragsize >> ichdev->pos_shift);
#if 0
- printk("bdbar[%i] = 0x%x [0x%x]\n",
+ printk(KERN_DEBUG "bdbar[%i] = 0x%x [0x%x]\n",
idx + 0, bdbar[idx + 0], bdbar[idx + 1]);
#endif
}
@@ -701,8 +701,10 @@
ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags;
ichdev->position = 0;
#if 0
- printk("lvi_frag = %i, frags = %i, period_size = 0x%x, period_size1 = 0x%x\n",
- ichdev->lvi_frag, ichdev->frags, ichdev->fragsize, ichdev->fragsize1);
+ printk(KERN_DEBUG "lvi_frag = %i, frags = %i, period_size = 0x%x, "
+ "period_size1 = 0x%x\n",
+ ichdev->lvi_frag, ichdev->frags, ichdev->fragsize,
+ ichdev->fragsize1);
#endif
/* clear interrupts */
iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
@@ -768,7 +770,8 @@
ichdev->lvi_frag %= ichdev->frags;
ichdev->bdbar[ichdev->lvi * 2] = cpu_to_le32(ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1);
#if 0
- printk("new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n",
+ printk(KERN_DEBUG "new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, "
+ "all = 0x%x, 0x%x\n",
ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2],
ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_OFF_PIV + port),
inl(port + 4), inb(port + ICH_REG_OFF_CR));
@@ -2287,23 +2290,23 @@
iputdword(chip, ICHREG(ALI_INTERRUPTSR), 0x00000000);
}
-static int snd_intel8x0_ich_chip_init(struct intel8x0 *chip, int probing)
-{
- unsigned long end_time;
- unsigned int cnt, status, nstatus;
-
- /* put logic to right state */
- /* first clear status bits */
- status = ICH_RCS | ICH_MCINT | ICH_POINT | ICH_PIINT;
- if (chip->device_type == DEVICE_NFORCE)
- status |= ICH_NVSPINT;
- cnt = igetdword(chip, ICHREG(GLOB_STA));
- iputdword(chip, ICHREG(GLOB_STA), cnt & status);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+static struct snd_pci_quirk ich_chip_reset_mode[] = {
+ SND_PCI_QUIRK(0x1014, 0x051f, "Thinkpad R32", 1),
+ { } /* end */
+};
+static int snd_intel8x0_ich_chip_cold_reset(struct intel8x0 *chip)
+{
+ unsigned int cnt;
/* ACLink on, 2 channels */
+
+ if (snd_pci_quirk_lookup(chip->pci, ich_chip_reset_mode))
+ return -EIO;
+
cnt = igetdword(chip, ICHREG(GLOB_CNT));
cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK);
-#ifdef CONFIG_SND_AC97_POWER_SAVE
+
/* do cold reset - the full ac97 powerdown may leave the controller
* in a warm state but actually it cannot communicate with the codec.
*/
@@ -2312,22 +2315,58 @@
udelay(10);
iputdword(chip, ICHREG(GLOB_CNT), cnt | ICH_AC97COLD);
msleep(1);
+ return 0;
+}
+#define snd_intel8x0_ich_chip_can_cold_reset(chip) \
+ (!snd_pci_quirk_lookup(chip->pci, ich_chip_reset_mode))
#else
+#define snd_intel8x0_ich_chip_cold_reset(chip) 0
+#define snd_intel8x0_ich_chip_can_cold_reset(chip) (0)
+#endif
+
+static int snd_intel8x0_ich_chip_reset(struct intel8x0 *chip)
+{
+ unsigned long end_time;
+ unsigned int cnt;
+ /* ACLink on, 2 channels */
+ cnt = igetdword(chip, ICHREG(GLOB_CNT));
+ cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK);
/* finish cold or do warm reset */
cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM;
iputdword(chip, ICHREG(GLOB_CNT), cnt);
end_time = (jiffies + (HZ / 4)) + 1;
do {
if ((igetdword(chip, ICHREG(GLOB_CNT)) & ICH_AC97WARM) == 0)
- goto __ok;
+ return 0;
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
snd_printk(KERN_ERR "AC'97 warm reset still in progress? [0x%x]\n",
igetdword(chip, ICHREG(GLOB_CNT)));
return -EIO;
+}
- __ok:
-#endif
+static int snd_intel8x0_ich_chip_init(struct intel8x0 *chip, int probing)
+{
+ unsigned long end_time;
+ unsigned int status, nstatus;
+ unsigned int cnt;
+ int err;
+
+ /* put logic to right state */
+ /* first clear status bits */
+ status = ICH_RCS | ICH_MCINT | ICH_POINT | ICH_PIINT;
+ if (chip->device_type == DEVICE_NFORCE)
+ status |= ICH_NVSPINT;
+ cnt = igetdword(chip, ICHREG(GLOB_STA));
+ iputdword(chip, ICHREG(GLOB_STA), cnt & status);
+
+ if (snd_intel8x0_ich_chip_can_cold_reset(chip))
+ err = snd_intel8x0_ich_chip_cold_reset(chip);
+ else
+ err = snd_intel8x0_ich_chip_reset(chip);
+ if (err < 0)
+ return err;
+
if (probing) {
/* wait for any codec ready status.
* Once it becomes ready it should remain ready
@@ -3058,9 +3097,9 @@
int err;
struct shortname_table *name;
- card = snd_card_new(index, id, THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
if (spdif_aclink < 0)
spdif_aclink = check_default_spdif_aclink(pci);
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index 93449e4..6ec0fc5 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -411,7 +411,10 @@
bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf + (((idx >> 1) * ichdev->fragsize) % ichdev->size));
bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
ichdev->fragsize >> chip->pcm_pos_shift);
- // printk("bdbar[%i] = 0x%x [0x%x]\n", idx + 0, bdbar[idx + 0], bdbar[idx + 1]);
+ /*
+ printk(KERN_DEBUG "bdbar[%i] = 0x%x [0x%x]\n",
+ idx + 0, bdbar[idx + 0], bdbar[idx + 1]);
+ */
}
ichdev->frags = ichdev->size / ichdev->fragsize;
}
@@ -421,8 +424,10 @@
ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags;
ichdev->position = 0;
#if 0
- printk("lvi_frag = %i, frags = %i, period_size = 0x%x, period_size1 = 0x%x\n",
- ichdev->lvi_frag, ichdev->frags, ichdev->fragsize, ichdev->fragsize1);
+ printk(KERN_DEBUG "lvi_frag = %i, frags = %i, period_size = 0x%x, "
+ "period_size1 = 0x%x\n",
+ ichdev->lvi_frag, ichdev->frags, ichdev->fragsize,
+ ichdev->fragsize1);
#endif
/* clear interrupts */
iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
@@ -465,7 +470,8 @@
ichdev->lvi_frag *
ichdev->fragsize1);
#if 0
- printk("new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n",
+ printk(KERN_DEBUG "new: bdbar[%i] = 0x%x [0x%x], "
+ "prefetch = %i, all = 0x%x, 0x%x\n",
ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2],
ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_OFF_PIV + port),
inl(port + 4), inb(port + ICH_REG_OFF_CR));
@@ -1269,9 +1275,9 @@
int err;
struct shortname_table *name;
- card = snd_card_new(index, id, THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
strcpy(card->driver, "ICH-MODEM");
strcpy(card->shortname, "Intel ICH");
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index 5f8006b..8b79969 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -2443,9 +2443,9 @@
dev++;
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
if ((err = snd_korg1212_create(card, pci, &korg1212)) < 0) {
snd_card_free(card);
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 59bbaf8..7014154 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -2691,9 +2691,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
switch (pci->device) {
case PCI_DEVICE_ID_ESS_ALLEGRO:
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index bb16250..c1eb84a 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -1366,12 +1366,12 @@
else
idx = index[dev] + i;
snprintf(tmpid, sizeof(tmpid), "%s-%d", id[dev] ? id[dev] : "MIXART", i);
- card = snd_card_new(idx, tmpid, THIS_MODULE, 0);
+ err = snd_card_create(idx, tmpid, THIS_MODULE, 0, &card);
- if (! card) {
+ if (err < 0) {
snd_printk(KERN_ERR "cannot allocate the card %d\n", i);
snd_mixart_free(mgr);
- return -ENOMEM;
+ return err;
}
strcpy(card->driver, CARD_NAME);
diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c
index 3782b52..4cf4cd8 100644
--- a/sound/pci/mixart/mixart_hwdep.c
+++ b/sound/pci/mixart/mixart_hwdep.c
@@ -345,8 +345,8 @@
status_daught = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
/* motherboard xilinx status 5 will say that the board is performing a reset */
- if( status_xilinx == 5 ) {
- snd_printk( KERN_ERR "miXart is resetting !\n");
+ if (status_xilinx == 5) {
+ snd_printk(KERN_ERR "miXart is resetting !\n");
return -EAGAIN; /* try again later */
}
@@ -354,13 +354,14 @@
case MIXART_MOTHERBOARD_XLX_INDEX:
/* xilinx already loaded ? */
- if( status_xilinx == 4 ) {
- snd_printk( KERN_DEBUG "xilinx is already loaded !\n");
+ if (status_xilinx == 4) {
+ snd_printk(KERN_DEBUG "xilinx is already loaded !\n");
return 0;
}
/* the status should be 0 == "idle" */
- if( status_xilinx != 0 ) {
- snd_printk( KERN_ERR "xilinx load error ! status = %d\n", status_xilinx);
+ if (status_xilinx != 0) {
+ snd_printk(KERN_ERR "xilinx load error ! status = %d\n",
+ status_xilinx);
return -EIO; /* modprob -r may help ? */
}
@@ -389,21 +390,23 @@
case MIXART_MOTHERBOARD_ELF_INDEX:
- if( status_elf == 4 ) {
- snd_printk( KERN_DEBUG "elf file already loaded !\n");
+ if (status_elf == 4) {
+ snd_printk(KERN_DEBUG "elf file already loaded !\n");
return 0;
}
/* the status should be 0 == "idle" */
- if( status_elf != 0 ) {
- snd_printk( KERN_ERR "elf load error ! status = %d\n", status_elf);
+ if (status_elf != 0) {
+ snd_printk(KERN_ERR "elf load error ! status = %d\n",
+ status_elf);
return -EIO; /* modprob -r may help ? */
}
/* wait for xilinx status == 4 */
err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET, 1, 4, 500); /* 5sec */
if (err < 0) {
- snd_printk( KERN_ERR "xilinx was not loaded or could not be started\n");
+ snd_printk(KERN_ERR "xilinx was not loaded or "
+ "could not be started\n");
return err;
}
@@ -424,7 +427,7 @@
/* wait for elf status == 4 */
err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET, 1, 4, 300); /* 3sec */
if (err < 0) {
- snd_printk( KERN_ERR "elf could not be started\n");
+ snd_printk(KERN_ERR "elf could not be started\n");
return err;
}
@@ -437,15 +440,16 @@
default:
/* elf and xilinx should be loaded */
- if( (status_elf != 4) || (status_xilinx != 4) ) {
- printk( KERN_ERR "xilinx or elf not successfully loaded\n");
+ if (status_elf != 4 || status_xilinx != 4) {
+ printk(KERN_ERR "xilinx or elf not "
+ "successfully loaded\n");
return -EIO; /* modprob -r may help ? */
}
/* wait for daughter detection != 0 */
err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET, 0, 0, 30); /* 300msec */
if (err < 0) {
- snd_printk( KERN_ERR "error starting elf file\n");
+ snd_printk(KERN_ERR "error starting elf file\n");
return err;
}
@@ -460,8 +464,9 @@
return -EINVAL;
/* daughter should be idle */
- if( status_daught != 0 ) {
- printk( KERN_ERR "daughter load error ! status = %d\n", status_daught);
+ if (status_daught != 0) {
+ printk(KERN_ERR "daughter load error ! status = %d\n",
+ status_daught);
return -EIO; /* modprob -r may help ? */
}
@@ -480,7 +485,7 @@
/* wait for status == 2 */
err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 2, 30); /* 300msec */
if (err < 0) {
- snd_printk( KERN_ERR "daughter board load error\n");
+ snd_printk(KERN_ERR "daughter board load error\n");
return err;
}
@@ -502,7 +507,8 @@
/* wait for daughter status == 3 */
err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 3, 300); /* 3sec */
if (err < 0) {
- snd_printk( KERN_ERR "daughter board could not be initialised\n");
+ snd_printk(KERN_ERR
+ "daughter board could not be initialised\n");
return err;
}
@@ -512,7 +518,7 @@
/* first communication with embedded */
err = mixart_first_init(mgr);
if (err < 0) {
- snd_printk( KERN_ERR "miXart could not be set up\n");
+ snd_printk(KERN_ERR "miXart could not be set up\n");
return err;
}
@@ -581,16 +587,6 @@
/* miXart hwdep interface id string */
#define SND_MIXART_HWDEP_ID "miXart Loader"
-static int mixart_hwdep_open(struct snd_hwdep *hw, struct file *file)
-{
- return 0;
-}
-
-static int mixart_hwdep_release(struct snd_hwdep *hw, struct file *file)
-{
- return 0;
-}
-
static int mixart_hwdep_dsp_status(struct snd_hwdep *hw,
struct snd_hwdep_dsp_status *info)
{
@@ -643,8 +639,6 @@
hw->iface = SNDRV_HWDEP_IFACE_MIXART;
hw->private_data = mgr;
- hw->ops.open = mixart_hwdep_open;
- hw->ops.release = mixart_hwdep_release;
hw->ops.dsp_status = mixart_hwdep_dsp_status;
hw->ops.dsp_load = mixart_hwdep_dsp_load;
hw->exclusive = 1;
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index 50c9f8a..522a040 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -1668,9 +1668,9 @@
}
}
- card = snd_card_new(index, id, THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
switch (pci->device) {
case PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO:
diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c
index 1ab833f..84ef131 100644
--- a/sound/pci/oxygen/hifier.c
+++ b/sound/pci/oxygen/hifier.c
@@ -45,6 +45,7 @@
static struct pci_device_id hifier_ids[] __devinitdata = {
{ OXYGEN_PCI_SUBID(0x14c3, 0x1710) },
{ OXYGEN_PCI_SUBID(0x14c3, 0x1711) },
+ { OXYGEN_PCI_SUBID_BROKEN_EEPROM },
{ }
};
MODULE_DEVICE_TABLE(pci, hifier_ids);
@@ -151,7 +152,6 @@
.shortname = "C-Media CMI8787",
.longname = "C-Media Oxygen HD Audio",
.chip = "CMI8788",
- .owner = THIS_MODULE,
.init = hifier_init,
.control_filter = hifier_control_filter,
.cleanup = hifier_cleanup,
@@ -173,6 +173,13 @@
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
+static int __devinit get_hifier_model(struct oxygen *chip,
+ const struct pci_device_id *id)
+{
+ chip->model = model_hifier;
+ return 0;
+}
+
static int __devinit hifier_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
@@ -185,7 +192,8 @@
++dev;
return -ENOENT;
}
- err = oxygen_pci_probe(pci, index[dev], id[dev], &model_hifier, 0);
+ err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
+ hifier_ids, get_hifier_model);
if (err >= 0)
++dev;
return err;
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index de999c6..72db4c3 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -1,5 +1,5 @@
/*
- * C-Media CMI8788 driver for C-Media's reference design and for the X-Meridian
+ * C-Media CMI8788 driver for C-Media's reference design and similar models
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
*
@@ -26,6 +26,7 @@
*
* GPIO 0 -> DFS0 of AK5385
* GPIO 1 -> DFS1 of AK5385
+ * GPIO 8 -> enable headphone amplifier on HT-Omega models
*/
#include <linux/delay.h>
@@ -61,7 +62,8 @@
enum {
MODEL_CMEDIA_REF, /* C-Media's reference design */
MODEL_MERIDIAN, /* AuzenTech X-Meridian */
- MODEL_HALO, /* HT-Omega Claro halo */
+ MODEL_CLARO, /* HT-Omega Claro */
+ MODEL_CLARO_HALO, /* HT-Omega Claro halo */
};
static struct pci_device_id oxygen_ids[] __devinitdata = {
@@ -74,8 +76,8 @@
{ OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN },
- { OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CMEDIA_REF },
- { OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_HALO },
+ { OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CLARO },
+ { OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_CLARO_HALO },
{ }
};
MODULE_DEVICE_TABLE(pci, oxygen_ids);
@@ -86,6 +88,8 @@
#define GPIO_AK5385_DFS_DOUBLE 0x0001
#define GPIO_AK5385_DFS_QUAD 0x0002
+#define GPIO_CLARO_HP 0x0100
+
struct generic_data {
u8 ak4396_ctl2;
u16 saved_wm8785_registers[2];
@@ -196,10 +200,46 @@
ak5385_init(chip);
}
+static void claro_enable_hp(struct oxygen *chip)
+{
+ msleep(300);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_HP);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_HP);
+}
+
+static void claro_init(struct oxygen *chip)
+{
+ ak4396_init(chip);
+ wm8785_init(chip);
+ claro_enable_hp(chip);
+}
+
+static void claro_halo_init(struct oxygen *chip)
+{
+ ak4396_init(chip);
+ ak5385_init(chip);
+ claro_enable_hp(chip);
+}
+
static void generic_cleanup(struct oxygen *chip)
{
}
+static void claro_disable_hp(struct oxygen *chip)
+{
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_HP);
+}
+
+static void claro_cleanup(struct oxygen *chip)
+{
+ claro_disable_hp(chip);
+}
+
+static void claro_suspend(struct oxygen *chip)
+{
+ claro_disable_hp(chip);
+}
+
static void generic_resume(struct oxygen *chip)
{
ak4396_registers_init(chip);
@@ -211,6 +251,12 @@
ak4396_registers_init(chip);
}
+static void claro_resume(struct oxygen *chip)
+{
+ ak4396_registers_init(chip);
+ claro_enable_hp(chip);
+}
+
static void set_ak4396_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
@@ -293,30 +339,10 @@
static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
-static int generic_probe(struct oxygen *chip, unsigned long driver_data)
-{
- if (driver_data == MODEL_MERIDIAN) {
- chip->model.init = meridian_init;
- chip->model.resume = meridian_resume;
- chip->model.set_adc_params = set_ak5385_params;
- chip->model.device_config = PLAYBACK_0_TO_I2S |
- PLAYBACK_1_TO_SPDIF |
- CAPTURE_0_FROM_I2S_2 |
- CAPTURE_1_FROM_SPDIF;
- }
- if (driver_data == MODEL_MERIDIAN || driver_data == MODEL_HALO) {
- chip->model.misc_flags = OXYGEN_MISC_MIDI;
- chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
- }
- return 0;
-}
-
static const struct oxygen_model model_generic = {
.shortname = "C-Media CMI8788",
.longname = "C-Media Oxygen HD Audio",
.chip = "CMI8788",
- .owner = THIS_MODULE,
- .probe = generic_probe,
.init = generic_init,
.cleanup = generic_cleanup,
.resume = generic_resume,
@@ -341,6 +367,42 @@
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
+static int __devinit get_oxygen_model(struct oxygen *chip,
+ const struct pci_device_id *id)
+{
+ chip->model = model_generic;
+ switch (id->driver_data) {
+ case MODEL_MERIDIAN:
+ chip->model.init = meridian_init;
+ chip->model.resume = meridian_resume;
+ chip->model.set_adc_params = set_ak5385_params;
+ chip->model.device_config = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_2 |
+ CAPTURE_1_FROM_SPDIF;
+ break;
+ case MODEL_CLARO:
+ chip->model.init = claro_init;
+ chip->model.cleanup = claro_cleanup;
+ chip->model.suspend = claro_suspend;
+ chip->model.resume = claro_resume;
+ break;
+ case MODEL_CLARO_HALO:
+ chip->model.init = claro_halo_init;
+ chip->model.cleanup = claro_cleanup;
+ chip->model.suspend = claro_suspend;
+ chip->model.resume = claro_resume;
+ chip->model.set_adc_params = set_ak5385_params;
+ break;
+ }
+ if (id->driver_data == MODEL_MERIDIAN ||
+ id->driver_data == MODEL_CLARO_HALO) {
+ chip->model.misc_flags = OXYGEN_MISC_MIDI;
+ chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
+ }
+ return 0;
+}
+
static int __devinit generic_oxygen_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
@@ -353,8 +415,8 @@
++dev;
return -ENOENT;
}
- err = oxygen_pci_probe(pci, index[dev], id[dev],
- &model_generic, pci_id->driver_data);
+ err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
+ oxygen_ids, get_oxygen_model);
if (err >= 0)
++dev;
return err;
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
index 19107c6..bd615db 100644
--- a/sound/pci/oxygen/oxygen.h
+++ b/sound/pci/oxygen/oxygen.h
@@ -18,6 +18,8 @@
#define OXYGEN_IO_SIZE 0x100
+#define OXYGEN_EEPROM_ID 0x434d /* "CM" */
+
/* model-specific configuration of outputs/inputs */
#define PLAYBACK_0_TO_I2S 0x0001
/* PLAYBACK_0_TO_AC97_0 not implemented */
@@ -49,7 +51,13 @@
.subvendor = sv, \
.subdevice = sd
+#define BROKEN_EEPROM_DRIVER_DATA ((unsigned long)-1)
+#define OXYGEN_PCI_SUBID_BROKEN_EEPROM \
+ OXYGEN_PCI_SUBID(PCI_VENDOR_ID_CMEDIA, 0x8788), \
+ .driver_data = BROKEN_EEPROM_DRIVER_DATA
+
struct pci_dev;
+struct pci_device_id;
struct snd_card;
struct snd_pcm_substream;
struct snd_pcm_hardware;
@@ -62,8 +70,6 @@
const char *shortname;
const char *longname;
const char *chip;
- struct module *owner;
- int (*probe)(struct oxygen *chip, unsigned long driver_data);
void (*init)(struct oxygen *chip);
int (*control_filter)(struct snd_kcontrol_new *template);
int (*mixer_init)(struct oxygen *chip);
@@ -83,6 +89,7 @@
void (*ac97_switch)(struct oxygen *chip,
unsigned int reg, unsigned int mute);
const unsigned int *dac_tlv;
+ unsigned long private_data;
size_t model_data_size;
unsigned int device_config;
u8 dac_channels;
@@ -134,8 +141,12 @@
/* oxygen_lib.c */
int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
- const struct oxygen_model *model,
- unsigned long driver_data);
+ struct module *owner,
+ const struct pci_device_id *ids,
+ int (*get_model)(struct oxygen *chip,
+ const struct pci_device_id *id
+ )
+ );
void oxygen_pci_remove(struct pci_dev *pci);
#ifdef CONFIG_PM
int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state);
@@ -180,6 +191,9 @@
void oxygen_reset_uart(struct oxygen *chip);
void oxygen_write_uart(struct oxygen *chip, u8 data);
+u16 oxygen_read_eeprom(struct oxygen *chip, unsigned int index);
+void oxygen_write_eeprom(struct oxygen *chip, unsigned int index, u16 value);
+
static inline void oxygen_set_bits8(struct oxygen *chip,
unsigned int reg, u8 value)
{
diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c
index 3126c4b..c1eb923 100644
--- a/sound/pci/oxygen/oxygen_io.c
+++ b/sound/pci/oxygen/oxygen_io.c
@@ -254,3 +254,34 @@
_write_uart(chip, 0, data);
}
EXPORT_SYMBOL(oxygen_write_uart);
+
+u16 oxygen_read_eeprom(struct oxygen *chip, unsigned int index)
+{
+ unsigned int timeout;
+
+ oxygen_write8(chip, OXYGEN_EEPROM_CONTROL,
+ index | OXYGEN_EEPROM_DIR_READ);
+ for (timeout = 0; timeout < 100; ++timeout) {
+ udelay(1);
+ if (!(oxygen_read8(chip, OXYGEN_EEPROM_STATUS)
+ & OXYGEN_EEPROM_BUSY))
+ break;
+ }
+ return oxygen_read16(chip, OXYGEN_EEPROM_DATA);
+}
+
+void oxygen_write_eeprom(struct oxygen *chip, unsigned int index, u16 value)
+{
+ unsigned int timeout;
+
+ oxygen_write16(chip, OXYGEN_EEPROM_DATA, value);
+ oxygen_write8(chip, OXYGEN_EEPROM_CONTROL,
+ index | OXYGEN_EEPROM_DIR_WRITE);
+ for (timeout = 0; timeout < 10; ++timeout) {
+ msleep(1);
+ if (!(oxygen_read8(chip, OXYGEN_EEPROM_STATUS)
+ & OXYGEN_EEPROM_BUSY))
+ return;
+ }
+ snd_printk(KERN_ERR "EEPROM write timeout\n");
+}
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index 84f481d..312251d 100644
--- a/sound/pci/oxygen/oxygen_lib.c
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -34,6 +34,7 @@
MODULE_DESCRIPTION("C-Media CMI8788 helper library");
MODULE_LICENSE("GPL v2");
+#define DRIVER "oxygen"
static inline int oxygen_uart_input_ready(struct oxygen *chip)
{
@@ -243,6 +244,62 @@
#define oxygen_proc_init(chip)
#endif
+static const struct pci_device_id *
+oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])
+{
+ u16 subdevice;
+
+ /*
+ * Make sure the EEPROM pins are available, i.e., not used for SPI.
+ * (This function is called before we initialize or use SPI.)
+ */
+ oxygen_clear_bits8(chip, OXYGEN_FUNCTION,
+ OXYGEN_FUNCTION_ENABLE_SPI_4_5);
+ /*
+ * Read the subsystem device ID directly from the EEPROM, because the
+ * chip didn't if the first EEPROM word was overwritten.
+ */
+ subdevice = oxygen_read_eeprom(chip, 2);
+ /*
+ * We use only the subsystem device ID for searching because it is
+ * unique even without the subsystem vendor ID, which may have been
+ * overwritten in the EEPROM.
+ */
+ for (; ids->vendor; ++ids)
+ if (ids->subdevice == subdevice &&
+ ids->driver_data != BROKEN_EEPROM_DRIVER_DATA)
+ return ids;
+ return NULL;
+}
+
+static void oxygen_restore_eeprom(struct oxygen *chip,
+ const struct pci_device_id *id)
+{
+ if (oxygen_read_eeprom(chip, 0) != OXYGEN_EEPROM_ID) {
+ /*
+ * This function gets called only when a known card model has
+ * been detected, i.e., we know there is a valid subsystem
+ * product ID at index 2 in the EEPROM. Therefore, we have
+ * been able to deduce the correct subsystem vendor ID, and
+ * this is enough information to restore the original EEPROM
+ * contents.
+ */
+ oxygen_write_eeprom(chip, 1, id->subvendor);
+ oxygen_write_eeprom(chip, 0, OXYGEN_EEPROM_ID);
+
+ oxygen_set_bits8(chip, OXYGEN_MISC,
+ OXYGEN_MISC_WRITE_PCI_SUBID);
+ pci_write_config_word(chip->pci, PCI_SUBSYSTEM_VENDOR_ID,
+ id->subvendor);
+ pci_write_config_word(chip->pci, PCI_SUBSYSTEM_ID,
+ id->subdevice);
+ oxygen_clear_bits8(chip, OXYGEN_MISC,
+ OXYGEN_MISC_WRITE_PCI_SUBID);
+
+ snd_printk(KERN_INFO "EEPROM ID restored\n");
+ }
+}
+
static void oxygen_init(struct oxygen *chip)
{
unsigned int i;
@@ -446,30 +503,33 @@
free_irq(chip->irq, chip);
flush_scheduled_work();
chip->model.cleanup(chip);
+ kfree(chip->model_data);
mutex_destroy(&chip->mutex);
pci_release_regions(chip->pci);
pci_disable_device(chip->pci);
}
int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
- const struct oxygen_model *model,
- unsigned long driver_data)
+ struct module *owner,
+ const struct pci_device_id *ids,
+ int (*get_model)(struct oxygen *chip,
+ const struct pci_device_id *id
+ )
+ )
{
struct snd_card *card;
struct oxygen *chip;
+ const struct pci_device_id *pci_id;
int err;
- card = snd_card_new(index, id, model->owner,
- sizeof *chip + model->model_data_size);
- if (!card)
- return -ENOMEM;
+ err = snd_card_create(index, id, owner, sizeof(*chip), &card);
+ if (err < 0)
+ return err;
chip = card->private_data;
chip->card = card;
chip->pci = pci;
chip->irq = -1;
- chip->model = *model;
- chip->model_data = chip + 1;
spin_lock_init(&chip->reg_lock);
mutex_init(&chip->mutex);
INIT_WORK(&chip->spdif_input_bits_work,
@@ -481,7 +541,7 @@
if (err < 0)
goto err_card;
- err = pci_request_regions(pci, model->chip);
+ err = pci_request_regions(pci, DRIVER);
if (err < 0) {
snd_printk(KERN_ERR "cannot reserve PCI resources\n");
goto err_pci_enable;
@@ -495,20 +555,34 @@
}
chip->addr = pci_resource_start(pci, 0);
+ pci_id = oxygen_search_pci_id(chip, ids);
+ if (!pci_id) {
+ err = -ENODEV;
+ goto err_pci_regions;
+ }
+ oxygen_restore_eeprom(chip, pci_id);
+ err = get_model(chip, pci_id);
+ if (err < 0)
+ goto err_pci_regions;
+
+ if (chip->model.model_data_size) {
+ chip->model_data = kzalloc(chip->model.model_data_size,
+ GFP_KERNEL);
+ if (!chip->model_data) {
+ err = -ENOMEM;
+ goto err_pci_regions;
+ }
+ }
+
pci_set_master(pci);
snd_card_set_dev(card, &pci->dev);
card->private_free = oxygen_card_free;
- if (chip->model.probe) {
- err = chip->model.probe(chip, driver_data);
- if (err < 0)
- goto err_card;
- }
oxygen_init(chip);
chip->model.init(chip);
err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED,
- chip->model.chip, chip);
+ DRIVER, chip);
if (err < 0) {
snd_printk(KERN_ERR "cannot grab interrupt %d\n", pci->irq);
goto err_card;
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index 6c870c1..bc5ce11 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -112,6 +112,34 @@
* CS4362A: AD0 <- 0
*/
+/*
+ * Xonar Essence STX
+ * -----------------
+ *
+ * CMI8788:
+ *
+ * I²C <-> PCM1792A
+ *
+ * GPI 0 <- external power present
+ *
+ * GPIO 0 -> enable output to speakers
+ * GPIO 1 -> route HP to front panel (0) or rear jack (1)
+ * GPIO 2 -> M0 of CS5381
+ * GPIO 3 -> M1 of CS5381
+ * GPIO 7 -> route output to speaker jacks (0) or HP (1)
+ * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
+ *
+ * PCM1792A:
+ *
+ * AD0 <- 0
+ *
+ * H6 daughterboard
+ * ----------------
+ *
+ * GPIO 4 <- 0
+ * GPIO 5 <- 0
+ */
+
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/mutex.h>
@@ -152,6 +180,7 @@
MODEL_DX,
MODEL_HDAV, /* without daughterboard */
MODEL_HDAV_H6, /* with H6 daughterboard */
+ MODEL_STX,
};
static struct pci_device_id xonar_ids[] __devinitdata = {
@@ -160,6 +189,8 @@
{ OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X },
{ OXYGEN_PCI_SUBID(0x1043, 0x8314), .driver_data = MODEL_HDAV },
{ OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 },
+ { OXYGEN_PCI_SUBID(0x1043, 0x835c), .driver_data = MODEL_STX },
+ { OXYGEN_PCI_SUBID_BROKEN_EEPROM },
{ }
};
MODULE_DEVICE_TABLE(pci, xonar_ids);
@@ -183,12 +214,14 @@
#define GPIO_HDAV_DB_H6 0x0000
#define GPIO_HDAV_DB_XX 0x0020
+#define GPIO_ST_HP_REAR 0x0002
+#define GPIO_ST_HP 0x0080
+
#define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ADx=i, /W=0 */
#define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */
#define I2C_DEVICE_CS4362A 0x30 /* 001100, AD0=0, /W=0 */
struct xonar_data {
- unsigned int model;
unsigned int anti_pop_delay;
unsigned int dacs;
u16 output_enable_bit;
@@ -334,15 +367,9 @@
struct xonar_data *data = chip->model_data;
data->anti_pop_delay = 300;
+ data->dacs = 4;
data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE;
data->pcm1796_oversampling = PCM1796_OS_64;
- if (data->model == MODEL_D2X) {
- data->ext_power_reg = OXYGEN_GPIO_DATA;
- data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK;
- data->ext_power_bit = GPIO_D2X_EXT_POWER;
- oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
- GPIO_D2X_EXT_POWER);
- }
pcm1796_init(chip);
@@ -355,6 +382,18 @@
snd_component_add(chip->card, "CS5381");
}
+static void xonar_d2x_init(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
+
+ data->ext_power_reg = OXYGEN_GPIO_DATA;
+ data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK;
+ data->ext_power_bit = GPIO_D2X_EXT_POWER;
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER);
+
+ xonar_d2_init(chip);
+}
+
static void update_cs4362a_volumes(struct oxygen *chip)
{
u8 mute;
@@ -422,11 +461,6 @@
data->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST;
data->cs4362a_fm = CS4362A_FM_SINGLE |
CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
- if (data->model == MODEL_DX) {
- data->ext_power_reg = OXYGEN_GPI_DATA;
- data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
- data->ext_power_bit = GPI_DX_EXT_POWER;
- }
oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
OXYGEN_2WIRE_LENGTH_8 |
@@ -447,6 +481,17 @@
snd_component_add(chip->card, "CS5361");
}
+static void xonar_dx_init(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
+
+ data->ext_power_reg = OXYGEN_GPI_DATA;
+ data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+ data->ext_power_bit = GPI_DX_EXT_POWER;
+
+ xonar_d1_init(chip);
+}
+
static void xonar_hdav_init(struct oxygen *chip)
{
struct xonar_data *data = chip->model_data;
@@ -458,6 +503,7 @@
OXYGEN_2WIRE_SPEED_FAST);
data->anti_pop_delay = 100;
+ data->dacs = chip->model.private_data == MODEL_HDAV_H6 ? 4 : 1;
data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
data->ext_power_reg = OXYGEN_GPI_DATA;
data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
@@ -484,6 +530,36 @@
snd_component_add(chip->card, "CS5381");
}
+static void xonar_stx_init(struct oxygen *chip)
+{
+ struct xonar_data *data = chip->model_data;
+
+ oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+ OXYGEN_2WIRE_LENGTH_8 |
+ OXYGEN_2WIRE_INTERRUPT_MASK |
+ OXYGEN_2WIRE_SPEED_FAST);
+
+ data->anti_pop_delay = 100;
+ data->dacs = 1;
+ data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
+ data->ext_power_reg = OXYGEN_GPI_DATA;
+ data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+ data->ext_power_bit = GPI_DX_EXT_POWER;
+ data->pcm1796_oversampling = PCM1796_OS_64;
+
+ pcm1796_init(chip);
+
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_DX_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
+ GPIO_DX_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
+
+ xonar_common_init(chip);
+
+ snd_component_add(chip->card, "PCM1792A");
+ snd_component_add(chip->card, "CS5381");
+}
+
static void xonar_disable_output(struct oxygen *chip)
{
struct xonar_data *data = chip->model_data;
@@ -511,6 +587,11 @@
xonar_disable_output(chip);
}
+static void xonar_st_cleanup(struct oxygen *chip)
+{
+ xonar_disable_output(chip);
+}
+
static void xonar_d2_suspend(struct oxygen *chip)
{
xonar_d2_cleanup(chip);
@@ -527,6 +608,11 @@
msleep(2);
}
+static void xonar_st_suspend(struct oxygen *chip)
+{
+ xonar_st_cleanup(chip);
+}
+
static void xonar_d2_resume(struct oxygen *chip)
{
pcm1796_init(chip);
@@ -554,6 +640,12 @@
xonar_enable_output(chip);
}
+static void xonar_st_resume(struct oxygen *chip)
+{
+ pcm1796_init(chip);
+ xonar_enable_output(chip);
+}
+
static void xonar_hdav_pcm_hardware_filter(unsigned int channel,
struct snd_pcm_hardware *hardware)
{
@@ -733,6 +825,72 @@
.private_value = GPIO_DX_FRONT_PANEL,
};
+static int st_output_switch_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[3] = {
+ "Speakers", "Headphones", "FP Headphones"
+ };
+
+ info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ info->count = 1;
+ info->value.enumerated.items = 3;
+ if (info->value.enumerated.item >= 3)
+ info->value.enumerated.item = 2;
+ strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+ return 0;
+}
+
+static int st_output_switch_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u16 gpio;
+
+ gpio = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+ if (!(gpio & GPIO_ST_HP))
+ value->value.enumerated.item[0] = 0;
+ else if (gpio & GPIO_ST_HP_REAR)
+ value->value.enumerated.item[0] = 1;
+ else
+ value->value.enumerated.item[0] = 2;
+ return 0;
+}
+
+
+static int st_output_switch_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct oxygen *chip = ctl->private_data;
+ u16 gpio_old, gpio;
+
+ mutex_lock(&chip->mutex);
+ gpio_old = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+ gpio = gpio_old;
+ switch (value->value.enumerated.item[0]) {
+ case 0:
+ gpio &= ~(GPIO_ST_HP | GPIO_ST_HP_REAR);
+ break;
+ case 1:
+ gpio |= GPIO_ST_HP | GPIO_ST_HP_REAR;
+ break;
+ case 2:
+ gpio = (gpio | GPIO_ST_HP) & ~GPIO_ST_HP_REAR;
+ break;
+ }
+ oxygen_write16(chip, OXYGEN_GPIO_DATA, gpio);
+ mutex_unlock(&chip->mutex);
+ return gpio != gpio_old;
+}
+
+static const struct snd_kcontrol_new st_output_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Output",
+ .info = st_output_switch_info,
+ .get = st_output_switch_get,
+ .put = st_output_switch_put,
+};
+
static void xonar_line_mic_ac97_switch(struct oxygen *chip,
unsigned int reg, unsigned int mute)
{
@@ -745,8 +903,8 @@
}
}
-static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -12000, 50, 0);
-static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -12700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -6000, 50, 0);
+static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0);
static int xonar_d2_control_filter(struct snd_kcontrol_new *template)
{
@@ -763,6 +921,15 @@
return 0;
}
+static int xonar_st_control_filter(struct snd_kcontrol_new *template)
+{
+ if (!strncmp(template->name, "CD Capture ", 11))
+ return 1; /* no CD input */
+ if (!strcmp(template->name, "Stereo Upmixing"))
+ return 1; /* stereo only - we don't need upmixing */
+ return 0;
+}
+
static int xonar_d2_mixer_init(struct oxygen *chip)
{
return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip));
@@ -773,51 +940,14 @@
return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip));
}
-static int xonar_model_probe(struct oxygen *chip, unsigned long driver_data)
+static int xonar_st_mixer_init(struct oxygen *chip)
{
- static const char *const names[] = {
- [MODEL_D1] = "Xonar D1",
- [MODEL_DX] = "Xonar DX",
- [MODEL_D2] = "Xonar D2",
- [MODEL_D2X] = "Xonar D2X",
- [MODEL_HDAV] = "Xonar HDAV1.3",
- [MODEL_HDAV_H6] = "Xonar HDAV1.3+H6",
- };
- static const u8 dacs[] = {
- [MODEL_D1] = 2,
- [MODEL_DX] = 2,
- [MODEL_D2] = 4,
- [MODEL_D2X] = 4,
- [MODEL_HDAV] = 1,
- [MODEL_HDAV_H6] = 4,
- };
- struct xonar_data *data = chip->model_data;
-
- data->model = driver_data;
- if (data->model == MODEL_HDAV) {
- oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
- GPIO_HDAV_DB_MASK);
- switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) &
- GPIO_HDAV_DB_MASK) {
- case GPIO_HDAV_DB_H6:
- data->model = MODEL_HDAV_H6;
- break;
- case GPIO_HDAV_DB_XX:
- snd_printk(KERN_ERR "unknown daughterboard\n");
- return -ENODEV;
- }
- }
-
- data->dacs = dacs[data->model];
- chip->model.shortname = names[data->model];
- return 0;
+ return snd_ctl_add(chip->card, snd_ctl_new1(&st_output_switch, chip));
}
static const struct oxygen_model model_xonar_d2 = {
.longname = "Asus Virtuoso 200",
.chip = "AV200",
- .owner = THIS_MODULE,
- .probe = xonar_model_probe,
.init = xonar_d2_init,
.control_filter = xonar_d2_control_filter,
.mixer_init = xonar_d2_mixer_init,
@@ -837,8 +967,8 @@
MIDI_OUTPUT |
MIDI_INPUT,
.dac_channels = 8,
- .dac_volume_min = 0x0f,
- .dac_volume_max = 0xff,
+ .dac_volume_min = 255 - 2*60,
+ .dac_volume_max = 255,
.misc_flags = OXYGEN_MISC_MIDI,
.function_flags = OXYGEN_FUNCTION_SPI |
OXYGEN_FUNCTION_ENABLE_SPI_4_5,
@@ -849,8 +979,6 @@
static const struct oxygen_model model_xonar_d1 = {
.longname = "Asus Virtuoso 100",
.chip = "AV200",
- .owner = THIS_MODULE,
- .probe = xonar_model_probe,
.init = xonar_d1_init,
.control_filter = xonar_d1_control_filter,
.mixer_init = xonar_d1_mixer_init,
@@ -868,7 +996,7 @@
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2,
.dac_channels = 8,
- .dac_volume_min = 0,
+ .dac_volume_min = 127 - 60,
.dac_volume_max = 127,
.function_flags = OXYGEN_FUNCTION_2WIRE,
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
@@ -878,8 +1006,6 @@
static const struct oxygen_model model_xonar_hdav = {
.longname = "Asus Virtuoso 200",
.chip = "AV200",
- .owner = THIS_MODULE,
- .probe = xonar_model_probe,
.init = xonar_hdav_init,
.cleanup = xonar_hdav_cleanup,
.suspend = xonar_hdav_suspend,
@@ -897,16 +1023,43 @@
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2,
.dac_channels = 8,
- .dac_volume_min = 0x0f,
- .dac_volume_max = 0xff,
+ .dac_volume_min = 255 - 2*60,
+ .dac_volume_max = 255,
.misc_flags = OXYGEN_MISC_MIDI,
.function_flags = OXYGEN_FUNCTION_2WIRE,
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
-static int __devinit xonar_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static const struct oxygen_model model_xonar_st = {
+ .longname = "Asus Virtuoso 100",
+ .chip = "AV200",
+ .init = xonar_stx_init,
+ .control_filter = xonar_st_control_filter,
+ .mixer_init = xonar_st_mixer_init,
+ .cleanup = xonar_st_cleanup,
+ .suspend = xonar_st_suspend,
+ .resume = xonar_st_resume,
+ .set_dac_params = set_pcm1796_params,
+ .set_adc_params = set_cs53x1_params,
+ .update_dac_volume = update_pcm1796_volume,
+ .update_dac_mute = update_pcm1796_mute,
+ .ac97_switch = xonar_line_mic_ac97_switch,
+ .dac_tlv = pcm1796_db_scale,
+ .model_data_size = sizeof(struct xonar_data),
+ .device_config = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_2,
+ .dac_channels = 2,
+ .dac_volume_min = 255 - 2*60,
+ .dac_volume_max = 255,
+ .function_flags = OXYGEN_FUNCTION_2WIRE,
+ .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+ .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+static int __devinit get_xonar_model(struct oxygen *chip,
+ const struct pci_device_id *id)
{
static const struct oxygen_model *const models[] = {
[MODEL_D1] = &model_xonar_d1,
@@ -914,7 +1067,57 @@
[MODEL_D2] = &model_xonar_d2,
[MODEL_D2X] = &model_xonar_d2,
[MODEL_HDAV] = &model_xonar_hdav,
+ [MODEL_STX] = &model_xonar_st,
};
+ static const char *const names[] = {
+ [MODEL_D1] = "Xonar D1",
+ [MODEL_DX] = "Xonar DX",
+ [MODEL_D2] = "Xonar D2",
+ [MODEL_D2X] = "Xonar D2X",
+ [MODEL_HDAV] = "Xonar HDAV1.3",
+ [MODEL_HDAV_H6] = "Xonar HDAV1.3+H6",
+ [MODEL_STX] = "Xonar Essence STX",
+ };
+ unsigned int model = id->driver_data;
+
+ if (model >= ARRAY_SIZE(models) || !models[model])
+ return -EINVAL;
+ chip->model = *models[model];
+
+ switch (model) {
+ case MODEL_D2X:
+ chip->model.init = xonar_d2x_init;
+ break;
+ case MODEL_DX:
+ chip->model.init = xonar_dx_init;
+ break;
+ case MODEL_HDAV:
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_HDAV_DB_MASK);
+ switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) &
+ GPIO_HDAV_DB_MASK) {
+ case GPIO_HDAV_DB_H6:
+ model = MODEL_HDAV_H6;
+ break;
+ case GPIO_HDAV_DB_XX:
+ snd_printk(KERN_ERR "unknown daughterboard\n");
+ return -ENODEV;
+ }
+ break;
+ case MODEL_STX:
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_HDAV_DB_MASK);
+ break;
+ }
+
+ chip->model.shortname = names[model];
+ chip->model.private_data = model;
+ return 0;
+}
+
+static int __devinit xonar_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
static int dev;
int err;
@@ -924,10 +1127,8 @@
++dev;
return -ENOENT;
}
- BUG_ON(pci_id->driver_data >= ARRAY_SIZE(models));
- err = oxygen_pci_probe(pci, index[dev], id[dev],
- models[pci_id->driver_data],
- pci_id->driver_data);
+ err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
+ xonar_ids, get_xonar_model);
if (err >= 0)
++dev;
return err;
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index 27cf2c2..80e064a 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -1334,6 +1334,40 @@
snd_iprintf(buffer, "\n");
}
+static void pcxhr_proc_gpio_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_pcxhr *chip = entry->private_data;
+ struct pcxhr_mgr *mgr = chip->mgr;
+ /* commands available when embedded DSP is running */
+ if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) {
+ /* gpio ports on stereo boards only available */
+ int value = 0;
+ hr222_read_gpio(mgr, 1, &value); /* GPI */
+ snd_iprintf(buffer, "GPI: 0x%x\n", value);
+ hr222_read_gpio(mgr, 0, &value); /* GP0 */
+ snd_iprintf(buffer, "GPO: 0x%x\n", value);
+ } else
+ snd_iprintf(buffer, "no firmware loaded\n");
+ snd_iprintf(buffer, "\n");
+}
+static void pcxhr_proc_gpo_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_pcxhr *chip = entry->private_data;
+ struct pcxhr_mgr *mgr = chip->mgr;
+ char line[64];
+ int value;
+ /* commands available when embedded DSP is running */
+ if (!(mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)))
+ return;
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "GPO: 0x%x", &value) != 1)
+ continue;
+ hr222_write_gpo(mgr, value); /* GP0 */
+ }
+}
+
static void __devinit pcxhr_proc_init(struct snd_pcxhr *chip)
{
struct snd_info_entry *entry;
@@ -1342,6 +1376,13 @@
snd_info_set_text_ops(entry, chip, pcxhr_proc_info);
if (! snd_card_proc_new(chip->card, "sync", &entry))
snd_info_set_text_ops(entry, chip, pcxhr_proc_sync);
+ /* gpio available on stereo sound cards only */
+ if (chip->mgr->is_hr_stereo &&
+ !snd_card_proc_new(chip->card, "gpio", &entry)) {
+ snd_info_set_text_ops(entry, chip, pcxhr_proc_gpio_read);
+ entry->c.text.write = pcxhr_proc_gpo_write;
+ entry->mode |= S_IWUSR;
+ }
}
/* end of proc interface */
@@ -1510,12 +1551,12 @@
snprintf(tmpid, sizeof(tmpid), "%s-%d",
id[dev] ? id[dev] : card_name, i);
- card = snd_card_new(idx, tmpid, THIS_MODULE, 0);
+ err = snd_card_create(idx, tmpid, THIS_MODULE, 0, &card);
- if (! card) {
+ if (err < 0) {
snd_printk(KERN_ERR "cannot allocate the card %d\n", i);
pcxhr_free(mgr);
- return -ENOMEM;
+ return err;
}
strcpy(card->driver, DRIVER_NAME);
diff --git a/sound/pci/pcxhr/pcxhr.h b/sound/pci/pcxhr/pcxhr.h
index 69d87de..bda776c 100644
--- a/sound/pci/pcxhr/pcxhr.h
+++ b/sound/pci/pcxhr/pcxhr.h
@@ -27,8 +27,8 @@
#include <linux/mutex.h>
#include <sound/pcm.h>
-#define PCXHR_DRIVER_VERSION 0x000905 /* 0.9.5 */
-#define PCXHR_DRIVER_VERSION_STRING "0.9.5" /* 0.9.5 */
+#define PCXHR_DRIVER_VERSION 0x000906 /* 0.9.6 */
+#define PCXHR_DRIVER_VERSION_STRING "0.9.6" /* 0.9.6 */
#define PCXHR_MAX_CARDS 6
@@ -124,6 +124,7 @@
unsigned char xlx_cfg; /* copy of PCXHR_XLX_CFG register */
unsigned char xlx_selmic; /* copy of PCXHR_XLX_SELMIC register */
+ unsigned char dsp_reset; /* copy of PCXHR_DSP_RESET register */
};
diff --git a/sound/pci/pcxhr/pcxhr_core.h b/sound/pci/pcxhr/pcxhr_core.h
index bbbd66d..be01737 100644
--- a/sound/pci/pcxhr/pcxhr_core.h
+++ b/sound/pci/pcxhr/pcxhr_core.h
@@ -1,7 +1,7 @@
/*
* Driver for Digigram pcxhr compatible soundcards
*
- * low level interface with interrupt ans message handling
+ * low level interface with interrupt and message handling
*
* Copyright (c) 2004 by Digigram <alsa@digigram.com>
*
diff --git a/sound/pci/pcxhr/pcxhr_hwdep.c b/sound/pci/pcxhr/pcxhr_hwdep.c
index 592743a..17cb123 100644
--- a/sound/pci/pcxhr/pcxhr_hwdep.c
+++ b/sound/pci/pcxhr/pcxhr_hwdep.c
@@ -471,16 +471,6 @@
return 0;
}
-static int pcxhr_hwdep_open(struct snd_hwdep *hw, struct file *file)
-{
- return 0;
-}
-
-static int pcxhr_hwdep_release(struct snd_hwdep *hw, struct file *file)
-{
- return 0;
-}
-
int pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
{
int err;
@@ -495,8 +485,6 @@
hw->iface = SNDRV_HWDEP_IFACE_PCXHR;
hw->private_data = mgr;
- hw->ops.open = pcxhr_hwdep_open;
- hw->ops.release = pcxhr_hwdep_release;
hw->ops.dsp_status = pcxhr_hwdep_dsp_status;
hw->ops.dsp_load = pcxhr_hwdep_dsp_load;
hw->exclusive = 1;
diff --git a/sound/pci/pcxhr/pcxhr_mix22.c b/sound/pci/pcxhr/pcxhr_mix22.c
index ff01912..1cb82c0 100644
--- a/sound/pci/pcxhr/pcxhr_mix22.c
+++ b/sound/pci/pcxhr/pcxhr_mix22.c
@@ -53,6 +53,8 @@
#define PCXHR_DSP_RESET_DSP 0x01
#define PCXHR_DSP_RESET_MUTE 0x02
#define PCXHR_DSP_RESET_CODEC 0x08
+#define PCXHR_DSP_RESET_GPO_OFFSET 5
+#define PCXHR_DSP_RESET_GPO_MASK 0x60
/* values for PCHR_XLX_CFG register */
#define PCXHR_CFG_SYNCDSP_MASK 0x80
@@ -81,6 +83,8 @@
/* values for PCHR_XLX_STATUS register - READ */
#define PCXHR_STAT_SRC_LOCK 0x01
#define PCXHR_STAT_LEVEL_IN 0x02
+#define PCXHR_STAT_GPI_OFFSET 2
+#define PCXHR_STAT_GPI_MASK 0x0C
#define PCXHR_STAT_MIC_CAPS 0x10
/* values for PCHR_XLX_STATUS register - WRITE */
#define PCXHR_STAT_FREQ_SYNC_MASK 0x01
@@ -291,10 +295,11 @@
PCXHR_OUTPB(mgr, PCXHR_DSP_RESET,
PCXHR_DSP_RESET_DSP);
msleep(5);
- PCXHR_OUTPB(mgr, PCXHR_DSP_RESET,
- PCXHR_DSP_RESET_DSP |
- PCXHR_DSP_RESET_MUTE |
- PCXHR_DSP_RESET_CODEC);
+ mgr->dsp_reset = PCXHR_DSP_RESET_DSP |
+ PCXHR_DSP_RESET_MUTE |
+ PCXHR_DSP_RESET_CODEC;
+ PCXHR_OUTPB(mgr, PCXHR_DSP_RESET, mgr->dsp_reset);
+ /* hr222_write_gpo(mgr, 0); does the same */
msleep(5);
/* config AKM */
@@ -496,6 +501,33 @@
}
+int hr222_read_gpio(struct pcxhr_mgr *mgr, int is_gpi, int *value)
+{
+ if (is_gpi) {
+ unsigned char reg = PCXHR_INPB(mgr, PCXHR_XLX_STATUS);
+ *value = (int)(reg & PCXHR_STAT_GPI_MASK) >>
+ PCXHR_STAT_GPI_OFFSET;
+ } else {
+ *value = (int)(mgr->dsp_reset & PCXHR_DSP_RESET_GPO_MASK) >>
+ PCXHR_DSP_RESET_GPO_OFFSET;
+ }
+ return 0;
+}
+
+
+int hr222_write_gpo(struct pcxhr_mgr *mgr, int value)
+{
+ unsigned char reg = mgr->dsp_reset & ~PCXHR_DSP_RESET_GPO_MASK;
+
+ reg |= (unsigned char)(value << PCXHR_DSP_RESET_GPO_OFFSET) &
+ PCXHR_DSP_RESET_GPO_MASK;
+
+ PCXHR_OUTPB(mgr, PCXHR_DSP_RESET, reg);
+ mgr->dsp_reset = reg;
+ return 0;
+}
+
+
int hr222_update_analog_audio_level(struct snd_pcxhr *chip,
int is_capture, int channel)
{
diff --git a/sound/pci/pcxhr/pcxhr_mix22.h b/sound/pci/pcxhr/pcxhr_mix22.h
index 6b318b2..5a37a00 100644
--- a/sound/pci/pcxhr/pcxhr_mix22.h
+++ b/sound/pci/pcxhr/pcxhr_mix22.h
@@ -32,6 +32,9 @@
enum pcxhr_clock_type clock_type,
int *sample_rate);
+int hr222_read_gpio(struct pcxhr_mgr *mgr, int is_gpi, int *value);
+int hr222_write_gpo(struct pcxhr_mgr *mgr, int value);
+
#define HR222_LINE_PLAYBACK_LEVEL_MIN 0 /* -25.5 dB */
#define HR222_LINE_PLAYBACK_ZERO_LEVEL 51 /* 0.0 dB */
#define HR222_LINE_PLAYBACK_LEVEL_MAX 99 /* +24.0 dB */
diff --git a/sound/pci/pcxhr/pcxhr_mixer.c b/sound/pci/pcxhr/pcxhr_mixer.c
index 2436e37..fec0493 100644
--- a/sound/pci/pcxhr/pcxhr_mixer.c
+++ b/sound/pci/pcxhr/pcxhr_mixer.c
@@ -789,11 +789,15 @@
if (mgr->use_clock_type != ucontrol->value.enumerated.item[0]) {
mutex_lock(&mgr->setup_mutex);
mgr->use_clock_type = ucontrol->value.enumerated.item[0];
- if (mgr->use_clock_type)
+ rate = 0;
+ if (mgr->use_clock_type != PCXHR_CLOCK_TYPE_INTERNAL) {
pcxhr_get_external_clock(mgr, mgr->use_clock_type,
&rate);
- else
+ } else {
rate = mgr->sample_rate;
+ if (!rate)
+ rate = 48000;
+ }
if (rate) {
pcxhr_set_clock(mgr, rate);
if (mgr->sample_rate)
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index 3caacfb..6f10344 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -2102,9 +2102,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
if ((err = snd_riptide_create(card, pci, &chip)) < 0) {
snd_card_free(card);
return err;
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index e7ef3a1..d7b966e 100644
--- a/sound/pci/rme32.c
+++ b/sound/pci/rme32.c
@@ -1941,9 +1941,10 @@
return -ENOENT;
}
- if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct rme32))) == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct rme32), &card);
+ if (err < 0)
+ return err;
card->private_free = snd_rme32_card_free;
rme32 = (struct rme32 *) card->private_data;
rme32->card = card;
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index 3fdd488..55fb1c1 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -2348,9 +2348,10 @@
dev++;
return -ENOENT;
}
- if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct rme96))) == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct rme96), &card);
+ if (err < 0)
+ return err;
card->private_free = snd_rme96_card_free;
rme96 = (struct rme96 *)card->private_data;
rme96->card = card;
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 44d0c15..314e735 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -113,7 +113,7 @@
/* the meters are regular i/o-mapped registers, but offset
considerably from the rest. the peak registers are reset
- when read; the least-significant 4 bits are full-scale counters;
+ when read; the least-significant 4 bits are full-scale counters;
the actual peak value is in the most-significant 24 bits.
*/
@@ -131,7 +131,7 @@
26*3 values are read in ss mode
14*3 in ds mode, with no gap between values
*/
-#define HDSP_9652_peakBase 7164
+#define HDSP_9652_peakBase 7164
#define HDSP_9652_rmsBase 4096
/* c.f. the hdsp_9632_meters_t struct */
@@ -173,12 +173,12 @@
#define HDSP_SPDIFEmphasis (1<<10) /* 0=none, 1=on */
#define HDSP_SPDIFNonAudio (1<<11) /* 0=off, 1=on */
#define HDSP_SPDIFOpticalOut (1<<12) /* 1=use 1st ADAT connector for SPDIF, 0=do not */
-#define HDSP_SyncRef2 (1<<13)
-#define HDSP_SPDIFInputSelect0 (1<<14)
-#define HDSP_SPDIFInputSelect1 (1<<15)
-#define HDSP_SyncRef0 (1<<16)
+#define HDSP_SyncRef2 (1<<13)
+#define HDSP_SPDIFInputSelect0 (1<<14)
+#define HDSP_SPDIFInputSelect1 (1<<15)
+#define HDSP_SyncRef0 (1<<16)
#define HDSP_SyncRef1 (1<<17)
-#define HDSP_AnalogExtensionBoard (1<<18) /* For H9632 cards */
+#define HDSP_AnalogExtensionBoard (1<<18) /* For H9632 cards */
#define HDSP_XLRBreakoutCable (1<<20) /* For H9632 cards */
#define HDSP_Midi0InterruptEnable (1<<22)
#define HDSP_Midi1InterruptEnable (1<<23)
@@ -314,7 +314,7 @@
#define HDSP_TimecodeSync (1<<27)
#define HDSP_AEBO (1<<28) /* H9632 specific Analog Extension Boards */
#define HDSP_AEBI (1<<29) /* 0 = present, 1 = absent */
-#define HDSP_midi0IRQPending (1<<30)
+#define HDSP_midi0IRQPending (1<<30)
#define HDSP_midi1IRQPending (1<<31)
#define HDSP_spdifFrequencyMask (HDSP_spdifFrequency0|HDSP_spdifFrequency1|HDSP_spdifFrequency2)
@@ -391,7 +391,7 @@
#define HDSP_CHANNEL_BUFFER_BYTES (4*HDSP_CHANNEL_BUFFER_SAMPLES)
/* the size of the area we need to allocate for DMA transfers. the
- size is the same regardless of the number of channels - the
+ size is the same regardless of the number of channels - the
Multiface still uses the same memory area.
Note that we allocate 1 more channel than is apparently needed
@@ -460,7 +460,7 @@
unsigned char qs_in_channels; /* quad speed mode for H9632 */
unsigned char ds_in_channels;
unsigned char ss_in_channels; /* different for multiface/digiface */
- unsigned char qs_out_channels;
+ unsigned char qs_out_channels;
unsigned char ds_out_channels;
unsigned char ss_out_channels;
@@ -502,9 +502,9 @@
static char channel_map_mf_ss[HDSP_MAX_CHANNELS] = { /* Multiface */
/* Analog */
- 0, 1, 2, 3, 4, 5, 6, 7,
+ 0, 1, 2, 3, 4, 5, 6, 7,
/* ADAT 2 */
- 16, 17, 18, 19, 20, 21, 22, 23,
+ 16, 17, 18, 19, 20, 21, 22, 23,
/* SPDIF */
24, 25,
-1, -1, -1, -1, -1, -1, -1, -1
@@ -525,11 +525,11 @@
/* SPDIF */
8, 9,
/* Analog */
- 10, 11,
+ 10, 11,
/* AO4S-192 and AI4S-192 extension boards */
12, 13, 14, 15,
/* others don't exist */
- -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1
};
@@ -539,7 +539,7 @@
/* SPDIF */
8, 9,
/* Analog */
- 10, 11,
+ 10, 11,
/* AO4S-192 and AI4S-192 extension boards */
12, 13, 14, 15,
/* others don't exist */
@@ -587,7 +587,7 @@
static struct pci_device_id snd_hdsp_ids[] = {
{
.vendor = PCI_VENDOR_ID_XILINX,
- .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP,
+ .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
}, /* RME Hammerfall-DSP */
@@ -653,7 +653,6 @@
static int hdsp_check_for_iobox (struct hdsp *hdsp)
{
-
if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return 0;
if (hdsp_read (hdsp, HDSP_statusRegister) & HDSP_ConfigError) {
snd_printk ("Hammerfall-DSP: no Digiface or Multiface connected!\n");
@@ -661,7 +660,29 @@
return -EIO;
}
return 0;
+}
+static int hdsp_wait_for_iobox(struct hdsp *hdsp, unsigned int loops,
+ unsigned int delay)
+{
+ unsigned int i;
+
+ if (hdsp->io_type == H9652 || hdsp->io_type == H9632)
+ return 0;
+
+ for (i = 0; i != loops; ++i) {
+ if (hdsp_read(hdsp, HDSP_statusRegister) & HDSP_ConfigError)
+ msleep(delay);
+ else {
+ snd_printd("Hammerfall-DSP: iobox found after %ums!\n",
+ i * delay);
+ return 0;
+ }
+ }
+
+ snd_printk("Hammerfall-DSP: no Digiface or Multiface connected!\n");
+ hdsp->state &= ~HDSP_FirmwareLoaded;
+ return -EIO;
}
static int snd_hdsp_load_firmware_from_cache(struct hdsp *hdsp) {
@@ -670,19 +691,19 @@
unsigned long flags;
if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) {
-
+
snd_printk ("Hammerfall-DSP: loading firmware\n");
hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_PROGRAM);
hdsp_write (hdsp, HDSP_fifoData, 0);
-
+
if (hdsp_fifo_wait (hdsp, 0, HDSP_LONG_WAIT)) {
snd_printk ("Hammerfall-DSP: timeout waiting for download preparation\n");
return -EIO;
}
-
+
hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD);
-
+
for (i = 0; i < 24413; ++i) {
hdsp_write(hdsp, HDSP_fifoData, hdsp->firmware_cache[i]);
if (hdsp_fifo_wait (hdsp, 127, HDSP_LONG_WAIT)) {
@@ -692,7 +713,7 @@
}
ssleep(3);
-
+
if (hdsp_fifo_wait (hdsp, 0, HDSP_LONG_WAIT)) {
snd_printk ("Hammerfall-DSP: timeout at end of firmware loading\n");
return -EIO;
@@ -705,15 +726,15 @@
#endif
hdsp_write (hdsp, HDSP_control2Reg, hdsp->control2_register);
snd_printk ("Hammerfall-DSP: finished firmware loading\n");
-
+
}
if (hdsp->state & HDSP_InitializationComplete) {
snd_printk(KERN_INFO "Hammerfall-DSP: firmware loaded from cache, restoring defaults\n");
spin_lock_irqsave(&hdsp->lock, flags);
snd_hdsp_set_defaults(hdsp);
- spin_unlock_irqrestore(&hdsp->lock, flags);
+ spin_unlock_irqrestore(&hdsp->lock, flags);
}
-
+
hdsp->state |= HDSP_FirmwareLoaded;
return 0;
@@ -722,7 +743,7 @@
static int hdsp_get_iobox_version (struct hdsp *hdsp)
{
if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) {
-
+
hdsp_write (hdsp, HDSP_control2Reg, HDSP_PROGRAM);
hdsp_write (hdsp, HDSP_fifoData, 0);
if (hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT) < 0)
@@ -738,7 +759,7 @@
hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT);
} else {
hdsp->io_type = Digiface;
- }
+ }
} else {
/* firmware was already loaded, get iobox type */
if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
@@ -786,13 +807,13 @@
static int hdsp_fifo_wait(struct hdsp *hdsp, int count, int timeout)
-{
+{
int i;
/* the fifoStatus registers reports on how many words
are available in the command FIFO.
*/
-
+
for (i = 0; i < timeout; i++) {
if ((int)(hdsp_read (hdsp, HDSP_fifoStatus) & 0xff) <= count)
@@ -824,11 +845,11 @@
if (addr >= HDSP_MATRIX_MIXER_SIZE)
return -1;
-
+
if (hdsp->io_type == H9652 || hdsp->io_type == H9632) {
/* from martin bjornsen:
-
+
"You can only write dwords to the
mixer memory which contain two
mixer values in the low and high
@@ -847,7 +868,7 @@
hdsp->mixer_matrix[addr] = data;
-
+
/* `addr' addresses a 16-bit wide address, but
the address space accessed via hdsp_write
uses byte offsets. put another way, addr
@@ -856,17 +877,17 @@
to access 0 to 2703 ...
*/
ad = addr/2;
-
- hdsp_write (hdsp, 4096 + (ad*4),
- (hdsp->mixer_matrix[(addr&0x7fe)+1] << 16) +
+
+ hdsp_write (hdsp, 4096 + (ad*4),
+ (hdsp->mixer_matrix[(addr&0x7fe)+1] << 16) +
hdsp->mixer_matrix[addr&0x7fe]);
-
+
return 0;
} else {
ad = (addr << 16) + data;
-
+
if (hdsp_fifo_wait(hdsp, 127, HDSP_LONG_WAIT))
return -1;
@@ -902,7 +923,7 @@
if (status & HDSP_SPDIFErrorFlag)
return 0;
-
+
switch (rate_bits) {
case HDSP_spdifFrequency32KHz: return 32000;
case HDSP_spdifFrequency44_1KHz: return 44100;
@@ -910,13 +931,13 @@
case HDSP_spdifFrequency64KHz: return 64000;
case HDSP_spdifFrequency88_2KHz: return 88200;
case HDSP_spdifFrequency96KHz: return 96000;
- case HDSP_spdifFrequency128KHz:
+ case HDSP_spdifFrequency128KHz:
if (hdsp->io_type == H9632) return 128000;
break;
- case HDSP_spdifFrequency176_4KHz:
+ case HDSP_spdifFrequency176_4KHz:
if (hdsp->io_type == H9632) return 176400;
break;
- case HDSP_spdifFrequency192KHz:
+ case HDSP_spdifFrequency192KHz:
if (hdsp->io_type == H9632) return 192000;
break;
default:
@@ -1027,7 +1048,7 @@
{
u64 n;
u32 r;
-
+
if (rate >= 112000)
rate /= 4;
else if (rate >= 56000)
@@ -1053,35 +1074,35 @@
there is no need for it (e.g. during module
initialization).
*/
-
- if (!(hdsp->control_register & HDSP_ClockModeMaster)) {
+
+ if (!(hdsp->control_register & HDSP_ClockModeMaster)) {
if (called_internally) {
/* request from ctl or card initialization */
snd_printk(KERN_ERR "Hammerfall-DSP: device is not running as a clock master: cannot set sample rate.\n");
return -1;
- } else {
+ } else {
/* hw_param request while in AutoSync mode */
int external_freq = hdsp_external_sample_rate(hdsp);
int spdif_freq = hdsp_spdif_sample_rate(hdsp);
-
+
if ((spdif_freq == external_freq*2) && (hdsp_autosync_ref(hdsp) >= HDSP_AUTOSYNC_FROM_ADAT1))
snd_printk(KERN_INFO "Hammerfall-DSP: Detected ADAT in double speed mode\n");
else if (hdsp->io_type == H9632 && (spdif_freq == external_freq*4) && (hdsp_autosync_ref(hdsp) >= HDSP_AUTOSYNC_FROM_ADAT1))
- snd_printk(KERN_INFO "Hammerfall-DSP: Detected ADAT in quad speed mode\n");
+ snd_printk(KERN_INFO "Hammerfall-DSP: Detected ADAT in quad speed mode\n");
else if (rate != external_freq) {
snd_printk(KERN_INFO "Hammerfall-DSP: No AutoSync source for requested rate\n");
return -1;
- }
- }
+ }
+ }
}
current_rate = hdsp->system_sample_rate;
/* Changing from a "single speed" to a "double speed" rate is
not allowed if any substreams are open. This is because
- such a change causes a shift in the location of
+ such a change causes a shift in the location of
the DMA buffers and a reduction in the number of available
- buffers.
+ buffers.
Note that a similar but essentially insoluble problem
exists for externally-driven rate changes. All we can do
@@ -1089,7 +1110,7 @@
if (rate > 96000 && hdsp->io_type != H9632)
return -EINVAL;
-
+
switch (rate) {
case 32000:
if (current_rate > 48000)
@@ -1179,7 +1200,7 @@
break;
}
}
-
+
hdsp->system_sample_rate = rate;
return 0;
@@ -1245,16 +1266,16 @@
unsigned char buf[128];
/* Output is not interrupt driven */
-
+
spin_lock_irqsave (&hmidi->lock, flags);
if (hmidi->output) {
if (!snd_rawmidi_transmit_empty (hmidi->output)) {
if ((n_pending = snd_hdsp_midi_output_possible (hmidi->hdsp, hmidi->id)) > 0) {
if (n_pending > (int)sizeof (buf))
n_pending = sizeof (buf);
-
+
if ((to_write = snd_rawmidi_transmit (hmidi->output, buf, n_pending)) > 0) {
- for (i = 0; i < to_write; ++i)
+ for (i = 0; i < to_write; ++i)
snd_hdsp_midi_write_byte (hmidi->hdsp, hmidi->id, buf[i]);
}
}
@@ -1325,14 +1346,14 @@
{
struct hdsp_midi *hmidi = (struct hdsp_midi *) data;
unsigned long flags;
-
+
snd_hdsp_midi_output_write(hmidi);
spin_lock_irqsave (&hmidi->lock, flags);
/* this does not bump hmidi->istimer, because the
kernel automatically removed the timer when it
expired, and we are now adding it back, thus
- leaving istimer wherever it was set before.
+ leaving istimer wherever it was set before.
*/
if (hmidi->istimer) {
@@ -1501,7 +1522,7 @@
static int snd_hdsp_control_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
snd_hdsp_convert_to_aes(&ucontrol->value.iec958, hdsp->creg_spdif);
return 0;
}
@@ -1511,7 +1532,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
u32 val;
-
+
val = snd_hdsp_convert_from_aes(&ucontrol->value.iec958);
spin_lock_irq(&hdsp->lock);
change = val != hdsp->creg_spdif;
@@ -1530,7 +1551,7 @@
static int snd_hdsp_control_spdif_stream_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
snd_hdsp_convert_to_aes(&ucontrol->value.iec958, hdsp->creg_spdif_stream);
return 0;
}
@@ -1540,7 +1561,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
u32 val;
-
+
val = snd_hdsp_convert_from_aes(&ucontrol->value.iec958);
spin_lock_irq(&hdsp->lock);
change = val != hdsp->creg_spdif_stream;
@@ -1602,7 +1623,7 @@
static int snd_hdsp_get_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.enumerated.item[0] = hdsp_spdif_in(hdsp);
return 0;
}
@@ -1612,7 +1633,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
unsigned int val;
-
+
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.enumerated.item[0] % ((hdsp->io_type == H9632) ? 4 : 3);
@@ -1649,7 +1670,7 @@
static int snd_hdsp_get_spdif_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.integer.value[0] = hdsp_spdif_out(hdsp);
return 0;
}
@@ -1659,7 +1680,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
unsigned int val;
-
+
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
@@ -1693,7 +1714,7 @@
static int snd_hdsp_get_spdif_professional(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.integer.value[0] = hdsp_spdif_professional(hdsp);
return 0;
}
@@ -1703,7 +1724,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
unsigned int val;
-
+
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
@@ -1737,7 +1758,7 @@
static int snd_hdsp_get_spdif_emphasis(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.integer.value[0] = hdsp_spdif_emphasis(hdsp);
return 0;
}
@@ -1747,7 +1768,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
unsigned int val;
-
+
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
@@ -1781,7 +1802,7 @@
static int snd_hdsp_get_spdif_nonaudio(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.integer.value[0] = hdsp_spdif_nonaudio(hdsp);
return 0;
}
@@ -1791,7 +1812,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
unsigned int val;
-
+
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
@@ -1828,7 +1849,7 @@
static int snd_hdsp_get_spdif_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
switch (hdsp_spdif_sample_rate(hdsp)) {
case 32000:
ucontrol->value.enumerated.item[0] = 0;
@@ -1858,7 +1879,7 @@
ucontrol->value.enumerated.item[0] = 9;
break;
default:
- ucontrol->value.enumerated.item[0] = 6;
+ ucontrol->value.enumerated.item[0] = 6;
}
return 0;
}
@@ -1882,7 +1903,7 @@
static int snd_hdsp_get_system_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.enumerated.item[0] = hdsp->system_sample_rate;
return 0;
}
@@ -1899,7 +1920,7 @@
static int snd_hdsp_info_autosync_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
- static char *texts[] = {"32000", "44100", "48000", "64000", "88200", "96000", "None", "128000", "176400", "192000"};
+ static char *texts[] = {"32000", "44100", "48000", "64000", "88200", "96000", "None", "128000", "176400", "192000"};
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = (hdsp->io_type == H9632) ? 10 : 7 ;
@@ -1912,7 +1933,7 @@
static int snd_hdsp_get_autosync_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
switch (hdsp_external_sample_rate(hdsp)) {
case 32000:
ucontrol->value.enumerated.item[0] = 0;
@@ -1940,9 +1961,9 @@
break;
case 192000:
ucontrol->value.enumerated.item[0] = 9;
- break;
+ break;
default:
- ucontrol->value.enumerated.item[0] = 6;
+ ucontrol->value.enumerated.item[0] = 6;
}
return 0;
}
@@ -1968,7 +1989,7 @@
static int snd_hdsp_info_system_clock_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = {"Master", "Slave" };
-
+
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
@@ -1981,7 +2002,7 @@
static int snd_hdsp_get_system_clock_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.enumerated.item[0] = hdsp_system_clock_mode(hdsp);
return 0;
}
@@ -2018,7 +2039,7 @@
case 192000:
return 9;
default:
- return 3;
+ return 3;
}
} else {
return 0;
@@ -2032,7 +2053,7 @@
case HDSP_CLOCK_SOURCE_AUTOSYNC:
if (hdsp_external_sample_rate(hdsp) != 0) {
if (!hdsp_set_rate(hdsp, hdsp_external_sample_rate(hdsp), 1)) {
- hdsp->control_register &= ~HDSP_ClockModeMaster;
+ hdsp->control_register &= ~HDSP_ClockModeMaster;
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
return 0;
}
@@ -2043,7 +2064,7 @@
break;
case HDSP_CLOCK_SOURCE_INTERNAL_44_1KHZ:
rate = 44100;
- break;
+ break;
case HDSP_CLOCK_SOURCE_INTERNAL_48KHZ:
rate = 48000;
break;
@@ -2078,13 +2099,13 @@
{
static char *texts[] = {"AutoSync", "Internal 32.0 kHz", "Internal 44.1 kHz", "Internal 48.0 kHz", "Internal 64.0 kHz", "Internal 88.2 kHz", "Internal 96.0 kHz", "Internal 128 kHz", "Internal 176.4 kHz", "Internal 192.0 KHz" };
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
if (hdsp->io_type == H9632)
uinfo->value.enumerated.items = 10;
else
- uinfo->value.enumerated.items = 7;
+ uinfo->value.enumerated.items = 7;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
@@ -2094,7 +2115,7 @@
static int snd_hdsp_get_clock_source(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.enumerated.item[0] = hdsp_clock_source(hdsp);
return 0;
}
@@ -2104,7 +2125,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
int val;
-
+
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.enumerated.item[0];
@@ -2130,7 +2151,7 @@
static int snd_hdsp_get_clock_source_lock(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.integer.value[0] = hdsp->clock_source_locked;
return 0;
}
@@ -2165,7 +2186,7 @@
case HDSP_DAGainMinus10dBV:
return 2;
default:
- return 1;
+ return 1;
}
}
@@ -2180,8 +2201,8 @@
hdsp->control_register |= HDSP_DAGainPlus4dBu;
break;
case 2:
- hdsp->control_register |= HDSP_DAGainMinus10dBV;
- break;
+ hdsp->control_register |= HDSP_DAGainMinus10dBV;
+ break;
default:
return -1;
@@ -2193,7 +2214,7 @@
static int snd_hdsp_info_da_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = {"Hi Gain", "+4 dBu", "-10 dbV"};
-
+
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 3;
@@ -2206,7 +2227,7 @@
static int snd_hdsp_get_da_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.enumerated.item[0] = hdsp_da_gain(hdsp);
return 0;
}
@@ -2216,7 +2237,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
int val;
-
+
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.enumerated.item[0];
@@ -2250,7 +2271,7 @@
case HDSP_ADGainLowGain:
return 2;
default:
- return 1;
+ return 1;
}
}
@@ -2262,11 +2283,11 @@
hdsp->control_register |= HDSP_ADGainMinus10dBV;
break;
case 1:
- hdsp->control_register |= HDSP_ADGainPlus4dBu;
+ hdsp->control_register |= HDSP_ADGainPlus4dBu;
break;
case 2:
- hdsp->control_register |= HDSP_ADGainLowGain;
- break;
+ hdsp->control_register |= HDSP_ADGainLowGain;
+ break;
default:
return -1;
@@ -2278,7 +2299,7 @@
static int snd_hdsp_info_ad_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = {"-10 dBV", "+4 dBu", "Lo Gain"};
-
+
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 3;
@@ -2291,7 +2312,7 @@
static int snd_hdsp_get_ad_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.enumerated.item[0] = hdsp_ad_gain(hdsp);
return 0;
}
@@ -2301,7 +2322,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
int val;
-
+
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.enumerated.item[0];
@@ -2335,7 +2356,7 @@
case HDSP_PhoneGainMinus12dB:
return 2;
default:
- return 0;
+ return 0;
}
}
@@ -2347,11 +2368,11 @@
hdsp->control_register |= HDSP_PhoneGain0dB;
break;
case 1:
- hdsp->control_register |= HDSP_PhoneGainMinus6dB;
+ hdsp->control_register |= HDSP_PhoneGainMinus6dB;
break;
case 2:
- hdsp->control_register |= HDSP_PhoneGainMinus12dB;
- break;
+ hdsp->control_register |= HDSP_PhoneGainMinus12dB;
+ break;
default:
return -1;
@@ -2363,7 +2384,7 @@
static int snd_hdsp_info_phone_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = {"0 dB", "-6 dB", "-12 dB"};
-
+
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 3;
@@ -2376,7 +2397,7 @@
static int snd_hdsp_get_phone_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.enumerated.item[0] = hdsp_phone_gain(hdsp);
return 0;
}
@@ -2386,7 +2407,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
int val;
-
+
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.enumerated.item[0];
@@ -2432,7 +2453,7 @@
static int snd_hdsp_get_xlr_breakout_cable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.enumerated.item[0] = hdsp_xlr_breakout_cable(hdsp);
return 0;
}
@@ -2442,7 +2463,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
int val;
-
+
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
@@ -2488,7 +2509,7 @@
static int snd_hdsp_get_aeb(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.enumerated.item[0] = hdsp_aeb(hdsp);
return 0;
}
@@ -2498,7 +2519,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
int val;
-
+
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
@@ -2576,7 +2597,7 @@
{
static char *texts[] = {"Word", "IEC958", "ADAT1", "ADAT Sync", "ADAT2", "ADAT3" };
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
@@ -2595,7 +2616,7 @@
uinfo->value.enumerated.items = 0;
break;
}
-
+
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
@@ -2605,7 +2626,7 @@
static int snd_hdsp_get_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.enumerated.item[0] = hdsp_pref_sync_ref(hdsp);
return 0;
}
@@ -2615,7 +2636,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change, max;
unsigned int val;
-
+
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
@@ -2664,7 +2685,7 @@
case HDSP_SelSyncRef_SPDIF:
return HDSP_AUTOSYNC_FROM_SPDIF;
case HDSP_SelSyncRefMask:
- return HDSP_AUTOSYNC_FROM_NONE;
+ return HDSP_AUTOSYNC_FROM_NONE;
case HDSP_SelSyncRef_ADAT1:
return HDSP_AUTOSYNC_FROM_ADAT1;
case HDSP_SelSyncRef_ADAT2:
@@ -2680,7 +2701,7 @@
static int snd_hdsp_info_autosync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = {"Word", "ADAT Sync", "IEC958", "None", "ADAT1", "ADAT2", "ADAT3" };
-
+
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 7;
@@ -2693,7 +2714,7 @@
static int snd_hdsp_get_autosync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.enumerated.item[0] = hdsp_autosync_ref(hdsp);
return 0;
}
@@ -2727,7 +2748,7 @@
static int snd_hdsp_get_line_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
spin_lock_irq(&hdsp->lock);
ucontrol->value.integer.value[0] = hdsp_line_out(hdsp);
spin_unlock_irq(&hdsp->lock);
@@ -2739,7 +2760,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
unsigned int val;
-
+
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
@@ -2773,7 +2794,7 @@
static int snd_hdsp_get_precise_pointer(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
spin_lock_irq(&hdsp->lock);
ucontrol->value.integer.value[0] = hdsp->precise_ptr;
spin_unlock_irq(&hdsp->lock);
@@ -2785,7 +2806,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
unsigned int val;
-
+
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
@@ -2819,7 +2840,7 @@
static int snd_hdsp_get_use_midi_tasklet(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
spin_lock_irq(&hdsp->lock);
ucontrol->value.integer.value[0] = hdsp->use_midi_tasklet;
spin_unlock_irq(&hdsp->lock);
@@ -2831,7 +2852,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
unsigned int val;
-
+
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
@@ -2873,12 +2894,12 @@
source = ucontrol->value.integer.value[0];
destination = ucontrol->value.integer.value[1];
-
+
if (source >= hdsp->max_channels)
addr = hdsp_playback_to_output_key(hdsp,source-hdsp->max_channels,destination);
else
addr = hdsp_input_to_output_key(hdsp,source, destination);
-
+
spin_lock_irq(&hdsp->lock);
ucontrol->value.integer.value[2] = hdsp_read_gain (hdsp, addr);
spin_unlock_irq(&hdsp->lock);
@@ -2926,7 +2947,7 @@
static int snd_hdsp_info_sync_check(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {"No Lock", "Lock", "Sync" };
+ static char *texts[] = {"No Lock", "Lock", "Sync" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 3;
@@ -2971,7 +2992,7 @@
int status = hdsp_read(hdsp, HDSP_statusRegister);
if (status & HDSP_SPDIFErrorFlag)
return 0;
- else {
+ else {
if (status & HDSP_SPDIFSync)
return 2;
else
@@ -3007,7 +3028,7 @@
return 1;
} else
return 0;
-}
+}
static int snd_hdsp_get_adatsync_sync_check(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
@@ -3025,17 +3046,17 @@
}
static int hdsp_adat_sync_check(struct hdsp *hdsp, int idx)
-{
+{
int status = hdsp_read(hdsp, HDSP_statusRegister);
-
+
if (status & (HDSP_Lock0>>idx)) {
if (status & (HDSP_Sync0>>idx))
return 2;
else
- return 1;
+ return 1;
} else
return 0;
-}
+}
static int snd_hdsp_get_adat_sync_check(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
@@ -3053,7 +3074,7 @@
break;
case Multiface:
case H9632:
- if (offset >= 1)
+ if (offset >= 1)
return -EINVAL;
break;
default:
@@ -3115,7 +3136,7 @@
static int snd_hdsp_get_dds_offset(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
-
+
ucontrol->value.enumerated.item[0] = hdsp_dds_offset(hdsp);
return 0;
}
@@ -3125,7 +3146,7 @@
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
int val;
-
+
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.enumerated.item[0];
@@ -3170,7 +3191,7 @@
.get = snd_hdsp_control_spdif_mask_get,
.private_value = IEC958_AES0_NONAUDIO |
IEC958_AES0_PROFESSIONAL |
- IEC958_AES0_CON_EMPHASIS,
+ IEC958_AES0_CON_EMPHASIS,
},
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
@@ -3188,7 +3209,7 @@
HDSP_SPDIF_PROFESSIONAL("IEC958 Professional Bit", 0),
HDSP_SPDIF_EMPHASIS("IEC958 Emphasis Bit", 0),
HDSP_SPDIF_NON_AUDIO("IEC958 Non-audio Bit", 0),
-/* 'Sample Clock Source' complies with the alsa control naming scheme */
+/* 'Sample Clock Source' complies with the alsa control naming scheme */
HDSP_CLOCK_SOURCE("Sample Clock Source", 0),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -3240,7 +3261,7 @@
return err;
}
}
-
+
/* DA, AD and Phone gain and XLR breakout cable controls for H9632 cards */
if (hdsp->io_type == H9632) {
for (idx = 0; idx < ARRAY_SIZE(snd_hdsp_9632_controls); idx++) {
@@ -3259,7 +3280,7 @@
}
/*------------------------------------------------------------
- /proc interface
+ /proc interface
------------------------------------------------------------*/
static void
@@ -3298,7 +3319,7 @@
}
}
}
-
+
status = hdsp_read(hdsp, HDSP_statusRegister);
status2 = hdsp_read(hdsp, HDSP_status2Register);
@@ -3362,17 +3383,17 @@
break;
case HDSP_CLOCK_SOURCE_INTERNAL_192KHZ:
clock_source = "Internal 192 kHz";
- break;
+ break;
default:
- clock_source = "Error";
+ clock_source = "Error";
}
snd_iprintf (buffer, "Sample Clock Source: %s\n", clock_source);
-
+
if (hdsp_system_clock_mode(hdsp))
system_clock_mode = "Slave";
else
system_clock_mode = "Master";
-
+
switch (hdsp_pref_sync_ref (hdsp)) {
case HDSP_SYNC_FROM_WORD:
pref_sync_ref = "Word Clock";
@@ -3397,7 +3418,7 @@
break;
}
snd_iprintf (buffer, "Preferred Sync Reference: %s\n", pref_sync_ref);
-
+
switch (hdsp_autosync_ref (hdsp)) {
case HDSP_AUTOSYNC_FROM_WORD:
autosync_ref = "Word Clock";
@@ -3410,7 +3431,7 @@
break;
case HDSP_AUTOSYNC_FROM_NONE:
autosync_ref = "None";
- break;
+ break;
case HDSP_AUTOSYNC_FROM_ADAT1:
autosync_ref = "ADAT1";
break;
@@ -3425,14 +3446,14 @@
break;
}
snd_iprintf (buffer, "AutoSync Reference: %s\n", autosync_ref);
-
+
snd_iprintf (buffer, "AutoSync Frequency: %d\n", hdsp_external_sample_rate(hdsp));
-
+
snd_iprintf (buffer, "System Clock Mode: %s\n", system_clock_mode);
snd_iprintf (buffer, "System Clock Frequency: %d\n", hdsp->system_sample_rate);
snd_iprintf (buffer, "System Clock Locked: %s\n", hdsp->clock_source_locked ? "Yes" : "No");
-
+
snd_iprintf(buffer, "\n");
switch (hdsp_spdif_in(hdsp)) {
@@ -3452,7 +3473,7 @@
snd_iprintf(buffer, "IEC958 input: ???\n");
break;
}
-
+
if (hdsp->control_register & HDSP_SPDIFOpticalOut)
snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n");
else
@@ -3510,13 +3531,13 @@
snd_iprintf (buffer, "SPDIF: No Lock\n");
else
snd_iprintf (buffer, "SPDIF: %s\n", x ? "Sync" : "Lock");
-
+
x = status2 & HDSP_wc_sync;
if (status2 & HDSP_wc_lock)
snd_iprintf (buffer, "Word Clock: %s\n", x ? "Sync" : "Lock");
else
snd_iprintf (buffer, "Word Clock: No Lock\n");
-
+
x = status & HDSP_TimecodeSync;
if (status & HDSP_TimecodeLock)
snd_iprintf(buffer, "ADAT Sync: %s\n", x ? "Sync" : "Lock");
@@ -3524,11 +3545,11 @@
snd_iprintf(buffer, "ADAT Sync: No Lock\n");
snd_iprintf(buffer, "\n");
-
+
/* Informations about H9632 specific controls */
if (hdsp->io_type == H9632) {
char *tmp;
-
+
switch (hdsp_ad_gain(hdsp)) {
case 0:
tmp = "-10 dBV";
@@ -3554,7 +3575,7 @@
break;
}
snd_iprintf(buffer, "DA Gain : %s\n", tmp);
-
+
switch (hdsp_phone_gain(hdsp)) {
case 0:
tmp = "0 dB";
@@ -3568,8 +3589,8 @@
}
snd_iprintf(buffer, "Phones Gain : %s\n", tmp);
- snd_iprintf(buffer, "XLR Breakout Cable : %s\n", hdsp_xlr_breakout_cable(hdsp) ? "yes" : "no");
-
+ snd_iprintf(buffer, "XLR Breakout Cable : %s\n", hdsp_xlr_breakout_cable(hdsp) ? "yes" : "no");
+
if (hdsp->control_register & HDSP_AnalogExtensionBoard)
snd_iprintf(buffer, "AEB : on (ADAT1 internal)\n");
else
@@ -3632,18 +3653,18 @@
/* set defaults:
- SPDIF Input via Coax
+ SPDIF Input via Coax
Master clock mode
maximum latency (7 => 2^7 = 8192 samples, 64Kbyte buffer,
which implies 2 4096 sample, 32Kbyte periods).
- Enable line out.
+ Enable line out.
*/
- hdsp->control_register = HDSP_ClockModeMaster |
- HDSP_SPDIFInputCoaxial |
- hdsp_encode_latency(7) |
+ hdsp->control_register = HDSP_ClockModeMaster |
+ HDSP_SPDIFInputCoaxial |
+ hdsp_encode_latency(7) |
HDSP_LineOut;
-
+
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
@@ -3661,7 +3682,7 @@
hdsp_compute_period_size(hdsp);
/* silence everything */
-
+
for (i = 0; i < HDSP_MATRIX_MIXER_SIZE; ++i)
hdsp->mixer_matrix[i] = MINUS_INFINITY_GAIN;
@@ -3669,7 +3690,7 @@
if (hdsp_write_gain (hdsp, i, MINUS_INFINITY_GAIN))
return -EIO;
}
-
+
/* H9632 specific defaults */
if (hdsp->io_type == H9632) {
hdsp->control_register |= (HDSP_DAGainPlus4dBu | HDSP_ADGainPlus4dBu | HDSP_PhoneGain0dB);
@@ -3687,12 +3708,12 @@
static void hdsp_midi_tasklet(unsigned long arg)
{
struct hdsp *hdsp = (struct hdsp *)arg;
-
+
if (hdsp->midi[0].pending)
snd_hdsp_midi_input_read (&hdsp->midi[0]);
if (hdsp->midi[1].pending)
snd_hdsp_midi_input_read (&hdsp->midi[1]);
-}
+}
static irqreturn_t snd_hdsp_interrupt(int irq, void *dev_id)
{
@@ -3704,7 +3725,7 @@
unsigned int midi0status;
unsigned int midi1status;
int schedule = 0;
-
+
status = hdsp_read(hdsp, HDSP_statusRegister);
audio = status & HDSP_audioIRQPending;
@@ -3718,15 +3739,18 @@
midi0status = hdsp_read (hdsp, HDSP_midiStatusIn0) & 0xff;
midi1status = hdsp_read (hdsp, HDSP_midiStatusIn1) & 0xff;
-
+
+ if (!(hdsp->state & HDSP_InitializationComplete))
+ return IRQ_HANDLED;
+
if (audio) {
if (hdsp->capture_substream)
snd_pcm_period_elapsed(hdsp->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
-
+
if (hdsp->playback_substream)
snd_pcm_period_elapsed(hdsp->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream);
}
-
+
if (midi0 && midi0status) {
if (hdsp->use_midi_tasklet) {
/* we disable interrupts for this input until processing is done */
@@ -3769,10 +3793,10 @@
if (snd_BUG_ON(channel < 0 || channel >= hdsp->max_channels))
return NULL;
-
+
if ((mapped_channel = hdsp->channel_map[channel]) < 0)
return NULL;
-
+
if (stream == SNDRV_PCM_STREAM_CAPTURE)
return hdsp->capture_buffer + (mapped_channel * HDSP_CHANNEL_BUFFER_BYTES);
else
@@ -3965,7 +3989,7 @@
struct hdsp *hdsp = snd_pcm_substream_chip(substream);
struct snd_pcm_substream *other;
int running;
-
+
if (hdsp_check_for_iobox (hdsp))
return -EIO;
@@ -4059,10 +4083,10 @@
.formats = SNDRV_PCM_FMTBIT_S32_LE,
#endif
.rates = (SNDRV_PCM_RATE_32000 |
- SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_64000 |
- SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_64000 |
+ SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000),
.rate_min = 32000,
.rate_max = 96000,
@@ -4088,10 +4112,10 @@
.formats = SNDRV_PCM_FMTBIT_S32_LE,
#endif
.rates = (SNDRV_PCM_RATE_32000 |
- SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_64000 |
- SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_64000 |
+ SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000),
.rate_min = 32000,
.rate_max = 96000,
@@ -4170,7 +4194,7 @@
.max = hdsp->qs_in_channels,
.integer = 1,
};
- return snd_interval_refine(c, &t);
+ return snd_interval_refine(c, &t);
} else if (r->min > 48000 && r->max <= 96000) {
struct snd_interval t = {
.min = hdsp->ds_in_channels,
@@ -4201,7 +4225,7 @@
.max = hdsp->qs_out_channels,
.integer = 1,
};
- return snd_interval_refine(c, &t);
+ return snd_interval_refine(c, &t);
} else if (r->min > 48000 && r->max <= 96000) {
struct snd_interval t = {
.min = hdsp->ds_out_channels,
@@ -4318,8 +4342,8 @@
if (hdsp->io_type == H9632) {
runtime->hw.channels_min = hdsp->qs_out_channels;
runtime->hw.channels_max = hdsp->ss_out_channels;
- }
-
+ }
+
snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
snd_hdsp_hw_rule_out_channels, hdsp,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
@@ -4413,13 +4437,6 @@
return 0;
}
-static int snd_hdsp_hwdep_dummy_op(struct snd_hwdep *hw, struct file *file)
-{
- /* we have nothing to initialize but the call is required */
- return 0;
-}
-
-
/* helper functions for copying meter values */
static inline int copy_u32_le(void __user *dest, void __iomem *src)
{
@@ -4536,7 +4553,7 @@
hdsp->iobase + HDSP_playbackRmsLevel + i * 8 + 4,
hdsp->iobase + HDSP_playbackRmsLevel + i * 8))
return -EFAULT;
- if (copy_u64_le(&peak_rms->input_rms[i],
+ if (copy_u64_le(&peak_rms->input_rms[i],
hdsp->iobase + HDSP_inputRmsLevel + i * 8 + 4,
hdsp->iobase + HDSP_inputRmsLevel + i * 8))
return -EFAULT;
@@ -4546,7 +4563,7 @@
static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigned int cmd, unsigned long arg)
{
- struct hdsp *hdsp = (struct hdsp *)hw->private_data;
+ struct hdsp *hdsp = (struct hdsp *)hw->private_data;
void __user *argp = (void __user *)arg;
int err;
@@ -4580,7 +4597,7 @@
struct hdsp_config_info info;
unsigned long flags;
int i;
-
+
err = hdsp_check_for_iobox(hdsp);
if (err < 0)
return err;
@@ -4614,7 +4631,7 @@
info.ad_gain = (unsigned char)hdsp_ad_gain(hdsp);
info.phone_gain = (unsigned char)hdsp_phone_gain(hdsp);
info.xlr_breakout_cable = (unsigned char)hdsp_xlr_breakout_cable(hdsp);
-
+
}
if (hdsp->io_type == H9632 || hdsp->io_type == H9652)
info.analog_extension_board = (unsigned char)hdsp_aeb(hdsp);
@@ -4625,7 +4642,7 @@
}
case SNDRV_HDSP_IOCTL_GET_9632_AEB: {
struct hdsp_9632_aeb h9632_aeb;
-
+
if (hdsp->io_type != H9632) return -EINVAL;
h9632_aeb.aebi = hdsp->ss_in_channels - H9632_SS_CHANNELS;
h9632_aeb.aebo = hdsp->ss_out_channels - H9632_SS_CHANNELS;
@@ -4636,7 +4653,7 @@
case SNDRV_HDSP_IOCTL_GET_VERSION: {
struct hdsp_version hdsp_version;
int err;
-
+
if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return -EINVAL;
if (hdsp->io_type == Undefined) {
if ((err = hdsp_get_iobox_version(hdsp)) < 0)
@@ -4652,7 +4669,7 @@
struct hdsp_firmware __user *firmware;
u32 __user *firmware_data;
int err;
-
+
if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return -EINVAL;
/* SNDRV_HDSP_IOCTL_GET_VERSION must have been called */
if (hdsp->io_type == Undefined) return -EINVAL;
@@ -4665,25 +4682,25 @@
if (get_user(firmware_data, &firmware->firmware_data))
return -EFAULT;
-
+
if (hdsp_check_for_iobox (hdsp))
return -EIO;
if (copy_from_user(hdsp->firmware_cache, firmware_data, sizeof(hdsp->firmware_cache)) != 0)
return -EFAULT;
-
+
hdsp->state |= HDSP_FirmwareCached;
if ((err = snd_hdsp_load_firmware_from_cache(hdsp)) < 0)
return err;
-
+
if (!(hdsp->state & HDSP_InitializationComplete)) {
if ((err = snd_hdsp_enable_io(hdsp)) < 0)
return err;
-
- snd_hdsp_initialize_channels(hdsp);
+
+ snd_hdsp_initialize_channels(hdsp);
snd_hdsp_initialize_midi_flush(hdsp);
-
+
if ((err = snd_hdsp_create_alsa_devices(hdsp->card, hdsp)) < 0) {
snd_printk(KERN_ERR "Hammerfall-DSP: error creating alsa devices\n");
return err;
@@ -4730,18 +4747,16 @@
{
struct snd_hwdep *hw;
int err;
-
+
if ((err = snd_hwdep_new(card, "HDSP hwdep", 0, &hw)) < 0)
return err;
-
+
hdsp->hwdep = hw;
hw->private_data = hdsp;
strcpy(hw->name, "HDSP hwdep interface");
- hw->ops.open = snd_hdsp_hwdep_dummy_op;
hw->ops.ioctl = snd_hdsp_hwdep_ioctl;
- hw->ops.release = snd_hdsp_hwdep_dummy_op;
-
+
return 0;
}
@@ -4774,24 +4789,24 @@
static int snd_hdsp_enable_io (struct hdsp *hdsp)
{
int i;
-
+
if (hdsp_fifo_wait (hdsp, 0, 100)) {
snd_printk(KERN_ERR "Hammerfall-DSP: enable_io fifo_wait failed\n");
return -EIO;
}
-
+
for (i = 0; i < hdsp->max_channels; ++i) {
hdsp_write (hdsp, HDSP_inputEnable + (4 * i), 1);
hdsp_write (hdsp, HDSP_outputEnable + (4 * i), 1);
}
-
+
return 0;
}
static void snd_hdsp_initialize_channels(struct hdsp *hdsp)
{
int status, aebi_channels, aebo_channels;
-
+
switch (hdsp->io_type) {
case Digiface:
hdsp->card_name = "RME Hammerfall DSP + Digiface";
@@ -4804,7 +4819,7 @@
hdsp->ss_in_channels = hdsp->ss_out_channels = H9652_SS_CHANNELS;
hdsp->ds_in_channels = hdsp->ds_out_channels = H9652_DS_CHANNELS;
break;
-
+
case H9632:
status = hdsp_read(hdsp, HDSP_statusRegister);
/* HDSP_AEBx bits are low when AEB are connected */
@@ -4824,7 +4839,7 @@
hdsp->ss_in_channels = hdsp->ss_out_channels = MULTIFACE_SS_CHANNELS;
hdsp->ds_in_channels = hdsp->ds_out_channels = MULTIFACE_DS_CHANNELS;
break;
-
+
default:
/* should never get here */
break;
@@ -4840,12 +4855,12 @@
static int snd_hdsp_create_alsa_devices(struct snd_card *card, struct hdsp *hdsp)
{
int err;
-
+
if ((err = snd_hdsp_create_pcm(card, hdsp)) < 0) {
snd_printk(KERN_ERR "Hammerfall-DSP: Error creating pcm interface\n");
return err;
}
-
+
if ((err = snd_hdsp_create_midi(card, hdsp, 0)) < 0) {
snd_printk(KERN_ERR "Hammerfall-DSP: Error creating first midi interface\n");
@@ -4876,19 +4891,19 @@
snd_printk(KERN_ERR "Hammerfall-DSP: Error setting default values\n");
return err;
}
-
+
if (!(hdsp->state & HDSP_InitializationComplete)) {
strcpy(card->shortname, "Hammerfall DSP");
- sprintf(card->longname, "%s at 0x%lx, irq %d", hdsp->card_name,
+ sprintf(card->longname, "%s at 0x%lx, irq %d", hdsp->card_name,
hdsp->port, hdsp->irq);
-
+
if ((err = snd_card_register(card)) < 0) {
snd_printk(KERN_ERR "Hammerfall-DSP: error registering card\n");
return err;
}
hdsp->state |= HDSP_InitializationComplete;
}
-
+
return 0;
}
@@ -4899,7 +4914,7 @@
const char *fwfile;
const struct firmware *fw;
int err;
-
+
if (hdsp->io_type == H9652 || hdsp->io_type == H9632)
return 0;
if (hdsp->io_type == Undefined) {
@@ -4908,7 +4923,7 @@
if (hdsp->io_type == H9652 || hdsp->io_type == H9632)
return 0;
}
-
+
/* caution: max length of firmware filename is 30! */
switch (hdsp->io_type) {
case Multiface:
@@ -4942,12 +4957,12 @@
memcpy(hdsp->firmware_cache, fw->data, sizeof(hdsp->firmware_cache));
release_firmware(fw);
-
+
hdsp->state |= HDSP_FirmwareCached;
if ((err = snd_hdsp_load_firmware_from_cache(hdsp)) < 0)
return err;
-
+
if (!(hdsp->state & HDSP_InitializationComplete)) {
if ((err = snd_hdsp_enable_io(hdsp)) < 0)
return err;
@@ -4994,14 +5009,14 @@
hdsp->max_channels = 26;
hdsp->card = card;
-
+
spin_lock_init(&hdsp->lock);
tasklet_init(&hdsp->midi_tasklet, hdsp_midi_tasklet, (unsigned long)hdsp);
-
+
pci_read_config_word(hdsp->pci, PCI_CLASS_REVISION, &hdsp->firmware_rev);
hdsp->firmware_rev &= 0xff;
-
+
/* From Martin Bjoernsen :
"It is important that the card's latency timer register in
the PCI configuration space is set to a value much larger
@@ -5010,7 +5025,7 @@
to its maximum 255 to avoid problems with some computers."
*/
pci_write_config_byte(hdsp->pci, PCI_LATENCY_TIMER, 0xFF);
-
+
strcpy(card->driver, "H-DSP");
strcpy(card->mixername, "Xilinx FPGA");
@@ -5024,7 +5039,7 @@
} else {
hdsp->card_name = "RME HDSP 9632";
hdsp->max_channels = 16;
- is_9632 = 1;
+ is_9632 = 1;
}
if ((err = pci_enable_device(pci)) < 0)
@@ -5053,12 +5068,12 @@
if ((err = snd_hdsp_initialize_memory(hdsp)) < 0)
return err;
-
- if (!is_9652 && !is_9632) {
- /* we wait 2 seconds to let freshly inserted cardbus cards do their hardware init */
- ssleep(2);
- err = hdsp_check_for_iobox(hdsp);
+ if (!is_9652 && !is_9632) {
+ /* we wait a maximum of 10 seconds to let freshly
+ * inserted cardbus cards do their hardware init */
+ err = hdsp_wait_for_iobox(hdsp, 1000, 10);
+
if (err < 0)
return err;
@@ -5080,35 +5095,35 @@
return err;
return 0;
} else {
- snd_printk(KERN_INFO "Hammerfall-DSP: Firmware already present, initializing card.\n");
+ snd_printk(KERN_INFO "Hammerfall-DSP: Firmware already present, initializing card.\n");
if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
hdsp->io_type = Multiface;
- else
+ else
hdsp->io_type = Digiface;
}
}
-
+
if ((err = snd_hdsp_enable_io(hdsp)) != 0)
return err;
-
+
if (is_9652)
hdsp->io_type = H9652;
-
+
if (is_9632)
hdsp->io_type = H9632;
if ((err = snd_hdsp_create_hwdep(card, hdsp)) < 0)
return err;
-
+
snd_hdsp_initialize_channels(hdsp);
snd_hdsp_initialize_midi_flush(hdsp);
- hdsp->state |= HDSP_FirmwareLoaded;
+ hdsp->state |= HDSP_FirmwareLoaded;
if ((err = snd_hdsp_create_alsa_devices(card, hdsp)) < 0)
return err;
- return 0;
+ return 0;
}
static int snd_hdsp_free(struct hdsp *hdsp)
@@ -5124,13 +5139,13 @@
free_irq(hdsp->irq, (void *)hdsp);
snd_hdsp_free_buffers(hdsp);
-
+
if (hdsp->iobase)
iounmap(hdsp->iobase);
if (hdsp->port)
pci_release_regions(hdsp->pci);
-
+
pci_disable_device(hdsp->pci);
return 0;
}
@@ -5158,8 +5173,10 @@
return -ENOENT;
}
- if (!(card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(struct hdsp))))
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct hdsp), &card);
+ if (err < 0)
+ return err;
hdsp = (struct hdsp *) card->private_data;
card->private_free = snd_hdsp_card_free;
@@ -5173,7 +5190,7 @@
}
strcpy(card->shortname, "Hammerfall DSP");
- sprintf(card->longname, "%s at 0x%lx, irq %d", hdsp->card_name,
+ sprintf(card->longname, "%s at 0x%lx, irq %d", hdsp->card_name,
hdsp->port, hdsp->irq);
if ((err = snd_card_register(card)) < 0) {
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index 71231cf..bac2dc0 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -4100,13 +4100,6 @@
return 0;
}
-static int snd_hdspm_hwdep_dummy_op(struct snd_hwdep * hw, struct file *file)
-{
- /* we have nothing to initialize but the call is required */
- return 0;
-}
-
-
static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file,
unsigned int cmd, unsigned long arg)
{
@@ -4213,9 +4206,7 @@
hw->private_data = hdspm;
strcpy(hw->name, "HDSPM hwdep interface");
- hw->ops.open = snd_hdspm_hwdep_dummy_op;
hw->ops.ioctl = snd_hdspm_hwdep_ioctl;
- hw->ops.release = snd_hdspm_hwdep_dummy_op;
return 0;
}
@@ -4503,10 +4494,10 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev],
- THIS_MODULE, sizeof(struct hdspm));
- if (!card)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev],
+ THIS_MODULE, sizeof(struct hdspm), &card);
+ if (err < 0)
+ return err;
hdspm = card->private_data;
card->private_free = snd_hdspm_card_free;
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
index 2570907..bc539ab 100644
--- a/sound/pci/rme9652/rme9652.c
+++ b/sound/pci/rme9652/rme9652.c
@@ -2594,11 +2594,11 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_rme9652));
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_rme9652), &card);
- if (!card)
- return -ENOMEM;
+ if (err < 0)
+ return err;
rme9652 = (struct snd_rme9652 *) card->private_data;
card->private_free = snd_rme9652_card_free;
diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c
index df2007e..baf6d8e 100644
--- a/sound/pci/sis7019.c
+++ b/sound/pci/sis7019.c
@@ -1387,9 +1387,8 @@
if (!enable)
goto error_out;
- rc = -ENOMEM;
- card = snd_card_new(index, id, THIS_MODULE, sizeof(*sis));
- if (!card)
+ rc = snd_card_create(index, id, THIS_MODULE, sizeof(*sis), &card);
+ if (rc < 0)
goto error_out;
strcpy(card->driver, "SiS7019");
diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
index cd408b8..d989215 100644
--- a/sound/pci/sonicvibes.c
+++ b/sound/pci/sonicvibes.c
@@ -273,7 +273,8 @@
outl(count, sonic->dmaa_port + SV_DMA_COUNT0);
outb(0x18, sonic->dmaa_port + SV_DMA_MODE);
#if 0
- printk("program dmaa: addr = 0x%x, paddr = 0x%x\n", addr, inl(sonic->dmaa_port + SV_DMA_ADDR0));
+ printk(KERN_DEBUG "program dmaa: addr = 0x%x, paddr = 0x%x\n",
+ addr, inl(sonic->dmaa_port + SV_DMA_ADDR0));
#endif
}
@@ -288,7 +289,8 @@
outl(count, sonic->dmac_port + SV_DMA_COUNT0);
outb(0x14, sonic->dmac_port + SV_DMA_MODE);
#if 0
- printk("program dmac: addr = 0x%x, paddr = 0x%x\n", addr, inl(sonic->dmac_port + SV_DMA_ADDR0));
+ printk(KERN_DEBUG "program dmac: addr = 0x%x, paddr = 0x%x\n",
+ addr, inl(sonic->dmac_port + SV_DMA_ADDR0));
#endif
}
@@ -355,71 +357,104 @@
#if 0
static void snd_sonicvibes_debug(struct sonicvibes * sonic)
{
- printk("SV REGS: INDEX = 0x%02x ", inb(SV_REG(sonic, INDEX)));
+ printk(KERN_DEBUG
+ "SV REGS: INDEX = 0x%02x ", inb(SV_REG(sonic, INDEX)));
printk(" STATUS = 0x%02x\n", inb(SV_REG(sonic, STATUS)));
- printk(" 0x00: left input = 0x%02x ", snd_sonicvibes_in(sonic, 0x00));
+ printk(KERN_DEBUG
+ " 0x00: left input = 0x%02x ", snd_sonicvibes_in(sonic, 0x00));
printk(" 0x20: synth rate low = 0x%02x\n", snd_sonicvibes_in(sonic, 0x20));
- printk(" 0x01: right input = 0x%02x ", snd_sonicvibes_in(sonic, 0x01));
+ printk(KERN_DEBUG
+ " 0x01: right input = 0x%02x ", snd_sonicvibes_in(sonic, 0x01));
printk(" 0x21: synth rate high = 0x%02x\n", snd_sonicvibes_in(sonic, 0x21));
- printk(" 0x02: left AUX1 = 0x%02x ", snd_sonicvibes_in(sonic, 0x02));
+ printk(KERN_DEBUG
+ " 0x02: left AUX1 = 0x%02x ", snd_sonicvibes_in(sonic, 0x02));
printk(" 0x22: ADC clock = 0x%02x\n", snd_sonicvibes_in(sonic, 0x22));
- printk(" 0x03: right AUX1 = 0x%02x ", snd_sonicvibes_in(sonic, 0x03));
+ printk(KERN_DEBUG
+ " 0x03: right AUX1 = 0x%02x ", snd_sonicvibes_in(sonic, 0x03));
printk(" 0x23: ADC alt rate = 0x%02x\n", snd_sonicvibes_in(sonic, 0x23));
- printk(" 0x04: left CD = 0x%02x ", snd_sonicvibes_in(sonic, 0x04));
+ printk(KERN_DEBUG
+ " 0x04: left CD = 0x%02x ", snd_sonicvibes_in(sonic, 0x04));
printk(" 0x24: ADC pll M = 0x%02x\n", snd_sonicvibes_in(sonic, 0x24));
- printk(" 0x05: right CD = 0x%02x ", snd_sonicvibes_in(sonic, 0x05));
+ printk(KERN_DEBUG
+ " 0x05: right CD = 0x%02x ", snd_sonicvibes_in(sonic, 0x05));
printk(" 0x25: ADC pll N = 0x%02x\n", snd_sonicvibes_in(sonic, 0x25));
- printk(" 0x06: left line = 0x%02x ", snd_sonicvibes_in(sonic, 0x06));
+ printk(KERN_DEBUG
+ " 0x06: left line = 0x%02x ", snd_sonicvibes_in(sonic, 0x06));
printk(" 0x26: Synth pll M = 0x%02x\n", snd_sonicvibes_in(sonic, 0x26));
- printk(" 0x07: right line = 0x%02x ", snd_sonicvibes_in(sonic, 0x07));
+ printk(KERN_DEBUG
+ " 0x07: right line = 0x%02x ", snd_sonicvibes_in(sonic, 0x07));
printk(" 0x27: Synth pll N = 0x%02x\n", snd_sonicvibes_in(sonic, 0x27));
- printk(" 0x08: MIC = 0x%02x ", snd_sonicvibes_in(sonic, 0x08));
+ printk(KERN_DEBUG
+ " 0x08: MIC = 0x%02x ", snd_sonicvibes_in(sonic, 0x08));
printk(" 0x28: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x28));
- printk(" 0x09: Game port = 0x%02x ", snd_sonicvibes_in(sonic, 0x09));
+ printk(KERN_DEBUG
+ " 0x09: Game port = 0x%02x ", snd_sonicvibes_in(sonic, 0x09));
printk(" 0x29: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x29));
- printk(" 0x0a: left synth = 0x%02x ", snd_sonicvibes_in(sonic, 0x0a));
+ printk(KERN_DEBUG
+ " 0x0a: left synth = 0x%02x ", snd_sonicvibes_in(sonic, 0x0a));
printk(" 0x2a: MPU401 = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2a));
- printk(" 0x0b: right synth = 0x%02x ", snd_sonicvibes_in(sonic, 0x0b));
+ printk(KERN_DEBUG
+ " 0x0b: right synth = 0x%02x ", snd_sonicvibes_in(sonic, 0x0b));
printk(" 0x2b: drive ctrl = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2b));
- printk(" 0x0c: left AUX2 = 0x%02x ", snd_sonicvibes_in(sonic, 0x0c));
+ printk(KERN_DEBUG
+ " 0x0c: left AUX2 = 0x%02x ", snd_sonicvibes_in(sonic, 0x0c));
printk(" 0x2c: SRS space = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2c));
- printk(" 0x0d: right AUX2 = 0x%02x ", snd_sonicvibes_in(sonic, 0x0d));
+ printk(KERN_DEBUG
+ " 0x0d: right AUX2 = 0x%02x ", snd_sonicvibes_in(sonic, 0x0d));
printk(" 0x2d: SRS center = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2d));
- printk(" 0x0e: left analog = 0x%02x ", snd_sonicvibes_in(sonic, 0x0e));
+ printk(KERN_DEBUG
+ " 0x0e: left analog = 0x%02x ", snd_sonicvibes_in(sonic, 0x0e));
printk(" 0x2e: wave source = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2e));
- printk(" 0x0f: right analog = 0x%02x ", snd_sonicvibes_in(sonic, 0x0f));
+ printk(KERN_DEBUG
+ " 0x0f: right analog = 0x%02x ", snd_sonicvibes_in(sonic, 0x0f));
printk(" 0x2f: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x2f));
- printk(" 0x10: left PCM = 0x%02x ", snd_sonicvibes_in(sonic, 0x10));
+ printk(KERN_DEBUG
+ " 0x10: left PCM = 0x%02x ", snd_sonicvibes_in(sonic, 0x10));
printk(" 0x30: analog power = 0x%02x\n", snd_sonicvibes_in(sonic, 0x30));
- printk(" 0x11: right PCM = 0x%02x ", snd_sonicvibes_in(sonic, 0x11));
+ printk(KERN_DEBUG
+ " 0x11: right PCM = 0x%02x ", snd_sonicvibes_in(sonic, 0x11));
printk(" 0x31: analog power = 0x%02x\n", snd_sonicvibes_in(sonic, 0x31));
- printk(" 0x12: DMA data format = 0x%02x ", snd_sonicvibes_in(sonic, 0x12));
+ printk(KERN_DEBUG
+ " 0x12: DMA data format = 0x%02x ", snd_sonicvibes_in(sonic, 0x12));
printk(" 0x32: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x32));
- printk(" 0x13: P/C enable = 0x%02x ", snd_sonicvibes_in(sonic, 0x13));
+ printk(KERN_DEBUG
+ " 0x13: P/C enable = 0x%02x ", snd_sonicvibes_in(sonic, 0x13));
printk(" 0x33: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x33));
- printk(" 0x14: U/D button = 0x%02x ", snd_sonicvibes_in(sonic, 0x14));
+ printk(KERN_DEBUG
+ " 0x14: U/D button = 0x%02x ", snd_sonicvibes_in(sonic, 0x14));
printk(" 0x34: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x34));
- printk(" 0x15: revision = 0x%02x ", snd_sonicvibes_in(sonic, 0x15));
+ printk(KERN_DEBUG
+ " 0x15: revision = 0x%02x ", snd_sonicvibes_in(sonic, 0x15));
printk(" 0x35: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x35));
- printk(" 0x16: ADC output ctrl = 0x%02x ", snd_sonicvibes_in(sonic, 0x16));
+ printk(KERN_DEBUG
+ " 0x16: ADC output ctrl = 0x%02x ", snd_sonicvibes_in(sonic, 0x16));
printk(" 0x36: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x36));
- printk(" 0x17: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x17));
+ printk(KERN_DEBUG
+ " 0x17: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x17));
printk(" 0x37: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x37));
- printk(" 0x18: DMA A upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x18));
+ printk(KERN_DEBUG
+ " 0x18: DMA A upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x18));
printk(" 0x38: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x38));
- printk(" 0x19: DMA A lower cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x19));
+ printk(KERN_DEBUG
+ " 0x19: DMA A lower cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x19));
printk(" 0x39: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x39));
- printk(" 0x1a: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x1a));
+ printk(KERN_DEBUG
+ " 0x1a: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x1a));
printk(" 0x3a: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3a));
- printk(" 0x1b: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x1b));
+ printk(KERN_DEBUG
+ " 0x1b: --- = 0x%02x ", snd_sonicvibes_in(sonic, 0x1b));
printk(" 0x3b: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3b));
- printk(" 0x1c: DMA C upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x1c));
+ printk(KERN_DEBUG
+ " 0x1c: DMA C upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x1c));
printk(" 0x3c: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3c));
- printk(" 0x1d: DMA C upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x1d));
+ printk(KERN_DEBUG
+ " 0x1d: DMA C upper cnt = 0x%02x ", snd_sonicvibes_in(sonic, 0x1d));
printk(" 0x3d: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3d));
- printk(" 0x1e: PCM rate low = 0x%02x ", snd_sonicvibes_in(sonic, 0x1e));
+ printk(KERN_DEBUG
+ " 0x1e: PCM rate low = 0x%02x ", snd_sonicvibes_in(sonic, 0x1e));
printk(" 0x3e: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3e));
- printk(" 0x1f: PCM rate high = 0x%02x ", snd_sonicvibes_in(sonic, 0x1f));
+ printk(KERN_DEBUG
+ " 0x1f: PCM rate high = 0x%02x ", snd_sonicvibes_in(sonic, 0x1f));
printk(" 0x3f: --- = 0x%02x\n", snd_sonicvibes_in(sonic, 0x3f));
}
@@ -476,8 +511,8 @@
*res_m = m;
*res_n = n;
#if 0
- printk("metric = %i, xm = %i, xn = %i\n", metric, xm, xn);
- printk("pll: m = 0x%x, r = 0x%x, n = 0x%x\n", reg, m, r, n);
+ printk(KERN_DEBUG "metric = %i, xm = %i, xn = %i\n", metric, xm, xn);
+ printk(KERN_DEBUG "pll: m = 0x%x, r = 0x%x, n = 0x%x\n", reg, m, r, n);
#endif
}
@@ -1423,9 +1458,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
for (idx = 0; idx < 5; idx++) {
if (pci_resource_start(pci, idx) == 0 ||
!(pci_resource_flags(pci, idx) & IORESOURCE_IO)) {
diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c
index d94b16f..21cef97 100644
--- a/sound/pci/trident/trident.c
+++ b/sound/pci/trident/trident.c
@@ -89,9 +89,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
if ((err = snd_trident_create(card, pci,
pcm_channels[dev],
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
index c612b43..a9da9c1 100644
--- a/sound/pci/trident/trident_main.c
+++ b/sound/pci/trident/trident_main.c
@@ -68,40 +68,40 @@
{
unsigned int val, tmp;
- printk("Trident voice %i:\n", voice);
+ printk(KERN_DEBUG "Trident voice %i:\n", voice);
outb(voice, TRID_REG(trident, T4D_LFO_GC_CIR));
val = inl(TRID_REG(trident, CH_LBA));
- printk("LBA: 0x%x\n", val);
+ printk(KERN_DEBUG "LBA: 0x%x\n", val);
val = inl(TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
- printk("GVSel: %i\n", val >> 31);
- printk("Pan: 0x%x\n", (val >> 24) & 0x7f);
- printk("Vol: 0x%x\n", (val >> 16) & 0xff);
- printk("CTRL: 0x%x\n", (val >> 12) & 0x0f);
- printk("EC: 0x%x\n", val & 0x0fff);
+ printk(KERN_DEBUG "GVSel: %i\n", val >> 31);
+ printk(KERN_DEBUG "Pan: 0x%x\n", (val >> 24) & 0x7f);
+ printk(KERN_DEBUG "Vol: 0x%x\n", (val >> 16) & 0xff);
+ printk(KERN_DEBUG "CTRL: 0x%x\n", (val >> 12) & 0x0f);
+ printk(KERN_DEBUG "EC: 0x%x\n", val & 0x0fff);
if (trident->device != TRIDENT_DEVICE_ID_NX) {
val = inl(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS));
- printk("CSO: 0x%x\n", val >> 16);
+ printk(KERN_DEBUG "CSO: 0x%x\n", val >> 16);
printk("Alpha: 0x%x\n", (val >> 4) & 0x0fff);
- printk("FMS: 0x%x\n", val & 0x0f);
+ printk(KERN_DEBUG "FMS: 0x%x\n", val & 0x0f);
val = inl(TRID_REG(trident, CH_DX_ESO_DELTA));
- printk("ESO: 0x%x\n", val >> 16);
- printk("Delta: 0x%x\n", val & 0xffff);
+ printk(KERN_DEBUG "ESO: 0x%x\n", val >> 16);
+ printk(KERN_DEBUG "Delta: 0x%x\n", val & 0xffff);
val = inl(TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
} else { // TRIDENT_DEVICE_ID_NX
val = inl(TRID_REG(trident, CH_NX_DELTA_CSO));
tmp = (val >> 24) & 0xff;
- printk("CSO: 0x%x\n", val & 0x00ffffff);
+ printk(KERN_DEBUG "CSO: 0x%x\n", val & 0x00ffffff);
val = inl(TRID_REG(trident, CH_NX_DELTA_ESO));
tmp |= (val >> 16) & 0xff00;
- printk("Delta: 0x%x\n", tmp);
- printk("ESO: 0x%x\n", val & 0x00ffffff);
+ printk(KERN_DEBUG "Delta: 0x%x\n", tmp);
+ printk(KERN_DEBUG "ESO: 0x%x\n", val & 0x00ffffff);
val = inl(TRID_REG(trident, CH_NX_ALPHA_FMS_FMC_RVOL_CVOL));
- printk("Alpha: 0x%x\n", val >> 20);
- printk("FMS: 0x%x\n", (val >> 16) & 0x0f);
+ printk(KERN_DEBUG "Alpha: 0x%x\n", val >> 20);
+ printk(KERN_DEBUG "FMS: 0x%x\n", (val >> 16) & 0x0f);
}
- printk("FMC: 0x%x\n", (val >> 14) & 3);
- printk("RVol: 0x%x\n", (val >> 7) & 0x7f);
- printk("CVol: 0x%x\n", val & 0x7f);
+ printk(KERN_DEBUG "FMC: 0x%x\n", (val >> 14) & 3);
+ printk(KERN_DEBUG "RVol: 0x%x\n", (val >> 7) & 0x7f);
+ printk(KERN_DEBUG "CVol: 0x%x\n", val & 0x7f);
}
#endif
@@ -496,12 +496,17 @@
outl(regs[4], TRID_REG(trident, CH_START + 16));
#if 0
- printk("written %i channel:\n", voice->number);
- printk(" regs[0] = 0x%x/0x%x\n", regs[0], inl(TRID_REG(trident, CH_START + 0)));
- printk(" regs[1] = 0x%x/0x%x\n", regs[1], inl(TRID_REG(trident, CH_START + 4)));
- printk(" regs[2] = 0x%x/0x%x\n", regs[2], inl(TRID_REG(trident, CH_START + 8)));
- printk(" regs[3] = 0x%x/0x%x\n", regs[3], inl(TRID_REG(trident, CH_START + 12)));
- printk(" regs[4] = 0x%x/0x%x\n", regs[4], inl(TRID_REG(trident, CH_START + 16)));
+ printk(KERN_DEBUG "written %i channel:\n", voice->number);
+ printk(KERN_DEBUG " regs[0] = 0x%x/0x%x\n",
+ regs[0], inl(TRID_REG(trident, CH_START + 0)));
+ printk(KERN_DEBUG " regs[1] = 0x%x/0x%x\n",
+ regs[1], inl(TRID_REG(trident, CH_START + 4)));
+ printk(KERN_DEBUG " regs[2] = 0x%x/0x%x\n",
+ regs[2], inl(TRID_REG(trident, CH_START + 8)));
+ printk(KERN_DEBUG " regs[3] = 0x%x/0x%x\n",
+ regs[3], inl(TRID_REG(trident, CH_START + 12)));
+ printk(KERN_DEBUG " regs[4] = 0x%x/0x%x\n",
+ regs[4], inl(TRID_REG(trident, CH_START + 16)));
#endif
}
@@ -583,7 +588,7 @@
outb(voice->Vol >> 2, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 2));
break;
case TRIDENT_DEVICE_ID_SI7018:
- // printk("voice->Vol = 0x%x\n", voice->Vol);
+ /* printk(KERN_DEBUG "voice->Vol = 0x%x\n", voice->Vol); */
outw((voice->CTRL << 12) | voice->Vol,
TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
break;
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index 1aafe95..809b233 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -466,7 +466,10 @@
flag = VIA_TBL_BIT_FLAG; /* period boundary */
} else
flag = 0; /* period continues to the next */
- // printk("via: tbl %d: at %d size %d (rest %d)\n", idx, ofs, r, rest);
+ /*
+ printk(KERN_DEBUG "via: tbl %d: at %d size %d "
+ "(rest %d)\n", idx, ofs, r, rest);
+ */
((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag);
dev->idx_table[idx].offset = ofs;
dev->idx_table[idx].size = r;
@@ -2360,14 +2363,14 @@
SND_PCI_QUIRK(0x1019, 0x0996, "ESC Mobo", VIA_DXS_48K),
SND_PCI_QUIRK(0x1019, 0x0a81, "ECS K7VTA3 v8.0", VIA_DXS_NO_VRA),
SND_PCI_QUIRK(0x1019, 0x0a85, "ECS L7VMM2", VIA_DXS_NO_VRA),
- SND_PCI_QUIRK(0x1019, 0, "ESC K8", VIA_DXS_SRC),
+ SND_PCI_QUIRK_VENDOR(0x1019, "ESC K8", VIA_DXS_SRC),
SND_PCI_QUIRK(0x1019, 0xaa01, "ESC K8T890-A", VIA_DXS_SRC),
SND_PCI_QUIRK(0x1025, 0x0033, "Acer Inspire 1353LM", VIA_DXS_NO_VRA),
SND_PCI_QUIRK(0x1025, 0x0046, "Acer Aspire 1524 WLMi", VIA_DXS_SRC),
- SND_PCI_QUIRK(0x1043, 0, "ASUS A7/A8", VIA_DXS_NO_VRA),
- SND_PCI_QUIRK(0x1071, 0, "Diverse Notebook", VIA_DXS_NO_VRA),
+ SND_PCI_QUIRK_VENDOR(0x1043, "ASUS A7/A8", VIA_DXS_NO_VRA),
+ SND_PCI_QUIRK_VENDOR(0x1071, "Diverse Notebook", VIA_DXS_NO_VRA),
SND_PCI_QUIRK(0x10cf, 0x118e, "FSC Laptop", VIA_DXS_ENABLE),
- SND_PCI_QUIRK(0x1106, 0, "ASRock", VIA_DXS_SRC),
+ SND_PCI_QUIRK_VENDOR(0x1106, "ASRock", VIA_DXS_SRC),
SND_PCI_QUIRK(0x1297, 0xa231, "Shuttle AK31v2", VIA_DXS_SRC),
SND_PCI_QUIRK(0x1297, 0xa232, "Shuttle", VIA_DXS_SRC),
SND_PCI_QUIRK(0x1297, 0xc160, "Shuttle Sk41G", VIA_DXS_SRC),
@@ -2375,7 +2378,7 @@
SND_PCI_QUIRK(0x1462, 0x3800, "MSI KT266", VIA_DXS_ENABLE),
SND_PCI_QUIRK(0x1462, 0x7120, "MSI KT4V", VIA_DXS_ENABLE),
SND_PCI_QUIRK(0x1462, 0x7142, "MSI K8MM-V", VIA_DXS_ENABLE),
- SND_PCI_QUIRK(0x1462, 0, "MSI Mobo", VIA_DXS_SRC),
+ SND_PCI_QUIRK_VENDOR(0x1462, "MSI Mobo", VIA_DXS_SRC),
SND_PCI_QUIRK(0x147b, 0x1401, "ABIT KD7(-RAID)", VIA_DXS_ENABLE),
SND_PCI_QUIRK(0x147b, 0x1411, "ABIT VA-20", VIA_DXS_ENABLE),
SND_PCI_QUIRK(0x147b, 0x1413, "ABIT KV8 Pro", VIA_DXS_ENABLE),
@@ -2389,11 +2392,11 @@
SND_PCI_QUIRK(0x161f, 0x2032, "m680x machines", VIA_DXS_48K),
SND_PCI_QUIRK(0x1631, 0xe004, "PB EasyNote 3174", VIA_DXS_ENABLE),
SND_PCI_QUIRK(0x1695, 0x3005, "EPoX EP-8K9A", VIA_DXS_ENABLE),
- SND_PCI_QUIRK(0x1695, 0, "EPoX mobo", VIA_DXS_SRC),
- SND_PCI_QUIRK(0x16f3, 0, "Jetway K8", VIA_DXS_SRC),
- SND_PCI_QUIRK(0x1734, 0, "FSC Laptop", VIA_DXS_SRC),
+ SND_PCI_QUIRK_VENDOR(0x1695, "EPoX mobo", VIA_DXS_SRC),
+ SND_PCI_QUIRK_VENDOR(0x16f3, "Jetway K8", VIA_DXS_SRC),
+ SND_PCI_QUIRK_VENDOR(0x1734, "FSC Laptop", VIA_DXS_SRC),
SND_PCI_QUIRK(0x1849, 0x3059, "ASRock K7VM2", VIA_DXS_NO_VRA),
- SND_PCI_QUIRK(0x1849, 0, "ASRock mobo", VIA_DXS_SRC),
+ SND_PCI_QUIRK_VENDOR(0x1849, "ASRock mobo", VIA_DXS_SRC),
SND_PCI_QUIRK(0x1919, 0x200a, "Soltek SL-K8", VIA_DXS_NO_VRA),
SND_PCI_QUIRK(0x4005, 0x4710, "MSI K7T266", VIA_DXS_SRC),
{ } /* terminator */
@@ -2433,9 +2436,9 @@
unsigned int i;
int err;
- card = snd_card_new(index, id, THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
card_type = pci_id->driver_data;
switch (card_type) {
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index 5bd79d2..0d54e35 100644
--- a/sound/pci/via82xx_modem.c
+++ b/sound/pci/via82xx_modem.c
@@ -328,7 +328,10 @@
flag = VIA_TBL_BIT_FLAG; /* period boundary */
} else
flag = 0; /* period continues to the next */
- // printk("via: tbl %d: at %d size %d (rest %d)\n", idx, ofs, r, rest);
+ /*
+ printk(KERN_DEBUG "via: tbl %d: at %d size %d "
+ "(rest %d)\n", idx, ofs, r, rest);
+ */
((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag);
dev->idx_table[idx].offset = ofs;
dev->idx_table[idx].size = r;
@@ -1167,9 +1170,9 @@
unsigned int i;
int err;
- card = snd_card_new(index, id, THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
card_type = pci_id->driver_data;
switch (card_type) {
diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c
index acc352f..fc9136c 100644
--- a/sound/pci/vx222/vx222.c
+++ b/sound/pci/vx222/vx222.c
@@ -204,9 +204,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
switch ((int)pci_id->driver_data) {
case VX_PCI_VX222_OLD:
diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c
index 7e87f39..c0efe44 100644
--- a/sound/pci/vx222/vx222_ops.c
+++ b/sound/pci/vx222/vx222_ops.c
@@ -107,7 +107,9 @@
static void vx2_outb(struct vx_core *chip, int offset, unsigned char val)
{
outb(val, vx2_reg_addr(chip, offset));
- //printk("outb: %x -> %x\n", val, vx2_reg_addr(chip, offset));
+ /*
+ printk(KERN_DEBUG "outb: %x -> %x\n", val, vx2_reg_addr(chip, offset));
+ */
}
/**
@@ -126,7 +128,9 @@
*/
static void vx2_outl(struct vx_core *chip, int offset, unsigned int val)
{
- // printk("outl: %x -> %x\n", val, vx2_reg_addr(chip, offset));
+ /*
+ printk(KERN_DEBUG "outl: %x -> %x\n", val, vx2_reg_addr(chip, offset));
+ */
outl(val, vx2_reg_addr(chip, offset));
}
diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c
index 2631a55..4af6666 100644
--- a/sound/pci/ymfpci/ymfpci.c
+++ b/sound/pci/ymfpci/ymfpci.c
@@ -187,9 +187,9 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
switch (pci_id->device) {
case 0x0004: str = "YMF724"; model = "DS-1"; break;
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 90d0d62..2f09252 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -318,7 +318,12 @@
ypcm->period_pos += delta;
ypcm->last_pos = pos;
if (ypcm->period_pos >= ypcm->period_size) {
- // printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start);
+ /*
+ printk(KERN_DEBUG
+ "done - active_bank = 0x%x, start = 0x%x\n",
+ chip->active_bank,
+ voice->bank[chip->active_bank].start);
+ */
ypcm->period_pos %= ypcm->period_size;
spin_unlock(&chip->reg_lock);
snd_pcm_period_elapsed(ypcm->substream);
@@ -366,7 +371,12 @@
ypcm->last_pos = pos;
if (ypcm->period_pos >= ypcm->period_size) {
ypcm->period_pos %= ypcm->period_size;
- // printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start);
+ /*
+ printk(KERN_DEBUG
+ "done - active_bank = 0x%x, start = 0x%x\n",
+ chip->active_bank,
+ voice->bank[chip->active_bank].start);
+ */
spin_unlock(&chip->reg_lock);
snd_pcm_period_elapsed(substream);
spin_lock(&chip->reg_lock);
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c
index 819aaaa..7dea74b 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf.c
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c
@@ -91,7 +91,7 @@
*/
static int snd_pdacf_probe(struct pcmcia_device *link)
{
- int i;
+ int i, err;
struct snd_pdacf *pdacf;
struct snd_card *card;
static struct snd_device_ops ops = {
@@ -112,20 +112,23 @@
return -ENODEV; /* disabled explicitly */
/* ok, create a card instance */
- card = snd_card_new(index[i], id[i], THIS_MODULE, 0);
- if (card == NULL) {
+ err = snd_card_create(index[i], id[i], THIS_MODULE, 0, &card);
+ if (err < 0) {
snd_printk(KERN_ERR "pdacf: cannot create a card instance\n");
- return -ENOMEM;
+ return err;
}
pdacf = snd_pdacf_create(card);
- if (! pdacf)
- return -EIO;
+ if (!pdacf) {
+ snd_card_free(card);
+ return -ENOMEM;
+ }
- if (snd_device_new(card, SNDRV_DEV_LOWLEVEL, pdacf, &ops) < 0) {
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, pdacf, &ops);
+ if (err < 0) {
kfree(pdacf);
snd_card_free(card);
- return -ENODEV;
+ return err;
}
snd_card_set_dev(card, &handle_to_dev(link));
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c
index dfa40b0..5d2afa0 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c
@@ -82,14 +82,21 @@
#if 0
void pdacf_dump(struct snd_pdacf *chip)
{
- printk("PDAUDIOCF DUMP (0x%lx):\n", chip->port);
- printk("WPD : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_WDP));
- printk("RDP : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_RDP));
- printk("TCR : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_TCR));
- printk("SCR : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_SCR));
- printk("ISR : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_ISR));
- printk("IER : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_IER));
- printk("AK_IFR : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_AK_IFR));
+ printk(KERN_DEBUG "PDAUDIOCF DUMP (0x%lx):\n", chip->port);
+ printk(KERN_DEBUG "WPD : 0x%x\n",
+ inw(chip->port + PDAUDIOCF_REG_WDP));
+ printk(KERN_DEBUG "RDP : 0x%x\n",
+ inw(chip->port + PDAUDIOCF_REG_RDP));
+ printk(KERN_DEBUG "TCR : 0x%x\n",
+ inw(chip->port + PDAUDIOCF_REG_TCR));
+ printk(KERN_DEBUG "SCR : 0x%x\n",
+ inw(chip->port + PDAUDIOCF_REG_SCR));
+ printk(KERN_DEBUG "ISR : 0x%x\n",
+ inw(chip->port + PDAUDIOCF_REG_ISR));
+ printk(KERN_DEBUG "IER : 0x%x\n",
+ inw(chip->port + PDAUDIOCF_REG_IER));
+ printk(KERN_DEBUG "AK_IFR : 0x%x\n",
+ inw(chip->port + PDAUDIOCF_REG_AK_IFR));
}
#endif
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
index ea903c8..dcd3220 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
@@ -269,7 +269,7 @@
rdp = inw(chip->port + PDAUDIOCF_REG_RDP);
wdp = inw(chip->port + PDAUDIOCF_REG_WDP);
- // printk("TASKLET: rdp = %x, wdp = %x\n", rdp, wdp);
+ /* printk(KERN_DEBUG "TASKLET: rdp = %x, wdp = %x\n", rdp, wdp); */
size = wdp - rdp;
if (size < 0)
size += 0x10000;
@@ -321,5 +321,5 @@
spin_lock(&chip->reg_lock);
}
spin_unlock(&chip->reg_lock);
- // printk("TASKLET: end\n");
+ /* printk(KERN_DEBUG "TASKLET: end\n"); */
}
diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c
index 706602a..7445cc8 100644
--- a/sound/pcmcia/vx/vxpocket.c
+++ b/sound/pcmcia/vx/vxpocket.c
@@ -130,23 +130,26 @@
/*
* create vxpocket instance
*/
-static struct snd_vxpocket *snd_vxpocket_new(struct snd_card *card, int ibl,
- struct pcmcia_device *link)
+static int snd_vxpocket_new(struct snd_card *card, int ibl,
+ struct pcmcia_device *link,
+ struct snd_vxpocket **chip_ret)
{
struct vx_core *chip;
struct snd_vxpocket *vxp;
static struct snd_device_ops ops = {
.dev_free = snd_vxpocket_dev_free,
};
+ int err;
chip = snd_vx_create(card, &vxpocket_hw, &snd_vxpocket_ops,
sizeof(struct snd_vxpocket) - sizeof(struct vx_core));
- if (! chip)
- return NULL;
+ if (!chip)
+ return -ENOMEM;
- if (snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops) < 0) {
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
kfree(chip);
- return NULL;
+ return err;
}
chip->ibl.size = ibl;
@@ -169,7 +172,8 @@
link->conf.ConfigIndex = 1;
link->conf.Present = PRESENT_OPTION;
- return vxp;
+ *chip_ret = vxp;
+ return 0;
}
@@ -292,7 +296,7 @@
{
struct snd_card *card;
struct snd_vxpocket *vxp;
- int i;
+ int i, err;
/* find an empty slot from the card list */
for (i = 0; i < SNDRV_CARDS; i++) {
@@ -307,16 +311,16 @@
return -ENODEV; /* disabled explicitly */
/* ok, create a card instance */
- card = snd_card_new(index[i], id[i], THIS_MODULE, 0);
- if (card == NULL) {
+ err = snd_card_create(index[i], id[i], THIS_MODULE, 0, &card);
+ if (err < 0) {
snd_printk(KERN_ERR "vxpocket: cannot create a card instance\n");
- return -ENOMEM;
+ return err;
}
- vxp = snd_vxpocket_new(card, ibl[i], p_dev);
- if (! vxp) {
+ err = snd_vxpocket_new(card, ibl[i], p_dev, &vxp);
+ if (err < 0) {
snd_card_free(card);
- return -ENODEV;
+ return err;
}
card->private_data = vxp;
diff --git a/sound/ppc/Kconfig b/sound/ppc/Kconfig
index 777de2b..bd2338a 100644
--- a/sound/ppc/Kconfig
+++ b/sound/ppc/Kconfig
@@ -13,6 +13,7 @@
tristate "PowerMac (AWACS, DACA, Burgundy, Tumbler, Keywest)"
depends on I2C && INPUT && PPC_PMAC
select SND_PCM
+ select SND_VMASTER
help
Say Y here to include support for the integrated sound device.
diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c
index 7bd33e6..80df9b1 100644
--- a/sound/ppc/awacs.c
+++ b/sound/ppc/awacs.c
@@ -608,9 +608,12 @@
AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_LINE, 0),
};
-static struct snd_kcontrol_new snd_pmac_screamer_mixers_imac[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_screamer_mixers_lo[] __initdata = {
AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
- AWACS_VOLUME("Master Playback Volume", 5, 6, 1),
+};
+
+static struct snd_kcontrol_new snd_pmac_screamer_mixers_imac[] __initdata = {
+ AWACS_VOLUME("Play-through Playback Volume", 5, 6, 1),
AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
};
@@ -627,6 +630,10 @@
AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
};
+static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac5500[] __initdata = {
+ AWACS_VOLUME("Headphone Playback Volume", 2, 6, 1),
+};
+
static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac[] __initdata = {
AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
@@ -645,12 +652,19 @@
AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_LINE, 0),
};
+static struct snd_kcontrol_new snd_pmac_awacs_mixers2_pmac5500[] __initdata = {
+ AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
+};
+
static struct snd_kcontrol_new snd_pmac_awacs_master_sw __initdata =
AWACS_SWITCH("Master Playback Switch", 1, SHIFT_HDMUTE, 1);
static struct snd_kcontrol_new snd_pmac_awacs_master_sw_imac __initdata =
AWACS_SWITCH("Line out Playback Switch", 1, SHIFT_HDMUTE, 1);
+static struct snd_kcontrol_new snd_pmac_awacs_master_sw_pmac5500 __initdata =
+AWACS_SWITCH("Headphone Playback Switch", 1, SHIFT_HDMUTE, 1);
+
static struct snd_kcontrol_new snd_pmac_awacs_mic_boost[] __initdata = {
AWACS_SWITCH("Mic Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
};
@@ -766,12 +780,16 @@
}
#endif /* CONFIG_PM */
-#define IS_PM7500 (machine_is_compatible("AAPL,7500"))
+#define IS_PM7500 (machine_is_compatible("AAPL,7500") \
+ || machine_is_compatible("AAPL,8500") \
+ || machine_is_compatible("AAPL,9500"))
+#define IS_PM5500 (machine_is_compatible("AAPL,e411"))
#define IS_BEIGE (machine_is_compatible("AAPL,Gossamer"))
#define IS_IMAC1 (machine_is_compatible("PowerMac2,1"))
#define IS_IMAC2 (machine_is_compatible("PowerMac2,2") \
|| machine_is_compatible("PowerMac4,1"))
#define IS_G4AGP (machine_is_compatible("PowerMac3,1"))
+#define IS_LOMBARD (machine_is_compatible("PowerBook1,1"))
static int imac1, imac2;
@@ -858,10 +876,14 @@
snd_pmac_awacs_init(struct snd_pmac *chip)
{
int pm7500 = IS_PM7500;
+ int pm5500 = IS_PM5500;
int beige = IS_BEIGE;
int g4agp = IS_G4AGP;
+ int lombard = IS_LOMBARD;
int imac;
int err, vol;
+ struct snd_kcontrol *vmaster_sw, *vmaster_vol;
+ struct snd_kcontrol *master_vol, *speaker_vol;
imac1 = IS_IMAC1;
imac2 = IS_IMAC2;
@@ -915,7 +937,7 @@
/* set headphone-jack detection bit */
switch (chip->model) {
case PMAC_AWACS:
- chip->hp_stat_mask = pm7500 ? MASK_HDPCONN
+ chip->hp_stat_mask = pm7500 || pm5500 ? MASK_HDPCONN
: MASK_LOCONN;
break;
case PMAC_SCREAMER:
@@ -954,7 +976,7 @@
return err;
if (beige || g4agp)
;
- else if (chip->model == PMAC_SCREAMER)
+ else if (chip->model == PMAC_SCREAMER || pm5500)
err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mixers2),
snd_pmac_screamer_mixers2);
else if (!pm7500)
@@ -962,19 +984,35 @@
snd_pmac_awacs_mixers2);
if (err < 0)
return err;
+ if (pm5500) {
+ err = build_mixers(chip,
+ ARRAY_SIZE(snd_pmac_awacs_mixers2_pmac5500),
+ snd_pmac_awacs_mixers2_pmac5500);
+ if (err < 0)
+ return err;
+ }
if (pm7500)
err = build_mixers(chip,
ARRAY_SIZE(snd_pmac_awacs_mixers_pmac7500),
snd_pmac_awacs_mixers_pmac7500);
+ else if (pm5500)
+ err = snd_ctl_add(chip->card,
+ (master_vol = snd_ctl_new1(snd_pmac_awacs_mixers_pmac5500,
+ chip)));
else if (beige)
err = build_mixers(chip,
ARRAY_SIZE(snd_pmac_screamer_mixers_beige),
snd_pmac_screamer_mixers_beige);
- else if (imac)
+ else if (imac || lombard) {
+ err = snd_ctl_add(chip->card,
+ (master_vol = snd_ctl_new1(snd_pmac_screamer_mixers_lo,
+ chip)));
+ if (err < 0)
+ return err;
err = build_mixers(chip,
ARRAY_SIZE(snd_pmac_screamer_mixers_imac),
snd_pmac_screamer_mixers_imac);
- else if (g4agp)
+ } else if (g4agp)
err = build_mixers(chip,
ARRAY_SIZE(snd_pmac_screamer_mixers_g4agp),
snd_pmac_screamer_mixers_g4agp);
@@ -984,8 +1022,10 @@
snd_pmac_awacs_mixers_pmac);
if (err < 0)
return err;
- chip->master_sw_ctl = snd_ctl_new1((pm7500 || imac || g4agp)
+ chip->master_sw_ctl = snd_ctl_new1((pm7500 || imac || g4agp || lombard)
? &snd_pmac_awacs_master_sw_imac
+ : pm5500
+ ? &snd_pmac_awacs_master_sw_pmac5500
: &snd_pmac_awacs_master_sw, chip);
err = snd_ctl_add(chip->card, chip->master_sw_ctl);
if (err < 0)
@@ -1017,8 +1057,9 @@
#endif /* PMAC_AMP_AVAIL */
{
/* route A = headphone, route C = speaker */
- err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_speaker_vol),
- snd_pmac_awacs_speaker_vol);
+ err = snd_ctl_add(chip->card,
+ (speaker_vol = snd_ctl_new1(snd_pmac_awacs_speaker_vol,
+ chip)));
if (err < 0)
return err;
chip->speaker_sw_ctl = snd_ctl_new1(imac1
@@ -1031,6 +1072,33 @@
return err;
}
+ if (pm5500 || imac || lombard) {
+ vmaster_sw = snd_ctl_make_virtual_master(
+ "Master Playback Switch", (unsigned int *) NULL);
+ err = snd_ctl_add_slave_uncached(vmaster_sw,
+ chip->master_sw_ctl);
+ if (err < 0)
+ return err;
+ err = snd_ctl_add_slave_uncached(vmaster_sw,
+ chip->speaker_sw_ctl);
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(chip->card, vmaster_sw);
+ if (err < 0)
+ return err;
+ vmaster_vol = snd_ctl_make_virtual_master(
+ "Master Playback Volume", (unsigned int *) NULL);
+ err = snd_ctl_add_slave(vmaster_vol, master_vol);
+ if (err < 0)
+ return err;
+ err = snd_ctl_add_slave(vmaster_vol, speaker_vol);
+ if (err < 0)
+ return err;
+ err = snd_ctl_add(chip->card, vmaster_vol);
+ if (err < 0)
+ return err;
+ }
+
if (beige || g4agp)
err = build_mixers(chip,
ARRAY_SIZE(snd_pmac_screamer_mic_boost_beige),
diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c
index f860d39..45a7629 100644
--- a/sound/ppc/burgundy.c
+++ b/sound/ppc/burgundy.c
@@ -35,7 +35,7 @@
int timeout = 50;
while ((in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) && timeout--)
udelay(1);
- if (! timeout)
+ if (timeout < 0)
printk(KERN_DEBUG "burgundy_busy_wait: timeout\n");
}
diff --git a/sound/ppc/daca.c b/sound/ppc/daca.c
index 8a5b290..f8d478c 100644
--- a/sound/ppc/daca.c
+++ b/sound/ppc/daca.c
@@ -82,7 +82,7 @@
data[1] |= mix->deemphasis ? 0x40 : 0;
if (i2c_smbus_write_block_data(mix->i2c.client, DACA_REG_AVOL,
2, data) < 0) {
- snd_printk("failed to set volume \n");
+ snd_printk(KERN_ERR "failed to set volume \n");
return -EINVAL;
}
return 0;
diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c
index af76ee8..9b4e9c3 100644
--- a/sound/ppc/pmac.c
+++ b/sound/ppc/pmac.c
@@ -299,7 +299,7 @@
case SNDRV_PCM_TRIGGER_SUSPEND:
spin_lock(&chip->reg_lock);
rec->running = 0;
- /*printk("stopped!!\n");*/
+ /*printk(KERN_DEBUG "stopped!!\n");*/
snd_pmac_dma_stop(rec);
for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++)
out_le16(&cp->command, DBDMA_STOP);
@@ -334,7 +334,7 @@
}
#endif
count += rec->cur_period * rec->period_size;
- /*printk("pointer=%d\n", count);*/
+ /*printk(KERN_DEBUG "pointer=%d\n", count);*/
return bytes_to_frames(subs->runtime, count);
}
@@ -486,7 +486,7 @@
if (! (stat & ACTIVE))
break;
- /*printk("update frag %d\n", rec->cur_period);*/
+ /*printk(KERN_DEBUG "update frag %d\n", rec->cur_period);*/
st_le16(&cp->xfer_status, 0);
st_le16(&cp->req_count, rec->period_size);
/*st_le16(&cp->res_count, 0);*/
@@ -806,7 +806,7 @@
struct snd_pmac *chip = devid;
int ctrl = in_le32(&chip->awacs->control);
- /*printk("pmac: control interrupt.. 0x%x\n", ctrl);*/
+ /*printk(KERN_DEBUG "pmac: control interrupt.. 0x%x\n", ctrl);*/
if (ctrl & MASK_PORTCHG) {
/* do something when headphone is plugged/unplugged? */
if (chip->update_automute)
@@ -1033,7 +1033,8 @@
}
if (of_device_is_compatible(sound, "tumbler")) {
chip->model = PMAC_TUMBLER;
- chip->can_capture = machine_is_compatible("PowerMac4,2");
+ chip->can_capture = machine_is_compatible("PowerMac4,2")
+ || machine_is_compatible("PowerBook4,1");
chip->can_duplex = 0;
// chip->can_byte_swap = 0; /* FIXME: check this */
chip->num_freqs = ARRAY_SIZE(tumbler_freqs);
diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c
index c936225..5a92906 100644
--- a/sound/ppc/powermac.c
+++ b/sound/ppc/powermac.c
@@ -58,9 +58,9 @@
char *name_ext;
int err;
- card = snd_card_new(index, id, THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
if ((err = snd_pmac_new(card, &chip)) < 0)
goto __error;
@@ -110,7 +110,7 @@
goto __error;
break;
default:
- snd_printk("unsupported hardware %d\n", chip->model);
+ snd_printk(KERN_ERR "unsupported hardware %d\n", chip->model);
err = -EINVAL;
goto __error;
}
diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c
index ff32111..f361c26 100644
--- a/sound/ppc/snd_ps3.c
+++ b/sound/ppc/snd_ps3.c
@@ -969,11 +969,9 @@
}
/* create card instance */
- the_card.card = snd_card_new(index, id, THIS_MODULE, 0);
- if (!the_card.card) {
- ret = -ENXIO;
+ ret = snd_card_create(index, id, THIS_MODULE, 0, &the_card.card);
+ if (ret < 0)
goto clean_irq;
- }
strcpy(the_card.card->driver, "PS3");
strcpy(the_card.card->shortname, "PS3");
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 3eb2233..40222fc 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -41,7 +41,7 @@
#undef DEBUG
#ifdef DEBUG
-#define DBG(fmt...) printk(fmt)
+#define DBG(fmt...) printk(KERN_DEBUG fmt)
#else
#define DBG(fmt...)
#endif
@@ -240,7 +240,7 @@
if (i2c_smbus_write_i2c_block_data(mix->i2c.client, TAS_REG_VOL, 6,
block) < 0) {
- snd_printk("failed to set volume \n");
+ snd_printk(KERN_ERR "failed to set volume \n");
return -EINVAL;
}
return 0;
@@ -350,7 +350,7 @@
if (i2c_smbus_write_i2c_block_data(mix->i2c.client, TAS_REG_DRC,
2, val) < 0) {
- snd_printk("failed to set DRC\n");
+ snd_printk(KERN_ERR "failed to set DRC\n");
return -EINVAL;
}
return 0;
@@ -386,7 +386,7 @@
if (i2c_smbus_write_i2c_block_data(mix->i2c.client, TAS_REG_DRC,
6, val) < 0) {
- snd_printk("failed to set DRC\n");
+ snd_printk(KERN_ERR "failed to set DRC\n");
return -EINVAL;
}
return 0;
@@ -506,7 +506,8 @@
block[i] = (vol >> ((info->bytes - i - 1) * 8)) & 0xff;
if (i2c_smbus_write_i2c_block_data(mix->i2c.client, info->reg,
info->bytes, block) < 0) {
- snd_printk("failed to set mono volume %d\n", info->index);
+ snd_printk(KERN_ERR "failed to set mono volume %d\n",
+ info->index);
return -EINVAL;
}
return 0;
@@ -643,7 +644,7 @@
}
if (i2c_smbus_write_i2c_block_data(mix->i2c.client, reg,
9, block) < 0) {
- snd_printk("failed to set mono volume %d\n", reg);
+ snd_printk(KERN_ERR "failed to set mono volume %d\n", reg);
return -EINVAL;
}
return 0;
diff --git a/sound/sh/Kconfig b/sound/sh/Kconfig
index cfc1439..aed0f90 100644
--- a/sound/sh/Kconfig
+++ b/sound/sh/Kconfig
@@ -15,6 +15,7 @@
tristate "Dreamcast Yamaha AICA sound"
depends on SH_DREAMCAST
select SND_PCM
+ select G2_DMA
help
ALSA Sound driver for the SEGA Dreamcast console.
diff --git a/sound/sh/aica.c b/sound/sh/aica.c
index 7c920f3..f551233 100644
--- a/sound/sh/aica.c
+++ b/sound/sh/aica.c
@@ -609,11 +609,11 @@
dreamcastcard = kmalloc(sizeof(struct snd_card_aica), GFP_KERNEL);
if (unlikely(!dreamcastcard))
return -ENOMEM;
- dreamcastcard->card =
- snd_card_new(index, SND_AICA_DRIVER, THIS_MODULE, 0);
- if (unlikely(!dreamcastcard->card)) {
+ err = snd_card_create(index, SND_AICA_DRIVER, THIS_MODULE, 0,
+ &dreamcastcard->card);
+ if (unlikely(err < 0)) {
kfree(dreamcastcard);
- return -ENODEV;
+ return err;
}
strcpy(dreamcastcard->card->driver, "snd_aica");
strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER);
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index ef025c6..3d2bb6f 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -6,6 +6,7 @@
tristate "ALSA for SoC audio support"
select SND_PCM
select AC97_BUS if SND_SOC_AC97_BUS
+ select SND_JACK if INPUT=y || INPUT=SND
---help---
If you want ASoC support, you should say Y here and also to the
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 86a9b1f..0237879 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,4 +1,4 @@
-snd-soc-core-objs := soc-core.o soc-dapm.o
+snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/
diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c
index 3dcdc4e..9ef6b96 100644
--- a/sound/soc/atmel/atmel-pcm.c
+++ b/sound/soc/atmel/atmel-pcm.c
@@ -347,7 +347,7 @@
vma->vm_end - vma->vm_start, vma->vm_page_prot);
}
-struct snd_pcm_ops atmel_pcm_ops = {
+static struct snd_pcm_ops atmel_pcm_ops = {
.open = atmel_pcm_open,
.close = atmel_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index ff0054b..e588e63 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -697,6 +697,15 @@
#define ATMEL_SSC_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 atmel_ssc_dai_ops = {
+ .startup = atmel_ssc_startup,
+ .shutdown = atmel_ssc_shutdown,
+ .prepare = atmel_ssc_prepare,
+ .hw_params = atmel_ssc_hw_params,
+ .set_fmt = atmel_ssc_set_dai_fmt,
+ .set_clkdiv = atmel_ssc_set_dai_clkdiv,
+};
+
struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = {
{ .name = "atmel-ssc0",
.id = 0,
@@ -712,13 +721,7 @@
.channels_max = 2,
.rates = ATMEL_SSC_RATES,
.formats = ATMEL_SSC_FORMATS,},
- .ops = {
- .startup = atmel_ssc_startup,
- .shutdown = atmel_ssc_shutdown,
- .prepare = atmel_ssc_prepare,
- .hw_params = atmel_ssc_hw_params,
- .set_fmt = atmel_ssc_set_dai_fmt,
- .set_clkdiv = atmel_ssc_set_dai_clkdiv,},
+ .ops = &atmel_ssc_dai_ops,
.private_data = &ssc_info[0],
},
#if NUM_SSC_DEVICES == 3
@@ -736,13 +739,7 @@
.channels_max = 2,
.rates = ATMEL_SSC_RATES,
.formats = ATMEL_SSC_FORMATS,},
- .ops = {
- .startup = atmel_ssc_startup,
- .shutdown = atmel_ssc_shutdown,
- .prepare = atmel_ssc_prepare,
- .hw_params = atmel_ssc_hw_params,
- .set_fmt = atmel_ssc_set_dai_fmt,
- .set_clkdiv = atmel_ssc_set_dai_clkdiv,},
+ .ops = &atmel_ssc_dai_ops,
.private_data = &ssc_info[1],
},
{ .name = "atmel-ssc2",
@@ -759,13 +756,7 @@
.channels_max = 2,
.rates = ATMEL_SSC_RATES,
.formats = ATMEL_SSC_FORMATS,},
- .ops = {
- .startup = atmel_ssc_startup,
- .shutdown = atmel_ssc_shutdown,
- .prepare = atmel_ssc_prepare,
- .hw_params = atmel_ssc_hw_params,
- .set_fmt = atmel_ssc_set_dai_fmt,
- .set_clkdiv = atmel_ssc_set_dai_clkdiv,},
+ .ops = &atmel_ssc_dai_ops,
.private_data = &ssc_info[2],
},
#endif
diff --git a/sound/soc/atmel/playpaq_wm8510.c b/sound/soc/atmel/playpaq_wm8510.c
index 43dd8ce..7065753 100644
--- a/sound/soc/atmel/playpaq_wm8510.c
+++ b/sound/soc/atmel/playpaq_wm8510.c
@@ -164,41 +164,41 @@
*/
switch (params_rate(params)) {
case 48000:
- pll_out = 12288000;
- mclk_div = WM8510_MCLKDIV_1;
- bclk = WM8510_BCLKDIV_8;
- break;
-
- case 44100:
- pll_out = 11289600;
- mclk_div = WM8510_MCLKDIV_1;
- bclk = WM8510_BCLKDIV_8;
- break;
-
- case 22050:
- pll_out = 11289600;
+ pll_out = 24576000;
mclk_div = WM8510_MCLKDIV_2;
bclk = WM8510_BCLKDIV_8;
break;
- case 16000:
- pll_out = 12288000;
- mclk_div = WM8510_MCLKDIV_3;
+ case 44100:
+ pll_out = 22579200;
+ mclk_div = WM8510_MCLKDIV_2;
bclk = WM8510_BCLKDIV_8;
break;
- case 11025:
- pll_out = 11289600;
+ case 22050:
+ pll_out = 22579200;
mclk_div = WM8510_MCLKDIV_4;
bclk = WM8510_BCLKDIV_8;
break;
- case 8000:
- pll_out = 12288000;
+ case 16000:
+ pll_out = 24576000;
mclk_div = WM8510_MCLKDIV_6;
bclk = WM8510_BCLKDIV_8;
break;
+ case 11025:
+ pll_out = 22579200;
+ mclk_div = WM8510_MCLKDIV_8;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ case 8000:
+ pll_out = 24576000;
+ mclk_div = WM8510_MCLKDIV_12;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
default:
pr_warning("playpaq_wm8510: Unsupported sample rate %d\n",
params_rate(params));
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index 6ea04be..173a239 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -36,6 +36,7 @@
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
+#include <linux/i2c.h>
#include <linux/atmel-ssc.h>
@@ -45,6 +46,7 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
+#include <asm/mach-types.h>
#include <mach/hardware.h>
#include <mach/gpio.h>
@@ -52,6 +54,9 @@
#include "atmel-pcm.h"
#include "atmel_ssc_dai.h"
+#define MCLK_RATE 12000000
+
+static struct clk *mclk;
static int at91sam9g20ek_startup(struct snd_pcm_substream *substream)
{
@@ -59,11 +64,12 @@
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
int ret;
- /* codec system clock is supplied by PCK0, set to 12MHz */
ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
- 12000000, SND_SOC_CLOCK_IN);
- if (ret < 0)
+ MCLK_RATE, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ clk_disable(mclk);
return ret;
+ }
return 0;
}
@@ -189,6 +195,31 @@
.shutdown = at91sam9g20ek_shutdown,
};
+static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,
+ enum snd_soc_bias_level level)
+{
+ static int mclk_on;
+ int ret = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ if (!mclk_on)
+ ret = clk_enable(mclk);
+ if (ret == 0)
+ mclk_on = 1;
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ case SND_SOC_BIAS_STANDBY:
+ if (mclk_on)
+ clk_disable(mclk);
+ mclk_on = 0;
+ break;
+ }
+
+ return ret;
+}
static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Int Mic", NULL),
@@ -243,21 +274,48 @@
};
static struct snd_soc_card snd_soc_at91sam9g20ek = {
- .name = "WM8731",
+ .name = "AT91SAMG20-EK",
.platform = &atmel_soc_platform,
.dai_link = &at91sam9g20ek_dai,
.num_links = 1,
+ .set_bias_level = at91sam9g20ek_set_bias_level,
};
-static struct wm8731_setup_data at91sam9g20ek_wm8731_setup = {
- .i2c_bus = 0,
- .i2c_address = 0x1b,
-};
+/*
+ * FIXME: This is a temporary bodge to avoid cross-tree merge issues.
+ * New drivers should register the wm8731 I2C device in the machine
+ * setup code (under arch/arm for ARM systems).
+ */
+static int wm8731_i2c_register(void)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = 0x1b;
+ strlcpy(info.type, "wm8731", I2C_NAME_SIZE);
+
+ adapter = i2c_get_adapter(0);
+ if (!adapter) {
+ printk(KERN_ERR "can't get i2c adapter 0\n");
+ return -ENODEV;
+ }
+
+ client = i2c_new_device(adapter, &info);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ printk(KERN_ERR "can't add i2c device at 0x%x\n",
+ (unsigned int)info.addr);
+ return -ENODEV;
+ }
+
+ return 0;
+}
static struct snd_soc_device at91sam9g20ek_snd_devdata = {
.card = &snd_soc_at91sam9g20ek,
.codec_dev = &soc_codec_dev_wm8731,
- .codec_data = &at91sam9g20ek_wm8731_setup,
};
static struct platform_device *at91sam9g20ek_snd_device;
@@ -266,23 +324,56 @@
{
struct atmel_ssc_info *ssc_p = at91sam9g20ek_dai.cpu_dai->private_data;
struct ssc_device *ssc = NULL;
+ struct clk *pllb;
int ret;
+ if (!machine_is_at91sam9g20ek())
+ return -ENODEV;
+
+ /*
+ * Codec MCLK is supplied by PCK0 - set it up.
+ */
+ mclk = clk_get(NULL, "pck0");
+ if (IS_ERR(mclk)) {
+ printk(KERN_ERR "ASoC: Failed to get MCLK\n");
+ ret = PTR_ERR(mclk);
+ goto err;
+ }
+
+ pllb = clk_get(NULL, "pllb");
+ if (IS_ERR(mclk)) {
+ printk(KERN_ERR "ASoC: Failed to get PLLB\n");
+ ret = PTR_ERR(mclk);
+ goto err_mclk;
+ }
+ ret = clk_set_parent(mclk, pllb);
+ clk_put(pllb);
+ if (ret != 0) {
+ printk(KERN_ERR "ASoC: Failed to set MCLK parent\n");
+ goto err_mclk;
+ }
+
+ clk_set_rate(mclk, MCLK_RATE);
+
/*
* Request SSC device
*/
ssc = ssc_request(0);
if (IS_ERR(ssc)) {
+ printk(KERN_ERR "ASoC: Failed to request SSC 0\n");
ret = PTR_ERR(ssc);
ssc = NULL;
goto err_ssc;
}
ssc_p->ssc = ssc;
+ ret = wm8731_i2c_register();
+ if (ret != 0)
+ goto err_ssc;
+
at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1);
if (!at91sam9g20ek_snd_device) {
- printk(KERN_DEBUG
- "platform device allocation failed\n");
+ printk(KERN_ERR "ASoC: Platform device allocation failed\n");
ret = -ENOMEM;
}
@@ -292,14 +383,19 @@
ret = platform_device_add(at91sam9g20ek_snd_device);
if (ret) {
- printk(KERN_DEBUG
- "platform device allocation failed\n");
+ printk(KERN_ERR "ASoC: Platform device allocation failed\n");
platform_device_put(at91sam9g20ek_snd_device);
}
return ret;
err_ssc:
+ ssc_free(ssc);
+ ssc_p->ssc = NULL;
+err_mclk:
+ clk_put(mclk);
+ mclk = NULL;
+err:
return ret;
}
@@ -317,6 +413,8 @@
platform_device_unregister(at91sam9g20ek_snd_device);
at91sam9g20ek_snd_device = NULL;
+ clk_put(mclk);
+ mclk = NULL;
}
module_init(at91sam9g20ek_init);
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c
index bc8d654..30490a2 100644
--- a/sound/soc/au1x/dbdma2.c
+++ b/sound/soc/au1x/dbdma2.c
@@ -305,7 +305,7 @@
return 0;
}
-struct snd_pcm_ops au1xpsc_pcm_ops = {
+static struct snd_pcm_ops au1xpsc_pcm_ops = {
.open = au1xpsc_pcm_open,
.close = au1xpsc_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
index f0e30ae..479d7bd 100644
--- a/sound/soc/au1x/psc-ac97.c
+++ b/sound/soc/au1x/psc-ac97.c
@@ -342,6 +342,11 @@
return 0;
}
+static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
+ .trigger = au1xpsc_ac97_trigger,
+ .hw_params = au1xpsc_ac97_hw_params,
+};
+
struct snd_soc_dai au1xpsc_ac97_dai = {
.name = "au1xpsc_ac97",
.ac97_control = 1,
@@ -361,10 +366,7 @@
.channels_min = 2,
.channels_max = 2,
},
- .ops = {
- .trigger = au1xpsc_ac97_trigger,
- .hw_params = au1xpsc_ac97_hw_params,
- },
+ .ops = &au1xpsc_ac97_dai_ops,
};
EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c
index f916de4..bb58932 100644
--- a/sound/soc/au1x/psc-i2s.c
+++ b/sound/soc/au1x/psc-i2s.c
@@ -367,6 +367,12 @@
return 0;
}
+static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = {
+ .trigger = au1xpsc_i2s_trigger,
+ .hw_params = au1xpsc_i2s_hw_params,
+ .set_fmt = au1xpsc_i2s_set_fmt,
+};
+
struct snd_soc_dai au1xpsc_i2s_dai = {
.name = "au1xpsc_i2s",
.probe = au1xpsc_i2s_probe,
@@ -385,11 +391,7 @@
.channels_min = 2,
.channels_max = 8, /* 2 without external help */
},
- .ops = {
- .trigger = au1xpsc_i2s_trigger,
- .hw_params = au1xpsc_i2s_hw_params,
- .set_fmt = au1xpsc_i2s_set_fmt,
- },
+ .ops = &au1xpsc_i2s_dai_ops,
};
EXPORT_SYMBOL(au1xpsc_i2s_dai);
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
index 8067cfa..8cfed1a 100644
--- a/sound/soc/blackfin/bf5xx-ac97-pcm.c
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c
@@ -297,7 +297,7 @@
}
#endif
-struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
+static struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
.open = bf5xx_pcm_open,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = bf5xx_pcm_hw_params,
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
index 3be2be6..8a935f2 100644
--- a/sound/soc/blackfin/bf5xx-ac97.c
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -31,72 +31,46 @@
#include "bf5xx-sport.h"
#include "bf5xx-ac97.h"
-#if defined(CONFIG_BF54x)
-#define PIN_REQ_SPORT_0 {P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, \
- P_SPORT0_RFS, P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0}
-
-#define PIN_REQ_SPORT_1 {P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, \
- P_SPORT1_RFS, P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0}
-
-#define PIN_REQ_SPORT_2 {P_SPORT2_TFS, P_SPORT2_DTPRI, P_SPORT2_TSCLK, \
- P_SPORT2_RFS, P_SPORT2_DRPRI, P_SPORT2_RSCLK, 0}
-
-#define PIN_REQ_SPORT_3 {P_SPORT3_TFS, P_SPORT3_DTPRI, P_SPORT3_TSCLK, \
- P_SPORT3_RFS, P_SPORT3_DRPRI, P_SPORT3_RSCLK, 0}
-#else
-#define PIN_REQ_SPORT_0 {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \
- P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0}
-
-#define PIN_REQ_SPORT_1 {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \
- P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0}
-#endif
-
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] = {
- PIN_REQ_SPORT_0,
-#ifdef PIN_REQ_SPORT_1
- PIN_REQ_SPORT_1,
+#ifdef SPORT0_TCR1
+ SPORT_REQ(0),
#endif
-#ifdef PIN_REQ_SPORT_2
- PIN_REQ_SPORT_2,
+#ifdef SPORT1_TCR1
+ SPORT_REQ(1),
#endif
-#ifdef PIN_REQ_SPORT_3
- PIN_REQ_SPORT_3,
+#ifdef SPORT2_TCR1
+ SPORT_REQ(2),
#endif
- };
+#ifdef SPORT3_TCR1
+ SPORT_REQ(3),
+#endif
+};
-static struct sport_param sport_params[4] = {
- {
- .dma_rx_chan = CH_SPORT0_RX,
- .dma_tx_chan = CH_SPORT0_TX,
- .err_irq = IRQ_SPORT0_ERROR,
- .regs = (struct sport_register *)SPORT0_TCR1,
- },
-#ifdef PIN_REQ_SPORT_1
- {
- .dma_rx_chan = CH_SPORT1_RX,
- .dma_tx_chan = CH_SPORT1_TX,
- .err_irq = IRQ_SPORT1_ERROR,
- .regs = (struct sport_register *)SPORT1_TCR1,
- },
-#endif
-#ifdef PIN_REQ_SPORT_2
- {
- .dma_rx_chan = CH_SPORT2_RX,
- .dma_tx_chan = CH_SPORT2_TX,
- .err_irq = IRQ_SPORT2_ERROR,
- .regs = (struct sport_register *)SPORT2_TCR1,
- },
-#endif
-#ifdef PIN_REQ_SPORT_3
- {
- .dma_rx_chan = CH_SPORT3_RX,
- .dma_tx_chan = CH_SPORT3_TX,
- .err_irq = IRQ_SPORT3_ERROR,
- .regs = (struct sport_register *)SPORT3_TCR1,
+#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
};
@@ -332,11 +306,11 @@
if (cmd_count == NULL)
return -ENOMEM;
- if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
+ if (peripheral_request_list(sport_req[sport_num], "soc-audio")) {
pr_err("Requesting Peripherals failed\n");
ret = -EFAULT;
goto peripheral_err;
- }
+ }
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
/* Request PB3 as reset pin */
@@ -383,9 +357,9 @@
sport_err:
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-#endif
gpio_err:
- peripheral_free_list(&sport_req[sport_num][0]);
+#endif
+ peripheral_free_list(sport_req[sport_num]);
peripheral_err:
free_page((unsigned long)cmd_count);
cmd_count = NULL;
@@ -398,7 +372,7 @@
{
free_page((unsigned long)cmd_count);
cmd_count = NULL;
- peripheral_free_list(&sport_req[sport_num][0]);
+ peripheral_free_list(sport_req[sport_num]);
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
#endif
diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c
index 7f2a5e1..edfbdc0 100644
--- a/sound/soc/blackfin/bf5xx-ad73311.c
+++ b/sound/soc/blackfin/bf5xx-ad73311.c
@@ -114,7 +114,7 @@
SSYNC();
/* When TUVF is set, the data is already send out */
- while (!(status & TUVF) && count++ < 10000) {
+ while (!(status & TUVF) && ++count < 10000) {
udelay(1);
status = bfin_read_SPORT_STAT();
SSYNC();
@@ -123,7 +123,7 @@
SSYNC();
local_irq_enable();
- if (count == 10000) {
+ if (count >= 10000) {
printk(KERN_ERR "ad73311: failed to configure codec\n");
return -1;
}
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c
index 53d290b..1318c4f 100644
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.c
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c
@@ -184,7 +184,7 @@
return 0 ;
}
-struct snd_pcm_ops bf5xx_pcm_i2s_ops = {
+static struct snd_pcm_ops bf5xx_pcm_i2s_ops = {
.open = bf5xx_pcm_open,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = bf5xx_pcm_hw_params,
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
index d1d95d2..9648244 100644
--- a/sound/soc/blackfin/bf5xx-i2s.c
+++ b/sound/soc/blackfin/bf5xx-i2s.c
@@ -287,6 +287,13 @@
#define BF5XX_I2S_FORMATS (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 = {
+ .startup = bf5xx_i2s_startup,
+ .shutdown = bf5xx_i2s_shutdown,
+ .hw_params = bf5xx_i2s_hw_params,
+ .set_fmt = bf5xx_i2s_set_dai_fmt,
+};
+
struct snd_soc_dai bf5xx_i2s_dai = {
.name = "bf5xx-i2s",
.id = 0,
@@ -304,12 +311,7 @@
.channels_max = 2,
.rates = BF5XX_I2S_RATES,
.formats = BF5XX_I2S_FORMATS,},
- .ops = {
- .startup = bf5xx_i2s_startup,
- .shutdown = bf5xx_i2s_shutdown,
- .hw_params = bf5xx_i2s_hw_params,
- .set_fmt = bf5xx_i2s_set_dai_fmt,
- },
+ .ops = &bf5xx_i2s_dai_ops,
};
EXPORT_SYMBOL_GPL(bf5xx_i2s_dai);
diff --git a/sound/soc/blackfin/bf5xx-sport.c b/sound/soc/blackfin/bf5xx-sport.c
index 3b99e48..b7953c8 100644
--- a/sound/soc/blackfin/bf5xx-sport.c
+++ b/sound/soc/blackfin/bf5xx-sport.c
@@ -133,7 +133,7 @@
int i;
for (i = 0; i < fragcount; ++i) {
- desc[i].next_desc_addr = (unsigned long)&(desc[i + 1]);
+ desc[i].next_desc_addr = &(desc[i + 1]);
desc[i].start_addr = (unsigned long)buf + i*fragsize;
desc[i].cfg = cfg;
desc[i].x_count = x_count;
@@ -143,12 +143,12 @@
}
/* make circular */
- desc[fragcount-1].next_desc_addr = (unsigned long)desc;
+ desc[fragcount-1].next_desc_addr = desc;
- pr_debug("setup desc: desc0=%p, next0=%lx, desc1=%p,"
- "next1=%lx\nx_count=%x,y_count=%x,addr=0x%lx,cfs=0x%x\n",
- &(desc[0]), desc[0].next_desc_addr,
- &(desc[1]), desc[1].next_desc_addr,
+ pr_debug("setup desc: desc0=%p, next0=%p, desc1=%p,"
+ "next1=%p\nx_count=%x,y_count=%x,addr=0x%lx,cfs=0x%x\n",
+ desc, desc[0].next_desc_addr,
+ desc+1, desc[1].next_desc_addr,
desc[0].x_count, desc[0].y_count,
desc[0].start_addr, desc[0].cfg);
}
@@ -184,22 +184,20 @@
BUG_ON(sport->curr_rx_desc == sport->dummy_rx_desc);
/* Maybe the dummy buffer descriptor ring is damaged */
- sport->dummy_rx_desc->next_desc_addr = \
- (unsigned long)(sport->dummy_rx_desc+1);
+ sport->dummy_rx_desc->next_desc_addr = sport->dummy_rx_desc + 1;
local_irq_save(flags);
- desc = (struct dmasg *)get_dma_next_desc_ptr(sport->dma_rx_chan);
+ desc = get_dma_next_desc_ptr(sport->dma_rx_chan);
/* Copy the descriptor which will be damaged to backup */
temp_desc = *desc;
desc->x_count = 0xa;
desc->y_count = 0;
- desc->next_desc_addr = (unsigned long)(sport->dummy_rx_desc);
+ desc->next_desc_addr = sport->dummy_rx_desc;
local_irq_restore(flags);
/* Waiting for dummy buffer descriptor is already hooked*/
while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) -
- sizeof(struct dmasg)) !=
- (unsigned long)sport->dummy_rx_desc)
- ;
+ sizeof(struct dmasg)) != sport->dummy_rx_desc)
+ continue;
sport->curr_rx_desc = sport->dummy_rx_desc;
/* Restore the damaged descriptor */
*desc = temp_desc;
@@ -210,14 +208,12 @@
static inline int sport_rx_dma_start(struct sport_device *sport, int dummy)
{
if (dummy) {
- sport->dummy_rx_desc->next_desc_addr = \
- (unsigned long) sport->dummy_rx_desc;
+ sport->dummy_rx_desc->next_desc_addr = sport->dummy_rx_desc;
sport->curr_rx_desc = sport->dummy_rx_desc;
} else
sport->curr_rx_desc = sport->dma_rx_desc;
- set_dma_next_desc_addr(sport->dma_rx_chan, \
- (unsigned long)(sport->curr_rx_desc));
+ set_dma_next_desc_addr(sport->dma_rx_chan, sport->curr_rx_desc);
set_dma_x_count(sport->dma_rx_chan, 0);
set_dma_x_modify(sport->dma_rx_chan, 0);
set_dma_config(sport->dma_rx_chan, (DMAFLOW_LARGE | NDSIZE_9 | \
@@ -231,14 +227,12 @@
static inline int sport_tx_dma_start(struct sport_device *sport, int dummy)
{
if (dummy) {
- sport->dummy_tx_desc->next_desc_addr = \
- (unsigned long) sport->dummy_tx_desc;
+ sport->dummy_tx_desc->next_desc_addr = sport->dummy_tx_desc;
sport->curr_tx_desc = sport->dummy_tx_desc;
} else
sport->curr_tx_desc = sport->dma_tx_desc;
- set_dma_next_desc_addr(sport->dma_tx_chan, \
- (unsigned long)(sport->curr_tx_desc));
+ set_dma_next_desc_addr(sport->dma_tx_chan, sport->curr_tx_desc);
set_dma_x_count(sport->dma_tx_chan, 0);
set_dma_x_modify(sport->dma_tx_chan, 0);
set_dma_config(sport->dma_tx_chan,
@@ -261,11 +255,9 @@
BUG_ON(sport->curr_rx_desc != sport->dummy_rx_desc);
local_irq_save(flags);
while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) -
- sizeof(struct dmasg)) !=
- (unsigned long)sport->dummy_rx_desc)
- ;
- sport->dummy_rx_desc->next_desc_addr =
- (unsigned long)(sport->dma_rx_desc);
+ sizeof(struct dmasg)) != sport->dummy_rx_desc)
+ continue;
+ sport->dummy_rx_desc->next_desc_addr = sport->dma_rx_desc;
local_irq_restore(flags);
sport->curr_rx_desc = sport->dma_rx_desc;
} else {
@@ -310,23 +302,21 @@
BUG_ON(sport->dummy_tx_desc == NULL);
BUG_ON(sport->curr_tx_desc == sport->dummy_tx_desc);
- sport->dummy_tx_desc->next_desc_addr = \
- (unsigned long)(sport->dummy_tx_desc+1);
+ sport->dummy_tx_desc->next_desc_addr = sport->dummy_tx_desc + 1;
/* Shorten the time on last normal descriptor */
local_irq_save(flags);
- desc = (struct dmasg *)get_dma_next_desc_ptr(sport->dma_tx_chan);
+ desc = get_dma_next_desc_ptr(sport->dma_tx_chan);
/* Store the descriptor which will be damaged */
temp_desc = *desc;
desc->x_count = 0xa;
desc->y_count = 0;
- desc->next_desc_addr = (unsigned long)(sport->dummy_tx_desc);
+ desc->next_desc_addr = sport->dummy_tx_desc;
local_irq_restore(flags);
/* Waiting for dummy buffer descriptor is already hooked*/
while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) - \
- sizeof(struct dmasg)) != \
- (unsigned long)sport->dummy_tx_desc)
- ;
+ sizeof(struct dmasg)) != sport->dummy_tx_desc)
+ continue;
sport->curr_tx_desc = sport->dummy_tx_desc;
/* Restore the damaged descriptor */
*desc = temp_desc;
@@ -347,11 +337,9 @@
/* Hook the normal buffer descriptor */
local_irq_save(flags);
while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) -
- sizeof(struct dmasg)) !=
- (unsigned long)sport->dummy_tx_desc)
- ;
- sport->dummy_tx_desc->next_desc_addr =
- (unsigned long)(sport->dma_tx_desc);
+ sizeof(struct dmasg)) != sport->dummy_tx_desc)
+ continue;
+ sport->dummy_tx_desc->next_desc_addr = sport->dma_tx_desc;
local_irq_restore(flags);
sport->curr_tx_desc = sport->dma_tx_desc;
} else {
@@ -536,19 +524,17 @@
unsigned config;
pr_debug("%s entered\n", __func__);
-#if L1_DATA_A_LENGTH != 0
- desc = (struct dmasg *) l1_data_sram_alloc(2 * sizeof(*desc));
-#else
- {
+ if (L1_DATA_A_LENGTH)
+ desc = l1_data_sram_zalloc(2 * sizeof(*desc));
+ else {
dma_addr_t addr;
desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0);
+ memset(desc, 0, 2 * sizeof(*desc));
}
-#endif
if (desc == NULL) {
pr_err("Failed to allocate memory for dummy rx desc\n");
return -ENOMEM;
}
- memset(desc, 0, 2 * sizeof(*desc));
sport->dummy_rx_desc = desc;
desc->start_addr = (unsigned long)sport->dummy_buf;
config = DMAFLOW_LARGE | NDSIZE_9 | compute_wdsize(sport->wdsize)
@@ -559,8 +545,8 @@
desc->y_count = 0;
desc->y_modify = 0;
memcpy(desc+1, desc, sizeof(*desc));
- desc->next_desc_addr = (unsigned long)(desc+1);
- desc[1].next_desc_addr = (unsigned long)desc;
+ desc->next_desc_addr = desc + 1;
+ desc[1].next_desc_addr = desc;
return 0;
}
@@ -571,19 +557,17 @@
pr_debug("%s entered\n", __func__);
-#if L1_DATA_A_LENGTH != 0
- desc = (struct dmasg *) l1_data_sram_alloc(2 * sizeof(*desc));
-#else
- {
+ if (L1_DATA_A_LENGTH)
+ desc = l1_data_sram_zalloc(2 * sizeof(*desc));
+ else {
dma_addr_t addr;
desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0);
+ memset(desc, 0, 2 * sizeof(*desc));
}
-#endif
if (!desc) {
pr_err("Failed to allocate memory for dummy tx desc\n");
return -ENOMEM;
}
- memset(desc, 0, 2 * sizeof(*desc));
sport->dummy_tx_desc = desc;
desc->start_addr = (unsigned long)sport->dummy_buf + \
sport->dummy_count;
@@ -595,8 +579,8 @@
desc->y_count = 0;
desc->y_modify = 0;
memcpy(desc+1, desc, sizeof(*desc));
- desc->next_desc_addr = (unsigned long)(desc+1);
- desc[1].next_desc_addr = (unsigned long)desc;
+ desc->next_desc_addr = desc + 1;
+ desc[1].next_desc_addr = desc;
return 0;
}
@@ -872,17 +856,15 @@
sport->wdsize = wdsize;
sport->dummy_count = dummy_count;
-#if L1_DATA_A_LENGTH != 0
- sport->dummy_buf = l1_data_sram_alloc(dummy_count * 2);
-#else
- sport->dummy_buf = kmalloc(dummy_count * 2, GFP_KERNEL);
-#endif
+ if (L1_DATA_A_LENGTH)
+ sport->dummy_buf = l1_data_sram_zalloc(dummy_count * 2);
+ else
+ sport->dummy_buf = kzalloc(dummy_count * 2, GFP_KERNEL);
if (sport->dummy_buf == NULL) {
pr_err("Failed to allocate dummy buffer\n");
goto __error;
}
- memset(sport->dummy_buf, 0, dummy_count * 2);
ret = sport_config_rx_dummy(sport);
if (ret) {
pr_err("Failed to config rx dummy ring\n");
@@ -939,6 +921,7 @@
sport = NULL;
}
EXPORT_SYMBOL(sport_done);
+
/*
* It is only used to send several bytes when dma is not enabled
* sport controller is configured but not enabled.
@@ -1029,4 +1012,3 @@
MODULE_AUTHOR("Roy Huang");
MODULE_DESCRIPTION("SPORT driver for ADI Blackfin");
MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index d0e0d69..b6c7f7a 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -10,9 +10,11 @@
config SND_SOC_ALL_CODECS
tristate "Build all ASoC CODEC drivers"
+ select SND_SOC_L3
select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
select SND_SOC_AD73311 if I2C
+ select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
select SND_SOC_CS4270 if I2C
select SND_SOC_PCM3008
@@ -24,6 +26,7 @@
select SND_SOC_UDA134X
select SND_SOC_UDA1380 if I2C
select SND_SOC_WM8350 if MFD_WM8350
+ select SND_SOC_WM8400 if MFD_WM8400
select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8580 if I2C
select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI
@@ -34,6 +37,7 @@
select SND_SOC_WM8903 if I2C
select SND_SOC_WM8971 if I2C
select SND_SOC_WM8990 if I2C
+ select SND_SOC_WM9705 if SND_SOC_AC97_BUS
select SND_SOC_WM9712 if SND_SOC_AC97_BUS
select SND_SOC_WM9713 if SND_SOC_AC97_BUS
help
@@ -58,6 +62,9 @@
config SND_SOC_AD73311
tristate
+config SND_SOC_AK4104
+ tristate
+
config SND_SOC_AK4535
tristate
@@ -65,12 +72,6 @@
config SND_SOC_CS4270
tristate
-# Cirrus Logic CS4270 Codec Hardware Mute Support
-# Select if you have external muting circuitry attached to your CS4270.
-config SND_SOC_CS4270_HWMUTE
- bool
- depends on SND_SOC_CS4270
-
# Cirrus Logic CS4270 Codec VD = 3.3V Errata
# Select if you are affected by the errata where the part will not function
# if MCLK divide-by-1.5 is selected and VD is set to 3.3V. The driver will
@@ -90,7 +91,6 @@
config SND_SOC_TLV320AIC23
tristate
- depends on I2C
config SND_SOC_TLV320AIC26
tristate "TI TLV320AIC26 Codec support" if SND_SOC_OF_SIMPLE
@@ -98,15 +98,12 @@
config SND_SOC_TLV320AIC3X
tristate
- depends on I2C
config SND_SOC_TWL4030
tristate
- depends on TWL4030_CORE
config SND_SOC_UDA134X
tristate
- select SND_SOC_L3
config SND_SOC_UDA1380
tristate
@@ -114,6 +111,9 @@
config SND_SOC_WM8350
tristate
+config SND_SOC_WM8400
+ tristate
+
config SND_SOC_WM8510
tristate
@@ -144,6 +144,9 @@
config SND_SOC_WM8990
tristate
+config SND_SOC_WM9705
+ tristate
+
config SND_SOC_WM9712
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index c4ddc9a..030d245 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,6 +1,7 @@
snd-soc-ac97-objs := ac97.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
+snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-l3-objs := l3.o
@@ -13,6 +14,7 @@
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
snd-soc-wm8350-objs := wm8350.o
+snd-soc-wm8400-objs := wm8400.o
snd-soc-wm8510-objs := wm8510.o
snd-soc-wm8580-objs := wm8580.o
snd-soc-wm8728-objs := wm8728.o
@@ -23,12 +25,14 @@
snd-soc-wm8903-objs := wm8903.o
snd-soc-wm8971-objs := wm8971.o
snd-soc-wm8990-objs := wm8990.o
+snd-soc-wm9705-objs := wm9705.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
+obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
@@ -41,6 +45,7 @@
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.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_WM8580) += snd-soc-wm8580.o
obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o
@@ -51,5 +56,7 @@
obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
+obj-$(CONFIG_SND_SOC_WM8991) += snd-soc-wm8991.o
+obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index fb53e65..b0d4af1 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -30,7 +30,7 @@
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
@@ -41,6 +41,10 @@
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000)
+static struct snd_soc_dai_ops ac97_dai_ops = {
+ .prepare = ac97_prepare,
+};
+
struct snd_soc_dai ac97_dai = {
.name = "AC97 HiFi",
.ac97_control = 1,
@@ -56,8 +60,7 @@
.channels_max = 2,
.rates = STD_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = {
- .prepare = ac97_prepare,},
+ .ops = &ac97_dai_ops,
};
EXPORT_SYMBOL_GPL(ac97_dai);
@@ -84,10 +87,10 @@
printk(KERN_INFO "AC97 SoC Audio Codec %s\n", AC97_VERSION);
- socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (!socdev->codec)
+ socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (!socdev->card->codec)
return -ENOMEM;
- codec = socdev->codec;
+ codec = socdev->card->codec;
mutex_init(&codec->mutex);
codec->name = "AC97";
@@ -123,23 +126,21 @@
snd_soc_free_pcms(socdev);
err:
- kfree(socdev->codec->reg_cache);
- kfree(socdev->codec);
- socdev->codec = NULL;
+ kfree(socdev->card->codec);
+ socdev->card->codec = NULL;
return ret;
}
static int ac97_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
if (!codec)
return 0;
snd_soc_free_pcms(socdev);
- kfree(socdev->codec->reg_cache);
- kfree(socdev->codec);
+ kfree(socdev->card->codec);
return 0;
}
@@ -149,7 +150,7 @@
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- snd_ac97_suspend(socdev->codec->ac97);
+ snd_ac97_suspend(socdev->card->codec->ac97);
return 0;
}
@@ -158,7 +159,7 @@
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- snd_ac97_resume(socdev->codec->ac97);
+ snd_ac97_resume(socdev->card->codec->ac97);
return 0;
}
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index 73fdbb4..ddb3b08 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -93,20 +93,6 @@
SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0),
};
-/* add non dapm controls */
-static int ad1980_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(ad1980_snd_ac97_controls); i++) {
- err = snd_ctl_add(codec->card, snd_soc_cnew(
- &ad1980_snd_ac97_controls[i], codec, NULL));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
@@ -123,7 +109,7 @@
default:
reg = reg >> 1;
- if (reg >= (ARRAY_SIZE(ad1980_reg)))
+ if (reg >= ARRAY_SIZE(ad1980_reg))
return -EINVAL;
return cache[reg];
@@ -137,7 +123,7 @@
soc_ac97_ops.write(codec->ac97, reg, val);
reg = reg >> 1;
- if (reg < (ARRAY_SIZE(ad1980_reg)))
+ if (reg < ARRAY_SIZE(ad1980_reg))
cache[reg] = val;
return 0;
@@ -200,10 +186,10 @@
printk(KERN_INFO "AD1980 SoC Audio Codec\n");
- socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (socdev->codec == NULL)
+ socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (socdev->card->codec == NULL)
return -ENOMEM;
- codec = socdev->codec;
+ codec = socdev->card->codec;
mutex_init(&codec->mutex);
codec->reg_cache =
@@ -269,7 +255,8 @@
ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
- ad1980_add_controls(codec);
+ snd_soc_add_controls(codec, ad1980_snd_ac97_controls,
+ ARRAY_SIZE(ad1980_snd_ac97_controls));
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "ad1980: failed to register card\n");
@@ -288,15 +275,15 @@
kfree(codec->reg_cache);
cache_err:
- kfree(socdev->codec);
- socdev->codec = NULL;
+ kfree(socdev->card->codec);
+ socdev->card->codec = NULL;
return ret;
}
static int ad1980_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
if (codec == NULL)
return 0;
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c
index b09289a..e61dac5 100644
--- a/sound/soc/codecs/ad73311.c
+++ b/sound/soc/codecs/ad73311.c
@@ -53,7 +53,7 @@
codec->owner = THIS_MODULE;
codec->dai = &ad73311_dai;
codec->num_dai = 1;
- socdev->codec = codec;
+ socdev->card->codec = codec;
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -75,15 +75,15 @@
register_err:
snd_soc_free_pcms(socdev);
pcm_err:
- kfree(socdev->codec);
- socdev->codec = NULL;
+ kfree(socdev->card->codec);
+ socdev->card->codec = NULL;
return ret;
}
static int ad73311_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
if (codec == NULL)
return 0;
diff --git a/sound/soc/codecs/ad73311.h b/sound/soc/codecs/ad73311.h
index 507ce0c..569573d 100644
--- a/sound/soc/codecs/ad73311.h
+++ b/sound/soc/codecs/ad73311.h
@@ -70,7 +70,7 @@
#define REGD_IGS(x) (x & 0x7)
#define REGD_RMOD (1 << 3)
#define REGD_OGS(x) ((x & 0x7) << 4)
-#define REGD_MUTE (x << 7)
+#define REGD_MUTE (1 << 7)
/* Control register E */
#define CTRL_REG_E (4 << 8)
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
new file mode 100644
index 0000000..4d47bc4
--- /dev/null
+++ b/sound/soc/codecs/ak4104.c
@@ -0,0 +1,365 @@
+/*
+ * AK4104 ALSA SoC (ASoC) driver
+ *
+ * Copyright (c) 2009 Daniel Mack <daniel@caiaq.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.
+ */
+
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <linux/spi/spi.h>
+#include <sound/asoundef.h>
+
+#include "ak4104.h"
+
+/* AK4104 registers addresses */
+#define AK4104_REG_CONTROL1 0x00
+#define AK4104_REG_RESERVED 0x01
+#define AK4104_REG_CONTROL2 0x02
+#define AK4104_REG_TX 0x03
+#define AK4104_REG_CHN_STATUS(x) ((x) + 0x04)
+#define AK4104_NUM_REGS 10
+
+#define AK4104_REG_MASK 0x1f
+#define AK4104_READ 0xc0
+#define AK4104_WRITE 0xe0
+#define AK4104_RESERVED_VAL 0x5b
+
+/* Bit masks for AK4104 registers */
+#define AK4104_CONTROL1_RSTN (1 << 0)
+#define AK4104_CONTROL1_PW (1 << 1)
+#define AK4104_CONTROL1_DIF0 (1 << 2)
+#define AK4104_CONTROL1_DIF1 (1 << 3)
+
+#define AK4104_CONTROL2_SEL0 (1 << 0)
+#define AK4104_CONTROL2_SEL1 (1 << 1)
+#define AK4104_CONTROL2_MODE (1 << 2)
+
+#define AK4104_TX_TXE (1 << 0)
+#define AK4104_TX_V (1 << 1)
+
+#define DRV_NAME "ak4104"
+
+struct ak4104_private {
+ struct snd_soc_codec codec;
+ u8 reg_cache[AK4104_NUM_REGS];
+};
+
+static int ak4104_fill_cache(struct snd_soc_codec *codec)
+{
+ int i;
+ u8 *reg_cache = codec->reg_cache;
+ struct spi_device *spi = codec->control_data;
+
+ for (i = 0; i < codec->reg_cache_size; i++) {
+ int ret = spi_w8r8(spi, i | AK4104_READ);
+ if (ret < 0) {
+ dev_err(&spi->dev, "SPI write failure\n");
+ return ret;
+ }
+
+ reg_cache[i] = ret;
+ }
+
+ return 0;
+}
+
+static unsigned int ak4104_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 *reg_cache = codec->reg_cache;
+
+ if (reg >= codec->reg_cache_size)
+ return -EINVAL;
+
+ return reg_cache[reg];
+}
+
+static int ak4104_spi_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 *cache = codec->reg_cache;
+ struct spi_device *spi = codec->control_data;
+
+ if (reg >= codec->reg_cache_size)
+ return -EINVAL;
+
+ reg &= AK4104_REG_MASK;
+ reg |= AK4104_WRITE;
+
+ /* only write to the hardware if value has changed */
+ if (cache[reg] != value) {
+ u8 tmp[2] = { reg, value };
+ if (spi_write(spi, tmp, sizeof(tmp))) {
+ dev_err(&spi->dev, "SPI write failed\n");
+ return -EIO;
+ }
+
+ cache[reg] = value;
+ }
+
+ return 0;
+}
+
+static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int val = 0;
+
+ val = ak4104_read_reg_cache(codec, AK4104_REG_CONTROL1);
+ if (val < 0)
+ return val;
+
+ val &= ~(AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1);
+
+ /* set DAI format */
+ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val |= AK4104_CONTROL1_DIF0;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val |= AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1;
+ break;
+ default:
+ dev_err(codec->dev, "invalid dai format\n");
+ return -EINVAL;
+ }
+
+ /* This device can only be slave */
+ if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
+ return -EINVAL;
+
+ return ak4104_spi_write(codec, AK4104_REG_CONTROL1, val);
+}
+
+static int ak4104_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_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ int val = 0;
+
+ /* set the IEC958 bits: consumer mode, no copyright bit */
+ val |= IEC958_AES0_CON_NOT_COPYRIGHT;
+ ak4104_spi_write(codec, AK4104_REG_CHN_STATUS(0), val);
+
+ val = 0;
+
+ switch (params_rate(params)) {
+ case 44100:
+ val |= IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ val |= IEC958_AES3_CON_FS_48000;
+ break;
+ case 32000:
+ val |= IEC958_AES3_CON_FS_32000;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported sampling rate\n");
+ return -EINVAL;
+ }
+
+ return ak4104_spi_write(codec, AK4104_REG_CHN_STATUS(3), val);
+}
+
+static struct snd_soc_dai_ops ak4101_dai_ops = {
+ .hw_params = ak4104_hw_params,
+ .set_fmt = ak4104_set_dai_fmt,
+};
+
+struct snd_soc_dai ak4104_dai = {
+ .name = DRV_NAME,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_32000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE
+ },
+ .ops = &ak4101_dai_ops,
+};
+
+static struct snd_soc_codec *ak4104_codec;
+
+static int ak4104_spi_probe(struct spi_device *spi)
+{
+ struct snd_soc_codec *codec;
+ struct ak4104_private *ak4104;
+ int ret, val;
+
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_0;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ ak4104 = kzalloc(sizeof(struct ak4104_private), GFP_KERNEL);
+ if (!ak4104) {
+ dev_err(&spi->dev, "could not allocate codec\n");
+ return -ENOMEM;
+ }
+
+ codec = &ak4104->codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->dev = &spi->dev;
+ codec->name = DRV_NAME;
+ codec->owner = THIS_MODULE;
+ codec->dai = &ak4104_dai;
+ codec->num_dai = 1;
+ codec->private_data = ak4104;
+ codec->control_data = spi;
+ codec->reg_cache = ak4104->reg_cache;
+ codec->reg_cache_size = AK4104_NUM_REGS;
+
+ /* read all regs and fill the cache */
+ ret = ak4104_fill_cache(codec);
+ if (ret < 0) {
+ dev_err(&spi->dev, "failed to fill register cache\n");
+ return ret;
+ }
+
+ /* read the 'reserved' register - according to the datasheet, it
+ * should contain 0x5b. Not a good way to verify the presence of
+ * the device, but there is no hardware ID register. */
+ if (ak4104_read_reg_cache(codec, AK4104_REG_RESERVED) !=
+ AK4104_RESERVED_VAL) {
+ ret = -ENODEV;
+ goto error_free_codec;
+ }
+
+ /* set power-up and non-reset bits */
+ val = ak4104_read_reg_cache(codec, AK4104_REG_CONTROL1);
+ val |= AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN;
+ ret = ak4104_spi_write(codec, AK4104_REG_CONTROL1, val);
+ if (ret < 0)
+ goto error_free_codec;
+
+ /* enable transmitter */
+ val = ak4104_read_reg_cache(codec, AK4104_REG_TX);
+ val |= AK4104_TX_TXE;
+ ret = ak4104_spi_write(codec, AK4104_REG_TX, val);
+ if (ret < 0)
+ goto error_free_codec;
+
+ ak4104_codec = codec;
+ ret = snd_soc_register_dai(&ak4104_dai);
+ if (ret < 0) {
+ dev_err(&spi->dev, "failed to register DAI\n");
+ goto error_free_codec;
+ }
+
+ spi_set_drvdata(spi, ak4104);
+ dev_info(&spi->dev, "SPI device initialized\n");
+ return 0;
+
+error_free_codec:
+ kfree(ak4104);
+ ak4104_dai.dev = NULL;
+ return ret;
+}
+
+static int __devexit ak4104_spi_remove(struct spi_device *spi)
+{
+ int ret, val;
+ struct ak4104_private *ak4104 = spi_get_drvdata(spi);
+
+ val = ak4104_read_reg_cache(&ak4104->codec, AK4104_REG_CONTROL1);
+ if (val < 0)
+ return val;
+
+ /* clear power-up and non-reset bits */
+ val &= ~(AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN);
+ ret = ak4104_spi_write(&ak4104->codec, AK4104_REG_CONTROL1, val);
+ if (ret < 0)
+ return ret;
+
+ ak4104_codec = NULL;
+ kfree(ak4104);
+ return 0;
+}
+
+static int ak4104_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = ak4104_codec;
+ int ret;
+
+ /* Connect the codec to the socdev. snd_soc_new_pcms() needs this. */
+ socdev->card->codec = codec;
+
+ /* Register PCMs */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to create pcms\n");
+ return ret;
+ }
+
+ /* Register the socdev */
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to register card\n");
+ snd_soc_free_pcms(socdev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ak4104_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ snd_soc_free_pcms(socdev);
+ return 0;
+};
+
+struct snd_soc_codec_device soc_codec_device_ak4104 = {
+ .probe = ak4104_probe,
+ .remove = ak4104_remove
+};
+EXPORT_SYMBOL_GPL(soc_codec_device_ak4104);
+
+static struct spi_driver ak4104_spi_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = ak4104_spi_probe,
+ .remove = __devexit_p(ak4104_spi_remove),
+};
+
+static int __init ak4104_init(void)
+{
+ pr_info("Asahi Kasei AK4104 ALSA SoC Codec Driver\n");
+ return spi_register_driver(&ak4104_spi_driver);
+}
+module_init(ak4104_init);
+
+static void __exit ak4104_exit(void)
+{
+ spi_unregister_driver(&ak4104_spi_driver);
+}
+module_exit(ak4104_exit);
+
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_DESCRIPTION("Asahi Kasei AK4104 ALSA SoC driver");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/codecs/ak4104.h b/sound/soc/codecs/ak4104.h
new file mode 100644
index 0000000..eb88fe7
--- /dev/null
+++ b/sound/soc/codecs/ak4104.h
@@ -0,0 +1,7 @@
+#ifndef _AK4104_H
+#define _AK4104_H
+
+extern struct snd_soc_dai ak4104_dai;
+extern struct snd_soc_codec_device soc_codec_device_ak4104;
+
+#endif
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 81300d8d..1f63d38 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -155,21 +155,6 @@
SOC_SINGLE("Mic Sidetone Volume", AK4535_VOL, 4, 7, 0),
};
-/* add non dapm controls */
-static int ak4535_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(ak4535_snd_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&ak4535_snd_controls[i], codec, NULL));
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
/* Mono 1 Mixer */
static const struct snd_kcontrol_new ak4535_mono1_mixer_controls[] = {
SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG1, 4, 1, 0),
@@ -344,7 +329,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct ak4535_priv *ak4535 = codec->private_data;
u8 mode2 = ak4535_read_reg_cache(codec, AK4535_MODE2) & ~(0x3 << 5);
int rate = params_rate(params), fs = 256;
@@ -436,6 +421,13 @@
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+static struct snd_soc_dai_ops ak4535_dai_ops = {
+ .hw_params = ak4535_hw_params,
+ .set_fmt = ak4535_set_dai_fmt,
+ .digital_mute = ak4535_mute,
+ .set_sysclk = ak4535_set_dai_sysclk,
+};
+
struct snd_soc_dai ak4535_dai = {
.name = "AK4535",
.playback = {
@@ -450,19 +442,14 @@
.channels_max = 2,
.rates = AK4535_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = {
- .hw_params = ak4535_hw_params,
- .set_fmt = ak4535_set_dai_fmt,
- .digital_mute = ak4535_mute,
- .set_sysclk = ak4535_set_dai_sysclk,
- },
+ .ops = &ak4535_dai_ops,
};
EXPORT_SYMBOL_GPL(ak4535_dai);
static int ak4535_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@@ -471,7 +458,7 @@
static int ak4535_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
ak4535_sync(codec);
ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
ak4535_set_bias_level(codec, codec->suspend_bias_level);
@@ -484,7 +471,7 @@
*/
static int ak4535_init(struct snd_soc_device *socdev)
{
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
codec->name = "AK4535";
@@ -510,7 +497,8 @@
/* power on device */
ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- ak4535_add_controls(codec);
+ snd_soc_add_controls(codec, ak4535_snd_controls,
+ ARRAY_SIZE(ak4535_snd_controls));
ak4535_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@@ -537,7 +525,7 @@
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = ak4535_socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@@ -636,7 +624,7 @@
}
codec->private_data = ak4535;
- socdev->codec = codec;
+ socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -663,7 +651,7 @@
static int ak4535_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index f1aa0c3..7fa09a3 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -3,27 +3,22 @@
*
* Author: Timur Tabi <timur@freescale.com>
*
- * Copyright 2007 Freescale Semiconductor, Inc. This file is licensed under
- * the terms of the GNU General Public License version 2. This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. This file is licensed
+ * under the terms of the GNU General Public License version 2. This
+ * program is licensed "as is" without any warranty of any kind, whether
+ * express or implied.
*
* This is an ASoC device driver for the Cirrus Logic CS4270 codec.
*
* Current features/limitations:
*
- * 1) Software mode is supported. Stand-alone mode is automatically
- * selected if I2C is disabled or if a CS4270 is not found on the I2C
- * bus. However, stand-alone mode is only partially implemented because
- * there is no mechanism yet for this driver and the machine driver to
- * communicate the values of the M0, M1, MCLK1, and MCLK2 pins.
- * 2) Only I2C is supported, not SPI
- * 3) Only Master mode is supported, not Slave.
- * 4) The machine driver's 'startup' function must call
- * cs4270_set_dai_sysclk() with the value of MCLK.
- * 5) Only I2S and left-justified modes are supported
- * 6) Power management is not supported
- * 7) The only supported control is volume and hardware mute (if enabled)
+ * - Software mode is supported. Stand-alone mode is not supported.
+ * - Only I2C is supported, not SPI
+ * - Support for master and slave mode
+ * - The machine driver's 'startup' function must call
+ * cs4270_set_dai_sysclk() with the value of MCLK.
+ * - Only I2S and left-justified modes are supported
+ * - Power management is not supported
*/
#include <linux/module.h>
@@ -35,18 +30,6 @@
#include "cs4270.h"
-/* If I2C is defined, then we support software mode. However, if we're
- not compiled as module but I2C is, then we can't use I2C calls. */
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-#define USE_I2C
-#endif
-
-/* Private data for the CS4270 */
-struct cs4270_private {
- unsigned int mclk; /* Input frequency of the MCLK pin */
- unsigned int mode; /* The mode (I2S or left-justified) */
-};
-
/*
* The codec isn't really big-endian or little-endian, since the I2S
* interface requires data to be sent serially with the MSbit first.
@@ -60,8 +43,6 @@
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
-#ifdef USE_I2C
-
/* CS4270 registers addresses */
#define CS4270_CHIPID 0x01 /* Chip ID */
#define CS4270_PWRCTL 0x02 /* Power Control */
@@ -121,8 +102,22 @@
#define CS4270_MUTE_DAC_A 0x01
#define CS4270_MUTE_DAC_B 0x02
-/*
- * Clock Ratio Selection for Master Mode with I2C enabled
+/* Private data for the CS4270 */
+struct cs4270_private {
+ struct snd_soc_codec codec;
+ u8 reg_cache[CS4270_NUMREGS];
+ unsigned int mclk; /* Input frequency of the MCLK pin */
+ unsigned int mode; /* The mode (I2S or left-justified) */
+ unsigned int slave_mode;
+};
+
+/**
+ * struct cs4270_mode_ratios - clock ratio tables
+ * @ratio: the ratio of MCLK to the sample rate
+ * @speed_mode: the Speed Mode bits to set in the Mode Control register for
+ * this ratio
+ * @mclk: the Ratio Select bits to set in the Mode Control register for this
+ * ratio
*
* The data for this chart is taken from Table 5 of the CS4270 reference
* manual.
@@ -131,31 +126,30 @@
* It is also used by cs4270_set_dai_sysclk() to tell ALSA which sampling
* rates the CS4270 currently supports.
*
- * Each element in this array corresponds to the ratios in mclk_ratios[].
- * These two arrays need to be in sync.
- *
- * 'speed_mode' is the corresponding bit pattern to be written to the
+ * @speed_mode is the corresponding bit pattern to be written to the
* MODE bits of the Mode Control Register
*
- * 'mclk' is the corresponding bit pattern to be wirten to the MCLK bits of
+ * @mclk is the corresponding bit pattern to be wirten to the MCLK bits of
* the Mode Control Register.
*
* In situations where a single ratio is represented by multiple speed
* modes, we favor the slowest speed. E.g, for a ratio of 128, we pick
* double-speed instead of quad-speed. However, the CS4270 errata states
- * that Divide-By-1.5 can cause failures, so we avoid that mode where
+ * that divide-By-1.5 can cause failures, so we avoid that mode where
* possible.
*
- * ERRATA: There is an errata for the CS4270 where divide-by-1.5 does not
- * work if VD = 3.3V. If this effects you, select the
+ * Errata: There is an errata for the CS4270 where divide-by-1.5 does not
+ * work if Vd is 3.3V. If this effects you, select the
* CONFIG_SND_SOC_CS4270_VD33_ERRATA Kconfig option, and the driver will
* never select any sample rates that require divide-by-1.5.
*/
-static struct {
+struct cs4270_mode_ratios {
unsigned int ratio;
u8 speed_mode;
u8 mclk;
-} cs4270_mode_ratios[] = {
+};
+
+static struct cs4270_mode_ratios cs4270_mode_ratios[] = {
{64, CS4270_MODE_4X, CS4270_MODE_DIV1},
#ifndef CONFIG_SND_SOC_CS4270_VD33_ERRATA
{96, CS4270_MODE_4X, CS4270_MODE_DIV15},
@@ -172,34 +166,27 @@
/* The number of MCLK/LRCK ratios supported by the CS4270 */
#define NUM_MCLK_RATIOS ARRAY_SIZE(cs4270_mode_ratios)
-/*
- * Determine the CS4270 samples rates.
+/**
+ * cs4270_set_dai_sysclk - determine the CS4270 samples rates.
+ * @codec_dai: the codec DAI
+ * @clk_id: the clock ID (ignored)
+ * @freq: the MCLK input frequency
+ * @dir: the clock direction (ignored)
*
- * 'freq' is the input frequency to MCLK. The other parameters are ignored.
+ * This function is used to tell the codec driver what the input MCLK
+ * frequency is.
*
* The value of MCLK is used to determine which sample rates are supported
* by the CS4270. The ratio of MCLK / Fs must be equal to one of nine
- * support values: 64, 96, 128, 192, 256, 384, 512, 768, and 1024.
+ * supported values - 64, 96, 128, 192, 256, 384, 512, 768, and 1024.
*
* This function calculates the nine ratios and determines which ones match
* a standard sample rate. If there's a match, then it is added to the list
- * of support sample rates.
+ * of supported sample rates.
*
* This function must be called by the machine driver's 'startup' function,
* otherwise the list of supported sample rates will not be available in
* time for ALSA.
- *
- * Note that in stand-alone mode, the sample rate is determined by input
- * pins M0, M1, MDIV1, and MDIV2. Also in stand-alone mode, divide-by-3
- * is not a programmable option. However, divide-by-3 is not an available
- * option in stand-alone mode. This cases two problems: a ratio of 768 is
- * not available (it requires divide-by-3) and B) ratios 192 and 384 can
- * only be selected with divide-by-1.5, but there is an errate that make
- * this selection difficult.
- *
- * In addition, there is no mechanism for communicating with the machine
- * driver what the input settings can be. This would need to be implemented
- * for stand-alone mode to work.
*/
static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
@@ -225,7 +212,7 @@
rates &= ~SNDRV_PCM_RATE_KNOT;
if (!rates) {
- printk(KERN_ERR "cs4270: could not find a valid sample rate\n");
+ dev_err(codec->dev, "could not find a valid sample rate\n");
return -EINVAL;
}
@@ -240,8 +227,10 @@
return 0;
}
-/*
- * Configure the codec for the selected audio format
+/**
+ * cs4270_set_dai_fmt - configure the codec for the selected audio format
+ * @codec_dai: the codec DAI
+ * @format: a SND_SOC_DAIFMT_x value indicating the data format
*
* This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the
* codec accordingly.
@@ -258,32 +247,43 @@
struct cs4270_private *cs4270 = codec->private_data;
int ret = 0;
+ /* set DAI format */
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
case SND_SOC_DAIFMT_LEFT_J:
cs4270->mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
break;
default:
- printk(KERN_ERR "cs4270: invalid DAI format\n");
+ dev_err(codec->dev, "invalid dai format\n");
+ ret = -EINVAL;
+ }
+
+ /* set master/slave audio interface */
+ switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ cs4270->slave_mode = 1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ cs4270->slave_mode = 0;
+ break;
+ default:
+ /* all other modes are unsupported by the hardware */
ret = -EINVAL;
}
return ret;
}
-/*
- * A list of addresses on which this CS4270 could use. I2C addresses are
- * 7 bits. For the CS4270, the upper four bits are always 1001, and the
- * lower three bits are determined via the AD2, AD1, and AD0 pins
- * (respectively).
- */
-static const unsigned short normal_i2c[] = {
- 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, I2C_CLIENT_END
-};
-I2C_CLIENT_INSMOD;
-
-/*
- * Pre-fill the CS4270 register cache.
+/**
+ * cs4270_fill_cache - pre-fill the CS4270 register cache.
+ * @codec: the codec for this CS4270
+ *
+ * This function fills in the CS4270 register cache by reading the register
+ * values from the hardware.
+ *
+ * This CS4270 registers are cached to avoid excessive I2C I/O operations.
+ * After the initial read to pre-fill the cache, the CS4270 never updates
+ * the register values, so we won't have a cache coherency problem.
*
* We use the auto-increment feature of the CS4270 to read all registers in
* one shot.
@@ -298,7 +298,7 @@
CS4270_FIRSTREG | 0x80, CS4270_NUMREGS, cache);
if (length != CS4270_NUMREGS) {
- printk(KERN_ERR "cs4270: I2C read failure, addr=0x%x\n",
+ dev_err(codec->dev, "i2c read failure, addr=0x%x\n",
i2c_client->addr);
return -EIO;
}
@@ -306,12 +306,17 @@
return 0;
}
-/*
- * Read from the CS4270 register cache.
+/**
+ * cs4270_read_reg_cache - read from the CS4270 register cache.
+ * @codec: the codec for this CS4270
+ * @reg: the register to read
+ *
+ * This function returns the value for a given register. It reads only from
+ * the register cache, not the hardware itself.
*
* This CS4270 registers are cached to avoid excessive I2C I/O operations.
* After the initial read to pre-fill the cache, the CS4270 never updates
- * the register values, so we won't have a cache coherncy problem.
+ * the register values, so we won't have a cache coherency problem.
*/
static unsigned int cs4270_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
@@ -324,8 +329,11 @@
return cache[reg - CS4270_FIRSTREG];
}
-/*
- * Write to a CS4270 register via the I2C bus.
+/**
+ * cs4270_i2c_write - write to a CS4270 register via the I2C bus.
+ * @codec: the codec for this CS4270
+ * @reg: the register to write
+ * @value: the value to write to the register
*
* This function writes the given value to the given CS4270 register, and
* also updates the register cache.
@@ -346,7 +354,7 @@
if (cache[reg - CS4270_FIRSTREG] != value) {
struct i2c_client *client = codec->control_data;
if (i2c_smbus_write_byte_data(client, reg, value)) {
- printk(KERN_ERR "cs4270: I2C write failed\n");
+ dev_err(codec->dev, "i2c write failed\n");
return -EIO;
}
@@ -357,11 +365,17 @@
return 0;
}
-/*
- * Program the CS4270 with the given hardware parameters.
+/**
+ * cs4270_hw_params - program the CS4270 with the given hardware parameters.
+ * @substream: the audio stream
+ * @params: the hardware parameters to set
+ * @dai: the SOC DAI (ignored)
*
- * The .ops functions are used to provide board-specific data, like
- * input frequencies, to this driver. This function takes that information,
+ * This function programs the hardware with the values provided.
+ * Specifically, the sample rate and the data format.
+ *
+ * The .ops functions are used to provide board-specific data, like input
+ * frequencies, to this driver. This function takes that information,
* combines it with the hardware parameters provided, and programs the
* hardware accordingly.
*/
@@ -371,7 +385,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct cs4270_private *cs4270 = codec->private_data;
int ret;
unsigned int i;
@@ -391,33 +405,28 @@
if (i == NUM_MCLK_RATIOS) {
/* We did not find a matching ratio */
- printk(KERN_ERR "cs4270: could not find matching ratio\n");
+ dev_err(codec->dev, "could not find matching ratio\n");
return -EINVAL;
}
- /* Freeze and power-down the codec */
-
- ret = snd_soc_write(codec, CS4270_PWRCTL, CS4270_PWRCTL_FREEZE |
- CS4270_PWRCTL_PDN_ADC | CS4270_PWRCTL_PDN_DAC |
- CS4270_PWRCTL_PDN);
- if (ret < 0) {
- printk(KERN_ERR "cs4270: I2C write failed\n");
- return ret;
- }
-
- /* Program the mode control register */
+ /* Set the sample rate */
reg = snd_soc_read(codec, CS4270_MODE);
reg &= ~(CS4270_MODE_SPEED_MASK | CS4270_MODE_DIV_MASK);
- reg |= cs4270_mode_ratios[i].speed_mode | cs4270_mode_ratios[i].mclk;
+ reg |= cs4270_mode_ratios[i].mclk;
+
+ if (cs4270->slave_mode)
+ reg |= CS4270_MODE_SLAVE;
+ else
+ reg |= cs4270_mode_ratios[i].speed_mode;
ret = snd_soc_write(codec, CS4270_MODE, reg);
if (ret < 0) {
- printk(KERN_ERR "cs4270: I2C write failed\n");
+ dev_err(codec->dev, "i2c write failed\n");
return ret;
}
- /* Program the format register */
+ /* Set the DAI format */
reg = snd_soc_read(codec, CS4270_FORMAT);
reg &= ~(CS4270_FORMAT_DAC_MASK | CS4270_FORMAT_ADC_MASK);
@@ -430,55 +439,23 @@
reg |= CS4270_FORMAT_DAC_LJ | CS4270_FORMAT_ADC_LJ;
break;
default:
- printk(KERN_ERR "cs4270: unknown format\n");
+ dev_err(codec->dev, "unknown dai format\n");
return -EINVAL;
}
ret = snd_soc_write(codec, CS4270_FORMAT, reg);
if (ret < 0) {
- printk(KERN_ERR "cs4270: I2C write failed\n");
- return ret;
- }
-
- /* Disable auto-mute. This feature appears to be buggy, because in
- some situations, auto-mute will not deactivate when it should. */
-
- reg = snd_soc_read(codec, CS4270_MUTE);
- reg &= ~CS4270_MUTE_AUTO;
- ret = snd_soc_write(codec, CS4270_MUTE, reg);
- if (ret < 0) {
- printk(KERN_ERR "cs4270: I2C write failed\n");
- return ret;
- }
-
- /* Disable automatic volume control. It's enabled by default, and
- * it causes volume change commands to be delayed, sometimes until
- * after playback has started.
- */
-
- reg = cs4270_read_reg_cache(codec, CS4270_TRANS);
- reg &= ~(CS4270_TRANS_SOFT | CS4270_TRANS_ZERO);
- ret = cs4270_i2c_write(codec, CS4270_TRANS, reg);
- if (ret < 0) {
- printk(KERN_ERR "I2C write failed\n");
- return ret;
- }
-
- /* Thaw and power-up the codec */
-
- ret = snd_soc_write(codec, CS4270_PWRCTL, 0);
- if (ret < 0) {
- printk(KERN_ERR "cs4270: I2C write failed\n");
+ dev_err(codec->dev, "i2c write failed\n");
return ret;
}
return ret;
}
-#ifdef CONFIG_SND_SOC_CS4270_HWMUTE
-
-/*
- * Set the CS4270 external mute
+/**
+ * cs4270_mute - enable/disable the CS4270 external mute
+ * @dai: the SOC DAI
+ * @mute: 0 = disable mute, 1 = enable mute
*
* This function toggles the mute bits in the MUTE register. The CS4270's
* mute capability is intended for external muting circuitry, so if the
@@ -493,150 +470,47 @@
reg6 = snd_soc_read(codec, CS4270_MUTE);
if (mute)
- reg6 |= CS4270_MUTE_ADC_A | CS4270_MUTE_ADC_B |
- CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B;
+ reg6 |= CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B;
else
- reg6 &= ~(CS4270_MUTE_ADC_A | CS4270_MUTE_ADC_B |
- CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B);
+ reg6 &= ~(CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B);
return snd_soc_write(codec, CS4270_MUTE, reg6);
}
-#endif
-
-static int cs4270_i2c_probe(struct i2c_client *, const struct i2c_device_id *);
-
/* A list of non-DAPM controls that the CS4270 supports */
static const struct snd_kcontrol_new cs4270_snd_controls[] = {
SOC_DOUBLE_R("Master Playback Volume",
- CS4270_VOLA, CS4270_VOLB, 0, 0xFF, 1)
-};
-
-static const struct i2c_device_id cs4270_id[] = {
- {"cs4270", 0},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, cs4270_id);
-
-static struct i2c_driver cs4270_i2c_driver = {
- .driver = {
- .name = "CS4270 I2C",
- .owner = THIS_MODULE,
- },
- .id_table = cs4270_id,
- .probe = cs4270_i2c_probe,
+ CS4270_VOLA, CS4270_VOLB, 0, 0xFF, 1),
+ SOC_SINGLE("Digital Sidetone Switch", CS4270_FORMAT, 5, 1, 0),
+ SOC_SINGLE("Soft Ramp Switch", CS4270_TRANS, 6, 1, 0),
+ SOC_SINGLE("Zero Cross Switch", CS4270_TRANS, 5, 1, 0),
+ SOC_SINGLE("Popguard Switch", CS4270_MODE, 0, 1, 1),
+ SOC_SINGLE("Auto-Mute Switch", CS4270_MUTE, 5, 1, 0),
+ SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 0)
};
/*
- * Global variable to store socdev for i2c probe function.
+ * cs4270_codec - global variable to store codec for the ASoC probe function
*
* If struct i2c_driver had a private_data field, we wouldn't need to use
- * cs4270_socdec. This is the only way to pass the socdev structure to
- * cs4270_i2c_probe().
- *
- * The real solution to cs4270_socdev is to create a mechanism
- * that maps I2C addresses to snd_soc_device structures. Perhaps the
- * creation of the snd_soc_device object should be moved out of
- * cs4270_probe() and into cs4270_i2c_probe(), but that would make this
- * driver dependent on I2C. The CS4270 supports "stand-alone" mode, whereby
- * the chip is *not* connected to the I2C bus, but is instead configured via
- * input pins.
+ * cs4270_codec. This is the only way to pass the codec structure from
+ * cs4270_i2c_probe() to cs4270_probe(). Unfortunately, there is no good
+ * way to synchronize these two functions. cs4270_i2c_probe() can be called
+ * multiple times before cs4270_probe() is called even once. So for now, we
+ * also only allow cs4270_i2c_probe() to be run once. That means that we do
+ * not support more than one cs4270 device in the system, at least for now.
*/
-static struct snd_soc_device *cs4270_socdev;
+static struct snd_soc_codec *cs4270_codec;
-/*
- * Initialize the I2C interface of the CS4270
- *
- * This function is called for whenever the I2C subsystem finds a device
- * at a particular address.
- *
- * Note: snd_soc_new_pcms() must be called before this function can be called,
- * because of snd_ctl_add().
- */
-static int cs4270_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
-{
- struct snd_soc_device *socdev = cs4270_socdev;
- struct snd_soc_codec *codec = socdev->codec;
- int i;
- int ret = 0;
-
- /* Probing all possible addresses has one drawback: if there are
- multiple CS4270s on the bus, then you cannot specify which
- socdev is matched with which CS4270. For now, we just reject
- this I2C device if the socdev already has one attached. */
- if (codec->control_data)
- return -ENODEV;
-
- /* Note: codec_dai->codec is NULL here */
-
- codec->reg_cache = kzalloc(CS4270_NUMREGS, GFP_KERNEL);
- if (!codec->reg_cache) {
- printk(KERN_ERR "cs4270: could not allocate register cache\n");
- ret = -ENOMEM;
- goto error;
- }
-
- /* Verify that we have a CS4270 */
-
- ret = i2c_smbus_read_byte_data(i2c_client, CS4270_CHIPID);
- if (ret < 0) {
- printk(KERN_ERR "cs4270: failed to read I2C\n");
- goto error;
- }
- /* The top four bits of the chip ID should be 1100. */
- if ((ret & 0xF0) != 0xC0) {
- /* The device at this address is not a CS4270 codec */
- ret = -ENODEV;
- goto error;
- }
-
- printk(KERN_INFO "cs4270: found device at I2C address %X\n",
- i2c_client->addr);
- printk(KERN_INFO "cs4270: hardware revision %X\n", ret & 0xF);
-
- codec->control_data = i2c_client;
- codec->read = cs4270_read_reg_cache;
- codec->write = cs4270_i2c_write;
- codec->reg_cache_size = CS4270_NUMREGS;
-
- /* The I2C interface is set up, so pre-fill our register cache */
-
- ret = cs4270_fill_cache(codec);
- if (ret < 0) {
- printk(KERN_ERR "cs4270: failed to fill register cache\n");
- goto error;
- }
-
- /* Add the non-DAPM controls */
-
- for (i = 0; i < ARRAY_SIZE(cs4270_snd_controls); i++) {
- struct snd_kcontrol *kctrl =
- snd_soc_cnew(&cs4270_snd_controls[i], codec, NULL);
-
- ret = snd_ctl_add(codec->card, kctrl);
- if (ret < 0)
- goto error;
- }
-
- i2c_set_clientdata(i2c_client, codec);
-
- return 0;
-
-error:
- codec->control_data = NULL;
-
- kfree(codec->reg_cache);
- codec->reg_cache = NULL;
- codec->reg_cache_size = 0;
-
- return ret;
-}
-
-#endif /* USE_I2C*/
+static struct snd_soc_dai_ops cs4270_dai_ops = {
+ .hw_params = cs4270_hw_params,
+ .set_sysclk = cs4270_set_dai_sysclk,
+ .set_fmt = cs4270_set_dai_fmt,
+ .digital_mute = cs4270_mute,
+};
struct snd_soc_dai cs4270_dai = {
- .name = "CS4270",
+ .name = "cs4270",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
@@ -651,118 +525,251 @@
.rates = 0,
.formats = CS4270_FORMATS,
},
+ .ops = &cs4270_dai_ops,
};
EXPORT_SYMBOL_GPL(cs4270_dai);
-/*
- * ASoC probe function
+/**
+ * cs4270_probe - ASoC probe function
+ * @pdev: platform device
*
- * This function is called when the machine driver calls
- * platform_device_add().
+ * This function is called when ASoC has all the pieces it needs to
+ * instantiate a sound driver.
*/
static int cs4270_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec;
- int ret = 0;
+ struct snd_soc_codec *codec = cs4270_codec;
+ int ret;
- printk(KERN_INFO "CS4270 ALSA SoC Codec\n");
-
- /* Allocate enough space for the snd_soc_codec structure
- and our private data together. */
- codec = kzalloc(ALIGN(sizeof(struct snd_soc_codec), 4) +
- sizeof(struct cs4270_private), GFP_KERNEL);
- if (!codec) {
- printk(KERN_ERR "cs4270: Could not allocate codec structure\n");
- return -ENOMEM;
- }
-
- mutex_init(&codec->mutex);
- INIT_LIST_HEAD(&codec->dapm_widgets);
- INIT_LIST_HEAD(&codec->dapm_paths);
-
- codec->name = "CS4270";
- codec->owner = THIS_MODULE;
- codec->dai = &cs4270_dai;
- codec->num_dai = 1;
- codec->private_data = (void *) codec +
- ALIGN(sizeof(struct snd_soc_codec), 4);
-
- socdev->codec = codec;
+ /* Connect the codec to the socdev. snd_soc_new_pcms() needs this. */
+ socdev->card->codec = codec;
/* Register PCMs */
-
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
- printk(KERN_ERR "cs4270: failed to create PCMs\n");
- goto error_free_codec;
+ dev_err(codec->dev, "failed to create pcms\n");
+ return ret;
}
-#ifdef USE_I2C
- cs4270_socdev = socdev;
-
- ret = i2c_add_driver(&cs4270_i2c_driver);
- if (ret) {
- printk(KERN_ERR "cs4270: failed to attach driver");
+ /* Add the non-DAPM controls */
+ ret = snd_soc_add_controls(codec, cs4270_snd_controls,
+ ARRAY_SIZE(cs4270_snd_controls));
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to add controls\n");
goto error_free_pcms;
}
- /* Did we find a CS4270 on the I2C bus? */
- if (codec->control_data) {
- /* Initialize codec ops */
- cs4270_dai.ops.hw_params = cs4270_hw_params;
- cs4270_dai.ops.set_sysclk = cs4270_set_dai_sysclk;
- cs4270_dai.ops.set_fmt = cs4270_set_dai_fmt;
-#ifdef CONFIG_SND_SOC_CS4270_HWMUTE
- cs4270_dai.ops.digital_mute = cs4270_mute;
-#endif
- } else
- printk(KERN_INFO "cs4270: no I2C device found, "
- "using stand-alone mode\n");
-#else
- printk(KERN_INFO "cs4270: I2C disabled, using stand-alone mode\n");
-#endif
-
+ /* And finally, register the socdev */
ret = snd_soc_init_card(socdev);
if (ret < 0) {
- printk(KERN_ERR "cs4270: failed to register card\n");
- goto error_del_driver;
+ dev_err(codec->dev, "failed to register card\n");
+ goto error_free_pcms;
}
return 0;
-error_del_driver:
-#ifdef USE_I2C
- i2c_del_driver(&cs4270_i2c_driver);
-
error_free_pcms:
-#endif
snd_soc_free_pcms(socdev);
-error_free_codec:
- kfree(socdev->codec);
- socdev->codec = NULL;
-
return ret;
}
+/**
+ * cs4270_remove - ASoC remove function
+ * @pdev: platform device
+ *
+ * This function is the counterpart to cs4270_probe().
+ */
static int cs4270_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
snd_soc_free_pcms(socdev);
-#ifdef USE_I2C
- i2c_del_driver(&cs4270_i2c_driver);
-#endif
+ return 0;
+};
- kfree(socdev->codec);
- socdev->codec = NULL;
+/**
+ * cs4270_i2c_probe - initialize the I2C interface of the CS4270
+ * @i2c_client: the I2C client object
+ * @id: the I2C device ID (ignored)
+ *
+ * This function is called whenever the I2C subsystem finds a device that
+ * matches the device ID given via a prior call to i2c_add_driver().
+ */
+static int cs4270_i2c_probe(struct i2c_client *i2c_client,
+ const struct i2c_device_id *id)
+{
+ struct snd_soc_codec *codec;
+ struct cs4270_private *cs4270;
+ unsigned int reg;
+ int ret;
+
+ /* For now, we only support one cs4270 device in the system. See the
+ * comment for cs4270_codec.
+ */
+ if (cs4270_codec) {
+ dev_err(&i2c_client->dev, "ignoring CS4270 at addr %X\n",
+ i2c_client->addr);
+ dev_err(&i2c_client->dev, "only one per board allowed\n");
+ /* Should we return something other than ENODEV here? */
+ return -ENODEV;
+ }
+
+ /* Verify that we have a CS4270 */
+
+ ret = i2c_smbus_read_byte_data(i2c_client, CS4270_CHIPID);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "failed to read i2c at addr %X\n",
+ i2c_client->addr);
+ return ret;
+ }
+ /* The top four bits of the chip ID should be 1100. */
+ if ((ret & 0xF0) != 0xC0) {
+ dev_err(&i2c_client->dev, "device at addr %X is not a CS4270\n",
+ i2c_client->addr);
+ return -ENODEV;
+ }
+
+ dev_info(&i2c_client->dev, "found device at i2c address %X\n",
+ i2c_client->addr);
+ dev_info(&i2c_client->dev, "hardware revision %X\n", ret & 0xF);
+
+ /* Allocate enough space for the snd_soc_codec structure
+ and our private data together. */
+ cs4270 = kzalloc(sizeof(struct cs4270_private), GFP_KERNEL);
+ if (!cs4270) {
+ dev_err(&i2c_client->dev, "could not allocate codec\n");
+ return -ENOMEM;
+ }
+ codec = &cs4270->codec;
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->dev = &i2c_client->dev;
+ codec->name = "CS4270";
+ codec->owner = THIS_MODULE;
+ codec->dai = &cs4270_dai;
+ codec->num_dai = 1;
+ codec->private_data = cs4270;
+ codec->control_data = i2c_client;
+ codec->read = cs4270_read_reg_cache;
+ codec->write = cs4270_i2c_write;
+ codec->reg_cache = cs4270->reg_cache;
+ codec->reg_cache_size = CS4270_NUMREGS;
+
+ /* The I2C interface is set up, so pre-fill our register cache */
+
+ ret = cs4270_fill_cache(codec);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "failed to fill register cache\n");
+ goto error_free_codec;
+ }
+
+ /* Disable auto-mute. This feature appears to be buggy. In some
+ * situations, auto-mute will not deactivate when it should, so we want
+ * this feature disabled by default. An application (e.g. alsactl) can
+ * re-enabled it by using the controls.
+ */
+
+ reg = cs4270_read_reg_cache(codec, CS4270_MUTE);
+ reg &= ~CS4270_MUTE_AUTO;
+ ret = cs4270_i2c_write(codec, CS4270_MUTE, reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "i2c write failed\n");
+ return ret;
+ }
+
+ /* Disable automatic volume control. The hardware enables, and it
+ * causes volume change commands to be delayed, sometimes until after
+ * playback has started. An application (e.g. alsactl) can
+ * re-enabled it by using the controls.
+ */
+
+ reg = cs4270_read_reg_cache(codec, CS4270_TRANS);
+ reg &= ~(CS4270_TRANS_SOFT | CS4270_TRANS_ZERO);
+ ret = cs4270_i2c_write(codec, CS4270_TRANS, reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "i2c write failed\n");
+ return ret;
+ }
+
+ /* Initialize the DAI. Normally, we'd prefer to have a kmalloc'd DAI
+ * structure for each CS4270 device, but the machine driver needs to
+ * have a pointer to the DAI structure, so for now it must be a global
+ * variable.
+ */
+ cs4270_dai.dev = &i2c_client->dev;
+
+ /* Register the DAI. If all the other ASoC driver have already
+ * registered, then this will call our probe function, so
+ * cs4270_codec needs to be ready.
+ */
+ cs4270_codec = codec;
+ ret = snd_soc_register_dai(&cs4270_dai);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "failed to register DAIe\n");
+ goto error_free_codec;
+ }
+
+ i2c_set_clientdata(i2c_client, cs4270);
+
+ return 0;
+
+error_free_codec:
+ kfree(cs4270);
+ cs4270_codec = NULL;
+ cs4270_dai.dev = NULL;
+
+ return ret;
+}
+
+/**
+ * cs4270_i2c_remove - remove an I2C device
+ * @i2c_client: the I2C client object
+ *
+ * This function is the counterpart to cs4270_i2c_probe().
+ */
+static int cs4270_i2c_remove(struct i2c_client *i2c_client)
+{
+ struct cs4270_private *cs4270 = i2c_get_clientdata(i2c_client);
+
+ kfree(cs4270);
+ cs4270_codec = NULL;
+ cs4270_dai.dev = NULL;
return 0;
}
/*
+ * cs4270_id - I2C device IDs supported by this driver
+ */
+static struct i2c_device_id cs4270_id[] = {
+ {"cs4270", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs4270_id);
+
+/*
+ * cs4270_i2c_driver - I2C device identification
+ *
+ * This structure tells the I2C subsystem how to identify and support a
+ * given I2C device type.
+ */
+static struct i2c_driver cs4270_i2c_driver = {
+ .driver = {
+ .name = "cs4270",
+ .owner = THIS_MODULE,
+ },
+ .id_table = cs4270_id,
+ .probe = cs4270_i2c_probe,
+ .remove = cs4270_i2c_remove,
+};
+
+/*
* ASoC codec device structure
*
* Assign this variable to the codec_dev field of the machine driver's
@@ -776,13 +783,15 @@
static int __init cs4270_init(void)
{
- return snd_soc_register_dai(&cs4270_dai);
+ pr_info("Cirrus Logic CS4270 ALSA SoC Codec Driver\n");
+
+ return i2c_add_driver(&cs4270_i2c_driver);
}
module_init(cs4270_init);
static void __exit cs4270_exit(void)
{
- snd_soc_unregister_dai(&cs4270_dai);
+ i2c_del_driver(&cs4270_i2c_driver);
}
module_exit(cs4270_exit);
diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c
index 9a3e67e..5cda9e6 100644
--- a/sound/soc/codecs/pcm3008.c
+++ b/sound/soc/codecs/pcm3008.c
@@ -67,11 +67,11 @@
printk(KERN_INFO "PCM3008 SoC Audio Codec %s\n", PCM3008_VERSION);
- socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (!socdev->codec)
+ socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (!socdev->card->codec)
return -ENOMEM;
- codec = socdev->codec;
+ codec = socdev->card->codec;
mutex_init(&codec->mutex);
codec->name = "PCM3008";
@@ -139,7 +139,7 @@
card_err:
snd_soc_free_pcms(socdev);
pcm_err:
- kfree(socdev->codec);
+ kfree(socdev->card->codec);
return ret;
}
@@ -147,7 +147,7 @@
static int pcm3008_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct pcm3008_setup_data *setup = socdev->codec_data;
if (!codec)
@@ -155,7 +155,7 @@
pcm3008_gpio_free(setup);
snd_soc_free_pcms(socdev);
- kfree(socdev->codec);
+ kfree(socdev->card->codec);
return 0;
}
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index cac3736..87f606c7 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -151,21 +151,6 @@
SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]),
};
-/* add non dapm controls */
-static int ssm2602_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(ssm2602_snd_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&ssm2602_snd_controls[i], codec, NULL));
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
/* Output Mixer */
static const struct snd_kcontrol_new ssm2602_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0),
@@ -291,7 +276,7 @@
u16 srate;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct ssm2602_priv *ssm2602 = codec->private_data;
struct i2c_client *i2c = codec->control_data;
u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3;
@@ -336,7 +321,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct ssm2602_priv *ssm2602 = codec->private_data;
struct i2c_client *i2c = codec->control_data;
struct snd_pcm_runtime *master_runtime;
@@ -373,7 +358,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
/* set active */
ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
@@ -385,7 +370,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct ssm2602_priv *ssm2602 = codec->private_data;
/* deactivate */
if (!codec->active)
@@ -521,6 +506,16 @@
#define SSM2602_FORMATS (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 ssm2602_dai_ops = {
+ .startup = ssm2602_startup,
+ .prepare = ssm2602_pcm_prepare,
+ .hw_params = ssm2602_hw_params,
+ .shutdown = ssm2602_shutdown,
+ .digital_mute = ssm2602_mute,
+ .set_sysclk = ssm2602_set_dai_sysclk,
+ .set_fmt = ssm2602_set_dai_fmt,
+};
+
struct snd_soc_dai ssm2602_dai = {
.name = "SSM2602",
.playback = {
@@ -535,22 +530,14 @@
.channels_max = 2,
.rates = SSM2602_RATES,
.formats = SSM2602_FORMATS,},
- .ops = {
- .startup = ssm2602_startup,
- .prepare = ssm2602_pcm_prepare,
- .hw_params = ssm2602_hw_params,
- .shutdown = ssm2602_shutdown,
- .digital_mute = ssm2602_mute,
- .set_sysclk = ssm2602_set_dai_sysclk,
- .set_fmt = ssm2602_set_dai_fmt,
- }
+ .ops = &ssm2602_dai_ops,
};
EXPORT_SYMBOL_GPL(ssm2602_dai);
static int ssm2602_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@@ -559,7 +546,7 @@
static int ssm2602_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
@@ -581,7 +568,7 @@
*/
static int ssm2602_init(struct snd_soc_device *socdev)
{
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int reg, ret = 0;
codec->name = "SSM2602";
@@ -622,7 +609,8 @@
APANA_ENABLE_MIC_BOOST);
ssm2602_write(codec, SSM2602_PWR, 0);
- ssm2602_add_controls(codec);
+ snd_soc_add_controls(codec, ssm2602_snd_controls,
+ ARRAY_SIZE(ssm2602_snd_controls));
ssm2602_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@@ -653,7 +641,7 @@
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = ssm2602_socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@@ -747,7 +735,7 @@
}
codec->private_data = ssm2602;
- socdev->codec = codec;
+ socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -768,7 +756,7 @@
static int ssm2602_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF);
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index cfdea00..c3f4afb 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -183,24 +183,6 @@
SOC_ENUM("Playback De-emphasis", tlv320aic23_deemph),
};
-/* add non dapm controls */
-static int tlv320aic23_add_controls(struct snd_soc_codec *codec)
-{
-
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(tlv320aic23_snd_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&tlv320aic23_snd_controls[i],
- codec, NULL));
- if (err < 0)
- return err;
- }
-
- return 0;
-
-}
-
/* PGA Mixer controls for Line and Mic switch */
static const struct snd_kcontrol_new tlv320aic23_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", TLV320AIC23_ANLG, 3, 1, 0),
@@ -423,7 +405,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
u16 iface_reg;
int ret;
struct aic23 *aic23 = container_of(codec, struct aic23, codec);
@@ -471,7 +453,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
/* set active */
tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0001);
@@ -484,7 +466,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct aic23 *aic23 = container_of(codec, struct aic23, codec);
/* deactivate */
@@ -598,6 +580,15 @@
#define AIC23_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+static struct snd_soc_dai_ops tlv320aic23_dai_ops = {
+ .prepare = tlv320aic23_pcm_prepare,
+ .hw_params = tlv320aic23_hw_params,
+ .shutdown = tlv320aic23_shutdown,
+ .digital_mute = tlv320aic23_mute,
+ .set_fmt = tlv320aic23_set_dai_fmt,
+ .set_sysclk = tlv320aic23_set_dai_sysclk,
+};
+
struct snd_soc_dai tlv320aic23_dai = {
.name = "tlv320aic23",
.playback = {
@@ -612,14 +603,7 @@
.channels_max = 2,
.rates = AIC23_RATES,
.formats = AIC23_FORMATS,},
- .ops = {
- .prepare = tlv320aic23_pcm_prepare,
- .hw_params = tlv320aic23_hw_params,
- .shutdown = tlv320aic23_shutdown,
- .digital_mute = tlv320aic23_mute,
- .set_fmt = tlv320aic23_set_dai_fmt,
- .set_sysclk = tlv320aic23_set_dai_sysclk,
- }
+ .ops = &tlv320aic23_dai_ops,
};
EXPORT_SYMBOL_GPL(tlv320aic23_dai);
@@ -627,7 +611,7 @@
pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0);
tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
@@ -638,7 +622,7 @@
static int tlv320aic23_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int i;
u16 reg;
@@ -660,7 +644,7 @@
*/
static int tlv320aic23_init(struct snd_soc_device *socdev)
{
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
u16 reg;
@@ -718,7 +702,8 @@
tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x1);
- tlv320aic23_add_controls(codec);
+ snd_soc_add_controls(codec, tlv320aic23_snd_controls,
+ ARRAY_SIZE(tlv320aic23_snd_controls));
tlv320aic23_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@@ -746,7 +731,7 @@
const struct i2c_device_id *i2c_id)
{
struct snd_soc_device *socdev = tlv320aic23_socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret;
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -804,7 +789,7 @@
if (aic23 == NULL)
return -ENOMEM;
codec = &aic23->codec;
- socdev->codec = codec;
+ socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -823,7 +808,7 @@
static int tlv320aic23_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct aic23 *aic23 = container_of(codec, struct aic23, codec);
if (codec->control_data)
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
index 29f2f1a..3387d9e 100644
--- a/sound/soc/codecs/tlv320aic26.c
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -130,7 +130,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct aic26 *aic26 = codec->private_data;
int fsref, divisor, wlen, pval, jval, dval, qval;
u16 reg;
@@ -270,6 +270,13 @@
#define AIC26_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |\
SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
+static struct snd_soc_dai_ops aic26_dai_ops = {
+ .hw_params = aic26_hw_params,
+ .digital_mute = aic26_mute,
+ .set_sysclk = aic26_set_sysclk,
+ .set_fmt = aic26_set_fmt,
+};
+
struct snd_soc_dai aic26_dai = {
.name = "tlv320aic26",
.playback = {
@@ -286,12 +293,7 @@
.rates = AIC26_RATES,
.formats = AIC26_FORMATS,
},
- .ops = {
- .hw_params = aic26_hw_params,
- .digital_mute = aic26_mute,
- .set_sysclk = aic26_set_sysclk,
- .set_fmt = aic26_set_fmt,
- },
+ .ops = &aic26_dai_ops,
};
EXPORT_SYMBOL_GPL(aic26_dai);
@@ -322,9 +324,8 @@
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
- struct snd_kcontrol *kcontrol;
struct aic26 *aic26;
- int i, ret, err;
+ int ret, err;
dev_info(&pdev->dev, "Probing AIC26 SoC CODEC driver\n");
dev_dbg(&pdev->dev, "socdev=%p\n", socdev);
@@ -338,7 +339,7 @@
return -ENODEV;
}
codec = &aic26->codec;
- socdev->codec = codec;
+ socdev->card->codec = codec;
dev_dbg(&pdev->dev, "Registering PCMs, dev=%p, socdev->dev=%p\n",
&pdev->dev, socdev->dev);
@@ -351,11 +352,9 @@
/* register controls */
dev_dbg(&pdev->dev, "Registering controls\n");
- for (i = 0; i < ARRAY_SIZE(aic26_snd_controls); i++) {
- kcontrol = snd_soc_cnew(&aic26_snd_controls[i], codec, NULL);
- err = snd_ctl_add(codec->card, kcontrol);
- WARN_ON(err < 0);
- }
+ err = snd_soc_add_controls(codec, aic26_snd_controls,
+ ARRAY_SIZE(aic26_snd_controls));
+ WARN_ON(err < 0);
/* CODEC is setup, we can register the card now */
dev_dbg(&pdev->dev, "Registering card\n");
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index aea0cb7..ab099f4 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -45,6 +45,7 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
+#include <sound/tlv.h>
#include "tlv320aic3x.h"
@@ -250,56 +251,86 @@
SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 6, 4, 4, aic3x_adc_hpf),
};
+/*
+ * DAC digital volumes. From -63.5 to 0 dB in 0.5 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(dac_tlv, -6350, 50, 0);
+/* ADC PGA gain volumes. From 0 to 59.5 dB in 0.5 dB steps */
+static DECLARE_TLV_DB_SCALE(adc_tlv, 0, 50, 0);
+/*
+ * Output stage volumes. From -78.3 to 0 dB. Muted below -78.3 dB.
+ * Step size is approximately 0.5 dB over most of the scale but increasing
+ * near the very low levels.
+ * Define dB scale so that it is mostly correct for range about -55 to 0 dB
+ * but having increasing dB difference below that (and where it doesn't count
+ * so much). This setting shows -50 dB (actual is -50.3 dB) for register
+ * value 100 and -58.5 dB (actual is -78.3 dB) for register value 117.
+ */
+static DECLARE_TLV_DB_SCALE(output_stage_tlv, -5900, 50, 1);
+
static const struct snd_kcontrol_new aic3x_snd_controls[] = {
/* Output */
- SOC_DOUBLE_R("PCM Playback Volume", LDAC_VOL, RDAC_VOL, 0, 0x7f, 1),
+ SOC_DOUBLE_R_TLV("PCM Playback Volume",
+ LDAC_VOL, RDAC_VOL, 0, 0x7f, 1, dac_tlv),
- SOC_DOUBLE_R("Line DAC Playback Volume", DACL1_2_LLOPM_VOL,
- DACR1_2_RLOPM_VOL, 0, 0x7f, 1),
+ SOC_DOUBLE_R_TLV("Line DAC Playback Volume",
+ DACL1_2_LLOPM_VOL, DACR1_2_RLOPM_VOL,
+ 0, 118, 1, output_stage_tlv),
SOC_SINGLE("LineL Playback Switch", LLOPM_CTRL, 3, 0x01, 0),
SOC_SINGLE("LineR Playback Switch", RLOPM_CTRL, 3, 0x01, 0),
- SOC_DOUBLE_R("LineL DAC Playback Volume", DACL1_2_LLOPM_VOL,
- DACR1_2_LLOPM_VOL, 0, 0x7f, 1),
- SOC_SINGLE("LineL Left PGA Bypass Playback Volume", PGAL_2_LLOPM_VOL,
- 0, 0x7f, 1),
- SOC_SINGLE("LineR Right PGA Bypass Playback Volume", PGAR_2_RLOPM_VOL,
- 0, 0x7f, 1),
- SOC_DOUBLE_R("LineL Line2 Bypass Playback Volume", LINE2L_2_LLOPM_VOL,
- LINE2R_2_LLOPM_VOL, 0, 0x7f, 1),
- SOC_DOUBLE_R("LineR Line2 Bypass Playback Volume", LINE2L_2_RLOPM_VOL,
- LINE2R_2_RLOPM_VOL, 0, 0x7f, 1),
+ SOC_DOUBLE_R_TLV("LineL DAC Playback Volume",
+ DACL1_2_LLOPM_VOL, DACR1_2_LLOPM_VOL,
+ 0, 118, 1, output_stage_tlv),
+ SOC_SINGLE_TLV("LineL Left PGA Bypass Playback Volume",
+ PGAL_2_LLOPM_VOL, 0, 118, 1, output_stage_tlv),
+ SOC_SINGLE_TLV("LineR Right PGA Bypass Playback Volume",
+ PGAR_2_RLOPM_VOL, 0, 118, 1, output_stage_tlv),
+ SOC_DOUBLE_R_TLV("LineL Line2 Bypass Playback Volume",
+ LINE2L_2_LLOPM_VOL, LINE2R_2_LLOPM_VOL,
+ 0, 118, 1, output_stage_tlv),
+ SOC_DOUBLE_R_TLV("LineR Line2 Bypass Playback Volume",
+ LINE2L_2_RLOPM_VOL, LINE2R_2_RLOPM_VOL,
+ 0, 118, 1, output_stage_tlv),
- SOC_DOUBLE_R("Mono DAC Playback Volume", DACL1_2_MONOLOPM_VOL,
- DACR1_2_MONOLOPM_VOL, 0, 0x7f, 1),
+ SOC_DOUBLE_R_TLV("Mono DAC Playback Volume",
+ DACL1_2_MONOLOPM_VOL, DACR1_2_MONOLOPM_VOL,
+ 0, 118, 1, output_stage_tlv),
SOC_SINGLE("Mono DAC Playback Switch", MONOLOPM_CTRL, 3, 0x01, 0),
- SOC_DOUBLE_R("Mono PGA Bypass Playback Volume", PGAL_2_MONOLOPM_VOL,
- PGAR_2_MONOLOPM_VOL, 0, 0x7f, 1),
- SOC_DOUBLE_R("Mono Line2 Bypass Playback Volume", LINE2L_2_MONOLOPM_VOL,
- LINE2R_2_MONOLOPM_VOL, 0, 0x7f, 1),
+ SOC_DOUBLE_R_TLV("Mono PGA Bypass Playback Volume",
+ PGAL_2_MONOLOPM_VOL, PGAR_2_MONOLOPM_VOL,
+ 0, 118, 1, output_stage_tlv),
+ SOC_DOUBLE_R_TLV("Mono Line2 Bypass Playback Volume",
+ LINE2L_2_MONOLOPM_VOL, LINE2R_2_MONOLOPM_VOL,
+ 0, 118, 1, output_stage_tlv),
- SOC_DOUBLE_R("HP DAC Playback Volume", DACL1_2_HPLOUT_VOL,
- DACR1_2_HPROUT_VOL, 0, 0x7f, 1),
+ SOC_DOUBLE_R_TLV("HP DAC Playback Volume",
+ DACL1_2_HPLOUT_VOL, DACR1_2_HPROUT_VOL,
+ 0, 118, 1, output_stage_tlv),
SOC_DOUBLE_R("HP DAC Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3,
0x01, 0),
- SOC_DOUBLE_R("HP Right PGA Bypass Playback Volume", PGAR_2_HPLOUT_VOL,
- PGAR_2_HPROUT_VOL, 0, 0x7f, 1),
- SOC_SINGLE("HPL PGA Bypass Playback Volume", PGAL_2_HPLOUT_VOL,
- 0, 0x7f, 1),
- SOC_SINGLE("HPR PGA Bypass Playback Volume", PGAL_2_HPROUT_VOL,
- 0, 0x7f, 1),
- SOC_DOUBLE_R("HP Line2 Bypass Playback Volume", LINE2L_2_HPLOUT_VOL,
- LINE2R_2_HPROUT_VOL, 0, 0x7f, 1),
+ SOC_DOUBLE_R_TLV("HP Right PGA Bypass Playback Volume",
+ PGAR_2_HPLOUT_VOL, PGAR_2_HPROUT_VOL,
+ 0, 118, 1, output_stage_tlv),
+ SOC_SINGLE_TLV("HPL PGA Bypass Playback Volume",
+ PGAL_2_HPLOUT_VOL, 0, 118, 1, output_stage_tlv),
+ SOC_SINGLE_TLV("HPR PGA Bypass Playback Volume",
+ PGAL_2_HPROUT_VOL, 0, 118, 1, output_stage_tlv),
+ SOC_DOUBLE_R_TLV("HP Line2 Bypass Playback Volume",
+ LINE2L_2_HPLOUT_VOL, LINE2R_2_HPROUT_VOL,
+ 0, 118, 1, output_stage_tlv),
- SOC_DOUBLE_R("HPCOM DAC Playback Volume", DACL1_2_HPLCOM_VOL,
- DACR1_2_HPRCOM_VOL, 0, 0x7f, 1),
+ SOC_DOUBLE_R_TLV("HPCOM DAC Playback Volume",
+ DACL1_2_HPLCOM_VOL, DACR1_2_HPRCOM_VOL,
+ 0, 118, 1, output_stage_tlv),
SOC_DOUBLE_R("HPCOM DAC Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3,
0x01, 0),
- SOC_SINGLE("HPLCOM PGA Bypass Playback Volume", PGAL_2_HPLCOM_VOL,
- 0, 0x7f, 1),
- SOC_SINGLE("HPRCOM PGA Bypass Playback Volume", PGAL_2_HPRCOM_VOL,
- 0, 0x7f, 1),
- SOC_DOUBLE_R("HPCOM Line2 Bypass Playback Volume", LINE2L_2_HPLCOM_VOL,
- LINE2R_2_HPRCOM_VOL, 0, 0x7f, 1),
+ SOC_SINGLE_TLV("HPLCOM PGA Bypass Playback Volume",
+ PGAL_2_HPLCOM_VOL, 0, 118, 1, output_stage_tlv),
+ SOC_SINGLE_TLV("HPRCOM PGA Bypass Playback Volume",
+ PGAL_2_HPRCOM_VOL, 0, 118, 1, output_stage_tlv),
+ SOC_DOUBLE_R_TLV("HPCOM Line2 Bypass Playback Volume",
+ LINE2L_2_HPLCOM_VOL, LINE2R_2_HPRCOM_VOL,
+ 0, 118, 1, output_stage_tlv),
/*
* Note: enable Automatic input Gain Controller with care. It can
@@ -308,28 +339,13 @@
SOC_DOUBLE_R("AGC Switch", LAGC_CTRL_A, RAGC_CTRL_A, 7, 0x01, 0),
/* Input */
- SOC_DOUBLE_R("PGA Capture Volume", LADC_VOL, RADC_VOL, 0, 0x7f, 0),
+ SOC_DOUBLE_R_TLV("PGA Capture Volume", LADC_VOL, RADC_VOL,
+ 0, 119, 0, adc_tlv),
SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1),
SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]),
};
-/* add non dapm controls */
-static int aic3x_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(aic3x_snd_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&aic3x_snd_controls[i],
- codec, NULL));
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
/* Left DAC Mux */
static const struct snd_kcontrol_new aic3x_left_dac_mux_controls =
SOC_DAPM_ENUM("Route", aic3x_enum[LDAC_ENUM]);
@@ -746,7 +762,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct aic3x_priv *aic3x = codec->private_data;
int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
u8 data, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
@@ -1072,6 +1088,13 @@
#define AIC3X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+static struct snd_soc_dai_ops aic3x_dai_ops = {
+ .hw_params = aic3x_hw_params,
+ .digital_mute = aic3x_mute,
+ .set_sysclk = aic3x_set_dai_sysclk,
+ .set_fmt = aic3x_set_dai_fmt,
+};
+
struct snd_soc_dai aic3x_dai = {
.name = "tlv320aic3x",
.playback = {
@@ -1086,19 +1109,14 @@
.channels_max = 2,
.rates = AIC3X_RATES,
.formats = AIC3X_FORMATS,},
- .ops = {
- .hw_params = aic3x_hw_params,
- .digital_mute = aic3x_mute,
- .set_sysclk = aic3x_set_dai_sysclk,
- .set_fmt = aic3x_set_dai_fmt,
- }
+ .ops = &aic3x_dai_ops,
};
EXPORT_SYMBOL_GPL(aic3x_dai);
static int aic3x_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
@@ -1108,7 +1126,7 @@
static int aic3x_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u8 *cache = codec->reg_cache;
@@ -1131,7 +1149,7 @@
*/
static int aic3x_init(struct snd_soc_device *socdev)
{
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct aic3x_setup_data *setup = socdev->codec_data;
int reg, ret = 0;
@@ -1227,7 +1245,8 @@
aic3x_write(codec, AIC3X_GPIO1_REG, (setup->gpio_func[0] & 0xf) << 4);
aic3x_write(codec, AIC3X_GPIO2_REG, (setup->gpio_func[1] & 0xf) << 4);
- aic3x_add_controls(codec);
+ snd_soc_add_controls(codec, aic3x_snd_controls,
+ ARRAY_SIZE(aic3x_snd_controls));
aic3x_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@@ -1261,7 +1280,7 @@
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = aic3x_socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@@ -1366,7 +1385,7 @@
}
codec->private_data = aic3x;
- socdev->codec = codec;
+ socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -1392,7 +1411,7 @@
static int aic3x_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
/* power down chip */
if (codec->control_data)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index ea370a4..97738e2 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -42,7 +42,7 @@
*/
static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
0x00, /* this register not used */
- 0x93, /* REG_CODEC_MODE (0x1) */
+ 0x91, /* REG_CODEC_MODE (0x1) */
0xc3, /* REG_OPTION (0x2) */
0x00, /* REG_UNKNOWN (0x3) */
0x00, /* REG_MICBIAS_CTL (0x4) */
@@ -117,6 +117,13 @@
0x00, /* REG_MISC_SET_2 (0x49) */
};
+/* codec private data */
+struct twl4030_priv {
+ unsigned int bypass_state;
+ unsigned int codec_powered;
+ unsigned int codec_muted;
+};
+
/*
* read twl4030 register cache
*/
@@ -125,6 +132,9 @@
{
u8 *cache = codec->reg_cache;
+ if (reg >= TWL4030_CACHEREGNUM)
+ return -EIO;
+
return cache[reg];
}
@@ -151,26 +161,22 @@
return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg);
}
-static void twl4030_clear_codecpdz(struct snd_soc_codec *codec)
+static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
{
+ struct twl4030_priv *twl4030 = codec->private_data;
u8 mode;
- mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
- twl4030_write(codec, TWL4030_REG_CODEC_MODE,
- mode & ~TWL4030_CODECPDZ);
-
- /* REVISIT: this delay is present in TI sample drivers */
- /* but there seems to be no TRM requirement for it */
- udelay(10);
-}
-
-static void twl4030_set_codecpdz(struct snd_soc_codec *codec)
-{
- u8 mode;
+ if (enable == twl4030->codec_powered)
+ return;
mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
- twl4030_write(codec, TWL4030_REG_CODEC_MODE,
- mode | TWL4030_CODECPDZ);
+ if (enable)
+ mode |= TWL4030_CODECPDZ;
+ else
+ mode &= ~TWL4030_CODECPDZ;
+
+ twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+ twl4030->codec_powered = enable;
/* REVISIT: this delay is present in TI sample drivers */
/* but there seems to be no TRM requirement for it */
@@ -182,7 +188,7 @@
int i;
/* clear CODECPDZ prior to setting register defaults */
- twl4030_clear_codecpdz(codec);
+ twl4030_codec_enable(codec, 0);
/* set all audio section registers to reasonable defaults */
for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++)
@@ -190,6 +196,122 @@
}
+static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute)
+{
+ struct twl4030_priv *twl4030 = codec->private_data;
+ u8 reg_val;
+
+ if (mute == twl4030->codec_muted)
+ return;
+
+ if (mute) {
+ /* Bypass the reg_cache and mute the volumes
+ * Headset mute is done in it's own event handler
+ * Things to mute: Earpiece, PreDrivL/R, CarkitL/R
+ */
+ reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL);
+ twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ reg_val & (~TWL4030_EAR_GAIN),
+ TWL4030_REG_EAR_CTL);
+
+ reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL);
+ twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ reg_val & (~TWL4030_PREDL_GAIN),
+ TWL4030_REG_PREDL_CTL);
+ reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL);
+ twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ reg_val & (~TWL4030_PREDR_GAIN),
+ TWL4030_REG_PREDL_CTL);
+
+ reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL);
+ twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ reg_val & (~TWL4030_PRECKL_GAIN),
+ TWL4030_REG_PRECKL_CTL);
+ reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL);
+ twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ reg_val & (~TWL4030_PRECKL_GAIN),
+ TWL4030_REG_PRECKR_CTL);
+
+ /* Disable PLL */
+ reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
+ reg_val &= ~TWL4030_APLL_EN;
+ twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
+ } else {
+ /* Restore the volumes
+ * Headset mute is done in it's own event handler
+ * Things to restore: Earpiece, PreDrivL/R, CarkitL/R
+ */
+ twl4030_write(codec, TWL4030_REG_EAR_CTL,
+ twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL));
+
+ twl4030_write(codec, TWL4030_REG_PREDL_CTL,
+ twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL));
+ twl4030_write(codec, TWL4030_REG_PREDR_CTL,
+ twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL));
+
+ twl4030_write(codec, TWL4030_REG_PRECKL_CTL,
+ twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL));
+ twl4030_write(codec, TWL4030_REG_PRECKR_CTL,
+ twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL));
+
+ /* Enable PLL */
+ reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
+ reg_val |= TWL4030_APLL_EN;
+ twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
+ }
+
+ twl4030->codec_muted = mute;
+}
+
+static void twl4030_power_up(struct snd_soc_codec *codec)
+{
+ struct twl4030_priv *twl4030 = codec->private_data;
+ u8 anamicl, regmisc1, byte;
+ int i = 0;
+
+ if (twl4030->codec_powered)
+ return;
+
+ /* set CODECPDZ to turn on codec */
+ twl4030_codec_enable(codec, 1);
+
+ /* initiate offset cancellation */
+ anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
+ twl4030_write(codec, TWL4030_REG_ANAMICL,
+ anamicl | TWL4030_CNCL_OFFSET_START);
+
+ /* wait for offset cancellation to complete */
+ do {
+ /* this takes a little while, so don't slam i2c */
+ udelay(2000);
+ twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
+ TWL4030_REG_ANAMICL);
+ } while ((i++ < 100) &&
+ ((byte & TWL4030_CNCL_OFFSET_START) ==
+ TWL4030_CNCL_OFFSET_START));
+
+ /* Make sure that the reg_cache has the same value as the HW */
+ twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte);
+
+ /* anti-pop when changing analog gain */
+ regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1);
+ twl4030_write(codec, TWL4030_REG_MISC_SET_1,
+ regmisc1 | TWL4030_SMOOTH_ANAVOL_EN);
+
+ /* toggle CODECPDZ as per TRM */
+ twl4030_codec_enable(codec, 0);
+ twl4030_codec_enable(codec, 1);
+}
+
+/*
+ * Unconditional power down
+ */
+static void twl4030_power_down(struct snd_soc_codec *codec)
+{
+ /* power down */
+ twl4030_codec_enable(codec, 0);
+}
+
/* Earpiece */
static const char *twl4030_earpiece_texts[] =
{"Off", "DACL1", "DACL2", "DACR1"};
@@ -366,6 +488,41 @@
static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control =
SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum);
+/* Analog bypass for AudioR1 */
+static const struct snd_kcontrol_new twl4030_dapm_abypassr1_control =
+ SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR1_APGA_CTL, 2, 1, 0);
+
+/* Analog bypass for AudioL1 */
+static const struct snd_kcontrol_new twl4030_dapm_abypassl1_control =
+ SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL1_APGA_CTL, 2, 1, 0);
+
+/* Analog bypass for AudioR2 */
+static const struct snd_kcontrol_new twl4030_dapm_abypassr2_control =
+ SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR2_APGA_CTL, 2, 1, 0);
+
+/* Analog bypass for AudioL2 */
+static const struct snd_kcontrol_new twl4030_dapm_abypassl2_control =
+ SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL2_APGA_CTL, 2, 1, 0);
+
+/* Digital bypass gain, 0 mutes the bypass */
+static const unsigned int twl4030_dapm_dbypass_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 3, TLV_DB_SCALE_ITEM(-2400, 0, 1),
+ 4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0),
+};
+
+/* Digital bypass left (TX1L -> RX2L) */
+static const struct snd_kcontrol_new twl4030_dapm_dbypassl_control =
+ SOC_DAPM_SINGLE_TLV("Volume",
+ TWL4030_REG_ATX2ARXPGA, 3, 7, 0,
+ twl4030_dapm_dbypass_tlv);
+
+/* Digital bypass right (TX1R -> RX2R) */
+static const struct snd_kcontrol_new twl4030_dapm_dbypassr_control =
+ SOC_DAPM_SINGLE_TLV("Volume",
+ TWL4030_REG_ATX2ARXPGA, 0, 7, 0,
+ twl4030_dapm_dbypass_tlv);
+
static int micpath_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -420,6 +577,79 @@
return 0;
}
+static int headsetl_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ unsigned char hs_gain, hs_pop;
+
+ /* Save the current volume */
+ hs_gain = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_GAIN_SET);
+ hs_pop = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_POPN_SET);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Do the anti-pop/bias ramp enable according to the TRM */
+ hs_pop |= TWL4030_VMID_EN;
+ twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ /* Is this needed? Can we just use whatever gain here? */
+ twl4030_write(w->codec, TWL4030_REG_HS_GAIN_SET,
+ (hs_gain & (~0x0f)) | 0x0a);
+ hs_pop |= TWL4030_RAMP_EN;
+ twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+
+ /* Restore the original volume */
+ twl4030_write(w->codec, TWL4030_REG_HS_GAIN_SET, hs_gain);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Do the anti-pop/bias ramp disable according to the TRM */
+ hs_pop &= ~TWL4030_RAMP_EN;
+ twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ /* Bypass the reg_cache to mute the headset */
+ twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ hs_gain & (~0x0f),
+ TWL4030_REG_HS_GAIN_SET);
+ hs_pop &= ~TWL4030_VMID_EN;
+ twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ break;
+ }
+ return 0;
+}
+
+static int bypass_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct soc_mixer_control *m =
+ (struct soc_mixer_control *)w->kcontrols->private_value;
+ struct twl4030_priv *twl4030 = w->codec->private_data;
+ unsigned char reg;
+
+ reg = twl4030_read_reg_cache(w->codec, m->reg);
+
+ if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) {
+ /* Analog bypass */
+ if (reg & (1 << m->shift))
+ twl4030->bypass_state |=
+ (1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));
+ else
+ twl4030->bypass_state &=
+ ~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));
+ } else {
+ /* Digital bypass */
+ if (reg & (0x7 << m->shift))
+ twl4030->bypass_state |= (1 << (m->shift ? 5 : 4));
+ else
+ twl4030->bypass_state &= ~(1 << (m->shift ? 5 : 4));
+ }
+
+ if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) {
+ if (twl4030->bypass_state)
+ twl4030_codec_mute(w->codec, 0);
+ else
+ twl4030_codec_mute(w->codec, 1);
+ }
+ return 0;
+}
+
/*
* Some of the gain controls in TWL (mostly those which are associated with
* the outputs) are implemented in an interesting way:
@@ -614,6 +844,17 @@
*/
static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0);
+static const char *twl4030_rampdelay_texts[] = {
+ "27/20/14 ms", "55/40/27 ms", "109/81/55 ms", "218/161/109 ms",
+ "437/323/218 ms", "874/645/437 ms", "1748/1291/874 ms",
+ "3495/2581/1748 ms"
+};
+
+static const struct soc_enum twl4030_rampdelay_enum =
+ SOC_ENUM_SINGLE(TWL4030_REG_HS_POPN_SET, 2,
+ ARRAY_SIZE(twl4030_rampdelay_texts),
+ twl4030_rampdelay_texts);
+
static const struct snd_kcontrol_new twl4030_snd_controls[] = {
/* Common playback gain controls */
SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume",
@@ -668,24 +909,10 @@
SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN,
0, 3, 5, 0, input_gain_tlv),
+
+ SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum),
};
-/* add non dapm controls */
-static int twl4030_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(twl4030_snd_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&twl4030_snd_controls[i],
- codec, NULL));
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
/* Left channel inputs */
SND_SOC_DAPM_INPUT("MAINMIC"),
@@ -714,13 +941,13 @@
/* DACs */
SND_SOC_DAPM_DAC("DAC Right1", "Right Front Playback",
- TWL4030_REG_AVDAC_CTL, 0, 0),
+ SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DAC Left1", "Left Front Playback",
- TWL4030_REG_AVDAC_CTL, 1, 0),
+ SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DAC Right2", "Right Rear Playback",
- TWL4030_REG_AVDAC_CTL, 2, 0),
+ SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DAC Left2", "Left Rear Playback",
- TWL4030_REG_AVDAC_CTL, 3, 0),
+ SND_SOC_NOPM, 0, 0),
/* Analog PGAs */
SND_SOC_DAPM_PGA("ARXR1_APGA", TWL4030_REG_ARXR1_APGA_CTL,
@@ -732,6 +959,37 @@
SND_SOC_DAPM_PGA("ARXL2_APGA", TWL4030_REG_ARXL2_APGA_CTL,
0, 0, NULL, 0),
+ /* Analog bypasses */
+ SND_SOC_DAPM_SWITCH_E("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassr1_control, bypass_event,
+ SND_SOC_DAPM_POST_REG),
+ SND_SOC_DAPM_SWITCH_E("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassl1_control,
+ bypass_event, SND_SOC_DAPM_POST_REG),
+ SND_SOC_DAPM_SWITCH_E("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassr2_control,
+ bypass_event, SND_SOC_DAPM_POST_REG),
+ SND_SOC_DAPM_SWITCH_E("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassl2_control,
+ bypass_event, SND_SOC_DAPM_POST_REG),
+
+ /* Digital bypasses */
+ SND_SOC_DAPM_SWITCH_E("Left Digital Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_dbypassl_control, bypass_event,
+ SND_SOC_DAPM_POST_REG),
+ SND_SOC_DAPM_SWITCH_E("Right Digital Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_dbypassr_control, bypass_event,
+ SND_SOC_DAPM_POST_REG),
+
+ SND_SOC_DAPM_MIXER("Analog R1 Playback Mixer", TWL4030_REG_AVDAC_CTL,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Analog L1 Playback Mixer", TWL4030_REG_AVDAC_CTL,
+ 1, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Analog R2 Playback Mixer", TWL4030_REG_AVDAC_CTL,
+ 2, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("Analog L2 Playback Mixer", TWL4030_REG_AVDAC_CTL,
+ 3, 0, NULL, 0),
+
/* Output MUX controls */
/* Earpiece */
SND_SOC_DAPM_VALUE_MUX("Earpiece Mux", SND_SOC_NOPM, 0, 0,
@@ -742,8 +1000,9 @@
SND_SOC_DAPM_VALUE_MUX("PredriveR Mux", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_predriver_control),
/* HeadsetL/R */
- SND_SOC_DAPM_MUX("HeadsetL Mux", SND_SOC_NOPM, 0, 0,
- &twl4030_dapm_hsol_control),
+ SND_SOC_DAPM_MUX_E("HeadsetL Mux", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_hsol_control, headsetl_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MUX("HeadsetR Mux", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_hsor_control),
/* CarkitL/R */
@@ -782,16 +1041,16 @@
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD|
SND_SOC_DAPM_POST_REG),
- /* Analog input muxes with power switch for the physical ADCL/R */
+ /* Analog input muxes with switch for the capture amplifiers */
SND_SOC_DAPM_VALUE_MUX("Analog Left Capture Route",
- TWL4030_REG_AVADC_CTL, 3, 0, &twl4030_dapm_analoglmic_control),
+ TWL4030_REG_ANAMICL, 4, 0, &twl4030_dapm_analoglmic_control),
SND_SOC_DAPM_VALUE_MUX("Analog Right Capture Route",
- TWL4030_REG_AVADC_CTL, 1, 0, &twl4030_dapm_analogrmic_control),
+ TWL4030_REG_ANAMICR, 4, 0, &twl4030_dapm_analogrmic_control),
- SND_SOC_DAPM_PGA("Analog Left Amplifier",
- TWL4030_REG_ANAMICL, 4, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Analog Right Amplifier",
- TWL4030_REG_ANAMICR, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC Physical Left",
+ TWL4030_REG_AVADC_CTL, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC Physical Right",
+ TWL4030_REG_AVADC_CTL, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("Digimic0 Enable",
TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0),
@@ -801,13 +1060,19 @@
SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0),
SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0),
SND_SOC_DAPM_MICBIAS("Headset Mic Bias", TWL4030_REG_MICBIAS_CTL, 2, 0),
+
};
static const struct snd_soc_dapm_route intercon[] = {
- {"ARXL1_APGA", NULL, "DAC Left1"},
- {"ARXR1_APGA", NULL, "DAC Right1"},
- {"ARXL2_APGA", NULL, "DAC Left2"},
- {"ARXR2_APGA", NULL, "DAC Right2"},
+ {"Analog L1 Playback Mixer", NULL, "DAC Left1"},
+ {"Analog R1 Playback Mixer", NULL, "DAC Right1"},
+ {"Analog L2 Playback Mixer", NULL, "DAC Left2"},
+ {"Analog R2 Playback Mixer", NULL, "DAC Right2"},
+
+ {"ARXL1_APGA", NULL, "Analog L1 Playback Mixer"},
+ {"ARXR1_APGA", NULL, "Analog R1 Playback Mixer"},
+ {"ARXL2_APGA", NULL, "Analog L2 Playback Mixer"},
+ {"ARXR2_APGA", NULL, "Analog R2 Playback Mixer"},
/* Internal playback routings */
/* Earpiece */
@@ -865,23 +1130,23 @@
{"Analog Right Capture Route", "Sub mic", "SUBMIC"},
{"Analog Right Capture Route", "AUXR", "AUXR"},
- {"Analog Left Amplifier", NULL, "Analog Left Capture Route"},
- {"Analog Right Amplifier", NULL, "Analog Right Capture Route"},
+ {"ADC Physical Left", NULL, "Analog Left Capture Route"},
+ {"ADC Physical Right", NULL, "Analog Right Capture Route"},
{"Digimic0 Enable", NULL, "DIGIMIC0"},
{"Digimic1 Enable", NULL, "DIGIMIC1"},
/* TX1 Left capture path */
- {"TX1 Capture Route", "Analog", "Analog Left Amplifier"},
+ {"TX1 Capture Route", "Analog", "ADC Physical Left"},
{"TX1 Capture Route", "Digimic0", "Digimic0 Enable"},
/* TX1 Right capture path */
- {"TX1 Capture Route", "Analog", "Analog Right Amplifier"},
+ {"TX1 Capture Route", "Analog", "ADC Physical Right"},
{"TX1 Capture Route", "Digimic0", "Digimic0 Enable"},
/* TX2 Left capture path */
- {"TX2 Capture Route", "Analog", "Analog Left Amplifier"},
+ {"TX2 Capture Route", "Analog", "ADC Physical Left"},
{"TX2 Capture Route", "Digimic1", "Digimic1 Enable"},
/* TX2 Right capture path */
- {"TX2 Capture Route", "Analog", "Analog Right Amplifier"},
+ {"TX2 Capture Route", "Analog", "ADC Physical Right"},
{"TX2 Capture Route", "Digimic1", "Digimic1 Enable"},
{"ADC Virtual Left1", NULL, "TX1 Capture Route"},
@@ -889,6 +1154,24 @@
{"ADC Virtual Left2", NULL, "TX2 Capture Route"},
{"ADC Virtual Right2", NULL, "TX2 Capture Route"},
+ /* Analog bypass routes */
+ {"Right1 Analog Loopback", "Switch", "Analog Right Capture Route"},
+ {"Left1 Analog Loopback", "Switch", "Analog Left Capture Route"},
+ {"Right2 Analog Loopback", "Switch", "Analog Right Capture Route"},
+ {"Left2 Analog Loopback", "Switch", "Analog Left Capture Route"},
+
+ {"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"},
+ {"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"},
+ {"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"},
+ {"Analog L2 Playback Mixer", NULL, "Left2 Analog Loopback"},
+
+ /* Digital bypass routes */
+ {"Right Digital Loopback", "Volume", "TX1 Capture Route"},
+ {"Left Digital Loopback", "Volume", "TX1 Capture Route"},
+
+ {"Analog R2 Playback Mixer", NULL, "Right Digital Loopback"},
+ {"Analog L2 Playback Mixer", NULL, "Left Digital Loopback"},
+
};
static int twl4030_add_widgets(struct snd_soc_codec *codec)
@@ -902,82 +1185,28 @@
return 0;
}
-static void twl4030_power_up(struct snd_soc_codec *codec)
-{
- u8 anamicl, regmisc1, byte, popn;
- int i = 0;
-
- /* set CODECPDZ to turn on codec */
- twl4030_set_codecpdz(codec);
-
- /* initiate offset cancellation */
- anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
- twl4030_write(codec, TWL4030_REG_ANAMICL,
- anamicl | TWL4030_CNCL_OFFSET_START);
-
-
- /* wait for offset cancellation to complete */
- do {
- /* this takes a little while, so don't slam i2c */
- udelay(2000);
- twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
- TWL4030_REG_ANAMICL);
- } while ((i++ < 100) &&
- ((byte & TWL4030_CNCL_OFFSET_START) ==
- TWL4030_CNCL_OFFSET_START));
-
- /* anti-pop when changing analog gain */
- regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1);
- twl4030_write(codec, TWL4030_REG_MISC_SET_1,
- regmisc1 | TWL4030_SMOOTH_ANAVOL_EN);
-
- /* toggle CODECPDZ as per TRM */
- twl4030_clear_codecpdz(codec);
- twl4030_set_codecpdz(codec);
-
- /* program anti-pop with bias ramp delay */
- popn = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
- popn &= TWL4030_RAMP_DELAY;
- popn |= TWL4030_RAMP_DELAY_645MS;
- twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
- popn |= TWL4030_VMID_EN;
- twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
-
- /* enable anti-pop ramp */
- popn |= TWL4030_RAMP_EN;
- twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
-}
-
-static void twl4030_power_down(struct snd_soc_codec *codec)
-{
- u8 popn;
-
- /* disable anti-pop ramp */
- popn = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
- popn &= ~TWL4030_RAMP_EN;
- twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
-
- /* disable bias out */
- popn &= ~TWL4030_VMID_EN;
- twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
-
- /* power down */
- twl4030_clear_codecpdz(codec);
-}
-
static int twl4030_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct twl4030_priv *twl4030 = codec->private_data;
+
switch (level) {
case SND_SOC_BIAS_ON:
- twl4030_power_up(codec);
+ twl4030_codec_mute(codec, 0);
break;
case SND_SOC_BIAS_PREPARE:
- /* TODO: develop a twl4030_prepare function */
+ twl4030_power_up(codec);
+ if (twl4030->bypass_state)
+ twl4030_codec_mute(codec, 0);
+ else
+ twl4030_codec_mute(codec, 1);
break;
case SND_SOC_BIAS_STANDBY:
- /* TODO: develop a twl4030_standby function */
- twl4030_power_down(codec);
+ twl4030_power_up(codec);
+ if (twl4030->bypass_state)
+ twl4030_codec_mute(codec, 0);
+ else
+ twl4030_codec_mute(codec, 1);
break;
case SND_SOC_BIAS_OFF:
twl4030_power_down(codec);
@@ -994,10 +1223,9 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
u8 mode, old_mode, format, old_format;
-
/* bit rate */
old_mode = twl4030_read_reg_cache(codec,
TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
@@ -1039,8 +1267,9 @@
if (mode != old_mode) {
/* change rate and set CODECPDZ */
+ twl4030_codec_enable(codec, 0);
twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
- twl4030_set_codecpdz(codec);
+ twl4030_codec_enable(codec, 1);
}
/* sample size */
@@ -1063,13 +1292,13 @@
if (format != old_format) {
/* clear CODECPDZ before changing format (codec requirement) */
- twl4030_clear_codecpdz(codec);
+ twl4030_codec_enable(codec, 0);
/* change format */
twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
/* set CODECPDZ afterwards */
- twl4030_set_codecpdz(codec);
+ twl4030_codec_enable(codec, 1);
}
return 0;
}
@@ -1139,13 +1368,13 @@
if (format != old_format) {
/* clear CODECPDZ before changing format (codec requirement) */
- twl4030_clear_codecpdz(codec);
+ twl4030_codec_enable(codec, 0);
/* change format */
twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
/* set CODECPDZ afterwards */
- twl4030_set_codecpdz(codec);
+ twl4030_codec_enable(codec, 1);
}
return 0;
@@ -1154,6 +1383,12 @@
#define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000)
#define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE)
+static struct snd_soc_dai_ops twl4030_dai_ops = {
+ .hw_params = twl4030_hw_params,
+ .set_sysclk = twl4030_set_dai_sysclk,
+ .set_fmt = twl4030_set_dai_fmt,
+};
+
struct snd_soc_dai twl4030_dai = {
.name = "twl4030",
.playback = {
@@ -1168,18 +1403,14 @@
.channels_max = 2,
.rates = TWL4030_RATES,
.formats = TWL4030_FORMATS,},
- .ops = {
- .hw_params = twl4030_hw_params,
- .set_sysclk = twl4030_set_dai_sysclk,
- .set_fmt = twl4030_set_dai_fmt,
- }
+ .ops = &twl4030_dai_ops,
};
EXPORT_SYMBOL_GPL(twl4030_dai);
static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
@@ -1189,7 +1420,7 @@
static int twl4030_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
twl4030_set_bias_level(codec, codec->suspend_bias_level);
@@ -1203,7 +1434,7 @@
static int twl4030_init(struct snd_soc_device *socdev)
{
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
printk(KERN_INFO "TWL4030 Audio Codec init \n");
@@ -1233,7 +1464,8 @@
/* power on device */
twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- twl4030_add_controls(codec);
+ snd_soc_add_controls(codec, twl4030_snd_controls,
+ ARRAY_SIZE(twl4030_snd_controls));
twl4030_add_widgets(codec);
ret = snd_soc_init_card(socdev);
@@ -1258,12 +1490,20 @@
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
+ struct twl4030_priv *twl4030;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
- socdev->codec = codec;
+ twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL);
+ if (twl4030 == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+
+ codec->private_data = twl4030;
+ socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -1277,11 +1517,13 @@
static int twl4030_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
printk(KERN_INFO "TWL4030 Audio Codec remove\n");
+ twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
+ kfree(codec->private_data);
kfree(codec);
return 0;
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
index 442e5a8..33dbb14 100644
--- a/sound/soc/codecs/twl4030.h
+++ b/sound/soc/codecs/twl4030.h
@@ -170,6 +170,9 @@
#define TWL4030_CLK256FS_EN 0x02
#define TWL4030_AIF_EN 0x01
+/* EAR_CTL (0x21) */
+#define TWL4030_EAR_GAIN 0x30
+
/* HS_GAIN_SET (0x23) Fields */
#define TWL4030_HSR_GAIN 0x0C
@@ -198,6 +201,18 @@
#define TWL4030_RAMP_DELAY_2581MS 0x1C
#define TWL4030_RAMP_EN 0x02
+/* PREDL_CTL (0x25) */
+#define TWL4030_PREDL_GAIN 0x30
+
+/* PREDR_CTL (0x26) */
+#define TWL4030_PREDR_GAIN 0x30
+
+/* PRECKL_CTL (0x27) */
+#define TWL4030_PRECKL_GAIN 0x30
+
+/* PRECKR_CTL (0x28) */
+#define TWL4030_PRECKR_GAIN 0x30
+
/* HFL_CTL (0x29, 0x2A) Fields */
#define TWL4030_HF_CTL_HB_EN 0x04
#define TWL4030_HF_CTL_LOOP_EN 0x08
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index a2c5064..ddefb8f 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -173,7 +173,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct uda134x_priv *uda134x = codec->private_data;
struct snd_pcm_runtime *master_runtime;
@@ -206,7 +206,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct uda134x_priv *uda134x = codec->private_data;
if (uda134x->master_substream == substream)
@@ -221,7 +221,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct uda134x_priv *uda134x = codec->private_data;
u8 hw_params;
@@ -431,38 +431,14 @@
SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
};
-static int uda134x_add_controls(struct snd_soc_codec *codec)
-{
- int err, i, n;
- const struct snd_kcontrol_new *ctrls;
- struct uda134x_platform_data *pd = codec->control_data;
-
- switch (pd->model) {
- case UDA134X_UDA1340:
- case UDA134X_UDA1344:
- n = ARRAY_SIZE(uda1340_snd_controls);
- ctrls = uda1340_snd_controls;
- break;
- case UDA134X_UDA1341:
- n = ARRAY_SIZE(uda1341_snd_controls);
- ctrls = uda1341_snd_controls;
- break;
- default:
- printk(KERN_ERR "%s unkown codec type: %d",
- __func__, pd->model);
- return -EINVAL;
- }
-
- for (i = 0; i < n; i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&ctrls[i],
- codec, NULL));
- if (err < 0)
- return err;
- }
-
- return 0;
-}
+static struct snd_soc_dai_ops uda134x_dai_ops = {
+ .startup = uda134x_startup,
+ .shutdown = uda134x_shutdown,
+ .hw_params = uda134x_hw_params,
+ .digital_mute = uda134x_mute,
+ .set_sysclk = uda134x_set_dai_sysclk,
+ .set_fmt = uda134x_set_dai_fmt,
+};
struct snd_soc_dai uda134x_dai = {
.name = "UDA134X",
@@ -483,14 +459,7 @@
.formats = UDA134X_FORMATS,
},
/* pcm operations */
- .ops = {
- .startup = uda134x_startup,
- .shutdown = uda134x_shutdown,
- .hw_params = uda134x_hw_params,
- .digital_mute = uda134x_mute,
- .set_sysclk = uda134x_set_dai_sysclk,
- .set_fmt = uda134x_set_dai_fmt,
- }
+ .ops = &uda134x_dai_ops,
};
EXPORT_SYMBOL(uda134x_dai);
@@ -525,11 +494,11 @@
return -EINVAL;
}
- socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (socdev->codec == NULL)
+ socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (socdev->card->codec == NULL)
return ret;
- codec = socdev->codec;
+ codec = socdev->card->codec;
uda134x = kzalloc(sizeof(struct uda134x_priv), GFP_KERNEL);
if (uda134x == NULL)
@@ -572,7 +541,22 @@
goto pcm_err;
}
- ret = uda134x_add_controls(codec);
+ switch (pd->model) {
+ case UDA134X_UDA1340:
+ case UDA134X_UDA1344:
+ ret = snd_soc_add_controls(codec, uda1340_snd_controls,
+ ARRAY_SIZE(uda1340_snd_controls));
+ break;
+ case UDA134X_UDA1341:
+ ret = snd_soc_add_controls(codec, uda1341_snd_controls,
+ ARRAY_SIZE(uda1341_snd_controls));
+ break;
+ default:
+ printk(KERN_ERR "%s unkown codec type: %d",
+ __func__, pd->model);
+ return -EINVAL;
+ }
+
if (ret < 0) {
printk(KERN_ERR "UDA134X: failed to register controls\n");
goto pcm_err;
@@ -602,7 +586,7 @@
static int uda134x_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
@@ -622,7 +606,7 @@
pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
@@ -632,7 +616,7 @@
static int uda134x_soc_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
uda134x_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
uda134x_set_bias_level(codec, SND_SOC_BIAS_ON);
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index e6bf084..5b21594 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -25,6 +25,7 @@
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/i2c.h>
+#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/initval.h>
@@ -35,7 +36,8 @@
#include "uda1380.h"
-#define UDA1380_VERSION "0.6"
+static struct work_struct uda1380_work;
+static struct snd_soc_codec *uda1380_codec;
/*
* uda1380 register cache
@@ -52,6 +54,8 @@
0x0000, 0x8000, 0x0002, 0x0000,
};
+static unsigned long uda1380_cache_dirty;
+
/*
* read uda1380 register cache
*/
@@ -73,8 +77,11 @@
u16 reg, unsigned int value)
{
u16 *cache = codec->reg_cache;
+
if (reg >= UDA1380_CACHEREGNUM)
return;
+ if ((reg >= 0x10) && (cache[reg] != value))
+ set_bit(reg - 0x10, &uda1380_cache_dirty);
cache[reg] = value;
}
@@ -113,6 +120,8 @@
(data[0]<<8) | data[1]);
return -EIO;
}
+ if (reg >= 0x10)
+ clear_bit(reg - 0x10, &uda1380_cache_dirty);
return 0;
} else
return -EIO;
@@ -120,6 +129,20 @@
#define uda1380_reset(c) uda1380_write(c, UDA1380_RESET, 0)
+static void uda1380_flush_work(struct work_struct *work)
+{
+ int bit, reg;
+
+ for_each_bit(bit, &uda1380_cache_dirty, UDA1380_CACHEREGNUM - 0x10) {
+ reg = 0x10 + bit;
+ pr_debug("uda1380: flush reg %x val %x:\n", reg,
+ uda1380_read_reg_cache(uda1380_codec, reg));
+ uda1380_write(uda1380_codec, reg,
+ uda1380_read_reg_cache(uda1380_codec, reg));
+ clear_bit(bit, &uda1380_cache_dirty);
+ }
+}
+
/* declarations of ALSA reg_elem_REAL controls */
static const char *uda1380_deemp[] = {
"None",
@@ -254,7 +277,6 @@
SOC_SINGLE("DAC Polarity inverting Switch", UDA1380_MIXER, 15, 1, 0), /* DA_POL_INV */
SOC_ENUM("Noise Shaper", uda1380_sel_ns_enum), /* SEL_NS */
SOC_ENUM("Digital Mixer Signal Control", uda1380_mix_enum), /* MIX_POS, MIX */
- SOC_SINGLE("Silence Switch", UDA1380_MIXER, 7, 1, 0), /* SILENCE, force DAC output to silence */
SOC_SINGLE("Silence Detector Switch", UDA1380_MIXER, 6, 1, 0), /* SDET_ON */
SOC_ENUM("Silence Detector Setting", uda1380_sdet_enum), /* SD_VALUE */
SOC_ENUM("Oversampling Input", uda1380_os_enum), /* OS */
@@ -271,21 +293,6 @@
SOC_SINGLE("AGC Switch", UDA1380_AGC, 0, 1, 0),
};
-/* add non dapm controls */
-static int uda1380_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(uda1380_snd_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&uda1380_snd_controls[i], codec, NULL));
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
/* Input mux */
static const struct snd_kcontrol_new uda1380_input_mux_control =
SOC_DAPM_ENUM("Route", uda1380_input_sel_enum);
@@ -371,7 +378,7 @@
return 0;
}
-static int uda1380_set_dai_fmt(struct snd_soc_dai *codec_dai,
+static int uda1380_set_dai_fmt_both(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -381,16 +388,75 @@
iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
iface &= ~(R01_SFORI_MASK | R01_SIM | R01_SFORO_MASK);
- /* FIXME: how to select I2S for DATAO and MSB for DATAI correctly? */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
iface |= R01_SFORI_I2S | R01_SFORO_I2S;
break;
case SND_SOC_DAIFMT_LSB:
- iface |= R01_SFORI_LSB16 | R01_SFORO_I2S;
+ iface |= R01_SFORI_LSB16 | R01_SFORO_LSB16;
break;
case SND_SOC_DAIFMT_MSB:
- iface |= R01_SFORI_MSB | R01_SFORO_I2S;
+ iface |= R01_SFORI_MSB | R01_SFORO_MSB;
+ }
+
+ /* DATAI is slave only, so in single-link mode, this has to be slave */
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
+ return -EINVAL;
+
+ uda1380_write(codec, UDA1380_IFACE, iface);
+
+ return 0;
+}
+
+static int uda1380_set_dai_fmt_playback(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int iface;
+
+ /* set up DAI based upon fmt */
+ iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
+ iface &= ~R01_SFORI_MASK;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= R01_SFORI_I2S;
+ break;
+ case SND_SOC_DAIFMT_LSB:
+ iface |= R01_SFORI_LSB16;
+ break;
+ case SND_SOC_DAIFMT_MSB:
+ iface |= R01_SFORI_MSB;
+ }
+
+ /* DATAI is slave only, so this has to be slave */
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
+ return -EINVAL;
+
+ uda1380_write(codec, UDA1380_IFACE, iface);
+
+ return 0;
+}
+
+static int uda1380_set_dai_fmt_capture(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int iface;
+
+ /* set up DAI based upon fmt */
+ iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
+ iface &= ~(R01_SIM | R01_SFORO_MASK);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= R01_SFORO_I2S;
+ break;
+ case SND_SOC_DAIFMT_LSB:
+ iface |= R01_SFORO_LSB16;
+ break;
+ case SND_SOC_DAIFMT_MSB:
+ iface |= R01_SFORO_MSB;
}
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)
@@ -401,41 +467,28 @@
return 0;
}
-/*
- * Flush reg cache
- * We can only write the interpolator and decimator registers
- * when the DAI is being clocked by the CPU DAI. It's up to the
- * machine and cpu DAI driver to do this before we are called.
- */
-static int uda1380_pcm_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
- int reg, reg_start, reg_end, clk;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- reg_start = UDA1380_MVOL;
- reg_end = UDA1380_MIXER;
- } else {
- reg_start = UDA1380_DEC;
- reg_end = UDA1380_AGC;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ uda1380_write_reg_cache(codec, UDA1380_MIXER,
+ mixer & ~R14_SILENCE);
+ schedule_work(&uda1380_work);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ uda1380_write_reg_cache(codec, UDA1380_MIXER,
+ mixer | R14_SILENCE);
+ schedule_work(&uda1380_work);
+ break;
}
-
- /* FIXME disable DAC_CLK */
- clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
- uda1380_write(codec, UDA1380_CLK, clk & ~R00_DAC_CLK);
-
- for (reg = reg_start; reg <= reg_end; reg++) {
- pr_debug("uda1380: flush reg %x val %x:", reg,
- uda1380_read_reg_cache(codec, reg));
- uda1380_write(codec, reg, uda1380_read_reg_cache(codec, reg));
- }
-
- /* FIXME enable DAC_CLK */
- uda1380_write(codec, UDA1380_CLK, clk | R00_DAC_CLK);
-
return 0;
}
@@ -445,7 +498,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
/* set WSPLL power and divider if running from this clock */
@@ -484,7 +537,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
/* shut down WSPLL power if running from this clock */
@@ -501,24 +554,6 @@
uda1380_write(codec, UDA1380_CLK, clk);
}
-static int uda1380_mute(struct snd_soc_dai *codec_dai, int mute)
-{
- struct snd_soc_codec *codec = codec_dai->codec;
- u16 mute_reg = uda1380_read_reg_cache(codec, UDA1380_DEEMP) & ~R13_MTM;
-
- /* FIXME: mute(codec,0) is called when the magician clock is already
- * set to WSPLL, but for some unknown reason writing to interpolator
- * registers works only when clocked by SYSCLK */
- u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
- uda1380_write(codec, UDA1380_CLK, ~R00_DAC_CLK & clk);
- if (mute)
- uda1380_write(codec, UDA1380_DEEMP, mute_reg | R13_MTM);
- else
- uda1380_write(codec, UDA1380_DEEMP, mute_reg);
- uda1380_write(codec, UDA1380_CLK, clk);
- return 0;
-}
-
static int uda1380_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
@@ -544,6 +579,27 @@
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+static struct snd_soc_dai_ops uda1380_dai_ops = {
+ .hw_params = uda1380_pcm_hw_params,
+ .shutdown = uda1380_pcm_shutdown,
+ .trigger = uda1380_trigger,
+ .set_fmt = uda1380_set_dai_fmt_both,
+};
+
+static struct snd_soc_dai_ops uda1380_dai_ops_playback = {
+ .hw_params = uda1380_pcm_hw_params,
+ .shutdown = uda1380_pcm_shutdown,
+ .trigger = uda1380_trigger,
+ .set_fmt = uda1380_set_dai_fmt_playback,
+};
+
+static struct snd_soc_dai_ops uda1380_dai_ops_capture = {
+ .hw_params = uda1380_pcm_hw_params,
+ .shutdown = uda1380_pcm_shutdown,
+ .trigger = uda1380_trigger,
+ .set_fmt = uda1380_set_dai_fmt_capture,
+};
+
struct snd_soc_dai uda1380_dai[] = {
{
.name = "UDA1380",
@@ -559,13 +615,7 @@
.channels_max = 2,
.rates = UDA1380_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = {
- .hw_params = uda1380_pcm_hw_params,
- .shutdown = uda1380_pcm_shutdown,
- .prepare = uda1380_pcm_prepare,
- .digital_mute = uda1380_mute,
- .set_fmt = uda1380_set_dai_fmt,
- },
+ .ops = &uda1380_dai_ops,
},
{ /* playback only - dual interface */
.name = "UDA1380",
@@ -576,13 +626,7 @@
.rates = UDA1380_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- .ops = {
- .hw_params = uda1380_pcm_hw_params,
- .shutdown = uda1380_pcm_shutdown,
- .prepare = uda1380_pcm_prepare,
- .digital_mute = uda1380_mute,
- .set_fmt = uda1380_set_dai_fmt,
- },
+ .ops = &uda1380_dai_ops_playback,
},
{ /* capture only - dual interface*/
.name = "UDA1380",
@@ -593,12 +637,7 @@
.rates = UDA1380_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- .ops = {
- .hw_params = uda1380_pcm_hw_params,
- .shutdown = uda1380_pcm_shutdown,
- .prepare = uda1380_pcm_prepare,
- .set_fmt = uda1380_set_dai_fmt,
- },
+ .ops = &uda1380_dai_ops_capture,
},
};
EXPORT_SYMBOL_GPL(uda1380_dai);
@@ -606,7 +645,7 @@
static int uda1380_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@@ -615,7 +654,7 @@
static int uda1380_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
@@ -637,7 +676,7 @@
*/
static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
{
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
codec->name = "UDA1380";
@@ -655,6 +694,9 @@
codec->reg_cache_step = 1;
uda1380_reset(codec);
+ uda1380_codec = codec;
+ INIT_WORK(&uda1380_work, uda1380_flush_work);
+
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
@@ -675,7 +717,8 @@
}
/* uda1380 init */
- uda1380_add_controls(codec);
+ snd_soc_add_controls(codec, uda1380_snd_controls,
+ ARRAY_SIZE(uda1380_snd_controls));
uda1380_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@@ -702,7 +745,7 @@
{
struct snd_soc_device *socdev = uda1380_socdev;
struct uda1380_setup_data *setup = socdev->codec_data;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@@ -786,14 +829,12 @@
struct snd_soc_codec *codec;
int ret;
- pr_info("UDA1380 Audio Codec %s", UDA1380_VERSION);
-
setup = socdev->codec_data;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
- socdev->codec = codec;
+ socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -817,7 +858,7 @@
static int uda1380_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index 35d9975..3b1d099 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -51,10 +51,17 @@
u16 mute;
};
+struct wm8350_jack_data {
+ struct snd_soc_jack *jack;
+ int report;
+};
+
struct wm8350_data {
struct snd_soc_codec codec;
struct wm8350_output out1;
struct wm8350_output out2;
+ struct wm8350_jack_data hpl;
+ struct wm8350_jack_data hpr;
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
};
@@ -775,21 +782,6 @@
{"Beep", NULL, "IN3R PGA"},
};
-static int wm8350_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(wm8350_snd_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8350_snd_controls[i],
- codec, NULL));
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
static int wm8350_add_widgets(struct snd_soc_codec *codec)
{
int ret;
@@ -1309,7 +1301,7 @@
static int wm8350_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@@ -1318,7 +1310,7 @@
static int wm8350_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -1328,6 +1320,95 @@
return 0;
}
+static void wm8350_hp_jack_handler(struct wm8350 *wm8350, int irq, void *data)
+{
+ struct wm8350_data *priv = data;
+ u16 reg;
+ int report;
+ int mask;
+ struct wm8350_jack_data *jack = NULL;
+
+ switch (irq) {
+ case WM8350_IRQ_CODEC_JCK_DET_L:
+ jack = &priv->hpl;
+ mask = WM8350_JACK_L_LVL;
+ break;
+
+ case WM8350_IRQ_CODEC_JCK_DET_R:
+ jack = &priv->hpr;
+ mask = WM8350_JACK_R_LVL;
+ break;
+
+ default:
+ BUG();
+ }
+
+ if (!jack->jack) {
+ dev_warn(wm8350->dev, "Jack interrupt called with no jack\n");
+ return;
+ }
+
+ /* Debounce */
+ msleep(200);
+
+ reg = wm8350_reg_read(wm8350, WM8350_JACK_PIN_STATUS);
+ if (reg & mask)
+ report = jack->report;
+ else
+ report = 0;
+
+ snd_soc_jack_report(jack->jack, report, jack->report);
+}
+
+/**
+ * wm8350_hp_jack_detect - Enable headphone jack detection.
+ *
+ * @codec: WM8350 codec
+ * @which: left or right jack detect signal
+ * @jack: jack to report detection events on
+ * @report: value to report
+ *
+ * Enables the headphone jack detection of the WM8350.
+ */
+int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
+ struct snd_soc_jack *jack, int report)
+{
+ struct wm8350_data *priv = codec->private_data;
+ struct wm8350 *wm8350 = codec->control_data;
+ int irq;
+ int ena;
+
+ switch (which) {
+ case WM8350_JDL:
+ priv->hpl.jack = jack;
+ priv->hpl.report = report;
+ irq = WM8350_IRQ_CODEC_JCK_DET_L;
+ ena = WM8350_JDL_ENA;
+ break;
+
+ case WM8350_JDR:
+ priv->hpr.jack = jack;
+ priv->hpr.report = report;
+ irq = WM8350_IRQ_CODEC_JCK_DET_R;
+ ena = WM8350_JDR_ENA;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
+ wm8350_set_bits(wm8350, WM8350_JACK_DETECT, ena);
+
+ /* Sync status */
+ wm8350_hp_jack_handler(wm8350, irq, priv);
+
+ wm8350_unmask_irq(wm8350, irq);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_hp_jack_detect);
+
static struct snd_soc_codec *wm8350_codec;
static int wm8350_probe(struct platform_device *pdev)
@@ -1342,8 +1423,8 @@
BUG_ON(!wm8350_codec);
- socdev->codec = wm8350_codec;
- codec = socdev->codec;
+ socdev->card->codec = wm8350_codec;
+ codec = socdev->card->codec;
wm8350 = codec->control_data;
priv = codec->private_data;
@@ -1381,13 +1462,21 @@
wm8350_set_bits(wm8350, WM8350_ROUT2_VOLUME,
WM8350_OUT2_VU | WM8350_OUT2R_MUTE);
+ wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L);
+ wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+ wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L,
+ wm8350_hp_jack_handler, priv);
+ wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R,
+ wm8350_hp_jack_handler, priv);
+
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
dev_err(&pdev->dev, "failed to create pcms\n");
return ret;
}
- wm8350_add_controls(codec);
+ snd_soc_add_controls(codec, wm8350_snd_controls,
+ ARRAY_SIZE(wm8350_snd_controls));
wm8350_add_widgets(codec);
wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -1409,10 +1498,23 @@
static int wm8350_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct wm8350 *wm8350 = codec->control_data;
+ struct wm8350_data *priv = codec->private_data;
int ret;
+ wm8350_clear_bits(wm8350, WM8350_JACK_DETECT,
+ WM8350_JDL_ENA | WM8350_JDR_ENA);
+ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
+
+ wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L);
+ wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+ wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L);
+ wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+
+ priv->hpl.jack = NULL;
+ priv->hpr.jack = NULL;
+
/* cancel any work waiting to be queued. */
ret = cancel_delayed_work(&codec->delayed_work);
@@ -1436,6 +1538,16 @@
SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static struct snd_soc_dai_ops wm8350_dai_ops = {
+ .hw_params = wm8350_pcm_hw_params,
+ .digital_mute = wm8350_mute,
+ .trigger = wm8350_pcm_trigger,
+ .set_fmt = wm8350_set_dai_fmt,
+ .set_sysclk = wm8350_set_dai_sysclk,
+ .set_pll = wm8350_set_fll,
+ .set_clkdiv = wm8350_set_clkdiv,
+};
+
struct snd_soc_dai wm8350_dai = {
.name = "WM8350",
.playback = {
@@ -1452,15 +1564,7 @@
.rates = WM8350_RATES,
.formats = WM8350_FORMATS,
},
- .ops = {
- .hw_params = wm8350_pcm_hw_params,
- .digital_mute = wm8350_mute,
- .trigger = wm8350_pcm_trigger,
- .set_fmt = wm8350_set_dai_fmt,
- .set_sysclk = wm8350_set_dai_sysclk,
- .set_pll = wm8350_set_fll,
- .set_clkdiv = wm8350_set_clkdiv,
- },
+ .ops = &wm8350_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8350_dai);
@@ -1472,7 +1576,7 @@
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8350);
-static int wm8350_codec_probe(struct platform_device *pdev)
+static __devinit int wm8350_codec_probe(struct platform_device *pdev)
{
struct wm8350 *wm8350 = platform_get_drvdata(pdev);
struct wm8350_data *priv;
diff --git a/sound/soc/codecs/wm8350.h b/sound/soc/codecs/wm8350.h
index cc2887a..d11bd92 100644
--- a/sound/soc/codecs/wm8350.h
+++ b/sound/soc/codecs/wm8350.h
@@ -17,4 +17,12 @@
extern struct snd_soc_dai wm8350_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8350;
+enum wm8350_jack {
+ WM8350_JDL = 1,
+ WM8350_JDR = 2,
+};
+
+int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
+ struct snd_soc_jack *jack, int report);
+
#endif
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
new file mode 100644
index 0000000..510efa6
--- /dev/null
+++ b/sound/soc/codecs/wm8400.c
@@ -0,0 +1,1582 @@
+/*
+ * wm8400.c -- WM8400 ALSA Soc Audio driver
+ *
+ * Copyright 2008, 2009 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/wm8400-audio.h>
+#include <linux/mfd/wm8400-private.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8400.h"
+
+/* Fake register for internal state */
+#define WM8400_INTDRIVBITS (WM8400_REGISTER_COUNT + 1)
+#define WM8400_INMIXL_PWR 0
+#define WM8400_AINLMUX_PWR 1
+#define WM8400_INMIXR_PWR 2
+#define WM8400_AINRMUX_PWR 3
+
+static struct regulator_bulk_data power[] = {
+ {
+ .supply = "I2S1VDD",
+ },
+ {
+ .supply = "I2S2VDD",
+ },
+ {
+ .supply = "DCVDD",
+ },
+ {
+ .supply = "AVDD",
+ },
+ {
+ .supply = "FLLVDD",
+ },
+ {
+ .supply = "HPVDD",
+ },
+ {
+ .supply = "SPKVDD",
+ },
+};
+
+/* codec private data */
+struct wm8400_priv {
+ struct snd_soc_codec codec;
+ struct wm8400 *wm8400;
+ u16 fake_register;
+ unsigned int sysclk;
+ unsigned int pcmclk;
+ struct work_struct work;
+ int fll_in, fll_out;
+};
+
+static inline unsigned int wm8400_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct wm8400_priv *wm8400 = codec->private_data;
+
+ if (reg == WM8400_INTDRIVBITS)
+ return wm8400->fake_register;
+ else
+ return wm8400_reg_read(wm8400->wm8400, reg);
+}
+
+/*
+ * write to the wm8400 register space
+ */
+static int wm8400_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct wm8400_priv *wm8400 = codec->private_data;
+
+ if (reg == WM8400_INTDRIVBITS) {
+ wm8400->fake_register = value;
+ return 0;
+ } else
+ return wm8400_set_bits(wm8400->wm8400, reg, 0xffff, value);
+}
+
+static void wm8400_codec_reset(struct snd_soc_codec *codec)
+{
+ struct wm8400_priv *wm8400 = codec->private_data;
+
+ wm8400_reset_codec_reg_cache(wm8400->wm8400);
+}
+
+static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
+
+static const DECLARE_TLV_DB_LINEAR(in_pga_tlv, -1650, 3000);
+
+static const DECLARE_TLV_DB_LINEAR(out_mix_tlv, -2100, 0);
+
+static const DECLARE_TLV_DB_LINEAR(out_pga_tlv, -7300, 600);
+
+static const DECLARE_TLV_DB_LINEAR(out_omix_tlv, -600, 0);
+
+static const DECLARE_TLV_DB_LINEAR(out_dac_tlv, -7163, 0);
+
+static const DECLARE_TLV_DB_LINEAR(in_adc_tlv, -7163, 1763);
+
+static const DECLARE_TLV_DB_LINEAR(out_sidetone_tlv, -3600, 0);
+
+static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int reg = mc->reg;
+ int ret;
+ u16 val;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ /* now hit the volume update bits (always bit 8) */
+ val = wm8400_read(codec, reg);
+ return wm8400_write(codec, reg, val | 0x0100);
+}
+
+#define WM8400_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .tlv.p = (tlv_array), \
+ .info = snd_soc_info_volsw, \
+ .get = snd_soc_get_volsw, .put = wm8400_outpga_put_volsw_vu, \
+ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+
+static const char *wm8400_digital_sidetone[] =
+ {"None", "Left ADC", "Right ADC", "Reserved"};
+
+static const struct soc_enum wm8400_left_digital_sidetone_enum =
+SOC_ENUM_SINGLE(WM8400_DIGITAL_SIDE_TONE,
+ WM8400_ADC_TO_DACL_SHIFT, 2, wm8400_digital_sidetone);
+
+static const struct soc_enum wm8400_right_digital_sidetone_enum =
+SOC_ENUM_SINGLE(WM8400_DIGITAL_SIDE_TONE,
+ WM8400_ADC_TO_DACR_SHIFT, 2, wm8400_digital_sidetone);
+
+static const char *wm8400_adcmode[] =
+ {"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"};
+
+static const struct soc_enum wm8400_right_adcmode_enum =
+SOC_ENUM_SINGLE(WM8400_ADC_CTRL, WM8400_ADC_HPF_CUT_SHIFT, 3, wm8400_adcmode);
+
+static const struct snd_kcontrol_new wm8400_snd_controls[] = {
+/* INMIXL */
+SOC_SINGLE("LIN12 PGA Boost", WM8400_INPUT_MIXER3, WM8400_L12MNBST_SHIFT,
+ 1, 0),
+SOC_SINGLE("LIN34 PGA Boost", WM8400_INPUT_MIXER3, WM8400_L34MNBST_SHIFT,
+ 1, 0),
+/* INMIXR */
+SOC_SINGLE("RIN12 PGA Boost", WM8400_INPUT_MIXER3, WM8400_R12MNBST_SHIFT,
+ 1, 0),
+SOC_SINGLE("RIN34 PGA Boost", WM8400_INPUT_MIXER3, WM8400_R34MNBST_SHIFT,
+ 1, 0),
+
+/* LOMIX */
+SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8400_OUTPUT_MIXER3,
+ WM8400_LLI3LOVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER3,
+ WM8400_LR12LOVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER3,
+ WM8400_LL12LOVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8400_OUTPUT_MIXER5,
+ WM8400_LRI3LOVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8400_OUTPUT_MIXER5,
+ WM8400_LRBLOVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8400_OUTPUT_MIXER5,
+ WM8400_LRBLOVOL_SHIFT, 7, 0, out_mix_tlv),
+
+/* ROMIX */
+SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8400_OUTPUT_MIXER4,
+ WM8400_RRI3ROVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER4,
+ WM8400_RL12ROVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER4,
+ WM8400_RR12ROVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8400_OUTPUT_MIXER6,
+ WM8400_RLI3ROVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8400_OUTPUT_MIXER6,
+ WM8400_RLBROVOL_SHIFT, 7, 0, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8400_OUTPUT_MIXER6,
+ WM8400_RRBROVOL_SHIFT, 7, 0, out_mix_tlv),
+
+/* LOUT */
+WM8400_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8400_LEFT_OUTPUT_VOLUME,
+ WM8400_LOUTVOL_SHIFT, WM8400_LOUTVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("LOUT ZC", WM8400_LEFT_OUTPUT_VOLUME, WM8400_LOZC_SHIFT, 1, 0),
+
+/* ROUT */
+WM8400_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8400_RIGHT_OUTPUT_VOLUME,
+ WM8400_ROUTVOL_SHIFT, WM8400_ROUTVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("ROUT ZC", WM8400_RIGHT_OUTPUT_VOLUME, WM8400_ROZC_SHIFT, 1, 0),
+
+/* LOPGA */
+WM8400_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8400_LEFT_OPGA_VOLUME,
+ WM8400_LOPGAVOL_SHIFT, WM8400_LOPGAVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("LOPGA ZC Switch", WM8400_LEFT_OPGA_VOLUME,
+ WM8400_LOPGAZC_SHIFT, 1, 0),
+
+/* ROPGA */
+WM8400_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8400_RIGHT_OPGA_VOLUME,
+ WM8400_ROPGAVOL_SHIFT, WM8400_ROPGAVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("ROPGA ZC Switch", WM8400_RIGHT_OPGA_VOLUME,
+ WM8400_ROPGAZC_SHIFT, 1, 0),
+
+SOC_SINGLE("LON Mute Switch", WM8400_LINE_OUTPUTS_VOLUME,
+ WM8400_LONMUTE_SHIFT, 1, 0),
+SOC_SINGLE("LOP Mute Switch", WM8400_LINE_OUTPUTS_VOLUME,
+ WM8400_LOPMUTE_SHIFT, 1, 0),
+SOC_SINGLE("LOP Attenuation Switch", WM8400_LINE_OUTPUTS_VOLUME,
+ WM8400_LOATTN_SHIFT, 1, 0),
+SOC_SINGLE("RON Mute Switch", WM8400_LINE_OUTPUTS_VOLUME,
+ WM8400_RONMUTE_SHIFT, 1, 0),
+SOC_SINGLE("ROP Mute Switch", WM8400_LINE_OUTPUTS_VOLUME,
+ WM8400_ROPMUTE_SHIFT, 1, 0),
+SOC_SINGLE("ROP Attenuation Switch", WM8400_LINE_OUTPUTS_VOLUME,
+ WM8400_ROATTN_SHIFT, 1, 0),
+
+SOC_SINGLE("OUT3 Mute Switch", WM8400_OUT3_4_VOLUME,
+ WM8400_OUT3MUTE_SHIFT, 1, 0),
+SOC_SINGLE("OUT3 Attenuation Switch", WM8400_OUT3_4_VOLUME,
+ WM8400_OUT3ATTN_SHIFT, 1, 0),
+
+SOC_SINGLE("OUT4 Mute Switch", WM8400_OUT3_4_VOLUME,
+ WM8400_OUT4MUTE_SHIFT, 1, 0),
+SOC_SINGLE("OUT4 Attenuation Switch", WM8400_OUT3_4_VOLUME,
+ WM8400_OUT4ATTN_SHIFT, 1, 0),
+
+SOC_SINGLE("Speaker Mode Switch", WM8400_CLASSD1,
+ WM8400_CDMODE_SHIFT, 1, 0),
+
+SOC_SINGLE("Speaker Output Attenuation Volume", WM8400_SPEAKER_VOLUME,
+ WM8400_SPKATTN_SHIFT, WM8400_SPKATTN_MASK, 0),
+SOC_SINGLE("Speaker DC Boost Volume", WM8400_CLASSD3,
+ WM8400_DCGAIN_SHIFT, 6, 0),
+SOC_SINGLE("Speaker AC Boost Volume", WM8400_CLASSD3,
+ WM8400_ACGAIN_SHIFT, 6, 0),
+
+WM8400_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume",
+ WM8400_LEFT_DAC_DIGITAL_VOLUME, WM8400_DACL_VOL_SHIFT,
+ 127, 0, out_dac_tlv),
+
+WM8400_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume",
+ WM8400_RIGHT_DAC_DIGITAL_VOLUME, WM8400_DACR_VOL_SHIFT,
+ 127, 0, out_dac_tlv),
+
+SOC_ENUM("Left Digital Sidetone", wm8400_left_digital_sidetone_enum),
+SOC_ENUM("Right Digital Sidetone", wm8400_right_digital_sidetone_enum),
+
+SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8400_DIGITAL_SIDE_TONE,
+ WM8400_ADCL_DAC_SVOL_SHIFT, 15, 0, out_sidetone_tlv),
+SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8400_DIGITAL_SIDE_TONE,
+ WM8400_ADCR_DAC_SVOL_SHIFT, 15, 0, out_sidetone_tlv),
+
+SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8400_ADC_CTRL,
+ WM8400_ADC_HPF_ENA_SHIFT, 1, 0),
+
+SOC_ENUM("ADC HPF Mode", wm8400_right_adcmode_enum),
+
+WM8400_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume",
+ WM8400_LEFT_ADC_DIGITAL_VOLUME,
+ WM8400_ADCL_VOL_SHIFT,
+ WM8400_ADCL_VOL_MASK,
+ 0,
+ in_adc_tlv),
+
+WM8400_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume",
+ WM8400_RIGHT_ADC_DIGITAL_VOLUME,
+ WM8400_ADCR_VOL_SHIFT,
+ WM8400_ADCR_VOL_MASK,
+ 0,
+ in_adc_tlv),
+
+WM8400_OUTPGA_SINGLE_R_TLV("LIN12 Volume",
+ WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8400_LIN12VOL_SHIFT,
+ WM8400_LIN12VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("LIN12 ZC Switch", WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8400_LI12ZC_SHIFT, 1, 0),
+
+SOC_SINGLE("LIN12 Mute Switch", WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8400_LI12MUTE_SHIFT, 1, 0),
+
+WM8400_OUTPGA_SINGLE_R_TLV("LIN34 Volume",
+ WM8400_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8400_LIN34VOL_SHIFT,
+ WM8400_LIN34VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("LIN34 ZC Switch", WM8400_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8400_LI34ZC_SHIFT, 1, 0),
+
+SOC_SINGLE("LIN34 Mute Switch", WM8400_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8400_LI34MUTE_SHIFT, 1, 0),
+
+WM8400_OUTPGA_SINGLE_R_TLV("RIN12 Volume",
+ WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8400_RIN12VOL_SHIFT,
+ WM8400_RIN12VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("RIN12 ZC Switch", WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8400_RI12ZC_SHIFT, 1, 0),
+
+SOC_SINGLE("RIN12 Mute Switch", WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8400_RI12MUTE_SHIFT, 1, 0),
+
+WM8400_OUTPGA_SINGLE_R_TLV("RIN34 Volume",
+ WM8400_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8400_RIN34VOL_SHIFT,
+ WM8400_RIN34VOL_MASK,
+ 0,
+ in_pga_tlv),
+
+SOC_SINGLE("RIN34 ZC Switch", WM8400_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8400_RI34ZC_SHIFT, 1, 0),
+
+SOC_SINGLE("RIN34 Mute Switch", WM8400_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8400_RI34MUTE_SHIFT, 1, 0),
+
+};
+
+/* add non dapm controls */
+static int wm8400_add_controls(struct snd_soc_codec *codec)
+{
+ return snd_soc_add_controls(codec, wm8400_snd_controls,
+ ARRAY_SIZE(wm8400_snd_controls));
+}
+
+/*
+ * _DAPM_ Controls
+ */
+
+static int inmixer_event (struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ u16 reg, fakepower;
+
+ reg = wm8400_read(w->codec, WM8400_POWER_MANAGEMENT_2);
+ fakepower = wm8400_read(w->codec, WM8400_INTDRIVBITS);
+
+ if (fakepower & ((1 << WM8400_INMIXL_PWR) |
+ (1 << WM8400_AINLMUX_PWR))) {
+ reg |= WM8400_AINL_ENA;
+ } else {
+ reg &= ~WM8400_AINL_ENA;
+ }
+
+ if (fakepower & ((1 << WM8400_INMIXR_PWR) |
+ (1 << WM8400_AINRMUX_PWR))) {
+ reg |= WM8400_AINR_ENA;
+ } else {
+ reg &= ~WM8400_AINL_ENA;
+ }
+ wm8400_write(w->codec, WM8400_POWER_MANAGEMENT_2, reg);
+
+ return 0;
+}
+
+static int outmixer_event (struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol * kcontrol, int event)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ u32 reg_shift = mc->shift;
+ int ret = 0;
+ u16 reg;
+
+ switch (reg_shift) {
+ case WM8400_SPEAKER_MIXER | (WM8400_LDSPK << 8) :
+ reg = wm8400_read(w->codec, WM8400_OUTPUT_MIXER1);
+ if (reg & WM8400_LDLO) {
+ printk(KERN_WARNING
+ "Cannot set as Output Mixer 1 LDLO Set\n");
+ ret = -1;
+ }
+ break;
+ case WM8400_SPEAKER_MIXER | (WM8400_RDSPK << 8):
+ reg = wm8400_read(w->codec, WM8400_OUTPUT_MIXER2);
+ if (reg & WM8400_RDRO) {
+ printk(KERN_WARNING
+ "Cannot set as Output Mixer 2 RDRO Set\n");
+ ret = -1;
+ }
+ break;
+ case WM8400_OUTPUT_MIXER1 | (WM8400_LDLO << 8):
+ reg = wm8400_read(w->codec, WM8400_SPEAKER_MIXER);
+ if (reg & WM8400_LDSPK) {
+ printk(KERN_WARNING
+ "Cannot set as Speaker Mixer LDSPK Set\n");
+ ret = -1;
+ }
+ break;
+ case WM8400_OUTPUT_MIXER2 | (WM8400_RDRO << 8):
+ reg = wm8400_read(w->codec, WM8400_SPEAKER_MIXER);
+ if (reg & WM8400_RDSPK) {
+ printk(KERN_WARNING
+ "Cannot set as Speaker Mixer RDSPK Set\n");
+ ret = -1;
+ }
+ break;
+ }
+
+ return ret;
+}
+
+/* INMIX dB values */
+static const unsigned int in_mix_tlv[] = {
+ TLV_DB_RANGE_HEAD(1),
+ 0,7, TLV_DB_LINEAR_ITEM(-1200, 600),
+};
+
+/* Left In PGA Connections */
+static const struct snd_kcontrol_new wm8400_dapm_lin12_pga_controls[] = {
+SOC_DAPM_SINGLE("LIN1 Switch", WM8400_INPUT_MIXER2, WM8400_LMN1_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LIN2 Switch", WM8400_INPUT_MIXER2, WM8400_LMP2_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8400_dapm_lin34_pga_controls[] = {
+SOC_DAPM_SINGLE("LIN3 Switch", WM8400_INPUT_MIXER2, WM8400_LMN3_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LIN4 Switch", WM8400_INPUT_MIXER2, WM8400_LMP4_SHIFT, 1, 0),
+};
+
+/* Right In PGA Connections */
+static const struct snd_kcontrol_new wm8400_dapm_rin12_pga_controls[] = {
+SOC_DAPM_SINGLE("RIN1 Switch", WM8400_INPUT_MIXER2, WM8400_RMN1_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("RIN2 Switch", WM8400_INPUT_MIXER2, WM8400_RMP2_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8400_dapm_rin34_pga_controls[] = {
+SOC_DAPM_SINGLE("RIN3 Switch", WM8400_INPUT_MIXER2, WM8400_RMN3_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("RIN4 Switch", WM8400_INPUT_MIXER2, WM8400_RMP4_SHIFT, 1, 0),
+};
+
+/* INMIXL */
+static const struct snd_kcontrol_new wm8400_dapm_inmixl_controls[] = {
+SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8400_INPUT_MIXER3,
+ WM8400_LDBVOL_SHIFT, WM8400_LDBVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8400_INPUT_MIXER5, WM8400_LI2BVOL_SHIFT,
+ 7, 0, in_mix_tlv),
+SOC_DAPM_SINGLE("LINPGA12 Switch", WM8400_INPUT_MIXER3, WM8400_L12MNB_SHIFT,
+ 1, 0),
+SOC_DAPM_SINGLE("LINPGA34 Switch", WM8400_INPUT_MIXER3, WM8400_L34MNB_SHIFT,
+ 1, 0),
+};
+
+/* INMIXR */
+static const struct snd_kcontrol_new wm8400_dapm_inmixr_controls[] = {
+SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8400_INPUT_MIXER4,
+ WM8400_RDBVOL_SHIFT, WM8400_RDBVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8400_INPUT_MIXER6, WM8400_RI2BVOL_SHIFT,
+ 7, 0, in_mix_tlv),
+SOC_DAPM_SINGLE("RINPGA12 Switch", WM8400_INPUT_MIXER3, WM8400_L12MNB_SHIFT,
+ 1, 0),
+SOC_DAPM_SINGLE("RINPGA34 Switch", WM8400_INPUT_MIXER3, WM8400_L34MNB_SHIFT,
+ 1, 0),
+};
+
+/* AINLMUX */
+static const char *wm8400_ainlmux[] =
+ {"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"};
+
+static const struct soc_enum wm8400_ainlmux_enum =
+SOC_ENUM_SINGLE( WM8400_INPUT_MIXER1, WM8400_AINLMODE_SHIFT,
+ ARRAY_SIZE(wm8400_ainlmux), wm8400_ainlmux);
+
+static const struct snd_kcontrol_new wm8400_dapm_ainlmux_controls =
+SOC_DAPM_ENUM("Route", wm8400_ainlmux_enum);
+
+/* DIFFINL */
+
+/* AINRMUX */
+static const char *wm8400_ainrmux[] =
+ {"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"};
+
+static const struct soc_enum wm8400_ainrmux_enum =
+SOC_ENUM_SINGLE( WM8400_INPUT_MIXER1, WM8400_AINRMODE_SHIFT,
+ ARRAY_SIZE(wm8400_ainrmux), wm8400_ainrmux);
+
+static const struct snd_kcontrol_new wm8400_dapm_ainrmux_controls =
+SOC_DAPM_ENUM("Route", wm8400_ainrmux_enum);
+
+/* RXVOICE */
+static const struct snd_kcontrol_new wm8400_dapm_rxvoice_controls[] = {
+SOC_DAPM_SINGLE_TLV("LIN4/RXN", WM8400_INPUT_MIXER5, WM8400_LR4BVOL_SHIFT,
+ WM8400_LR4BVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("RIN4/RXP", WM8400_INPUT_MIXER6, WM8400_RL4BVOL_SHIFT,
+ WM8400_RL4BVOL_MASK, 0, in_mix_tlv),
+};
+
+/* LOMIX */
+static const struct snd_kcontrol_new wm8400_dapm_lomix_controls[] = {
+SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8400_OUTPUT_MIXER1,
+ WM8400_LRBLO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8400_OUTPUT_MIXER1,
+ WM8400_LLBLO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8400_OUTPUT_MIXER1,
+ WM8400_LRI3LO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8400_OUTPUT_MIXER1,
+ WM8400_LLI3LO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER1,
+ WM8400_LR12LO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER1,
+ WM8400_LL12LO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8400_OUTPUT_MIXER1,
+ WM8400_LDLO_SHIFT, 1, 0),
+};
+
+/* ROMIX */
+static const struct snd_kcontrol_new wm8400_dapm_romix_controls[] = {
+SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8400_OUTPUT_MIXER2,
+ WM8400_RLBRO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8400_OUTPUT_MIXER2,
+ WM8400_RRBRO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8400_OUTPUT_MIXER2,
+ WM8400_RLI3RO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8400_OUTPUT_MIXER2,
+ WM8400_RRI3RO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER2,
+ WM8400_RL12RO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER2,
+ WM8400_RR12RO_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8400_OUTPUT_MIXER2,
+ WM8400_RDRO_SHIFT, 1, 0),
+};
+
+/* LONMIX */
+static const struct snd_kcontrol_new wm8400_dapm_lonmix_controls[] = {
+SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8400_LINE_MIXER1,
+ WM8400_LLOPGALON_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8400_LINE_MIXER1,
+ WM8400_LROPGALON_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8400_LINE_MIXER1,
+ WM8400_LOPLON_SHIFT, 1, 0),
+};
+
+/* LOPMIX */
+static const struct snd_kcontrol_new wm8400_dapm_lopmix_controls[] = {
+SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8400_LINE_MIXER1,
+ WM8400_LR12LOP_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8400_LINE_MIXER1,
+ WM8400_LL12LOP_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8400_LINE_MIXER1,
+ WM8400_LLOPGALOP_SHIFT, 1, 0),
+};
+
+/* RONMIX */
+static const struct snd_kcontrol_new wm8400_dapm_ronmix_controls[] = {
+SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8400_LINE_MIXER2,
+ WM8400_RROPGARON_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8400_LINE_MIXER2,
+ WM8400_RLOPGARON_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8400_LINE_MIXER2,
+ WM8400_ROPRON_SHIFT, 1, 0),
+};
+
+/* ROPMIX */
+static const struct snd_kcontrol_new wm8400_dapm_ropmix_controls[] = {
+SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8400_LINE_MIXER2,
+ WM8400_RL12ROP_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8400_LINE_MIXER2,
+ WM8400_RR12ROP_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8400_LINE_MIXER2,
+ WM8400_RROPGAROP_SHIFT, 1, 0),
+};
+
+/* OUT3MIX */
+static const struct snd_kcontrol_new wm8400_dapm_out3mix_controls[] = {
+SOC_DAPM_SINGLE("OUT3MIX LIN4/RXP Bypass Switch", WM8400_OUT3_4_MIXER,
+ WM8400_LI4O3_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8400_OUT3_4_MIXER,
+ WM8400_LPGAO3_SHIFT, 1, 0),
+};
+
+/* OUT4MIX */
+static const struct snd_kcontrol_new wm8400_dapm_out4mix_controls[] = {
+SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8400_OUT3_4_MIXER,
+ WM8400_RPGAO4_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("OUT4MIX RIN4/RXP Bypass Switch", WM8400_OUT3_4_MIXER,
+ WM8400_RI4O4_SHIFT, 1, 0),
+};
+
+/* SPKMIX */
+static const struct snd_kcontrol_new wm8400_dapm_spkmix_controls[] = {
+SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8400_SPEAKER_MIXER,
+ WM8400_LI2SPK_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8400_SPEAKER_MIXER,
+ WM8400_LB2SPK_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8400_SPEAKER_MIXER,
+ WM8400_LOPGASPK_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8400_SPEAKER_MIXER,
+ WM8400_LDSPK_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8400_SPEAKER_MIXER,
+ WM8400_RDSPK_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8400_SPEAKER_MIXER,
+ WM8400_ROPGASPK_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8400_SPEAKER_MIXER,
+ WM8400_RL12ROP_SHIFT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8400_SPEAKER_MIXER,
+ WM8400_RI2SPK_SHIFT, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8400_dapm_widgets[] = {
+/* Input Side */
+/* Input Lines */
+SND_SOC_DAPM_INPUT("LIN1"),
+SND_SOC_DAPM_INPUT("LIN2"),
+SND_SOC_DAPM_INPUT("LIN3"),
+SND_SOC_DAPM_INPUT("LIN4/RXN"),
+SND_SOC_DAPM_INPUT("RIN3"),
+SND_SOC_DAPM_INPUT("RIN4/RXP"),
+SND_SOC_DAPM_INPUT("RIN1"),
+SND_SOC_DAPM_INPUT("RIN2"),
+SND_SOC_DAPM_INPUT("Internal ADC Source"),
+
+/* DACs */
+SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8400_POWER_MANAGEMENT_2,
+ WM8400_ADCL_ENA_SHIFT, 0),
+SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8400_POWER_MANAGEMENT_2,
+ WM8400_ADCR_ENA_SHIFT, 0),
+
+/* Input PGAs */
+SND_SOC_DAPM_MIXER("LIN12 PGA", WM8400_POWER_MANAGEMENT_2,
+ WM8400_LIN12_ENA_SHIFT,
+ 0, &wm8400_dapm_lin12_pga_controls[0],
+ ARRAY_SIZE(wm8400_dapm_lin12_pga_controls)),
+SND_SOC_DAPM_MIXER("LIN34 PGA", WM8400_POWER_MANAGEMENT_2,
+ WM8400_LIN34_ENA_SHIFT,
+ 0, &wm8400_dapm_lin34_pga_controls[0],
+ ARRAY_SIZE(wm8400_dapm_lin34_pga_controls)),
+SND_SOC_DAPM_MIXER("RIN12 PGA", WM8400_POWER_MANAGEMENT_2,
+ WM8400_RIN12_ENA_SHIFT,
+ 0, &wm8400_dapm_rin12_pga_controls[0],
+ ARRAY_SIZE(wm8400_dapm_rin12_pga_controls)),
+SND_SOC_DAPM_MIXER("RIN34 PGA", WM8400_POWER_MANAGEMENT_2,
+ WM8400_RIN34_ENA_SHIFT,
+ 0, &wm8400_dapm_rin34_pga_controls[0],
+ ARRAY_SIZE(wm8400_dapm_rin34_pga_controls)),
+
+/* INMIXL */
+SND_SOC_DAPM_MIXER_E("INMIXL", WM8400_INTDRIVBITS, WM8400_INMIXL_PWR, 0,
+ &wm8400_dapm_inmixl_controls[0],
+ ARRAY_SIZE(wm8400_dapm_inmixl_controls),
+ inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* AINLMUX */
+SND_SOC_DAPM_MUX_E("AILNMUX", WM8400_INTDRIVBITS, WM8400_AINLMUX_PWR, 0,
+ &wm8400_dapm_ainlmux_controls, inmixer_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* INMIXR */
+SND_SOC_DAPM_MIXER_E("INMIXR", WM8400_INTDRIVBITS, WM8400_INMIXR_PWR, 0,
+ &wm8400_dapm_inmixr_controls[0],
+ ARRAY_SIZE(wm8400_dapm_inmixr_controls),
+ inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* AINRMUX */
+SND_SOC_DAPM_MUX_E("AIRNMUX", WM8400_INTDRIVBITS, WM8400_AINRMUX_PWR, 0,
+ &wm8400_dapm_ainrmux_controls, inmixer_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* Output Side */
+/* DACs */
+SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8400_POWER_MANAGEMENT_3,
+ WM8400_DACL_ENA_SHIFT, 0),
+SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8400_POWER_MANAGEMENT_3,
+ WM8400_DACR_ENA_SHIFT, 0),
+
+/* LOMIX */
+SND_SOC_DAPM_MIXER_E("LOMIX", WM8400_POWER_MANAGEMENT_3,
+ WM8400_LOMIX_ENA_SHIFT,
+ 0, &wm8400_dapm_lomix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_lomix_controls),
+ outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+/* LONMIX */
+SND_SOC_DAPM_MIXER("LONMIX", WM8400_POWER_MANAGEMENT_3, WM8400_LON_ENA_SHIFT,
+ 0, &wm8400_dapm_lonmix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_lonmix_controls)),
+
+/* LOPMIX */
+SND_SOC_DAPM_MIXER("LOPMIX", WM8400_POWER_MANAGEMENT_3, WM8400_LOP_ENA_SHIFT,
+ 0, &wm8400_dapm_lopmix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_lopmix_controls)),
+
+/* OUT3MIX */
+SND_SOC_DAPM_MIXER("OUT3MIX", WM8400_POWER_MANAGEMENT_1, WM8400_OUT3_ENA_SHIFT,
+ 0, &wm8400_dapm_out3mix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_out3mix_controls)),
+
+/* SPKMIX */
+SND_SOC_DAPM_MIXER_E("SPKMIX", WM8400_POWER_MANAGEMENT_1, WM8400_SPK_ENA_SHIFT,
+ 0, &wm8400_dapm_spkmix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_spkmix_controls), outmixer_event,
+ SND_SOC_DAPM_PRE_REG),
+
+/* OUT4MIX */
+SND_SOC_DAPM_MIXER("OUT4MIX", WM8400_POWER_MANAGEMENT_1, WM8400_OUT4_ENA_SHIFT,
+ 0, &wm8400_dapm_out4mix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_out4mix_controls)),
+
+/* ROPMIX */
+SND_SOC_DAPM_MIXER("ROPMIX", WM8400_POWER_MANAGEMENT_3, WM8400_ROP_ENA_SHIFT,
+ 0, &wm8400_dapm_ropmix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_ropmix_controls)),
+
+/* RONMIX */
+SND_SOC_DAPM_MIXER("RONMIX", WM8400_POWER_MANAGEMENT_3, WM8400_RON_ENA_SHIFT,
+ 0, &wm8400_dapm_ronmix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_ronmix_controls)),
+
+/* ROMIX */
+SND_SOC_DAPM_MIXER_E("ROMIX", WM8400_POWER_MANAGEMENT_3,
+ WM8400_ROMIX_ENA_SHIFT,
+ 0, &wm8400_dapm_romix_controls[0],
+ ARRAY_SIZE(wm8400_dapm_romix_controls),
+ outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+/* LOUT PGA */
+SND_SOC_DAPM_PGA("LOUT PGA", WM8400_POWER_MANAGEMENT_1, WM8400_LOUT_ENA_SHIFT,
+ 0, NULL, 0),
+
+/* ROUT PGA */
+SND_SOC_DAPM_PGA("ROUT PGA", WM8400_POWER_MANAGEMENT_1, WM8400_ROUT_ENA_SHIFT,
+ 0, NULL, 0),
+
+/* LOPGA */
+SND_SOC_DAPM_PGA("LOPGA", WM8400_POWER_MANAGEMENT_3, WM8400_LOPGA_ENA_SHIFT, 0,
+ NULL, 0),
+
+/* ROPGA */
+SND_SOC_DAPM_PGA("ROPGA", WM8400_POWER_MANAGEMENT_3, WM8400_ROPGA_ENA_SHIFT, 0,
+ NULL, 0),
+
+/* MICBIAS */
+SND_SOC_DAPM_MICBIAS("MICBIAS", WM8400_POWER_MANAGEMENT_1,
+ WM8400_MIC1BIAS_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_OUTPUT("LON"),
+SND_SOC_DAPM_OUTPUT("LOP"),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("SPKN"),
+SND_SOC_DAPM_OUTPUT("SPKP"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("OUT4"),
+SND_SOC_DAPM_OUTPUT("ROP"),
+SND_SOC_DAPM_OUTPUT("RON"),
+
+SND_SOC_DAPM_OUTPUT("Internal DAC Sink"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Make DACs turn on when playing even if not mixed into any outputs */
+ {"Internal DAC Sink", NULL, "Left DAC"},
+ {"Internal DAC Sink", NULL, "Right DAC"},
+
+ /* Make ADCs turn on when recording
+ * even if not mixed from any inputs */
+ {"Left ADC", NULL, "Internal ADC Source"},
+ {"Right ADC", NULL, "Internal ADC Source"},
+
+ /* Input Side */
+ /* LIN12 PGA */
+ {"LIN12 PGA", "LIN1 Switch", "LIN1"},
+ {"LIN12 PGA", "LIN2 Switch", "LIN2"},
+ /* LIN34 PGA */
+ {"LIN34 PGA", "LIN3 Switch", "LIN3"},
+ {"LIN34 PGA", "LIN4 Switch", "LIN4/RXN"},
+ /* INMIXL */
+ {"INMIXL", "Record Left Volume", "LOMIX"},
+ {"INMIXL", "LIN2 Volume", "LIN2"},
+ {"INMIXL", "LINPGA12 Switch", "LIN12 PGA"},
+ {"INMIXL", "LINPGA34 Switch", "LIN34 PGA"},
+ /* AILNMUX */
+ {"AILNMUX", "INMIXL Mix", "INMIXL"},
+ {"AILNMUX", "DIFFINL Mix", "LIN12 PGA"},
+ {"AILNMUX", "DIFFINL Mix", "LIN34 PGA"},
+ {"AILNMUX", "RXVOICE Mix", "LIN4/RXN"},
+ {"AILNMUX", "RXVOICE Mix", "RIN4/RXP"},
+ /* ADC */
+ {"Left ADC", NULL, "AILNMUX"},
+
+ /* RIN12 PGA */
+ {"RIN12 PGA", "RIN1 Switch", "RIN1"},
+ {"RIN12 PGA", "RIN2 Switch", "RIN2"},
+ /* RIN34 PGA */
+ {"RIN34 PGA", "RIN3 Switch", "RIN3"},
+ {"RIN34 PGA", "RIN4 Switch", "RIN4/RXP"},
+ /* INMIXL */
+ {"INMIXR", "Record Right Volume", "ROMIX"},
+ {"INMIXR", "RIN2 Volume", "RIN2"},
+ {"INMIXR", "RINPGA12 Switch", "RIN12 PGA"},
+ {"INMIXR", "RINPGA34 Switch", "RIN34 PGA"},
+ /* AIRNMUX */
+ {"AIRNMUX", "INMIXR Mix", "INMIXR"},
+ {"AIRNMUX", "DIFFINR Mix", "RIN12 PGA"},
+ {"AIRNMUX", "DIFFINR Mix", "RIN34 PGA"},
+ {"AIRNMUX", "RXVOICE Mix", "LIN4/RXN"},
+ {"AIRNMUX", "RXVOICE Mix", "RIN4/RXP"},
+ /* ADC */
+ {"Right ADC", NULL, "AIRNMUX"},
+
+ /* LOMIX */
+ {"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"},
+ {"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"},
+ {"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+ {"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+ {"LOMIX", "LOMIX Right ADC Bypass Switch", "AIRNMUX"},
+ {"LOMIX", "LOMIX Left ADC Bypass Switch", "AILNMUX"},
+ {"LOMIX", "LOMIX Left DAC Switch", "Left DAC"},
+
+ /* ROMIX */
+ {"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"},
+ {"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"},
+ {"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+ {"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+ {"ROMIX", "ROMIX Right ADC Bypass Switch", "AIRNMUX"},
+ {"ROMIX", "ROMIX Left ADC Bypass Switch", "AILNMUX"},
+ {"ROMIX", "ROMIX Right DAC Switch", "Right DAC"},
+
+ /* SPKMIX */
+ {"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"},
+ {"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"},
+ {"SPKMIX", "SPKMIX LADC Bypass Switch", "AILNMUX"},
+ {"SPKMIX", "SPKMIX RADC Bypass Switch", "AIRNMUX"},
+ {"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"},
+ {"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"},
+ {"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"},
+ {"SPKMIX", "SPKMIX Left DAC Switch", "Right DAC"},
+
+ /* LONMIX */
+ {"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"},
+ {"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"},
+ {"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"},
+
+ /* LOPMIX */
+ {"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+ {"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+ {"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"},
+
+ /* OUT3MIX */
+ {"OUT3MIX", "OUT3MIX LIN4/RXP Bypass Switch", "LIN4/RXN"},
+ {"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"},
+
+ /* OUT4MIX */
+ {"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"},
+ {"OUT4MIX", "OUT4MIX RIN4/RXP Bypass Switch", "RIN4/RXP"},
+
+ /* RONMIX */
+ {"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"},
+ {"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"},
+ {"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"},
+
+ /* ROPMIX */
+ {"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+ {"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+ {"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"},
+
+ /* Out Mixer PGAs */
+ {"LOPGA", NULL, "LOMIX"},
+ {"ROPGA", NULL, "ROMIX"},
+
+ {"LOUT PGA", NULL, "LOMIX"},
+ {"ROUT PGA", NULL, "ROMIX"},
+
+ /* Output Pins */
+ {"LON", NULL, "LONMIX"},
+ {"LOP", NULL, "LOPMIX"},
+ {"OUT3", NULL, "OUT3MIX"},
+ {"LOUT", NULL, "LOUT PGA"},
+ {"SPKN", NULL, "SPKMIX"},
+ {"ROUT", NULL, "ROUT PGA"},
+ {"OUT4", NULL, "OUT4MIX"},
+ {"ROP", NULL, "ROPMIX"},
+ {"RON", NULL, "RONMIX"},
+};
+
+static int wm8400_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, wm8400_dapm_widgets,
+ ARRAY_SIZE(wm8400_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+/*
+ * Clock after FLL and dividers
+ */
+static int wm8400_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 wm8400_priv *wm8400 = codec->private_data;
+
+ wm8400->sysclk = freq;
+ return 0;
+}
+
+struct fll_factors {
+ u16 n;
+ u16 k;
+ u16 outdiv;
+ u16 fratio;
+ u16 freq_ref;
+};
+
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static int fll_factors(struct wm8400_priv *wm8400, struct fll_factors *factors,
+ unsigned int Fref, unsigned int Fout)
+{
+ u64 Kpart;
+ unsigned int K, Nmod, target;
+
+ factors->outdiv = 2;
+ while (Fout * factors->outdiv < 90000000 ||
+ Fout * factors->outdiv > 100000000) {
+ factors->outdiv *= 2;
+ if (factors->outdiv > 32) {
+ dev_err(wm8400->wm8400->dev,
+ "Unsupported FLL output frequency %dHz\n",
+ Fout);
+ return -EINVAL;
+ }
+ }
+ target = Fout * factors->outdiv;
+ factors->outdiv = factors->outdiv >> 2;
+
+ if (Fref < 48000)
+ factors->freq_ref = 1;
+ else
+ factors->freq_ref = 0;
+
+ if (Fref < 1000000)
+ factors->fratio = 9;
+ else
+ factors->fratio = 0;
+
+ /* Ensure we have a fractional part */
+ do {
+ if (Fref < 1000000)
+ factors->fratio--;
+ else
+ factors->fratio++;
+
+ if (factors->fratio < 1 || factors->fratio > 8) {
+ dev_err(wm8400->wm8400->dev,
+ "Unable to calculate FRATIO\n");
+ return -EINVAL;
+ }
+
+ factors->n = target / (Fref * factors->fratio);
+ Nmod = target % (Fref * factors->fratio);
+ } while (Nmod == 0);
+
+ /* Calculate fractional part - scale up so we can round. */
+ Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, (Fref * factors->fratio));
+
+ K = Kpart & 0xFFFFFFFF;
+
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ factors->k = K / 10;
+
+ dev_dbg(wm8400->wm8400->dev,
+ "FLL: Fref=%d Fout=%d N=%x K=%x, FRATIO=%x OUTDIV=%x\n",
+ Fref, Fout,
+ factors->n, factors->k, factors->fratio, factors->outdiv);
+
+ return 0;
+}
+
+static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8400_priv *wm8400 = codec->private_data;
+ struct fll_factors factors;
+ int ret;
+ u16 reg;
+
+ if (freq_in == wm8400->fll_in && freq_out == wm8400->fll_out)
+ return 0;
+
+ if (freq_out != 0) {
+ ret = fll_factors(wm8400, &factors, freq_in, freq_out);
+ if (ret != 0)
+ return ret;
+ }
+
+ wm8400->fll_out = freq_out;
+ wm8400->fll_in = freq_in;
+
+ /* We *must* disable the FLL before any changes */
+ reg = wm8400_read(codec, WM8400_POWER_MANAGEMENT_2);
+ reg &= ~WM8400_FLL_ENA;
+ wm8400_write(codec, WM8400_POWER_MANAGEMENT_2, reg);
+
+ reg = wm8400_read(codec, WM8400_FLL_CONTROL_1);
+ reg &= ~WM8400_FLL_OSC_ENA;
+ wm8400_write(codec, WM8400_FLL_CONTROL_1, reg);
+
+ if (freq_out == 0)
+ return 0;
+
+ reg &= ~(WM8400_FLL_REF_FREQ | WM8400_FLL_FRATIO_MASK);
+ reg |= WM8400_FLL_FRAC | factors.fratio;
+ reg |= factors.freq_ref << WM8400_FLL_REF_FREQ_SHIFT;
+ wm8400_write(codec, WM8400_FLL_CONTROL_1, reg);
+
+ wm8400_write(codec, WM8400_FLL_CONTROL_2, factors.k);
+ wm8400_write(codec, WM8400_FLL_CONTROL_3, factors.n);
+
+ reg = wm8400_read(codec, WM8400_FLL_CONTROL_4);
+ reg &= WM8400_FLL_OUTDIV_MASK;
+ reg |= factors.outdiv;
+ wm8400_write(codec, WM8400_FLL_CONTROL_4, reg);
+
+ return 0;
+}
+
+/*
+ * Sets ADC and Voice DAC format.
+ */
+static int wm8400_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 audio1, audio3;
+
+ audio1 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_1);
+ audio3 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_3);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ audio3 &= ~WM8400_AIF_MSTR1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ audio3 |= WM8400_AIF_MSTR1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ audio1 &= ~WM8400_AIF_FMT_MASK;
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ audio1 |= WM8400_AIF_FMT_I2S;
+ audio1 &= ~WM8400_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audio1 |= WM8400_AIF_FMT_RIGHTJ;
+ audio1 &= ~WM8400_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audio1 |= WM8400_AIF_FMT_LEFTJ;
+ audio1 &= ~WM8400_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audio1 |= WM8400_AIF_FMT_DSP;
+ audio1 &= ~WM8400_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audio1 |= WM8400_AIF_FMT_DSP | WM8400_AIF_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8400_write(codec, WM8400_AUDIO_INTERFACE_1, audio1);
+ wm8400_write(codec, WM8400_AUDIO_INTERFACE_3, audio3);
+ return 0;
+}
+
+static int wm8400_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ switch (div_id) {
+ case WM8400_MCLK_DIV:
+ reg = wm8400_read(codec, WM8400_CLOCKING_2) &
+ ~WM8400_MCLK_DIV_MASK;
+ wm8400_write(codec, WM8400_CLOCKING_2, reg | div);
+ break;
+ case WM8400_DACCLK_DIV:
+ reg = wm8400_read(codec, WM8400_CLOCKING_2) &
+ ~WM8400_DAC_CLKDIV_MASK;
+ wm8400_write(codec, WM8400_CLOCKING_2, reg | div);
+ break;
+ case WM8400_ADCCLK_DIV:
+ reg = wm8400_read(codec, WM8400_CLOCKING_2) &
+ ~WM8400_ADC_CLKDIV_MASK;
+ wm8400_write(codec, WM8400_CLOCKING_2, reg | div);
+ break;
+ case WM8400_BCLK_DIV:
+ reg = wm8400_read(codec, WM8400_CLOCKING_1) &
+ ~WM8400_BCLK_DIV_MASK;
+ wm8400_write(codec, WM8400_CLOCKING_1, reg | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8400_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_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ u16 audio1 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_1);
+
+ audio1 &= ~WM8400_AIF_WL_MASK;
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ audio1 |= WM8400_AIF_WL_20BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ audio1 |= WM8400_AIF_WL_24BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio1 |= WM8400_AIF_WL_32BITS;
+ break;
+ }
+
+ wm8400_write(codec, WM8400_AUDIO_INTERFACE_1, audio1);
+ return 0;
+}
+
+static int wm8400_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 val = wm8400_read(codec, WM8400_DAC_CTRL) & ~WM8400_DAC_MUTE;
+
+ if (mute)
+ wm8400_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE);
+ else
+ wm8400_write(codec, WM8400_DAC_CTRL, val);
+
+ return 0;
+}
+
+/* TODO: set bias for best performance at standby */
+static int wm8400_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8400_priv *wm8400 = codec->private_data;
+ u16 val;
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* VMID=2*50k */
+ val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1) &
+ ~WM8400_VMID_MODE_MASK;
+ wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x2);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(power),
+ &power[0]);
+ if (ret != 0) {
+ dev_err(wm8400->wm8400->dev,
+ "Failed to enable regulators: %d\n",
+ ret);
+ return ret;
+ }
+
+ wm8400_write(codec, WM8400_POWER_MANAGEMENT_1,
+ WM8400_CODEC_ENA | WM8400_SYSCLK_ENA);
+
+ /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
+ wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+ WM8400_BUFDCOPEN | WM8400_POBCTRL);
+
+ msleep(50);
+
+ /* Enable VREF & VMID at 2x50k */
+ val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1);
+ val |= 0x2 | WM8400_VREF_ENA;
+ wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+
+ /* Enable BUFIOEN */
+ wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+ WM8400_BUFDCOPEN | WM8400_POBCTRL |
+ WM8400_BUFIOEN);
+
+ /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+ wm8400_write(codec, WM8400_ANTIPOP2, WM8400_BUFIOEN);
+ }
+
+ /* VMID=2*300k */
+ val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1) &
+ ~WM8400_VMID_MODE_MASK;
+ wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x4);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* Enable POBCTRL and SOFT_ST */
+ wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+ WM8400_POBCTRL | WM8400_BUFIOEN);
+
+ /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
+ wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+ WM8400_BUFDCOPEN | WM8400_POBCTRL |
+ WM8400_BUFIOEN);
+
+ /* mute DAC */
+ val = wm8400_read(codec, WM8400_DAC_CTRL);
+ wm8400_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE);
+
+ /* Enable any disabled outputs */
+ val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1);
+ val |= WM8400_SPK_ENA | WM8400_OUT3_ENA |
+ WM8400_OUT4_ENA | WM8400_LOUT_ENA |
+ WM8400_ROUT_ENA;
+ wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+
+ /* Disable VMID */
+ val &= ~WM8400_VMID_MODE_MASK;
+ wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+
+ msleep(300);
+
+ /* Enable all output discharge bits */
+ wm8400_write(codec, WM8400_ANTIPOP1, WM8400_DIS_LLINE |
+ WM8400_DIS_RLINE | WM8400_DIS_OUT3 |
+ WM8400_DIS_OUT4 | WM8400_DIS_LOUT |
+ WM8400_DIS_ROUT);
+
+ /* Disable VREF */
+ val &= ~WM8400_VREF_ENA;
+ wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+
+ /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+ wm8400_write(codec, WM8400_ANTIPOP2, 0x0);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(power),
+ &power[0]);
+ if (ret != 0)
+ return ret;
+
+ break;
+ }
+
+ codec->bias_level = level;
+ return 0;
+}
+
+#define WM8400_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8400_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8400_dai_ops = {
+ .hw_params = wm8400_hw_params,
+ .digital_mute = wm8400_mute,
+ .set_fmt = wm8400_set_dai_fmt,
+ .set_clkdiv = wm8400_set_dai_clkdiv,
+ .set_sysclk = wm8400_set_dai_sysclk,
+ .set_pll = wm8400_set_dai_pll,
+};
+
+/*
+ * The WM8400 supports 2 different and mutually exclusive DAI
+ * configurations.
+ *
+ * 1. ADC/DAC on Primary Interface
+ * 2. ADC on Primary Interface/DAC on secondary
+ */
+struct snd_soc_dai wm8400_dai = {
+/* ADC/DAC on primary */
+ .name = "WM8400 ADC/DAC Primary",
+ .id = 1,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8400_RATES,
+ .formats = WM8400_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8400_RATES,
+ .formats = WM8400_FORMATS,
+ },
+ .ops = &wm8400_dai_ops,
+};
+EXPORT_SYMBOL_GPL(wm8400_dai);
+
+static int wm8400_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ wm8400_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int wm8400_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+static struct snd_soc_codec *wm8400_codec;
+
+static int wm8400_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret;
+
+ if (!wm8400_codec) {
+ dev_err(&pdev->dev, "wm8400 not yet discovered\n");
+ return -ENODEV;
+ }
+ codec = wm8400_codec;
+
+ socdev->card->codec = codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ wm8400_add_controls(codec);
+ wm8400_add_widgets(codec);
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to register card\n");
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ return ret;
+}
+
+/* power down chip */
+static int wm8400_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8400 = {
+ .probe = wm8400_probe,
+ .remove = wm8400_remove,
+ .suspend = wm8400_suspend,
+ .resume = wm8400_resume,
+};
+
+static void wm8400_probe_deferred(struct work_struct *work)
+{
+ struct wm8400_priv *priv = container_of(work, struct wm8400_priv,
+ work);
+ struct snd_soc_codec *codec = &priv->codec;
+ int ret;
+
+ /* charge output caps */
+ wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* We're done, tell the subsystem. */
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(priv->wm8400->dev,
+ "Failed to register codec: %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_soc_register_dai(&wm8400_dai);
+ if (ret != 0) {
+ dev_err(priv->wm8400->dev,
+ "Failed to register DAI: %d\n", ret);
+ goto err_codec;
+ }
+
+ return;
+
+err_codec:
+ snd_soc_unregister_codec(codec);
+err:
+ wm8400_set_bias_level(codec, SND_SOC_BIAS_OFF);
+}
+
+static int wm8400_codec_probe(struct platform_device *dev)
+{
+ struct wm8400_priv *priv;
+ int ret;
+ u16 reg;
+ struct snd_soc_codec *codec;
+
+ priv = kzalloc(sizeof(struct wm8400_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+
+ codec = &priv->codec;
+ codec->private_data = priv;
+ codec->control_data = dev->dev.driver_data;
+ priv->wm8400 = dev->dev.driver_data;
+
+ ret = regulator_bulk_get(priv->wm8400->dev,
+ ARRAY_SIZE(power), &power[0]);
+ if (ret != 0) {
+ dev_err(&dev->dev, "Failed to get regulators: %d\n", ret);
+ goto err;
+ }
+
+ codec->dev = &dev->dev;
+ wm8400_dai.dev = &dev->dev;
+
+ codec->name = "WM8400";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8400_read;
+ codec->write = wm8400_write;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = wm8400_set_bias_level;
+ codec->dai = &wm8400_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = WM8400_REGISTER_COUNT;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ INIT_WORK(&priv->work, wm8400_probe_deferred);
+
+ wm8400_codec_reset(codec);
+
+ reg = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1);
+ wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, reg | WM8400_CODEC_ENA);
+
+ /* Latch volume update bits */
+ reg = wm8400_read(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME);
+ wm8400_write(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
+ reg & WM8400_IPVU);
+ reg = wm8400_read(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME);
+ wm8400_write(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
+ reg & WM8400_IPVU);
+
+ wm8400_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
+ wm8400_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
+
+ wm8400_codec = codec;
+
+ if (!schedule_work(&priv->work)) {
+ ret = -EINVAL;
+ goto err_regulator;
+ }
+
+ return 0;
+
+err_regulator:
+ wm8400_codec = NULL;
+ regulator_bulk_free(ARRAY_SIZE(power), power);
+err:
+ kfree(priv);
+ return ret;
+}
+
+static int __exit wm8400_codec_remove(struct platform_device *dev)
+{
+ struct wm8400_priv *priv = wm8400_codec->private_data;
+ u16 reg;
+
+ snd_soc_unregister_dai(&wm8400_dai);
+ snd_soc_unregister_codec(wm8400_codec);
+
+ reg = wm8400_read(wm8400_codec, WM8400_POWER_MANAGEMENT_1);
+ wm8400_write(wm8400_codec, WM8400_POWER_MANAGEMENT_1,
+ reg & (~WM8400_CODEC_ENA));
+
+ regulator_bulk_free(ARRAY_SIZE(power), power);
+ kfree(priv);
+
+ wm8400_codec = NULL;
+
+ return 0;
+}
+
+static struct platform_driver wm8400_codec_driver = {
+ .driver = {
+ .name = "wm8400-codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8400_codec_probe,
+ .remove = __exit_p(wm8400_codec_remove),
+};
+
+static int __init wm8400_codec_init(void)
+{
+ return platform_driver_register(&wm8400_codec_driver);
+}
+module_init(wm8400_codec_init);
+
+static void __exit wm8400_codec_exit(void)
+{
+ platform_driver_unregister(&wm8400_codec_driver);
+}
+module_exit(wm8400_codec_exit);
+
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8400);
+
+MODULE_DESCRIPTION("ASoC WM8400 driver");
+MODULE_AUTHOR("Mark Brown");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8400-codec");
diff --git a/sound/soc/codecs/wm8400.h b/sound/soc/codecs/wm8400.h
new file mode 100644
index 0000000..79c5934
--- /dev/null
+++ b/sound/soc/codecs/wm8400.h
@@ -0,0 +1,62 @@
+/*
+ * wm8400.h -- audio driver for WM8400
+ *
+ * Copyright 2008 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 _WM8400_CODEC_H
+#define _WM8400_CODEC_H
+
+#define WM8400_MCLK_DIV 0
+#define WM8400_DACCLK_DIV 1
+#define WM8400_ADCCLK_DIV 2
+#define WM8400_BCLK_DIV 3
+
+#define WM8400_MCLK_DIV_1 0x400
+#define WM8400_MCLK_DIV_2 0x800
+
+#define WM8400_DAC_CLKDIV_1 0x00
+#define WM8400_DAC_CLKDIV_1_5 0x04
+#define WM8400_DAC_CLKDIV_2 0x08
+#define WM8400_DAC_CLKDIV_3 0x0c
+#define WM8400_DAC_CLKDIV_4 0x10
+#define WM8400_DAC_CLKDIV_5_5 0x14
+#define WM8400_DAC_CLKDIV_6 0x18
+
+#define WM8400_ADC_CLKDIV_1 0x00
+#define WM8400_ADC_CLKDIV_1_5 0x20
+#define WM8400_ADC_CLKDIV_2 0x40
+#define WM8400_ADC_CLKDIV_3 0x60
+#define WM8400_ADC_CLKDIV_4 0x80
+#define WM8400_ADC_CLKDIV_5_5 0xa0
+#define WM8400_ADC_CLKDIV_6 0xc0
+
+
+#define WM8400_BCLK_DIV_1 (0x0 << 1)
+#define WM8400_BCLK_DIV_1_5 (0x1 << 1)
+#define WM8400_BCLK_DIV_2 (0x2 << 1)
+#define WM8400_BCLK_DIV_3 (0x3 << 1)
+#define WM8400_BCLK_DIV_4 (0x4 << 1)
+#define WM8400_BCLK_DIV_5_5 (0x5 << 1)
+#define WM8400_BCLK_DIV_6 (0x6 << 1)
+#define WM8400_BCLK_DIV_8 (0x7 << 1)
+#define WM8400_BCLK_DIV_11 (0x8 << 1)
+#define WM8400_BCLK_DIV_12 (0x9 << 1)
+#define WM8400_BCLK_DIV_16 (0xA << 1)
+#define WM8400_BCLK_DIV_22 (0xB << 1)
+#define WM8400_BCLK_DIV_24 (0xC << 1)
+#define WM8400_BCLK_DIV_32 (0xD << 1)
+#define WM8400_BCLK_DIV_44 (0xE << 1)
+#define WM8400_BCLK_DIV_48 (0xF << 1)
+
+extern struct snd_soc_dai wm8400_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8400;
+
+#endif
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index 40f8238..6a4cea0 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -171,22 +171,6 @@
SOC_SINGLE("Mono Playback Switch", WM8510_MONOMIX, 6, 1, 1),
};
-/* add non dapm controls */
-static int wm8510_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(wm8510_snd_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8510_snd_controls[i], codec,
- NULL));
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
/* Speaker Output Mixer */
static const struct snd_kcontrol_new wm8510_speaker_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_SPKMIX, 1, 1, 0),
@@ -352,7 +336,7 @@
return 0;
}
- pll_factors(freq_out*8, freq_in);
+ pll_factors(freq_out*4, freq_in);
wm8510_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
wm8510_write(codec, WM8510_PLLK1, pll_div.k >> 18);
@@ -383,7 +367,7 @@
wm8510_write(codec, WM8510_GPIO, reg | div);
break;
case WM8510_MCLKDIV:
- reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1f;
+ reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x11f;
wm8510_write(codec, WM8510_CLOCK, reg | div);
break;
case WM8510_ADCCLK:
@@ -468,7 +452,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
u16 iface = wm8510_read_reg_cache(codec, WM8510_IFACE) & 0x19f;
u16 adn = wm8510_read_reg_cache(codec, WM8510_ADD) & 0x1f1;
@@ -570,6 +554,14 @@
#define WM8510_FORMATS (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 wm8510_dai_ops = {
+ .hw_params = wm8510_pcm_hw_params,
+ .digital_mute = wm8510_mute,
+ .set_fmt = wm8510_set_dai_fmt,
+ .set_clkdiv = wm8510_set_dai_clkdiv,
+ .set_pll = wm8510_set_dai_pll,
+};
+
struct snd_soc_dai wm8510_dai = {
.name = "WM8510 HiFi",
.playback = {
@@ -584,20 +576,14 @@
.channels_max = 2,
.rates = WM8510_RATES,
.formats = WM8510_FORMATS,},
- .ops = {
- .hw_params = wm8510_pcm_hw_params,
- .digital_mute = wm8510_mute,
- .set_fmt = wm8510_set_dai_fmt,
- .set_clkdiv = wm8510_set_dai_clkdiv,
- .set_pll = wm8510_set_dai_pll,
- },
+ .ops = &wm8510_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8510_dai);
static int wm8510_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@@ -606,7 +592,7 @@
static int wm8510_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
@@ -628,7 +614,7 @@
*/
static int wm8510_init(struct snd_soc_device *socdev)
{
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
codec->name = "WM8510";
@@ -656,7 +642,8 @@
/* power on device */
codec->bias_level = SND_SOC_BIAS_OFF;
wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- wm8510_add_controls(codec);
+ snd_soc_add_controls(codec, wm8510_snd_controls,
+ ARRAY_SIZE(wm8510_snd_controls));
wm8510_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@@ -685,7 +672,7 @@
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = wm8510_socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@@ -766,7 +753,7 @@
static int __devinit wm8510_spi_probe(struct spi_device *spi)
{
struct snd_soc_device *socdev = wm8510_socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret;
codec->control_data = spi;
@@ -832,7 +819,7 @@
if (codec == NULL)
return -ENOMEM;
- socdev->codec = codec;
+ socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -862,7 +849,7 @@
static int wm8510_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF);
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index d004e58..442ea6f 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -1,7 +1,7 @@
/*
* wm8580.c -- WM8580 ALSA Soc Audio driver
*
- * Copyright 2008 Wolfson Microelectronics PLC.
+ * Copyright 2008, 2009 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
@@ -35,19 +35,6 @@
#include "wm8580.h"
-#define WM8580_VERSION "0.1"
-
-struct pll_state {
- unsigned int in;
- unsigned int out;
-};
-
-/* codec private data */
-struct wm8580_priv {
- struct pll_state a;
- struct pll_state b;
-};
-
/* WM8580 register space */
#define WM8580_PLLA1 0x00
#define WM8580_PLLA2 0x01
@@ -102,6 +89,8 @@
#define WM8580_READBACK 0x34
#define WM8580_RESET 0x35
+#define WM8580_MAX_REGISTER 0x35
+
/* PLLB4 (register 7h) */
#define WM8580_PLLB4_MCLKOUTSRC_MASK 0x60
#define WM8580_PLLB4_MCLKOUTSRC_PLLA 0x20
@@ -193,6 +182,20 @@
0x0000, 0x0000 /*R53*/
};
+struct pll_state {
+ unsigned int in;
+ unsigned int out;
+};
+
+/* codec private data */
+struct wm8580_priv {
+ struct snd_soc_codec codec;
+ u16 reg_cache[WM8580_MAX_REGISTER + 1];
+ struct pll_state a;
+ struct pll_state b;
+};
+
+
/*
* read wm8580 register cache
*/
@@ -200,7 +203,7 @@
unsigned int reg)
{
u16 *cache = codec->reg_cache;
- BUG_ON(reg > ARRAY_SIZE(wm8580_reg));
+ BUG_ON(reg >= ARRAY_SIZE(wm8580_reg));
return cache[reg];
}
@@ -223,7 +226,7 @@
{
u8 data[2];
- BUG_ON(reg > ARRAY_SIZE(wm8580_reg));
+ BUG_ON(reg >= ARRAY_SIZE(wm8580_reg));
/* Registers are 9 bits wide */
value &= 0x1ff;
@@ -330,20 +333,6 @@
SOC_SINGLE("ADC High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0),
};
-/* Add non-DAPM controls */
-static int wm8580_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(wm8580_snd_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8580_snd_controls[i],
- codec, NULL));
- if (err < 0)
- return err;
- }
- return 0;
-}
static const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC1", "Playback", WM8580_PWRDN1, 2, 1),
SND_SOC_DAPM_DAC("DAC2", "Playback", WM8580_PWRDN1, 3, 1),
@@ -553,7 +542,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->id);
paifb &= ~WM8580_AIF_LENGTH_MASK;
@@ -771,8 +760,22 @@
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
- case SND_SOC_BIAS_STANDBY:
break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ /* Power up and get individual control of the DACs */
+ reg = wm8580_read(codec, WM8580_PWRDN1);
+ reg &= ~(WM8580_PWRDN1_PWDN | WM8580_PWRDN1_ALLDACPD);
+ wm8580_write(codec, WM8580_PWRDN1, reg);
+
+ /* Make VMID high impedence */
+ reg = wm8580_read(codec, WM8580_ADC_CONTROL1);
+ reg &= ~0x100;
+ wm8580_write(codec, WM8580_ADC_CONTROL1, reg);
+ }
+ break;
+
case SND_SOC_BIAS_OFF:
reg = wm8580_read(codec, WM8580_PWRDN1);
wm8580_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN);
@@ -785,6 +788,21 @@
#define WM8580_FORMATS (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 wm8580_dai_ops_playback = {
+ .hw_params = wm8580_paif_hw_params,
+ .set_fmt = wm8580_set_paif_dai_fmt,
+ .set_clkdiv = wm8580_set_dai_clkdiv,
+ .set_pll = wm8580_set_dai_pll,
+ .digital_mute = wm8580_digital_mute,
+};
+
+static struct snd_soc_dai_ops wm8580_dai_ops_capture = {
+ .hw_params = wm8580_paif_hw_params,
+ .set_fmt = wm8580_set_paif_dai_fmt,
+ .set_clkdiv = wm8580_set_dai_clkdiv,
+ .set_pll = wm8580_set_dai_pll,
+};
+
struct snd_soc_dai wm8580_dai[] = {
{
.name = "WM8580 PAIFRX",
@@ -796,13 +814,7 @@
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = WM8580_FORMATS,
},
- .ops = {
- .hw_params = wm8580_paif_hw_params,
- .set_fmt = wm8580_set_paif_dai_fmt,
- .set_clkdiv = wm8580_set_dai_clkdiv,
- .set_pll = wm8580_set_dai_pll,
- .digital_mute = wm8580_digital_mute,
- },
+ .ops = &wm8580_dai_ops_playback,
},
{
.name = "WM8580 PAIFTX",
@@ -814,109 +826,168 @@
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = WM8580_FORMATS,
},
- .ops = {
- .hw_params = wm8580_paif_hw_params,
- .set_fmt = wm8580_set_paif_dai_fmt,
- .set_clkdiv = wm8580_set_dai_clkdiv,
- .set_pll = wm8580_set_dai_pll,
- },
+ .ops = &wm8580_dai_ops_capture,
},
};
EXPORT_SYMBOL_GPL(wm8580_dai);
-/*
- * initialise the WM8580 driver
- * register the mixer and dsp interfaces with the kernel
- */
-static int wm8580_init(struct snd_soc_device *socdev)
+static struct snd_soc_codec *wm8580_codec;
+
+static int wm8580_probe(struct platform_device *pdev)
{
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
int ret = 0;
- codec->name = "WM8580";
- codec->owner = THIS_MODULE;
- codec->read = wm8580_read_reg_cache;
- codec->write = wm8580_write;
- codec->set_bias_level = wm8580_set_bias_level;
- codec->dai = wm8580_dai;
- codec->num_dai = ARRAY_SIZE(wm8580_dai);
- codec->reg_cache_size = ARRAY_SIZE(wm8580_reg);
- codec->reg_cache = kmemdup(wm8580_reg, sizeof(wm8580_reg),
- GFP_KERNEL);
+ if (wm8580_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
- if (codec->reg_cache == NULL)
- return -ENOMEM;
-
- /* Get the codec into a known state */
- wm8580_write(codec, WM8580_RESET, 0);
-
- /* Power up and get individual control of the DACs */
- wm8580_write(codec, WM8580_PWRDN1, wm8580_read(codec, WM8580_PWRDN1) &
- ~(WM8580_PWRDN1_PWDN | WM8580_PWRDN1_ALLDACPD));
-
- /* Make VMID high impedence */
- wm8580_write(codec, WM8580_ADC_CONTROL1,
- wm8580_read(codec, WM8580_ADC_CONTROL1) & ~0x100);
+ socdev->card->codec = wm8580_codec;
+ codec = wm8580_codec;
/* register pcms */
- ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1,
- SNDRV_DEFAULT_STR1);
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
- printk(KERN_ERR "wm8580: failed to create pcms\n");
+ dev_err(codec->dev, "failed to create pcms: %d\n", ret);
goto pcm_err;
}
- wm8580_add_controls(codec);
+ snd_soc_add_controls(codec, wm8580_snd_controls,
+ ARRAY_SIZE(wm8580_snd_controls));
wm8580_add_widgets(codec);
-
ret = snd_soc_init_card(socdev);
if (ret < 0) {
- printk(KERN_ERR "wm8580: failed to register card\n");
+ dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
+
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
- kfree(codec->reg_cache);
return ret;
}
-/* If the i2c layer weren't so broken, we could pass this kind of data
- around */
-static struct snd_soc_device *wm8580_socdev;
+/* power down chip */
+static int wm8580_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8580 = {
+ .probe = wm8580_probe,
+ .remove = wm8580_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580);
+
+static int wm8580_register(struct wm8580_priv *wm8580)
+{
+ int ret, i;
+ struct snd_soc_codec *codec = &wm8580->codec;
+
+ if (wm8580_codec) {
+ dev_err(codec->dev, "Another WM8580 is registered\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->private_data = wm8580;
+ codec->name = "WM8580";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8580_read_reg_cache;
+ codec->write = wm8580_write;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = wm8580_set_bias_level;
+ codec->dai = wm8580_dai;
+ codec->num_dai = ARRAY_SIZE(wm8580_dai);
+ codec->reg_cache_size = ARRAY_SIZE(wm8580->reg_cache);
+ codec->reg_cache = &wm8580->reg_cache;
+
+ memcpy(codec->reg_cache, wm8580_reg, sizeof(wm8580_reg));
+
+ /* Get the codec into a known state */
+ ret = wm8580_write(codec, WM8580_RESET, 0);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to reset codec: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8580_dai); i++)
+ wm8580_dai[i].dev = codec->dev;
+
+ wm8580_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ wm8580_codec = codec;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ goto err_codec;
+ }
+
+ return 0;
+
+err_codec:
+ snd_soc_unregister_codec(codec);
+err:
+ kfree(wm8580);
+ return ret;
+}
+
+static void wm8580_unregister(struct wm8580_priv *wm8580)
+{
+ wm8580_set_bias_level(&wm8580->codec, SND_SOC_BIAS_OFF);
+ snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
+ snd_soc_unregister_codec(&wm8580->codec);
+ kfree(wm8580);
+ wm8580_codec = NULL;
+}
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-
-/*
- * WM8580 2 wire address is determined by GPIO5
- * state during powerup.
- * low = 0x1a
- * high = 0x1b
- */
-
static int wm8580_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- struct snd_soc_device *socdev = wm8580_socdev;
- struct snd_soc_codec *codec = socdev->codec;
- int ret;
+ struct wm8580_priv *wm8580;
+ struct snd_soc_codec *codec;
- i2c_set_clientdata(i2c, codec);
+ wm8580 = kzalloc(sizeof(struct wm8580_priv), GFP_KERNEL);
+ if (wm8580 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8580->codec;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+
+ i2c_set_clientdata(i2c, wm8580);
codec->control_data = i2c;
- ret = wm8580_init(socdev);
- if (ret < 0)
- dev_err(&i2c->dev, "failed to initialise WM8580\n");
- return ret;
+ codec->dev = &i2c->dev;
+
+ return wm8580_register(wm8580);
}
static int wm8580_i2c_remove(struct i2c_client *client)
{
- struct snd_soc_codec *codec = i2c_get_clientdata(client);
- kfree(codec->reg_cache);
+ struct wm8580_priv *wm8580 = i2c_get_clientdata(client);
+ wm8580_unregister(wm8580);
return 0;
}
@@ -928,129 +999,35 @@
static struct i2c_driver wm8580_i2c_driver = {
.driver = {
- .name = "WM8580 I2C Codec",
+ .name = "wm8580",
.owner = THIS_MODULE,
},
.probe = wm8580_i2c_probe,
.remove = wm8580_i2c_remove,
.id_table = wm8580_i2c_id,
};
-
-static int wm8580_add_i2c_device(struct platform_device *pdev,
- const struct wm8580_setup_data *setup)
-{
- struct i2c_board_info info;
- struct i2c_adapter *adapter;
- struct i2c_client *client;
- int ret;
-
- ret = i2c_add_driver(&wm8580_i2c_driver);
- if (ret != 0) {
- dev_err(&pdev->dev, "can't add i2c driver\n");
- return ret;
- }
-
- memset(&info, 0, sizeof(struct i2c_board_info));
- info.addr = setup->i2c_address;
- strlcpy(info.type, "wm8580", I2C_NAME_SIZE);
-
- adapter = i2c_get_adapter(setup->i2c_bus);
- if (!adapter) {
- dev_err(&pdev->dev, "can't get i2c adapter %d\n",
- setup->i2c_bus);
- goto err_driver;
- }
-
- client = i2c_new_device(adapter, &info);
- i2c_put_adapter(adapter);
- if (!client) {
- dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
- (unsigned int)info.addr);
- goto err_driver;
- }
-
- return 0;
-
-err_driver:
- i2c_del_driver(&wm8580_i2c_driver);
- return -ENODEV;
-}
#endif
-static int wm8580_probe(struct platform_device *pdev)
-{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct wm8580_setup_data *setup;
- struct snd_soc_codec *codec;
- struct wm8580_priv *wm8580;
- int ret = 0;
-
- pr_info("WM8580 Audio Codec %s\n", WM8580_VERSION);
-
- setup = socdev->codec_data;
- codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (codec == NULL)
- return -ENOMEM;
-
- wm8580 = kzalloc(sizeof(struct wm8580_priv), GFP_KERNEL);
- if (wm8580 == NULL) {
- kfree(codec);
- return -ENOMEM;
- }
-
- codec->private_data = wm8580;
- socdev->codec = codec;
- mutex_init(&codec->mutex);
- INIT_LIST_HEAD(&codec->dapm_widgets);
- INIT_LIST_HEAD(&codec->dapm_paths);
- wm8580_socdev = socdev;
-
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- if (setup->i2c_address) {
- codec->hw_write = (hw_write_t)i2c_master_send;
- ret = wm8580_add_i2c_device(pdev, setup);
- }
-#else
- /* Add other interfaces here */
-#endif
- return ret;
-}
-
-/* power down chip */
-static int wm8580_remove(struct platform_device *pdev)
-{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
-
- if (codec->control_data)
- wm8580_set_bias_level(codec, SND_SOC_BIAS_OFF);
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- i2c_unregister_device(codec->control_data);
- i2c_del_driver(&wm8580_i2c_driver);
-#endif
- kfree(codec->private_data);
- kfree(codec);
-
- return 0;
-}
-
-struct snd_soc_codec_device soc_codec_dev_wm8580 = {
- .probe = wm8580_probe,
- .remove = wm8580_remove,
-};
-EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580);
-
static int __init wm8580_modinit(void)
{
- return snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
+ int ret;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ ret = i2c_add_driver(&wm8580_i2c_driver);
+ if (ret != 0) {
+ pr_err("Failed to register WM8580 I2C driver: %d\n", ret);
+ }
+#endif
+
+ return 0;
}
module_init(wm8580_modinit);
static void __exit wm8580_exit(void)
{
- snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&wm8580_i2c_driver);
+#endif
}
module_exit(wm8580_exit);
diff --git a/sound/soc/codecs/wm8580.h b/sound/soc/codecs/wm8580.h
index 09e4422..0dfb5dd 100644
--- a/sound/soc/codecs/wm8580.h
+++ b/sound/soc/codecs/wm8580.h
@@ -28,11 +28,6 @@
#define WM8580_CLKSRC_OSC 4
#define WM8580_CLKSRC_NONE 5
-struct wm8580_setup_data {
- int i2c_bus;
- unsigned short i2c_address;
-};
-
#define WM8580_DAI_PAIFRX 0
#define WM8580_DAI_PAIFTX 1
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index 80b1198..e7ff212 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -47,7 +47,7 @@
unsigned int reg)
{
u16 *cache = codec->reg_cache;
- BUG_ON(reg > ARRAY_SIZE(wm8728_reg_defaults));
+ BUG_ON(reg >= ARRAY_SIZE(wm8728_reg_defaults));
return cache[reg];
}
@@ -55,7 +55,7 @@
u16 reg, unsigned int value)
{
u16 *cache = codec->reg_cache;
- BUG_ON(reg > ARRAY_SIZE(wm8728_reg_defaults));
+ BUG_ON(reg >= ARRAY_SIZE(wm8728_reg_defaults));
cache[reg] = value;
}
@@ -92,21 +92,6 @@
SOC_SINGLE("Deemphasis", WM8728_DACCTL, 1, 1, 0),
};
-static int wm8728_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(wm8728_snd_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8728_snd_controls[i],
- codec, NULL));
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
/*
* DAPM controls.
*/
@@ -152,7 +137,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
u16 dac = wm8728_read_reg_cache(codec, WM8728_DACCTL);
dac &= ~0x18;
@@ -259,6 +244,12 @@
#define WM8728_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static struct snd_soc_dai_ops wm8728_dai_ops = {
+ .hw_params = wm8728_hw_params,
+ .digital_mute = wm8728_mute,
+ .set_fmt = wm8728_set_dai_fmt,
+};
+
struct snd_soc_dai wm8728_dai = {
.name = "WM8728",
.playback = {
@@ -268,18 +259,14 @@
.rates = WM8728_RATES,
.formats = WM8728_FORMATS,
},
- .ops = {
- .hw_params = wm8728_hw_params,
- .digital_mute = wm8728_mute,
- .set_fmt = wm8728_set_dai_fmt,
- }
+ .ops = &wm8728_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8728_dai);
static int wm8728_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
@@ -289,7 +276,7 @@
static int wm8728_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
wm8728_set_bias_level(codec, codec->suspend_bias_level);
@@ -302,7 +289,7 @@
*/
static int wm8728_init(struct snd_soc_device *socdev)
{
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
codec->name = "WM8728";
@@ -330,7 +317,8 @@
/* power on device */
wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- wm8728_add_controls(codec);
+ snd_soc_add_controls(codec, wm8728_snd_controls,
+ ARRAY_SIZE(wm8728_snd_controls));
wm8728_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@@ -363,7 +351,7 @@
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = wm8728_socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@@ -444,7 +432,7 @@
static int __devinit wm8728_spi_probe(struct spi_device *spi)
{
struct snd_soc_device *socdev = wm8728_socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret;
codec->control_data = spi;
@@ -508,7 +496,7 @@
if (codec == NULL)
return -ENOMEM;
- socdev->codec = codec;
+ socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -541,7 +529,7 @@
static int wm8728_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index c444b9f..e043e3f 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -29,15 +29,20 @@
#include "wm8731.h"
-#define WM8731_VERSION "0.13"
-
+static struct snd_soc_codec *wm8731_codec;
struct snd_soc_codec_device soc_codec_dev_wm8731;
/* codec private data */
struct wm8731_priv {
+ struct snd_soc_codec codec;
+ u16 reg_cache[WM8731_CACHEREGNUM];
unsigned int sysclk;
};
+#ifdef CONFIG_SPI_MASTER
+static int wm8731_spi_write(struct spi_device *spi, const char *data, int len);
+#endif
+
/*
* wm8731 register cache
* We can't read the WM8731 register space when we are
@@ -129,22 +134,6 @@
SOC_ENUM("Playback De-emphasis", wm8731_enum[1]),
};
-/* add non dapm controls */
-static int wm8731_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(wm8731_snd_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8731_snd_controls[i],
- codec, NULL));
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
/* Output Mixer */
static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
@@ -269,7 +258,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct wm8731_priv *wm8731 = codec->private_data;
u16 iface = wm8731_read_reg_cache(codec, WM8731_IFACE) & 0xfff3;
int i = get_coeff(wm8731->sysclk, params_rate(params));
@@ -299,7 +288,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
/* set active */
wm8731_write(codec, WM8731_ACTIVE, 0x0001);
@@ -312,7 +301,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
/* deactivate */
if (!codec->active) {
@@ -414,21 +403,19 @@
static int wm8731_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- u16 reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f;
+ u16 reg;
switch (level) {
case SND_SOC_BIAS_ON:
- /* vref/mid, osc on, dac unmute */
- wm8731_write(codec, WM8731_PWR, reg);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- /* everything off except vref/vmid, */
+ /* Clear PWROFF, gate CLKOUT, everything else as-is */
+ reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f;
wm8731_write(codec, WM8731_PWR, reg | 0x0040);
break;
case SND_SOC_BIAS_OFF:
- /* everything off, dac mute, inactive */
wm8731_write(codec, WM8731_ACTIVE, 0x0);
wm8731_write(codec, WM8731_PWR, 0xffff);
break;
@@ -446,6 +433,15 @@
#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static struct snd_soc_dai_ops wm8731_dai_ops = {
+ .prepare = wm8731_pcm_prepare,
+ .hw_params = wm8731_hw_params,
+ .shutdown = wm8731_shutdown,
+ .digital_mute = wm8731_mute,
+ .set_sysclk = wm8731_set_dai_sysclk,
+ .set_fmt = wm8731_set_dai_fmt,
+};
+
struct snd_soc_dai wm8731_dai = {
.name = "WM8731",
.playback = {
@@ -460,21 +456,14 @@
.channels_max = 2,
.rates = WM8731_RATES,
.formats = WM8731_FORMATS,},
- .ops = {
- .prepare = wm8731_pcm_prepare,
- .hw_params = wm8731_hw_params,
- .shutdown = wm8731_shutdown,
- .digital_mute = wm8731_mute,
- .set_sysclk = wm8731_set_dai_sysclk,
- .set_fmt = wm8731_set_dai_fmt,
- }
+ .ops = &wm8731_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8731_dai);
static int wm8731_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
wm8731_write(codec, WM8731_ACTIVE, 0x0);
wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
@@ -484,7 +473,7 @@
static int wm8731_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
@@ -500,54 +489,33 @@
return 0;
}
-/*
- * initialise the WM8731 driver
- * register the mixer and dsp interfaces with the kernel
- */
-static int wm8731_init(struct snd_soc_device *socdev)
+static int wm8731_probe(struct platform_device *pdev)
{
- struct snd_soc_codec *codec = socdev->codec;
- int reg, ret = 0;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
- codec->name = "WM8731";
- codec->owner = THIS_MODULE;
- codec->read = wm8731_read_reg_cache;
- codec->write = wm8731_write;
- codec->set_bias_level = wm8731_set_bias_level;
- codec->dai = &wm8731_dai;
- codec->num_dai = 1;
- codec->reg_cache_size = ARRAY_SIZE(wm8731_reg);
- codec->reg_cache = kmemdup(wm8731_reg, sizeof(wm8731_reg), GFP_KERNEL);
- if (codec->reg_cache == NULL)
- return -ENOMEM;
+ if (wm8731_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
- wm8731_reset(codec);
+ socdev->card->codec = wm8731_codec;
+ codec = wm8731_codec;
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
- printk(KERN_ERR "wm8731: failed to create pcms\n");
+ dev_err(codec->dev, "failed to create pcms: %d\n", ret);
goto pcm_err;
}
- /* power on device */
- wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
- /* set the update bits */
- reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V);
- wm8731_write(codec, WM8731_LOUT1V, reg & ~0x0100);
- reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V);
- wm8731_write(codec, WM8731_ROUT1V, reg & ~0x0100);
- reg = wm8731_read_reg_cache(codec, WM8731_LINVOL);
- wm8731_write(codec, WM8731_LINVOL, reg & ~0x0100);
- reg = wm8731_read_reg_cache(codec, WM8731_RINVOL);
- wm8731_write(codec, WM8731_RINVOL, reg & ~0x0100);
-
- wm8731_add_controls(codec);
+ snd_soc_add_controls(codec, wm8731_snd_controls,
+ ARRAY_SIZE(wm8731_snd_controls));
wm8731_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
- printk(KERN_ERR "wm8731: failed to register card\n");
+ dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
@@ -557,133 +525,109 @@
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
- kfree(codec->reg_cache);
return ret;
}
-static struct snd_soc_device *wm8731_socdev;
-
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-
-/*
- * WM8731 2 wire address is determined by GPIO5
- * state during powerup.
- * low = 0x1a
- * high = 0x1b
- */
-
-static int wm8731_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+/* power down chip */
+static int wm8731_remove(struct platform_device *pdev)
{
- struct snd_soc_device *socdev = wm8731_socdev;
- struct snd_soc_codec *codec = socdev->codec;
- int ret;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- i2c_set_clientdata(i2c, codec);
- codec->control_data = i2c;
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
- ret = wm8731_init(socdev);
- if (ret < 0)
- pr_err("failed to initialise WM8731\n");
-
- return ret;
-}
-
-static int wm8731_i2c_remove(struct i2c_client *client)
-{
- struct snd_soc_codec *codec = i2c_get_clientdata(client);
- kfree(codec->reg_cache);
return 0;
}
-static const struct i2c_device_id wm8731_i2c_id[] = {
- { "wm8731", 0 },
- { }
+struct snd_soc_codec_device soc_codec_dev_wm8731 = {
+ .probe = wm8731_probe,
+ .remove = wm8731_remove,
+ .suspend = wm8731_suspend,
+ .resume = wm8731_resume,
};
-MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
-static struct i2c_driver wm8731_i2c_driver = {
- .driver = {
- .name = "WM8731 I2C Codec",
- .owner = THIS_MODULE,
- },
- .probe = wm8731_i2c_probe,
- .remove = wm8731_i2c_remove,
- .id_table = wm8731_i2c_id,
-};
-
-static int wm8731_add_i2c_device(struct platform_device *pdev,
- const struct wm8731_setup_data *setup)
+static int wm8731_register(struct wm8731_priv *wm8731)
{
- struct i2c_board_info info;
- struct i2c_adapter *adapter;
- struct i2c_client *client;
int ret;
+ struct snd_soc_codec *codec = &wm8731->codec;
+ u16 reg;
- ret = i2c_add_driver(&wm8731_i2c_driver);
- if (ret != 0) {
- dev_err(&pdev->dev, "can't add i2c driver\n");
+ if (wm8731_codec) {
+ dev_err(codec->dev, "Another WM8731 is registered\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->private_data = wm8731;
+ codec->name = "WM8731";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8731_read_reg_cache;
+ codec->write = wm8731_write;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = wm8731_set_bias_level;
+ codec->dai = &wm8731_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = WM8731_CACHEREGNUM;
+ codec->reg_cache = &wm8731->reg_cache;
+
+ memcpy(codec->reg_cache, wm8731_reg, sizeof(wm8731_reg));
+
+ ret = wm8731_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
return ret;
}
- memset(&info, 0, sizeof(struct i2c_board_info));
- info.addr = setup->i2c_address;
- strlcpy(info.type, "wm8731", I2C_NAME_SIZE);
+ wm8731_dai.dev = codec->dev;
- adapter = i2c_get_adapter(setup->i2c_bus);
- if (!adapter) {
- dev_err(&pdev->dev, "can't get i2c adapter %d\n",
- setup->i2c_bus);
- goto err_driver;
+ wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* Latch the update bits */
+ reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V);
+ wm8731_write(codec, WM8731_LOUT1V, reg & ~0x0100);
+ reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V);
+ wm8731_write(codec, WM8731_ROUT1V, reg & ~0x0100);
+ reg = wm8731_read_reg_cache(codec, WM8731_LINVOL);
+ wm8731_write(codec, WM8731_LINVOL, reg & ~0x0100);
+ reg = wm8731_read_reg_cache(codec, WM8731_RINVOL);
+ wm8731_write(codec, WM8731_RINVOL, reg & ~0x0100);
+
+ /* Disable bypass path by default */
+ reg = wm8731_read_reg_cache(codec, WM8731_APANA);
+ wm8731_write(codec, WM8731_APANA, reg & ~0x4);
+
+ wm8731_codec = codec;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ return ret;
}
- client = i2c_new_device(adapter, &info);
- i2c_put_adapter(adapter);
- if (!client) {
- dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
- (unsigned int)info.addr);
- goto err_driver;
+ ret = snd_soc_register_dai(&wm8731_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ snd_soc_unregister_codec(codec);
+ return ret;
}
return 0;
-
-err_driver:
- i2c_del_driver(&wm8731_i2c_driver);
- return -ENODEV;
}
-#endif
+
+static void wm8731_unregister(struct wm8731_priv *wm8731)
+{
+ wm8731_set_bias_level(&wm8731->codec, SND_SOC_BIAS_OFF);
+ snd_soc_unregister_dai(&wm8731_dai);
+ snd_soc_unregister_codec(&wm8731->codec);
+ kfree(wm8731);
+ wm8731_codec = NULL;
+}
#if defined(CONFIG_SPI_MASTER)
-static int __devinit wm8731_spi_probe(struct spi_device *spi)
-{
- struct snd_soc_device *socdev = wm8731_socdev;
- struct snd_soc_codec *codec = socdev->codec;
- int ret;
-
- codec->control_data = spi;
-
- ret = wm8731_init(socdev);
- if (ret < 0)
- dev_err(&spi->dev, "failed to initialise WM8731\n");
-
- return ret;
-}
-
-static int __devexit wm8731_spi_remove(struct spi_device *spi)
-{
- return 0;
-}
-
-static struct spi_driver wm8731_spi_driver = {
- .driver = {
- .name = "wm8731",
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
- },
- .probe = wm8731_spi_probe,
- .remove = __devexit_p(wm8731_spi_remove),
-};
-
static int wm8731_spi_write(struct spi_device *spi, const char *data, int len)
{
struct spi_transfer t;
@@ -707,101 +651,121 @@
return len;
}
-#endif /* CONFIG_SPI_MASTER */
-static int wm8731_probe(struct platform_device *pdev)
+static int __devinit wm8731_spi_probe(struct spi_device *spi)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct wm8731_setup_data *setup;
struct snd_soc_codec *codec;
struct wm8731_priv *wm8731;
- int ret = 0;
-
- pr_info("WM8731 Audio Codec %s", WM8731_VERSION);
-
- setup = socdev->codec_data;
- codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (codec == NULL)
- return -ENOMEM;
wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL);
- if (wm8731 == NULL) {
- kfree(codec);
+ if (wm8731 == NULL)
return -ENOMEM;
- }
- codec->private_data = wm8731;
- socdev->codec = codec;
- mutex_init(&codec->mutex);
- INIT_LIST_HEAD(&codec->dapm_widgets);
- INIT_LIST_HEAD(&codec->dapm_paths);
+ codec = &wm8731->codec;
+ codec->control_data = spi;
+ codec->hw_write = (hw_write_t)wm8731_spi_write;
+ codec->dev = &spi->dev;
- wm8731_socdev = socdev;
- ret = -ENODEV;
+ spi->dev.driver_data = wm8731;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- if (setup->i2c_address) {
- codec->hw_write = (hw_write_t)i2c_master_send;
- ret = wm8731_add_i2c_device(pdev, setup);
- }
-#endif
-#if defined(CONFIG_SPI_MASTER)
- if (setup->spi) {
- codec->hw_write = (hw_write_t)wm8731_spi_write;
- ret = spi_register_driver(&wm8731_spi_driver);
- if (ret != 0)
- printk(KERN_ERR "can't add spi driver");
- }
-#endif
-
- if (ret != 0) {
- kfree(codec->private_data);
- kfree(codec);
- }
- return ret;
+ return wm8731_register(wm8731);
}
-/* power down chip */
-static int wm8731_remove(struct platform_device *pdev)
+static int __devexit wm8731_spi_remove(struct spi_device *spi)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct wm8731_priv *wm8731 = spi->dev.driver_data;
- if (codec->control_data)
- wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- i2c_unregister_device(codec->control_data);
- i2c_del_driver(&wm8731_i2c_driver);
-#endif
-#if defined(CONFIG_SPI_MASTER)
- spi_unregister_driver(&wm8731_spi_driver);
-#endif
- kfree(codec->private_data);
- kfree(codec);
+ wm8731_unregister(wm8731);
return 0;
}
-struct snd_soc_codec_device soc_codec_dev_wm8731 = {
- .probe = wm8731_probe,
- .remove = wm8731_remove,
- .suspend = wm8731_suspend,
- .resume = wm8731_resume,
+static struct spi_driver wm8731_spi_driver = {
+ .driver = {
+ .name = "wm8731",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8731_spi_probe,
+ .remove = __devexit_p(wm8731_spi_remove),
};
-EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
+#endif /* CONFIG_SPI_MASTER */
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int wm8731_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8731_priv *wm8731;
+ struct snd_soc_codec *codec;
+
+ wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL);
+ if (wm8731 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8731->codec;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+
+ i2c_set_clientdata(i2c, wm8731);
+ codec->control_data = i2c;
+
+ codec->dev = &i2c->dev;
+
+ return wm8731_register(wm8731);
+}
+
+static __devexit int wm8731_i2c_remove(struct i2c_client *client)
+{
+ struct wm8731_priv *wm8731 = i2c_get_clientdata(client);
+ wm8731_unregister(wm8731);
+ return 0;
+}
+
+static const struct i2c_device_id wm8731_i2c_id[] = {
+ { "wm8731", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
+
+static struct i2c_driver wm8731_i2c_driver = {
+ .driver = {
+ .name = "WM8731 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8731_i2c_probe,
+ .remove = __devexit_p(wm8731_i2c_remove),
+ .id_table = wm8731_i2c_id,
+};
+#endif
static int __init wm8731_modinit(void)
{
- return snd_soc_register_dai(&wm8731_dai);
+ int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ ret = i2c_add_driver(&wm8731_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8731 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8731_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8731 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return 0;
}
module_init(wm8731_modinit);
static void __exit wm8731_exit(void)
{
- snd_soc_unregister_dai(&wm8731_dai);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&wm8731_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8731_spi_driver);
+#endif
}
module_exit(wm8731_exit);
diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h
index 95190e9..cd7b806 100644
--- a/sound/soc/codecs/wm8731.h
+++ b/sound/soc/codecs/wm8731.h
@@ -34,12 +34,6 @@
#define WM8731_SYSCLK 0
#define WM8731_DAI 0
-struct wm8731_setup_data {
- int spi;
- int i2c_bus;
- unsigned short i2c_address;
-};
-
extern struct snd_soc_dai wm8731_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8731;
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 5997fa6..b64509b 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -231,21 +231,6 @@
};
-/* add non dapm controls */
-static int wm8750_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(wm8750_snd_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8750_snd_controls[i],
- codec, NULL));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
/*
* DAPM Controls
*/
@@ -619,7 +604,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct wm8750_priv *wm8750 = codec->private_data;
u16 iface = wm8750_read_reg_cache(codec, WM8750_IFACE) & 0x1f3;
u16 srate = wm8750_read_reg_cache(codec, WM8750_SRATE) & 0x1c0;
@@ -694,6 +679,13 @@
#define WM8750_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static struct snd_soc_dai_ops wm8750_dai_ops = {
+ .hw_params = wm8750_pcm_hw_params,
+ .digital_mute = wm8750_mute,
+ .set_fmt = wm8750_set_dai_fmt,
+ .set_sysclk = wm8750_set_dai_sysclk,
+};
+
struct snd_soc_dai wm8750_dai = {
.name = "WM8750",
.playback = {
@@ -708,12 +700,7 @@
.channels_max = 2,
.rates = WM8750_RATES,
.formats = WM8750_FORMATS,},
- .ops = {
- .hw_params = wm8750_pcm_hw_params,
- .digital_mute = wm8750_mute,
- .set_fmt = wm8750_set_dai_fmt,
- .set_sysclk = wm8750_set_dai_sysclk,
- },
+ .ops = &wm8750_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8750_dai);
@@ -727,7 +714,7 @@
static int wm8750_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@@ -736,7 +723,7 @@
static int wm8750_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
@@ -769,7 +756,7 @@
*/
static int wm8750_init(struct snd_soc_device *socdev)
{
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int reg, ret = 0;
codec->name = "WM8750";
@@ -816,7 +803,8 @@
reg = wm8750_read_reg_cache(codec, WM8750_RINVOL);
wm8750_write(codec, WM8750_RINVOL, reg | 0x0100);
- wm8750_add_controls(codec);
+ snd_soc_add_controls(codec, wm8750_snd_controls,
+ ARRAY_SIZE(wm8750_snd_controls));
wm8750_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@@ -850,7 +838,7 @@
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = wm8750_socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@@ -931,7 +919,7 @@
static int __devinit wm8750_spi_probe(struct spi_device *spi)
{
struct snd_soc_device *socdev = wm8750_socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret;
codec->control_data = spi;
@@ -1003,7 +991,7 @@
}
codec->private_data = wm8750;
- socdev->codec = codec;
+ socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -1057,7 +1045,7 @@
static int wm8750_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index 77620ab..a6e8f3f 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -51,8 +51,6 @@
#include "wm8753.h"
-#define WM8753_VERSION "0.16"
-
static int caps_charge = 2000;
module_param(caps_charge, int, 0);
MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)");
@@ -60,12 +58,6 @@
static void wm8753_set_dai_mode(struct snd_soc_codec *codec,
unsigned int mode);
-/* codec private data */
-struct wm8753_priv {
- unsigned int sysclk;
- unsigned int pcmclk;
-};
-
/*
* wm8753 register cache
* We can't read the WM8753 register space when we
@@ -90,6 +82,14 @@
0x0000, 0x0000
};
+/* codec private data */
+struct wm8753_priv {
+ unsigned int sysclk;
+ unsigned int pcmclk;
+ struct snd_soc_codec codec;
+ u16 reg_cache[ARRAY_SIZE(wm8753_reg)];
+};
+
/*
* read wm8753 register cache
*/
@@ -97,7 +97,7 @@
unsigned int reg)
{
u16 *cache = codec->reg_cache;
- if (reg < 1 || reg > (ARRAY_SIZE(wm8753_reg) + 1))
+ if (reg < 1 || reg >= (ARRAY_SIZE(wm8753_reg) + 1))
return -1;
return cache[reg - 1];
}
@@ -109,7 +109,7 @@
unsigned int reg, unsigned int value)
{
u16 *cache = codec->reg_cache;
- if (reg < 1 || reg > 0x3f)
+ if (reg < 1 || reg >= (ARRAY_SIZE(wm8753_reg) + 1))
return;
cache[reg - 1] = value;
}
@@ -339,21 +339,6 @@
SOC_ENUM("ROUT2 Phase", wm8753_enum[28]),
};
-/* add non dapm controls */
-static int wm8753_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(wm8753_snd_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8753_snd_controls[i],
- codec, NULL));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
/*
* _DAPM_ Controls
*/
@@ -927,7 +912,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct wm8753_priv *wm8753 = codec->private_data;
u16 voice = wm8753_read_reg_cache(codec, WM8753_PCM) & 0x01f3;
u16 srate = wm8753_read_reg_cache(codec, WM8753_SRATE1) & 0x017f;
@@ -1161,7 +1146,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct wm8753_priv *wm8753 = codec->private_data;
u16 srate = wm8753_read_reg_cache(codec, WM8753_SRATE1) & 0x01c0;
u16 hifi = wm8753_read_reg_cache(codec, WM8753_HIFI) & 0x01f3;
@@ -1316,6 +1301,51 @@
* 3. Voice disabled - HIFI over HIFI
* 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture
*/
+static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode1 = {
+ .hw_params = wm8753_i2s_hw_params,
+ .digital_mute = wm8753_mute,
+ .set_fmt = wm8753_mode1h_set_dai_fmt,
+ .set_clkdiv = wm8753_set_dai_clkdiv,
+ .set_pll = wm8753_set_dai_pll,
+ .set_sysclk = wm8753_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode1 = {
+ .hw_params = wm8753_pcm_hw_params,
+ .digital_mute = wm8753_mute,
+ .set_fmt = wm8753_mode1v_set_dai_fmt,
+ .set_clkdiv = wm8753_set_dai_clkdiv,
+ .set_pll = wm8753_set_dai_pll,
+ .set_sysclk = wm8753_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode2 = {
+ .hw_params = wm8753_pcm_hw_params,
+ .digital_mute = wm8753_mute,
+ .set_fmt = wm8753_mode2_set_dai_fmt,
+ .set_clkdiv = wm8753_set_dai_clkdiv,
+ .set_pll = wm8753_set_dai_pll,
+ .set_sysclk = wm8753_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode3 = {
+ .hw_params = wm8753_i2s_hw_params,
+ .digital_mute = wm8753_mute,
+ .set_fmt = wm8753_mode3_4_set_dai_fmt,
+ .set_clkdiv = wm8753_set_dai_clkdiv,
+ .set_pll = wm8753_set_dai_pll,
+ .set_sysclk = wm8753_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode4 = {
+ .hw_params = wm8753_i2s_hw_params,
+ .digital_mute = wm8753_mute,
+ .set_fmt = wm8753_mode3_4_set_dai_fmt,
+ .set_clkdiv = wm8753_set_dai_clkdiv,
+ .set_pll = wm8753_set_dai_pll,
+ .set_sysclk = wm8753_set_dai_sysclk,
+};
+
static const struct snd_soc_dai wm8753_all_dai[] = {
/* DAI HiFi mode 1 */
{ .name = "WM8753 HiFi",
@@ -1332,14 +1362,7 @@
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS},
- .ops = {
- .hw_params = wm8753_i2s_hw_params,
- .digital_mute = wm8753_mute,
- .set_fmt = wm8753_mode1h_set_dai_fmt,
- .set_clkdiv = wm8753_set_dai_clkdiv,
- .set_pll = wm8753_set_dai_pll,
- .set_sysclk = wm8753_set_dai_sysclk,
- },
+ .ops = &wm8753_dai_ops_hifi_mode1,
},
/* DAI Voice mode 1 */
{ .name = "WM8753 Voice",
@@ -1356,14 +1379,7 @@
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
- .ops = {
- .hw_params = wm8753_pcm_hw_params,
- .digital_mute = wm8753_mute,
- .set_fmt = wm8753_mode1v_set_dai_fmt,
- .set_clkdiv = wm8753_set_dai_clkdiv,
- .set_pll = wm8753_set_dai_pll,
- .set_sysclk = wm8753_set_dai_sysclk,
- },
+ .ops = &wm8753_dai_ops_voice_mode1,
},
/* DAI HiFi mode 2 - dummy */
{ .name = "WM8753 HiFi",
@@ -1384,14 +1400,7 @@
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
- .ops = {
- .hw_params = wm8753_pcm_hw_params,
- .digital_mute = wm8753_mute,
- .set_fmt = wm8753_mode2_set_dai_fmt,
- .set_clkdiv = wm8753_set_dai_clkdiv,
- .set_pll = wm8753_set_dai_pll,
- .set_sysclk = wm8753_set_dai_sysclk,
- },
+ .ops = &wm8753_dai_ops_voice_mode2,
},
/* DAI HiFi mode 3 */
{ .name = "WM8753 HiFi",
@@ -1408,14 +1417,7 @@
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
- .ops = {
- .hw_params = wm8753_i2s_hw_params,
- .digital_mute = wm8753_mute,
- .set_fmt = wm8753_mode3_4_set_dai_fmt,
- .set_clkdiv = wm8753_set_dai_clkdiv,
- .set_pll = wm8753_set_dai_pll,
- .set_sysclk = wm8753_set_dai_sysclk,
- },
+ .ops = &wm8753_dai_ops_hifi_mode3,
},
/* DAI Voice mode 3 - dummy */
{ .name = "WM8753 Voice",
@@ -1436,14 +1438,7 @@
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
- .ops = {
- .hw_params = wm8753_i2s_hw_params,
- .digital_mute = wm8753_mute,
- .set_fmt = wm8753_mode3_4_set_dai_fmt,
- .set_clkdiv = wm8753_set_dai_clkdiv,
- .set_pll = wm8753_set_dai_pll,
- .set_sysclk = wm8753_set_dai_sysclk,
- },
+ .ops = &wm8753_dai_ops_hifi_mode4,
},
/* DAI Voice mode 4 - dummy */
{ .name = "WM8753 Voice",
@@ -1466,30 +1461,35 @@
if (mode < 4) {
int playback_active, capture_active, codec_active, pop_wait;
void *private_data;
+ struct list_head list;
playback_active = wm8753_dai[0].playback.active;
capture_active = wm8753_dai[0].capture.active;
codec_active = wm8753_dai[0].active;
private_data = wm8753_dai[0].private_data;
pop_wait = wm8753_dai[0].pop_wait;
+ list = wm8753_dai[0].list;
wm8753_dai[0] = wm8753_all_dai[mode << 1];
wm8753_dai[0].playback.active = playback_active;
wm8753_dai[0].capture.active = capture_active;
wm8753_dai[0].active = codec_active;
wm8753_dai[0].private_data = private_data;
wm8753_dai[0].pop_wait = pop_wait;
+ wm8753_dai[0].list = list;
playback_active = wm8753_dai[1].playback.active;
capture_active = wm8753_dai[1].capture.active;
codec_active = wm8753_dai[1].active;
private_data = wm8753_dai[1].private_data;
pop_wait = wm8753_dai[1].pop_wait;
+ list = wm8753_dai[1].list;
wm8753_dai[1] = wm8753_all_dai[(mode << 1) + 1];
wm8753_dai[1].playback.active = playback_active;
wm8753_dai[1].capture.active = capture_active;
wm8753_dai[1].active = codec_active;
wm8753_dai[1].private_data = private_data;
wm8753_dai[1].pop_wait = pop_wait;
+ wm8753_dai[1].list = list;
}
wm8753_dai[0].codec = codec;
wm8753_dai[1].codec = codec;
@@ -1505,7 +1505,7 @@
static int wm8753_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
/* we only need to suspend if we are a valid card */
if (!codec->card)
@@ -1518,7 +1518,7 @@
static int wm8753_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
@@ -1531,6 +1531,11 @@
for (i = 0; i < ARRAY_SIZE(wm8753_reg); i++) {
if (i + 1 == WM8753_RESET)
continue;
+
+ /* No point in writing hardware default values back */
+ if (cache[i] == wm8753_reg[i])
+ continue;
+
data[0] = ((i + 1) << 1) | ((cache[i] >> 8) & 0x0001);
data[1] = cache[i] & 0x00ff;
codec->hw_write(codec->control_data, data, 2);
@@ -1549,32 +1554,24 @@
return 0;
}
-/*
- * initialise the WM8753 driver
- * register the mixer and dsp interfaces with the kernel
- */
-static int wm8753_init(struct snd_soc_device *socdev)
+static struct snd_soc_codec *wm8753_codec;
+
+static int wm8753_probe(struct platform_device *pdev)
{
- struct snd_soc_codec *codec = socdev->codec;
- int reg, ret = 0;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
- codec->name = "WM8753";
- codec->owner = THIS_MODULE;
- codec->read = wm8753_read_reg_cache;
- codec->write = wm8753_write;
- codec->set_bias_level = wm8753_set_bias_level;
- codec->dai = wm8753_dai;
- codec->num_dai = 2;
- codec->reg_cache_size = ARRAY_SIZE(wm8753_reg);
- codec->reg_cache = kmemdup(wm8753_reg, sizeof(wm8753_reg), GFP_KERNEL);
+ if (!wm8753_codec) {
+ dev_err(&pdev->dev, "WM8753 codec not yet registered\n");
+ return -EINVAL;
+ }
- if (codec->reg_cache == NULL)
- return -ENOMEM;
+ socdev->card->codec = wm8753_codec;
+ codec = wm8753_codec;
wm8753_set_dai_mode(codec, 0);
- wm8753_reset(codec);
-
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
@@ -1582,35 +1579,8 @@
goto pcm_err;
}
- /* charge output caps */
- wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
- codec->bias_level = SND_SOC_BIAS_STANDBY;
- schedule_delayed_work(&codec->delayed_work,
- msecs_to_jiffies(caps_charge));
-
- /* set the update bits */
- reg = wm8753_read_reg_cache(codec, WM8753_LDAC);
- wm8753_write(codec, WM8753_LDAC, reg | 0x0100);
- reg = wm8753_read_reg_cache(codec, WM8753_RDAC);
- wm8753_write(codec, WM8753_RDAC, reg | 0x0100);
- reg = wm8753_read_reg_cache(codec, WM8753_LADC);
- wm8753_write(codec, WM8753_LADC, reg | 0x0100);
- reg = wm8753_read_reg_cache(codec, WM8753_RADC);
- wm8753_write(codec, WM8753_RADC, reg | 0x0100);
- reg = wm8753_read_reg_cache(codec, WM8753_LOUT1V);
- wm8753_write(codec, WM8753_LOUT1V, reg | 0x0100);
- reg = wm8753_read_reg_cache(codec, WM8753_ROUT1V);
- wm8753_write(codec, WM8753_ROUT1V, reg | 0x0100);
- reg = wm8753_read_reg_cache(codec, WM8753_LOUT2V);
- wm8753_write(codec, WM8753_LOUT2V, reg | 0x0100);
- reg = wm8753_read_reg_cache(codec, WM8753_ROUT2V);
- wm8753_write(codec, WM8753_ROUT2V, reg | 0x0100);
- reg = wm8753_read_reg_cache(codec, WM8753_LINVOL);
- wm8753_write(codec, WM8753_LINVOL, reg | 0x0100);
- reg = wm8753_read_reg_cache(codec, WM8753_RINVOL);
- wm8753_write(codec, WM8753_RINVOL, reg | 0x0100);
-
- wm8753_add_controls(codec);
+ snd_soc_add_controls(codec, wm8753_snd_controls,
+ ARRAY_SIZE(wm8753_snd_controls));
wm8753_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@@ -1618,215 +1588,13 @@
goto card_err;
}
- return ret;
+ return 0;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
+
pcm_err:
- kfree(codec->reg_cache);
- return ret;
-}
-
-/* If the i2c layer weren't so broken, we could pass this kind of data
- around */
-static struct snd_soc_device *wm8753_socdev;
-
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-
-/*
- * WM8753 2 wire address is determined by GPIO5
- * state during powerup.
- * low = 0x1a
- * high = 0x1b
- */
-
-static int wm8753_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
-{
- struct snd_soc_device *socdev = wm8753_socdev;
- struct snd_soc_codec *codec = socdev->codec;
- int ret;
-
- i2c_set_clientdata(i2c, codec);
- codec->control_data = i2c;
-
- ret = wm8753_init(socdev);
- if (ret < 0)
- pr_err("failed to initialise WM8753\n");
-
- return ret;
-}
-
-static int wm8753_i2c_remove(struct i2c_client *client)
-{
- struct snd_soc_codec *codec = i2c_get_clientdata(client);
- kfree(codec->reg_cache);
- return 0;
-}
-
-static const struct i2c_device_id wm8753_i2c_id[] = {
- { "wm8753", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, wm8753_i2c_id);
-
-static struct i2c_driver wm8753_i2c_driver = {
- .driver = {
- .name = "WM8753 I2C Codec",
- .owner = THIS_MODULE,
- },
- .probe = wm8753_i2c_probe,
- .remove = wm8753_i2c_remove,
- .id_table = wm8753_i2c_id,
-};
-
-static int wm8753_add_i2c_device(struct platform_device *pdev,
- const struct wm8753_setup_data *setup)
-{
- struct i2c_board_info info;
- struct i2c_adapter *adapter;
- struct i2c_client *client;
- int ret;
-
- ret = i2c_add_driver(&wm8753_i2c_driver);
- if (ret != 0) {
- dev_err(&pdev->dev, "can't add i2c driver\n");
- return ret;
- }
-
- memset(&info, 0, sizeof(struct i2c_board_info));
- info.addr = setup->i2c_address;
- strlcpy(info.type, "wm8753", I2C_NAME_SIZE);
-
- adapter = i2c_get_adapter(setup->i2c_bus);
- if (!adapter) {
- dev_err(&pdev->dev, "can't get i2c adapter %d\n",
- setup->i2c_bus);
- goto err_driver;
- }
-
- client = i2c_new_device(adapter, &info);
- i2c_put_adapter(adapter);
- if (!client) {
- dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
- (unsigned int)info.addr);
- goto err_driver;
- }
-
- return 0;
-
-err_driver:
- i2c_del_driver(&wm8753_i2c_driver);
- return -ENODEV;
-}
-#endif
-
-#if defined(CONFIG_SPI_MASTER)
-static int __devinit wm8753_spi_probe(struct spi_device *spi)
-{
- struct snd_soc_device *socdev = wm8753_socdev;
- struct snd_soc_codec *codec = socdev->codec;
- int ret;
-
- codec->control_data = spi;
-
- ret = wm8753_init(socdev);
- if (ret < 0)
- dev_err(&spi->dev, "failed to initialise WM8753\n");
-
- return ret;
-}
-
-static int __devexit wm8753_spi_remove(struct spi_device *spi)
-{
- return 0;
-}
-
-static struct spi_driver wm8753_spi_driver = {
- .driver = {
- .name = "wm8753",
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
- },
- .probe = wm8753_spi_probe,
- .remove = __devexit_p(wm8753_spi_remove),
-};
-
-static int wm8753_spi_write(struct spi_device *spi, const char *data, int len)
-{
- 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;
-}
-#endif
-
-
-static int wm8753_probe(struct platform_device *pdev)
-{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct wm8753_setup_data *setup;
- struct snd_soc_codec *codec;
- struct wm8753_priv *wm8753;
- int ret = 0;
-
- pr_info("WM8753 Audio Codec %s", WM8753_VERSION);
-
- setup = socdev->codec_data;
- codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (codec == NULL)
- return -ENOMEM;
-
- wm8753 = kzalloc(sizeof(struct wm8753_priv), GFP_KERNEL);
- if (wm8753 == NULL) {
- kfree(codec);
- return -ENOMEM;
- }
-
- codec->private_data = wm8753;
- socdev->codec = codec;
- mutex_init(&codec->mutex);
- INIT_LIST_HEAD(&codec->dapm_widgets);
- INIT_LIST_HEAD(&codec->dapm_paths);
- wm8753_socdev = socdev;
- INIT_DELAYED_WORK(&codec->delayed_work, wm8753_work);
-
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- if (setup->i2c_address) {
- codec->hw_write = (hw_write_t)i2c_master_send;
- ret = wm8753_add_i2c_device(pdev, setup);
- }
-#endif
-#if defined(CONFIG_SPI_MASTER)
- if (setup->spi) {
- codec->hw_write = (hw_write_t)wm8753_spi_write;
- ret = spi_register_driver(&wm8753_spi_driver);
- if (ret != 0)
- printk(KERN_ERR "can't add spi driver");
- }
-#endif
-
- if (ret != 0) {
- kfree(codec->private_data);
- kfree(codec);
- }
return ret;
}
@@ -1853,22 +1621,9 @@
static int wm8753_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
- if (codec->control_data)
- wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
- run_delayed_work(&codec->delayed_work);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- i2c_unregister_device(codec->control_data);
- i2c_del_driver(&wm8753_i2c_driver);
-#endif
-#if defined(CONFIG_SPI_MASTER)
- spi_unregister_driver(&wm8753_spi_driver);
-#endif
- kfree(codec->private_data);
- kfree(codec);
return 0;
}
@@ -1881,15 +1636,240 @@
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8753);
+static int wm8753_register(struct wm8753_priv *wm8753)
+{
+ int ret, i;
+ struct snd_soc_codec *codec = &wm8753->codec;
+ u16 reg;
+
+ if (wm8753_codec) {
+ dev_err(codec->dev, "Multiple WM8753 devices not supported\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->name = "WM8753";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8753_read_reg_cache;
+ codec->write = wm8753_write;
+ codec->bias_level = SND_SOC_BIAS_STANDBY;
+ codec->set_bias_level = wm8753_set_bias_level;
+ codec->dai = wm8753_dai;
+ codec->num_dai = 2;
+ codec->reg_cache_size = ARRAY_SIZE(wm8753->reg_cache);
+ codec->reg_cache = &wm8753->reg_cache;
+ codec->private_data = wm8753;
+
+ memcpy(codec->reg_cache, wm8753_reg, sizeof(codec->reg_cache));
+ INIT_DELAYED_WORK(&codec->delayed_work, wm8753_work);
+
+ ret = wm8753_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ goto err;
+ }
+
+ /* charge output caps */
+ wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+ schedule_delayed_work(&codec->delayed_work,
+ msecs_to_jiffies(caps_charge));
+
+ /* set the update bits */
+ reg = wm8753_read_reg_cache(codec, WM8753_LDAC);
+ wm8753_write(codec, WM8753_LDAC, reg | 0x0100);
+ reg = wm8753_read_reg_cache(codec, WM8753_RDAC);
+ wm8753_write(codec, WM8753_RDAC, reg | 0x0100);
+ reg = wm8753_read_reg_cache(codec, WM8753_LADC);
+ wm8753_write(codec, WM8753_LADC, reg | 0x0100);
+ reg = wm8753_read_reg_cache(codec, WM8753_RADC);
+ wm8753_write(codec, WM8753_RADC, reg | 0x0100);
+ reg = wm8753_read_reg_cache(codec, WM8753_LOUT1V);
+ wm8753_write(codec, WM8753_LOUT1V, reg | 0x0100);
+ reg = wm8753_read_reg_cache(codec, WM8753_ROUT1V);
+ wm8753_write(codec, WM8753_ROUT1V, reg | 0x0100);
+ reg = wm8753_read_reg_cache(codec, WM8753_LOUT2V);
+ wm8753_write(codec, WM8753_LOUT2V, reg | 0x0100);
+ reg = wm8753_read_reg_cache(codec, WM8753_ROUT2V);
+ wm8753_write(codec, WM8753_ROUT2V, reg | 0x0100);
+ reg = wm8753_read_reg_cache(codec, WM8753_LINVOL);
+ wm8753_write(codec, WM8753_LINVOL, reg | 0x0100);
+ reg = wm8753_read_reg_cache(codec, WM8753_RINVOL);
+ wm8753_write(codec, WM8753_RINVOL, reg | 0x0100);
+
+ wm8753_codec = codec;
+
+ for (i = 0; i < ARRAY_SIZE(wm8753_dai); i++)
+ wm8753_dai[i].dev = codec->dev;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_soc_register_dais(&wm8753_dai[0], ARRAY_SIZE(wm8753_dai));
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+ goto err_codec;
+ }
+
+ return 0;
+
+err_codec:
+ run_delayed_work(&codec->delayed_work);
+ snd_soc_unregister_codec(codec);
+err:
+ kfree(wm8753);
+ return ret;
+}
+
+static void wm8753_unregister(struct wm8753_priv *wm8753)
+{
+ wm8753_set_bias_level(&wm8753->codec, SND_SOC_BIAS_OFF);
+ run_delayed_work(&wm8753->codec.delayed_work);
+ snd_soc_unregister_dais(&wm8753_dai[0], ARRAY_SIZE(wm8753_dai));
+ snd_soc_unregister_codec(&wm8753->codec);
+ kfree(wm8753);
+ wm8753_codec = NULL;
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+static int wm8753_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct snd_soc_codec *codec;
+ struct wm8753_priv *wm8753;
+
+ wm8753 = kzalloc(sizeof(struct wm8753_priv), GFP_KERNEL);
+ if (wm8753 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8753->codec;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ codec->control_data = i2c;
+ i2c_set_clientdata(i2c, wm8753);
+
+ codec->dev = &i2c->dev;
+
+ return wm8753_register(wm8753);
+}
+
+static int wm8753_i2c_remove(struct i2c_client *client)
+{
+ struct wm8753_priv *wm8753 = i2c_get_clientdata(client);
+ wm8753_unregister(wm8753);
+ return 0;
+}
+
+static const struct i2c_device_id wm8753_i2c_id[] = {
+ { "wm8753", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8753_i2c_id);
+
+static struct i2c_driver wm8753_i2c_driver = {
+ .driver = {
+ .name = "wm8753",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8753_i2c_probe,
+ .remove = wm8753_i2c_remove,
+ .id_table = wm8753_i2c_id,
+};
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8753_spi_write(struct spi_device *spi, const char *data, int len)
+{
+ 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;
+}
+
+static int __devinit wm8753_spi_probe(struct spi_device *spi)
+{
+ struct snd_soc_codec *codec;
+ struct wm8753_priv *wm8753;
+
+ wm8753 = kzalloc(sizeof(struct wm8753_priv), GFP_KERNEL);
+ if (wm8753 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8753->codec;
+ codec->control_data = spi;
+ codec->hw_write = (hw_write_t)wm8753_spi_write;
+ codec->dev = &spi->dev;
+
+ spi->dev.driver_data = wm8753;
+
+ return wm8753_register(wm8753);
+}
+
+static int __devexit wm8753_spi_remove(struct spi_device *spi)
+{
+ struct wm8753_priv *wm8753 = spi->dev.driver_data;
+ wm8753_unregister(wm8753);
+ return 0;
+}
+
+static struct spi_driver wm8753_spi_driver = {
+ .driver = {
+ .name = "wm8753",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8753_spi_probe,
+ .remove = __devexit_p(wm8753_spi_remove),
+};
+#endif
+
static int __init wm8753_modinit(void)
{
- return snd_soc_register_dais(wm8753_dai, ARRAY_SIZE(wm8753_dai));
+ int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ ret = i2c_add_driver(&wm8753_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM8753 I2C driver: %d\n", ret);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8753_spi_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM8753 SPI driver: %d\n", ret);
+#endif
+ return 0;
}
module_init(wm8753_modinit);
static void __exit wm8753_exit(void)
{
- snd_soc_unregister_dais(wm8753_dai, ARRAY_SIZE(wm8753_dai));
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&wm8753_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8753_spi_driver);
+#endif
}
module_exit(wm8753_exit);
diff --git a/sound/soc/codecs/wm8753.h b/sound/soc/codecs/wm8753.h
index f55704c..57b2ba2 100644
--- a/sound/soc/codecs/wm8753.h
+++ b/sound/soc/codecs/wm8753.h
@@ -77,12 +77,6 @@
#define WM8753_BIASCTL 0x3d
#define WM8753_ADCTL2 0x3f
-struct wm8753_setup_data {
- int spi;
- int i2c_bus;
- unsigned short i2c_address;
-};
-
#define WM8753_PLL1 0
#define WM8753_PLL2 1
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index 6767de1..46c5ea1 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -517,22 +517,6 @@
};
-/* add non dapm controls */
-static int wm8900_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(wm8900_snd_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8900_snd_controls[i],
- codec, NULL));
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
static const struct snd_kcontrol_new wm8900_dapm_loutput2_control =
SOC_DAPM_SINGLE("LINEOUT2L Switch", WM8900_REG_POWER3, 6, 1, 0);
@@ -736,7 +720,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
u16 reg;
reg = wm8900_read(codec, WM8900_REG_AUDIO1) & ~0x60;
@@ -1104,6 +1088,14 @@
(SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
SNDRV_PCM_FORMAT_S24_LE)
+static struct snd_soc_dai_ops wm8900_dai_ops = {
+ .hw_params = wm8900_hw_params,
+ .set_clkdiv = wm8900_set_dai_clkdiv,
+ .set_pll = wm8900_set_dai_pll,
+ .set_fmt = wm8900_set_dai_fmt,
+ .digital_mute = wm8900_digital_mute,
+};
+
struct snd_soc_dai wm8900_dai = {
.name = "WM8900 HiFi",
.playback = {
@@ -1120,13 +1112,7 @@
.rates = WM8900_RATES,
.formats = WM8900_PCM_FORMATS,
},
- .ops = {
- .hw_params = wm8900_hw_params,
- .set_clkdiv = wm8900_set_dai_clkdiv,
- .set_pll = wm8900_set_dai_pll,
- .set_fmt = wm8900_set_dai_fmt,
- .digital_mute = wm8900_digital_mute,
- },
+ .ops = &wm8900_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8900_dai);
@@ -1226,7 +1212,7 @@
static int wm8900_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct wm8900_priv *wm8900 = codec->private_data;
int fll_out = wm8900->fll_out;
int fll_in = wm8900->fll_in;
@@ -1250,7 +1236,7 @@
static int wm8900_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct wm8900_priv *wm8900 = codec->private_data;
u16 *cache;
int i, ret;
@@ -1288,8 +1274,8 @@
static struct snd_soc_codec *wm8900_codec;
-static int wm8900_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static __devinit int wm8900_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
{
struct wm8900_priv *wm8900;
struct snd_soc_codec *codec;
@@ -1388,7 +1374,7 @@
return ret;
}
-static int wm8900_i2c_remove(struct i2c_client *client)
+static __devexit int wm8900_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_dai(&wm8900_dai);
snd_soc_unregister_codec(wm8900_codec);
@@ -1414,7 +1400,7 @@
.owner = THIS_MODULE,
},
.probe = wm8900_i2c_probe,
- .remove = wm8900_i2c_remove,
+ .remove = __devexit_p(wm8900_i2c_remove),
.id_table = wm8900_i2c_id,
};
@@ -1430,7 +1416,7 @@
}
codec = wm8900_codec;
- socdev->codec = codec;
+ socdev->card->codec = codec;
/* Register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
@@ -1439,7 +1425,8 @@
goto pcm_err;
}
- wm8900_add_controls(codec);
+ snd_soc_add_controls(codec, wm8900_snd_controls,
+ ARRAY_SIZE(wm8900_snd_controls));
wm8900_add_widgets(codec);
ret = snd_soc_init_card(socdev);
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index bde7454..8cf571f 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -744,21 +744,6 @@
0, 63, 0, out_tlv),
};
-static int wm8903_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(wm8903_snd_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8903_snd_controls[i],
- codec, NULL));
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
static const struct snd_kcontrol_new linput_mode_mux =
SOC_DAPM_ENUM("Left Input Mode Mux", linput_mode_enum);
@@ -1276,7 +1261,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct wm8903_priv *wm8903 = codec->private_data;
struct i2c_client *i2c = codec->control_data;
struct snd_pcm_runtime *master_runtime;
@@ -1318,7 +1303,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct wm8903_priv *wm8903 = codec->private_data;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -1338,7 +1323,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct wm8903_priv *wm8903 = codec->private_data;
struct i2c_client *i2c = codec->control_data;
int fs = params_rate(params);
@@ -1512,6 +1497,15 @@
SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static struct snd_soc_dai_ops wm8903_dai_ops = {
+ .startup = wm8903_startup,
+ .shutdown = wm8903_shutdown,
+ .hw_params = wm8903_hw_params,
+ .digital_mute = wm8903_digital_mute,
+ .set_fmt = wm8903_set_dai_fmt,
+ .set_sysclk = wm8903_set_dai_sysclk,
+};
+
struct snd_soc_dai wm8903_dai = {
.name = "WM8903",
.playback = {
@@ -1528,21 +1522,14 @@
.rates = WM8903_CAPTURE_RATES,
.formats = WM8903_FORMATS,
},
- .ops = {
- .startup = wm8903_startup,
- .shutdown = wm8903_shutdown,
- .hw_params = wm8903_hw_params,
- .digital_mute = wm8903_digital_mute,
- .set_fmt = wm8903_set_dai_fmt,
- .set_sysclk = wm8903_set_dai_sysclk
- }
+ .ops = &wm8903_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8903_dai);
static int wm8903_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
@@ -1552,7 +1539,7 @@
static int wm8903_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct i2c_client *i2c = codec->control_data;
int i;
u16 *reg_cache = codec->reg_cache;
@@ -1577,8 +1564,8 @@
static struct snd_soc_codec *wm8903_codec;
-static int wm8903_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static __devinit int wm8903_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
{
struct wm8903_priv *wm8903;
struct snd_soc_codec *codec;
@@ -1684,7 +1671,7 @@
return ret;
}
-static int wm8903_i2c_remove(struct i2c_client *client)
+static __devexit int wm8903_i2c_remove(struct i2c_client *client)
{
struct snd_soc_codec *codec = i2c_get_clientdata(client);
@@ -1714,7 +1701,7 @@
.owner = THIS_MODULE,
},
.probe = wm8903_i2c_probe,
- .remove = wm8903_i2c_remove,
+ .remove = __devexit_p(wm8903_i2c_remove),
.id_table = wm8903_i2c_id,
};
@@ -1728,7 +1715,7 @@
goto err;
}
- socdev->codec = wm8903_codec;
+ socdev->card->codec = wm8903_codec;
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
@@ -1737,8 +1724,9 @@
goto err;
}
- wm8903_add_controls(socdev->codec);
- wm8903_add_widgets(socdev->codec);
+ snd_soc_add_controls(socdev->card->codec, wm8903_snd_controls,
+ ARRAY_SIZE(wm8903_snd_controls));
+ wm8903_add_widgets(socdev->card->codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@@ -1759,7 +1747,7 @@
static int wm8903_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index 88ead7f..032dca2 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -195,21 +195,6 @@
SOC_DOUBLE_R("Mic Boost", WM8971_LADCIN, WM8971_RADCIN, 4, 3, 0),
};
-/* add non-DAPM controls */
-static int wm8971_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(wm8971_snd_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8971_snd_controls[i],
- codec, NULL));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
/*
* DAPM Controls
*/
@@ -546,7 +531,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct wm8971_priv *wm8971 = codec->private_data;
u16 iface = wm8971_read_reg_cache(codec, WM8971_IFACE) & 0x1f3;
u16 srate = wm8971_read_reg_cache(codec, WM8971_SRATE) & 0x1c0;
@@ -619,6 +604,13 @@
#define WM8971_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static struct snd_soc_dai_ops wm8971_dai_ops = {
+ .hw_params = wm8971_pcm_hw_params,
+ .digital_mute = wm8971_mute,
+ .set_fmt = wm8971_set_dai_fmt,
+ .set_sysclk = wm8971_set_dai_sysclk,
+};
+
struct snd_soc_dai wm8971_dai = {
.name = "WM8971",
.playback = {
@@ -633,12 +625,7 @@
.channels_max = 2,
.rates = WM8971_RATES,
.formats = WM8971_FORMATS,},
- .ops = {
- .hw_params = wm8971_pcm_hw_params,
- .digital_mute = wm8971_mute,
- .set_fmt = wm8971_set_dai_fmt,
- .set_sysclk = wm8971_set_dai_sysclk,
- },
+ .ops = &wm8971_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8971_dai);
@@ -652,7 +639,7 @@
static int wm8971_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@@ -661,7 +648,7 @@
static int wm8971_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
@@ -692,7 +679,7 @@
static int wm8971_init(struct snd_soc_device *socdev)
{
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int reg, ret = 0;
codec->name = "WM8971";
@@ -745,7 +732,8 @@
reg = wm8971_read_reg_cache(codec, WM8971_RINVOL);
wm8971_write(codec, WM8971_RINVOL, reg | 0x0100);
- wm8971_add_controls(codec);
+ snd_soc_add_controls(codec, wm8971_snd_controls,
+ ARRAY_SIZE(wm8971_snd_controls));
wm8971_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@@ -772,7 +760,7 @@
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = wm8971_socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@@ -873,7 +861,7 @@
}
codec->private_data = wm8971;
- socdev->codec = codec;
+ socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -908,7 +896,7 @@
static int wm8971_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF);
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index a5731fa..c518c3e 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -115,7 +115,7 @@
unsigned int reg)
{
u16 *cache = codec->reg_cache;
- BUG_ON(reg > (ARRAY_SIZE(wm8990_reg)) - 1);
+ BUG_ON(reg >= ARRAY_SIZE(wm8990_reg));
return cache[reg];
}
@@ -128,7 +128,7 @@
u16 *cache = codec->reg_cache;
/* Reset register and reserved registers are uncached */
- if (reg == 0 || reg > ARRAY_SIZE(wm8990_reg) - 1)
+ if (reg == 0 || reg >= ARRAY_SIZE(wm8990_reg))
return;
cache[reg] = value;
@@ -418,21 +418,6 @@
};
-/* add non dapm controls */
-static int wm8990_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(wm8990_snd_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8990_snd_controls[i], codec,
- NULL));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
/*
* _DAPM_ Controls
*/
@@ -1178,7 +1163,7 @@
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
u16 audio1 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_1);
audio1 &= ~WM8990_AIF_WL_MASK;
@@ -1347,6 +1332,15 @@
* 1. ADC/DAC on Primary Interface
* 2. ADC on Primary Interface/DAC on secondary
*/
+static struct snd_soc_dai_ops wm8990_dai_ops = {
+ .hw_params = wm8990_hw_params,
+ .digital_mute = wm8990_mute,
+ .set_fmt = wm8990_set_dai_fmt,
+ .set_clkdiv = wm8990_set_dai_clkdiv,
+ .set_pll = wm8990_set_dai_pll,
+ .set_sysclk = wm8990_set_dai_sysclk,
+};
+
struct snd_soc_dai wm8990_dai = {
/* ADC/DAC on primary */
.name = "WM8990 ADC/DAC Primary",
@@ -1363,21 +1357,14 @@
.channels_max = 2,
.rates = WM8990_RATES,
.formats = WM8990_FORMATS,},
- .ops = {
- .hw_params = wm8990_hw_params,
- .digital_mute = wm8990_mute,
- .set_fmt = wm8990_set_dai_fmt,
- .set_clkdiv = wm8990_set_dai_clkdiv,
- .set_pll = wm8990_set_dai_pll,
- .set_sysclk = wm8990_set_dai_sysclk,
- },
+ .ops = &wm8990_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8990_dai);
static int wm8990_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
/* we only need to suspend if we are a valid card */
if (!codec->card)
@@ -1390,7 +1377,7 @@
static int wm8990_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
@@ -1418,7 +1405,7 @@
*/
static int wm8990_init(struct snd_soc_device *socdev)
{
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
u16 reg;
int ret = 0;
@@ -1461,7 +1448,8 @@
wm8990_write(codec, WM8990_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
wm8990_write(codec, WM8990_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
- wm8990_add_controls(codec);
+ snd_soc_add_controls(codec, wm8990_snd_controls,
+ ARRAY_SIZE(wm8990_snd_controls));
wm8990_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@@ -1495,7 +1483,7 @@
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = wm8990_socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@@ -1594,7 +1582,7 @@
}
codec->private_data = wm8990;
- socdev->codec = codec;
+ socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@@ -1620,7 +1608,7 @@
static int wm8990_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF);
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
new file mode 100644
index 0000000..3265817
--- /dev/null
+++ b/sound/soc/codecs/wm9705.c
@@ -0,0 +1,415 @@
+/*
+ * wm9705.c -- ALSA Soc WM9705 codec support
+ *
+ * Copyright 2008 Ian Molton <spyro@f2s.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; Version 2 of the License only.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "wm9705.h"
+
+/*
+ * WM9705 register cache
+ */
+static const u16 wm9705_reg[] = {
+ 0x6150, 0x8000, 0x8000, 0x8000, /* 0x0 */
+ 0x0000, 0x8000, 0x8008, 0x8008, /* 0x8 */
+ 0x8808, 0x8808, 0x8808, 0x8808, /* 0x10 */
+ 0x8808, 0x0000, 0x8000, 0x0000, /* 0x18 */
+ 0x0000, 0x0000, 0x0000, 0x000f, /* 0x20 */
+ 0x0605, 0x0000, 0xbb80, 0x0000, /* 0x28 */
+ 0x0000, 0xbb80, 0x0000, 0x0000, /* 0x30 */
+ 0x0000, 0x2000, 0x0000, 0x0000, /* 0x38 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 0x40 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 0x48 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 0x50 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 0x58 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 0x60 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 0x68 */
+ 0x0000, 0x0808, 0x0000, 0x0006, /* 0x70 */
+ 0x0000, 0x0000, 0x574d, 0x4c05, /* 0x78 */
+};
+
+static const struct snd_kcontrol_new wm9705_snd_ac97_controls[] = {
+ SOC_DOUBLE("Master Playback Volume", AC97_MASTER, 8, 0, 31, 1),
+ SOC_SINGLE("Master Playback Switch", AC97_MASTER, 15, 1, 1),
+ SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
+ SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1),
+ SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1),
+ SOC_SINGLE("PCM Playback Switch", AC97_PCM, 15, 1, 1),
+ SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1),
+ SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
+ SOC_SINGLE("PCBeep Playback Volume", AC97_PC_BEEP, 1, 15, 1),
+ SOC_SINGLE("Phone Playback Volume", AC97_PHONE, 0, 31, 1),
+ SOC_DOUBLE("Line Playback Volume", AC97_LINE, 8, 0, 31, 1),
+ SOC_DOUBLE("CD Playback Volume", AC97_CD, 8, 0, 31, 1),
+ SOC_SINGLE("Mic Playback Volume", AC97_MIC, 0, 31, 1),
+ SOC_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 6, 1, 0),
+ SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0),
+ SOC_SINGLE("Capture Switch", AC97_REC_GAIN, 15, 1, 1),
+};
+
+static const char *wm9705_mic[] = {"Mic 1", "Mic 2"};
+static const char *wm9705_rec_sel[] = {"Mic", "CD", "NC", "NC",
+ "Line", "Stereo Mix", "Mono Mix", "Phone"};
+
+static const struct soc_enum wm9705_enum_mic =
+ SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 8, 2, wm9705_mic);
+static const struct soc_enum wm9705_enum_rec_l =
+ SOC_ENUM_SINGLE(AC97_REC_SEL, 8, 8, wm9705_rec_sel);
+static const struct soc_enum wm9705_enum_rec_r =
+ SOC_ENUM_SINGLE(AC97_REC_SEL, 0, 8, wm9705_rec_sel);
+
+/* Headphone Mixer */
+static const struct snd_kcontrol_new wm9705_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PCBeep Playback Switch", AC97_PC_BEEP, 15, 1, 1),
+ SOC_DAPM_SINGLE("CD Playback Switch", AC97_CD, 15, 1, 1),
+ SOC_DAPM_SINGLE("Mic Playback Switch", AC97_MIC, 15, 1, 1),
+ SOC_DAPM_SINGLE("Phone Playback Switch", AC97_PHONE, 15, 1, 1),
+ SOC_DAPM_SINGLE("Line Playback Switch", AC97_LINE, 15, 1, 1),
+};
+
+/* Mic source */
+static const struct snd_kcontrol_new wm9705_mic_src_controls =
+ SOC_DAPM_ENUM("Route", wm9705_enum_mic);
+
+/* Capture source */
+static const struct snd_kcontrol_new wm9705_capture_selectl_controls =
+ SOC_DAPM_ENUM("Route", wm9705_enum_rec_l);
+static const struct snd_kcontrol_new wm9705_capture_selectr_controls =
+ SOC_DAPM_ENUM("Route", wm9705_enum_rec_r);
+
+/* DAPM widgets */
+static const struct snd_soc_dapm_widget wm9705_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("Mic Source", SND_SOC_NOPM, 0, 0,
+ &wm9705_mic_src_controls),
+ SND_SOC_DAPM_MUX("Left Capture Source", SND_SOC_NOPM, 0, 0,
+ &wm9705_capture_selectl_controls),
+ SND_SOC_DAPM_MUX("Right Capture Source", SND_SOC_NOPM, 0, 0,
+ &wm9705_capture_selectr_controls),
+ SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback",
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback",
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MIXER_NAMED_CTL("HP Mixer", SND_SOC_NOPM, 0, 0,
+ &wm9705_hp_mixer_controls[0],
+ ARRAY_SIZE(wm9705_hp_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA("Headphone PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Speaker PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Line PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Line out PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mono PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Phone PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PCBEEP PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("CD PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("HPOUTL"),
+ SND_SOC_DAPM_OUTPUT("HPOUTR"),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+ SND_SOC_DAPM_OUTPUT("MONOOUT"),
+ SND_SOC_DAPM_INPUT("PHONE"),
+ SND_SOC_DAPM_INPUT("LINEINL"),
+ SND_SOC_DAPM_INPUT("LINEINR"),
+ SND_SOC_DAPM_INPUT("CDINL"),
+ SND_SOC_DAPM_INPUT("CDINR"),
+ SND_SOC_DAPM_INPUT("PCBEEP"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+};
+
+/* Audio map
+ * WM9705 has no switches to disable the route from the inputs to the HP mixer
+ * so in order to prevent active inputs from forcing the audio outputs to be
+ * constantly enabled, we use the mutes on those inputs to simulate such
+ * controls.
+ */
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* HP mixer */
+ {"HP Mixer", "PCBeep Playback Switch", "PCBEEP PGA"},
+ {"HP Mixer", "CD Playback Switch", "CD PGA"},
+ {"HP Mixer", "Mic Playback Switch", "Mic PGA"},
+ {"HP Mixer", "Phone Playback Switch", "Phone PGA"},
+ {"HP Mixer", "Line Playback Switch", "Line PGA"},
+ {"HP Mixer", NULL, "Left DAC"},
+ {"HP Mixer", NULL, "Right DAC"},
+
+ /* mono mixer */
+ {"Mono Mixer", NULL, "HP Mixer"},
+
+ /* outputs */
+ {"Headphone PGA", NULL, "HP Mixer"},
+ {"HPOUTL", NULL, "Headphone PGA"},
+ {"HPOUTR", NULL, "Headphone PGA"},
+ {"Line out PGA", NULL, "HP Mixer"},
+ {"LOUT", NULL, "Line out PGA"},
+ {"ROUT", NULL, "Line out PGA"},
+ {"Mono PGA", NULL, "Mono Mixer"},
+ {"MONOOUT", NULL, "Mono PGA"},
+
+ /* inputs */
+ {"CD PGA", NULL, "CDINL"},
+ {"CD PGA", NULL, "CDINR"},
+ {"Line PGA", NULL, "LINEINL"},
+ {"Line PGA", NULL, "LINEINR"},
+ {"Phone PGA", NULL, "PHONE"},
+ {"Mic Source", "Mic 1", "MIC1"},
+ {"Mic Source", "Mic 2", "MIC2"},
+ {"Mic PGA", NULL, "Mic Source"},
+ {"PCBEEP PGA", NULL, "PCBEEP"},
+
+ /* Left capture selector */
+ {"Left Capture Source", "Mic", "Mic Source"},
+ {"Left Capture Source", "CD", "CDINL"},
+ {"Left Capture Source", "Line", "LINEINL"},
+ {"Left Capture Source", "Stereo Mix", "HP Mixer"},
+ {"Left Capture Source", "Mono Mix", "HP Mixer"},
+ {"Left Capture Source", "Phone", "PHONE"},
+
+ /* Right capture source */
+ {"Right Capture Source", "Mic", "Mic Source"},
+ {"Right Capture Source", "CD", "CDINR"},
+ {"Right Capture Source", "Line", "LINEINR"},
+ {"Right Capture Source", "Stereo Mix", "HP Mixer"},
+ {"Right Capture Source", "Mono Mix", "HP Mixer"},
+ {"Right Capture Source", "Phone", "PHONE"},
+
+ {"ADC PGA", NULL, "Left Capture Source"},
+ {"ADC PGA", NULL, "Right Capture Source"},
+
+ /* ADC's */
+ {"Left ADC", NULL, "ADC PGA"},
+ {"Right ADC", NULL, "ADC PGA"},
+};
+
+static int wm9705_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, wm9705_dapm_widgets,
+ ARRAY_SIZE(wm9705_dapm_widgets));
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+ snd_soc_dapm_new_widgets(codec);
+
+ return 0;
+}
+
+/* We use a register cache to enhance read performance. */
+static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+
+ switch (reg) {
+ case AC97_RESET:
+ case AC97_VENDOR_ID1:
+ case AC97_VENDOR_ID2:
+ return soc_ac97_ops.read(codec->ac97, reg);
+ default:
+ reg = reg >> 1;
+
+ if (reg >= (ARRAY_SIZE(wm9705_reg)))
+ return -EIO;
+
+ return cache[reg];
+ }
+}
+
+static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int val)
+{
+ u16 *cache = codec->reg_cache;
+
+ soc_ac97_ops.write(codec->ac97, reg, val);
+ reg = reg >> 1;
+ if (reg < (ARRAY_SIZE(wm9705_reg)))
+ cache[reg] = val;
+
+ return 0;
+}
+
+static int ac97_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ int reg;
+ u16 vra;
+
+ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
+ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ reg = AC97_PCM_FRONT_DAC_RATE;
+ else
+ reg = AC97_PCM_LR_ADC_RATE;
+
+ return ac97_write(codec, reg, runtime->rate);
+}
+
+#define WM9705_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+static struct snd_soc_dai_ops wm9705_dai_ops = {
+ .prepare = ac97_prepare,
+};
+
+struct snd_soc_dai wm9705_dai[] = {
+ {
+ .name = "AC97 HiFi",
+ .ac97_control = 1,
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM9705_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM9705_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &wm9705_dai_ops,
+ },
+ {
+ .name = "AC97 Aux",
+ .playback = {
+ .stream_name = "Aux Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = WM9705_AC97_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ }
+};
+EXPORT_SYMBOL_GPL(wm9705_dai);
+
+static int wm9705_reset(struct snd_soc_codec *codec)
+{
+ if (soc_ac97_ops.reset) {
+ soc_ac97_ops.reset(codec->ac97);
+ if (ac97_read(codec, 0) == wm9705_reg[0])
+ return 0; /* Success */
+ }
+
+ return -EIO;
+}
+
+static int wm9705_soc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ printk(KERN_INFO "WM9705 SoC Audio Codec\n");
+
+ socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec),
+ GFP_KERNEL);
+ if (socdev->card->codec == NULL)
+ return -ENOMEM;
+ codec = socdev->card->codec;
+ mutex_init(&codec->mutex);
+
+ codec->reg_cache = kmemdup(wm9705_reg, sizeof(wm9705_reg), GFP_KERNEL);
+ if (codec->reg_cache == NULL) {
+ ret = -ENOMEM;
+ goto cache_err;
+ }
+ codec->reg_cache_size = sizeof(wm9705_reg);
+ codec->reg_cache_step = 2;
+
+ codec->name = "WM9705";
+ codec->owner = THIS_MODULE;
+ codec->dai = wm9705_dai;
+ codec->num_dai = ARRAY_SIZE(wm9705_dai);
+ codec->write = ac97_write;
+ codec->read = ac97_read;
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ if (ret < 0) {
+ printk(KERN_ERR "wm9705: failed to register AC97 codec\n");
+ goto codec_err;
+ }
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0)
+ goto pcm_err;
+
+ ret = wm9705_reset(codec);
+ if (ret)
+ goto reset_err;
+
+ snd_soc_add_controls(codec, wm9705_snd_ac97_controls,
+ ARRAY_SIZE(wm9705_snd_ac97_controls));
+ wm9705_add_widgets(codec);
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "wm9705: failed to register card\n");
+ goto pcm_err;
+ }
+
+ return 0;
+
+reset_err:
+ snd_soc_free_pcms(socdev);
+pcm_err:
+ snd_soc_free_ac97_codec(codec);
+codec_err:
+ kfree(codec->reg_cache);
+cache_err:
+ kfree(socdev->card->codec);
+ socdev->card->codec = NULL;
+ return ret;
+}
+
+static int wm9705_soc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ if (codec == NULL)
+ return 0;
+
+ snd_soc_dapm_free(socdev);
+ snd_soc_free_pcms(socdev);
+ snd_soc_free_ac97_codec(codec);
+ kfree(codec->reg_cache);
+ kfree(codec);
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm9705 = {
+ .probe = wm9705_soc_probe,
+ .remove = wm9705_soc_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm9705);
+
+MODULE_DESCRIPTION("ASoC WM9705 driver");
+MODULE_AUTHOR("Ian Molton");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wm9705.h b/sound/soc/codecs/wm9705.h
new file mode 100644
index 0000000..d380f11
--- /dev/null
+++ b/sound/soc/codecs/wm9705.h
@@ -0,0 +1,14 @@
+/*
+ * wm9705.h -- WM9705 Soc Audio driver
+ */
+
+#ifndef _WM9705_H
+#define _WM9705_H
+
+#define WM9705_DAI_AC97_HIFI 0
+#define WM9705_DAI_AC97_AUX 1
+
+extern struct snd_soc_dai wm9705_dai[2];
+extern struct snd_soc_codec_device soc_codec_dev_wm9705;
+
+#endif
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index af83d62..765cf1e 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -154,21 +154,6 @@
SOC_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 7, 1, 0),
};
-/* add non dapm controls */
-static int wm9712_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(wm9712_snd_ac97_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm9712_snd_ac97_controls[i],
- codec, NULL));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
/* We have to create a fake left and right HP mixers because
* the codec only has a single control that is shared by both channels.
* This makes it impossible to determine the audio path.
@@ -467,7 +452,7 @@
else {
reg = reg >> 1;
- if (reg > (ARRAY_SIZE(wm9712_reg)))
+ if (reg >= (ARRAY_SIZE(wm9712_reg)))
return -EIO;
return cache[reg];
@@ -481,7 +466,7 @@
soc_ac97_ops.write(codec->ac97, reg, val);
reg = reg >> 1;
- if (reg <= (ARRAY_SIZE(wm9712_reg)))
+ if (reg < (ARRAY_SIZE(wm9712_reg)))
cache[reg] = val;
return 0;
@@ -493,7 +478,7 @@
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int reg;
u16 vra;
@@ -514,7 +499,7 @@
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
u16 vra, xsle;
vra = ac97_read(codec, AC97_EXTENDED_STATUS);
@@ -532,6 +517,14 @@
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000)
+static struct snd_soc_dai_ops wm9712_dai_ops_hifi = {
+ .prepare = ac97_prepare,
+};
+
+static struct snd_soc_dai_ops wm9712_dai_ops_aux = {
+ .prepare = ac97_aux_prepare,
+};
+
struct snd_soc_dai wm9712_dai[] = {
{
.name = "AC97 HiFi",
@@ -548,8 +541,7 @@
.channels_max = 2,
.rates = WM9712_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = {
- .prepare = ac97_prepare,},
+ .ops = &wm9712_dai_ops_hifi,
},
{
.name = "AC97 Aux",
@@ -559,8 +551,7 @@
.channels_max = 1,
.rates = WM9712_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = {
- .prepare = ac97_aux_prepare,},
+ .ops = &wm9712_dai_ops_aux,
}
};
EXPORT_SYMBOL_GPL(wm9712_dai);
@@ -607,7 +598,7 @@
pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
wm9712_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@@ -616,7 +607,7 @@
static int wm9712_soc_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
int i, ret;
u16 *cache = codec->reg_cache;
@@ -652,10 +643,11 @@
printk(KERN_INFO "WM9711/WM9712 SoC Audio Codec %s\n", WM9712_VERSION);
- socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (socdev->codec == NULL)
+ socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec),
+ GFP_KERNEL);
+ if (socdev->card->codec == NULL)
return -ENOMEM;
- codec = socdev->codec;
+ codec = socdev->card->codec;
mutex_init(&codec->mutex);
codec->reg_cache = kmemdup(wm9712_reg, sizeof(wm9712_reg), GFP_KERNEL);
@@ -698,7 +690,8 @@
ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- wm9712_add_controls(codec);
+ snd_soc_add_controls(codec, wm9712_snd_ac97_controls,
+ ARRAY_SIZE(wm9712_snd_ac97_controls));
wm9712_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@@ -718,15 +711,15 @@
kfree(codec->reg_cache);
cache_err:
- kfree(socdev->codec);
- socdev->codec = NULL;
+ kfree(socdev->card->codec);
+ socdev->card->codec = NULL;
return ret;
}
static int wm9712_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
if (codec == NULL)
return 0;
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index f3ca8aa..523bad0 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -32,7 +32,6 @@
struct wm9713_priv {
u32 pll_in; /* PLL input frequency */
- u32 pll_out; /* PLL output frequency */
};
static unsigned int ac97_read(struct snd_soc_codec *codec,
@@ -190,21 +189,6 @@
SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1),
};
-/* add non dapm controls */
-static int wm9713_add_controls(struct snd_soc_codec *codec)
-{
- int err, i;
-
- for (i = 0; i < ARRAY_SIZE(wm9713_snd_ac97_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm9713_snd_ac97_controls[i],
- codec, NULL));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
/* We have to create a fake left and right HP mixers because
* the codec only has a single control that is shared by both channels.
* This makes it impossible to determine the audio path using the current
@@ -636,7 +620,7 @@
else {
reg = reg >> 1;
- if (reg > (ARRAY_SIZE(wm9713_reg)))
+ if (reg >= (ARRAY_SIZE(wm9713_reg)))
return -EIO;
return cache[reg];
@@ -650,7 +634,7 @@
if (reg < 0x7c)
soc_ac97_ops.write(codec->ac97, reg, val);
reg = reg >> 1;
- if (reg <= (ARRAY_SIZE(wm9713_reg)))
+ if (reg < (ARRAY_SIZE(wm9713_reg)))
cache[reg] = val;
return 0;
@@ -738,13 +722,13 @@
struct _pll_div pll_div;
/* turn PLL off ? */
- if (freq_in == 0 || freq_out == 0) {
+ if (freq_in == 0) {
/* disable PLL power and select ext source */
reg = ac97_read(codec, AC97_HANDSET_RATE);
ac97_write(codec, AC97_HANDSET_RATE, reg | 0x0080);
reg = ac97_read(codec, AC97_EXTENDED_MID);
ac97_write(codec, AC97_EXTENDED_MID, reg | 0x0200);
- wm9713->pll_out = 0;
+ wm9713->pll_in = 0;
return 0;
}
@@ -788,7 +772,6 @@
ac97_write(codec, AC97_EXTENDED_MID, reg & 0xfdff);
reg = ac97_read(codec, AC97_HANDSET_RATE);
ac97_write(codec, AC97_HANDSET_RATE, reg & 0xff7f);
- wm9713->pll_out = freq_out;
wm9713->pll_in = freq_in;
/* wait 10ms AC97 link frames for the link to stabilise */
@@ -957,13 +940,14 @@
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
- u16 status;
+ u16 status, rate;
/* Gracefully shut down the voice interface. */
status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000;
- ac97_write(codec, AC97_HANDSET_RATE, 0x0280);
+ rate = ac97_read(codec, AC97_HANDSET_RATE) & 0xF0FF;
+ ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0200);
schedule_timeout_interruptible(msecs_to_jiffies(1));
- ac97_write(codec, AC97_HANDSET_RATE, 0x0F80);
+ ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0F00);
ac97_write(codec, AC97_EXTENDED_MID, status);
}
@@ -1021,6 +1005,27 @@
(SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
SNDRV_PCM_FORMAT_S24_LE)
+static struct snd_soc_dai_ops wm9713_dai_ops_hifi = {
+ .prepare = ac97_hifi_prepare,
+ .set_clkdiv = wm9713_set_dai_clkdiv,
+ .set_pll = wm9713_set_dai_pll,
+};
+
+static struct snd_soc_dai_ops wm9713_dai_ops_aux = {
+ .prepare = ac97_aux_prepare,
+ .set_clkdiv = wm9713_set_dai_clkdiv,
+ .set_pll = wm9713_set_dai_pll,
+};
+
+static struct snd_soc_dai_ops wm9713_dai_ops_voice = {
+ .hw_params = wm9713_pcm_hw_params,
+ .shutdown = wm9713_voiceshutdown,
+ .set_clkdiv = wm9713_set_dai_clkdiv,
+ .set_pll = wm9713_set_dai_pll,
+ .set_fmt = wm9713_set_dai_fmt,
+ .set_tristate = wm9713_set_dai_tristate,
+};
+
struct snd_soc_dai wm9713_dai[] = {
{
.name = "AC97 HiFi",
@@ -1037,10 +1042,7 @@
.channels_max = 2,
.rates = WM9713_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = {
- .prepare = ac97_hifi_prepare,
- .set_clkdiv = wm9713_set_dai_clkdiv,
- .set_pll = wm9713_set_dai_pll,},
+ .ops = &wm9713_dai_ops_hifi,
},
{
.name = "AC97 Aux",
@@ -1050,10 +1052,7 @@
.channels_max = 1,
.rates = WM9713_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = {
- .prepare = ac97_aux_prepare,
- .set_clkdiv = wm9713_set_dai_clkdiv,
- .set_pll = wm9713_set_dai_pll,},
+ .ops = &wm9713_dai_ops_aux,
},
{
.name = "WM9713 Voice",
@@ -1069,14 +1068,7 @@
.channels_max = 2,
.rates = WM9713_PCM_RATES,
.formats = WM9713_PCM_FORMATS,},
- .ops = {
- .hw_params = wm9713_pcm_hw_params,
- .shutdown = wm9713_voiceshutdown,
- .set_clkdiv = wm9713_set_dai_clkdiv,
- .set_pll = wm9713_set_dai_pll,
- .set_fmt = wm9713_set_dai_fmt,
- .set_tristate = wm9713_set_dai_tristate,
- },
+ .ops = &wm9713_dai_ops_voice,
},
};
EXPORT_SYMBOL_GPL(wm9713_dai);
@@ -1132,7 +1124,7 @@
pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
u16 reg;
/* Disable everything except touchpanel - that will be handled
@@ -1150,7 +1142,7 @@
static int wm9713_soc_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
struct wm9713_priv *wm9713 = codec->private_data;
int i, ret;
u16 *cache = codec->reg_cache;
@@ -1164,8 +1156,8 @@
wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* do we need to re-start the PLL ? */
- if (wm9713->pll_out)
- wm9713_set_pll(codec, 0, wm9713->pll_in, wm9713->pll_out);
+ if (wm9713->pll_in)
+ wm9713_set_pll(codec, 0, wm9713->pll_in, 0);
/* only synchronise the codec if warm reset failed */
if (ret == 0) {
@@ -1191,10 +1183,11 @@
printk(KERN_INFO "WM9713/WM9714 SoC Audio Codec %s\n", WM9713_VERSION);
- socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (socdev->codec == NULL)
+ socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec),
+ GFP_KERNEL);
+ if (socdev->card->codec == NULL)
return -ENOMEM;
- codec = socdev->codec;
+ codec = socdev->card->codec;
mutex_init(&codec->mutex);
codec->reg_cache = kmemdup(wm9713_reg, sizeof(wm9713_reg), GFP_KERNEL);
@@ -1245,7 +1238,8 @@
reg = ac97_read(codec, AC97_CD) & 0x7fff;
ac97_write(codec, AC97_CD, reg);
- wm9713_add_controls(codec);
+ snd_soc_add_controls(codec, wm9713_snd_ac97_controls,
+ ARRAY_SIZE(wm9713_snd_ac97_controls));
wm9713_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0)
@@ -1265,15 +1259,15 @@
kfree(codec->reg_cache);
cache_err:
- kfree(socdev->codec);
- socdev->codec = NULL;
+ kfree(socdev->card->codec);
+ socdev->card->codec = NULL;
return ret;
}
static int wm9713_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
if (codec == NULL)
return 0;
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index b502741..bd7392c 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -20,7 +20,7 @@
config SND_DAVINCI_SOC_SFFSDR
tristate "SoC Audio support for SFFSDR"
- depends on SND_DAVINCI_SOC && MACH_DAVINCI_SFFSDR
+ depends on SND_DAVINCI_SOC && MACH_SFFSDR
select SND_DAVINCI_SOC_I2S
select SND_SOC_PCM3008
select SFFSDR_FPGA
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index 54851f3..9b90b34 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -186,7 +186,8 @@
platform_set_drvdata(evm_snd_device, &evm_snd_devdata);
evm_snd_devdata.dev = &evm_snd_device->dev;
- evm_snd_device->dev.platform_data = &evm_snd_data;
+ platform_device_add_data(evm_snd_device, &evm_snd_data,
+ sizeof(evm_snd_data));
ret = platform_device_add_resources(evm_snd_device, evm_snd_resources,
ARRAY_SIZE(evm_snd_resources));
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c
index 0fee779..ffdb943 100644
--- a/sound/soc/davinci/davinci-i2s.c
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -499,6 +499,13 @@
#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000
+static struct snd_soc_dai_ops davinci_i2s_dai_ops = {
+ .startup = davinci_i2s_startup,
+ .trigger = davinci_i2s_trigger,
+ .hw_params = davinci_i2s_hw_params,
+ .set_fmt = davinci_i2s_set_dai_fmt,
+};
+
struct snd_soc_dai davinci_i2s_dai = {
.name = "davinci-i2s",
.id = 0,
@@ -514,12 +521,7 @@
.channels_max = 2,
.rates = DAVINCI_I2S_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = {
- .startup = davinci_i2s_startup,
- .trigger = davinci_i2s_trigger,
- .hw_params = davinci_i2s_hw_params,
- .set_fmt = davinci_i2s_set_dai_fmt,
- },
+ .ops = &davinci_i2s_dai_ops,
};
EXPORT_SYMBOL_GPL(davinci_i2s_dai);
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index 366049d..7af3b5b 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -286,7 +286,7 @@
runtime->dma_bytes);
}
-struct snd_pcm_ops davinci_pcm_ops = {
+static struct snd_pcm_ops davinci_pcm_ops = {
.open = davinci_pcm_open,
.close = davinci_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/soc/davinci/davinci-sffsdr.c b/sound/soc/davinci/davinci-sffsdr.c
index 4935d1b..40eccfe 100644
--- a/sound/soc/davinci/davinci-sffsdr.c
+++ b/sound/soc/davinci/davinci-sffsdr.c
@@ -25,7 +25,9 @@
#include <asm/dma.h>
#include <asm/mach-types.h>
+#ifdef CONFIG_SFFSDR_FPGA
#include <asm/plat-sffsdr/sffsdr-fpga.h>
+#endif
#include <mach/mcbsp.h>
#include <mach/edma.h>
@@ -34,31 +36,45 @@
#include "davinci-pcm.h"
#include "davinci-i2s.h"
+/*
+ * CLKX and CLKR are the inputs for the Sample Rate Generator.
+ * FSX and FSR are outputs, driven by the sample Rate Generator.
+ */
+#define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \
+ SND_SOC_DAIFMT_CBM_CFS | \
+ SND_SOC_DAIFMT_IB_NF)
+
static int sffsdr_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+ struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
int fs;
int ret = 0;
- /* Set cpu DAI configuration:
- * CLKX and CLKR are the inputs for the Sample Rate Generator.
- * FSX and FSR are outputs, driven by the sample Rate Generator. */
- ret = snd_soc_dai_set_fmt(cpu_dai,
- SND_SOC_DAIFMT_RIGHT_J |
- SND_SOC_DAIFMT_CBM_CFS |
- SND_SOC_DAIFMT_IB_NF);
- if (ret < 0)
- return ret;
-
/* Fsref can be 32000, 44100 or 48000. */
fs = params_rate(params);
+#ifndef CONFIG_SFFSDR_FPGA
+ /* Without the FPGA module, the Fs is fixed at 44100 Hz */
+ if (fs != 44100) {
+ pr_debug("warning: only 44.1 kHz is supported without SFFSDR FPGA module\n");
+ return -EINVAL;
+ }
+#endif
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT);
+ if (ret < 0)
+ return ret;
+
pr_debug("sffsdr_hw_params: rate = %d Hz\n", fs);
+#ifndef CONFIG_SFFSDR_FPGA
+ return 0;
+#else
return sffsdr_fpga_set_codec_fs(fs);
+#endif
}
static struct snd_soc_ops sffsdr_ops = {
@@ -127,7 +143,8 @@
platform_set_drvdata(sffsdr_snd_device, &sffsdr_snd_devdata);
sffsdr_snd_devdata.dev = &sffsdr_snd_device->dev;
- sffsdr_snd_device->dev.platform_data = &sffsdr_snd_data;
+ platform_device_add_data(sffsdr_snd_device, &sffsdr_snd_data,
+ sizeof(sffsdr_snd_data));
ret = platform_device_add_resources(sffsdr_snd_device,
sffsdr_snd_resources,
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 95c12b2..9fc9082 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,17 +1,18 @@
config SND_SOC_OF_SIMPLE
tristate
+# ASoC platform support for the Freescale MPC8610 SOC. This compiles drivers
+# for the SSI and the Elo DMA controller. You will still need to select
+# a platform driver and a codec driver.
config SND_SOC_MPC8610
- bool "ALSA SoC support for the MPC8610 SOC"
- depends on MPC8610_HPCD
- default y if MPC8610
- help
- Say Y if you want to add support for codecs attached to the SSI
- device on an MPC8610.
+ tristate
+ depends on MPC8610
config SND_SOC_MPC8610_HPCD
- bool "ALSA SoC support for the Freescale MPC8610 HPCD board"
- depends on SND_SOC_MPC8610
+ tristate "ALSA SoC support for the Freescale MPC8610 HPCD board"
+ # I2C is necessary for the CS4270 driver
+ depends on MPC8610_HPCD && I2C
+ select SND_SOC_MPC8610
select SND_SOC_CS4270
select SND_SOC_CS4270_VD33_ERRATA
default y if MPC8610_HPCD
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 035da4a..f85134c 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -2,10 +2,13 @@
obj-$(CONFIG_SND_SOC_OF_SIMPLE) += soc-of-simple.o
# MPC8610 HPCD Machine Support
-obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += mpc8610_hpcd.o
+snd-soc-mpc8610-hpcd-objs := mpc8610_hpcd.o
+obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += snd-soc-mpc8610-hpcd.o
# MPC8610 Platform Support
-obj-$(CONFIG_SND_SOC_MPC8610) += fsl_ssi.o fsl_dma.o
+snd-soc-fsl-ssi-objs := fsl_ssi.o
+snd-soc-fsl-dma-objs := fsl_dma.o
+obj-$(CONFIG_SND_SOC_MPC8610) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o
obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index 64993ed..b3eb857 100644
--- a/sound/soc/fsl/fsl_dma.c
+++ b/sound/soc/fsl/fsl_dma.c
@@ -142,7 +142,8 @@
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_JOINT_DUPLEX,
+ SNDRV_PCM_INFO_JOINT_DUPLEX |
+ SNDRV_PCM_INFO_PAUSE,
.formats = FSLDMA_PCM_FORMATS,
.rates = FSLDMA_PCM_RATES,
.rate_min = 5512,
@@ -464,11 +465,7 @@
sizeof(struct fsl_dma_link_descriptor);
for (i = 0; i < NUM_DMA_LINKS; i++) {
- struct fsl_dma_link_descriptor *link = &dma_private->link[i];
-
- link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP);
- link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP);
- link->next = cpu_to_be64(temp_link);
+ dma_private->link[i].next = cpu_to_be64(temp_link);
temp_link += sizeof(struct fsl_dma_link_descriptor);
}
@@ -525,79 +522,9 @@
* This function obtains hardware parameters about the opened stream and
* programs the DMA controller accordingly.
*
- * Note that due to a quirk of the SSI's STX register, the target address
- * for the DMA operations depends on the sample size. So we don't program
- * the dest_addr (for playback -- source_addr for capture) fields in the
- * link descriptors here. We do that in fsl_dma_prepare()
- */
-static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct fsl_dma_private *dma_private = runtime->private_data;
-
- dma_addr_t temp_addr; /* Pointer to next period */
-
- unsigned int i;
-
- /* Get all the parameters we need */
- size_t buffer_size = params_buffer_bytes(hw_params);
- size_t period_size = params_period_bytes(hw_params);
-
- /* Initialize our DMA tracking variables */
- dma_private->period_size = period_size;
- dma_private->num_periods = params_periods(hw_params);
- dma_private->dma_buf_end = dma_private->dma_buf_phys + buffer_size;
- dma_private->dma_buf_next = dma_private->dma_buf_phys +
- (NUM_DMA_LINKS * period_size);
- if (dma_private->dma_buf_next >= dma_private->dma_buf_end)
- dma_private->dma_buf_next = dma_private->dma_buf_phys;
-
- /*
- * The actual address in STX0 (destination for playback, source for
- * capture) is based on the sample size, but we don't know the sample
- * size in this function, so we'll have to adjust that later. See
- * comments in fsl_dma_prepare().
- *
- * The DMA controller does not have a cache, so the CPU does not
- * need to tell it to flush its cache. However, the DMA
- * controller does need to tell the CPU to flush its cache.
- * That's what the SNOOP bit does.
- *
- * Also, even though the DMA controller supports 36-bit addressing, for
- * simplicity we currently support only 32-bit addresses for the audio
- * buffer itself.
- */
- temp_addr = substream->dma_buffer.addr;
-
- for (i = 0; i < NUM_DMA_LINKS; i++) {
- struct fsl_dma_link_descriptor *link = &dma_private->link[i];
-
- link->count = cpu_to_be32(period_size);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- link->source_addr = cpu_to_be32(temp_addr);
- else
- link->dest_addr = cpu_to_be32(temp_addr);
-
- temp_addr += period_size;
- }
-
- return 0;
-}
-
-/**
- * fsl_dma_prepare - prepare the DMA registers for playback.
- *
- * This function is called after the specifics of the audio data are known,
- * i.e. snd_pcm_runtime is initialized.
- *
- * In this function, we finish programming the registers of the DMA
- * controller that are dependent on the sample size.
- *
- * One of the drawbacks with big-endian is that when copying integers of
- * different sizes to a fixed-sized register, the address to which the
- * integer must be copied is dependent on the size of the integer.
+ * One drawback of big-endian is that when copying integers of different
+ * sizes to a fixed-sized register, the address to which the integer must be
+ * copied is dependent on the size of the integer.
*
* For example, if P is the address of a 32-bit register, and X is a 32-bit
* integer, then X should be copied to address P. However, if X is a 16-bit
@@ -613,22 +540,58 @@
* and 8 bytes at a time). So we do not support packed 24-bit samples.
* 24-bit data must be padded to 32 bits.
*/
-static int fsl_dma_prepare(struct snd_pcm_substream *substream)
+static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_dma_private *dma_private = runtime->private_data;
- struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
- u32 mr;
- unsigned int i;
- dma_addr_t ssi_sxx_phys; /* Bus address of SSI STX register */
- unsigned int frame_size; /* Number of bytes per frame */
- ssi_sxx_phys = dma_private->ssi_sxx_phys;
+ /* Number of bits per sample */
+ unsigned int sample_size =
+ snd_pcm_format_physical_width(params_format(hw_params));
+
+ /* Number of bytes per frame */
+ unsigned int frame_size = 2 * (sample_size / 8);
+
+ /* Bus address of SSI STX register */
+ dma_addr_t ssi_sxx_phys = dma_private->ssi_sxx_phys;
+
+ /* Size of the DMA buffer, in bytes */
+ size_t buffer_size = params_buffer_bytes(hw_params);
+
+ /* Number of bytes per period */
+ size_t period_size = params_period_bytes(hw_params);
+
+ /* Pointer to next period */
+ dma_addr_t temp_addr = substream->dma_buffer.addr;
+
+ /* Pointer to DMA controller */
+ struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
+
+ u32 mr; /* DMA Mode Register */
+
+ unsigned int i;
+
+ /* Initialize our DMA tracking variables */
+ dma_private->period_size = period_size;
+ dma_private->num_periods = params_periods(hw_params);
+ dma_private->dma_buf_end = dma_private->dma_buf_phys + buffer_size;
+ dma_private->dma_buf_next = dma_private->dma_buf_phys +
+ (NUM_DMA_LINKS * period_size);
+
+ if (dma_private->dma_buf_next >= dma_private->dma_buf_end)
+ /* This happens if the number of periods == NUM_DMA_LINKS */
+ dma_private->dma_buf_next = dma_private->dma_buf_phys;
mr = in_be32(&dma_channel->mr) & ~(CCSR_DMA_MR_BWC_MASK |
CCSR_DMA_MR_SAHTS_MASK | CCSR_DMA_MR_DAHTS_MASK);
- switch (runtime->sample_bits) {
+ /* Due to a quirk of the SSI's STX register, the target address
+ * for the DMA operations depends on the sample size. So we calculate
+ * that offset here. While we're at it, also tell the DMA controller
+ * how much data to transfer per sample.
+ */
+ switch (sample_size) {
case 8:
mr |= CCSR_DMA_MR_DAHTS_1 | CCSR_DMA_MR_SAHTS_1;
ssi_sxx_phys += 3;
@@ -641,12 +604,12 @@
mr |= CCSR_DMA_MR_DAHTS_4 | CCSR_DMA_MR_SAHTS_4;
break;
default:
+ /* We should never get here */
dev_err(substream->pcm->card->dev,
- "unsupported sample size %u\n", runtime->sample_bits);
+ "unsupported sample size %u\n", sample_size);
return -EINVAL;
}
- frame_size = runtime->frame_bits / 8;
/*
* BWC should always be a multiple of the frame size. BWC determines
* how many bytes are sent/received before the DMA controller checks the
@@ -655,7 +618,6 @@
* capture, the receive FIFO is triggered when it contains one frame, so
* we want to receive one frame at a time.
*/
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
mr |= CCSR_DMA_MR_BWC(2 * frame_size);
else
@@ -663,16 +625,48 @@
out_be32(&dma_channel->mr, mr);
- /*
- * Program the address of the DMA transfer to/from the SSI.
- */
for (i = 0; i < NUM_DMA_LINKS; i++) {
struct fsl_dma_link_descriptor *link = &dma_private->link[i];
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ link->count = cpu_to_be32(period_size);
+
+ /* Even though the DMA controller supports 36-bit addressing,
+ * for simplicity we allow only 32-bit addresses for the audio
+ * buffer itself. This was enforced in fsl_dma_new() with the
+ * DMA mask.
+ *
+ * The snoop bit tells the DMA controller whether it should tell
+ * the ECM to snoop during a read or write to an address. For
+ * audio, we use DMA to transfer data between memory and an I/O
+ * device (the SSI's STX0 or SRX0 register). Snooping is only
+ * needed if there is a cache, so we need to snoop memory
+ * addresses only. For playback, that means we snoop the source
+ * but not the destination. For capture, we snoop the
+ * destination but not the source.
+ *
+ * Note that failing to snoop properly is unlikely to cause
+ * cache incoherency if the period size is larger than the
+ * size of L1 cache. This is because filling in one period will
+ * flush out the data for the previous period. So if you
+ * increased period_bytes_min to a large enough size, you might
+ * get more performance by not snooping, and you'll still be
+ * okay.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ link->source_addr = cpu_to_be32(temp_addr);
+ link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP);
+
link->dest_addr = cpu_to_be32(ssi_sxx_phys);
- else
+ link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_NOSNOOP);
+ } else {
link->source_addr = cpu_to_be32(ssi_sxx_phys);
+ link->source_attr = cpu_to_be32(CCSR_DMA_ATR_NOSNOOP);
+
+ link->dest_addr = cpu_to_be32(temp_addr);
+ link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP);
+ }
+
+ temp_addr += period_size;
}
return 0;
@@ -808,7 +802,6 @@
.ioctl = snd_pcm_lib_ioctl,
.hw_params = fsl_dma_hw_params,
.hw_free = fsl_dma_hw_free,
- .prepare = fsl_dma_prepare,
.pointer = fsl_dma_pointer,
};
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index c6d6eb7..169bca2 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -72,6 +72,7 @@
* @dev: struct device pointer
* @playback: the number of playback streams opened
* @capture: the number of capture streams opened
+ * @asynchronous: 0=synchronous mode, 1=asynchronous mode
* @cpu_dai: the CPU DAI for this device
* @dev_attr: the sysfs device attribute structure
* @stats: SSI statistics
@@ -86,6 +87,7 @@
struct device *dev;
unsigned int playback;
unsigned int capture;
+ int asynchronous;
struct snd_soc_dai cpu_dai;
struct device_attribute dev_attr;
@@ -301,9 +303,10 @@
*
* FIXME: Little-endian samples require a different shift dir
*/
- clrsetbits_be32(&ssi->scr, CCSR_SSI_SCR_I2S_MODE_MASK,
- CCSR_SSI_SCR_TFR_CLK_DIS |
- CCSR_SSI_SCR_I2S_MODE_SLAVE | CCSR_SSI_SCR_SYN);
+ clrsetbits_be32(&ssi->scr,
+ CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
+ CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
+ | (ssi_private->asynchronous ? 0 : CCSR_SSI_SCR_SYN));
out_be32(&ssi->stcr,
CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
@@ -382,10 +385,15 @@
SNDRV_PCM_HW_PARAM_RATE,
first_runtime->rate, first_runtime->rate);
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- first_runtime->sample_bits,
- first_runtime->sample_bits);
+ /* If we're in synchronous mode, then we need to constrain
+ * the sample size as well. We don't support independent sample
+ * rates in asynchronous mode.
+ */
+ if (!ssi_private->asynchronous)
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ first_runtime->sample_bits,
+ first_runtime->sample_bits);
ssi_private->second_stream = substream;
}
@@ -400,7 +408,7 @@
}
/**
- * fsl_ssi_prepare: prepare the SSI.
+ * fsl_ssi_hw_params - program the sample size
*
* Most of the SSI registers have been programmed in the startup function,
* but the word length must be programmed here. Unfortunately, programming
@@ -412,23 +420,27 @@
* Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the
* clock master.
*/
-static int fsl_ssi_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
-
- struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+ struct fsl_ssi_private *ssi_private = cpu_dai->private_data;
if (substream == ssi_private->first_stream) {
- u32 wl;
+ struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+ unsigned int sample_size =
+ snd_pcm_format_width(params_format(hw_params));
+ u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
/* The SSI should always be disabled at this points (SSIEN=0) */
- wl = CCSR_SSI_SxCCR_WL(snd_pcm_format_width(runtime->format));
/* In synchronous mode, the SSI uses STCCR for capture */
- clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
+ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
+ !ssi_private->asynchronous)
+ clrsetbits_be32(&ssi->stccr,
+ CCSR_SSI_SxCCR_WL_MASK, wl);
+ else
+ clrsetbits_be32(&ssi->srccr,
+ CCSR_SSI_SxCCR_WL_MASK, wl);
}
return 0;
@@ -452,28 +464,33 @@
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
+ clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
setbits32(&ssi->scr,
CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
} else {
- clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
+ long timeout = jiffies + 10;
+
setbits32(&ssi->scr,
CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
- /*
- * I think we need this delay to allow time for the SSI
- * to put data into its FIFO. Without it, ALSA starts
- * to complain about overruns.
+ /* Wait until the SSI has filled its FIFO. Without this
+ * delay, ALSA complains about overruns. When the FIFO
+ * is full, the DMA controller initiates its first
+ * transfer. Until then, however, the DMA's DAR
+ * register is zero, which translates to an
+ * out-of-bounds pointer. This makes ALSA think an
+ * overrun has occurred.
*/
- mdelay(1);
+ while (!(in_be32(&ssi->sisr) & CCSR_SSI_SISR_RFF0) &&
+ (jiffies < timeout));
+ if (!(in_be32(&ssi->sisr) & CCSR_SSI_SISR_RFF0))
+ return -EIO;
}
break;
case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
clrbits32(&ssi->scr, CCSR_SSI_SCR_TE);
@@ -563,6 +580,15 @@
/**
* fsl_ssi_dai_template: template CPU DAI for the SSI
*/
+static struct snd_soc_dai_ops fsl_ssi_dai_ops = {
+ .startup = fsl_ssi_startup,
+ .hw_params = fsl_ssi_hw_params,
+ .shutdown = fsl_ssi_shutdown,
+ .trigger = fsl_ssi_trigger,
+ .set_sysclk = fsl_ssi_set_sysclk,
+ .set_fmt = fsl_ssi_set_fmt,
+};
+
static struct snd_soc_dai fsl_ssi_dai_template = {
.playback = {
/* The SSI does not support monaural audio. */
@@ -577,14 +603,7 @@
.rates = FSLSSI_I2S_RATES,
.formats = FSLSSI_I2S_FORMATS,
},
- .ops = {
- .startup = fsl_ssi_startup,
- .prepare = fsl_ssi_prepare,
- .shutdown = fsl_ssi_shutdown,
- .trigger = fsl_ssi_trigger,
- .set_sysclk = fsl_ssi_set_sysclk,
- .set_fmt = fsl_ssi_set_fmt,
- },
+ .ops = &fsl_ssi_dai_ops,
};
/**
@@ -654,6 +673,7 @@
ssi_private->ssi_phys = ssi_info->ssi_phys;
ssi_private->irq = ssi_info->irq;
ssi_private->dev = ssi_info->dev;
+ ssi_private->asynchronous = ssi_info->asynchronous;
ssi_private->dev->driver_data = fsl_ssi_dai;
@@ -704,6 +724,14 @@
}
EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai);
+static int __init fsl_ssi_init(void)
+{
+ printk(KERN_INFO "Freescale Synchronous Serial Interface (SSI) ASoC Driver\n");
+
+ return 0;
+}
+module_init(fsl_ssi_init);
+
MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h
index 83b44d7..eade01f 100644
--- a/sound/soc/fsl/fsl_ssi.h
+++ b/sound/soc/fsl/fsl_ssi.h
@@ -208,6 +208,7 @@
* ssi_phys: physical address of the SSI registers
* irq: IRQ of this SSI
* dev: struct device, used to create the sysfs statistics file
+ * asynchronous: 0=synchronous mode, 1=asynchronous mode
*/
struct fsl_ssi_info {
unsigned int id;
@@ -215,6 +216,7 @@
dma_addr_t ssi_phys;
unsigned int irq;
struct device *dev;
+ int asynchronous;
};
struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info);
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c
index 9eb1ce1..3aa729d 100644
--- a/sound/soc/fsl/mpc5200_psc_i2s.c
+++ b/sound/soc/fsl/mpc5200_psc_i2s.c
@@ -468,6 +468,16 @@
/**
* psc_i2s_dai_template: template CPU Digital Audio Interface
*/
+static struct snd_soc_dai_ops psc_i2s_dai_ops = {
+ .startup = psc_i2s_startup,
+ .hw_params = psc_i2s_hw_params,
+ .hw_free = psc_i2s_hw_free,
+ .shutdown = psc_i2s_shutdown,
+ .trigger = psc_i2s_trigger,
+ .set_sysclk = psc_i2s_set_sysclk,
+ .set_fmt = psc_i2s_set_fmt,
+};
+
static struct snd_soc_dai psc_i2s_dai_template = {
.playback = {
.channels_min = 2,
@@ -481,15 +491,7 @@
.rates = PSC_I2S_RATES,
.formats = PSC_I2S_FORMATS,
},
- .ops = {
- .startup = psc_i2s_startup,
- .hw_params = psc_i2s_hw_params,
- .hw_free = psc_i2s_hw_free,
- .shutdown = psc_i2s_shutdown,
- .trigger = psc_i2s_trigger,
- .set_sysclk = psc_i2s_set_sysclk,
- .set_fmt = psc_i2s_set_fmt,
- },
+ .ops = &psc_i2s_dai_ops,
};
/* ---------------------------------------------------------------------
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
index acf39a6..ef67d1c 100644
--- a/sound/soc/fsl/mpc8610_hpcd.c
+++ b/sound/soc/fsl/mpc8610_hpcd.c
@@ -353,6 +353,11 @@
}
ssi_info.irq = machine_data->ssi_irq;
+ /* Do we want to use asynchronous mode? */
+ ssi_info.asynchronous =
+ of_find_property(np, "fsl,ssi-asynchronous", NULL) ? 1 : 0;
+ if (ssi_info.asynchronous)
+ dev_info(&ofdev->dev, "using asynchronous mode\n");
/* Map the global utilities registers. */
guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts");
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 4f7f040..675732e 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -8,7 +8,7 @@
config SND_OMAP_SOC_N810
tristate "SoC Audio support for Nokia N810"
- depends on SND_OMAP_SOC && MACH_NOKIA_N810
+ depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C
select SND_OMAP_SOC_MCBSP
select OMAP_MUX
select SND_SOC_TLV320AIC3X
@@ -17,7 +17,7 @@
config SND_OMAP_SOC_OSK5912
tristate "SoC Audio support for omap osk5912"
- depends on SND_OMAP_SOC && MACH_OMAP_OSK
+ depends on SND_OMAP_SOC && MACH_OMAP_OSK && I2C
select SND_OMAP_SOC_MCBSP
select SND_SOC_TLV320AIC23
help
@@ -55,3 +55,13 @@
select SND_SOC_TWL4030
help
Say Y if you want to add support for SoC audio on the OMAP3 Pandora.
+
+config SND_OMAP_SOC_OMAP3_BEAGLE
+ tristate "SoC Audio support for OMAP3 Beagle"
+ depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3_BEAGLE
+ select SND_OMAP_SOC_MCBSP
+ select SND_SOC_TWL4030
+ help
+ Say Y if you want to add support for SoC audio on the Beagleboard.
+
+
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index 76fedd9..0c9e4ac 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -12,6 +12,7 @@
snd-soc-omap2evm-objs := omap2evm.o
snd-soc-sdp3430-objs := sdp3430.o
snd-soc-omap3pandora-objs := omap3pandora.o
+snd-soc-omap3beagle-objs := omap3beagle.o
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
@@ -19,3 +20,4 @@
obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o
obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
+obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index 25593fe..a6d1178 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -40,6 +40,13 @@
#define N810_HEADSET_AMP_GPIO 10
#define N810_SPEAKER_AMP_GPIO 101
+enum {
+ N810_JACK_DISABLED,
+ N810_JACK_HP,
+ N810_JACK_HS,
+ N810_JACK_MIC,
+};
+
static struct clk *sys_clkout2;
static struct clk *sys_clkout2_src;
static struct clk *func96m_clk;
@@ -50,15 +57,32 @@
static void n810_ext_control(struct snd_soc_codec *codec)
{
+ int hp = 0, line1l = 0;
+
+ switch (n810_jack_func) {
+ case N810_JACK_HS:
+ line1l = 1;
+ case N810_JACK_HP:
+ hp = 1;
+ break;
+ case N810_JACK_MIC:
+ line1l = 1;
+ break;
+ }
+
if (n810_spk_func)
snd_soc_dapm_enable_pin(codec, "Ext Spk");
else
snd_soc_dapm_disable_pin(codec, "Ext Spk");
- if (n810_jack_func)
+ if (hp)
snd_soc_dapm_enable_pin(codec, "Headphone Jack");
else
snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+ if (line1l)
+ snd_soc_dapm_enable_pin(codec, "LINE1L");
+ else
+ snd_soc_dapm_disable_pin(codec, "LINE1L");
if (n810_dmic_func)
snd_soc_dapm_enable_pin(codec, "DMic");
@@ -72,7 +96,7 @@
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->socdev->codec;
+ struct snd_soc_codec *codec = rtd->socdev->card->codec;
snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
@@ -229,7 +253,7 @@
};
static const char *spk_function[] = {"Off", "On"};
-static const char *jack_function[] = {"Off", "Headphone"};
+static const char *jack_function[] = {"Off", "Headphone", "Headset", "Mic"};
static const char *input_function[] = {"ADC", "Digital Mic"};
static const struct soc_enum n810_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
@@ -248,20 +272,23 @@
static int n810_aic33_init(struct snd_soc_codec *codec)
{
- int i, err;
+ int err;
/* Not connected */
snd_soc_dapm_nc_pin(codec, "MONO_LOUT");
snd_soc_dapm_nc_pin(codec, "HPLCOM");
snd_soc_dapm_nc_pin(codec, "HPRCOM");
+ snd_soc_dapm_nc_pin(codec, "MIC3L");
+ snd_soc_dapm_nc_pin(codec, "MIC3R");
+ snd_soc_dapm_nc_pin(codec, "LINE1R");
+ snd_soc_dapm_nc_pin(codec, "LINE2L");
+ snd_soc_dapm_nc_pin(codec, "LINE2R");
/* Add N810 specific controls */
- for (i = 0; i < ARRAY_SIZE(aic33_n810_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&aic33_n810_controls[i], codec, NULL));
- if (err < 0)
- return err;
- }
+ err = snd_soc_add_controls(codec, aic33_n810_controls,
+ ARRAY_SIZE(aic33_n810_controls));
+ if (err < 0)
+ return err;
/* Add N810 specific widgets */
snd_soc_dapm_new_controls(codec, aic33_dapm_widgets,
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 05dd5ab..d6882be 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -461,6 +461,16 @@
return err;
}
+static struct snd_soc_dai_ops omap_mcbsp_dai_ops = {
+ .startup = omap_mcbsp_dai_startup,
+ .shutdown = omap_mcbsp_dai_shutdown,
+ .trigger = omap_mcbsp_dai_trigger,
+ .hw_params = omap_mcbsp_dai_hw_params,
+ .set_fmt = omap_mcbsp_dai_set_dai_fmt,
+ .set_clkdiv = omap_mcbsp_dai_set_clkdiv,
+ .set_sysclk = omap_mcbsp_dai_set_dai_sysclk,
+};
+
#define OMAP_MCBSP_DAI_BUILDER(link_id) \
{ \
.name = "omap-mcbsp-dai-"#link_id, \
@@ -477,15 +487,7 @@
.rates = OMAP_MCBSP_RATES, \
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
}, \
- .ops = { \
- .startup = omap_mcbsp_dai_startup, \
- .shutdown = omap_mcbsp_dai_shutdown, \
- .trigger = omap_mcbsp_dai_trigger, \
- .hw_params = omap_mcbsp_dai_hw_params, \
- .set_fmt = omap_mcbsp_dai_set_dai_fmt, \
- .set_clkdiv = omap_mcbsp_dai_set_clkdiv, \
- .set_sysclk = omap_mcbsp_dai_set_dai_sysclk, \
- }, \
+ .ops = &omap_mcbsp_dai_ops, \
.private_data = &mcbsp_data[(link_id)].bus_id, \
}
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index dd3bb29..8e1431c 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -265,7 +265,7 @@
runtime->dma_bytes);
}
-struct snd_pcm_ops omap_pcm_ops = {
+static struct snd_pcm_ops omap_pcm_ops = {
.open = omap_pcm_open,
.close = omap_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c
index fcc2f5d..fe282d4 100644
--- a/sound/soc/omap/omap3pandora.c
+++ b/sound/soc/omap/omap3pandora.c
@@ -143,7 +143,7 @@
};
static const struct snd_soc_dapm_widget omap3pandora_in_dapm_widgets[] = {
- SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
+ SND_SOC_DAPM_MIC("Mic (internal)", NULL),
SND_SOC_DAPM_MIC("Mic (external)", NULL),
SND_SOC_DAPM_LINE("Line In", NULL),
};
@@ -155,16 +155,33 @@
};
static const struct snd_soc_dapm_route omap3pandora_in_map[] = {
- {"INL", NULL, "Line In"},
- {"INR", NULL, "Line In"},
- {"INL", NULL, "Mic (Internal)"},
- {"INR", NULL, "Mic (external)"},
+ {"AUXL", NULL, "Line In"},
+ {"AUXR", NULL, "Line In"},
+
+ {"MAINMIC", NULL, "Mic Bias 1"},
+ {"Mic Bias 1", NULL, "Mic (internal)"},
+
+ {"SUBMIC", NULL, "Mic Bias 2"},
+ {"Mic Bias 2", NULL, "Mic (external)"},
};
static int omap3pandora_out_init(struct snd_soc_codec *codec)
{
int ret;
+ /* All TWL4030 output pins are floating */
+ snd_soc_dapm_nc_pin(codec, "OUTL");
+ snd_soc_dapm_nc_pin(codec, "OUTR");
+ snd_soc_dapm_nc_pin(codec, "EARPIECE");
+ snd_soc_dapm_nc_pin(codec, "PREDRIVEL");
+ snd_soc_dapm_nc_pin(codec, "PREDRIVER");
+ snd_soc_dapm_nc_pin(codec, "HSOL");
+ snd_soc_dapm_nc_pin(codec, "HSOR");
+ snd_soc_dapm_nc_pin(codec, "CARKITL");
+ snd_soc_dapm_nc_pin(codec, "CARKITR");
+ snd_soc_dapm_nc_pin(codec, "HFL");
+ snd_soc_dapm_nc_pin(codec, "HFR");
+
ret = snd_soc_dapm_new_controls(codec, omap3pandora_out_dapm_widgets,
ARRAY_SIZE(omap3pandora_out_dapm_widgets));
if (ret < 0)
@@ -180,18 +197,11 @@
{
int ret;
- /* All TWL4030 output pins are floating */
- snd_soc_dapm_nc_pin(codec, "OUTL"),
- snd_soc_dapm_nc_pin(codec, "OUTR"),
- snd_soc_dapm_nc_pin(codec, "EARPIECE"),
- snd_soc_dapm_nc_pin(codec, "PREDRIVEL"),
- snd_soc_dapm_nc_pin(codec, "PREDRIVER"),
- snd_soc_dapm_nc_pin(codec, "HSOL"),
- snd_soc_dapm_nc_pin(codec, "HSOR"),
- snd_soc_dapm_nc_pin(codec, "CARKITL"),
- snd_soc_dapm_nc_pin(codec, "CARKITR"),
- snd_soc_dapm_nc_pin(codec, "HFL"),
- snd_soc_dapm_nc_pin(codec, "HFR"),
+ /* Not comnnected */
+ snd_soc_dapm_nc_pin(codec, "HSMIC");
+ snd_soc_dapm_nc_pin(codec, "CARKITMIC");
+ snd_soc_dapm_nc_pin(codec, "DIGIMIC0");
+ snd_soc_dapm_nc_pin(codec, "DIGIMIC1");
ret = snd_soc_dapm_new_controls(codec, omap3pandora_in_dapm_widgets,
ARRAY_SIZE(omap3pandora_in_dapm_widgets));
@@ -251,10 +261,9 @@
{
int ret;
- if (!machine_is_omap3_pandora()) {
- pr_debug(PREFIX "Not OMAP3 Pandora\n");
+ if (!machine_is_omap3_pandora())
return -ENODEV;
- }
+
pr_info("OMAP3 Pandora SoC init\n");
ret = gpio_request(OMAP3_PANDORA_DAC_POWER_GPIO, "dac_power");
diff --git a/sound/soc/omap/osk5912.c b/sound/soc/omap/osk5912.c
index cd41a94..a952a4e 100644
--- a/sound/soc/omap/osk5912.c
+++ b/sound/soc/omap/osk5912.c
@@ -186,13 +186,6 @@
return -ENODEV;
}
- if (clk_get_usecount(tlv320aic23_mclk) > 0) {
- /* MCLK is already in use */
- printk(KERN_WARNING
- "MCLK in use at %d Hz. We change it to %d Hz\n",
- (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK);
- }
-
/*
* Configure 12 MHz output on MCLK.
*/
@@ -205,9 +198,8 @@
}
}
- printk(KERN_INFO "MCLK = %d [%d], usecount = %d\n",
- (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK,
- clk_get_usecount(tlv320aic23_mclk));
+ printk(KERN_INFO "MCLK = %d [%d]\n",
+ (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK);
return 0;
err1:
diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c
index e226fa7..10f1c86 100644
--- a/sound/soc/omap/sdp3430.c
+++ b/sound/soc/omap/sdp3430.c
@@ -28,6 +28,7 @@
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
+#include <sound/jack.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
@@ -38,6 +39,8 @@
#include "omap-pcm.h"
#include "../codecs/twl4030.h"
+static struct snd_soc_card snd_soc_sdp3430;
+
static int sdp3430_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -81,12 +84,121 @@
.hw_params = sdp3430_hw_params,
};
+/* Headset jack */
+static struct snd_soc_jack hs_jack;
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin hs_jack_pins[] = {
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headset Stereophone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+/* Headset jack detection gpios */
+static struct snd_soc_jack_gpio hs_jack_gpios[] = {
+ {
+ .gpio = (OMAP_MAX_GPIO_LINES + 2),
+ .name = "hsdet-gpio",
+ .report = SND_JACK_HEADSET,
+ .debounce_time = 200,
+ },
+};
+
+/* SDP3430 machine DAPM */
+static const struct snd_soc_dapm_widget sdp3430_twl4030_dapm_widgets[] = {
+ SND_SOC_DAPM_MIC("Ext Mic", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_HP("Headset Stereophone", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* External Mics: MAINMIC, SUBMIC with bias*/
+ {"MAINMIC", NULL, "Mic Bias 1"},
+ {"SUBMIC", NULL, "Mic Bias 2"},
+ {"Mic Bias 1", NULL, "Ext Mic"},
+ {"Mic Bias 2", NULL, "Ext Mic"},
+
+ /* External Speakers: HFL, HFR */
+ {"Ext Spk", NULL, "HFL"},
+ {"Ext Spk", NULL, "HFR"},
+
+ /* Headset Mic: HSMIC with bias */
+ {"HSMIC", NULL, "Headset Mic Bias"},
+ {"Headset Mic Bias", NULL, "Headset Mic"},
+
+ /* Headset Stereophone (Headphone): HSOL, HSOR */
+ {"Headset Stereophone", NULL, "HSOL"},
+ {"Headset Stereophone", NULL, "HSOR"},
+};
+
+static int sdp3430_twl4030_init(struct snd_soc_codec *codec)
+{
+ int ret;
+
+ /* Add SDP3430 specific widgets */
+ ret = snd_soc_dapm_new_controls(codec, sdp3430_twl4030_dapm_widgets,
+ ARRAY_SIZE(sdp3430_twl4030_dapm_widgets));
+ if (ret)
+ return ret;
+
+ /* Set up SDP3430 specific audio path audio_map */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ /* SDP3430 connected pins */
+ snd_soc_dapm_enable_pin(codec, "Ext Mic");
+ snd_soc_dapm_enable_pin(codec, "Ext Spk");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Headset Stereophone");
+
+ /* TWL4030 not connected pins */
+ snd_soc_dapm_nc_pin(codec, "AUXL");
+ snd_soc_dapm_nc_pin(codec, "AUXR");
+ snd_soc_dapm_nc_pin(codec, "CARKITMIC");
+ snd_soc_dapm_nc_pin(codec, "DIGIMIC0");
+ snd_soc_dapm_nc_pin(codec, "DIGIMIC1");
+
+ snd_soc_dapm_nc_pin(codec, "OUTL");
+ snd_soc_dapm_nc_pin(codec, "OUTR");
+ snd_soc_dapm_nc_pin(codec, "EARPIECE");
+ snd_soc_dapm_nc_pin(codec, "PREDRIVEL");
+ snd_soc_dapm_nc_pin(codec, "PREDRIVER");
+ snd_soc_dapm_nc_pin(codec, "CARKITL");
+ snd_soc_dapm_nc_pin(codec, "CARKITR");
+
+ ret = snd_soc_dapm_sync(codec);
+ if (ret)
+ return ret;
+
+ /* Headset jack detection */
+ ret = snd_soc_jack_new(&snd_soc_sdp3430, "Headset Jack",
+ SND_JACK_HEADSET, &hs_jack);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
+ hs_jack_pins);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
+ hs_jack_gpios);
+
+ return ret;
+}
+
/* Digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link sdp3430_dai = {
.name = "TWL4030",
.stream_name = "TWL4030",
.cpu_dai = &omap_mcbsp_dai[0],
.codec_dai = &twl4030_dai,
+ .init = sdp3430_twl4030_init,
.ops = &sdp3430_ops,
};
@@ -142,6 +254,9 @@
static void __exit sdp3430_soc_exit(void)
{
+ snd_soc_jack_free_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
+ hs_jack_gpios);
+
platform_device_unregister(sdp3430_snd_device);
}
module_exit(sdp3430_soc_exit);
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index f82e106..5998ab3 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -61,6 +61,24 @@
Say Y if you want to add support for SoC audio on Sharp
Zaurus SL-C6000x models (Tosa).
+config SND_PXA2XX_SOC_E740
+ tristate "SoC AC97 Audio support for e740"
+ depends on SND_PXA2XX_SOC && MACH_E740
+ select SND_SOC_WM9705
+ select SND_PXA2XX_SOC_AC97
+ help
+ Say Y if you want to add support for SoC audio on the
+ toshiba e740 PDA
+
+config SND_PXA2XX_SOC_E750
+ tristate "SoC AC97 Audio support for e750"
+ depends on SND_PXA2XX_SOC && MACH_E750
+ select SND_SOC_WM9705
+ select SND_PXA2XX_SOC_AC97
+ help
+ Say Y if you want to add support for SoC audio on the
+ toshiba e750 PDA
+
config SND_PXA2XX_SOC_E800
tristate "SoC AC97 Audio support for e800"
depends on SND_PXA2XX_SOC && MACH_E800
@@ -97,3 +115,12 @@
help
Say Y if you want to add support for SoC audio on the
Marvell Zylonite reference platform.
+
+config SND_PXA2XX_SOC_MIOA701
+ tristate "SoC Audio support for MIO A701"
+ depends on SND_PXA2XX_SOC && MACH_MIOA701
+ select SND_PXA2XX_SOC_AC97
+ select SND_SOC_WM9713
+ help
+ Say Y if you want to add support for SoC audio on the
+ MIO A701.
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index 08a9f27..8ed881c 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -13,17 +13,23 @@
snd-soc-corgi-objs := corgi.o
snd-soc-poodle-objs := poodle.o
snd-soc-tosa-objs := tosa.o
+snd-soc-e740-objs := e740_wm9705.o
+snd-soc-e750-objs := e750_wm9705.o
snd-soc-e800-objs := e800_wm9712.o
snd-soc-spitz-objs := spitz.o
snd-soc-em-x270-objs := em-x270.o
snd-soc-palm27x-objs := palm27x.o
snd-soc-zylonite-objs := zylonite.o
+snd-soc-mioa701-objs := mioa701_wm9713.o
obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o
+obj-$(CONFIG_SND_PXA2XX_SOC_E740) += snd-soc-e740.o
+obj-$(CONFIG_SND_PXA2XX_SOC_E750) += snd-soc-e750.o
obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.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_MIOA701) += snd-soc-mioa701.o
obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 1ba25a5..02263e5 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/timer.h>
+#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
@@ -100,7 +101,7 @@
static int corgi_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->socdev->codec;
+ struct snd_soc_codec *codec = rtd->socdev->card->codec;
/* check the jack status at stream startup */
corgi_ext_control(codec);
@@ -275,18 +276,16 @@
*/
static int corgi_wm8731_init(struct snd_soc_codec *codec)
{
- int i, err;
+ int err;
snd_soc_dapm_nc_pin(codec, "LLINEIN");
snd_soc_dapm_nc_pin(codec, "RLINEIN");
/* Add corgi specific controls */
- for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8731_corgi_controls[i], codec, NULL));
- if (err < 0)
- return err;
- }
+ err = snd_soc_add_controls(codec, wm8731_corgi_controls,
+ ARRAY_SIZE(wm8731_corgi_controls));
+ if (err < 0)
+ return err;
/* Add corgi specific widgets */
snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets,
@@ -317,19 +316,44 @@
.num_links = 1,
};
-/* corgi audio private data */
-static struct wm8731_setup_data corgi_wm8731_setup = {
- .i2c_bus = 0,
- .i2c_address = 0x1b,
-};
-
/* corgi audio subsystem */
static struct snd_soc_device corgi_snd_devdata = {
.card = &snd_soc_corgi,
.codec_dev = &soc_codec_dev_wm8731,
- .codec_data = &corgi_wm8731_setup,
};
+/*
+ * FIXME: This is a temporary bodge to avoid cross-tree merge issues.
+ * New drivers should register the wm8731 I2C device in the machine
+ * setup code (under arch/arm for ARM systems).
+ */
+static int wm8731_i2c_register(void)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = 0x1b;
+ strlcpy(info.type, "wm8731", I2C_NAME_SIZE);
+
+ adapter = i2c_get_adapter(0);
+ if (!adapter) {
+ printk(KERN_ERR "can't get i2c adapter 0\n");
+ return -ENODEV;
+ }
+
+ client = i2c_new_device(adapter, &info);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ printk(KERN_ERR "can't add i2c device at 0x%x\n",
+ (unsigned int)info.addr);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static struct platform_device *corgi_snd_device;
static int __init corgi_init(void)
@@ -340,6 +364,10 @@
machine_is_husky()))
return -ENODEV;
+ ret = wm8731_i2c_register();
+ if (ret != 0)
+ return ret;
+
corgi_snd_device = platform_device_alloc("soc-audio", -1);
if (!corgi_snd_device)
return -ENOMEM;
diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c
new file mode 100644
index 0000000..7cd2f89
--- /dev/null
+++ b/sound/soc/pxa/e740_wm9705.c
@@ -0,0 +1,211 @@
+/*
+ * e740-wm9705.c -- SoC audio for e740
+ *
+ * Copyright 2007 (c) Ian Molton <spyro@f2s.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; version 2 ONLY.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/gpio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <mach/audio.h>
+#include <mach/eseries-gpio.h>
+
+#include <asm/mach-types.h>
+
+#include "../codecs/wm9705.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-ac97.h"
+
+
+#define E740_AUDIO_OUT 1
+#define E740_AUDIO_IN 2
+
+static int e740_audio_power;
+
+static void e740_sync_audio_power(int status)
+{
+ gpio_set_value(GPIO_E740_WM9705_nAVDD2, !status);
+ gpio_set_value(GPIO_E740_AMP_ON, (status & E740_AUDIO_OUT) ? 1 : 0);
+ gpio_set_value(GPIO_E740_MIC_ON, (status & E740_AUDIO_IN) ? 1 : 0);
+}
+
+static int e740_mic_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (event & SND_SOC_DAPM_PRE_PMU)
+ e740_audio_power |= E740_AUDIO_IN;
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ e740_audio_power &= ~E740_AUDIO_IN;
+
+ e740_sync_audio_power(e740_audio_power);
+
+ return 0;
+}
+
+static int e740_output_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (event & SND_SOC_DAPM_PRE_PMU)
+ e740_audio_power |= E740_AUDIO_OUT;
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ e740_audio_power &= ~E740_AUDIO_OUT;
+
+ e740_sync_audio_power(e740_audio_power);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget e740_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
+ SND_SOC_DAPM_PGA_E("Output Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
+ e740_output_amp_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("Mic Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
+ e740_mic_amp_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"Output Amp", NULL, "LOUT"},
+ {"Output Amp", NULL, "ROUT"},
+ {"Output Amp", NULL, "MONOOUT"},
+
+ {"Speaker", NULL, "Output Amp"},
+ {"Headphone Jack", NULL, "Output Amp"},
+
+ {"MIC1", NULL, "Mic Amp"},
+ {"Mic Amp", NULL, "Mic (Internal)"},
+};
+
+static int e740_ac97_init(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_nc_pin(codec, "HPOUTL");
+ snd_soc_dapm_nc_pin(codec, "HPOUTR");
+ snd_soc_dapm_nc_pin(codec, "PHONE");
+ snd_soc_dapm_nc_pin(codec, "LINEINL");
+ snd_soc_dapm_nc_pin(codec, "LINEINR");
+ snd_soc_dapm_nc_pin(codec, "CDINL");
+ snd_soc_dapm_nc_pin(codec, "CDINR");
+ snd_soc_dapm_nc_pin(codec, "PCBEEP");
+ snd_soc_dapm_nc_pin(codec, "MIC2");
+
+ snd_soc_dapm_new_controls(codec, e740_dapm_widgets,
+ ARRAY_SIZE(e740_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link e740_dai[] = {
+ {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
+ .codec_dai = &wm9705_dai[WM9705_DAI_AC97_HIFI],
+ .init = e740_ac97_init,
+ },
+ {
+ .name = "AC97 Aux",
+ .stream_name = "AC97 Aux",
+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
+ .codec_dai = &wm9705_dai[WM9705_DAI_AC97_AUX],
+ },
+};
+
+static struct snd_soc_card e740 = {
+ .name = "Toshiba e740",
+ .platform = &pxa2xx_soc_platform,
+ .dai_link = e740_dai,
+ .num_links = ARRAY_SIZE(e740_dai),
+};
+
+static struct snd_soc_device e740_snd_devdata = {
+ .card = &e740,
+ .codec_dev = &soc_codec_dev_wm9705,
+};
+
+static struct platform_device *e740_snd_device;
+
+static int __init e740_init(void)
+{
+ int ret;
+
+ if (!machine_is_e740())
+ return -ENODEV;
+
+ ret = gpio_request(GPIO_E740_MIC_ON, "Mic amp");
+ if (ret)
+ return ret;
+
+ ret = gpio_request(GPIO_E740_AMP_ON, "Output amp");
+ if (ret)
+ goto free_mic_amp_gpio;
+
+ ret = gpio_request(GPIO_E740_WM9705_nAVDD2, "Audio power");
+ if (ret)
+ goto free_op_amp_gpio;
+
+ /* Disable audio */
+ ret = gpio_direction_output(GPIO_E740_MIC_ON, 0);
+ if (ret)
+ goto free_apwr_gpio;
+ ret = gpio_direction_output(GPIO_E740_AMP_ON, 0);
+ if (ret)
+ goto free_apwr_gpio;
+ ret = gpio_direction_output(GPIO_E740_WM9705_nAVDD2, 1);
+ if (ret)
+ goto free_apwr_gpio;
+
+ e740_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!e740_snd_device) {
+ ret = -ENOMEM;
+ goto free_apwr_gpio;
+ }
+
+ platform_set_drvdata(e740_snd_device, &e740_snd_devdata);
+ e740_snd_devdata.dev = &e740_snd_device->dev;
+ ret = platform_device_add(e740_snd_device);
+
+ if (!ret)
+ return 0;
+
+/* Fail gracefully */
+ platform_device_put(e740_snd_device);
+free_apwr_gpio:
+ gpio_free(GPIO_E740_WM9705_nAVDD2);
+free_op_amp_gpio:
+ gpio_free(GPIO_E740_AMP_ON);
+free_mic_amp_gpio:
+ gpio_free(GPIO_E740_MIC_ON);
+
+ return ret;
+}
+
+static void __exit e740_exit(void)
+{
+ platform_device_unregister(e740_snd_device);
+}
+
+module_init(e740_init);
+module_exit(e740_exit);
+
+/* Module information */
+MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
+MODULE_DESCRIPTION("ALSA SoC driver for e740");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c
new file mode 100644
index 0000000..8dceccc
--- /dev/null
+++ b/sound/soc/pxa/e750_wm9705.c
@@ -0,0 +1,187 @@
+/*
+ * e750-wm9705.c -- SoC audio for e750
+ *
+ * Copyright 2007 (c) Ian Molton <spyro@f2s.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; version 2 ONLY.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/gpio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <mach/audio.h>
+#include <mach/eseries-gpio.h>
+
+#include <asm/mach-types.h>
+
+#include "../codecs/wm9705.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-ac97.h"
+
+static int e750_spk_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (event & SND_SOC_DAPM_PRE_PMU)
+ gpio_set_value(GPIO_E750_SPK_AMP_OFF, 0);
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ gpio_set_value(GPIO_E750_SPK_AMP_OFF, 1);
+
+ return 0;
+}
+
+static int e750_hp_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (event & SND_SOC_DAPM_PRE_PMU)
+ gpio_set_value(GPIO_E750_HP_AMP_OFF, 0);
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ gpio_set_value(GPIO_E750_HP_AMP_OFF, 1);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget e750_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
+ SND_SOC_DAPM_PGA_E("Headphone Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
+ e750_hp_amp_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("Speaker Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
+ e750_spk_amp_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"Headphone Amp", NULL, "HPOUTL"},
+ {"Headphone Amp", NULL, "HPOUTR"},
+ {"Headphone Jack", NULL, "Headphone Amp"},
+
+ {"Speaker Amp", NULL, "MONOOUT"},
+ {"Speaker", NULL, "Speaker Amp"},
+
+ {"MIC1", NULL, "Mic (Internal)"},
+};
+
+static int e750_ac97_init(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_nc_pin(codec, "LOUT");
+ snd_soc_dapm_nc_pin(codec, "ROUT");
+ snd_soc_dapm_nc_pin(codec, "PHONE");
+ snd_soc_dapm_nc_pin(codec, "LINEINL");
+ snd_soc_dapm_nc_pin(codec, "LINEINR");
+ snd_soc_dapm_nc_pin(codec, "CDINL");
+ snd_soc_dapm_nc_pin(codec, "CDINR");
+ snd_soc_dapm_nc_pin(codec, "PCBEEP");
+ snd_soc_dapm_nc_pin(codec, "MIC2");
+
+ snd_soc_dapm_new_controls(codec, e750_dapm_widgets,
+ ARRAY_SIZE(e750_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link e750_dai[] = {
+ {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
+ .codec_dai = &wm9705_dai[WM9705_DAI_AC97_HIFI],
+ .init = e750_ac97_init,
+ /* use ops to check startup state */
+ },
+ {
+ .name = "AC97 Aux",
+ .stream_name = "AC97 Aux",
+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
+ .codec_dai = &wm9705_dai[WM9705_DAI_AC97_AUX],
+ },
+};
+
+static struct snd_soc_card e750 = {
+ .name = "Toshiba e750",
+ .platform = &pxa2xx_soc_platform,
+ .dai_link = e750_dai,
+ .num_links = ARRAY_SIZE(e750_dai),
+};
+
+static struct snd_soc_device e750_snd_devdata = {
+ .card = &e750,
+ .codec_dev = &soc_codec_dev_wm9705,
+};
+
+static struct platform_device *e750_snd_device;
+
+static int __init e750_init(void)
+{
+ int ret;
+
+ if (!machine_is_e750())
+ return -ENODEV;
+
+ ret = gpio_request(GPIO_E750_HP_AMP_OFF, "Headphone amp");
+ if (ret)
+ return ret;
+
+ ret = gpio_request(GPIO_E750_SPK_AMP_OFF, "Speaker amp");
+ if (ret)
+ goto free_hp_amp_gpio;
+
+ ret = gpio_direction_output(GPIO_E750_HP_AMP_OFF, 1);
+ if (ret)
+ goto free_spk_amp_gpio;
+
+ ret = gpio_direction_output(GPIO_E750_SPK_AMP_OFF, 1);
+ if (ret)
+ goto free_spk_amp_gpio;
+
+ e750_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!e750_snd_device) {
+ ret = -ENOMEM;
+ goto free_spk_amp_gpio;
+ }
+
+ platform_set_drvdata(e750_snd_device, &e750_snd_devdata);
+ e750_snd_devdata.dev = &e750_snd_device->dev;
+ ret = platform_device_add(e750_snd_device);
+
+ if (!ret)
+ return 0;
+
+/* Fail gracefully */
+ platform_device_put(e750_snd_device);
+free_spk_amp_gpio:
+ gpio_free(GPIO_E750_SPK_AMP_OFF);
+free_hp_amp_gpio:
+ gpio_free(GPIO_E750_HP_AMP_OFF);
+
+ return ret;
+}
+
+static void __exit e750_exit(void)
+{
+ platform_device_unregister(e750_snd_device);
+ gpio_free(GPIO_E750_SPK_AMP_OFF);
+ gpio_free(GPIO_E750_HP_AMP_OFF);
+}
+
+module_init(e750_init);
+module_exit(e750_exit);
+
+/* Module information */
+MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
+MODULE_DESCRIPTION("ALSA SoC driver for e750");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c
index 2e3386d..bc019cd 100644
--- a/sound/soc/pxa/e800_wm9712.c
+++ b/sound/soc/pxa/e800_wm9712.c
@@ -1,8 +1,6 @@
/*
* e800-wm9712.c -- SoC audio for e800
*
- * Based on tosa.c
- *
* Copyright 2007 (c) Ian Molton <spyro@f2s.com>
*
* This program is free software; you can redistribute it and/or modify it
@@ -13,7 +11,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/device.h>
+#include <linux/gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -21,23 +19,85 @@
#include <sound/soc-dapm.h>
#include <asm/mach-types.h>
-#include <mach/pxa-regs.h>
-#include <mach/hardware.h>
#include <mach/audio.h>
+#include <mach/eseries-gpio.h>
#include "../codecs/wm9712.h"
#include "pxa2xx-pcm.h"
#include "pxa2xx-ac97.h"
-static struct snd_soc_card e800;
+static int e800_spk_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (event & SND_SOC_DAPM_PRE_PMU)
+ gpio_set_value(GPIO_E800_SPK_AMP_ON, 1);
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ gpio_set_value(GPIO_E800_SPK_AMP_ON, 0);
+
+ return 0;
+}
+
+static int e800_hp_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (event & SND_SOC_DAPM_PRE_PMU)
+ gpio_set_value(GPIO_E800_HP_AMP_OFF, 0);
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ gpio_set_value(GPIO_E800_HP_AMP_OFF, 1);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget e800_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic (Internal1)", NULL),
+ SND_SOC_DAPM_MIC("Mic (Internal2)", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_PGA_E("Headphone Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
+ e800_hp_amp_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("Speaker Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
+ e800_spk_amp_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Headphone Jack", NULL, "HPOUTR"},
+ {"Headphone Jack", NULL, "Headphone Amp"},
+
+ {"Speaker Amp", NULL, "MONOOUT"},
+ {"Speaker", NULL, "Speaker Amp"},
+
+ {"MIC1", NULL, "Mic (Internal1)"},
+ {"MIC2", NULL, "Mic (Internal2)"},
+};
+
+static int e800_ac97_init(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, e800_dapm_widgets,
+ ARRAY_SIZE(e800_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
static struct snd_soc_dai_link e800_dai[] = {
-{
- .name = "AC97 Aux",
- .stream_name = "AC97 Aux",
- .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
- .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
-},
+ {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
+ .init = e800_ac97_init,
+ },
+ {
+ .name = "AC97 Aux",
+ .stream_name = "AC97 Aux",
+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
+ },
};
static struct snd_soc_card e800 = {
@@ -61,6 +121,22 @@
if (!machine_is_e800())
return -ENODEV;
+ ret = gpio_request(GPIO_E800_HP_AMP_OFF, "Headphone amp");
+ if (ret)
+ return ret;
+
+ ret = gpio_request(GPIO_E800_SPK_AMP_ON, "Speaker amp");
+ if (ret)
+ goto free_hp_amp_gpio;
+
+ ret = gpio_direction_output(GPIO_E800_HP_AMP_OFF, 1);
+ if (ret)
+ goto free_spk_amp_gpio;
+
+ ret = gpio_direction_output(GPIO_E800_SPK_AMP_ON, 1);
+ if (ret)
+ goto free_spk_amp_gpio;
+
e800_snd_device = platform_device_alloc("soc-audio", -1);
if (!e800_snd_device)
return -ENOMEM;
@@ -69,8 +145,15 @@
e800_snd_devdata.dev = &e800_snd_device->dev;
ret = platform_device_add(e800_snd_device);
- if (ret)
- platform_device_put(e800_snd_device);
+ if (!ret)
+ return 0;
+
+/* Fail gracefully */
+ platform_device_put(e800_snd_device);
+free_spk_amp_gpio:
+ gpio_free(GPIO_E800_SPK_AMP_ON);
+free_hp_amp_gpio:
+ gpio_free(GPIO_E800_HP_AMP_OFF);
return ret;
}
@@ -78,6 +161,8 @@
static void __exit e800_exit(void)
{
platform_device_unregister(e800_snd_device);
+ gpio_free(GPIO_E800_SPK_AMP_ON);
+ gpio_free(GPIO_E800_HP_AMP_OFF);
}
module_init(e800_init);
@@ -86,4 +171,4 @@
/* Module information */
MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
MODULE_DESCRIPTION("ALSA SoC driver for e800");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c
new file mode 100644
index 0000000..19eda8b
--- /dev/null
+++ b/sound/soc/pxa/mioa701_wm9713.c
@@ -0,0 +1,250 @@
+/*
+ * Handles the Mitac mioa701 SoC system
+ *
+ * Copyright (C) 2008 Robert Jarzmik
+ *
+ * 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 in version 2 of the License.
+ *
+ * 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
+ *
+ * This is a little schema of the sound interconnections :
+ *
+ * Sagem X200 Wolfson WM9713
+ * +--------+ +-------------------+ Rear Speaker
+ * | | | | /-+
+ * | +--->----->---+MONOIN SPKL+--->----+-+ |
+ * | GSM | | | | | |
+ * | +--->----->---+PCBEEP SPKR+--->----+-+ |
+ * | CHIP | | | \-+
+ * | +---<-----<---+MONO |
+ * | | | | Front Speaker
+ * +--------+ | | /-+
+ * | HPL+--->----+-+ |
+ * | | | | |
+ * | OUT3+--->----+-+ |
+ * | | \-+
+ * | |
+ * | | Front Micro
+ * | | +
+ * | MIC1+-----<--+o+
+ * | | +
+ * +-------------------+ ---
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-types.h>
+#include <mach/audio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/ac97_codec.h>
+
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-ac97.h"
+#include "../codecs/wm9713.h"
+
+#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x)
+
+#define AC97_GPIO_PULL 0x58
+
+/* Use GPIO8 for rear speaker amplifier */
+static int rear_amp_power(struct snd_soc_codec *codec, int power)
+{
+ unsigned short reg;
+
+ if (power) {
+ reg = snd_soc_read(codec, AC97_GPIO_CFG);
+ snd_soc_write(codec, AC97_GPIO_CFG, reg | 0x0100);
+ reg = snd_soc_read(codec, AC97_GPIO_PULL);
+ snd_soc_write(codec, AC97_GPIO_PULL, reg | (1<<15));
+ } else {
+ reg = snd_soc_read(codec, AC97_GPIO_CFG);
+ snd_soc_write(codec, AC97_GPIO_CFG, reg & ~0x0100);
+ reg = snd_soc_read(codec, AC97_GPIO_PULL);
+ snd_soc_write(codec, AC97_GPIO_PULL, reg & ~(1<<15));
+ }
+
+ return 0;
+}
+
+static int rear_amp_event(struct snd_soc_dapm_widget *widget,
+ struct snd_kcontrol *kctl, int event)
+{
+ struct snd_soc_codec *codec = widget->codec;
+
+ return rear_amp_power(codec, SND_SOC_DAPM_EVENT_ON(event));
+}
+
+/* mioa701 machine dapm widgets */
+static const struct snd_soc_dapm_widget mioa701_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Front Speaker", NULL),
+ SND_SOC_DAPM_SPK("Rear Speaker", rear_amp_event),
+ SND_SOC_DAPM_MIC("Headset", NULL),
+ SND_SOC_DAPM_LINE("GSM Line Out", NULL),
+ SND_SOC_DAPM_LINE("GSM Line In", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Front Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Call Mic */
+ {"Mic Bias", NULL, "Front Mic"},
+ {"MIC1", NULL, "Mic Bias"},
+
+ /* Headset Mic */
+ {"LINEL", NULL, "Headset Mic"},
+ {"LINER", NULL, "Headset Mic"},
+
+ /* GSM Module */
+ {"MONOIN", NULL, "GSM Line Out"},
+ {"PCBEEP", NULL, "GSM Line Out"},
+ {"GSM Line In", NULL, "MONO"},
+
+ /* headphone connected to HPL, HPR */
+ {"Headset", NULL, "HPL"},
+ {"Headset", NULL, "HPR"},
+
+ /* front speaker connected to HPL, OUT3 */
+ {"Front Speaker", NULL, "HPL"},
+ {"Front Speaker", NULL, "OUT3"},
+
+ /* rear speaker connected to SPKL, SPKR */
+ {"Rear Speaker", NULL, "SPKL"},
+ {"Rear Speaker", NULL, "SPKR"},
+};
+
+static int mioa701_wm9713_init(struct snd_soc_codec *codec)
+{
+ unsigned short reg;
+
+ /* Add mioa701 specific widgets */
+ snd_soc_dapm_new_controls(codec, ARRAY_AND_SIZE(mioa701_dapm_widgets));
+
+ /* Set up mioa701 specific audio path audio_mapnects */
+ snd_soc_dapm_add_routes(codec, ARRAY_AND_SIZE(audio_map));
+
+ /* Prepare GPIO8 for rear speaker amplifier */
+ reg = codec->read(codec, AC97_GPIO_CFG);
+ codec->write(codec, AC97_GPIO_CFG, reg | 0x0100);
+
+ /* Prepare MIC input */
+ reg = codec->read(codec, AC97_3D_CONTROL);
+ codec->write(codec, AC97_3D_CONTROL, reg | 0xc000);
+
+ snd_soc_dapm_enable_pin(codec, "Front Speaker");
+ snd_soc_dapm_enable_pin(codec, "Rear Speaker");
+ snd_soc_dapm_enable_pin(codec, "Front Mic");
+ snd_soc_dapm_enable_pin(codec, "GSM Line In");
+ snd_soc_dapm_enable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static struct snd_soc_ops mioa701_ops;
+
+static struct snd_soc_dai_link mioa701_dai[] = {
+ {
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
+ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
+ .init = mioa701_wm9713_init,
+ .ops = &mioa701_ops,
+ },
+ {
+ .name = "AC97 Aux",
+ .stream_name = "AC97 Aux",
+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
+ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_AUX],
+ .ops = &mioa701_ops,
+ },
+};
+
+static struct snd_soc_card mioa701 = {
+ .name = "MioA701",
+ .platform = &pxa2xx_soc_platform,
+ .dai_link = mioa701_dai,
+ .num_links = ARRAY_SIZE(mioa701_dai),
+};
+
+static struct snd_soc_device mioa701_snd_devdata = {
+ .card = &mioa701,
+ .codec_dev = &soc_codec_dev_wm9713,
+};
+
+static struct platform_device *mioa701_snd_device;
+
+static int mioa701_wm9713_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ if (!machine_is_mioa701())
+ return -ENODEV;
+
+ dev_warn(&pdev->dev, "Be warned that incorrect mixers/muxes setup will"
+ "lead to overheating and possible destruction of your device."
+ "Do not use without a good knowledge of mio's board design!\n");
+
+ mioa701_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!mioa701_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(mioa701_snd_device, &mioa701_snd_devdata);
+ mioa701_snd_devdata.dev = &mioa701_snd_device->dev;
+
+ ret = platform_device_add(mioa701_snd_device);
+ if (!ret)
+ return 0;
+
+ platform_device_put(mioa701_snd_device);
+ return ret;
+}
+
+static int __devexit mioa701_wm9713_remove(struct platform_device *pdev)
+{
+ platform_device_unregister(mioa701_snd_device);
+ return 0;
+}
+
+static struct platform_driver mioa701_wm9713_driver = {
+ .probe = mioa701_wm9713_probe,
+ .remove = __devexit_p(mioa701_wm9713_remove),
+ .driver = {
+ .name = "mioa701-wm9713",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mioa701_asoc_init(void)
+{
+ return platform_driver_register(&mioa701_wm9713_driver);
+}
+
+static void __exit mioa701_asoc_exit(void)
+{
+ platform_driver_unregister(&mioa701_wm9713_driver);
+}
+
+module_init(mioa701_asoc_init);
+module_exit(mioa701_asoc_exit);
+
+/* Module information */
+MODULE_AUTHOR("Robert Jarzmik (rjarzmik@free.fr)");
+MODULE_DESCRIPTION("ALSA SoC WM9713 MIO A701");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c
index 4a9cf30..48a73f6 100644
--- a/sound/soc/pxa/palm27x.c
+++ b/sound/soc/pxa/palm27x.c
@@ -55,7 +55,7 @@
static int palm27x_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->socdev->codec;
+ struct snd_soc_codec *codec = rtd->socdev->card->codec;
/* check the jack status at stream startup */
palm27x_ext_control(codec);
@@ -146,19 +146,16 @@
static int palm27x_ac97_init(struct snd_soc_codec *codec)
{
- int i, err;
+ int err;
snd_soc_dapm_nc_pin(codec, "OUT3");
snd_soc_dapm_nc_pin(codec, "MONOOUT");
/* add palm27x specific controls */
- for (i = 0; i < ARRAY_SIZE(palm27x_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&palm27x_controls[i],
- codec, NULL));
- if (err < 0)
- return err;
- }
+ err = snd_soc_add_controls(codec, palm27x_controls,
+ ARRAY_SIZE(palm27x_controls));
+ if (err < 0)
+ return err;
/* add palm27x specific widgets */
snd_soc_dapm_new_controls(codec, palm27x_dapm_widgets,
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index 6e98271..ef7c6c8 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -17,6 +17,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/timer.h>
+#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <sound/core.h>
@@ -77,7 +78,7 @@
static int poodle_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->socdev->codec;
+ struct snd_soc_codec *codec = rtd->socdev->card->codec;
/* check the jack status at stream startup */
poodle_ext_control(codec);
@@ -240,19 +241,17 @@
*/
static int poodle_wm8731_init(struct snd_soc_codec *codec)
{
- int i, err;
+ int err;
snd_soc_dapm_nc_pin(codec, "LLINEIN");
snd_soc_dapm_nc_pin(codec, "RLINEIN");
snd_soc_dapm_enable_pin(codec, "MICIN");
/* Add poodle specific controls */
- for (i = 0; i < ARRAY_SIZE(wm8731_poodle_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8731_poodle_controls[i], codec, NULL));
- if (err < 0)
- return err;
- }
+ err = snd_soc_add_controls(codec, wm8731_poodle_controls,
+ ARRAY_SIZE(wm8731_poodle_controls));
+ if (err < 0)
+ return err;
/* Add poodle specific widgets */
snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets,
@@ -283,17 +282,42 @@
.num_links = 1,
};
-/* poodle audio private data */
-static struct wm8731_setup_data poodle_wm8731_setup = {
- .i2c_bus = 0,
- .i2c_address = 0x1b,
-};
+/*
+ * FIXME: This is a temporary bodge to avoid cross-tree merge issues.
+ * New drivers should register the wm8731 I2C device in the machine
+ * setup code (under arch/arm for ARM systems).
+ */
+static int wm8731_i2c_register(void)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = 0x1b;
+ strlcpy(info.type, "wm8731", I2C_NAME_SIZE);
+
+ adapter = i2c_get_adapter(0);
+ if (!adapter) {
+ printk(KERN_ERR "can't get i2c adapter 0\n");
+ return -ENODEV;
+ }
+
+ client = i2c_new_device(adapter, &info);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ printk(KERN_ERR "can't add i2c device at 0x%x\n",
+ (unsigned int)info.addr);
+ return -ENODEV;
+ }
+
+ return 0;
+}
/* poodle audio subsystem */
static struct snd_soc_device poodle_snd_devdata = {
.card = &snd_soc_poodle,
.codec_dev = &soc_codec_dev_wm8731,
- .codec_data = &poodle_wm8731_setup,
};
static struct platform_device *poodle_snd_device;
@@ -305,6 +329,10 @@
if (!machine_is_poodle())
return -ENODEV;
+ ret = wm8731_i2c_register();
+ if (ret != 0)
+ return ret;
+
locomo_gpio_set_dir(&poodle_locomo_device.dev,
POODLE_LOCOMO_GPIO_AMP_ON, 0);
/* should we mute HP at startup - burning power ?*/
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index 73cb6b4..b0bf409 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -1,4 +1,3 @@
-#define DEBUG
/*
* pxa-ssp.c -- ALSA Soc Audio Layer
*
@@ -21,6 +20,8 @@
#include <linux/clk.h>
#include <linux/io.h>
+#include <asm/irq.h>
+
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
@@ -221,9 +222,9 @@
int ret = 0;
if (!cpu_dai->active) {
- ret = ssp_init(&priv->dev, cpu_dai->id + 1, SSP_NO_IRQ);
- if (ret < 0)
- return ret;
+ priv->dev.port = cpu_dai->id + 1;
+ priv->dev.irq = NO_IRQ;
+ clk_enable(priv->dev.ssp->clk);
ssp_disable(&priv->dev);
}
return ret;
@@ -238,7 +239,7 @@
if (!cpu_dai->active) {
ssp_disable(&priv->dev);
- ssp_exit(&priv->dev);
+ clk_disable(priv->dev.ssp->clk);
}
}
@@ -298,7 +299,7 @@
int val;
u32 sscr0 = ssp_read_reg(ssp, SSCR0) &
- ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ADC);
+ ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS);
dev_dbg(&ssp->pdev->dev,
"pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %d\n",
@@ -326,7 +327,7 @@
case PXA_SSP_CLK_AUDIO:
priv->sysclk = 0;
ssp_set_scr(&priv->dev, 1);
- sscr0 |= SSCR0_ADC;
+ sscr0 |= SSCR0_ACS;
break;
default:
return -ENODEV;
@@ -520,9 +521,20 @@
u32 sscr1;
u32 sspsp;
+ /* check if we need to change anything at all */
+ if (priv->dai_fmt == fmt)
+ return 0;
+
+ /* we can only change the settings if the port is not in use */
+ if (ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) {
+ dev_err(&ssp->pdev->dev,
+ "can't change hardware dai format: stream is in use");
+ return -EINVAL;
+ }
+
/* reset port settings */
sscr0 = ssp_read_reg(ssp, SSCR0) &
- (SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ADC);
+ (SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS);
sscr1 = SSCR1_RxTresh(8) | SSCR1_TxTresh(7);
sspsp = 0;
@@ -545,18 +557,18 @@
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- sscr0 |= SSCR0_MOD | SSCR0_PSP;
+ sscr0 |= SSCR0_PSP;
sscr1 |= SSCR1_RWOT | SSCR1_TRAIL;
+ /* See hw_params() */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
- sspsp |= SSPSP_FSRT;
+ sspsp |= SSPSP_SFRMP;
break;
case SND_SOC_DAIFMT_NB_IF:
- sspsp |= SSPSP_SFRMP | SSPSP_FSRT;
break;
case SND_SOC_DAIFMT_IB_IF:
- sspsp |= SSPSP_SFRMP;
+ sspsp |= SSPSP_SCMODE(3);
break;
default:
return -EINVAL;
@@ -642,34 +654,65 @@
sscr0 |= SSCR0_FPCKE;
#endif
sscr0 |= SSCR0_DataSize(16);
- if (params_channels(params) > 1)
- sscr0 |= SSCR0_EDSS;
break;
case SNDRV_PCM_FORMAT_S24_LE:
sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(8));
- /* we must be in network mode (2 slots) for 24 bit stereo */
break;
case SNDRV_PCM_FORMAT_S32_LE:
sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(16));
- /* we must be in network mode (2 slots) for 32 bit stereo */
break;
}
ssp_write_reg(ssp, SSCR0, sscr0);
switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- /* Cleared when the DAI format is set */
- sspsp = ssp_read_reg(ssp, SSPSP) | SSPSP_SFRMWDTH(width);
+ sspsp = ssp_read_reg(ssp, SSPSP);
+
+ if (((sscr0 & SSCR0_SCR) == SSCR0_SerClkDiv(4)) &&
+ (width == 16)) {
+ /* This is a special case where the bitclk is 64fs
+ * and we're not dealing with 2*32 bits of audio
+ * samples.
+ *
+ * The SSP values used for that are all found out by
+ * trying and failing a lot; some of the registers
+ * needed for that mode are only available on PXA3xx.
+ */
+
+#ifdef CONFIG_PXA3xx
+ if (!cpu_is_pxa3xx())
+ return -EINVAL;
+
+ sspsp |= SSPSP_SFRMWDTH(width * 2);
+ sspsp |= SSPSP_SFRMDLY(width * 4);
+ sspsp |= SSPSP_EDMYSTOP(3);
+ sspsp |= SSPSP_DMYSTOP(3);
+ sspsp |= SSPSP_DMYSTRT(1);
+#else
+ return -EINVAL;
+#endif
+ } else {
+ /* The frame width is the width the LRCLK is
+ * asserted for; the delay is expressed in
+ * half cycle units. We need the extra cycle
+ * because the data starts clocking out one BCLK
+ * after LRCLK changes polarity.
+ */
+ sspsp |= SSPSP_SFRMWDTH(width + 1);
+ sspsp |= SSPSP_SFRMDLY((width + 1) * 2);
+ sspsp |= SSPSP_DMYSTRT(1);
+ }
+
ssp_write_reg(ssp, SSPSP, sspsp);
break;
default:
break;
}
- /* We always use a network mode so we always require TDM slots
+ /* When we use a network mode, we always require TDM slots
* - complain loudly and fail if they've not been set up yet.
*/
- if (!(ssp_read_reg(ssp, SSTSA) & 0xf)) {
+ if ((sscr0 & SSCR0_MOD) && !(ssp_read_reg(ssp, SSTSA) & 0xf)) {
dev_err(&ssp->pdev->dev, "No TDM timeslot configured\n");
return -EINVAL;
}
@@ -751,7 +794,7 @@
if (!priv)
return -ENOMEM;
- priv->dev.ssp = ssp_request(dai->id, "SoC audio");
+ priv->dev.ssp = ssp_request(dai->id + 1, "SoC audio");
if (priv->dev.ssp == NULL) {
ret = -ENODEV;
goto err_priv;
@@ -782,6 +825,19 @@
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
+static struct snd_soc_dai_ops pxa_ssp_dai_ops = {
+ .startup = pxa_ssp_startup,
+ .shutdown = pxa_ssp_shutdown,
+ .trigger = pxa_ssp_trigger,
+ .hw_params = pxa_ssp_hw_params,
+ .set_sysclk = pxa_ssp_set_dai_sysclk,
+ .set_clkdiv = pxa_ssp_set_dai_clkdiv,
+ .set_pll = pxa_ssp_set_dai_pll,
+ .set_fmt = pxa_ssp_set_dai_fmt,
+ .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
+ .set_tristate = pxa_ssp_set_dai_tristate,
+};
+
struct snd_soc_dai pxa_ssp_dai[] = {
{
.name = "pxa2xx-ssp1",
@@ -802,18 +858,7 @@
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
- .ops = {
- .startup = pxa_ssp_startup,
- .shutdown = pxa_ssp_shutdown,
- .trigger = pxa_ssp_trigger,
- .hw_params = pxa_ssp_hw_params,
- .set_sysclk = pxa_ssp_set_dai_sysclk,
- .set_clkdiv = pxa_ssp_set_dai_clkdiv,
- .set_pll = pxa_ssp_set_dai_pll,
- .set_fmt = pxa_ssp_set_dai_fmt,
- .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
- .set_tristate = pxa_ssp_set_dai_tristate,
- },
+ .ops = &pxa_ssp_dai_ops,
},
{ .name = "pxa2xx-ssp2",
.id = 1,
@@ -833,18 +878,7 @@
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
- .ops = {
- .startup = pxa_ssp_startup,
- .shutdown = pxa_ssp_shutdown,
- .trigger = pxa_ssp_trigger,
- .hw_params = pxa_ssp_hw_params,
- .set_sysclk = pxa_ssp_set_dai_sysclk,
- .set_clkdiv = pxa_ssp_set_dai_clkdiv,
- .set_pll = pxa_ssp_set_dai_pll,
- .set_fmt = pxa_ssp_set_dai_fmt,
- .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
- .set_tristate = pxa_ssp_set_dai_tristate,
- },
+ .ops = &pxa_ssp_dai_ops,
},
{
.name = "pxa2xx-ssp3",
@@ -865,18 +899,7 @@
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
- .ops = {
- .startup = pxa_ssp_startup,
- .shutdown = pxa_ssp_shutdown,
- .trigger = pxa_ssp_trigger,
- .hw_params = pxa_ssp_hw_params,
- .set_sysclk = pxa_ssp_set_dai_sysclk,
- .set_clkdiv = pxa_ssp_set_dai_clkdiv,
- .set_pll = pxa_ssp_set_dai_pll,
- .set_fmt = pxa_ssp_set_dai_fmt,
- .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
- .set_tristate = pxa_ssp_set_dai_tristate,
- },
+ .ops = &pxa_ssp_dai_ops,
},
{
.name = "pxa2xx-ssp4",
@@ -897,18 +920,7 @@
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
- .ops = {
- .startup = pxa_ssp_startup,
- .shutdown = pxa_ssp_shutdown,
- .trigger = pxa_ssp_trigger,
- .hw_params = pxa_ssp_hw_params,
- .set_sysclk = pxa_ssp_set_dai_sysclk,
- .set_clkdiv = pxa_ssp_set_dai_clkdiv,
- .set_pll = pxa_ssp_set_dai_pll,
- .set_fmt = pxa_ssp_set_dai_fmt,
- .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
- .set_tristate = pxa_ssp_set_dai_tristate,
- },
+ .ops = &pxa_ssp_dai_ops,
},
};
EXPORT_SYMBOL_GPL(pxa_ssp_dai);
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index 812c2b4..01c21c6 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -106,13 +106,13 @@
static int pxa2xx_ac97_probe(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
- return pxa2xx_ac97_hw_probe(pdev);
+ return pxa2xx_ac97_hw_probe(to_platform_device(dai->dev));
}
static void pxa2xx_ac97_remove(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
- pxa2xx_ac97_hw_remove(pdev);
+ pxa2xx_ac97_hw_remove(to_platform_device(dai->dev));
}
static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
@@ -164,6 +164,18 @@
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000)
+static struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = {
+ .hw_params = pxa2xx_ac97_hw_params,
+};
+
+static struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = {
+ .hw_params = pxa2xx_ac97_hw_aux_params,
+};
+
+static struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = {
+ .hw_params = pxa2xx_ac97_hw_mic_params,
+};
+
/*
* There is only 1 physical AC97 interface for pxa2xx, but it
* has extra fifo's that can be used for aux DACs and ADCs.
@@ -189,8 +201,7 @@
.channels_max = 2,
.rates = PXA2XX_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = {
- .hw_params = pxa2xx_ac97_hw_params,},
+ .ops = &pxa_ac97_hifi_dai_ops,
},
{
.name = "pxa2xx-ac97-aux",
@@ -208,8 +219,7 @@
.channels_max = 1,
.rates = PXA2XX_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = {
- .hw_params = pxa2xx_ac97_hw_aux_params,},
+ .ops = &pxa_ac97_aux_dai_ops,
},
{
.name = "pxa2xx-ac97-mic",
@@ -221,23 +231,52 @@
.channels_max = 1,
.rates = PXA2XX_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = {
- .hw_params = pxa2xx_ac97_hw_mic_params,},
+ .ops = &pxa_ac97_mic_dai_ops,
},
};
EXPORT_SYMBOL_GPL(pxa_ac97_dai);
EXPORT_SYMBOL_GPL(soc_ac97_ops);
+static int __devinit pxa2xx_ac97_dev_probe(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pxa_ac97_dai); i++)
+ pxa_ac97_dai[i].dev = &pdev->dev;
+
+ /* Punt most of the init to the SoC probe; we may need the machine
+ * driver to do interesting things with the clocking to get us up
+ * and running.
+ */
+ return snd_soc_register_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai));
+}
+
+static int __devexit pxa2xx_ac97_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai));
+
+ return 0;
+}
+
+static struct platform_driver pxa2xx_ac97_driver = {
+ .probe = pxa2xx_ac97_dev_probe,
+ .remove = __devexit_p(pxa2xx_ac97_dev_remove),
+ .driver = {
+ .name = "pxa2xx-ac97",
+ .owner = THIS_MODULE,
+ },
+};
+
static int __init pxa_ac97_init(void)
{
- return snd_soc_register_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai));
+ return platform_driver_register(&pxa2xx_ac97_driver);
}
module_init(pxa_ac97_init);
static void __exit pxa_ac97_exit(void)
{
- snd_soc_unregister_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai));
+ platform_driver_unregister(&pxa2xx_ac97_driver);
}
module_exit(pxa_ac97_exit);
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index 517991f..e6c2440 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -25,20 +25,11 @@
#include <mach/hardware.h>
#include <mach/pxa-regs.h>
-#include <mach/pxa2xx-gpio.h>
#include <mach/audio.h>
#include "pxa2xx-pcm.h"
#include "pxa2xx-i2s.h"
-struct pxa2xx_gpio {
- u32 sys;
- u32 rx;
- u32 tx;
- u32 clk;
- u32 frm;
-};
-
/*
* I2S Controller Register and Bit Definitions
*/
@@ -106,21 +97,6 @@
DCMD_BURST32 | DCMD_WIDTH4,
};
-static struct pxa2xx_gpio gpio_bus[] = {
- { /* I2S SoC Slave */
- .rx = GPIO29_SDATA_IN_I2S_MD,
- .tx = GPIO30_SDATA_OUT_I2S_MD,
- .clk = GPIO28_BITCLK_IN_I2S_MD,
- .frm = GPIO31_SYNC_I2S_MD,
- },
- { /* I2S SoC Master */
- .rx = GPIO29_SDATA_IN_I2S_MD,
- .tx = GPIO30_SDATA_OUT_I2S_MD,
- .clk = GPIO28_BITCLK_OUT_I2S_MD,
- .frm = GPIO31_SYNC_I2S_MD,
- },
-};
-
static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -181,9 +157,6 @@
if (clk_id != PXA2XX_I2S_SYSCLK)
return -ENODEV;
- if (pxa_i2s.master && dir == SND_SOC_CLOCK_OUT)
- pxa_gpio_mode(gpio_bus[pxa_i2s.master].sys);
-
return 0;
}
@@ -194,10 +167,6 @@
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
- pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx);
- pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx);
- pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm);
- pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk);
BUG_ON(IS_ERR(clk_i2s));
clk_enable(clk_i2s);
pxa_i2s_wait();
@@ -335,6 +304,15 @@
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
+static struct snd_soc_dai_ops pxa_i2s_dai_ops = {
+ .startup = pxa2xx_i2s_startup,
+ .shutdown = pxa2xx_i2s_shutdown,
+ .trigger = pxa2xx_i2s_trigger,
+ .hw_params = pxa2xx_i2s_hw_params,
+ .set_fmt = pxa2xx_i2s_set_dai_fmt,
+ .set_sysclk = pxa2xx_i2s_set_dai_sysclk,
+};
+
struct snd_soc_dai pxa_i2s_dai = {
.name = "pxa2xx-i2s",
.id = 0,
@@ -350,14 +328,7 @@
.channels_max = 2,
.rates = PXA2XX_I2S_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = {
- .startup = pxa2xx_i2s_startup,
- .shutdown = pxa2xx_i2s_shutdown,
- .trigger = pxa2xx_i2s_trigger,
- .hw_params = pxa2xx_i2s_hw_params,
- .set_fmt = pxa2xx_i2s_set_dai_fmt,
- .set_sysclk = pxa2xx_i2s_set_dai_sysclk,
- },
+ .ops = &pxa_i2s_dai_ops,
};
EXPORT_SYMBOL_GPL(pxa_i2s_dai);
@@ -398,11 +369,6 @@
static int __init pxa2xx_i2s_init(void)
{
- if (cpu_is_pxa27x())
- gpio_bus[1].sys = GPIO113_I2S_SYSCLK_MD;
- else
- gpio_bus[1].sys = GPIO32_SYSCLK_I2S_MD;
-
clk_i2s = ERR_PTR(-ENOENT);
return platform_driver_register(&pxa2xx_i2s_driver);
}
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index a3b9e6b..6ca9f53 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -109,7 +109,7 @@
static int spitz_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->socdev->codec;
+ struct snd_soc_codec *codec = rtd->socdev->card->codec;
/* check the jack status at stream startup */
spitz_ext_control(codec);
@@ -278,7 +278,7 @@
*/
static int spitz_wm8750_init(struct snd_soc_codec *codec)
{
- int i, err;
+ int err;
/* NC codec pins */
snd_soc_dapm_nc_pin(codec, "RINPUT1");
@@ -290,12 +290,10 @@
snd_soc_dapm_nc_pin(codec, "MONO1");
/* Add spitz specific controls */
- for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8750_spitz_controls[i], codec, NULL));
- if (err < 0)
- return err;
- }
+ err = snd_soc_add_controls(codec, wm8750_spitz_controls,
+ ARRAY_SIZE(wm8750_spitz_controls));
+ if (err < 0)
+ return err;
/* Add spitz specific widgets */
snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets,
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
index c77194f..fc78137 100644
--- a/sound/soc/pxa/tosa.c
+++ b/sound/soc/pxa/tosa.c
@@ -82,7 +82,7 @@
static int tosa_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->socdev->codec;
+ struct snd_soc_codec *codec = rtd->socdev->card->codec;
/* check the jack status at stream startup */
tosa_ext_control(codec);
@@ -188,18 +188,16 @@
static int tosa_ac97_init(struct snd_soc_codec *codec)
{
- int i, err;
+ int err;
snd_soc_dapm_nc_pin(codec, "OUT3");
snd_soc_dapm_nc_pin(codec, "MONOOUT");
/* add tosa specific controls */
- for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&tosa_controls[i],codec, NULL));
- if (err < 0)
- return err;
- }
+ err = snd_soc_add_controls(codec, tosa_controls,
+ ARRAY_SIZE(tosa_controls));
+ if (err < 0)
+ return err;
/* add tosa specific widgets */
snd_soc_dapm_new_controls(codec, tosa_dapm_widgets,
diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c
index f8e9ecd..9a386b4 100644
--- a/sound/soc/pxa/zylonite.c
+++ b/sound/soc/pxa/zylonite.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
+#include <linux/clk.h>
#include <linux/i2c.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -26,6 +27,17 @@
#include "pxa2xx-ac97.h"
#include "pxa-ssp.h"
+/*
+ * There is a physical switch SW15 on the board which changes the MCLK
+ * for the WM9713 between the standard AC97 master clock and the
+ * output of the CLK_POUT signal from the PXA.
+ */
+static int clk_pout;
+module_param(clk_pout, int, 0);
+MODULE_PARM_DESC(clk_pout, "Use CLK_POUT as WM9713 MCLK (SW15 on board).");
+
+static struct clk *pout;
+
static struct snd_soc_card zylonite;
static const struct snd_soc_dapm_widget zylonite_dapm_widgets[] = {
@@ -61,10 +73,8 @@
static int zylonite_wm9713_init(struct snd_soc_codec *codec)
{
- /* Currently we only support use of the AC97 clock here. If
- * CLK_POUT is selected by SW15 then the clock API will need
- * to be used to request and enable it here.
- */
+ if (clk_pout)
+ snd_soc_dai_set_pll(&codec->dai[0], 0, clk_get_rate(pout), 0);
snd_soc_dapm_new_controls(codec, zylonite_dapm_widgets,
ARRAY_SIZE(zylonite_dapm_widgets));
@@ -86,27 +96,51 @@
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
unsigned int pll_out = 0;
- unsigned int acds = 0;
unsigned int wm9713_div = 0;
int ret = 0;
+ int rate = params_rate(params);
+ int width = snd_pcm_format_physical_width(params_format(params));
- switch (params_rate(params)) {
+ /* Only support ratios that we can generate neatly from the AC97
+ * based master clock - in particular, this excludes 44.1kHz.
+ * In most applications the voice DAC will be used for telephony
+ * data so multiples of 8kHz will be the common case.
+ */
+ switch (rate) {
case 8000:
wm9713_div = 12;
- pll_out = 2048000;
break;
case 16000:
wm9713_div = 6;
- pll_out = 4096000;
break;
case 48000:
- default:
wm9713_div = 2;
- pll_out = 12288000;
- acds = 1;
break;
+ default:
+ /* Don't support OSS emulation */
+ return -EINVAL;
}
+ /* Add 1 to the width for the leading clock cycle */
+ pll_out = rate * (width + 1) * 8;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, pll_out);
+ if (ret < 0)
+ return ret;
+
+ if (clk_pout)
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_PLL_DIV,
+ WM9713_PCMDIV(wm9713_div));
+ else
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_DIV,
+ WM9713_PCMDIV(wm9713_div));
+ if (ret < 0)
+ return ret;
+
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
@@ -117,32 +151,6 @@
if (ret < 0)
return ret;
- ret = snd_soc_dai_set_tdm_slot(cpu_dai,
- params_channels(params),
- params_channels(params));
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, pll_out);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_AUDIO_DIV_ACDS, acds);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1);
- if (ret < 0)
- return ret;
-
- /* Note that if the PLL is in use the WM9713_PCMCLK_PLL_DIV needs
- * to be set instead.
- */
- ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_DIV,
- WM9713_PCMDIV(wm9713_div));
- if (ret < 0)
- return ret;
-
return 0;
}
@@ -173,8 +181,72 @@
},
};
+static int zylonite_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ if (clk_pout) {
+ pout = clk_get(NULL, "CLK_POUT");
+ if (IS_ERR(pout)) {
+ dev_err(&pdev->dev, "Unable to obtain CLK_POUT: %ld\n",
+ PTR_ERR(pout));
+ return PTR_ERR(pout);
+ }
+
+ ret = clk_enable(pout);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Unable to enable CLK_POUT: %d\n",
+ ret);
+ clk_put(pout);
+ return ret;
+ }
+
+ dev_dbg(&pdev->dev, "MCLK enabled at %luHz\n",
+ clk_get_rate(pout));
+ }
+
+ return 0;
+}
+
+static int zylonite_remove(struct platform_device *pdev)
+{
+ if (clk_pout) {
+ clk_disable(pout);
+ clk_put(pout);
+ }
+
+ return 0;
+}
+
+static int zylonite_suspend_post(struct platform_device *pdev,
+ pm_message_t state)
+{
+ if (clk_pout)
+ clk_disable(pout);
+
+ return 0;
+}
+
+static int zylonite_resume_pre(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ if (clk_pout) {
+ ret = clk_enable(pout);
+ if (ret != 0)
+ dev_err(&pdev->dev, "Unable to enable CLK_POUT: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+
static struct snd_soc_card zylonite = {
.name = "Zylonite",
+ .probe = &zylonite_probe,
+ .remove = &zylonite_remove,
+ .suspend_post = &zylonite_suspend_post,
+ .resume_pre = &zylonite_resume_pre,
.platform = &pxa2xx_soc_platform,
.dai_link = zylonite_dai,
.num_links = ARRAY_SIZE(zylonite_dai),
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index fcd03ac..2f3a21e 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -1,19 +1,31 @@
config SND_S3C24XX_SOC
- tristate "SoC Audio for the Samsung S3C24XX chips"
- depends on ARCH_S3C2410
+ tristate "SoC Audio for the Samsung S3CXXXX chips"
+ depends on ARCH_S3C2410 || ARCH_S3C64XX
help
Say Y or M if you want to add support for codecs attached to
- the S3C24XX AC97, I2S or SSP interface. You will also need
- to select the audio interfaces to support below.
+ the S3C24XX and S3C64XX AC97, I2S or SSP interface. You will
+ also need to select the audio interfaces to support below.
config SND_S3C24XX_SOC_I2S
tristate
+ select S3C2410_DMA
+
+config SND_S3C_I2SV2_SOC
+ tristate
config SND_S3C2412_SOC_I2S
tristate
+ select SND_S3C_I2SV2_SOC
+ select S3C2410_DMA
+
+config SND_S3C64XX_SOC_I2S
+ tristate
+ select SND_S3C_I2SV2_SOC
+ select S3C64XX_DMA
config SND_S3C2443_SOC_AC97
tristate
+ select S3C2410_DMA
select AC97_BUS
select SND_SOC_AC97_BUS
@@ -26,6 +38,14 @@
Say Y if you want to add support for SoC audio on smdk2440
with the WM8753.
+config SND_S3C24XX_SOC_JIVE_WM8750
+ tristate "SoC I2S Audio support for Jive"
+ depends on SND_S3C24XX_SOC && MACH_JIVE
+ select SND_SOC_WM8750
+ select SND_S3C2412_SOC_I2S
+ help
+ Sat Y if you want to add support for SoC audio on the Jive.
+
config SND_S3C24XX_SOC_SMDK2443_WM9710
tristate "SoC AC97 Audio support for SMDK2443 - WM9710"
depends on SND_S3C24XX_SOC && MACH_SMDK2443
@@ -48,4 +68,5 @@
tristate "SoC I2S Audio support UDA134X wired to a S3C24XX"
depends on SND_S3C24XX_SOC
select SND_S3C24XX_SOC_I2S
+ select SND_SOC_L3
select SND_SOC_UDA134X
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
index 96b3f3f..07a93a2 100644
--- a/sound/soc/s3c24xx/Makefile
+++ b/sound/soc/s3c24xx/Makefile
@@ -2,19 +2,25 @@
snd-soc-s3c24xx-objs := s3c24xx-pcm.o
snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o
+snd-soc-s3c64xx-i2s-objs := s3c64xx-i2s.o
snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o
+snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o
obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
+obj-$(CONFIG_SND_S3C64XX_SOC_I2S) += snd-soc-s3c64xx-i2s.o
+obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
# S3C24XX Machine Support
+snd-soc-jive-wm8750-objs := jive_wm8750.o
snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
+obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o
obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o
diff --git a/sound/soc/s3c24xx/jive_wm8750.c b/sound/soc/s3c24xx/jive_wm8750.c
new file mode 100644
index 0000000..3206379
--- /dev/null
+++ b/sound/soc/s3c24xx/jive_wm8750.c
@@ -0,0 +1,201 @@
+/* sound/soc/s3c24xx/jive_wm8750.c
+ *
+ * Copyright 2007,2008 Simtec Electronics
+ *
+ * Based on sound/soc/pxa/spitz.c
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * 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/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+
+#include "s3c24xx-pcm.h"
+#include "s3c2412-i2s.h"
+
+#include "../codecs/wm8750.h"
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ { "Headphone Jack", NULL, "LOUT1" },
+ { "Headphone Jack", NULL, "ROUT1" },
+ { "Internal Speaker", NULL, "LOUT2" },
+ { "Internal Speaker", NULL, "ROUT2" },
+ { "LINPUT1", NULL, "Line Input" },
+ { "RINPUT1", NULL, "Line Input" },
+};
+
+static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Internal Speaker", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+};
+
+static int jive_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->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct s3c_i2sv2_rate_calc div;
+ unsigned int clk = 0;
+ int ret = 0;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 48000:
+ case 96000:
+ clk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ clk = 11289600;
+ break;
+ }
+
+ s3c_i2sv2_calc_rate(&div, NULL, params_rate(params),
+ s3c2412_get_iisclk());
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ 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_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C2412_DIV_RCLK, div.fs_div);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C2412_DIV_PRESCALER,
+ div.clk_div - 1);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops jive_ops = {
+ .hw_params = jive_hw_params,
+};
+
+static int jive_wm8750_init(struct snd_soc_codec *codec)
+{
+ int err;
+
+ /* These endpoints are not being used. */
+ snd_soc_dapm_nc_pin(codec, "LINPUT2");
+ snd_soc_dapm_nc_pin(codec, "RINPUT2");
+ snd_soc_dapm_nc_pin(codec, "LINPUT3");
+ snd_soc_dapm_nc_pin(codec, "RINPUT3");
+ snd_soc_dapm_nc_pin(codec, "OUT3");
+ snd_soc_dapm_nc_pin(codec, "MONO");
+
+ /* Add jive specific widgets */
+ err = snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets,
+ ARRAY_SIZE(wm8750_dapm_widgets));
+ if (err) {
+ printk(KERN_ERR "%s: failed to add widgets (%d)\n",
+ __func__, err);
+ return err;
+ }
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link jive_dai = {
+ .name = "wm8750",
+ .stream_name = "WM8750",
+ .cpu_dai = &s3c2412_i2s_dai,
+ .codec_dai = &wm8750_dai,
+ .init = jive_wm8750_init,
+ .ops = &jive_ops,
+};
+
+/* jive audio machine driver */
+static struct snd_soc_machine snd_soc_machine_jive = {
+ .name = "Jive",
+ .dai_link = &jive_dai,
+ .num_links = 1,
+};
+
+/* jive audio private data */
+static struct wm8750_setup_data jive_wm8750_setup = {
+};
+
+/* jive audio subsystem */
+static struct snd_soc_device jive_snd_devdata = {
+ .machine = &snd_soc_machine_jive,
+ .platform = &s3c24xx_soc_platform,
+ .codec_dev = &soc_codec_dev_wm8750_spi,
+ .codec_data = &jive_wm8750_setup,
+};
+
+static struct platform_device *jive_snd_device;
+
+static int __init jive_init(void)
+{
+ int ret;
+
+ if (!machine_is_jive())
+ return 0;
+
+ printk("JIVE WM8750 Audio support\n");
+
+ jive_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!jive_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(jive_snd_device, &jive_snd_devdata);
+ jive_snd_devdata.dev = &jive_snd_device->dev;
+ ret = platform_device_add(jive_snd_device);
+
+ if (ret)
+ platform_device_put(jive_snd_device);
+
+ return ret;
+}
+
+static void __exit jive_exit(void)
+{
+ platform_device_unregister(jive_snd_device);
+}
+
+module_init(jive_init);
+module_exit(jive_exit);
+
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("ALSA SoC Jive Audio support");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c
index 45bb12e..289fadf 100644
--- a/sound/soc/s3c24xx/neo1973_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_wm8753.c
@@ -29,25 +29,17 @@
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
-#include <mach/audio.h>
+#include <plat/audio.h>
#include <linux/io.h>
#include <mach/spi-gpio.h>
-#include <asm/plat-s3c24xx/regs-iis.h>
+#include <plat/regs-iis.h>
#include "../codecs/wm8753.h"
#include "lm4857.h"
#include "s3c24xx-pcm.h"
#include "s3c24xx-i2s.h"
-/* Debugging stuff */
-#define S3C24XX_SOC_NEO1973_WM8753_DEBUG 0
-#if S3C24XX_SOC_NEO1973_WM8753_DEBUG
-#define DBG(x...) printk(KERN_DEBUG "s3c24xx-soc-neo1973-wm8753: " x)
-#else
-#define DBG(x...)
-#endif
-
/* define the scenarios */
#define NEO_AUDIO_OFF 0
#define NEO_GSM_CALL_AUDIO_HANDSET 1
@@ -72,7 +64,7 @@
int ret = 0;
unsigned long iis_clkrate;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
iis_clkrate = s3c24xx_i2s_get_clockrate();
@@ -158,7 +150,7 @@
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
/* disable the PLL */
return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
@@ -181,7 +173,7 @@
int ret = 0;
unsigned long iis_clkrate;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
iis_clkrate = s3c24xx_i2s_get_clockrate();
@@ -224,7 +216,7 @@
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
/* disable the PLL */
return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
@@ -246,7 +238,7 @@
static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario)
{
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
switch (neo1973_scenario) {
case NEO_AUDIO_OFF:
@@ -330,7 +322,7 @@
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
if (neo1973_scenario == ucontrol->value.integer.value[0])
return 0;
@@ -344,7 +336,7 @@
static void lm4857_write_regs(void)
{
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
if (i2c_master_send(i2c, lm4857_regs, 4) != 4)
printk(KERN_ERR "lm4857: i2c write failed\n");
@@ -357,7 +349,7 @@
int shift = (kcontrol->private_value >> 8) & 0x0F;
int mask = (kcontrol->private_value >> 16) & 0xFF;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask;
return 0;
@@ -385,7 +377,7 @@
{
u8 value = lm4857_regs[LM4857_CTRL] & 0x0F;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
if (value)
value -= 5;
@@ -399,7 +391,7 @@
{
u8 value = ucontrol->value.integer.value[0];
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
if (value)
value += 5;
@@ -506,9 +498,9 @@
*/
static int neo1973_wm8753_init(struct snd_soc_codec *codec)
{
- int i, err;
+ int err;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
/* set up NC codec pins */
snd_soc_dapm_nc_pin(codec, "LOUT2");
@@ -526,13 +518,10 @@
set_scenario_endpoints(codec, NEO_AUDIO_OFF);
/* add neo1973 specific controls */
- for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_controls); i++) {
- err = snd_ctl_add(codec->card,
- snd_soc_cnew(&wm8753_neo1973_controls[i],
- codec, NULL));
- if (err < 0)
- return err;
- }
+ err = snd_soc_add_controls(codec, wm8753_neo1973_controls,
+ ARRAY_SIZE(8753_neo1973_controls));
+ if (err < 0)
+ return err;
/* set up neo1973 specific audio routes */
err = snd_soc_dapm_add_routes(codec, dapm_routes,
@@ -585,21 +574,15 @@
.num_links = ARRAY_SIZE(neo1973_dai),
};
-static struct wm8753_setup_data neo1973_wm8753_setup = {
- .i2c_bus = 0,
- .i2c_address = 0x1a,
-};
-
static struct snd_soc_device neo1973_snd_devdata = {
.card = &neo1973,
.codec_dev = &soc_codec_dev_wm8753,
- .codec_data = &neo1973_wm8753_setup,
};
static int lm4857_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
i2c = client;
@@ -609,7 +592,7 @@
static int lm4857_i2c_remove(struct i2c_client *client)
{
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
i2c = NULL;
@@ -620,7 +603,7 @@
static int lm4857_suspend(struct i2c_client *dev, pm_message_t state)
{
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
dev_dbg(&dev->dev, "lm4857_suspend\n");
lm4857_state = lm4857_regs[LM4857_CTRL] & 0xf;
@@ -633,7 +616,7 @@
static int lm4857_resume(struct i2c_client *dev)
{
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
if (lm4857_state) {
lm4857_regs[LM4857_CTRL] |= (lm4857_state & 0x0f);
@@ -644,7 +627,7 @@
static void lm4857_shutdown(struct i2c_client *dev)
{
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
dev_dbg(&dev->dev, "lm4857_shutdown\n");
lm4857_regs[LM4857_CTRL] &= 0xf0;
@@ -675,7 +658,7 @@
{
int ret;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
if (!machine_is_neo1973_gta01()) {
printk(KERN_INFO
@@ -706,7 +689,7 @@
static void __exit neo1973_exit(void)
{
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
i2c_del_driver(&lm4857_i2c_driver);
platform_device_unregister(neo1973_snd_device);
diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c
new file mode 100644
index 0000000..295a4c9
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c-i2s-v2.c
@@ -0,0 +1,638 @@
+/* sound/soc/s3c24xx/s3c-i2c-v2.c
+ *
+ * ALSA Soc Audio Layer - I2S core for newer Samsung SoCs.
+ *
+ * Copyright (c) 2006 Wolfson Microelectronics PLC.
+ * Graeme Gregory graeme.gregory@wolfsonmicro.com
+ * linux@wolfsonmicro.com
+ *
+ * Copyright (c) 2008, 2007, 2004-2005 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <plat/regs-s3c2412-iis.h>
+
+#include <plat/audio.h>
+#include <mach/dma.h>
+
+#include "s3c-i2s-v2.h"
+
+#define S3C2412_I2S_DEBUG_CON 0
+
+static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai)
+{
+ return cpu_dai->private_data;
+}
+
+#define bit_set(v, b) (((v) & (b)) ? 1 : 0)
+
+#if S3C2412_I2S_DEBUG_CON
+static void dbg_showcon(const char *fn, u32 con)
+{
+ printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn,
+ bit_set(con, S3C2412_IISCON_LRINDEX),
+ bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY),
+ bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY),
+ bit_set(con, S3C2412_IISCON_TXFIFO_FULL),
+ bit_set(con, S3C2412_IISCON_RXFIFO_FULL));
+
+ printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n",
+ fn,
+ bit_set(con, S3C2412_IISCON_TXDMA_PAUSE),
+ bit_set(con, S3C2412_IISCON_RXDMA_PAUSE),
+ bit_set(con, S3C2412_IISCON_TXCH_PAUSE),
+ bit_set(con, S3C2412_IISCON_RXCH_PAUSE));
+ printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn,
+ bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE),
+ bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE),
+ bit_set(con, S3C2412_IISCON_IIS_ACTIVE));
+}
+#else
+static inline void dbg_showcon(const char *fn, u32 con)
+{
+}
+#endif
+
+
+/* Turn on or off the transmission path. */
+void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
+{
+ void __iomem *regs = i2s->regs;
+ u32 fic, con, mod;
+
+ pr_debug("%s(%d)\n", __func__, on);
+
+ fic = readl(regs + S3C2412_IISFIC);
+ con = readl(regs + S3C2412_IISCON);
+ mod = readl(regs + S3C2412_IISMOD);
+
+ pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
+
+ if (on) {
+ con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
+ con &= ~S3C2412_IISCON_TXDMA_PAUSE;
+ con &= ~S3C2412_IISCON_TXCH_PAUSE;
+
+ switch (mod & S3C2412_IISMOD_MODE_MASK) {
+ case S3C2412_IISMOD_MODE_TXONLY:
+ case S3C2412_IISMOD_MODE_TXRX:
+ /* do nothing, we are in the right mode */
+ break;
+
+ case S3C2412_IISMOD_MODE_RXONLY:
+ mod &= ~S3C2412_IISMOD_MODE_MASK;
+ mod |= S3C2412_IISMOD_MODE_TXRX;
+ break;
+
+ default:
+ dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n");
+ }
+
+ writel(con, regs + S3C2412_IISCON);
+ writel(mod, regs + S3C2412_IISMOD);
+ } else {
+ /* Note, we do not have any indication that the FIFO problems
+ * tha the S3C2410/2440 had apply here, so we should be able
+ * to disable the DMA and TX without resetting the FIFOS.
+ */
+
+ con |= S3C2412_IISCON_TXDMA_PAUSE;
+ con |= S3C2412_IISCON_TXCH_PAUSE;
+ con &= ~S3C2412_IISCON_TXDMA_ACTIVE;
+
+ switch (mod & S3C2412_IISMOD_MODE_MASK) {
+ case S3C2412_IISMOD_MODE_TXRX:
+ mod &= ~S3C2412_IISMOD_MODE_MASK;
+ mod |= S3C2412_IISMOD_MODE_RXONLY;
+ break;
+
+ case S3C2412_IISMOD_MODE_TXONLY:
+ mod &= ~S3C2412_IISMOD_MODE_MASK;
+ con &= ~S3C2412_IISCON_IIS_ACTIVE;
+ break;
+
+ default:
+ dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n");
+ }
+
+ writel(mod, regs + S3C2412_IISMOD);
+ writel(con, regs + S3C2412_IISCON);
+ }
+
+ fic = readl(regs + S3C2412_IISFIC);
+ dbg_showcon(__func__, con);
+ pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
+}
+EXPORT_SYMBOL_GPL(s3c2412_snd_txctrl);
+
+void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
+{
+ void __iomem *regs = i2s->regs;
+ u32 fic, con, mod;
+
+ pr_debug("%s(%d)\n", __func__, on);
+
+ fic = readl(regs + S3C2412_IISFIC);
+ con = readl(regs + S3C2412_IISCON);
+ mod = readl(regs + S3C2412_IISMOD);
+
+ pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
+
+ if (on) {
+ con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
+ con &= ~S3C2412_IISCON_RXDMA_PAUSE;
+ con &= ~S3C2412_IISCON_RXCH_PAUSE;
+
+ switch (mod & S3C2412_IISMOD_MODE_MASK) {
+ case S3C2412_IISMOD_MODE_TXRX:
+ case S3C2412_IISMOD_MODE_RXONLY:
+ /* do nothing, we are in the right mode */
+ break;
+
+ case S3C2412_IISMOD_MODE_TXONLY:
+ mod &= ~S3C2412_IISMOD_MODE_MASK;
+ mod |= S3C2412_IISMOD_MODE_TXRX;
+ break;
+
+ default:
+ dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
+ }
+
+ writel(mod, regs + S3C2412_IISMOD);
+ writel(con, regs + S3C2412_IISCON);
+ } else {
+ /* See txctrl notes on FIFOs. */
+
+ con &= ~S3C2412_IISCON_RXDMA_ACTIVE;
+ con |= S3C2412_IISCON_RXDMA_PAUSE;
+ con |= S3C2412_IISCON_RXCH_PAUSE;
+
+ switch (mod & S3C2412_IISMOD_MODE_MASK) {
+ case S3C2412_IISMOD_MODE_RXONLY:
+ con &= ~S3C2412_IISCON_IIS_ACTIVE;
+ mod &= ~S3C2412_IISMOD_MODE_MASK;
+ break;
+
+ case S3C2412_IISMOD_MODE_TXRX:
+ mod &= ~S3C2412_IISMOD_MODE_MASK;
+ mod |= S3C2412_IISMOD_MODE_TXONLY;
+ break;
+
+ default:
+ dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
+ }
+
+ writel(con, regs + S3C2412_IISCON);
+ writel(mod, regs + S3C2412_IISMOD);
+ }
+
+ fic = readl(regs + S3C2412_IISFIC);
+ pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
+}
+EXPORT_SYMBOL_GPL(s3c2412_snd_rxctrl);
+
+/*
+ * Wait for the LR signal to allow synchronisation to the L/R clock
+ * from the codec. May only be needed for slave mode.
+ */
+static int s3c2412_snd_lrsync(struct s3c_i2sv2_info *i2s)
+{
+ u32 iiscon;
+ unsigned long timeout = jiffies + msecs_to_jiffies(5);
+
+ pr_debug("Entered %s\n", __func__);
+
+ while (1) {
+ iiscon = readl(i2s->regs + S3C2412_IISCON);
+ if (iiscon & S3C2412_IISCON_LRINDEX)
+ break;
+
+ if (timeout < jiffies) {
+ printk(KERN_ERR "%s: timeout\n", __func__);
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Set S3C2412 I2S DAI format
+ */
+static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
+ u32 iismod;
+
+ pr_debug("Entered %s\n", __func__);
+
+ iismod = readl(i2s->regs + S3C2412_IISMOD);
+ pr_debug("hw_params r: IISMOD: %x \n", iismod);
+
+#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413)
+#define IISMOD_MASTER_MASK S3C2412_IISMOD_MASTER_MASK
+#define IISMOD_SLAVE S3C2412_IISMOD_SLAVE
+#define IISMOD_MASTER S3C2412_IISMOD_MASTER_INTERNAL
+#endif
+
+#if defined(CONFIG_PLAT_S3C64XX)
+/* From Rev1.1 datasheet, we have two master and two slave modes:
+ * IMS[11:10]:
+ * 00 = master mode, fed from PCLK
+ * 01 = master mode, fed from CLKAUDIO
+ * 10 = slave mode, using PCLK
+ * 11 = slave mode, using I2SCLK
+ */
+#define IISMOD_MASTER_MASK (1 << 11)
+#define IISMOD_SLAVE (1 << 11)
+#define IISMOD_MASTER (0x0)
+#endif
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ i2s->master = 0;
+ iismod &= ~IISMOD_MASTER_MASK;
+ iismod |= IISMOD_SLAVE;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ i2s->master = 1;
+ iismod &= ~IISMOD_MASTER_MASK;
+ iismod |= IISMOD_MASTER;
+ break;
+ default:
+ pr_debug("unknwon master/slave format\n");
+ return -EINVAL;
+ }
+
+ iismod &= ~S3C2412_IISMOD_SDF_MASK;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ iismod |= S3C2412_IISMOD_SDF_MSB;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iismod |= S3C2412_IISMOD_SDF_LSB;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ iismod |= S3C2412_IISMOD_SDF_IIS;
+ break;
+ default:
+ pr_debug("Unknown data format\n");
+ return -EINVAL;
+ }
+
+ writel(iismod, i2s->regs + S3C2412_IISMOD);
+ pr_debug("hw_params w: IISMOD: %x \n", iismod);
+ return 0;
+}
+
+static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *socdai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *dai = rtd->dai;
+ struct s3c_i2sv2_info *i2s = to_info(dai->cpu_dai);
+ u32 iismod;
+
+ pr_debug("Entered %s\n", __func__);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dai->cpu_dai->dma_data = i2s->dma_playback;
+ else
+ dai->cpu_dai->dma_data = i2s->dma_capture;
+
+ /* Working copies of register */
+ iismod = readl(i2s->regs + S3C2412_IISMOD);
+ pr_debug("%s: r: IISMOD: %x\n", __func__, iismod);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ iismod |= S3C2412_IISMOD_8BIT;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ iismod &= ~S3C2412_IISMOD_8BIT;
+ break;
+ }
+
+ writel(iismod, i2s->regs + S3C2412_IISMOD);
+ pr_debug("%s: w: IISMOD: %x\n", __func__, iismod);
+ return 0;
+}
+
+static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct s3c_i2sv2_info *i2s = to_info(rtd->dai->cpu_dai);
+ int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+ unsigned long irqs;
+ int ret = 0;
+
+ pr_debug("Entered %s\n", __func__);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ /* On start, ensure that the FIFOs are cleared and reset. */
+
+ writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH,
+ i2s->regs + S3C2412_IISFIC);
+
+ /* clear again, just in case */
+ writel(0x0, i2s->regs + S3C2412_IISFIC);
+
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (!i2s->master) {
+ ret = s3c2412_snd_lrsync(i2s);
+ if (ret)
+ goto exit_err;
+ }
+
+ local_irq_save(irqs);
+
+ if (capture)
+ s3c2412_snd_rxctrl(i2s, 1);
+ else
+ s3c2412_snd_txctrl(i2s, 1);
+
+ local_irq_restore(irqs);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ local_irq_save(irqs);
+
+ if (capture)
+ s3c2412_snd_rxctrl(i2s, 0);
+ else
+ s3c2412_snd_txctrl(i2s, 0);
+
+ local_irq_restore(irqs);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+exit_err:
+ return ret;
+}
+
+/*
+ * Set S3C2412 Clock dividers
+ */
+static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
+ int div_id, int div)
+{
+ struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
+ u32 reg;
+
+ pr_debug("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div);
+
+ switch (div_id) {
+ case S3C_I2SV2_DIV_BCLK:
+ reg = readl(i2s->regs + S3C2412_IISMOD);
+ reg &= ~S3C2412_IISMOD_BCLK_MASK;
+ writel(reg | div, i2s->regs + S3C2412_IISMOD);
+
+ pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
+ break;
+
+ case S3C_I2SV2_DIV_RCLK:
+ if (div > 3) {
+ /* convert value to bit field */
+
+ switch (div) {
+ case 256:
+ div = S3C2412_IISMOD_RCLK_256FS;
+ break;
+
+ case 384:
+ div = S3C2412_IISMOD_RCLK_384FS;
+ break;
+
+ case 512:
+ div = S3C2412_IISMOD_RCLK_512FS;
+ break;
+
+ case 768:
+ div = S3C2412_IISMOD_RCLK_768FS;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ }
+
+ reg = readl(i2s->regs + S3C2412_IISMOD);
+ reg &= ~S3C2412_IISMOD_RCLK_MASK;
+ writel(reg | div, i2s->regs + S3C2412_IISMOD);
+ pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
+ break;
+
+ case S3C_I2SV2_DIV_PRESCALER:
+ if (div >= 0) {
+ writel((div << 8) | S3C2412_IISPSR_PSREN,
+ i2s->regs + S3C2412_IISPSR);
+ } else {
+ writel(0x0, i2s->regs + S3C2412_IISPSR);
+ }
+ pr_debug("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR));
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* default table of all avaialable root fs divisors */
+static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 };
+
+int s3c2412_iis_calc_rate(struct s3c_i2sv2_rate_calc *info,
+ unsigned int *fstab,
+ unsigned int rate, struct clk *clk)
+{
+ unsigned long clkrate = clk_get_rate(clk);
+ unsigned int div;
+ unsigned int fsclk;
+ unsigned int actual;
+ unsigned int fs;
+ unsigned int fsdiv;
+ signed int deviation = 0;
+ unsigned int best_fs = 0;
+ unsigned int best_div = 0;
+ unsigned int best_rate = 0;
+ unsigned int best_deviation = INT_MAX;
+
+ if (fstab == NULL)
+ fstab = iis_fs_tab;
+
+ for (fs = 0; fs < ARRAY_SIZE(iis_fs_tab); fs++) {
+ fsdiv = iis_fs_tab[fs];
+
+ fsclk = clkrate / fsdiv;
+ div = fsclk / rate;
+
+ if ((fsclk % rate) > (rate / 2))
+ div++;
+
+ if (div <= 1)
+ continue;
+
+ actual = clkrate / (fsdiv * div);
+ deviation = actual - rate;
+
+ printk(KERN_DEBUG "%dfs: div %d => result %d, deviation %d\n",
+ fsdiv, div, actual, deviation);
+
+ deviation = abs(deviation);
+
+ if (deviation < best_deviation) {
+ best_fs = fsdiv;
+ best_div = div;
+ best_rate = actual;
+ best_deviation = deviation;
+ }
+
+ if (deviation == 0)
+ break;
+ }
+
+ printk(KERN_DEBUG "best: fs=%d, div=%d, rate=%d\n",
+ best_fs, best_div, best_rate);
+
+ info->fs_div = best_fs;
+ info->clk_div = best_div;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate);
+
+int s3c_i2sv2_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai,
+ struct s3c_i2sv2_info *i2s,
+ unsigned long base)
+{
+ struct device *dev = &pdev->dev;
+
+ i2s->dev = dev;
+
+ /* record our i2s structure for later use in the callbacks */
+ dai->private_data = i2s;
+
+ i2s->regs = ioremap(base, 0x100);
+ if (i2s->regs == NULL) {
+ dev_err(dev, "cannot ioremap registers\n");
+ return -ENXIO;
+ }
+
+ i2s->iis_pclk = clk_get(dev, "iis");
+ if (i2s->iis_pclk == NULL) {
+ dev_err(dev, "failed to get iis_clock\n");
+ iounmap(i2s->regs);
+ return -ENOENT;
+ }
+
+ clk_enable(i2s->iis_pclk);
+
+ s3c2412_snd_txctrl(i2s, 0);
+ s3c2412_snd_rxctrl(i2s, 0);
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(s3c_i2sv2_probe);
+
+#ifdef CONFIG_PM
+static int s3c2412_i2s_suspend(struct snd_soc_dai *dai)
+{
+ struct s3c_i2sv2_info *i2s = to_info(dai);
+ u32 iismod;
+
+ if (dai->active) {
+ i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
+ i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
+ i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
+
+ /* some basic suspend checks */
+
+ iismod = readl(i2s->regs + S3C2412_IISMOD);
+
+ if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
+ pr_warning("%s: RXDMA active?\n", __func__);
+
+ if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
+ pr_warning("%s: TXDMA active?\n", __func__);
+
+ if (iismod & S3C2412_IISCON_IIS_ACTIVE)
+ pr_warning("%s: IIS active\n", __func__);
+ }
+
+ return 0;
+}
+
+static int s3c2412_i2s_resume(struct snd_soc_dai *dai)
+{
+ struct s3c_i2sv2_info *i2s = to_info(dai);
+
+ pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n",
+ dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
+
+ if (dai->active) {
+ writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
+ writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
+ writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
+
+ writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
+ i2s->regs + S3C2412_IISFIC);
+
+ ndelay(250);
+ writel(0x0, i2s->regs + S3C2412_IISFIC);
+ }
+
+ return 0;
+}
+#else
+#define s3c2412_i2s_suspend NULL
+#define s3c2412_i2s_resume NULL
+#endif
+
+int s3c_i2sv2_register_dai(struct snd_soc_dai *dai)
+{
+ dai->ops.trigger = s3c2412_i2s_trigger;
+ dai->ops.hw_params = s3c2412_i2s_hw_params;
+ dai->ops.set_fmt = s3c2412_i2s_set_fmt;
+ dai->ops.set_clkdiv = s3c2412_i2s_set_clkdiv;
+
+ dai->suspend = s3c2412_i2s_suspend;
+ dai->resume = s3c2412_i2s_resume;
+
+ return snd_soc_register_dai(dai);
+}
+
+EXPORT_SYMBOL_GPL(s3c_i2sv2_register_dai);
diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.h b/sound/soc/s3c24xx/s3c-i2s-v2.h
new file mode 100644
index 0000000..f66854a
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c-i2s-v2.h
@@ -0,0 +1,90 @@
+/* sound/soc/s3c24xx/s3c-i2s-v2.h
+ *
+ * ALSA Soc Audio Layer - S3C_I2SV2 I2S driver
+ *
+ * Copyright (c) 2007 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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 code is the core support for the I2S block found in a number of
+ * Samsung SoC devices which is unofficially named I2S-V2. Currently the
+ * S3C2412 and the S3C64XX series use this block to provide 1 or 2 I2S
+ * channels via configurable GPIO.
+ */
+
+#ifndef __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H
+#define __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H __FILE__
+
+#define S3C_I2SV2_DIV_BCLK (1)
+#define S3C_I2SV2_DIV_RCLK (2)
+#define S3C_I2SV2_DIV_PRESCALER (3)
+
+/**
+ * struct s3c_i2sv2_info - S3C I2S-V2 information
+ * @dev: The parent device passed to use from the probe.
+ * @regs: The pointer to the device registe block.
+ * @master: True if the I2S core is the I2S bit clock master.
+ * @dma_playback: DMA information for playback channel.
+ * @dma_capture: DMA information for capture channel.
+ * @suspend_iismod: PM save for the IISMOD register.
+ * @suspend_iiscon: PM save for the IISCON register.
+ * @suspend_iispsr: PM save for the IISPSR register.
+ *
+ * This is the private codec state for the hardware associated with an
+ * I2S channel such as the register mappings and clock sources.
+ */
+struct s3c_i2sv2_info {
+ struct device *dev;
+ void __iomem *regs;
+
+ struct clk *iis_pclk;
+ struct clk *iis_cclk;
+ struct clk *iis_clk;
+
+ unsigned char master;
+
+ struct s3c24xx_pcm_dma_params *dma_playback;
+ struct s3c24xx_pcm_dma_params *dma_capture;
+
+ u32 suspend_iismod;
+ u32 suspend_iiscon;
+ u32 suspend_iispsr;
+};
+
+struct s3c_i2sv2_rate_calc {
+ unsigned int clk_div; /* for prescaler */
+ unsigned int fs_div; /* for root frame clock */
+};
+
+extern int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info,
+ unsigned int *fstab,
+ unsigned int rate, struct clk *clk);
+
+/**
+ * s3c_i2sv2_probe - probe for i2s device helper
+ * @pdev: The platform device supplied to the original probe.
+ * @dai: The ASoC DAI structure supplied to the original probe.
+ * @i2s: Our local i2s structure to fill in.
+ * @base: The base address for the registers.
+ */
+extern int s3c_i2sv2_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai,
+ struct s3c_i2sv2_info *i2s,
+ unsigned long base);
+
+/**
+ * s3c_i2sv2_register_dai - register dai with soc core
+ * @dai: The snd_soc_dai structure to register
+ *
+ * Fill in any missing fields and then register the given dai with the
+ * soc core.
+ */
+extern int s3c_i2sv2_register_dai(struct snd_soc_dai *dai);
+
+#endif /* __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H */
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c
index f3fc0ab..1ca3cda 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.c
+++ b/sound/soc/s3c24xx/s3c2412-i2s.c
@@ -22,6 +22,7 @@
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/kernel.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -30,26 +31,16 @@
#include <sound/soc.h>
#include <mach/hardware.h>
-#include <linux/io.h>
-#include <asm/dma.h>
+#include <plat/regs-s3c2412-iis.h>
-#include <asm/plat-s3c24xx/regs-s3c2412-iis.h>
-
-#include <mach/regs-gpio.h>
-#include <mach/audio.h>
+#include <plat/regs-gpio.h>
+#include <plat/audio.h>
#include <mach/dma.h>
#include "s3c24xx-pcm.h"
#include "s3c2412-i2s.h"
#define S3C2412_I2S_DEBUG 0
-#define S3C2412_I2S_DEBUG_CON 0
-
-#if S3C2412_I2S_DEBUG
-#define DBG(x...) printk(KERN_INFO x)
-#else
-#define DBG(x...) do { } while (0)
-#endif
static struct s3c2410_dma_client s3c2412_dma_client_out = {
.name = "I2S PCM Stereo out"
@@ -73,431 +64,7 @@
.dma_size = 4,
};
-struct s3c2412_i2s_info {
- struct device *dev;
- void __iomem *regs;
- struct clk *iis_clk;
- struct clk *iis_pclk;
- struct clk *iis_cclk;
-
- u32 suspend_iismod;
- u32 suspend_iiscon;
- u32 suspend_iispsr;
-};
-
-static struct s3c2412_i2s_info s3c2412_i2s;
-
-#define bit_set(v, b) (((v) & (b)) ? 1 : 0)
-
-#if S3C2412_I2S_DEBUG_CON
-static void dbg_showcon(const char *fn, u32 con)
-{
- printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn,
- bit_set(con, S3C2412_IISCON_LRINDEX),
- bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY),
- bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY),
- bit_set(con, S3C2412_IISCON_TXFIFO_FULL),
- bit_set(con, S3C2412_IISCON_RXFIFO_FULL));
-
- printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n",
- fn,
- bit_set(con, S3C2412_IISCON_TXDMA_PAUSE),
- bit_set(con, S3C2412_IISCON_RXDMA_PAUSE),
- bit_set(con, S3C2412_IISCON_TXCH_PAUSE),
- bit_set(con, S3C2412_IISCON_RXCH_PAUSE));
- printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn,
- bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE),
- bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE),
- bit_set(con, S3C2412_IISCON_IIS_ACTIVE));
-}
-#else
-static inline void dbg_showcon(const char *fn, u32 con)
-{
-}
-#endif
-
-/* Turn on or off the transmission path. */
-static void s3c2412_snd_txctrl(int on)
-{
- struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
- void __iomem *regs = i2s->regs;
- u32 fic, con, mod;
-
- DBG("%s(%d)\n", __func__, on);
-
- fic = readl(regs + S3C2412_IISFIC);
- con = readl(regs + S3C2412_IISCON);
- mod = readl(regs + S3C2412_IISMOD);
-
- DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
-
- if (on) {
- con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
- con &= ~S3C2412_IISCON_TXDMA_PAUSE;
- con &= ~S3C2412_IISCON_TXCH_PAUSE;
-
- switch (mod & S3C2412_IISMOD_MODE_MASK) {
- case S3C2412_IISMOD_MODE_TXONLY:
- case S3C2412_IISMOD_MODE_TXRX:
- /* do nothing, we are in the right mode */
- break;
-
- case S3C2412_IISMOD_MODE_RXONLY:
- mod &= ~S3C2412_IISMOD_MODE_MASK;
- mod |= S3C2412_IISMOD_MODE_TXRX;
- break;
-
- default:
- dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n");
- }
-
- writel(con, regs + S3C2412_IISCON);
- writel(mod, regs + S3C2412_IISMOD);
- } else {
- /* Note, we do not have any indication that the FIFO problems
- * tha the S3C2410/2440 had apply here, so we should be able
- * to disable the DMA and TX without resetting the FIFOS.
- */
-
- con |= S3C2412_IISCON_TXDMA_PAUSE;
- con |= S3C2412_IISCON_TXCH_PAUSE;
- con &= ~S3C2412_IISCON_TXDMA_ACTIVE;
-
- switch (mod & S3C2412_IISMOD_MODE_MASK) {
- case S3C2412_IISMOD_MODE_TXRX:
- mod &= ~S3C2412_IISMOD_MODE_MASK;
- mod |= S3C2412_IISMOD_MODE_RXONLY;
- break;
-
- case S3C2412_IISMOD_MODE_TXONLY:
- mod &= ~S3C2412_IISMOD_MODE_MASK;
- con &= ~S3C2412_IISCON_IIS_ACTIVE;
- break;
-
- default:
- dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n");
- }
-
- writel(mod, regs + S3C2412_IISMOD);
- writel(con, regs + S3C2412_IISCON);
- }
-
- fic = readl(regs + S3C2412_IISFIC);
- dbg_showcon(__func__, con);
- DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
-}
-
-static void s3c2412_snd_rxctrl(int on)
-{
- struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
- void __iomem *regs = i2s->regs;
- u32 fic, con, mod;
-
- DBG("%s(%d)\n", __func__, on);
-
- fic = readl(regs + S3C2412_IISFIC);
- con = readl(regs + S3C2412_IISCON);
- mod = readl(regs + S3C2412_IISMOD);
-
- DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
-
- if (on) {
- con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
- con &= ~S3C2412_IISCON_RXDMA_PAUSE;
- con &= ~S3C2412_IISCON_RXCH_PAUSE;
-
- switch (mod & S3C2412_IISMOD_MODE_MASK) {
- case S3C2412_IISMOD_MODE_TXRX:
- case S3C2412_IISMOD_MODE_RXONLY:
- /* do nothing, we are in the right mode */
- break;
-
- case S3C2412_IISMOD_MODE_TXONLY:
- mod &= ~S3C2412_IISMOD_MODE_MASK;
- mod |= S3C2412_IISMOD_MODE_TXRX;
- break;
-
- default:
- dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
- }
-
- writel(mod, regs + S3C2412_IISMOD);
- writel(con, regs + S3C2412_IISCON);
- } else {
- /* See txctrl notes on FIFOs. */
-
- con &= ~S3C2412_IISCON_RXDMA_ACTIVE;
- con |= S3C2412_IISCON_RXDMA_PAUSE;
- con |= S3C2412_IISCON_RXCH_PAUSE;
-
- switch (mod & S3C2412_IISMOD_MODE_MASK) {
- case S3C2412_IISMOD_MODE_RXONLY:
- con &= ~S3C2412_IISCON_IIS_ACTIVE;
- mod &= ~S3C2412_IISMOD_MODE_MASK;
- break;
-
- case S3C2412_IISMOD_MODE_TXRX:
- mod &= ~S3C2412_IISMOD_MODE_MASK;
- mod |= S3C2412_IISMOD_MODE_TXONLY;
- break;
-
- default:
- dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
- }
-
- writel(con, regs + S3C2412_IISCON);
- writel(mod, regs + S3C2412_IISMOD);
- }
-
- fic = readl(regs + S3C2412_IISFIC);
- DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
-}
-
-
-/*
- * Wait for the LR signal to allow synchronisation to the L/R clock
- * from the codec. May only be needed for slave mode.
- */
-static int s3c2412_snd_lrsync(void)
-{
- u32 iiscon;
- unsigned long timeout = jiffies + msecs_to_jiffies(5);
-
- DBG("Entered %s\n", __func__);
-
- while (1) {
- iiscon = readl(s3c2412_i2s.regs + S3C2412_IISCON);
- if (iiscon & S3C2412_IISCON_LRINDEX)
- break;
-
- if (timeout < jiffies) {
- printk(KERN_ERR "%s: timeout\n", __func__);
- return -ETIMEDOUT;
- }
- }
-
- return 0;
-}
-
-/*
- * Check whether CPU is the master or slave
- */
-static inline int s3c2412_snd_is_clkmaster(void)
-{
- u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
-
- DBG("Entered %s\n", __func__);
-
- iismod &= S3C2412_IISMOD_MASTER_MASK;
- return !(iismod == S3C2412_IISMOD_SLAVE);
-}
-
-/*
- * Set S3C2412 I2S DAI format
- */
-static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- u32 iismod;
-
-
- DBG("Entered %s\n", __func__);
-
- iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
- DBG("hw_params r: IISMOD: %x \n", iismod);
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- iismod &= ~S3C2412_IISMOD_MASTER_MASK;
- iismod |= S3C2412_IISMOD_SLAVE;
- break;
- case SND_SOC_DAIFMT_CBS_CFS:
- iismod &= ~S3C2412_IISMOD_MASTER_MASK;
- iismod |= S3C2412_IISMOD_MASTER_INTERNAL;
- break;
- default:
- DBG("unknwon master/slave format\n");
- return -EINVAL;
- }
-
- iismod &= ~S3C2412_IISMOD_SDF_MASK;
-
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_RIGHT_J:
- iismod |= S3C2412_IISMOD_SDF_MSB;
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- iismod |= S3C2412_IISMOD_SDF_LSB;
- break;
- case SND_SOC_DAIFMT_I2S:
- iismod |= S3C2412_IISMOD_SDF_IIS;
- break;
- default:
- DBG("Unknown data format\n");
- return -EINVAL;
- }
-
- writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD);
- DBG("hw_params w: IISMOD: %x \n", iismod);
- return 0;
-}
-
-static int s3c2412_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;
- u32 iismod;
-
- DBG("Entered %s\n", __func__);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_out;
- else
- rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_in;
-
- /* Working copies of register */
- iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
- DBG("%s: r: IISMOD: %x\n", __func__, iismod);
-
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S8:
- iismod |= S3C2412_IISMOD_8BIT;
- break;
- case SNDRV_PCM_FORMAT_S16_LE:
- iismod &= ~S3C2412_IISMOD_8BIT;
- break;
- }
-
- writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD);
- DBG("%s: w: IISMOD: %x\n", __func__, iismod);
- return 0;
-}
-
-static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
- unsigned long irqs;
- int ret = 0;
-
- DBG("Entered %s\n", __func__);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- /* On start, ensure that the FIFOs are cleared and reset. */
-
- writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH,
- s3c2412_i2s.regs + S3C2412_IISFIC);
-
- /* clear again, just in case */
- writel(0x0, s3c2412_i2s.regs + S3C2412_IISFIC);
-
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (!s3c2412_snd_is_clkmaster()) {
- ret = s3c2412_snd_lrsync();
- if (ret)
- goto exit_err;
- }
-
- local_irq_save(irqs);
-
- if (capture)
- s3c2412_snd_rxctrl(1);
- else
- s3c2412_snd_txctrl(1);
-
- local_irq_restore(irqs);
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- local_irq_save(irqs);
-
- if (capture)
- s3c2412_snd_rxctrl(0);
- else
- s3c2412_snd_txctrl(0);
-
- local_irq_restore(irqs);
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
-exit_err:
- return ret;
-}
-
-/* default table of all avaialable root fs divisors */
-static unsigned int s3c2412_iis_fs[] = { 256, 512, 384, 768, 0 };
-
-int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info,
- unsigned int *fstab,
- unsigned int rate, struct clk *clk)
-{
- unsigned long clkrate = clk_get_rate(clk);
- unsigned int div;
- unsigned int fsclk;
- unsigned int actual;
- unsigned int fs;
- unsigned int fsdiv;
- signed int deviation = 0;
- unsigned int best_fs = 0;
- unsigned int best_div = 0;
- unsigned int best_rate = 0;
- unsigned int best_deviation = INT_MAX;
-
-
- if (fstab == NULL)
- fstab = s3c2412_iis_fs;
-
- for (fs = 0;; fs++) {
- fsdiv = s3c2412_iis_fs[fs];
-
- if (fsdiv == 0)
- break;
-
- fsclk = clkrate / fsdiv;
- div = fsclk / rate;
-
- if ((fsclk % rate) > (rate / 2))
- div++;
-
- if (div <= 1)
- continue;
-
- actual = clkrate / (fsdiv * div);
- deviation = actual - rate;
-
- printk(KERN_DEBUG "%dfs: div %d => result %d, deviation %d\n",
- fsdiv, div, actual, deviation);
-
- deviation = abs(deviation);
-
- if (deviation < best_deviation) {
- best_fs = fsdiv;
- best_div = div;
- best_rate = actual;
- best_deviation = deviation;
- }
-
- if (deviation == 0)
- break;
- }
-
- printk(KERN_DEBUG "best: fs=%d, div=%d, rate=%d\n",
- best_fs, best_div, best_rate);
-
- info->fs_div = best_fs;
- info->clk_div = best_div;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate);
+static struct s3c_i2sv2_info s3c2412_i2s;
/*
* Set S3C2412 Clock source
@@ -507,15 +74,17 @@
{
u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
- DBG("%s(%p, %d, %u, %d)\n", __func__, cpu_dai, clk_id,
+ pr_debug("%s(%p, %d, %u, %d)\n", __func__, cpu_dai, clk_id,
freq, dir);
switch (clk_id) {
case S3C2412_CLKSRC_PCLK:
+ s3c2412_i2s.master = 1;
iismod &= ~S3C2412_IISMOD_MASTER_MASK;
iismod |= S3C2412_IISMOD_MASTER_INTERNAL;
break;
case S3C2412_CLKSRC_I2SCLK:
+ s3c2412_i2s.master = 0;
iismod &= ~S3C2412_IISMOD_MASTER_MASK;
iismod |= S3C2412_IISMOD_MASTER_EXTERNAL;
break;
@@ -527,74 +96,6 @@
return 0;
}
-/*
- * Set S3C2412 Clock dividers
- */
-static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
- int div_id, int div)
-{
- struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
- u32 reg;
-
- DBG("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div);
-
- switch (div_id) {
- case S3C2412_DIV_BCLK:
- reg = readl(i2s->regs + S3C2412_IISMOD);
- reg &= ~S3C2412_IISMOD_BCLK_MASK;
- writel(reg | div, i2s->regs + S3C2412_IISMOD);
-
- DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
- break;
-
- case S3C2412_DIV_RCLK:
- if (div > 3) {
- /* convert value to bit field */
-
- switch (div) {
- case 256:
- div = S3C2412_IISMOD_RCLK_256FS;
- break;
-
- case 384:
- div = S3C2412_IISMOD_RCLK_384FS;
- break;
-
- case 512:
- div = S3C2412_IISMOD_RCLK_512FS;
- break;
-
- case 768:
- div = S3C2412_IISMOD_RCLK_768FS;
- break;
-
- default:
- return -EINVAL;
- }
- }
-
- reg = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
- reg &= ~S3C2412_IISMOD_RCLK_MASK;
- writel(reg | div, i2s->regs + S3C2412_IISMOD);
- DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
- break;
-
- case S3C2412_DIV_PRESCALER:
- if (div >= 0) {
- writel((div << 8) | S3C2412_IISPSR_PSREN,
- i2s->regs + S3C2412_IISPSR);
- } else {
- writel(0x0, i2s->regs + S3C2412_IISPSR);
- }
- DBG("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR));
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
struct clk *s3c2412_get_iisclk(void)
{
@@ -606,34 +107,30 @@
static int s3c2412_i2s_probe(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
- DBG("Entered %s\n", __func__);
+ int ret;
- s3c2412_i2s.dev = &pdev->dev;
+ pr_debug("Entered %s\n", __func__);
- s3c2412_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100);
- if (s3c2412_i2s.regs == NULL)
- return -ENXIO;
+ ret = s3c_i2sv2_probe(pdev, dai, &s3c2412_i2s, S3C2410_PA_IIS);
+ if (ret)
+ return ret;
- s3c2412_i2s.iis_pclk = clk_get(&pdev->dev, "iis");
- if (s3c2412_i2s.iis_pclk == NULL) {
- DBG("failed to get iis_clock\n");
- iounmap(s3c2412_i2s.regs);
- return -ENODEV;
- }
+ s3c2412_i2s.dma_capture = &s3c2412_i2s_pcm_stereo_in;
+ s3c2412_i2s.dma_playback = &s3c2412_i2s_pcm_stereo_out;
s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk");
if (s3c2412_i2s.iis_cclk == NULL) {
- DBG("failed to get i2sclk clock\n");
+ pr_debug("failed to get i2sclk clock\n");
iounmap(s3c2412_i2s.regs);
return -ENODEV;
}
- clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll"));
+ /* Set MPLL as the source for IIS CLK */
- clk_enable(s3c2412_i2s.iis_pclk);
+ clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll"));
clk_enable(s3c2412_i2s.iis_cclk);
- s3c2412_i2s.iis_clk = s3c2412_i2s.iis_pclk;
+ s3c2412_i2s.iis_cclk = s3c2412_i2s.iis_pclk;
/* Configure the I2S pins in correct mode */
s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
@@ -642,78 +139,22 @@
s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
- s3c2412_snd_txctrl(0);
- s3c2412_snd_rxctrl(0);
-
return 0;
}
-#ifdef CONFIG_PM
-static int s3c2412_i2s_suspend(struct snd_soc_dai *dai)
-{
- struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
- u32 iismod;
-
- if (dai->active) {
- i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
- i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
- i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
-
- /* some basic suspend checks */
-
- iismod = readl(i2s->regs + S3C2412_IISMOD);
-
- if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
- pr_warning("%s: RXDMA active?\n", __func__);
-
- if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
- pr_warning("%s: TXDMA active?\n", __func__);
-
- if (iismod & S3C2412_IISCON_IIS_ACTIVE)
- pr_warning("%s: IIS active\n", __func__);
- }
-
- return 0;
-}
-
-static int s3c2412_i2s_resume(struct snd_soc_dai *dai)
-{
- struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
-
- pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n",
- dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
-
- if (dai->active) {
- writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
- writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
- writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
-
- writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
- i2s->regs + S3C2412_IISFIC);
-
- ndelay(250);
- writel(0x0, i2s->regs + S3C2412_IISFIC);
-
- }
-
- return 0;
-}
-#else
-#define s3c2412_i2s_suspend NULL
-#define s3c2412_i2s_resume NULL
-#endif /* CONFIG_PM */
-
#define S3C2412_I2S_RATES \
(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+static struct snd_soc_dai_ops s3c2412_i2s_dai_ops = {
+ .set_sysclk = s3c2412_i2s_set_sysclk,
+};
+
struct snd_soc_dai s3c2412_i2s_dai = {
- .name = "s3c2412-i2s",
- .id = 0,
- .probe = s3c2412_i2s_probe,
- .suspend = s3c2412_i2s_suspend,
- .resume = s3c2412_i2s_resume,
+ .name = "s3c2412-i2s",
+ .id = 0,
+ .probe = s3c2412_i2s_probe,
.playback = {
.channels_min = 2,
.channels_max = 2,
@@ -726,19 +167,13 @@
.rates = S3C2412_I2S_RATES,
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
},
- .ops = {
- .trigger = s3c2412_i2s_trigger,
- .hw_params = s3c2412_i2s_hw_params,
- .set_fmt = s3c2412_i2s_set_fmt,
- .set_clkdiv = s3c2412_i2s_set_clkdiv,
- .set_sysclk = s3c2412_i2s_set_sysclk,
- },
+ .ops = &s3c2412_i2s_dai_ops,
};
EXPORT_SYMBOL_GPL(s3c2412_i2s_dai);
static int __init s3c2412_i2s_init(void)
{
- return snd_soc_register_dai(&s3c2412_i2s_dai);
+ return s3c_i2sv2_register_dai(&s3c2412_i2s_dai);
}
module_init(s3c2412_i2s_init);
@@ -748,7 +183,6 @@
}
module_exit(s3c2412_i2s_exit);
-
/* Module information */
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
MODULE_DESCRIPTION("S3C2412 I2S SoC Interface");
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.h b/sound/soc/s3c24xx/s3c2412-i2s.h
index aac08a2..92848e5 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.h
+++ b/sound/soc/s3c24xx/s3c2412-i2s.h
@@ -15,9 +15,11 @@
#ifndef __SND_SOC_S3C24XX_S3C2412_I2S_H
#define __SND_SOC_S3C24XX_S3C2412_I2S_H __FILE__
-#define S3C2412_DIV_BCLK (1)
-#define S3C2412_DIV_RCLK (2)
-#define S3C2412_DIV_PRESCALER (3)
+#include "s3c-i2s-v2.h"
+
+#define S3C2412_DIV_BCLK S3C_I2SV2_DIV_BCLK
+#define S3C2412_DIV_RCLK S3C_I2SV2_DIV_RCLK
+#define S3C2412_DIV_PRESCALER S3C_I2SV2_DIV_PRESCALER
#define S3C2412_CLKSRC_PCLK (0)
#define S3C2412_CLKSRC_I2SCLK (1)
@@ -26,13 +28,4 @@
extern struct snd_soc_dai s3c2412_i2s_dai;
-struct s3c2412_rate_calc {
- unsigned int clk_div; /* for prescaler */
- unsigned int fs_div; /* for root frame clock */
-};
-
-extern int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info,
- unsigned int *fstab,
- unsigned int rate, struct clk *clk);
-
#endif /* __SND_SOC_S3C24XX_S3C2412_I2S_H */
diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c
index 5822d2d..3698f70 100644
--- a/sound/soc/s3c24xx/s3c2443-ac97.c
+++ b/sound/soc/s3c24xx/s3c2443-ac97.c
@@ -31,7 +31,7 @@
#include <plat/regs-ac97.h>
#include <mach/regs-gpio.h>
#include <mach/regs-clock.h>
-#include <mach/audio.h>
+#include <plat/audio.h>
#include <asm/dma.h>
#include <mach/dma.h>
@@ -355,6 +355,16 @@
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+static struct snd_soc_dai_ops s3c2443_ac97_dai_ops = {
+ .hw_params = s3c2443_ac97_hw_params,
+ .trigger = s3c2443_ac97_trigger,
+};
+
+static struct snd_soc_dai_ops s3c2443_ac97_mic_dai_ops = {
+ .hw_params = s3c2443_ac97_hw_mic_params,
+ .trigger = s3c2443_ac97_mic_trigger,
+};
+
struct snd_soc_dai s3c2443_ac97_dai[] = {
{
.name = "s3c2443-ac97",
@@ -374,9 +384,7 @@
.channels_max = 2,
.rates = s3c2443_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = {
- .hw_params = s3c2443_ac97_hw_params,
- .trigger = s3c2443_ac97_trigger},
+ .ops = &s3c2443_ac97_dai_ops,
},
{
.name = "pxa2xx-ac97-mic",
@@ -388,9 +396,7 @@
.channels_max = 1,
.rates = s3c2443_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = {
- .hw_params = s3c2443_ac97_hw_mic_params,
- .trigger = s3c2443_ac97_mic_trigger,},
+ .ops = &s3c2443_ac97_mic_dai_ops,
},
};
EXPORT_SYMBOL_GPL(s3c2443_ac97_dai);
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c
index 6f4d439..cc06696 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.c
@@ -4,7 +4,7 @@
* (c) 2006 Wolfson Microelectronics PLC.
* Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
*
- * (c) 2004-2005 Simtec Electronics
+ * Copyright 2004-2005 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
*
@@ -30,22 +30,15 @@
#include <mach/hardware.h>
#include <mach/regs-gpio.h>
#include <mach/regs-clock.h>
-#include <mach/audio.h>
+#include <plat/audio.h>
#include <asm/dma.h>
#include <mach/dma.h>
-#include <asm/plat-s3c24xx/regs-iis.h>
+#include <plat/regs-iis.h>
#include "s3c24xx-pcm.h"
#include "s3c24xx-i2s.h"
-#define S3C24XX_I2S_DEBUG 0
-#if S3C24XX_I2S_DEBUG
-#define DBG(x...) printk(KERN_DEBUG "s3c24xx-i2s: " x)
-#else
-#define DBG(x...)
-#endif
-
static struct s3c2410_dma_client s3c24xx_dma_client_out = {
.name = "I2S PCM Stereo out"
};
@@ -84,13 +77,13 @@
u32 iiscon;
u32 iismod;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
- DBG("r: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);
+ pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
if (on) {
iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;
@@ -120,7 +113,7 @@
writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
}
- DBG("w: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);
+ pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
}
static void s3c24xx_snd_rxctrl(int on)
@@ -129,13 +122,13 @@
u32 iiscon;
u32 iismod;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
- DBG("r: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);
+ pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
if (on) {
iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
@@ -165,7 +158,7 @@
writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
}
- DBG("w: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);
+ pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
}
/*
@@ -177,7 +170,7 @@
u32 iiscon;
int timeout = 50; /* 5ms */
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
while (1) {
iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
@@ -197,7 +190,7 @@
*/
static inline int s3c24xx_snd_is_clkmaster(void)
{
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1;
}
@@ -210,10 +203,10 @@
{
u32 iismod;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
- DBG("hw_params r: IISMOD: %lx \n", iismod);
+ pr_debug("hw_params r: IISMOD: %x \n", iismod);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
@@ -238,7 +231,7 @@
}
writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
- DBG("hw_params w: IISMOD: %lx \n", iismod);
+ pr_debug("hw_params w: IISMOD: %x \n", iismod);
return 0;
}
@@ -249,7 +242,7 @@
struct snd_soc_pcm_runtime *rtd = substream->private_data;
u32 iismod;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_out;
@@ -258,7 +251,7 @@
/* Working copies of register */
iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
- DBG("hw_params r: IISMOD: %lx\n", iismod);
+ pr_debug("hw_params r: IISMOD: %x\n", iismod);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
@@ -276,7 +269,7 @@
}
writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
- DBG("hw_params w: IISMOD: %lx\n", iismod);
+ pr_debug("hw_params w: IISMOD: %x\n", iismod);
return 0;
}
@@ -285,7 +278,7 @@
{
int ret = 0;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -327,7 +320,7 @@
{
u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
iismod &= ~S3C2440_IISMOD_MPLL;
@@ -353,7 +346,7 @@
{
u32 reg;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
switch (div_id) {
case S3C24XX_DIV_BCLK:
@@ -389,7 +382,7 @@
static int s3c24xx_i2s_probe(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
s3c24xx_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100);
if (s3c24xx_i2s.regs == NULL)
@@ -397,7 +390,7 @@
s3c24xx_i2s.iis_clk = clk_get(&pdev->dev, "iis");
if (s3c24xx_i2s.iis_clk == NULL) {
- DBG("failed to get iis_clock\n");
+ pr_err("failed to get iis_clock\n");
iounmap(s3c24xx_i2s.regs);
return -ENODEV;
}
@@ -421,7 +414,7 @@
#ifdef CONFIG_PM
static int s3c24xx_i2s_suspend(struct snd_soc_dai *cpu_dai)
{
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
@@ -435,7 +428,7 @@
static int s3c24xx_i2s_resume(struct snd_soc_dai *cpu_dai)
{
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
clk_enable(s3c24xx_i2s.iis_clk);
writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
@@ -456,6 +449,14 @@
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+static struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = {
+ .trigger = s3c24xx_i2s_trigger,
+ .hw_params = s3c24xx_i2s_hw_params,
+ .set_fmt = s3c24xx_i2s_set_fmt,
+ .set_clkdiv = s3c24xx_i2s_set_clkdiv,
+ .set_sysclk = s3c24xx_i2s_set_sysclk,
+};
+
struct snd_soc_dai s3c24xx_i2s_dai = {
.name = "s3c24xx-i2s",
.id = 0,
@@ -472,13 +473,7 @@
.channels_max = 2,
.rates = S3C24XX_I2S_RATES,
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = {
- .trigger = s3c24xx_i2s_trigger,
- .hw_params = s3c24xx_i2s_hw_params,
- .set_fmt = s3c24xx_i2s_set_fmt,
- .set_clkdiv = s3c24xx_i2s_set_clkdiv,
- .set_sysclk = s3c24xx_i2s_set_sysclk,
- },
+ .ops = &s3c24xx_i2s_dai_ops,
};
EXPORT_SYMBOL_GPL(s3c24xx_i2s_dai);
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c
index 7c64d31..a9d68fa 100644
--- a/sound/soc/s3c24xx/s3c24xx-pcm.c
+++ b/sound/soc/s3c24xx/s3c24xx-pcm.c
@@ -4,7 +4,7 @@
* (c) 2006 Wolfson Microelectronics PLC.
* Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
*
- * (c) 2004-2005 Simtec Electronics
+ * Copyright 2004-2005 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
*
@@ -29,17 +29,10 @@
#include <asm/dma.h>
#include <mach/hardware.h>
#include <mach/dma.h>
-#include <mach/audio.h>
+#include <plat/audio.h>
#include "s3c24xx-pcm.h"
-#define S3C24XX_PCM_DEBUG 0
-#if S3C24XX_PCM_DEBUG
-#define DBG(x...) printk(KERN_DEBUG "s3c24xx-pcm: " x)
-#else
-#define DBG(x...)
-#endif
-
static const struct snd_pcm_hardware s3c24xx_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -84,16 +77,16 @@
dma_addr_t pos = prtd->dma_pos;
int ret;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
while (prtd->dma_loaded < prtd->dma_limit) {
unsigned long len = prtd->dma_period;
- DBG("dma_loaded: %d\n", prtd->dma_loaded);
+ pr_debug("dma_loaded: %d\n", prtd->dma_loaded);
if ((pos + len) > prtd->dma_end) {
len = prtd->dma_end - pos;
- DBG(KERN_DEBUG "%s: corrected dma len %ld\n",
+ pr_debug(KERN_DEBUG "%s: corrected dma len %ld\n",
__func__, len);
}
@@ -119,7 +112,7 @@
struct snd_pcm_substream *substream = dev_id;
struct s3c24xx_runtime_data *prtd;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR)
return;
@@ -148,7 +141,7 @@
unsigned long totbytes = params_buffer_bytes(params);
int ret = 0;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
/* return if this is a bufferless transfer e.g.
* codec <--> BT codec or GSM modem -- lg FIXME */
@@ -161,14 +154,14 @@
/* prepare DMA */
prtd->params = dma;
- DBG("params %p, client %p, channel %d\n", prtd->params,
+ pr_debug("params %p, client %p, channel %d\n", prtd->params,
prtd->params->client, prtd->params->channel);
ret = s3c2410_dma_request(prtd->params->channel,
prtd->params->client, NULL);
if (ret < 0) {
- DBG(KERN_ERR "failed to get dma channel\n");
+ printk(KERN_ERR "failed to get dma channel\n");
return ret;
}
}
@@ -196,7 +189,7 @@
{
struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
/* TODO - do we need to ensure DMA flushed */
snd_pcm_set_runtime_buffer(substream, NULL);
@@ -214,7 +207,7 @@
struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
int ret = 0;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
/* return if this is a bufferless transfer e.g.
* codec <--> BT codec or GSM modem -- lg FIXME */
@@ -259,7 +252,7 @@
struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
int ret = 0;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
spin_lock(&prtd->lock);
@@ -297,7 +290,7 @@
unsigned long res;
dma_addr_t src, dst;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
spin_lock(&prtd->lock);
s3c2410_dma_getposition(prtd->params->channel, &src, &dst);
@@ -309,7 +302,7 @@
spin_unlock(&prtd->lock);
- DBG("Pointer %x %x\n", src, dst);
+ pr_debug("Pointer %x %x\n", src, dst);
/* we seem to be getting the odd error from the pcm library due
* to out-of-bounds pointers. this is maybe due to the dma engine
@@ -330,7 +323,7 @@
struct snd_pcm_runtime *runtime = substream->runtime;
struct s3c24xx_runtime_data *prtd;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
snd_soc_set_runtime_hwparams(substream, &s3c24xx_pcm_hardware);
@@ -349,10 +342,10 @@
struct snd_pcm_runtime *runtime = substream->runtime;
struct s3c24xx_runtime_data *prtd = runtime->private_data;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
if (!prtd)
- DBG("s3c24xx_pcm_close called with prtd == NULL\n");
+ pr_debug("s3c24xx_pcm_close called with prtd == NULL\n");
kfree(prtd);
@@ -364,7 +357,7 @@
{
struct snd_pcm_runtime *runtime = substream->runtime;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
return dma_mmap_writecombine(substream->pcm->card->dev, vma,
runtime->dma_area,
@@ -390,7 +383,7 @@
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = s3c24xx_pcm_hardware.buffer_bytes_max;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
@@ -409,7 +402,7 @@
struct snd_dma_buffer *buf;
int stream;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
for (stream = 0; stream < 2; stream++) {
substream = pcm->streams[stream].substream;
@@ -433,7 +426,7 @@
{
int ret = 0;
- DBG("Entered %s\n", __func__);
+ pr_debug("Entered %s\n", __func__);
if (!card->dev->dma_mask)
card->dev->dma_mask = &s3c24xx_pcm_dmamask;
diff --git a/sound/soc/s3c24xx/s3c24xx_uda134x.c b/sound/soc/s3c24xx/s3c24xx_uda134x.c
index a0a4d183..8e79a41 100644
--- a/sound/soc/s3c24xx/s3c24xx_uda134x.c
+++ b/sound/soc/s3c24xx/s3c24xx_uda134x.c
@@ -22,7 +22,7 @@
#include <sound/s3c24xx_uda134x.h>
#include <sound/uda134x.h>
-#include <asm/plat-s3c24xx/regs-iis.h>
+#include <plat/regs-iis.h>
#include "s3c24xx-pcm.h"
#include "s3c24xx-i2s.h"
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c
new file mode 100644
index 0000000..33c5de7
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c64xx-i2s.c
@@ -0,0 +1,222 @@
+/* sound/soc/s3c24xx/s3c64xx-i2s.c
+ *
+ * ALSA SoC Audio Layer - S3C64XX I2S driver
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <plat/regs-s3c2412-iis.h>
+#include <plat/gpio-bank-d.h>
+#include <plat/gpio-bank-e.h>
+#include <plat/gpio-cfg.h>
+#include <plat/audio.h>
+
+#include <mach/map.h>
+#include <mach/dma.h>
+
+#include "s3c24xx-pcm.h"
+#include "s3c64xx-i2s.h"
+
+static struct s3c2410_dma_client s3c64xx_dma_client_out = {
+ .name = "I2S PCM Stereo out"
+};
+
+static struct s3c2410_dma_client s3c64xx_dma_client_in = {
+ .name = "I2S PCM Stereo in"
+};
+
+static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_out[2] = {
+ [0] = {
+ .channel = DMACH_I2S0_OUT,
+ .client = &s3c64xx_dma_client_out,
+ .dma_addr = S3C64XX_PA_IIS0 + S3C2412_IISTXD,
+ .dma_size = 4,
+ },
+ [1] = {
+ .channel = DMACH_I2S1_OUT,
+ .client = &s3c64xx_dma_client_out,
+ .dma_addr = S3C64XX_PA_IIS1 + S3C2412_IISTXD,
+ .dma_size = 4,
+ },
+};
+
+static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_in[2] = {
+ [0] = {
+ .channel = DMACH_I2S0_IN,
+ .client = &s3c64xx_dma_client_in,
+ .dma_addr = S3C64XX_PA_IIS0 + S3C2412_IISRXD,
+ .dma_size = 4,
+ },
+ [1] = {
+ .channel = DMACH_I2S1_IN,
+ .client = &s3c64xx_dma_client_in,
+ .dma_addr = S3C64XX_PA_IIS1 + S3C2412_IISRXD,
+ .dma_size = 4,
+ },
+};
+
+static struct s3c_i2sv2_info s3c64xx_i2s[2];
+
+static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai)
+{
+ return cpu_dai->private_data;
+}
+
+static int s3c64xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
+ u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
+
+ switch (clk_id) {
+ case S3C64XX_CLKSRC_PCLK:
+ iismod &= ~S3C64XX_IISMOD_IMS_SYSMUX;
+ break;
+
+ case S3C64XX_CLKSRC_MUX:
+ iismod |= S3C64XX_IISMOD_IMS_SYSMUX;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ writel(iismod, i2s->regs + S3C2412_IISMOD);
+
+ return 0;
+}
+
+
+unsigned long s3c64xx_i2s_get_clockrate(struct snd_soc_dai *dai)
+{
+ struct s3c_i2sv2_info *i2s = to_info(dai);
+
+ return clk_get_rate(i2s->iis_cclk);
+}
+EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clockrate);
+
+static int s3c64xx_i2s_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = &pdev->dev;
+ struct s3c_i2sv2_info *i2s;
+ int ret;
+
+ dev_dbg(dev, "%s: probing dai %d\n", __func__, pdev->id);
+
+ if (pdev->id < 0 || pdev->id > ARRAY_SIZE(s3c64xx_i2s)) {
+ dev_err(dev, "id %d out of range\n", pdev->id);
+ return -EINVAL;
+ }
+
+ i2s = &s3c64xx_i2s[pdev->id];
+
+ ret = s3c_i2sv2_probe(pdev, dai, i2s,
+ pdev->id ? S3C64XX_PA_IIS1 : S3C64XX_PA_IIS0);
+ if (ret)
+ return ret;
+
+ i2s->dma_capture = &s3c64xx_i2s_pcm_stereo_in[pdev->id];
+ i2s->dma_playback = &s3c64xx_i2s_pcm_stereo_out[pdev->id];
+
+ i2s->iis_cclk = clk_get(dev, "audio-bus");
+ if (IS_ERR(i2s->iis_cclk)) {
+ dev_err(dev, "failed to get audio-bus");
+ iounmap(i2s->regs);
+ return -ENODEV;
+ }
+
+ /* configure GPIO for i2s port */
+ switch (pdev->id) {
+ case 0:
+ s3c_gpio_cfgpin(S3C64XX_GPD(0), S3C64XX_GPD0_I2S0_CLK);
+ s3c_gpio_cfgpin(S3C64XX_GPD(1), S3C64XX_GPD1_I2S0_CDCLK);
+ s3c_gpio_cfgpin(S3C64XX_GPD(2), S3C64XX_GPD2_I2S0_LRCLK);
+ s3c_gpio_cfgpin(S3C64XX_GPD(3), S3C64XX_GPD3_I2S0_DI);
+ s3c_gpio_cfgpin(S3C64XX_GPD(4), S3C64XX_GPD4_I2S0_D0);
+ break;
+ case 1:
+ s3c_gpio_cfgpin(S3C64XX_GPE(0), S3C64XX_GPE0_I2S1_CLK);
+ s3c_gpio_cfgpin(S3C64XX_GPE(1), S3C64XX_GPE1_I2S1_CDCLK);
+ s3c_gpio_cfgpin(S3C64XX_GPE(2), S3C64XX_GPE2_I2S1_LRCLK);
+ s3c_gpio_cfgpin(S3C64XX_GPE(3), S3C64XX_GPE3_I2S1_DI);
+ s3c_gpio_cfgpin(S3C64XX_GPE(4), S3C64XX_GPE4_I2S1_D0);
+ }
+
+ return 0;
+}
+
+
+#define S3C64XX_I2S_RATES \
+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define S3C64XX_I2S_FMTS \
+ (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE)
+
+static struct snd_soc_dai_ops s3c64xx_i2s_dai_ops = {
+ .set_sysclk = s3c64xx_i2s_set_sysclk,
+};
+
+struct snd_soc_dai s3c64xx_i2s_dai = {
+ .name = "s3c64xx-i2s",
+ .id = 0,
+ .probe = s3c64xx_i2s_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = S3C64XX_I2S_RATES,
+ .formats = S3C64XX_I2S_FMTS,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = S3C64XX_I2S_RATES,
+ .formats = S3C64XX_I2S_FMTS,
+ },
+ .ops = &s3c64xx_i2s_dai_ops,
+};
+EXPORT_SYMBOL_GPL(s3c64xx_i2s_dai);
+
+static int __init s3c64xx_i2s_init(void)
+{
+ return s3c_i2sv2_register_dai(&s3c64xx_i2s_dai);
+}
+module_init(s3c64xx_i2s_init);
+
+static void __exit s3c64xx_i2s_exit(void)
+{
+ snd_soc_unregister_dai(&s3c64xx_i2s_dai);
+}
+module_exit(s3c64xx_i2s_exit);
+
+/* Module information */
+MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("S3C64XX I2S SoC Interface");
+MODULE_LICENSE("GPL");
+
+
+
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.h b/sound/soc/s3c24xx/s3c64xx-i2s.h
new file mode 100644
index 0000000..b7ffe3c
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c64xx-i2s.h
@@ -0,0 +1,31 @@
+/* sound/soc/s3c24xx/s3c64xx-i2s.h
+ *
+ * ALSA SoC Audio Layer - S3C64XX I2S driver
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * 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 __SND_SOC_S3C24XX_S3C64XX_I2S_H
+#define __SND_SOC_S3C24XX_S3C64XX_I2S_H __FILE__
+
+#include "s3c-i2s-v2.h"
+
+#define S3C64XX_DIV_BCLK S3C_I2SV2_DIV_BCLK
+#define S3C64XX_DIV_RCLK S3C_I2SV2_DIV_RCLK
+#define S3C64XX_DIV_PRESCALER S3C_I2SV2_DIV_PRESCALER
+
+#define S3C64XX_CLKSRC_PCLK (0)
+#define S3C64XX_CLKSRC_MUX (1)
+
+extern struct snd_soc_dai s3c64xx_i2s_dai;
+
+extern unsigned long s3c64xx_i2s_get_clockrate(struct snd_soc_dai *cpu_dai);
+
+#endif /* __SND_SOC_S3C24XX_S3C64XX_I2S_H */
diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c
index eab3183..41db75a 100644
--- a/sound/soc/sh/hac.c
+++ b/sound/soc/sh/hac.c
@@ -267,6 +267,10 @@
#define AC97_FMTS \
SNDRV_PCM_FMTBIT_S16_LE
+static struct snd_soc_dai_ops hac_dai_ops = {
+ .hw_params = hac_hw_params,
+};
+
struct snd_soc_dai sh4_hac_dai[] = {
{
.name = "HAC0",
@@ -284,9 +288,7 @@
.channels_min = 2,
.channels_max = 2,
},
- .ops = {
- .hw_params = hac_hw_params,
- },
+ .ops = &hac_dai_ops,
},
#ifdef CONFIG_CPU_SUBTYPE_SH7760
{
@@ -305,9 +307,7 @@
.channels_min = 2,
.channels_max = 2,
},
- .ops = {
- .hw_params = hac_hw_params,
- },
+ .ops = &hac_dai_ops,
},
#endif
diff --git a/sound/soc/sh/ssi.c b/sound/soc/sh/ssi.c
index d1e5390..56fa087 100644
--- a/sound/soc/sh/ssi.c
+++ b/sound/soc/sh/ssi.c
@@ -336,6 +336,16 @@
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE | \
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
+static struct snd_soc_dai_ops ssi_dai_ops = {
+ .startup = ssi_startup,
+ .shutdown = ssi_shutdown,
+ .trigger = ssi_trigger,
+ .hw_params = ssi_hw_params,
+ .set_sysclk = ssi_set_sysclk,
+ .set_clkdiv = ssi_set_clkdiv,
+ .set_fmt = ssi_set_fmt,
+};
+
struct snd_soc_dai sh4_ssi_dai[] = {
{
.name = "SSI0",
@@ -352,15 +362,7 @@
.channels_min = 2,
.channels_max = 8,
},
- .ops = {
- .startup = ssi_startup,
- .shutdown = ssi_shutdown,
- .trigger = ssi_trigger,
- .hw_params = ssi_hw_params,
- .set_sysclk = ssi_set_sysclk,
- .set_clkdiv = ssi_set_clkdiv,
- .set_fmt = ssi_set_fmt,
- },
+ .ops = &ssi_dai_ops,
},
#ifdef CONFIG_CPU_SUBTYPE_SH7760
{
@@ -378,15 +380,7 @@
.channels_min = 2,
.channels_max = 8,
},
- .ops = {
- .startup = ssi_startup,
- .shutdown = ssi_shutdown,
- .trigger = ssi_trigger,
- .hw_params = ssi_hw_params,
- .set_sysclk = ssi_set_sysclk,
- .set_clkdiv = ssi_set_clkdiv,
- .set_fmt = ssi_set_fmt,
- },
+ .ops = &ssi_dai_ops,
},
#endif
};
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index ec3f8bb..6e710f7 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -133,8 +133,8 @@
mutex_lock(&pcm_mutex);
/* startup the audio subsystem */
- if (cpu_dai->ops.startup) {
- ret = cpu_dai->ops.startup(substream, cpu_dai);
+ if (cpu_dai->ops->startup) {
+ ret = cpu_dai->ops->startup(substream, cpu_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open interface %s\n",
cpu_dai->name);
@@ -150,8 +150,8 @@
}
}
- if (codec_dai->ops.startup) {
- ret = codec_dai->ops.startup(substream, codec_dai);
+ if (codec_dai->ops->startup) {
+ ret = codec_dai->ops->startup(substream, codec_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open codec %s\n",
codec_dai->name);
@@ -234,7 +234,7 @@
cpu_dai->capture.active = codec_dai->capture.active = 1;
cpu_dai->active = codec_dai->active = 1;
cpu_dai->runtime = runtime;
- socdev->codec->active++;
+ card->codec->active++;
mutex_unlock(&pcm_mutex);
return 0;
@@ -247,8 +247,8 @@
platform->pcm_ops->close(substream);
platform_err:
- if (cpu_dai->ops.shutdown)
- cpu_dai->ops.shutdown(substream, cpu_dai);
+ if (cpu_dai->ops->shutdown)
+ cpu_dai->ops->shutdown(substream, cpu_dai);
out:
mutex_unlock(&pcm_mutex);
return ret;
@@ -264,7 +264,7 @@
struct snd_soc_card *card = container_of(work, struct snd_soc_card,
delayed_work.work);
struct snd_soc_device *socdev = card->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = card->codec;
struct snd_soc_dai *codec_dai;
int i;
@@ -319,7 +319,7 @@
struct snd_soc_platform *platform = card->platform;
struct snd_soc_dai *cpu_dai = machine->cpu_dai;
struct snd_soc_dai *codec_dai = machine->codec_dai;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = card->codec;
mutex_lock(&pcm_mutex);
@@ -340,11 +340,11 @@
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_soc_dai_digital_mute(codec_dai, 1);
- if (cpu_dai->ops.shutdown)
- cpu_dai->ops.shutdown(substream, cpu_dai);
+ if (cpu_dai->ops->shutdown)
+ cpu_dai->ops->shutdown(substream, cpu_dai);
- if (codec_dai->ops.shutdown)
- codec_dai->ops.shutdown(substream, codec_dai);
+ if (codec_dai->ops->shutdown)
+ codec_dai->ops->shutdown(substream, codec_dai);
if (machine->ops && machine->ops->shutdown)
machine->ops->shutdown(substream);
@@ -387,7 +387,7 @@
struct snd_soc_platform *platform = card->platform;
struct snd_soc_dai *cpu_dai = machine->cpu_dai;
struct snd_soc_dai *codec_dai = machine->codec_dai;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = card->codec;
int ret = 0;
mutex_lock(&pcm_mutex);
@@ -408,16 +408,16 @@
}
}
- if (codec_dai->ops.prepare) {
- ret = codec_dai->ops.prepare(substream, codec_dai);
+ if (codec_dai->ops->prepare) {
+ ret = codec_dai->ops->prepare(substream, codec_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: codec DAI prepare error\n");
goto out;
}
}
- if (cpu_dai->ops.prepare) {
- ret = cpu_dai->ops.prepare(substream, cpu_dai);
+ if (cpu_dai->ops->prepare) {
+ ret = cpu_dai->ops->prepare(substream, cpu_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: cpu DAI prepare error\n");
goto out;
@@ -494,8 +494,8 @@
}
}
- if (codec_dai->ops.hw_params) {
- ret = codec_dai->ops.hw_params(substream, params, codec_dai);
+ if (codec_dai->ops->hw_params) {
+ ret = codec_dai->ops->hw_params(substream, params, codec_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: can't set codec %s hw params\n",
codec_dai->name);
@@ -503,8 +503,8 @@
}
}
- if (cpu_dai->ops.hw_params) {
- ret = cpu_dai->ops.hw_params(substream, params, cpu_dai);
+ if (cpu_dai->ops->hw_params) {
+ ret = cpu_dai->ops->hw_params(substream, params, cpu_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: interface %s hw params failed\n",
cpu_dai->name);
@@ -526,12 +526,12 @@
return ret;
platform_err:
- if (cpu_dai->ops.hw_free)
- cpu_dai->ops.hw_free(substream, cpu_dai);
+ if (cpu_dai->ops->hw_free)
+ cpu_dai->ops->hw_free(substream, cpu_dai);
interface_err:
- if (codec_dai->ops.hw_free)
- codec_dai->ops.hw_free(substream, codec_dai);
+ if (codec_dai->ops->hw_free)
+ codec_dai->ops->hw_free(substream, codec_dai);
codec_err:
if (machine->ops && machine->ops->hw_free)
@@ -553,7 +553,7 @@
struct snd_soc_platform *platform = card->platform;
struct snd_soc_dai *cpu_dai = machine->cpu_dai;
struct snd_soc_dai *codec_dai = machine->codec_dai;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = card->codec;
mutex_lock(&pcm_mutex);
@@ -570,11 +570,11 @@
platform->pcm_ops->hw_free(substream);
/* now free hw params for the DAI's */
- if (codec_dai->ops.hw_free)
- codec_dai->ops.hw_free(substream, codec_dai);
+ if (codec_dai->ops->hw_free)
+ codec_dai->ops->hw_free(substream, codec_dai);
- if (cpu_dai->ops.hw_free)
- cpu_dai->ops.hw_free(substream, cpu_dai);
+ if (cpu_dai->ops->hw_free)
+ cpu_dai->ops->hw_free(substream, cpu_dai);
mutex_unlock(&pcm_mutex);
return 0;
@@ -591,8 +591,8 @@
struct snd_soc_dai *codec_dai = machine->codec_dai;
int ret;
- if (codec_dai->ops.trigger) {
- ret = codec_dai->ops.trigger(substream, cmd, codec_dai);
+ if (codec_dai->ops->trigger) {
+ ret = codec_dai->ops->trigger(substream, cmd, codec_dai);
if (ret < 0)
return ret;
}
@@ -603,8 +603,8 @@
return ret;
}
- if (cpu_dai->ops.trigger) {
- ret = cpu_dai->ops.trigger(substream, cmd, cpu_dai);
+ if (cpu_dai->ops->trigger) {
+ ret = cpu_dai->ops->trigger(substream, cmd, cpu_dai);
if (ret < 0)
return ret;
}
@@ -629,7 +629,7 @@
struct snd_soc_card *card = socdev->card;
struct snd_soc_platform *platform = card->platform;
struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = card->codec;
int i;
/* Due to the resume being scheduled into a workqueue we could
@@ -645,8 +645,8 @@
/* mute any active DAC's */
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai *dai = card->dai_link[i].codec_dai;
- if (dai->ops.digital_mute && dai->playback.active)
- dai->ops.digital_mute(dai, 1);
+ if (dai->ops->digital_mute && dai->playback.active)
+ dai->ops->digital_mute(dai, 1);
}
/* suspend all pcms */
@@ -705,7 +705,7 @@
struct snd_soc_device *socdev = card->socdev;
struct snd_soc_platform *platform = card->platform;
struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = card->codec;
struct platform_device *pdev = to_platform_device(socdev->dev);
int i;
@@ -741,8 +741,8 @@
/* unmute any active DACs */
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai *dai = card->dai_link[i].codec_dai;
- if (dai->ops.digital_mute && dai->playback.active)
- dai->ops.digital_mute(dai, 0);
+ if (dai->ops->digital_mute && dai->playback.active)
+ dai->ops->digital_mute(dai, 0);
}
for (i = 0; i < card->num_links; i++) {
@@ -982,8 +982,8 @@
static int soc_new_pcm(struct snd_soc_device *socdev,
struct snd_soc_dai_link *dai_link, int num)
{
- struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_card *card = socdev->card;
+ struct snd_soc_codec *codec = card->codec;
struct snd_soc_platform *platform = card->platform;
struct snd_soc_dai *codec_dai = dai_link->codec_dai;
struct snd_soc_dai *cpu_dai = dai_link->cpu_dai;
@@ -998,7 +998,7 @@
rtd->dai = dai_link;
rtd->socdev = socdev;
- codec_dai->codec = socdev->codec;
+ codec_dai->codec = card->codec;
/* check client and interface hw capabilities */
sprintf(new_name, "%s %s-%d", dai_link->stream_name, codec_dai->name,
@@ -1048,9 +1048,8 @@
}
/* codec register dump */
-static ssize_t soc_codec_reg_show(struct snd_soc_device *devdata, char *buf)
+static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf)
{
- struct snd_soc_codec *codec = devdata->codec;
int i, step = 1, count = 0;
if (!codec->reg_cache_size)
@@ -1090,7 +1089,7 @@
struct device_attribute *attr, char *buf)
{
struct snd_soc_device *devdata = dev_get_drvdata(dev);
- return soc_codec_reg_show(devdata, buf);
+ return soc_codec_reg_show(devdata->card->codec, buf);
}
static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
@@ -1107,12 +1106,10 @@
{
ssize_t ret;
struct snd_soc_codec *codec = file->private_data;
- struct device *card_dev = codec->card->dev;
- struct snd_soc_device *devdata = card_dev->driver_data;
char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
- ret = soc_codec_reg_show(devdata, buf);
+ ret = soc_codec_reg_show(codec, buf);
if (ret >= 0)
ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
kfree(buf);
@@ -1309,19 +1306,19 @@
*/
int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
{
- struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_card *card = socdev->card;
- int ret = 0, i;
+ struct snd_soc_codec *codec = card->codec;
+ int ret, i;
mutex_lock(&codec->mutex);
/* register a sound card */
- codec->card = snd_card_new(idx, xid, codec->owner, 0);
- if (!codec->card) {
+ ret = snd_card_create(idx, xid, codec->owner, 0, &codec->card);
+ if (ret < 0) {
printk(KERN_ERR "asoc: can't create sound card for codec %s\n",
codec->name);
mutex_unlock(&codec->mutex);
- return -ENODEV;
+ return ret;
}
codec->card->dev = socdev->dev;
@@ -1355,8 +1352,8 @@
*/
int snd_soc_init_card(struct snd_soc_device *socdev)
{
- struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_card *card = socdev->card;
+ struct snd_soc_codec *codec = card->codec;
int ret = 0, i, ac97 = 0, err = 0;
for (i = 0; i < card->num_links; i++) {
@@ -1407,7 +1404,7 @@
if (err < 0)
printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");
- soc_init_codec_debugfs(socdev->codec);
+ soc_init_codec_debugfs(codec);
mutex_unlock(&codec->mutex);
out:
@@ -1424,18 +1421,19 @@
*/
void snd_soc_free_pcms(struct snd_soc_device *socdev)
{
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
#ifdef CONFIG_SND_SOC_AC97_BUS
struct snd_soc_dai *codec_dai;
int i;
#endif
mutex_lock(&codec->mutex);
- soc_cleanup_codec_debugfs(socdev->codec);
+ soc_cleanup_codec_debugfs(codec);
#ifdef CONFIG_SND_SOC_AC97_BUS
for (i = 0; i < codec->num_dai; i++) {
codec_dai = &codec->dai[i];
- if (codec_dai->ac97_control && codec->ac97) {
+ if (codec_dai->ac97_control && codec->ac97 &&
+ strcmp(codec->name, "AC97") != 0) {
soc_ac97_dev_unregister(codec);
goto free_card;
}
@@ -1498,6 +1496,37 @@
EXPORT_SYMBOL_GPL(snd_soc_cnew);
/**
+ * snd_soc_add_controls - add an array of controls to a codec.
+ * Convienience function to add a list of controls. Many codecs were
+ * duplicating this code.
+ *
+ * @codec: codec to add controls to
+ * @controls: array of controls to add
+ * @num_controls: number of elements in the array
+ *
+ * Return 0 for success, else error.
+ */
+int snd_soc_add_controls(struct snd_soc_codec *codec,
+ const struct snd_kcontrol_new *controls, int num_controls)
+{
+ struct snd_card *card = codec->card;
+ int err, i;
+
+ for (i = 0; i < num_controls; i++) {
+ const struct snd_kcontrol_new *control = &controls[i];
+ err = snd_ctl_add(card, snd_soc_cnew(control, codec, NULL));
+ if (err < 0) {
+ dev_err(codec->dev, "%s: Failed to add %s\n",
+ codec->name, control->name);
+ return err;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_add_controls);
+
+/**
* snd_soc_info_enum_double - enumerated double mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
@@ -2023,8 +2052,8 @@
int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
- if (dai->ops.set_sysclk)
- return dai->ops.set_sysclk(dai, clk_id, freq, dir);
+ if (dai->ops->set_sysclk)
+ return dai->ops->set_sysclk(dai, clk_id, freq, dir);
else
return -EINVAL;
}
@@ -2043,8 +2072,8 @@
int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
int div_id, int div)
{
- if (dai->ops.set_clkdiv)
- return dai->ops.set_clkdiv(dai, div_id, div);
+ if (dai->ops->set_clkdiv)
+ return dai->ops->set_clkdiv(dai, div_id, div);
else
return -EINVAL;
}
@@ -2062,8 +2091,8 @@
int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
int pll_id, unsigned int freq_in, unsigned int freq_out)
{
- if (dai->ops.set_pll)
- return dai->ops.set_pll(dai, pll_id, freq_in, freq_out);
+ if (dai->ops->set_pll)
+ return dai->ops->set_pll(dai, pll_id, freq_in, freq_out);
else
return -EINVAL;
}
@@ -2078,8 +2107,8 @@
*/
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- if (dai->ops.set_fmt)
- return dai->ops.set_fmt(dai, fmt);
+ if (dai->ops->set_fmt)
+ return dai->ops->set_fmt(dai, fmt);
else
return -EINVAL;
}
@@ -2097,8 +2126,8 @@
int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
unsigned int mask, int slots)
{
- if (dai->ops.set_sysclk)
- return dai->ops.set_tdm_slot(dai, mask, slots);
+ if (dai->ops->set_sysclk)
+ return dai->ops->set_tdm_slot(dai, mask, slots);
else
return -EINVAL;
}
@@ -2113,8 +2142,8 @@
*/
int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate)
{
- if (dai->ops.set_sysclk)
- return dai->ops.set_tristate(dai, tristate);
+ if (dai->ops->set_sysclk)
+ return dai->ops->set_tristate(dai, tristate);
else
return -EINVAL;
}
@@ -2129,8 +2158,8 @@
*/
int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
{
- if (dai->ops.digital_mute)
- return dai->ops.digital_mute(dai, mute);
+ if (dai->ops->digital_mute)
+ return dai->ops->digital_mute(dai, mute);
else
return -EINVAL;
}
@@ -2183,6 +2212,9 @@
return 0;
}
+static struct snd_soc_dai_ops null_dai_ops = {
+};
+
/**
* snd_soc_register_dai - Register a DAI with the ASoC core
*
@@ -2197,6 +2229,9 @@
if (!dai->dev)
printk(KERN_WARNING "No device for DAI %s\n", dai->name);
+ if (!dai->ops)
+ dai->ops = &null_dai_ops;
+
INIT_LIST_HEAD(&dai->list);
mutex_lock(&client_mutex);
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index a2f1da8..735903a 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -54,14 +54,15 @@
static int dapm_up_seq[] = {
snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic,
snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_dac,
- snd_soc_dapm_mixer, snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp,
- snd_soc_dapm_spk, snd_soc_dapm_post
+ snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_pga,
+ snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post
};
+
static int dapm_down_seq[] = {
snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
- snd_soc_dapm_pga, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic,
- snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_value_mux,
- snd_soc_dapm_post
+ snd_soc_dapm_pga, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_mixer,
+ snd_soc_dapm_dac, snd_soc_dapm_mic, snd_soc_dapm_micbias,
+ snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_post
};
static int dapm_status = 1;
@@ -101,7 +102,8 @@
{
switch (w->id) {
case snd_soc_dapm_switch:
- case snd_soc_dapm_mixer: {
+ case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl: {
int val;
struct soc_mixer_control *mc = (struct soc_mixer_control *)
w->kcontrols[i].private_value;
@@ -323,15 +325,32 @@
if (path->name != (char*)w->kcontrols[i].name)
continue;
- /* add dapm control with long name */
- name_len = 2 + strlen(w->name)
- + strlen(w->kcontrols[i].name);
+ /* 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;
+ 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)
return -ENOMEM;
- snprintf(path->long_name, name_len, "%s %s",
- w->name, w->kcontrols[i].name);
+ switch (w->id) {
+ default:
+ snprintf(path->long_name, name_len, "%s %s",
+ w->name, w->kcontrols[i].name);
+ break;
+ case snd_soc_dapm_mixer_named_ctl:
+ snprintf(path->long_name, name_len, "%s",
+ w->kcontrols[i].name);
+ break;
+ }
+
path->long_name[name_len - 1] = '\0';
path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
@@ -503,6 +522,137 @@
EXPORT_SYMBOL_GPL(dapm_reg_event);
/*
+ * Scan a single DAPM widget for a complete audio path and update the
+ * power status appropriately.
+ */
+static int dapm_power_widget(struct snd_soc_codec *codec, int event,
+ struct snd_soc_dapm_widget *w)
+{
+ int in, out, power_change, power, ret;
+
+ /* vmid - no action */
+ if (w->id == snd_soc_dapm_vmid)
+ return 0;
+
+ /* active ADC */
+ if (w->id == snd_soc_dapm_adc && w->active) {
+ in = is_connected_input_ep(w);
+ dapm_clear_walk(w->codec);
+ w->power = (in != 0) ? 1 : 0;
+ dapm_update_bits(w);
+ return 0;
+ }
+
+ /* active DAC */
+ if (w->id == snd_soc_dapm_dac && w->active) {
+ out = is_connected_output_ep(w);
+ dapm_clear_walk(w->codec);
+ w->power = (out != 0) ? 1 : 0;
+ dapm_update_bits(w);
+ return 0;
+ }
+
+ /* pre and post event widgets */
+ if (w->id == snd_soc_dapm_pre) {
+ if (!w->event)
+ return 0;
+
+ if (event == SND_SOC_DAPM_STREAM_START) {
+ ret = w->event(w,
+ NULL, SND_SOC_DAPM_PRE_PMU);
+ if (ret < 0)
+ return ret;
+ } else if (event == SND_SOC_DAPM_STREAM_STOP) {
+ ret = w->event(w,
+ NULL, SND_SOC_DAPM_PRE_PMD);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+ }
+ if (w->id == snd_soc_dapm_post) {
+ if (!w->event)
+ return 0;
+
+ if (event == SND_SOC_DAPM_STREAM_START) {
+ ret = w->event(w,
+ NULL, SND_SOC_DAPM_POST_PMU);
+ if (ret < 0)
+ return ret;
+ } else if (event == SND_SOC_DAPM_STREAM_STOP) {
+ ret = w->event(w,
+ NULL, SND_SOC_DAPM_POST_PMD);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+ }
+
+ /* all other widgets */
+ in = is_connected_input_ep(w);
+ dapm_clear_walk(w->codec);
+ out = is_connected_output_ep(w);
+ dapm_clear_walk(w->codec);
+ power = (out != 0 && in != 0) ? 1 : 0;
+ power_change = (w->power == power) ? 0 : 1;
+ w->power = power;
+
+ if (!power_change)
+ return 0;
+
+ /* call any power change event handlers */
+ if (w->event)
+ pr_debug("power %s event for %s flags %x\n",
+ w->power ? "on" : "off",
+ w->name, w->event_flags);
+
+ /* power up pre event */
+ if (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 (!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;
+ }
+
+ /* Lower PGA volume to reduce pops */
+ if (w->id == snd_soc_dapm_pga && !power)
+ dapm_set_pga(w, power);
+
+ dapm_update_bits(w);
+
+ /* Raise PGA volume to reduce pops */
+ if (w->id == snd_soc_dapm_pga && power)
+ dapm_set_pga(w, power);
+
+ /* power up post event */
+ if (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 (!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;
+}
+
+/*
* Scan each dapm widget for complete audio path.
* A complete path is a route that has valid endpoints i.e.:-
*
@@ -514,7 +664,7 @@
static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
{
struct snd_soc_dapm_widget *w;
- int in, out, i, c = 1, *seq = NULL, ret = 0, power_change, power;
+ int i, c = 1, *seq = NULL, ret = 0;
/* do we have a sequenced stream event */
if (event == SND_SOC_DAPM_STREAM_START) {
@@ -525,135 +675,20 @@
seq = dapm_down_seq;
}
- for(i = 0; i < c; i++) {
+ for (i = 0; i < c; i++) {
list_for_each_entry(w, &codec->dapm_widgets, list) {
/* is widget in stream order */
if (seq && seq[i] && w->id != seq[i])
continue;
- /* vmid - no action */
- if (w->id == snd_soc_dapm_vmid)
- continue;
-
- /* active ADC */
- if (w->id == snd_soc_dapm_adc && w->active) {
- in = is_connected_input_ep(w);
- dapm_clear_walk(w->codec);
- w->power = (in != 0) ? 1 : 0;
- dapm_update_bits(w);
- continue;
- }
-
- /* active DAC */
- if (w->id == snd_soc_dapm_dac && w->active) {
- out = is_connected_output_ep(w);
- dapm_clear_walk(w->codec);
- w->power = (out != 0) ? 1 : 0;
- dapm_update_bits(w);
- continue;
- }
-
- /* pre and post event widgets */
- if (w->id == snd_soc_dapm_pre) {
- if (!w->event)
- continue;
-
- if (event == SND_SOC_DAPM_STREAM_START) {
- ret = w->event(w,
- NULL, SND_SOC_DAPM_PRE_PMU);
- if (ret < 0)
- return ret;
- } else if (event == SND_SOC_DAPM_STREAM_STOP) {
- ret = w->event(w,
- NULL, SND_SOC_DAPM_PRE_PMD);
- if (ret < 0)
- return ret;
- }
- continue;
- }
- if (w->id == snd_soc_dapm_post) {
- if (!w->event)
- continue;
-
- if (event == SND_SOC_DAPM_STREAM_START) {
- ret = w->event(w,
- NULL, SND_SOC_DAPM_POST_PMU);
- if (ret < 0)
- return ret;
- } else if (event == SND_SOC_DAPM_STREAM_STOP) {
- ret = w->event(w,
- NULL, SND_SOC_DAPM_POST_PMD);
- if (ret < 0)
- return ret;
- }
- continue;
- }
-
- /* all other widgets */
- in = is_connected_input_ep(w);
- dapm_clear_walk(w->codec);
- out = is_connected_output_ep(w);
- dapm_clear_walk(w->codec);
- power = (out != 0 && in != 0) ? 1 : 0;
- power_change = (w->power == power) ? 0: 1;
- w->power = power;
-
- if (!power_change)
- continue;
-
- /* call any power change event handlers */
- if (w->event)
- pr_debug("power %s event for %s flags %x\n",
- w->power ? "on" : "off",
- w->name, w->event_flags);
-
- /* power up pre event */
- if (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 (!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;
- }
-
- /* Lower PGA volume to reduce pops */
- if (w->id == snd_soc_dapm_pga && !power)
- dapm_set_pga(w, power);
-
- dapm_update_bits(w);
-
- /* Raise PGA volume to reduce pops */
- if (w->id == snd_soc_dapm_pga && power)
- dapm_set_pga(w, power);
-
- /* power up post event */
- if (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 (!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;
- }
+ ret = dapm_power_widget(codec, event, w);
+ if (ret != 0)
+ return ret;
}
}
- return ret;
+ return 0;
}
#ifdef DEBUG
@@ -687,6 +722,7 @@
case snd_soc_dapm_adc:
case snd_soc_dapm_pga:
case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl:
if (w->name) {
in = is_connected_input_ep(w);
dapm_clear_walk(w->codec);
@@ -760,6 +796,7 @@
int found = 0;
if (widget->id != snd_soc_dapm_mixer &&
+ widget->id != snd_soc_dapm_mixer_named_ctl &&
widget->id != snd_soc_dapm_switch)
return -ENODEV;
@@ -795,7 +832,7 @@
struct device_attribute *attr, char *buf)
{
struct snd_soc_device *devdata = dev_get_drvdata(dev);
- struct snd_soc_codec *codec = devdata->codec;
+ struct snd_soc_codec *codec = devdata->card->codec;
struct snd_soc_dapm_widget *w;
int count = 0;
char *state = "not set";
@@ -813,6 +850,7 @@
case snd_soc_dapm_adc:
case snd_soc_dapm_pga:
case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl:
if (w->name)
count += sprintf(buf + count, "%s: %s\n",
w->name, w->power ? "On":"Off");
@@ -876,7 +914,7 @@
}
static int snd_soc_dapm_set_pin(struct snd_soc_codec *codec,
- char *pin, int status)
+ const char *pin, int status)
{
struct snd_soc_dapm_widget *w;
@@ -991,6 +1029,7 @@
break;
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl:
ret = dapm_connect_mixer(codec, wsource, wsink, path, control);
if (ret != 0)
goto err;
@@ -1068,6 +1107,7 @@
switch(w->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl:
dapm_new_mixer(codec, w);
break;
case snd_soc_dapm_mux:
@@ -1396,6 +1436,76 @@
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double);
/**
+ * snd_soc_dapm_info_pin_switch - Info for a pin switch
+ *
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a pin switch control.
+ */
+int snd_soc_dapm_info_pin_switch(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;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_info_pin_switch);
+
+/**
+ * snd_soc_dapm_get_pin_switch - Get information for a pin switch
+ *
+ * @kcontrol: mixer control
+ * @ucontrol: Value
+ */
+int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ const char *pin = (const char *)kcontrol->private_value;
+
+ mutex_lock(&codec->mutex);
+
+ ucontrol->value.integer.value[0] =
+ snd_soc_dapm_get_pin_status(codec, pin);
+
+ mutex_unlock(&codec->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_switch);
+
+/**
+ * snd_soc_dapm_put_pin_switch - Set information for a pin switch
+ *
+ * @kcontrol: mixer control
+ * @ucontrol: Value
+ */
+int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ const char *pin = (const char *)kcontrol->private_value;
+
+ mutex_lock(&codec->mutex);
+
+ if (ucontrol->value.integer.value[0])
+ snd_soc_dapm_enable_pin(codec, pin);
+ else
+ snd_soc_dapm_disable_pin(codec, pin);
+
+ snd_soc_dapm_sync(codec);
+
+ mutex_unlock(&codec->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch);
+
+/**
* snd_soc_dapm_new_control - create new dapm control
* @codec: audio codec
* @widget: widget template
@@ -1527,8 +1637,8 @@
int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
enum snd_soc_bias_level level)
{
- struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_card *card = socdev->card;
+ struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
if (card->set_bias_level)
@@ -1549,7 +1659,7 @@
* NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
* do any widget power switching.
*/
-int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, char *pin)
+int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin)
{
return snd_soc_dapm_set_pin(codec, pin, 1);
}
@@ -1564,7 +1674,7 @@
* NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
* do any widget power switching.
*/
-int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin)
+int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, const char *pin)
{
return snd_soc_dapm_set_pin(codec, pin, 0);
}
@@ -1584,7 +1694,7 @@
* NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
* do any widget power switching.
*/
-int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, char *pin)
+int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, const char *pin)
{
return snd_soc_dapm_set_pin(codec, pin, 0);
}
@@ -1599,7 +1709,7 @@
*
* Returns 1 for connected otherwise 0.
*/
-int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, char *pin)
+int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, const char *pin)
{
struct snd_soc_dapm_widget *w;
@@ -1620,7 +1730,7 @@
*/
void snd_soc_dapm_free(struct snd_soc_device *socdev)
{
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = socdev->card->codec;
snd_soc_dapm_sys_remove(socdev->dev);
dapm_free_widgets(codec);
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
new file mode 100644
index 0000000..28346fb
--- /dev/null
+++ b/sound/soc/soc-jack.c
@@ -0,0 +1,267 @@
+/*
+ * soc-jack.c -- ALSA SoC jack handling
+ *
+ * Copyright 2008 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 <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+
+/**
+ * snd_soc_jack_new - Create a new jack
+ * @card: ASoC card
+ * @id: an identifying string for this jack
+ * @type: a bitmask of enum snd_jack_type values that can be detected by
+ * this jack
+ * @jack: structure to use for the jack
+ *
+ * Creates a new jack object.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ * On success jack will be initialised.
+ */
+int snd_soc_jack_new(struct snd_soc_card *card, const char *id, int type,
+ struct snd_soc_jack *jack)
+{
+ jack->card = card;
+ INIT_LIST_HEAD(&jack->pins);
+
+ return snd_jack_new(card->codec->card, id, type, &jack->jack);
+}
+EXPORT_SYMBOL_GPL(snd_soc_jack_new);
+
+/**
+ * snd_soc_jack_report - Report the current status for a jack
+ *
+ * @jack: the jack
+ * @status: a bitmask of enum snd_jack_type values that are currently detected.
+ * @mask: a bitmask of enum snd_jack_type values that being reported.
+ *
+ * If configured using snd_soc_jack_add_pins() then the associated
+ * DAPM pins will be enabled or disabled as appropriate and DAPM
+ * synchronised.
+ *
+ * Note: This function uses mutexes and should be called from a
+ * context which can sleep (such as a workqueue).
+ */
+void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
+{
+ struct snd_soc_codec *codec = jack->card->codec;
+ struct snd_soc_jack_pin *pin;
+ int enable;
+ int oldstatus;
+
+ if (!jack) {
+ WARN_ON_ONCE(!jack);
+ return;
+ }
+
+ mutex_lock(&codec->mutex);
+
+ oldstatus = jack->status;
+
+ jack->status &= ~mask;
+ jack->status |= status;
+
+ /* The DAPM sync is expensive enough to be worth skipping */
+ if (jack->status == oldstatus)
+ goto out;
+
+ list_for_each_entry(pin, &jack->pins, list) {
+ enable = pin->mask & status;
+
+ if (pin->invert)
+ enable = !enable;
+
+ if (enable)
+ snd_soc_dapm_enable_pin(codec, pin->pin);
+ else
+ snd_soc_dapm_disable_pin(codec, pin->pin);
+ }
+
+ snd_soc_dapm_sync(codec);
+
+ snd_jack_report(jack->jack, status);
+
+out:
+ mutex_unlock(&codec->mutex);
+}
+EXPORT_SYMBOL_GPL(snd_soc_jack_report);
+
+/**
+ * snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack
+ *
+ * @jack: ASoC jack
+ * @count: Number of pins
+ * @pins: Array of pins
+ *
+ * After this function has been called the DAPM pins specified in the
+ * pins array will have their status updated to reflect the current
+ * state of the jack whenever the jack status is updated.
+ */
+int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
+ struct snd_soc_jack_pin *pins)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (!pins[i].pin) {
+ printk(KERN_ERR "No name for pin %d\n", i);
+ return -EINVAL;
+ }
+ if (!pins[i].mask) {
+ printk(KERN_ERR "No mask for pin %d (%s)\n", i,
+ pins[i].pin);
+ return -EINVAL;
+ }
+
+ INIT_LIST_HEAD(&pins[i].list);
+ list_add(&(pins[i].list), &jack->pins);
+ }
+
+ /* Update to reflect the last reported status; canned jack
+ * implementations are likely to set their state before the
+ * card has an opportunity to associate pins.
+ */
+ snd_soc_jack_report(jack, 0, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_jack_add_pins);
+
+#ifdef CONFIG_GPIOLIB
+/* gpio detect */
+static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio)
+{
+ struct snd_soc_jack *jack = gpio->jack;
+ int enable;
+ int report;
+
+ if (gpio->debounce_time > 0)
+ mdelay(gpio->debounce_time);
+
+ enable = gpio_get_value(gpio->gpio);
+ if (gpio->invert)
+ enable = !enable;
+
+ if (enable)
+ report = gpio->report;
+ else
+ report = 0;
+
+ snd_soc_jack_report(jack, report, gpio->report);
+}
+
+/* irq handler for gpio pin */
+static irqreturn_t gpio_handler(int irq, void *data)
+{
+ struct snd_soc_jack_gpio *gpio = data;
+
+ schedule_work(&gpio->work);
+
+ return IRQ_HANDLED;
+}
+
+/* gpio work */
+static void gpio_work(struct work_struct *work)
+{
+ struct snd_soc_jack_gpio *gpio;
+
+ gpio = container_of(work, struct snd_soc_jack_gpio, work);
+ snd_soc_jack_gpio_detect(gpio);
+}
+
+/**
+ * snd_soc_jack_add_gpios - Associate GPIO pins with an ASoC jack
+ *
+ * @jack: ASoC jack
+ * @count: number of pins
+ * @gpios: array of gpio pins
+ *
+ * This function will request gpio, set data direction and request irq
+ * for each gpio in the array.
+ */
+int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
+ struct snd_soc_jack_gpio *gpios)
+{
+ int i, ret;
+
+ for (i = 0; i < count; i++) {
+ if (!gpio_is_valid(gpios[i].gpio)) {
+ printk(KERN_ERR "Invalid gpio %d\n",
+ gpios[i].gpio);
+ ret = -EINVAL;
+ goto undo;
+ }
+ if (!gpios[i].name) {
+ printk(KERN_ERR "No name for gpio %d\n",
+ gpios[i].gpio);
+ ret = -EINVAL;
+ goto undo;
+ }
+
+ ret = gpio_request(gpios[i].gpio, gpios[i].name);
+ if (ret)
+ goto undo;
+
+ ret = gpio_direction_input(gpios[i].gpio);
+ if (ret)
+ goto err;
+
+ ret = request_irq(gpio_to_irq(gpios[i].gpio),
+ gpio_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ jack->card->dev->driver->name,
+ &gpios[i]);
+ if (ret)
+ goto err;
+
+ INIT_WORK(&gpios[i].work, gpio_work);
+ gpios[i].jack = jack;
+ }
+
+ return 0;
+
+err:
+ gpio_free(gpios[i].gpio);
+undo:
+ snd_soc_jack_free_gpios(jack, i, gpios);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_jack_add_gpios);
+
+/**
+ * snd_soc_jack_free_gpios - Release GPIO pins' resources of an ASoC jack
+ *
+ * @jack: ASoC jack
+ * @count: number of pins
+ * @gpios: array of gpio pins
+ *
+ * Release gpio and irq resources for gpio pins associated with an ASoC jack.
+ */
+void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
+ struct snd_soc_jack_gpio *gpios)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ free_irq(gpio_to_irq(gpios[i].gpio), &gpios[i]);
+ gpio_free(gpios[i].gpio);
+ gpios[i].jack = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(snd_soc_jack_free_gpios);
+#endif /* CONFIG_GPIOLIB */
diff --git a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c
index f87933e..574af56 100644
--- a/sound/sparc/amd7930.c
+++ b/sound/sparc/amd7930.c
@@ -954,7 +954,8 @@
amd->regs = of_ioremap(&op->resource[0], 0,
resource_size(&op->resource[0]), "amd7930");
if (!amd->regs) {
- snd_printk("amd7930-%d: Unable to map chip registers.\n", dev);
+ snd_printk(KERN_ERR
+ "amd7930-%d: Unable to map chip registers.\n", dev);
return -EIO;
}
@@ -962,7 +963,7 @@
if (request_irq(irq, snd_amd7930_interrupt,
IRQF_DISABLED | IRQF_SHARED, "amd7930", amd)) {
- snd_printk("amd7930-%d: Unable to grab IRQ %d\n",
+ snd_printk(KERN_ERR "amd7930-%d: Unable to grab IRQ %d\n",
dev, irq);
snd_amd7930_free(amd);
return -EBUSY;
@@ -1018,9 +1019,10 @@
return -ENOENT;
}
- card = snd_card_new(index[dev_num], id[dev_num], THIS_MODULE, 0);
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev_num], id[dev_num], THIS_MODULE, 0,
+ &card);
+ if (err < 0)
+ return err;
strcpy(card->driver, "AMD7930");
strcpy(card->shortname, "Sun AMD7930");
diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c
index 41c3875..7d93fa7 100644
--- a/sound/sparc/cs4231.c
+++ b/sound/sparc/cs4231.c
@@ -1563,6 +1563,7 @@
{
struct snd_card *card;
struct snd_cs4231 *chip;
+ int err;
*rcard = NULL;
@@ -1574,10 +1575,10 @@
return -ENOENT;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_cs4231));
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_cs4231), &card);
+ if (err < 0)
+ return err;
strcpy(card->driver, "CS4231");
strcpy(card->shortname, "Sun CS4231");
diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c
index 23ed6f0..af95ff1 100644
--- a/sound/sparc/dbri.c
+++ b/sound/sparc/dbri.c
@@ -2612,10 +2612,10 @@
return -ENODEV;
}
- card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_dbri));
- if (card == NULL)
- return -ENOMEM;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_dbri), &card);
+ if (err < 0)
+ return err;
strcpy(card->driver, "DBRI");
strcpy(card->shortname, "Sun DBRI");
diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c
index 09802e8..4c7b051 100644
--- a/sound/spi/at73c213.c
+++ b/sound/spi/at73c213.c
@@ -965,12 +965,11 @@
return PTR_ERR(board->dac_clk);
}
- retval = -ENOMEM;
-
/* Allocate "card" using some unused identifiers. */
snprintf(id, sizeof id, "at73c213_%d", board->ssc_id);
- card = snd_card_new(-1, id, THIS_MODULE, sizeof(struct snd_at73c213));
- if (!card)
+ retval = snd_card_create(-1, id, THIS_MODULE,
+ sizeof(struct snd_at73c213), &card);
+ if (retval < 0)
goto out;
chip = card->private_data;
diff --git a/sound/synth/emux/emux_hwdep.c b/sound/synth/emux/emux_hwdep.c
index 0a53914..ff0b2a8 100644
--- a/sound/synth/emux/emux_hwdep.c
+++ b/sound/synth/emux/emux_hwdep.c
@@ -24,25 +24,6 @@
#include <asm/uaccess.h>
#include "emux_voice.h"
-/*
- * open the hwdep device
- */
-static int
-snd_emux_hwdep_open(struct snd_hwdep *hw, struct file *file)
-{
- return 0;
-}
-
-
-/*
- * close the device
- */
-static int
-snd_emux_hwdep_release(struct snd_hwdep *hw, struct file *file)
-{
- return 0;
-}
-
#define TMP_CLIENT_ID 0x1001
@@ -146,8 +127,6 @@
emu->hwdep = hw;
strcpy(hw->name, SNDRV_EMUX_HWDEP_NAME);
hw->iface = SNDRV_HWDEP_IFACE_EMUX_WAVETABLE;
- hw->ops.open = snd_emux_hwdep_open;
- hw->ops.release = snd_emux_hwdep_release;
hw->ops.ioctl = snd_emux_hwdep_ioctl;
hw->exclusive = 1;
hw->private_data = emu;
diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c
index 5c47b6c..87e4220 100644
--- a/sound/synth/emux/emux_oss.c
+++ b/sound/synth/emux/emux_oss.c
@@ -132,7 +132,7 @@
p = snd_emux_create_port(emu, tmpname, 32,
1, &callback);
if (p == NULL) {
- snd_printk("can't create port\n");
+ snd_printk(KERN_ERR "can't create port\n");
snd_emux_dec_count(emu);
mutex_unlock(&emu->register_mutex);
return -ENOMEM;
diff --git a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c
index 335aa2c..ca5f7ef 100644
--- a/sound/synth/emux/emux_seq.c
+++ b/sound/synth/emux/emux_seq.c
@@ -74,15 +74,15 @@
emu->client = snd_seq_create_kernel_client(card, index,
"%s WaveTable", emu->name);
if (emu->client < 0) {
- snd_printk("can't create client\n");
+ snd_printk(KERN_ERR "can't create client\n");
return -ENODEV;
}
if (emu->num_ports < 0) {
- snd_printk("seqports must be greater than zero\n");
+ snd_printk(KERN_WARNING "seqports must be greater than zero\n");
emu->num_ports = 1;
} else if (emu->num_ports >= SNDRV_EMUX_MAX_PORTS) {
- snd_printk("too many ports."
+ snd_printk(KERN_WARNING "too many ports."
"limited max. ports %d\n", SNDRV_EMUX_MAX_PORTS);
emu->num_ports = SNDRV_EMUX_MAX_PORTS;
}
@@ -100,7 +100,7 @@
p = snd_emux_create_port(emu, tmpname, MIDI_CHANNELS,
0, &pinfo);
if (p == NULL) {
- snd_printk("can't create port\n");
+ snd_printk(KERN_ERR "can't create port\n");
return -ENOMEM;
}
@@ -147,12 +147,12 @@
/* Allocate structures for this channel */
if ((p = kzalloc(sizeof(*p), GFP_KERNEL)) == NULL) {
- snd_printk("no memory\n");
+ snd_printk(KERN_ERR "no memory\n");
return NULL;
}
p->chset.channels = kcalloc(max_channels, sizeof(struct snd_midi_channel), GFP_KERNEL);
if (p->chset.channels == NULL) {
- snd_printk("no memory\n");
+ snd_printk(KERN_ERR "no memory\n");
kfree(p);
return NULL;
}
@@ -376,12 +376,12 @@
goto __error;
}
emu->vmidi[i] = rmidi;
- //snd_printk("virmidi %d ok\n", i);
+ /* snd_printk(KERN_DEBUG "virmidi %d ok\n", i); */
}
return 0;
__error:
- //snd_printk("error init..\n");
+ /* snd_printk(KERN_DEBUG "error init..\n"); */
snd_emux_delete_virmidi(emu);
return -ENOMEM;
}
diff --git a/sound/synth/emux/emux_synth.c b/sound/synth/emux/emux_synth.c
index 2cc6f6f..3e921b3 100644
--- a/sound/synth/emux/emux_synth.c
+++ b/sound/synth/emux/emux_synth.c
@@ -956,7 +956,8 @@
if (emu->voices[voice].state == SNDRV_EMUX_ST_OFF)
emu->voices[voice].state = SNDRV_EMUX_ST_LOCKED;
else
- snd_printk("invalid voice for lock %d (state = %x)\n",
+ snd_printk(KERN_WARNING
+ "invalid voice for lock %d (state = %x)\n",
voice, emu->voices[voice].state);
spin_unlock_irqrestore(&emu->voice_lock, flags);
}
@@ -973,7 +974,8 @@
if (emu->voices[voice].state == SNDRV_EMUX_ST_LOCKED)
emu->voices[voice].state = SNDRV_EMUX_ST_OFF;
else
- snd_printk("invalid voice for unlock %d (state = %x)\n",
+ snd_printk(KERN_WARNING
+ "invalid voice for unlock %d (state = %x)\n",
voice, emu->voices[voice].state);
spin_unlock_irqrestore(&emu->voice_lock, flags);
}
diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c
index 36d53bd..63c8f45 100644
--- a/sound/synth/emux/soundfont.c
+++ b/sound/synth/emux/soundfont.c
@@ -133,7 +133,7 @@
int rc;
if (count < (long)sizeof(patch)) {
- snd_printk("patch record too small %ld\n", count);
+ snd_printk(KERN_ERR "patch record too small %ld\n", count);
return -EINVAL;
}
if (copy_from_user(&patch, data, sizeof(patch)))
@@ -143,15 +143,16 @@
data += sizeof(patch);
if (patch.key != SNDRV_OSS_SOUNDFONT_PATCH) {
- snd_printk("'The wrong kind of patch' %x\n", patch.key);
+ snd_printk(KERN_ERR "The wrong kind of patch %x\n", patch.key);
return -EINVAL;
}
if (count < patch.len) {
- snd_printk("Patch too short %ld, need %d\n", count, patch.len);
+ snd_printk(KERN_ERR "Patch too short %ld, need %d\n",
+ count, patch.len);
return -EINVAL;
}
if (patch.len < 0) {
- snd_printk("poor length %d\n", patch.len);
+ snd_printk(KERN_ERR "poor length %d\n", patch.len);
return -EINVAL;
}
@@ -195,7 +196,8 @@
case SNDRV_SFNT_REMOVE_INFO:
/* patch must be opened */
if (!sflist->currsf) {
- snd_printk("soundfont: remove_info: patch not opened\n");
+ snd_printk(KERN_ERR "soundfont: remove_info: "
+ "patch not opened\n");
rc = -EINVAL;
} else {
int bank, instr;
@@ -531,7 +533,7 @@
return -EINVAL;
if (count < (long)sizeof(hdr)) {
- printk("Soundfont error: invalid patch zone length\n");
+ printk(KERN_ERR "Soundfont error: invalid patch zone length\n");
return -EINVAL;
}
if (copy_from_user((char*)&hdr, data, sizeof(hdr)))
@@ -541,12 +543,14 @@
count -= sizeof(hdr);
if (hdr.nvoices <= 0 || hdr.nvoices >= 100) {
- printk("Soundfont error: Illegal voice number %d\n", hdr.nvoices);
+ printk(KERN_ERR "Soundfont error: Illegal voice number %d\n",
+ hdr.nvoices);
return -EINVAL;
}
if (count < (long)sizeof(struct soundfont_voice_info) * hdr.nvoices) {
- printk("Soundfont Error: patch length(%ld) is smaller than nvoices(%d)\n",
+ printk(KERN_ERR "Soundfont Error: "
+ "patch length(%ld) is smaller than nvoices(%d)\n",
count, hdr.nvoices);
return -EINVAL;
}
@@ -952,7 +956,7 @@
int rc;
if (count < (long)sizeof(patch)) {
- snd_printk("patch record too small %ld\n", count);
+ snd_printk(KERN_ERR "patch record too small %ld\n", count);
return -EINVAL;
}
if (copy_from_user(&patch, data, sizeof(patch)))
@@ -1034,7 +1038,8 @@
/* panning position; -128 - 127 => 0-127 */
zone->v.pan = (patch.panning + 128) / 2;
#if 0
- snd_printk("gus: basefrq=%d (ofs=%d) root=%d,tune=%d, range:%d-%d\n",
+ snd_printk(KERN_DEBUG
+ "gus: basefrq=%d (ofs=%d) root=%d,tune=%d, range:%d-%d\n",
(int)patch.base_freq, zone->v.rate_offset,
zone->v.root, zone->v.tune, zone->v.low, zone->v.high);
#endif
@@ -1068,7 +1073,8 @@
zone->v.parm.volrelease = 0x8000 | snd_sf_calc_parm_decay(release);
zone->v.attenuation = calc_gus_attenuation(patch.env_offset[0]);
#if 0
- snd_printk("gus: atkhld=%x, dcysus=%x, volrel=%x, att=%d\n",
+ snd_printk(KERN_DEBUG
+ "gus: atkhld=%x, dcysus=%x, volrel=%x, att=%d\n",
zone->v.parm.volatkhld,
zone->v.parm.voldcysus,
zone->v.parm.volrelease,
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index 4f0eac9..523aec1 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -48,7 +48,10 @@
* Native Instruments Kore Controller
* Native Instruments Kore Controller 2
* Native Instruments Audio Kontrol 1
+ * Native Instruments Audio 4 DJ
* Native Instruments Audio 8 DJ
+ * Native Instruments Guitar Rig Session I/O
+ * Native Instruments Guitar Rig mobile
To compile this driver as a module, choose M here: the module
will be called snd-usb-caiaq.
diff --git a/sound/usb/caiaq/caiaq-audio.c b/sound/usb/caiaq/caiaq-audio.c
index b3a6033..08d51e0 100644
--- a/sound/usb/caiaq/caiaq-audio.c
+++ b/sound/usb/caiaq/caiaq-audio.c
@@ -114,6 +114,7 @@
dev->output_panic = 0;
dev->first_packet = 1;
dev->streaming = 1;
+ dev->warned = 0;
for (i = 0; i < N_URBS; i++) {
ret = usb_submit_urb(dev->data_urbs_in[i], GFP_ATOMIC);
@@ -376,6 +377,9 @@
for (stream = 0; stream < dev->n_streams; stream++, i++) {
sub = dev->sub_capture[stream];
+ if (dev->input_panic)
+ usb_buf[i] = 0;
+
if (sub) {
struct snd_pcm_runtime *rt = sub->runtime;
char *audio_buf = rt->dma_area;
@@ -397,6 +401,9 @@
if (!dev->streaming)
return;
+ if (iso->actual_length < dev->bpp)
+ return;
+
switch (dev->spec.data_alignment) {
case 0:
read_in_urb_mode0(dev, urb, iso);
@@ -406,10 +413,11 @@
break;
}
- if (dev->input_panic || dev->output_panic) {
+ if ((dev->input_panic || dev->output_panic) && !dev->warned) {
debug("streaming error detected %s %s\n",
dev->input_panic ? "(input)" : "",
dev->output_panic ? "(output)" : "");
+ dev->warned = 1;
}
}
@@ -638,9 +646,10 @@
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_SESSIONIO):
- dev->samplerates |= SNDRV_PCM_RATE_88200;
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_GUITARRIGMOBILE):
dev->samplerates |= SNDRV_PCM_RATE_192000;
- break;
+ /* fall thru */
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
dev->samplerates |= SNDRV_PCM_RATE_88200;
break;
diff --git a/sound/usb/caiaq/caiaq-control.c b/sound/usb/caiaq/caiaq-control.c
index ccd763d..e92c2bb 100644
--- a/sound/usb/caiaq/caiaq-control.c
+++ b/sound/usb/caiaq/caiaq-control.c
@@ -39,12 +39,12 @@
struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
int pos = kcontrol->private_value;
int is_intval = pos & CNT_INTVAL;
+ unsigned int id = dev->chip.usb_id;
uinfo->count = 1;
pos &= ~CNT_INTVAL;
- if (dev->chip.usb_id ==
- USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ)
+ if (id == USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ)
&& (pos == 0)) {
/* current input mode of A8DJ */
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -53,6 +53,15 @@
return 0;
}
+ if (id == USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ)
+ && (pos == 0)) {
+ /* current input mode of A4DJ */
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+ }
+
if (is_intval) {
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->value.integer.min = 0;
@@ -73,6 +82,14 @@
struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
int pos = kcontrol->private_value;
+ if (dev->chip.usb_id ==
+ USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ)) {
+ /* A4DJ has only one control */
+ /* do not expose hardware input mode 0 */
+ ucontrol->value.integer.value[0] = dev->control_state[0] - 1;
+ return 0;
+ }
+
if (pos & CNT_INTVAL)
ucontrol->value.integer.value[0]
= dev->control_state[pos & ~CNT_INTVAL];
@@ -90,10 +107,20 @@
struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
int pos = kcontrol->private_value;
+ if (dev->chip.usb_id ==
+ USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ)) {
+ /* A4DJ has only one control */
+ /* do not expose hardware input mode 0 */
+ dev->control_state[0] = ucontrol->value.integer.value[0] + 1;
+ snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO,
+ dev->control_state, sizeof(dev->control_state));
+ return 1;
+ }
+
if (pos & CNT_INTVAL) {
dev->control_state[pos & ~CNT_INTVAL]
= ucontrol->value.integer.value[0];
- snd_usb_caiaq_send_command(dev, EP1_CMD_DIMM_LEDS,
+ snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO,
dev->control_state, sizeof(dev->control_state));
} else {
if (ucontrol->value.integer.value[0])
@@ -243,10 +270,13 @@
{ "GND lift for TC Vinyl mode", 24 + 0 },
{ "GND lift for TC CD/Line mode", 24 + 1 },
{ "GND lift for phono mode", 24 + 2 },
- { "GND lift for TC Vinyl mode", 24 + 3 },
{ "Software lock", 40 }
};
+static struct caiaq_controller a4dj_controller[] = {
+ { "Current input mode", 0 | CNT_INTVAL }
+};
+
static int __devinit add_controls(struct caiaq_controller *c, int num,
struct snd_usb_caiaqdev *dev)
{
@@ -295,6 +325,10 @@
ret = add_controls(a8dj_controller,
ARRAY_SIZE(a8dj_controller), dev);
break;
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ):
+ ret = add_controls(a4dj_controller,
+ ARRAY_SIZE(a4dj_controller), dev);
+ break;
}
return ret;
diff --git a/sound/usb/caiaq/caiaq-device.c b/sound/usb/caiaq/caiaq-device.c
index 41c36b0..cf573a9 100644
--- a/sound/usb/caiaq/caiaq-device.c
+++ b/sound/usb/caiaq/caiaq-device.c
@@ -42,15 +42,17 @@
#endif
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
-MODULE_DESCRIPTION("caiaq USB audio, version 1.3.10");
+MODULE_DESCRIPTION("caiaq USB audio, version 1.3.13");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
"{Native Instruments, RigKontrol3},"
"{Native Instruments, Kore Controller},"
"{Native Instruments, Kore Controller 2},"
"{Native Instruments, Audio Kontrol 1},"
+ "{Native Instruments, Audio 4 DJ},"
"{Native Instruments, Audio 8 DJ},"
- "{Native Instruments, Session I/O}}");
+ "{Native Instruments, Session I/O},"
+ "{Native Instruments, GuitarRig mobile}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
@@ -116,6 +118,16 @@
.idVendor = USB_VID_NATIVEINSTRUMENTS,
.idProduct = USB_PID_SESSIONIO
},
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = USB_VID_NATIVEINSTRUMENTS,
+ .idProduct = USB_PID_GUITARRIGMOBILE
+ },
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = USB_VID_NATIVEINSTRUMENTS,
+ .idProduct = USB_PID_AUDIO4DJ
+ },
{ /* terminator */ }
};
@@ -239,6 +251,8 @@
if (dev->audio_parm_answer != 1)
debug("unable to set the device's audio params\n");
+ else
+ dev->bpp = bpp;
return dev->audio_parm_answer == 1 ? 0 : -EINVAL;
}
@@ -300,6 +314,12 @@
}
break;
+ case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ):
+ /* Audio 4 DJ - default input mode to phono */
+ dev->control_state[0] = 2;
+ snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO,
+ dev->control_state, 1);
+ break;
}
if (dev->spec.num_analog_audio_out +
@@ -336,9 +356,10 @@
log("Unable to set up control system (ret=%d)\n", ret);
}
-static struct snd_card* create_card(struct usb_device* usb_dev)
+static int create_card(struct usb_device* usb_dev, struct snd_card **cardp)
{
int devnum;
+ int err;
struct snd_card *card;
struct snd_usb_caiaqdev *dev;
@@ -347,12 +368,12 @@
break;
if (devnum >= SNDRV_CARDS)
- return NULL;
+ return -ENODEV;
- card = snd_card_new(index[devnum], id[devnum], THIS_MODULE,
- sizeof(struct snd_usb_caiaqdev));
- if (!card)
- return NULL;
+ err = snd_card_create(index[devnum], id[devnum], THIS_MODULE,
+ sizeof(struct snd_usb_caiaqdev), &card);
+ if (err < 0)
+ return err;
dev = caiaqdev(card);
dev->chip.dev = usb_dev;
@@ -362,7 +383,8 @@
spin_lock_init(&dev->spinlock);
snd_card_set_dev(card, &usb_dev->dev);
- return card;
+ *cardp = card;
+ return 0;
}
static int __devinit init_card(struct snd_usb_caiaqdev *dev)
@@ -441,10 +463,10 @@
struct snd_card *card;
struct usb_device *device = interface_to_usbdev(intf);
- card = create_card(device);
+ ret = create_card(device, &card);
- if (!card)
- return -ENOMEM;
+ if (ret < 0)
+ return ret;
usb_set_intfdata(intf, card);
ret = init_card(caiaqdev(card));
diff --git a/sound/usb/caiaq/caiaq-device.h b/sound/usb/caiaq/caiaq-device.h
index ab56e73..4cce1ad 100644
--- a/sound/usb/caiaq/caiaq-device.h
+++ b/sound/usb/caiaq/caiaq-device.h
@@ -10,8 +10,10 @@
#define USB_PID_KORECONTROLLER 0x4711
#define USB_PID_KORECONTROLLER2 0x4712
#define USB_PID_AK1 0x0815
+#define USB_PID_AUDIO4DJ 0x0839
#define USB_PID_AUDIO8DJ 0x1978
#define USB_PID_SESSIONIO 0x1915
+#define USB_PID_GUITARRIGMOBILE 0x0d8d
#define EP1_BUFSIZE 64
#define CAIAQ_USB_STR_LEN 0xff
@@ -87,9 +89,9 @@
int audio_out_buf_pos[MAX_STREAMS];
int period_in_count[MAX_STREAMS];
int period_out_count[MAX_STREAMS];
- int input_panic, output_panic;
+ int input_panic, output_panic, warned;
char *audio_in_buf, *audio_out_buf;
- unsigned int samplerates;
+ unsigned int samplerates, bpp;
struct snd_pcm_substream *sub_playback[MAX_STREAMS];
struct snd_pcm_substream *sub_capture[MAX_STREAMS];
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index 19e3745..c2db0f9 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -107,7 +107,7 @@
#define MAX_PACKS_HS (MAX_PACKS * 8) /* in high speed mode */
#define MAX_URBS 8
#define SYNC_URBS 4 /* always four urbs for sync */
-#define MIN_PACKS_URB 1 /* minimum 1 packet per urb */
+#define MAX_QUEUE 24 /* try not to exceed this queue length, in ms */
struct audioformat {
struct list_head list;
@@ -525,7 +525,7 @@
/*
* Prepare urb for streaming before playback starts or when paused.
*
- * We don't have any data, so we send a frame of silence.
+ * We don't have any data, so we send silence.
*/
static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
@@ -537,13 +537,13 @@
offs = 0;
urb->dev = ctx->subs->dev;
- urb->number_of_packets = subs->packs_per_ms;
- for (i = 0; i < subs->packs_per_ms; ++i) {
+ for (i = 0; i < ctx->packets; ++i) {
counts = snd_usb_audio_next_packet_size(subs);
urb->iso_frame_desc[i].offset = offs * stride;
urb->iso_frame_desc[i].length = counts * stride;
offs += counts;
}
+ urb->number_of_packets = ctx->packets;
urb->transfer_buffer_length = offs * stride;
memset(urb->transfer_buffer,
subs->cur_audiofmt->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0,
@@ -1034,9 +1034,9 @@
static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int period_bytes,
unsigned int rate, unsigned int frame_bits)
{
- unsigned int maxsize, n, i;
+ unsigned int maxsize, i;
int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
- unsigned int npacks[MAX_URBS], urb_packs, total_packs, packs_per_ms;
+ unsigned int urb_packs, total_packs, packs_per_ms;
/* calculate the frequency in 16.16 format */
if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
@@ -1070,8 +1070,7 @@
subs->packs_per_ms = packs_per_ms;
if (is_playback) {
- urb_packs = nrpacks;
- urb_packs = max(urb_packs, (unsigned int)MIN_PACKS_URB);
+ urb_packs = max(nrpacks, 1);
urb_packs = min(urb_packs, (unsigned int)MAX_PACKS);
} else
urb_packs = 1;
@@ -1079,7 +1078,7 @@
/* decide how many packets to be used */
if (is_playback) {
- unsigned int minsize;
+ unsigned int minsize, maxpacks;
/* determine how small a packet can be */
minsize = (subs->freqn >> (16 - subs->datainterval))
* (frame_bits >> 3);
@@ -1092,8 +1091,13 @@
total_packs = (total_packs + packs_per_ms - 1)
& ~(packs_per_ms - 1);
/* we need at least two URBs for queueing */
- if (total_packs < 2 * MIN_PACKS_URB * packs_per_ms)
- total_packs = 2 * MIN_PACKS_URB * packs_per_ms;
+ if (total_packs < 2 * packs_per_ms) {
+ total_packs = 2 * packs_per_ms;
+ } else {
+ /* and we don't want too long a queue either */
+ maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
+ total_packs = min(total_packs, maxpacks);
+ }
} else {
total_packs = MAX_URBS * urb_packs;
}
@@ -1102,31 +1106,11 @@
/* too much... */
subs->nurbs = MAX_URBS;
total_packs = MAX_URBS * urb_packs;
- }
- n = total_packs;
- for (i = 0; i < subs->nurbs; i++) {
- npacks[i] = n > urb_packs ? urb_packs : n;
- n -= urb_packs;
- }
- if (subs->nurbs <= 1) {
+ } else if (subs->nurbs < 2) {
/* too little - we need at least two packets
* to ensure contiguous playback/capture
*/
subs->nurbs = 2;
- npacks[0] = (total_packs + 1) / 2;
- npacks[1] = total_packs - npacks[0];
- } else if (npacks[subs->nurbs-1] < MIN_PACKS_URB * packs_per_ms) {
- /* the last packet is too small.. */
- if (subs->nurbs > 2) {
- /* merge to the first one */
- npacks[0] += npacks[subs->nurbs - 1];
- subs->nurbs--;
- } else {
- /* divide to two */
- subs->nurbs = 2;
- npacks[0] = (total_packs + 1) / 2;
- npacks[1] = total_packs - npacks[0];
- }
}
/* allocate and initialize data urbs */
@@ -1134,7 +1118,8 @@
struct snd_urb_ctx *u = &subs->dataurb[i];
u->index = i;
u->subs = subs;
- u->packets = npacks[i];
+ u->packets = (i + 1) * total_packs / subs->nurbs
+ - i * total_packs / subs->nurbs;
u->buffer_size = maxsize * u->packets;
if (subs->fmt_type == USB_FORMAT_TYPE_II)
u->packets++; /* for transfer delimiter */
@@ -1292,14 +1277,14 @@
if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR,
USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000)) < 0) {
- snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep 0x%x\n",
+ snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
dev->devnum, iface, fmt->altsetting, rate, ep);
return err;
}
if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR,
USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN,
SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000)) < 0) {
- snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep 0x%x\n",
+ snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
dev->devnum, iface, fmt->altsetting, ep);
return 0; /* some devices don't support reading */
}
@@ -1431,9 +1416,11 @@
subs->cur_audiofmt = fmt;
#if 0
- printk("setting done: format = %d, rate = %d..%d, channels = %d\n",
+ printk(KERN_DEBUG
+ "setting done: format = %d, rate = %d..%d, channels = %d\n",
fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels);
- printk(" datapipe = 0x%0x, syncpipe = 0x%0x\n",
+ printk(KERN_DEBUG
+ " datapipe = 0x%0x, syncpipe = 0x%0x\n",
subs->datapipe, subs->syncpipe);
#endif
@@ -1468,7 +1455,7 @@
channels = params_channels(hw_params);
fmt = find_format(subs, format, rate, channels);
if (!fmt) {
- snd_printd(KERN_DEBUG "cannot set format: format = 0x%x, rate = %d, channels = %d\n",
+ snd_printd(KERN_DEBUG "cannot set format: format = %#x, rate = %d, channels = %d\n",
format, rate, channels);
return -EINVAL;
}
@@ -1795,7 +1782,7 @@
if (rates[f->format] && rates[f->format] != f->rates)
goto __out;
}
- channels[f->format] |= (1 << f->channels);
+ channels[f->format] |= 1 << (f->channels - 1);
rates[f->format] |= f->rates;
/* needs knot? */
if (f->rates & SNDRV_PCM_RATE_KNOT)
@@ -1822,7 +1809,7 @@
continue;
for (i = 0; i < 32; i++) {
if (f->rates & (1 << i))
- channels[i] |= (1 << f->channels);
+ channels[i] |= 1 << (f->channels - 1);
}
}
cmaster = 0;
@@ -1919,7 +1906,7 @@
* in the current code assume the 1ms period.
*/
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
- 1000 * MIN_PACKS_URB,
+ 1000,
/*(nrpacks * MAX_URBS) * 1000*/ UINT_MAX);
err = check_hw_params_convention(subs);
@@ -2160,7 +2147,7 @@
fp = list_entry(p, struct audioformat, list);
snd_iprintf(buffer, " Interface %d\n", fp->iface);
snd_iprintf(buffer, " Altset %d\n", fp->altsetting);
- snd_iprintf(buffer, " Format: 0x%x\n", fp->format);
+ snd_iprintf(buffer, " Format: %#x\n", fp->format);
snd_iprintf(buffer, " Channels: %d\n", fp->channels);
snd_iprintf(buffer, " Endpoint: %d %s (%s)\n",
fp->endpoint & USB_ENDPOINT_NUMBER_MASK,
@@ -2180,7 +2167,7 @@
snd_iprintf(buffer, "\n");
}
// snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize);
- // snd_iprintf(buffer, " EP Attribute = 0x%x\n", fp->attributes);
+ // snd_iprintf(buffer, " EP Attribute = %#x\n", fp->attributes);
}
}
@@ -2621,7 +2608,7 @@
fp->format = SNDRV_PCM_FORMAT_MPEG;
break;
default:
- snd_printd(KERN_INFO "%d:%u:%d : unknown format tag 0x%x is detected. processed as MPEG.\n",
+ snd_printd(KERN_INFO "%d:%u:%d : unknown format tag %#x is detected. processed as MPEG.\n",
chip->dev->devnum, fp->iface, fp->altsetting, format);
fp->format = SNDRV_PCM_FORMAT_MPEG;
break;
@@ -2819,7 +2806,7 @@
continue;
}
- snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint 0x%x\n", dev->devnum, iface_no, altno, fp->endpoint);
+ snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint %#x\n", dev->devnum, iface_no, altno, fp->endpoint);
err = add_audio_endpoint(chip, stream, fp);
if (err < 0) {
kfree(fp->rate_table);
@@ -3466,10 +3453,10 @@
return -ENXIO;
}
- card = snd_card_new(index[idx], id[idx], THIS_MODULE, 0);
- if (card == NULL) {
+ err = snd_card_create(index[idx], id[idx], THIS_MODULE, 0, &card);
+ if (err < 0) {
snd_printk(KERN_ERR "cannot create card instance %d\n", idx);
- return -ENOMEM;
+ return err;
}
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
@@ -3766,7 +3753,7 @@
static int __init snd_usb_audio_init(void)
{
- if (nrpacks < MIN_PACKS_URB || nrpacks > MAX_PACKS) {
+ if (nrpacks < 1 || nrpacks > MAX_PACKS) {
printk(KERN_WARNING "invalid nrpacks value.\n");
return -EINVAL;
}
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
index 00397c8..ecb58e7 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/usbmixer.c
@@ -66,6 +66,7 @@
{ USB_ID(0x041e, 0x3000), 0, 1, 2, 1, 18, 0x0013 }, /* Extigy */
{ 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, 0x3048), 2, 2, 6, 6, 2, 0x6e91 }, /* Toshiba SB0500 */
};
struct usb_mixer_interface {
@@ -78,7 +79,6 @@
/* Sound Blaster remote control stuff */
const struct rc_config *rc_cfg;
- unsigned long rc_hwdep_open;
u32 rc_code;
wait_queue_head_t rc_waitq;
struct urb *rc_urb;
@@ -110,6 +110,8 @@
const struct usbmix_selector_map *selector_map;
};
+#define MAX_CHANNELS 10 /* max logical channels */
+
struct usb_mixer_elem_info {
struct usb_mixer_interface *mixer;
struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */
@@ -120,6 +122,8 @@
int channels;
int val_type;
int min, max, res;
+ int cached;
+ int cache_val[MAX_CHANNELS];
u8 initialized;
};
@@ -181,8 +185,6 @@
USB_PROC_DCR_RELEASE = 6,
};
-#define MAX_CHANNELS 10 /* max logical channels */
-
/*
* manual mapping of mixer names
@@ -219,7 +221,10 @@
for (p = state->map; p->id; p++) {
if (p->id == unitid && ! p->name &&
(! control || ! p->control || control == p->control)) {
- // printk("ignored control %d:%d\n", unitid, control);
+ /*
+ printk(KERN_DEBUG "ignored control %d:%d\n",
+ unitid, control);
+ */
return 1;
}
}
@@ -376,11 +381,35 @@
}
/* channel = 0: master, 1 = first channel */
-static inline int get_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int *value)
+static inline int get_cur_mix_raw(struct usb_mixer_elem_info *cval,
+ int channel, int *value)
{
return get_ctl_value(cval, GET_CUR, (cval->control << 8) | channel, value);
}
+static int get_cur_mix_value(struct usb_mixer_elem_info *cval,
+ int channel, int index, int *value)
+{
+ int err;
+
+ if (cval->cached & (1 << channel)) {
+ *value = cval->cache_val[index];
+ return 0;
+ }
+ err = get_cur_mix_raw(cval, channel, value);
+ if (err < 0) {
+ if (!cval->mixer->ignore_ctl_error)
+ snd_printd(KERN_ERR "cannot get current value for "
+ "control %d ch %d: err = %d\n",
+ cval->control, channel, err);
+ return err;
+ }
+ cval->cached |= 1 << channel;
+ cval->cache_val[index] = *value;
+ return 0;
+}
+
+
/*
* set a mixer value
*/
@@ -412,9 +441,17 @@
return set_ctl_value(cval, SET_CUR, validx, value);
}
-static inline int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int value)
+static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
+ int index, int value)
{
- return set_ctl_value(cval, SET_CUR, (cval->control << 8) | channel, value);
+ int err;
+ err = set_ctl_value(cval, SET_CUR, (cval->control << 8) | channel,
+ value);
+ if (err < 0)
+ return err;
+ cval->cached |= 1 << channel;
+ cval->cache_val[index] = value;
+ return 0;
}
/*
@@ -718,7 +755,7 @@
if (cval->min + cval->res < cval->max) {
int last_valid_res = cval->res;
int saved, test, check;
- get_cur_mix_value(cval, minchn, &saved);
+ get_cur_mix_raw(cval, minchn, &saved);
for (;;) {
test = saved;
if (test < cval->max)
@@ -726,8 +763,8 @@
else
test -= cval->res;
if (test < cval->min || test > cval->max ||
- set_cur_mix_value(cval, minchn, test) ||
- get_cur_mix_value(cval, minchn, &check)) {
+ set_cur_mix_value(cval, minchn, 0, test) ||
+ get_cur_mix_raw(cval, minchn, &check)) {
cval->res = last_valid_res;
break;
}
@@ -735,7 +772,7 @@
break;
cval->res *= 2;
}
- set_cur_mix_value(cval, minchn, saved);
+ set_cur_mix_value(cval, minchn, 0, saved);
}
cval->initialized = 1;
@@ -775,35 +812,25 @@
struct usb_mixer_elem_info *cval = kcontrol->private_data;
int c, cnt, val, err;
+ ucontrol->value.integer.value[0] = cval->min;
if (cval->cmask) {
cnt = 0;
for (c = 0; c < MAX_CHANNELS; c++) {
- if (cval->cmask & (1 << c)) {
- err = get_cur_mix_value(cval, c + 1, &val);
- if (err < 0) {
- if (cval->mixer->ignore_ctl_error) {
- ucontrol->value.integer.value[0] = cval->min;
- return 0;
- }
- snd_printd(KERN_ERR "cannot get current value for control %d ch %d: err = %d\n", cval->control, c + 1, err);
- return err;
- }
- val = get_relative_value(cval, val);
- ucontrol->value.integer.value[cnt] = val;
- cnt++;
- }
+ if (!(cval->cmask & (1 << c)))
+ continue;
+ err = get_cur_mix_value(cval, c + 1, cnt, &val);
+ if (err < 0)
+ return cval->mixer->ignore_ctl_error ? 0 : err;
+ val = get_relative_value(cval, val);
+ ucontrol->value.integer.value[cnt] = val;
+ cnt++;
}
+ return 0;
} else {
/* master channel */
- err = get_cur_mix_value(cval, 0, &val);
- if (err < 0) {
- if (cval->mixer->ignore_ctl_error) {
- ucontrol->value.integer.value[0] = cval->min;
- return 0;
- }
- snd_printd(KERN_ERR "cannot get current value for control %d master ch: err = %d\n", cval->control, err);
- return err;
- }
+ err = get_cur_mix_value(cval, 0, 0, &val);
+ if (err < 0)
+ return cval->mixer->ignore_ctl_error ? 0 : err;
val = get_relative_value(cval, val);
ucontrol->value.integer.value[0] = val;
}
@@ -820,34 +847,28 @@
if (cval->cmask) {
cnt = 0;
for (c = 0; c < MAX_CHANNELS; c++) {
- if (cval->cmask & (1 << c)) {
- err = get_cur_mix_value(cval, c + 1, &oval);
- if (err < 0) {
- if (cval->mixer->ignore_ctl_error)
- return 0;
- return err;
- }
- val = ucontrol->value.integer.value[cnt];
- val = get_abs_value(cval, val);
- if (oval != val) {
- set_cur_mix_value(cval, c + 1, val);
- changed = 1;
- }
- get_cur_mix_value(cval, c + 1, &val);
- cnt++;
+ if (!(cval->cmask & (1 << c)))
+ continue;
+ err = get_cur_mix_value(cval, c + 1, cnt, &oval);
+ if (err < 0)
+ return cval->mixer->ignore_ctl_error ? 0 : err;
+ val = ucontrol->value.integer.value[cnt];
+ val = get_abs_value(cval, val);
+ if (oval != val) {
+ set_cur_mix_value(cval, c + 1, cnt, val);
+ changed = 1;
}
+ cnt++;
}
} else {
/* master channel */
- err = get_cur_mix_value(cval, 0, &oval);
- if (err < 0 && cval->mixer->ignore_ctl_error)
- return 0;
+ err = get_cur_mix_value(cval, 0, 0, &oval);
if (err < 0)
- return err;
+ return cval->mixer->ignore_ctl_error ? 0 : err;
val = ucontrol->value.integer.value[0];
val = get_abs_value(cval, val);
if (val != oval) {
- set_cur_mix_value(cval, 0, val);
+ set_cur_mix_value(cval, 0, 0, val);
changed = 1;
}
}
@@ -1706,7 +1727,8 @@
break;
/* live24ext: 4 = line-in jack */
case 3: /* hp-out jack (may actuate Mute) */
- if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040))
+ if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+ mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id);
break;
default:
@@ -1797,24 +1819,6 @@
wake_up(&mixer->rc_waitq);
}
-static int snd_usb_sbrc_hwdep_open(struct snd_hwdep *hw, struct file *file)
-{
- struct usb_mixer_interface *mixer = hw->private_data;
-
- if (test_and_set_bit(0, &mixer->rc_hwdep_open))
- return -EBUSY;
- return 0;
-}
-
-static int snd_usb_sbrc_hwdep_release(struct snd_hwdep *hw, struct file *file)
-{
- struct usb_mixer_interface *mixer = hw->private_data;
-
- clear_bit(0, &mixer->rc_hwdep_open);
- smp_mb__after_clear_bit();
- return 0;
-}
-
static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf,
long count, loff_t *offset)
{
@@ -1867,9 +1871,8 @@
hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC;
hwdep->private_data = mixer;
hwdep->ops.read = snd_usb_sbrc_hwdep_read;
- hwdep->ops.open = snd_usb_sbrc_hwdep_open;
- hwdep->ops.release = snd_usb_sbrc_hwdep_release;
hwdep->ops.poll = snd_usb_sbrc_hwdep_poll;
+ hwdep->exclusive = 1;
mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!mixer->rc_urb)
@@ -1956,8 +1959,9 @@
int i, err;
for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) {
- if (i > 1 && /* Live24ext has 2 LEDs only */
- mixer->chip->usb_id == USB_ID(0x041e, 0x3040))
+ if (i > 1 && /* Live24ext has 2 LEDs only */
+ (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+ mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
break;
err = snd_ctl_add(mixer->chip->card,
snd_ctl_new1(&snd_audigy2nx_controls[i], mixer));
@@ -1994,7 +1998,8 @@
snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname);
if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020))
jacks = jacks_audigy2nx;
- else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040))
+ else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+ mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
jacks = jacks_live24ext;
else
return;
@@ -2044,7 +2049,8 @@
goto _error;
if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020) ||
- mixer->chip->usb_id == USB_ID(0x041e, 0x3040)) {
+ mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+ mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) {
struct snd_info_entry *entry;
if ((err = snd_audigy2nx_controls_create(mixer)) < 0)
diff --git a/sound/usb/usbmixer_maps.c b/sound/usb/usbmixer_maps.c
index d755be0..3e5d66c 100644
--- a/sound/usb/usbmixer_maps.c
+++ b/sound/usb/usbmixer_maps.c
@@ -261,6 +261,22 @@
{} /* terminator */
};
+static struct usbmix_name_map scratch_live_map[] = {
+ /* 1: IT Line 1 (USB streaming) */
+ /* 2: OT Line 1 (Speaker) */
+ /* 3: IT Line 1 (Line connector) */
+ { 4, "Line 1 In" }, /* FU */
+ /* 5: OT Line 1 (USB streaming) */
+ /* 6: IT Line 2 (USB streaming) */
+ /* 7: OT Line 2 (Speaker) */
+ /* 8: IT Line 2 (Line connector) */
+ { 9, "Line 2 In" }, /* FU */
+ /* 10: OT Line 2 (USB streaming) */
+ /* 11: IT Mic (Line connector) */
+ /* 12: OT Mic (USB streaming) */
+ { 0 } /* terminator */
+};
+
/*
* Control map entries
*/
@@ -285,6 +301,11 @@
.map = live24ext_map,
},
{
+ .id = USB_ID(0x041e, 0x3048),
+ .map = audigy2nx_map,
+ .selector_map = audigy2nx_selectors,
+ },
+ {
/* Hercules DJ Console (Windows Edition) */
.id = USB_ID(0x06f8, 0xb000),
.ignore_ctl_error = 1,
@@ -311,6 +332,11 @@
.id = USB_ID(0x0ccd, 0x0028),
.map = aureon_51_2_map,
},
+ {
+ .id = USB_ID(0x13e5, 0x0001),
+ .map = scratch_live_map,
+ .ignore_ctl_error = 1,
+ },
{ 0 } /* terminator */
};
diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h
index 5d8ef09..647ef50 100644
--- a/sound/usb/usbquirks.h
+++ b/sound/usb/usbquirks.h
@@ -39,6 +39,16 @@
.idProduct = prod, \
.bInterfaceClass = USB_CLASS_VENDOR_SPEC
+/* Creative/Toshiba Multimedia Center SB-0500 */
+{
+ USB_DEVICE(0x041e, 0x3048),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .vendor_name = "Toshiba",
+ .product_name = "SB-0500",
+ .ifnum = QUIRK_NO_INTERFACE
+ }
+},
+
/* Creative/E-Mu devices */
{
USB_DEVICE(0x041e, 0x3010),
diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c
index 73e59f4..98276aa 100644
--- a/sound/usb/usx2y/us122l.c
+++ b/sound/usb/usx2y/us122l.c
@@ -478,19 +478,21 @@
return true;
}
-static struct snd_card *usx2y_create_card(struct usb_device *device)
+static int usx2y_create_card(struct usb_device *device, struct snd_card **cardp)
{
int dev;
struct snd_card *card;
+ int err;
+
for (dev = 0; dev < SNDRV_CARDS; ++dev)
if (enable[dev] && !snd_us122l_card_used[dev])
break;
if (dev >= SNDRV_CARDS)
- return NULL;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE,
- sizeof(struct us122l));
- if (!card)
- return NULL;
+ return -ENODEV;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct us122l), &card);
+ if (err < 0)
+ return err;
snd_us122l_card_used[US122L(card)->chip.index = dev] = 1;
US122L(card)->chip.dev = device;
@@ -509,46 +511,57 @@
US122L(card)->chip.dev->devnum
);
snd_card_set_dev(card, &device->dev);
- return card;
+ *cardp = card;
+ return 0;
}
-static void *us122l_usb_probe(struct usb_interface *intf,
- const struct usb_device_id *device_id)
+static int us122l_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *device_id,
+ struct snd_card **cardp)
{
struct usb_device *device = interface_to_usbdev(intf);
- struct snd_card *card = usx2y_create_card(device);
+ struct snd_card *card;
+ int err;
- if (!card)
- return NULL;
+ err = usx2y_create_card(device, &card);
+ if (err < 0)
+ return err;
- if (!us122l_create_card(card) ||
- snd_card_register(card) < 0) {
+ if (!us122l_create_card(card)) {
snd_card_free(card);
- return NULL;
+ return -EINVAL;
+ }
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
}
usb_get_dev(device);
- return card;
+ *cardp = card;
+ return 0;
}
static int snd_us122l_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct snd_card *card;
+ int err;
+
snd_printdd(KERN_DEBUG"%p:%i\n",
intf, intf->cur_altsetting->desc.bInterfaceNumber);
if (intf->cur_altsetting->desc.bInterfaceNumber != 1)
return 0;
- card = us122l_usb_probe(usb_get_intf(intf), id);
-
- if (card) {
- usb_set_intfdata(intf, card);
- return 0;
+ err = us122l_usb_probe(usb_get_intf(intf), id, &card);
+ if (err < 0) {
+ usb_put_intf(intf);
+ return err;
}
- usb_put_intf(intf);
- return -EIO;
+ usb_set_intfdata(intf, card);
+ return 0;
}
static void snd_us122l_disconnect(struct usb_interface *intf)
diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
index 1558a5c..4af8740 100644
--- a/sound/usb/usx2y/usX2Yhwdep.c
+++ b/sound/usb/usx2y/usX2Yhwdep.c
@@ -30,9 +30,6 @@
#include "usbusx2y.h"
#include "usX2Yhwdep.h"
-int usX2Y_hwdep_pcm_new(struct snd_card *card);
-
-
static int snd_us428ctls_vm_fault(struct vm_area_struct *area,
struct vm_fault *vmf)
{
@@ -106,16 +103,6 @@
}
-static int snd_usX2Y_hwdep_open(struct snd_hwdep *hw, struct file *file)
-{
- return 0;
-}
-
-static int snd_usX2Y_hwdep_release(struct snd_hwdep *hw, struct file *file)
-{
- return 0;
-}
-
static int snd_usX2Y_hwdep_dsp_status(struct snd_hwdep *hw,
struct snd_hwdep_dsp_status *info)
{
@@ -267,8 +254,6 @@
hw->iface = SNDRV_HWDEP_IFACE_USX2Y;
hw->private_data = usX2Y(card);
- hw->ops.open = snd_usX2Y_hwdep_open;
- hw->ops.release = snd_usX2Y_hwdep_release;
hw->ops.dsp_status = snd_usX2Y_hwdep_dsp_status;
hw->ops.dsp_load = snd_usX2Y_hwdep_dsp_load;
hw->ops.mmap = snd_us428ctls_mmap;
diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c
index 70b9635..24393da 100644
--- a/sound/usb/usx2y/usb_stream.c
+++ b/sound/usb/usx2y/usb_stream.c
@@ -557,7 +557,7 @@
s->idle_insize -= max_diff - max_diff_0;
s->idle_insize += urb_size - s->period_size;
if (s->idle_insize < 0) {
- snd_printk("%i %i %i\n",
+ snd_printk(KERN_WARNING "%i %i %i\n",
s->idle_insize, urb_size, s->period_size);
return;
} else if (s->idle_insize == 0) {
diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
index 11639bd..5ce0da2 100644
--- a/sound/usb/usx2y/usbusx2y.c
+++ b/sound/usb/usx2y/usbusx2y.c
@@ -227,9 +227,9 @@
if (usX2Y->US04) {
if (0 == usX2Y->US04->submitted)
- do
+ do {
err = usb_submit_urb(usX2Y->US04->urb[usX2Y->US04->submitted++], GFP_ATOMIC);
- while (!err && usX2Y->US04->submitted < usX2Y->US04->len);
+ } while (!err && usX2Y->US04->submitted < usX2Y->US04->len);
} else
if (us428ctls && us428ctls->p4outLast >= 0 && us428ctls->p4outLast < N_us428_p4out_BUFS) {
if (us428ctls->p4outLast != us428ctls->p4outSent) {
@@ -333,18 +333,21 @@
{ /* terminator */ }
};
-static struct snd_card *usX2Y_create_card(struct usb_device *device)
+static int usX2Y_create_card(struct usb_device *device, struct snd_card **cardp)
{
int dev;
struct snd_card * card;
+ int err;
+
for (dev = 0; dev < SNDRV_CARDS; ++dev)
if (enable[dev] && !snd_usX2Y_card_used[dev])
break;
if (dev >= SNDRV_CARDS)
- return NULL;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(struct usX2Ydev));
- if (!card)
- return NULL;
+ return -ENODEV;
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct usX2Ydev), &card);
+ if (err < 0)
+ return err;
snd_usX2Y_card_used[usX2Y(card)->chip.index = dev] = 1;
card->private_free = snd_usX2Y_card_private_free;
usX2Y(card)->chip.dev = device;
@@ -362,26 +365,36 @@
usX2Y(card)->chip.dev->bus->busnum, usX2Y(card)->chip.dev->devnum
);
snd_card_set_dev(card, &device->dev);
- return card;
+ *cardp = card;
+ return 0;
}
-static void *usX2Y_usb_probe(struct usb_device *device, struct usb_interface *intf, const struct usb_device_id *device_id)
+static int usX2Y_usb_probe(struct usb_device *device,
+ struct usb_interface *intf,
+ const struct usb_device_id *device_id,
+ struct snd_card **cardp)
{
int err;
struct snd_card * card;
+
+ *cardp = NULL;
if (le16_to_cpu(device->descriptor.idVendor) != 0x1604 ||
(le16_to_cpu(device->descriptor.idProduct) != USB_ID_US122 &&
le16_to_cpu(device->descriptor.idProduct) != USB_ID_US224 &&
- le16_to_cpu(device->descriptor.idProduct) != USB_ID_US428) ||
- !(card = usX2Y_create_card(device)))
- return NULL;
+ le16_to_cpu(device->descriptor.idProduct) != USB_ID_US428))
+ return -EINVAL;
+
+ err = usX2Y_create_card(device, &card);
+ if (err < 0)
+ return err;
if ((err = usX2Y_hwdep_new(card, device)) < 0 ||
(err = snd_card_register(card)) < 0) {
snd_card_free(card);
- return NULL;
+ return err;
}
- return card;
+ *cardp = card;
+ return 0;
}
/*
@@ -389,13 +402,14 @@
*/
static int snd_usX2Y_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
- void *chip;
- chip = usX2Y_usb_probe(interface_to_usbdev(intf), intf, id);
- if (chip) {
- usb_set_intfdata(intf, chip);
- return 0;
- } else
- return -EIO;
+ struct snd_card *card;
+ int err;
+
+ err = usX2Y_usb_probe(interface_to_usbdev(intf), intf, id, &card);
+ if (err < 0)
+ return err;
+ dev_set_drvdata(&intf->dev, card);
+ return 0;
}
static void snd_usX2Y_disconnect(struct usb_interface *intf)
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.h b/sound/usb/usx2y/usx2yhwdeppcm.h
index c3382fd..9c4fb84 100644
--- a/sound/usb/usx2y/usx2yhwdeppcm.h
+++ b/sound/usb/usx2y/usx2yhwdeppcm.h
@@ -18,3 +18,5 @@
volatile unsigned captured_iso_frames;
int capture_iso_start;
};
+
+int usX2Y_hwdep_pcm_new(struct snd_card *card);